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