www.sas.com > Service and Support > Technical Support
 
Technical Support SAS - The power to know(tm)
  TS Home | Intro to Services | News and Info | Contact TS | Site Map | FAQ | Feedback


/*---------------------------------------------------------------------+
|                Copyright (c) 1995, SAS Institute Inc.                |
|                  Unpublished - All Rights Reserved                   |
|                      S A S / C   S A M P L E                         |
|                                                                      |
|         NAME: TCPOED                                                 |
|     LANGUAGE: C                                                      |
|      PURPOSE: TCPIP server/daemon, that uses Open Edition services to|
|               communicate with clients. The daemon "listens" on a    |
|               "known" port, a remote client connects to the daemon,  |
|               the daemon "forks" a child process to manage the client|
|               socket. The child process reads and writes data to the |
|               socket, and communicates with the parent via a pipe.   |
|               The sample program also implements several POSIX       |
|               signal processes and signal handling.                  |
|   MVS - BATCH                                                        |
|      COMPILE, LINK, EXECUTE: prefix.SAMPLE.AUX(TCPOED)               |
|   TSO -                                                              |
|      COMPILE: LC370 CLIST - POSIX RENT EXT options                   |
|         LINK: COOL CLIST                                             |
|      EXECUTE: call 'userid.load.lib(TCPOED)                          |
|   CMS - N/A                                                          |
|   MISC NOTES: TCPOED uses the call, getservbyname() to obtain the    |
|               well-known port value. Customize the ETC.SERVICES if   |
|               desired, by adding the following statement:            |
|                     tcpoed     6177/tcp                              |
|               where 6177 is the well-known port. If getservbyname()  |
|               fails, the LISTENPORT is used, refer to the following  |
|               note, and the LISTENPORT.                              |
|   MISC NOTES: There are several options that may be tailored via     |
|               a #define. The options are:                            |
|               LISTENPORT - sets the port TCPOED will bind() to, and  |
|                            listen(). Will be used if tcpoed is not   |
|                            defined in ETC.SERVICES. Default - 6177   |
|               TRACE_CHLD - When enabled, reads pipe data sent by     |
|                            child processes, and prints to SYSOUT     |
|                            The #define is commented by default.      |
|               TRACE_SIG  - When enabled, signal handler trace data   |
|                            is printed to the DD TRACESIG.            |
|                            The #define is commented by default.      |
|          **NOTE - The TRACE_CHLD and TRACE_SIG are implemented to    |
|                   show process activity. Enabling the options        |
|                   will affect overall performance of the program.    |
|   MISC NOTES: The fork() system call creates a duplicate process.    |
|               Refer to the SAS/C Library Reference, Release 6.00,    |
|               Introduction to POSIX, for additional details and      |
|               attributes for a child process. For example, open      |
|               MVS files and DD statements are not copied to the      |
|               child process, as well as some system control blocks.  |
|                                                                      |
|   MISC: The following is a conceptual diagram of the processes       |
|         and flow of data for TCPOED.                                 |
|                                                                      |
|    parent process                       remote client                |
|   +------------------------+            +-------------------------+  |
|   | initialize()           |            |  socket()               |  |
|   |   create pipe          |     +-------- connect()              |  |
|   |   set signal handling  |     |      |  send() token >----+    |  |
|   | create_socket()        |     |      |  read() data  <<--+ |    |  |
|   |   socket()             |     |      |  close()      >--|-|-+  |  |
|   |   bind()               |     |      |                  | | |  |  |
|   |   listen()             |     |      +------------------|-|-|--+  |
|   | select() - blocking    |     |                         | | |     |
|   | | if sock_fd ready     |     |      child process      | | |     |
|   | |  accept_socket()     | <---+      +------------------|-|-|--+  |
|   | |  fork() child proc. ------------> |  read() token <--|-+ |  |  |
|   | |                      |            |  write() data >--+   |  |  |
|   | |                      |            |  read() EOF   <------+  |  |
|   | | if pipe_fd ready     |            |  close() socket         |  |
|   | |  read(pipe data)     | <------------ write() pipe data      |  |
|   | |  write(stdout)       |            |  kill(parent)?sigusr2   |  |
|   | +--<<                  |       +------ exit                   |  |
|   |                        |       |    +-------------------------+  |
|   | signal handler()       | <-----+                                 |
|   |  sigchld -> waitpid()  |            **NOTE: There may be         |
|   |  sigusr2 -> finished!  |                    multiple child       |
|   |                        |                    socket processes.    |
|   | finished               |                                         |
|   | exit()                 |                                         |
|   |                        |                                         |
|   +------------------------+                                         |
+---------------------------------------------------------------------*/
#define _SASC_POSIX_SOURCE
/*-------------------------------------------------------------------+
| headers for socket calls.                                          |
+-------------------------------------------------------------------*/
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <netdb.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
/*-------------------------------------------------------------------+
| Miscellaneous headers                                              |
+-------------------------------------------------------------------*/
#include <lcsignal.h>
#include <limits.h>
#include <os.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <sys/utsname.h>
#include <time.h>
#include <wait.h>
/*-------------------------------------------------------------------+
| Customize the following defines as desired                         |
+-------------------------------------------------------------------*/
#define LISTENPORT 6177       /* default w/out etc/services setup   */
/* #define TRACE_SIG     */   /* uncomment to trace signal handling */
/* #define TRACE_CHLD    */   /* uncomment to read pipe data        */

