| t9p.3 - plan9port - [fork] Plan 9 from user space | |
| git clone git://src.adamsgaard.dk/plan9port | |
| Log | |
| Files | |
| Refs | |
| README | |
| LICENSE | |
| --- | |
| t9p.3 (15034B) | |
| --- | |
| 1 .TH 9P 3 | |
| 2 .SH NAME | |
| 3 Srv, | |
| 4 dirread9p, | |
| 5 emalloc9p, | |
| 6 erealloc9p, | |
| 7 estrdup9p, | |
| 8 postfd, | |
| 9 readbuf, | |
| 10 readstr, | |
| 11 respond, | |
| 12 srv, | |
| 13 threadpostmountsrv, | |
| 14 walkandclone \- 9P file service | |
| 15 .SH SYNOPSIS | |
| 16 .ft L | |
| 17 .nf | |
| 18 #include <u.h> | |
| 19 #include <libc.h> | |
| 20 #include <fcall.h> | |
| 21 #include <thread.h> | |
| 22 #include <9p.h> | |
| 23 .fi | |
| 24 .PP | |
| 25 .ft L | |
| 26 .nf | |
| 27 .ta \w'\fL1234'u +\w'\fLTree* 'u | |
| 28 typedef struct Srv { | |
| 29 Tree* tree; | |
| 30 | |
| 31 void (*attach)(Req *r); | |
| 32 void (*auth)(Req *r); | |
| 33 void (*open)(Req *r); | |
| 34 void (*create)(Req *r); | |
| 35 void (*read)(Req *r); | |
| 36 void (*write)(Req *r); | |
| 37 void (*remove)(Req *r); | |
| 38 void (*flush)(Req *r); | |
| 39 void (*stat)(Req *r); | |
| 40 void (*wstat)(Req *r); | |
| 41 void (*walk)(Req *r); | |
| 42 | |
| 43 char* (*walk1)(Fid *fid, char *name, Qid *qid); | |
| 44 char* (*clone)(Fid *oldfid, Fid *newfid); | |
| 45 | |
| 46 void (*destroyfid)(Fid *fid); | |
| 47 void (*destroyreq)(Req *r); | |
| 48 void (*start)(Srv *s); | |
| 49 void (*end)(Srv *s); | |
| 50 void* aux; | |
| 51 | |
| 52 int infd; | |
| 53 int outfd; | |
| 54 int srvfd; | |
| 55 int nopipe; | |
| 56 } Srv; | |
| 57 .fi | |
| 58 .PP | |
| 59 .nf | |
| 60 .ft L | |
| 61 .ta \w'\fLvoid* 'u | |
| 62 int srv(Srv *s) | |
| 63 void threadpostmountsrv(Srv *s, char *name, char *mtpt, int flag) | |
| 64 int postfd(char *srvname, int fd) | |
| 65 void respond(Req *r, char *error) | |
| 66 ulong readstr(Req *r, char *src) | |
| 67 ulong readbuf(Req *r, void *src, ulong nsrc) | |
| 68 typedef int Dirgen(int n, Dir *dir, void *aux) | |
| 69 void dirread9p(Req *r, Dirgen *gen, void *aux) | |
| 70 void walkandclone(Req *r, char *(*walk1)(Fid *old, char *name, vo… | |
| 71 char *(*clone)(Fid *old, Fid *new, void *v), void *v) | |
| 72 .fi | |
| 73 .PP | |
| 74 .nf | |
| 75 .ft L | |
| 76 .ta \w'\fLvoid* 'u | |
| 77 void* emalloc9p(ulong n) | |
| 78 void* erealloc9p(void *v, ulong n) | |
| 79 char* estrdup9p(char *s) | |
| 80 .fi | |
| 81 .PP | |
| 82 .nf | |
| 83 .ft L | |
| 84 extern int chatty9p; | |
| 85 .fi | |
| 86 .SH DESCRIPTION | |
| 87 The function | |
| 88 .I srv | |
| 89 serves a 9P session by reading requests from | |
| 90 .BR s->infd , | |
| 91 dispatching them to the function pointers kept in | |
| 92 .BR Srv , | |
| 93 and | |
| 94 writing the responses to | |
| 95 .BR s->outfd . | |
| 96 (Typically, | |
| 97 .I threadpostmountsrv | |
| 98 initializes the | |
| 99 .B infd | |
| 100 and | |
| 101 .B outfd | |
| 102 structure members. See the description below.) | |
| 103 .PP | |
| 104 .B Req | |
| 105 and | |
| 106 .B Fid | |
| 107 structures are allocated one-to-one with uncompleted | |
| 108 requests and active fids, and are described in | |
| 109 .MR 9p-fid (3) . | |
| 110 .PP | |
| 111 The behavior of | |
| 112 .I srv | |
| 113 depends on whether there is a file tree | |
| 114 (see | |
| 115 .MR 9p-file (3) ) | |
| 116 associated with the server, that is, | |
| 117 whether the | |
| 118 .B tree | |
| 119 element is nonzero. | |
| 120 The differences are made explicit in the | |
| 121 discussion of the service loop below. | |
| 122 The | |
| 123 .B aux | |
| 124 element is the client's, to do with as it pleases. | |
| 125 .PP | |
| 126 .I Srv | |
| 127 does not return until the 9P conversation is finished. | |
| 128 Since it is usually run in a separate process so that | |
| 129 the caller can exit, the service loop has little chance | |
| 130 to return gracefully on out of memory errors. | |
| 131 It calls | |
| 132 .IR emalloc9p , | |
| 133 .IR erealloc9p , | |
| 134 and | |
| 135 .I estrdup9p | |
| 136 to obtain its memory. | |
| 137 The default implementations of these functions | |
| 138 act as | |
| 139 .IR malloc , | |
| 140 .IR realloc , | |
| 141 and | |
| 142 .I strdup | |
| 143 but abort the program if they run out of memory. | |
| 144 If alternate behavior is desired, clients can link against | |
| 145 alternate implementations of these functions. | |
| 146 .PP | |
| 147 .I threadpostmountsrv | |
| 148 is a wrapper that creates a separate process in which to run | |
| 149 .IR srv . | |
| 150 It does the following: | |
| 151 .IP | |
| 152 If | |
| 153 .IB s -> nopipe | |
| 154 is zero (the common case), | |
| 155 initialize | |
| 156 .IB s -> infd | |
| 157 and | |
| 158 .IB s -> outfd | |
| 159 to be one end of a freshly allocated pipe, | |
| 160 with | |
| 161 .IB s -> srvfd | |
| 162 initialized as the other end. | |
| 163 .IP | |
| 164 If | |
| 165 .B name | |
| 166 is non-nil, call | |
| 167 .BI postfd( s -> srvfd , | |
| 168 .IB name ) | |
| 169 to post | |
| 170 .IB s -> srvfd | |
| 171 as | |
| 172 .BI /srv/ name . | |
| 173 .IP | |
| 174 Fork a child process via | |
| 175 .MR rfork (3) | |
| 176 or | |
| 177 .I procrfork | |
| 178 (see | |
| 179 .MR thread (3) ), | |
| 180 using the | |
| 181 .BR RFFDG , | |
| 182 .RR RFNOTEG , | |
| 183 .BR RFNAMEG , | |
| 184 and | |
| 185 .BR RFMEM | |
| 186 flags. | |
| 187 The child process | |
| 188 calls | |
| 189 .IB close( s -> srvfd ) | |
| 190 and then | |
| 191 .IB srv( s ) \fR; | |
| 192 it will exit once | |
| 193 .I srv | |
| 194 returns. | |
| 195 .IP | |
| 196 If | |
| 197 .I mtpt | |
| 198 is non-nil, | |
| 199 call | |
| 200 .BI amount( s -> srvfd, | |
| 201 .IB mtpt , | |
| 202 .IB flag , | |
| 203 \fB"")\fR; | |
| 204 otherwise, close | |
| 205 .IB s -> srvfd \fR. | |
| 206 .IP | |
| 207 The parent returns to the caller. | |
| 208 .LP | |
| 209 If any error occurs during | |
| 210 this process, the entire process is terminated by calling | |
| 211 .MR sysfatal (3) . | |
| 212 .SS Service functions | |
| 213 The functions in a | |
| 214 .B Srv | |
| 215 structure named after 9P transactions | |
| 216 are called to satisfy requests as they arrive. | |
| 217 If a function is provided, it | |
| 218 .I must | |
| 219 arrange for | |
| 220 .I respond | |
| 221 to be called when the request is satisfied. | |
| 222 The only parameter of each service function | |
| 223 is a | |
| 224 .B Req* | |
| 225 parameter (say | |
| 226 .IR r ). | |
| 227 The incoming request parameters are stored in | |
| 228 .IB r -> ifcall \fR; | |
| 229 .IB r -> fid | |
| 230 and | |
| 231 .IB r -> newfid | |
| 232 are pointers to | |
| 233 .B Fid | |
| 234 structures corresponding to the | |
| 235 numeric fids in | |
| 236 .IB r -> ifcall \fR; | |
| 237 similarly, | |
| 238 .IB r -> oldreq | |
| 239 is the | |
| 240 .B Req | |
| 241 structure corresponding to | |
| 242 .IB r -> ifcall.oldtag \fR. | |
| 243 The outgoing response data should be stored in | |
| 244 .IB r -> ofcall \fR. | |
| 245 The one exception to this rule is that | |
| 246 .I stat | |
| 247 should fill in | |
| 248 .IB r -> d | |
| 249 rather than | |
| 250 .IB r -> ofcall.stat \fR: | |
| 251 the library will convert the structure into the machine-independent | |
| 252 wire representation. | |
| 253 Similarly, | |
| 254 .I wstat | |
| 255 may consult | |
| 256 .IB r -> d | |
| 257 rather than decoding | |
| 258 .IB r -> ifcall . stat | |
| 259 itself. | |
| 260 When a request has been handled, | |
| 261 .I respond | |
| 262 should be called with | |
| 263 .I r | |
| 264 and an error string. | |
| 265 If the request was satisfied successfully, the error | |
| 266 string should be a nil pointer. | |
| 267 Note that it is permissible for a function to return | |
| 268 without itself calling | |
| 269 .IR respond , | |
| 270 as long as it has arranged for | |
| 271 .I respond | |
| 272 to be called at some point in the future | |
| 273 by another proc sharing its address space, | |
| 274 but see the discussion of | |
| 275 .I flush | |
| 276 below. | |
| 277 Once | |
| 278 .I respond | |
| 279 has been called, the | |
| 280 .B Req* | |
| 281 as well as any pointers it once contained must | |
| 282 be considered freed and not referenced. | |
| 283 .PP | |
| 284 If the service loop detects an error in a request | |
| 285 (e.g., an attempt to reuse an extant fid, an open of | |
| 286 an already open fid, a read from a fid opened for write, etc.) | |
| 287 it will reply with an error without consulting | |
| 288 the service functions. | |
| 289 .PP | |
| 290 The service loop provided by | |
| 291 .I srv | |
| 292 (and indirectly by | |
| 293 .I threadpostmountsrv ) | |
| 294 is single-threaded. | |
| 295 If it is expected that some requests might | |
| 296 block, arranging for alternate processes | |
| 297 to handle them is suggested. | |
| 298 .PP | |
| 299 The constraints on the service functions are as follows. | |
| 300 These constraints are checked while the server executes. | |
| 301 If a service function fails to do something it ought to have, | |
| 302 .I srv | |
| 303 will call | |
| 304 .I end | |
| 305 and then abort. | |
| 306 .TP | |
| 307 .I Auth | |
| 308 If authentication is desired, | |
| 309 the | |
| 310 .I auth | |
| 311 function should record that | |
| 312 .I afid | |
| 313 is the new authentication fid and | |
| 314 set | |
| 315 .I afid->qid | |
| 316 and | |
| 317 .IR ofcall.qid . | |
| 318 .I Auth | |
| 319 may be nil, in which case it will be treated as having | |
| 320 responded with the error | |
| 321 .RI `` "argv0: authentication not required" ,'' | |
| 322 where | |
| 323 .I argv0 | |
| 324 is the program name variable as set by | |
| 325 .I ARGBEGIN | |
| 326 (see | |
| 327 .MR arg (3) ). | |
| 328 .TP | |
| 329 .I Attach | |
| 330 The | |
| 331 .I attach | |
| 332 function should check the authentication state of | |
| 333 .I afid | |
| 334 if desired, | |
| 335 and set | |
| 336 .IB r -> fid -> qid | |
| 337 and | |
| 338 .I ofcall.qid | |
| 339 to the qid of the file system root. | |
| 340 .I Attach | |
| 341 may be nil only if file trees are in use; | |
| 342 in this case, the qid will be filled from the root | |
| 343 of the tree, and no authentication will be done. | |
| 344 .TP | |
| 345 .I Walk | |
| 346 If file trees are in use, | |
| 347 .I walk | |
| 348 is handled internally, and | |
| 349 .IB srv -> walk | |
| 350 is never called. | |
| 351 .IP | |
| 352 If file trees are not in use, | |
| 353 .I walk | |
| 354 should consult | |
| 355 .IB r -> ifcall . wname | |
| 356 and | |
| 357 .IB r -> ifcall . nwname \fR, | |
| 358 filling in | |
| 359 .IB ofcall . qid | |
| 360 and | |
| 361 .IB ofcall . nqid \fR, | |
| 362 and also copying any necessary | |
| 363 .I aux | |
| 364 state from | |
| 365 .IB r -> fid | |
| 366 to | |
| 367 .IB r -> newfid | |
| 368 when the two are different. | |
| 369 As long as | |
| 370 .I walk | |
| 371 sets | |
| 372 .IB ofcall . nqid | |
| 373 appropriately, it can | |
| 374 .I respond | |
| 375 with a nil error string even when 9P | |
| 376 demands an error | |
| 377 .RI ( e.g. , | |
| 378 in the case of a short walk); | |
| 379 the library detects error conditions and handles them appropriately. | |
| 380 .IP | |
| 381 Because implementing the full walk message is intricate and | |
| 382 prone to error, the helper routine | |
| 383 .I walkandclone | |
| 384 will handle the request given pointers to two functions | |
| 385 .I walk1 | |
| 386 and (optionally) | |
| 387 .I clone . | |
| 388 .IR Clone , | |
| 389 if non-nil, is called to signal the creation of | |
| 390 .I newfid | |
| 391 from | |
| 392 .IR oldfid . | |
| 393 Typically a | |
| 394 .I clone | |
| 395 routine will copy or increment a reference count in | |
| 396 .IR oldfid 's | |
| 397 .I aux | |
| 398 element. | |
| 399 .I Walk1 | |
| 400 should walk | |
| 401 .I fid | |
| 402 to | |
| 403 .IR name , | |
| 404 initializing | |
| 405 .IB fid -> qid | |
| 406 to the new path's qid. | |
| 407 Both should return nil | |
| 408 on success or an error message on error. | |
| 409 .I Walkandclone | |
| 410 will call | |
| 411 .I respond | |
| 412 after handling the request. | |
| 413 .TP | |
| 414 .I Walk1\fR, \fPClone | |
| 415 If the client provides functions | |
| 416 .IB srv -> walk1 | |
| 417 and (optionally) | |
| 418 .IB srv -> clone \fR, | |
| 419 the 9P service loop will call | |
| 420 .I walkandclone | |
| 421 with these functions to handle the request. | |
| 422 Unlike the | |
| 423 .I walk1 | |
| 424 above, | |
| 425 .IB srv -> walk1 | |
| 426 must fill in both | |
| 427 .IB fid -> qid | |
| 428 and | |
| 429 .BI * qid | |
| 430 with the new qid on a successful walk. | |
| 431 .TP | |
| 432 .I Open | |
| 433 If file trees are in use, the file | |
| 434 metadata will be consulted on open, create, remove, and wstat | |
| 435 to see if the requester has the appropriate permissions. | |
| 436 If not, an error will be sent back without consulting a service function. | |
| 437 .PP | |
| 438 If not using file trees or the user has the appropriate permissions, | |
| 439 .I open | |
| 440 is called with | |
| 441 .IB r -> ofcall . qid | |
| 442 already initialized to the one stored in the | |
| 443 .B Fid | |
| 444 structure (that is, the one returned in the previous walk). | |
| 445 If the qid changes, both should be updated. | |
| 446 .TP | |
| 447 .I Create | |
| 448 The | |
| 449 .I create | |
| 450 function must fill in | |
| 451 both | |
| 452 .IB r -> fid -> qid | |
| 453 and | |
| 454 .IB r -> ofcall . qid | |
| 455 on success. | |
| 456 When using file trees, | |
| 457 .I create | |
| 458 should allocate a new | |
| 459 .B File | |
| 460 with | |
| 461 .IR createfile ; | |
| 462 note that | |
| 463 .I createfile | |
| 464 may return nil (because, say, the file already exists). | |
| 465 If the | |
| 466 .I create | |
| 467 function is nil, | |
| 468 .I srv | |
| 469 behaves as though it were a function that always responded | |
| 470 with the error ``create prohibited''. | |
| 471 .TP | |
| 472 .I Remove | |
| 473 .I Remove | |
| 474 should mark the file as removed, whether | |
| 475 by calling | |
| 476 .I removefile | |
| 477 when using file trees, or by updating an internal data structure. | |
| 478 In general it is not a good idea to clean up the | |
| 479 .I aux | |
| 480 information associated with the corresponding | |
| 481 .B File | |
| 482 at this time, to avoid memory errors if other | |
| 483 fids have references to that file. | |
| 484 Instead, it is suggested that | |
| 485 .I remove | |
| 486 simply mark the file as removed (so that further | |
| 487 operations on it know to fail) and wait until the | |
| 488 file tree's destroy function is called to reclaim the | |
| 489 .I aux | |
| 490 pointer. | |
| 491 If not using file trees, it is prudent to take the | |
| 492 analogous measures. | |
| 493 If | |
| 494 .I remove | |
| 495 is not provided, all remove requests will draw | |
| 496 ``remove prohibited'' errors. | |
| 497 .TP | |
| 498 .I Read | |
| 499 The | |
| 500 .I read | |
| 501 function must be provided; it fills | |
| 502 .IB r -> ofcall . data | |
| 503 with at most | |
| 504 .IB r -> ifcall . count | |
| 505 bytes of data from offset | |
| 506 .IB r -> ifcall . offset | |
| 507 of the file. | |
| 508 It also sets | |
| 509 .IB r -> ofcall . count | |
| 510 to the number of bytes being returned. | |
| 511 If using file trees, | |
| 512 .I srv | |
| 513 will handle reads of directories internally, only | |
| 514 calling | |
| 515 .I read | |
| 516 for requests on files. | |
| 517 .I Readstr | |
| 518 and | |
| 519 .I readbuf | |
| 520 are useful for satisfying read requests on a string or buffer. | |
| 521 Consulting the request in | |
| 522 .IB r -> ifcall \fR, | |
| 523 they fill | |
| 524 .IB r -> ofcall . data | |
| 525 and set | |
| 526 .IB r -> ofcall . count \fR; | |
| 527 they do not call | |
| 528 .IB respond . | |
| 529 Similarly, | |
| 530 .I dirread9p | |
| 531 can be used to handle directory reads in servers | |
| 532 not using file trees. | |
| 533 The passed | |
| 534 .I gen | |
| 535 function will be called as necessary to | |
| 536 fill | |
| 537 .I dir | |
| 538 with information for the | |
| 539 .IR n th | |
| 540 entry in the directory. | |
| 541 The string pointers placed in | |
| 542 .I dir | |
| 543 should be fresh copies | |
| 544 made with | |
| 545 .IR estrdup9p ; | |
| 546 they will be freed by | |
| 547 .I dirread9p | |
| 548 after each successful call to | |
| 549 .IR gen . | |
| 550 .I Gen | |
| 551 should return zero if it successfully filled | |
| 552 .IR dir , | |
| 553 minus one on end of directory. | |
| 554 .TP | |
| 555 .I Write | |
| 556 The | |
| 557 .I write | |
| 558 function is similar but need not be provided. | |
| 559 If it is not, all writes will draw | |
| 560 ``write prohibited'' errors. | |
| 561 Otherwise, | |
| 562 .I write | |
| 563 should attempt to write the | |
| 564 .IB r -> ifcall . count | |
| 565 bytes of | |
| 566 .IB r -> ifcall . data | |
| 567 to offset | |
| 568 .IB r -> ifcall . offset | |
| 569 of the file, setting | |
| 570 .IB r -> ofcall . count | |
| 571 to the number of bytes actually written. | |
| 572 Most programs consider it an error to | |
| 573 write less than the requested amount. | |
| 574 .TP | |
| 575 .I Stat | |
| 576 .I Stat | |
| 577 should fill | |
| 578 .IB r -> d | |
| 579 with the stat information for | |
| 580 .IB r -> fid \fR. | |
| 581 If using file trees, | |
| 582 .IB r -> d | |
| 583 will have been initialized with the stat info from | |
| 584 the tree, and | |
| 585 .I stat | |
| 586 itself may be nil. | |
| 587 .TP | |
| 588 .I Wstat | |
| 589 The | |
| 590 .I wstat | |
| 591 consults | |
| 592 .IB r -> d | |
| 593 in changing the metadata for | |
| 594 .IB r -> fid | |
| 595 as described in | |
| 596 .IR stat (9p). | |
| 597 When using file trees, | |
| 598 .I srv | |
| 599 will take care to check that the request satisfies | |
| 600 the permissions outlined in | |
| 601 .IR stat (9p). | |
| 602 Otherwise | |
| 603 .I wstat | |
| 604 should take care to enforce permissions | |
| 605 where appropriate. | |
| 606 .TP | |
| 607 .I Flush | |
| 608 Servers that always call | |
| 609 .I respond | |
| 610 before returning from the service functions | |
| 611 need not provide a | |
| 612 .I flush | |
| 613 implementation: | |
| 614 .I flush | |
| 615 is only necessary in programs that | |
| 616 arrange for | |
| 617 .I respond | |
| 618 to be called asynchronously. | |
| 619 .I Flush | |
| 620 should cause the request | |
| 621 .IB r -> oldreq | |
| 622 to be cancelled or hurried along. | |
| 623 If | |
| 624 .I oldreq | |
| 625 is cancelled, this should be signalled by calling | |
| 626 .I respond | |
| 627 on | |
| 628 .I oldreq | |
| 629 with error string | |
| 630 .RB ` interrupted '. | |
| 631 .I Flush | |
| 632 must respond to | |
| 633 .I r | |
| 634 with a nil error string. | |
| 635 .I Flush | |
| 636 may respond to | |
| 637 .I r | |
| 638 before forcing a response to | |
| 639 .IB r -> oldreq \fR. | |
| 640 In this case, the library will delay sending | |
| 641 the | |
| 642 .I Rflush | |
| 643 message until the response to | |
| 644 .IB r -> oldreq | |
| 645 has been sent. | |
| 646 .PD | |
| 647 .PP | |
| 648 .IR Destroyfid , | |
| 649 .IR destroyreq , | |
| 650 .IR start , | |
| 651 and | |
| 652 .I end | |
| 653 are auxiliary functions, not called in direct response to 9P requests. | |
| 654 .TP | |
| 655 .I Destroyfid | |
| 656 When a | |
| 657 .BR Fid 's | |
| 658 reference count drops to zero | |
| 659 .RI ( i.e., | |
| 660 it has been clunked and there are no outstanding | |
| 661 requests referring to it), | |
| 662 .I destroyfid | |
| 663 is called to allow the program to dispose | |
| 664 of the | |
| 665 .IB fid -> aux | |
| 666 pointer. | |
| 667 .TP | |
| 668 .I Destroyreq | |
| 669 Similarly, when a | |
| 670 .BR Req 's | |
| 671 reference count drops to zero | |
| 672 .RI ( i.e. , | |
| 673 it has been handled via | |
| 674 .I respond | |
| 675 and other outstanding pointers to it have been closed), | |
| 676 .I destroyreq | |
| 677 is called to allow the program to dispose of the | |
| 678 .IB r -> aux | |
| 679 pointer. | |
| 680 .TP | |
| 681 .I Start | |
| 682 Before the 9P service loop begins, the service proc calls | |
| 683 .I start | |
| 684 so that the server can run any initialization that must be | |
| 685 done from inside the service proc. | |
| 686 .TP | |
| 687 .I End | |
| 688 Once the 9P service loop has finished | |
| 689 (end of file been reached on the service pipe | |
| 690 or a bad message has been read), | |
| 691 .I end | |
| 692 is called (if provided) to allow any final cleanup. | |
| 693 For example, it was used by the Palm Pilot synchronization | |
| 694 file system (never finished) to gracefully terminate the serial conversa… | |
| 695 the file system had been unmounted. | |
| 696 After calling | |
| 697 .IR end , | |
| 698 the service loop (which runs in a separate process | |
| 699 from its caller) terminates using | |
| 700 .I _exits | |
| 701 (see | |
| 702 .MR exits (3) ). | |
| 703 .PD | |
| 704 .PP | |
| 705 If the | |
| 706 .B chatty9p | |
| 707 flag is at least one, | |
| 708 a transcript of the 9P session is printed | |
| 709 on standard error. | |
| 710 If the | |
| 711 .B chatty9p | |
| 712 flag is greater than one, | |
| 713 additional unspecified debugging output is generated. | |
| 714 By convention, servers written using this library | |
| 715 accept the | |
| 716 .B -D | |
| 717 option to increment | |
| 718 .BR chatty9p . | |
| 719 .SH EXAMPLES | |
| 720 .B \*9/src/lib9p/ramfs.c | |
| 721 is an example of a simple single-threaded file server. | |
| 722 On Plan 9, see | |
| 723 .IR archfs , | |
| 724 .IR cdfs , | |
| 725 .IR nntpfs , | |
| 726 .IR webfs , | |
| 727 and | |
| 728 .I sshnet | |
| 729 for more examples. | |
| 730 .PP | |
| 731 In general, the | |
| 732 .B File | |
| 733 interface is appropriate for maintaining arbitrary file trees (as in | |
| 734 .IR ramfs ). | |
| 735 The | |
| 736 .B File | |
| 737 interface is best avoided when the | |
| 738 tree structure is easily generated as necessary; | |
| 739 this is true when the tree is highly structured (as in | |
| 740 .I cdfs | |
| 741 and | |
| 742 .IR nntpfs ) | |
| 743 or is maintained elsewhere. | |
| 744 .SH SOURCE | |
| 745 .B \*9/src/lib9p | |
| 746 .SH SEE ALSO | |
| 747 .MR 9p-fid (3) , | |
| 748 .MR 9p-file (3) , | |
| 749 .IR intro (9p) |