Errata for UNIX Network Programming, Volume 2

This file contains all typos found in the book UNIX Network Programming, Volume 2, Second Edition: Interprocess Communications by W. Richard Stevens, Prentice Hall, 1999, ISBN 0-13-081081-9.


Page Description
111 4th line from bottom of page: "message queue has been initialized" should be "message queue has not been initialized" (990107).
202 In the definitions of the two macros is_read_lockable and is_write_lockable the return value from lock_test should be negated, as in
#define is_read_lockable(fd, offset, whence, len) \
                        !lock_test(fd, F_RDLCK, offset, whence, len)
because the region is lockable if lock_test returns 0 (990105).
209 2nd line from bottom of page: "starvation of write locks" should be "starvation of writers" (990105).
233 1st line of item "1." near top of page: "In our first solution (Section 7.2)" should be "In our first solution (Section 7.3)" (990107).
249 1st line on page: "as each consumer thread terminates" should be "as each producer thread terminates" (990107).
263 The last line of the description for lines 19-23 ("A file is created ... is turned on.") should be the first line of the description for lines 24-32 (990107).
339 In the first two lines of the description for lines 19-31 the two occurrences of "consumer" should be "producer" (990208).
376 Line 22 of Figure 15.16: server1 should be serverunref1 (990215).
378 Line 23 of Figure 15.17: server1 should be serverunref2 (990215).
378 8th line from bottom of page (continuation line of command): "clientunref2 /tmp/door2 55 &" should be "clientunref2 /tmp/door2 66 &" (990215).
386 Fifth line from top of page: replace my_thread with my_create (990215).
386 Second line above Server creation procedure: replace "calls my_thread" with "specifies my_thread" (990215).
387 Line 27 of Figure 15.23: arg should be "(Door_server_proc *) iptr->di_proc" (990215).
387 Line 40 of Figure 15.23: my_thread should be my_create (990215).
388 Last line of page: replace my_thread with my_create (990215).
389 First line of server output (15 lines from bottom of page): replace my_thread with my_create (990215).
390 Second and fourth lines from top of page: replace my_thread with my_create (990215).
391 First line on page: replace thread_exit with pthread_exit (990215).
399 8th line from bottom of page: replace "or within threads in different processes" with "or between threads in different processes" (990226).
400 First line beneath Figure 16.1: replace "whose name end" with "whose names end" (990226).
417 In the 3rd paragaph ("An RPC packet ..."): replace the three occurrences of "picture" with "photo" (990226). In the 5th line of that paragraph, replace "different forms of credentials" with "different forms of verifiers".
424 Third line below Premature Termination of Server: replace clnt_call with clnt_create (990409).
441 First line of description for lines 20-22: replace "host-to-network" with "network-to-host" (990226).
531 Last line on page: replace my_thread with my_create (990215).
532 Second line for 16.1: replace "After we terminate out client" with "After we terminate out server" (990226).
532 Second line for 16.2: replace "The next reply for" with "The next reply from" (990226).
550 Add page 391 to the index entry for pthread_exit (990215).
557 Delete the index entry for thread_exit (990215).
Page Description
15 Indented paragraph one-third of the way down the page: change "from this URL" to "from this site" (981120).
16 9th line of Section 1.9: in "each type of IPC as either can be" delete "as either" (981120).
28 Item 3 near the bottom of the page: "the low-order 8 bits of the id." should be "the low-order 8 bits of the id (which must not be 0)." (981020). See the Comments to Readers at the end of this file for more details on this correction.
37 First line of first example (from /etc/system file): change "4et" to "set" (981216).
48 Line 12 of Figure 4.9: in the comment, change "delete" to "ignore" (981125). (Compare this ignoring of the newline to the actual deletion of the newline in the subsequent errata for Figure 4.15 on p. 53.)
53 Line 12 of Figure 4.15: replace n--; with buff[n-1] = '\0'; (981125).
62 Line 7 of Figure 4.23: change the declaration of buff to buff[MAXLINE+1] (981125).
62 Figure 4.23: add exit(0); between lines 45 and 46 (981102).
65 13th line from top of page: "if we invoke the client" should be "if we invoke the server" (981125).
75 Last line of 1st paragraph: "for which it having" should be "for which having" (981215).
82 6th line from top of page: "(indicated by an option letter followed by a colon)" is better as "(indicated by a colon following the option letter in the third argument)" (981215).
95 1st line of 2nd indented paragraph: "Two more variants" should be "Two variants" (981215).
100 Parenthetical note, about Digital Unix 4.0B: "33 through 38" should be "33 through 48" (980906).
102 Description for lines 18-21: add the following sentence to the end of this paragraph: "This function also sets the mask of signals to block while the signal handler is executing." (981020).

See the Comments to Readers at the end of this file for more details on this correction and the following corrections to pages 103-105.

103 Figure 5.17: add a third argument, &newset, in the three calls to the function Signal_rt on lines 19-21 (981020):
    Signal_rt(SIGRTMAX, sig_rt, &newset);
    Signal_rt(SIGRTMAX - 1, sig_rt, &newset);
    Signal_rt(SIGRTMAX - 2, sig_rt, &newset);
