]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/cwfs/uidgid.c
webfs(4): document -d and -D flags
[plan9front.git] / sys / src / cmd / cwfs / uidgid.c
1 #include "all.h"
2
3 struct {
4         char*   name;
5         Userid  uid;
6         Userid  lead;
7 } minusers[] = {
8         "adm",          -1,     -1,
9         "none",         0,      -1,
10         "tor",          1,      1,
11         "sys",          10000,  0,
12         "map",          10001,  10001,
13         "doc",          10002,  0,
14         "upas",         10003,  10003,
15         "font",         10004,  0,
16         "bootes",       10005,  10005,
17         0
18 };
19
20 static char buf[4096];
21 static Rune ichar[] = L"?=+-/:";
22
23 Uid*    chkuid(char *name, int chk);
24 void    do_newuser(int, char*[]);
25 char*   getword(char*, Rune, char*, int);
26 void    pentry(char*, Uid*);
27 int     readln(char*, int);
28 void    setminusers(void);
29 Uid*    uidtop(int);
30
31 void
32 cmd_users(int argc, char *argv[])
33 {
34         Uid *ui;
35         int u, g, o, line;
36         char *file, *p, *uname, *ulead, *unext;
37
38         file = "/adm/users";
39         if(argc > 1)
40                 file = argv[1];
41
42         if(strcmp(file, "default") == 0) {
43                 setminusers();
44                 return;
45         }
46
47         uidgc.uidbuf = getbuf(devnone, Cuidbuf, 0);
48         if(walkto(file) || con_open(FID2, 0)) {
49                 print("cmd_users: cannot access %s\n", file);
50                 putbuf(uidgc.uidbuf);
51                 return;
52         }
53
54         uidgc.flen = 0;
55         uidgc.find = 0;
56         cons.offset = 0;
57         cons.nuid = 0;
58
59         u = 0;
60         line = 0;
61         while(readln(buf, sizeof buf) != 0) {
62                 line++;
63                 p = getword(buf, L':', "no : after number", line);
64                 if(p == nil)
65                         continue;
66                 ulead = getword(p, L':', "no : after name", line);
67                 if(ulead == nil)
68                         continue;
69
70                 if(strlen(p) > NAMELEN-1) {
71                         print("%s: name too long\n", p);
72                         continue;
73                 }
74                 strcpy(uid[u].name, p);
75                 uid[u].uid = number(buf, 0, 10);
76                 uid[u].lead = 0;
77                 uid[u].ngrp = 0;
78                 u++;
79                 if(u >= conf.nuid) {
80                         print("conf.nuid too small (%ld)\n", conf.nuid);
81                         break;
82                 }
83         }
84
85         /* Sorted by uid for use in uidtostr */
86         wlock(&uidgc.uidlock);
87         qsort(uid, u, sizeof(uid[0]), byuid);
88         cons.nuid = u;
89         wunlock(&uidgc.uidlock);
90
91         /* Parse group table */
92         uidgc.flen = 0;
93         uidgc.find = 0;
94         cons.offset = 0;
95         cons.ngid = 0;
96
97         g = 0;
98         line = 0;
99         while(readln(buf, sizeof buf) != 0) {
100                 line++;
101                 uname = getword(buf, L':', 0, 0);       /* skip number */
102                 if(uname == nil)
103                         continue;
104
105                 ulead = getword(uname, L':', 0, 0);     /* skip name */
106                 if(ulead == nil)
107                         continue;
108
109                 p = getword(ulead, L':', "no : after leader", line);
110                 if(p == nil)
111                         continue;
112
113                 ui = uidpstr(uname);
114                 if(ui == nil)
115                         continue;
116
117                 /* set to owner if name not known */
118                 ui->lead = 0;
119                 if(ulead[0]) {
120                         o = strtouid(ulead);
121                         if(o >= 0)
122                                 ui->lead = o;
123                         else
124                                 ui->lead = ui->uid;
125                 }
126                 ui->gtab = &gidspace[g];
127                 ui->ngrp = 0;
128                 while (p != nil) {
129                         unext = getword(p, L',', 0, 0);
130                         o = strtouid(p);
131                         if(o >= 0) {
132                                 gidspace[g++] = o;
133                                 ui->ngrp++;
134                         }
135                         p = unext;
136                 }
137         }
138
139         cons.ngid = g;
140
141         putbuf(uidgc.uidbuf);
142         print("%d uids read, %d groups used\n", cons.nuid, cons.ngid);
143 }
144
145 void
146 cmd_newuser(int argc, char *argv[])
147 {
148         if(argc <= 1) {
149                 print("usage: newuser args\n");
150                 print("\tname -- create a new user\n");
151                 print("\tname : -- create a new group\n");
152                 print("\tname ? -- show entry for user\n");
153                 print("\tname name -- rename\n");
154                 print("\tname =[name] -- add/alter/remove leader\n");
155                 print("\tname +name -- add member\n");
156                 print("\tname -name -- delete member\n");
157                 return;
158         }
159         do_newuser(argc, argv);
160 }
161
162 void
163 do_newuser(int argc, char *argv[])
164 {
165         int i, l, n, nuid;
166         char *p, *md, *q;
167         Rune *r;
168         Userid *s;
169         Uid *ui, *u2;
170
171         nuid = 10000;
172         md = 0;
173         if(argc == 2) {
174                 nuid = 1;
175                 argv[2] = ":";
176         }
177
178         for(r = ichar; *r; r++)
179                 if(utfrune(argv[1], *r)) {
180                         print("illegal character in name\n");
181                         return;
182                 }
183         if(strlen(argv[1]) > NAMELEN-1) {
184                 print("name %s too long\n", argv[1]);
185                 return;
186         }
187
188         p = argv[2];
189         switch(*p) {
190         case '?':
191                 ui = chkuid(argv[1], 1);
192                 if(ui == 0)
193                         return;
194                 pentry(buf, ui);
195                 n = strlen(buf);
196                 p = buf;
197                 while(n > PRINTSIZE-5) {
198                         q = p;
199                         p += PRINTSIZE-5;
200                         n -= PRINTSIZE-5;
201                         i = *p;
202                         *p = 0;
203                         print("%s", q);
204                         *p = i;
205                 }
206                 print("%s\n", p);
207                 return;
208
209         case ':':
210                 if(chkuid(argv[1], 0))
211                         return;
212                 while(uidtop(nuid) != 0)
213                         nuid++;
214                 if(cons.nuid >= conf.nuid) {
215                         print("conf.nuid too small (%ld)\n", conf.nuid);
216                         return;
217                 }
218
219                 wlock(&uidgc.uidlock);
220                 ui = &uid[cons.nuid++];
221                 ui->uid = nuid;
222                 ui->lead = 0;
223                 if(nuid < 10000) {
224                         ui->lead = ui->uid;
225                         md = argv[1];
226                 }
227                 strcpy(ui->name, argv[1]);
228                 ui->ngrp = 0;
229                 qsort(uid, cons.nuid, sizeof(uid[0]), byuid);
230                 wunlock(&uidgc.uidlock);
231                 break;
232
233         case '=':
234                 ui = chkuid(argv[1], 1);
235                 if(ui == 0)
236                         return;
237                 p++;
238                 if(*p == '\0') {
239                         ui->lead = 0;
240                         break;
241                 }
242                 u2 = chkuid(p, 1);
243                 if(u2 == 0)
244                         return;
245                 ui->lead = u2->uid;
246                 break;
247
248         case '+':
249                 ui = chkuid(argv[1], 1);
250                 if(ui == 0)
251                         return;
252                 p++;
253                 u2 = chkuid(p, 1);
254                 if(u2 == 0)
255                         return;
256                 if(u2->uid == ui->uid)
257                         return;
258                 if(cons.ngid+ui->ngrp+1 >= conf.gidspace) {
259                         print("conf.gidspace too small (%ld)\n", conf.gidspace);
260                         return;
261                 }
262                 for(i = 0; i < ui->ngrp; i++) {
263                         if(ui->gtab[i] == u2->uid) {
264                                 print("member already in group\n");
265                                 return;
266                         }
267                 }
268
269                 wlock(&uidgc.uidlock);
270                 s = gidspace+cons.ngid;
271                 memmove(s, ui->gtab, ui->ngrp*sizeof(*s));
272                 ui->gtab = s;
273                 s[ui->ngrp++] = u2->uid;
274                 cons.ngid += ui->ngrp+1;
275                 wunlock(&uidgc.uidlock);
276                 break;
277
278         case '-':
279                 ui = chkuid(argv[1], 1);
280                 if(ui == 0)
281                         return;
282                 p++;
283                 u2 = chkuid(p, 1);
284                 if(u2 == 0)
285                         return;
286                 for(i = 0; i < ui->ngrp; i++)
287                         if(ui->gtab[i] == u2->uid)
288                                 break;
289
290                 if(i == ui->ngrp) {
291                         print("%s not in group\n", p);
292                         return;
293                 }
294
295                 wlock(&uidgc.uidlock);
296                 s = ui->gtab+i;
297                 ui->ngrp--;
298                 memmove(s, s+1, (ui->ngrp-i)*sizeof(*s));
299                 wunlock(&uidgc.uidlock);
300                 break;
301
302         default:
303                 if(chkuid(argv[2], 0))
304                         return;
305
306                 for(r = ichar; *r; r++)
307                         if(utfrune(argv[2], *r)) {
308                                 print("illegal character in name\n");
309                                 return;
310                         }
311
312                 ui = chkuid(argv[1], 1);
313                 if(ui == 0)
314                         return;
315
316                 if(strlen(argv[2]) > NAMELEN-1) {
317                         print("name %s too long\n", argv[2]);
318                         return;
319                 }
320
321                 wlock(&uidgc.uidlock);
322                 strcpy(ui->name, argv[2]);
323                 wunlock(&uidgc.uidlock);
324                 break;
325         }
326
327
328         if(walkto("/adm/users") || con_open(FID2, OWRITE|OTRUNC)) {
329                 print("can't open /adm/users for write\n");
330                 return;
331         }
332
333         cons.offset = 0;
334         for(i = 0; i < cons.nuid; i++) {
335                 pentry(buf, &uid[i]);
336                 l = strlen(buf);
337                 n = con_write(FID2, buf, cons.offset, l);
338                 if(l != n)
339                         print("short write on /adm/users\n");
340                 cons.offset += n;
341         }
342
343         if(md != 0) {
344                 sprint(buf, "create /usr/%s %s %s 755 d", md, md, md);
345                 print("%s\n", buf);
346                 cmd_exec(buf);
347         }
348 }
349
350 Uid*
351 chkuid(char *name, int chk)
352 {
353         Uid *u;
354
355         u = uidpstr(name);
356         if(chk == 1) {
357                 if(u == 0)
358                         print("%s does not exist\n", name);
359         }
360         else {
361                 if(u != 0)
362                         print("%s already exists\n", name);
363         }
364         return u;
365 }
366
367 void
368 pentry(char *buf, Uid *u)
369 {
370         int i, posn;
371         Uid *p;
372
373         posn = sprint(buf, "%d:%s:", u->uid, u->name);
374         p = uidtop(u->lead);
375         if(p && u->lead != 0)
376                 posn += sprint(buf+posn, "%s", p->name);
377
378         posn += sprint(buf+posn, ":");
379         for(i = 0; i < u->ngrp; i++) {
380                 p = uidtop(u->gtab[i]);
381                 if(i != 0)
382                         posn += sprint(buf+posn, ",");
383                 if(p != 0)
384                         posn += sprint(buf+posn, "%s", p->name);
385                 else
386                         posn += sprint(buf+posn, "%d", u->gtab[i]);
387         }
388         sprint(buf+posn, "\n");
389 }
390
391 void
392 setminusers(void)
393 {
394         int u;
395
396         for(u = 0; minusers[u].name; u++) {
397                 strcpy(uid[u].name, minusers[u].name);
398                 uid[u].uid = minusers[u].uid;
399                 uid[u].lead = minusers[u].lead;
400         }
401         cons.nuid = u;
402         qsort(uid, u, sizeof(uid[0]), byuid);
403 }
404
405 Uid*
406 uidpstr(char *name)
407 {
408         Uid *s, *e;
409
410         s = uid;
411         for(e = s+cons.nuid; s < e; s++) {
412                 if(strcmp(name, s->name) == 0)
413                         return s;
414         }
415         return 0;
416 }
417
418 char*
419 getword(char *buf, Rune delim, char *error, int line)
420 {
421         char *p;
422
423         p = utfrune(buf, delim);
424         if(p == 0) {
425                 if(error)
426                         print("cmd_users: %s line %d\n", error, line);
427                 return 0;
428         }
429         *p = '\0';
430         return p+1;
431 }
432
433 int
434 strtouid(char *name)
435 {
436         Uid *u;
437         int id;
438
439         rlock(&uidgc.uidlock);
440
441         u = uidpstr(name);
442         id = -2;
443         if(u != 0)
444                 id = u->uid;
445
446         runlock(&uidgc.uidlock);
447
448         return id;
449 }
450
451 Uid*
452 uidtop(int id)
453 {
454         Uid *bot, *top, *new;
455
456         bot = uid;
457         top = bot + cons.nuid-1;
458
459         while(bot <= top){
460                 new = bot + (top - bot)/2;
461                 if(new->uid == id)
462                         return new;
463                 if(new->uid < id)
464                         bot = new + 1;
465                 else
466                         top = new - 1;
467         }
468         return 0;
469 }
470
471 void
472 uidtostr(char *name, int id, int dolock)
473 {
474         Uid *p;
475
476         if(dolock)
477                 rlock(&uidgc.uidlock);
478
479         p = uidtop(id);
480         if(p == 0)
481                 strcpy(name, "none");
482         else
483                 strcpy(name, p->name);
484
485         if(dolock)
486                 runlock(&uidgc.uidlock);
487 }
488
489 int
490 ingroup(int u, int g)
491 {
492         Uid *p;
493         Userid *s, *e;
494
495         if(u == g)
496                 return 1;
497
498         rlock(&uidgc.uidlock);
499         p = uidtop(g);
500         if(p != 0) {
501                 s = p->gtab;
502                 for(e = s + p->ngrp; s < e; s++) {
503                         if(*s == u) {
504                                 runlock(&uidgc.uidlock);
505                                 return 1;
506                         }
507                 }
508         }
509         runlock(&uidgc.uidlock);
510         return 0;
511 }
512
513 int
514 leadgroup(int ui, int gi)
515 {
516         int i;
517         Uid *u;
518
519         /* user 'none' cannot be a group leader */
520         if(ui == 0)
521                 return 0;
522
523         rlock(&uidgc.uidlock);
524         u = uidtop(gi);
525         if(u == 0) {
526                 runlock(&uidgc.uidlock);
527                 return 0;
528         }
529         i = u->lead;
530         runlock(&uidgc.uidlock);
531         if(i == ui)
532                 return 1;
533         if(i == 0)
534                 return ingroup(ui, gi);
535
536         return 0;
537 }
538
539 int
540 byuid(void *a1, void *a2)
541 {
542         Uid *u1, *u2;
543
544         u1 = a1;
545         u2 = a2;
546         return u1->uid - u2->uid;
547 }
548
549 int
550 fchar(void)
551 {
552         int n;
553
554         n = BUFSIZE;
555         if(n > MAXDAT)
556                 n = MAXDAT;
557         if(uidgc.find >= uidgc.flen) {
558                 uidgc.find = 0;
559                 uidgc.flen = con_read(FID2, uidgc.uidbuf->iobuf, cons.offset, n);
560                 if(uidgc.flen <= 0)
561                         return -1;
562                 cons.offset += uidgc.flen;
563         }
564         return (uchar)uidgc.uidbuf->iobuf[uidgc.find++];
565 }
566
567 int
568 readln(char *p, int len)
569 {
570         int n, c;
571
572         n = 0;
573         while(len--) {
574                 c = fchar();
575                 if(c == -1 || c == '\n')
576                         break;
577                 n++;
578                 *p++ = c;
579         }
580         *p = '\0';
581         return n;
582 }