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