104 Bottom portion of page showing the Digital Unix 4.0B results (981020): given the corrections to Figures 5.17 and 5.18, these results are now correct. Change this line to "We now run the program under Digital Unix 4.0B and see the expected results." The last nine lines on the page are now:
    received signal #46, code = -1, ival = 0
    received signal #46, code = -1, ival = 1
    received signal #46, code = -1, ival = 2
    received signal #47, code = -1, ival = 0
    received signal #47, code = -1, ival = 1
    received signal #47, code = -1, ival = 2
    received signal #48, code = -1, ival = 0
    received signal #48, code = -1, ival = 1
    received signal #48, code = -1, ival = 2
105 Top four lines of page, change to (981020):

The nine signals are queued and then delivered in the order that we expect: the lowest-numbered-signal first, and for a given signal, the three occurrences are delivered in FIFO order.

     The Solaris 2.6 implementation appears to have a bug.

105 2nd line of signal_rt Function paragraph: "that provides realtime Posix semantics" should be "that provides reliable Posix semantics" (981215).
105 Figure 5.18: a third argument is being added to this function, a signal set that specifies the set of signals to be blocked while the signal handler is executing (981020). Change line 3 to
    signal_rt(int signo, Sigfunc_rt *func, sigset_t *mask)
and change line 7 to
    act.sa_mask = *mask;    /* signals to block */
109 Last word of 1st line of 2nd paragraph describing lines 26-32: "initial-ized" should be "initial-ized," (981215).
136 Figure 6.4, line 16: change second argument to Ftok from 0 to 1 (981020).
136 Figure 6.5, line 13: change second argument to Ftok from 0 to 1 (981020).
137 Figure 6.6, line 23: change second argument to Ftok from 0 to 1 (981020).
138 Figure 6.7, line 8: change second argument to Ftok from 0 to 1 (981020).
164 Caption for Figure 7.3: "consumer" should be "consume" and "producer" should be "produce" (981215).
171 2nd line from top: "by the thread calling pthread_cond_wait" should be "by the thread calling pthread_cond_signal" (980910).
177 2nd line in paragraph following indented items 1 and 2: in "as long as no thread is reading or modifying" delete "reading or" (981203). In the next sentence change "modified only if no other thread is reading the data" to "modified only if no other thread is reading or modifying the data".
186 Comment on line 14 of Figure 8.8: "releasing a reader" should be "releasing a writer" (981216).
272 Figure 10.52, line 29: change second argument to ftok from 0 to 1 (981020).
274 Figure 10.53, line 60: change second argument to ftok from 0 to 1 (981020).
276 Figure 10.55, line 8: change second argument to ftok from 0 to 1 (981020).
289 Figure 11.2, line 17: change second argument to Ftok from 0 to 1 (981020).
291 Figure 11.3, line 8: change second argument to Ftok from 0 to 1 (981020).
291 Figure 11.4, line 12: change second argument to Ftok from 0 to 1 (981020).
292 Figure 11.5, line 12: change second argument to Ftok from 0 to 1 (981020).
293 Figure 11.6, line 20: change second argument to Ftok from 0 to 1 (981020).
295 Figure 11.7, lines 14 and 20: change second argument to Ftok from 0 to 1 (981020).
346 Figure 14.1, line 19: change second argument to Ftok from 0 to 1 (981020).
347 Figure 14.2, line 8: change second argument to Ftok from 0 to 1 (981020).
347 Figure 14.3, line 10: change second argument to Ftok from 0 to 1 (981020).
348 Figure 14.4, line 10: change second argument to Ftok from 0 to 1 (981020).
355 7th line of item 2: remove the extraneous comma (980928).
507 Line 71: change "three headers" to "two headers" and remove ", and <sys/sockio.h>" from line 72 (981120).
512
513
The strcat on line 78 (p. 513) can write one byte beyond the end of the buffer (980714). One solution is to change line 68 to char buf[MAXLINE + 1]; and then change the two sizeof(buf) (lines 71 and 77) to MAXLINE. This guarantees that the buffer is always terminated by a newline followed by a null byte.
518 Solution for Exercise 4.6: in the first line change "(write-only)" to "(read-only)" and in the second line change "(read-only)" to "(write-only)" (981125).
524 2nd line of solution for Exercise 10.2: "exists it, is" should be "exists, it is" (980924).
527 Figure D.9, line 20: change second argument to Ftok from 0 to 1 (981020).
  inside back cover: Add a third argument to the prototype for signal_rt, as per the correction shown earlier for page 105 (971020).


