1 /*
2  * exec.c: handles exec'd process for IRCII
3  *
4  * Copyright (c) 1990 Michael Sandroff.
5  * Copyright (c) 1991, 1992 Troy Rollo.
6  * Copyright (c) 1992-1996 Matthew Green.
7  * Copyright 1997, 2014, 2020 EPIC Software Labs
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notices, the above paragraph (the one permitting redistribution),
17  *    this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. The names of the author(s) may not be used to endorse or promote
20  *    products derived from this software without specific prior written
21  *    permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
24  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26  * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
30  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #include "irc.h"
37 #include "dcc.h"
38 #include "exec.h"
39 #include "vars.h"
40 #include "ircaux.h"
41 #include "commands.h"
42 #include "window.h"
43 #include "screen.h"
44 #include "hook.h"
45 #include "input.h"
46 #include "server.h"
47 #include "output.h"
48 #include "parse.h"
49 #include "newio.h"
50 #include "ifcmd.h"
51 #include "functions.h"
52 
53 #ifdef NO_JOB_CONTROL
BUILT_IN_COMMAND(execcmd)54 BUILT_IN_COMMAND(execcmd)
55 {
56 	say("Your system does not support job control, sorry.");
57 	return;
58 }
get_child_exit(pid_t x)59 int	get_child_exit (pid_t x)			{ return -1; }
clean_up_processes(void)60 void	clean_up_processes (void)			{ return; }
text_to_process(const char * x,const char * y,int z)61 int	text_to_process (const char *x, const char *y, int z)
62 {
63 	say("Cannot send text to process without job control, sorry.");
64 	return 1;
65 }
add_process_wait(const char *,const char * y)66 void	add_process_wait (const char *, const char *y)	{ return; }
is_valid_process(const char * x)67 int	is_valid_process (const char *x)		{ return -1; }
BUILT_IN_FUNCTION(execctl)68 BUILT_IN_FUNCTION(execctl) { RETURN_EMPTY; }
69 #else
70 
71 #include <sys/wait.h>
72 #include <sys/ioctl.h>
73 #ifdef HAVE_SYS_FILIO_H
74 #include <sys/filio.h>
75 #endif
76 
77 typedef struct WaitCmdStru
78 {
79 	struct WaitCmdStru *	next;
80 	char *			commands;
81 }	WaitCmd;
82 
83 /* Process: the structure that has all the info needed for each process */
84 typedef struct
85 {
86 	int	refnum;			/* The logical refnum of an /EXECd process */
87 	char *	refnum_desc;		/* The %<refnum> */
88 	char *	logical;		/* The logical name supplied by the user  (-NAME) */
89 	char *	logical_desc;		/* The %<logical> */
90 	char *	commands;		/* The commands being executed */
91 	int	direct;			/* Whether to use a shell or not */
92 
93 	pid_t	pid;			/* process-id of process */
94 	int	p_stdin;		/* fd through which we send to process's stdin */
95 	int	p_stdout;		/* fd through which we receive from process's stdout */
96 	int	p_stderr;		/* fd through which we receive from process's stderr */
97 
98 	int	exited;			/* 0 if process is running, 1 if it exited or was killed */
99 	int	termsig;		/* The signal that killed it (if any) */
100 	int	retcode;		/* It's exit code (if any) */
101 
102 	char *	redirect;		/* Either PRIVMSG or NOTICE (for send_to_target) */
103 	char *	who;			/* Who all output is sent to (for send_to_target) */
104 	unsigned  window_refnum;	/* A window all output shall be /echo'd to */
105 	int	server_refnum;		/* The server this /exec process is attached to */
106 
107 	int	dumb;			/* 0 if we're handling output from it, 1 if we stopped listening */
108 	int	disowned;		/* NOT IMPLEMENTED - 1 if we disowned it for cleanup */
109 
110 	char *	stdoutc;		/* Commands to run on each line of stdout (-LINE) */
111 	char *	stdoutpc;		/* Commands to run on each partial line of output (-LINEPART) */
112 	char *	stderrc;		/* Commands to run on each line of stderr (-ERROR) */
113 	char *	stderrpc;		/* Commands to run on each partial line of error (-ERRORPART) */
114 	WaitCmd *waitcmds;		/* Commands to run on process exit (-END or /WAIT -CMD) */
115 
116 	Timeval	started_at;		/* When the process began */
117 	int	lines_recvd;		/* How many lines we've received from process */
118 	int	lines_sent;		/* How many lines we've sent */
119 	int	lines_limit;		/* How many lines till we give up */
120 }	Process;
121 
122 
123 static	Process **process_list = NULL;
124 static	int	process_list_size = 0;
125 static	int	next_process_refnum = 1;
126 
127 static 	void 	handle_filedesc 	(Process *, int *, int, int);
128 static 	void 	cleanup_dead_processes 	(void);
129 static 	void 	ignore_process 		(Process *);
130 static 	void 	exec_close_in		(Process *);
131 static 	void 	exec_close_out		(Process *);
132 static 	void 	kill_process 		(Process *, int);
133 static 	void 	kill_all_processes 	(int signo);
134 static 	int 	is_logical_unique 	(char *logical);
135 static	void 	do_exec 		(int fd);
136 static	Process *	get_process_by_refnum (int refnum);
137 static int 	get_process_refnum 	(const char *desc);
138 
set_message_from_for_process(Process * proc)139 static	int	set_message_from_for_process (Process *proc)
140 {
141 	const char *logical_name;
142 	int	l;
143 
144 	if (proc->logical_desc)
145 		logical_name = proc->logical_desc;
146 	else
147 		logical_name = proc->refnum_desc;
148 
149 	if (proc->window_refnum)
150 		l = message_setall(proc->window_refnum, logical_name, LEVEL_OTHER);
151 	else
152 		l = message_from(logical_name, LEVEL_OTHER);
153 
154 	return l;
155 }
156 
157 
158 /*************************************************************/
159 /*
160  * summarize_process
161  */
summarize_process(Process * proc,char * outbuf,size_t outbufsize)162 static size_t	summarize_process (Process *proc, char *outbuf, size_t outbufsize)
163 {
164 	if (proc)
165 	{
166 		if (proc->logical)
167 			return snprintf(outbuf, outbufsize,
168 					"%d (%s) (pid %d): %s",
169 				        proc->refnum, proc->logical, proc->pid,
170 					proc->commands);
171 		else
172 			return snprintf(outbuf, outbufsize,
173 					"%d (pid %d): %s",
174 				        proc->refnum, proc->pid, proc->commands);
175 	}
176 	else
177 		return 0;
178 }
179 
180 /*
181  * output_process
182  */
output_process(Process * process)183 static void	output_process (Process *process)
184 {
185 }
186 
187 /*
188  * check_process_list
189  * - This defrags the process list, moving all alive processes to
190  *   the lowest index, and if none are alive, deleting the list.
191  */
check_process_list(void)192 static void	check_process_list (void)
193 {
194 }
195 
196 /*************************************************************/
197 /*
198  * execcmd_list_processes
199  */
execcmd_list_processes(void)200 static void	execcmd_list_processes (void)
201 {
202 	int		i;
203 	Process	*	proc;
204 	char		outbuf[BIG_BUFFER_SIZE + 1];
205 	size_t		outbufsiz = sizeof(outbuf);
206 
207 	if (process_list)
208 	{
209 		say("Process List:");
210 		for (i = 0; i < process_list_size; i++)
211 		{
212 			if ((proc = process_list[i]))
213 			{
214 				if (summarize_process(proc, outbuf, outbufsiz))
215 					say("\t%s", outbuf);
216 			}
217 		}
218 	}
219 	else
220 		say("No processes are running");
221 }
222 
223 
224 /*
225  * do_processes: This is called from the main io() loop to handle any
226  * pending /exec'd events.  All this does is call handle_filedesc() on
227  * the two reading descriptors.  If an EOF is asserted on either, then they
228  * are closed.  If EOF has been asserted on both, then  we mark the process
229  * as being "dumb".  Once it is reaped (exited), it is expunged.
230  */
do_exec(int fd)231 static void 		do_exec (int fd)
232 {
233 	int	i;
234 	int	global_limit, limit;
235 
236 	if (!process_list)
237 		return;
238 
239 	global_limit = get_int_var(SHELL_LIMIT_VAR);
240 
241 	for (i = 0; i < process_list_size; i++)
242 	{
243 		Process *proc = process_list[i];
244 
245 		if (!proc)
246 			continue;
247 
248 		if (proc->p_stdout != -1 && proc->p_stdout == fd)
249 		{
250 			handle_filedesc(proc, &proc->p_stdout,
251 					EXEC_PROMPT_LIST, EXEC_LIST);
252 		}
253 
254 		if (proc->p_stderr != -1 && proc->p_stderr == fd)
255 		{
256 			handle_filedesc(proc, &proc->p_stderr,
257 					EXEC_PROMPT_LIST, EXEC_ERRORS_LIST);
258 		}
259 
260 		if (proc->lines_limit)
261 			limit = proc->lines_limit;
262 		else
263 			limit = global_limit;
264 
265 		if (limit > 0 && proc->lines_recvd >= limit)
266 		{
267 			int l = set_message_from_for_process(proc);
268 			say("Ignoring process %d (reached output limit): %s", proc->refnum, proc->commands);
269 			ignore_process(proc);
270 			pop_message_from(l);
271 		}
272 	}
273 
274 	/* Clean up any (now) dead processes */
275 	cleanup_dead_processes();
276 }
277 
278 /*
279  * This is the back end to do_processes, saves some repeated code
280  */
handle_filedesc(Process * proc,int * fd,int hook_nonl,int hook_nl)281 static void 	handle_filedesc (Process *proc, int *fd, int hook_nonl, int hook_nl)
282 {
283 	char 	exec_buffer[IO_BUFFER_SIZE + 1];
284 	ssize_t	len;
285 	int	ofs;
286 	const char *callback = NULL;
287 	int	hook = -1;
288 	int	l;
289 	const char *logical_name;
290 	const char	*utf8_text;
291 	char *extra = NULL;
292 
293 	/* No buffering! */
294 	switch ((len = dgets(*fd, exec_buffer, IO_BUFFER_SIZE, 0)))
295 	{
296 	    case -1:		/* Something died */
297 	    {
298 		*fd = new_close(*fd);
299 		if (proc->p_stdout == -1 && proc->p_stderr == -1)
300 			proc->dumb = 1;
301 		return;				/* PUNT! */
302 	    }
303 
304 	    case 0:		/* We didnt get a full line */
305 	    {
306 		/*
307 		 * XXX This is a hack.  dgets() can return 0 for a line
308 		 * containing solely a newline, as well as a line that didn't
309 		 * have a newline.  So we have to check to see if the line
310 		 * contains only a newline!
311 		 */
312 		if (exec_buffer[0] != '\n')
313 		{
314 		    if (hook_nl == EXEC_LIST)
315 		    {
316 			if (proc->stdoutpc && *proc->stdoutpc)
317 			    callback = proc->stdoutpc;
318 		    }
319 		    else if (hook_nl == EXEC_ERRORS_LIST)
320 		    {
321 			if (proc->stderrpc && *proc->stderrpc)
322 			    callback = proc->stderrpc;
323 		    }
324 		    hook = hook_nonl;
325 		    break;
326 		}
327 
328 		/* XXX HACK -- Line contains only a newline.  */
329 		*exec_buffer = 0;
330 		FALLTHROUGH
331 	    }
332 
333 	    default:		/* We got a full line */
334 	    {
335 		if (hook_nl == EXEC_LIST)
336 		{
337 		    if (proc->stdoutc && *proc->stdoutc)
338 			callback = proc->stdoutc;
339 		}
340 		else if (hook_nl == EXEC_ERRORS_LIST)
341 		{
342 		    if (proc->stderrc && *proc->stderrc)
343 			callback = proc->stderrc;
344 		}
345 		hook = hook_nl;
346 		break;
347 	    }
348 	}
349 
350 	ofs = from_server;
351 	from_server = proc->server_refnum;
352 	proc->lines_recvd++;
353 
354 	while (len > 0 && (exec_buffer[len - 1] == '\n' ||
355 			   exec_buffer[len - 1] == '\r'))
356 	     exec_buffer[--len] = 0;
357 
358 	if (proc->logical_desc)
359 		logical_name = proc->logical_desc;
360 	else
361 		logical_name = proc->refnum_desc;
362 	utf8_text = inbound_recode(logical_name, proc->server_refnum, empty_string, exec_buffer, &extra);
363 
364 	l = set_message_from_for_process (proc);
365 	if (proc->redirect && proc->who)
366 	     redirect_text(proc->server_refnum, proc->who, utf8_text, proc->redirect, 1);
367 
368 	if (callback)
369 	    call_lambda_command("EXEC", callback, utf8_text);
370 	else if (proc->logical)
371 	{
372 	     if ((do_hook(hook, "%s %s", proc->logical, utf8_text)))
373 		if (!proc->redirect)
374 		    put_it("%s", utf8_text);
375 	}
376 	else
377 	{
378 	    if ((do_hook(hook, "%d %s", proc->refnum, utf8_text)))
379 		if (!proc->redirect)
380 		    put_it("%s", utf8_text);
381 	}
382 	pop_message_from(l);
383 
384 	new_free(&extra);
385 	from_server = ofs;
386 }
387 
388 /*
389  * get_child_exit: This looks for dead child processes of the client.
390  * There are two main sources of dead children:  Either an /exec'd process
391  * has exited, or the client has attempted to fork() off a helper process
392  * (such as wserv or gzip) and that process has choked on itself.
393  *
394  * When SIGCHLD is recieved, the global variable 'dead_children_processes'
395  * is incremented.  When this function is called, we go through and call
396  * waitpid() on all of the outstanding zombies, conditionally stopping when
397  * we reach a specific wanted sub-process.
398  *
399  * If you want to stop reaping children when a specific subprocess is
400  * reached, specify the process in 'wanted'.  If all youre doing is cleaning
401  * up after zombies and /exec's, then 'wanted' should be -1.
402  */
get_child_exit(pid_t wanted)403 int 		get_child_exit (pid_t wanted)
404 {
405 	Process	*proc;
406 	pid_t	pid;
407 	int	status, i;
408 
409 	/*
410 	 * Iterate until we've reaped all of the dead processes
411 	 * or we've found the one asked for.
412 	 */
413 	if (dead_children_processes)
414 	{
415 	    block_signal(SIGCHLD);
416 	    while ((pid = waitpid(wanted, &status, WNOHANG)) > 0)
417 	    {
418 		/*
419 		 * First thing we do is look to see if the process we're
420 		 * working on is the one that was asked for.  If it is,
421 		 * then we get its exit status information and return it.
422 		 */
423 		if (wanted != -1 && pid == wanted)
424 		{
425 			/*
426 			 * We do not clear 'dead_children_processes' here
427 			 * because we do not know if we've reaped all of
428 			 * the children yet!  Leaving it set means this
429 			 * function is called again, and then if there are
430 			 * no more left, it is cleared (below).
431 			 */
432 		        unblock_signal(SIGCHLD);
433 
434 			if (WIFEXITED(status))
435 				return WEXITSTATUS(status);
436 			if (WIFSTOPPED(status))
437 				return -(WSTOPSIG(status));
438 			if (WIFSIGNALED(status))
439 				return -(WTERMSIG(status));
440 		}
441 
442 		/*
443 		 * If it wasnt the process asked for, then we've probably
444 		 * stumbled across a dead /exec'd process.  Look for the
445 		 * corresponding child process, and mark it as being dead.
446 		 */
447 		else
448 		{
449 			for (i = 0; i < process_list_size; i++)
450 			{
451 				proc = process_list[i];
452 				if (proc && proc->pid != -1 && proc->pid == pid)
453 				{
454 					proc->exited = 1;
455 					proc->termsig = WTERMSIG(status);
456 					proc->retcode = WEXITSTATUS(status);
457 					break;
458 				}
459 			}
460 		}
461 	    }
462 	    dead_children_processes = 0;
463 	    unblock_signal(SIGCHLD);
464 	}
465 
466 	/*
467 	 * Now we may have reaped some /exec'd processes that were previously
468 	 * dumb and have now exited.  So we call cleanup_dead_processes() to
469 	 * find and delete any such processes.
470 	 */
471 	cleanup_dead_processes();
472 
473 	/*
474 	 * If 'wanted' is not -1, then we didnt find that process, and
475 	 * if 'wanted' is -1, then you should ignore the retval anyhow. ;-)
476 	 */
477 	return -1;
478 }
479 
480 
481 /*
482  * clean_up_processes: In effect, we want to tell all of our sub processes
483  * that we're going away.  We cant be 100% sure that theyre all dead by
484  * the time this function returns, but we can be 100% sure that they will
485  * be killed off next time they come up to run.  This is the only thing that
486  * can be guaranteed, and is in fact all we really need to know.
487  */
clean_up_processes(void)488 void 		clean_up_processes (void)
489 {
490 	if (process_list_size)
491 	{
492 		say("Closing all left over exec's");
493 		kill_all_processes(SIGTERM);
494 		sleep(2);
495 		get_child_exit(-1);
496 		kill_all_processes(SIGKILL);
497 	}
498 }
499 
500 
501 /*
502  * text_to_process: sends the given text to the given process.  If the given
503  * process reference is not valid, an error is reported and 1 is returned.
504  * Otherwise 0 is returned.
505  * Added show, to remove some bad recursion, phone, april 1993
506  */
text_to_process(const char * target,const char * text,int show)507 int 		text_to_process (const char *target, const char *text, int show)
508 {
509 	int	process_refnum;
510 	Process	*proc;
511 	char *	my_buffer;
512 	size_t	size;
513 	const char *logical_name;
514 	const char *recoded_text;
515 	char *	extra = NULL;
516 	int	l;
517 
518 	if (!(process_refnum = get_process_refnum(target)))
519 	{
520 		say("Cannot send text to invalid process %s", target);
521 		return -1;
522 	}
523 
524 	if (!(proc = get_process_by_refnum(process_refnum)))
525 	{
526 		say("Cannot send text to invalid process %s", target);
527 		return -1;
528 	}
529 
530 	if (proc->pid == -1)
531 	{
532 		say("Cannot send text to unlaunched process %s", target);
533 		return -1;
534 	}
535 
536 	if (proc->p_stdin == -1)
537 	{
538 		say("Cannot send text to -CLOSEd process %s", target);
539 		return -1;
540 	}
541 
542 	if (proc->logical_desc)
543 		logical_name = proc->logical_desc;
544 	else
545 		logical_name = proc->refnum_desc;
546 
547 	/* Say nothing, do nothing */
548 	if (!text)
549 		return -1;
550 
551 	size = strlen(text) + 2;
552 	my_buffer = alloca(size);
553 	snprintf(my_buffer, size, "%s\n", text);
554 	recoded_text = outbound_recode(logical_name, proc->server_refnum, my_buffer, &extra);
555 
556 	l = set_message_from_for_process (proc);
557 	if (write(proc->p_stdin, recoded_text, strlen(recoded_text)) <= 0)
558 		yell("Was unable to write text %s to process %s", text, target);
559 	new_free(&extra);
560 	set_prompt_by_refnum(proc->window_refnum, empty_string);
561 
562 	if (show)
563 		if ((do_hook(SEND_EXEC_LIST, "%s %d %s", logical_name, process_refnum, text)))
564 			put_it("%s%s", get_prompt_by_refnum(proc->window_refnum), text);
565 
566 	pop_message_from(l);
567 	return (0);
568 }
569 
570 /*
571  * This adds a new /wait %proc -cmd   entry to a running process.
572  */
add_process_wait(const char * target,const char * cmd)573 void 		add_process_wait (const char *target, const char *cmd)
574 {
575 	Process *proc;
576 	WaitCmd *new_ewl, *posn;
577 	int	process_refnum;
578 
579 	if (!(process_refnum = get_process_refnum(target)))
580 	{
581 		say("Cannot add wait command to invalid process %s", target);
582 		return;
583 	}
584 
585 	if (!(proc = get_process_by_refnum(process_refnum)))
586 	{
587 		say("Cannot add wait command to invalid process %s", target);
588 		return;
589 	}
590 
591 	new_ewl = new_malloc(sizeof(WaitCmd));
592 	new_ewl->next = NULL;
593 	new_ewl->commands = malloc_strdup(cmd);
594 
595 	if ((posn = proc->waitcmds))
596 	{
597 		while (posn->next)
598 			posn = posn->next;
599 		posn->next = new_ewl;
600 	}
601 	else
602 		proc->waitcmds = new_ewl;
603 
604 }
605 
606 
607 
608 /* - - - - - - - - -  - - - - - - - - - - - - - - - - - */
609 
610 /*
611  * A process must go through two stages to be completely obliterated from
612  * the client.  Either stage may happen first, but until both are completed
613  * we keep the process around.
614  *
615  *	1) We must recieve an EOF on both stdin and stderr, or we must
616  *	   have closed stdin and stderr already (handled by do_processes)
617  *	2) The process must have died (handled by get_child_exit)
618  *
619  * The reason why both must happen is becuase the process can die (and
620  * we would get an async signal) before we read all of its output on the
621  * pipe, and if we simply deleted the process when it dies, we could lose
622  * some of its output.  The reason why we cant delete a process that has
623  * asserted EOF on its output is because it could still be running (duh! ;-)
624  * So we wait for both to happen.
625  */
626 
627 /*
628  * This function is called by the three places that can effect a change
629  * on the state of a running process:
630  * 	1) get_child_exit, which can mark a process as exited
631  *	2) do_processes, which can mark a child as being done with I/O
632  *	3) execcmd, which can mark a child as being done with I/O
633  *
634  * Any processes that are found to have both exited and having completed
635  * their I/O will be summarily destroyed.
636  */
cleanup_dead_processes(void)637 static void 	cleanup_dead_processes (void)
638 {
639 	int	i;
640 	WaitCmd	*cmd, *next;
641 	Process *deadproc, *proc;
642 	char	*exit_info;
643 	int	old_from_server, l;
644 
645 	if (!process_list)
646 		return;		/* Nothing to do */
647 
648 	old_from_server = from_server;
649 	for (i = 0; i < process_list_size; i++)
650 	{
651 		if (!(proc = process_list[i]))
652 			continue;
653 
654 		/*
655 		 * We do not parse the process if it has not
656 		 * both exited and finished its io, UNLESS
657 		 * it has been disowned.
658 		 */
659 		if ((!proc->exited || !proc->dumb) && !proc->disowned)
660 			continue;		/* Not really dead yet */
661 
662 		deadproc = process_list[i];
663 		process_list[i] = NULL;
664 
665 		/*
666 		 * First thing to do is fill out the exit information
667 		 */
668 		if (deadproc->logical)
669 		{
670 			size_t	len = strlen(deadproc->logical) + 25;
671 
672 			exit_info = alloca(len);
673 			snprintf(exit_info, len, "%s %d %d",
674 					deadproc->logical, deadproc->termsig,
675 					deadproc->retcode);
676 		}
677 		else
678 		{
679 			exit_info = alloca(40);
680 			snprintf(exit_info, 32, "%d %d %d",
681 				deadproc->refnum, deadproc->termsig, deadproc->retcode);
682 		}
683 
684 		from_server = deadproc->server_refnum;
685 		l = set_message_from_for_process(deadproc);
686 
687 		/*
688 		 * First thing we do is run any /wait %proc -cmd commands
689 		 */
690 		next = deadproc->waitcmds;
691 		deadproc->waitcmds = NULL;
692 		while ((cmd = next))
693 		{
694 			next = cmd->next;
695 			call_lambda_command("WAITPROC", cmd->commands, exit_info);
696 			new_free(&cmd->commands);
697 			new_free((char **)&cmd);
698 		}
699 
700 		/*
701 		 * Throw /on exec_exit
702 		 */
703 		if (do_hook(EXEC_EXIT_LIST, "%s", exit_info))
704 		{
705 		    if (get_int_var(NOTIFY_ON_TERMINATION_VAR))
706 		    {
707 			if (deadproc->termsig > 0 && deadproc->termsig < NSIG)
708 			{
709 				say("Process %d (%s) terminated "
710 					"with signal %s (%d)",
711 				   deadproc->refnum, deadproc->commands,
712 				   get_signal_name(deadproc->termsig),
713 				   deadproc->termsig);
714 
715 			}
716 			else if (deadproc->disowned)
717 			{
718 				say("Process %d (%s) disowned",
719 				   deadproc->refnum, deadproc->commands);
720 			}
721 			else
722 			{
723 				say("Process %d (%s) terminated "
724 					"with return code %d",
725 				   deadproc->refnum, deadproc->commands,
726 				   deadproc->retcode);
727 			}
728 		    }
729 		}
730 		pop_message_from(l);
731 
732 		deadproc->p_stdin = new_close(deadproc->p_stdin);
733 		deadproc->p_stdout = new_close(deadproc->p_stdout);
734 		deadproc->p_stderr = new_close(deadproc->p_stderr);
735 		new_free(&deadproc->commands);
736 		new_free(&deadproc->logical);
737 		new_free(&deadproc->who);
738 		new_free(&deadproc->redirect);
739 		new_free(&deadproc->stdoutc);
740 		new_free(&deadproc->stdoutpc);
741 		new_free(&deadproc->stderrc);
742 		new_free(&deadproc->stderrpc);
743 		new_free((char **)&deadproc);
744 	}
745 
746 	/*
747 	 * Resize away any dead processes at the end
748 	 */
749 	for (i = process_list_size - 1; i >= 0; i--)
750 	{
751 		if (process_list[i])
752 			break;
753 	}
754 
755 	if (process_list_size != i + 1)
756 	{
757 		process_list_size = i + 1;
758 		RESIZE(process_list, Process *, process_list_size);
759 	}
760 
761 	from_server = old_from_server;
762 }
763 
764 
765 
766 /*
767  * ignore_process: When we no longer want to communicate with the process
768  * any longer, we call here.  It continues execution until it is done, but
769  * we are oblivious to any output it sends.  Now, it will get an EOF
770  * condition on its output fd, so it will probably either take the hint, or
771  * its output will go the bit bucket (which we want to happen)
772  */
ignore_process(Process * proc)773 static void 	ignore_process (Process *proc)
774 {
775 	if (!proc)
776 		return;
777 
778 	exec_close_in(proc);
779 	exec_close_out(proc);
780 }
781 
782 /*
783  * close_in:  When we are finished with the process but still want the
784  * rest of its output, we close its input, and hopefully it will get the
785  * message and close up shop.
786  */
exec_close_in(Process * proc)787 static void 	exec_close_in (Process *proc)
788 {
789 	if (!proc)
790 		return;
791 
792 	if (proc->p_stdin != -1)
793 		proc->p_stdin = new_close(proc->p_stdin);
794 }
795 
796 /*
797  * close_out:  When we are done sending to a process sometimes we have to
798  * close our stdout before they will do their thing and send us data back
799  * to stdin.
800  */
exec_close_out(Process * proc)801 static void 	exec_close_out (Process *proc)
802 {
803 	if (!proc)
804 		return;
805 
806 	if (proc->p_stdout != -1)
807 		proc->p_stdout = new_close(proc->p_stdout);
808 	if (proc->p_stderr != -1)
809 		proc->p_stderr = new_close(proc->p_stderr);
810 
811 	proc->dumb = 1;
812 }
813 
814 
815 /*
816  * kill_process: sends the given signal to the specified process.  It does
817  * not delete the process from the process table or anything like that, it
818  * only is for sending a signal to a sub process (most likely in an attempt
819  * to kill it.)  The actual reaping of the children will take place async
820  * on the next parsing run.
821  */
kill_process(Process * proc,int sig)822 static void 	kill_process (Process *proc, int sig)
823 {
824 	pid_t	pgid;
825 	int	old_from_server, l;
826 
827 	if (!proc)
828 		return;
829 
830 	old_from_server = from_server;
831 	from_server = proc->server_refnum;
832 	l = set_message_from_for_process(proc);
833 
834 	say("Sending signal %s (%d) to process %d: %s",
835 		get_signal_name(sig), sig, proc->refnum, proc->commands);
836 
837 	pop_message_from(l);
838 	from_server = old_from_server;
839 
840 	/* Administrative kill of unlaunched process */
841 	if (proc->pid == -1)
842 	{
843 		proc->exited = 1;
844 		proc->disowned = 1;
845 		proc->termsig = sig;
846 		return;
847 	}
848 
849 #ifdef HAVE_GETPGID
850 	pgid = getpgid(proc->pid);
851 #else
852 	pgid = proc->pid;	/* Oh well.... */
853 #endif
854 
855 #ifndef HAVE_KILLPG
856 # define killpg(pg, sig) kill(-(pg), (sig))
857 #endif
858 
859 	/* The exec'd process shouldn't be in our process group */
860 	if (pgid == getpid())
861 	{
862 		yell("--- exec'd process is in my job control session!  Something is hosed ---");
863 		return;
864 	}
865 
866 	killpg(pgid, sig);
867 	kill(proc->pid, sig);
868 }
869 
870 
871 /*
872  * This kills (sends a signal, *NOT* ``make it stop running'') all of the
873  * currently running subprocesses with the given signal.  Presumably this
874  * is because you want them to die.
875  *
876  * Remember that UNIX signals are asynchronous.  At best, you should assume
877  * that they have an advisory effect.  You can tell a process that it should
878  * die, but you cannot tell it *when* it will die -- that is up to the system.
879  * That means that it is pointless to assume the condition of any of the
880  * kill()ed processes after the kill().  They may indeed be dead, or they may
881  * be ``sleeping but runnable'', or they might even be waiting for a hardware
882  * condition (such as a swap in).  You do not know when the process will
883  * actually die.  It could be 15 ns, it could be 15 minutes, it could be
884  * 15 years.  Its also useful to note that we, as the parent process, will not
885  * recieve the SIGCHLD signal until after the child dies.  That means it is
886  * pointless to try to reap any children processes here.  The main io()
887  * loop handles reaping children (by calling get_child_exit()).
888  */
kill_all_processes(int signo)889 static void 	kill_all_processes (int signo)
890 {
891 	int	i;
892 	int	tmp;
893 
894 	tmp = window_display;
895 	window_display = 0;
896 	for (i = 0; i < process_list_size; i++)
897 	{
898 		if (process_list[i])
899 		{
900 			ignore_process(process_list[i]);
901 			kill_process(process_list[i], signo);
902 		}
903 	}
904 	window_display = tmp;
905 }
906 
907 
908 
909 
910 /* * * * * * logical stuff * * * * * * */
is_logical_unique(char * logical)911 static int 	is_logical_unique (char *logical)
912 {
913 	Process	*proc;
914 	int	i;
915 
916 	if (!logical)
917 		return 1;
918 
919 	for (i = 0; i < process_list_size; i++)
920 	{
921 		if (!(proc = process_list[i]) || !proc->logical)
922 			continue;
923 
924 		if (!my_stricmp(proc->logical, logical))
925 			return 0;
926 	}
927 
928 	return 1;
929 }
930 
931 
get_process_by_refnum(int refnum)932 static	Process *	get_process_by_refnum (int refnum)
933 {
934 	int	i;
935 
936 	for (i = 0; i < process_list_size; i++)
937 	{
938 		if (process_list[i] && process_list[i]->refnum == refnum)
939 			return process_list[i];
940 	}
941 	return NULL;
942 }
943 
944 /*
945  * get_process_refnum: parses out a process index or logical name from the
946  * given string
947  */
get_process_refnum(const char * desc)948 static int 	get_process_refnum (const char *desc)
949 {
950 	int	i;
951 
952 	if (!desc || *desc != '%')
953 		return 0;
954 
955 	for (i = 0; i < process_list_size; i++)
956 	{
957 		if (process_list[i])
958 		{
959 			if (process_list[i]->refnum_desc && !my_stricmp(process_list[i]->refnum_desc, desc))
960 				return process_list[i]->refnum;
961 			else if (process_list[i]->logical_desc && !my_stricmp(process_list[i]->logical_desc, desc))
962 				return process_list[i]->refnum;
963 		}
964 	}
965 
966 	return 0;
967 }
968 
get_process_by_description(const char * desc)969 static  Process *       get_process_by_description (const char *desc)
970 {
971        	int	i;
972 
973 	if (!desc || *desc != '%')
974 		return NULL;
975 
976 	for (i = 0; i < process_list_size; i++)
977 	{
978 		if (process_list[i])
979 		{
980 			if (process_list[i]->refnum_desc && !my_stricmp(process_list[i]->refnum_desc, desc))
981 				return process_list[i];
982 			else if (process_list[i]->logical_desc && !my_stricmp(process_list[i]->logical_desc, desc))
983 				return process_list[i];
984 		}
985 	}
986 	return NULL;
987 }
988 
989 
990 /*
991  * is_valid_process: convert a %procdesc into a process index.
992  * running or still has not closed its pipes, or both.
993  */
is_valid_process(const char * desc)994 int	is_valid_process (const char *desc)
995 {
996 	if (get_process_refnum(desc))
997 		return 1;
998 	return 0;
999 }
1000 
new_process(const char * commands)1001 static Process *	new_process (const char *commands)
1002 {
1003 	Process *proc;
1004 	int	i;
1005 
1006 	proc = new_malloc(sizeof(Process));
1007 
1008 	/*
1009 	 * Init the proc list if neccesary
1010 	 */
1011 	if (!process_list)
1012 	{
1013 	    process_list = new_malloc(sizeof(Process *));
1014 	    process_list_size = 1;
1015 	    process_list[0] = NULL;
1016 	}
1017 
1018 	/*
1019 	 * Find the first empty proc entry
1020 	 */
1021 	for (i = 0; i < process_list_size; i++)
1022 	{
1023 		if (!process_list[i])
1024 		{
1025 			process_list[i] = proc;
1026 			break;
1027 		}
1028 	}
1029 
1030 	/*
1031 	 * If there are no empty proc entries, make a new one.
1032 	 */
1033 	if (i == process_list_size)
1034 	{
1035 	    process_list_size++;
1036 	    RESIZE(process_list, Process *, process_list_size);
1037 	    process_list[i] = proc;
1038 	}
1039 
1040 	proc->refnum = next_process_refnum++;
1041 	proc->refnum_desc = NULL;
1042 	malloc_sprintf(&proc->refnum_desc, "%%%d", proc->refnum);
1043 	proc->logical = NULL;
1044 	proc->logical_desc = NULL;
1045 	proc->commands = malloc_strdup(commands);
1046 	proc->direct = 0;
1047 
1048 	proc->pid = -1;
1049 	proc->p_stdin = -1;
1050 	proc->p_stdout = -1;
1051 	proc->p_stderr = -1;
1052 
1053 	proc->exited = 0;
1054 	proc->termsig = 0;
1055 	proc->retcode = 0;
1056 
1057 	proc->redirect = NULL;
1058 	proc->who = NULL;
1059 	proc->window_refnum = 0;
1060 	proc->server_refnum = -1;
1061 
1062 	proc->dumb = 0;
1063 	proc->disowned = 0;
1064 
1065 	proc->stdoutc = NULL;
1066 	proc->stdoutpc = NULL;
1067 	proc->stderrc = NULL;
1068 	proc->stderrpc = NULL;
1069 	proc->waitcmds = NULL;
1070 
1071 	get_time(&proc->started_at);
1072 	proc->lines_recvd = 0;
1073 	proc->lines_sent = 0;
1074 
1075 	return proc;
1076 }
1077 
start_process(Process * proc)1078 static int	start_process (Process *proc)
1079 {
1080 	int	p0[2], p1[2], p2[2],
1081 		pid, cnt;
1082 	const char *shell;
1083 	char	*arg;
1084 	char *	commands;
1085 
1086 	if (proc->commands == NULL)
1087 		return -1;
1088 
1089 	commands = LOCAL_COPY(proc->commands);
1090 
1091 	p0[0] = p1[0] = p2[0] = -1;
1092 	p0[1] = p1[1] = p2[1] = -1;
1093 
1094 	/*
1095 	 * Open up the communication pipes
1096 	 */
1097 	if (pipe(p0) || pipe(p1) || pipe(p2))
1098 	{
1099 		say("Unable to start new process: %s", strerror(errno));
1100 		close(p0[0]);
1101 		close(p0[1]);
1102 		close(p1[0]);
1103 		close(p1[1]);
1104 		close(p2[0]);
1105 		close(p2[1]);
1106 		return -1;
1107 	}
1108 
1109 	switch ((pid = fork()))
1110 	{
1111 	case -1:
1112 		say("Couldn't start new process!");
1113 		close(p0[0]);
1114 		close(p0[1]);
1115 		close(p1[0]);
1116 		close(p1[1]);
1117 		close(p2[0]);
1118 		close(p2[1]);
1119 		return -1;
1120 
1121 	/*
1122 	 * CHILD: set up and exec the process
1123 	 */
1124 	case 0:
1125 	{
1126 		int	i;
1127 
1128 		/*
1129 		 * Fire up a new job control session,
1130 		 * Sever all ties we had with the parent ircII process
1131 		 */
1132 		setsid();
1133 		if (setgid(getgid()))
1134 			_exit(0);
1135 		if (setuid(getuid()))
1136 			_exit(0);
1137 		my_signal(SIGINT, SIG_IGN);
1138 		my_signal(SIGQUIT, SIG_DFL);
1139 		my_signal(SIGSEGV, SIG_DFL);
1140 		my_signal(SIGBUS, SIG_DFL);
1141 
1142 		dup2(p0[0], 0);
1143 		dup2(p1[1], 1);
1144 		dup2(p2[1], 2);
1145 		for (i = 3; i < 256; i++)
1146 			close(i);
1147 
1148 		/*
1149 		 * Pretend to be just a dumb terminal
1150 		 */
1151 		setenv("TERM", "tty", 1);
1152 
1153 		/*
1154 		 * Figure out what shell (if any) we're using
1155 		 */
1156 		shell = get_string_var(SHELL_VAR);
1157 
1158 		/*
1159 		 * If we're not using a shell, doovie up the exec args
1160 		 * array and pass it off to execvp
1161 		 */
1162 		if (proc->direct || !shell)
1163 		{
1164 			int	max;
1165 			char **my_args;
1166 
1167 			cnt = 0;
1168 			max = 5;
1169 			my_args = new_malloc(sizeof(char *) * max);
1170 			while ((arg = new_next_arg(commands, &commands)))
1171 			{
1172 				my_args[cnt] = arg;
1173 
1174 				if (++cnt >= max)
1175 				{
1176 					max += 5;
1177 					RESIZE(my_args, char *, max);
1178 				}
1179 			}
1180 			my_args[cnt] = NULL;
1181 			execvp(my_args[0], my_args);
1182 
1183 			printf("*** Error running program \"%s\": %s\n",
1184 					proc->commands, strerror(errno));
1185 		}
1186 
1187 		/*
1188 		 * If we're using a shell, let them have all the fun
1189 		 */
1190 		else
1191 		{
1192 			const char *flag;
1193 
1194 			if (!(flag = get_string_var(SHELL_FLAGS_VAR)))
1195 				flag = empty_string;
1196 
1197 			execl(shell, shell, flag, proc->commands, NULL);
1198 
1199 			printf("*** Error running program \"%s %s %s\": %s\n",
1200 					shell, flag, proc->commands, strerror(errno));
1201 		}
1202 
1203 		/*
1204 		 * Something really died if we got here
1205 		 */
1206 		_exit(-1);
1207 		break;
1208 	}
1209 
1210 	default:
1211 	{
1212 		proc->pid = pid;
1213 		proc->server_refnum = from_server;
1214 		get_time(&proc->started_at);
1215 
1216 		close(p0[0]);
1217 		close(p1[1]);
1218 		close(p2[1]);
1219 		proc->p_stdin = p0[1];
1220 		proc->p_stdout = p1[0];
1221 		proc->p_stderr = p2[0];
1222 
1223 		proc->lines_recvd = 0;
1224 		proc->exited = 0;
1225 		proc->termsig = 0;
1226 		proc->retcode = 0;
1227 		proc->disowned = 0;
1228 		proc->dumb = 0;
1229 
1230 		new_open(proc->p_stdout, do_exec, NEWIO_READ, 1, proc->server_refnum);
1231 		new_open(proc->p_stderr, do_exec, NEWIO_READ, 1, proc->server_refnum);
1232 		break;
1233 	}
1234 	}
1235 
1236 	return 0;
1237 }
1238 /********************************/
1239 /*
1240  * execcmd: The /EXEC command - run sub processes underneath the client.
1241  *
1242  * The unified general syntax of /EXEC:
1243  *	/EXEC [-flag [param]]* commands
1244  *	/EXEC [-flag [param]]* %proc extra-args
1245  *	/EXEC [-flag [param]]* (commands) extra-args
1246  * Double quotes ar enot supported anywhere.
1247  *
1248  * Each /EXEC command must have a "Process Context"
1249  *    1.) A %proc of an already running process
1250  *    2.) A parenthesis-surrounded command line to run
1251  *    3.) A command line to run
1252  *
1253  * For backwards compatability, it was never possible to specify
1254  * both commands and flags that required "extra args" (such as -IN).
1255  * To allow you to specify both commands and extra-args, you may
1256  * surround the commands with parenthesis.
1257  *
1258  * You can string together as many -flag's as you want, and they will be
1259  * evaluated in the order you provide.
1260  *
1261  * Here is the canonical list of /EXEC flags:
1262  *	-CLOSE
1263  *		Close the process's stdin, stdout, and stderr (ie, EOF)
1264  * 		Many programs will shut down cleanly when they get an EOF on stdin
1265  *		No further output from the program will be received
1266  *	-CLOSEIN
1267  *		Close the process's stdin (ie, EOF)
1268  *	-CLOSEOUT
1269  *		Close the process's stdout and stderr (ie, EOF)
1270  *		No further output from the program will be received
1271  *	-IN (requires extra-args)
1272  *		Send a message to a process.
1273  *		This is equivalent to /MSG %proc extra-args
1274  *
1275  *	-SIGNAL number
1276  *	-SIGNAL signame
1277  *	-number
1278  *	-signame
1279  *		Send a signal to the process ("kill").
1280  *		The signal may either be a number of its short name (ie, HUP)
1281  *
1282  *	-NAME local-name
1283  *		Set the process's logical name (ie, for %procname)
1284  *	-OUT
1285  *		Directs all output from the process to the current window's current channel.
1286  *		If you change the current channel, it will keep sending to the original
1287  *		   current channel.
1288  *	-WINDOW
1289  *		Directs all output from the process to be displayed in the current window.
1290  *		Normally, output from the process goes to the OTHER window level.
1291  *	-WINTARGET windesc
1292  *		Directs all output from the process to be displayed in the specified window
1293  *		"windesc" can be either a window refnum or window name.
1294  *		This is handy if you are using a centralized window for /EXEC output.
1295  *	-MSG target
1296  *		Directs all output from the process to be sent to the "target"
1297  *		Target is anything you can send a message to: a nick or channel on irc,
1298  *		a DCC Chat, a window, a logfile, or even another exec process.
1299  *		If sent over IRC, it will be sent as a PRIVMSG.
1300  *	-NOTICE target
1301  *		Directs all output from the process to be sent to the "target"
1302  *		If sent over IRC, it will be sent as a NOTICE.
1303  *
1304  *	-LINE {block}
1305  *		Run {block} for each complete line of stdout from the process as $*
1306  *		The curly braces are required
1307  *	-LINEPART {block}
1308  *		Run {block} for each incomplete line of stdout from the process as $*
1309  *		"Incomplete line" means it didn't end with a newline
1310  *		The curly braces are required
1311  *	-ERROR {block}
1312  *		Run {block} for each complete line of stderr from the process as $*
1313  *		The curly braces are required
1314  *	-ERRORPART {block}
1315  *		Run {block} for each incomplete line of stdout from the process as $*
1316  *		"Incomplete line" means it didn't end with a newline
1317  *		The curly braces are required
1318  *	-END {block}
1319  *		Run {block} when the process exits.
1320  *		This does the same thing as /WAIT %proc -CMD {code}
1321  *		The curly braces are required
1322  *
1323  *	-DIRECT
1324  *		Run the command directly with execvp(), do not use a shell
1325  *		-> double quoted words are honored and the double quotes are removed
1326  *		-> everything else is treated literally
1327  *	-LIMIT
1328  *		Limit the number of lines to receive from the process before doing an implicit -CLOSE
1329  *		This overrules /SET SHELL_LIMIT for this process only.
1330  *		The value 0 means "unlimited"
1331  *
1332  * Ordinarily (unless you use -DIRECT) your commands are run by a shell, which allows you to use
1333  * filename wildcard matching, shell variable expansion, etc.  The /SET SHELL (default: "/bin/sh")
1334  * value is the shell that will be used, and /SET SHELL_FLAGS (default: "-c") is the shell's flag
1335  * to tell it that the rest of the arguments are commands to run.
1336  *
1337  * It gets run something like this:
1338  *		/bin/sh -c you_commands_here
1339  */
1340 
execcmd_push_arg(char ** arg_list,size_t arg_list_size,int arg_list_idx,char * flag)1341 static void	execcmd_push_arg (char **arg_list, size_t arg_list_size, int arg_list_idx, char *flag)
1342 {
1343 	if (arg_list_idx >= 0 && (unsigned long)arg_list_idx < arg_list_size - 1)
1344 	{
1345 		/* yell("Adding [%d] [%s] argument to list", arg_list_idx, flag); */
1346 		arg_list[arg_list_idx] = flag;
1347 	}
1348 	else
1349 		/* yell("Not adding [%s] argument to list - overflow", flag) */ (void) 0;
1350 }
1351 
execcmd_tokenize_arguments(const char * args,Process ** process,char ** free_ptr,char ** extra_args,int * numargs,int * flags_size)1352 static char **	execcmd_tokenize_arguments (const char *args, Process **process, char **free_ptr, char **extra_args, int *numargs, int *flags_size)
1353 {
1354 	char **	arg_list;
1355 	int	arg_list_size;
1356 	int	arg_list_idx;
1357 	char *	args_copy;
1358 	char *	flag;
1359 	size_t	len;
1360 	Process	*proc = NULL;
1361 
1362 	args_copy = malloc_strdup(args);
1363 	*free_ptr = args_copy;
1364 
1365 	/* XXX hardcoding 64 here is bogus */
1366 	arg_list_size = 64;
1367 	arg_list = (char **)new_malloc(sizeof(char *) * arg_list_size);
1368 	arg_list_idx = 0;
1369 
1370 	while ((args_copy && *args_copy == '-')  && (flag = next_arg(args_copy, &args_copy)))
1371 	{
1372 		len = strlen(flag);
1373 
1374 		if (my_strnicmp(flag, "-CLOSE", len) == 0)
1375 			execcmd_push_arg(arg_list, arg_list_size, arg_list_idx++, flag);
1376 		else if (my_strnicmp(flag, "-CLOSEIN", len) == 0)
1377 			execcmd_push_arg(arg_list, arg_list_size, arg_list_idx++, flag);
1378 		else if (my_strnicmp(flag, "-CLOSEOUT", len) == 0)
1379 			execcmd_push_arg(arg_list, arg_list_size, arg_list_idx++, flag);
1380 		else if (my_strnicmp(flag, "-IN", len) == 0)
1381 			execcmd_push_arg(arg_list, arg_list_size, arg_list_idx++, flag);
1382 		else if (my_strnicmp(flag, "-NAME", len) == 0)
1383 		{
1384 			char *arg = next_arg(args_copy, &args_copy);
1385 			execcmd_push_arg(arg_list, arg_list_size, arg_list_idx++, flag);
1386 			execcmd_push_arg(arg_list, arg_list_size, arg_list_idx++, arg);
1387 		}
1388 		else if (my_strnicmp(flag, "-OUT", len) == 0)
1389 			execcmd_push_arg(arg_list, arg_list_size, arg_list_idx++, flag);
1390 		else if (my_strnicmp(flag, "-WINDOW", len) == 0)
1391 			execcmd_push_arg(arg_list, arg_list_size, arg_list_idx++, flag);
1392 		else if (my_strnicmp(flag, "-WINTARGET", len) == 0)
1393 		{
1394 			char *arg = next_arg(args_copy, &args_copy);
1395 			if (arg && *arg)
1396 			{
1397 				execcmd_push_arg(arg_list, arg_list_size, arg_list_idx++, flag);
1398 				execcmd_push_arg(arg_list, arg_list_size, arg_list_idx++, arg);
1399 			}
1400 			else
1401 				say("EXEC: Error: -WINTARGET requires an argument");
1402 		}
1403 		else if (my_strnicmp(flag, "-MSG", len) == 0)
1404 		{
1405 			char *arg = next_arg(args_copy, &args_copy);
1406 			if (arg && *arg)
1407 			{
1408 				execcmd_push_arg(arg_list, arg_list_size, arg_list_idx++, flag);
1409 				execcmd_push_arg(arg_list, arg_list_size, arg_list_idx++, arg);
1410 			}
1411 			else
1412 				say("EXEC: Error: -MSG requires a nick or channel argument");
1413 		}
1414 		else if (my_strnicmp(flag, "-NOTICE", len) == 0)
1415 		{
1416 			char *arg = next_arg(args_copy, &args_copy);
1417 			if (arg && *arg)
1418 			{
1419 				execcmd_push_arg(arg_list, arg_list_size, arg_list_idx++, flag);
1420 				execcmd_push_arg(arg_list, arg_list_size, arg_list_idx++, arg);
1421 			}
1422 			else
1423 				say("EXEC: Error: -MSG requires a nick or channel argument");
1424 		}
1425 		else if (my_strnicmp(flag, "-LINE", len) == 0)
1426 		{
1427 			char *arg = next_expr(&args_copy, '{');
1428 			if (arg && *arg)
1429 			{
1430 				execcmd_push_arg(arg_list, arg_list_size, arg_list_idx++, flag);
1431 				execcmd_push_arg(arg_list, arg_list_size, arg_list_idx++, arg);
1432 			}
1433 			else
1434 				say("EXEC: Error: Need {...} argument for -LINE flag");
1435 		}
1436 		else if (my_strnicmp(flag, "-LINEPART", len) == 0)
1437 		{
1438 			char *arg = next_expr(&args_copy, '{');
1439 			if (arg && *arg)
1440 			{
1441 				execcmd_push_arg(arg_list, arg_list_size, arg_list_idx++, flag);
1442 				execcmd_push_arg(arg_list, arg_list_size, arg_list_idx++, arg);
1443 			}
1444 			else
1445 				say("EXEC: Error: Need {...} argument for -ERRORPART flag");
1446 		}
1447 		else if (my_strnicmp(flag, "-ERROR", len) == 0)
1448 		{
1449 			char *arg = next_expr(&args_copy, '{');
1450 			if (arg && *arg)
1451 			{
1452 				execcmd_push_arg(arg_list, arg_list_size, arg_list_idx++, flag);
1453 				execcmd_push_arg(arg_list, arg_list_size, arg_list_idx++, arg);
1454 			}
1455 			else
1456 				say("EXEC: Error: Need {...} argument for -ERROR flag");
1457 		}
1458 		else if (my_strnicmp(flag, "-ERRORPART", len) == 0)
1459 		{
1460 			char *arg = next_expr(&args_copy, '{');
1461 			if (arg && *arg)
1462 			{
1463 				execcmd_push_arg(arg_list, arg_list_size, arg_list_idx++, flag);
1464 				execcmd_push_arg(arg_list, arg_list_size, arg_list_idx++, arg);
1465 			}
1466 			else
1467 				say("EXEC: Error: Need {...} argument for -ERRORPART flag");
1468 		}
1469 		else if (my_strnicmp(flag, "-END", len) == 0)
1470 		{
1471 			char *arg = next_expr(&args_copy, '{');
1472 			if (arg && *arg)
1473 			{
1474 				execcmd_push_arg(arg_list, arg_list_size, arg_list_idx++, flag);
1475 				execcmd_push_arg(arg_list, arg_list_size, arg_list_idx++, arg);
1476 			}
1477 		}
1478 		else if (my_strnicmp(flag, "-DIRECT", len) == 0)
1479 			execcmd_push_arg(arg_list, arg_list_size, arg_list_idx++, flag);
1480 		else if (my_strnicmp(flag, "-LIMIT", len) == 0)
1481 		{
1482 			char *arg = next_arg(args_copy, &args_copy);
1483 			if (arg && *arg)
1484 			{
1485 				execcmd_push_arg(arg_list, arg_list_size, arg_list_idx++, flag);
1486 				execcmd_push_arg(arg_list, arg_list_size, arg_list_idx++, arg);
1487 			}
1488 		}
1489 		else if (my_strnicmp(flag, "-NOLAUNCH", len) == 0)
1490 			execcmd_push_arg(arg_list, arg_list_size, arg_list_idx++, flag);
1491 
1492 		else if (my_atol(flag + 1) > 0)
1493 		{
1494 			execcmd_push_arg(arg_list, arg_list_size, arg_list_idx++, "-SIGNAL");
1495 			execcmd_push_arg(arg_list, arg_list_size, arg_list_idx++, flag + 1);
1496 		}
1497 		else if (get_signal_by_name(flag + 1) > 0)
1498 		{
1499 			execcmd_push_arg(arg_list, arg_list_size, arg_list_idx++, "-SIGNAL");
1500 			execcmd_push_arg(arg_list, arg_list_size, arg_list_idx++, flag + 1);
1501 		}
1502 		else
1503 			continue;		/* Do we handle errors, or not? */
1504 	}
1505 
1506 	execcmd_push_arg(arg_list, arg_list_size, arg_list_idx, NULL);
1507 
1508 	if (args_copy && *args_copy)
1509 	{
1510 		if (*args_copy == '%')
1511 		{
1512 			const char *proc_desc;
1513 			int	refnum;
1514 
1515 			/* yell("Looking up existing process %s", args_copy); */
1516 			proc_desc = next_arg(args_copy, &args_copy);
1517 			refnum = get_process_refnum(proc_desc);
1518 
1519 			if (!(proc = get_process_by_refnum(refnum)))
1520 			{
1521 				say("EXEC: Error: The process %s is not a valid process", proc_desc);
1522 				return NULL;
1523 			}
1524 		}
1525 		else if (*args_copy == '(')
1526 		{
1527 			const char *cmds = next_expr(&args_copy, '(');
1528 			/* yell("STarting up new process from parethensis"); */
1529 			proc = new_process(cmds);
1530 		}
1531 		else
1532 		{
1533 			/* yell("Starting up new naked process from [%s]", args_copy); */
1534 			proc = new_process(args_copy);
1535 			args_copy = NULL;
1536 		}
1537 	}
1538 
1539 	if (proc == NULL)
1540 	{
1541 		say("EXEC: Error: After all flags, there should be either a %%proc or commands to run");
1542 		return NULL;
1543 	}
1544 
1545 	*process = proc;
1546 	*extra_args = args_copy;
1547 	*numargs = arg_list_idx;
1548 	*flags_size = arg_list_size;
1549 	return arg_list;
1550 }
1551 
BUILT_IN_COMMAND(execcmd)1552 BUILT_IN_COMMAND(execcmd)
1553 {
1554 	char *	free_ptr;
1555 	Process *process;
1556 	char **	flags;
1557 	int	flags_size;
1558 	char *	extra_args;
1559 	int	numargs;
1560 	int	i, l;
1561 	int	launch = 1;
1562 
1563 	if (!args || !*args)
1564 	{
1565 		execcmd_list_processes();
1566 		return;
1567 	}
1568 
1569 	if (!(flags = execcmd_tokenize_arguments(args, &process, &free_ptr, &extra_args, &numargs, &flags_size)))
1570 		return;
1571 
1572 	l = set_message_from_for_process(process);
1573 
1574 	for (i = 0; i < numargs; i++)
1575 	{
1576 		const char *flag;
1577 		size_t	len;
1578 
1579 		flag = flags[i];
1580 		len = strlen(flag);
1581 
1582 		if (my_strnicmp(flag, "-CLOSE", len) == 0)
1583 			ignore_process(process);
1584 		else if (my_strnicmp(flag, "-CLOSEIN", len) == 0)
1585 			exec_close_in(process);
1586 		else if (my_strnicmp(flag, "-CLOSEOUT", len) == 0)
1587 			exec_close_out(process);
1588 		else if (my_strnicmp(flag, "-IN", len) == 0)
1589 			text_to_process(process->logical_desc, extra_args, 1);
1590 
1591 		else if (my_strnicmp(flag, "-NAME", len) == 0)
1592 		{
1593 			char *new_name = flags[++i];
1594 
1595 			if (is_logical_unique(new_name))
1596 			{
1597 				malloc_strcpy(&process->logical, new_name);
1598 				malloc_sprintf(&process->logical_desc, "%%%s", new_name);
1599 				say("Process %d (%s) is now called %s",
1600 					process->refnum, process->commands, process->logical);
1601 			}
1602 			else
1603 				say("The name %s is not unique!", new_name);
1604 		}
1605 		else if (my_strnicmp(flag, "-OUT", len) == 0)
1606 		{
1607 			const char *who;
1608 
1609 			if (!(who = get_target_by_refnum(0)))
1610 				say("No query or channel in this window for -OUT");
1611 			else
1612 			{
1613 				malloc_strcpy(&process->who, who);
1614 				malloc_strcpy(&process->redirect, "PRIVMSG");
1615 			}
1616 
1617 		}
1618 		else if (my_strnicmp(flag, "-WINDOW", len) == 0)
1619 		{
1620 			pop_message_from(l);
1621 			l = set_message_from_for_process(process);
1622 			process->window_refnum = current_refnum();
1623 			say("Output from process %d (%s) now going to window %d",
1624 					process->refnum, process->commands, process->window_refnum);
1625 		}
1626 		else if (my_strnicmp(flag, "-WINTARGET", len) == 0)
1627 		{
1628 			const char *desc = flags[++i];
1629 			Window *w;
1630 
1631 			if (!desc || !(w = get_window_by_desc(desc)))
1632 				say("Target window not found");
1633 			else
1634 			{
1635 				process->window_refnum = w->refnum;
1636 				pop_message_from(l);
1637 				l = set_message_from_for_process(process);
1638 				say("Output from process %d (%s) now going to window %d",
1639 					process->refnum, process->commands, process->window_refnum);
1640 			}
1641 		}
1642 		else if (my_strnicmp(flag, "-MSG", len) == 0)
1643 		{
1644 			const char *arg = flags[++i];
1645 
1646 			if (arg && *arg)
1647 			{
1648 				malloc_strcpy(&process->redirect, "PRIVMSG");
1649 				malloc_strcpy(&process->who, arg);
1650 			}
1651 			else
1652 				say("EXEC: Error: -MSG requires a nick or channel argument");
1653 		}
1654 		else if (my_strnicmp(flag, "-NOTICE", len) == 0)
1655 		{
1656 			const char *arg = flags[++i];
1657 
1658 			if (arg && *arg)
1659 			{
1660 				malloc_strcpy(&process->redirect, "NOTICE");
1661 				malloc_strcpy(&process->who, arg);
1662 			}
1663 			else
1664 				say("EXEC: Error: -NOTICE requires a nick or channel argument");
1665 		}
1666 
1667 		else if (my_strnicmp(flag, "-LINE", len) == 0)
1668 		{
1669 			const char *arg = flags[++i];
1670 
1671 			if (arg && *arg)
1672 				malloc_strcpy(&process->stdoutc, arg);
1673 			else
1674 				say("EXEC: Error: Need {...} argument for -LINE flag");
1675 		}
1676 		else if (my_strnicmp(flag, "-LINEPART", len) == 0)
1677 		{
1678 			const char *arg = flags[++i];
1679 
1680 			if (arg && *arg)
1681 				malloc_strcpy(&process->stdoutpc, arg);
1682 			else
1683 				say("EXEC: Error: Need {...} argument for -LINEPART flag");
1684 		}
1685 		else if (my_strnicmp(flag, "-ERROR", len) == 0)
1686 		{
1687 			const char *arg = flags[++i];
1688 
1689 			if (arg && *arg)
1690 				malloc_strcpy(&process->stderrc, arg);
1691 			else
1692 				say("EXEC: Error: Need {...} argument for -ERROR flag");
1693 		}
1694 		else if (my_strnicmp(flag, "-ERRORPART", len) == 0)
1695 		{
1696 			const char *arg = flags[++i];
1697 
1698 			if (arg && *arg)
1699 				malloc_strcpy(&process->stderrpc, arg);
1700 			else
1701 				say("EXEC: Error: Need {...} argument for -ERRORPART flag");
1702 		}
1703 		else if (my_strnicmp(flag, "-END", len) == 0)
1704 		{
1705 			const char *arg = flags[++i];
1706 
1707 			if (arg && *arg)
1708 				add_process_wait(process->refnum_desc, arg);
1709 			else
1710 				say("EXEC: Error: Need {...} argument for -END flag");
1711 		}
1712 		else if (my_strnicmp(flag, "-DIRECT", len) == 0)
1713 			process->direct = 1;
1714 		else if (my_strnicmp(flag, "-LIMIT", len) == 0)
1715 		{
1716 			const char *arg = flags[++i];
1717 			if (is_number(arg))
1718 				process->lines_limit = my_atol(arg);
1719 		}
1720 		else if (my_strnicmp(flag, "-NOLAUNCH", len) == 0)
1721 			launch = 0;
1722 
1723 		else if (my_strnicmp(flag, "-SIGNAL", len) == 0)
1724 		{
1725 			const char *arg = flags[++i];
1726 			int	signum;
1727 
1728 			if (is_number(arg))
1729 				signum = my_atol(arg);
1730 			else
1731 				signum = get_signal_by_name(arg);
1732 
1733 			if (signum > 0 && signum < NSIG)
1734 				kill_process(process, signum);
1735 			else
1736 				say("No such signal: %s", arg);
1737 		}
1738 
1739 		else
1740 			continue;
1741 	}
1742 
1743 	if (process->pid == -1 && launch)
1744 		start_process(process);
1745 
1746 	pop_message_from(l);
1747 
1748 	new_free((char **)&flags);
1749 	new_free((char **)&free_ptr);
1750 }
1751 
1752 /*
1753  * Here's the plan:
1754  *
1755  * $execctl(REFNUM <description>)
1756  * $execctl(REFNUMS)
1757  * $execctl(NEW <commands>)
1758  * $execctl(LAUNCH <refnum>)
1759  * $execctl(CLOSEIN <refnum>)
1760  * $execctl(CLOSEOUT <refnum>)
1761  * $execctl(SIGNAL <signal> %<refnum>)
1762  *
1763  * $execctl(GET <refnum> [FIELD])
1764  * $execclt(SET <refnum> [FIELD] [VALUE])
1765  *
1766  * [FIELD] is one of the following:
1767  *	REFNUM		The process's unique integer refnum
1768  *	REFNUM_DESC	The user-exposed version of REFNUM (ie, prefixed with %)
1769  *	LOGICAL		The process's logical name (provided by user)
1770  *	LOGICAL_DESC	LOGICAL, prefixed with a %
1771  *	COMMANDS	The commands htat will be run. -- (* cannot be SET after launch)
1772  *	DIRECT		Whether to run <commands> in a shell -- (*)
1773  *	REDIRECT	Either PRIVMSG or NOTICE
1774  *	WHO		Who to send all output from process to
1775  *	WINDOW_REFNUM	Which window to send all exec output to
1776  *	SERVER_REFNUM	Which server to use for redirects
1777  *	STARTED_AT	The time when the process was created or launched
1778  *	STDOUTC		Code to execute when a complete line of stdout happens
1779  *	STDOUTPC	Code to execute when an incomplete line of stdout happens
1780  *	STDERRC		Code to execute when a complete line of stderr happens
1781  *	STDERRPC	Code to execute when an incomplete line of stderr happens
1782  *
1783  * All of the following [LIST] values may be GET but not SET.
1784  * I make absolutely no guarantees these things won't change. use at your own risk.
1785  *
1786  *	PID		The process's PID, if it has been launched -- (** Cannot be SET)
1787  *	P_STDIN		The fd attached to the process's stdin (**)
1788  *	P_STDOUT	The fd attached to the process's stdout (**)
1789  *	P_STDERR	The fd attached to the process's stdout (**)
1790  *	EXITED		1 if the process has exited, 0 if not yet (**)
1791  *	TERMSIG		If the process died of a signal, what signal killed it
1792  *	RETCODE		If the process exited cleanly, what its exit code was
1793  *	DUMB		0 if we're handling output from process, 1 if we're ignoring it
1794  *	LINES_RECVD
1795  *	LINES_SENT
1796  *	LINES_LIMIT
1797  *	DISOWNED	0 - not implemented
1798  *	WAITCMDS	empty-string - not implemented
1799  */
execctl(char * input)1800 char *  execctl (char *input)
1801 {
1802         char *  listc;
1803         int     len;
1804 	int	refnum;
1805 	char *	field;
1806 	Process *proc;
1807 	char *	retval = NULL;
1808 	size_t	clue = 0;
1809 
1810         GET_FUNC_ARG(listc, input);
1811         len = strlen(listc);
1812         if (!my_strnicmp(listc, "REFNUM", len)) {
1813 		RETURN_INT(get_process_refnum(input));
1814         } else if (!my_strnicmp(listc, "REFNUMS", len)) {
1815 		int	i;
1816 
1817 		for (i = 0; i < process_list_size; i++)
1818 			if ((proc = process_list[i]))
1819 				malloc_strcat_word_c(&retval, space, proc->refnum_desc, DWORD_NO, &clue);
1820 		RETURN_MSTR(retval);
1821         } else if (!my_strnicmp(listc, "NEW", len)) {
1822 		proc = new_process(input);
1823 		RETURN_INT(proc->refnum);
1824         } else if (!my_strnicmp(listc, "LAUNCH", len)) {
1825 		GET_INT_ARG(refnum, input);
1826 		if (!(proc = get_process_by_refnum(refnum)))
1827 			RETURN_EMPTY;
1828 
1829 		if (proc->pid == -1)
1830 			start_process(proc);
1831         } else if (!my_strnicmp(listc, "CLOSEIN", len)) {
1832 		GET_INT_ARG(refnum, input);
1833 		if (!(proc = get_process_by_refnum(refnum)))
1834 			RETURN_EMPTY;
1835 
1836 		exec_close_in(proc);
1837         } else if (!my_strnicmp(listc, "CLOSEOUT", len)) {
1838 		GET_INT_ARG(refnum, input);
1839 		if (!(proc = get_process_by_refnum(refnum)))
1840 			RETURN_EMPTY;
1841 
1842 		exec_close_out(proc);
1843         } else if (!my_strnicmp(listc, "SIGNAL", len)) {
1844 		int	signum;
1845 
1846 		GET_INT_ARG(refnum, input);
1847 		if (!(proc = get_process_by_refnum(refnum)))
1848 			RETURN_EMPTY;
1849 
1850 		if (is_number(input))
1851 			signum = my_atol(input);
1852 		else
1853 			signum = get_signal_by_name(input);
1854 
1855 		if (signum > 0 && signum < NSIG)
1856 		{
1857 			kill_process(proc, signum);
1858 			RETURN_INT(1);
1859 		}
1860 		RETURN_INT(0);
1861         } else if (!my_strnicmp(listc, "GET", len)) {
1862 		GET_INT_ARG(refnum, input);
1863 		GET_FUNC_ARG(field, input);
1864 
1865 		if (!(proc = get_process_by_refnum(refnum)))
1866 			RETURN_EMPTY;
1867 
1868 		if (!my_stricmp(field, "REFNUM")) {
1869 			RETURN_INT(proc->refnum);
1870 		} else if (!my_stricmp(field, "REFNUM_DESC")) {
1871 			RETURN_STR(proc->refnum_desc);
1872 		} else if (!my_stricmp(field, "LOGICAL")) {
1873 			RETURN_STR(proc->logical);
1874 		} else if (!my_stricmp(field, "LOGICAL_DESC")) {
1875 			RETURN_STR(proc->logical_desc);
1876 		} else if (!my_stricmp(field, "COMMANDS")) {
1877 			RETURN_STR(proc->commands);
1878 		} else if (!my_stricmp(field, "DIRECT")) {
1879 			RETURN_INT(proc->direct);
1880 		} else if (!my_stricmp(field, "REDIRECT")) {
1881 			RETURN_STR(proc->redirect);
1882 		} else if (!my_stricmp(field, "WHO")) {
1883 			RETURN_STR(proc->who);
1884 		} else if (!my_stricmp(field, "WINDOW_REFNUM")) {
1885 			RETURN_INT(proc->window_refnum);
1886 		} else if (!my_stricmp(field, "SERVER_REFNUM")) {
1887 			RETURN_INT(proc->server_refnum);
1888 		} else if (!my_stricmp(field, "LINES_LIMIT")) {
1889 			RETURN_INT(proc->lines_limit);
1890 		} else if (!my_stricmp(field, "STDOUTC")) {
1891 			RETURN_STR(proc->stdoutc);
1892 		} else if (!my_stricmp(field, "STDOUTPC")) {
1893 			RETURN_STR(proc->stdoutpc);
1894 		} else if (!my_stricmp(field, "STDERRC")) {
1895 			RETURN_STR(proc->stderrc);
1896 		} else if (!my_stricmp(field, "STDERRPC")) {
1897 			RETURN_STR(proc->stderrpc);
1898 		} else if (!my_stricmp(field, "PID")) {
1899 			RETURN_INT(proc->pid);
1900 		} else if (!my_stricmp(field, "STARTED_AT")) {
1901                         return malloc_sprintf(&retval, "%ld %ld",
1902                                 (long) proc->started_at.tv_sec,
1903                                 (long) proc->started_at.tv_usec);
1904 		} else if (!my_stricmp(field, "P_STDIN")) {
1905 			RETURN_INT(proc->p_stdin);
1906 		} else if (!my_stricmp(field, "P_STDOUT")) {
1907 			RETURN_INT(proc->p_stdout);
1908 		} else if (!my_stricmp(field, "P_STDERR")) {
1909 			RETURN_INT(proc->p_stderr);
1910 		} else if (!my_stricmp(field, "LINES_RECVD")) {
1911 			RETURN_INT(proc->lines_recvd);
1912 		} else if (!my_stricmp(field, "LINES_SENT")) {
1913 			RETURN_INT(proc->lines_sent);
1914 		} else if (!my_stricmp(field, "EXITED")) {
1915 			RETURN_INT(proc->exited);
1916 		} else if (!my_stricmp(field, "TERMSIG")) {
1917 			RETURN_INT(proc->termsig);
1918 		} else if (!my_stricmp(field, "RETCODE")) {
1919 			RETURN_INT(proc->retcode);
1920 		} else if (!my_stricmp(field, "DUMB")) {
1921 			RETURN_INT(proc->dumb);
1922 		} else if (!my_stricmp(field, "DISOWNED")) {
1923 			RETURN_INT(proc->disowned);
1924 		}
1925 
1926         } else if (!my_strnicmp(listc, "SET", len)) {
1927 		GET_INT_ARG(refnum, input);
1928 		GET_FUNC_ARG(field, input);
1929 
1930 		if (!(proc = get_process_by_refnum(refnum)))
1931 			RETURN_EMPTY;
1932 
1933 		if (!my_stricmp(field, "LOGICAL")) {
1934 			char *new_name;
1935 			GET_FUNC_ARG(new_name, input);
1936 
1937 			if (is_logical_unique(new_name))
1938 			{
1939 				malloc_strcpy(&proc->logical, new_name);
1940 				malloc_sprintf(&proc->logical_desc, "%%%s", new_name);
1941 				RETURN_INT(1);
1942 			}
1943 			else
1944 				RETURN_INT(0);
1945 		} else if (!my_stricmp(field, "COMMANDS")) {
1946 			if (proc->pid != -1)
1947 				RETURN_INT(0);
1948 			malloc_strcpy(&proc->commands, input);
1949 			RETURN_INT(1);
1950 		} else if (!my_stricmp(field, "DIRECT")) {
1951 			int	newval;
1952 
1953 			GET_INT_ARG(newval, input);
1954 			if (proc->pid != -1)
1955 				RETURN_INT(0);
1956 			if (newval == 0 || newval == 1)
1957 			{
1958 				proc->direct = newval;
1959 				RETURN_INT(1);
1960 			}
1961 			RETURN_INT(0);
1962 		} else if (!my_stricmp(field, "REDIRECT")) {
1963 			char *	newval;
1964 
1965 			GET_FUNC_ARG(newval, input);
1966 			if (!strcmp(newval, "PRIVMSG") || !strcmp(newval, "NOTICE"))
1967 			{
1968 				malloc_strcpy(&proc->redirect, newval);
1969 				RETURN_INT(1);
1970 			}
1971 			RETURN_INT(0);
1972 		} else if (!my_stricmp(field, "WHO")) {
1973 			char *	newval;
1974 
1975 			GET_FUNC_ARG(newval, input);
1976 			malloc_strcpy(&proc->who, newval);
1977 			RETURN_INT(1);
1978 		} else if (!my_stricmp(field, "WINDOW_REFNUM")) {
1979 			int	newval;
1980 
1981 			GET_INT_ARG(newval, input);
1982 			if (newval > 0)
1983 			{
1984 				proc->window_refnum = newval;
1985 				RETURN_INT(1);
1986 			}
1987 			RETURN_INT(0);
1988 		} else if (!my_stricmp(field, "SERVER_REFNUM")) {
1989 			int	newval;
1990 
1991 			GET_INT_ARG(newval, input);
1992 			if (newval >= 0)
1993 			{
1994 				proc->server_refnum = newval;
1995 				RETURN_INT(1);
1996 			}
1997 			RETURN_INT(0);
1998 		} else if (!my_stricmp(field, "STDOUTC")) {
1999 			malloc_strcpy(&proc->stdoutc, input);
2000 			RETURN_INT(1);
2001 		} else if (!my_stricmp(field, "STDOUTPC")) {
2002 			malloc_strcpy(&proc->stdoutpc, input);
2003 			RETURN_INT(1);
2004 		} else if (!my_stricmp(field, "STDERRC")) {
2005 			malloc_strcpy(&proc->stderrc, input);
2006 			RETURN_INT(1);
2007 		} else if (!my_stricmp(field, "STDERRPC")) {
2008 			malloc_strcpy(&proc->stderrpc, input);
2009 			RETURN_INT(1);
2010 		} else if (!my_stricmp(field, "LINES_LIMIT")) {
2011 			int	newval;
2012 
2013 			GET_INT_ARG(newval, input);
2014 			if (newval >= 0)
2015 			{
2016 				proc->lines_limit = newval;
2017 				RETURN_INT(1);
2018 			}
2019 			RETURN_INT(0);
2020 		}
2021 	}
2022 
2023 	RETURN_EMPTY;
2024 }
2025 
2026 #endif
2027 
2028