URI:
       tcpu.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       tcpu.c (20341B)
       ---
            1 /*
            2  * cpu.c - Make a connection to a cpu server
            3  *
            4  *           Invoked by listen as 'cpu -R | -N service net netdir'
            5  *                       by users  as 'cpu [-h system] [-c cmd args ...]'
            6  */
            7 
            8 #include <u.h>
            9 #include <libc.h>
           10 #include <bio.h>
           11 #include <auth.h>
           12 #include <fcall.h>
           13 #include <libsec.h>
           14 
           15 #define        Maxfdata 8192
           16 
           17 void        remoteside(int);
           18 void        fatal(int, char*, ...);
           19 void        lclnoteproc(int);
           20 void        rmtnoteproc(void);
           21 void        catcher(void*, char*);
           22 void        usage(void);
           23 void        writestr(int, char*, char*, int);
           24 int        readstr(int, char*, int);
           25 char        *rexcall(int*, char*, char*);
           26 int        setamalg(char*);
           27 
           28 int         notechan;
           29 char        system[32];
           30 int        cflag;
           31 int        hflag;
           32 int        dbg;
           33 char        *user;
           34 
           35 char        *srvname = "ncpu";
           36 char        *exportfs = "/bin/exportfs";
           37 char        *ealgs = "rc4_256 sha1";
           38 
           39 /* message size for exportfs; may be larger so we can do big graphics in CPU window */
           40 int        msgsize = 8192+IOHDRSZ;
           41 
           42 /* authentication mechanisms */
           43 static int        netkeyauth(int);
           44 static int        netkeysrvauth(int, char*);
           45 static int        p9auth(int);
           46 static int        srvp9auth(int, char*);
           47 static int        noauth(int);
           48 static int        srvnoauth(int, char*);
           49 
           50 typedef struct AuthMethod AuthMethod;
           51 struct AuthMethod {
           52         char        *name;                        /* name of method */
           53         int        (*cf)(int);                /* client side authentication */
           54         int        (*sf)(int, char*);        /* server side authentication */
           55 } authmethod[] =
           56 {
           57         { "p9",                p9auth,                srvp9auth,},
           58         { "netkey",        netkeyauth,        netkeysrvauth,},
           59 /*        { "none",        noauth,                srvnoauth,}, */
           60         { nil,        nil}
           61 };
           62 AuthMethod *am = authmethod;        /* default is p9 */
           63 
           64 char *p9authproto = "p9any";
           65 
           66 int setam(char*);
           67 
           68 void
           69 usage(void)
           70 {
           71         fprint(2, "usage: cpu [-h system] [-a authmethod] [-e 'crypt hash'] [-c cmd args ...]\n");
           72         exits("usage");
           73 }
           74 int fdd;
           75 
           76 void
           77 main(int argc, char **argv)
           78 {
           79         char dat[128], buf[128], cmd[128], *p, *err;
           80         int fd, ms, kms, data;
           81 
           82         /* see if we should use a larger message size */
           83         fd = open("/dev/draw", OREAD);
           84         if(fd > 0){
           85                 ms = iounit(fd);
           86                 if(ms != 0 && ms < ms+IOHDRSZ)
           87                         msgsize = ms+IOHDRSZ;
           88                 close(fd);
           89         }
           90         kms = kiounit();
           91         if(msgsize > kms-IOHDRSZ-100)        /* 100 for network packets, etc. */
           92                 msgsize = kms-IOHDRSZ-100;
           93 
           94         user = getuser();
           95         if(user == nil)
           96                 fatal(1, "can't read user name");
           97         ARGBEGIN{
           98         case 'a':
           99                 p = EARGF(usage());
          100                 if(setam(p) < 0)
          101                         fatal(0, "unknown auth method %s", p);
          102                 break;
          103         case 'e':
          104                 ealgs = EARGF(usage());
          105                 if(*ealgs == 0 || strcmp(ealgs, "clear") == 0)
          106                         ealgs = nil;
          107                 break;
          108         case 'd':
          109                 dbg++;
          110                 break;
          111         case 'f':
          112                 /* ignored but accepted for compatibility */
          113                 break;
          114         case 'O':
          115                 p9authproto = "p9sk2";
          116                 remoteside(1);                                /* From listen */
          117                 break;
          118         case 'R':                                /* From listen */
          119                 remoteside(0);
          120                 break;
          121         case 'h':
          122                 hflag++;
          123                 p = EARGF(usage());
          124                 strcpy(system, p);
          125                 break;
          126         case 'c':
          127                 cflag++;
          128                 cmd[0] = '!';
          129                 cmd[1] = '\0';
          130                 while(p = ARGF()) {
          131                         strcat(cmd, " ");
          132                         strcat(cmd, p);
          133                 }
          134                 break;
          135         case 'o':
          136                 p9authproto = "p9sk2";
          137                 srvname = "cpu";
          138                 break;
          139         case 'u':
          140                 user = EARGF(usage());
          141                 break;
          142         default:
          143                 usage();
          144         }ARGEND;
          145 
          146 
          147         if(argc != 0)
          148                 usage();
          149 
          150         if(hflag == 0) {
          151                 p = getenv("cpu");
          152                 if(p == 0)
          153                         fatal(0, "set $cpu");
          154                 strcpy(system, p);
          155         }
          156 
          157         if(err = rexcall(&data, system, srvname))
          158                 fatal(1, "%s: %s", err, system);
          159 
          160         /* Tell the remote side the command to execute and where our working directory is */
          161         if(cflag)
          162                 writestr(data, cmd, "command", 0);
          163         if(getwd(dat, sizeof(dat)) == 0)
          164                 writestr(data, "NO", "dir", 0);
          165         else
          166                 writestr(data, dat, "dir", 0);
          167 
          168         /* start up a process to pass along notes */
          169         lclnoteproc(data);
          170 
          171         /*
          172          *  Wait for the other end to execute and start our file service
          173          *  of /mnt/term
          174          */
          175         if(readstr(data, buf, sizeof(buf)) < 0)
          176                 fatal(1, "waiting for FS");
          177         if(strncmp("FS", buf, 2) != 0) {
          178                 print("remote cpu: %s", buf);
          179                 exits(buf);
          180         }
          181 
          182         /* Begin serving the gnot namespace */
          183         close(0);
          184         dup(data, 0);
          185         close(data);
          186         sprint(buf, "%d", msgsize);
          187         if(dbg)
          188                 execl(exportfs, exportfs, "-dm", buf, 0);
          189         else
          190                 execl(exportfs, exportfs, "-m", buf, 0);
          191         fatal(1, "starting exportfs");
          192 }
          193 
          194 void
          195 fatal(int syserr, char *fmt, ...)
          196 {
          197         char buf[ERRMAX];
          198         va_list arg;
          199 
          200         va_start(arg, fmt);
          201         doprint(buf, buf+sizeof(buf), fmt, arg);
          202         va_end(arg);
          203         if(syserr)
          204                 fprint(2, "cpu: %s: %r\n", buf);
          205         else
          206                 fprint(2, "cpu: %s\n", buf);
          207         exits(buf);
          208 }
          209 
          210 char *negstr = "negotiating authentication method";
          211 
          212 char bug[256];
          213 
          214 int
          215 old9p(int fd)
          216 {
          217         int p[2];
          218 
          219         if(pipe(p) < 0)
          220                 fatal(1, "pipe");
          221 
          222         switch(rfork(RFPROC|RFFDG|RFNAMEG)) {
          223         case -1:
          224                 fatal(1, "rfork srvold9p");
          225         case 0:
          226                 if(fd != 1){
          227                         dup(fd, 1);
          228                         close(fd);
          229                 }
          230                 if(p[0] != 0){
          231                         dup(p[0], 0);
          232                         close(p[0]);
          233                 }
          234                 close(p[1]);
          235                 if(0){
          236                         fd = open("/sys/log/cpu", OWRITE);
          237                         if(fd != 2){
          238                                 dup(fd, 2);
          239                                 close(fd);
          240                         }
          241                         execl("/bin/srvold9p", "srvold9p", "-ds", 0);
          242                 } else
          243                         execl("/bin/srvold9p", "srvold9p", "-s", 0);
          244                 fatal(1, "exec srvold9p");
          245         default:
          246                 close(fd);
          247                 close(p[0]);
          248         }
          249         return p[1];
          250 }
          251 
          252 /* Invoked with stdin, stdout and stderr connected to the network connection */
          253 void
          254 remoteside(int old)
          255 {
          256         char user[128], home[128], buf[128], xdir[128], cmd[128];
          257         int i, n, fd, badchdir, gotcmd;
          258 
          259         fd = 0;
          260 
          261         /* negotiate authentication mechanism */
          262         n = readstr(fd, cmd, sizeof(cmd));
          263         if(n < 0)
          264                 fatal(1, "authenticating");
          265         if(setamalg(cmd) < 0){
          266                 writestr(fd, "unsupported auth method", nil, 0);
          267                 fatal(1, "bad auth method %s", cmd);
          268         } else
          269                 writestr(fd, "", "", 1);
          270 
          271         fd = (*am->sf)(fd, user);
          272         if(fd < 0)
          273                 fatal(1, "srvauth");
          274 
          275         /* Set environment values for the user */
          276         putenv("user", user);
          277         sprint(home, "/usr/%s", user);
          278         putenv("home", home);
          279 
          280         /* Now collect invoking cpu's current directory or possibly a command */
          281         gotcmd = 0;
          282         if(readstr(fd, xdir, sizeof(xdir)) < 0)
          283                 fatal(1, "dir/cmd");
          284         if(xdir[0] == '!') {
          285                 strcpy(cmd, &xdir[1]);
          286                 gotcmd = 1;
          287                 if(readstr(fd, xdir, sizeof(xdir)) < 0)
          288                         fatal(1, "dir");
          289         }
          290 
          291         /* Establish the new process at the current working directory of the
          292          * gnot */
          293         badchdir = 0;
          294         if(strcmp(xdir, "NO") == 0)
          295                 chdir(home);
          296         else if(chdir(xdir) < 0) {
          297                 badchdir = 1;
          298                 chdir(home);
          299         }
          300 
          301         /* Start the gnot serving its namespace */
          302         writestr(fd, "FS", "FS", 0);
          303         writestr(fd, "/", "exportfs dir", 0);
          304 
          305         n = read(fd, buf, sizeof(buf));
          306         if(n != 2 || buf[0] != 'O' || buf[1] != 'K')
          307                 exits("remote tree");
          308 
          309         if(old)
          310                 fd = old9p(fd);
          311 
          312         /* make sure buffers are big by doing fversion explicitly; pick a huge number; other side will trim */
          313         strcpy(buf, VERSION9P);
          314         if(fversion(fd, 64*1024, buf, sizeof buf) < 0)
          315                 exits("fversion failed");
          316         if(mount(fd, -1, "/mnt/term", MCREATE|MREPL, "") < 0)
          317                 exits("mount failed");
          318 
          319         close(fd);
          320 
          321         /* the remote noteproc uses the mount so it must follow it */
          322         rmtnoteproc();
          323 
          324         for(i = 0; i < 3; i++)
          325                 close(i);
          326 
          327         if(open("/mnt/term/dev/cons", OREAD) != 0)
          328                 exits("open stdin");
          329         if(open("/mnt/term/dev/cons", OWRITE) != 1)
          330                 exits("open stdout");
          331         dup(1, 2);
          332 
          333         if(badchdir)
          334                 print("cpu: failed to chdir to '%s'\n", xdir);
          335 
          336         if(gotcmd)
          337                 execl("/bin/rc", "rc", "-lc", cmd, 0);
          338         else
          339                 execl("/bin/rc", "rc", "-li", 0);
          340         fatal(1, "exec shell");
          341 }
          342 
          343 char*
          344 rexcall(int *fd, char *host, char *service)
          345 {
          346         char *na;
          347         char dir[128];
          348         char err[ERRMAX];
          349         char msg[128];
          350         int n;
          351 
          352         na = netmkaddr(host, 0, service);
          353         if((*fd = dial(na, 0, dir, 0)) < 0)
          354                 return "can't dial";
          355 
          356         /* negotiate authentication mechanism */
          357         if(ealgs != nil)
          358                 snprint(msg, sizeof(msg), "%s %s", am->name, ealgs);
          359         else
          360                 snprint(msg, sizeof(msg), "%s", am->name);
          361         writestr(*fd, msg, negstr, 0);
          362         n = readstr(*fd, err, sizeof err);
          363         if(n < 0)
          364                 return negstr;
          365         if(*err){
          366                 werrstr(err);
          367                 return negstr;
          368         }
          369 
          370         /* authenticate */
          371         *fd = (*am->cf)(*fd);
          372         if(*fd < 0)
          373                 return "can't authenticate";
          374         return 0;
          375 }
          376 
          377 void
          378 writestr(int fd, char *str, char *thing, int ignore)
          379 {
          380         int l, n;
          381 
          382         l = strlen(str);
          383         n = write(fd, str, l+1);
          384         if(!ignore && n < 0)
          385                 fatal(1, "writing network: %s", thing);
          386 }
          387 
          388 int
          389 readstr(int fd, char *str, int len)
          390 {
          391         int n;
          392 
          393         while(len) {
          394                 n = read(fd, str, 1);
          395                 if(n < 0)
          396                         return -1;
          397                 if(*str == '\0')
          398                         return 0;
          399                 str++;
          400                 len--;
          401         }
          402         return -1;
          403 }
          404 
          405 static int
          406 readln(char *buf, int n)
          407 {
          408         char *p = buf;
          409 
          410         n--;
          411         while(n > 0){
          412                 if(read(0, p, 1) != 1)
          413                         break;
          414                 if(*p == '\n' || *p == '\r'){
          415                         *p = 0;
          416                         return p-buf;
          417                 }
          418                 p++;
          419         }
          420         *p = 0;
          421         return p-buf;
          422 }
          423 
          424 /*
          425  *  user level challenge/response
          426  */
          427 static int
          428 netkeyauth(int fd)
          429 {
          430         char chall[32];
          431         char resp[32];
          432 
          433         strcpy(chall, getuser());
          434         print("user[%s]: ", chall);
          435         if(readln(resp, sizeof(resp)) < 0)
          436                 return -1;
          437         if(*resp != 0)
          438                 strcpy(chall, resp);
          439         writestr(fd, chall, "challenge/response", 1);
          440 
          441         for(;;){
          442                 if(readstr(fd, chall, sizeof chall) < 0)
          443                         break;
          444                 if(*chall == 0)
          445                         return fd;
          446                 print("challenge: %s\nresponse: ", chall);
          447                 if(readln(resp, sizeof(resp)) < 0)
          448                         break;
          449                 writestr(fd, resp, "challenge/response", 1);
          450         }
          451         return -1;
          452 }
          453 
          454 static int
          455 netkeysrvauth(int fd, char *user)
          456 {
          457         char response[32];
          458         Chalstate *ch;
          459         int tries;
          460         AuthInfo *ai;
          461 
          462         if(readstr(fd, user, 32) < 0)
          463                 return -1;
          464 
          465         ai = nil;
          466         ch = nil;
          467         for(tries = 0; tries < 10; tries++){
          468                 if((ch = auth_challenge("p9cr", user, nil)) == nil)
          469                         return -1;
          470                 writestr(fd, ch->chal, "challenge", 1);
          471                 if(readstr(fd, response, sizeof response) < 0)
          472                         return -1;
          473                 ch->resp = response;
          474                 ch->nresp = strlen(response);
          475                 if((ai = auth_response(ch)) != nil)
          476                         break;
          477         }
          478         auth_freechal(ch);
          479         if(ai == nil)
          480                 return -1;
          481         writestr(fd, "", "challenge", 1);
          482         if(auth_chuid(ai, 0) < 0)
          483                 fatal(1, "newns");
          484         auth_freeAI(ai);
          485         return fd;
          486 }
          487 
          488 static void
          489 mksecret(char *t, uchar *f)
          490 {
          491         sprint(t, "%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux",
          492                 f[0], f[1], f[2], f[3], f[4], f[5], f[6], f[7], f[8], f[9]);
          493 }
          494 
          495 /*
          496  *  plan9 authentication followed by rc4 encryption
          497  */
          498 static int
          499 p9auth(int fd)
          500 {
          501         uchar key[16];
          502         uchar digest[SHA1dlen];
          503         char fromclientsecret[21];
          504         char fromserversecret[21];
          505         int i;
          506         AuthInfo *ai;
          507 
          508         ai = auth_proxy(fd, auth_getkey, "proto=%q user=%q role=client", p9authproto, user);
          509         if(ai == nil)
          510                 return -1;
          511         memmove(key+4, ai->secret, ai->nsecret);
          512         if(ealgs == nil)
          513                 return fd;
          514 
          515         /* exchange random numbers */
          516         srand(truerand());
          517         for(i = 0; i < 4; i++)
          518                 key[i] = rand();
          519         if(write(fd, key, 4) != 4)
          520                 return -1;
          521         if(readn(fd, key+12, 4) != 4)
          522                 return -1;
          523 
          524         /* scramble into two secrets */
          525         sha1(key, sizeof(key), digest, nil);
          526         mksecret(fromclientsecret, digest);
          527         mksecret(fromserversecret, digest+10);
          528 
          529         /* set up encryption */
          530         i = pushssl(fd, ealgs, fromclientsecret, fromserversecret, nil);
          531         if(i < 0)
          532                 werrstr("can't establish ssl connection: %r");
          533         return i;
          534 }
          535 
          536 static int
          537 noauth(int fd)
          538 {
          539         ealgs = nil;
          540         return fd;
          541 }
          542 
          543 static int
          544 srvnoauth(int fd, char *user)
          545 {
          546         strcpy(user, getuser());
          547         ealgs = nil;
          548         return fd;
          549 }
          550 
          551 void
          552 loghex(uchar *p, int n)
          553 {
          554         char buf[100];
          555         int i;
          556 
          557         for(i = 0; i < n; i++)
          558                 sprint(buf+2*i, "%2.2ux", p[i]);
          559         syslog(0, "cpu", buf);
          560 }
          561 
          562 static int
          563 srvp9auth(int fd, char *user)
          564 {
          565         uchar key[16];
          566         uchar digest[SHA1dlen];
          567         char fromclientsecret[21];
          568         char fromserversecret[21];
          569         int i;
          570         AuthInfo *ai;
          571 
          572         ai = auth_proxy(0, nil, "proto=%q role=server", p9authproto);
          573         if(ai == nil)
          574                 return -1;
          575         if(auth_chuid(ai, nil) < 0)
          576                 return -1;
          577         strcpy(user, ai->cuid);
          578         memmove(key+4, ai->secret, ai->nsecret);
          579 
          580         if(ealgs == nil)
          581                 return fd;
          582 
          583         /* exchange random numbers */
          584         srand(truerand());
          585         for(i = 0; i < 4; i++)
          586                 key[i+12] = rand();
          587         if(readn(fd, key, 4) != 4)
          588                 return -1;
          589         if(write(fd, key+12, 4) != 4)
          590                 return -1;
          591 
          592         /* scramble into two secrets */
          593         sha1(key, sizeof(key), digest, nil);
          594         mksecret(fromclientsecret, digest);
          595         mksecret(fromserversecret, digest+10);
          596 
          597         /* set up encryption */
          598         i = pushssl(fd, ealgs, fromserversecret, fromclientsecret, nil);
          599         if(i < 0)
          600                 werrstr("can't establish ssl connection: %r");
          601         return i;
          602 }
          603 
          604 /*
          605  *  set authentication mechanism
          606  */
          607 int
          608 setam(char *name)
          609 {
          610         for(am = authmethod; am->name != nil; am++)
          611                 if(strcmp(am->name, name) == 0)
          612                         return 0;
          613         am = authmethod;
          614         return -1;
          615 }
          616 
          617 /*
          618  *  set authentication mechanism and encryption/hash algs
          619  */
          620 int
          621 setamalg(char *s)
          622 {
          623         ealgs = strchr(s, ' ');
          624         if(ealgs != nil)
          625                 *ealgs++ = 0;
          626         return setam(s);
          627 }
          628 
          629 char *rmtnotefile = "/mnt/term/dev/cpunote";
          630 
          631 /*
          632  *  loop reading /mnt/term/dev/note looking for notes.
          633  *  The child returns to start the shell.
          634  */
          635 void
          636 rmtnoteproc(void)
          637 {
          638         int n, fd, pid, notepid;
          639         char buf[256];
          640 
          641         /* new proc returns to start shell */
          642         pid = rfork(RFPROC|RFFDG|RFNOTEG|RFNAMEG|RFMEM);
          643         switch(pid){
          644         case -1:
          645                 syslog(0, "cpu", "cpu -R: can't start noteproc: %r");
          646                 return;
          647         case 0:
          648                 return;
          649         }
          650 
          651         /* new proc reads notes from other side and posts them to shell */
          652         switch(notepid = rfork(RFPROC|RFFDG|RFMEM)){
          653         case -1:
          654                 syslog(0, "cpu", "cpu -R: can't start wait proc: %r");
          655                 _exits(0);
          656         case 0:
          657                 fd = open(rmtnotefile, OREAD);
          658                 if(fd < 0){
          659                         syslog(0, "cpu", "cpu -R: can't open %s", rmtnotefile);
          660                         _exits(0);
          661                 }
          662 
          663                 for(;;){
          664                         n = read(fd, buf, sizeof(buf)-1);
          665                         if(n <= 0){
          666                                 postnote(PNGROUP, pid, "hangup");
          667                                 _exits(0);
          668                         }
          669                         buf[n] = 0;
          670                         postnote(PNGROUP, pid, buf);
          671                 }
          672                 break;
          673         }
          674 
          675         /* original proc waits for shell proc to die and kills note proc */
          676         for(;;){
          677                 n = waitpid();
          678                 if(n < 0 || n == pid)
          679                         break;
          680         }
          681         postnote(PNPROC, notepid, "kill");
          682         _exits(0);
          683 }
          684 
          685 enum
          686 {
          687         Qdir,
          688         Qcpunote,
          689 
          690         Nfid = 32
          691 };
          692 
          693 struct {
          694         char        *name;
          695         Qid        qid;
          696         ulong        perm;
          697 } fstab[] =
          698 {
          699         [Qdir]                { ".",                {Qdir, 0, QTDIR},        DMDIR|0555        },
          700         [Qcpunote]        { "cpunote",        {Qcpunote, 0},                0444                }
          701 };
          702 
          703 typedef struct Note Note;
          704 struct Note
          705 {
          706         Note *next;
          707         char msg[ERRMAX];
          708 };
          709 
          710 typedef struct Request Request;
          711 struct Request
          712 {
          713         Request *next;
          714         Fcall f;
          715 };
          716 
          717 typedef struct Fid Fid;
          718 struct Fid
          719 {
          720         int        fid;
          721         int        file;
          722 };
          723 Fid fids[Nfid];
          724 
          725 struct {
          726         Lock;
          727         Note *nfirst, *nlast;
          728         Request *rfirst, *rlast;
          729 } nfs;
          730 
          731 int
          732 fsreply(int fd, Fcall *f)
          733 {
          734         uchar buf[IOHDRSZ+Maxfdata];
          735         int n;
          736 
          737         if(dbg)
          738                 fprint(2, "<-%F\n", f);
          739         n = convS2M(f, buf, sizeof buf);
          740         if(n > 0){
          741                 if(write(fd, buf, n) != n){
          742                         close(fd);
          743                         return -1;
          744                 }
          745         }
          746         return 0;
          747 }
          748 
          749 /* match a note read request with a note, reply to the request */
          750 int
          751 kick(int fd)
          752 {
          753         Request *rp;
          754         Note *np;
          755         int rv;
          756 
          757         for(;;){
          758                 lock(&nfs);
          759                 rp = nfs.rfirst;
          760                 np = nfs.nfirst;
          761                 if(rp == nil || np == nil){
          762                         unlock(&nfs);
          763                         break;
          764                 }
          765                 nfs.rfirst = rp->next;
          766                 nfs.nfirst = np->next;
          767                 unlock(&nfs);
          768 
          769                 rp->f.type = Rread;
          770                 rp->f.count = strlen(np->msg);
          771                 rp->f.data = np->msg;
          772                 rv = fsreply(fd, &rp->f);
          773                 free(rp);
          774                 free(np);
          775                 if(rv < 0)
          776                         return -1;
          777         }
          778         return 0;
          779 }
          780 
          781 void
          782 flushreq(int tag)
          783 {
          784         Request **l, *rp;
          785 
          786         lock(&nfs);
          787         for(l = &nfs.rfirst; *l != nil; l = &(*l)->next){
          788                 rp = *l;
          789                 if(rp->f.tag == tag){
          790                         *l = rp->next;
          791                         unlock(&nfs);
          792                         free(rp);
          793                         return;
          794                 }
          795         }
          796         unlock(&nfs);
          797 }
          798 
          799 Fid*
          800 getfid(int fid)
          801 {
          802         int i, freefid;
          803 
          804         freefid = -1;
          805         for(i = 0; i < Nfid; i++){
          806                 if(freefid < 0 && fids[i].file < 0)
          807                         freefid = i;
          808                 if(fids[i].fid == fid)
          809                         return &fids[i];
          810         }
          811         if(freefid >= 0){
          812                 fids[freefid].fid = fid;
          813                 return &fids[freefid];
          814         }
          815         return nil;
          816 }
          817 
          818 int
          819 fsstat(int fd, Fid *fid, Fcall *f)
          820 {
          821         Dir d;
          822         uchar statbuf[256];
          823 
          824         memset(&d, 0, sizeof(d));
          825         d.name = fstab[fid->file].name;
          826         d.uid = user;
          827         d.gid = user;
          828         d.muid = user;
          829         d.qid = fstab[fid->file].qid;
          830         d.mode = fstab[fid->file].perm;
          831         d.atime = d.mtime = time(0);
          832         f->stat = statbuf;
          833         f->nstat = convD2M(&d, statbuf, sizeof statbuf);
          834         return fsreply(fd, f);
          835 }
          836 
          837 int
          838 fsread(int fd, Fid *fid, Fcall *f)
          839 {
          840         Dir d;
          841         uchar buf[256];
          842         Request *rp;
          843 
          844         switch(fid->file){
          845         default:
          846                 return -1;
          847         case Qdir:
          848                 if(f->offset == 0 && f->count >0){
          849                         memset(&d, 0, sizeof(d));
          850                         d.name = fstab[Qcpunote].name;
          851                         d.uid = user;
          852                         d.gid = user;
          853                         d.muid = user;
          854                         d.qid = fstab[Qcpunote].qid;
          855                         d.mode = fstab[Qcpunote].perm;
          856                         d.atime = d.mtime = time(0);
          857                         f->count = convD2M(&d, buf, sizeof buf);
          858                         f->data = (char*)buf;
          859                 } else
          860                         f->count = 0;
          861                 return fsreply(fd, f);
          862         case Qcpunote:
          863                 rp = mallocz(sizeof(*rp), 1);
          864                 if(rp == nil)
          865                         return -1;
          866                 rp->f = *f;
          867                 lock(&nfs);
          868                 if(nfs.rfirst == nil)
          869                         nfs.rfirst = rp;
          870                 else
          871                         nfs.rlast->next = rp;
          872                 nfs.rlast = rp;
          873                 unlock(&nfs);
          874                 return kick(fd);;
          875         }
          876 }
          877 
          878 char Eperm[] = "permission denied";
          879 char Enofile[] = "out of files";
          880 char Enotdir[] = "not a directory";
          881 
          882 void
          883 notefs(int fd)
          884 {
          885         uchar buf[IOHDRSZ+Maxfdata];
          886         int i, j, n;
          887         char err[ERRMAX];
          888         Fcall f;
          889         Fid *fid, *nfid;
          890         int doreply;
          891 
          892         rfork(RFNOTEG);
          893         fmtinstall('F', fcallconv);
          894 
          895         for(n = 0; n < Nfid; n++)
          896                 fids[n].file = -1;
          897 
          898         for(;;){
          899                 n = read9pmsg(fd, buf, sizeof(buf));
          900                 if(n <= 0){
          901                         if(dbg)
          902                                 fprint(2, "read9pmsg(%d) returns %d: %r\n", fd, n);
          903                         break;
          904                 }
          905                 if(convM2S(buf, n, &f) < 0)
          906                         break;
          907                 if(dbg)
          908                         fprint(2, "->%F\n", &f);
          909                 doreply = 1;
          910                 fid = getfid(f.fid);
          911                 if(fid == nil){
          912 nofids:
          913                         f.type = Rerror;
          914                         f.ename = Enofile;
          915                         fsreply(fd, &f);
          916                         continue;
          917                 }
          918                 switch(f.type++){
          919                 default:
          920                         f.type = Rerror;
          921                         f.ename = "unknown type";
          922                         break;
          923                 case Tflush:
          924                         flushreq(f.oldtag);
          925                         break;
          926                 case Tversion:
          927                         if(f.msize > IOHDRSZ+Maxfdata)
          928                                 f.msize = IOHDRSZ+Maxfdata;
          929                         break;
          930                 case Tauth:
          931                         f.type = Rerror;
          932                         f.ename = "cpu: authentication not required";
          933                         break;
          934                 case Tattach:
          935                         f.qid = fstab[Qdir].qid;
          936                         fid->file = Qdir;
          937                         break;
          938                 case Twalk:
          939                         nfid = nil;
          940                         if(f.newfid != f.fid){
          941                                 nfid = getfid(f.newfid);
          942                                 if(nfid == nil)
          943                                         goto nofids;
          944                                 nfid->file = fid->file;
          945                                 fid = nfid;
          946                         }
          947 
          948                         f.ename = nil;
          949                         for(i=0; i<f.nwname; i++){
          950                                 if(i > MAXWELEM){
          951                                         f.type = Rerror;
          952                                         f.ename = "too many name elements";
          953                                         break;
          954                                 }
          955                                 if(fid->file != Qdir){
          956                                         f.type = Rerror;
          957                                         f.ename = Enotdir;
          958                                         break;
          959                                 }
          960                                 if(strcmp(f.wname[i], "cpunote") == 0){
          961                                         fid->file = Qcpunote;
          962                                         f.wqid[i] = fstab[Qcpunote].qid;
          963                                         continue;
          964                                 }
          965                                 f.type = Rerror;
          966                                 f.ename = err;
          967                                 strcpy(err, "cpu: file \"");
          968                                 for(j=0; j<=i; j++){
          969                                         if(strlen(err)+1+strlen(f.wname[j])+32 > sizeof err)
          970                                                 break;
          971                                         if(j != 0)
          972                                                 strcat(err, "/");
          973                                         strcat(err, f.wname[j]);
          974                                 }
          975                                 strcat(err, "\" does not exist");
          976                                 break;
          977                         }
          978                         if(nfid != nil && (f.ename != nil || i < f.nwname))
          979                                 nfid ->file = -1;
          980                         if(f.type != Rerror)
          981                                 f.nwqid = i;
          982                         break;
          983                 case Topen:
          984                         if(f.mode != OREAD){
          985                                 f.type = Rerror;
          986                                 f.ename = Eperm;
          987                         }
          988                         f.qid = fstab[fid->file].qid;
          989                         break;
          990                 case Tcreate:
          991                         f.type = Rerror;
          992                         f.ename = Eperm;
          993                         break;
          994                 case Tread:
          995                         if(fsread(fd, fid, &f) < 0)
          996                                 goto err;
          997                         doreply = 0;
          998                         break;
          999                 case Twrite:
         1000                         f.type = Rerror;
         1001                         f.ename = Eperm;
         1002                         break;
         1003                 case Tclunk:
         1004                         fid->file = -1;
         1005                         break;
         1006                 case Tremove:
         1007                         f.type = Rerror;
         1008                         f.ename = Eperm;
         1009                         break;
         1010                 case Tstat:
         1011                         if(fsstat(fd, fid, &f) < 0)
         1012                                 goto err;
         1013                         doreply = 0;
         1014                         break;
         1015                 case Twstat:
         1016                         f.type = Rerror;
         1017                         f.ename = Eperm;
         1018                         break;
         1019                 }
         1020                 if(doreply)
         1021                         if(fsreply(fd, &f) < 0)
         1022                                 break;
         1023         }
         1024 err:
         1025         if(dbg)
         1026                 fprint(2, "notefs exiting: %r\n");
         1027         close(fd);
         1028 }
         1029 
         1030 char         notebuf[ERRMAX];
         1031 
         1032 void
         1033 catcher(void*, char *text)
         1034 {
         1035         int n;
         1036 
         1037         n = strlen(text);
         1038         if(n >= sizeof(notebuf))
         1039                 n = sizeof(notebuf)-1;
         1040         memmove(notebuf, text, n);
         1041         notebuf[n] = '\0';
         1042         noted(NCONT);
         1043 }
         1044 
         1045 /*
         1046  *  mount in /dev a note file for the remote side to read.
         1047  */
         1048 void
         1049 lclnoteproc(int netfd)
         1050 {
         1051         int exportfspid;
         1052         Waitmsg *w;
         1053         Note *np;
         1054         int pfd[2];
         1055 
         1056         if(pipe(pfd) < 0){
         1057                 fprint(2, "cpu: can't start note proc: pipe: %r\n");
         1058                 return;
         1059         }
         1060 
         1061         /* new proc mounts and returns to start exportfs */
         1062         switch(exportfspid = rfork(RFPROC|RFNAMEG|RFFDG|RFMEM)){
         1063         case -1:
         1064                 fprint(2, "cpu: can't start note proc: rfork: %r\n");
         1065                 return;
         1066         case 0:
         1067                 close(pfd[0]);
         1068                 if(mount(pfd[1], -1, "/dev", MBEFORE, "") < 0)
         1069                         fprint(2, "cpu: can't mount note proc: %r\n");
         1070                 close(pfd[1]);
         1071                 return;
         1072         }
         1073 
         1074         close(netfd);
         1075         close(pfd[1]);
         1076 
         1077         /* new proc listens for note file system rpc's */
         1078         switch(rfork(RFPROC|RFNAMEG|RFMEM)){
         1079         case -1:
         1080                 fprint(2, "cpu: can't start note proc: rfork1: %r\n");
         1081                 _exits(0);
         1082         case 0:
         1083                 notefs(pfd[0]);
         1084                 _exits(0);
         1085         }
         1086 
         1087         /* original proc waits for notes */
         1088         notify(catcher);
         1089         w = nil;
         1090         for(;;) {
         1091                 *notebuf = 0;
         1092                 free(w);
         1093                 w = wait();
         1094                 if(w == nil) {
         1095                         if(*notebuf == 0)
         1096                                 break;
         1097                         np = mallocz(sizeof(Note), 1);
         1098                         if(np != nil){
         1099                                 strcpy(np->msg, notebuf);
         1100                                 lock(&nfs);
         1101                                 if(nfs.nfirst == nil)
         1102                                         nfs.nfirst = np;
         1103                                 else
         1104                                         nfs.nlast->next = np;
         1105                                 nfs.nlast = np;
         1106                                 unlock(&nfs);
         1107                                 kick(pfd[0]);
         1108                         }
         1109                         unlock(&nfs);
         1110                 } else if(w->pid == exportfspid)
         1111                         break;
         1112         }
         1113 
         1114         if(w == nil)
         1115                 exits(nil);
         1116         exits(w->msg);
         1117 }