xref: /plan9/sys/src/cmd/disk/kfs/con.c (revision 9a747e4fd48b9f4522c70c07e8f882a15030f964)
1 #include	"all.h"
2 #include	"9p1.h"
3 
4 static	char	elem[NAMELEN];
5 static	Filsys*	cur_fs;
6 static	char	conline[100];
7 
8 void
9 consserve(void)
10 {
11 	con_session();
12 	cmd_exec("cfs");
13 	cmd_exec("user");
14 }
15 
16 int
17 cmd_exec(char *arg)
18 {
19 	char *s, *c;
20 	int i;
21 
22 	for(i=0; s = command[i].string; i++) {
23 		for(c=arg; *s; c++)
24 			if(*c != *s++)
25 				goto brk;
26 		if(*c == '\0' || *c == ' ' || *c == '\t'){
27 			cons.arg = c;
28 			(*command[i].func)();
29 			return 1;
30 		}
31 	brk:;
32 	}
33 	return 0;
34 }
35 
36 void
37 cmd_check(void)
38 {
39 	char *s;
40 	int flags;
41 
42 	flags = 0;
43 	for(s = cons.arg; *s; s++){
44 		while(*s == ' ' || *s == '\t')
45 			s++;
46 		if(*s == '\0')
47 			break;
48 		switch(*s){
49 		/* rebuild the free list */
50 		case 'f':	flags |= Cfree;			break;
51 		/* fix bad tags */
52 		case 't':	flags |= Ctag;			break;
53 		/* fix bad tags and clear the contents of the block */
54 		case 'c':	flags |= Cream;			break;
55 		/* delete all redundant references to a block */
56 		case 'd':	flags |= Cbad;			break;
57 		/* read and check tags on all blocks */
58 		case 'r':	flags |= Crdall;		break;
59 		/* write all of the blocks you touch */
60 		case 'w':	flags |= Ctouch;		break;
61 		/* print all directories as they are read */
62 		case 'p':	flags |= Cpdir;			break;
63 		/* print all files as they are read */
64 		case 'P':	flags |= Cpfile;		break;
65 		/* quiet, just report really nasty stuff */
66 		case 'q':	flags |= Cquiet;		break;
67 		}
68 	}
69 	check(cur_fs, flags);
70 }
71 
72 enum
73 {
74 	Sset	= (1<<0),
75 	Setc	= (1<<1),
76 };
77 void
78 cmd_stats(void)
79 {
80 	cprint("work stats\n");
81 	cprint("	work = %A rps\n", (Filta){&cons.work, 1});
82 	cprint("	rate = %A tBps\n", (Filta){&cons.rate, 1000});
83 	cprint("	hits = %A iops\n", (Filta){&cons.bhit, 1});
84 	cprint("	read = %A iops\n", (Filta){&cons.bread, 1});
85 	cprint("	init = %A iops\n", (Filta){&cons.binit, 1});
86 /*	for(i = 0; i < MAXTAG; i++)
87 		cprint("	tag %G = %A iops\n", i, (Filta){&cons.tags[i], 1});
88 */
89 }
90 
91 void
92 cmd_sync(void)
93 {
94 	rlock(&mainlock);
95 	syncall();
96 	runlock(&mainlock);
97 }
98 
99 void
100 cmd_halt(void)
101 {
102 	wlock(&mainlock);
103 	syncall();
104 	superok(cur_fs->dev, superaddr(cur_fs->dev), 1);
105 	print("kfs: file system halted\n");
106 }
107 
108 void
109 cmd_start(void)
110 {
111 	superok(cur_fs->dev, superaddr(cur_fs->dev), 0);
112 	wunlock(&mainlock);
113 	print("kfs: file system started\n");
114 }
115 
116 void
117 cmd_help(void)
118 {
119 	int i;
120 
121 	for(i=0; command[i].string; i++)
122 		cprint("	%s %s\n", command[i].string, command[i].args);
123 	cprint("check options:\n"
124 		" r	read all blocks\n"
125 		" f	rebuild the free list\n"
126 		" t	fix all bad tags\n"
127 		" c	fix bad tags and zero the blocks\n"
128 		" d	delete all redundant references to blocks\n"
129 		" p	print directories as they are checked\n"
130 		" P	print all files as they are checked\n"
131 		" w	write all blocks that are read\n");
132 }
133 
134 void
135 cmd_create(void)
136 {
137 	int uid, gid, err;
138 	long perm;
139 	char oelem[NAMELEN];
140 	char name[NAMELEN];
141 
142 	if(err = con_clone(FID1, FID2)){
143 		cprint("clone failed: %s\n", errstring[err]);
144 		return;
145 	}
146 	if(skipbl(1)){
147 		cprint("skipbl\n");
148 		return;
149 	}
150 	oelem[0] = 0;
151 	while(nextelem()) {
152 		if(oelem[0])
153 			if(err = con_walk(FID2, oelem)){
154 				cprint("walk failed: %s\n", errstring[err]);
155 				return;
156 			}
157 		memmove(oelem, elem, NAMELEN);
158 	}
159 	if(skipbl(1))
160 		return;
161 	uid = strtouid(cname(name));
162 	if(uid == 0){
163 		cprint("unknown user %s\n", name);
164 		return;
165 	}
166 	gid = strtouid(cname(name));
167 	if(gid == 0){
168 		cprint("unknown group %s\n", name);
169 		return;
170 	}
171 	perm = number(0777, 8);
172 	skipbl(0);
173 	for(; *cons.arg; cons.arg++){
174 		if(*cons.arg == 'l')
175 			perm |= PLOCK;
176 		else
177 		if(*cons.arg == 'a')
178 			perm |= PAPND;
179 		else
180 		if(*cons.arg == 'd')
181 			perm |= PDIR;
182 		else
183 			break;
184 	}
185 	err = con_create(FID2, elem, uid, gid, perm, 0);
186 	if(err)
187 		cprint("can't create %s: %s\n", elem, errstring[err]);
188 }
189 
190 void
191 cmd_clri(void)
192 {
193 	if(con_clone(FID1, FID2))
194 		return;
195 	if(skipbl(1))
196 		return;
197 	while(nextelem())
198 		if(con_walk(FID2, elem)){
199 			cprint("can't walk %s\n", elem);
200 			return;
201 		}
202 	con_clri(FID2);
203 }
204 
205 void
206 cmd_rename(void)
207 {
208 	ulong perm;
209 	Dentry d;
210 	char stat[DIRREC];
211 	char oelem[NAMELEN], noelem[NAMELEN], nxelem[NAMELEN];
212 	int err;
213 
214 	if(con_clone(FID1, FID2))
215 		return;
216 	if(skipbl(1))
217 		return;
218 	oelem[0] = 0;
219 	while(nextelem()) {
220 		if(oelem[0])
221 			if(con_walk(FID2, oelem)){
222 				cprint("file does not exits");
223 				return;
224 			}
225 		memmove(oelem, elem, NAMELEN);
226 	}
227 	if(skipbl(1))
228 		return;
229 	if(cons.arg[0]=='/'){
230 		if(con_clone(FID1, FID3))
231 			return;
232 		noelem[0] = 0;
233 		while(nextelem()){
234 			if(noelem[0])
235 				if(con_walk(FID3, noelem)){
236 					cprint("target path %s does not exist", noelem);
237 					return;
238 				}
239 			memmove(noelem, elem, NAMELEN);
240 		}
241 		if(!con_walk(FID3, elem)){
242 			cprint("target %s already exists\n", elem);
243 			return;
244 		}
245 		if(con_walk(FID2, oelem)){
246 			cprint("src %s does not exist\n", oelem);
247 			return;
248 		}
249 		/*
250 		 * we know the target does not exist,
251 		 * the source does exist.
252 		 * to do the rename, create the target and then
253 		 * copy the directory entry directly.  then remove the source.
254 		 */
255 		if(err = con_stat(FID2, stat)){
256 			cprint("can't stat file: %s\n", errstring[err]);
257 			return;
258 		}
259 		convM2D9p1(stat, &d);
260 		perm = (d.mode&0777)|((d.mode&0x7000)<<17);
261 		if(err = con_create(FID3, elem, d.uid, d.gid, perm, (d.mode&DDIR)?OREAD:ORDWR)){
262 			cprint("can't create %s: %s\n", elem, errstring[err]);
263 			return;
264 		}
265 		if(err = con_swap(FID2, FID3)){
266 			cprint("can't swap data: %s\n", errstring[err]);
267 			return;
268 		}
269 		if(err = con_remove(FID2)){
270 			cprint("can't remove file: %s\n", errstring[err]);
271 			return;
272 		}
273 	}else{
274 		cname(nxelem);
275 		if(strchr(nxelem, '/')){
276 			cprint("bad rename target: not full path, but contains slashes\n");
277 			return;
278 		}
279 		if(!con_walk(FID2, nxelem))
280 			cprint("file %s already exists\n", nxelem);
281 		else if(con_walk(FID2, oelem))
282 			cprint("file does not already exist\n");
283 		else if(err = con_stat(FID2, stat))
284 			cprint("can't stat file: %s\n", errstring[err]);
285 		else{
286 			convM2D9p1(stat, &d);
287 			strncpy(d.name, nxelem, NAMELEN);
288 			convD2M9p1(&d, stat);
289 			if(err = con_wstat(FID2, stat))
290 				cprint("can't move file: %s\n", errstring[err]);
291 		}
292 	}
293 }
294 
295 void
296 cmd_remove(void)
297 {
298 	if(con_clone(FID1, FID2))
299 		return;
300 	if(skipbl(1))
301 		return;
302 	while(nextelem())
303 		if(con_walk(FID2, elem)){
304 			cprint("can't walk %s\n", elem);
305 			return;
306 		}
307 	con_remove(FID2);
308 }
309 
310 void
311 cmd_cfs(void)
312 {
313 	Filsys *fs;
314 
315 	if(*cons.arg != ' ') {
316 		fs = &filesys[0];		/* default */
317 	} else {
318 		if(skipbl(1)){
319 			cprint("skipbl\n");
320 			return;
321 		}
322 		if(!nextelem())
323 			fs = &filesys[0];	/* default */
324 		else
325 			fs = fsstr(elem);
326 	}
327 	if(fs == 0) {
328 		cprint("unknown file system %s\n", elem);
329 		return;
330 	}
331 	if(con_attach(FID1, "adm", fs->name))
332 		panic("FID1 attach to root");
333 	cur_fs = fs;
334 }
335 
336 /*
337  * find out the length of a file
338  * given the mesg version of a stat buffer
339  * we call this because convM2D is different
340  * for the file system than in the os
341  */
342 static uvlong
343 statlen(char *ap)
344 {
345 	uchar *p;
346 	ulong ll, hl;
347 
348 	p = (uchar*)ap;
349 	p += 3*28+5*4;
350 	ll = p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24);
351 	hl = p[4] | (p[5]<<8) | (p[6]<<16) | (p[7]<<24);
352 	return ll | ((uvlong) hl << 32);
353 }
354 
355 int
356 adduser(char *user)
357 {
358 	char stat[DIRREC];
359 	char msg[100];
360 	Uid *u;
361 	int i, c, nu;
362 
363 	/*
364 	 * check uniq of name
365 	 * and get next uid
366 	 */
367 	cmd_exec("cfs");
368 	cmd_exec("user");
369 	nu = 0;
370 	for(i=0, u=uid; i<conf.nuid; i++,u++) {
371 		c = u->uid;
372 		if(c == 0)
373 			break;
374 		if(strcmp(uidspace+u->offset, user) == 0)
375 			return 1;
376 		if(c >= 9000)
377 			continue;
378 		if(c > nu)
379 			nu = c;
380 	}
381 	nu++;
382 	if(nu >= 9000) {
383 		cprint("out of user ids\n");
384 		return 0;
385 	}
386 
387 	/*
388 	 * write onto adm/users
389 	 */
390 	if(con_clone(FID1, FID2)
391 	|| con_path(FID2, "/adm/users")
392 	|| con_open(FID2, 1)) {
393 		cprint("can't open /adm/users\n");
394 		return 0;
395 	}
396 
397 	sprint(msg, "%d:%s:%s:\n", nu, user, user);
398 	cprint("add user '%s'", msg);
399 	c = strlen(msg);
400 	i = con_stat(FID2, stat);
401 	if(i){
402 		cprint("can't stat /adm/users: %s\n", errstring[i]);
403 		return 0;
404 	}
405 	i = con_write(FID2, msg, statlen(stat), c);
406 	if(i != c){
407 		cprint("short write on /adm/users: %d %d\n", c, i);
408 		return 0;
409 	}
410 	return 1;
411 }
412 
413 void
414 cmd_newuser(void)
415 {
416 	char user[NAMELEN], msg[100];
417 	int i, c;
418 
419 	/*
420 	 * get uid
421 	 */
422 	cname(user);
423 	for(i=0; i<NAMELEN; i++) {
424 		c = user[i];
425 		if(c == 0)
426 			break;
427 		if(c >= '0' && c <= '9'
428 		|| c >= 'a' && c <= 'z'
429 		|| c >= 'A' && c <= 'Z')
430 			continue;
431 		cprint("bad character in name: 0x%x\n", c);
432 		return;
433 	}
434 	if(i < 2) {
435 		cprint("name too short: %s\n", user);
436 		return;
437 	}
438 	if(i >= NAMELEN) {
439 		cprint("name too long: %s\n", user);
440 		return;
441 	}
442 
443 	/*
444 	 * install and create directory
445 	 */
446 	if(!adduser(user))
447 		return;
448 
449 	cmd_exec("user");
450 	sprint(msg, "create /usr/%s %s %s 775 d", user, user, user);
451 	cmd_exec(msg);
452 	sprint(msg, "create /usr/%s/tmp %s %s 775 d", user, user, user);
453 	cmd_exec(msg);
454 	sprint(msg, "create /usr/%s/lib %s %s 775 d", user, user, user);
455 	cmd_exec(msg);
456 	sprint(msg, "create /usr/%s/bin %s %s 775 d", user, user, user);
457 	cmd_exec(msg);
458 	sprint(msg, "create /usr/%s/bin/rc %s %s 775 d", user, user, user);
459 	cmd_exec(msg);
460 	sprint(msg, "create /usr/%s/bin/mips %s %s 775 d", user, user, user);
461 	cmd_exec(msg);
462 	sprint(msg, "create /usr/%s/bin/386 %s %s 775 d", user, user, user);
463 	cmd_exec(msg);
464 	sprint(msg, "create /usr/%s/bin/power %s %s 775 d", user, user, user);
465 	cmd_exec(msg);
466 	sprint(msg, "create /usr/%s/bin/alpha %s %s 775 d", user, user, user);
467 	cmd_exec(msg);
468 }
469 
470 void
471 cmd_checkuser(void)
472 {
473 	uchar buf[DIRREC], *p;
474 	static char utime[4];
475 
476 	if(con_clone(FID1, FID2)
477 	|| con_path(FID2, "/adm/users")
478 	|| con_open(FID2, 0)
479 	|| con_stat(FID2, (char*)buf))
480 		return;
481 	p = buf + 3*NAMELEN + 4*4;
482 	if(memcmp(utime, p, 4) == 0)
483 		return;
484 	memmove(utime, p, 4);
485 	cmd_user();
486 }
487 
488 void
489 cmd_allow(void)
490 {
491 	wstatallow = 1;
492 }
493 
494 void
495 cmd_disallow(void)
496 {
497 	wstatallow = 0;
498 }
499 
500 void
501 cmd_chat(void)
502 {
503 	chat = 1 - chat;
504 }
505 
506 void
507 cmd_atime(void)
508 {
509 	noatime = !noatime;
510 	if(noatime)
511 		cprint("atimes will not be updated\n");
512 	else
513 		cprint("atimes will be updated\n");
514 }
515 
516 void
517 cmd_noneattach(void)
518 {
519 	allownone = !allownone;
520 	if(allownone)
521 		cprint("none can attach to new connections\n");
522 	else
523 		cprint("none can only attach on authenticated connections\n");
524 }
525 
526 void
527 cmd_listen(void)
528 {
529 	char addr[NAMELEN];
530 
531 	if(skipbl(0))
532 		strcpy(addr, "il!*!17008");
533 	else
534 		cname(addr);
535 
536 	if(netserve(addr))
537 		cprint("announce %s failed\n", addr);
538 	else
539 		cprint("announce %s\n", addr);
540 }
541 
542 Command	command[] =
543 {
544 	"allow",	cmd_allow,	"",
545 	"allowoff",	cmd_disallow,	"",
546 	"atime",		cmd_atime,	"",
547 	"cfs",		cmd_cfs,	"[filesys]",
548 	"chat",		cmd_chat,	"",
549 	"check",	cmd_check,	"[rftRdPpw]",
550 	"clri",		cmd_clri,	"filename",
551 	"create",	cmd_create,	"filename user group perm [ald]",
552 	"disallow",	cmd_disallow,	"",
553 	"halt",		cmd_halt,	"",
554 	"help",		cmd_help,	"",
555 	"listen",		cmd_listen,	"[address]",
556 	"newuser",	cmd_newuser,	"username",
557 	"noneattach",	cmd_noneattach,	"",
558 	"remove",	cmd_remove,	"filename",
559 	"rename",	cmd_rename,	"file newname",
560 	"start",	cmd_start, "",
561 	"stats",	cmd_stats,	"[fw]",
562 	"sync",		cmd_sync,	"",
563 	"user",		cmd_user,	"",
564 	0
565 };
566 
567 int
568 skipbl(int err)
569 {
570 	if(*cons.arg != ' ') {
571 		if(err)
572 			cprint("syntax error\n");
573 		return 1;
574 	}
575 	while(*cons.arg == ' ')
576 		cons.arg++;
577 	return 0;
578 }
579 
580 char*
581 _cname(char *name)
582 {
583 	int i, c;
584 
585 	memset(name, 0, NAMELEN);
586 	for(i=0;; i++) {
587 		c = *cons.arg;
588 		switch(c) {
589 		case ' ':
590 		case '\t':
591 		case '\n':
592 		case '\0':
593 			return name;
594 		}
595 		if(i < NAMELEN-1)
596 			name[i] = c;
597 		cons.arg++;
598 	}
599 	return 0;
600 }
601 
602 char*
603 cname(char *name)
604 {
605 	skipbl(0);
606 	return _cname(name);
607 }
608 
609 int
610 nextelem(void)
611 {
612 	char *e;
613 	int i, c;
614 
615 	e = elem;
616 	while(*cons.arg == '/')
617 		cons.arg++;
618 	c = *cons.arg;
619 	if(c == 0 || c == ' ')
620 		return 0;
621 	for(i = 0; c = *cons.arg; i++) {
622 		if(c == ' ' || c == '/')
623 			break;
624 		if(i == NAMELEN) {
625 			cprint("path name component too long\n");
626 			return 0;
627 		}
628 		*e++ = c;
629 		cons.arg++;
630 	}
631 	*e = 0;
632 	return 1;
633 }
634 
635 long
636 number(int d, int base)
637 {
638 	int c, sign, any;
639 	long n;
640 
641 	sign = 0;
642 	any = 0;
643 	n = 0;
644 
645 	c = *cons.arg;
646 	while(c == ' ') {
647 		cons.arg++;
648 		c = *cons.arg;
649 	}
650 	if(c == '-') {
651 		sign = 1;
652 		cons.arg++;
653 		c = *cons.arg;
654 	}
655 	while((c >= '0' && c <= '9') ||
656 	      (base == 16 && c >= 'a' && c <= 'f') ||
657 	      (base == 16 && c >= 'A' && c <= 'F')) {
658 		n *= base;
659 		if(c >= 'a' && c <= 'f')
660 			n += c - 'a' + 10;
661 		else
662 		if(c >= 'A' && c <= 'F')
663 			n += c - 'A' + 10;
664 		else
665 			n += c - '0';
666 		cons.arg++;
667 		c = *cons.arg;
668 		any = 1;
669 	}
670 	if(!any)
671 		return d;
672 	if(sign)
673 		n = -n;
674 	return n;
675 }
676