Warning: Attempt to read property "date" on null in /usr/local/www/websvn.planix.org/blame.php on line 247

Warning: Attempt to read property "msg" on null in /usr/local/www/websvn.planix.org/blame.php on line 247
WebSVN – planix.SVN – Blame – /os/branches/feature-vt/sys/src/cmd/cpu.c – Rev 2

Subversion Repositories planix.SVN

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

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