/* @(#)sp_spmd.c 17.1.1.1 (ESO-IPG) 01/25/02 17:26:43 */ /*--------------------------------------------------------------------- * $Date: 93/09/09 20:35:30 $ $Revision: 2.9.6.3 $ *--------------------------------------------------------------------- * Copyright (c) 1992, Visual Edge Software Ltd. * * ALL RIGHTS RESERVED. Permission to use, copy, modify, and * distribute this software and its documentation for any purpose * and without fee is hereby granted, provided that the above * copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Visual Edge Software not be * used in advertising or publicity pertaining to distribution of * the software without specific, written prior permission. The year * included in the notice is the year of the creation of the work. *--------------------------------------------------------------------- * Motorola additions to make the subprocess management more amenable to * an application doing it's own signal handling and/or child process * creation (esp. in the "inocuous" cases of calls to popen(3S), system(3S) * or getcwd(3C) are contained in "ifdef SPMD conditionals. *--------------------------------------------------------------------*/ /*--- include files ---*/ #include #include #include "uxproto.h" #include "version.h" #include "subproc.h" #include "sp_spmd.h" #include "sp_pty.h" /*--- macro symbolic constants ---*/ /*--- macro functions ---*/ /*--- types ---*/ /*--- external functions ---*/ /*--- external variables ---*/ /*--- global variables ---*/ spmd_info_t UxSpmd = { -1, 0, 0, 0}; /*--- file global variables ---*/ M_FILE_VERSION("$Header") int UxSpmdSubprocCount; spmd_pid_pty_t UxSpmdStates[MAX_SUBPROC]; long UxSpmdInterval = LONG_INTERVAL; int MultiByteMode; static int TimeOutId = -1; /*--- forward declaration of static functions ---*/ static void SpmdCiao UXPROTO((void)); static void SpmdDeadProc UXPROTO((int i)); static void SpmdSigHandler UXPROTO((int sig)); static int SpmdReadPtys UXPROTO((void)); static void SpmdMain UXPROTO((void)); static void SpmdSendExitMsg UXPROTO((void)); /*---------------------------------------------------------------------- * NAME: * DESCRIPTION: * PARAMETERS: SubprocInfo_t *sp - new process char *args - command line * RETURN: ERROR/NO_ERROR * EXT REFS: UxSpmd * EXT EFFECTS: UxSpArray via sp * ASSUMPTIONS: * REVISIONS: 05/01/93 fix3810 clean-up * dd/mm/yy fix# fix_description *--------------------------------------------------------------------*/ int UxExecSubprocess(sp, args) SubprocInfo_t *sp; char *args; { int cmdlen; char *process; spmd_create_req req; spmd_create_rsp *rsp; process = sp->process; req.mtype = SPMD_CREATE; req.echo = sp->echo; req.cmd[0] = '\0'; /*EOS*/ if (args == NULL) args = ""; sprintf(req.cmd, "%s %s", process, args); cmdlen = strlen(req.cmd); /* also used in msgsnd() */ if (-1 == msgsnd(UxSpmd.spmd_pc_msqid, &req, cmdlen+sizeof(req.echo), IPC_NOWAIT)) { perror("exec_subprocess: can not send SPMD_CREATE to daemon"); return(ERROR); } /* now we must wait for the daemon to do it's magic ... */ if (UxSpmdReadMsg(SPMD_RSP) == ERROR) { return(ERROR); } rsp = (spmd_create_rsp *)UxSpmd.spmd_rmsgp; if (rsp->response == NACK) { UxStandardError("Cannot start child process %s \n", process); return(ERROR); } sp->handle = rsp->handle; sp->pid = rsp->pid; return(NO_ERROR); } /*---------------------------------------------------------------------- * NAME: * DESCRIPTION: * PARAMETERS: long msgtyp - message type we are waiting for * RETURN: int - ERROR or 0 * EXT REFS: UxSpArray, UxSpmd * EXT EFFECTS: UxSpmd * ASSUMPTIONS: * REVISIONS: 05/01/93 fix3810 clean-up dd/mm/yy fix# fix_description *--------------------------------------------------------------------*/ int UxSpmdReadMsg (msgtyp) long msgtyp; /* zero -> called from timeout * non-zero -> explicit rsp */ { int i, rc, msgflg; extern int errno; int msgfound = 0; msgflg = msgtyp ? 0 : IPC_NOWAIT; UxSpmd.spmd_rmsgtyp = 0; /* clear buffer */ while ((rc=msgrcv(UxSpmd.spmd_cp_msqid, UxSpmd.spmd_rmsgp, MSGMAX, msgtyp, msgflg)) != -1) { msgfound = 1; switch (UxSpmd.spmd_rmsgp->mtype) { case SPMD_OUTPUT: /* output from child process */ { spmd_output_t *msg = (spmd_output_t *)UxSpmd.spmd_rmsgp; UxSpmd.spmd_rmsgrp = msg->output; UxSpmd.spmd_rmsgsiz = rc - sizeof(msg->pid); for (i=0; ipid == msg->pid)) { UxOutputHandler(UxSpArray[i]); break; } } ASSERT (i * DESCRIPTION: * PARAMETERS: none * RETURN: True/False * EXT REFS: UxSpmd * EXT EFFECTS: * ASSUMPTIONS: * REVISIONS: 25/04/93 *--------------------------------------------------------------------*/ static int IsSpmdAlive (tmsgp) struct msgbuf *tmsgp; { if (tmsgp && (tmsgp->mtype == SPMD_EXIT)) { return False; } if ((UxSpmd.spmd_pc_msqid == -1) || (UxSpmd.spmd_cp_msqid == -1) || (UxSpmd.spmd_pid == -1)) { return False; } return True; } /*---------------------------------------------------------------------- * NAME: * DESCRIPTION: * PARAMETERS: none * RETURN: void * EXT REFS: UxSpmdStates, UxSpmdSubprocCount * EXT EFFECTS: UxSpmdStates, UxSpmdSubprocCount * ASSUMPTIONS: * REVISIONS: 25/04/93 *--------------------------------------------------------------------*/ static void ResetSpmdSubprocs () { int i; for (i=0;i * DESCRIPTION: * PARAMETERS: none * RETURN: void * EXT REFS: UxSpmdStates, UxSpmdSubprocCount, UxSpmd * EXT EFFECTS: UxSpmdStates, UxSpmdSubprocCount, UxSpmd * ASSUMPTIONS: * REVISIONS: 05/01/93 fix3810 clean-up * 25/03/93 fix4037 to deal with multibyte * 29/04/93 fix4207 *--------------------------------------------------------------------*/ static void SpmdMain() { int i; int rc = 0; struct msgbuf *tmsgp; extern int errno; #ifdef RUNTIME /*----------------------------------------------------- * In DESIGN_TIME, UIM/X properly treats all these signals. *-----------------------------------------------------*/ signal(SIGINT, SpmdCiao); /* to handle Ctrl-C */ signal(SIGQUIT, SpmdCiao); #endif signal(SIGALRM, SpmdSigHandler); /* arm the signal catcher */ if ( !(tmsgp=(struct msgbuf *)UxMalloc(SIZEOFMAXMSGBUF))) { perror("Subprocess manager: memory allocation failed"); exit (errno); } /*--------------------------------------------------------- /* Note: on sun4 MB_CUR_MAX doesn't work properly. It is * O which is wrong but doesn't break this code. It * works for those with proper international sun os's. *---------------------------------------------------------*/ MultiByteMode = (MB_CUR_MAX > 1) ? 1 : 0; /* Set if in multibyte mode */ ResetSpmdSubprocs (); /* * Main processing loop is to wait on the message queue from the parent. * Note: will be extended to select on this queue plus all managed * sub-process pty's */ for(;;) { /* Read a message from the application * (more frequently if we have subprocs) */ alarm ((UxSpmdSubprocCount ? 1 : 5)); while (SpmdReadPtys() && (rc=msgrcv(UxSpmd.spmd_pc_msqid,tmsgp, MSGMAX, 0, 0)) == -1 && errno == EINTR) { if (!IsSpmdAlive(tmsgp)) { SpmdCiao(); } /* nop body; skip interrupts */ ; } alarm(0); if (rc == -1) { if (!IsSpmdAlive(tmsgp)) { SpmdCiao(); } perror("UIM/X: spmd_main: msgrcv failed"); #ifdef DEBUG /* check out the error ... could be fatal */ exit (errno); #endif /*DEBUG*/ } if (!IsSpmdAlive(tmsgp)) { SpmdCiao(); } /*----------------------------------------------------- * Process the message. *-----------------------------------------------------*/ switch (tmsgp->mtype) { case SPMD_CREATE: /* create a sub-process */ { int new_handle, new_pid; spmd_create_req *req = (spmd_create_req *)tmsgp; spmd_create_rsp msg; req->cmd[rc-sizeof(req->echo)] = '\0'; /* */ /* Disable the SIGCHLD signal while we are creating the process because it could terminate and the daemon wouldn't know its pid. */ signal(SIGCHLD, SIG_DFL); /* disable */ if((i=UX_SPMD_EXEC_SUBPROCESS(req->cmd, req->echo, &new_handle, &new_pid)) == ERROR) { msg.response = NACK; } else { msg.response = ACK; for (i = 0; i < MAX_SUBPROC; i++) { if (UxSpmdStates[i].pid == -1) { break; } } UxSpmdStates[i].pid = new_pid; UxSpmdStates[i].pty = new_handle; msg.handle = new_handle; msg.pid = new_pid; UxSpmdSubprocCount++; } /* Now that the process is created, we can rearm the signal and catch all the children that died during the creation of the process. */ signal(SIGCHLD, SpmdSigHandler); /* arm the signal */ msg.mtype = SPMD_RSP; while ( (msgsnd(UxSpmd.spmd_cp_msqid, &msg, sizeof(spmd_create_rsp)-sizeof(msg.mtype), 0) == -1) && (errno == EINTR) ) { /* nop body */ ; } break; } case SPMD_INPUT: /* input for a process */ { spmd_input_t *msg = (spmd_input_t *)tmsgp; msg->input[rc-sizeof(msg->pid)] = '\0'; /* */ for (i=0; i < MAX_SUBPROC; i++) { if (UxSpmdStates[i].pid == msg->pid) { break; } } if (i == MAX_SUBPROC) { /* App. gave us a bad pid */ #ifdef DEBUG fprintf(stderr, ">>(%d)) [spmd_main] SPMD_INPUT received BUT %d is a BAD PID !\n", getpid(), msg->pid); #endif /*DEBUG*/ break; /* silently ignore */ } if (UxSpmdStates[i].exit_status == -1) { /* not dead yet ? */ ASSERT (UxSpmdStates[i].pty >= 0); write(UxSpmdStates[i].pty, msg->input, rc-sizeof(msg->pid)); } break; } default: /* garbage or wrong directional msg */ #ifdef DEBUG fprintf(stderr,"UIM/X: spmd_main: protocol error\n"); #endif /*DEBUG*/ break; } tmsgp->mtype = 0; /* A.Nil. Legal value */ } } /*---------------------------------------------------------------------- * NAME: * DESCRIPTION: * PARAMETERS: none * RETURN: returns: 0 on success, non-0 on error/failure * EXT REFS: UxSpmd * EXT EFFECTS: spmd_info_t struct is filled in with relevant info. UxSpmd * ASSUMPTIONS: * REVISIONS: 05/01/93 fix3810 clean-up 23/04/93 fix4181 handle Ctrl-C at runtime dd/mm/yy fix# fix_description *--------------------------------------------------------------------*/ int UxSpmdCreateDaemon() { void SpmdMain(); /* create directional message queues (one each direction) */ UxSpmd.spmd_pc_msqid = msgget(IPC_PRIVATE,IPC_CREAT|S_IRUSR|S_IWUSR); UxSpmd.spmd_cp_msqid = msgget(IPC_PRIVATE,IPC_CREAT|S_IRUSR|S_IWUSR); if ( (UxSpmd.spmd_cp_msqid == -1) || (UxSpmd.spmd_pc_msqid == -1) ) { #ifdef DEBUG perror("spmd_create_daemon: msgget failed"); #endif /*DEBUG*/ return(ERROR); } #ifdef DEBUG fprintf(stderr, "spmd_create_daemon c->p msqid = 0x%x, p->c msqid = 0x%x\n", UxSpmd.spmd_cp_msqid, UxSpmd.spmd_pc_msqid); #endif /*DEBUG*/ /* allocate the receive-message buffer */ if ( !(UxSpmd.spmd_rmsgp=(struct msgbuf *)UxMalloc(SIZEOFMAXMSGBUF)) ) { #ifdef DEBUG perror("spmd: spmd_create_daemon: UxMalloc failed"); #endif /*DEBUG*/ return(ERROR); } switch (UxSpmd.spmd_pid=fork()) { case -1: /* forking error */ #ifdef DEBUG perror("spmd: spmd_create_daemon: fork failed"); #endif /*DEBUG*/ return(ERROR); case 0: /* child: the sub-process manager daemon */ SpmdMain(); /*NOTREACHED*/ break; default: /* parent: the application */ /*------------------------------------------- * Start reading the Spmd msg queue. *-------------------------------------------*/ TimeOutId = XtAppAddTimeOut(UxAppContext, UxSpmdInterval, UxSpmdReadMsg, 0); break; } return (0); } /*---------------------------------------------------------------------- * NAME: * DESCRIPTION: * PARAMETERS: int sig -- signal being handled * RETURN: void * EXT REFS: UxSpmdStates, UxSpmdSubprocCount * EXT EFFECTS: UxSpmdStates * ASSUMPTIONS: * REVISIONS: 05/01/93 fix3810 clean-up dd/mm/yy fix# fix_description *--------------------------------------------------------------------*/ static void SpmdSigHandler(sig) int sig; { int status, pid, i; switch (sig) { case SIGCHLD: while ( (pid=wait(&status)) == -1 && (errno == EINTR) ) { /* nop body */ ; } if (pid == -1) { perror("subprocess manager: wait failed"); } for (i=0; i < MAX_SUBPROC; i++) { /* Mark the process dead. */ if (UxSpmdStates[i].pid == pid) { ASSERT (UxSpmdStates[i].exit_status == -1); #ifdef DEBUG fprintf(stderr, "<<(%d) [spmd_sig_handler] pid %d died, status %d\n", getpid(), pid, status); #endif /*DEBUG*/ UxSpmdStates[i].exit_status = status; break; } } /* * Don't check for falling off the end of the loop because * a forked child would only be placed into the table if the * handshake, pty's, exec, etc. all worked. */ break; case SIGALRM: /* used merely to break a blocked msgrcv(). */ if (getppid() == 1) { /* have we been adopted by init ? */ SpmdCiao(); } alarm ((UxSpmdSubprocCount?1:5)); break; default: fprintf(stderr,"Unexpected signal caught. Impossible!\n"); break; } signal(sig, SpmdSigHandler); /* re-arm the signal catcher */ } /*---------------------------------------------------------------------- * NAME: * DESCRIPTION: * PARAMETERS: none * RETURN: int - always 1 * EXT REFS: UxSpmdSubprocCount, UxSpmdStates, UxSpmd * EXT EFFECTS: globals or other things altered * ASSUMPTIONS: * REVISIONS: 05/01/93 fix3810 clean-up * 02/02/93 fix3890 change size of message sent in msgsnd * 25/03/93 fix4037 to deal with multibyte * sept 93 fix4491 sun4 MB_CUR_MAX workaround. *--------------------------------------------------------------------*/ static int SpmdReadPtys() { int i, j; int n; int ok; char wc_buffer[MB_LEN_MAX * MSGMAX]; void SpmdDeadProc(); if (!IsSpmdAlive(NULL)) { SpmdCiao(); } /* while there are children ... */ for (i=0; UxSpmdSubprocCount && (i < MAX_SUBPROC); i++) { if (UxSpmdStates[i].pid != -1) { /* slot is associated with a process */ int rec = 0, wanted, num; spmd_output_t msg; char *buf = msg.output; /* subtract 1 to leave space for '\0' */ if (MultiByteMode) { #ifdef sun4 wanted = sizeof(msg.output) - (MB_CUR_MAX > 0 ? MB_CUR_MAX : 1) - 1; #else wanted = sizeof(msg.output) - MB_CUR_MAX - 1; #endif } else { wanted = sizeof(msg.output) - 1; } /* read must be non-blocking */ buf[0] = '\0'; while ( wanted && (num=read(UxSpmdStates[i].pty, buf, wanted)) ) { if (num==-1) { if (errno == EINTR) { continue; } break; } wanted -= num; buf[num] = '\0'; /* tail the string */ buf += num; /* buffer pointer bump */ rec += num; /* increment received counter */ } /* In multibyte mode, be sure that we have a * complete character, so that we pass meaningful * strings along to the application. */ if (MultiByteMode) { /* Keep reading 1 char at a time until * we get a complete mbs string. */ while (mbstowcs(wc_buffer, msg.output, rec) == -1) { n = read(UxSpmdStates[i].pty, buf, 1); if (n == -1) { if (errno == EINTR || errno == EAGAIN) { continue; /* while */ } else { /* Read really failed. * Give up. */ break; } } else { rec += n; buf[n] = '\0'; buf += n; } } } /* send output from process to the application */ if (rec) { msg.mtype = SPMD_OUTPUT; msg.pid = UxSpmdStates[i].pid; /* send SPMD_OUTPUT message to application */ while ((msgsnd(UxSpmd.spmd_cp_msqid, &msg, rec + sizeof(msg.pid), 0) == -1) && (errno == EINTR) ) { /* nop body */ ; } } if (UxSpmdStates[i].exit_status != -1) { /* Process has died. */ SpmdDeadProc(i); } } } return(1); /* spmd_main() relies on this always succeeding :-) */ } /*---------------------------------------------------------------------- * NAME: * DESCRIPTION: * PARAMETERS: int i -- subprocess table index * RETURN: void * EXT REFS: UxSpmdStates, UxSpmd, UxSpmdSubprocCount * EXT EFFECTS: UxSpmdStates, UxSpmdSubprocCount * ASSUMPTIONS: * REVISIONS: 05/01/93 fix3810 clean-up dd/mm/yy fix# fix_description *--------------------------------------------------------------------*/ static void SpmdDeadProc(i) int i; { spmd_dead_msg msg; msg.mtype = SPMD_DIED; ASSERT((UxSpmdStates[i].pid != -1)&&(UxSpmdStates[i].exit_status != -1)); msg.pid = UxSpmdStates[i].pid; msg.status = UxSpmdStates[i].exit_status; /* generate the SPMD_DIED message and send it to the application */ while ( (msgsnd(UxSpmd.spmd_cp_msqid, &msg, sizeof(msg)-sizeof(msg.mtype), 0) == -1) && (errno == EINTR) ) { /* nop body */ ; } /* Assume the PTY is done with. * This guy is dead. Bury him and forget him ! */ (void) close(UxSpmdStates[i].pty); UxSpmdStates[i].pid = UxSpmdStates[i].pty = UxSpmdStates[i].exit_status = -1; UxSpmdSubprocCount--; ASSERT(UxSpmdSubprocCount >= 0); } /*---------------------------------------------------------------------- * NAME: * DESCRIPTION: * PARAMETERS: none * RETURN: void * EXT REFS: UxSpmd * EXT EFFECTS: * ASSUMPTIONS: * REVISIONS: 23/04/93 fix4181 handle Ctrl-C at runtime * dd/mm/yy fix# fix_description *--------------------------------------------------------------------*/ static void SpmdSendExitMsg () { int cmdlen; spmd_dead_msg req; int status, pid; req.mtype = SPMD_EXIT; req.pid = 0; req.status = -1; cmdlen = 1; if (-1 == msgsnd(UxSpmd.spmd_pc_msqid, &req, cmdlen+sizeof(req.pid), IPC_NOWAIT)) { return; } /*----------------------------------------------------- * Wait for the SPMD to die before handing control * back to the main application. *-----------------------------------------------------*/ while ( (pid=wait(&status)) == -1 && (errno == EINTR) ) { /* nop body */ ; } } /*---------------------------------------------------------------------- * NAME: * DESCRIPTION: * PARAMETERS: none * RETURN: void * EXT REFS: UxSpmd * EXT EFFECTS: globals or other things altered * ASSUMPTIONS: * REVISIONS: 23/04/93 fix4181 handle Ctrl-C at runtime * dd/mm/yy fix# fix_description *--------------------------------------------------------------------*/ void UxDestroySpmd() { /*----------------------------------------------------- * Now don't you just love looking at this code ? * Look no further cause bugs you shall find none :-) * but just in case you do, just send us the fix * report instead of a bug report :-) * After all, this code was mostly written by one of * you collegues. *-----------------------------------------------------*/ /*----------------------------------------------------- * If the SPMD is not running, nothing to do. Just return. *-----------------------------------------------------*/ if (!IsSpmdAlive(NULL)) { return; } UxMarkFileCloseOnExec(1); if (TimeOutId != -1) { XtRemoveTimeOut (TimeOutId); TimeOutId = -1; } /*----------------------------------------------------- * When the parent process is about to die, send a msg * to the SPMD so that it does its cleanup and exits. *-----------------------------------------------------*/ SpmdSendExitMsg (); /*----------------------------------------------------- * In case the parent crashes or receives an interrupt * signal, we are not guaranteed that the SPMD main loop * will still be called so we force a cleanup of the msg * queues at the application level. *-----------------------------------------------------*/ if ((UxSpmd.spmd_pid != 0) && (UxSpmd.spmd_pid != -1)) { SpmdCiao(); } UxSpmd.spmd_pid = -1; } /*---------------------------------------------------------------------- * NAME: * DESCRIPTION: * PARAMETERS: none * RETURN: void * EXT REFS: UxSpmd * EXT EFFECTS: globals or other things altered * ASSUMPTIONS: * REVISIONS: 05/01/93 fix3810 cleanup * dd/mm/yy fix# fix_description *--------------------------------------------------------------------*/ static void SpmdCiao() { /*----------------------------------------------------- * Upon exiting, clean the message queues. *-----------------------------------------------------*/ if ((UxSpmd.spmd_pid != -1) && (UxSpmd.spmd_pc_msqid != -1)) { (void)msgctl(UxSpmd.spmd_pc_msqid, IPC_RMID,NULL); UxSpmd.spmd_pc_msqid = -1; } if ((UxSpmd.spmd_pid != -1) && (UxSpmd.spmd_cp_msqid != -1)) { (void)msgctl(UxSpmd.spmd_cp_msqid, IPC_RMID,NULL); UxSpmd.spmd_cp_msqid = -1; } /*----------------------------------------------------- * The child process will kill all remaining subbprocess * and exit itself. *-----------------------------------------------------*/ if (UxSpmd.spmd_pid == 0) { UxMarkFileCloseOnExec(1); UxKillAllSubprocs (); UxSpmd.spmd_pid = -1; exit(0); } } /*---------------------------------------------------------------------- * NAME: * DESCRIPTION: * PARAMETERS: int *sp_status - returned status of the subprocess * RETURN: int - pid of dead subprocess * EXT REFS: UxSpmd * EXT EFFECTS: globals or other things altered * ASSUMPTIONS: * REVISIONS: 08/01/93 fix3810 creation * dd/mm/yy fix# fix_description *--------------------------------------------------------------------*/ int UxWhatSpIsDead(sp_status) int *sp_status; { int pid; spmd_dead_msg *msg = (spmd_dead_msg *)UxSpmd.spmd_rmsgp; pid = msg->pid; *sp_status = msg->status; return(pid); }