/*---------------------------------------------------------------------+
| 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;
}
|