/*-------------------------------------------------------------------+
| Set default values and defines                                     |
+-------------------------------------------------------------------*/
enum { FORK_P, CHILD_P, KILL_P};

#define FALSE  0              /* define false as a zero             */
#define TRUE   1              /* define true as a one               */
#define SERVERNAME "tcpoed"   /* etc/services server name           */
#define SERVERPROTO "tcp"     /* etc/services protocol              */
#define MAXSOCKETS 50         /* maximum open sockets               */
#define TOK_SIZE 8            /* number of bytes read from client   */

typedef struct _process_t             /* parent process data struct */
   {
   pid_t  p_id;                       /* process id for parent      */
   int    p_sockfd;                   /* socket for read_fds,       */
   int    p_pipe_r[2];                /* read pipe from child       */
   struct sigaction p_sa;             /* parent sigaction structure */
   } process_t;

/*-------------------------------------------------------------------+
| globals for child_sig_handler                                      |
+-------------------------------------------------------------------*/
static int tracesig_fd;               /* signal handler file desc.  */
static int select_go;                 /* while TRUE loop value      */
static int sigusr2_set;               /* SIGUSR2 received           */

/*-------------------------------------------------------------------+
| prototypes                                                         |
+-------------------------------------------------------------------*/
int  initialize(process_t *);         /* set up environment         */
void pid_error(pid_t, int, char *);   /* perror() w/ pid_t          */
int  get_sysinfo(pid_t);              /* get utsname information    */
int  create_socket(pid_t);            /* get initial sock_fd        */
int  accept_socket(process_t *);      /* accept socket in read_fds  */
void fork_error(process_t *, int);    /* fork() error function      */
void child_process(process_t *, int); /* fork() child process       */
void parent_process(process_t *, int, int); /* fork() parent process*/
int  read_token(pid_t, int, char *);  /* recv data from tcpip client*/
int  read_eof(pid_t, int);            /* read EOF on the socket     */
int  send_token(pid_t, int, int);     /* send data to tcpip client  */
void child_sig_handler(int);          /* child signal handler       */

/*-------------------------------------------------------------------+
| Macros for converting EBCDIC <-> ASCII.                            |
+-------------------------------------------------------------------*/
#define convFromAscii(str) {cp=(str); while(*cp = ntohcs(*cp)) cp++;}
#define convToAscii(str)   {cp=(str); while(*cp = htoncs(*cp)) cp++;}

