1 /* 2 * QEMU Guest Agent helpers for win32 service management 3 * 4 * Copyright IBM Corp. 2012 5 * 6 * Authors: 7 * Gal Hammer <ghammer@redhat.com> 8 * Michael Roth <mdroth@linux.vnet.ibm.com> 9 * 10 * This work is licensed under the terms of the GNU GPL, version 2 or later. 11 * See the COPYING file in the top-level directory. 12 */ 13 #include <stdlib.h> 14 #include <stdio.h> 15 #include <glib.h> 16 #include <windows.h> 17 #include "qga/service-win32.h" 18 19 static int printf_win_error(const char *text) 20 { 21 DWORD err = GetLastError(); 22 char *message; 23 int n; 24 25 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | 26 FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 27 NULL, 28 err, 29 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 30 (char *)&message, 0, 31 NULL); 32 n = fprintf(stderr, "%s. (Error: %d) %s", text, (int)err, message); 33 LocalFree(message); 34 35 return n; 36 } 37 38 /* Windows command line escaping. Based on 39 * <http://blogs.msdn.com/b/oldnewthing/archive/2010/09/17/10063629.aspx> and 40 * <http://msdn.microsoft.com/en-us/library/windows/desktop/17w5ykft%28v=vs.85%29.aspx>. 41 * 42 * The caller is responsible for initializing @buffer; prior contents are lost. 43 */ 44 static const char *win_escape_arg(const char *to_escape, GString *buffer) 45 { 46 size_t backslash_count; 47 const char *c; 48 49 /* open with a double quote */ 50 g_string_assign(buffer, "\""); 51 52 backslash_count = 0; 53 for (c = to_escape; *c != '\0'; ++c) { 54 switch (*c) { 55 case '\\': 56 /* The meaning depends on the first non-backslash character coming 57 * up. 58 */ 59 ++backslash_count; 60 break; 61 62 case '"': 63 /* We must escape each pending backslash, then escape the double 64 * quote. This creates a case of "odd number of backslashes [...] 65 * followed by a double quotation mark". 66 */ 67 while (backslash_count) { 68 --backslash_count; 69 g_string_append(buffer, "\\\\"); 70 } 71 g_string_append(buffer, "\\\""); 72 break; 73 74 default: 75 /* Any pending backslashes are without special meaning, flush them. 76 * "Backslashes are interpreted literally, unless they immediately 77 * precede a double quotation mark." 78 */ 79 while (backslash_count) { 80 --backslash_count; 81 g_string_append_c(buffer, '\\'); 82 } 83 g_string_append_c(buffer, *c); 84 } 85 } 86 87 /* We're about to close with a double quote in string delimiter role. 88 * Double all pending backslashes, creating a case of "even number of 89 * backslashes [...] followed by a double quotation mark". 90 */ 91 while (backslash_count) { 92 --backslash_count; 93 g_string_append(buffer, "\\\\"); 94 } 95 g_string_append_c(buffer, '"'); 96 97 return buffer->str; 98 } 99 100 int ga_install_service(const char *path, const char *logfile, 101 const char *state_dir) 102 { 103 int ret = EXIT_FAILURE; 104 SC_HANDLE manager; 105 SC_HANDLE service; 106 TCHAR module_fname[MAX_PATH]; 107 GString *esc; 108 GString *cmdline; 109 SERVICE_DESCRIPTION desc = { (char *)QGA_SERVICE_DESCRIPTION }; 110 111 if (GetModuleFileName(NULL, module_fname, MAX_PATH) == 0) { 112 printf_win_error("No full path to service's executable"); 113 return EXIT_FAILURE; 114 } 115 116 esc = g_string_new(""); 117 cmdline = g_string_new(""); 118 119 g_string_append_printf(cmdline, "%s -d", 120 win_escape_arg(module_fname, esc)); 121 122 if (path) { 123 g_string_append_printf(cmdline, " -p %s", win_escape_arg(path, esc)); 124 } 125 if (logfile) { 126 g_string_append_printf(cmdline, " -l %s -v", 127 win_escape_arg(logfile, esc)); 128 } 129 if (state_dir) { 130 g_string_append_printf(cmdline, " -t %s", 131 win_escape_arg(state_dir, esc)); 132 } 133 134 g_debug("service's cmdline: %s", cmdline->str); 135 136 manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); 137 if (manager == NULL) { 138 printf_win_error("No handle to service control manager"); 139 goto out_strings; 140 } 141 142 service = CreateService(manager, QGA_SERVICE_NAME, QGA_SERVICE_DISPLAY_NAME, 143 SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, 144 SERVICE_ERROR_NORMAL, cmdline->str, NULL, NULL, NULL, NULL, NULL); 145 if (service == NULL) { 146 printf_win_error("Failed to install service"); 147 goto out_manager; 148 } 149 150 ChangeServiceConfig2(service, SERVICE_CONFIG_DESCRIPTION, &desc); 151 fprintf(stderr, "Service was installed successfully.\n"); 152 ret = EXIT_SUCCESS; 153 CloseServiceHandle(service); 154 155 out_manager: 156 CloseServiceHandle(manager); 157 158 out_strings: 159 g_string_free(cmdline, TRUE); 160 g_string_free(esc, TRUE); 161 return ret; 162 } 163 164 int ga_uninstall_service(void) 165 { 166 SC_HANDLE manager; 167 SC_HANDLE service; 168 169 manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); 170 if (manager == NULL) { 171 printf_win_error("No handle to service control manager"); 172 return EXIT_FAILURE; 173 } 174 175 service = OpenService(manager, QGA_SERVICE_NAME, DELETE); 176 if (service == NULL) { 177 printf_win_error("No handle to service"); 178 CloseServiceHandle(manager); 179 return EXIT_FAILURE; 180 } 181 182 if (DeleteService(service) == FALSE) { 183 printf_win_error("Failed to delete service"); 184 } else { 185 fprintf(stderr, "Service was deleted successfully.\n"); 186 } 187 188 CloseServiceHandle(service); 189 CloseServiceHandle(manager); 190 191 return EXIT_SUCCESS; 192 } 193