13e12c5d1SDavid du Colombier /* 23e12c5d1SDavid du Colombier * cpu.c - Make a connection to a cpu server 33e12c5d1SDavid du Colombier * 4219b2ee8SDavid du Colombier * Invoked by listen as 'cpu -R | -N service net netdir' 5219b2ee8SDavid du Colombier * by users as 'cpu [-h system] [-c cmd args ...]' 63e12c5d1SDavid du Colombier */ 73e12c5d1SDavid du Colombier 83e12c5d1SDavid du Colombier #include <u.h> 93e12c5d1SDavid du Colombier #include <libc.h> 103e12c5d1SDavid du Colombier #include <bio.h> 113e12c5d1SDavid du Colombier #include <auth.h> 127dd7cddfSDavid du Colombier #include <fcall.h> 1359cc4ca5SDavid du Colombier #include <libsec.h> 143e12c5d1SDavid du Colombier 159a747e4fSDavid du Colombier #define Maxfdata 8192 169800bf03SDavid du Colombier #define MaxStr 128 179a747e4fSDavid du Colombier 18*7fc543e0SDavid du Colombier void remoteside(void); 193e12c5d1SDavid du Colombier void fatal(int, char*, ...); 207dd7cddfSDavid du Colombier void lclnoteproc(int); 217dd7cddfSDavid du Colombier void rmtnoteproc(void); 223e12c5d1SDavid du Colombier void catcher(void*, char*); 233e12c5d1SDavid du Colombier void usage(void); 243e12c5d1SDavid du Colombier void writestr(int, char*, char*, int); 25219b2ee8SDavid du Colombier int readstr(int, char*, int); 263e12c5d1SDavid du Colombier char *rexcall(int*, char*, char*); 2759cc4ca5SDavid du Colombier int setamalg(char*); 28d9306527SDavid du Colombier char *keyspec = ""; 293e12c5d1SDavid du Colombier 303e12c5d1SDavid du Colombier int notechan; 3142bd533dSDavid du Colombier int exportpid; 329800bf03SDavid du Colombier char *system; 333e12c5d1SDavid du Colombier int cflag; 34219b2ee8SDavid du Colombier int dbg; 359a747e4fSDavid du Colombier char *user; 367a3484c3SDavid du Colombier char *patternfile; 37d1be6b08SDavid du Colombier char *origargs; 383e12c5d1SDavid du Colombier 399a747e4fSDavid du Colombier char *srvname = "ncpu"; 403e12c5d1SDavid du Colombier char *exportfs = "/bin/exportfs"; 4159cc4ca5SDavid du Colombier char *ealgs = "rc4_256 sha1"; 423e12c5d1SDavid du Colombier 439a747e4fSDavid du Colombier /* message size for exportfs; may be larger so we can do big graphics in CPU window */ 449a747e4fSDavid du Colombier int msgsize = Maxfdata+IOHDRSZ; 459a747e4fSDavid du Colombier 467dd7cddfSDavid du Colombier /* authentication mechanisms */ 477dd7cddfSDavid du Colombier static int netkeyauth(int); 487dd7cddfSDavid du Colombier static int netkeysrvauth(int, char*); 4959cc4ca5SDavid du Colombier static int p9auth(int); 5059cc4ca5SDavid du Colombier static int srvp9auth(int, char*); 519a747e4fSDavid du Colombier static int noauth(int); 529a747e4fSDavid du Colombier static int srvnoauth(int, char*); 537dd7cddfSDavid du Colombier 547dd7cddfSDavid du Colombier typedef struct AuthMethod AuthMethod; 557dd7cddfSDavid du Colombier struct AuthMethod { 567dd7cddfSDavid du Colombier char *name; /* name of method */ 577dd7cddfSDavid du Colombier int (*cf)(int); /* client side authentication */ 587dd7cddfSDavid du Colombier int (*sf)(int, char*); /* server side authentication */ 597dd7cddfSDavid du Colombier } authmethod[] = 607dd7cddfSDavid du Colombier { 619a747e4fSDavid du Colombier { "p9", p9auth, srvp9auth,}, 629a747e4fSDavid du Colombier { "netkey", netkeyauth, netkeysrvauth,}, 639a747e4fSDavid du Colombier // { "none", noauth, srvnoauth,}, 647dd7cddfSDavid du Colombier { nil, nil} 657dd7cddfSDavid du Colombier }; 667dd7cddfSDavid du Colombier AuthMethod *am = authmethod; /* default is p9 */ 677dd7cddfSDavid du Colombier 689a747e4fSDavid du Colombier char *p9authproto = "p9any"; 699a747e4fSDavid du Colombier 707dd7cddfSDavid du Colombier int setam(char*); 717dd7cddfSDavid du Colombier 723e12c5d1SDavid du Colombier void 73219b2ee8SDavid du Colombier usage(void) 74219b2ee8SDavid du Colombier { 7587a56857SDavid du Colombier fprint(2, "usage: cpu [-h system] [-u user] [-a authmethod] " 7687a56857SDavid du Colombier "[-e 'crypt hash'] [-k keypattern] [-P patternfile] " 7787a56857SDavid du Colombier "[-c cmd arg ...]\n"); 78219b2ee8SDavid du Colombier exits("usage"); 79219b2ee8SDavid du Colombier } 80219b2ee8SDavid du Colombier 81d1be6b08SDavid du Colombier /* 82d1be6b08SDavid du Colombier * reading /proc/pid/args yields either "name args" or "name [display args]", 83d1be6b08SDavid du Colombier * so return only args or display args. 84d1be6b08SDavid du Colombier */ 85d1be6b08SDavid du Colombier static char * 86d1be6b08SDavid du Colombier procgetname(void) 87d1be6b08SDavid du Colombier { 88d1be6b08SDavid du Colombier int fd, n; 89d1be6b08SDavid du Colombier char *lp, *rp; 90d1be6b08SDavid du Colombier char buf[256]; 91d1be6b08SDavid du Colombier 92d1be6b08SDavid du Colombier snprint(buf, sizeof buf, "#p/%d/args", getpid()); 93d1be6b08SDavid du Colombier if((fd = open(buf, OREAD)) < 0) 94d1be6b08SDavid du Colombier return strdup(""); 95d1be6b08SDavid du Colombier *buf = '\0'; 96d1be6b08SDavid du Colombier n = read(fd, buf, sizeof buf-1); 97d1be6b08SDavid du Colombier close(fd); 98d1be6b08SDavid du Colombier if (n >= 0) 99d1be6b08SDavid du Colombier buf[n] = '\0'; 100d1be6b08SDavid du Colombier if ((lp = strchr(buf, '[')) == nil || (rp = strrchr(buf, ']')) == nil) { 101d1be6b08SDavid du Colombier lp = strchr(buf, ' '); 102d1be6b08SDavid du Colombier if (lp == nil) 103d1be6b08SDavid du Colombier return strdup(""); 104d1be6b08SDavid du Colombier else 105d1be6b08SDavid du Colombier return strdup(lp+1); 106d1be6b08SDavid du Colombier } 107d1be6b08SDavid du Colombier *rp = '\0'; 108d1be6b08SDavid du Colombier return strdup(lp+1); 109d1be6b08SDavid du Colombier } 110d1be6b08SDavid du Colombier 111d1be6b08SDavid du Colombier /* 112d1be6b08SDavid du Colombier * based on libthread's threadsetname, but drags in less library code. 113d1be6b08SDavid du Colombier * actually just sets the arguments displayed. 114d1be6b08SDavid du Colombier */ 115d1be6b08SDavid du Colombier void 116d1be6b08SDavid du Colombier procsetname(char *fmt, ...) 117d1be6b08SDavid du Colombier { 118d1be6b08SDavid du Colombier int fd; 119d1be6b08SDavid du Colombier char *cmdname; 120d1be6b08SDavid du Colombier char buf[128]; 121d1be6b08SDavid du Colombier va_list arg; 122d1be6b08SDavid du Colombier 123d1be6b08SDavid du Colombier va_start(arg, fmt); 124d1be6b08SDavid du Colombier cmdname = vsmprint(fmt, arg); 125d1be6b08SDavid du Colombier va_end(arg); 126d1be6b08SDavid du Colombier if (cmdname == nil) 127d1be6b08SDavid du Colombier return; 128d1be6b08SDavid du Colombier snprint(buf, sizeof buf, "#p/%d/args", getpid()); 129d1be6b08SDavid du Colombier if((fd = open(buf, OWRITE)) >= 0){ 130d1be6b08SDavid du Colombier write(fd, cmdname, strlen(cmdname)+1); 131d1be6b08SDavid du Colombier close(fd); 132d1be6b08SDavid du Colombier } 133d1be6b08SDavid du Colombier free(cmdname); 134d1be6b08SDavid du Colombier } 135d1be6b08SDavid du Colombier 136219b2ee8SDavid du Colombier void 1373e12c5d1SDavid du Colombier main(int argc, char **argv) 1383e12c5d1SDavid du Colombier { 1399800bf03SDavid du Colombier char dat[MaxStr], buf[MaxStr], cmd[MaxStr], *p, *err; 140759d2aa8SDavid du Colombier int ac, fd, ms, data; 1417a3484c3SDavid du Colombier char *av[10]; 1423e12c5d1SDavid du Colombier 143d1be6b08SDavid du Colombier quotefmtinstall(); 144d1be6b08SDavid du Colombier origargs = procgetname(); 1459a747e4fSDavid du Colombier /* see if we should use a larger message size */ 1469a747e4fSDavid du Colombier fd = open("/dev/draw", OREAD); 1479a747e4fSDavid du Colombier if(fd > 0){ 1489a747e4fSDavid du Colombier ms = iounit(fd); 1499a747e4fSDavid du Colombier if(msgsize < ms+IOHDRSZ) 1509a747e4fSDavid du Colombier msgsize = ms+IOHDRSZ; 1519a747e4fSDavid du Colombier close(fd); 1529a747e4fSDavid du Colombier } 1539a747e4fSDavid du Colombier 1549a747e4fSDavid du Colombier user = getuser(); 1559a747e4fSDavid du Colombier if(user == nil) 1569a747e4fSDavid du Colombier fatal(1, "can't read user name"); 1573e12c5d1SDavid du Colombier ARGBEGIN{ 1587dd7cddfSDavid du Colombier case 'a': 1599a747e4fSDavid du Colombier p = EARGF(usage()); 1607dd7cddfSDavid du Colombier if(setam(p) < 0) 1619a747e4fSDavid du Colombier fatal(0, "unknown auth method %s", p); 1627dd7cddfSDavid du Colombier break; 16359cc4ca5SDavid du Colombier case 'e': 1649a747e4fSDavid du Colombier ealgs = EARGF(usage()); 16559cc4ca5SDavid du Colombier if(*ealgs == 0 || strcmp(ealgs, "clear") == 0) 16659cc4ca5SDavid du Colombier ealgs = nil; 16759cc4ca5SDavid du Colombier break; 168219b2ee8SDavid du Colombier case 'd': 169219b2ee8SDavid du Colombier dbg++; 170219b2ee8SDavid du Colombier break; 1717dd7cddfSDavid du Colombier case 'f': 1729a747e4fSDavid du Colombier /* ignored but accepted for compatibility */ 1739a747e4fSDavid du Colombier break; 1743e12c5d1SDavid du Colombier case 'R': /* From listen */ 175*7fc543e0SDavid du Colombier remoteside(); 1763e12c5d1SDavid du Colombier break; 1773e12c5d1SDavid du Colombier case 'h': 1789800bf03SDavid du Colombier system = EARGF(usage()); 1793e12c5d1SDavid du Colombier break; 1803e12c5d1SDavid du Colombier case 'c': 1813e12c5d1SDavid du Colombier cflag++; 1823e12c5d1SDavid du Colombier cmd[0] = '!'; 1833e12c5d1SDavid du Colombier cmd[1] = '\0'; 1843e12c5d1SDavid du Colombier while(p = ARGF()) { 1853e12c5d1SDavid du Colombier strcat(cmd, " "); 1863e12c5d1SDavid du Colombier strcat(cmd, p); 1873e12c5d1SDavid du Colombier } 1883e12c5d1SDavid du Colombier break; 189d9306527SDavid du Colombier case 'k': 190f366f900SDavid du Colombier keyspec = smprint("%s %s", keyspec, EARGF(usage())); 191d9306527SDavid du Colombier break; 192b280a0acSDavid du Colombier case 'P': 1937a3484c3SDavid du Colombier patternfile = EARGF(usage()); 1947a3484c3SDavid du Colombier break; 19567493d07SDavid du Colombier case 'u': 19667493d07SDavid du Colombier user = EARGF(usage()); 197f366f900SDavid du Colombier keyspec = smprint("%s user=%s", keyspec, user); 19867493d07SDavid du Colombier break; 19959cc4ca5SDavid du Colombier default: 20059cc4ca5SDavid du Colombier usage(); 2013e12c5d1SDavid du Colombier }ARGEND; 2023e12c5d1SDavid du Colombier 2039a747e4fSDavid du Colombier 2043e12c5d1SDavid du Colombier if(argc != 0) 2053e12c5d1SDavid du Colombier usage(); 2063e12c5d1SDavid du Colombier 2079800bf03SDavid du Colombier if(system == nil) { 2083e12c5d1SDavid du Colombier p = getenv("cpu"); 2093e12c5d1SDavid du Colombier if(p == 0) 2103e12c5d1SDavid du Colombier fatal(0, "set $cpu"); 2119800bf03SDavid du Colombier system = p; 2123e12c5d1SDavid du Colombier } 2133e12c5d1SDavid du Colombier 2143e12c5d1SDavid du Colombier if(err = rexcall(&data, system, srvname)) 2153e12c5d1SDavid du Colombier fatal(1, "%s: %s", err, system); 2163e12c5d1SDavid du Colombier 217d1be6b08SDavid du Colombier procsetname("%s", origargs); 2183e12c5d1SDavid du Colombier /* Tell the remote side the command to execute and where our working directory is */ 2193e12c5d1SDavid du Colombier if(cflag) 2203e12c5d1SDavid du Colombier writestr(data, cmd, "command", 0); 2213e12c5d1SDavid du Colombier if(getwd(dat, sizeof(dat)) == 0) 2223e12c5d1SDavid du Colombier writestr(data, "NO", "dir", 0); 2233e12c5d1SDavid du Colombier else 2243e12c5d1SDavid du Colombier writestr(data, dat, "dir", 0); 2253e12c5d1SDavid du Colombier 2267dd7cddfSDavid du Colombier /* start up a process to pass along notes */ 2277dd7cddfSDavid du Colombier lclnoteproc(data); 2283e12c5d1SDavid du Colombier 22959cc4ca5SDavid du Colombier /* 23059cc4ca5SDavid du Colombier * Wait for the other end to execute and start our file service 23159cc4ca5SDavid du Colombier * of /mnt/term 23259cc4ca5SDavid du Colombier */ 233219b2ee8SDavid du Colombier if(readstr(data, buf, sizeof(buf)) < 0) 234fb7f0c93SDavid du Colombier fatal(1, "waiting for FS: %r"); 2353e12c5d1SDavid du Colombier if(strncmp("FS", buf, 2) != 0) { 2363e12c5d1SDavid du Colombier print("remote cpu: %s", buf); 2373e12c5d1SDavid du Colombier exits(buf); 2383e12c5d1SDavid du Colombier } 2393e12c5d1SDavid du Colombier 2403e12c5d1SDavid du Colombier /* Begin serving the gnot namespace */ 2413e12c5d1SDavid du Colombier close(0); 2423e12c5d1SDavid du Colombier dup(data, 0); 2433e12c5d1SDavid du Colombier close(data); 244759d2aa8SDavid du Colombier 2459a747e4fSDavid du Colombier sprint(buf, "%d", msgsize); 2467a3484c3SDavid du Colombier ac = 0; 247759d2aa8SDavid du Colombier av[ac++] = exportfs; 2487a3484c3SDavid du Colombier av[ac++] = "-m"; 249759d2aa8SDavid du Colombier av[ac++] = buf; 250219b2ee8SDavid du Colombier if(dbg) 2517a3484c3SDavid du Colombier av[ac++] = "-d"; 2527a3484c3SDavid du Colombier if(patternfile != nil){ 253b280a0acSDavid du Colombier av[ac++] = "-P"; 2547a3484c3SDavid du Colombier av[ac++] = patternfile; 2557a3484c3SDavid du Colombier } 256759d2aa8SDavid du Colombier av[ac] = nil; 2577a3484c3SDavid du Colombier exec(exportfs, av); 2583e12c5d1SDavid du Colombier fatal(1, "starting exportfs"); 2593e12c5d1SDavid du Colombier } 2603e12c5d1SDavid du Colombier 2613e12c5d1SDavid du Colombier void 2623e12c5d1SDavid du Colombier fatal(int syserr, char *fmt, ...) 2633e12c5d1SDavid du Colombier { 2649a747e4fSDavid du Colombier Fmt f; 2659a747e4fSDavid du Colombier char *str; 2667dd7cddfSDavid du Colombier va_list arg; 2673e12c5d1SDavid du Colombier 2689a747e4fSDavid du Colombier fmtstrinit(&f); 2699a747e4fSDavid du Colombier fmtprint(&f, "cpu: "); 2707dd7cddfSDavid du Colombier va_start(arg, fmt); 2719a747e4fSDavid du Colombier fmtvprint(&f, fmt, arg); 2727dd7cddfSDavid du Colombier va_end(arg); 2733e12c5d1SDavid du Colombier if(syserr) 2749a747e4fSDavid du Colombier fmtprint(&f, ": %r"); 2759a747e4fSDavid du Colombier str = fmtstrflush(&f); 27687a56857SDavid du Colombier 27787a56857SDavid du Colombier fprint(2, "%s\n", str); 27887a56857SDavid du Colombier syslog(0, "cpu", str); 2799a747e4fSDavid du Colombier exits(str); 2803e12c5d1SDavid du Colombier } 2813e12c5d1SDavid du Colombier 2827dd7cddfSDavid du Colombier char *negstr = "negotiating authentication method"; 283219b2ee8SDavid du Colombier 2843e12c5d1SDavid du Colombier /* Invoked with stdin, stdout and stderr connected to the network connection */ 2853e12c5d1SDavid du Colombier void 286*7fc543e0SDavid du Colombier remoteside(void) 2873e12c5d1SDavid du Colombier { 2889800bf03SDavid du Colombier char user[MaxStr], home[MaxStr], buf[MaxStr], xdir[MaxStr], cmd[MaxStr]; 2893e12c5d1SDavid du Colombier int i, n, fd, badchdir, gotcmd; 2903e12c5d1SDavid du Colombier 291da51d93aSDavid du Colombier rfork(RFENVG); 292da51d93aSDavid du Colombier putenv("service", "cpu"); 29359cc4ca5SDavid du Colombier fd = 0; 29459cc4ca5SDavid du Colombier 2957dd7cddfSDavid du Colombier /* negotiate authentication mechanism */ 29659cc4ca5SDavid du Colombier n = readstr(fd, cmd, sizeof(cmd)); 2977dd7cddfSDavid du Colombier if(n < 0) 2987dd7cddfSDavid du Colombier fatal(1, "authenticating"); 29959cc4ca5SDavid du Colombier if(setamalg(cmd) < 0){ 3009a747e4fSDavid du Colombier writestr(fd, "unsupported auth method", nil, 0); 3017dd7cddfSDavid du Colombier fatal(1, "bad auth method %s", cmd); 3027dd7cddfSDavid du Colombier } else 30359cc4ca5SDavid du Colombier writestr(fd, "", "", 1); 3047dd7cddfSDavid du Colombier 30559cc4ca5SDavid du Colombier fd = (*am->sf)(fd, user); 30659cc4ca5SDavid du Colombier if(fd < 0) 307219b2ee8SDavid du Colombier fatal(1, "srvauth"); 3083e12c5d1SDavid du Colombier 3093e12c5d1SDavid du Colombier /* Set environment values for the user */ 3103e12c5d1SDavid du Colombier putenv("user", user); 3113e12c5d1SDavid du Colombier sprint(home, "/usr/%s", user); 3123e12c5d1SDavid du Colombier putenv("home", home); 3133e12c5d1SDavid du Colombier 3149a747e4fSDavid du Colombier /* Now collect invoking cpu's current directory or possibly a command */ 3153e12c5d1SDavid du Colombier gotcmd = 0; 31659cc4ca5SDavid du Colombier if(readstr(fd, xdir, sizeof(xdir)) < 0) 317219b2ee8SDavid du Colombier fatal(1, "dir/cmd"); 3183e12c5d1SDavid du Colombier if(xdir[0] == '!') { 3193e12c5d1SDavid du Colombier strcpy(cmd, &xdir[1]); 3203e12c5d1SDavid du Colombier gotcmd = 1; 32159cc4ca5SDavid du Colombier if(readstr(fd, xdir, sizeof(xdir)) < 0) 322219b2ee8SDavid du Colombier fatal(1, "dir"); 3233e12c5d1SDavid du Colombier } 3243e12c5d1SDavid du Colombier 3253e12c5d1SDavid du Colombier /* Establish the new process at the current working directory of the 3263e12c5d1SDavid du Colombier * gnot */ 3273e12c5d1SDavid du Colombier badchdir = 0; 3283e12c5d1SDavid du Colombier if(strcmp(xdir, "NO") == 0) 3293e12c5d1SDavid du Colombier chdir(home); 3303e12c5d1SDavid du Colombier else if(chdir(xdir) < 0) { 3313e12c5d1SDavid du Colombier badchdir = 1; 3323e12c5d1SDavid du Colombier chdir(home); 3333e12c5d1SDavid du Colombier } 3343e12c5d1SDavid du Colombier 3353e12c5d1SDavid du Colombier /* Start the gnot serving its namespace */ 33659cc4ca5SDavid du Colombier writestr(fd, "FS", "FS", 0); 33759cc4ca5SDavid du Colombier writestr(fd, "/", "exportfs dir", 0); 3383e12c5d1SDavid du Colombier 33959cc4ca5SDavid du Colombier n = read(fd, buf, sizeof(buf)); 3403e12c5d1SDavid du Colombier if(n != 2 || buf[0] != 'O' || buf[1] != 'K') 3413e12c5d1SDavid du Colombier exits("remote tree"); 3423e12c5d1SDavid du Colombier 3439a747e4fSDavid du Colombier /* make sure buffers are big by doing fversion explicitly; pick a huge number; other side will trim */ 3449a747e4fSDavid du Colombier strcpy(buf, VERSION9P); 3459a747e4fSDavid du Colombier if(fversion(fd, 64*1024, buf, sizeof buf) < 0) 3469a747e4fSDavid du Colombier exits("fversion failed"); 3479a747e4fSDavid du Colombier if(mount(fd, -1, "/mnt/term", MCREATE|MREPL, "") < 0) 3483e12c5d1SDavid du Colombier exits("mount failed"); 3499a747e4fSDavid du Colombier 3503e12c5d1SDavid du Colombier close(fd); 3513e12c5d1SDavid du Colombier 3527dd7cddfSDavid du Colombier /* the remote noteproc uses the mount so it must follow it */ 3537dd7cddfSDavid du Colombier rmtnoteproc(); 3547dd7cddfSDavid du Colombier 3553e12c5d1SDavid du Colombier for(i = 0; i < 3; i++) 3563e12c5d1SDavid du Colombier close(i); 3573e12c5d1SDavid du Colombier 3589a747e4fSDavid du Colombier if(open("/mnt/term/dev/cons", OREAD) != 0) 3593e12c5d1SDavid du Colombier exits("open stdin"); 3609a747e4fSDavid du Colombier if(open("/mnt/term/dev/cons", OWRITE) != 1) 3613e12c5d1SDavid du Colombier exits("open stdout"); 3623e12c5d1SDavid du Colombier dup(1, 2); 3633e12c5d1SDavid du Colombier 3643e12c5d1SDavid du Colombier if(badchdir) 3653e12c5d1SDavid du Colombier print("cpu: failed to chdir to '%s'\n", xdir); 3663e12c5d1SDavid du Colombier 3673e12c5d1SDavid du Colombier if(gotcmd) 368f19e7b74SDavid du Colombier execl("/bin/rc", "rc", "-lc", cmd, nil); 3693e12c5d1SDavid du Colombier else 370f19e7b74SDavid du Colombier execl("/bin/rc", "rc", "-li", nil); 3713e12c5d1SDavid du Colombier fatal(1, "exec shell"); 3723e12c5d1SDavid du Colombier } 3733e12c5d1SDavid du Colombier 3743e12c5d1SDavid du Colombier char* 3753e12c5d1SDavid du Colombier rexcall(int *fd, char *host, char *service) 3763e12c5d1SDavid du Colombier { 3773e12c5d1SDavid du Colombier char *na; 3789800bf03SDavid du Colombier char dir[MaxStr]; 3799a747e4fSDavid du Colombier char err[ERRMAX]; 3809800bf03SDavid du Colombier char msg[MaxStr]; 3817dd7cddfSDavid du Colombier int n; 3823e12c5d1SDavid du Colombier 3833e12c5d1SDavid du Colombier na = netmkaddr(host, 0, service); 384d1be6b08SDavid du Colombier procsetname("dialing %s", na); 3857dd7cddfSDavid du Colombier if((*fd = dial(na, 0, dir, 0)) < 0) 3863e12c5d1SDavid du Colombier return "can't dial"; 3877dd7cddfSDavid du Colombier 3887dd7cddfSDavid du Colombier /* negotiate authentication mechanism */ 38959cc4ca5SDavid du Colombier if(ealgs != nil) 39059cc4ca5SDavid du Colombier snprint(msg, sizeof(msg), "%s %s", am->name, ealgs); 39159cc4ca5SDavid du Colombier else 39259cc4ca5SDavid du Colombier snprint(msg, sizeof(msg), "%s", am->name); 393d1be6b08SDavid du Colombier procsetname("writing %s", msg); 39459cc4ca5SDavid du Colombier writestr(*fd, msg, negstr, 0); 395d1be6b08SDavid du Colombier procsetname("awaiting auth method"); 3969a747e4fSDavid du Colombier n = readstr(*fd, err, sizeof err); 3977dd7cddfSDavid du Colombier if(n < 0) 3987dd7cddfSDavid du Colombier return negstr; 3997dd7cddfSDavid du Colombier if(*err){ 4007dd7cddfSDavid du Colombier werrstr(err); 4017dd7cddfSDavid du Colombier return negstr; 4027dd7cddfSDavid du Colombier } 4037dd7cddfSDavid du Colombier 4047dd7cddfSDavid du Colombier /* authenticate */ 405d1be6b08SDavid du Colombier procsetname("%s: auth via %s", origargs, am->name); 40659cc4ca5SDavid du Colombier *fd = (*am->cf)(*fd); 40759cc4ca5SDavid du Colombier if(*fd < 0) 408219b2ee8SDavid du Colombier return "can't authenticate"; 4093e12c5d1SDavid du Colombier return 0; 4103e12c5d1SDavid du Colombier } 4113e12c5d1SDavid du Colombier 4123e12c5d1SDavid du Colombier void 4133e12c5d1SDavid du Colombier writestr(int fd, char *str, char *thing, int ignore) 4143e12c5d1SDavid du Colombier { 4153e12c5d1SDavid du Colombier int l, n; 4163e12c5d1SDavid du Colombier 4173e12c5d1SDavid du Colombier l = strlen(str); 4183e12c5d1SDavid du Colombier n = write(fd, str, l+1); 4193e12c5d1SDavid du Colombier if(!ignore && n < 0) 4203e12c5d1SDavid du Colombier fatal(1, "writing network: %s", thing); 4213e12c5d1SDavid du Colombier } 4223e12c5d1SDavid du Colombier 423219b2ee8SDavid du Colombier int 424219b2ee8SDavid du Colombier readstr(int fd, char *str, int len) 4253e12c5d1SDavid du Colombier { 4263e12c5d1SDavid du Colombier int n; 4273e12c5d1SDavid du Colombier 4283e12c5d1SDavid du Colombier while(len) { 4293e12c5d1SDavid du Colombier n = read(fd, str, 1); 430219b2ee8SDavid du Colombier if(n < 0) 431219b2ee8SDavid du Colombier return -1; 4323e12c5d1SDavid du Colombier if(*str == '\0') 433219b2ee8SDavid du Colombier return 0; 4343e12c5d1SDavid du Colombier str++; 4353e12c5d1SDavid du Colombier len--; 4363e12c5d1SDavid du Colombier } 437219b2ee8SDavid du Colombier return -1; 4383e12c5d1SDavid du Colombier } 4397dd7cddfSDavid du Colombier 4407dd7cddfSDavid du Colombier static int 4417dd7cddfSDavid du Colombier readln(char *buf, int n) 4427dd7cddfSDavid du Colombier { 4439a747e4fSDavid du Colombier int i; 4449a747e4fSDavid du Colombier char *p; 4457dd7cddfSDavid du Colombier 4469a747e4fSDavid du Colombier n--; /* room for \0 */ 4479a747e4fSDavid du Colombier p = buf; 4489a747e4fSDavid du Colombier for(i=0; i<n; i++){ 4497dd7cddfSDavid du Colombier if(read(0, p, 1) != 1) 4507dd7cddfSDavid du Colombier break; 4519a747e4fSDavid du Colombier if(*p == '\n' || *p == '\r') 4529a747e4fSDavid du Colombier break; 4537dd7cddfSDavid du Colombier p++; 4547dd7cddfSDavid du Colombier } 4559a747e4fSDavid du Colombier *p = '\0'; 4567dd7cddfSDavid du Colombier return p-buf; 4577dd7cddfSDavid du Colombier } 4587dd7cddfSDavid du Colombier 45959cc4ca5SDavid du Colombier /* 46059cc4ca5SDavid du Colombier * user level challenge/response 46159cc4ca5SDavid du Colombier */ 4627dd7cddfSDavid du Colombier static int 4637dd7cddfSDavid du Colombier netkeyauth(int fd) 4647dd7cddfSDavid du Colombier { 4659a747e4fSDavid du Colombier char chall[32]; 4669a747e4fSDavid du Colombier char resp[32]; 4677dd7cddfSDavid du Colombier 4689800bf03SDavid du Colombier strecpy(chall, chall+sizeof chall, getuser()); 4697dd7cddfSDavid du Colombier print("user[%s]: ", chall); 4707dd7cddfSDavid du Colombier if(readln(resp, sizeof(resp)) < 0) 4717dd7cddfSDavid du Colombier return -1; 4727dd7cddfSDavid du Colombier if(*resp != 0) 4737dd7cddfSDavid du Colombier strcpy(chall, resp); 4747dd7cddfSDavid du Colombier writestr(fd, chall, "challenge/response", 1); 4757dd7cddfSDavid du Colombier 4767dd7cddfSDavid du Colombier for(;;){ 4777dd7cddfSDavid du Colombier if(readstr(fd, chall, sizeof chall) < 0) 4787dd7cddfSDavid du Colombier break; 4797dd7cddfSDavid du Colombier if(*chall == 0) 48059cc4ca5SDavid du Colombier return fd; 4817dd7cddfSDavid du Colombier print("challenge: %s\nresponse: ", chall); 4827dd7cddfSDavid du Colombier if(readln(resp, sizeof(resp)) < 0) 4837dd7cddfSDavid du Colombier break; 4847dd7cddfSDavid du Colombier writestr(fd, resp, "challenge/response", 1); 4857dd7cddfSDavid du Colombier } 4867dd7cddfSDavid du Colombier return -1; 4877dd7cddfSDavid du Colombier } 4887dd7cddfSDavid du Colombier 4897dd7cddfSDavid du Colombier static int 4907dd7cddfSDavid du Colombier netkeysrvauth(int fd, char *user) 4917dd7cddfSDavid du Colombier { 4929a747e4fSDavid du Colombier char response[32]; 4939a747e4fSDavid du Colombier Chalstate *ch; 4947dd7cddfSDavid du Colombier int tries; 4959a747e4fSDavid du Colombier AuthInfo *ai; 4967dd7cddfSDavid du Colombier 4979a747e4fSDavid du Colombier if(readstr(fd, user, 32) < 0) 4987dd7cddfSDavid du Colombier return -1; 4997dd7cddfSDavid du Colombier 5009a747e4fSDavid du Colombier ai = nil; 5019a747e4fSDavid du Colombier ch = nil; 5027dd7cddfSDavid du Colombier for(tries = 0; tries < 10; tries++){ 5039a747e4fSDavid du Colombier if((ch = auth_challenge("proto=p9cr role=server user=%q", user)) == nil) 5047dd7cddfSDavid du Colombier return -1; 5059a747e4fSDavid du Colombier writestr(fd, ch->chal, "challenge", 1); 5067dd7cddfSDavid du Colombier if(readstr(fd, response, sizeof response) < 0) 5077dd7cddfSDavid du Colombier return -1; 5089a747e4fSDavid du Colombier ch->resp = response; 5099a747e4fSDavid du Colombier ch->nresp = strlen(response); 5109a747e4fSDavid du Colombier if((ai = auth_response(ch)) != nil) 5117dd7cddfSDavid du Colombier break; 5127dd7cddfSDavid du Colombier } 5139a747e4fSDavid du Colombier auth_freechal(ch); 5149a747e4fSDavid du Colombier if(ai == nil) 5157dd7cddfSDavid du Colombier return -1; 5167dd7cddfSDavid du Colombier writestr(fd, "", "challenge", 1); 5179a747e4fSDavid du Colombier if(auth_chuid(ai, 0) < 0) 5189a747e4fSDavid du Colombier fatal(1, "newns"); 5199a747e4fSDavid du Colombier auth_freeAI(ai); 52059cc4ca5SDavid du Colombier return fd; 52159cc4ca5SDavid du Colombier } 52259cc4ca5SDavid du Colombier 52359cc4ca5SDavid du Colombier static void 52459cc4ca5SDavid du Colombier mksecret(char *t, uchar *f) 52559cc4ca5SDavid du Colombier { 52659cc4ca5SDavid du Colombier sprint(t, "%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux", 52759cc4ca5SDavid du Colombier f[0], f[1], f[2], f[3], f[4], f[5], f[6], f[7], f[8], f[9]); 52859cc4ca5SDavid du Colombier } 52959cc4ca5SDavid du Colombier 53059cc4ca5SDavid du Colombier /* 53159cc4ca5SDavid du Colombier * plan9 authentication followed by rc4 encryption 53259cc4ca5SDavid du Colombier */ 53359cc4ca5SDavid du Colombier static int 53459cc4ca5SDavid du Colombier p9auth(int fd) 53559cc4ca5SDavid du Colombier { 53659cc4ca5SDavid du Colombier uchar key[16]; 53759cc4ca5SDavid du Colombier uchar digest[SHA1dlen]; 53859cc4ca5SDavid du Colombier char fromclientsecret[21]; 53959cc4ca5SDavid du Colombier char fromserversecret[21]; 54059cc4ca5SDavid du Colombier int i; 5419a747e4fSDavid du Colombier AuthInfo *ai; 54259cc4ca5SDavid du Colombier 543d1be6b08SDavid du Colombier procsetname("%s: auth_proxy proto=%q role=client %s", 544d1be6b08SDavid du Colombier origargs, p9authproto, keyspec); 545d9306527SDavid du Colombier ai = auth_proxy(fd, auth_getkey, "proto=%q role=client %s", p9authproto, keyspec); 5469a747e4fSDavid du Colombier if(ai == nil) 54759cc4ca5SDavid du Colombier return -1; 548c403fce2SDavid du Colombier if(ai->nsecret < 8){ 549c403fce2SDavid du Colombier werrstr("negotiated secret too short"); 550c403fce2SDavid du Colombier return -1; 551c403fce2SDavid du Colombier } 552c403fce2SDavid du Colombier memmove(key+4, ai->secret, 8); 55359cc4ca5SDavid du Colombier if(ealgs == nil) 55459cc4ca5SDavid du Colombier return fd; 55559cc4ca5SDavid du Colombier 55659cc4ca5SDavid du Colombier /* exchange random numbers */ 55759cc4ca5SDavid du Colombier srand(truerand()); 55859cc4ca5SDavid du Colombier for(i = 0; i < 4; i++) 55959cc4ca5SDavid du Colombier key[i] = rand(); 560d1be6b08SDavid du Colombier procsetname("writing p9 key"); 56159cc4ca5SDavid du Colombier if(write(fd, key, 4) != 4) 56259cc4ca5SDavid du Colombier return -1; 563d1be6b08SDavid du Colombier procsetname("reading p9 key"); 56459cc4ca5SDavid du Colombier if(readn(fd, key+12, 4) != 4) 56559cc4ca5SDavid du Colombier return -1; 56659cc4ca5SDavid du Colombier 56759cc4ca5SDavid du Colombier /* scramble into two secrets */ 56859cc4ca5SDavid du Colombier sha1(key, sizeof(key), digest, nil); 56959cc4ca5SDavid du Colombier mksecret(fromclientsecret, digest); 57059cc4ca5SDavid du Colombier mksecret(fromserversecret, digest+10); 57159cc4ca5SDavid du Colombier 57259cc4ca5SDavid du Colombier /* set up encryption */ 573d1be6b08SDavid du Colombier procsetname("pushssl"); 5749a747e4fSDavid du Colombier i = pushssl(fd, ealgs, fromclientsecret, fromserversecret, nil); 5759a747e4fSDavid du Colombier if(i < 0) 5769a747e4fSDavid du Colombier werrstr("can't establish ssl connection: %r"); 5779a747e4fSDavid du Colombier return i; 5789a747e4fSDavid du Colombier } 5799a747e4fSDavid du Colombier 5809a747e4fSDavid du Colombier static int 5819a747e4fSDavid du Colombier noauth(int fd) 5829a747e4fSDavid du Colombier { 5839a747e4fSDavid du Colombier ealgs = nil; 5849a747e4fSDavid du Colombier return fd; 5859a747e4fSDavid du Colombier } 5869a747e4fSDavid du Colombier 5879a747e4fSDavid du Colombier static int 5889a747e4fSDavid du Colombier srvnoauth(int fd, char *user) 5899a747e4fSDavid du Colombier { 5909800bf03SDavid du Colombier strecpy(user, user+MaxStr, getuser()); 5919a747e4fSDavid du Colombier ealgs = nil; 592225077b0SDavid du Colombier newns(user, nil); 5939a747e4fSDavid du Colombier return fd; 5949a747e4fSDavid du Colombier } 5959a747e4fSDavid du Colombier 5969a747e4fSDavid du Colombier void 5979a747e4fSDavid du Colombier loghex(uchar *p, int n) 5989a747e4fSDavid du Colombier { 5999a747e4fSDavid du Colombier char buf[100]; 6009a747e4fSDavid du Colombier int i; 6019a747e4fSDavid du Colombier 6029a747e4fSDavid du Colombier for(i = 0; i < n; i++) 6039a747e4fSDavid du Colombier sprint(buf+2*i, "%2.2ux", p[i]); 6049a747e4fSDavid du Colombier syslog(0, "cpu", buf); 60559cc4ca5SDavid du Colombier } 60659cc4ca5SDavid du Colombier 60759cc4ca5SDavid du Colombier static int 60859cc4ca5SDavid du Colombier srvp9auth(int fd, char *user) 60959cc4ca5SDavid du Colombier { 61059cc4ca5SDavid du Colombier uchar key[16]; 61159cc4ca5SDavid du Colombier uchar digest[SHA1dlen]; 61259cc4ca5SDavid du Colombier char fromclientsecret[21]; 61359cc4ca5SDavid du Colombier char fromserversecret[21]; 61459cc4ca5SDavid du Colombier int i; 6159a747e4fSDavid du Colombier AuthInfo *ai; 61659cc4ca5SDavid du Colombier 617d9306527SDavid du Colombier ai = auth_proxy(0, nil, "proto=%q role=server %s", p9authproto, keyspec); 6189a747e4fSDavid du Colombier if(ai == nil) 61959cc4ca5SDavid du Colombier return -1; 6209a747e4fSDavid du Colombier if(auth_chuid(ai, nil) < 0) 6219a747e4fSDavid du Colombier return -1; 6229800bf03SDavid du Colombier strecpy(user, user+MaxStr, ai->cuid); 623c403fce2SDavid du Colombier if(ai->nsecret < 8){ 624c403fce2SDavid du Colombier werrstr("negotiated secret too short"); 625c403fce2SDavid du Colombier return -1; 626c403fce2SDavid du Colombier } 627c403fce2SDavid du Colombier memmove(key+4, ai->secret, 8); 62859cc4ca5SDavid du Colombier 62959cc4ca5SDavid du Colombier if(ealgs == nil) 63059cc4ca5SDavid du Colombier return fd; 63159cc4ca5SDavid du Colombier 63259cc4ca5SDavid du Colombier /* exchange random numbers */ 63359cc4ca5SDavid du Colombier srand(truerand()); 63459cc4ca5SDavid du Colombier for(i = 0; i < 4; i++) 63559cc4ca5SDavid du Colombier key[i+12] = rand(); 63659cc4ca5SDavid du Colombier if(readn(fd, key, 4) != 4) 63759cc4ca5SDavid du Colombier return -1; 63859cc4ca5SDavid du Colombier if(write(fd, key+12, 4) != 4) 63959cc4ca5SDavid du Colombier return -1; 64059cc4ca5SDavid du Colombier 64159cc4ca5SDavid du Colombier /* scramble into two secrets */ 64259cc4ca5SDavid du Colombier sha1(key, sizeof(key), digest, nil); 64359cc4ca5SDavid du Colombier mksecret(fromclientsecret, digest); 64459cc4ca5SDavid du Colombier mksecret(fromserversecret, digest+10); 64559cc4ca5SDavid du Colombier 64659cc4ca5SDavid du Colombier /* set up encryption */ 6479a747e4fSDavid du Colombier i = pushssl(fd, ealgs, fromserversecret, fromclientsecret, nil); 6489a747e4fSDavid du Colombier if(i < 0) 6499a747e4fSDavid du Colombier werrstr("can't establish ssl connection: %r"); 6509a747e4fSDavid du Colombier return i; 6517dd7cddfSDavid du Colombier } 6527dd7cddfSDavid du Colombier 65359cc4ca5SDavid du Colombier /* 65459cc4ca5SDavid du Colombier * set authentication mechanism 65559cc4ca5SDavid du Colombier */ 6567dd7cddfSDavid du Colombier int 6577dd7cddfSDavid du Colombier setam(char *name) 6587dd7cddfSDavid du Colombier { 6597dd7cddfSDavid du Colombier for(am = authmethod; am->name != nil; am++) 6607dd7cddfSDavid du Colombier if(strcmp(am->name, name) == 0) 6617dd7cddfSDavid du Colombier return 0; 6627dd7cddfSDavid du Colombier am = authmethod; 6637dd7cddfSDavid du Colombier return -1; 6647dd7cddfSDavid du Colombier } 6657dd7cddfSDavid du Colombier 66659cc4ca5SDavid du Colombier /* 66759cc4ca5SDavid du Colombier * set authentication mechanism and encryption/hash algs 66859cc4ca5SDavid du Colombier */ 66959cc4ca5SDavid du Colombier int 67059cc4ca5SDavid du Colombier setamalg(char *s) 67159cc4ca5SDavid du Colombier { 67259cc4ca5SDavid du Colombier ealgs = strchr(s, ' '); 67359cc4ca5SDavid du Colombier if(ealgs != nil) 67459cc4ca5SDavid du Colombier *ealgs++ = 0; 67559cc4ca5SDavid du Colombier return setam(s); 67659cc4ca5SDavid du Colombier } 67759cc4ca5SDavid du Colombier 6787dd7cddfSDavid du Colombier char *rmtnotefile = "/mnt/term/dev/cpunote"; 6797dd7cddfSDavid du Colombier 6807dd7cddfSDavid du Colombier /* 6817dd7cddfSDavid du Colombier * loop reading /mnt/term/dev/note looking for notes. 6827dd7cddfSDavid du Colombier * The child returns to start the shell. 6837dd7cddfSDavid du Colombier */ 6847dd7cddfSDavid du Colombier void 6857dd7cddfSDavid du Colombier rmtnoteproc(void) 6867dd7cddfSDavid du Colombier { 6877dd7cddfSDavid du Colombier int n, fd, pid, notepid; 6887dd7cddfSDavid du Colombier char buf[256]; 6897dd7cddfSDavid du Colombier 6907dd7cddfSDavid du Colombier /* new proc returns to start shell */ 6917dd7cddfSDavid du Colombier pid = rfork(RFPROC|RFFDG|RFNOTEG|RFNAMEG|RFMEM); 6927dd7cddfSDavid du Colombier switch(pid){ 6937dd7cddfSDavid du Colombier case -1: 6947dd7cddfSDavid du Colombier syslog(0, "cpu", "cpu -R: can't start noteproc: %r"); 6957dd7cddfSDavid du Colombier return; 6967dd7cddfSDavid du Colombier case 0: 6977dd7cddfSDavid du Colombier return; 6987dd7cddfSDavid du Colombier } 6997dd7cddfSDavid du Colombier 7007dd7cddfSDavid du Colombier /* new proc reads notes from other side and posts them to shell */ 7017dd7cddfSDavid du Colombier switch(notepid = rfork(RFPROC|RFFDG|RFMEM)){ 7027dd7cddfSDavid du Colombier case -1: 7037dd7cddfSDavid du Colombier syslog(0, "cpu", "cpu -R: can't start wait proc: %r"); 7047dd7cddfSDavid du Colombier _exits(0); 7057dd7cddfSDavid du Colombier case 0: 7067dd7cddfSDavid du Colombier fd = open(rmtnotefile, OREAD); 7077dd7cddfSDavid du Colombier if(fd < 0){ 7087dd7cddfSDavid du Colombier syslog(0, "cpu", "cpu -R: can't open %s", rmtnotefile); 7097dd7cddfSDavid du Colombier _exits(0); 7107dd7cddfSDavid du Colombier } 7117dd7cddfSDavid du Colombier 7127dd7cddfSDavid du Colombier for(;;){ 7137dd7cddfSDavid du Colombier n = read(fd, buf, sizeof(buf)-1); 7147dd7cddfSDavid du Colombier if(n <= 0){ 7157dd7cddfSDavid du Colombier postnote(PNGROUP, pid, "hangup"); 7167dd7cddfSDavid du Colombier _exits(0); 7177dd7cddfSDavid du Colombier } 7187dd7cddfSDavid du Colombier buf[n] = 0; 7197dd7cddfSDavid du Colombier postnote(PNGROUP, pid, buf); 7207dd7cddfSDavid du Colombier } 7217dd7cddfSDavid du Colombier } 7227dd7cddfSDavid du Colombier 7237dd7cddfSDavid du Colombier /* original proc waits for shell proc to die and kills note proc */ 7247dd7cddfSDavid du Colombier for(;;){ 7259a747e4fSDavid du Colombier n = waitpid(); 7267dd7cddfSDavid du Colombier if(n < 0 || n == pid) 7277dd7cddfSDavid du Colombier break; 7287dd7cddfSDavid du Colombier } 7297dd7cddfSDavid du Colombier postnote(PNPROC, notepid, "kill"); 7307dd7cddfSDavid du Colombier _exits(0); 7317dd7cddfSDavid du Colombier } 7327dd7cddfSDavid du Colombier 7337dd7cddfSDavid du Colombier enum 7347dd7cddfSDavid du Colombier { 7357dd7cddfSDavid du Colombier Qdir, 7367dd7cddfSDavid du Colombier Qcpunote, 7377dd7cddfSDavid du Colombier 7387dd7cddfSDavid du Colombier Nfid = 32, 7397dd7cddfSDavid du Colombier }; 7407dd7cddfSDavid du Colombier 7417dd7cddfSDavid du Colombier struct { 7427dd7cddfSDavid du Colombier char *name; 7437dd7cddfSDavid du Colombier Qid qid; 7447dd7cddfSDavid du Colombier ulong perm; 7457dd7cddfSDavid du Colombier } fstab[] = 7467dd7cddfSDavid du Colombier { 7479a747e4fSDavid du Colombier [Qdir] { ".", {Qdir, 0, QTDIR}, DMDIR|0555 }, 7487dd7cddfSDavid du Colombier [Qcpunote] { "cpunote", {Qcpunote, 0}, 0444 }, 7497dd7cddfSDavid du Colombier }; 7507dd7cddfSDavid du Colombier 7517dd7cddfSDavid du Colombier typedef struct Note Note; 7527dd7cddfSDavid du Colombier struct Note 7537dd7cddfSDavid du Colombier { 7547dd7cddfSDavid du Colombier Note *next; 7559a747e4fSDavid du Colombier char msg[ERRMAX]; 7567dd7cddfSDavid du Colombier }; 7577dd7cddfSDavid du Colombier 7587dd7cddfSDavid du Colombier typedef struct Request Request; 7597dd7cddfSDavid du Colombier struct Request 7607dd7cddfSDavid du Colombier { 7617dd7cddfSDavid du Colombier Request *next; 7627dd7cddfSDavid du Colombier Fcall f; 7637dd7cddfSDavid du Colombier }; 7647dd7cddfSDavid du Colombier 7657dd7cddfSDavid du Colombier typedef struct Fid Fid; 7667dd7cddfSDavid du Colombier struct Fid 7677dd7cddfSDavid du Colombier { 7687dd7cddfSDavid du Colombier int fid; 7697dd7cddfSDavid du Colombier int file; 77042bd533dSDavid du Colombier int omode; 7717dd7cddfSDavid du Colombier }; 7727dd7cddfSDavid du Colombier Fid fids[Nfid]; 7737dd7cddfSDavid du Colombier 7747dd7cddfSDavid du Colombier struct { 7757dd7cddfSDavid du Colombier Lock; 7767dd7cddfSDavid du Colombier Note *nfirst, *nlast; 7777dd7cddfSDavid du Colombier Request *rfirst, *rlast; 7787dd7cddfSDavid du Colombier } nfs; 7797dd7cddfSDavid du Colombier 7807dd7cddfSDavid du Colombier int 7817dd7cddfSDavid du Colombier fsreply(int fd, Fcall *f) 7827dd7cddfSDavid du Colombier { 7839a747e4fSDavid du Colombier uchar buf[IOHDRSZ+Maxfdata]; 7847dd7cddfSDavid du Colombier int n; 7857dd7cddfSDavid du Colombier 7867dd7cddfSDavid du Colombier if(dbg) 787225077b0SDavid du Colombier fprint(2, "notefs: <-%F\n", f); 7889a747e4fSDavid du Colombier n = convS2M(f, buf, sizeof buf); 7897dd7cddfSDavid du Colombier if(n > 0){ 7907dd7cddfSDavid du Colombier if(write(fd, buf, n) != n){ 7917dd7cddfSDavid du Colombier close(fd); 7927dd7cddfSDavid du Colombier return -1; 7937dd7cddfSDavid du Colombier } 7947dd7cddfSDavid du Colombier } 7957dd7cddfSDavid du Colombier return 0; 7967dd7cddfSDavid du Colombier } 7977dd7cddfSDavid du Colombier 7987dd7cddfSDavid du Colombier /* match a note read request with a note, reply to the request */ 7997dd7cddfSDavid du Colombier int 8007dd7cddfSDavid du Colombier kick(int fd) 8017dd7cddfSDavid du Colombier { 8027dd7cddfSDavid du Colombier Request *rp; 8037dd7cddfSDavid du Colombier Note *np; 8047dd7cddfSDavid du Colombier int rv; 8057dd7cddfSDavid du Colombier 8067dd7cddfSDavid du Colombier for(;;){ 8077dd7cddfSDavid du Colombier lock(&nfs); 8087dd7cddfSDavid du Colombier rp = nfs.rfirst; 8097dd7cddfSDavid du Colombier np = nfs.nfirst; 8107dd7cddfSDavid du Colombier if(rp == nil || np == nil){ 8117dd7cddfSDavid du Colombier unlock(&nfs); 8127dd7cddfSDavid du Colombier break; 8137dd7cddfSDavid du Colombier } 8147dd7cddfSDavid du Colombier nfs.rfirst = rp->next; 8157dd7cddfSDavid du Colombier nfs.nfirst = np->next; 8167dd7cddfSDavid du Colombier unlock(&nfs); 8177dd7cddfSDavid du Colombier 8187dd7cddfSDavid du Colombier rp->f.type = Rread; 8197dd7cddfSDavid du Colombier rp->f.count = strlen(np->msg); 8207dd7cddfSDavid du Colombier rp->f.data = np->msg; 8217dd7cddfSDavid du Colombier rv = fsreply(fd, &rp->f); 8227dd7cddfSDavid du Colombier free(rp); 8237dd7cddfSDavid du Colombier free(np); 8247dd7cddfSDavid du Colombier if(rv < 0) 8257dd7cddfSDavid du Colombier return -1; 8267dd7cddfSDavid du Colombier } 8277dd7cddfSDavid du Colombier return 0; 8287dd7cddfSDavid du Colombier } 8297dd7cddfSDavid du Colombier 8307dd7cddfSDavid du Colombier void 8317dd7cddfSDavid du Colombier flushreq(int tag) 8327dd7cddfSDavid du Colombier { 8337dd7cddfSDavid du Colombier Request **l, *rp; 8347dd7cddfSDavid du Colombier 8357dd7cddfSDavid du Colombier lock(&nfs); 8367dd7cddfSDavid du Colombier for(l = &nfs.rfirst; *l != nil; l = &(*l)->next){ 8377dd7cddfSDavid du Colombier rp = *l; 8387dd7cddfSDavid du Colombier if(rp->f.tag == tag){ 8397dd7cddfSDavid du Colombier *l = rp->next; 8407dd7cddfSDavid du Colombier unlock(&nfs); 8417dd7cddfSDavid du Colombier free(rp); 8427dd7cddfSDavid du Colombier return; 8437dd7cddfSDavid du Colombier } 8447dd7cddfSDavid du Colombier } 8457dd7cddfSDavid du Colombier unlock(&nfs); 8467dd7cddfSDavid du Colombier } 8477dd7cddfSDavid du Colombier 8487dd7cddfSDavid du Colombier Fid* 8497dd7cddfSDavid du Colombier getfid(int fid) 8507dd7cddfSDavid du Colombier { 8517dd7cddfSDavid du Colombier int i, freefid; 8527dd7cddfSDavid du Colombier 8537dd7cddfSDavid du Colombier freefid = -1; 8547dd7cddfSDavid du Colombier for(i = 0; i < Nfid; i++){ 8557dd7cddfSDavid du Colombier if(freefid < 0 && fids[i].file < 0) 8567dd7cddfSDavid du Colombier freefid = i; 8577dd7cddfSDavid du Colombier if(fids[i].fid == fid) 8587dd7cddfSDavid du Colombier return &fids[i]; 8597dd7cddfSDavid du Colombier } 8607dd7cddfSDavid du Colombier if(freefid >= 0){ 8617dd7cddfSDavid du Colombier fids[freefid].fid = fid; 8627dd7cddfSDavid du Colombier return &fids[freefid]; 8637dd7cddfSDavid du Colombier } 8647dd7cddfSDavid du Colombier return nil; 8657dd7cddfSDavid du Colombier } 8667dd7cddfSDavid du Colombier 8677dd7cddfSDavid du Colombier int 8687dd7cddfSDavid du Colombier fsstat(int fd, Fid *fid, Fcall *f) 8697dd7cddfSDavid du Colombier { 8707dd7cddfSDavid du Colombier Dir d; 8719a747e4fSDavid du Colombier uchar statbuf[256]; 8727dd7cddfSDavid du Colombier 8737dd7cddfSDavid du Colombier memset(&d, 0, sizeof(d)); 8749a747e4fSDavid du Colombier d.name = fstab[fid->file].name; 8759a747e4fSDavid du Colombier d.uid = user; 8769a747e4fSDavid du Colombier d.gid = user; 8779a747e4fSDavid du Colombier d.muid = user; 8787dd7cddfSDavid du Colombier d.qid = fstab[fid->file].qid; 8797dd7cddfSDavid du Colombier d.mode = fstab[fid->file].perm; 8807dd7cddfSDavid du Colombier d.atime = d.mtime = time(0); 8819a747e4fSDavid du Colombier f->stat = statbuf; 8829a747e4fSDavid du Colombier f->nstat = convD2M(&d, statbuf, sizeof statbuf); 8837dd7cddfSDavid du Colombier return fsreply(fd, f); 8847dd7cddfSDavid du Colombier } 8857dd7cddfSDavid du Colombier 8867dd7cddfSDavid du Colombier int 8877dd7cddfSDavid du Colombier fsread(int fd, Fid *fid, Fcall *f) 8887dd7cddfSDavid du Colombier { 8897dd7cddfSDavid du Colombier Dir d; 8909a747e4fSDavid du Colombier uchar buf[256]; 8917dd7cddfSDavid du Colombier Request *rp; 8927dd7cddfSDavid du Colombier 8937dd7cddfSDavid du Colombier switch(fid->file){ 8947dd7cddfSDavid du Colombier default: 8957dd7cddfSDavid du Colombier return -1; 8967dd7cddfSDavid du Colombier case Qdir: 8979a747e4fSDavid du Colombier if(f->offset == 0 && f->count >0){ 8987dd7cddfSDavid du Colombier memset(&d, 0, sizeof(d)); 8999a747e4fSDavid du Colombier d.name = fstab[Qcpunote].name; 9009a747e4fSDavid du Colombier d.uid = user; 9019a747e4fSDavid du Colombier d.gid = user; 9029a747e4fSDavid du Colombier d.muid = user; 9037dd7cddfSDavid du Colombier d.qid = fstab[Qcpunote].qid; 9047dd7cddfSDavid du Colombier d.mode = fstab[Qcpunote].perm; 9057dd7cddfSDavid du Colombier d.atime = d.mtime = time(0); 9069a747e4fSDavid du Colombier f->count = convD2M(&d, buf, sizeof buf); 9079a747e4fSDavid du Colombier f->data = (char*)buf; 9087dd7cddfSDavid du Colombier } else 9097dd7cddfSDavid du Colombier f->count = 0; 9107dd7cddfSDavid du Colombier return fsreply(fd, f); 9117dd7cddfSDavid du Colombier case Qcpunote: 9127dd7cddfSDavid du Colombier rp = mallocz(sizeof(*rp), 1); 9137dd7cddfSDavid du Colombier if(rp == nil) 9147dd7cddfSDavid du Colombier return -1; 9157dd7cddfSDavid du Colombier rp->f = *f; 9167dd7cddfSDavid du Colombier lock(&nfs); 9177dd7cddfSDavid du Colombier if(nfs.rfirst == nil) 9187dd7cddfSDavid du Colombier nfs.rfirst = rp; 9197dd7cddfSDavid du Colombier else 9207dd7cddfSDavid du Colombier nfs.rlast->next = rp; 9217dd7cddfSDavid du Colombier nfs.rlast = rp; 9227dd7cddfSDavid du Colombier unlock(&nfs); 9237dd7cddfSDavid du Colombier return kick(fd);; 9247dd7cddfSDavid du Colombier } 9257dd7cddfSDavid du Colombier } 9267dd7cddfSDavid du Colombier 9279a747e4fSDavid du Colombier char Eperm[] = "permission denied"; 9289a747e4fSDavid du Colombier char Enofile[] = "out of files"; 9299a747e4fSDavid du Colombier char Enotdir[] = "not a directory"; 9307dd7cddfSDavid du Colombier 9317dd7cddfSDavid du Colombier void 9327dd7cddfSDavid du Colombier notefs(int fd) 9337dd7cddfSDavid du Colombier { 9349a747e4fSDavid du Colombier uchar buf[IOHDRSZ+Maxfdata]; 935225077b0SDavid du Colombier int i, n, ncpunote; 9367dd7cddfSDavid du Colombier Fcall f; 937225077b0SDavid du Colombier Qid wqid[MAXWELEM]; 9387dd7cddfSDavid du Colombier Fid *fid, *nfid; 9397dd7cddfSDavid du Colombier int doreply; 9407dd7cddfSDavid du Colombier 9417dd7cddfSDavid du Colombier rfork(RFNOTEG); 9429a747e4fSDavid du Colombier fmtinstall('F', fcallfmt); 9437dd7cddfSDavid du Colombier 94442bd533dSDavid du Colombier for(n = 0; n < Nfid; n++){ 9457dd7cddfSDavid du Colombier fids[n].file = -1; 94642bd533dSDavid du Colombier fids[n].omode = -1; 94742bd533dSDavid du Colombier } 9487dd7cddfSDavid du Colombier 94942bd533dSDavid du Colombier ncpunote = 0; 9507dd7cddfSDavid du Colombier for(;;){ 9519a747e4fSDavid du Colombier n = read9pmsg(fd, buf, sizeof(buf)); 9529a747e4fSDavid du Colombier if(n <= 0){ 9539a747e4fSDavid du Colombier if(dbg) 9549a747e4fSDavid du Colombier fprint(2, "read9pmsg(%d) returns %d: %r\n", fd, n); 9557dd7cddfSDavid du Colombier break; 9569a747e4fSDavid du Colombier } 95722a127bbSDavid du Colombier if(convM2S(buf, n, &f) <= BIT16SZ) 9587dd7cddfSDavid du Colombier break; 9597dd7cddfSDavid du Colombier if(dbg) 960225077b0SDavid du Colombier fprint(2, "notefs: ->%F\n", &f); 9617dd7cddfSDavid du Colombier doreply = 1; 9627dd7cddfSDavid du Colombier fid = getfid(f.fid); 9637dd7cddfSDavid du Colombier if(fid == nil){ 9647dd7cddfSDavid du Colombier nofids: 9657dd7cddfSDavid du Colombier f.type = Rerror; 9669a747e4fSDavid du Colombier f.ename = Enofile; 9677dd7cddfSDavid du Colombier fsreply(fd, &f); 9687dd7cddfSDavid du Colombier continue; 9697dd7cddfSDavid du Colombier } 9707dd7cddfSDavid du Colombier switch(f.type++){ 9717dd7cddfSDavid du Colombier default: 9727dd7cddfSDavid du Colombier f.type = Rerror; 9739a747e4fSDavid du Colombier f.ename = "unknown type"; 9747dd7cddfSDavid du Colombier break; 9757dd7cddfSDavid du Colombier case Tflush: 9767dd7cddfSDavid du Colombier flushreq(f.oldtag); 9777dd7cddfSDavid du Colombier break; 9789a747e4fSDavid du Colombier case Tversion: 9799a747e4fSDavid du Colombier if(f.msize > IOHDRSZ+Maxfdata) 9809a747e4fSDavid du Colombier f.msize = IOHDRSZ+Maxfdata; 9817dd7cddfSDavid du Colombier break; 9829a747e4fSDavid du Colombier case Tauth: 9839a747e4fSDavid du Colombier f.type = Rerror; 984225077b0SDavid du Colombier f.ename = "authentication not required"; 9857dd7cddfSDavid du Colombier break; 9867dd7cddfSDavid du Colombier case Tattach: 9877dd7cddfSDavid du Colombier f.qid = fstab[Qdir].qid; 9887dd7cddfSDavid du Colombier fid->file = Qdir; 9897dd7cddfSDavid du Colombier break; 9909a747e4fSDavid du Colombier case Twalk: 9919a747e4fSDavid du Colombier nfid = nil; 9929a747e4fSDavid du Colombier if(f.newfid != f.fid){ 9937dd7cddfSDavid du Colombier nfid = getfid(f.newfid); 9947dd7cddfSDavid du Colombier if(nfid == nil) 9957dd7cddfSDavid du Colombier goto nofids; 9967dd7cddfSDavid du Colombier nfid->file = fid->file; 9977dd7cddfSDavid du Colombier fid = nfid; 9989a747e4fSDavid du Colombier } 999225077b0SDavid du Colombier for(i=0; i<f.nwname && i<MAXWELEM; i++){ 10007dd7cddfSDavid du Colombier if(fid->file != Qdir){ 10017dd7cddfSDavid du Colombier f.type = Rerror; 10029a747e4fSDavid du Colombier f.ename = Enotdir; 10039a747e4fSDavid du Colombier break; 10047dd7cddfSDavid du Colombier } 1005225077b0SDavid du Colombier if(strcmp(f.wname[i], "..") == 0){ 1006225077b0SDavid du Colombier wqid[i] = fstab[Qdir].qid; 10079a747e4fSDavid du Colombier continue; 10089a747e4fSDavid du Colombier } 1009225077b0SDavid du Colombier if(strcmp(f.wname[i], "cpunote") != 0){ 1010225077b0SDavid du Colombier if(i == 0){ 10119a747e4fSDavid du Colombier f.type = Rerror; 1012225077b0SDavid du Colombier f.ename = "file does not exist"; 10139a747e4fSDavid du Colombier } 10149a747e4fSDavid du Colombier break; 10159a747e4fSDavid du Colombier } 1016225077b0SDavid du Colombier fid->file = Qcpunote; 1017225077b0SDavid du Colombier wqid[i] = fstab[Qcpunote].qid; 1018225077b0SDavid du Colombier } 1019225077b0SDavid du Colombier if(nfid != nil && (f.type == Rerror || i < f.nwname)) 10209a747e4fSDavid du Colombier nfid ->file = -1; 1021225077b0SDavid du Colombier if(f.type != Rerror){ 10229a747e4fSDavid du Colombier f.nwqid = i; 1023225077b0SDavid du Colombier for(i=0; i<f.nwqid; i++) 1024225077b0SDavid du Colombier f.wqid[i] = wqid[i]; 1025225077b0SDavid du Colombier } 10267dd7cddfSDavid du Colombier break; 10277dd7cddfSDavid du Colombier case Topen: 10287dd7cddfSDavid du Colombier if(f.mode != OREAD){ 10297dd7cddfSDavid du Colombier f.type = Rerror; 10309a747e4fSDavid du Colombier f.ename = Eperm; 1031225077b0SDavid du Colombier break; 10327dd7cddfSDavid du Colombier } 103342bd533dSDavid du Colombier fid->omode = f.mode; 103442bd533dSDavid du Colombier if(fid->file == Qcpunote) 103542bd533dSDavid du Colombier ncpunote++; 103680ee5cbfSDavid du Colombier f.qid = fstab[fid->file].qid; 1037225077b0SDavid du Colombier f.iounit = 0; 10387dd7cddfSDavid du Colombier break; 10397dd7cddfSDavid du Colombier case Tread: 10407dd7cddfSDavid du Colombier if(fsread(fd, fid, &f) < 0) 10417dd7cddfSDavid du Colombier goto err; 10427dd7cddfSDavid du Colombier doreply = 0; 10437dd7cddfSDavid du Colombier break; 10447dd7cddfSDavid du Colombier case Tclunk: 104542bd533dSDavid du Colombier if(fid->omode != -1 && fid->file == Qcpunote){ 104642bd533dSDavid du Colombier ncpunote--; 104742bd533dSDavid du Colombier if(ncpunote == 0) /* remote side is done */ 104842bd533dSDavid du Colombier goto err; 104942bd533dSDavid du Colombier } 10507dd7cddfSDavid du Colombier fid->file = -1; 105142bd533dSDavid du Colombier fid->omode = -1; 10527dd7cddfSDavid du Colombier break; 10537dd7cddfSDavid du Colombier case Tstat: 10547dd7cddfSDavid du Colombier if(fsstat(fd, fid, &f) < 0) 10557dd7cddfSDavid du Colombier goto err; 10567dd7cddfSDavid du Colombier doreply = 0; 10577dd7cddfSDavid du Colombier break; 1058225077b0SDavid du Colombier case Tcreate: 1059225077b0SDavid du Colombier case Twrite: 1060225077b0SDavid du Colombier case Tremove: 10617dd7cddfSDavid du Colombier case Twstat: 10627dd7cddfSDavid du Colombier f.type = Rerror; 10639a747e4fSDavid du Colombier f.ename = Eperm; 10647dd7cddfSDavid du Colombier break; 10657dd7cddfSDavid du Colombier } 10667dd7cddfSDavid du Colombier if(doreply) 10677dd7cddfSDavid du Colombier if(fsreply(fd, &f) < 0) 10687dd7cddfSDavid du Colombier break; 10697dd7cddfSDavid du Colombier } 10707dd7cddfSDavid du Colombier err: 10719a747e4fSDavid du Colombier if(dbg) 10729a747e4fSDavid du Colombier fprint(2, "notefs exiting: %r\n"); 107342bd533dSDavid du Colombier werrstr("success"); 10746d87198cSDavid du Colombier postnote(PNGROUP, exportpid, "kill"); 107542bd533dSDavid du Colombier if(dbg) 107642bd533dSDavid du Colombier fprint(2, "postnote PNGROUP %d: %r\n", exportpid); 10777dd7cddfSDavid du Colombier close(fd); 10787dd7cddfSDavid du Colombier } 10797dd7cddfSDavid du Colombier 10809a747e4fSDavid du Colombier char notebuf[ERRMAX]; 10817dd7cddfSDavid du Colombier 10827dd7cddfSDavid du Colombier void 10837dd7cddfSDavid du Colombier catcher(void*, char *text) 10847dd7cddfSDavid du Colombier { 10859a747e4fSDavid du Colombier int n; 10869a747e4fSDavid du Colombier 10879a747e4fSDavid du Colombier n = strlen(text); 10889a747e4fSDavid du Colombier if(n >= sizeof(notebuf)) 10899a747e4fSDavid du Colombier n = sizeof(notebuf)-1; 10909a747e4fSDavid du Colombier memmove(notebuf, text, n); 10919a747e4fSDavid du Colombier notebuf[n] = '\0'; 10927dd7cddfSDavid du Colombier noted(NCONT); 10937dd7cddfSDavid du Colombier } 10947dd7cddfSDavid du Colombier 10957dd7cddfSDavid du Colombier /* 10967dd7cddfSDavid du Colombier * mount in /dev a note file for the remote side to read. 10977dd7cddfSDavid du Colombier */ 10987dd7cddfSDavid du Colombier void 10997dd7cddfSDavid du Colombier lclnoteproc(int netfd) 11007dd7cddfSDavid du Colombier { 11019a747e4fSDavid du Colombier Waitmsg *w; 11027dd7cddfSDavid du Colombier Note *np; 11037dd7cddfSDavid du Colombier int pfd[2]; 110442bd533dSDavid du Colombier int pid; 11057dd7cddfSDavid du Colombier 11067dd7cddfSDavid du Colombier if(pipe(pfd) < 0){ 11079a747e4fSDavid du Colombier fprint(2, "cpu: can't start note proc: pipe: %r\n"); 11087dd7cddfSDavid du Colombier return; 11097dd7cddfSDavid du Colombier } 11107dd7cddfSDavid du Colombier 11117dd7cddfSDavid du Colombier /* new proc mounts and returns to start exportfs */ 111242bd533dSDavid du Colombier switch(pid = rfork(RFPROC|RFNAMEG|RFFDG|RFMEM)){ 111342bd533dSDavid du Colombier default: 111442bd533dSDavid du Colombier exportpid = pid; 111542bd533dSDavid du Colombier break; 11167dd7cddfSDavid du Colombier case -1: 11179a747e4fSDavid du Colombier fprint(2, "cpu: can't start note proc: rfork: %r\n"); 11187dd7cddfSDavid du Colombier return; 11197dd7cddfSDavid du Colombier case 0: 11207dd7cddfSDavid du Colombier close(pfd[0]); 11219a747e4fSDavid du Colombier if(mount(pfd[1], -1, "/dev", MBEFORE, "") < 0) 11229a747e4fSDavid du Colombier fprint(2, "cpu: can't mount note proc: %r\n"); 11237dd7cddfSDavid du Colombier close(pfd[1]); 11247dd7cddfSDavid du Colombier return; 11257dd7cddfSDavid du Colombier } 11267dd7cddfSDavid du Colombier 11277dd7cddfSDavid du Colombier close(netfd); 11287dd7cddfSDavid du Colombier close(pfd[1]); 11297dd7cddfSDavid du Colombier 11307dd7cddfSDavid du Colombier /* new proc listens for note file system rpc's */ 11317dd7cddfSDavid du Colombier switch(rfork(RFPROC|RFNAMEG|RFMEM)){ 11327dd7cddfSDavid du Colombier case -1: 11339a747e4fSDavid du Colombier fprint(2, "cpu: can't start note proc: rfork1: %r\n"); 11347dd7cddfSDavid du Colombier _exits(0); 11357dd7cddfSDavid du Colombier case 0: 11367dd7cddfSDavid du Colombier notefs(pfd[0]); 11377dd7cddfSDavid du Colombier _exits(0); 11387dd7cddfSDavid du Colombier } 11397dd7cddfSDavid du Colombier 11407dd7cddfSDavid du Colombier /* original proc waits for notes */ 11417dd7cddfSDavid du Colombier notify(catcher); 11429a747e4fSDavid du Colombier w = nil; 11437dd7cddfSDavid du Colombier for(;;) { 11447dd7cddfSDavid du Colombier *notebuf = 0; 11459a747e4fSDavid du Colombier free(w); 11469a747e4fSDavid du Colombier w = wait(); 11479a747e4fSDavid du Colombier if(w == nil) { 11487dd7cddfSDavid du Colombier if(*notebuf == 0) 11497dd7cddfSDavid du Colombier break; 11507dd7cddfSDavid du Colombier np = mallocz(sizeof(Note), 1); 11517dd7cddfSDavid du Colombier if(np != nil){ 11527dd7cddfSDavid du Colombier strcpy(np->msg, notebuf); 11537dd7cddfSDavid du Colombier lock(&nfs); 11547dd7cddfSDavid du Colombier if(nfs.nfirst == nil) 11557dd7cddfSDavid du Colombier nfs.nfirst = np; 11567dd7cddfSDavid du Colombier else 11577dd7cddfSDavid du Colombier nfs.nlast->next = np; 11587dd7cddfSDavid du Colombier nfs.nlast = np; 11597dd7cddfSDavid du Colombier unlock(&nfs); 11607dd7cddfSDavid du Colombier kick(pfd[0]); 11617dd7cddfSDavid du Colombier } 11627dd7cddfSDavid du Colombier unlock(&nfs); 116342bd533dSDavid du Colombier } else if(w->pid == exportpid) 11647dd7cddfSDavid du Colombier break; 11657dd7cddfSDavid du Colombier } 11667dd7cddfSDavid du Colombier 11679a747e4fSDavid du Colombier if(w == nil) 11689a747e4fSDavid du Colombier exits(nil); 11692db064f5SDavid du Colombier exits(0); 11702db064f5SDavid du Colombier /* exits(w->msg); */ 11717dd7cddfSDavid du Colombier } 1172