9user.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957
  1. /*
  2. * This file is part of the UCB release of Plan 9. It is subject to the license
  3. * terms in the LICENSE file found in the top-level directory of this
  4. * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
  5. * part of the UCB release of Plan 9, including this file, may be copied,
  6. * modified, propagated, or distributed except according to the terms contained
  7. * in the LICENSE file.
  8. */
  9. #include "stdinc.h"
  10. #include "9.h"
  11. enum {
  12. NUserHash = 1009,
  13. };
  14. typedef struct Ubox Ubox;
  15. typedef struct User User;
  16. struct User {
  17. char* uid;
  18. char* uname;
  19. char* leader;
  20. char** group;
  21. int ngroup;
  22. User* next; /* */
  23. User* ihash; /* lookup by .uid */
  24. User* nhash; /* lookup by .uname */
  25. };
  26. struct Ubox {
  27. User* head;
  28. User* tail;
  29. int nuser;
  30. int len;
  31. User* ihash[NUserHash]; /* lookup by .uid */
  32. User* nhash[NUserHash]; /* lookup by .uname */
  33. };
  34. static struct {
  35. VtLock* lock;
  36. Ubox* box;
  37. } ubox;
  38. static char usersDefault[] = {
  39. "adm:adm:adm:sys\n"
  40. "none:none::\n"
  41. "noworld:noworld::\n"
  42. "sys:sys::glenda\n"
  43. "glenda:glenda:glenda:\n"
  44. };
  45. static char* usersMandatory[] = {
  46. "adm",
  47. "none",
  48. "noworld",
  49. "sys",
  50. nil,
  51. };
  52. char* uidadm = "adm";
  53. char* unamenone = "none";
  54. char* uidnoworld = "noworld";
  55. static uint32_t
  56. userHash(char* s)
  57. {
  58. uint8_t *p;
  59. uint32_t hash;
  60. hash = 0;
  61. for(p = (uint8_t*)s; *p != '\0'; p++)
  62. hash = hash*7 + *p;
  63. return hash % NUserHash;
  64. }
  65. static User*
  66. _userByUid(Ubox* box, char* uid)
  67. {
  68. User *u;
  69. if(box != nil){
  70. for(u = box->ihash[userHash(uid)]; u != nil; u = u->ihash){
  71. if(strcmp(u->uid, uid) == 0)
  72. return u;
  73. }
  74. }
  75. vtSetError("uname: uid '%s' not found", uid);
  76. return nil;
  77. }
  78. char*
  79. unameByUid(char* uid)
  80. {
  81. User *u;
  82. char *uname;
  83. vtRLock(ubox.lock);
  84. if((u = _userByUid(ubox.box, uid)) == nil){
  85. vtRUnlock(ubox.lock);
  86. return nil;
  87. }
  88. uname = vtStrDup(u->uname);
  89. vtRUnlock(ubox.lock);
  90. return uname;
  91. }
  92. static User*
  93. _userByUname(Ubox* box, char* uname)
  94. {
  95. User *u;
  96. if(box != nil){
  97. for(u = box->nhash[userHash(uname)]; u != nil; u = u->nhash){
  98. if(strcmp(u->uname, uname) == 0)
  99. return u;
  100. }
  101. }
  102. vtSetError("uname: uname '%s' not found", uname);
  103. return nil;
  104. }
  105. char*
  106. uidByUname(char* uname)
  107. {
  108. User *u;
  109. char *uid;
  110. vtRLock(ubox.lock);
  111. if((u = _userByUname(ubox.box, uname)) == nil){
  112. vtRUnlock(ubox.lock);
  113. return nil;
  114. }
  115. uid = vtStrDup(u->uid);
  116. vtRUnlock(ubox.lock);
  117. return uid;
  118. }
  119. static int
  120. _groupMember(Ubox* box, char* group, char* member, int whenNoGroup)
  121. {
  122. int i;
  123. User *g, *m;
  124. /*
  125. * Is 'member' a member of 'group'?
  126. * Note that 'group' is a 'uid' and not a 'uname'.
  127. * A 'member' is automatically in their own group.
  128. */
  129. if((g = _userByUid(box, group)) == nil)
  130. return whenNoGroup;
  131. if((m = _userByUname(box, member)) == nil)
  132. return 0;
  133. if(m == g)
  134. return 1;
  135. for(i = 0; i < g->ngroup; i++){
  136. if(strcmp(g->group[i], member) == 0)
  137. return 1;
  138. }
  139. return 0;
  140. }
  141. int
  142. groupWriteMember(char* uname)
  143. {
  144. int ret;
  145. /*
  146. * If there is a ``write'' group, then only its members can write
  147. * to the file system, no matter what the permission bits say.
  148. *
  149. * To users not in the ``write'' group, the file system appears
  150. * read only. This is used to serve sources.cs.bell-labs.com
  151. * to the world.
  152. *
  153. * Note that if there is no ``write'' group, then this routine
  154. * makes it look like everyone is a member -- the opposite
  155. * of what groupMember does.
  156. *
  157. * We use this for sources.cs.bell-labs.com.
  158. * If this slows things down too much on systems that don't
  159. * use this functionality, we could cache the write group lookup.
  160. */
  161. vtRLock(ubox.lock);
  162. ret = _groupMember(ubox.box, "write", uname, 1);
  163. vtRUnlock(ubox.lock);
  164. return ret;
  165. }
  166. static int
  167. _groupRemMember(Ubox* box, User* g, char* member)
  168. {
  169. int i;
  170. if(_userByUname(box, member) == nil)
  171. return 0;
  172. for(i = 0; i < g->ngroup; i++){
  173. if(strcmp(g->group[i], member) == 0)
  174. break;
  175. }
  176. if(i >= g->ngroup){
  177. if(strcmp(g->uname, member) == 0)
  178. vtSetError("uname: '%s' always in own group", member);
  179. else
  180. vtSetError("uname: '%s' not in group '%s'",
  181. member, g->uname);
  182. return 0;
  183. }
  184. vtMemFree(g->group[i]);
  185. box->len -= strlen(member);
  186. if(g->ngroup > 1)
  187. box->len--;
  188. g->ngroup--;
  189. switch(g->ngroup){
  190. case 0:
  191. vtMemFree(g->group);
  192. g->group = nil;
  193. break;
  194. default:
  195. for(; i < g->ngroup; i++)
  196. g->group[i] = g->group[i+1];
  197. g->group[i] = nil; /* prevent accidents */
  198. g->group = vtMemRealloc(g->group, g->ngroup * sizeof(char*));
  199. break;
  200. }
  201. return 1;
  202. }
  203. static int
  204. _groupAddMember(Ubox* box, User* g, char* member)
  205. {
  206. User *u;
  207. if((u = _userByUname(box, member)) == nil)
  208. return 0;
  209. if(_groupMember(box, g->uid, u->uname, 0)){
  210. if(strcmp(g->uname, member) == 0)
  211. vtSetError("uname: '%s' always in own group", member);
  212. else
  213. vtSetError("uname: '%s' already in group '%s'",
  214. member, g->uname);
  215. return 0;
  216. }
  217. g->group = vtMemRealloc(g->group, (g->ngroup+1)*sizeof(char*));
  218. g->group[g->ngroup] = vtStrDup(member);
  219. box->len += strlen(member);
  220. g->ngroup++;
  221. if(g->ngroup > 1)
  222. box->len++;
  223. return 1;
  224. }
  225. int
  226. groupMember(char* group, char* member)
  227. {
  228. int r;
  229. if(group == nil)
  230. return 0;
  231. vtRLock(ubox.lock);
  232. r = _groupMember(ubox.box, group, member, 0);
  233. vtRUnlock(ubox.lock);
  234. return r;
  235. }
  236. int
  237. groupLeader(char* group, char* member)
  238. {
  239. int r;
  240. User *g;
  241. /*
  242. * Is 'member' the leader of 'group'?
  243. * Note that 'group' is a 'uid' and not a 'uname'.
  244. * Uname 'none' cannot be a group leader.
  245. */
  246. if(strcmp(member, unamenone) == 0 || group == nil)
  247. return 0;
  248. vtRLock(ubox.lock);
  249. if((g = _userByUid(ubox.box, group)) == nil){
  250. vtRUnlock(ubox.lock);
  251. return 0;
  252. }
  253. if(g->leader != nil){
  254. if(strcmp(g->leader, member) == 0){
  255. vtRUnlock(ubox.lock);
  256. return 1;
  257. }
  258. r = 0;
  259. }
  260. else
  261. r = _groupMember(ubox.box, group, member, 0);
  262. vtRUnlock(ubox.lock);
  263. return r;
  264. }
  265. static void
  266. userFree(User* u)
  267. {
  268. int i;
  269. vtMemFree(u->uid);
  270. vtMemFree(u->uname);
  271. if(u->leader != nil)
  272. vtMemFree(u->leader);
  273. if(u->ngroup){
  274. for(i = 0; i < u->ngroup; i++)
  275. vtMemFree(u->group[i]);
  276. vtMemFree(u->group);
  277. }
  278. vtMemFree(u);
  279. }
  280. static User*
  281. userAlloc(char* uid, char* uname)
  282. {
  283. User *u;
  284. u = vtMemAllocZ(sizeof(User));
  285. u->uid = vtStrDup(uid);
  286. u->uname = vtStrDup(uname);
  287. return u;
  288. }
  289. int
  290. validUserName(char* name)
  291. {
  292. Rune *r;
  293. //static Rune invalid[] = L"#:,()";
  294. static Rune invalid[] = {'#', ':', ',', '(', ')', '\0'};
  295. for(r = invalid; *r != '\0'; r++){
  296. if(utfrune(name, *r))
  297. return 0;
  298. }
  299. return 1;
  300. }
  301. static int
  302. userFmt(Fmt* fmt)
  303. {
  304. User *u;
  305. int i, r;
  306. u = va_arg(fmt->args, User*);
  307. r = fmtprint(fmt, "%s:%s:", u->uid, u->uname);
  308. if(u->leader != nil)
  309. r += fmtprint(fmt, u->leader);
  310. r += fmtprint(fmt, ":");
  311. if(u->ngroup){
  312. r += fmtprint(fmt, u->group[0]);
  313. for(i = 1; i < u->ngroup; i++)
  314. r += fmtprint(fmt, ",%s", u->group[i]);
  315. }
  316. return r;
  317. }
  318. static int
  319. usersFileWrite(Ubox* box)
  320. {
  321. Fs *fs;
  322. User *u;
  323. int i, r;
  324. Fsys *fsys;
  325. char *p, *q, *s;
  326. File *dir, *file;
  327. if((fsys = fsysGet("main")) == nil)
  328. return 0;
  329. fsysFsRlock(fsys);
  330. fs = fsysGetFs(fsys);
  331. /*
  332. * BUG:
  333. * the owner/group/permissions need to be thought out.
  334. */
  335. r = 0;
  336. if((dir = fileOpen(fs, "/active")) == nil)
  337. goto tidy0;
  338. if((file = fileWalk(dir, uidadm)) == nil)
  339. file = fileCreate(dir, uidadm, ModeDir|0775, uidadm);
  340. fileDecRef(dir);
  341. if(file == nil)
  342. goto tidy;
  343. dir = file;
  344. if((file = fileWalk(dir, "users")) == nil)
  345. file = fileCreate(dir, "users", 0664, uidadm);
  346. fileDecRef(dir);
  347. if(file == nil)
  348. goto tidy;
  349. if(!fileTruncate(file, uidadm))
  350. goto tidy;
  351. p = s = vtMemAlloc(box->len+1);
  352. q = p + box->len+1;
  353. for(u = box->head; u != nil; u = u->next){
  354. p += snprint(p, q-p, "%s:%s:", u->uid, u->uname);
  355. if(u->leader != nil)
  356. p+= snprint(p, q-p, u->leader);
  357. p += snprint(p, q-p, ":");
  358. if(u->ngroup){
  359. p += snprint(p, q-p, u->group[0]);
  360. for(i = 1; i < u->ngroup; i++)
  361. p += snprint(p, q-p, ",%s", u->group[i]);
  362. }
  363. p += snprint(p, q-p, "\n");
  364. }
  365. r = fileWrite(file, s, box->len, 0, uidadm);
  366. vtMemFree(s);
  367. tidy:
  368. if(file != nil)
  369. fileDecRef(file);
  370. tidy0:
  371. fsysFsRUnlock(fsys);
  372. fsysPut(fsys);
  373. return r;
  374. }
  375. static void
  376. uboxRemUser(Ubox* box, User *u)
  377. {
  378. User **h, *up;
  379. h = &box->ihash[userHash(u->uid)];
  380. for(up = *h; up != nil && up != u; up = up->ihash)
  381. h = &up->ihash;
  382. assert(up == u);
  383. *h = up->ihash;
  384. box->len -= strlen(u->uid);
  385. h = &box->nhash[userHash(u->uname)];
  386. for(up = *h; up != nil && up != u; up = up->nhash)
  387. h = &up->nhash;
  388. assert(up == u);
  389. *h = up->nhash;
  390. box->len -= strlen(u->uname);
  391. h = &box->head;
  392. for(up = *h; up != nil && strcmp(up->uid, u->uid) != 0; up = up->next)
  393. h = &up->next;
  394. assert(up == u);
  395. *h = u->next;
  396. u->next = nil;
  397. box->len -= 4;
  398. box->nuser--;
  399. }
  400. static void
  401. uboxAddUser(Ubox* box, User* u)
  402. {
  403. User **h, *up;
  404. h = &box->ihash[userHash(u->uid)];
  405. u->ihash = *h;
  406. *h = u;
  407. box->len += strlen(u->uid);
  408. h = &box->nhash[userHash(u->uname)];
  409. u->nhash = *h;
  410. *h = u;
  411. box->len += strlen(u->uname);
  412. h = &box->head;
  413. for(up = *h; up != nil && strcmp(up->uid, u->uid) < 0; up = up->next)
  414. h = &up->next;
  415. u->next = *h;
  416. *h = u;
  417. box->len += 4;
  418. box->nuser++;
  419. }
  420. static void
  421. uboxDump(Ubox* box)
  422. {
  423. User* u;
  424. consPrint("nuser %d len = %d\n", box->nuser, box->len);
  425. for(u = box->head; u != nil; u = u->next)
  426. consPrint("%U\n", u);
  427. }
  428. static void
  429. uboxFree(Ubox* box)
  430. {
  431. User *next, *u;
  432. for(u = box->head; u != nil; u = next){
  433. next = u->next;
  434. userFree(u);
  435. }
  436. vtMemFree(box);
  437. }
  438. static int
  439. uboxInit(char* users, int len)
  440. {
  441. User *g, *u;
  442. Ubox *box, *obox;
  443. int blank, comment, i, nline, nuser;
  444. char *buf, *f[5], **line, *p, *q, *s;
  445. /*
  446. * Strip out whitespace and comments.
  447. * Note that comments are pointless, they disappear
  448. * when the server writes the database back out.
  449. */
  450. blank = 1;
  451. comment = nline = 0;
  452. s = p = buf = vtMemAlloc(len+1);
  453. for(q = users; *q != '\0'; q++){
  454. if(*q == '\r' || *q == '\t' || *q == ' ')
  455. continue;
  456. if(*q == '\n'){
  457. if(!blank){
  458. if(p != s){
  459. *p++ = '\n';
  460. nline++;
  461. s = p;
  462. }
  463. blank = 1;
  464. }
  465. comment = 0;
  466. continue;
  467. }
  468. if(*q == '#')
  469. comment = 1;
  470. blank = 0;
  471. if(!comment)
  472. *p++ = *q;
  473. }
  474. *p = '\0';
  475. line = vtMemAllocZ((nline+2)*sizeof(char*));
  476. if((i = gettokens(buf, line, nline+2, "\n")) != nline){
  477. fprint(2, "nline %d (%d) botch\n", nline, i);
  478. vtMemFree(line);
  479. vtMemFree(buf);
  480. return 0;
  481. }
  482. /*
  483. * Everything is updated in a local Ubox until verified.
  484. */
  485. box = vtMemAllocZ(sizeof(Ubox));
  486. /*
  487. * First pass - check format, check for duplicates
  488. * and enter in hash buckets.
  489. */
  490. nuser = 0;
  491. for(i = 0; i < nline; i++){
  492. s = vtStrDup(line[i]);
  493. if(getfields(s, f, nelem(f), 0, ":") != 4){
  494. fprint(2, "bad line '%s'\n", line[i]);
  495. vtMemFree(s);
  496. continue;
  497. }
  498. if(*f[0] == '\0' || *f[1] == '\0'){
  499. fprint(2, "bad line '%s'\n", line[i]);
  500. vtMemFree(s);
  501. continue;
  502. }
  503. if(!validUserName(f[0])){
  504. fprint(2, "invalid uid '%s'\n", f[0]);
  505. vtMemFree(s);
  506. continue;
  507. }
  508. if(_userByUid(box, f[0]) != nil){
  509. fprint(2, "duplicate uid '%s'\n", f[0]);
  510. vtMemFree(s);
  511. continue;
  512. }
  513. if(!validUserName(f[1])){
  514. fprint(2, "invalid uname '%s'\n", f[0]);
  515. vtMemFree(s);
  516. continue;
  517. }
  518. if(_userByUname(box, f[1]) != nil){
  519. fprint(2, "duplicate uname '%s'\n", f[1]);
  520. vtMemFree(s);
  521. continue;
  522. }
  523. u = userAlloc(f[0], f[1]);
  524. uboxAddUser(box, u);
  525. line[nuser] = line[i];
  526. nuser++;
  527. vtMemFree(s);
  528. }
  529. assert(box->nuser == nuser);
  530. /*
  531. * Second pass - fill in leader and group information.
  532. */
  533. for(i = 0; i < nuser; i++){
  534. s = vtStrDup(line[i]);
  535. getfields(s, f, nelem(f), 0, ":");
  536. assert(g = _userByUname(box, f[1]));
  537. if(*f[2] != '\0'){
  538. if((u = _userByUname(box, f[2])) == nil)
  539. g->leader = vtStrDup(g->uname);
  540. else
  541. g->leader = vtStrDup(u->uname);
  542. box->len += strlen(g->leader);
  543. }
  544. for(p = f[3]; p != nil; p = q){
  545. if((q = utfrune(p, L',')) != nil)
  546. *q++ = '\0';
  547. if(!_groupAddMember(box, g, p)){
  548. // print/log error here
  549. }
  550. }
  551. vtMemFree(s);
  552. }
  553. vtMemFree(line);
  554. vtMemFree(buf);
  555. for(i = 0; usersMandatory[i] != nil; i++){
  556. if((u = _userByUid(box, usersMandatory[i])) == nil){
  557. vtSetError("user '%s' is mandatory", usersMandatory[i]);
  558. uboxFree(box);
  559. return 0;
  560. }
  561. if(strcmp(u->uid, u->uname) != 0){
  562. vtSetError("uid/uname for user '%s' must match",
  563. usersMandatory[i]);
  564. uboxFree(box);
  565. return 0;
  566. }
  567. }
  568. vtLock(ubox.lock);
  569. obox = ubox.box;
  570. ubox.box = box;
  571. vtUnlock(ubox.lock);
  572. if(obox != nil)
  573. uboxFree(obox);
  574. return 1;
  575. }
  576. int
  577. usersFileRead(char* path)
  578. {
  579. char *p;
  580. File *file;
  581. Fsys *fsys;
  582. int len, r;
  583. uint64_t size;
  584. if((fsys = fsysGet("main")) == nil)
  585. return 0;
  586. fsysFsRlock(fsys);
  587. if(path == nil)
  588. path = "/active/adm/users";
  589. r = 0;
  590. if((file = fileOpen(fsysGetFs(fsys), path)) != nil){
  591. if(fileGetSize(file, &size)){
  592. len = size;
  593. p = vtMemAlloc(size+1);
  594. if(fileRead(file, p, len, 0) == len){
  595. p[len] = '\0';
  596. r = uboxInit(p, len);
  597. }
  598. }
  599. fileDecRef(file);
  600. }
  601. fsysFsRUnlock(fsys);
  602. fsysPut(fsys);
  603. return r;
  604. }
  605. static int
  606. cmdUname(int argc, char* argv[])
  607. {
  608. User *u, *up;
  609. int d, dflag, i, r;
  610. char *p, *uid, *uname;
  611. char *createfmt = "fsys main create /active/usr/%s %s %s d775";
  612. char *usage = "usage: uname [-d] uname [uid|:uid|%%newname|=leader|+member|-member]";
  613. dflag = 0;
  614. ARGBEGIN{
  615. default:
  616. return cliError(usage);
  617. case 'd':
  618. dflag = 1;
  619. break;
  620. }ARGEND
  621. if(argc < 1){
  622. if(!dflag)
  623. return cliError(usage);
  624. vtRLock(ubox.lock);
  625. uboxDump(ubox.box);
  626. vtRUnlock(ubox.lock);
  627. return 1;
  628. }
  629. uname = argv[0];
  630. argc--; argv++;
  631. if(argc == 0){
  632. vtRLock(ubox.lock);
  633. if((u = _userByUname(ubox.box, uname)) == nil){
  634. vtRUnlock(ubox.lock);
  635. return 0;
  636. }
  637. consPrint("\t%U\n", u);
  638. vtRUnlock(ubox.lock);
  639. return 1;
  640. }
  641. vtLock(ubox.lock);
  642. u = _userByUname(ubox.box, uname);
  643. while(argc--){
  644. if(argv[0][0] == '%'){
  645. if(u == nil){
  646. vtUnlock(ubox.lock);
  647. return 0;
  648. }
  649. p = &argv[0][1];
  650. if((up = _userByUname(ubox.box, p)) != nil){
  651. vtSetError("uname: uname '%s' already exists",
  652. up->uname);
  653. vtUnlock(ubox.lock);
  654. return 0;
  655. }
  656. for(i = 0; usersMandatory[i] != nil; i++){
  657. if(strcmp(usersMandatory[i], uname) != 0)
  658. continue;
  659. vtSetError("uname: uname '%s' is mandatory",
  660. uname);
  661. vtUnlock(ubox.lock);
  662. return 0;
  663. }
  664. d = strlen(p) - strlen(u->uname);
  665. for(up = ubox.box->head; up != nil; up = up->next){
  666. if(up->leader != nil){
  667. if(strcmp(up->leader, u->uname) == 0){
  668. vtMemFree(up->leader);
  669. up->leader = vtStrDup(p);
  670. ubox.box->len += d;
  671. }
  672. }
  673. for(i = 0; i < up->ngroup; i++){
  674. if(strcmp(up->group[i], u->uname) != 0)
  675. continue;
  676. vtMemFree(up->group[i]);
  677. up->group[i] = vtStrDup(p);
  678. ubox.box->len += d;
  679. break;
  680. }
  681. }
  682. uboxRemUser(ubox.box, u);
  683. vtMemFree(u->uname);
  684. u->uname = vtStrDup(p);
  685. uboxAddUser(ubox.box, u);
  686. }
  687. else if(argv[0][0] == '='){
  688. if(u == nil){
  689. vtUnlock(ubox.lock);
  690. return 0;
  691. }
  692. if((up = _userByUname(ubox.box, &argv[0][1])) == nil){
  693. if(argv[0][1] != '\0'){
  694. vtUnlock(ubox.lock);
  695. return 0;
  696. }
  697. }
  698. if(u->leader != nil){
  699. ubox.box->len -= strlen(u->leader);
  700. vtMemFree(u->leader);
  701. u->leader = nil;
  702. }
  703. if(up != nil){
  704. u->leader = vtStrDup(up->uname);
  705. ubox.box->len += strlen(u->leader);
  706. }
  707. }
  708. else if(argv[0][0] == '+'){
  709. if(u == nil){
  710. vtUnlock(ubox.lock);
  711. return 0;
  712. }
  713. if((up = _userByUname(ubox.box, &argv[0][1])) == nil){
  714. vtUnlock(ubox.lock);
  715. return 0;
  716. }
  717. if(!_groupAddMember(ubox.box, u, up->uname)){
  718. vtUnlock(ubox.lock);
  719. return 0;
  720. }
  721. }
  722. else if(argv[0][0] == '-'){
  723. if(u == nil){
  724. vtUnlock(ubox.lock);
  725. return 0;
  726. }
  727. if((up = _userByUname(ubox.box, &argv[0][1])) == nil){
  728. vtUnlock(ubox.lock);
  729. return 0;
  730. }
  731. if(!_groupRemMember(ubox.box, u, up->uname)){
  732. vtUnlock(ubox.lock);
  733. return 0;
  734. }
  735. }
  736. else{
  737. if(u != nil){
  738. vtSetError("uname: uname '%s' already exists",
  739. u->uname);
  740. vtUnlock(ubox.lock);
  741. return 0;
  742. }
  743. uid = argv[0];
  744. if(*uid == ':')
  745. uid++;
  746. if((u = _userByUid(ubox.box, uid)) != nil){
  747. vtSetError("uname: uid '%s' already exists",
  748. u->uid);
  749. vtUnlock(ubox.lock);
  750. return 0;
  751. }
  752. u = userAlloc(uid, uname);
  753. uboxAddUser(ubox.box, u);
  754. if(argv[0][0] != ':'){
  755. // should have an option for the mode and gid
  756. p = smprint(createfmt, uname, uname, uname);
  757. r = cliExec(p);
  758. vtMemFree(p);
  759. if(r == 0){
  760. vtUnlock(ubox.lock);
  761. return 0;
  762. }
  763. }
  764. }
  765. argv++;
  766. }
  767. if(usersFileWrite(ubox.box) == 0){
  768. vtUnlock(ubox.lock);
  769. return 0;
  770. }
  771. if(dflag)
  772. uboxDump(ubox.box);
  773. vtUnlock(ubox.lock);
  774. return 1;
  775. }
  776. static int
  777. cmdUsers(int argc, char* argv[])
  778. {
  779. Ubox *box;
  780. int dflag, r, wflag;
  781. char *file;
  782. char *usage = "usage: users [-d | -r file] [-w]";
  783. dflag = wflag = 0;
  784. file = nil;
  785. ARGBEGIN{
  786. default:
  787. return cliError(usage);
  788. case 'd':
  789. dflag = 1;
  790. break;
  791. case 'r':
  792. file = ARGF();
  793. if(file == nil)
  794. return cliError(usage);
  795. break;
  796. case 'w':
  797. wflag = 1;
  798. break;
  799. }ARGEND
  800. if(argc)
  801. return cliError(usage);
  802. if(dflag && file)
  803. return cliError("cannot use -d and -r together");
  804. if(dflag)
  805. uboxInit(usersDefault, sizeof(usersDefault));
  806. else if(file){
  807. if(usersFileRead(file) == 0)
  808. return 0;
  809. }
  810. vtRLock(ubox.lock);
  811. box = ubox.box;
  812. consPrint("\tnuser %d len %d\n", box->nuser, box->len);
  813. r = 1;
  814. if(wflag)
  815. r = usersFileWrite(box);
  816. vtRUnlock(ubox.lock);
  817. return r;
  818. }
  819. int
  820. usersInit(void)
  821. {
  822. fmtinstall('U', userFmt);
  823. ubox.lock = vtLockAlloc();
  824. uboxInit(usersDefault, sizeof(usersDefault));
  825. cliAddCmd("users", cmdUsers);
  826. cliAddCmd("uname", cmdUname);
  827. return 1;
  828. }