9proc.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834
  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. #include "dat.h"
  12. #include "fns.h"
  13. enum {
  14. NConInit = 128,
  15. NMsgInit = 384,
  16. NMsgProcInit = 64,
  17. NMsizeInit = 8192+IOHDRSZ,
  18. };
  19. static struct {
  20. VtLock* alock; /* alloc */
  21. Msg* ahead;
  22. VtRendez* arendez;
  23. int maxmsg;
  24. int nmsg;
  25. int nmsgstarve;
  26. VtLock* rlock; /* read */
  27. Msg* rhead;
  28. Msg* rtail;
  29. VtRendez* rrendez;
  30. int maxproc;
  31. int nproc;
  32. int nprocstarve;
  33. uint32_t msize; /* immutable */
  34. } mbox;
  35. static struct {
  36. VtLock* alock; /* alloc */
  37. Con* ahead;
  38. VtRendez* arendez;
  39. VtLock* clock;
  40. Con* chead;
  41. Con* ctail;
  42. int maxcon;
  43. int ncon;
  44. int nconstarve;
  45. uint32_t msize;
  46. } cbox;
  47. static void
  48. conFree(Con* con)
  49. {
  50. assert(con->version == nil);
  51. assert(con->mhead == nil);
  52. assert(con->whead == nil);
  53. assert(con->nfid == 0);
  54. assert(con->state == ConMoribund);
  55. if(con->fd >= 0){
  56. close(con->fd);
  57. con->fd = -1;
  58. }
  59. con->state = ConDead;
  60. con->aok = 0;
  61. con->flags = 0;
  62. con->isconsole = 0;
  63. vtLock(cbox.alock);
  64. if(con->cprev != nil)
  65. con->cprev->cnext = con->cnext;
  66. else
  67. cbox.chead = con->cnext;
  68. if(con->cnext != nil)
  69. con->cnext->cprev = con->cprev;
  70. else
  71. cbox.ctail = con->cprev;
  72. con->cprev = con->cnext = nil;
  73. if(cbox.ncon > cbox.maxcon){
  74. if(con->name != nil)
  75. vtMemFree(con->name);
  76. vtLockFree(con->fidlock);
  77. vtMemFree(con->data);
  78. vtRendezFree(con->wrendez);
  79. vtLockFree(con->wlock);
  80. vtRendezFree(con->mrendez);
  81. vtLockFree(con->mlock);
  82. vtRendezFree(con->rendez);
  83. vtLockFree(con->lock);
  84. vtMemFree(con);
  85. cbox.ncon--;
  86. vtUnlock(cbox.alock);
  87. return;
  88. }
  89. con->anext = cbox.ahead;
  90. cbox.ahead = con;
  91. if(con->anext == nil)
  92. vtWakeup(cbox.arendez);
  93. vtUnlock(cbox.alock);
  94. }
  95. static void
  96. msgFree(Msg* m)
  97. {
  98. assert(m->rwnext == nil);
  99. assert(m->flush == nil);
  100. vtLock(mbox.alock);
  101. if(mbox.nmsg > mbox.maxmsg){
  102. vtMemFree(m->data);
  103. vtMemFree(m);
  104. mbox.nmsg--;
  105. vtUnlock(mbox.alock);
  106. return;
  107. }
  108. m->anext = mbox.ahead;
  109. mbox.ahead = m;
  110. if(m->anext == nil)
  111. vtWakeup(mbox.arendez);
  112. vtUnlock(mbox.alock);
  113. }
  114. static Msg*
  115. msgAlloc(Con* con)
  116. {
  117. Msg *m;
  118. vtLock(mbox.alock);
  119. while(mbox.ahead == nil){
  120. if(mbox.nmsg >= mbox.maxmsg){
  121. mbox.nmsgstarve++;
  122. vtSleep(mbox.arendez);
  123. continue;
  124. }
  125. m = vtMemAllocZ(sizeof(Msg));
  126. m->data = vtMemAlloc(mbox.msize);
  127. m->msize = mbox.msize;
  128. mbox.nmsg++;
  129. mbox.ahead = m;
  130. break;
  131. }
  132. m = mbox.ahead;
  133. mbox.ahead = m->anext;
  134. m->anext = nil;
  135. vtUnlock(mbox.alock);
  136. m->con = con;
  137. m->state = MsgR;
  138. m->nowq = 0;
  139. return m;
  140. }
  141. static void
  142. msgMunlink(Msg* m)
  143. {
  144. Con *con;
  145. con = m->con;
  146. if(m->mprev != nil)
  147. m->mprev->mnext = m->mnext;
  148. else
  149. con->mhead = m->mnext;
  150. if(m->mnext != nil)
  151. m->mnext->mprev = m->mprev;
  152. else
  153. con->mtail = m->mprev;
  154. m->mprev = m->mnext = nil;
  155. }
  156. void
  157. msgFlush(Msg* m)
  158. {
  159. Con *con;
  160. Msg *flush, *old;
  161. con = m->con;
  162. if(Dflag)
  163. fprint(2, "msgFlush %F\n", &m->t);
  164. /*
  165. * If this Tflush has been flushed, nothing to do.
  166. * Look for the message to be flushed in the
  167. * queue of all messages still on this connection.
  168. * If it's not found must assume Elvis has already
  169. * left the building and reply normally.
  170. */
  171. vtLock(con->mlock);
  172. if(m->state == MsgF){
  173. vtUnlock(con->mlock);
  174. return;
  175. }
  176. for(old = con->mhead; old != nil; old = old->mnext)
  177. if(old->t.tag == m->t.oldtag)
  178. break;
  179. if(old == nil){
  180. if(Dflag)
  181. fprint(2, "msgFlush: cannot find %d\n", m->t.oldtag);
  182. vtUnlock(con->mlock);
  183. return;
  184. }
  185. if(Dflag)
  186. fprint(2, "\tmsgFlush found %F\n", &old->t);
  187. /*
  188. * Found it.
  189. * There are two cases where the old message can be
  190. * truly flushed and no reply to the original message given.
  191. * The first is when the old message is in MsgR state; no
  192. * processing has been done yet and it is still on the read
  193. * queue. The second is if old is a Tflush, which doesn't
  194. * affect the server state. In both cases, put the old
  195. * message into MsgF state and let MsgWrite toss it after
  196. * pulling it off the queue.
  197. */
  198. if(old->state == MsgR || old->t.type == Tflush){
  199. old->state = MsgF;
  200. if(Dflag)
  201. fprint(2, "msgFlush: change %d from MsgR to MsgF\n",
  202. m->t.oldtag);
  203. }
  204. /*
  205. * Link this flush message and the old message
  206. * so multiple flushes can be coalesced (if there are
  207. * multiple Tflush messages for a particular pending
  208. * request, it is only necessary to respond to the last
  209. * one, so any previous can be removed) and to be
  210. * sure flushes wait for their corresponding old
  211. * message to go out first.
  212. * Waiting flush messages do not go on the write queue,
  213. * they are processed after the old message is dealt
  214. * with. There's no real need to protect the setting of
  215. * Msg.nowq, the only code to check it runs in this
  216. * process after this routine returns.
  217. */
  218. if((flush = old->flush) != nil){
  219. if(Dflag)
  220. fprint(2, "msgFlush: remove %d from %d list\n",
  221. old->flush->t.tag, old->t.tag);
  222. m->flush = flush->flush;
  223. flush->flush = nil;
  224. msgMunlink(flush);
  225. msgFree(flush);
  226. }
  227. old->flush = m;
  228. m->nowq = 1;
  229. if(Dflag)
  230. fprint(2, "msgFlush: add %d to %d queue\n",
  231. m->t.tag, old->t.tag);
  232. vtUnlock(con->mlock);
  233. }
  234. static void
  235. msgProc(void *v)
  236. {
  237. Msg *m;
  238. char *e;
  239. Con *con;
  240. vtThreadSetName("msgProc");
  241. for(;;){
  242. /*
  243. * If surplus to requirements, exit.
  244. * If not, wait for and pull a message off
  245. * the read queue.
  246. */
  247. vtLock(mbox.rlock);
  248. if(mbox.nproc > mbox.maxproc){
  249. mbox.nproc--;
  250. vtUnlock(mbox.rlock);
  251. break;
  252. }
  253. while(mbox.rhead == nil)
  254. vtSleep(mbox.rrendez);
  255. m = mbox.rhead;
  256. mbox.rhead = m->rwnext;
  257. m->rwnext = nil;
  258. vtUnlock(mbox.rlock);
  259. con = m->con;
  260. e = nil;
  261. /*
  262. * If the message has been flushed before
  263. * any 9P processing has started, mark it so
  264. * none will be attempted.
  265. */
  266. vtLock(con->mlock);
  267. if(m->state == MsgF)
  268. e = "flushed";
  269. else
  270. m->state = Msg9;
  271. vtUnlock(con->mlock);
  272. if(e == nil){
  273. /*
  274. * explain this
  275. */
  276. vtLock(con->lock);
  277. if(m->t.type == Tversion){
  278. con->version = m;
  279. con->state = ConDown;
  280. while(con->mhead != m)
  281. vtSleep(con->rendez);
  282. assert(con->state == ConDown);
  283. if(con->version == m){
  284. con->version = nil;
  285. con->state = ConInit;
  286. }
  287. else
  288. e = "Tversion aborted";
  289. }
  290. else if(con->state != ConUp)
  291. e = "connection not ready";
  292. vtUnlock(con->lock);
  293. }
  294. /*
  295. * Dispatch if not error already.
  296. */
  297. m->r.tag = m->t.tag;
  298. if(e == nil && !(*rFcall[m->t.type])(m))
  299. e = vtGetError();
  300. if(e != nil){
  301. m->r.type = Rerror;
  302. m->r.ename = e;
  303. }
  304. else
  305. m->r.type = m->t.type+1;
  306. /*
  307. * Put the message (with reply) on the
  308. * write queue and wakeup the write process.
  309. */
  310. if(!m->nowq){
  311. vtLock(con->wlock);
  312. if(con->whead == nil)
  313. con->whead = m;
  314. else
  315. con->wtail->rwnext = m;
  316. con->wtail = m;
  317. vtWakeup(con->wrendez);
  318. vtUnlock(con->wlock);
  319. }
  320. }
  321. }
  322. static void
  323. msgRead(void* v)
  324. {
  325. Msg *m;
  326. Con *con;
  327. int eof, fd, n;
  328. vtThreadSetName("msgRead");
  329. con = v;
  330. fd = con->fd;
  331. eof = 0;
  332. while(!eof){
  333. m = msgAlloc(con);
  334. while((n = read9pmsg(fd, m->data, con->msize)) == 0)
  335. ;
  336. if(n < 0){
  337. m->t.type = Tversion;
  338. m->t.fid = NOFID;
  339. m->t.tag = NOTAG;
  340. m->t.msize = con->msize;
  341. m->t.version = "9PEoF";
  342. eof = 1;
  343. }
  344. else if(convM2S(m->data, n, &m->t) != n){
  345. if(Dflag)
  346. fprint(2, "msgRead: convM2S error: %s\n",
  347. con->name);
  348. msgFree(m);
  349. continue;
  350. }
  351. if(Dflag)
  352. fprint(2, "msgRead %p: t %F\n", con, &m->t);
  353. vtLock(con->mlock);
  354. if(con->mtail != nil){
  355. m->mprev = con->mtail;
  356. con->mtail->mnext = m;
  357. }
  358. else{
  359. con->mhead = m;
  360. m->mprev = nil;
  361. }
  362. con->mtail = m;
  363. vtUnlock(con->mlock);
  364. vtLock(mbox.rlock);
  365. if(mbox.rhead == nil){
  366. mbox.rhead = m;
  367. if(!vtWakeup(mbox.rrendez)){
  368. if(mbox.nproc < mbox.maxproc){
  369. if(vtThread(msgProc, nil) > 0)
  370. mbox.nproc++;
  371. }
  372. else
  373. mbox.nprocstarve++;
  374. }
  375. /*
  376. * don't need this surely?
  377. vtWakeup(mbox.rrendez);
  378. */
  379. }
  380. else
  381. mbox.rtail->rwnext = m;
  382. mbox.rtail = m;
  383. vtUnlock(mbox.rlock);
  384. }
  385. }
  386. static void
  387. msgWrite(void* v)
  388. {
  389. Con *con;
  390. int eof, n;
  391. Msg *flush, *m;
  392. vtThreadSetName("msgWrite");
  393. con = v;
  394. if(vtThread(msgRead, con) < 0){
  395. conFree(con);
  396. return;
  397. }
  398. for(;;){
  399. /*
  400. * Wait for and pull a message off the write queue.
  401. */
  402. vtLock(con->wlock);
  403. while(con->whead == nil)
  404. vtSleep(con->wrendez);
  405. m = con->whead;
  406. con->whead = m->rwnext;
  407. m->rwnext = nil;
  408. assert(!m->nowq);
  409. vtUnlock(con->wlock);
  410. eof = 0;
  411. /*
  412. * Write each message (if it hasn't been flushed)
  413. * followed by any messages waiting for it to complete.
  414. */
  415. vtLock(con->mlock);
  416. while(m != nil){
  417. msgMunlink(m);
  418. if(Dflag)
  419. fprint(2, "msgWrite %d: r %F\n",
  420. m->state, &m->r);
  421. if(m->state != MsgF){
  422. m->state = MsgW;
  423. vtUnlock(con->mlock);
  424. n = convS2M(&m->r, con->data, con->msize);
  425. if(write(con->fd, con->data, n) != n)
  426. eof = 1;
  427. vtLock(con->mlock);
  428. }
  429. if((flush = m->flush) != nil){
  430. assert(flush->nowq);
  431. m->flush = nil;
  432. }
  433. msgFree(m);
  434. m = flush;
  435. }
  436. vtUnlock(con->mlock);
  437. vtLock(con->lock);
  438. if(eof && con->fd >= 0){
  439. close(con->fd);
  440. con->fd = -1;
  441. }
  442. if(con->state == ConDown)
  443. vtWakeup(con->rendez);
  444. if(con->state == ConMoribund && con->mhead == nil){
  445. vtUnlock(con->lock);
  446. conFree(con);
  447. break;
  448. }
  449. vtUnlock(con->lock);
  450. }
  451. }
  452. Con*
  453. conAlloc(int fd, char* name, int flags)
  454. {
  455. Con *con;
  456. char buf[128], *p;
  457. int rfd, n;
  458. vtLock(cbox.alock);
  459. while(cbox.ahead == nil){
  460. if(cbox.ncon >= cbox.maxcon){
  461. cbox.nconstarve++;
  462. vtSleep(cbox.arendez);
  463. continue;
  464. }
  465. con = vtMemAllocZ(sizeof(Con));
  466. con->lock = vtLockAlloc();
  467. con->rendez = vtRendezAlloc(con->lock);
  468. con->data = vtMemAlloc(cbox.msize);
  469. con->msize = cbox.msize;
  470. con->alock = vtLockAlloc();
  471. con->mlock = vtLockAlloc();
  472. con->mrendez = vtRendezAlloc(con->mlock);
  473. con->wlock = vtLockAlloc();
  474. con->wrendez = vtRendezAlloc(con->wlock);
  475. con->fidlock = vtLockAlloc();
  476. cbox.ncon++;
  477. cbox.ahead = con;
  478. break;
  479. }
  480. con = cbox.ahead;
  481. cbox.ahead = con->anext;
  482. con->anext = nil;
  483. if(cbox.ctail != nil){
  484. con->cprev = cbox.ctail;
  485. cbox.ctail->cnext = con;
  486. }
  487. else{
  488. cbox.chead = con;
  489. con->cprev = nil;
  490. }
  491. cbox.ctail = con;
  492. assert(con->mhead == nil);
  493. assert(con->whead == nil);
  494. assert(con->fhead == nil);
  495. assert(con->nfid == 0);
  496. con->state = ConNew;
  497. con->fd = fd;
  498. if(con->name != nil){
  499. vtMemFree(con->name);
  500. con->name = nil;
  501. }
  502. if(name != nil)
  503. con->name = vtStrDup(name);
  504. else
  505. con->name = vtStrDup("unknown");
  506. con->remote[0] = 0;
  507. snprint(buf, sizeof buf, "%s/remote", con->name);
  508. if((rfd = open(buf, OREAD)) >= 0){
  509. n = read(rfd, buf, sizeof buf-1);
  510. close(rfd);
  511. if(n > 0){
  512. buf[n] = 0;
  513. if((p = strchr(buf, '\n')) != nil)
  514. *p = 0;
  515. strecpy(con->remote, con->remote+sizeof con->remote, buf);
  516. }
  517. }
  518. con->flags = flags;
  519. con->isconsole = 0;
  520. vtUnlock(cbox.alock);
  521. if(vtThread(msgWrite, con) < 0){
  522. conFree(con);
  523. return nil;
  524. }
  525. return con;
  526. }
  527. static int
  528. cmdMsg(int argc, char* argv[])
  529. {
  530. char *p;
  531. char *usage = "usage: msg [-m nmsg] [-p nproc]";
  532. int maxmsg, nmsg, nmsgstarve, maxproc, nproc, nprocstarve;
  533. maxmsg = maxproc = 0;
  534. ARGBEGIN{
  535. default:
  536. return cliError(usage);
  537. case 'm':
  538. p = ARGF();
  539. if(p == nil)
  540. return cliError(usage);
  541. maxmsg = strtol(argv[0], &p, 0);
  542. if(maxmsg <= 0 || p == argv[0] || *p != '\0')
  543. return cliError(usage);
  544. break;
  545. case 'p':
  546. p = ARGF();
  547. if(p == nil)
  548. return cliError(usage);
  549. maxproc = strtol(argv[0], &p, 0);
  550. if(maxproc <= 0 || p == argv[0] || *p != '\0')
  551. return cliError(usage);
  552. break;
  553. }ARGEND
  554. if(argc)
  555. return cliError(usage);
  556. vtLock(mbox.alock);
  557. if(maxmsg)
  558. mbox.maxmsg = maxmsg;
  559. maxmsg = mbox.maxmsg;
  560. nmsg = mbox.nmsg;
  561. nmsgstarve = mbox.nmsgstarve;
  562. vtUnlock(mbox.alock);
  563. vtLock(mbox.rlock);
  564. if(maxproc)
  565. mbox.maxproc = maxproc;
  566. maxproc = mbox.maxproc;
  567. nproc = mbox.nproc;
  568. nprocstarve = mbox.nprocstarve;
  569. vtUnlock(mbox.rlock);
  570. consPrint("\tmsg -m %d -p %d\n", maxmsg, maxproc);
  571. consPrint("\tnmsg %d nmsgstarve %d nproc %d nprocstarve %d\n",
  572. nmsg, nmsgstarve, nproc, nprocstarve);
  573. return 1;
  574. }
  575. static int
  576. scmp(Fid *a, Fid *b)
  577. {
  578. if(a == 0)
  579. return 1;
  580. if(b == 0)
  581. return -1;
  582. return strcmp(a->uname, b->uname);
  583. }
  584. static Fid*
  585. fidMerge(Fid *a, Fid *b)
  586. {
  587. Fid *s, **l;
  588. l = &s;
  589. while(a || b){
  590. if(scmp(a, b) < 0){
  591. *l = a;
  592. l = &a->sort;
  593. a = a->sort;
  594. }else{
  595. *l = b;
  596. l = &b->sort;
  597. b = b->sort;
  598. }
  599. }
  600. *l = 0;
  601. return s;
  602. }
  603. static Fid*
  604. fidMergeSort(Fid *f)
  605. {
  606. int delay;
  607. Fid *a, *b;
  608. if(f == nil)
  609. return nil;
  610. if(f->sort == nil)
  611. return f;
  612. a = b = f;
  613. delay = 1;
  614. while(a && b){
  615. if(delay) /* easy way to handle 2-element list */
  616. delay = 0;
  617. else
  618. a = a->sort;
  619. if(b = b->sort)
  620. b = b->sort;
  621. }
  622. b = a->sort;
  623. a->sort = nil;
  624. a = fidMergeSort(f);
  625. b = fidMergeSort(b);
  626. return fidMerge(a, b);
  627. }
  628. static int
  629. cmdWho(int argc, char* argv[])
  630. {
  631. char *usage = "usage: who";
  632. int i, l1, l2, l;
  633. Con *con;
  634. Fid *fid, *last;
  635. ARGBEGIN{
  636. default:
  637. return cliError(usage);
  638. }ARGEND
  639. if(argc > 0)
  640. return cliError(usage);
  641. vtRLock(cbox.clock);
  642. l1 = 0;
  643. l2 = 0;
  644. for(con=cbox.chead; con; con=con->cnext){
  645. if((l = strlen(con->name)) > l1)
  646. l1 = l;
  647. if((l = strlen(con->remote)) > l2)
  648. l2 = l;
  649. }
  650. for(con=cbox.chead; con; con=con->cnext){
  651. consPrint("\t%-*s %-*s", l1, con->name, l2, con->remote);
  652. vtLock(con->fidlock);
  653. last = nil;
  654. for(i=0; i<NFidHash; i++)
  655. for(fid=con->fidhash[i]; fid; fid=fid->hash)
  656. if(fid->fidno != NOFID && fid->uname){
  657. fid->sort = last;
  658. last = fid;
  659. }
  660. fid = fidMergeSort(last);
  661. last = nil;
  662. for(; fid; last=fid, fid=fid->sort)
  663. if(last==nil || strcmp(fid->uname, last->uname) != 0)
  664. consPrint(" %q", fid->uname);
  665. vtUnlock(con->fidlock);
  666. consPrint("\n");
  667. }
  668. vtRUnlock(cbox.clock);
  669. return 1;
  670. }
  671. void
  672. msgInit(void)
  673. {
  674. mbox.alock = vtLockAlloc();
  675. mbox.arendez = vtRendezAlloc(mbox.alock);
  676. mbox.rlock = vtLockAlloc();
  677. mbox.rrendez = vtRendezAlloc(mbox.rlock);
  678. mbox.maxmsg = NMsgInit;
  679. mbox.maxproc = NMsgProcInit;
  680. mbox.msize = NMsizeInit;
  681. cliAddCmd("msg", cmdMsg);
  682. }
  683. static int
  684. cmdCon(int argc, char* argv[])
  685. {
  686. char *p;
  687. Con *con;
  688. char *usage = "usage: con [-m ncon]";
  689. int maxcon, ncon, nconstarve;
  690. maxcon = 0;
  691. ARGBEGIN{
  692. default:
  693. return cliError(usage);
  694. case 'm':
  695. p = ARGF();
  696. if(p == nil)
  697. return cliError(usage);
  698. maxcon = strtol(argv[0], &p, 0);
  699. if(maxcon <= 0 || p == argv[0] || *p != '\0')
  700. return cliError(usage);
  701. break;
  702. }ARGEND
  703. if(argc)
  704. return cliError(usage);
  705. vtLock(cbox.clock);
  706. if(maxcon)
  707. cbox.maxcon = maxcon;
  708. maxcon = cbox.maxcon;
  709. ncon = cbox.ncon;
  710. nconstarve = cbox.nconstarve;
  711. vtUnlock(cbox.clock);
  712. consPrint("\tcon -m %d\n", maxcon);
  713. consPrint("\tncon %d nconstarve %d\n", ncon, nconstarve);
  714. vtRLock(cbox.clock);
  715. for(con = cbox.chead; con != nil; con = con->cnext){
  716. consPrint("\t%s\n", con->name);
  717. }
  718. vtRUnlock(cbox.clock);
  719. return 1;
  720. }
  721. void
  722. conInit(void)
  723. {
  724. cbox.alock = vtLockAlloc();
  725. cbox.arendez = vtRendezAlloc(cbox.alock);
  726. cbox.clock = vtLockAlloc();
  727. cbox.maxcon = NConInit;
  728. cbox.msize = NMsizeInit;
  729. cliAddCmd("con", cmdCon);
  730. cliAddCmd("who", cmdWho);
  731. }