1 /* exec.c --- executes a program in *this* pid, without an intervening process.
2 * xscreensaver, Copyright (c) 1991-2002 Jamie Zawinski <jwz@jwz.org>
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation. No representations are made about the suitability of this
9 * software for any purpose. It is provided "as is" without express or
14 /* I don't believe what a sorry excuse for an operating system UNIX is!
16 - I want to spawn a process.
17 - I want to know it's pid so that I can kill it.
18 - I would like to receive a message when it dies of natural causes.
19 - I want the spawned process to have user-specified arguments.
21 If shell metacharacters are present (wildcards, backquotes, etc), the
22 only way to parse those arguments is to run a shell to do the parsing
25 And the only way to know the pid of the process is to fork() and exec()
26 it in the spawned side of the fork.
28 But if you're running a shell to parse your arguments, this gives you
29 the pid of the *shell*, not the pid of the *process* that you're
30 actually interested in, which is an *inferior* of the shell. This also
31 means that the SIGCHLD you get applies to the shell, not its inferior.
32 (Why isn't that sufficient? I don't remember any more, but it turns
35 So, the only solution, when metacharacters are present, is to force the
36 shell to exec() its inferior. What a fucking hack! We prepend "exec "
37 to the command string, and hope it doesn't contain unquoted semicolons
38 or ampersands (we don't search for them, because we don't want to
39 prohibit their use in quoted strings (messages, for example) and parsing
40 out the various quote characters is too much of a pain.)
42 (Actually, Clint Wong <clint@jts.com> points out that process groups
43 might be used to take care of this problem; this may be worth considering
44 some day, except that, 1: this code works now, so why fix it, and 2: from
45 what I've seen in Emacs, dealing with process groups isn't especially
66 #if defined(HAVE_SETPRIORITY) && defined(PRIO_PROCESS)
67 # include <sys/resource.h> /* for setpriority() and PRIO_PROCESS */
68 /* and also setrlimit() and RLIMIT_AS */
72 # include <processes.h>
73 # include <unixio.h> /* for close */
74 # include <unixlib.h> /* for getpid */
80 extern const char *blurb (void);
82 static void nice_process (int nice_level);
88 exec_simple_command (const char *command)
92 char *token = strtok (strdup(command), " \t");
96 token = strtok(0, " \t");
100 execvp (av[0], av); /* shouldn't return. */
105 exec_complex_command (const char *shell, const char *command)
109 char *command2 = (char *) malloc (strlen (command) + 10);
112 const char *after_vars;
114 /* Skip leading whitespace.
116 while (*command == ' ' || *command == '\t')
119 /* If the string has a series of tokens with "=" in them at them, set
120 `after_vars' to point into the string after those tokens and any
121 trailing whitespace. Otherwise, after_vars == command.
123 after_vars = command;
124 for (s = command; *s; s++)
126 if (*s == '=') got_eq = 1;
131 while (*s == ' ' || *s == '\t')
142 strncat (command2, command, after_vars - command);
143 strcat (command2, "exec ");
144 strcat (command2, after_vars);
146 /* We have now done these transformations:
147 "foo -x -y" ==> "exec foo -x -y"
148 "BLAT=foop foo -x" ==> "BLAT=foop exec foo -x"
149 "BLAT=foop A=b foo -x" ==> "BLAT=foop A=b exec foo -x"
153 /* Invoke the shell as "/bin/sh -c 'exec prog -arg -arg ...'" */
154 av [ac++] = (char *) shell;
156 av [ac++] = command2;
159 execvp (av[0], av); /* shouldn't return. */
165 exec_vms_command (const char *command)
170 exit (1); /* Note that this only exits a child fork. */
177 exec_command (const char *shell, const char *command, int nice_level)
183 nice_process (nice_level);
185 hairy_p = !!strpbrk (command, "*?$&!<>[];`'\\\"=");
186 /* note: = is in the above because of the sh syntax "FOO=bar cmd". */
188 if (getuid() == (uid_t) 0 || geteuid() == (uid_t) 0)
190 /* If you're thinking of commenting this out, think again.
191 If you do so, you will open a security hole. Mail jwz
192 so that he may enlighten you as to the error of your ways.
194 fprintf (stderr, "%s: we're still running as root! Disaster!\n",
200 /* If it contains any shell metacharacters, do it the hard way,
201 and fork a shell to parse the arguments for us. */
202 exec_complex_command (shell, command);
204 /* Otherwise, we can just exec the program directly. */
205 exec_simple_command (command);
208 exec_vms_command (command);
213 /* Setting process priority
217 nice_process (int nice_level)
222 #if defined(HAVE_NICE)
224 int old_nice = nice (0);
225 int n = nice_level - old_nice;
227 if (nice (n) == -1 && errno != 0)
230 sprintf (buf, "%s: nice(%d) failed", blurb(), n);
234 #elif defined(HAVE_SETPRIORITY) && defined(PRIO_PROCESS)
235 if (setpriority (PRIO_PROCESS, getpid(), nice_level) != 0)
238 sprintf (buf, "%s: setpriority(PRIO_PROCESS, %lu, %d) failed",
239 blurb(), (unsigned long) getpid(), nice_level);
244 "%s: don't know how to change process priority on this system.\n",