123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606 |
- .HTML "Changes to the Programming Environment in the Fourth Release of Plan 9
- .FP lucidasans
- .TL
- Changes to the Programming Environment
- .br
- in the
- .br
- Fourth Release of Plan 9
- .AU
- Rob Pike
- .sp
- rob@plan9.bell-labs.com
- .SH
- Introduction
- .PP
- The fourth release of Plan 9 includes changes at many levels of the system,
- with repercussions in the libraries and program interfaces.
- This document summarizes the changes and describes how
- existing programs must be modified to run in the new release.
- It is not exhaustive, of course; for further detail about any of the
- topics refer to the manual pages, as always.
- .PP
- Programmers new to Plan 9 may find valuable tidbits here, but the
- real audience for this paper is those with a need to update applications
- and servers written in C for earlier releases of the Plan 9 operating system.
- .SH
- 9P, NAMELEN, and strings
- .PP
- The underlying file service protocol for Plan 9, 9P, retains its basic form
- but has had a number of adjustments to deal with longer file names and error strings,
- new authentication mechanisms, and to make it more efficient at
- evaluating file names.
- The change to file names affects a number of system interfaces;
- because file name elements are no longer of fixed size, they can
- no longer be stored as arrays.
- .PP
- 9P used to be a fixed-format protocol with
- .CW NAMELEN -sized
- byte arrays representing file name elements.
- Now, it is a variable-format protocol, as described in
- .I intro (5),
- in which strings are represented by a count followed by that many bytes.
- Thus, the string
- .CW ken
- would previously have occupied 28
- .CW NAMELEN ) (
- bytes in the message; now it occupies 5: a two-byte count followed by the three bytes of
- .CW ken
- and no terminal zero.
- (And of course, a name could now be much longer.)
- A similar format change has been made to
- .CW stat
- buffers: they are no longer
- .CW DIRLEN
- bytes long but instead have variable size prefixed by a two-byte count.
- And in fact the entire 9P message syntax has changed: every message
- now begins with a message length field that makes it trivial to break the
- string into messages without parsing them, so
- .CW aux/fcall
- is gone.
- A new library entry point,
- .CW read9pmsg ,
- makes it easy for user-level servers to break the client data stream into 9P messages.
- All servers should switch from using
- .CW read
- (or the now gone
- .CW getS)
- to using
- .CW read9pmsg .
- .PP
- This change to 9P affects the way strings are handled by the kernel and throughout
- the system.
- The consequences are primarily that fixed-size arrays have been replaced
- by pointers and counts in a variety of system interfaces.
- Most programs will need at least some adjustment to the new style.
- In summary:
- .CW NAMELEN
- is gone, except as a vestige in the authentication libraries, where it has been
- rechristened
- .CW ANAMELEN .
- .CW DIRLEN
- and
- .CW ERRLEN
- are also gone.
- All programs that mention
- these constants
- will need to be fixed.
- .PP
- The simplest place to see this change is in the
- .CW errstr
- system call, which no longer assumes a buffer of length
- .CW ERRLEN
- but now requires a byte-count argument:
- .P1
- char buf[...];
- errstr(buf, sizeof buf);
- .P2
- The buffer can be any size you like.
- For convenience, the kernel stores error strings internally as 256-byte arrays,
- so if you like \(em but it's not required \(em you can use the defined constant
- .CW ERRMAX= 256
- as a good buffer size.
- Unlike the old
- .CW ERRLEN
- (which had value 64),
- .CW ERRMAX
- is advisory, not mandatory, and is not part of the 9P specification.
- .PP
- With names, stat buffers, and directories, there isn't even an echo of a fixed-size array any more.
- .SH
- Directories and wait messages
- .PP
- With strings now variable-length, a number of system calls needed to change:
- .CW errstr ,
- .CW stat ,
- .CW fstat ,
- .CW wstat ,
- .CW fwstat ,
- and
- .CW wait
- are all affected, as is
- .CW read
- when applied to directories.
- .PP
- As far as directories are concerned, most programs don't use the system calls
- directly anyway, since they operate on the machine-independent form, but
- instead call the machine-dependent
- .CW Dir
- routines
- .CW dirstat ,
- .CW dirread ,
- etc.
- These used to fill user-provided fixed-size buffers; now they return objects allocated
- by
- .CW malloc
- (which must therefore be freed after use).
- To `stat' a file:
- .P1
- Dir *d;
- d = dirstat(filename);
- if(d == nil){
- fprint(2, "can't stat %s: %r\en", filename);
- exits("stat");
- }
- use(d);
- free(d);
- .P2
- A common new bug is to forget to free a
- .CW Dir
- returned by
- .CW dirstat .
- .PP
- .CW Dirfstat
- and
- .CW Dirfwstat
- work pretty much as before, but changes to 9P make
- it possible to exercise finer-grained control on what fields
- of the
- .CW Dir
- are to be changed; see
- .I stat (2)
- and
- .I stat (5)
- for details.
- .PP
- Reading a directory works in a similar way to
- .CW dirstat ,
- with
- .CW dirread
- allocating and filling in an array of
- .CW Dir
- structures.
- The return value is the number of elements of the array.
- The arguments to
- .CW dirread
- now include a pointer to a
- .CW Dir*
- to be filled in with the address of the allocated array:
- .P1
- Dir *d;
- int i, n;
- while((n = dirread(fd, &d)) > 0){
- for(i=0; i<n; i++)
- use(&d[i]);
- free(d);
- }
- .P2
- A new library function,
- .CW dirreadall ,
- has the same form as
- .CW dirread
- but returns the entire directory in one call:
- .P1
- n = dirreadall(fd, &d)
- for(i=0; i<n; i++)
- use(&d[i]);
- free(d);
- .P2
- If your program insists on using the underlying
- .CW stat
- system call or its relatives, or wants to operate directly on the
- machine-independent format returned by
- .CW stat
- or
- .CW read ,
- it will need to be modified.
- Such programs are rare enough that we'll not discuss them here beyond referring to
- the man page
- .I stat (2)
- for details.
- Be aware, though, that it used to be possible to regard the buffer returned by
- .CW stat
- as a byte array that began with the zero-terminated
- name of the file; this is no longer true.
- With very rare exceptions, programs that call
- .CW stat
- would be better recast to use the
- .CW dir
- routines or, if their goal is just to test the existence of a file,
- .CW access .
- .PP
- Similar changes have affected the
- .CW wait
- system call. In fact,
- .CW wait
- is no longer a system call but a library routine that calls the new
- .CW await
- system call and returns a newly allocated machine-dependent
- .CW Waitmsg
- structure:
- .P1
- Waitmsg *w;
- w = wait();
- if(w == nil)
- error("wait: %r");
- print("pid is %d; exit string %s\en", w->pid, w->msg);
- free(w);
- .P2
- The exit string
- .CW w->msg
- may be empty but it will never be a nil pointer.
- Again, don't forget to free the structure returned by
- .CW wait .
- If all you need is the pid, you can call
- .CW waitpid ,
- which reports just the pid and doesn't return an allocated structure:
- .P1
- int pid;
- pid = waitpid();
- if(pid < 0)
- error("wait: %r");
- print("pid is %d\en", pid);
- .P2
- .SH
- Quoted strings and tokenize
- .PP
- .CW Wait
- gives us a good opportunity to describe how the system copes with all this
- free-format data.
- Consider the text returned by the
- .CW await
- system call, which includes a set of integers (pids and times) and a string (the exit status).
- This information is formatted free-form; here is the statement in the kernel that
- generates the message:
- .P1
- n = snprint(a, n, "%d %lud %lud %lud %q",
- wq->w.pid,
- wq->w.time[TUser], wq->w.time[TSys], wq->w.time[TReal],
- wq->w.msg);
- .P2
- Note the use of
- .CW %q
- to produce a quoted-string representation of the exit status.
- The
- .CW %q
- format is like %s but will wrap
- .CW rc -style
- single quotes around the string if it contains white space or is otherwise ambiguous.
- The library routine
- .CW tokenize
- can be used to parse data formatted this way: it splits white-space-separated
- fields but understands the
- .CW %q
- quoting conventions.
- Here is how the
- .CW wait
- library routine builds its
- .CW Waitmsg
- from the data returned by
- .CW await :
- .P1
- Waitmsg*
- wait(void)
- {
- int n, l;
- char buf[512], *fld[5];
- Waitmsg *w;
- n = await(buf, sizeof buf-1);
- if(n < 0)
- return nil;
- buf[n] = '\0';
- if(tokenize(buf, fld, nelem(fld)) != nelem(fld)){
- werrstr("couldn't parse wait message");
- return nil;
- }
- l = strlen(fld[4])+1;
- w = malloc(sizeof(Waitmsg)+l);
- if(w == nil)
- return nil;
- w->pid = atoi(fld[0]);
- w->time[0] = atoi(fld[1]);
- w->time[1] = atoi(fld[2]);
- w->time[2] = atoi(fld[3]);
- w->msg = (char*)&w[1];
- memmove(w->msg, fld[4], l);
- return w;
- }
- .P2
- .PP
- This style of quoted-string and
- .CW tokenize
- is used all through the system now.
- In particular, devices now
- .CW tokenize
- the messages written to their
- .CW ctl
- files, which means that you can send messages that contain white space, by quoting them,
- and that you no longer need to worry about whether or not the device accepts a newline.
- In other words, you can say
- .P1
- echo message > /dev/xx/ctl
- .P2
- instead of
- .CW echo
- .CW -n
- because
- .CW tokenize
- treats the newline character as white space and discards it.
- .PP
- While we're on the subject of quotes and strings, note that the implementation of
- .CW await
- used
- .CW snprint
- rather than
- .CW sprint .
- We now deprecate
- .CW sprint
- because it has no protection against buffer overflow.
- We prefer
- .CW snprint
- or
- .CW seprint ,
- to constrain the output.
- The
- .CW %q
- format is cleverer than most in this regard:
- if the string is too long to be represented in full,
- .CW %q
- is smart enough to produce a truncated but correctly quoted
- string within the available space.
- .SH
- Mount
- .PP
- Although strings in 9P are now variable-length and not zero-terminated,
- this has little direct effect in most of the system interfaces.
- File and user names are still zero-terminated strings as always;
- the kernel does the work of translating them as necessary for
- transport.
- And of course, they are now free to be as long as you might want;
- the only hard limit is that their length must be represented in 16 bits.
- .PP
- One example where this matters is that the file system specification in the
- .CW mount
- system call can now be much longer.
- Programs like
- .CW rio
- that used the specification string in creative ways were limited by the
- .CW NAMELEN
- restriction; now they can use the string more freely.
- .CW Rio
- now accepts a simple but less cryptic specification language for the window
- to be created by the
- .CW mount
- call, e.g.:
- .P1
- % mount $wsys /mnt/wsys 'new -dx 250 -dy 250 -pid 1234'
- .P2
- In the old system, this sort of control was impossible through the
- .CW mount
- interface.
- .PP
- While we're on the subject of
- .CW mount ,
- note that with the new security architecture
- (see
- .I factotum (4)),
- 9P has moved its authentication outside the protocol proper.
- (For a full description of this change to 9P, see
- .I fauth (2),
- .I attach (5),
- and the paper
- .I "Security in Plan 9\f1.)
- The most explicit effect of this change is that
- .CW mount
- now takes another argument,
- .CW afd ,
- a file descriptor for the
- authentication file through which the authentication will be made.
- For most user-level file servers, which do not require authentication, it is
- sufficient to provide
- .CW -1
- as the value of
- .CW afd:
- .P1
- if(mount(fd, -1, "/mnt/wsys", MREPL,
- "new -dx 250 -dy 250 -pid 1234") < 0)
- error("mount failed: %r");
- .P2
- To connect to servers that require authentication, use the new
- .CW fauth
- system call or the reimplemented
- .CW amount
- (authenticated mount) library call.
- In fact, since
- .CW amount
- handles both authenticating and non-authenticating servers, it is often
- easiest just to replace calls to
- .CW mount
- by calls to
- .CW amount ;
- see
- .I auth (2)
- for details.
- .SH
- Print
- .PP
- The C library has been heavily reworked in places.
- Besides the changes mentioned above, it
- now has a much more complete set of routines for handling
- .CW Rune
- strings (that is, zero-terminated arrays of 16-bit character values).
- The most sweeping changes, however, are in the way formatted I/O is performed.
- .PP
- The
- .CW print
- routine and all its relatives have been reimplemented to offer a number
- of improvements:
- .IP (1)
- Better buffer management, including the provision of an internal flush
- routine, makes it unnecessary to provide large buffers.
- For example,
- .CW print
- uses a much smaller buffer now (reducing stack load) while simultaneously
- removing the need to truncate the output string if it doesn't fit in the buffer.
- .IP (2)
- Global variables have been eliminated so no locking is necessary.
- .IP (3)
- The combination of (1) and (2) means that the standard implementation of
- .CW print
- now works fine in threaded programs, and
- .CW threadprint
- is gone.
- .IP (4)
- The new routine
- .CW smprint
- prints into, and returns, storage allocated on demand by
- .CW malloc .
- .IP (5)
- It is now possible to print into a
- .CW Rune
- string; for instance,
- .CW runesmprint
- is the
- .CW Rune
- analog of
- .CW smprint .
- .IP (6)
- There is improved support for custom
- print verbs and custom output routines such as error handlers.
- The routine
- .CW doprint
- is gone, but
- .CW vseprint
- can always be used instead.
- However, the new routines
- .CW fmtfdinit ,
- .CW fmtstrinit ,
- .CW fmtprint ,
- and friends
- are often a better replacement.
- The details are too long for exposition here;
- .I fmtinstall (2)
- explains the new interface and provides examples.
- .IP (7)
- Two new format flags, space and comma, close somewhat the gap between
- Plan 9 and ANSI C.
- .PP
- Despite these changes, most programs will be unaffected;
- .CW print
- is still
- .CW print .
- Don't forget, though, that
- you should eliminate calls to
- .CW sprint
- and use the
- .CW %q
- format when appropriate.
- .SH
- Binary compatibility
- .PP
- The discussion so far has been about changes at the source level.
- Existing binaries will probably run without change in the new
- environment, since the kernel provides backward-compatible
- system calls for
- .CW errstr ,
- .CW stat ,
- .CW wait ,
- etc.
- The only exceptions are programs that do either a
- .CW mount
- system call, because of the security changes and because
- the file descriptor in
- .CW mount
- must point to a new 9P connection; or a
- .CW read
- system call on a directory, since the returned data will
- be in the new format.
- A moment's reflection will discover that this means old
- user-level file servers will need to be fixed to run on the new system.
- .SH
- File servers
- .PP
- A full description of what user-level servers must do to provide service with
- the new 9P is beyond the scope of this paper.
- Your best source of information is section 5 of the manual,
- combined with study of a few examples.
- .CW /sys/src/cmd/ramfs.c
- is a simple example; it has a counterpart
- .CW /sys/src/lib9p/ramfs.c
- that implements the same service using the new
- .I 9p (2)
- library.
- .PP
- That said, it's worth summarizing what to watch for when converting a file server.
- The
- .CW session
- message is gone, and there is a now a
- .CW version
- message that is exchanged at the start of a connection to establish
- the version of the protocol to use (there's only one at the moment, identified by
- the string
- .CW 9P2000 )
- and what the maximum message size will be.
- This negotiation makes it easier to handle 9P encapsulation, such as with
- .CW exportfs ,
- and also permits larger message sizes when appropriate.
- .PP
- If your server wants to authenticate, it will need to implement an authentication file
- and implement the
- .CW auth
- message; otherwise it should return a helpful error string to the
- .CW Tauth
- request to signal that authentication is not required.
- .PP
- The handling of
- .CW stat
- and directory reads will require some changes but they should not be fundamental.
- Be aware that seeking on directories is forbidden, so it is fine if you disregard the
- file offset when implementing directory reads; this makes it a little easier to handle
- the variable-length entries.
- You should still never return a partial directory entry; if the I/O count is too small
- to return even one entry, you should return two bytes containing the byte count
- required to represent the next entry in the directory.
- User code can use this value to formulate a retry if it desires.
- See the
- DIAGNOSTICS section of
- .I stat (2)
- for a description of this process.
- .PP
- The trickiest part of updating a file server is that the
- .CW clone
- and
- .CW walk
- messages have been merged into a single message, a sort of `clone-multiwalk'.
- The new message, still called
- .CW walk ,
- proposes a sequence of file name elements to be evaluated using a possibly
- cloned fid.
- The return message contains the qids of the files reached by
- walking to the sequential elements.
- If all the elements can be walked, the fid will be cloned if requested.
- If a non-zero number of elements are requested, but none
- can be walked, an error should be returned.
- If only some can be walked, the fid is not cloned, the original fid is left
- where it was, and the returned
- .CW Rwalk
- message should contain the partial list of successfully reached qids.
- See
- .I walk (5)
- for a full description.
|