/*---------------------------------------------------------------------+ | 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 #include #include #include #include #include #include /*-------------------------------------------------------------------+ | Miscellaneous headers | +-------------------------------------------------------------------*/ #include #include #include #include #include #include #include #include #include #include /*-------------------------------------------------------------------+ | 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; }