/*-------------------------------------------------------------------+
| main() - initialize processes, handle client requests.             |
+-------------------------------------------------------------------*/
main()
{

char pipe_buf[_POSIX_PIPE_BUF]; /* buffer to read pipe data        */

int newsock_fd,                 /* accept_request() return value   */
    num_readfds,                /* readfds returned by select()    */
    pipe_byte_count;            /* number bytes read from pipe     */
pid_t fork_pid;                 /* pid_t from fork() call          */

fd_set read_fds;                /* select() read fd_set            */

process_t parent;               /* parent process data structure   */

memset(&parent, '\0', sizeof(process_t));
memset(&pipe_buf, '\0',_POSIX_PIPE_BUF);

/*-------------------------------------------------------------------+
| Perform initial setup of daemon process.                           |
+-------------------------------------------------------------------*/
parent.p_id = getpid();
if (initialize(&parent) != 0)
   {
   pid_error(parent.p_id, errno,
                   "error during initilization, exiting");
   exit(-1);
   }

parent.p_sockfd = create_socket(parent.p_id);
if (parent.p_sockfd < 0)
   exit(-1);

sigusr2_set = FALSE;            /* see child_sig_handler           */
select_go = TRUE;               /* see child_sig_handler & select  */

/*-------------------------------------------------------------------+
| Use select() to wait for activity on a file descriptor in read_fds.|
| When a file descriptor is ready, perform the required task:        |
| 1. an error was returned by select(), terminate                    |
| 2. call accept_request() and fork() a child process to handle      |
| 3. if data is available on the read pipe fd, print the data        |
| When select_go is FALSE exit, refer to child_sig_handler           |
+-------------------------------------------------------------------*/
while (select_go)
   {
   FD_ZERO(&read_fds);
   FD_SET(parent.p_sockfd, &read_fds);
   #if defined(TRACE_CHLD)
      FD_SET(parent.p_pipe_r[0], &read_fds);
   #endif

   num_readfds =
           select(sizeof(read_fds)*8, &read_fds, NULL, NULL, NULL);

   printf("%6x: return value from select(): %d\n",
                                   parent.p_id, num_readfds);
   /*-------------------------------------------------------------+
   | select() will return a -1 when interrupted by a signal, in   |
   | this case errno equals EINTR. Ignore this case and select()  |
   +-------------------------------------------------------------*/
   if ((num_readfds == -1) && (errno != EINTR) )
      {
      pid_error(parent.p_id, errno, "select() failed");
      select_go = FALSE;
      }

   /*-------------------------------------------------------------+
   | select() will return a 0 when a timeout occurs. In this      |
   | case, timeout is NULL, and therefore we should never see     |
   | this condition                                               |
   +-------------------------------------------------------------*/
   if (num_readfds == 0 )
      {
      printf("%6x: select() returned zero\n", parent.p_id);
      }

   /*-------------------------------------------------------------+
   | check read_fds for a new socket connection and handle        |
   +-------------------------------------------------------------*/
   if (FD_ISSET(parent.p_sockfd, &read_fds) && (num_readfds > 0))
      {
      /*-------------------------------------------------------------+
      | NOTE: WE NEED TO BLOCK THE sa_mask TO KEEP ACCEPT FROM       |
      |       GETTING INTERRUPTED, AND THEN UN-BLOCK                 |
      +-------------------------------------------------------------*/
      sigprocmask(SIG_BLOCK, &parent.p_sa.sa_mask, NULL);
      newsock_fd = accept_socket(&parent);
      sigprocmask(SIG_UNBLOCK, &parent.p_sa.sa_mask, NULL);
      /*-------------------------------------------------------------+
      | check newsock_fd and fork() a child to handle client         |
      +-------------------------------------------------------------*/
      if (newsock_fd < 0)
         {
         printf("%6x: accept_socket() failed\n",parent.p_id);
         }
      else
         {
         printf("%6x: calling fork()\n",parent.p_id);
         fork_pid = fork();
      /*-------------------------------------------------------------+
      | use the fork() return code to determine if we are the parent |
      | or child task, and continue execution accordingly.           |
      +-------------------------------------------------------------*/
         switch(fork_pid)
            {
            case -1: fork_error(&parent, newsock_fd); break;
            case  0: child_process(&parent, newsock_fd); break;
            default: parent_process(&parent, newsock_fd, fork_pid);
            } /* switch */
         } /* else */
      } /* FD_ISSET */

   /*-------------------------------------------------------------+
   | check read_fds for data in the p_pipe_r and handle           |
   +-------------------------------------------------------------*/
   if (FD_ISSET(parent.p_pipe_r[0], &read_fds) && (num_readfds > 0))
      {
      pipe_byte_count =
              read(parent.p_pipe_r[0], pipe_buf, _POSIX_PIPE_BUF);
      write(STDOUT_FILENO, pipe_buf, pipe_byte_count);
      } /* FD_ISSET */

   } /* while select_go TRUE */

close(parent.p_sockfd);
close(tracesig_fd);
printf("%6x: tcpoed terminating\n",parent.p_id);
exit(0);
}

