Annotation of 43BSDReno/share/doc/ps1/08.ipc/4.t, revision 1.1

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

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.