From d4476d6ef03f54b6ec7d382eb8b0f4e256a5ba90 Mon Sep 17 00:00:00 2001 From: Mattias Gustavsson Date: Thu, 25 Mar 2021 08:34:34 +0100 Subject: [PATCH] SDA-3024 Initial code setup for auto update --- auto_update/auto_update.sln | 41 +++ auto_update/auto_update_helper.cpp | 47 +++ auto_update/auto_update_helper.vcxproj | 135 ++++++++ auto_update/auto_update_service.cpp | 298 +++++++++++++++++ auto_update/auto_update_service.vcxproj | 131 ++++++++ auto_update/ipc.h | 425 ++++++++++++++++++++++++ auto_update/package.json | 10 + package.json | 1 + 8 files changed, 1088 insertions(+) create mode 100644 auto_update/auto_update.sln create mode 100644 auto_update/auto_update_helper.cpp create mode 100644 auto_update/auto_update_helper.vcxproj create mode 100644 auto_update/auto_update_service.cpp create mode 100644 auto_update/auto_update_service.vcxproj create mode 100644 auto_update/ipc.h create mode 100644 auto_update/package.json diff --git a/auto_update/auto_update.sln b/auto_update/auto_update.sln new file mode 100644 index 00000000..32c519af --- /dev/null +++ b/auto_update/auto_update.sln @@ -0,0 +1,41 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31112.23 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "auto_update_helper", "auto_update_helper.vcxproj", "{9A61CB06-1A97-440C-A931-508C9019F0FB}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "auto_update_service", "auto_update_service.vcxproj", "{7E157BD0-86F0-4325-848C-BA1C87D9C99C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {9A61CB06-1A97-440C-A931-508C9019F0FB}.Debug|x64.ActiveCfg = Debug|x64 + {9A61CB06-1A97-440C-A931-508C9019F0FB}.Debug|x64.Build.0 = Debug|x64 + {9A61CB06-1A97-440C-A931-508C9019F0FB}.Debug|x86.ActiveCfg = Debug|Win32 + {9A61CB06-1A97-440C-A931-508C9019F0FB}.Debug|x86.Build.0 = Debug|Win32 + {9A61CB06-1A97-440C-A931-508C9019F0FB}.Release|x64.ActiveCfg = Release|x64 + {9A61CB06-1A97-440C-A931-508C9019F0FB}.Release|x64.Build.0 = Release|x64 + {9A61CB06-1A97-440C-A931-508C9019F0FB}.Release|x86.ActiveCfg = Release|Win32 + {9A61CB06-1A97-440C-A931-508C9019F0FB}.Release|x86.Build.0 = Release|Win32 + {7E157BD0-86F0-4325-848C-BA1C87D9C99C}.Debug|x64.ActiveCfg = Debug|x64 + {7E157BD0-86F0-4325-848C-BA1C87D9C99C}.Debug|x64.Build.0 = Debug|x64 + {7E157BD0-86F0-4325-848C-BA1C87D9C99C}.Debug|x86.ActiveCfg = Debug|Win32 + {7E157BD0-86F0-4325-848C-BA1C87D9C99C}.Debug|x86.Build.0 = Debug|Win32 + {7E157BD0-86F0-4325-848C-BA1C87D9C99C}.Release|x64.ActiveCfg = Release|x64 + {7E157BD0-86F0-4325-848C-BA1C87D9C99C}.Release|x64.Build.0 = Release|x64 + {7E157BD0-86F0-4325-848C-BA1C87D9C99C}.Release|x86.ActiveCfg = Release|Win32 + {7E157BD0-86F0-4325-848C-BA1C87D9C99C}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {ACFD70FC-B270-4021-9FB7-2FB6C2EB1C43} + EndGlobalSection +EndGlobal diff --git a/auto_update/auto_update_helper.cpp b/auto_update/auto_update_helper.cpp new file mode 100644 index 00000000..812ee4b6 --- /dev/null +++ b/auto_update/auto_update_helper.cpp @@ -0,0 +1,47 @@ +#define _CRT_SECURE_NO_WARNINGS +#include +#include +#pragma comment(lib, "advapi32.lib") +#pragma comment( lib, "shell32.lib" ) + +#include +#include +#include "ipc.h" + + +int main( int argc, char** argv ) { + if( argc < 3 ) { + printf( "Not enough arguments" ); + return EXIT_FAILURE; + } + char const* installer_filename = argv[ 1 ]; + char const* application_filename = argv[ 2 ]; + ipc_client_t* client = ipc_client_connect(); + ipc_client_send( client, installer_filename ); + char response[ 256 ]; + int size = 0; + int temp_size = 0; + ipc_receive_status_t status = IPC_RECEIVE_STATUS_MORE_DATA; + while( size < sizeof( response ) - 1 && status == IPC_RECEIVE_STATUS_MORE_DATA ) { + status = ipc_client_receive( client, response + size, + sizeof( response ) - size - 1, &temp_size ); + size += temp_size; + } + response[ size ] = '\0'; + printf( "%s\n", response ); + ipc_client_connect(); + + int result = (int)(uintptr_t) ShellExecute( NULL, NULL, application_filename, + NULL, NULL, SW_SHOWNORMAL ); + + if( result <= 32 ) { + printf( "Failed to launch SDA after installation" ); + } + + if( strcmp( response, "OK" ) != 0 ) { + printf( "Installation failed" ); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/auto_update/auto_update_helper.vcxproj b/auto_update/auto_update_helper.vcxproj new file mode 100644 index 00000000..ac5fb195 --- /dev/null +++ b/auto_update/auto_update_helper.vcxproj @@ -0,0 +1,135 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {9A61CB06-1A97-440C-A931-508C9019F0FB} + autoupdate + 10.0 + auto_update_helper + + + + Application + true + v142 + MultiByte + + + Application + false + v142 + true + MultiByte + + + Application + true + v142 + MultiByte + + + Application + false + v142 + true + MultiByte + + + + + + + + + + + + + + + + + + + + + + + Level3 + Disabled + true + true + + + Console + + + + + Level3 + Disabled + true + true + + + Console + + + + + Level3 + MaxSpeed + true + true + true + true + + + Console + true + true + + + + + Level3 + MaxSpeed + true + true + true + true + + + Console + true + true + + + + + + + + + + + + \ No newline at end of file diff --git a/auto_update/auto_update_service.cpp b/auto_update/auto_update_service.cpp new file mode 100644 index 00000000..44e7feee --- /dev/null +++ b/auto_update/auto_update_service.cpp @@ -0,0 +1,298 @@ +#define _CRT_SECURE_NO_WARNINGS +#include +#include +#include +#include +#include + +#include "ipc.h" + +#pragma comment(lib, "advapi32.lib") +#pragma comment(lib, "Shell32.lib") + +#define SVCNAME TEXT("SDA Auto Update") + +SERVICE_STATUS gSvcStatus; +SERVICE_STATUS_HANDLE gSvcStatusHandle; +HANDLE ghSvcStopEvent = NULL; + +VOID SvcInstall(void); +VOID WINAPI SvcCtrlHandler( DWORD ); +VOID WINAPI SvcMain( DWORD, LPTSTR * ); + +VOID ReportSvcStatus( DWORD, DWORD, DWORD ); +VOID SvcInit( DWORD, LPTSTR * ); + + +// +// Purpose: +// Entry point for the process +// +// Parameters: +// None +// +// Return value: +// None, defaults to 0 (zero) +// +int __cdecl _tmain(int argc, TCHAR *argv[]) +{ + // If command-line parameter is "install", install the service. + // Otherwise, the service is probably being started by the SCM. + + if( lstrcmpi( argv[1], TEXT("install")) == 0 ) + { + SvcInstall(); + return 0; + } + + // TO_DO: Add any additional services for the process to this table. + SERVICE_TABLE_ENTRY DispatchTable[] = + { + { (LPSTR)SVCNAME, (LPSERVICE_MAIN_FUNCTION) SvcMain }, + { NULL, NULL } + }; + + // This call returns when the service has stopped. + // The process should simply terminate when the call returns. + + if (!StartServiceCtrlDispatcher( DispatchTable )) + { + //SvcReportEvent(TEXT("StartServiceCtrlDispatcher")); + } + return 0; +} + +// +// Purpose: +// Installs a service in the SCM database +// +// Parameters: +// None +// +// Return value: +// None +// +VOID SvcInstall() +{ + SC_HANDLE schSCManager; + SC_HANDLE schService; + TCHAR szPath[MAX_PATH]; + + if( !GetModuleFileName( NULL, szPath, MAX_PATH ) ) + { + printf("Cannot install service (%d)\n", GetLastError()); + return; + } + + // Get a handle to the SCM database. + + schSCManager = OpenSCManager( + NULL, // local computer + NULL, // ServicesActive database + SC_MANAGER_ALL_ACCESS); // full access rights + + if (NULL == schSCManager) + { + printf("OpenSCManager failed (%d)\n", GetLastError()); + return; + } + + // Create the service + + schService = CreateService( + schSCManager, // SCM database + SVCNAME, // name of service + SVCNAME, // service name to display + SERVICE_ALL_ACCESS, // desired access + SERVICE_WIN32_OWN_PROCESS, // service type + SERVICE_DEMAND_START, // start type + SERVICE_ERROR_NORMAL, // error control type + szPath, // path to service's binary + NULL, // no load ordering group + NULL, // no tag identifier + NULL, // no dependencies + NULL, // LocalSystem account + NULL); // no password + + if (schService == NULL) + { + printf("CreateService failed (%d)\n", GetLastError()); + CloseServiceHandle(schSCManager); + return; + } + else printf("Service installed successfully\n"); + + CloseServiceHandle(schService); + CloseServiceHandle(schSCManager); +} + +// +// Purpose: +// Entry point for the service +// +// Parameters: +// dwArgc - Number of arguments in the lpszArgv array +// lpszArgv - Array of strings. The first string is the name of +// the service and subsequent strings are passed by the process +// that called the StartService function to start the service. +// +// Return value: +// None. +// +VOID WINAPI SvcMain( DWORD dwArgc, LPTSTR *lpszArgv ) +{ + // Register the handler function for the service + + gSvcStatusHandle = RegisterServiceCtrlHandler( + SVCNAME, + SvcCtrlHandler); + + if( !gSvcStatusHandle ) + { +// SvcReportEvent(TEXT("RegisterServiceCtrlHandler")); + return; + } + + // These SERVICE_STATUS members remain as set here + + gSvcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + gSvcStatus.dwServiceSpecificExitCode = 0; + + // Report initial status to the SCM + + ReportSvcStatus( SERVICE_START_PENDING, NO_ERROR, 3000 ); + + // Perform service-specific initialization and work. + + SvcInit( dwArgc, lpszArgv ); +} + + +// +// Purpose: +// The service code +// +// Parameters: +// dwArgc - Number of arguments in the lpszArgv array +// lpszArgv - Array of strings. The first string is the name of +// the service and subsequent strings are passed by the process +// that called the StartService function to start the service. +// +// Return value: +// None +// +VOID SvcInit( DWORD dwArgc, LPTSTR *lpszArgv) +{ + ipc_server_t* server = ipc_server_start(); + + // TO_DO: Declare and set any required variables. + // Be sure to periodically call ReportSvcStatus() with + // SERVICE_START_PENDING. If initialization fails, call + // ReportSvcStatus with SERVICE_STOPPED. + + // Create an event. The control handler function, SvcCtrlHandler, + // signals this event when it receives the stop control code. + + ghSvcStopEvent = CreateEvent( + NULL, // default security attributes + TRUE, // manual reset event + FALSE, // not signaled + NULL); // no name + + if ( ghSvcStopEvent == NULL) + { + ReportSvcStatus( SERVICE_STOPPED, NO_ERROR, 0 ); + return; + } + + // Report running status when initialization is complete. + + ReportSvcStatus( SERVICE_RUNNING, NO_ERROR, 0 ); + + // TO_DO: Perform work until service stops. + + while(1) + { + // Check whether to stop the service. + + WaitForSingleObject(ghSvcStopEvent, INFINITE); + + ReportSvcStatus( SERVICE_STOPPED, NO_ERROR, 0 ); + return; + } +} + +// +// Purpose: +// Sets the current service status and reports it to the SCM. +// +// Parameters: +// dwCurrentState - The current state (see SERVICE_STATUS) +// dwWin32ExitCode - The system error code +// dwWaitHint - Estimated time for pending operation, +// in milliseconds +// +// Return value: +// None +// +VOID ReportSvcStatus( DWORD dwCurrentState, + DWORD dwWin32ExitCode, + DWORD dwWaitHint) +{ + static DWORD dwCheckPoint = 1; + + // Fill in the SERVICE_STATUS structure. + + gSvcStatus.dwCurrentState = dwCurrentState; + gSvcStatus.dwWin32ExitCode = dwWin32ExitCode; + gSvcStatus.dwWaitHint = dwWaitHint; + + if (dwCurrentState == SERVICE_START_PENDING) + gSvcStatus.dwControlsAccepted = 0; + else gSvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP; + + if ( (dwCurrentState == SERVICE_RUNNING) || + (dwCurrentState == SERVICE_STOPPED) ) + gSvcStatus.dwCheckPoint = 0; + else gSvcStatus.dwCheckPoint = dwCheckPoint++; + + // Report the status of the service to the SCM. + SetServiceStatus( gSvcStatusHandle, &gSvcStatus ); +} + +// +// Purpose: +// Called by SCM whenever a control code is sent to the service +// using the ControlService function. +// +// Parameters: +// dwCtrl - control code +// +// Return value: +// None +// +VOID WINAPI SvcCtrlHandler( DWORD dwCtrl ) +{ + // Handle the requested control code. + + switch(dwCtrl) + { + case SERVICE_CONTROL_STOP: + ReportSvcStatus(SERVICE_STOP_PENDING, NO_ERROR, 0); + + // Signal the service to stop. + + SetEvent(ghSvcStopEvent); + ReportSvcStatus(gSvcStatus.dwCurrentState, NO_ERROR, 0); + + return; + + case SERVICE_CONTROL_INTERROGATE: + break; + + default: + break; + } + +} + diff --git a/auto_update/auto_update_service.vcxproj b/auto_update/auto_update_service.vcxproj new file mode 100644 index 00000000..1bfe9c8e --- /dev/null +++ b/auto_update/auto_update_service.vcxproj @@ -0,0 +1,131 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {7E157BD0-86F0-4325-848C-BA1C87D9C99C} + autoupdateservice + 10.0 + + + + Application + true + v142 + MultiByte + + + Application + false + v142 + true + MultiByte + + + Application + true + v142 + MultiByte + + + Application + false + v142 + true + MultiByte + + + + + + + + + + + + + + + + + + + + + + + Level3 + Disabled + true + true + + + Console + + + + + Level3 + Disabled + true + true + + + Console + + + + + Level3 + MaxSpeed + true + true + true + true + + + Console + true + true + + + + + Level3 + MaxSpeed + true + true + true + true + + + Console + true + true + + + + + + + + + \ No newline at end of file diff --git a/auto_update/ipc.h b/auto_update/ipc.h new file mode 100644 index 00000000..b208868f --- /dev/null +++ b/auto_update/ipc.h @@ -0,0 +1,425 @@ + + +bool pipe_exists( const char* pipe_name ) { + WIN32_FIND_DATA data; + memset( &data, 0, sizeof( data ) ); + + HANDLE hfind = FindFirstFileA( "\\\\.\\pipe\\*", &data ); + if( hfind != INVALID_HANDLE_VALUE ) { + do { + char const* filename = data.cFileName; + if( _stricmp( filename, pipe_name ) == 0 ) { + FindClose( hfind ); + return true; + } + } while( FindNextFileA( hfind, &data ) ); + FindClose( hfind ); + } + + return false; +} + +#define PIPE_NAME "\\\\.\\pipe\\symphony_sda_auto_update_ipc" +#define BUFSIZE 512 + +struct ipc_client_t { + HANDLE pipe; +}; + + +ipc_client_t* ipc_client_connect() { + if( !pipe_exists( PIPE_NAME ) ) { + printf( "Named pipe does not exits\n" ); + return NULL; + } + + HANDLE pipe = NULL; + for( ; ; ) { // Keep trying to connect while pipe is busy + pipe = CreateFile( + PIPE_NAME, // pipe name + GENERIC_READ | // read and write access + GENERIC_WRITE, + 0, // no sharing + NULL, // default security attributes + OPEN_EXISTING, // opens existing pipe + 0, // default attributes + NULL ); // no template file + + // Break if the pipe handle is valid. + if( pipe != INVALID_HANDLE_VALUE ) { + break; + } + + // Exit if an error other than ERROR_PIPE_BUSY occurs. + if( GetLastError() != ERROR_PIPE_BUSY ) { + printf( "Could not open pipe. LastError=%d\n", GetLastError() ); + return NULL; + } + + // All pipe instances are busy, so wait for 20 seconds. + if( !WaitNamedPipe( PIPE_NAME, 20000 ) ) { + printf( "Could not open pipe: 20 second wait timed out." ); + return NULL; + } + } + + ipc_client_t* connection = new ipc_client_t(); + connection->pipe = pipe; + return connection; +} + + +void ipc_client_disconnect( ipc_client_t* connection ) { + CloseHandle( connection->pipe ); + delete connection; +} + + +bool ipc_client_send( ipc_client_t* connection, char const* message ) { + DWORD mode = PIPE_READMODE_MESSAGE; + BOOL success = SetNamedPipeHandleState( + connection->pipe, // pipe handle + &mode, // new pipe mode + NULL, // don't set maximum bytes + NULL ); // don't set maximum time + if( !success ) { + printf( "SetNamedPipeHandleState failed. LastError=%d\n", GetLastError() ); + return false; + } + + // Send a message to the pipe server. + DWORD written = 0; + success = WriteFile( + connection->pipe, // pipe handle + message, // message + (DWORD) strlen( message ) + 1, // message length + &written, // bytes written + NULL ); // not overlapped + + if( !success ) { + printf( "WriteFile to pipe failed. LastError=%d\n", GetLastError() ); + return false; + } + + return true; +} + +enum ipc_receive_status_t { + IPC_RECEIVE_STATUS_DONE, + IPC_RECEIVE_STATUS_MORE_DATA, + IPC_RECEIVE_STATUS_ERROR, +}; + +ipc_receive_status_t ipc_client_receive( ipc_client_t* connection, char* output, int output_size, int* received_size ) { + DWORD size_read = 0; + BOOL success = ReadFile( + connection->pipe, // pipe handle + output, // buffer to receive reply + output_size, // size of buffer + &size_read, // number of bytes read + NULL ); // not overlapped + + if( !success && GetLastError() != ERROR_MORE_DATA ) { + printf( "ReadFile from pipe failed. LastError=%d\n", GetLastError() ); + return IPC_RECEIVE_STATUS_ERROR; + } + + if( received_size ) { + *received_size = size_read; + } + + if( success ) { + return IPC_RECEIVE_STATUS_DONE; + } else { + return IPC_RECEIVE_STATUS_MORE_DATA; + } +} + + +// This routine is a simple function to print the client request to the console +// and populate the reply buffer with a default data string. This is where you +// would put the actual client request processing code that runs in the context +// of an instance thread. Keep in mind the main thread will continue to wait for +// and receive other client connections while the instance thread is working. +VOID run_installer( LPSTR pchRequest, LPSTR pchReply, LPDWORD pchBytes ) { + printf( "Client Request String:\"%s\"\n", pchRequest ); + + char command[ 512 ]; + sprintf( command, "/i %s /q", pchRequest ); + + SHELLEXECUTEINFO ShExecInfo = { 0 }; + ShExecInfo.cbSize = sizeof( SHELLEXECUTEINFO ); + ShExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS; + ShExecInfo.hwnd = NULL; + ShExecInfo.lpVerb = "open"; + ShExecInfo.lpFile = "msiexec"; + ShExecInfo.lpParameters = command; + ShExecInfo.lpDirectory = NULL; + ShExecInfo.nShow = SW_SHOW; + ShExecInfo.hInstApp = NULL; + ShellExecuteEx( &ShExecInfo ); + WaitForSingleObject( ShExecInfo.hProcess, INFINITE ); + CloseHandle( ShExecInfo.hProcess ); + + strcpy( pchReply, "OK" ); + *pchBytes = (DWORD)( ( strlen( pchReply ) + 1 ) * sizeof( CHAR ) ); +} + + + +struct ipc_server_t { + HANDLE thread; +}; + + +// This routine is a thread processing function to read from and reply to a client +// via the open pipe connection passed from the main loop. Note this allows +// the main loop to continue executing, potentially creating more threads of +// this procedure to run concurrently, depending on the number of incoming +// client connections. +DWORD WINAPI ipc_client_thread( LPVOID param ) { + HANDLE hHeap = GetProcessHeap(); + CHAR* pchRequest = (CHAR*) HeapAlloc( hHeap, 0, BUFSIZE * sizeof( CHAR ) ); + CHAR* pchReply = (CHAR*) HeapAlloc( hHeap, 0, BUFSIZE * sizeof( CHAR ) ); + + DWORD cbBytesRead = 0, cbReplyBytes = 0, cbWritten = 0; + + // Do some extra error checking since the app will keep running even if this + // thread fails. + + if( param == NULL ) { + printf( "\nERROR - Pipe Server Failure:\n" ); + printf( " ipc_client_thread got an unexpected NULL value in lpvParam.\n" ); + printf( " ipc_client_thread exitting.\n" ); + if( pchReply != NULL ) { + HeapFree( hHeap, 0, pchReply ); + } + if( pchRequest != NULL ) { + HeapFree( hHeap, 0, pchRequest ); + } + return EXIT_FAILURE; + } + + if( pchRequest == NULL ) { + printf( "\nERROR - Pipe Server Failure:\n" ); + printf( " ipc_client_thread got an unexpected NULL heap allocation.\n" ); + printf( " ipc_client_thread exitting.\n" ); + if( pchReply != NULL ) { + HeapFree( hHeap, 0, pchReply ); + } + return EXIT_FAILURE; + } + + if( pchReply == NULL ) { + printf( "\nERROR - Pipe Server Failure:\n"); + printf( " ipc_client_thread got an unexpected NULL heap allocation.\n" ); + printf( " ipc_client_thread exitting.\n"); + if( pchRequest != NULL ) { + HeapFree( hHeap, 0, pchRequest ); + } + return EXIT_FAILURE; + } + + // Print verbose messages. In production code, this should be for debugging only. + printf( "ipc_client_thread created, receiving and processing messages.\n" ); + + // The thread's parameter is a handle to a pipe object instance. + HANDLE hPipe = (HANDLE) param; + + // Loop until done reading + for( ; ; ) { + // Read client requests from the pipe. This simplistic code only allows messages + // up to BUFSIZE characters in length. + BOOL fSuccess = ReadFile( + hPipe, // handle to pipe + pchRequest, // buffer to receive data + BUFSIZE * sizeof( CHAR ), // size of buffer + &cbBytesRead, // number of bytes read + NULL ); // not overlapped I/O + + if( !fSuccess || cbBytesRead == 0 ) { + if (GetLastError() == ERROR_BROKEN_PIPE) { + printf( "ipc_client_thread: client disconnected.\n" ); + } else { + printf( "ipc_client_thread ReadFile failed, LastError=%d.\n", GetLastError() ); + } + break; + } + + // Process the incoming message. + run_installer( pchRequest, pchReply, &cbReplyBytes ); + + // Write the reply to the pipe. + fSuccess = WriteFile( + hPipe, // handle to pipe + pchReply, // buffer to write from + cbReplyBytes, // number of bytes to write + &cbWritten, // number of bytes written + NULL ); // not overlapped I/O + + if( !fSuccess || cbReplyBytes != cbWritten ) { + printf( ("ipc_client_thread WriteFile failed, LastError=%d.\n"), GetLastError()); + break; + } + } + + // Flush the pipe to allow the client to read the pipe's contents + // before disconnecting. Then disconnect the pipe, and close the + // handle to this pipe instance. + + FlushFileBuffers( hPipe ); + DisconnectNamedPipe( hPipe ); + CloseHandle( hPipe ); + + HeapFree( hHeap, 0, pchRequest ); + HeapFree( hHeap, 0, pchReply ); + + printf( "ipc_client_thread exiting.\n" ); + return EXIT_SUCCESS; +} + + +DWORD WINAPI ipc_server_thread( LPVOID param ) { + ipc_server_t* server = (ipc_server_t*) param; + + // TODO: logging + //fp = fopen( "C:\\auto_update_poc\\log.txt", "w" ); + //setvbuf(fp, NULL, _IONBF, 0); + + // Create security attribs + SID_IDENTIFIER_AUTHORITY auth = { SECURITY_WORLD_SID_AUTHORITY }; + PSID sid; + if( !AllocateAndInitializeSid( &auth, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &sid ) ) { + return EXIT_FAILURE; + } + + EXPLICIT_ACCESS access = { 0 }; + access.grfAccessPermissions = FILE_ALL_ACCESS; + access.grfAccessMode = SET_ACCESS; + access.grfInheritance = NO_INHERITANCE; + access.Trustee.TrusteeForm = TRUSTEE_IS_SID; + access.Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP; + access.Trustee.ptstrName = (LPTSTR)sid; + + PACL acl; + if( SetEntriesInAcl(1, &access, NULL, &acl) != ERROR_SUCCESS ) { + FreeSid(sid); + return EXIT_FAILURE; + } + + PSECURITY_DESCRIPTOR sd = (PSECURITY_DESCRIPTOR)LocalAlloc( LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH ); + if( !sd ) { + FreeSid( sid ); + return EXIT_FAILURE; + } + + if( !InitializeSecurityDescriptor( sd, SECURITY_DESCRIPTOR_REVISION ) ) { + LocalFree(sd); + FreeSid(sid); + return EXIT_FAILURE; + } + + if( !SetSecurityDescriptorDacl( sd, TRUE, acl, FALSE ) ) { + LocalFree( sd ); + FreeSid( sid ); + return EXIT_FAILURE; + } + + SECURITY_ATTRIBUTES attribs; + attribs.nLength = sizeof( SECURITY_ATTRIBUTES ); + attribs.lpSecurityDescriptor = sd; + attribs.bInheritHandle = -1; + + + // The main loop creates an instance of the named pipe and + // then waits for a client to connect to it. When the client + // connects, a thread is created to handle communications + // with that client, and this loop is free to wait for the + // next client connect request. It is an infinite loop. + + for( ; ; ) { + printf( "\nPipe Server: Main thread awaiting client connection on %s\n", PIPE_NAME ); + HANDLE hPipe = CreateNamedPipe( + PIPE_NAME, // pipe name + PIPE_ACCESS_DUPLEX, // read/write access + PIPE_TYPE_MESSAGE | // message type pipe + PIPE_READMODE_MESSAGE | // message-read mode + PIPE_WAIT, // blocking mode + PIPE_UNLIMITED_INSTANCES, // max. instances + BUFSIZE, // output buffer size + BUFSIZE, // input buffer size + 0, // client time-out + &attribs ); // default security attribute + + if( hPipe == INVALID_HANDLE_VALUE ) { + printf( "CreateNamedPipe failed, LastError=%d.\n", GetLastError() ); + LocalFree( acl ); + LocalFree( sd ); + FreeSid( sid ); + return EXIT_FAILURE; + } + + // Wait for the client to connect; if it succeeds, + // the function returns a nonzero value. If the function + // returns zero, GetLastError returns ERROR_PIPE_CONNECTED. + + BOOL fConnected = ConnectNamedPipe( hPipe, NULL ) ? + TRUE : ( GetLastError() == ERROR_PIPE_CONNECTED ); + + if( fConnected ) { + printf( "Client connected, creating a processing thread.\n" ); + + // Create a thread for this client. + DWORD dwThreadId = 0; + HANDLE hThread = CreateThread( + NULL, // no security attribute + 0, // default stack size + ipc_client_thread, // thread proc + (LPVOID) hPipe, // thread parameter + 0, // not suspended + &dwThreadId ); // returns thread ID + + if( hThread == NULL ) { + printf( "CreateThread failed, LastError=%d.\n", GetLastError() ); + LocalFree( acl ); + LocalFree( sd ); + FreeSid( sid ); + return EXIT_FAILURE; + } else { + CloseHandle( hThread ); + } + } else { + // The client could not connect, so close the pipe. + CloseHandle( hPipe ); + } + } + + + LocalFree( acl ); + LocalFree( sd ); + FreeSid( sid ); + return EXIT_SUCCESS; +} + + +ipc_server_t* ipc_server_start() { + DWORD threadId = 0; + HANDLE thread = CreateThread( + NULL, // default security attributes + 0, // use default stack size + ipc_server_thread, // thread function name + NULL, // argument to thread function + 0, // use default creation flags + &threadId ); // returns the thread identifier + + ipc_server_t* server = new ipc_server_t; + server->thread = thread; + return server; +} + + +void ipc_server_stop( ipc_server_t* server ) { + TerminateThread( server->thread, EXIT_SUCCESS ); + WaitForSingleObject( server->thread, INFINITE ); + delete server; +} diff --git a/auto_update/package.json b/auto_update/package.json new file mode 100644 index 00000000..746e0373 --- /dev/null +++ b/auto_update/package.json @@ -0,0 +1,10 @@ +{ + "name": "auto_update", + "version": "0.0.1", + "description": "SDA Auto Update", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "preinstall": "npm run build", + "build": "cl auto_update_helper.cpp /O2 /MT /nologo /link /SUBSYSTEM:CONSOLE" + } +} diff --git a/package.json b/package.json index 78c23103..2bd8e2c7 100644 --- a/package.json +++ b/package.json @@ -176,6 +176,7 @@ "shell-path": "2.1.0" }, "optionalDependencies": { + "auto-update": "file:auto_update", "screen-snippet": "git+https://github.com/symphonyoss/ScreenSnippet2.git#v1.0.11", "screen-share-indicator-frame": "git+https://github.com/symphonyoss/ScreenShareIndicatorFrame.git#v1.4.10", "swift-search": "2.0.2"