/* * exec.c: handles exec'd process for IRCII * * Written By Michael Sandrof * * Copyright (c) 1990 Michael Sandrof. * Copyright (c) 1991, 1992 Troy Rollo. * Copyright (c) 1992-2004 Matthew R. Green. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "irc.h" IRCII_RCSID("@(#)$eterna: exec.c,v 1.76 2005/09/21 22:19:20 mrg Exp $"); #ifdef M_UNIX # define __SCO_WAIT3__ # include # include #else /* M_UNIX */ # ifdef HAVE_SYS_WAIT_H # include # endif /* HAVE_SYS_WAIT_H */ #endif /* M_UNIX */ #ifdef ISC #include #include #endif /* ISC */ #ifdef XD88 # define ISC #endif /* XD88 */ #ifdef HAVE_UNISTD_H #include #endif #include "exec.h" #include "vars.h" #include "ircaux.h" #include "edit.h" #include "window.h" #include "screen.h" #include "hook.h" #include "input.h" #include "list.h" #include "server.h" #include "output.h" #include "parse.h" #include "dcc.h" #include "newio.h" #include "alias.h" #if defined(SVR3) && defined(HAVE_SOCKETPAIR) /* SVR3's pipe's are *unidirectional*! We could spend all day pushing STREAMS modules onto two pipes to get bidirectionality, or we can just take the easy way out...like so: */ #define pipe(p) socketpair(AF_UNIX, SOCK_STREAM, 0, (p)) #endif /* SVR3 && HAVE_SOCKETPAIR */ static int wait_index = -1; /* Process: the structure that has all the info needed for each process */ typedef struct { u_char *name; /* full process name */ u_char *logical; pid_t pid; /* process-id of process */ int p_stdin; /* stdin description for process */ int p_stdout; /* stdout descriptor for process */ int p_stderr; /* stderr descriptor for process */ int counter; /* output line counter for process */ u_char *redirect; /* redirection command (MSG, NOTICE) */ unsigned int refnum; /* a window for output to go to */ int server; /* the server to use for output */ u_char *who; /* nickname used for redirection */ int exited; /* true if process has exited */ int termsig; /* The signal that terminated * the process */ int retcode; /* return code of process */ List *waitcmds; /* commands queued by WAIT -CMD */ } Process; static Process **process_list = NULL; static int process_list_size = 0; static int exec_close(int); static void start_process(u_char *, u_char *, u_char *, u_char *, unsigned int); static void kill_process(int, int); static int delete_process(int); static void list_processes(void); static int valid_process_index(int); static void add_process(u_char *, u_char *, int, int, int, int, u_char *, u_char *, unsigned int); static int is_logical_unique(u_char *); static void send_exec_result(Process *, u_char *); /* * A nice array of the possible signals. Used by the coredump trapping * routines and in the exec.c package . * * We really rely on the signals being all upper case. */ #include "sig.inc" /* * exec_close: silly, eh? Well, it makes the code look nicer. Or does it * really? No. But what the hell */ static int exec_close(des) int des; { if (des != -1) new_close(des); return (-1); } /* * set_wait_process: Sets the given index number so that it will be checked * for upon process exit. This is used by waitcmd() in edit.c. An index of * -1 disables this. */ void set_wait_process(proccess) int proccess; { wait_index = proccess; } /* * valid_process_index: checks to see if index refers to a valid running * process and returns true if this is the case. Returns false otherwise */ static int valid_process_index(proccess) int proccess; { if ((proccess < 0) || (proccess >= process_list_size)) return (0); if (process_list[proccess]) return (1); return (0); } #if !defined(BSDWAIT) && defined(NEED_WAITPID) #ifndef WNOHANG # define WNOHANG 1 #endif /* WNOHANG */ #ifndef SIGCHLD # define SIGCHLD SIGCLD #endif /* SIGCHLD */ volatile static int _child_died = 0; static void _child_death() { _child_died = 1; } int waitpid(pid, status, options) int pid; /* Only works if pid == -1! */ int *status; int options; { int rval; void (*prev_sigcld)(); if (options & WNOHANG) { _child_died = 0; prev_sigcld = signal(SIGCHLD, _child_death); if (_child_died == 0) { signal(SIGCHLD, prev_sigcld); return (0); } } rval = wait(status); if (options & WNOHANG) { signal(SIGCHLD, prev_sigcld); } return rval; } #endif /* not BSDWAIT && NEED_WAITPID */ int get_child_exit(pid) int pid; { return (check_wait_status(pid)); } /* * check_wait_status: This waits on child processes, reporting terminations * as needed, etc */ int check_wait_status(wanted) int wanted; { Process *proc; #ifdef BSDWAIT union wait status; #else int status; #endif /* BSDWAIT */ int pid, i; #if defined(use_wait2) || defined(MUNIX) if ((pid = wait2(&status, WNOHANG, 0)) > 0) #else # ifdef BSDWAIT if ((pid = wait3(&status, WNOHANG, NULL)) > 0) # else if ((pid = waitpid(wanted, &status, WNOHANG)) > 0) # endif /* BSDWAIT */ #endif /* defined(use_wait2) || defined(MUNIX) */ { if (wanted != -1 && pid == wanted) { if (WIFEXITED(status)) return WEXITSTATUS(status); if (WIFSTOPPED(status)) return - (WSTOPSIG(status)); if (WIFSIGNALED(status)) return - (WTERMSIG(status)); } errno = 0; /* reset errno, cause wait3 changes it */ for (i = 0; i < process_list_size; i++) { if ((proc = process_list[i]) && proc->pid == pid) { proc->exited = 1; proc->termsig = WTERMSIG(status); proc->retcode = WEXITSTATUS(status); if ((proc->p_stderr == -1) && (proc->p_stdout == -1)) delete_process(i); return 0; } } } return -1; } /* * check_process_limits: checks each running process to see if it's reached * the user selected maximum number of output lines. If so, the processes is * effectively killed */ void check_process_limits() { int limit; int i; Process *proc; if ((limit = get_int_var(SHELL_LIMIT_VAR)) && process_list) { for (i = 0; i < process_list_size; i++) { if ((proc = process_list[i]) != NULL) { if (proc->counter >= limit) { proc->p_stdin = exec_close(proc->p_stdin); proc->p_stdout = exec_close(proc->p_stdout); proc->p_stderr = exec_close(proc->p_stderr); if (proc->exited) delete_process(i); } } } } } static void send_exec_result(proc, exec_buffer) Process *proc; u_char *exec_buffer; { if (proc->redirect) { if (!my_strcmp(proc->redirect, "FILTER")) { u_char *rest, *filter; int arg_flag = 0; rest = exec_buffer; while (*rest && *rest != ' ') ++rest; while (*rest == ' ') ++rest; filter = call_function(proc->who, exec_buffer /* f_args */, empty_string /* args */, &arg_flag); if (filter) { u_char *sub_buffer = NULL; malloc_strcpy(&sub_buffer, filter); malloc_strcat(&sub_buffer, UP(" ")); malloc_strcat(&sub_buffer, rest); parse_command(sub_buffer, 0, empty_string); new_free(&sub_buffer); new_free(&filter); } } else { send_text(proc->who, exec_buffer, proc->redirect); } } else put_it("%s", exec_buffer); } /* * do_processes: given a set of read-descriptors (as returned by a call to * select()), this will determine which of the process has produced output * and will hadle it appropriately */ void do_processes(rd) fd_set *rd; { int i, flag; u_char exec_buffer[INPUT_BUFFER_SIZE]; u_char *ptr; Process *proc; int old_timeout; int server; if (process_list == (Process **) 0) return; old_timeout = dgets_timeout(1); for (i = 0; i < process_list_size && !break_io_processing; i++) { if ((proc = process_list[i]) && proc->p_stdout != -1) { if (FD_ISSET(proc->p_stdout, rd)) { switch (dgets(exec_buffer, sizeof exec_buffer, proc->p_stdout, (u_char *) 0)) { case 0: if (proc->p_stderr == -1) { proc->p_stdin = exec_close(proc->p_stdin); proc->p_stdout = exec_close(proc->p_stdout); if (proc->exited) delete_process(i); } else proc->p_stdout = exec_close(proc->p_stdout); break; case -1: server = from_server; from_server = proc->server; if (proc->logical) flag = do_hook(EXEC_PROMPT_LIST, "%s %s", proc->logical, exec_buffer); else flag = do_hook(EXEC_PROMPT_LIST, "%d %s", i, exec_buffer); from_server = server; set_prompt_by_refnum(proc->refnum, exec_buffer); update_input(UPDATE_ALL); /* if (flag == 0) */ break; default: server = from_server; from_server = proc->server; message_to(proc->refnum); proc->counter++; exec_buffer[sizeof(exec_buffer) - 1] = '\0'; /* blah... */ ptr = exec_buffer + my_strlen(exec_buffer) - 1; if ((*ptr == '\n') || (*ptr == '\r')) { *ptr = (u_char) 0; ptr = exec_buffer + my_strlen(exec_buffer) - 1; if ((*ptr == '\n') || (*ptr == '\r')) *ptr = (u_char) 0; } if (proc->logical) flag = do_hook(EXEC_LIST, "%s %s", proc->logical, exec_buffer); else flag = do_hook(EXEC_LIST, "%d %s", i, exec_buffer); if (flag) send_exec_result(proc, exec_buffer); message_to(0); from_server = server; break; } } } if (process_list && i < process_list_size && (proc = process_list[i]) && proc->p_stderr != -1) { if (FD_ISSET(proc->p_stderr, rd)) { switch (dgets(exec_buffer, sizeof exec_buffer, proc->p_stderr, (u_char *) 0)) { case 0: if (proc->p_stdout == -1) { proc->p_stderr = exec_close(proc->p_stderr); proc->p_stdin = exec_close(proc->p_stdin); if (proc->exited) delete_process(i); } else proc->p_stderr = exec_close(proc->p_stderr); break; case -1: server = from_server; from_server = proc->server; if (proc->logical) flag = do_hook(EXEC_PROMPT_LIST, "%s %s", proc->logical, exec_buffer); else flag = do_hook(EXEC_PROMPT_LIST, "%d %s", i, exec_buffer); set_prompt_by_refnum(proc->refnum, exec_buffer); update_input(UPDATE_ALL); from_server = server; if (flag == 0) break; default: server = from_server; from_server = proc->server; message_to(proc->refnum); (proc->counter)++; ptr = exec_buffer + my_strlen(exec_buffer) - 1; if ((*ptr == '\n') || (*ptr == '\r')) { *ptr = (u_char) 0; ptr = exec_buffer + my_strlen(exec_buffer) - 1; if ((*ptr == '\n') || (*ptr == '\r')) *ptr = (u_char) 0; } if (proc->logical) flag = do_hook(EXEC_ERRORS_LIST, "%s %s", proc->logical, exec_buffer); else flag = do_hook(EXEC_ERRORS_LIST, "%d %s", i, exec_buffer); if (flag) send_exec_result(proc, exec_buffer); message_to(0); from_server = server; break; } } } } (void) dgets_timeout(old_timeout); } /* * set_process_bits: This will set the bits in a fd_set map for each of the * process descriptors. */ void set_process_bits(rd) fd_set *rd; { int i; Process *proc; if (process_list) { for (i = 0; i < process_list_size; i++) { if ((proc = process_list[i]) != NULL) { if (proc->p_stdout != -1) FD_SET(proc->p_stdout, rd); if (proc->p_stderr != -1) FD_SET(proc->p_stderr, rd); } } } } /* * list_processes: displays a list of all currently running processes, * including index number, pid, and process name */ static void list_processes() { Process *proc; int i; int lastlog_level; lastlog_level = set_lastlog_msg_level(LOG_CRAP); if (process_list) { say("Process List:"); for (i = 0; i < process_list_size; i++) { if ((proc = process_list[i]) != NULL) { if (proc->logical) say("\t%d (%s): %s", i, proc->logical, proc->name); else say("\t%d: %s", i, proc->name); } } } else say("No processes are running"); set_lastlog_msg_level(lastlog_level); } void add_process_wait(proc_index, cmd) int proc_index; u_char *cmd; { List *new, **posn; for (posn = &process_list[proc_index]->waitcmds; *posn != (List *) 0; posn = &(*posn)->next) ; new = (List *) new_malloc(sizeof(List)); *posn = new; new->next = (List *) 0; new->name = (u_char *) 0; malloc_strcpy(&new->name, cmd); } /* * delete_process: Removes the process specifed by index from the process * list. The does not kill the process, close the descriptors, or any such * thing. It only deletes it from the list. If appropriate, this will also * shrink the list as needed */ static int delete_process(process) int process; { int flag; List *cmd, *next; if (process_list) { if (process >= process_list_size) return (-1); if (process_list[process]) { Process *dead; if (process == wait_index) { wait_index = -1; irc_io_loop = 0; } dead = process_list[process]; process_list[process] = (Process *) 0; if (process == (process_list_size - 1)) { int i; for (i = process_list_size - 1; process_list_size; process_list_size--, i--) { if (process_list[i]) break; } if (process_list_size) process_list = (Process **) new_realloc((u_char *) process_list, sizeof(Process *) * process_list_size); else { new_free(&process_list); process_list = (Process **) 0; } } for (next = dead->waitcmds; next;) { cmd = next; next = next->next; parse_command(cmd->name, 0, empty_string); new_free(&cmd->name); new_free(&cmd); } dead->waitcmds = (List *) 0; if (dead->logical) flag = do_hook(EXEC_EXIT_LIST, "%s %d %d", dead->logical, dead->termsig, dead->retcode); else flag = do_hook(EXEC_EXIT_LIST, "%d %d %d", process, dead->termsig, dead->retcode); if (flag) { if (get_int_var(NOTIFY_ON_TERMINATION_VAR)) { if (dead->termsig) say("Process %d (%s) terminated with signal %s (%d)", process, dead->name, signals[dead->termsig], dead->termsig); else say("Process %d (%s) terminated with return code %d", process, dead->name, dead->retcode); } } new_free(&dead->name); new_free(&dead->logical); new_free(&dead->who); new_free(&dead->redirect); new_free(&dead); return (0); } } return (-1); } /* * add_process: adds a new process to the process list, expanding the list as * needed. It will first try to add the process to a currently unused spot * in the process table before it increases its size. */ static void add_process(name, logical, pid, p_stdin, p_stdout, p_stderr, redirect, who, refnum) u_char *name, *logical; int pid, p_stdin, p_stdout, p_stderr; u_char *redirect; u_char *who; unsigned int refnum; { int i; Process *proc; if (process_list == (Process **) 0) { process_list = (Process **) new_malloc(sizeof(Process *)); process_list_size = 1; process_list[0] = (Process *) 0; } for (i = 0; i < process_list_size; i++) { if (!process_list[i]) { proc = process_list[i] = (Process *) new_malloc(sizeof(Process)); proc->name = (u_char *) 0; malloc_strcpy(&(proc->name), name); proc->logical = (u_char *) 0; malloc_strcpy(&(proc->logical), logical); proc->pid = pid; proc->p_stdin = p_stdin; proc->p_stdout = p_stdout; proc->p_stderr = p_stderr; proc->refnum = refnum; proc->redirect = (u_char *) 0; if (redirect) malloc_strcpy(&(proc->redirect), redirect); if (parsing_server_index != -1) proc->server = parsing_server_index; else proc->server = curr_scr_win->server; proc->counter = 0; proc->exited = 0; proc->termsig = 0; proc->retcode = 0; proc->who = (u_char *) 0; proc->waitcmds = (List *) 0; if (who) malloc_strcpy(&(process_list[i]->who), who); return; } } process_list_size++; process_list = (Process **) new_realloc(UP(process_list), sizeof(Process *) * process_list_size); process_list[process_list_size - 1] = (Process *) 0; proc = process_list[i] = (Process *) new_malloc(sizeof(Process)); proc->name = (u_char *) 0; malloc_strcpy(&(proc->name), name); proc->logical = (u_char *) 0; malloc_strcpy(&(proc->logical), logical); proc->pid = pid; proc->p_stdin = p_stdin; proc->p_stdout = p_stdout; proc->p_stderr = p_stderr; proc->refnum = refnum; proc->redirect = (u_char *) 0; if (redirect) malloc_strcpy(&(proc->redirect), redirect); proc->server = curr_scr_win->server; proc->counter = 0; proc->exited = 0; proc->termsig = 0; proc->retcode = 0; proc->who = (u_char *) 0; proc->waitcmds = (List *) 0; if (who) malloc_strcpy(&(proc->who), who); } /* * kill_process: sends the given signal to the process specified by the given * index into the process table. After the signal is sent, the process is * deleted from the process table */ static void kill_process(kill_index, sig) int kill_index, sig; { if (process_list && (kill_index < process_list_size) && process_list[kill_index]) { pid_t pgid; say("Sending signal %s (%d) to process %d: %s", signals[sig], sig, kill_index, process_list[kill_index]->name); #ifdef HAVE_GETPGID pgid = getpgid(process_list[kill_index]->pid); #else # ifdef __sgi pgid = BSDgetpgrp(process_list[kill_index]->pid); # else # ifdef HPUX pgid = getpgrp2(process_list[kill_index]->pid); # else # ifdef mips /* XXX */ pgid = getpgrp(process_list[kill_index]->pid); # else # ifdef HAVE_GETSID pgid = getsid(process_list[kill_index]->pid); # else # if defined(ISC) || defined(MUNIX) || defined(BROKEN_GETPGRP) pgid = process_list[kill_index]->pid; # else pgid = getpgrp(process_list[kill_index]->pid); # endif /* ISC || MUNIX || BROKEN_GETPGRP */ # endif /* HAVE_GETSID */ # endif /* mips */ # endif /* HPUX */ # endif /* __sgi */ #endif /* HAVE_GETPGID */ #if !defined(HAVE_KILLPG) && !defined(killpg) # define killpg(pg, sig) kill(-(pg), (sig)) #endif /* HAVE_KILLPG */ if (pgid == getpid()) { yell("--- kill_process got pgid of pid!!! something is very wrong"); return; } killpg(pgid, sig); kill(process_list[kill_index]->pid, sig); } else say("There is no process %d", kill_index); } static int is_logical_unique(logical) u_char *logical; { Process *proc; int i; if (logical) for (i = 0; i < process_list_size; i++) if ((proc = process_list[i]) && proc->logical && (my_stricmp(proc->logical, logical) == 0)) return 0; return 1; } /* * start_process: Attempts to start the given process using the SHELL as set * by the user. */ static void start_process(name, logical, redirect, who, refnum) u_char *name, *logical, *who, *redirect; unsigned int refnum; { int p0[2], p1[2], p2[2], pid, cnt; u_char *shell, *flag, *arg; u_char buffer[BIG_BUFFER_SIZE]; #ifdef DAEMON_UID if (getuid() == DAEMON_UID) { say("Sorry, you are not allowed to use EXEC"); return; } #endif /* DAEMON_UID */ p0[0] = p0[1] = -1; p1[0] = p1[1] = -1; p2[0] = p2[1] = -1; if (pipe(p0) || pipe(p1) || pipe(p2)) { say("Unable to start new process: %s", strerror(errno)); if (p0[0] != -1) { new_close(p0[0]); new_close(p0[1]); } if (p1[0] != -1) { new_close(p1[0]); new_close(p1[1]); } if (p2[0] != -1) { new_close(p2[0]); new_close(p2[1]); } return; } switch (pid = fork()) { case -1: say("Couldn't start new process!"); break; case 0: #ifdef HAVE_SETSID setsid(); #else setpgrp(0, getpid()); #endif /* HAVE_SETSID */ MY_SIGNAL(SIGINT, (sigfunc *) SIG_IGN, 0); dup2(p0[0], 0); dup2(p1[1], 1); dup2(p2[1], 2); new_close(p0[0]); new_close(p0[1]); new_close(p1[0]); new_close(p1[1]); new_close(p2[0]); new_close(p2[1]); close_all_server(); close_all_dcc(); close_all_exec(); close_all_screen(); /* fix environment */ for (cnt = 0, arg = UP(environ[0]); arg; arg = UP(environ[++cnt])) { if (my_strncmp(arg, "TERM=", 5) == 0) { environ[cnt] = "TERM=tty"; break; } } if ((shell = get_string_var(SHELL_VAR)) == (u_char *) 0) { u_char **args; int max; cnt = 0; max = 5; args = (u_char **) new_malloc(sizeof(u_char *) * max); while ((arg = next_arg(name, &name)) != NULL) { if (cnt == max) { max += 5; args = (u_char **) new_realloc((u_char *) args, sizeof(u_char *) * max); } args[cnt++] = arg; } args[cnt] = (u_char *) 0; setuid(getuid()); /* If we are setuid, set it back! */ setgid(getgid()); execvp((char *) args[0], (char **) args); } else { if ((flag = get_string_var(SHELL_FLAGS_VAR)) == (u_char *) 0) flag = empty_string; setuid(getuid()); setgid(getgid()); execl(CP(shell), CP(shell), flag, name, (u_char *) 0); } snprintf(CP(buffer), sizeof buffer, "*** Error starting shell \"%s\": %s\n", shell, strerror(errno)); write(1, buffer, my_strlen(buffer)); _exit(-1); break; default: new_close(p0[0]); new_close(p1[1]); new_close(p2[1]); add_process(name, logical, pid, p0[1], p1[0], p2[0], redirect, who, refnum); break; } } /* * text_to_process: sends the given text to the given process. If the given * process index is not valid, an error is reported and 1 is returned. * Otherwise 0 is returned. * Added show, to remove some bad recursion, phone, april 1993 */ int text_to_process(proc_index, text, show) int proc_index; u_char *text; int show; { u_int ref; Process *proc; if (valid_process_index(proc_index) == 0) { say("No such process number %d", proc_index); return (1); } ref = process_list[proc_index]->refnum; proc = process_list[proc_index]; message_to(ref); if (show) put_it("%s%s", get_prompt_by_refnum(ref), text); /* lynx */ write(proc->p_stdin, text, my_strlen(text)); write(proc->p_stdin, "\n", 1); set_prompt_by_refnum(ref, empty_string); /* update_input(UPDATE_ALL); */ message_to(0); return (0); } /* * is_process_running: Given an index, this returns true if the index referes * to a currently running process, 0 otherwise */ int is_process_running(proc_index) int proc_index; { if (proc_index < 0 || proc_index >= process_list_size) return (0); if (process_list && process_list[proc_index]) return (!process_list[proc_index]->exited); return (0); } /* * lofical_to_index: converts a logical process name to its approriate index * in the process list, or -1 if not found */ int logical_to_index(logical) u_char *logical; { Process *proc; int i; for (i = 0; i < process_list_size; i++) { if ((proc = process_list[i]) && proc->logical && (my_stricmp(proc->logical, logical) == 0)) return i; } return -1; } /* * get_process_index: parses out a process index or logical name from the * given string */ int get_process_index(args) u_char **args; { u_char *s; if ((s = next_arg(*args, args)) != NULL) { if (*s == '%') s++; else return (-1); if (is_number(s)) return (my_atoi(s)); else return (logical_to_index(s)); } else return (-1); } /* is_process: checks to see if arg is a valid process specification */ int is_process(arg) u_char *arg; { if (arg && *arg == '%') { arg++; if (is_number(arg) || (logical_to_index(arg) != -1)) return (1); } return (0); } /* * exec: the /EXEC command. Handles the whole IRCII process crap. */ /*ARGSUSED*/ void execcmd(command, args, subargs) u_char *command, *args, *subargs; { u_char *who = (u_char *) 0, *logical = (u_char *) 0, *redirect, /* = (u_char *) 0, */ *flag, *cmd = (u_char *) 0; unsigned int refnum = 0; int sig, i, refnum_flag = 0, logical_flag = 0; size_t len; Process *proc; if (get_int_var(EXEC_PROTECTION_VAR) && (send_text_flag != -1)) { say("Attempt to use EXEC from within an ON function!"); say("Command \"%s\" not executed!", args); say("Please read /HELP SET EXEC_PROTECTION"); say("or important details about this!"); return; } #ifdef DAEMON_UID if (getuid() == DAEMON_UID) { say("You are not permitted to use EXEC."); return; } #endif /* DAEMON_UID */ if (*args == '\0') { list_processes(); return; } redirect = NULL; while ((*args == '-') && (flag = next_arg(args, &args))) { if (*flag == '-') { len = my_strlen(++flag); malloc_strcpy(&cmd, flag); upper(cmd); if (my_strncmp(cmd, "OUT", len) == 0) { redirect = empty_string; /* UP("PRIVMSG"); */ if (!(who = get_channel_by_refnum(0))) { say("No current channel in this window for -OUT"); new_free(&cmd); return; } } else if (my_strncmp(cmd, "TARGET", len) == 0) { redirect = UP("PRIVMSG"); if (!(who = get_target_by_refnum(0))) { say("No current target in this window for -TARGET"); new_free(&cmd); return; } } else if (my_strncmp(cmd, "NAME", len) == 0) { logical_flag = 1; if ((logical = next_arg(args, &args)) == (u_char *) 0) { say("You must specify a logical name"); new_free(&cmd); return; } } else if (my_strncmp(cmd, "WINDOW", len) == 0) { refnum_flag = 1; refnum = current_refnum(); } else if (my_strncmp(cmd, "MSG", len) == 0) { if (doing_privmsg) redirect = UP("NOTICE"); else redirect = UP("PRIVMSG"); if ((who = next_arg(args, &args)) == (u_char *) 0) { say("No nicknames specified"); new_free(&cmd); return; } } else if (my_strncmp(cmd, "FILTER", len) == 0) { redirect = UP("FILTER"); if ((who = next_arg(args, &args)) == (u_char *) 0) { say("No filter function specified"); new_free(&cmd); return; } } else if (my_strncmp(cmd, "CLOSE", len) == 0) { if ((i = get_process_index(&args)) == -1) { say("Missing process number or logical name."); new_free(&cmd); return; } if (is_process_running(i)) { proc = process_list[i]; proc->p_stdin = exec_close(proc->p_stdin); proc->p_stdout = exec_close(proc->p_stdout); proc->p_stderr = exec_close(proc->p_stderr); } else say("No such process running!"); new_free(&cmd); return; } else if (my_strncmp(cmd, "NOTICE", len) == 0) { redirect = UP("NOTICE"); if ((who = next_arg(args, &args)) == (u_char *) 0) { say("No nicknames specified"); new_free(&cmd); return; } } else if (my_strncmp(cmd, "IN", len) == 0) { if ((i = get_process_index(&args)) == -1) { say("Missing process number or logical name."); new_free(&cmd); return; } text_to_process(i, args, 1); new_free(&cmd); return; } else { u_char *cmd2 = (u_char *) 0; if ((i = get_process_index(&args)) == -1) { say("Invalid process specification"); goto out; } if ((sig = my_atoi(flag)) > 0) { if ((sig > 0) && (sig < max_signo)) kill_process(i, sig); else say("Signal number can be from 1 to %d", max_signo); goto out; } malloc_strcpy(&cmd2, flag); upper(cmd2); for (sig = 1; sig < max_signo && signals[sig]; sig++) { if (!my_strncmp(signals[sig], flag, len)) { kill_process(i, sig); goto out2; } } say("No such signal: %s", flag); out2: new_free(&cmd2); out: new_free(&cmd); return; } new_free(&cmd); } else break; } if (is_process(args)) { if ((i = get_process_index(&args)) == -1) { say("Invalid process specification"); return; } if (valid_process_index(i)) { proc = process_list[i]; message_to(refnum); if (refnum_flag) { proc->refnum = refnum; if (refnum) say("Output from process %d (%s) now going to this window", i, proc->name); else say("Output from process %d (%s) not going to any window", i, proc->name); } malloc_strcpy(&(proc->redirect), redirect); malloc_strcpy(&(proc->who), who); if (redirect) { say("Output from process %d (%s) now going to %s", i, proc->name, who); } else say("Output from process %d (%s) now going to you", i, proc->name); if (logical_flag) { if (logical) { if (is_logical_unique(logical)) { malloc_strcpy(&( proc->logical), logical); say("Process %d (%s) is now called %s", i, proc->name, proc->logical); } else say("The name %s is not unique!" , logical); } else say("The name for process %d (%s) has been removed", i, proc->name); } message_to(0); } else say("Invalid process specification"); } else { if (is_logical_unique(logical)) start_process(args, logical, redirect, who, refnum); else say("The name %s is not unique!", logical); } } /* * clean_up_processes: kills all processes in the procss list by first * sending a SIGTERM, then a SIGKILL to clean things up */ void clean_up_processes() { int i; if (process_list_size) { say("Cleaning up left over processes...."); for (i = 0; i < process_list_size; i++) { if (process_list[i]) kill_process(i, SIGTERM); } sleep(2); /* Give them a chance for a graceful exit */ for (i = 0; i < process_list_size; i++) { if (process_list[i]) kill_process(i, SIGKILL); } } } /* * close_all_exec: called when we fork of a wserv process for interaction * with screen/X, to close all unnessicary fd's that might cause problems * later. */ void close_all_exec() { int i; int tmp; tmp = window_display; window_display = 0; for (i = 0; i < process_list_size; i++) if (process_list[i]) { if (process_list[i]->p_stdin) new_close(process_list[i]->p_stdin); if (process_list[i]->p_stdout) new_close(process_list[i]->p_stdout); if (process_list[i]->p_stderr) new_close(process_list[i]->p_stderr); delete_process(i); kill_process(i, SIGKILL); } window_display = tmp; } void exec_server_delete(i) int i; { int j; for (j = 0; j < process_list_size; j++) if (process_list[j] && process_list[j]->server >= i) process_list[j]->server--; }