service.c (9633B)
1 /* Mixmaster version 3.0 -- (C) 1999 - 2006 Anonymizer Inc. and others. 2 3 Mixmaster may be redistributed and modified under certain conditions. 4 This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF 5 ANY KIND, either express or implied. See the file COPYRIGHT for 6 details. 7 8 Win32 Service support 9 $Id: service.c 934 2006-06-24 13:40:39Z rabbi $ */ 10 11 12 #include <windows.h> 13 #include <stdio.h> 14 #include <direct.h> 15 #include <io.h> 16 #include <fcntl.h> 17 18 #include "mix3.h" 19 20 #ifdef WIN32SERVICE 21 22 #define SVCNAME "Mixmaster" 23 #define SVCDISPLAYNAME "Mixmaster Service" 24 25 26 /* internal variables */ 27 static SERVICE_STATUS ssStatus; 28 static SERVICE_STATUS_HANDLE sshStatusHandle; 29 static BOOL not_service = FALSE; 30 31 static HANDLE hThread = NULL; 32 static HANDLE hMustTerminate = NULL; 33 34 /* internal function prototypes */ 35 VOID WINAPI service_ctrl(DWORD ctrl_code); 36 VOID WINAPI service_main(DWORD argc, LPSTR *argv); 37 static DWORD service_run(void); 38 static void service_stop(); 39 static int set_stdfiles(); 40 static int install_service(); 41 static int remove_service(); 42 static int run_notservice(int argc, char **argv); 43 BOOL WINAPI console_ctrl_handler(DWORD ctrl_type); 44 static char *GetLastErrorText(); 45 static BOOL send_status(DWORD current_state, DWORD exit_code, DWORD wait_hint, DWORD id); 46 static void event_log(DWORD id, char *eventmsg); 47 48 int mix_main(int argc, char *argv[]); 49 50 51 int main(int argc, char *argv[]) 52 { 53 SERVICE_TABLE_ENTRY dispatchTable[] = { 54 {SVCNAME, (LPSERVICE_MAIN_FUNCTION)service_main}, 55 {NULL, NULL} }; 56 57 if ((argc > 1) && ((argv[1][0] == '-') && (argv[1][1] == '-'))) { 58 if (!_stricmp("install-svc", argv[1]+2)) 59 return install_service(); 60 else if (!_stricmp("remove-svc", argv[1]+2)) 61 return remove_service(); 62 else if (_stricmp("run-svc", argv[1]+2) && !is_nt_service()) 63 return run_notservice(argc, argv); 64 } else if (!is_nt_service()) { 65 return run_notservice(argc, argv); 66 } 67 printf("mix --install-svc install the service\n"); 68 printf("mix --remove-svc remove the service\n"); 69 printf("mix --run-svc run as a service\n"); 70 printf("mix -h view a summary of the command line options.\n"); 71 72 printf("\nStartServiceCtrlDispatcher being called.\n" ); 73 printf("This may take several seconds. Please wait.\n" ); 74 if (!StartServiceCtrlDispatcher(dispatchTable)) { 75 printf("Service not started: StartServiceCtrlDispatcher failed.\n" ); 76 event_log(1000, "Service not started: StartServiceCtrlDispatcher failed"); 77 } 78 return 0; 79 } /* main */ 80 81 82 VOID WINAPI service_main(DWORD argc, LPSTR *argv) 83 { 84 DWORD err = 0; 85 86 if (!(sshStatusHandle = RegisterServiceCtrlHandler(SVCNAME, service_ctrl))) 87 return; 88 89 ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; 90 ssStatus.dwServiceSpecificExitCode = 0; 91 if (send_status(SERVICE_START_PENDING, NO_ERROR, 1000, 1020)) 92 err = service_run(); 93 94 send_status(SERVICE_STOPPED, err, 0, err ? 1030 : 30); 95 } /* service_main */ 96 97 98 VOID WINAPI service_ctrl(DWORD ctrl_code) 99 { /* Handle the requested control code. */ 100 if (ctrl_code == SERVICE_CONTROL_STOP || ctrl_code == SERVICE_CONTROL_SHUTDOWN) 101 service_stop(); 102 else 103 send_status(ssStatus.dwCurrentState, NO_ERROR, 0, 1040 + ctrl_code); 104 } /* service_ctrl */ 105 106 107 static DWORD service_run(void) 108 { 109 char filename[_MAX_PATH+1]; 110 char home[_MAX_PATH+1], *p; 111 char *svc_argv[2] = {filename, "-D"}; 112 113 if (!hMustTerminate) 114 hMustTerminate = CreateEvent(NULL, FALSE, FALSE, NULL); 115 set_nt_exit_event(hMustTerminate); 116 DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), 117 &hThread, 0, FALSE, DUPLICATE_SAME_ACCESS); 118 119 GetModuleFileName(NULL , filename, _MAX_PATH); 120 strcpy(home, filename); 121 if (p = strrchr(home, '\\')) { 122 *p = 0; 123 chdir(home); 124 } 125 126 if (!set_stdfiles()) { 127 event_log(1010, "stdin|stdout|stderr not created"); 128 return ERROR_SERVICE_NOT_ACTIVE; 129 } 130 131 send_status(SERVICE_RUNNING, NO_ERROR, 0, 1060); 132 event_log(10, "Mixmaster Service started"); 133 134 mix_main(2, svc_argv); 135 return 0; 136 } /* service_run */ 137 138 139 static void service_stop(void) 140 { 141 send_status(SERVICE_STOP_PENDING, NO_ERROR, 5000, 1070); 142 if (hMustTerminate) { 143 SetEvent(hMustTerminate); 144 if (WaitForSingleObject(hThread, 4500) == WAIT_TIMEOUT) { 145 if (hThread) { 146 TerminateThread(hThread, 0); 147 event_log(1080, "Mixmaster Service terminated forcibly"); 148 } 149 } else 150 event_log(20, "Mixmaster Service stopped"); 151 CloseHandle(hMustTerminate); 152 hMustTerminate = NULL; 153 } else 154 if (hThread) 155 TerminateThread(hThread, 0); 156 if (hThread) 157 CloseHandle(hThread); 158 hThread = NULL; 159 ssStatus.dwCurrentState = SERVICE_STOPPED; 160 } /* service_stop */ 161 162 163 static int set_stdfiles() 164 { /* needed for _popen() */ 165 static DWORD std_handles[]={STD_INPUT_HANDLE, STD_OUTPUT_HANDLE, STD_ERROR_HANDLE}; 166 FILE *stdfile[]={stdin, stdout, stderr}; 167 HANDLE hStd; 168 int fh, stf_fileno; 169 FILE *fl; 170 171 AllocConsole(); 172 for (stf_fileno=0; stf_fileno<=2; stf_fileno++) { 173 hStd = GetStdHandle(std_handles[stf_fileno]); 174 if (hStd == INVALID_HANDLE_VALUE) 175 return 0; 176 fh = _open_osfhandle((long)std_handles[stf_fileno], (stf_fileno ? _O_WRONLY : _O_RDONLY ) | _O_BINARY); 177 dup2(fh, stf_fileno); 178 fl = _fdopen(stf_fileno, (stf_fileno ? "wcb" : "rcb" )); 179 fflush(stdfile[stf_fileno]); 180 memcpy(stdfile[stf_fileno], fl, sizeof(FILE)); 181 } 182 return 1; 183 } /* set_stdfiles */ 184 185 186 static BOOL send_status(DWORD current_state, DWORD exit_code, DWORD wait_hint, DWORD id) 187 { 188 static DWORD dwCheckPoint = 1; 189 BOOL ret_val; 190 191 if (not_service) 192 return TRUE; 193 194 ssStatus.dwCurrentState = current_state; 195 ssStatus.dwWin32ExitCode = exit_code; 196 ssStatus.dwWaitHint = wait_hint; 197 ssStatus.dwControlsAccepted = (current_state == SERVICE_START_PENDING) ? 198 0 : SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; 199 ssStatus.dwCheckPoint = ((current_state == SERVICE_RUNNING) || (current_state == SERVICE_STOPPED)) ? 200 0 : dwCheckPoint++; 201 202 if (!(ret_val = SetServiceStatus(sshStatusHandle, &ssStatus))) 203 event_log(id, "SetServiceStatus failed"); 204 return ret_val; 205 } /* send_status */ 206 207 208 static void event_log(DWORD id, char *eventmsg) 209 { 210 HANDLE hEventSource; 211 char *pStrings[2] = {"", eventmsg}; 212 213 if (not_service) 214 return; 215 216 if (id > 1000) 217 pStrings[0] = GetLastErrorText(); 218 219 if (!(hEventSource = RegisterEventSource(NULL, SVCNAME))) 220 return; 221 ReportEvent(hEventSource, (WORD)((id < 1000) ? EVENTLOG_SUCCESS : EVENTLOG_ERROR_TYPE), 222 0, id, NULL, 2, 0, pStrings, NULL); 223 DeregisterEventSource(hEventSource); 224 } /* event_log */ 225 226 227 static int run_notservice(int argc, char ** argv) 228 { 229 not_service = TRUE; 230 return mix_main(argc, argv); 231 } /* run_notservice */ 232 233 234 static int install_service() 235 { 236 SC_HANDLE schService, schSCManager; 237 char filename[_MAX_PATH+10]; 238 239 if (GetModuleFileName(NULL, filename, _MAX_PATH) == 0) { 240 printf("Unable to install Mixmaster Service: %s\n", GetLastErrorText()); 241 return 1; 242 } 243 strcat(filename, " --run-svc"); 244 245 if (!(schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS))) { 246 printf("OpenSCManager failed: %s\n", GetLastErrorText()); 247 return 1; 248 } 249 schService = CreateService(schSCManager, SVCNAME, SVCDISPLAYNAME, 250 SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, 251 filename, NULL, NULL, NULL, NULL, NULL); 252 253 if (schService) { 254 printf("Mixmaster Service installed.\n"); 255 CloseServiceHandle(schService); 256 } else { 257 printf("CreateService failed: %s\n", GetLastErrorText()); 258 } 259 260 CloseServiceHandle(schSCManager); 261 return 0; 262 } /* install_service */ 263 264 265 static int remove_service() 266 { 267 SC_HANDLE schService, schSCManager; 268 int ret_val = 0; 269 270 if (!(schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS))) { 271 printf("OpenSCManager failed: %s\n", GetLastErrorText()); 272 return 1; 273 } 274 if (!(schService = OpenService(schSCManager, SVCNAME, SERVICE_ALL_ACCESS))) { 275 CloseServiceHandle(schSCManager); 276 printf("OpenService failed: %s\n", GetLastErrorText()); 277 return 1; 278 } 279 /* try to stop the service */ 280 if (ControlService(schService, SERVICE_CONTROL_STOP, &ssStatus)) { 281 printf("Stopping Mixmaster Service"); 282 do { 283 sleep(1); 284 printf("."); 285 QueryServiceStatus(schService, &ssStatus); 286 } while (ssStatus.dwCurrentState != SERVICE_STOP_PENDING); 287 288 if (ssStatus.dwCurrentState == SERVICE_STOPPED) 289 printf("\nMixmaster Service stopped.\n"); 290 else 291 printf("\n%Mixmaster Service failed to stop.\n"); 292 } 293 294 /* now remove the service */ 295 if (!DeleteService(schService)) { 296 ret_val = 1; 297 printf("DeleteService failed: %s\n", GetLastErrorText()); 298 } else 299 printf("Mixmaster Service removed.\n"); 300 301 CloseServiceHandle(schService); 302 CloseServiceHandle(schSCManager); 303 return ret_val; 304 } /* remove_service */ 305 306 307 static char *GetLastErrorText() 308 { 309 static char error_buf[256]; 310 DWORD dwRet, err; 311 LPSTR lpszTemp = NULL; 312 313 dwRet = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ARGUMENT_ARRAY, 314 NULL, err=GetLastError(), LANG_NEUTRAL, (LPSTR)&lpszTemp, 0, NULL); 315 316 /* supplied buffer is not long enough */ 317 if (!dwRet || (256 < (long)dwRet+14)) 318 sprintf(error_buf, "Error (0x%x)", err); 319 else { 320 lpszTemp[lstrlen(lpszTemp)-2] = '\0'; 321 /* remove cr and newline character */ 322 sprintf(error_buf, "%s (0x%x)", lpszTemp, err); 323 } 324 325 if (lpszTemp) 326 LocalFree((HLOCAL)lpszTemp); 327 328 return error_buf; 329 } /* GetLastErrorText */ 330 331 #endif /* WIN32SERVICE */