/*----------------------------------------------------------------------+*/ static int set_pty_state(int fd, struct termios *term_state) /*----------------------------------------------------------------------+*/ { if (fd < 0 || !isatty(fd)) return(TRUE); /* Go to cooked mode (modify terminal state) */ if(tcsetattr(fd, TCSANOW, term_state)==ERROR) { SPC_Error(SPC_Bad_tc_Call,(XeString)"tcsetattr"); return(SPC_ERROR); } #ifdef __sun { struct winsize size; size.ws_row = 0; size.ws_col = 0; size.ws_xpixel = 0; size.ws_ypixel = 0; if(ioctl(fd, TIOCSWINSZ, &size) == ERROR) { SPC_Error(SPC_Bad_tc_Call,(XeString)"tc_setwinsize"); return(SPC_ERROR); } } #endif return(TRUE); }
/*----------------------------------------------------------------------+*/ static int initpty(Wire *wire) /*----------------------------------------------------------------------+*/ { /* Find first available master/slave pair */ /* set both sides of wire to -1 (unitialized convention) */ wire->fd[MASTER_SIDE] = wire->fd[SLAVE_SIDE] = -1; #ifdef HAVE_PTMS if (!getspec1170ptypair(wire)) #endif { /* Start with convention 1 */ strcpy(wire->master_name, MPREFIX_1); strcpy(wire->slave_name, SPREFIX_1); if (!getptypair(wire)) { /* Cannot get that pair, so try convention 2 */ strcpy(wire->master_name, MPREFIX_2); strcpy(wire->slave_name, SPREFIX_2); if (!getptypair(wire)) { /* No available pty's ?? */ wire->master_name[0] = wire->slave_name[0] = (XeChar)'\0'; wire->fd[MASTER_SIDE] = wire->fd[SLAVE_SIDE] = -1; SPC_Error(SPC_No_Pty); return(SPC_ERROR); } } } return(TRUE); }
static char *get_path_from_context ( char *context) { char *host = NULL; char *file = NULL; char *netfile = NULL; char *path = NULL; char tmp[MAXPATHLEN]; char *pch; /* * Break context into its host and file parts. */ if (context == NULL) return (NULL); (void) strcpy (tmp, context); file = tmp; if ((pch = (char *) strchr (tmp, ':')) != NULL) { host = tmp; file = pch + 1; *pch = '\000'; } if (!host) return (strdup (file)); netfile = (char *) tt_host_file_netfile (host, file); if (tt_ptr_error (netfile) != TT_OK) { SPC_Error (SPC_Cannot_Create_Netfilename, context, host); return (NULL); } path = (char *) tt_netfile_file (netfile); tt_free (netfile); if (tt_ptr_error (path) != TT_OK) { SPC_Error (SPC_Cannot_Create_Netfilename, context, host); return (NULL); } return (path); }
/*----------------------------------------------------------------------+*/ int close_channel_object (SPC_Channel_Ptr channel) /*----------------------------------------------------------------------+*/ { Wire *wirelist, *next_wire; int i; SPC_Channel_Ptr trail, ptr; /* Remove the channel from the activation list */ if(spc_activation_list == channel) spc_activation_list = channel->next; else { trail = spc_activation_list; while(trail) { ptr = trail->next; if(ptr == channel) { trail->next = ptr->next; break; } trail=ptr; } if(!trail) { SPC_Error(SPC_Closed_Channel); return(SPC_ERROR); } } /* Deallocate any memory allocated to the subfields */ if(IS_SPCIO_DEALLOC_ARGV(channel->IOMode)) SPC_Free_Envp(channel->argv); SPC_Free_Envp(channel->envp); wirelist=channel->wire_list; while(wirelist) { next_wire=wirelist->next; free_wire(wirelist); wirelist=next_wire; } for(i=1; i<3; i++) if(channel->linebufs[i]) free((char *)channel->linebufs[i]); /* Free the queue associated with the channel */ SPC_Flush_Queued_Data(channel); Xe_release_queue(channel->queued_remote_data); /* Deallocate the channel */ free((char *)channel); return(TRUE); }
/*----------------------------------------------------------------------+*/ int SPC_Check_Style(int iomode) /*----------------------------------------------------------------------+*/ { int stylecount=0; /* First, make sure that we have only one style bit set */ /*** NOTE - We can probably do something more tricky here, to be more efficient. However, I am going to do this the slow way to be safe */ if(IS_SPCIO_NOIOMODE(iomode)) stylecount++; if(IS_SPCIO_PIPE(iomode)) stylecount++; if(IS_SPCIO_PTY(iomode)) stylecount++; if(stylecount != 1) { SPC_Error(SPC_Illegal_Iomode); return(SPC_ERROR); } /* Okay, now check to make sure we don't have any conflicting modes set */ if ((IS_SPCIO_LINEEDIT(iomode) && IS_SPCIO_PIPE(iomode)) || (IS_SPCIO_PTY(iomode) && IS_SPCIO_WAIT(iomode) && !IS_SPCIO_TOOLKIT(iomode)) || (!IS_SPCIO_NOIO(iomode) && IS_SPCIO_USE_LOGFILE(iomode)) ) { SPC_Error(SPC_Illegal_Iomode); return(SPC_ERROR); } return(TRUE); }
/*----------------------------------------------------------------------+*/ int SPC_Read_Chars(SPC_Connection_Ptr connection, int request_len, XeString charptr) /*----------------------------------------------------------------------+*/ { int numchars, numread; int numtoread=request_len; int sid=connection->sid; numread=0; while(numread<request_len) { do numchars=read(sid, charptr, numtoread); while(numchars == ERROR && errno == EINTR); if(numchars == ERROR) { XeString connection_hostname = CONNECTION_HOSTNAME(connection); if(errno == ECONNRESET) SPC_Error(SPC_Connection_Reset, connection_hostname); else SPC_Error(SPC_Reading, connection_hostname); XeFree(connection_hostname); return(SPC_ERROR); } if(numchars == 0) { XeString connection_hostname = CONNECTION_HOSTNAME(connection); SPC_Error(SPC_Connection_EOF, connection_hostname); XeFree(connection_hostname); return(SPC_ERROR); /* Bad news, EOF on incoming channel */ } charptr += numchars; numread += numchars; numtoread -= numchars; } *charptr=(XeChar)'\0'; return(numread); }
/*----------------------------------------------------------------------+*/ int SPC_ResetTerminator(void) /*----------------------------------------------------------------------+*/ { struct sigaction svect; _DtSvcProcessLock(); if (SPC_who_am_i == SPC_I_AM_A_DAEMON) { svect.sa_handler = SPC_Child_Terminated; sigemptyset(&svect.sa_mask); svect.sa_flags = 0; if(sigaction(SIGCHLD, &svect, (struct sigaction *)NULL)==ERROR) { SPC_Error(SPC_No_Signal_Handler); return(SPC_ERROR); } } _DtSvcProcessUnlock(); return (TRUE); }
/*----------------------------------------------------------------------+*/ SPC_Setup_Synchronous_Terminator(void) /*----------------------------------------------------------------------+*/ { int pipes[2]; _DtSvcProcessLock(); if(write_terminator) { _DtSvcProcessUnlock(); return(TRUE); } if(pipe(pipes)<0) { SPC_Error(SPC_No_Pipe); _DtSvcProcessUnlock(); return(SPC_ERROR); } if((write_terminator=SPC_Alloc_Connection())==SPC_ERROR) { _DtSvcProcessUnlock(); return(SPC_ERROR); } SPC_Add_Connection(write_terminator); if((read_terminator=SPC_Alloc_Connection())==SPC_ERROR) { _DtSvcProcessUnlock(); return(SPC_ERROR); } SPC_Add_Connection(read_terminator); write_terminator->sid=pipes[WRITE_SIDE]; write_terminator->connected=TRUE; read_terminator->sid=pipes[READ_SIDE]; read_terminator->connected=TRUE; SPC_XtAddInput(NULL, &read_terminator->termination_id, read_terminator->sid, SPC_Conditional_Packet_Handler, SPC_Terminator); _DtSvcProcessUnlock(); return(TRUE); }
/*----------------------------------------------------------------------+*/ int master_pty(int fd, struct termios *state) /*----------------------------------------------------------------------+*/ { /* Make any special circumstances required on master side of pty */ int enable = 1; if (fd < 0) return(TRUE); #ifdef __hpux_pty /* Enable trapping of ioctl/open/close (we care about close()) */ if(ioctl(fd, TIOCTRAP, &enable)==ERROR) { SPC_Error(SPC_Bad_Ioctl); return(SPC_ERROR); } #endif /* __hpux_pty */ set_pty_state(fd, state); return(TRUE); }
/*----------------------------------------------------------------------+*/ int exec_proc_local_channel_object(SPC_Channel_Ptr channel) /*----------------------------------------------------------------------+*/ { sigset_t newsigmask, oldsigmask; pid_t pid; int result; XeString *envp; XeString dir = XeString_NULL; int retval; int i, reuse_pid = 0; call_parent_method(channel, exec_proc, (channel), result); if(result==SPC_ERROR) return(SPC_ERROR); /* Check to see if the channel pathname points to a valid executable. We do this by using the _path_search function. If the channel has a PATH variable set in its local environment, use it, otherwise use the "global" environment. We can accomplish this by using the spc_getenv call in the _path_search call. If the channel doesn't have a PATH variable, then spc_getenv will return NULL, which indicates use of the global environment. */ if(!_path_search(SPC_Getenv("PATH", channel->envp), channel->path, executable_predicate)) { SPC_Error(SPC_Cannot_Exec, channel->path); return(SPC_ERROR); } /* If we were passed a host:dir to cd to, make sure it exists. */ /* We want to do this before we fork the child. */ if((channel->context_dir) && (channel->context_dir[0])) { struct stat stat_info; Boolean ok = FALSE; _DtSvcProcessLock(); if (SPC_client_version_number < SPC_PROTOCOL_VERSION_CDE_BASE) dir = get_path_from_context(channel->context_dir); else { /* * context_dir is actually a "netfile" so it needs to * be converted to a "real" path. */ dir = (char *) tt_netfile_file (channel->context_dir); if (tt_ptr_error (dir) != TT_OK) dir = NULL; } _DtSvcProcessUnlock(); if (dir == NULL) /* can't make connection ... */; else if (stat(dir,&stat_info) != 0) /* directory not there */; else if ((stat_info.st_mode & S_IFDIR) == 0) /* path is not a directory ... */; else ok = TRUE; if (!ok && IS_SPCIO_FORCE_CONTEXT(channel->IOMode)) { if (dir != NULL && (strcmp (dir, channel->context_dir) != 0)) SPC_Error(SPC_cannot_Chdir, dir); SPC_Error(SPC_cannot_Chdir, channel->context_dir); XeFree(dir); dir = XeString_NULL; return(SPC_ERROR); } } if(mempf0(channel, pre_fork)==SPC_ERROR) return(SPC_ERROR); /* When using HP NLIO (xj0input) we have a problem. Namely, */ /* the xj0 processs uses signal() to deal with SIGCLD which */ /* is incompatible with sigaction/sigprogmask/etc. Even */ /* though xj0 resets the signal handler, since the signal */ /* routines are incompatible, our original handler gets lost. */ /* Hence, we need to reset it. We do it here everytime we */ /* fork a child just to be on the safe side. */ SPC_ResetTerminator(); sigemptyset(&newsigmask); sigemptyset(&oldsigmask); sigaddset(&newsigmask, SIGCHLD); if (sigprocmask(SIG_BLOCK, &newsigmask, &oldsigmask) == ERROR) return(SPC_ERROR); pid = channel->pid = fork(); /* * Must save this pid so that when the daemon's timer goes off, * if there has been no activity and there are no sub-processes * running, the daemon can exit. */ i = 0; _DtSvcProcessLock(); if (SPC_pid_list == NULL) /* * Create the first block plus the NULL terminator. */ SPC_pid_list = (pid_t *) malloc (2 * sizeof (pid_t)); else { /* * If a dead pid entry exists, reuse it; otherwise, must create * room for the new pid. */ for (i = 0; SPC_pid_list[i] != NULL; i++) if (SPC_pid_list[i] == SPCD_DEAD_PROCESS) { SPC_pid_list[i] = pid; reuse_pid = 1; break; } if (!reuse_pid) SPC_pid_list = (pid_t *) realloc (SPC_pid_list, (i+2) * sizeof (pid_t)); } if (!reuse_pid) { SPC_pid_list[i] = pid; SPC_pid_list[i+1] = NULL; } _DtSvcProcessUnlock(); if (pid) { XeFree(dir); /* Did we really fork? */ if (pid == ERROR) { SPC_Error(SPC_Cannot_Fork); retval = SPC_ERROR; } else { /* Do any set up for the parent process here */ mempf1(channel, post_fork, pid); retval = TRUE; } /* Reinstate the old signal mask (unblock SIGCLD). */ sigprocmask(SIG_SETMASK, &oldsigmask, (sigset_t *)NULL); return(retval); } else { /* Child process: connect wires, make environment and exec sub-process */ sigprocmask(SIG_SETMASK, &oldsigmask, (sigset_t *)NULL); /* Make sure the child is the process group leader. In the case of ptys, we also want to break the current terminal affiliation. We want to be the process group leader so XeSPCKillProcess (which does a kill(-pid, 9)) will kill all processes associated with us. For PTY's, we need to break the terminal affiliation so the next open (which will be a pty) will cause us to become affiliated with the pty. We do this so when the parent process closes the master side of the pty, the slave side processes get SIGHUP. If they ignore SIGHUP, they will never die. So it goes... */ if(IS_SPCIO_PTY(channel->IOMode)) setsid(); else { pid_t tmppid = getpid(); if(setpgid(tmppid, tmppid) == -1) fprintf(stderr, (XeString)"setpgid failed, errno: %d\n", errno); } /* Connect wires to sub-process standard files */ result=mempf1(channel, post_fork, pid); if(result!=SPC_ERROR) { int indx = -1; int i; char **ppch; /* * Before adding in the list of environment variables * from the environment variable files, must search * the list for LANG definitions. If found, the * last definition must be putenv'ed to assure the * multi-byte parsing code is using the correct locale. */ for (i = 0, ppch = channel->envp; *ppch; *ppch++, i++) if (!strncmp (*ppch, "LANG=", 5)) indx = i; if (indx != -1) resolve_variable_reference (&channel->envp[indx]); _DtSvcProcessLock(); if (!setlocale (LC_CTYPE, "")) /* * setlocale failed - log a message but execute * the command anyway. */ if (SPC_Print_Protocol != NULL) (void) fprintf(SPC_Print_Protocol, "+++> Failed to 'setlocale'; LANG = %s\n", getenv("LANG")); /* Mix in the stashed environment */ for(envp=channel->envp; *envp; envp++) resolve_variable_reference(&*envp); if (SPC_mount_point_env_var != NULL) /* * The mount point environment variable was * inherited by the daemon or was given to the * daemon via the command line. In either case * this subprocess must inherit the daemon's * value. */ (void) putenv (SPC_mount_point_env_var); _DtSvcProcessUnlock(); /* Connect to the context directory */ /* We have already validated this guy exists */ if(dir) Xechdir(dir); } XeFree(dir); if(result!=SPC_ERROR) { /* Execute */ /* Compiler barfs without cast ? */ #if defined(__hpux_8_0) || defined(__aix) result=execvp(channel->path, channel->argv); #else result=execvp(channel->path, channel->argv); #endif /* If we return from exec, it failed */ SPC_Error(SPC_Cannot_Exec, channel->path); } /* We want to get rid of this child image (carefully) */ _exit(42); } }
/* This is the right way according to the Spec 1170 */ void SPC_Child_Terminated(int i) /*----------------------------------------------------------------------+*/ { /* This catches signals for sub-process termination */ int type, cause, status; pid_t wait_pid, pid; SPC_Channel_Ptr channel; protocol_request req, *prot; buffered_data data, *pdata; int length; int indx; int saved_errno = errno; prot = (&req); pdata = (&data); prot->dataptr=pdata; wait_pid = -1; while(pid = waitpid(wait_pid, &status, WNOHANG)) { if((pid == -1 && errno == ECHILD) || pid == 0) { /* no more children. Return */ errno = saved_errno; return; } /* Okay, we got the process ID of a terminated child. Find the channel associated with this PID. */ channel=SPC_Find_PID(pid); #ifdef DEBUG fprintf(stderr, (XeString)"got SIGCHLD, pid: %d, channel: %p\n", pid, channel); #endif if(!channel) { continue; } _DtSvcProcessLock(); /* * Look for this process in the pid list. If found, mark it * as done. */ if (SPC_pid_list != NULL) { for (indx=0; SPC_pid_list[indx] != NULL; indx++) if (SPC_pid_list[indx] == pid) { SPC_pid_list[indx] = SPCD_DEAD_PROCESS; break; } } _DtSvcProcessUnlock(); /* We have the channel. Mark it as being closed. */ channel->status = status; /* If we this channel is set up for synchronous termination, write the protocol request to record that this guy died. Otherwise, call the termination handler directly. */ if(IS_SPCIO_SYNC_TERM(channel->IOMode)) { /* This code is basically what SPC_Write_Protocol_Request does. It is replicated here because a call to SPC_W_P_R would have to be re-enterant if we called it here, and SPC_W_P_R is not re-enterant at this time. */ SPC_Reset_Protocol_Ptr(prot, channel, APPLICATION_DIED, 0); pdata->len=WRITE_APPLICATION_DIED(pdata, status); length=WRITE_HEADER(pdata, channel->cid, prot->request_type, pdata->len, 0); pdata->data[length]=(XeChar)' '; length=pdata->len+REQUEST_HEADER_LENGTH; if(write(write_terminator->sid, pdata->data, length)==ERROR) SPC_Error(SPC_Internal_Error); pdata->offset=REQUEST_HEADER_LENGTH; print_protocol_request((XeString) (XeString)" <-- INTERNAL APPLICATION_DIED", prot); } else { SPC_Change_State(channel, NULL, -1, 0); if(channel->Terminate_Handler) { XeSPCGetProcessStatus(channel, &type, &cause); (* channel->Terminate_Handler) (channel, channel->pid, type, cause, channel->Terminate_Data); } } /* Loop around & get another PID */ } errno = saved_errno; }
/*----------------------------------------------------------------------+*/ SPC_MakeSystemCommand(SPC_Channel_Ptr channel) /*----------------------------------------------------------------------+*/ { XeString shell; XeString *argv; XeString *tmp_argv; XeChar newargtwo[_POSIX_ARG_MAX]; int argtwolen=0, tmplen=0; /* Allocate our memory up front */ argv=Alloc_Argv(4); newargtwo[argtwolen]=0; /* copy path into newargtwo */ strncat(newargtwo, channel->path, _POSIX_ARG_MAX-1); strcat(newargtwo, (XeString)" "); argtwolen=strlen(newargtwo); /* copy argv into newargtwo */ for(tmp_argv=channel->argv; tmp_argv && *tmp_argv; tmp_argv++) { tmplen=strlen(*tmp_argv)+1; /* Room for extra space */ if((tmplen+argtwolen)<_POSIX_ARG_MAX-1) { strcat(newargtwo, *tmp_argv); strcat(newargtwo, (XeString)" "); argtwolen += tmplen; } else { XeChar *errbuf; errbuf = malloc(sizeof(XeChar) * 100); if (errbuf) { SPC_Free_Envp(argv); sprintf(errbuf,"(%d chars), max. length is %d",tmplen,_POSIX_ARG_MAX); SPC_Error(SPC_Arg_Too_Long, tmp_argv, _POSIX_ARG_MAX); free(errbuf); } return(SPC_ERROR); } } /* get a shell -- First use the value of $SB_SHELL (if any), then try $SHELL, then use DEFAULT_SHELL */ if(!(shell=getenv((XeString)"SB_SHELL"))) if(!(shell=getenv((XeString)"SHELL"))) shell = DEFAULT_SHELL; /* setup argv properly */ argv[0]=SPC_copy_string(shell); argv[1]=SPC_copy_string((XeString)"-c"); argv[2]=SPC_copy_string(newargtwo); argv[3]=NULL; channel->argv = argv; channel->IOMode |= SPCIO_DEALLOC_ARGV; /* Now set this shell as the path */ channel->path = shell; return(TRUE); }
/*----------------------------------------------------------------------+*/ int read_pty_channel_object(SPC_Channel_Ptr channel, int connector, /* STDOUT or STDERR */ XeString buffer, int nbytes) /*----------------------------------------------------------------------+*/ #ifdef __hpux_pty { int result, select_value; struct fd_set read_mask, except_mask; int fd=channel->file_descs[connector]; struct request_info req_info; struct timeval timeout, *timeptr; int i; call_parent_method(channel, read, (channel, connector, buffer, nbytes), result); if(result==SPC_ERROR) return(SPC_ERROR); if(!IS_SPCIO_DATA(channel->wires[connector]->flags)) return(0); FD_ZERO(&read_mask); FD_ZERO(&except_mask); FD_SET(fd, &read_mask); FD_SET(fd, &except_mask); if(channel->close_timeout) { timeout.tv_sec=channel->close_timeout; timeout.tv_usec=0; timeptr = (&timeout); } else timeptr=NULL; do select_value=select(fd+1, &read_mask, NULL, &except_mask, timeptr); while(select_value==ERROR && errno==EINTR); if(select_value==ERROR) { SPC_Error(SPC_Bad_Select); return(SPC_ERROR); } /* If there is anything to read, read it & return */ IS_FD_SET(&read_mask, result); if(result) { do { result = read(fd, buffer, nbytes); } while (result<0 && errno == EINTR); if(result==ERROR) { SPC_Error(SPC_Reading); return(SPC_ERROR); } return(result); } /* Nothing to read. We either timed out or got an exception. */ if(select_value != 0) { /* We got an exception */ ioctl(fd, TIOCREQGET, &req_info); /* Clear the request (Not really necessary in the case of a close, but do it anyway) */ ioctl(fd, TIOCREQSET, &req_info); } if((select_value == 0) || (req_info.request == TIOCCLOSE)) { /* Close, disable trapping on this fd & return EOF. We regard a timeout as being the same as a close. */ SPC_Disable_Trapping(fd); SPC_Change_State(channel, connector, 0, -1); return(0); } else /* Otherwise (open or IOCTL), return -1 */ return(EXCEPT_FLAG); }
/*----------------------------------------------------------------------+*/ protocol_request_ptr SPC_Read_Protocol(SPC_Connection_Ptr connection) /*----------------------------------------------------------------------+*/ { int channel_id; protocol_request_ptr prot; buffered_data_ptr dptr; int len; if(!connection->connected) return(SPC_ERROR); if((prot=SPC_New_Protocol_Ptr(NULL, 0, 0))==SPC_ERROR) { SPC_Close_Connection(connection); return(SPC_ERROR); } /* read header */ dptr=prot->dataptr; len=SPC_Read_Chars(connection, REQUEST_HEADER_LENGTH, dptr->data); if(len != REQUEST_HEADER_LENGTH) { SPC_Close_Connection(connection); SPC_Free_Protocol_Ptr(prot); return(SPC_ERROR); } /* we have the header. Parse out the fields */ READ_HEADER(dptr, &channel_id, &prot->request_type, &dptr->len, &prot->seqno); prot->channel=SPC_Lookup_Channel(channel_id, connection); /* JET - 11/12/2001 - correct an exploitable buffer overrun where the user */ /* can supply a data len that is larger than the available buffer */ /* MAXREQLEN */ /* CERT - VU#172583 */ if (dptr->len >= MAXREQLEN) { /* we have a problem. Initiate DefCon 1 */ /* and launch our missiles. */ XeString connection_hostname = CONNECTION_HOSTNAME(connection); SPC_Error(SPC_Buffer_Overflow, connection_hostname); XeFree(connection_hostname); SPC_Close_Connection(connection); SPC_Free_Protocol_Ptr(prot); return(SPC_ERROR); } /* read header */ len=SPC_Read_Chars(connection, dptr->len, dptr->data+REQUEST_HEADER_LENGTH); if(len != dptr->len) { SPC_Close_Connection(connection); SPC_Free_Protocol_Ptr(prot); return(SPC_ERROR); } dptr->offset=REQUEST_HEADER_LENGTH; return(prot); }