/*----------------------------------------------------------------+
| initialize() - obtain utsname data, setsockimp(), set signals,  |
| and set up read pipe for data passed from the child to parent   |
| process.                                                        |
+----------------------------------------------------------------*/
int initialize(process_t *parent)
{
sigset_t sascsigs;

printf("%6x: entered initialize()\n",parent->p_id);

if (get_sysinfo(parent->p_id) != 0 )
   {
   printf("%6x: unable to get_sysinfo(), exiting\n",parent->p_id);
   return(-1);
   }

if (pipe(parent->p_pipe_r) < 0)
   {
   pid_error(parent->p_id, errno, "error opening read pipe_fd");
   return(-1);
   }

if (setsockimp("OE") != 0)
   {
   printf("%6x: unable to enable setsockimp(), exiting\n",parent->p_id);
   return(-1);
   }

/*----------------------------------------------------------------+
| set child signal handler routine and define sascsigs            |
+----------------------------------------------------------------*/
parent->p_sa.sa_handler = child_sig_handler;
parent->p_sa.sa_flags = 0;

if (sigemptyset(&sascsigs) < 0)
   {
   pid_error(parent->p_id, errno, "sigemptyset sascsigs");
   return(-1);
   }

if (sigemptyset(&parent->p_sa.sa_mask) < 0)
   {
   pid_error(parent->p_id, errno, "sigemptyset sa_mask");
   return(-1);
   }

/*----------------------------------------------------------------+
| sigaddset()  - add SIGCHLD and SIGUSR2 to the signal mask       |
| oesigsetup() - define which signals are handled by SAS/C or OE  |
| sigaction()  - define the appropriate action to take when the   |
|                signal is raised                                 |
+----------------------------------------------------------------*/
sigaddset(&parent->p_sa.sa_mask, SIGCHLD);
sigaddset(&parent->p_sa.sa_mask, SIGUSR2);
oesigsetup(&parent->p_sa.sa_mask, &sascsigs);
if (sigaction(SIGCHLD, &parent->p_sa, NULL) < 0)
   {
   pid_error(parent->p_id, errno, "sigaction sigchld");
   return(-1);
   }

if (sigaction(SIGUSR2, &parent->p_sa, NULL) < 0)
   {
   pid_error(parent->p_id, errno, "sigaction sigusr2");
   return(-1);
   }

/*----------------------------------------------------------------+
| When TRACE_SIG defined, open the DD.                            |
+----------------------------------------------------------------*/
#if defined(TRACE_SIG)
   tracesig_fd = open("//DDN:TRACESIG", O_WRONLY|O_TRUNC|O_TEXT);
   if (tracesig_fd < 0)
      {
      pid_error(parent->p_id, errno,
            "open() failed for signal handler tracing");
      return(-1);
      }
#endif

return(0);
}

