.if n .pH port.chap08 @(#)chap08	40.7
.\" Porting manual: Network Interface Ch 8
.\"
.\" Ps = POSTSCRIPT
.\" Sd = NeWS
.\" Cs = CSCRIPT
.ds Ps P\s-2OST\s+2S\s-2CRIPT\s+2
.ds Sd \s-2X11/Ne\h'-0.2n'WS\s+2
.ds Cs \CS\s-2CRIPT\s+2
.BK "Porting the X11/NeWS Server"
.CH "Network Interface" 8
.H 1 "Network Interface" 
.H 2 "Introduction"
Once the server can multiplex on input and output streams,
the next step is to add networking capabilities.
System V Release 4.0 supports both socket and TLI-style
networking interfaces.
X11/NeWS includes both implementations.
The coding excerpts and
descriptions for network interface refer to the 
socket version.
Note that the actual subroutine
functionality between socket and TLI implementations will be similar, 
but some TLI interfacing may be 
different, in which case you should refer to the source code
for details.
To build the System V code and integrate it with the
interpreter, go to the directory
.UI server/portinghelp/network
and execute the shell scripts.
An example of the difference between the Sun and System V interface is 
the format of 
\f4%socket\f1.
Refer to the System V code for specifics. 
.H 2 "Networking Functions"
Adding networking capabilities to the server consists of ensuring that
the two networking functions, 
.UI open_socket()
and
.UI accept_socket() ,
perform their appropriate tasks on a given hardware platform (these tasks 
are described in detail below).  In particular, 
.UI open_socket
must initially establish the passive (or listening) socket in order
for networking to be successful.  Both functions are provided in the source
code, however, your system must have a BSD derived UNIX operating system,
or a System V operating system using Wollongong's TCP/IP in order to use them
(note that some modification and debugging will probably be necessary).
If you do not have one of these systems, then you will have to write your
own functions. 
.P
This section describes the interface and functionality of 
.UI open_socket()
and
.UI accept_socket() .
Afterwards we describe a procedure for debugging these functions.
This section assumes that your machine has an Wollongong
implementation (TCP/IP).
If not, then you will have to make some adjustments of the concepts
involved here.
.H 2 "The Server Networking Process"
The server makes use of networking to provide
services to client processes.
There are two O/S dependent functions which the O/S independent
code relies upon to assist in handling network connections.
One of these is called 
.UI open_socket() .
The other is called 
.UI accept_connect() .
.P
.UI open_socket() 
provides the server with a mechanism 
to create network connection endpoints.
Generally such endpoints are either passive or active.
Passive sockets are used for accepting incoming connections.
Active sockets are used for establishing connections with another
process which is accepting incoming connections.
.P
.UI accept_connect() 
is used to actually accept an incoming connection
on a passive socket.
This is a distinct operation from creating the passive 
socket (also known as a listening socket),
as the same passive socket can be used over and over again
to accept multiple connections. So, 
.UI open_socket()
can be used to create a listening socket,
and 
.UI accept_connect() 
is used to accept connections on the listening socket.
.P
There exists PostScript/\f3NeWS\fP\u\s-3\(tm\s0\d 
interfaces to the networking functions.
The interface to 
.UI open_socket() 
is via the NeWS 
.B file 
primitive.
.B file 
accepts a number of special names for files,
such as 
\f4%stdin\f1 
and 
\f4%stdout\f1.
Sockets are created by presenting a special name to the 
\f3file\f1
primitive.
The name starts with the string ``
\f4%socket\f1.''
\f4%socket\f1''
is followed by a letter indicating whether the socket is
passive or active.  
.B l 
is used for passive (listening) sockets, and
.B c 
is used for active (connecting) sockets.
.P
System V release 4.0 supports both socket and TLI stream-based
network implementations.
In the socket implementation, the special character is 
followed by the TCP port number.
For active sockets,
the port number is followed by an optional non\(hyalphabetic separator and
then the name of the host on which to connect.
With this implementation, the local communication is setup
through the TCP loop back mode. 
Only the TCP network type is supported.
.P
For example,
\f4%socketl144\f1
would be passed to 
.B file 
to indicate that a passive socket bound to
port 144 is to be created.
\f4%socketc23;myhost\f1 
could be passed to 
.B file 
to indicate that a connection
is to be established to port 23 on myhost.
.P
In the TLI streams-based implementation, local communication 
is setup through the usage of streams pipes.  For systems that only 
intend to use local communication, no network package installation
is necessary.  For remote communication, TLI provides multiple network 
selections
(such as tcp/ip, starlan, npack, .. etc.) and transparent
name to address mapping facilities.  Hence, network 
connection(s) to establish
can be dynamically determined at run time.
As a result, support for new networks that support 
a TLI interface can be added without
the need for modifications to
either server or client
software.  For detailed information refer to
SVR4.0 documents.
.P
For establishing a local listener, the special character (l)
is followed by the server well know address (\f4server.0\f1
for X, \f4news.0\f1 for NeWS), a separator (;), and then 
\f4xlocal\f1
for X and
\f4newslocal\f1 
for NeWS.
.P
For a remote listener, the special character is followed by
the service name defined in the \f4/etc/services\f1 file which
describes what port the server is listening on for that
particular service, a separator (;),
and the the network type.  
.P
For example:
.P
.DS I UI
%socketlserver.0;xlocal     ==> for X local listener setup
%socketlnews.0;newslocal    ==> for NeWS local listener setup
%socketlserver0;tcp         ==> for X TCP listener setup
%socketlnews0;tcp           ==> for NeWS TCP listener setup
%socketlserver0;starlan     ==> for X STARLAN listener setup
%socketlnews0;starlan       ==> for NeWS STARLAN listener setup
.DE
.P
\f4/etc/services\f1 file should contain the following lines:
.P
.DS I UI
xserver0   06000/tcp
news0      2000/tcp
.DE
.P
\f4/etc/net/starlan/services\f1 contains:
.DS I UI
xserver0         0
.DE
.P
For local connections, the special character (c) is followed by the
same information as described for the listener setup above.  For 
a remote connection, the special character is followed by the service
name (as described above), a separator, and then the node it
wishes to connect to and it has to be defined in \f4/etc/hosts\f1 file.
\f4NETPATH\f1 is used to determine which network connections
to go through.
\f4NETPATH\f1 can be set as follows:
.P
\f4NETPATH="net1:net2:...netN" export NETPATH\f1
.P
where \f4net1\f1, \f4net2\f1,...\f4netN\f1 are the network identifiers (\f4NID\f1).  Each
\f4NID\f1 must match the first colomn of a line entry in the \f4/etc/netconfig\f1 file.
If \f4NETPATH\f1 is not set, then the client will loop through all
visible entries in \f4etc/netconfig/f1 until a connection is
set up.
.P
For example:
.P
.DS I UI
%socketcxserver0;xlocal     ==> for X local connection
%socketcnews0;newslocal     ==> for NeWS local connection
%socketcxserver0;nodename   ==> for X remote connection
%socketcnews0;nodename      ==> for NeWS remote connection
.DE
.P
The 
.UI accept_connect() 
function is activated via the 
.B acceptconnection
NeWS primitive.
It is described later in the chapter.
.H 3 "\f5open_socket\fP Interface"
.UI open_socket() 
is called from 
.UI open_file() 
in 
.UI nucleus/objects.c :
.P
.SS
struct object
open_file(fn, len, dir)
    char  *fn;
    int   len;
    char  *dir;
{
    struct object   ret;
    register struct corpus *c = 0;
    PSFILE  *f = 0;
    PSFILE  *inf = 0;
    char    fnb[200];
    REF	    ptmp;

    if (len >= sizeof fnb)
        len = sizeof fnb - 1;
    strncpy(fnb, fn, len);
    fnb[len] = 0;
    if (fnb[0] != '%') {
    } else if (strcmp(fnb, "%stdin") == 0)
        c = stdin_file;
    else if (strcmp(fnb, "%stdout") == 0)
        c = stdout_file;
    else if (strcmp(fnb, "%stderr") == 0)
        c = stderr_file;
    else if (strncmp(fnb, "%socket", 7) == 0)
        open_socket(fnb + 7, &inf, &f);
    .
    .
    .
    if (inf) {
        make_async(psio_fileno(inf));
        if (psio_fileno(inf) > 2)
            fcntl(psio_fileno(inf), F_SETFD, 1);
    }
    if (f) {
        if (psio_fileno(f) > 2)
            fcntl(psio_fileno(f), F_SETFD, 1);
    }
    return ret;
}
.SE
.UI open_socket() 
is passed three parameters. The first is a character string, which is the 
\f4%socket...\f1 
string passed to 
.B file
without the 
\f4%socket\f1 
at the beginning. In other words, using the examples above,
.UI open_socket() 
might be passed
\f4l144\f1
for a listening socket to be bound to port 144, or,
\f4c23;myhost\f1 
could be passed in to indicate that a connection
is to be established to port 23 on myhost in the socket
implementation, and lnews0; TCP in the stream
implementation.
.P
The following two parameters are pointers to pointers to 
.UI PSFILE
structures.
These will be used to initialize pointers to 
.UI PSFILE 
structures. The first pointer is to the 
.UI PSFILE 
structure to associate with the network endpoint as an input stream.
The second pointer is to the 
.UI PSFILE 
structure to associate with
the network endpoint as an output stream. A pointer to a 
pointer is used because the
.UI PSFILE 
structure is initialized here, not by the calling routine.
This will become clearer in the discussion below.
.P
The C interface to 
.UI open_socket()
is as follows:
.SS
void
open_socket(name, inf, outf)
    char       *name;
    PSFILE     **inf, **outf;
.SE
.H 3 "\f5open_socket\fP Operation"
This section describes the functionality of the socket version of 
.UI open_socket().
The TLI version can be found in \f4open-socket.c\f1 and \f4xstreams.c\f1
in \f4libcps\f1.
.P
The first thing 
.UI open_socket() 
does is determine whether it is to make an active or a passive socket.
The socket is then created by calling the 
.UI socket(2) 
system call:
.SS
if (*p && (fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP))> 0) {
.SE
Then 
.UI open_socket() 
determines the port number to bind to the socket:
.SS
if ((addr.sin_port = htons((unsigned short) 
                strtol(p, &hostname, 10))) != 0) {
.SE
If the socket is passive,
the port number is bound to the address by calling 
.UI bind(2)
and the socket is put into the listen state by calling 
.UI listen(2).
.SS
      if (bind(fd, (struct sockaddr *) &addr, sizeof addr) < 0
          || lis && listen(fd, 3) < 0)
.SE
Once the socket is set up,
the psio buffering layer is initialized for the file descriptor
by calling 
.UI psio_fdopen 
located in 
.UI usr.lib/libcps/psio.c .
Then the pointer to the 
.UI PSFILE 
structure used by the calling code
is initialized to point to the buffering layer just set up:
.SS
        *inf = psio_fdopen(fd, "r");
.SE
For a listening socket, only the input side is set up this way,
since the socket will not be used for output.
Accepting connections on a listening socket is considered an input activity.
.P
If the socket is to be used to establish an active connection
to another machine,
the Internet address of that machine is determined by calling the 
.UI gethostbyname(3) 
library function.
.SS
        hp = gethostbyname(hostname);
.SE
The 
.UI connect(2) 
system call is then used to establish the
connection, with the address of the host to be connected  
passed as a parameter.
.SS
   bcopy((char *) hp->h_addr, (char *) &addr.sin_addr, 
              (int) hp->h_length); /*Copy the address from the*/ 
                          /* struct hostent filled in by */
                          /* gethostbyname to the struct */
                          /* sockaddr passed to connect */
   if (addr.sin_port == 0 || connect(fd, (struct sockaddr *) 
                        &addr, sizeof addr))
.SE
As with a passive socket,
a psio buffering layer is set up for the connected socket.
In this case, two layers are set up,
one for the socket as an input stream and one for it as an output stream:
.SS
        *inf = psio_fdopen(fd, "r");
        *outf = psio_fdopen(fd, "w");
.SE
The TLI version may vary somewhat from this description, but the 
basic functionality is the same.

.H 3 "\f5accept_connect()\fP Interface"
.P
.UI accept_connect() 
is called from the 
.UI acceptconnection_primitive 
case in 
.UI operators.h .
The first thing the code does is put the thread to sleep until 
an incoming connection has arrived. It is assumed that multiplexing 
on input for the file descriptor on which the listen was done 
will awaken the thread when an incoming connection has arrived.
.P
This is done in the code as follows:
.SS
case acceptconnection_primitive:
    .
    .
    .
    ee->restart_state = 11;
    ee->event = 
        psio_fileno(corpus_of(optop[-1])->corpus.file.inbuf) + 1;
    goto suspend_process;
.SE
When the thread is reawakened because a connection has arrived,
execution continues at the label 
.UI accept_11 
in 
.UI PostScript().
This is where 
.UI accept_connect() 
is called:
.SS
accept_11:
    {
        REF file;
        register struct corpus *c;
        register fd;

           fd = accept_connect(psio_fileno(corpus_of(optop[-1])
                ->corpus.file.inbuf));
        if (fd < 0) {
        perror("accept");
        goto accept_error;
        }
        else if (verbose)
        psio_fprintf(psio_stdout,
                 "Accepted new connection on fd %d\n", fd);
        file = pnew(current_pool, file_type, 0);
        c = corpus_of(file);
        c->corpus.file.inbuf = psio_fdopen(fd, "r");
        c->corpus.file.outbuf = psio_fdopen(fd, "w");
        set_object_from_ref(&newobj, file);
    }
	goto typed_result;
.SE
.UI accept_connect 
is passed the file descriptor of the listening socket
on which the connection has arrived.

.H 3 "\f5accept_connect()\fP Operation"
.P
The most important activity 
of
.UI accept_connect 
for socket implementation
is calling 
.UI accept(2) 
to
accept the connection on the listening socket.
This returns a new file descriptor so that the original
one can continue to be used for listening.
.SS
int
accept_connect(s)
int s;
{
    union genericaddr addr;
    int         namelen = sizeof addr;
    int         fd;

    bzero((char *) &addr, sizeof addr);
    fd = accept(s, (struct sockaddr *)&addr, &namelen);
    if (fd < 0) {
        perror("accept");
        return(-1);
    } else switch (addr.generic.sa_family) {
        case AF_UNSPEC:
        case AF_UNIX:
        if (verbose)
            (void) fprintf(stderr,
                "accept(%d, 0x%x, %d) => fd %d len %d [%d,%s]\n",
                s, &addr, sizeof addr, fd, namelen, 
                addr.nuxi.sun_family, addr.nuxi.sun_path);
        break;
        case AF_INET:
        break;
        default:
        (void) fprintf(stderr,
                "Bad address family %d\n", addr.generic.sa_family);
        break;
    }
    if (fd >= maximum_fd)
        maximum_fd = fd + 1;
    
    if (fcntl(fd, F_SETFD, 1) < 0)
        perror("fcntl: accept_connect setfd");
    make_async(fd);
    return(fd);
}
.SE
The TLI version of 
.UI accept_connection 
is different,
and calls 
.UI AcceptConnections 
.UI (xstreams.c) .
.P
Clients can be connected to the server in one of two ways: locally via
"streams pipes", which are pseudo-tty's with no line 
discipline processing modules, or remotely, using 
TLI (Transport Layer Interface).
.P
The procedure for local clients connection establishment
is described below. 
The server opens the streams pipe
clone device 
\f4/dev/ptmx\f1.
The clone device returns a pseudo tty master,
\f4ptmxxx\f1,
which has associated with it a corresponding  slave,
\f4ptsxxx\f1.
After determining the corresponding slave name,
server links the slave pts device to 
\f4/dev/X/server.0\f1
for X  or 
\f4/dev/X/news.0\f1
for NeWS, which are the "well-known address"
used by local clients.  A client will open this file to 
initially connect to the server.  
It will also open the clone device to obtain a second 
master/slave pair.  
After obtaining the slave
name of the new psuedo tty and sending it to the
server, the client then closes the initial connection to the server. 
The server reads the client's slave name and
opens that device.  
This gives the client a private
connection to the server that is used for all further communication.
.P
The basic structure for establishing a remote connection between
the client and the server is the same as 
that described in socket implementation.
A dynamic shared library, \f4libnsl.so\f1, provides
the name/address translation and network selection
facilities.  The functions 
\f4netdir_getbyaddr\f1() 
and 
\f4netdir_getbyname\f1()
are used by both client and server.
These functions will call the routines which correspond to
various transport providers (listed in \f4/etc/netconfig\f1 file). 
These routines are not
part of \f4libnsl.so\f1 but are included in separate 
dynamic shared libraries (\f4straddr.so\f1 and \f4tcpip.so\f1).
.P
The server opens the network clone device (for example: \f4/etc/tcp\f1, 
\f4/etc/starlan\f1, as specified 
in \f4/etc/netconfig\f1), binds to the address returned from
the above functions and waits for connection request.
After a connection has been established, both the client and the
server will push a streams module 
.B tirdwr
to the top of the streams.  
Thereafter, the \f4read()\f1 and \f4write()\f1 system call is used to
receive and send data instead of the \f4t_rcv()\f1 and \f4t_snd()\f1 TLI calls.
.P
The code implementing these
listener/connection mechanisms is located in the \f4xstreams.c\f1 file.
.H 2 "Debugging Network Interfaces"
Debugging the network connection interfaces should be
fairly straightforward. This should be handled in two distinct cases,
one for active and one for passive connections.
.H 2 "Debugging \f4open_socket\fP Passive Connections" 
After the \f4%socketlxxx\f1 \f4r\f1 \f3file\f1
PostScript code is executed,
you should be able to examine the operating system's networking interface to
see that a socket has been created and is in the
listen state with
the specified port number.
On Sun systems and some System V systems with TCP/IP, 
this is done with the 
.UI "netstat -a"
command.
.P
The output from this command will look like this:
.SS
Active Internet connections (including servers)
Proto Recv-Q Send-Q  Local Address    Foreign Address   (state)
      .
      .
      .
tcp        0      0  *.20001          *.*                LISTEN
      .
      .
      .
.SE
In this example, the port number bound to the listening socket was 20001.
.P
On different systems, there may be different methods for examining the state
of the networking subsystem. If no listening socket is created,
you will have to debug your 
.UI open_socket()
routine.
Single step through it and examine each operation for correctness.
Make sure the socket is being created correctly,
that the port number bound to it is correct and that it
is properly being put into the listen state.

.H 2 "Debugging \f4accept_connect\fP Passive Connections" 
.P
Once a listening socket has been created, you should be able to connect to it.
A good way to test this is to place the following code in \f4init.ps\f1:
.SS
\fH
 /&main {
    (%socketl20001) (r) file acceptconnection cvx exec
} def
\fP
.SE
This will be much the same as the example in the
I/O multiplexing chapter,
with exception being that the server is operating over a network connection.
The incoming connection will be accepted and \*(Ps code
entered over the connection will be executed with its
output going back over the connection.
.P
The 
.UI telnet 
command can be used to make a connection to
server's listening socket, as follows:
.SS
    telnet localhost 20001
.ft1
.SE
.UI localhost 
is an alias for the loopback networking interface.
20001 is the port on which to connect
(the default is the telnet port, port 23).
.P
You should get a message from the telnet program saying the
connection has been established.
Then, typing in PostScript code like
.SS
    2 2 add =
.ft1
.SE
Should result in output from the server.
.P
If the connection is established but the server does not respond,
debug through the code at 
.UI acceptconnection_primitive 
in 
.UI operators.h.
First see if the process is being suspended properly with
the right file descriptor being added to the list of file descriptors on which 
to multiplex. Perhaps putting a breakpoint at 
.UI add_selectable_file()
would be appropriate. Next, place a breakpoint at 
.UI WaitForInput()
and see if the multiplexing system call returns when the connection is
established. If not, you either have a problem with the way the I/O 
Multiplexing system call is being called or it is not working in the 
expected manner for accepting connections.
.P
If the process thread is awakened properly but the connection still doesn't
work, check 
.UI accept_connect() 
to see that the connection is accepted properly. If it has been, doing another 
.UI "netstat -a" 
should show the following:
.SS
Active Internet connections (including servers)
Proto Recv-Q Send-Q Local Address    Foreign Address  (state)
      .
      .
      .
tcp        0      0 localhost.20001  localhost.1340   ESTABLISHED
tcp        0      0 localhost.1340   localhost.20001  ESTABLISHED
      .
      .
      .
.SE
If this is working,
but the server still does not respond to entered PostScript code,
you may have problems in multiplexing on input on network connections.
Debug this in the same way that I/O multiplexing was debugged
on ttys.
.H 2 "Debugging Active Connections"
.P
Active connections are made with \*(Ps code as in the following 
example:
.P
    (\f4%socketc23;myhost\f1) (\f4r\f1) \f3file\f1
.P
Once the connection is established,
you should be able to write down it and read from it.
If this doesn't work at first,
effort on this element of functionality can probably
be deferred until later,
as it is not critical to getting the server or any clients
running.
.P
Setting up a test configuration may take some doing,
as you need a listening port from which you can connect, either
locally or on a remote machine.
Furthermore, whatever service to which you connect has to provide
some kind of meaningful output.
One possibility is to use an echo server.  An echo server 
echoes back everything sent to it.
You could send \*(Ps code to it which would be
interpreted by the X11/NeWS server when sent back.
You may have to write a simple network server which listens
at a given address, accepts an incoming connection
and writes some PostScript code down the socket.
.P
If this doesn't seem to work, you 
will have to debug the active connection part of
.UI open_socket() .
.P
Check to make sure that the port address is being computed
properly, that the hostname is extracted from the input string
properly, and that 
.UI gethostbyname 
is returning the correct Internet address for the machine specified.
.P
Then make sure the 
.UI connect(2) 
system call is being properly
made and that the connection is being established.
The 
.UI netstat 
command should show a connection in the established state
to whatever service you connect.
.P
If a connection is established but input from and output to it
doesn't seem to work, you may have a problem in I/O multiplexing.
