1 #ifndef ForNT4 2 /* only for XP, 2000 and above - JobObject only available on these*/ 3 #undef _WIN32_WINNT 4 #define _WIN32_WINNT 0x0500 5 #endif 6 7 #include "lib9.h" 8 #include <windows.h> 9 #include <winsvc.h> 10 #include <stdarg.h> 11 12 #ifdef JOB_OBJECT_TERMINATE 13 #define Jobs 14 #endif 15 16 #ifdef ForNT4 17 #undef Jobs 18 #endif 19 20 /* 21 * ntsrv add Name Inferno-root Cmds 22 * ntsrv del Name 23 * ntsrv run Name Inferno-root Cmds 24 * 25 * 'add' registers service: Name with args "run Name Inferno-root Cmds" 26 * 'del' unregisters service Name 27 * 'run' - only given by NT service manager when starting the service (see 'add') 28 * 29 * Cmds are cat'd (with space separator) and requoted for CreateProcess() 30 * 31 * There must be an ntsrv.exe in Inferno-root/Nt/386/bin 32 */ 33 34 35 SERVICE_STATUS status = { 36 SERVICE_WIN32_OWN_PROCESS, /* dwServiceType */ 37 0, /* dwCurrentState */ 38 SERVICE_ACCEPT_STOP /* dwControlsAccepted */ 39 }; 40 41 typedef struct Emu Emu; 42 struct Emu { 43 HANDLE proc; /* NULL if error */ 44 HANDLE job; /* job for all children */ 45 HANDLE stdin; /* stdio pipes */ 46 HANDLE stdout; 47 DWORD notepg; /* process group ID (in case we lack Jobs) */ 48 }; 49 50 typedef struct Copyargs Copyargs; 51 struct Copyargs { 52 HANDLE in; 53 HANDLE out; 54 }; 55 56 #ifdef Jobs 57 static char *myname = "ntsrv.exe"; 58 #else 59 static char *myname = "ntsrv4.exe"; 60 #endif 61 #define LOGFILE "grid\\slave\\svclog" 62 static char *name; 63 static char *root; 64 static char *cmds; 65 static SERVICE_STATUS_HANDLE statush; 66 static HANDLE emujob; /* win32 job object for emu session */ 67 static DWORD emugroup; /* process group ID for emu session */ 68 static HANDLE emuin; /* stdin pipe of emu */ 69 static HANDLE logfile; 70 71 HANDLE openlog(char*); 72 void logmsg(char*, ...); 73 void WINAPI infmain(ulong, LPTSTR[]); 74 void WINAPI infctl(ulong); 75 Emu runemu(char*); 76 HANDLE exporthandle(HANDLE, int); 77 DWORD WINAPI copy(LPVOID); 78 int shuttingdown = 0; 79 int nice = 0; 80 81 static void 82 usage() 83 { 84 fprint(2, "usage: ntsrv [-n] add name root cmds | del name\n"); 85 } 86 87 /* (from rcsh) 88 * windows quoting rules - I think 89 * Words are seperated by space or tab 90 * Words containing a space or tab can be quoted using " 91 * 2N backslashes + " ==> N backslashes and end quote 92 * 2N+1 backslashes + " ==> N backslashes + literal " 93 * N backslashes not followed by " ==> N backslashes 94 */ 95 static char * 96 dblquote(char *cmd, char *s) 97 { 98 int nb; 99 char *p; 100 101 for(p=s; *p; p++) 102 if(*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r' || *p == '"') 103 break; 104 105 if(*p == 0){ /* easy case */ 106 strcpy(cmd, s); 107 return cmd+(p-s); 108 } 109 110 *cmd++ = '"'; 111 for(;;) { 112 for(nb=0; *s=='\\'; nb++) 113 *cmd++ = *s++; 114 115 if(*s == 0) { /* trailing backslashes -> 2N */ 116 while(nb-- > 0) 117 *cmd++ = '\\'; 118 break; 119 } 120 121 if(*s == '"') { /* literal quote -> 2N+1 backslashes */ 122 while(nb-- > 0) 123 *cmd++ = '\\'; 124 *cmd++ = '\\'; /* escape the quote */ 125 } 126 *cmd++ = *s++; 127 } 128 129 *cmd++ = '"'; 130 *cmd = 0; 131 132 return cmd; 133 } 134 135 static char * 136 proccmd(char **argv) 137 { 138 int i, n; 139 char *cmd, *p; 140 141 /* conservatively calculate length of command; 142 * backslash expansion can cause growth in dblquote(). 143 */ 144 for(i=0,n=0; argv[i]; i++) { 145 n += 2*strlen(argv[i]); 146 } 147 n++; 148 149 cmd = malloc(n); 150 for(i=0,p=cmd; argv[i]; i++) { 151 p = dblquote(p, argv[i]); 152 *p++ = ' '; 153 } 154 if(p != cmd) 155 p--; 156 *p = 0; 157 158 return cmd; 159 } 160 161 int 162 installnewemu() 163 { 164 LPCTSTR currpath, newpath; 165 currpath = smprint("%s\\Nt\\386\\bin\\emu.exe", root); 166 newpath = smprint("%s\\Nt\\386\\bin\\newemu.exe", root); 167 if(GetFileAttributes(newpath) == 0xffffffff) // INVALID_FILE_ATTRIBUTES is not defined 168 return 0; 169 DeleteFile(currpath); // ignore error message - it might not be there. 170 if(MoveFile(newpath, currpath) == 0){ 171 logmsg("cannot rename %s to %s: %r", newpath, currpath); 172 return -1; 173 } 174 return 0; 175 } 176 177 void WINAPI 178 infmain(ulong argc, char *argv[]) 179 { 180 HANDLE cpt; 181 Emu emu; 182 Copyargs cp; 183 DWORD tid; 184 char *cmd; 185 186 argc--; 187 argv++; 188 cmd = smprint("%s\\%s", root, LOGFILE); 189 logfile = openlog(cmd); 190 free(cmd); 191 statush = RegisterServiceCtrlHandler(name, infctl); 192 if (statush == 0) 193 return; 194 195 status.dwCurrentState = SERVICE_START_PENDING; 196 SetServiceStatus(statush, &status); 197 198 while(installnewemu() != -1){ 199 /* start the service */ 200 cmd = smprint("%s\\Nt\\386\\bin\\emu.exe -r%s %s", root, root, cmds); 201 logmsg("starting %s", cmd); 202 emu = runemu(cmd); 203 free(cmd); 204 if (emu.proc == NULL) { 205 logmsg("runemu failed: %r"); 206 status.dwCurrentState = SERVICE_STOPPED; 207 SetServiceStatus(statush, &status); 208 return; 209 } 210 211 cp.in = emu.stdout; 212 cp.out = logfile; 213 cpt = CreateThread(NULL, 0, copy, (void*)&cp, 0, &tid); 214 if (cpt == NULL) { 215 logmsg("failed to create copy thread: %r"); 216 CloseHandle(emu.stdout); 217 } 218 219 logmsg("infmain blocking on emu proc"); 220 emujob = emu.job; 221 emugroup = emu.notepg; 222 status.dwCurrentState = SERVICE_RUNNING; 223 SetServiceStatus(statush, &status); 224 WaitForSingleObject(emu.proc, INFINITE); 225 logmsg("infmain emu proc terminated"); 226 emujob = NULL; 227 emugroup = 0; 228 #ifdef Jobs 229 logmsg("terminating job"); 230 TerminateJobObject(emu.job, 0); 231 #else 232 logmsg("notepg (%d)", emu.notepg); 233 if(emu.notepg) 234 GenerateConsoleCtrlEvent(CTRL_C_EVENT, emu.notepg); 235 #endif 236 if (cpt) { 237 /* copy() sees eof on emu.stdout and exits */ 238 WaitForSingleObject(cpt, INFINITE); 239 CloseHandle(cpt); 240 CloseHandle(emu.stdout); 241 } 242 CloseHandle(emu.proc); 243 if(emu.job != NULL) 244 CloseHandle(emu.job); 245 CloseHandle(emu.stdin); 246 // XXX should check to see that we're not starting up again too quickly, as 247 // it's quite possible to get into an infinite loop here. 248 // but what are good criteria? 5 times? 100 times? 249 // 10 times within a minute? 250 // for the moment, just sleep for a while before restarting... 251 if(shuttingdown) 252 break; 253 SleepEx(10000, FALSE); 254 } 255 logmsg("infmain done"); 256 if (logfile) 257 CloseHandle(logfile); 258 status.dwCurrentState = SERVICE_STOPPED; 259 SetServiceStatus(statush, &status); 260 return; 261 } 262 263 void WINAPI 264 infctl(ulong op) 265 { 266 if (op != SERVICE_CONTROL_STOP) 267 return; 268 269 /* stop the service (status set by infmain() 270 * 271 * NOTE: there is a race for emujob - may have been closed 272 * after test, but before TerminateJobObject() 273 * MSDN is unclear as to whether TerminatJobObject() handles 274 * NULL job ptr - should probably use a mutex 275 */ 276 shuttingdown = 1; 277 #ifdef Jobs 278 logmsg("svc stop: stopping job"); 279 if (emujob) 280 TerminateJobObject(emujob, 0); 281 #else 282 logmsg("svc stop: interrupting emu"); 283 if (emugroup) 284 GenerateConsoleCtrlEvent(CTRL_C_EVENT, emugroup); 285 #endif 286 } 287 288 void 289 printerror(char *s) 290 { 291 char *msg; 292 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER| 293 FORMAT_MESSAGE_FROM_SYSTEM| 294 FORMAT_MESSAGE_IGNORE_INSERTS, 295 NULL, 296 GetLastError(), 297 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 298 (LPTSTR)&msg, 299 0, 300 NULL); 301 fprint(2, "%s: %s\n", s, msg); 302 LocalFree(msg); 303 } 304 305 int 306 add(char *name, char *root, char *cmds) 307 { 308 char *path; 309 int r; 310 SC_HANDLE scm, scs; 311 char *nopt; 312 313 nopt = nice ? " -n" : ""; 314 path = smprint("%s\\Nt\\386\\bin\\%s%s run %s %s %s", root, myname, nopt, name, root, cmds); 315 r = 0; 316 scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); 317 if (scm == NULL) { 318 printerror("cannot open service control manager"); 319 return -1; 320 } 321 scs = CreateService(scm, 322 name, 323 name, 324 SERVICE_START|SERVICE_STOP, 325 SERVICE_WIN32_OWN_PROCESS, 326 SERVICE_AUTO_START, 327 SERVICE_ERROR_IGNORE, 328 path, 329 NULL, 330 NULL, 331 NULL, 332 NULL, 333 NULL 334 ); 335 if (scs == NULL) { 336 printerror("cannot create service"); 337 r = -1; 338 } else { 339 CloseServiceHandle(scs); 340 } 341 CloseServiceHandle(scm); 342 return r; 343 } 344 345 int 346 del(char *name) 347 { 348 SC_HANDLE scm, scs; 349 350 scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); 351 if (scm == NULL) { 352 printerror("cannot open service control manager"); 353 return -1; 354 } 355 356 scs = OpenService(scm, name, DELETE); 357 if (scs == NULL) { 358 printerror("cannot open service"); 359 CloseServiceHandle(scm); 360 return -1; 361 } 362 if (!DeleteService(scs)) { 363 printerror("cannot delete Iservice"); 364 CloseServiceHandle(scs); 365 CloseServiceHandle(scm); 366 return -1; 367 } 368 CloseServiceHandle(scs); 369 CloseServiceHandle(scm); 370 return 0; 371 } 372 373 HANDLE 374 openlog(char *p) 375 { 376 HANDLE h; 377 h = CreateFile(p, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); 378 if (h == INVALID_HANDLE_VALUE) 379 return NULL; 380 SetFilePointer(h, 0, NULL, FILE_END); 381 return h; 382 } 383 384 void 385 logmsg(char *fmt, ...) 386 { 387 int n; 388 char *p; 389 va_list args; 390 if(logfile == 0) 391 return; 392 va_start(args, fmt); 393 p = vsmprint(fmt, args); 394 va_end(args); 395 n = strlen(p); 396 if (n) 397 WriteFile(logfile, p, n, &n, NULL); 398 WriteFile(logfile, "\n", 1, &n, NULL); 399 } 400 401 Emu 402 runemu(char *cmd) 403 { 404 Emu r = {NULL, NULL, NULL}; 405 STARTUPINFO si; 406 PROCESS_INFORMATION pinfo; 407 HANDLE job, emu, emut, stdin, stdout, stderr, emui, emuo; 408 SECURITY_ATTRIBUTES sec; 409 DWORD flags; 410 411 job = emu = emut = stdin = stdout = stderr = emui = emuo = NULL; 412 #ifdef Jobs 413 job = CreateJobObject(NULL, NULL); 414 if (job == NULL) { 415 logmsg("cannot create job object: %r"); 416 goto error; 417 } 418 #endif 419 420 /* set up pipes */ 421 sec.nLength = sizeof(sec); 422 sec.lpSecurityDescriptor = 0; 423 sec.bInheritHandle = 0; 424 if (!CreatePipe(&stdin, &emui, &sec, 0)) { 425 logmsg("cannot create stdin pipe: %r"); 426 goto error; 427 } 428 if (!CreatePipe(&emuo, &stdout, &sec, 0)) { 429 logmsg("cannot create stdout pipe: %r"); 430 goto error; 431 } 432 stdin = exporthandle(stdin, 1); 433 stdout = exporthandle(stdout, 1); 434 stderr = exporthandle(stdout, 0); 435 436 /* create emu process (suspended) */ 437 memset(&si, 0, sizeof(si)); 438 si.cb = sizeof(si); 439 si.dwFlags = STARTF_USESTDHANDLES; 440 si.hStdInput = stdin; 441 si.hStdOutput = stdout; 442 si.hStdError = stderr; 443 444 flags = CREATE_NEW_PROCESS_GROUP|CREATE_DEFAULT_ERROR_MODE|CREATE_SUSPENDED; 445 if(nice) 446 flags |= IDLE_PRIORITY_CLASS; 447 if(!CreateProcess(0, cmd, 0, 0, 1, flags, 0, 0, &si, &pinfo)) { 448 logmsg("cannot create process: %r"); 449 goto error; 450 } 451 emu = pinfo.hProcess; 452 emut = pinfo.hThread; 453 CloseHandle(stdin); 454 stdin = NULL; 455 CloseHandle(stdout); 456 stdout = NULL; 457 CloseHandle(stderr); 458 stderr = NULL; 459 460 #ifdef Jobs 461 if(!AssignProcessToJobObject(job, emu)) { 462 logmsg("failed to assign emu to job: %r"); 463 goto error; 464 } 465 #endif 466 ResumeThread(emut); 467 CloseHandle(emut); 468 469 r.proc = emu; 470 r.notepg = pinfo.dwProcessId; 471 r.job = job; /* will be NULL if not implemented (NT4) */ 472 r.stdin = emui; 473 r.stdout = emuo; 474 return r; 475 476 error: 477 if (stdin) 478 CloseHandle(stdin); 479 if (stdout) 480 CloseHandle(stdout); 481 if (stderr) 482 CloseHandle(stderr); 483 if (emui) 484 CloseHandle(emuin); 485 if (emuo) 486 CloseHandle(emuo); 487 if (emut) 488 CloseHandle(emut); 489 if (emu) { 490 TerminateProcess(emu, 0); 491 CloseHandle(emu); 492 } 493 if (job) 494 CloseHandle(job); 495 return r; 496 } 497 498 HANDLE 499 exporthandle(HANDLE h, int close) 500 { 501 HANDLE cp, dh; 502 DWORD flags = DUPLICATE_SAME_ACCESS; 503 if (close) 504 flags |= DUPLICATE_CLOSE_SOURCE; 505 cp = GetCurrentProcess(); 506 if (!DuplicateHandle(cp, h, cp, &dh, DUPLICATE_SAME_ACCESS, 1, flags)) 507 return nil; 508 return dh; 509 } 510 511 DWORD WINAPI 512 copy(void *arg) 513 { 514 Copyargs *cp = (Copyargs*)arg; 515 char buf[1024]; 516 DWORD n; 517 518 while (ReadFile(cp->in, buf, sizeof(buf), &n, NULL)) { 519 if (n && cp->out) 520 WriteFile(cp->out, buf, n, &n, NULL); 521 } 522 return 0; 523 } 524 525 void 526 main(int argc, char *argv[]) 527 { 528 char *verb; 529 SERVICE_TABLE_ENTRY services[2]; 530 531 memset(services, 0, sizeof(services)); 532 533 ARGBEGIN{ 534 case 'n': 535 nice = 1; 536 break; 537 default: 538 usage(); 539 }ARGEND 540 541 if (argc < 2) { 542 usage(); 543 return; 544 } 545 546 verb = argv[0]; 547 name = argv[1]; 548 if (argc > 2) 549 root = argv[2]; 550 if (argc > 3) 551 cmds = proccmd(argv+3); 552 553 if (strcmp(verb, "del") == 0) 554 exit(del(name)); 555 if (strcmp(verb, "add") == 0) { 556 if (root == NULL || cmds == NULL) { 557 usage(); 558 return; 559 } 560 exit(add(name, root, cmds)); 561 } 562 if (strcmp(verb, "run") == 0) { 563 if (root == NULL || cmds == NULL || *cmds == '\0') { 564 usage(); 565 return; 566 } 567 services[0].lpServiceName = name; 568 services[0].lpServiceProc = infmain; 569 StartServiceCtrlDispatcher(services); 570 exit(0); 571 } 572 usage(); 573 } 574