Initial import from FreeBSD RELENG_4:
[dragonfly.git] / share / doc / psd / 21.ipc / 4.t
1 .\" Copyright (c) 1986, 1993
2 .\"     The Regents of the University of California.  All rights reserved.
3 .\"
4 .\" Redistribution and use in source and binary forms, with or without
5 .\" modification, are permitted provided that the following conditions
6 .\" are met:
7 .\" 1. Redistributions of source code must retain the above copyright
8 .\"    notice, this list of conditions and the following disclaimer.
9 .\" 2. Redistributions in binary form must reproduce the above copyright
10 .\"    notice, this list of conditions and the following disclaimer in the
11 .\"    documentation and/or other materials provided with the distribution.
12 .\" 3. All advertising materials mentioning features or use of this software
13 .\"    must display the following acknowledgement:
14 .\"     This product includes software developed by the University of
15 .\"     California, Berkeley and its contributors.
16 .\" 4. Neither the name of the University nor the names of its contributors
17 .\"    may be used to endorse or promote products derived from this software
18 .\"    without specific prior written permission.
19 .\"
20 .\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 .\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 .\" SUCH DAMAGE.
31 .\"
32 .\"     @(#)4.t 8.1 (Berkeley) 6/8/93
33 .\"
34 .\".ds RH "Client/Server Model
35 .bp
36 .nr H1 4
37 .nr H2 0
38 .sp 8i
39 .bp
40 .LG
41 .B
42 .ce
43 4. CLIENT/SERVER MODEL
44 .sp 2
45 .R
46 .NL
47 .PP
48 The most commonly used paradigm in constructing distributed applications
49 is the client/server model.  In this scheme client applications request
50 services from a server process.  This implies an asymmetry in establishing
51 communication between the client and server which has been examined
52 in section 2.  In this section we will look more closely at the interactions
53 between client and server, and consider some of the problems in developing
54 client and server applications.
55 .PP
56 The client and server require a well known set of conventions before
57 service may be rendered (and accepted).  This set of conventions
58 comprises a protocol which must be implemented at both ends of a
59 connection.  Depending on the situation, the protocol may be symmetric
60 or asymmetric.  In a symmetric protocol, either side may play the 
61 master or slave roles.  In an asymmetric protocol, one side is
62 immutably recognized as the master, with the other as the slave.  
63 An example of a symmetric protocol is the TELNET protocol used in
64 the Internet for remote terminal emulation.  An example
65 of an asymmetric protocol is the Internet file transfer protocol,
66 FTP.  No matter whether the specific protocol used in obtaining
67 a service is symmetric or asymmetric, when accessing a service there
68 is a \*(lqclient process\*(rq and a \*(lqserver process\*(rq.  We
69 will first consider the properties of server processes, then
70 client processes.
71 .PP
72 A server process normally listens at a well known address for
73 service requests.  That is, the server process remains dormant
74 until a connection is requested by a client's connection
75 to the server's address.  At such a time
76 the server process ``wakes up'' and services the client,
77 performing whatever appropriate actions the client requests of it.
78 .PP
79 Alternative schemes which use a service server
80 may be used to eliminate a flock of server processes clogging the
81 system while remaining dormant most of the time.  For Internet
82 servers in 4.4BSD,
83 this scheme has been implemented via \fIinetd\fP, the so called
84 ``internet super-server.''  \fIInetd\fP listens at a variety
85 of ports, determined at start-up by reading a configuration file.
86 When a connection is requested to a port on which \fIinetd\fP is
87 listening, \fIinetd\fP executes the appropriate server program to handle the
88 client.  With this method, clients are unaware that an
89 intermediary such as \fIinetd\fP has played any part in the
90 connection.  \fIInetd\fP will be described in more detail in
91 section 5.
92 .PP
93 A similar alternative scheme is used by most Xerox services.  In general,
94 the Courier dispatch process (if used) accepts connections from
95 processes requesting services of some sort or another.  The client
96 processes request a particular <program number, version number, procedure
97 number> triple.  If the dispatcher knows of such a program, it is
98 started to handle the request; if not, an error is reported to the
99 client.  In this way, only one port is required to service a large
100 variety of different requests.  Again, the Courier facilities are
101 not available without the use and installation of the Courier
102 compiler.  The information presented in this section applies only
103 to NS clients and services that do not use Courier.
104 .NH 2
105 Servers
106 .PP
107 In 4.4BSD most servers are accessed at well known Internet addresses
108 or UNIX domain names.  For
109 example, the remote login server's main loop is of the form shown
110 in Figure 2.
111 .KF
112 .if t .ta .5i 1.0i 1.5i 2.0i 2.5i 3.0i 3.5i
113 .if n .ta .7i 1.4i 2.1i 2.8i 3.5i 4.2i 4.9i
114 .sp 0.5i
115 .DS
116 main(argc, argv)
117         int argc;
118         char *argv[];
119 {
120         int f;
121         struct sockaddr_in from;
122         struct servent *sp;
123
124         sp = getservbyname("login", "tcp");
125         if (sp == NULL) {
126                 fprintf(stderr, "rlogind: tcp/login: unknown service\en");
127                 exit(1);
128         }
129         ...
130 #ifndef DEBUG
131         /* Disassociate server from controlling terminal */
132         ...
133 #endif
134
135         sin.sin_port = sp->s_port;      /* Restricted port -- see section 5 */
136         ...
137         f = socket(AF_INET, SOCK_STREAM, 0);
138         ...
139         if (bind(f, (struct sockaddr *) &sin, sizeof (sin)) < 0) {
140                 ...
141         }
142         ...
143         listen(f, 5);
144         for (;;) {
145                 int g, len = sizeof (from);
146
147                 g = accept(f, (struct sockaddr *) &from, &len);
148                 if (g < 0) {
149                         if (errno != EINTR)
150                                 syslog(LOG_ERR, "rlogind: accept: %m");
151                         continue;
152                 }
153                 if (fork() == 0) {
154                         close(f);
155                         doit(g, &from);
156                 }
157                 close(g);
158         }
159 }
160 .DE
161 .ce
162 Figure 2.  Remote login server.
163 .sp 0.5i
164 .KE
165 .PP
166 The first step taken by the server is look up its service
167 definition:
168 .sp 1
169 .nf
170 .in +5
171 .if t .ta .5i 1.0i 1.5i 2.0i
172 .if n .ta .7i 1.4i 2.1i 2.8i
173 sp = getservbyname("login", "tcp");
174 if (sp == NULL) {
175         fprintf(stderr, "rlogind: tcp/login: unknown service\en");
176         exit(1);
177 }
178 .sp 1
179 .in -5
180 .fi
181 The result of the \fIgetservbyname\fP call
182 is used in later portions of the code to
183 define the Internet port at which it listens for service
184 requests (indicated by a connection).
185 .KS
186 .PP
187 Step two is to disassociate the server from the controlling
188 terminal of its invoker:
189 .DS
190         for (i = 0; i < 3; ++i)
191                 close(i);
192
193         open("/", O_RDONLY);
194         dup2(0, 1);
195         dup2(0, 2);
196
197         i = open("/dev/tty", O_RDWR);
198         if (i >= 0) {
199                 ioctl(i, TIOCNOTTY, 0);
200                 close(i);
201         }
202 .DE
203 .KE
204 This step is important as the server will
205 likely not want to receive signals delivered to the process
206 group of the controlling terminal.  Note, however, that
207 once a server has disassociated itself it can no longer
208 send reports of errors to a terminal, and must log errors
209 via \fIsyslog\fP.
210 .PP
211 Once a server has established a pristine environment, it
212 creates a socket and begins accepting service requests.
213 The \fIbind\fP call is required to insure the server listens
214 at its expected location.  It should be noted that the
215 remote login server listens at a restricted port number, and must
216 therefore be run
217 with a user-id of root.
218 This concept of a ``restricted port number'' is 4BSD
219 specific, and is covered in section 5.
220 .PP
221 The main body of the loop is fairly simple:
222 .DS
223 .if t .ta .5i 1.0i 1.5i 2.0i
224 .if n .ta .7i 1.4i 2.1i 2.8i
225 for (;;) {
226         int g, len = sizeof (from);
227
228         g = accept(f, (struct sockaddr *)&from, &len);
229         if (g < 0) {
230                 if (errno != EINTR)
231                         syslog(LOG_ERR, "rlogind: accept: %m");
232                 continue;
233         }
234         if (fork() == 0) {      /* Child */
235                 close(f);
236                 doit(g, &from);
237         }
238         close(g);               /* Parent */
239 }
240 .DE
241 An \fIaccept\fP call blocks the server until
242 a client requests service.  This call could return a
243 failure status if the call is interrupted by a signal
244 such as SIGCHLD (to be discussed in section 5).  Therefore,
245 the return value from \fIaccept\fP is checked to insure
246 a connection has actually been established, and
247 an error report is logged via \fIsyslog\fP if an error
248 has occurred.
249 .PP
250 With a connection
251 in hand, the server then forks a child process and invokes
252 the main body of the remote login protocol processing.  Note
253 how the socket used by the parent for queuing connection
254 requests is closed in the child, while the socket created as
255 a result of the \fIaccept\fP is closed in the parent.  The
256 address of the client is also handed the \fIdoit\fP routine
257 because it requires it in authenticating clients.
258 .NH 2
259 Clients
260 .PP
261 The client side of the remote login service was shown
262 earlier in Figure 1.
263 One can see the separate, asymmetric roles of the client
264 and server clearly in the code.  The server is a passive entity,
265 listening for client connections, while the client process is
266 an active entity, initiating a connection when invoked.  
267 .PP
268 Let us consider more closely the steps taken
269 by the client remote login process.  As in the server process,
270 the first step is to locate the service definition for a remote
271 login:
272 .DS
273 sp = getservbyname("login", "tcp");
274 if (sp == NULL) {
275         fprintf(stderr, "rlogin: tcp/login: unknown service\en");
276         exit(1);
277 }
278 .DE
279 Next the destination host is looked up with a
280 \fIgethostbyname\fP call:
281 .DS
282 hp = gethostbyname(argv[1]);
283 if (hp == NULL) {
284         fprintf(stderr, "rlogin: %s: unknown host\en", argv[1]);
285         exit(2);
286 }
287 .DE
288 With this accomplished, all that is required is to establish a
289 connection to the server at the requested host and start up the
290 remote login protocol.  The address buffer is cleared, then filled
291 in with the Internet address of the foreign host and the port
292 number at which the login process resides on the foreign host:
293 .DS
294 bzero((char *)&server, sizeof (server));
295 bcopy(hp->h_addr, (char *) &server.sin_addr, hp->h_length);
296 server.sin_family = hp->h_addrtype;
297 server.sin_port = sp->s_port;
298 .DE
299 A socket is created, and a connection initiated.  Note
300 that \fIconnect\fP implicitly performs a \fIbind\fP
301 call, since \fIs\fP is unbound.
302 .DS
303 s = socket(hp->h_addrtype, SOCK_STREAM, 0);
304 if (s < 0) {
305         perror("rlogin: socket");
306         exit(3);
307 }
308  ...
309 if (connect(s, (struct sockaddr *) &server, sizeof (server)) < 0) {
310         perror("rlogin: connect");
311         exit(4);
312 }
313 .DE
314 The details of the remote login protocol will not be considered here.
315 .NH 2
316 Connectionless servers
317 .PP
318 While connection-based services are the norm, some services
319 are based on the use of datagram sockets.  One, in particular,
320 is the \*(lqrwho\*(rq service which provides users with status
321 information for hosts connected to a local area
322 network.  This service, while predicated on the ability to
323 \fIbroadcast\fP information to all hosts connected to a particular
324 network, is of interest as an example usage of datagram sockets.
325 .PP
326 A user on any machine running the rwho server may find out
327 the current status of a machine with the \fIruptime\fP(1) program.
328 The output generated is illustrated in Figure 3.
329 .KF
330 .DS B
331 .TS
332 l r l l l l l.
333 arpa    up      9:45,   5 users, load   1.15,   1.39,   1.31
334 cad     up      2+12:04,        8 users, load   4.67,   5.13,   4.59
335 calder  up      10:10,  0 users, load   0.27,   0.15,   0.14
336 dali    up      2+06:28,        9 users, load   1.04,   1.20,   1.65
337 degas   up      25+09:48,       0 users, load   1.49,   1.43,   1.41
338 ear     up      5+00:05,        0 users, load   1.51,   1.54,   1.56
339 ernie   down    0:24
340 esvax   down    17:04
341 ingres  down    0:26
342 kim     up      3+09:16,        8 users, load   2.03,   2.46,   3.11
343 matisse up      3+06:18,        0 users, load   0.03,   0.03,   0.05
344 medea   up      3+09:39,        2 users, load   0.35,   0.37,   0.50
345 merlin  down    19+15:37
346 miro    up      1+07:20,        7 users, load   4.59,   3.28,   2.12
347 monet   up      1+00:43,        2 users, load   0.22,   0.09,   0.07
348 oz      down    16:09
349 statvax up      2+15:57,        3 users, load   1.52,   1.81,   1.86
350 ucbvax  up      9:34,   2 users, load   6.08,   5.16,   3.28
351 .TE
352 .DE
353 .ce
354 Figure 3. ruptime output.
355 .sp
356 .KE
357 .PP
358 Status information for each host is periodically broadcast
359 by rwho server processes on each machine.  The same server
360 process also receives the status information and uses it
361 to update a database.  This database is then interpreted
362 to generate the status information for each host.  Servers
363 operate autonomously, coupled only by the local network and
364 its broadcast capabilities.
365 .PP
366 Note that the use of broadcast for such a task is fairly inefficient,
367 as all hosts must process each message, whether or not using an rwho server.
368 Unless such a service is sufficiently universal and is frequently used,
369 the expense of periodic broadcasts outweighs the simplicity.
370 .PP
371 Multicasting is an alternative to broadcasting.
372 Setting up multicast sockets is described in Section 5.10.
373 .PP
374 The rwho server, in a simplified form, is pictured in Figure
375 4.  There are two separate tasks performed by the server.  The
376 first task is to act as a receiver of status information broadcast
377 by other hosts on the network.  This job is carried out in the
378 main loop of the program.  Packets received at the rwho port
379 are interrogated to insure they've been sent by another rwho
380 server process, then are time stamped with their arrival time
381 and used to update a file indicating the status of the host.
382 When a host has not been heard from for an extended period of
383 time, the database interpretation routines assume the host is
384 down and indicate such on the status reports.  This algorithm
385 is prone to error as a server may be down while a host is actually
386 up, but serves our current needs.
387 .KF
388 .DS
389 .if t .ta .5i 1.0i 1.5i 2.0i
390 .if n .ta .7i 1.4i 2.1i 2.8i
391 main()
392 {
393         ...
394         sp = getservbyname("who", "udp");
395         net = getnetbyname("localnet");
396         sin.sin_addr = inet_makeaddr(INADDR_ANY, net);
397         sin.sin_port = sp->s_port;
398         ...
399         s = socket(AF_INET, SOCK_DGRAM, 0);
400         ...
401         on = 1;
402         if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0) {
403                 syslog(LOG_ERR, "setsockopt SO_BROADCAST: %m");
404                 exit(1);
405         }
406         bind(s, (struct sockaddr *) &sin, sizeof (sin));
407         ...
408         signal(SIGALRM, onalrm);
409         onalrm();
410         for (;;) {
411                 struct whod wd;
412                 int cc, whod, len = sizeof (from);
413
414                 cc = recvfrom(s, (char *)&wd, sizeof (struct whod), 0,
415                     (struct sockaddr *)&from, &len);
416                 if (cc <= 0) {
417                         if (cc < 0 && errno != EINTR)
418                                 syslog(LOG_ERR, "rwhod: recv: %m");
419                         continue;
420                 }
421                 if (from.sin_port != sp->s_port) {
422                         syslog(LOG_ERR, "rwhod: %d: bad from port",
423                                 ntohs(from.sin_port));
424                         continue;
425                 }
426                 ...
427                 if (!verify(wd.wd_hostname)) {
428                         syslog(LOG_ERR, "rwhod: malformed host name from %x",
429                                 ntohl(from.sin_addr.s_addr));
430                         continue;
431                 }
432                 (void) sprintf(path, "%s/whod.%s", RWHODIR, wd.wd_hostname);
433                 whod = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0666);
434                 ...
435                 (void) time(&wd.wd_recvtime);
436                 (void) write(whod, (char *)&wd, cc);
437                 (void) close(whod);
438         }
439 }
440 .DE
441 .ce
442 Figure 4.  rwho server.
443 .sp
444 .KE
445 .PP
446 The second task performed by the server is to supply information
447 regarding the status of its host.  This involves periodically
448 acquiring system status information, packaging it up in a message
449 and broadcasting it on the local network for other rwho servers
450 to hear.  The supply function is triggered by a timer and 
451 runs off a signal.  Locating the system status
452 information is somewhat involved, but uninteresting.  Deciding
453 where to transmit the resultant packet
454 is somewhat problematical, however.
455 .PP
456 Status information must be broadcast on the local network.
457 For networks which do not support the notion of broadcast another
458 scheme must be used to simulate or
459 replace broadcasting.  One possibility is to enumerate the
460 known neighbors (based on the status messages received
461 from other rwho servers).  This, unfortunately,
462 requires some bootstrapping information,
463 for a server will have no idea what machines are its
464 neighbors until it receives status messages from them.
465 Therefore, if all machines on a net are freshly booted,
466 no machine will have any
467 known neighbors and thus never receive, or send, any status information.
468 This is the identical problem faced by the routing table management
469 process in propagating routing status information.  The standard
470 solution, unsatisfactory as it may be, is to inform one or more servers
471 of known neighbors and request that they always communicate with
472 these neighbors.  If each server has at least one neighbor supplied
473 to it, status information may then propagate through
474 a neighbor to hosts which
475 are not (possibly) directly neighbors.  If the server is able to
476 support networks which provide a broadcast capability, as well as
477 those which do not, then networks with an
478 arbitrary topology may share status information*.
479 .FS
480 * One must, however, be concerned about \*(lqloops\*(rq.
481 That is, if a host is connected to multiple networks, it
482 will receive status information from itself.  This can lead
483 to an endless, wasteful, exchange of information.
484 .FE
485 .PP
486 It is important that software operating in a distributed
487 environment not have any site-dependent information compiled into it.
488 This would require a separate copy of the server at each host and
489 make maintenance a severe headache.  4.4BSD attempts to isolate
490 host-specific information from applications by providing system
491 calls which return the necessary information*.
492 .FS
493 * An example of such a system call is the \fIgethostname\fP(2)
494 call which returns the host's \*(lqofficial\*(rq name.
495 .FE
496 A mechanism exists, in the form of an \fIioctl\fP call,
497 for finding the collection
498 of networks to which a host is directly connected.
499 Further, a local network broadcasting mechanism
500 has been implemented at the socket level.
501 Combining these two features allows a process
502 to broadcast on any directly connected local
503 network which supports the notion of broadcasting
504 in a site independent manner.  This allows 4.4BSD
505 to solve the problem of deciding how to propagate
506 status information in the case of \fIrwho\fP, or
507 more generally in broadcasting:
508 Such status information is broadcast to connected
509 networks at the socket level, where the connected networks
510 have been obtained via the appropriate \fIioctl\fP
511 calls.
512 The specifics of
513 such broadcastings are complex, however, and will
514 be covered in section 5.