#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <signal.h> #include <ipc.h> #include <errno.h> #include <libc.h> #include <pwd.h> #include <grp.h> typedef struct { char *address; char *arg; char *mpoint; char *callusr; int rootino; dev_t rootdev; char proto; char stat; char devnum; char debug; int retry; } Friend; #define SOK 01 /* filesystem running ok */ #define SFOUND 02 /* found in most recent friends pass */ #define MAXRETRY 60 /* slow down after this many retries */ #define LONGRETRY 20 /* after slowdown, retry after this many cycles */ #define MAXFRIENDS 128 Friend friends[MAXFRIENDS]; /* should probably be dynamic */ #define MAXMSG 5 /* largest message 5*MSGBLOCK -- known to kernel too */ #define MSGBLOCK 1024 #define NBFS 4 /* our filesystem type */ char frfile[] = "/usr/netb/friends"; /* should be an argument */ char *estr(); Friend *findmpoint(); main(argc, argv) int argc; char **argv; { Friend f; int fd; if (argc <= 1) { mkdaemon(); rundaemon(); } if (argc < 6 || argc > 8) { fprintf(stderr, "usage: setup netaddr arg mountpoint protocol devnum [debug userid]\n"); exit(1); } f.address = argv[1]; f.arg = argv[2]; f.mpoint = argv[3]; f.proto = argv[4][0]; f.devnum = atoi(argv[5]); if (argc <= 6) f.debug = 0; else f.debug = atoi(argv[6]); if (argc <= 7) f.callusr = "daemon"; else f.callusr = argv[7]; if ((fd = callsys(&f)) < 0) { fprintf(stderr, "%s %s %c %s: %s\n", f.address, f.arg, f.proto, f.callusr, errstr); exit(1); } if (setup(fd, &f) < 0) exit(1); exit(0); } nulltrap(s) { signal(s, nulltrap); } /* * prepare to be a daemon */ mkdaemon() { switch (fork()) { case -1: perror("fork"); exit(1); default: /* parent */ exit(0); case 0: /* child */ break; } signal(SIGINT, SIG_IGN); signal(SIGQUIT, SIG_IGN); signal(SIGHUP, SIG_IGN); signal(SIGPIPE, SIG_IGN); /* needed? */ signal(SIGALRM, nulltrap); /* miscellaneous alarms */ } /* * the daemon: * once a minute, * see if the filesystems we've already mounted are ok * see if the friends file has changed, and read it if so * try to mount any filesystem that isn't */ rundaemon() { static time_t fmtime; struct stat st; register Friend *fp; int fd; for (;; sleep(60)) { for (fp = friends; fp < &friends[MAXFRIENDS]; fp++) { if (fp->address == NULL || (fp->stat & SOK) == 0) continue; alarm(60); if (stat(fp->mpoint, &st) < 0) { plog("stat %s: %s\n", fp->mpoint, estr()); fp->stat &=~ SOK; } else if (st.st_dev != fp->rootdev || st.st_ino != fp->rootino) { plog("%s: not root: want 0x%x:%d, have 0x%x:%d\n", fp->mpoint, fp->rootdev, fp->rootino, st.st_dev, st.st_ino); fp->stat &=~ SOK; } alarm(0); } if (stat(frfile, &st) < 0) plog("stat %s: %s\n", frfile, estr()); else if (fmtime != st.st_mtime) { fmtime = st.st_mtime; newfriends(); } for (fp = friends; fp < &friends[MAXFRIENDS]; fp++) { if (fp->address == NULL || fp->stat & SOK) continue; if (fp->retry > MAXRETRY) { if (fp->retry < MAXRETRY+LONGRETRY) { fp->retry++; continue; /* skip this time */ } fp->retry = MAXRETRY; /* do this one, then skip */ } plog("%s: calling %s %s %c %s\n", fp->mpoint, fp->address, fp->arg, fp->proto, fp->callusr); if ((fd = callsys(fp)) < 0) { plog("%s: %s\n", fp->address, errstr); fp->retry++; continue; } if (setup(fd, fp) < 0) { plog("%s: setup failed\n", fp->mpoint); alarm(30); close(fd); alarm(0); fp->retry++; continue; } close(fd); plog("%s started\n", fp->mpoint); fp->stat |= SOK; fp->retry = 0; } } } /* * reread the friends file * the algorithm is quadratic, * but we don't do it often */ #define ASIZE 8 newfriends() { FILE *f; char line[512]; /* huge */ char *args[ASIZE]; char proto, devnum, debug; char *callusr; register Friend *fp; if ((f = fopen(frfile, "r")) == NULL) { plog("can't open %s: %s\n", frfile, estr()); return; } setfields(" \t\n"); for (fp = friends; fp < &friends[MAXFRIENDS]; fp++) fp->stat &=~ SFOUND; while (fgets(line, sizeof(line), f)) { if (line[0] == '#') continue; /* * backward compatibility: * login name may be omitted */ switch (getmfields(line, args, ASIZE)) { default: continue; case 6: callusr = "daemon"; break; case 7: callusr = args[6]; break; } proto = args[3][0]; devnum = atoi(args[4]); debug = atoi(args[5]); fp = findmpoint(args[2]); if (fp->address /* didn't return an empty */ && strcmp(fp->address, args[0]) == 0 && strcmp(fp->arg, args[1]) == 0 /* mount point already checked */ && strcmp(fp->callusr, callusr) == 0 && fp->proto == proto && fp->devnum == devnum && fp->debug == debug) { fp->stat |= SFOUND; fp->retry = 0; continue; /* it hasn't changed */ } if (fp->address) { /* was already there, clear it */ plog("changing %s\n", fp->mpoint); free(fp->address); free(fp->arg); free(fp->mpoint); free(fp->callusr); } fp->address = strdup(args[0]); fp->arg = strdup(args[1]); fp->mpoint = strdup(args[2]); fp->callusr = strdup(callusr); fp->proto = proto; fp->devnum = devnum; fp->debug = debug; fp->stat = SFOUND; /* nb OK not set yet */ fp->retry = 0; } fclose(f); for (fp = friends; fp < &friends[MAXFRIENDS]; fp++) { if (fp->address == NULL || fp->stat & SFOUND) continue; plog("dropping %s\n", fp->mpoint); free(fp->address); free(fp->arg); free(fp->mpoint); fp->address = NULL; } } Friend * findmpoint(mp) char *mp; { register Friend *fp, *ep; static Friend junkfriend; ep = NULL; for (fp = friends; fp < &friends[MAXFRIENDS]; fp++) { if (fp->address == NULL) { if (ep == NULL) ep = fp; continue; } if (strcmp(fp->mpoint, mp) == 0) return (fp); } if (ep) return (ep); plog("too many friends; no room for %s\n", mp); return (&junkfriend); } /* * call a server * according to network protocol */ callsys(fp) register Friend *fp; { register int fd; alarm(120); setlogname(fp->callusr); /* who the network thinks we are */ switch (fp->proto) { case 'd': /* `datakit' -- general connection */ fd = ipcopen(ipcpath(fp->address, "dk", "fsb"), "heavy hup"); break; case 'e': /* same, but use rexec -- why bother? */ fd = ipcexec(ipcpath(fp->address, "dk", "exec"), "heavy hup", fp->arg); break; case 't': fd = ipcrexec(fp->address, "heavy hup", fp->arg); break; default: errstr = "unknown setup protocol"; fd = -1; break; } alarm(0); return (fd); } /* * initial protocol with server: * client sends 16 bytes: * max buffer size in 1024-byte units * device number in use * protocol letter * debugging flag * 12 unused * server sends one byte: 1 * client sends a list of userids: maxsize bytes * if list won't fit in one buffer, last username is `:' * server acks with one byte 012; client sends more * server sends one byte: 2 * client sends a list of groupids: maxsize bytes * if list won't fit in one buffer, last groupname is `:' * server acks with one byte 013; client sends more * server sends one byte: 3 * then we mount it, and further messages come from the kernel */ #define M1LEN 16 setup(fd, fp) int fd; register Friend *fp; { char m[M1LEN]; register int n; struct stat st; memset(m, 0, M1LEN); m[0] = MAXMSG; m[1] = fp->devnum; m[2] = fp->proto; if (m[2] == 'e') m[2] = 'd'; /* hack */ m[3] = fp->debug; alarm(600); if ((n = write(fd, m, M1LEN)) != M1LEN) { plog("%s setup 1: write returned %d; %s\n", fp->mpoint, n, estr()); alarm(0); return (-1); } if (getresp(fd, 1, fp->mpoint) == 0) return (-1); alarm(600); if (senduid(fd, fp->mpoint) < 0) { alarm(0); return (-1); } alarm(600); if (sendgid(fd, fp->mpoint) < 0) { alarm(0); return (-1); } alarm(60); funmount(fp->mpoint); /* bad -- might unmount wrong fs */ if (fmount(NBFS, fd, fp->mpoint, makedev(fp->devnum, 0)) < 0) { plog("%s: fmount: %s\n", fp->mpoint, estr()); alarm(0); return (-1); } alarm(60); if (stat(fp->mpoint, &st) < 0) { plog("%s: initial root stat: %s\n", fp->mpoint, estr()); alarm(0); return (-1); } alarm(0); fp->rootino = st.st_ino; fp->rootdev = st.st_dev; return (0); } /* * get a single-byte response from server * try to cope with unexpected noise, * usually something like /etc/motd or `You have mail.' * fortunately server responses are all characters * unlikely to appear in ordinary ASCII text (viz. octal 1 2 3). * it is more important to copy the junk to the logfile * (so someone can get rid of it) than to survive it */ getresp(fd, id, fs) int fd, id; char *fs; { register int i, n; char m[400]; int tries, tmo; tmo = 600; /* timeout */ for (tries = 0; tries < 5 && tmo > 0; tries++) { alarm(tmo); n = read(fd, m, sizeof(m)-1); tmo = alarm(0); /* assume errno untouched */ m[n] = 0; if (n <= 0) { plog("%s setup %d: read returned %d: %s\n", fs, id, n, estr()); return (0); } for (i = 0; i < n; i++) if (m[i] == id) break; if (i) { m[i] = 0; plog("%s setup %d: read junk: %s\n", fs, id, m); } if (i < n) /* found it */ return (1); } plog("%s setup %d: no response\n", fs, id); return (0); } #define SLOP 50 senduid(fd, mpoint) int fd; char *mpoint; { char idbuf[MAXMSG*MSGBLOCK+SLOP]; char onebuf[SLOP]; register char *p; register struct passwd *pw; register int n, wn; setpwent(); p = idbuf; while ((pw = getpwent()) != NULL) { sprintf(onebuf, "%s %d\n", pw->pw_name, pw->pw_uid); n = strlen(onebuf); if (n > &idbuf[MAXMSG*MSGBLOCK] - p - 4) { /* room for ": 1\n" */ strcpy(p, ": 1\n"); /* `more coming' */ if ((wn = write(fd, idbuf, MAXMSG*MSGBLOCK)) != MAXMSG*MSGBLOCK) { plog("%s: write uid: %d; %s\n", mpoint, wn, estr()); return (-1); } if (getresp(fd, 012, mpoint) == 0) return (-1); p = idbuf; } strcpy(p, onebuf); p += n; } endpwent(); if ((wn = write(fd, idbuf, MAXMSG*MSGBLOCK)) != MAXMSG*MSGBLOCK) { plog("%s: write uid: %d; %s\n", mpoint, wn, estr()); return (-1); } if (getresp(fd, 2, mpoint) == 0) return (-1); return (0); } sendgid(fd, mpoint) int fd; char *mpoint; { char idbuf[MAXMSG*MSGBLOCK+SLOP]; char onebuf[SLOP]; register char *p; register struct group *gr; register int n, wn; setgrent(); p = idbuf; while ((gr = getgrent()) != NULL) { sprintf(onebuf, "%s %d\n", gr->gr_name, gr->gr_gid); n = strlen(onebuf); if (n > &idbuf[MAXMSG*MSGBLOCK] - p - 4) { /* room for ": 1\n" */ strcpy(p, ": 1\n"); /* `more coming' */ if ((wn = write(fd, idbuf, MAXMSG*MSGBLOCK)) != MAXMSG*MSGBLOCK) { plog("%s: write gid: %d; %s\n", mpoint, wn, estr()); return (-1); } if (getresp(fd, 013, mpoint) == 0) return (-1); p = idbuf; } strcpy(p, onebuf); p += n; } endgrent(); if ((wn = write(fd, idbuf, MAXMSG*MSGBLOCK)) != MAXMSG*MSGBLOCK) { plog("%s: write gid: %d; %s\n", mpoint, wn, estr()); return (-1); } if (getresp(fd, 3, mpoint) == 0) return (-1); return (0); } /* * error logging stuff * just print to stderr for now */ /* PRINTFLIKE1 */ plog(s, a, b, c, d, e, f) char *s; { long now; time(&now); fseek(stderr, 0L, 2); fprintf(stderr, "%.15s ", ctime(&now)+4); /* trim day of week, year */ fprintf(stderr, s, a, b, c, d, e, f); fflush(stderr); } char * estr() { static char buf[] = "Error -2147483648"; if (errno < 0 || errno >= sys_nerr) { sprintf(buf, "Error %d", errno); return (buf); } return (sys_errlist[errno]); }