#include <9pm/windows.h> #include <9pm/u.h> #include <9pm/libc.h> #include <9pm/ns.h> #include <9pm/thread.h> #include <9pm/threadimpl.h> static void forkpasser(void); static void procsched(Tproc*); int mainstacksize; static int passerpid; static int notefd; /* for use only by passer */ static ulong tlsproc = ~0; static Proc thesysproc; static Rune* proccmd(char**); extern Rune* _fetchegrp(Egrp*); extern HANDLE _exporthandle(Chan*); extern Rune* _execpath(char*); typedef struct Mainarg Mainarg; struct Mainarg { int argc; char **argv; }; static void mainlauncher(void *arg) { Mainarg *a; a = arg; threadmain(a->argc, a->argv); } void pm_main(int argc, char *argv[]) { Mainarg *a; Tproc *p; extern void (*pm__sysfatal)(char *fmt, va_list arg); pm__qlockinit(_threadrendezvous); pm__sysfatal = _threadsysfatal; pm_quotefmtinstall(); /* for chanprint */ if(mainstacksize == 0) mainstacksize = 512*1024; /* Fork off the passer, set up the process groups. */ // forkpasser(); a = smalloc(sizeof(*a)); a->argc = argc; a->argv = argv; p = _newproc(mainlauncher, a, mainstacksize, "threadmain", 0, 0); tlsproc = TlsAlloc(); TlsSetValue(tlsproc, p); p->sysproc = thesysproc; _threaddebug(DBGPROC, "calling procsched"); procsched(p); /* not reached */ } static DWORD WINAPI proclauncher(LPVOID p) { TlsSetValue(tlsproc, p); _threaddebug(DBGPROC, "proclauncher %p\n", p); procsched(p); return 0; } /* * The general structure of procsched is system-independent, * but the system-dependent parts are special enough that it's * very hard to tease them out into separate functions. */ static void procsched(Tproc *self) { char exitstr[ERRMAX]; int action, flag, i, r; Fgrp *f; Execproc *e; HANDLE h, he[3]; PROCESS_INFORMATION pi; Rune *cmd, *eb, *file; STARTUPINFO si; Tproc *p; _threaddebug(DBGPROC, "procsched self=%p", self); self->pid = getpid(); self->sysproc.pid = self->pid; r = ~0; for(;;){ _threaddebug(DBGPROC, "procsched run", self->pid, self->curthread->id); switch(action=_threadswtch(self->oldenv, self->curthread->env, r)){ default: /* can't happen */ threadprint(2, "runproc, can't happen: %d\n", action); threadassert(0); case DOEXEC: /* fork off a program, return pid */ self = _threadgetproc(); _threaddebug(DBGPROC, "doexec"); e = &self->execproc; file = _execpath(e->file); if(file == nil){ r = -1; continue; } f = pm_getproc()->fgrp; for(i=0; i<3; i++) he[i] = INVALID_HANDLE_VALUE; if(f->maxfd > 0){ for(i=0; i<3; i++){ he[i] = _exporthandle(f->fd[i]); _threaddebug(DBGPROC, "_exporthandle %p = %p", f->fd[i], he[i]); } } memset(&si, 0, sizeof(si)); si.cb = sizeof(si); si.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES; si.wShowWindow = SW_SHOW; si.hStdInput = he[0]; si.hStdOutput = he[1]; si.hStdError = he[2]; _threaddebug(DBGPROC, "si handles %p %p %p", he[0], he[1], he[2]); flag = CREATE_UNICODE_ENVIRONMENT|CREATE_SEPARATE_WOW_VDM; // if(!hasconsole && type == Econsole) { // /* keep the console window hidden */ // si.wShowWindow = SW_HIDE; // } eb = _fetchegrp(self->sysproc.egrp); cmd = proccmd(e->arg); _threaddebug(DBGPROC, "CreateProcess..."); r = CreateProcess(file, cmd, 0, 0, 1, flag, eb, 0, &si, &pi); /* allow child to run */ Sleep(0); free(file); free(cmd); free(eb); CloseHandle(he[0]); CloseHandle(he[1]); CloseHandle(he[2]); if(!r){ oserror(); _threaddebug(DBGPROC, "CreateProcess: %r"); r = -1; }else{ CloseHandle(pi.hThread); CloseHandle(pi.hProcess); r = pi.dwProcessId; _threaddebug(DBGPROC, "CreateProcess => %d", r); } continue; case DOPROC: /* start a new Tproc running */ self = _threadgetproc(); _threaddebug(DBGPROC, "doproc"); p = self->arg; r = p->curthread->id; h = CreateThread(NULL, 8192, proclauncher, p, 0, NULL); CloseHandle(h); continue; case DOEXIT: /* destroy this proc */ self = _threadgetproc(); _threaddebug(DBGPROC, "doexit"); /* remove ourself from the list of active procs */ lock(&_threadpq.lk); if(_threadpq.head == self){ _threadpq.head = self->next; if(_threadpq.tail == self){ _threadpq.tail = nil; /* no procs left, signal the passer to exit */ _threadsignalpasser(); } }else{ for(p=_threadpq.head; p->next; p=p->next){ if(p->next == self){ p->next = self->next; if(_threadpq.tail == self) _threadpq.tail = p; break; } } } unlock(&_threadpq.lk); /* * Clean up and exit. Remember that we're on the * system stack segment (not a malloced stack), * thus it is okay to free self and all associated data. */ utfecpy(exitstr, exitstr+sizeof exitstr, self->exitstr); _freeproc(self); _exits(exitstr); } /* not reached */ abort(); } } void _threadsignal(void) { // pm_postnote(PNGROUP, getpid(), "threadsignal"); } void _threadsignalpasser(void) { // pm_postnote(PNPROC, passerpid, "threadsignal"); } void _osnewproc(Tproc *p) { p->impl = CreateSemaphore(NULL, 0, 1, NULL); if(p->impl == NULL) panic("CreateSemaphore"); } void _osfreeproc(Tproc *p) { CloseHandle(p->impl); } void _osexitsall(char *status) { if(status && *status) ExitProcess(1); ExitProcess(0); } void pm_nap(void) { Tproc *tp; tp = _threadgetproc(); _threaddebug(DBGPROC, "Proc %d sleeping", tp->pid); WaitForSingleObject(tp->impl, INFINITE); _threaddebug(DBGPROC, "Proc %d awake", tp->pid); } void pm_wake(Proc *p) { Tproc *tp; tp = (Tproc*)((ulong)p-(ulong)&((Tproc*)0)->sysproc); _threaddebug(DBGPROC, "Waking %d", tp->pid); ReleaseSemaphore(tp->impl, 1, NULL); } void _procexecwait(int pid) { Channel *c; Waitmsg *w; HANDLE h; DWORD status; if(pid == -1) return; _threaddebug(DBGCHLD, "Proc %d waiting for exec-ed child %d", getpid(), pid); h = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid); if(h == INVALID_HANDLE_VALUE || h == NULL){ _threaddebug(DBGCHLD, "Pid %d already dead\n"); if((c = _threadwaitchan) != nil){ w = _threadmalloc(sizeof(*w)+20, 1); w->pid = pid; w->msg = "missing"; sendp(c, w); } return; } for(;;){ if(!GetExitCodeProcess(h, &status)){ oserror(); _threaddebug(DBGCHLD, "GetExitCodeProcess: %r"); threadassert(0); } if(status != STILL_ACTIVE) break; WaitForSingleObject(h, INFINITE); } CloseHandle(h); if ((c = _threadwaitchan) != nil) { /* global is exposed */ _threaddebug(DBGCHLD, "Sending exit status for exec: pid %d, waiter pid %d, status %d", pid, getpid(), status); w = _threadmalloc(sizeof(*w)+20, 1); w->pid = pid; w->msg = (char*)&w[1]; snprint(w->msg, 15, "%d", status); sendp(c, w); } } Tproc* _threadgetproc(void) { if(tlsproc == ~0) return nil; return TlsGetValue(tlsproc); } Proc* pm_getproc(void) { Tproc *p; if(p = _threadgetproc()) return &p->sysproc; return &thesysproc; } long _threadtimes(long *t) { t[0] = t[1] = t[2] = t[3] = 0; return 0; } /* * 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 Rune * dblquote(Rune *cmd, char *s) { int nb, i; char *p; for(p=s; *p; p++) if(*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r' || *p == '"') break; if(*p == 0) { /* easy case */ while(*s) s += chartorune(cmd++, s); *cmd = 0; return cmd; } *cmd++ = '"'; for(;;) { for(nb=0; *s=='\\'; s++) nb++; if(*s == 0) { for(i=0; i