From 3ac5605a52a21c6ebb9dde0c7f0ab368cce9f5fb Mon Sep 17 00:00:00 2001 From: EnderIce2 Date: Thu, 30 May 2024 05:32:56 +0300 Subject: [PATCH] Implement experimental bridge for kvm virtual machines --- .gitignore | 1 + .vscode/c_cpp_properties.json | 11 ++ Makefile | 1 + bridge.c | 298 +++++++++++++++++++++++++++++----- bridge.h | 13 ++ gui.c | 15 +- kvm.c | 100 ++++++++++++ main.c | 28 ++-- service.c | 9 - 9 files changed, 401 insertions(+), 75 deletions(-) create mode 100644 bridge.h create mode 100644 kvm.c diff --git a/.gitignore b/.gitignore index 41b749b..4170fd1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ *.o *.exe *.res +*.elf diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index 4bb8f11..b015af5 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -9,6 +9,17 @@ "cStandard": "c17", "cppStandard": "gnu++17", "intelliSenseMode": "windows-gcc-x64" + }, + { + "name": "Linux", + "includePath": [ + "${workspaceFolder}/**" + ], + "defines": [], + "cStandard": "c17", + "cppStandard": "gnu++17", + "intelliSenseMode": "linux-gcc-x64", + "compilerPath": "/usr/bin/gcc" } ], "version": 4 diff --git a/Makefile b/Makefile index c21b5f1..0e965f2 100644 --- a/Makefile +++ b/Makefile @@ -12,6 +12,7 @@ build: $(C_OBJECTS) $(info Linking) x86_64-w64-mingw32-windres bridge.rc -O coff -o bridge.res x86_64-w64-mingw32-gcc $(C_OBJECTS) bridge.res $(LFLAGS) $(DBGFLAGS) -o build/bridge.exe + gcc kvm.c -o build/rpc-bridge.elf %.o: %.c $(info Compiling $<) diff --git a/bridge.c b/bridge.c index ee8635d..2cc4759 100644 --- a/bridge.c +++ b/bridge.c @@ -4,6 +4,8 @@ #include #include +#include "bridge.h" + #define __linux_read 3 #define __linux_write 4 #define __linux_open 5 @@ -64,7 +66,7 @@ void print(char const *fmt, ...); LPTSTR GetErrorMessage(); extern BOOL RunningAsService; BOOL RetryNewConnection; -BOOL IsLinux; +OS_INFO OSInfo = {0}; HANDLE hOut = NULL; HANDLE hIn = NULL; @@ -102,7 +104,7 @@ static naked int darwin_syscall(int num, static inline int sys_read(int fd, void *buf, size_t count) { - if (IsLinux) + if (OSInfo.IsLinux) return linux_syscall(__linux_read, fd, buf, count, 0, 0, 0); else return darwin_syscall(__darwin_read, fd, buf, count, 0, 0, 0); @@ -110,7 +112,7 @@ static inline int sys_read(int fd, void *buf, size_t count) static inline int sys_write(int fd, const void *buf, size_t count) { - if (IsLinux) + if (OSInfo.IsLinux) return linux_syscall(__linux_write, fd, buf, count, 0, 0, 0); else return darwin_syscall(__darwin_write, fd, buf, count, 0, 0, 0); @@ -118,7 +120,7 @@ static inline int sys_write(int fd, const void *buf, size_t count) static inline int sys_open(const char *pathname, int flags, int mode) { - if (IsLinux) + if (OSInfo.IsLinux) return linux_syscall(__linux_open, pathname, flags, mode, 0, 0, 0); else return darwin_syscall(__darwin_open, pathname, flags, mode, 0, 0, 0); @@ -126,7 +128,7 @@ static inline int sys_open(const char *pathname, int flags, int mode) static inline int sys_close(int fd) { - if (IsLinux) + if (OSInfo.IsLinux) return linux_syscall(__linux_close, fd, 0, 0, 0, 0, 0); else return darwin_syscall(__darwin_close, fd, 0, 0, 0, 0, 0); @@ -134,25 +136,25 @@ static inline int sys_close(int fd) static inline unsigned int *sys_mmap(unsigned int *addr, size_t length, int prot, int flags, int fd, off_t offset) { - assert(IsLinux); + assert(OSInfo.IsLinux); return linux_syscall(__linux_mmap2, addr, length, prot, flags, fd, offset); } static inline int sys_munmap(unsigned int *addr, size_t length) { - assert(IsLinux); + assert(OSInfo.IsLinux); return linux_syscall(__linux_munmap, addr, length, 0, 0, 0, 0); } static inline int sys_socketcall(int call, unsigned long *args) { - assert(IsLinux); + assert(OSInfo.IsLinux); return linux_syscall(__linux_socketcall, call, args, 0, 0, 0, 0); } static inline int sys_socket(int domain, int type, int protocol) { - if (IsLinux) + if (OSInfo.IsLinux) return linux_syscall(__linux_socket, domain, type, protocol, 0, 0, 0); else return darwin_syscall(__darwin_socket, domain, type, protocol, 0, 0, 0); @@ -160,7 +162,7 @@ static inline int sys_socket(int domain, int type, int protocol) static inline int sys_connect(int s, caddr_t name, socklen_t namelen) { - if (IsLinux) + if (OSInfo.IsLinux) return linux_syscall(__linux_connect, s, name, namelen, 0, 0, 0); else return darwin_syscall(__darwin_connect, s, name, namelen, 0, 0, 0); @@ -174,7 +176,7 @@ char *native_getenv(const char *name) if (ret != 0) return lpBuffer; - if (!IsLinux) + if (!OSInfo.IsLinux) { char *value = getenv(name); if (value == NULL) @@ -251,9 +253,9 @@ void ConnectToSocket(int fd) { print("Connecting to socket\n"); const char *runtime; - if (IsLinux) + if (OSInfo.IsLinux) runtime = native_getenv("XDG_RUNTIME_DIR"); - else + else if (OSInfo.IsDarwin) { runtime = native_getenv("TMPDIR"); if (runtime == NULL) @@ -278,6 +280,11 @@ void ConnectToSocket(int fd) } } } + else + { + print("Unsupported OS\n"); + ExitProcess(1); + } print("IPC directory: %s\n", runtime); @@ -302,7 +309,7 @@ void ConnectToSocket(int fd) print("Connecting to %s\n", pipePath); - if (IsLinux) + if (OSInfo.IsLinux) { unsigned long socketArgs[] = { (unsigned long)fd, @@ -480,6 +487,162 @@ void PipeBufferOutThread(LPVOID lpParam) } } +void COMPipeBufferInThread(LPVOID lpParam) +{ + bridge_thread *bt = (bridge_thread *)lpParam; + print("COM In thread started using COM port and pipe %#x\n", bt->hPipe); + + HANDLE hComPort = CreateFile("\\\\.\\COM2", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); + if (hComPort == INVALID_HANDLE_VALUE) + { + print("Failed to open COM2 port: %s\n", GetErrorMessage()); + return; + } + + int EOFCount = 0; + while (TRUE) + { + char buffer[BUFFER_LENGTH]; + DWORD bytesRead; + + if (!ReadFile(hComPort, buffer, sizeof(buffer), &bytesRead, NULL)) + { + print("Failed to read from COM2 port: %s\n", GetErrorMessage()); + Sleep(1000); + continue; + } + + if (EOFCount > 4) + { + print("EOF count exceeded\n"); + RetryNewConnection = TRUE; + TerminateThread(hOut, 0); + break; + } + + if (bytesRead == 0) + { + print("EOF\n"); + Sleep(1000); + EOFCount++; + continue; + } + EOFCount = 0; + + print("Reading %d bytes from COM2 port: \"", bytesRead); + for (DWORD i = 0; i < bytesRead; i++) + print("%c", buffer[i]); + print("\"\n"); + + DWORD dwWritten; + if (!WriteFile(bt->hPipe, buffer, bytesRead, &dwWritten, NULL)) + { + if (GetLastError() == ERROR_BROKEN_PIPE) + { + RetryNewConnection = TRUE; + print("In Broken pipe\n"); + break; + } + + print("Failed to write to pipe: %s\n", GetErrorMessage()); + Sleep(1000); + continue; + } + + while (dwWritten < bytesRead) + { + int last_written = dwWritten; + if (!WriteFile(bt->hPipe, buffer + dwWritten, bytesRead - dwWritten, &dwWritten, NULL)) + { + if (GetLastError() == ERROR_BROKEN_PIPE) + { + RetryNewConnection = TRUE; + print("In Broken pipe\n"); + break; + } + + print("Failed to write to pipe: %s\n", GetErrorMessage()); + Sleep(1000); + continue; + } + + if (last_written == dwWritten) + { + print("Failed to write to pipe: %s\n", GetErrorMessage()); + Sleep(1000); + continue; + } + } + } + + CloseHandle(hComPort); +} + +void COMPipeBufferOutThread(LPVOID lpParam) +{ + bridge_thread *bt = (bridge_thread *)lpParam; + print("COM Out thread started using COM port and pipe %#x\n", bt->hPipe); + + HANDLE hComPort = CreateFile("\\\\.\\COM2", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); + if (hComPort == INVALID_HANDLE_VALUE) + { + print("Failed to open COM2 port: %s\n", GetErrorMessage()); + return; + } + + while (TRUE) + { + char buffer[BUFFER_LENGTH]; + DWORD dwRead; + + if (!ReadFile(bt->hPipe, buffer, sizeof(buffer), &dwRead, NULL)) + { + if (GetLastError() == ERROR_BROKEN_PIPE) + { + RetryNewConnection = TRUE; + print("Out Broken pipe\n"); + break; + } + + print("Failed to read from pipe: %s\n", GetErrorMessage()); + Sleep(1000); + continue; + } + + print("Writing %d bytes to COM2 port: \"", dwRead); + for (DWORD i = 0; i < dwRead; i++) + print("%c", buffer[i]); + print("\"\n"); + + DWORD bytesWritten; + if (!WriteFile(hComPort, buffer, dwRead, &bytesWritten, NULL)) + { + print("Failed to write to COM2 port: %s\n", GetErrorMessage()); + continue; + } + + while (bytesWritten < dwRead) + { + int last_written = bytesWritten; + if (!WriteFile(hComPort, buffer + bytesWritten, dwRead - bytesWritten, &bytesWritten, NULL)) + { + print("Failed to write to COM2 port: %s\n", GetErrorMessage()); + Sleep(1000); + continue; + } + + if (last_written == bytesWritten) + { + print("Failed to write to COM2 port: %s\n", GetErrorMessage()); + Sleep(1000); + continue; + } + } + } + + CloseHandle(hComPort); +} + void CreateBridge() { LPCTSTR lpszPipename = TEXT("\\\\.\\pipe\\discord-ipc-0"); @@ -533,43 +696,87 @@ NewConnection: print("Pipe connected\n"); int fd; - if (IsLinux) + if (!OSInfo.IsWine) { - unsigned long socketArgs[] = { - (unsigned long)AF_UNIX, - (unsigned long)SOCK_STREAM, - 0}; - fd = sys_socketcall(SYS_SOCKET, socketArgs); + print("Running on Windows\n"); + /* KVM, send 0xBB1569 and wait with timeout (TODO) for response 0xB41D6E */ + + print("Trying \\\\.\\COM2\n"); + HANDLE hComPort = CreateFile("\\\\.\\COM2", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); + assert(hComPort != INVALID_HANDLE_VALUE); + DWORD dwWritten; + print("Writing to COM port\n"); + char buffer[4] = "\x69\x15\xBB"; + assert(WriteFile(hComPort, buffer, sizeof(buffer), &dwWritten, NULL)); + print("Wrote %d bytes to COM port\n", dwWritten); + DWORD dwRead; + print("Reading from COM port\n"); + assert(ReadFile(hComPort, buffer, sizeof(buffer), &dwRead, NULL)); + print("Read %d bytes from COM port\n", dwRead); + + if (dwRead != 4 || memcmp(buffer, "\x6E\x1D\xB4", 4) != 0) + { + CloseHandle(hComPort); + print("Failed to connect to COM2\n"); + return; + } + + CloseHandle(hComPort); + print("Connected to COM2\n"); + + bridge_thread bt = {0, hPipe}; + + hIn = CreateThread(NULL, 0, + (LPTHREAD_START_ROUTINE)COMPipeBufferInThread, + (LPVOID)&bt, + 0, NULL); + + hOut = CreateThread(NULL, 0, + (LPTHREAD_START_ROUTINE)COMPipeBufferOutThread, + (LPVOID)&bt, + 0, NULL); } else - fd = sys_socket(AF_UNIX, SOCK_STREAM, 0); - - print("Socket %d created\n", fd); - - if (fd < 0) { - print("Failed to create socket: %d\n", fd); - if (!RunningAsService) - MessageBox(NULL, "Failed to create socket", - NULL, MB_OK | MB_ICONSTOP); - ExitProcess(1); + if (OSInfo.IsLinux) + { + unsigned long socketArgs[] = { + (unsigned long)AF_UNIX, + (unsigned long)SOCK_STREAM, + 0}; + fd = sys_socketcall(SYS_SOCKET, socketArgs); + } + else + fd = sys_socket(AF_UNIX, SOCK_STREAM, 0); + + print("Socket %d created\n", fd); + + if (fd < 0) + { + print("Failed to create socket: %d\n", fd); + if (!RunningAsService) + MessageBox(NULL, "Failed to create socket", + NULL, MB_OK | MB_ICONSTOP); + ExitProcess(1); + } + + ConnectToSocket(fd); + print("Connected to Discord\n"); + + bridge_thread bt = {fd, hPipe}; + + hIn = CreateThread(NULL, 0, + (LPTHREAD_START_ROUTINE)PipeBufferInThread, + (LPVOID)&bt, + 0, NULL); + + hOut = CreateThread(NULL, 0, + (LPTHREAD_START_ROUTINE)PipeBufferOutThread, + (LPVOID)&bt, + 0, NULL); } - ConnectToSocket(fd); - print("Connected to Discord\n"); - - bridge_thread bt = {fd, hPipe}; - - hIn = CreateThread(NULL, 0, - (LPTHREAD_START_ROUTINE)PipeBufferInThread, - (LPVOID)&bt, - 0, NULL); print("Created in thread %#lx\n", hIn); - - hOut = CreateThread(NULL, 0, - (LPTHREAD_START_ROUTINE)PipeBufferOutThread, - (LPVOID)&bt, - 0, NULL); print("Created out thread %#lx\n", hOut); if (hIn == NULL || hOut == NULL) @@ -600,7 +807,8 @@ NewConnection: print("Failed to terminate thread: %s\n", GetErrorMessage()); - sys_close(fd); + if (OSInfo.IsWine) + sys_close(fd); CloseHandle(hOut); CloseHandle(hIn); CloseHandle(hPipe); diff --git a/bridge.h b/bridge.h new file mode 100644 index 0000000..fcd5c5a --- /dev/null +++ b/bridge.h @@ -0,0 +1,13 @@ +#ifndef _BRIDGE_H +#define _BRIDGE_H + +#include + +typedef struct _OS_INFO +{ + BOOL IsLinux; + BOOL IsDarwin; + BOOL IsWine; +} OS_INFO; + +#endif // _BRIDGE_H diff --git a/gui.c b/gui.c index 6d4ebb2..4977a7f 100644 --- a/gui.c +++ b/gui.c @@ -4,6 +4,7 @@ #include #include +#include "bridge.h" #include "resource.h" /** @@ -17,7 +18,8 @@ void print(char const *fmt, ...); void InstallService(int ServiceStartType, LPCSTR Path); void RemoveService(); void CreateBridge(); -extern BOOL IsLinux; +extern OS_INFO OSInfo; +extern char *logFilePath; HWND hwnd = NULL; HANDLE hBridge = NULL; @@ -49,7 +51,7 @@ VOID HandleStartButton(BOOL Silent) return; } - SC_HANDLE hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); + SC_HANDLE hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT); if (hSCManager == NULL) { print("OpenSCManager failed: %s\n", GetErrorMessage()); @@ -180,7 +182,7 @@ LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) HandleRemoveButton(); break; case IDM_VIEW_LOG: - ShellExecute(NULL, "open", "C:\\windows\\notepad.exe", "C:\\windows\\logs\\bridge.log", NULL, SW_SHOW); + ShellExecute(NULL, "open", "C:\\windows\\notepad.exe", logFilePath, NULL, SW_SHOW); break; case IDM_HELP_DOCUMENTATION: ShellExecute(NULL, "open", "https://enderice2.github.io/rpc-bridge/index.html", NULL, NULL, SW_SHOWNORMAL); @@ -229,13 +231,6 @@ VOID SetButtonStyles(INT *btnStartStyle, INT *btnRemoveStyle, INT *btnInstallSty *btnRemoveStyle = WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP; *btnInstallStyle = WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP; - // if (!IsLinux) - // { - // *btnInstallStyle |= WS_DISABLED; - // *btnRemoveStyle |= WS_DISABLED; - // return; - // } - SC_HANDLE hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); SC_HANDLE schService = OpenService(hSCManager, "rpc-bridge", SERVICE_START | SERVICE_QUERY_STATUS); diff --git a/kvm.c b/kvm.c new file mode 100644 index 0000000..e313973 --- /dev/null +++ b/kvm.c @@ -0,0 +1,100 @@ +#if defined(__linux__) + +#include +#include +#include +#include +#include +#include +#include +#include + +void transferData(int fromSock, int toSock) +{ + char buffer[256]; + ssize_t bytesRead; + while ((bytesRead = read(fromSock, buffer, sizeof(buffer))) > 0) + { + write(toSock, buffer, bytesRead); + } +} + +int connectToSocket(const char *socketPath) +{ + int sock = socket(AF_UNIX, SOCK_STREAM, 0); + assert(sock >= 0); + + struct sockaddr_un addr; + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, socketPath, sizeof(addr.sun_path) - 1); + + int result = connect(sock, (struct sockaddr *)&addr, sizeof(addr)); + assert(result == 0); + + return sock; +} + +int kvmSock; +int discordSock; +void sigintHandler(int sig) +{ + close(kvmSock); + close(discordSock); + exit(0); +} + +int main() +{ + signal(SIGINT, sigintHandler); + + const char *kvmSocketPath = "/tmp/kvm-rpc-bridge"; + const char *discordSocketPath = getenv("XDG_RUNTIME_DIR"); + if (discordSocketPath == NULL) + { + fprintf(stderr, "XDG_RUNTIME_DIR environment variable not set\n"); + return EXIT_FAILURE; + } + char discordSocketFullPath[256]; + snprintf(discordSocketFullPath, sizeof(discordSocketFullPath), "%s/discord-ipc-0", discordSocketPath); + + printf("Trying Discord socket at %s\n", discordSocketFullPath); + discordSock = connectToSocket(discordSocketFullPath); + printf("Connected to Discord socket successfully\n"); + + // Connect to KVM socket + printf("Trying KVM socket at %s\n", kvmSocketPath); + kvmSock = connectToSocket(kvmSocketPath); + + // Read specific message from KVM and send confirmation + const char kvmExpectedMsg[] = {0x69, 0x15, 0xBB}; + const char kvmResponseMsg[] = {0x6E, 0x1D, 0xB4}; + + char buffer[3]; + ssize_t bytesRead = read(kvmSock, buffer, sizeof(buffer)); + printf("Read %ld bytes from KVM (%x %x %x)\n", bytesRead, buffer[0], buffer[1], buffer[2]); + assert(bytesRead == sizeof(kvmExpectedMsg)); + assert(memcmp(buffer, kvmExpectedMsg, sizeof(kvmExpectedMsg)) == 0); + + ssize_t bytesSent = write(kvmSock, kvmResponseMsg, sizeof(kvmResponseMsg)); + assert(bytesSent == sizeof(kvmResponseMsg)); + printf("Connected to KVM socket successfully\n"); + + // Transfer data between KVM and Discord sockets + if (fork() == 0) + { + // Child process: transfer data from KVM to Discord + transferData(kvmSock, discordSock); + } + else + { + // Parent process: transfer data from Discord to KVM + transferData(discordSock, kvmSock); + } + + close(kvmSock); + close(discordSock); + return 0; +} + +#endif // __linux__ diff --git a/main.c b/main.c index 92d4d7d..fff7ae5 100644 --- a/main.c +++ b/main.c @@ -3,10 +3,12 @@ #include #include +#include "bridge.h" #include "resource.h" FILE *g_logFile = NULL; BOOL RunningAsService = FALSE; +char *logFilePath = NULL; void CreateGUI(); void CreateBridge(); @@ -15,7 +17,7 @@ void ServiceMain(int argc, char *argv[]); void InstallService(int ServiceStartType, LPCSTR Path); char *native_getenv(const char *name); void RemoveService(); -extern BOOL IsLinux; +extern OS_INFO OSInfo; LPTSTR GetErrorMessage() { @@ -57,11 +59,8 @@ void DetectWine() } if (!GetProcAddress(hNTdll, "wine_get_version")) - { - MessageBox(NULL, "This program is only intended to run under Wine.", - GetErrorMessage(), MB_OK | MB_ICONINFORMATION); - ExitProcess(1); - } + return; + OSInfo.IsWine = TRUE; static void(CDECL * wine_get_host_version)(const char **sysname, const char **release); wine_get_host_version = (void *)GetProcAddress(hNTdll, "wine_get_host_version"); @@ -78,7 +77,8 @@ void DetectWine() ExitProcess(1); } - IsLinux = strcmp(__sysname, "Linux") == 0; + OSInfo.IsLinux = strcmp(__sysname, "Linux") == 0; + OSInfo.IsDarwin = strcmp(__sysname, "Darwin") == 0; } void print(char const *fmt, ...) @@ -113,14 +113,13 @@ void HandleArguments(int argc, char *argv[]) } else if (strcmp(argv[1], "--steam") == 0) { + assert(OSInfo.IsWine == TRUE); + /* All this mess just so when you close the game, it automatically closes the bridge and Steam will not say that the game is still running. */ print("Running as Steam\n"); - if (IsLinux == FALSE) - CreateBridge(); - if (argc > 2) { if (strcmp(argv[2], "--no-service") == 0) @@ -312,7 +311,14 @@ void HandleArguments(int argc, char *argv[]) int main(int argc, char *argv[]) { DetectWine(); - char *logFilePath = "C:\\windows\\logs\\bridge.log"; + if (OSInfo.IsWine) + logFilePath = "C:\\windows\\logs\\bridge.log"; + else + { + logFilePath = (char *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, MAX_PATH); + GetTempPath(MAX_PATH, logFilePath); + strcat_s(logFilePath, MAX_PATH, "bridge.log"); + } g_logFile = fopen(logFilePath, "w"); if (g_logFile == NULL) { diff --git a/service.c b/service.c index c38f7b6..9efcf8e 100644 --- a/service.c +++ b/service.c @@ -8,7 +8,6 @@ SERVICE_STATUS_HANDLE g_StatusHandle = NULL; void print(char const *fmt, ...); void CreateBridge(); LPTSTR GetErrorMessage(); -extern BOOL IsLinux; void WINAPI ServiceCtrlHandler(DWORD CtrlCode) { @@ -88,14 +87,6 @@ void InstallService(int ServiceStartType, LPCSTR Path) { print("Registering service\n"); - // if (IsLinux == FALSE) - // { - // /* FIXME: I don't know how to get the TMPDIR without getenv */ - // MessageBox(NULL, "Registering as a service is not supported on macOS at the moment.", - // "Unsupported", MB_OK | MB_ICONINFORMATION); - // ExitProcess(1); - // } - SC_HANDLE schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE); if (schSCManager == NULL) {