/*----------------------------------------------------------------+
| pid_error() - perror() related function with a prefix of pid_t  |
+----------------------------------------------------------------*/
void pid_error(pid_t pid, int p_errno, char *text)
{
char *t_error, buffer[132];
memset(buffer, '\0', 132);
t_error = strerror(p_errno);
sprintf(buffer,"%6x: %s: %s\n", pid, text, t_error);
write(STDERR_FILENO, buffer, strlen(buffer));
return;
}

/*----------------------------------------------------------------+
| get_sysinfo() - obtain and print utsname data                   |
+----------------------------------------------------------------*/
int get_sysinfo(pid_t parent_pid)
{
struct utsname ut;

printf("%6x: entered get_sysinfo()\n",parent_pid);

if (uname(&ut) < 0)
   return(-1);

printf("%6x: Executing on nodename: %s, Version %s Release %s of %s\n",
         parent_pid, ut.nodename, ut.version, ut.release, ut.sysname);
return(0);
}

/*----------------------------------------------------------------+
| create_socket() - initiates a socket(), bind() and listen()     |
+----------------------------------------------------------------*/
int create_socket(pid_t parent_pid)
{
int sock_fd;                   /* socket file descriptor          */
int so_optval;                 /* setsockopt value                */
struct sockaddr_in sa_serv;    /* server socket structure         */
struct servent *get_serv;      /* getservbyname structure         */

printf("%6x: entered create_socket()\n",parent_pid);

memset(&sa_serv, '\0', sizeof(sa_serv));
sa_serv.sin_family = AF_INET;
sa_serv.sin_addr.s_addr = htonl(INADDR_ANY);

/*----------------------------------------------------------------+
| getservbyname() - determine our "well-known" port: etc/services |
+----------------------------------------------------------------*/
get_serv = getservbyname(SERVERNAME, SERVERPROTO);
if (get_serv == NULL)
   {
   printf("%6x: service: %s, not found for protocol: %s\n",
                 parent_pid,SERVERNAME,SERVERPROTO);
   sa_serv.sin_port = htons(LISTENPORT);
   }
else
   {
   printf("%6x: service: %s, found for protocol: %s\n",
                 parent_pid,SERVERNAME,SERVERPROTO);
   sa_serv.sin_port = htons(get_serv->s_port);
   }

/*----------------------------------------------------------------+
| Get a socket of the proper type.                                |
+----------------------------------------------------------------*/
sock_fd  = socket(AF_INET, SOCK_STREAM, 0);
if (sock_fd == -1)
   {
   pid_error(parent_pid, errno, "socket() call failed");
   return(-1);
   }

/*----------------------------------------------------------------+
| set so_resueaddr - allow deamon to restart immediately          |
+----------------------------------------------------------------*/
so_optval = 1;
if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (char *) &so_optval,
           sizeof(SO_REUSEADDR)) == -1)
   {
   pid_error(parent_pid, errno, "setsockopt() so_reuseaddr failed");
   }
else
   printf("%6x: setsockopt() so_reuseaddr successful\n",parent_pid);

/*----------------------------------------------------------------+
| Bind our socket to the desired address so clients may reach us. |
+----------------------------------------------------------------*/
if (bind(sock_fd, &sa_serv, sizeof(sa_serv)) == -1)
   {
   pid_error(parent_pid, errno, "bind() call failed");
   close(sock_fd);
   return(-1);
   }

/*----------------------------------------------------------------+
| Set up a queue for incoming requests. Set maximum queue size to |
| 20. Typically, one would use SOMAXCONN in socket.h              |
+----------------------------------------------------------------*/
if (listen(sock_fd, 20 ) == -1)
   {
   pid_error(parent_pid, errno, "listen() call failed");
   close(sock_fd);
   return(-1);
   }

return(sock_fd);
}

