Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
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
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
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
153
dingdong(void*, char *msg)
154
{
155
	if(strstr(msg, "alarm") != nil)
156
		noted(NCONT);
157
	noted(NDFLT);
158
}
159
 
160
void
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
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
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
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
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
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
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
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
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 *
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
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
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
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
}