xref: /plan9/sys/src/cmd/iostats/statsrv.c (revision c93608cc76758b2be624199c6208a0f90bad298d)
1 #include <u.h>
2 #include <libc.h>
3 #include <auth.h>
4 #include <fcall.h>
5 #define Extern	extern
6 #include "statfs.h"
7 
8 char Ebadfid[]	= "Bad fid";
9 char Enotdir[]	="Not a directory";
10 char Edupfid[]	= "Fid already in use";
11 char Eopen[]	= "Fid already opened";
12 char Exmnt[]	= "Cannot .. past mount point";
13 char Enoauth[]	= "iostats: Authentication failed";
14 char Ebadver[]	= "Unrecognized 9P version";
15 
16 int
okfile(char * s,int mode)17 okfile(char *s, int mode)
18 {
19 	if(strncmp(s, "/fd/", 3) == 0){
20 		/* 0, 1, and 2 we handle ourselves */
21 		if(s[4]=='/' || atoi(s+4) > 2)
22 			return 0;
23 		return 1;
24 	}
25 	if(strncmp(s, "/net/ssl", 8) == 0)
26 		return 0;
27 	if(strncmp(s, "/net/tls", 8) == 0)
28 		return 0;
29 	if(strncmp(s, "/srv/", 5) == 0 && ((mode&3) == OWRITE || (mode&3) == ORDWR))
30 		return 0;
31 	return 1;
32 }
33 
34 void
update(Rpc * rpc,vlong t)35 update(Rpc *rpc, vlong t)
36 {
37 	vlong t2;
38 
39 	t2 = nsec();
40 	t = t2 - t;
41 	if(t < 0)
42 		t = 0;
43 
44 	rpc->time += t;
45 	if(t < rpc->lo)
46 		rpc->lo = t;
47 	if(t > rpc->hi)
48 		rpc->hi = t;
49 }
50 
51 void
Xversion(Fsrpc * r)52 Xversion(Fsrpc *r)
53 {
54 	Fcall thdr;
55 	vlong t;
56 
57 	t = nsec();
58 
59 	if(r->work.msize > IOHDRSZ+Maxfdata)
60 		thdr.msize = IOHDRSZ+Maxfdata;
61 	else
62 		thdr.msize = r->work.msize;
63 	myiounit = thdr.msize - IOHDRSZ;
64 	if(strncmp(r->work.version, "9P2000", 6) != 0){
65 		reply(&r->work, &thdr, Ebadver);
66 		r->busy = 0;
67 		return;
68 	}
69 	thdr.version = "9P2000";
70 	/* BUG: should clunk all fids */
71 	reply(&r->work, &thdr, 0);
72 	r->busy = 0;
73 
74 	update(&stats->rpc[Tversion], t);
75 }
76 
77 void
Xauth(Fsrpc * r)78 Xauth(Fsrpc *r)
79 {
80 	Fcall thdr;
81 	vlong t;
82 
83 	t = nsec();
84 
85 	reply(&r->work, &thdr, Enoauth);
86 	r->busy = 0;
87 
88 	update(&stats->rpc[Tauth], t);
89 }
90 
91 void
Xflush(Fsrpc * r)92 Xflush(Fsrpc *r)
93 {
94 	Fsrpc *t, *e;
95 	Fcall thdr;
96 
97 	e = &Workq[Nr_workbufs];
98 
99 	for(t = Workq; t < e; t++) {
100 		if(t->work.tag == r->work.oldtag) {
101 			DEBUG(2, "\tQ busy %d pid %p can %d\n", t->busy, t->pid, t->canint);
102 			if(t->busy && t->pid) {
103 				t->flushtag = r->work.tag;
104 				DEBUG(2, "\tset flushtag %d\n", r->work.tag);
105 				if(t->canint)
106 					postnote(PNPROC, t->pid, "flush");
107 				r->busy = 0;
108 				return;
109 			}
110 		}
111 	}
112 
113 	reply(&r->work, &thdr, 0);
114 	DEBUG(2, "\tflush reply\n");
115 	r->busy = 0;
116 }
117 
118 void
Xattach(Fsrpc * r)119 Xattach(Fsrpc *r)
120 {
121 	Fcall thdr;
122 	Fid *f;
123 	vlong t;
124 
125 	t = nsec();
126 
127 	f = newfid(r->work.fid);
128 	if(f == 0) {
129 		reply(&r->work, &thdr, Ebadfid);
130 		r->busy = 0;
131 		return;
132 	}
133 
134 	f->f = root;
135 	thdr.qid = f->f->qid;
136 	reply(&r->work, &thdr, 0);
137 	r->busy = 0;
138 
139 	update(&stats->rpc[Tattach], t);
140 }
141 
142 void
Xwalk(Fsrpc * r)143 Xwalk(Fsrpc *r)
144 {
145 	char errbuf[ERRMAX], *err;
146 	Fcall thdr;
147 	Fid *f, *n;
148 	File *nf;
149 	vlong t;
150 	int i;
151 
152 	t = nsec();
153 
154 	f = getfid(r->work.fid);
155 	if(f == 0) {
156 		reply(&r->work, &thdr, Ebadfid);
157 		r->busy = 0;
158 		return;
159 	}
160 	n = nil;
161 	if(r->work.newfid != r->work.fid){
162 		n = newfid(r->work.newfid);
163 		if(n == 0) {
164 			reply(&r->work, &thdr, Edupfid);
165 			r->busy = 0;
166 			return;
167 		}
168 		n->f = f->f;
169 		f = n;	/* walk new guy */
170 	}
171 
172 	thdr.nwqid = 0;
173 	err = nil;
174 	for(i=0; i<r->work.nwname; i++){
175 		if(i >= MAXWELEM)
176 			break;
177 		if(strcmp(r->work.wname[i], "..") == 0) {
178 			if(f->f->parent == 0) {
179 				err = Exmnt;
180 				break;
181 			}
182 			f->f = f->f->parent;
183 			thdr.wqid[thdr.nwqid++] = f->f->qid;
184 			continue;
185 		}
186 
187 		nf = file(f->f, r->work.wname[i]);
188 		if(nf == 0) {
189 			errstr(errbuf, sizeof errbuf);
190 			err = errbuf;
191 			break;
192 		}
193 
194 		f->f = nf;
195 		thdr.wqid[thdr.nwqid++] = nf->qid;
196 		continue;
197 	}
198 
199 	if(err == nil && thdr.nwqid == 0 && r->work.nwname > 0)
200 		err = "file does not exist";
201 
202 	if(n != nil && (err != 0 || thdr.nwqid < r->work.nwname)){
203 		/* clunk the new fid, which is the one we walked */
204 		freefid(n->nr);
205 	}
206 
207 	if(thdr.nwqid > 0)
208 		err = nil;
209 	reply(&r->work, &thdr, err);
210 	r->busy = 0;
211 
212 	update(&stats->rpc[Twalk], t);
213 }
214 
215 void
Xclunk(Fsrpc * r)216 Xclunk(Fsrpc *r)
217 {
218 	Fcall thdr;
219 	Fid *f;
220 	vlong t;
221 	int fid;
222 
223 	t = nsec();
224 
225 	f = getfid(r->work.fid);
226 	if(f == 0) {
227 		reply(&r->work, &thdr, Ebadfid);
228 		r->busy = 0;
229 		return;
230 	}
231 
232 	if(f->fid >= 0)
233 		close(f->fid);
234 
235 	fid = r->work.fid;
236 	reply(&r->work, &thdr, 0);
237 	r->busy = 0;
238 
239 	update(&stats->rpc[Tclunk], t);
240 
241 	if(f->nread || f->nwrite)
242 		fidreport(f);
243 
244 	freefid(fid);
245 }
246 
247 void
Xstat(Fsrpc * r)248 Xstat(Fsrpc *r)
249 {
250 	char err[ERRMAX], path[128];
251 	uchar statbuf[STATMAX];
252 	Fcall thdr;
253 	Fid *f;
254 	int s;
255 	vlong t;
256 
257 	t = nsec();
258 
259 	f = getfid(r->work.fid);
260 	if(f == 0) {
261 		reply(&r->work, &thdr, Ebadfid);
262 		r->busy = 0;
263 		return;
264 	}
265 	makepath(path, f->f, "");
266 	if(!okfile(path, -1)){
267 		snprint(err, sizeof err, "iostats: can't simulate %s", path);
268 		reply(&r->work, &thdr, err);
269 		r->busy = 0;
270 		return;
271 	}
272 
273 	if(f->fid >= 0)
274 		s = fstat(f->fid, statbuf, sizeof statbuf);
275 	else
276 		s = stat(path, statbuf, sizeof statbuf);
277 
278 	if(s < 0) {
279 		errstr(err, sizeof err);
280 		reply(&r->work, &thdr, err);
281 		r->busy = 0;
282 		return;
283 	}
284 	thdr.stat = statbuf;
285 	thdr.nstat = s;
286 	reply(&r->work, &thdr, 0);
287 	r->busy = 0;
288 
289 	update(&stats->rpc[Tstat], t);
290 }
291 
292 void
Xcreate(Fsrpc * r)293 Xcreate(Fsrpc *r)
294 {
295 	char err[ERRMAX], path[128];
296 	Fcall thdr;
297 	Fid *f;
298 	File *nf;
299 	vlong t;
300 
301 	t = nsec();
302 
303 	f = getfid(r->work.fid);
304 	if(f == 0) {
305 		reply(&r->work, &thdr, Ebadfid);
306 		r->busy = 0;
307 		return;
308 	}
309 
310 
311 	makepath(path, f->f, r->work.name);
312 	f->fid = create(path, r->work.mode, r->work.perm);
313 	if(f->fid < 0) {
314 		errstr(err, sizeof err);
315 		reply(&r->work, &thdr, err);
316 		r->busy = 0;
317 		return;
318 	}
319 
320 	nf = file(f->f, r->work.name);
321 	if(nf == 0) {
322 		errstr(err, sizeof err);
323 		reply(&r->work, &thdr, err);
324 		r->busy = 0;
325 		return;
326 	}
327 
328 	f->mode = r->work.mode;
329 	f->f = nf;
330 	thdr.iounit = myiounit;
331 	thdr.qid = f->f->qid;
332 	reply(&r->work, &thdr, 0);
333 	r->busy = 0;
334 
335 	update(&stats->rpc[Tcreate], t);
336 }
337 
338 
339 void
Xremove(Fsrpc * r)340 Xremove(Fsrpc *r)
341 {
342 	char err[ERRMAX], path[128];
343 	Fcall thdr;
344 	Fid *f;
345 	vlong t;
346 
347 	t = nsec();
348 
349 	f = getfid(r->work.fid);
350 	if(f == 0) {
351 		reply(&r->work, &thdr, Ebadfid);
352 		r->busy = 0;
353 		return;
354 	}
355 
356 	makepath(path, f->f, "");
357 	DEBUG(2, "\tremove: %s\n", path);
358 	if(remove(path) < 0) {
359 		errstr(err, sizeof err);
360 		reply(&r->work, &thdr, err);
361 		freefid(r->work.fid);
362 		r->busy = 0;
363 		return;
364 	}
365 
366 	f->f->inval = 1;
367 	if(f->fid >= 0)
368 		close(f->fid);
369 	freefid(r->work.fid);
370 
371 	reply(&r->work, &thdr, 0);
372 	r->busy = 0;
373 
374 	update(&stats->rpc[Tremove], t);
375 }
376 
377 void
Xwstat(Fsrpc * r)378 Xwstat(Fsrpc *r)
379 {
380 	char err[ERRMAX], path[128];
381 	Fcall thdr;
382 	Fid *f;
383 	int s;
384 	vlong t;
385 
386 	t = nsec();
387 
388 	f = getfid(r->work.fid);
389 	if(f == 0) {
390 		reply(&r->work, &thdr, Ebadfid);
391 		r->busy = 0;
392 		return;
393 	}
394 	if(f->fid >= 0)
395 		s = fwstat(f->fid, r->work.stat, r->work.nstat);
396 	else {
397 		makepath(path, f->f, "");
398 		s = wstat(path, r->work.stat, r->work.nstat);
399 	}
400 	if(s < 0) {
401 		errstr(err, sizeof err);
402 		reply(&r->work, &thdr, err);
403 	}
404 	else
405 		reply(&r->work, &thdr, 0);
406 
407 	r->busy = 0;
408 	update(&stats->rpc[Twstat], t);
409 }
410 
411 void
slave(Fsrpc * f)412 slave(Fsrpc *f)
413 {
414 	int r;
415 	Proc *p;
416 	uintptr pid;
417 	static int nproc;
418 
419 	for(;;) {
420 		for(p = Proclist; p; p = p->next) {
421 			if(p->busy == 0) {
422 				f->pid = p->pid;
423 				p->busy = 1;
424 				pid = (uintptr)rendezvous((void*)p->pid, f);
425 				if(pid != p->pid)
426 					fatal("rendezvous sync fail");
427 				return;
428 			}
429 		}
430 
431 		if(++nproc > MAXPROC)
432 			fatal("too many procs");
433 
434 		r = rfork(RFPROC|RFMEM);
435 		if(r < 0)
436 			fatal("rfork");
437 
438 		if(r == 0)
439 			blockingslave();
440 
441 		p = malloc(sizeof(Proc));
442 		if(p == 0)
443 			fatal("out of memory");
444 
445 		p->busy = 0;
446 		p->pid = r;
447 		p->next = Proclist;
448 		Proclist = p;
449 
450 		rendezvous((void*)p->pid, p);
451 	}
452 }
453 
454 void
blockingslave(void)455 blockingslave(void)
456 {
457 	Proc *m;
458 	uintptr pid;
459 	Fsrpc *p;
460 	Fcall thdr;
461 
462 	notify(flushaction);
463 
464 	pid = getpid();
465 
466 	m = rendezvous((void*)pid, 0);
467 
468 	for(;;) {
469 		p = rendezvous((void*)pid, (void*)pid);
470 		if(p == (void*)~0)			/* Interrupted */
471 			continue;
472 
473 		DEBUG(2, "\tslave: %p %F b %d p %p\n", pid, &p->work, p->busy, p->pid);
474 		if(p->flushtag != NOTAG)
475 			return;
476 
477 		switch(p->work.type) {
478 		case Tread:
479 			slaveread(p);
480 			break;
481 		case Twrite:
482 			slavewrite(p);
483 			break;
484 		case Topen:
485 			slaveopen(p);
486 			break;
487 		default:
488 			reply(&p->work, &thdr, "exportfs: slave type error");
489 		}
490 		if(p->flushtag != NOTAG) {
491 			p->work.type = Tflush;
492 			p->work.tag = p->flushtag;
493 			reply(&p->work, &thdr, 0);
494 		}
495 		p->busy = 0;
496 		m->busy = 0;
497 	}
498 }
499 
500 void
slaveopen(Fsrpc * p)501 slaveopen(Fsrpc *p)
502 {
503 	char err[ERRMAX], path[128];
504 	Fcall *work, thdr;
505 	Fid *f;
506 	vlong t;
507 
508 	work = &p->work;
509 
510 	t = nsec();
511 
512 	f = getfid(work->fid);
513 	if(f == 0) {
514 		reply(work, &thdr, Ebadfid);
515 		return;
516 	}
517 	if(f->fid >= 0) {
518 		close(f->fid);
519 		f->fid = -1;
520 	}
521 
522 	makepath(path, f->f, "");
523 	DEBUG(2, "\topen: %s %d\n", path, work->mode);
524 
525 	p->canint = 1;
526 	if(p->flushtag != NOTAG)
527 		return;
528 
529 	if(!okfile(path, work->mode)){
530 		snprint(err, sizeof err, "iostats can't simulate %s", path);
531 		reply(work, &thdr, err);
532 		return;
533 	}
534 
535 	/* There is a race here I ignore because there are no locks */
536 	f->fid = open(path, work->mode);
537 	p->canint = 0;
538 	if(f->fid < 0) {
539 		errstr(err, sizeof err);
540 		reply(work, &thdr, err);
541 		return;
542 	}
543 
544 	DEBUG(2, "\topen: fd %d\n", f->fid);
545 	f->mode = work->mode;
546 	thdr.iounit = myiounit;
547 	thdr.qid = f->f->qid;
548 	reply(work, &thdr, 0);
549 
550 	update(&stats->rpc[Topen], t);
551 }
552 
553 void
slaveread(Fsrpc * p)554 slaveread(Fsrpc *p)
555 {
556 	char data[Maxfdata], err[ERRMAX];
557 	Fcall *work, thdr;
558 	Fid *f;
559 	int n, r;
560 	vlong t;
561 
562 	work = &p->work;
563 
564 	t = nsec();
565 
566 	f = getfid(work->fid);
567 	if(f == 0) {
568 		reply(work, &thdr, Ebadfid);
569 		return;
570 	}
571 
572 	n = (work->count > Maxfdata) ? Maxfdata : work->count;
573 	p->canint = 1;
574 	if(p->flushtag != NOTAG)
575 		return;
576 	/* can't just call pread, since directories must update the offset */
577 	if(f->f->qid.type&QTDIR){
578 		if(work->offset != f->offset){
579 			if(work->offset != 0){
580 				snprint(err, sizeof err, "can't seek in directory from %lld to %lld", f->offset, work->offset);
581 				reply(work, &thdr, err);
582 				return;
583 			}
584 			if(seek(f->fid, 0, 0) != 0){
585 				errstr(err, sizeof err);
586 				reply(work, &thdr, err);
587 				return;
588 			}
589 			f->offset = 0;
590 		}
591 		r = read(f->fid, data, n);
592 		if(r > 0)
593 			f->offset += r;
594 	}else
595 		r = pread(f->fid, data, n, work->offset);
596 	p->canint = 0;
597 	if(r < 0) {
598 		errstr(err, sizeof err);
599 		reply(work, &thdr, err);
600 		return;
601 	}
602 
603 	DEBUG(2, "\tread: fd=%d %d bytes\n", f->fid, r);
604 
605 	thdr.data = data;
606 	thdr.count = r;
607 	stats->totread += r;
608 	f->nread++;
609 	f->bread += r;
610 	reply(work, &thdr, 0);
611 
612 	update(&stats->rpc[Tread], t);
613 }
614 
615 void
slavewrite(Fsrpc * p)616 slavewrite(Fsrpc *p)
617 {
618 	char err[ERRMAX];
619 	Fcall *work, thdr;
620 	Fid *f;
621 	int n;
622 	vlong t;
623 
624 	work = &p->work;
625 
626 	t = nsec();
627 
628 	f = getfid(work->fid);
629 	if(f == 0) {
630 		reply(work, &thdr, Ebadfid);
631 		return;
632 	}
633 
634 	n = (work->count > Maxfdata) ? Maxfdata : work->count;
635 	p->canint = 1;
636 	if(p->flushtag != NOTAG)
637 		return;
638 	n = pwrite(f->fid, work->data, n, work->offset);
639 	p->canint = 0;
640 	if(n < 0) {
641 		errstr(err, sizeof err);
642 		reply(work, &thdr, err);
643 		return;
644 	}
645 
646 	DEBUG(2, "\twrite: %d bytes fd=%d\n", n, f->fid);
647 
648 	thdr.count = n;
649 	f->nwrite++;
650 	f->bwrite += n;
651 	stats->totwrite += n;
652 	reply(work, &thdr, 0);
653 
654 	update(&stats->rpc[Twrite], t);
655 }
656 
657 void
reopen(Fid * f)658 reopen(Fid *f)
659 {
660 	USED(f);
661 	fatal("reopen");
662 }
663 
664 void
flushaction(void * a,char * cause)665 flushaction(void *a, char *cause)
666 {
667 	USED(a);
668 	if(strncmp(cause, "kill", 4) == 0)
669 		noted(NDFLT);
670 
671 	noted(NCONT);
672 }
673