/*----------------------------------------------------------------+
| accept_socket() - called when select() returns a socket in the  |
| read_fds. accept() the socket, and return the value in sock_fd  |
+----------------------------------------------------------------*/
int accept_socket(process_t *parent)
{
int sa_len,                     /* sizeof struct sock_addr_in      */
    sock_fd;                    /* sock_fd returned from accept()  */
struct sockaddr_in sa_client;   /* client sock_addr_in struct      */

printf("%6x: entered accept_socket()\n",parent->p_id);

memset(&sa_client, '\0', sizeof(sa_client));
sa_len = sizeof(sa_client);

sock_fd = accept(parent->p_sockfd, &sa_client, &sa_len);
if (sock_fd == -1)
   {
   pid_error(parent->p_id, errno, "accept() call failed");
   return(-1);
   }

return(sock_fd);
}

/*----------------------------------------------------------------+
| fork_error() - called when fork() returns a -1.                 |
| attempt to read the token sent by the client, and inform the    |
| client of an error condition.                                   |
+----------------------------------------------------------------*/
void fork_error(process_t *parent, int newsock_fd)
{
char child_token[TOK_SIZE + 1];
int  send_rc;

printf("%6x: entered fork_error()\n",parent->p_id);
memset(&child_token, '\0', TOK_SIZE + 1);

if (read_token(parent->p_id, newsock_fd, child_token) < 0)
   {
   pid_error(parent->p_id, errno,
                          "read() failed in read_token()");
   }
else
   {
   printf("%6x: token received from client ==> %s\n",
                                   parent->p_id,child_token);
   }

send_rc = send_token(parent->p_id, newsock_fd, FORK_P);
if (send_rc < 0)
   {
   pid_error(parent->p_id, errno,
                          "write() failed in send_token()");
   }

/*----------------------------------------------------------------+
| call read_eof - expect EOF from the client                      |
+----------------------------------------------------------------*/
if (read_eof(parent->p_id, newsock_fd) < 0)
   {
   pid_error(parent->p_id, errno,
                          "client error closing connection");
   }
else
   {
   printf("%6x: client closed connection successfully\n",
                                   parent->p_id);
   }

close(newsock_fd);
}


