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
consserve(void)9 consserve(void)
10 {
11 con_session();
12 cmd_exec("cfs");
13 cmd_exec("user");
14 }
15
16 int
cmd_exec(char * arg)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
cmd_check(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
cmd_stats(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
cmd_sync(void)92 cmd_sync(void)
93 {
94 rlock(&mainlock);
95 syncall();
96 runlock(&mainlock);
97 }
98
99 void
cmd_halt(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
cmd_start(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
cmd_help(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
cmd_create(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
cmd_clri(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
cmd_rename(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
cmd_remove(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
cmd_cfs(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
statlen(char * ap)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
adduser(char * user,int isgroup)356 adduser(char *user, int isgroup)
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 if(isgroup)
370 nu = 9000;
371 else
372 nu = 0;
373 for(i=0, u=uid; i<conf.nuid; i++,u++) {
374 c = u->uid;
375 if(c == 0)
376 break;
377 if(strcmp(uidspace+u->offset, user) == 0)
378 return 1;
379 if(c >= 9000 && !isgroup)
380 continue;
381 if(c > nu)
382 nu = c;
383 }
384 nu++;
385 if(isgroup){
386 if(nu >= 0x10000) {
387 cprint("out of group ids\n");
388 return 0;
389 }
390 } else {
391 if(nu >= 9000) {
392 cprint("out of user ids\n");
393 return 0;
394 }
395 }
396
397 /*
398 * write onto adm/users
399 */
400 if(con_clone(FID1, FID2)
401 || con_path(FID2, "/adm/users")
402 || con_open(FID2, 1)) {
403 cprint("can't open /adm/users\n");
404 return 0;
405 }
406
407 sprint(msg, "%d:%s:%s:\n", nu, user, user);
408 cprint("add user %s", msg);
409 c = strlen(msg);
410 i = con_stat(FID2, stat);
411 if(i){
412 cprint("can't stat /adm/users: %s\n", errstring[i]);
413 return 0;
414 }
415 i = con_write(FID2, msg, statlen(stat), c);
416 if(i != c){
417 cprint("short write on /adm/users: %d %d\n", c, i);
418 return 0;
419 }
420 return 1;
421 }
422
423 void
cmd_newuser(void)424 cmd_newuser(void)
425 {
426 char user[NAMELEN], param[NAMELEN], msg[100];
427 int i, c;
428
429 /*
430 * get uid
431 */
432 cname(user);
433 cname(param);
434 for(i=0; i<NAMELEN; i++) {
435 c = user[i];
436 if(c == 0)
437 break;
438 if(c >= '0' && c <= '9'
439 || c >= 'a' && c <= 'z'
440 || c >= 'A' && c <= 'Z')
441 continue;
442 cprint("bad character in name: 0x%x\n", c);
443 return;
444 }
445 if(i < 2) {
446 cprint("name too short: %s\n", user);
447 return;
448 }
449 if(i >= NAMELEN) {
450 cprint("name too long: %s\n", user);
451 return;
452 }
453
454 switch(param[0]){
455 case 0:
456 if(!adduser(user, 0))
457 return;
458 cmd_exec("user");
459 break;
460 case ':':
461 adduser(user, 1);
462 cmd_exec("user");
463 return;
464 case '#':
465 adduser(user, 0);
466 cmd_exec("user");
467 return;
468 }
469
470 /*
471 * create directories
472 */
473 cmd_exec("user");
474 sprint(msg, "create /usr/%s %s %s 775 d", user, user, user);
475 cmd_exec(msg);
476 sprint(msg, "create /usr/%s/tmp %s %s 775 d", user, user, user);
477 cmd_exec(msg);
478 sprint(msg, "create /usr/%s/lib %s %s 775 d", user, user, user);
479 cmd_exec(msg);
480 sprint(msg, "create /usr/%s/bin %s %s 775 d", user, user, user);
481 cmd_exec(msg);
482 sprint(msg, "create /usr/%s/bin/rc %s %s 775 d", user, user, user);
483 cmd_exec(msg);
484 sprint(msg, "create /usr/%s/bin/mips %s %s 775 d", user, user, user);
485 cmd_exec(msg);
486 sprint(msg, "create /usr/%s/bin/386 %s %s 775 d", user, user, user);
487 cmd_exec(msg);
488 sprint(msg, "create /usr/%s/bin/power %s %s 775 d", user, user, user);
489 cmd_exec(msg);
490 sprint(msg, "create /usr/%s/bin/alpha %s %s 775 d", user, user, user);
491 cmd_exec(msg);
492 }
493
494 void
cmd_checkuser(void)495 cmd_checkuser(void)
496 {
497 uchar buf[DIRREC], *p;
498 static char utime[4];
499
500 if(con_clone(FID1, FID2)
501 || con_path(FID2, "/adm/users")
502 || con_open(FID2, 0)
503 || con_stat(FID2, (char*)buf))
504 return;
505 p = buf + 3*NAMELEN + 4*4;
506 if(memcmp(utime, p, 4) == 0)
507 return;
508 memmove(utime, p, 4);
509 cmd_user();
510 }
511
512 void
cmd_allow(void)513 cmd_allow(void)
514 {
515 wstatallow = 1;
516 }
517
518 void
cmd_disallow(void)519 cmd_disallow(void)
520 {
521 wstatallow = 0;
522 }
523
524 void
cmd_chat(void)525 cmd_chat(void)
526 {
527 chat = 1 - chat;
528 }
529
530 void
cmd_atime(void)531 cmd_atime(void)
532 {
533 noatime = !noatime;
534 if(noatime)
535 cprint("atimes will not be updated\n");
536 else
537 cprint("atimes will be updated\n");
538 }
539
540 void
cmd_noneattach(void)541 cmd_noneattach(void)
542 {
543 allownone = !allownone;
544 if(allownone)
545 cprint("none can attach to new connections\n");
546 else
547 cprint("none can only attach on authenticated connections\n");
548 }
549
550 void
cmd_listen(void)551 cmd_listen(void)
552 {
553 char addr[NAMELEN];
554
555 if(skipbl(0))
556 strcpy(addr, "tcp!*!564"); /* 9fs */
557 else
558 cname(addr);
559
560 if(netserve(addr))
561 cprint("announce %s failed\n", addr);
562 else
563 cprint("announce %s\n", addr);
564 }
565
566 void
cmd_nowritegroup(void)567 cmd_nowritegroup(void)
568 {
569 writegroup = 0;
570 }
571
572 Command command[] =
573 {
574 "allow", cmd_allow, "",
575 "allowoff", cmd_disallow, "",
576 "atime", cmd_atime, "",
577 "cfs", cmd_cfs, "[filesys]",
578 "chat", cmd_chat, "",
579 "check", cmd_check, "[cdfpPqrtw]",
580 "clri", cmd_clri, "filename",
581 "create", cmd_create, "filename user group perm [ald]",
582 "disallow", cmd_disallow, "",
583 "halt", cmd_halt, "",
584 "help", cmd_help, "",
585 "listen", cmd_listen, "[address]",
586 "newuser", cmd_newuser, "username",
587 "noneattach", cmd_noneattach, "",
588 "nowritegroup", cmd_nowritegroup, "",
589 "remove", cmd_remove, "filename",
590 "rename", cmd_rename, "file newname",
591 "start", cmd_start, "",
592 "stats", cmd_stats, "[fw]",
593 "sync", cmd_sync, "",
594 "user", cmd_user, "",
595 0
596 };
597
598 int
skipbl(int err)599 skipbl(int err)
600 {
601 if(*cons.arg != ' ') {
602 if(err)
603 cprint("syntax error\n");
604 return 1;
605 }
606 while(*cons.arg == ' ')
607 cons.arg++;
608 return 0;
609 }
610
611 char*
_cname(char * name)612 _cname(char *name)
613 {
614 int i, c;
615
616 memset(name, 0, NAMELEN);
617 for(i=0;; i++) {
618 c = *cons.arg;
619 switch(c) {
620 case ' ':
621 case '\t':
622 case '\n':
623 case '\0':
624 return name;
625 }
626 if(i < NAMELEN-1)
627 name[i] = c;
628 cons.arg++;
629 }
630 }
631
632 char*
cname(char * name)633 cname(char *name)
634 {
635 skipbl(0);
636 return _cname(name);
637 }
638
639 int
nextelem(void)640 nextelem(void)
641 {
642 char *e;
643 int i, c;
644
645 e = elem;
646 while(*cons.arg == '/')
647 cons.arg++;
648 c = *cons.arg;
649 if(c == 0 || c == ' ')
650 return 0;
651 for(i = 0; c = *cons.arg; i++) {
652 if(c == ' ' || c == '/')
653 break;
654 if(i == NAMELEN) {
655 cprint("path name component too long\n");
656 return 0;
657 }
658 *e++ = c;
659 cons.arg++;
660 }
661 *e = 0;
662 return 1;
663 }
664
665 long
number(int d,int base)666 number(int d, int base)
667 {
668 int c, sign, any;
669 long n;
670
671 sign = 0;
672 any = 0;
673 n = 0;
674
675 c = *cons.arg;
676 while(c == ' ') {
677 cons.arg++;
678 c = *cons.arg;
679 }
680 if(c == '-') {
681 sign = 1;
682 cons.arg++;
683 c = *cons.arg;
684 }
685 while((c >= '0' && c <= '9') ||
686 (base == 16 && c >= 'a' && c <= 'f') ||
687 (base == 16 && c >= 'A' && c <= 'F')) {
688 n *= base;
689 if(c >= 'a' && c <= 'f')
690 n += c - 'a' + 10;
691 else
692 if(c >= 'A' && c <= 'F')
693 n += c - 'A' + 10;
694 else
695 n += c - '0';
696 cons.arg++;
697 c = *cons.arg;
698 any = 1;
699 }
700 if(!any)
701 return d;
702 if(sign)
703 n = -n;
704 return n;
705 }
706