Page Comment
28 Unix 98 now states that the behavior of ftok is unspecified if the low-order 8 bits of the id are 0. Looking around I see the Solaris and Digital Unix man pages now make this same statement. I have no idea when this disclaimer was added, and my 1991 "System V Interface Definition" does not have this statement. AIX even goes so far as to return an error if the id is 0, which IMHO is going just a little too far. Indeed, looking at three different implementations of ftok--System V Release 2, GNU libc, and BSD/OS--not one of these three requires that the id be nonzero: all just logically-OR in the low-order 8 bits of the id regardless of its value. Nevertheless all 19 uses of an id of 0 in the 1st printing will be changed to 1 in the 2nd printing, to avoid problems on systems such as AIX.
101 My reason for the second parenthetical remark concerning siginfo_t is that of all the structures defined by Posix.1 (aiocb, group, itimerspec, lock, mq_attr, passwd, sched_param, sigaction, sigevent, siginfo_t, sival, stat, termio, timespec, and utimbuf) only siginfo_t is defined using a typedef. Yes, Posix.1 appends _t to the names of all its primitive system data types (pid_t, pthread_t, etc.) but none of these need be structures and there are no structure member names defined for any of these data types. Therefore, naming siginfo_t, which must be a structure, with the _t suffix struck me as a mistake. The reason for this oddity is probably that siginfo_t was defined by SVR4 (the earliest definition that I can find for it is in the "System V Interface Definition," Third Edition, Aug. 1989) and then ignored by Posix until the P1003.4 realtime extensions. Indeed, p. 354 (the Rationale) of 1003.1b-1993 talks about this name breaking the convention, and the reason listed is to follow existing practice and thus promote portability.
102 There was a bug in the test program in Figure 5.17 of the 1st printing. The correction in the 2nd printing is listed earlier in this errata, for pages 102-105. The bug illustrates a "feature" of realtime signals that we must account for.

The correction is to specify a third argument to our signal_rt function (Figure 5.18) that specifies the set of signals to be blocked while the signal handler is executing. In Figure 5.17 this argument is now specified as &newset, which contains all three of the realtime signals that the test program is generating. The underlying problem is that while the signal numbers of the realtime signals (33 through 48 for Digital Unix) specify their priority with regard to delivery when more than one of the realtime signals is pending (in our example, nine signals with numbers 46, 47, and 48 are all pending when all three signal numbers are unblocked, so the lowest numbered signal is delivered first), what determines whether another signal can then be delivered while that signal handling function is executing is the sa_mask of that signal handling function. Therefore, whenever we are dealing with more than one realtime signal, we must specify an sa_mask value for each of the realtime signal's signal handling function, and that mask should block all remaining realtime signals with a higher number (that is, with a lower priority). The rules of Posix (p. 75 of [IEEE 1996]) guarantee that when multiple realtime signals are pending the lowest numbered is delivered first, but it is our responsibility to then guarantee that a lower priority realtime signal does not interrupt our signal handling function; we do that by specifying an sa_mask value for the signal handling function.

Since we are now blocking all three of the realtime signals in this example from the signal handling function, calling printf as a simple diagnostic tool is probably OK. But a better technique of printing the order in which the signals are delivered is to change Figure 5.17 as follows: First, allocate two globals:

    static volatile siginfo_t  arrival[10];
    static volatile int        nsig;
Then have the signal handler save its info argument in this array:
    static void
    sig_rt(int signo, siginfo_t *info, void *context)
    {
	arrival[nsig++] = *info; /* save info for child to print */
    }
Lastly, have the child print the information in this array before terminating:
    sleep(3);     /* let all queued signals be delivered */
    for (i = 0; i < nsig; i++) {
        printf("received signal #%d, code = %d, ival = %d\n",
               arrival[i].si_signo, arrival[i].si_code,
               arrival[i].si_value.sival_int);
    }
    exit(0);
102 One reader has suggested an alternate solution to the just-listed problem: retain the 2-argument version of the signal_rt function on p. 105 but have the function block all lower priority signals. To do this, add the following two declarations to the function
    sigset_t  mask;
    int       s;
and then replace line 7 with the following
        /* compute mask of all lower-priority signals */
    Sigemptyset(&mask);
    for (s = signo + 1; s <= SIGRTMAX; s++)
        Sigaddset(&mask, s);
    act.sa_mask = mask;    /* block all lower-priority signals */
205 Note that it is easier to initialize the sequence number file to 1 using "echo 1 > seqno".
205 Be aware that the loopfcntl program can take much longer to execute if the seqno file is on an NFS mounted filesystem. You should try this if you have access to NFS--it is somewhat an extension to Exercise 9.7. Another interesting measurement is to time the program when the file is on the /tmp filesystem, as many implementations allow this filesystem to be stored in memory.
209 You can add the line "wait(NULL); wait(NULL);" between lines 34 and 35 of Figure 9.8 to wait for both children to terminate, if you want to avoid the shell prompt during the children's output.
211 Same comment as earlier for page 209.
312 You can add the line "wait(NULL);" between lines 34 and 35 of Figure 12.10 to wait for the child to terminate, if you want to avoid the shell prompt during the child's output.
314 Same comment as earlier for page 312, between lines 36-37.
356 The URL for the Linux implementation of doors has moved to http://www.rampant.org/doors/.