/*----------------------------------------------------------------+
| child_process() - entered when a child is spawned, fork_pid = 0 |
| read the token from the client, and set SIGUSR2 if              |
| requested. Send data back to the client, and close the          |
| connection. Send output to the parent via a pipe.               |
+----------------------------------------------------------------*/
void child_process(process_t *parent, int sock_fd)
{

char  child_token[TOK_SIZE + 1], /* data sent by remote client   */
      *endpipe_buf,              /* pointer to pipe_buf          */
      pipe_buf[_POSIX_PIPE_BUF], /* pipe buffer to send to parent*/
      *t_error;                  /* return from strerror()       */
int  send_rc,                    /* return code from send_token()*/
     so_optval,                  /* so_keepalive value           */
     patricide;                  /* kill the parent              */
struct linger hangon;            /* struct to delay close()      */
pid_t  child_pid;

child_pid = getpid();
close(parent->p_sockfd);

memset(&child_token, '\0', TOK_SIZE + 1);
memset(&pipe_buf, '\0', _POSIX_PIPE_BUF);

patricide = FALSE;
endpipe_buf = pipe_buf;
/*----------------------------------------------------------------+
| set so_keepalive - terminates disconnected sockets              |
+----------------------------------------------------------------*/
so_optval = 1;
if (setsockopt(sock_fd, SOL_SOCKET, SO_KEEPALIVE,
             (char *) &so_optval,  sizeof(SO_KEEPALIVE)) == -1)
   {
   t_error = strerror(errno);
   endpipe_buf += sprintf(endpipe_buf, "%6x: "
                   "setsockopt() so_keepalive failed\n",
                                        child_pid, t_error);
   }
else
   {
   endpipe_buf += sprintf(endpipe_buf, "%6x: setsockopt() so_keepalive "
                  "successful\n", child_pid);
   }

/*----------------------------------------------------------------+
| set so_linger - delay close() until data sent by tcpip          |
+----------------------------------------------------------------*/
hangon.l_onoff = 1;
hangon.l_linger = 1;
if (setsockopt(sock_fd, SOL_SOCKET, SO_LINGER,
             (char *) &hangon,  sizeof(hangon)) == -1)
   {
   t_error = strerror(errno);
   endpipe_buf += sprintf(endpipe_buf, "%6x: "
                   "setsockopt() so_linger failed\n",
                                        child_pid, t_error);
   }
else
   {
   endpipe_buf += sprintf(endpipe_buf, "%6x: setsockopt() so_linger "
                  "successful\n", child_pid);
   }

if (read_token(parent->p_id, sock_fd, child_token) < 0)
   {
   t_error = strerror(errno);
   endpipe_buf += sprintf(endpipe_buf, "%6x: "
                   "read() failed in read_token(): %s\n",
                                        child_pid, t_error);
   }
else
   {
   endpipe_buf += sprintf(endpipe_buf, "%6x: token received "
                  "from client ==> %s\n", child_pid, child_token);
   }

/*----------------------------------------------------------------+
| check the child_token and terminate as directed by the client   |
+----------------------------------------------------------------*/
if (memcmp(child_token,"ttfn",4) == 0)
   {
   patricide = TRUE;
   send_rc = send_token(child_pid, sock_fd, KILL_P);
   }
else
   {
   send_rc = send_token(child_pid, sock_fd, CHILD_P);
   }

/*----------------------------------------------------------------+
| check the send_rc and update pipe data                          |
+----------------------------------------------------------------*/
if (send_rc < 0)
   {
   t_error = strerror(errno);
   endpipe_buf += sprintf(endpipe_buf, "%6x: write() failed in"
                "send_token(): %s\n", child_pid, t_error);
   }
else
   {
   endpipe_buf += sprintf(endpipe_buf, "%6x: sent %d bytes to "
                  "remote client\n", child_pid, send_rc);
   }

/*----------------------------------------------------------------+
| call read_eof - expect EOF from the client                      |
+----------------------------------------------------------------*/
if (read_eof(parent->p_id, sock_fd) < 0)
   {
   t_error = strerror(errno);
   endpipe_buf += sprintf(endpipe_buf, "%6x: "
                   "client error closing connection: %s\n",
                                        child_pid, t_error);
   }
else
   {
   endpipe_buf += sprintf(endpipe_buf, "%6x: client closed "
                  "connection successfully\n", child_pid);
   }

write(parent->p_pipe_r[1], pipe_buf, strlen(pipe_buf));
close(sock_fd);

if (patricide)
   kill(getppid(),SIGUSR2);     /* SIGUSR2 - child_sig_handler   */

exit(0);
}

/*----------------------------------------------------------------+
| parent_process() - entered when a successful fork occurs, and   |
| the parent is executing. Print the processid of the child, and  |
| close the parent socket association.                            |
+----------------------------------------------------------------*/
void parent_process(process_t *parent,
                              int newsock_fd, pid_t fork_pid)
{
printf("%6x: entered parent_process()\n",parent->p_id);
printf("%6x: process id: %6x started\n",parent->p_id,fork_pid);
close(newsock_fd);
}

/*----------------------------------------------------------------+
| read_token() - read the first message sent by the client,       |
| convFromAscii, and return number of bytes received or an error. |
 +---------------------------------------------------------------*/
int  read_token(pid_t gen_pid, int sock_fd, char *token)
{
char *cp, *ptr, buffer[TOK_SIZE + 1];
int  bytes_rem, num_read, tot_read;

memset(buffer, '\0', TOK_SIZE + 1);
ptr = buffer;

bytes_rem = TOK_SIZE;
tot_read = 0;
while (bytes_rem > 0)
   {
   num_read = read(sock_fd, ptr, bytes_rem);
   if (num_read <= 0)
      {
      return(-1);
      }
   else
      {
      bytes_rem -= num_read;
      tot_read += num_read;
      ptr += num_read;
      }
   }

buffer[tot_read] = '\0';
convFromAscii(buffer);
memcpy(token, buffer, tot_read);
return(tot_read);
}

