5 #define NAMELEN 64 /* reasonable upper limit for name elements */
7 typedef struct Service Service;
10 char serv[NAMELEN]; /* name of the service */
11 char remote[3*NAMELEN]; /* address of remote system */
12 char prog[5*NAMELEN+1]; /* program to execute */
15 typedef struct Announce Announce;
25 int readstr(char*, char*, char*, int);
26 void dolisten(char*, char*, int, char*, char*, long*);
27 void newcall(int, char*, char*, Service*);
28 int findserv(char*, char*, Service*, char*);
29 int getserv(char*, char*, Service*);
31 void scandir(char*, char*, char*);
32 void becomenone(void);
33 void listendir(char*, char*, int);
35 char listenlog[] = "listen";
43 Announce *announcements;
51 error("usage: aux/listen [-q] [-n namespace] [-d servdir] [-t trustdir] [-p maxprocs]"
56 * based on libthread's threadsetname, but drags in less library code.
57 * actually just sets the arguments displayed.
60 procsetname(char *fmt, ...)
68 cmdname = vsmprint(fmt, arg);
72 snprint(buf, sizeof buf, "#p/%d/args", getpid());
73 if((fd = open(buf, OWRITE)) >= 0){
74 write(fd, cmdname, strlen(cmdname)+1);
81 main(int argc, char *argv[])
95 cpu = getenv("cputype");
97 error("can't get cputype");
101 servdir = EARGF(usage());
107 trustdir = EARGF(usage());
110 namespace = EARGF(usage());
113 maxprocs = atoi(EARGF(usage()));
117 * fixed configuration, no periodic
118 * rescan of the service directory.
126 if(!servdir && !trustdir)
127 servdir = "/bin/service";
129 if(servdir && strlen(servdir) + NAMELEN >= sizeof(s->prog))
130 error("service directory too long");
131 if(trustdir && strlen(trustdir) + NAMELEN >= sizeof(s->prog))
132 error("trusted service directory too long");
144 syslog(0, listenlog, "started on %s", proto);
147 proto = strrchr(proto, '/');
152 listendir(protodir, servdir, 0);
153 listendir(protodir, trustdir, 1);
155 /* command returns */
160 dingdong(void*, char *msg)
162 if(strstr(msg, "alarm") != nil)
168 listendir(char *protodir, char *srvdir, int trusted)
171 char dir[40], err[128], ds[128];
181 * insulate ourselves from later
182 * changing of console environment variables
183 * erase privileged crypt state
185 switch(rfork(RFNOTEG|RFPROC|RFFDG|RFNOWAIT|RFENVG|RFNAMEG)) {
194 procsetname("%s %s %s", protodir, srvdir, namespace);
201 scandir(proto, protodir, srvdir);
204 * loop through announcements and process trusted services in
205 * invoker's ns and untrusted in none's.
207 for(a = announcements; a; a = a->next){
214 strncpy(ds, a->a, sizeof(ds));
217 /* a process per service */
218 switch(pid = rfork(RFFDG|RFPROC|RFMEM)){
220 syslog(1, listenlog, "couldn't fork for %s", ds);
225 ctl = announce(ds, dir);
227 errstr(err, sizeof err);
230 "giving up on %s: %r",
232 if(strstr(err, "address in use")
234 exits("addr-in-use");
238 dolisten(proto, dir, ctl, srvdir, ds, &childs);
248 * if not running a fixed configuration,
249 * pick up any children that gave up and
250 * sleep for at least 60 seconds.
251 * If a service process dies in a fixed
252 * configuration what should be done -
253 * nothing? restart? restart after a delay?
254 * - currently just wait for something to
255 * die and delay at least 60 seconds
261 while((wm = wait()) != nil) {
262 for(a = announcements; a; a = a->next)
263 if(a->announced == wm->pid) {
265 if (strstr(wm->msg, "addr-in-use") !=
267 /* don't fill log file */
276 scandir(proto, protodir, srvdir);
278 start = 60 - (time(0)-start);
286 * make a list of all services to announce for
289 addannounce(char *str)
293 /* look for duplicate */
295 for(a = announcements; a; a = a->next){
296 if(strcmp(str, a->a) == 0){
304 a = mallocz(sizeof(*a) + strlen(str) + 1, 1);
312 scandir(char *proto, char *protodir, char *dname)
320 for(a = announcements; a != nil; a = a->next)
323 fd = open(dname, OREAD);
327 nlen = strlen(proto);
328 while((n=dirread(fd, &db)) > 0){
331 if(db[i].qid.type&QTDIR)
333 if(db[i].length <= 0)
335 if(strncmp(nm, proto, nlen) != 0)
337 snprint(ds, sizeof ds, "%s!*!%s", protodir, nm + nlen);
346 while((a = *l) != nil){
349 if (a->announced > 0)
350 postnote(PNPROC, a->announced, "die");
363 fd = open("#c/user", OWRITE);
364 if(fd < 0 || write(fd, "none", strlen("none")) < 0)
365 error("can't become none");
367 if(newns("none", namespace) < 0)
368 error("can't build namespace");
372 dolisten(char *proto, char *dir, int ctl, char *srvdir, char *dialstr, long *pchilds)
375 char ndir[40], wbuf[64];
376 int nctl, data, wfd, nowait;
378 procsetname("%s %s", dir, dialstr);
382 if(pchilds && maxprocs > 0){
383 snprint(wbuf, sizeof(wbuf), "/proc/%d/wait", getpid());
384 if((wfd = open(wbuf, OREAD)) >= 0)
394 * check for exited subprocesses
396 if(procs >= maxprocs || (*pchilds % 8) == 0)
399 if(d == nil || d->length == 0){
404 if(read(wfd, wbuf, sizeof(wbuf)) > 0){
410 if(procs >= maxprocs){
412 syslog(1, listenlog, "%s: process limit of %ld reached",
424 * wait for a call (or an error)
426 nctl = listen(dir, ndir);
429 syslog(1, listenlog, "listen: %r");
436 * start a subprocess for the connection
438 switch(rfork(RFFDG|RFPROC|RFMEM|RFENVG|RFNAMEG|RFNOTEG|RFREND|nowait)){
440 reject(nctl, ndir, "host overloaded");
445 * see if we know the service requested
447 memset(&s, 0, sizeof s);
448 if(!findserv(proto, ndir, &s, srvdir)){
450 syslog(1, listenlog, "%s: unknown service '%s' from '%s': %r",
451 proto, s.serv, s.remote);
452 reject(nctl, ndir, "connection refused");
455 data = accept(nctl, ndir);
457 syslog(1, listenlog, "can't open %s/data: %r", ndir);
460 fprint(nctl, "keepalive");
465 newcall(data, proto, ndir, &s);
479 * look in the service directory for the service.
480 * if the shell script or program is zero-length, ignore it,
481 * thus providing a way to disable a service with a bind.
484 findserv(char *proto, char *dir, Service *s, char *srvdir)
489 if(!getserv(proto, dir, s))
491 snprint(s->prog, sizeof s->prog, "%s/%s", srvdir, s->serv);
492 d = dirstat(s->prog);
493 rv = d && d->length > 0; /* ignore unless it's non-empty */
499 * get the service name out of the local address
502 getserv(char *proto, char *dir, Service *s)
504 char addr[128], *serv, *p;
507 readstr(dir, "remote", s->remote, sizeof(s->remote)-1);
508 if(p = utfrune(s->remote, L'\n'))
511 n = readstr(dir, "local", addr, sizeof(addr)-1);
514 if(p = utfrune(addr, L'\n'))
516 serv = utfrune(addr, L'!');
523 * disallow service names like
524 * ../../usr/user/bin/rc/su
526 if(strlen(serv) +strlen(proto) >= NAMELEN || utfrune(serv, L'/') || *serv == '.')
528 snprint(s->serv, sizeof s->serv, "%s%s", proto, serv);
534 remoteaddr(char *dir)
539 snprint(buf, sizeof buf, "%s/remote", dir);
540 fd = open(buf, OREAD);
543 n = read(fd, buf, sizeof(buf));
547 p = strchr(buf, '!');
556 newcall(int fd, char *proto, char *dir, Service *s)
558 char data[4*NAMELEN];
564 syslog(0, listenlog, "%s call for %s on chan %s (%s)", proto, s->serv, dir, p);
567 syslog(0, listenlog, "%s call for %s on chan %s", proto, s->serv, dir);
570 snprint(data, sizeof data, "%s/data", dir);
571 bind(data, "/dev/cons", MREPL);
574 /* dup(fd, 2); keep stderr */
580 for(fd=3; fd<20; fd++)
582 execl(s->prog, s->prog, s->serv, proto, dir, nil);
589 syslog(1, listenlog, "%s: %s: %r", proto, s);
594 * read a string from a device
597 readstr(char *dir, char *info, char *s, int len)
600 char buf[3*NAMELEN+4];
602 snprint(buf, sizeof buf, "%s/%s", dir, info);
603 fd = open(buf, OREAD);
607 n = read(fd, s, len-1);