diff options
| author | Charles.Forsyth <devnull@localhost> | 2006-12-22 21:39:35 +0000 |
|---|---|---|
| committer | Charles.Forsyth <devnull@localhost> | 2006-12-22 21:39:35 +0000 |
| commit | 74a4d8c26dd3c1e9febcb717cfd6cb6512991a7a (patch) | |
| tree | c6e220ba61db3a6ea4052e6841296d829654e664 /utils/ntsrv | |
| parent | 46439007cf417cbd9ac8049bb4122c890097a0fa (diff) | |
20060303
Diffstat (limited to 'utils/ntsrv')
| -rw-r--r-- | utils/ntsrv/domk | 2 | ||||
| -rwxr-xr-x | utils/ntsrv/mkfile | 25 | ||||
| -rw-r--r-- | utils/ntsrv/ntsrv.c | 573 |
3 files changed, 600 insertions, 0 deletions
diff --git a/utils/ntsrv/domk b/utils/ntsrv/domk new file mode 100644 index 00000000..544ad331 --- /dev/null +++ b/utils/ntsrv/domk @@ -0,0 +1,2 @@ +os -d $emuroot\utils\ntsrv mk | sed 's/\(([0-9]*)\)/:\1/ +s/
//' diff --git a/utils/ntsrv/mkfile b/utils/ntsrv/mkfile new file mode 100755 index 00000000..8d15bec8 --- /dev/null +++ b/utils/ntsrv/mkfile @@ -0,0 +1,25 @@ +#uncomment following line for full Microsoft debug symbols +#LDEBUG=-debug -debugtype:cv -pdb:none +<../../mkconfig +SYSTARG=Nt +SYSHOST=Nt +OBJTYPE=386 +OBJDIR=$SYSTARG/$OBJTYPE +<$ROOT/mkfiles/mkhost-$SYSHOST +<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE + +TARG=ntsrv + +OFILES= ntsrv.$O\ + +HFILES= + +LIBS=9 + +CFLAGS=$CFLAGS -I. + +BIN=$ROOT/$OBJDIR/bin + +SYSLIBS= $SYSLIBS wsock32.lib user32.lib gdi32.lib advapi32.lib winmm.lib mpr.lib + +<$ROOT/mkfiles/mkone-$SHELLTYPE diff --git a/utils/ntsrv/ntsrv.c b/utils/ntsrv/ntsrv.c new file mode 100644 index 00000000..881a0c3f --- /dev/null +++ b/utils/ntsrv/ntsrv.c @@ -0,0 +1,573 @@ +#ifndef ForNT4 +/* only for XP, 2000 and above - JobObject only available on these*/ +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x0500 +#endif + +#include "lib9.h" +#include <windows.h> +#include <winsvc.h> +#include <stdarg.h> + +#ifdef JOB_OBJECT_TERMINATE +#define Jobs +#endif + +#ifdef ForNT4 +#undef Jobs +#endif + +/* + * ntsrv add Name Inferno-root Cmds + * ntsrv del Name + * ntsrv run Name Inferno-root Cmds + * + * 'add' registers service: Name with args "run Name Inferno-root Cmds" + * 'del' unregisters service Name + * 'run' - only given by NT service manager when starting the service (see 'add') + * + * Cmds are cat'd (with space separator) and requoted for CreateProcess() + * + * There must be an ntsrv.exe in Inferno-root/Nt/386/bin + */ + + +SERVICE_STATUS status = { + SERVICE_WIN32_OWN_PROCESS, /* dwServiceType */ + 0, /* dwCurrentState */ + SERVICE_ACCEPT_STOP /* dwControlsAccepted */ +}; + +typedef struct Emu Emu; +struct Emu { + HANDLE proc; /* NULL if error */ + HANDLE job; /* job for all children */ + HANDLE stdin; /* stdio pipes */ + HANDLE stdout; + DWORD notepg; /* process group ID (in case we lack Jobs) */ +}; + +typedef struct Copyargs Copyargs; +struct Copyargs { + HANDLE in; + HANDLE out; +}; + +#ifdef Jobs +static char *myname = "ntsrv.exe"; +#else +static char *myname = "ntsrv4.exe"; +#endif +#define LOGFILE "grid\\slave\\svclog" +static char *name; +static char *root; +static char *cmds; +static SERVICE_STATUS_HANDLE statush; +static HANDLE emujob; /* win32 job object for emu session */ +static DWORD emugroup; /* process group ID for emu session */ +static HANDLE emuin; /* stdin pipe of emu */ +static HANDLE logf; + +HANDLE openlog(char*); +void logmsg(char*, ...); +void WINAPI infmain(ulong, LPTSTR[]); +void WINAPI infctl(ulong); +Emu runemu(char*); +HANDLE exporthandle(HANDLE, int); +DWORD WINAPI copy(LPVOID); +int shuttingdown = 0; +int nice = 0; + +static void +usage() +{ + fprint(2, "usage: ntsrv [-n] add name root cmds | del name\n"); +} + +/* (from rcsh) + * windows quoting rules - I think + * Words are seperated by space or tab + * Words containing a space or tab can be quoted using " + * 2N backslashes + " ==> N backslashes and end quote + * 2N+1 backslashes + " ==> N backslashes + literal " + * N backslashes not followed by " ==> N backslashes + */ +static char * +dblquote(char *cmd, char *s) +{ + int nb; + char *p; + + for(p=s; *p; p++) + if(*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r' || *p == '"') + break; + + if(*p == 0){ /* easy case */ + strcpy(cmd, s); + return cmd+(p-s); + } + + *cmd++ = '"'; + for(;;) { + for(nb=0; *s=='\\'; nb++) + *cmd++ = *s++; + + if(*s == 0) { /* trailing backslashes -> 2N */ + while(nb-- > 0) + *cmd++ = '\\'; + break; + } + + if(*s == '"') { /* literal quote -> 2N+1 backslashes */ + while(nb-- > 0) + *cmd++ = '\\'; + *cmd++ = '\\'; /* escape the quote */ + } + *cmd++ = *s++; + } + + *cmd++ = '"'; + *cmd = 0; + + return cmd; +} + +static char * +proccmd(char **argv) +{ + int i, n; + char *cmd, *p; + + /* conservatively calculate length of command; + * backslash expansion can cause growth in dblquote(). + */ + for(i=0,n=0; argv[i]; i++) { + n += 2*strlen(argv[i]); + } + n++; + + cmd = malloc(n); + for(i=0,p=cmd; argv[i]; i++) { + p = dblquote(p, argv[i]); + *p++ = ' '; + } + if(p != cmd) + p--; + *p = 0; + + return cmd; +} + +int +installnewemu() +{ + LPCTSTR currpath, newpath; + currpath = smprint("%s\\Nt\\386\\bin\\emu.exe", root); + newpath = smprint("%s\\Nt\\386\\bin\\newemu.exe", root); + if(GetFileAttributes(newpath) == 0xffffffff) // INVALID_FILE_ATTRIBUTES is not defined + return 0; + DeleteFile(currpath); // ignore error message - it might not be there. + if(MoveFile(newpath, currpath) == 0){ + logmsg("cannot rename %s to %s: %r", newpath, currpath); + return -1; + } + return 0; +} + +void WINAPI +infmain(ulong argc, char *argv[]) +{ + HANDLE cpt; + Emu emu; + Copyargs cp; + DWORD tid; + char *cmd; + + argc--; + argv++; + cmd = smprint("%s\\%s", root, LOGFILE); + logf = openlog(cmd); + free(cmd); + statush = RegisterServiceCtrlHandler(name, infctl); + if (statush == 0) + return; + + status.dwCurrentState = SERVICE_START_PENDING; + SetServiceStatus(statush, &status); + + while(installnewemu() != -1){ + /* start the service */ + cmd = smprint("%s\\Nt\\386\\bin\\emu.exe -r%s %s", root, root, cmds); + logmsg("starting %s", cmd); + emu = runemu(cmd); + free(cmd); + if (emu.proc == NULL) { + logmsg("runemu failed: %r"); + status.dwCurrentState = SERVICE_STOPPED; + SetServiceStatus(statush, &status); + return; + } + + cp.in = emu.stdout; + cp.out = logf; + cpt = CreateThread(NULL, 0, copy, (void*)&cp, 0, &tid); + if (cpt == NULL) { + logmsg("failed to create copy thread: %r"); + CloseHandle(emu.stdout); + } + + logmsg("infmain blocking on emu proc"); + emujob = emu.job; + emugroup = emu.notepg; + status.dwCurrentState = SERVICE_RUNNING; + SetServiceStatus(statush, &status); + WaitForSingleObject(emu.proc, INFINITE); + logmsg("infmain emu proc terminated"); + emujob = NULL; + emugroup = 0; +#ifdef Jobs + logmsg("terminating job"); + TerminateJobObject(emu.job, 0); +#else + logmsg("notepg (%d)", emu.notepg); + if(emu.notepg) + GenerateConsoleCtrlEvent(CTRL_C_EVENT, emu.notepg); +#endif + if (cpt) { + /* copy() sees eof on emu.stdout and exits */ + WaitForSingleObject(cpt, INFINITE); + CloseHandle(cpt); + CloseHandle(emu.stdout); + } + CloseHandle(emu.proc); + if(emu.job != NULL) + CloseHandle(emu.job); + CloseHandle(emu.stdin); + // XXX should check to see that we're not starting up again too quickly, as + // it's quite possible to get into an infinite loop here. + // but what are good criteria? 5 times? 100 times? + // 10 times within a minute? + // for the moment, just sleep for a while before restarting... + if(shuttingdown) + break; + SleepEx(10000, FALSE); + } + logmsg("infmain done"); + if (logf) + CloseHandle(logf); + status.dwCurrentState = SERVICE_STOPPED; + SetServiceStatus(statush, &status); + return; +} + +void WINAPI +infctl(ulong op) +{ + if (op != SERVICE_CONTROL_STOP) + return; + + /* stop the service (status set by infmain() + * + * NOTE: there is a race for emujob - may have been closed + * after test, but before TerminateJobObject() + * MSDN is unclear as to whether TerminatJobObject() handles + * NULL job ptr - should probably use a mutex + */ + shuttingdown = 1; +#ifdef Jobs + logmsg("svc stop: stopping job"); + if (emujob) + TerminateJobObject(emujob, 0); +#else + logmsg("svc stop: interrupting emu"); + if (emugroup) + GenerateConsoleCtrlEvent(CTRL_C_EVENT, emugroup); +#endif +} + +void +printerror(char *s) +{ + char *msg; + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER| + FORMAT_MESSAGE_FROM_SYSTEM| + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)&msg, + 0, + NULL); + fprint(2, "%s: %s\n", s, msg); + LocalFree(msg); +} + +int +add(char *name, char *root, char *cmds) +{ + char *path; + int r; + SC_HANDLE scm, scs; + char *nopt; + + nopt = nice ? " -n" : ""; + path = smprint("%s\\Nt\\386\\bin\\%s%s run %s %s %s", root, myname, nopt, name, root, cmds); + r = 0; + scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); + if (scm == NULL) { + printerror("cannot open service control manager"); + return -1; + } + scs = CreateService(scm, + name, + name, + SERVICE_START|SERVICE_STOP, + SERVICE_WIN32_OWN_PROCESS, + SERVICE_AUTO_START, + SERVICE_ERROR_IGNORE, + path, + NULL, + NULL, + NULL, + NULL, + NULL + ); + if (scs == NULL) { + printerror("cannot create service"); + r = -1; + } else { + CloseServiceHandle(scs); + } + CloseServiceHandle(scm); + return r; +} + +int +del(char *name) +{ + SC_HANDLE scm, scs; + + scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); + if (scm == NULL) { + printerror("cannot open service control manager"); + return -1; + } + + scs = OpenService(scm, name, DELETE); + if (scs == NULL) { + printerror("cannot open service"); + CloseServiceHandle(scm); + return -1; + } + if (!DeleteService(scs)) { + printerror("cannot delete Iservice"); + CloseServiceHandle(scs); + CloseServiceHandle(scm); + return -1; + } + CloseServiceHandle(scs); + CloseServiceHandle(scm); + return 0; +} + +HANDLE +openlog(char *p) +{ + HANDLE h; + h = CreateFile(p, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (h == INVALID_HANDLE_VALUE) + return NULL; + SetFilePointer(h, 0, NULL, FILE_END); + return h; +} + +void +logmsg(char *fmt, ...) +{ + int n; + char *p; + va_list args; + if(logf == 0) + return; + va_start(args, fmt); + p = vsmprint(fmt, args); + va_end(args); + n = strlen(p); + if (n) + WriteFile(logf, p, n, &n, NULL); + WriteFile(logf, "\n", 1, &n, NULL); +} + +Emu +runemu(char *cmd) +{ + Emu r = {NULL, NULL, NULL}; + STARTUPINFO si; + PROCESS_INFORMATION pinfo; + HANDLE job, emu, emut, stdin, stdout, stderr, emui, emuo; + SECURITY_ATTRIBUTES sec; + DWORD flags; + + job = emu = emut = stdin = stdout = stderr = emui = emuo = NULL; +#ifdef Jobs + job = CreateJobObject(NULL, NULL); + if (job == NULL) { + logmsg("cannot create job object: %r"); + goto error; + } +#endif + + /* set up pipes */ + sec.nLength = sizeof(sec); + sec.lpSecurityDescriptor = 0; + sec.bInheritHandle = 0; + if (!CreatePipe(&stdin, &emui, &sec, 0)) { + logmsg("cannot create stdin pipe: %r"); + goto error; + } + if (!CreatePipe(&emuo, &stdout, &sec, 0)) { + logmsg("cannot create stdout pipe: %r"); + goto error; + } + stdin = exporthandle(stdin, 1); + stdout = exporthandle(stdout, 1); + stderr = exporthandle(stdout, 0); + + /* create emu process (suspended) */ + memset(&si, 0, sizeof(si)); + si.cb = sizeof(si); + si.dwFlags = STARTF_USESTDHANDLES; + si.hStdInput = stdin; + si.hStdOutput = stdout; + si.hStdError = stderr; + + flags = CREATE_NEW_PROCESS_GROUP|CREATE_DEFAULT_ERROR_MODE|CREATE_SUSPENDED; + if(nice) + flags |= IDLE_PRIORITY_CLASS; + if(!CreateProcess(0, cmd, 0, 0, 1, flags, 0, 0, &si, &pinfo)) { + logmsg("cannot create process: %r"); + goto error; + } + emu = pinfo.hProcess; + emut = pinfo.hThread; + CloseHandle(stdin); + stdin = NULL; + CloseHandle(stdout); + stdout = NULL; + CloseHandle(stderr); + stderr = NULL; + +#ifdef Jobs + if(!AssignProcessToJobObject(job, emu)) { + logmsg("failed to assign emu to job: %r"); + goto error; + } +#endif + ResumeThread(emut); + CloseHandle(emut); + + r.proc = emu; + r.notepg = pinfo.dwProcessId; + r.job = job; /* will be NULL if not implemented (NT4) */ + r.stdin = emui; + r.stdout = emuo; + return r; + +error: + if (stdin) + CloseHandle(stdin); + if (stdout) + CloseHandle(stdout); + if (stderr) + CloseHandle(stderr); + if (emui) + CloseHandle(emuin); + if (emuo) + CloseHandle(emuo); + if (emut) + CloseHandle(emut); + if (emu) { + TerminateProcess(emu, 0); + CloseHandle(emu); + } + if (job) + CloseHandle(job); + return r; +} + +HANDLE +exporthandle(HANDLE h, int close) +{ + HANDLE cp, dh; + DWORD flags = DUPLICATE_SAME_ACCESS; + if (close) + flags |= DUPLICATE_CLOSE_SOURCE; + cp = GetCurrentProcess(); + if (!DuplicateHandle(cp, h, cp, &dh, DUPLICATE_SAME_ACCESS, 1, flags)) + return nil; + return dh; +} + +DWORD WINAPI +copy(void *arg) +{ + Copyargs *cp = (Copyargs*)arg; + char buf[1024]; + DWORD n; + + while (ReadFile(cp->in, buf, sizeof(buf), &n, NULL)) { + if (n && cp->out) + WriteFile(cp->out, buf, n, &n, NULL); + } + return 0; +} + +void +main(int argc, char *argv[]) +{ + char *verb; + SERVICE_TABLE_ENTRY services[2]; + + memset(services, 0, sizeof(services)); + + ARGBEGIN{ + case 'n': + nice = 1; + break; + default: + usage(); + }ARGEND + + if (argc < 2) { + usage(); + return; + } + + verb = argv[0]; + name = argv[1]; + if (argc > 2) + root = argv[2]; + if (argc > 3) + cmds = proccmd(argv+3); + + if (strcmp(verb, "del") == 0) + exit(del(name)); + if (strcmp(verb, "add") == 0) { + if (root == NULL || cmds == NULL) { + usage(); + return; + } + exit(add(name, root, cmds)); + } + if (strcmp(verb, "run") == 0) { + if (root == NULL || cmds == NULL || *cmds == '\0') { + usage(); + return; + } + services[0].lpServiceName = name; + services[0].lpServiceProc = infmain; + StartServiceCtrlDispatcher(services); + exit(0); + } + usage(); +} |