/*----------------------------------------------------------------+
| read_eof() - read on the socket, expect EOF                     |
+----------------------------------------------------------------*/
int read_eof(pid_t gen_pid, int sock_fd)
{
char ptr[1];
int  num_read;

num_read = read(sock_fd, ptr, 1);
if (num_read < 0)
   {
   return(-1);
   }

/*----------------------------------------------------------------+
| we expect an EOF, if data is available on the socket, ignore    |
| the condition. Set num_read to 0 and return.                    |
+---------------------------------------------------------------*/
if (num_read > 0)
   {
   num_read = 0;
   }

return(num_read);
}


/*----------------------------------------------------------------+
| send_token() - called by fork_error(), or child_process().      |
| determine who the caller is, build the message, convToAscii,    |
| send the message length and message to the client.              |
+----------------------------------------------------------------*/
int  send_token(pid_t gen_pid, int sock_fd, int call_p)
{
#define MES_SIZE 80
char message[MES_SIZE + 1], *cp;
int msg_len, send_cnt, send_rc;

memset(message,'\0',MES_SIZE + 1);
switch(call_p)
   {
   case FORK_P: sprintf(message,"%6x: error creating server, "
                                "closing connection\n",gen_pid);
                break;
   case CHILD_P: sprintf(message,"%6x: child started successfully, "
                                 "closing connection\n",gen_pid);
                break;
   case KILL_P: sprintf(message,"%6x: child started successfully, "
                            "terminating daemon\n",gen_pid);
                break;
   default: sprintf(message,"%6x: error, called by invalid process, "
                            "closing connection\n",gen_pid);
   }

convToAscii(message);
msg_len = strlen(message);

send_cnt = 0;
send_rc = write(sock_fd, &msg_len, sizeof(int));
if (send_rc < 0)
   return(send_rc);
else
   send_cnt += send_rc;

send_rc = write(sock_fd, message, msg_len + 1 );
if (send_rc < 0)
   return(send_rc);
else
   send_cnt += send_rc;

return(send_cnt);
}

/*----------------------------------------------------------------+
| child_sig_handler() - process SIGCHLD and SIGUSR2 signals.      |
| A SIGCHLD signals the parent process that a child process has   |
| terminated. The SIGUSR2 is a user-defined signal that           |
| (indirectly) terminates the parent process.                     |
+----------------------------------------------------------------*/
void child_sig_handler(int signal)
{

#if defined(TRACE_SIG)
   char  *endsig_buf,            /* pointer to sig_buf           */
      sig_buf[_POSIX_PIPE_BUF];  /* sig buffer write signal data */
#endif
int status;

pid_t parent_pid, child_pid;
parent_pid = getpid();

#if defined(TRACE_SIG)
   memset(&sig_buf, '\0', _POSIX_PIPE_BUF);
   endsig_buf = sig_buf;

   endsig_buf += sprintf(endsig_buf,"%6x: "
                      "entered child_sig_handler()\n",parent_pid);
#endif

if (signal == SIGCHLD)
   {
   child_pid = waitpid(-1,&status,WNOHANG);

   #if defined(TRACE_SIG)
      endsig_buf += sprintf(endsig_buf,"%6x: "
                       "received a SIGCHLD\n",parent_pid);
      endsig_buf += sprintf(endsig_buf,"%6x: "
                       "return code from child process, %6x: %d\n",
                                       parent_pid, child_pid, status);
   #endif
   }

if (signal == SIGUSR2)
   {
   select_go = FALSE;

   #if defined(TRACE_SIG)
      endsig_buf += sprintf(endsig_buf,"%6x: "
                         "received a SIGUSR2\n",parent_pid);
   #endif
   }

#if defined(TRACE_SIG)
   write(tracesig_fd, sig_buf, strlen(sig_buf));
#endif

return;
}


Copyright (c) 2000 SAS Institute Inc. All Rights Reserved.
Terms of Use & Legal Information | Privacy Statement