#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<nb; i++){
*cmd++ = '\\';
*cmd++ = '\\';
}
break;
}
if(*s == '"') {
for(i=0; i<nb; i++){
*cmd++ = '\\';
*cmd++ = '\\';
}
*cmd++ = '\\';
*cmd++ = '"';
s++;
} else {
for(i=0; i<nb; i++)
*cmd++ = '\\';
s += chartorune(cmd++, s);
}
}
*cmd++ = '"';
*cmd = 0;
return cmd;
}
static Rune*
proccmd(char **argv)
{
int i, n;
Rune *cmd, *p;
/* conservative guess at size of cmd line */
for(i=0,n=0; argv[i]; i++) {
n += 3 + 2*strlen(argv[i]);
}
n += 2;
cmd = malloc(n*sizeof(Rune));
for(i=0,p=cmd; argv[i]; i++) {
p = dblquote(p, argv[i]);
*p++ = ' ';
}
*--p = 0;
return cmd;
}
syntax highlighted by Code2HTML, v. 0.9.1