1 #include <u.h>
2 #include <libc.h>
3 #include <auth.h>
4
5 #define NAMELEN 64 /* reasonable upper limit for name elements */
6
7 typedef struct Service Service;
8 struct Service
9 {
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 */
13 };
14
15 typedef struct Announce Announce;
16 struct Announce
17 {
18 Announce *next;
19 char *a;
20 int announced;
21 int whined;
22 };
23
24 int readstr(char*, char*, char*, int);
25 void dolisten(char*, char*, int, char*, char*);
26 void newcall(int, char*, char*, Service*);
27 int findserv(char*, char*, Service*, char*);
28 int getserv(char*, char*, Service*);
29 void error(char*);
30 void scandir(char*, char*, char*);
31 void becomenone(void);
32 void listendir(char*, char*, int);
33
34 char listenlog[] = "listen";
35
36 int quiet;
37 int immutable;
38 char *cpu;
39 char *proto;
40 Announce *announcements;
41 #define SEC 1000
42
43 char *namespace;
44
45 void
usage(void)46 usage(void)
47 {
48 error("usage: aux/listen [-q] [-n namespace] [-d servdir] [-t trustdir]"
49 " [proto]");
50 }
51
52 /*
53 * based on libthread's threadsetname, but drags in less library code.
54 * actually just sets the arguments displayed.
55 */
56 static void
procsetname(char * fmt,...)57 procsetname(char *fmt, ...)
58 {
59 int fd;
60 char *cmdname;
61 char buf[128];
62 va_list arg;
63
64 va_start(arg, fmt);
65 cmdname = vsmprint(fmt, arg);
66 va_end(arg);
67 if (cmdname == nil)
68 return;
69 snprint(buf, sizeof buf, "#p/%d/args", getpid());
70 if((fd = open(buf, OWRITE)) >= 0){
71 write(fd, cmdname, strlen(cmdname)+1);
72 close(fd);
73 }
74 free(cmdname);
75 }
76
77 void
main(int argc,char * argv[])78 main(int argc, char *argv[])
79 {
80 Service *s;
81 char *protodir;
82 char *trustdir;
83 char *servdir;
84
85 servdir = 0;
86 trustdir = 0;
87 proto = "tcp";
88 quiet = 0;
89 immutable = 0;
90 argv0 = argv[0];
91 cpu = getenv("cputype");
92 if(cpu == 0)
93 error("can't get cputype");
94
95 ARGBEGIN{
96 case 'd':
97 servdir = EARGF(usage());
98 break;
99 case 'q':
100 quiet = 1;
101 break;
102 case 't':
103 trustdir = EARGF(usage());
104 break;
105 case 'n':
106 namespace = EARGF(usage());
107 break;
108 case 'i':
109 /*
110 * fixed configuration, no periodic
111 * rescan of the service directory.
112 */
113 immutable = 1;
114 break;
115 default:
116 usage();
117 }ARGEND;
118
119 if(!servdir && !trustdir)
120 servdir = "/bin/service";
121
122 if(servdir && strlen(servdir) + NAMELEN >= sizeof(s->prog))
123 error("service directory too long");
124 if(trustdir && strlen(trustdir) + NAMELEN >= sizeof(s->prog))
125 error("trusted service directory too long");
126
127 switch(argc){
128 case 1:
129 proto = argv[0];
130 break;
131 case 0:
132 break;
133 default:
134 usage();
135 }
136
137 syslog(0, listenlog, "started on %s", proto);
138
139 protodir = proto;
140 proto = strrchr(proto, '/');
141 if(proto == 0)
142 proto = protodir;
143 else
144 proto++;
145 listendir(protodir, servdir, 0);
146 listendir(protodir, trustdir, 1);
147
148 /* command returns */
149 exits(0);
150 }
151
152 static void
dingdong(void *,char * msg)153 dingdong(void*, char *msg)
154 {
155 if(strstr(msg, "alarm") != nil)
156 noted(NCONT);
157 noted(NDFLT);
158 }
159
160 void
listendir(char * protodir,char * srvdir,int trusted)161 listendir(char *protodir, char *srvdir, int trusted)
162 {
163 int ctl, pid, start;
164 char dir[40], err[128];
165 Announce *a;
166 Waitmsg *wm;
167
168 if (srvdir == 0)
169 return;
170
171 /*
172 * insulate ourselves from later
173 * changing of console environment variables
174 * erase privileged crypt state
175 */
176 switch(rfork(RFNOTEG|RFPROC|RFFDG|RFNOWAIT|RFENVG|RFNAMEG)) {
177 case -1:
178 error("fork");
179 case 0:
180 break;
181 default:
182 return;
183 }
184
185 procsetname("%s %s %s", protodir, srvdir, namespace);
186 if (!trusted)
187 becomenone();
188
189 notify(dingdong);
190
191 pid = getpid();
192 scandir(proto, protodir, srvdir);
193 for(;;){
194 /*
195 * loop through announcements and process trusted services in
196 * invoker's ns and untrusted in none's.
197 */
198 for(a = announcements; a; a = a->next){
199 if(a->announced > 0)
200 continue;
201
202 sleep((pid*10)%200);
203
204 /* a process per service */
205 switch(pid = rfork(RFFDG|RFPROC)){
206 case -1:
207 syslog(1, listenlog, "couldn't fork for %s", a->a);
208 break;
209 case 0:
210 for(;;){
211 ctl = announce(a->a, dir);
212 if(ctl < 0) {
213 errstr(err, sizeof err);
214 if (!a->whined)
215 syslog(1, listenlog,
216 "giving up on %s: %r",
217 a->a);
218 if(strstr(err, "address in use")
219 != nil)
220 exits("addr-in-use");
221 else
222 exits("ctl");
223 }
224 dolisten(proto, dir, ctl, srvdir, a->a);
225 close(ctl);
226 }
227 default:
228 a->announced = pid;
229 break;
230 }
231 }
232
233 /*
234 * if not running a fixed configuration,
235 * pick up any children that gave up and
236 * sleep for at least 60 seconds.
237 * If a service process dies in a fixed
238 * configuration what should be done -
239 * nothing? restart? restart after a delay?
240 * - currently just wait for something to
241 * die and delay at least 60 seconds
242 * between restarts.
243 */
244 start = time(0);
245 if(!immutable)
246 alarm(60*1000);
247 while((wm = wait()) != nil) {
248 for(a = announcements; a; a = a->next)
249 if(a->announced == wm->pid) {
250 a->announced = 0;
251 if (strstr(wm->msg, "addr-in-use") !=
252 nil)
253 /* don't fill log file */
254 a->whined = 1;
255 }
256 free(wm);
257 if(immutable)
258 break;
259 }
260 if(!immutable){
261 alarm(0);
262 scandir(proto, protodir, srvdir);
263 }
264 start = 60 - (time(0)-start);
265 if(start > 0)
266 sleep(start*1000);
267 }
268 /* not reached */
269 }
270
271 /*
272 * make a list of all services to announce for
273 */
274 void
addannounce(char * str)275 addannounce(char *str)
276 {
277 Announce *a, **l;
278
279 /* look for duplicate */
280 l = &announcements;
281 for(a = announcements; a; a = a->next){
282 if(strcmp(str, a->a) == 0)
283 return;
284 l = &a->next;
285 }
286
287 /* accept it */
288 a = mallocz(sizeof(*a) + strlen(str) + 1, 1);
289 if(a == 0)
290 return;
291 a->a = ((char*)a)+sizeof(*a);
292 strcpy(a->a, str);
293 a->announced = 0;
294 *l = a;
295 }
296
297 /*
298 * delete a service for announcement list
299 */
300 void
delannounce(char * str)301 delannounce(char *str)
302 {
303 Announce *a, **l;
304
305 /* look for service */
306 l = &announcements;
307 for(a = announcements; a; a = a->next){
308 if(strcmp(str, a->a) == 0)
309 break;
310 l = &a->next;
311 }
312 if (a == nil)
313 return;
314 *l = a->next; /* drop from the list */
315 if (a->announced > 0)
316 postnote(PNPROC, a->announced, "die");
317 a->announced = 0;
318 free(a);
319 }
320
321 static int
ignore(char * srvdir,char * name)322 ignore(char *srvdir, char *name)
323 {
324 int rv;
325 char *file = smprint("%s/%s", srvdir, name);
326 Dir *d = dirstat(file);
327
328 rv = !d || d->length <= 0; /* ignore unless it's non-empty */
329 free(d);
330 free(file);
331 return rv;
332 }
333
334 void
scandir(char * proto,char * protodir,char * dname)335 scandir(char *proto, char *protodir, char *dname)
336 {
337 int fd, i, n, nlen;
338 char *nm;
339 char ds[128];
340 Dir *db;
341
342 fd = open(dname, OREAD);
343 if(fd < 0)
344 return;
345
346 nlen = strlen(proto);
347 while((n=dirread(fd, &db)) > 0){
348 for(i=0; i<n; i++){
349 nm = db[i].name;
350 if(!(db[i].qid.type&QTDIR) &&
351 strncmp(nm, proto, nlen) == 0) {
352 snprint(ds, sizeof ds, "%s!*!%s", protodir,
353 nm + nlen);
354 if (ignore(dname, nm))
355 delannounce(ds);
356 else
357 addannounce(ds);
358 }
359 }
360 free(db);
361 }
362
363 close(fd);
364 }
365
366 void
becomenone(void)367 becomenone(void)
368 {
369 int fd;
370
371 fd = open("#c/user", OWRITE);
372 if(fd < 0 || write(fd, "none", strlen("none")) < 0)
373 error("can't become none");
374 close(fd);
375 if(newns("none", namespace) < 0)
376 error("can't build namespace");
377 }
378
379 void
dolisten(char * proto,char * dir,int ctl,char * srvdir,char * dialstr)380 dolisten(char *proto, char *dir, int ctl, char *srvdir, char *dialstr)
381 {
382 Service s;
383 char ndir[40];
384 int nctl, data;
385
386 procsetname("%s %s", dir, dialstr);
387 for(;;){
388 /*
389 * wait for a call (or an error)
390 */
391 nctl = listen(dir, ndir);
392 if(nctl < 0){
393 if(!quiet)
394 syslog(1, listenlog, "listen: %r");
395 return;
396 }
397
398 /*
399 * start a subprocess for the connection
400 */
401 switch(rfork(RFFDG|RFPROC|RFNOWAIT|RFENVG|RFNAMEG|RFNOTEG)){
402 case -1:
403 reject(nctl, ndir, "host overloaded");
404 close(nctl);
405 continue;
406 case 0:
407 /*
408 * see if we know the service requested
409 */
410 memset(&s, 0, sizeof s);
411 if(!findserv(proto, ndir, &s, srvdir)){
412 if(!quiet)
413 syslog(1, listenlog, "%s: unknown service '%s' from '%s': %r",
414 proto, s.serv, s.remote);
415 reject(nctl, ndir, "connection refused");
416 exits(0);
417 }
418 data = accept(nctl, ndir);
419 if(data < 0){
420 syslog(1, listenlog, "can't open %s/data: %r", ndir);
421 exits(0);
422 }
423 fprint(nctl, "keepalive");
424 close(ctl);
425 close(nctl);
426 newcall(data, proto, ndir, &s);
427 exits(0);
428 default:
429 close(nctl);
430 break;
431 }
432 }
433 }
434
435 /*
436 * look in the service directory for the service.
437 * if the shell script or program is zero-length, ignore it,
438 * thus providing a way to disable a service with a bind.
439 */
440 int
findserv(char * proto,char * dir,Service * s,char * srvdir)441 findserv(char *proto, char *dir, Service *s, char *srvdir)
442 {
443 int rv;
444 Dir *d;
445
446 if(!getserv(proto, dir, s))
447 return 0;
448 snprint(s->prog, sizeof s->prog, "%s/%s", srvdir, s->serv);
449 d = dirstat(s->prog);
450 rv = d && d->length > 0; /* ignore unless it's non-empty */
451 free(d);
452 return rv;
453 }
454
455 /*
456 * get the service name out of the local address
457 */
458 int
getserv(char * proto,char * dir,Service * s)459 getserv(char *proto, char *dir, Service *s)
460 {
461 char addr[128], *serv, *p;
462 long n;
463
464 readstr(dir, "remote", s->remote, sizeof(s->remote)-1);
465 if(p = utfrune(s->remote, L'\n'))
466 *p = '\0';
467
468 n = readstr(dir, "local", addr, sizeof(addr)-1);
469 if(n <= 0)
470 return 0;
471 if(p = utfrune(addr, L'\n'))
472 *p = '\0';
473 serv = utfrune(addr, L'!');
474 if(!serv)
475 serv = "login";
476 else
477 serv++;
478
479 /*
480 * disallow service names like
481 * ../../usr/user/bin/rc/su
482 */
483 if(strlen(serv) +strlen(proto) >= NAMELEN || utfrune(serv, L'/') || *serv == '.')
484 return 0;
485 snprint(s->serv, sizeof s->serv, "%s%s", proto, serv);
486
487 return 1;
488 }
489
490 char *
remoteaddr(char * dir)491 remoteaddr(char *dir)
492 {
493 char buf[128], *p;
494 int n, fd;
495
496 snprint(buf, sizeof buf, "%s/remote", dir);
497 fd = open(buf, OREAD);
498 if(fd < 0)
499 return strdup("");
500 n = read(fd, buf, sizeof(buf));
501 close(fd);
502 if(n > 0){
503 buf[n] = 0;
504 p = strchr(buf, '!');
505 if(p)
506 *p = 0;
507 return strdup(buf);
508 }
509 return strdup("");
510 }
511
512 void
newcall(int fd,char * proto,char * dir,Service * s)513 newcall(int fd, char *proto, char *dir, Service *s)
514 {
515 char data[4*NAMELEN];
516 char *p;
517
518 if(!quiet){
519 if(dir != nil){
520 p = remoteaddr(dir);
521 syslog(0, listenlog, "%s call for %s on chan %s (%s)",
522 proto, s->serv, dir, p);
523 free(p);
524 } else
525 syslog(0, listenlog, "%s call for %s on chan %s",
526 proto, s->serv, dir);
527 }
528
529 snprint(data, sizeof data, "%s/data", dir);
530 bind(data, "/dev/cons", MREPL);
531 dup(fd, 0);
532 dup(fd, 1);
533 dup(fd, 2);
534 close(fd);
535
536 /*
537 * close all the fds
538 */
539 for(fd=3; fd<20; fd++)
540 close(fd);
541 execl(s->prog, s->prog, s->serv, proto, dir, nil);
542 error(s->prog);
543 }
544
545 void
error(char * s)546 error(char *s)
547 {
548 syslog(1, listenlog, "%s: %s: %r", proto, s);
549 exits(0);
550 }
551
552 /*
553 * read a string from a device
554 */
555 int
readstr(char * dir,char * info,char * s,int len)556 readstr(char *dir, char *info, char *s, int len)
557 {
558 int n, fd;
559 char buf[3*NAMELEN+4];
560
561 snprint(buf, sizeof buf, "%s/%s", dir, info);
562 fd = open(buf, OREAD);
563 if(fd<0)
564 return 0;
565
566 n = read(fd, s, len-1);
567 if(n<=0){
568 close(fd);
569 return -1;
570 }
571 s[n] = 0;
572 close(fd);
573
574 return n+1;
575 }
576