1 /*
2  * exec.c: handles exec'd process for IRCII
3  *
4  * Written By Michael Sandrof
5  *
6  * Copyright (c) 1990 Michael Sandrof.
7  * Copyright (c) 1991, 1992 Troy Rollo.
8  * Copyright (c) 1992-2003 Matthew R. Green.
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. The name of the author may not be used to endorse or promote products
20  *    derived from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
23  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25  * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  *
34  * $Id: exec.c,v 1.14 2006-04-30 14:15:43 f Exp $
35  */
36 
37 #include "irc.h"
38 
39 #ifdef M_UNIX
40 # define __SCO_WAIT3__
41 # include <sys/wait.h>
42 # include <sys/resource.h>
43 #else /* M_UNIX */
44 # ifdef HAVE_SYS_WAIT_H
45 #  include <sys/wait.h>
46 # endif
47 #endif /* M_UNIX */
48 
49 #ifdef ISC
50 #include <sys/bsdtypes.h>
51 #include <wait.h>
52 #endif /* ISC */
53 
54 #ifdef XD88
55 # define ISC
56 #endif /* XD88 */
57 
58 #ifdef HAVE_UNISTD_H
59 #include <unistd.h>
60 #endif
61 
62 #include "exec.h"
63 #include "vars.h"
64 #include "ircaux.h"
65 #include "edit.h"
66 #include "window.h"
67 #include "screen.h"
68 #include "hook.h"
69 #include "input.h"
70 #include "list.h"
71 #include "server.h"
72 #include "output.h"
73 #include "parse.h"
74 #include "dcc.h"
75 #include "newio.h"
76 #include "alias.h"
77 
78 #if defined(SVR3) && defined(HAVE_SOCKETPAIR)
79 /* SVR3's pipe's are *unidirectional*!  We could spend all day pushing
80    STREAMS modules onto two pipes to get bidirectionality, or we can just
81    take the easy way out...like so:
82 */
83 #define pipe(p) socketpair(AF_UNIX, SOCK_STREAM, 0, (p))
84 #endif
85 
86 #if !defined(NSIG) && defined(_SIGMAX)
87 /* mmm, posix */
88 # define NSIG _SIGMAX
89 #endif
90 
91 static	int	wait_index = -1;
92 
93 /* Process: the structure that has all the info needed for each process */
94 typedef struct
95 {
96 	char	*name;			/* full process name */
97 	char	*logical;
98 	pid_t	pid;			/* process-id of process */
99 	int	p_stdin;		/* stdin description for process */
100 	int	p_stdout;		/* stdout descriptor for process */
101 	int	p_stderr;		/* stderr descriptor for process */
102 	int	counter;		/* output line counter for process */
103 	char	*redirect;		/* redirection command (MSG, NOTICE) */
104 	unsigned int	refnum;		/* a window for output to go to */
105 	int	server;			/* the server to use for output */
106 	char	*who;			/* nickname used for redirection */
107 	int	exited;			/* true if process has exited */
108 	int	termsig;		/* The signal that terminated
109 					 * the process */
110 	int	retcode;		/* return code of process */
111 	List	*waitcmds;		/* commands queued by WAIT -CMD */
112 }	Process;
113 
114 static	Process **process_list = NULL;
115 static	int	process_list_size = 0;
116 
117 static	int	exec_close _((int));
118 static	void	start_process _((char *, char *, char *, char *, unsigned int));
119 static	void	kill_process _((int, int));
120 static	int	delete_process _((int));
121 static	void	list_processes _((void));
122 static	int	valid_process_index _((int));
123 static	void	add_process _((char *, char *, int, int, int, int, char *, char *, unsigned int));
124 static	int	is_logical_unique _((char *));
125 static	void	send_exec_result _((Process *, char *));
126 
127 /*
128  * A nice array of the possible signals.  Used by the coredump trapping
129  * routines and in the exec.c package .
130  *
131  * We really rely on the signals being all upper case.
132  */
133 
134 #include "sig.inc"
135 
136 /*
137  * exec_close: silly, eh?  Well, it makes the code look nicer.  Or does it
138  * really?  No.  But what the hell
139  */
140 static	int
exec_close(des)141 exec_close(des)
142 	int	des;
143 {
144 	if (des != -1)
145 		new_close(des);
146 	return (-1);
147 }
148 
149 /*
150  * set_wait_process: Sets the given index number so that it will be checked
151  * for upon process exit.  This is used by waitcmd() in edit.c.  An index of
152  * -1 disables this.
153  */
154 void
set_wait_process(proccess)155 set_wait_process(proccess)
156 	int	proccess;
157 {
158 	wait_index = proccess;
159 }
160 
161 /*
162  * valid_process_index: checks to see if index refers to a valid running
163  * process and returns true if this is the case.  Returns false otherwise
164  */
165 static	int
valid_process_index(proccess)166 valid_process_index(proccess)
167 	int	proccess;
168 {
169 	if ((proccess < 0) || (proccess >= process_list_size))
170 		return (0);
171 	if (process_list[proccess])
172 		return (1);
173 	return (0);
174 }
175 
176 #if !defined(BSDWAIT) && defined(NEED_WAITPID)
177 
178 #ifndef WNOHANG
179 # define WNOHANG 1
180 #endif
181 
182 #ifndef SIGCLD
183 # define SIGCLD SIGCHLD
184 #endif
185 
186 volatile static int _child_died = 0;
187 
188 static	void
_child_death()189 _child_death()
190 {
191 	_child_died = 1;
192 }
193 
194 int
waitpid(pid,status,options)195 waitpid(pid, status, options)
196 	int	pid;	/* Only works if pid == -1! */
197 	int	*status;
198 	int	options;
199 {
200 	int rval;
201 	void (*prev_sigcld)();
202 
203 	if (options & WNOHANG)
204 	{
205 		_child_died = 0;
206 		prev_sigcld = signal(SIGCLD, _child_death);
207 		if (_child_died == 0)
208 		{
209 			signal(SIGCLD, prev_sigcld);
210 			return (0);
211 		}
212 	}
213 	rval = wait(status);
214 	if (options & WNOHANG)
215 	{
216 		signal(SIGCLD, prev_sigcld);
217 	}
218 	return rval;
219 }
220 #endif /* not BSDWAIT && NEED_WAITPID */
221 
222 int
get_child_exit(pid)223 get_child_exit(pid)
224 	int	pid;
225 {
226 	return (check_wait_status(pid));
227 }
228 
229 /*
230  * check_wait_status: This waits on child processes, reporting terminations
231  * as needed, etc
232  */
233 int
check_wait_status(wanted)234 check_wait_status(wanted)
235 	int wanted;
236 {
237 	Process	*proc;
238 #ifdef BSDWAIT
239 	union wait status;
240 #else
241 	int	status;
242 #endif /* BSDWAIT */
243 	int	pid,
244 		i;
245 
246 #if defined(use_wait2) || defined(MUNIX)
247 	if ((pid = wait2(&status, WNOHANG, 0)) > 0)
248 #else
249 # ifdef BSDWAIT
250 	if ((pid = wait3(&status, WNOHANG, NULL)) > 0)
251 # else
252 	if ((pid = waitpid(wanted, &status, WNOHANG)) > 0)
253 # endif /* BSDWAIT */
254 #endif /* defined(use_wait2) || defined(MUNIX) */
255 	{
256 		if (wanted != -1 && pid == wanted)
257 		{
258 			if (WIFEXITED(status))
259 				return WEXITSTATUS(status);
260 			if (WIFSTOPPED(status))
261 				return - (WSTOPSIG(status));
262 			if (WIFSIGNALED(status))
263 				return - (WTERMSIG(status));
264 		}
265 		errno = 0;	/* reset errno, cause wait3 changes it */
266 		for (i = 0; i < process_list_size; i++)
267 		{
268 			if ((proc = process_list[i]) && proc->pid == pid)
269 			{
270 				proc->exited = 1;
271 				proc->termsig = WTERMSIG(status);
272 				proc->retcode = WEXITSTATUS(status);
273 				if ((proc->p_stderr == -1) &&
274 				    (proc->p_stdout == -1))
275 					delete_process(i);
276 				return 0;
277 			}
278 		}
279 	}
280 	return -1;
281 }
282 
283 /*
284  * check_process_limits: checks each running process to see if it's reached
285  * the user selected maximum number of output lines.  If so, the processes is
286  * effectively killed
287  */
288 void
check_process_limits()289 check_process_limits()
290 {
291 	int	limit;
292 	int	i;
293 	Process	*proc;
294 
295 	if ((limit = get_int_var(SHELL_LIMIT_VAR)) && process_list)
296 	{
297 		for (i = 0; i < process_list_size; i++)
298 		{
299 			if ((proc = process_list[i]) != NULL)
300 			{
301 				if (proc->counter >= limit)
302 				{
303 					proc->p_stdin = exec_close(proc->p_stdin);
304 					proc->p_stdout = exec_close(proc->p_stdout);
305 					proc->p_stderr = exec_close(proc->p_stderr);
306 					if (proc->exited)
307 						delete_process(i);
308 				}
309 			}
310 		}
311 	}
312 }
313 
314 static void
send_exec_result(proc,exec_buffer)315 send_exec_result(proc, exec_buffer)
316 	Process *proc;
317 	char *exec_buffer;
318 {
319 	if (proc->redirect)
320 	{
321 		if (!strcmp(proc->redirect, "FILTER"))
322 		{
323 			char *rest, *filter;
324 			int arg_flag = 0;
325 
326 			rest = exec_buffer;
327 			while (*rest && *rest != ' ')
328 				++rest;
329 
330 			while (*rest == ' ')
331 				++rest;
332 
333 			filter = call_function(proc->who,
334 				exec_buffer /* f_args */,
335 				empty_string /* args */,
336 				&arg_flag);
337 			if (filter)
338 			{
339 				char *sub_buffer = NULL;
340 
341 				malloc_strcpy(&sub_buffer, filter);
342 				malloc_strcat(&sub_buffer, " ");
343 				malloc_strcat(&sub_buffer, rest);
344 
345 				parse_command(sub_buffer, 0, empty_string);
346 				new_free(&sub_buffer);
347 				new_free(&filter);
348 			}
349 		}
350 		else
351 		{
352 			send_text(proc->who, exec_buffer, proc->redirect);
353 		}
354 	}
355 	else
356 		put_it("%s", exec_buffer);
357 }
358 
359 /*
360  * do_processes: given a set of read-descriptors (as returned by a call to
361  * select()), this will determine which of the process has produced output
362  * and will hadle it appropriately
363  */
364 void
do_processes(rd)365 do_processes(rd)
366 	fd_set	*rd;
367 {
368 	int	i,
369 		flag;
370 	char	exec_buffer[INPUT_BUFFER_SIZE + 1];
371 	char	*ptr;
372 	Process	*proc;
373 	int	old_timeout;
374 	int	server;
375 
376 	if (process_list == (Process **) 0)
377 		return;
378 	old_timeout = dgets_timeout(1);
379 	for (i = 0; i < process_list_size && !break_io_processing; i++)
380 	{
381 		if ((proc = process_list[i]) && proc->p_stdout != -1)
382 		{
383 			if (FD_ISSET(proc->p_stdout, rd))
384 			{
385 				switch (dgets(exec_buffer, INPUT_BUFFER_SIZE, proc->p_stdout, (u_char *) 0))
386 				{
387 				case 0:
388 					if (proc->p_stderr == -1)
389 					{
390 						proc->p_stdin = exec_close(proc->p_stdin);
391 						proc->p_stdout = exec_close(proc->p_stdout);
392 						if (proc->exited)
393 							delete_process(i);
394 					}
395 					else
396 						proc->p_stdout = exec_close(proc->p_stdout);
397 					break;
398 				case -1:
399 					server = from_server;
400 					from_server = proc->server;
401 					if (proc->logical)
402 						flag = do_hook(EXEC_PROMPT_LIST, "%s %s", proc->logical, exec_buffer);
403 					else
404 						flag = do_hook(EXEC_PROMPT_LIST, "%d %s", i, exec_buffer);
405 					from_server = server;
406 					set_prompt_by_refnum(proc->refnum, exec_buffer);
407 					update_input(UPDATE_ALL);
408 					/* if (flag == 0) */
409 					break;
410 				default:
411 					server = from_server;
412 					from_server = proc->server;
413 					message_to(proc->refnum);
414 					proc->counter++;
415 					exec_buffer[sizeof(exec_buffer) - 1] = '\0';	/* blah... */
416 					ptr = exec_buffer + strlen(exec_buffer) - 1;
417 					if ((*ptr == '\n') || (*ptr == '\r'))
418 					{
419 						*ptr = (u_char) 0;
420 						ptr = exec_buffer + strlen(exec_buffer) - 1;
421 						if ((*ptr == '\n') || (*ptr == '\r'))
422 							*ptr = (u_char) 0;
423 					}
424 					if (proc->logical)
425 						flag = do_hook(EXEC_LIST, "%s %s", proc->logical, exec_buffer);
426 					else
427 						flag = do_hook(EXEC_LIST, "%d %s", i, exec_buffer);
428 
429 					if (flag)
430 						send_exec_result(proc, exec_buffer);
431 					message_to(0);
432 					from_server = server;
433 					break;
434 				}
435 			}
436 		}
437 		if (process_list && i < process_list_size &&
438 		    (proc = process_list[i]) && proc->p_stderr != -1)
439 		{
440 			if (FD_ISSET(proc->p_stderr, rd))
441 			{
442 				switch (dgets(exec_buffer, INPUT_BUFFER_SIZE, proc->p_stderr, (u_char *) 0))
443 				{
444 				case 0:
445 					if (proc->p_stdout == -1)
446 					{
447 						proc->p_stderr = exec_close(proc->p_stderr);
448 						proc->p_stdin = exec_close(proc->p_stdin);
449 						if (proc->exited)
450 							delete_process(i);
451 					}
452 					else
453 						proc->p_stderr = exec_close(proc->p_stderr);
454 					break;
455 
456 				case -1:
457 					server = from_server;
458 					from_server = proc->server;
459 					if (proc->logical)
460 						flag = do_hook(EXEC_PROMPT_LIST, "%s %s", proc->logical, exec_buffer);
461 					else
462 						flag = do_hook(EXEC_PROMPT_LIST, "%d %s", i, exec_buffer);
463 					set_prompt_by_refnum(proc->refnum, exec_buffer);
464 					update_input(UPDATE_ALL);
465 					from_server = server;
466 					if (flag == 0)
467 						break;
468 
469 				default:
470 					server = from_server;
471 					from_server = proc->server;
472 					message_to(proc->refnum);
473 					(proc->counter)++;
474 					ptr = exec_buffer + strlen(exec_buffer) - 1;
475 					if ((*ptr == '\n') || (*ptr == '\r'))
476 					{
477 						*ptr = (u_char) 0;
478 						ptr = exec_buffer + strlen(exec_buffer) - 1;
479 						if ((*ptr == '\n') || (*ptr == '\r'))
480 							*ptr = (u_char) 0;
481 					}
482 					if (proc->logical)
483 						flag = do_hook(EXEC_ERRORS_LIST, "%s %s", proc->logical, exec_buffer);
484 					else
485 						flag = do_hook(EXEC_ERRORS_LIST, "%d %s", i, exec_buffer);
486 					if (flag)
487 						send_exec_result(proc, exec_buffer);
488 					message_to(0);
489 					from_server = server;
490 					break;
491 				}
492 			}
493 		}
494 	}
495 	(void) dgets_timeout(old_timeout);
496 }
497 
498 /*
499  * set_process_bits: This will set the bits in a fd_set map for each of the
500  * process descriptors.
501  */
502 void
set_process_bits(rd)503 set_process_bits(rd)
504 	fd_set	*rd;
505 {
506 	int	i;
507 	Process	*proc;
508 
509 	if (process_list)
510 	{
511 		for (i = 0; i < process_list_size; i++)
512 		{
513 			if ((proc = process_list[i]) != NULL)
514 			{
515 				if (proc->p_stdout != -1)
516 					FD_SET(proc->p_stdout, rd);
517 				if (proc->p_stderr != -1)
518 					FD_SET(proc->p_stderr, rd);
519 			}
520 		}
521 	}
522 }
523 
524 /*
525  * list_processes: displays a list of all currently running processes,
526  * including index number, pid, and process name
527  */
528 static	void
list_processes()529 list_processes()
530 {
531 	Process	*proc;
532 	int	i;
533 	int     lastlog_level;
534 
535 	lastlog_level = set_lastlog_msg_level(LOG_CRAP);
536 	if (process_list)
537 	{
538 		say("Process List:");
539 		for (i = 0; i < process_list_size; i++)
540 		{
541 			if ((proc = process_list[i]) != NULL)
542 			{
543 				if (proc->logical)
544 					say("\t%d (%s): %s", i,
545 						proc->logical,
546 						proc->name);
547 				else
548 					say("\t%d: %s", i,
549 						proc->name);
550 			}
551 		}
552 	}
553 	else
554 		say("No processes are running");
555 	set_lastlog_msg_level(lastlog_level);
556 }
557 
558 void
add_process_wait(proc_index,cmd)559 add_process_wait(proc_index, cmd)
560 	int 	proc_index;
561 	char 	*cmd;
562 {
563 	List	*new,
564 		**posn;
565 
566 	for (posn = &process_list[proc_index]->waitcmds; *posn != (List *) 0; posn = &(*posn)->next)
567 		;
568 	new = (List *) new_malloc(sizeof(List));
569 	*posn = new;
570 	new->next = (List *) 0;
571 	new->name = (char *) 0;
572 	malloc_strcpy(&new->name, cmd);
573 }
574 
575 /*
576  * delete_process: Removes the process specifed by index from the process
577  * list.  The does not kill the process, close the descriptors, or any such
578  * thing.  It only deletes it from the list.  If appropriate, this will also
579  * shrink the list as needed
580  */
581 static	int
delete_process(process)582 delete_process(process)
583 	int	process;
584 {
585 	int	flag;
586 	List	*cmd,
587 		*next;
588 
589 	if (process_list)
590 	{
591 		if (process >= process_list_size)
592 			return (-1);
593 		if (process_list[process])
594 		{
595 			Process *dead;
596 
597 			if (process == wait_index)
598 			{
599 				wait_index = -1;
600 				irc_io_loop = 0;
601 			}
602 			dead = process_list[process];
603 			process_list[process] = (Process *) 0;
604 			if (process == (process_list_size - 1))
605 			{
606 				int	i;
607 
608 				for (i = process_list_size - 1;
609 						process_list_size;
610 						process_list_size--, i--)
611 				{
612 					if (process_list[i])
613 						break;
614 				}
615 				if (process_list_size)
616 					process_list = (Process **)
617 						new_realloc((char *) process_list, sizeof(Process *) * process_list_size);
618 				else
619 				{
620 					new_free(&process_list);
621 					process_list = (Process **) 0;
622 				}
623 			}
624 			for (next = dead->waitcmds; next;)
625 			{
626 				cmd = next;
627 				next = next->next;
628 				parse_command(cmd->name, 0, empty_string);
629 				new_free(&cmd->name);
630 				new_free(&cmd);
631 			}
632 			dead->waitcmds = (List *) 0;
633 			if (dead->logical)
634 				flag = do_hook(EXEC_EXIT_LIST, "%s %d %d",
635 					dead->logical, dead->termsig,
636 					dead->retcode);
637 			else
638 				flag = do_hook(EXEC_EXIT_LIST, "%d %d %d",
639 					process, dead->termsig, dead->retcode);
640 			if (flag)
641 			{
642 				if (get_int_var(NOTIFY_ON_TERMINATION_VAR))
643 				{
644 					if (dead->termsig)
645 						say("Process %d (%s) terminated with signal %s (%d)", process, dead->name, signals[dead->termsig],
646 							dead->termsig);
647 					else
648 						say("Process %d (%s) terminated with return code %d", process, dead->name, dead->retcode);
649 				}
650 			}
651 			new_free(&dead->name);
652 			new_free(&dead->logical);
653 			new_free(&dead->who);
654 			new_free(&dead->redirect);
655 			new_free(&dead);
656 			return (0);
657 		}
658 	}
659 	return (-1);
660 }
661 
662 /*
663  * add_process: adds a new process to the process list, expanding the list as
664  * needed.  It will first try to add the process to a currently unused spot
665  * in the process table before it increases it's size.
666  */
667 static	void
add_process(name,logical,pid,p_stdin,p_stdout,p_stderr,redirect,who,refnum)668 add_process(name, logical, pid, p_stdin, p_stdout, p_stderr, redirect, who, refnum)
669 	char	*name,
670 		*logical;
671 	int	pid,
672 		p_stdin,
673 		p_stdout,
674 		p_stderr;
675 	char	*redirect;
676 	char	*who;
677 	unsigned int	refnum;
678 {
679 	int	i;
680 	Process	*proc;
681 
682 	if (process_list == (Process **) 0)
683 	{
684 		process_list = (Process **) new_malloc(sizeof(Process *));
685 		process_list_size = 1;
686 		process_list[0] = (Process *) 0;
687 	}
688 	for (i = 0; i < process_list_size; i++)
689 	{
690 		if (!process_list[i])
691 		{
692 			proc = process_list[i] = (Process *)
693 				new_malloc(sizeof(Process));
694 			proc->name = (char *) 0;
695 			malloc_strcpy(&(proc->name), name);
696 			proc->logical = (char *) 0;
697 			malloc_strcpy(&(proc->logical), logical);
698 			proc->pid = pid;
699 			proc->p_stdin = p_stdin;
700 			proc->p_stdout = p_stdout;
701 			proc->p_stderr = p_stderr;
702 			proc->refnum = refnum;
703 			proc->redirect = (char *) 0;
704 			if (redirect)
705 				malloc_strcpy(&(proc->redirect),
706 					redirect);
707 			if (parsing_server_index != -1)
708 				proc->server = parsing_server_index;
709 			else
710 				proc->server = curr_scr_win->server;
711 			proc->counter = 0;
712 			proc->exited = 0;
713 			proc->termsig = 0;
714 			proc->retcode = 0;
715 			proc->who = (char *) 0;
716 			proc->waitcmds = (List *) 0;
717 			if (who)
718 				malloc_strcpy(&(process_list[i]->who), who);
719 			return;
720 		}
721 	}
722 	process_list_size++;
723 	process_list = (Process **) new_realloc((char *) process_list, sizeof(Process *) * process_list_size);
724 	process_list[process_list_size - 1] = (Process *) 0;
725 	proc = process_list[i] = (Process *) new_malloc(sizeof(Process));
726 	proc->name = (char *) 0;
727 	malloc_strcpy(&(proc->name), name);
728 	proc->logical = (char *) 0;
729 	malloc_strcpy(&(proc->logical), logical);
730 	proc->pid = pid;
731 	proc->p_stdin = p_stdin;
732 	proc->p_stdout = p_stdout;
733 	proc->p_stderr = p_stderr;
734 	proc->refnum = refnum;
735 	proc->redirect = (char *) 0;
736 	if (redirect)
737 		malloc_strcpy(&(proc->redirect), redirect);
738 	proc->server = curr_scr_win->server;
739 	proc->counter = 0;
740 	proc->exited = 0;
741 	proc->termsig = 0;
742 	proc->retcode = 0;
743 	proc->who = (char *) 0;
744 	proc->waitcmds = (List *) 0;
745 	if (who)
746 		malloc_strcpy(&(proc->who), who);
747 }
748 
749 /*
750  * kill_process: sends the given signal to the process specified by the given
751  * index into the process table.  After the signal is sent, the process is
752  * deleted from the process table
753  */
754 static	void
kill_process(kill_index,sig)755 kill_process(kill_index, sig)
756 	int	kill_index,
757 		sig;
758 {
759 	if (process_list && (kill_index < process_list_size) && process_list[kill_index])
760 	{
761 		pid_t	pgid;
762 
763 		say("Sending signal %s (%d) to process %d: %s", signals[sig], sig, kill_index, process_list[kill_index]->name);
764 #ifdef HAVE_GETPGID
765 		pgid = getpgid(process_list[kill_index]->pid);
766 #else
767 # ifdef __sgi
768 		pgid = BSDgetpgrp(process_list[kill_index]->pid);
769 # else
770 #  ifdef HPUX
771 		pgid = getpgrp2(process_list[kill_index]->pid);
772 #  else
773 #   ifdef mips	/* XXX */
774 		pgid = getpgrp(process_list[kill_index]->pid);
775 #   else
776 #    ifdef HAVE_GETSID
777 		pgid = getsid(process_list[kill_index]->pid);
778 #    else
779 #     if defined(ISC) || defined(MUNIX) || defined(BROKEN_GETPGRP)
780 		pgid = process_list[kill_index]->pid;
781 #     else
782 		pgid = getpgrp(process_list[kill_index]->pid);
783 #     endif /* ISC || MUNIX || BROKEN_GETPGRP */
784 #    endif /* HAVE_GETSID */
785 #   endif /* mips */
786 #  endif /* HPUX */
787 # endif /* __sgi */
788 #endif /* HAVE_GETPGID */
789 
790 #ifndef HAVE_KILLPG
791 # define killpg(pg, sig) kill(-(pg), (sig))
792 #endif /* HAVE_KILLPG */
793 
794 		if (pgid == getpid())
795 		{
796 			yell("--- kill_process got pgid of pid!!!  something is very wrong");
797 			return;
798 		}
799 		killpg(pgid, sig);
800 		kill(process_list[kill_index]->pid, sig);
801 	}
802 	else
803 		say("There is no process %d", kill_index);
804 }
805 
806 static	int
is_logical_unique(logical)807 is_logical_unique(logical)
808 	char	*logical;
809 {
810 	Process	*proc;
811 	int	i;
812 
813 	if (logical)
814 		for (i = 0; i < process_list_size; i++)
815 			if ((proc = process_list[i]) && proc->logical &&
816 			    (my_stricmp(proc->logical, logical) == 0))
817 				return 0;
818 	return 1;
819 }
820 
821 /*
822  * start_process: Attempts to start the given process using the SHELL as set
823  * by the user.
824  */
825 static	void
start_process(name,logical,redirect,who,refnum)826 start_process(name, logical, redirect, who, refnum)
827 	char	*name,
828 		*logical,
829 		*who,
830 		*redirect;
831 	unsigned int	refnum;
832 {
833 	int	p0[2],
834 		p1[2],
835 		p2[2],
836 		pid,
837 		cnt;
838 	char	*shell,
839 		*flag,
840 		*arg;
841 	char	buffer[BIG_BUFFER_SIZE+1];
842 
843 #ifdef DAEMON_UID
844 	if (getuid() == DAEMON_UID)
845 	{
846 		say("Sorry, you are not allowed to use EXEC");
847 		return;
848 	}
849 #endif /* DAEMON_UID */
850 	p0[0] = p0[1] = -1;
851 	p1[0] = p1[1] = -1;
852 	p2[0] = p2[1] = -1;
853 	if (pipe(p0) || pipe(p1) || pipe(p2))
854 	{
855 		say("Unable to start new process: %s", strerror(errno));
856 		if (p0[0] != -1)
857 		{
858 			new_close(p0[0]);
859 			new_close(p0[1]);
860 		}
861 		if (p1[0] != -1)
862 		{
863 			new_close(p1[0]);
864 			new_close(p1[1]);
865 		}
866 		if (p2[0] != -1)
867 		{
868 			new_close(p2[0]);
869 			new_close(p2[1]);
870 		}
871 		return;
872 	}
873 	switch (pid = fork())
874 	{
875 	case -1:
876 		say("Couldn't start new process!");
877 		break;
878 	case 0:
879 #ifdef HAVE_SETSID
880 		setsid();
881 #else
882 		setpgrp(0, getpid());
883 #endif /* HAVE_SETSID */
884 		MY_SIGNAL(SIGINT, (sigfunc *) SIG_IGN, 0);
885 		dup2(p0[0], 0);
886 		dup2(p1[1], 1);
887 		dup2(p2[1], 2);
888 		new_close(p0[0]);
889 		new_close(p0[1]);
890 		new_close(p1[0]);
891 		new_close(p1[1]);
892 		new_close(p2[0]);
893 		new_close(p2[1]);
894 		close_all_server();
895 		close_all_dcc();
896 		close_all_exec();
897 		close_all_screen();
898 
899 		/* fix environment */
900 		for (cnt = 0, arg = environ[0]; arg; arg = environ[++cnt])
901 		{
902 			if (strncmp(arg, "TERM=", 5) == 0)
903 			{
904 				environ[cnt] = "TERM=tty";
905 				break;
906 			}
907 		}
908 		if ((shell = get_string_var(SHELL_VAR)) == (char *) 0)
909 		{
910 			char	**args; /* = (char **) 0 */
911 			int	max;
912 
913 			cnt = 0;
914 			max = 5;
915 			args = (char **) new_malloc(sizeof(char *) * max);
916 			while ((arg = next_arg(name, &name)) != NULL)
917 			{
918 				if (cnt == max)
919 				{
920 					max += 5;
921 					args = (char **) new_realloc((char *) args, sizeof(char *) * max);
922 				}
923 				args[cnt++] = arg;
924 			}
925 			args[cnt] = (char *) 0;
926 			setuid(getuid()); /* If we are setuid, set it back! */
927 			setgid(getgid());
928 			execvp(args[0], args);
929 		}
930 		else
931 		{
932 			if ((flag = get_string_var(SHELL_FLAGS_VAR)) ==
933 					(char *) 0)
934 				flag = empty_string;
935 			setuid(getuid());
936 			setgid(getgid());
937 			execl(shell, shell, flag, name, (char *) 0);
938 		}
939 		snprintf(buffer, sizeof buffer, "*** Error starting shell \"%s\": %s\n", shell,
940 			strerror(errno));
941 		write(1, buffer, strlen(buffer));
942 		_exit(-1);
943 		break;
944 	default:
945 		new_close(p0[0]);
946 		new_close(p1[1]);
947 		new_close(p2[1]);
948 		add_process(name, logical, pid, p0[1], p1[0], p2[0], redirect,
949 			who, refnum);
950 		break;
951 	}
952 }
953 
954 /*
955  * text_to_process: sends the given text to the given process.  If the given
956  * process index is not valid, an error is reported and 1 is returned.
957  * Otherwise 0 is returned.
958  * Added show, to remove some bad recursion, phone, april 1993
959  */
960 int
text_to_process(proc_index,text,show)961 text_to_process(proc_index, text, show)
962 	int	proc_index;
963 	char	*text;
964 	int	show;
965 {
966  	u_int	ref;
967 	Process	*proc;
968 
969 	if (valid_process_index(proc_index) == 0)
970 	{
971 		say("No such process number %d", proc_index);
972 		return (1);
973 	}
974 	ref = process_list[proc_index]->refnum;
975 	proc = process_list[proc_index];
976 	message_to(ref);
977 	if (show)
978 		put_it("%s%s", get_prompt_by_refnum(ref), text); /* lynx */
979 	write(proc->p_stdin, text, strlen(text));
980 	write(proc->p_stdin, "\n", 1);
981 	set_prompt_by_refnum(ref, empty_string);
982 	/*  update_input(UPDATE_ALL); */
983 	message_to(0);
984 	return (0);
985 }
986 
987 /*
988  * is_process_running: Given an index, this returns true if the index referes
989  * to a currently running process, 0 otherwise
990  */
991 int
is_process_running(proc_index)992 is_process_running(proc_index)
993 	int	proc_index;
994 {
995 	if (proc_index < 0 || proc_index >= process_list_size)
996 		return (0);
997 	if (process_list && process_list[proc_index])
998 		return (!process_list[proc_index]->exited);
999 	return (0);
1000 }
1001 
1002 /*
1003  * logical_to_index: converts a logical process name to it's approriate index
1004  * in the process list, or -1 if not found
1005  */
1006 int
logical_to_index(logical)1007 logical_to_index(logical)
1008 	char	*logical;
1009 {
1010 	Process	*proc;
1011 	int	i;
1012 
1013 	for (i = 0; i < process_list_size; i++)
1014 	{
1015 		if ((proc = process_list[i]) && proc->logical &&
1016 		    (my_stricmp(proc->logical, logical) == 0))
1017 			return i;
1018 	}
1019 	return -1;
1020 }
1021 
1022 /*
1023  * get_process_index: parses out a process index or logical name from the
1024  * given string
1025  */
1026 int
get_process_index(args)1027 get_process_index(args)
1028 	char	**args;
1029 {
1030 	char	*s;
1031 
1032 	if ((s = next_arg(*args, args)) != NULL)
1033 	{
1034 		if (*s == '%')
1035 			s++;
1036 		else
1037 			return (-1);
1038 		if (is_number(s))
1039 			return (atoi(s));
1040 		else
1041 			return (logical_to_index(s));
1042 	}
1043 	else
1044 		return (-1);
1045 }
1046 
1047 /* is_process: checks to see if arg is a valid process specification */
1048 int
is_process(arg)1049 is_process(arg)
1050 	char	*arg;
1051 {
1052 	if (arg && *arg == '%')
1053 	{
1054 		arg++;
1055 		if (is_number(arg) || (logical_to_index(arg) != -1))
1056 			return (1);
1057 	}
1058 	return (0);
1059 }
1060 
1061 /*
1062  * exec: the /EXEC command.  Handles the whole IRCII process crap.
1063  */
1064 /*ARGSUSED*/
1065 void
execcmd(command,args,subargs)1066 execcmd(command, args, subargs)
1067 	char	*command,
1068 		*args,
1069 		*subargs;
1070 {
1071 	char	*who = (char *) 0,
1072 		*logical = (char *) 0,
1073 		*redirect, /* = (char *) 0, */
1074 		*flag,
1075 		*cmd = (char *) 0;
1076 	unsigned int	refnum = 0;
1077 	int	sig,
1078 		i,
1079 		refnum_flag = 0,
1080 		logical_flag = 0;
1081  	size_t	len;
1082 	Process	*proc;
1083 
1084 	if (get_int_var(EXEC_PROTECTION_VAR) && (send_text_flag != -1))
1085 	{
1086 		say("Attempt to use EXEC from within an ON function!");
1087 		say("Command \"%s\" not executed!", args);
1088 		say("Please read /HELP SET EXEC_PROTECTION");
1089 		say("or important details about this!");
1090 		return;
1091 	}
1092 #ifdef DAEMON_UID
1093 	if (getuid() == DAEMON_UID)
1094 	{
1095 		say("You are not permitted to use EXEC.");
1096 		return;
1097 	}
1098 #endif /* DAEMON_UID */
1099 	if (*args == '\0')
1100 	{
1101 		list_processes();
1102 		return;
1103 	}
1104 	redirect = NULL;
1105 	while ((*args == '-') && (flag = next_arg(args, &args)))
1106 	{
1107 		if (*flag == '-')
1108 		{
1109 			len = strlen(++flag);
1110 			malloc_strcpy(&cmd, flag);
1111 			upper(cmd);
1112 			if (strncmp(cmd, "OUT", len) == 0)
1113 			{
1114 				redirect = "PRIVMSG";
1115 				if (!(who = get_channel_by_refnum(0)))
1116 				{
1117 					say("No current channel in this window for -OUT");
1118  					new_free(&cmd);
1119  					return;
1120  				}
1121  			}
1122  			else if (strncmp(cmd, "TARGET", len) == 0)
1123  			{
1124  				redirect = "PRIVMSG";
1125  				if (!(who = get_target_by_refnum(0)))
1126  				{
1127  					say("No current target in this window for -TARGET");
1128 					new_free(&cmd);
1129 					return;
1130 				}
1131 			}
1132 			else if (strncmp(cmd, "NAME", len) == 0)
1133 			{
1134 				logical_flag = 1;
1135 				if ((logical = next_arg(args, &args)) ==
1136 						(char *) 0)
1137 				{
1138 					say("You must specify a logical name");
1139 					new_free(&cmd);
1140 					return;
1141 				}
1142 			}
1143 			else if (strncmp(cmd, "WINDOW", len) == 0)
1144 			{
1145 				refnum_flag = 1;
1146 				refnum = current_refnum();
1147 			}
1148 			else if (strncmp(cmd, "MSG", len) == 0)
1149 			{
1150 				if (doing_privmsg)
1151 					redirect = "NOTICE";
1152 				else
1153 					redirect = "PRIVMSG";
1154 				if ((who = next_arg(args, &args)) ==
1155 						(char *) 0)
1156 				{
1157 					say("No nicknames specified");
1158 					new_free(&cmd);
1159 					return;
1160 				}
1161 			}
1162 			else if (strncmp(cmd, "FILTER", len) == 0)
1163 			{
1164 				redirect = "FILTER";
1165 				if ((who = next_arg(args, &args)) ==
1166 						(char *) 0)
1167 				{
1168 					say("No filter function specified");
1169 					new_free(&cmd);
1170 					return;
1171 				}
1172 			}
1173 			else if (strncmp(cmd, "CLOSE", len) == 0)
1174 			{
1175 				if ((i = get_process_index(&args)) == -1)
1176 				{
1177 					say("Missing process number or logical name.");
1178 					new_free(&cmd);
1179 					return;
1180 				}
1181 				if (is_process_running(i))
1182 				{
1183 				    proc = process_list[i];
1184 				    proc->p_stdin = exec_close(proc->p_stdin);
1185 				    proc->p_stdout = exec_close(proc->p_stdout);
1186 				    proc->p_stderr = exec_close(proc->p_stderr);
1187 				}
1188 				else
1189 					say("No such process running!");
1190 				new_free(&cmd);
1191 				return;
1192 			}
1193 			else if (strncmp(cmd, "NOTICE", len) == 0)
1194 			{
1195 				redirect = "NOTICE";
1196 				if ((who = next_arg(args, &args)) ==
1197 						(char *) 0)
1198 				{
1199 					say("No nicknames specified");
1200 					new_free(&cmd);
1201 					return;
1202 				}
1203 			}
1204 			else if (strncmp(cmd, "IN", len) == 0)
1205 			{
1206 				if ((i = get_process_index(&args)) == -1)
1207 				{
1208 					say("Missing process number or logical name.");
1209 					new_free(&cmd);
1210 					return;
1211 				}
1212 				text_to_process(i, args, 1);
1213 				new_free(&cmd);
1214 				return;
1215 			}
1216 			else
1217 			{
1218 				char	*cmd2 = (char *) 0;
1219 
1220 				if ((i = get_process_index(&args)) == -1)
1221 				{
1222 					say("Invalid process specification");
1223 					goto out;
1224 				}
1225 				if ((sig = atoi(flag)) > 0)
1226 				{
1227 					if ((sig > 0) && (sig < NSIG))
1228 						kill_process(i, sig);
1229 					else
1230 						say("Signal number can be from 1 to %d", NSIG);
1231 					goto out;
1232 				}
1233 				malloc_strcpy(&cmd2, flag);
1234 				upper(cmd2);
1235  				for (sig = 1; sig < NSIG && signals[sig]; sig++)
1236 				{
1237 					if (!strncmp(signals[sig], flag, len))
1238 					{
1239 						kill_process(i, sig);
1240 						goto out2;
1241 					}
1242 				}
1243 				say("No such signal: %s", flag);
1244 out2:
1245 				new_free(&cmd2);
1246 out:
1247 				new_free(&cmd);
1248 				return;
1249 			}
1250 			new_free(&cmd);
1251 		}
1252 		else
1253 			break;
1254 	}
1255 	if (is_process(args))
1256 	{
1257 		if ((i = get_process_index(&args)) == -1)
1258 		{
1259 			say("Invalid process specification");
1260 			return;
1261 		}
1262 		if (valid_process_index(i))
1263 		{
1264 			proc = process_list[i];
1265 			message_to(refnum);
1266 			if (refnum_flag)
1267 			{
1268 				proc->refnum = refnum;
1269 				if (refnum)
1270 					say("Output from process %d (%s) now going to this window", i, proc->name);
1271 				else
1272 					say("Output from process %d (%s) not going to any window", i, proc->name);
1273 			}
1274 			malloc_strcpy(&(proc->redirect), redirect);
1275 			malloc_strcpy(&(proc->who), who);
1276 			if (redirect)
1277 			{
1278 				say("Output from process %d (%s) now going to %s", i, proc->name, who);
1279 			}
1280 			else
1281 				say("Output from process %d (%s) now going to you", i, proc->name);
1282 			if (logical_flag)
1283 			{
1284 				if (logical)
1285 				{
1286 					if (is_logical_unique(logical))
1287 					{
1288 						malloc_strcpy(&(
1289 						    proc->logical),
1290 						    logical);
1291 						say("Process %d (%s) is now called %s",
1292 							i, proc->name, proc->logical);
1293 					}
1294 					else
1295 						say("The name %s is not unique!"
1296 							, logical);
1297 				}
1298 				else
1299 					say("The name for process %d (%s) has been removed", i, proc->name);
1300 			}
1301 			message_to(0);
1302 		}
1303 		else
1304 			say("Invalid process specification");
1305 	}
1306 	else
1307 	{
1308 		if (is_logical_unique(logical))
1309 			start_process(args, logical, redirect, who, refnum);
1310 		else
1311 			say("The name %s is not unique!", logical);
1312 	}
1313 }
1314 
1315 /*
1316  * clean_up_processes: kills all processes in the procss list by first
1317  * sending a SIGTERM, then a SIGKILL to clean things up
1318  */
1319 void
clean_up_processes()1320 clean_up_processes()
1321 {
1322 	int	i;
1323 
1324 	if (process_list_size)
1325 	{
1326 		say("Cleaning up left over processes....");
1327 		for (i = 0; i < process_list_size; i++)
1328 		{
1329 			if (process_list[i])
1330 				kill_process(i, SIGTERM);
1331 		}
1332 		sleep(2);	/* Give them a chance for a graceful exit */
1333 		for (i = 0; i < process_list_size; i++)
1334 		{
1335 			if (process_list[i])
1336 				kill_process(i, SIGKILL);
1337 		}
1338 	}
1339 }
1340 
1341 /*
1342  * close_all_exec:  called when we fork of a wserv process for interaction
1343  * with screen/X, to close all unnessicary fd's that might cause problems
1344  * later.
1345  */
1346 void
close_all_exec()1347 close_all_exec()
1348 {
1349 	int	i;
1350 	int	tmp;
1351 
1352 	tmp = window_display;
1353 	window_display = 0;
1354 	for (i = 0; i < process_list_size; i++)
1355 		if (process_list[i])
1356 		{
1357 			if (process_list[i]->p_stdin)
1358 				new_close(process_list[i]->p_stdin);
1359 			if (process_list[i]->p_stdout)
1360 				new_close(process_list[i]->p_stdout);
1361 			if (process_list[i]->p_stderr)
1362 				new_close(process_list[i]->p_stderr);
1363 			delete_process(i);
1364 			kill_process(i, SIGKILL);
1365 		}
1366 	window_display = tmp;
1367 }
1368 
1369 void
exec_server_delete(i)1370 exec_server_delete(i)
1371 	int	i;
1372 {
1373 	int	j;
1374 
1375 	for (j = 0; j < process_list_size; j++)
1376 		if (process_list[j] && process_list[j]->server >= i)
1377 			process_list[j]->server--;
1378 }
1379