1 /* exec.c --- executes a program in *this* pid, without an intervening process.
2 * xscreensaver, Copyright (c) 1991-2008 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
67 #if defined(HAVE_SETPRIORITY) && defined(PRIO_PROCESS)
68 # include <sys/resource.h> /* for setpriority() and PRIO_PROCESS */
69 /* and also setrlimit() and RLIMIT_AS */
73 # include <processes.h>
74 # include <unixio.h> /* for close */
75 # include <unixlib.h> /* for getpid */
82 extern const char *blurb (void);
84 static void nice_process (int nice_level);
90 exec_simple_command (const char *command)
94 char *token = strtok (strdup(command), " \t");
98 token = strtok(0, " \t");
102 execvp (av[0], av); /* shouldn't return. */
107 exec_complex_command (const char *shell, const char *command)
111 char *command2 = (char *) malloc (strlen (command) + 10);
114 const char *after_vars;
116 /* Skip leading whitespace.
118 while (*command == ' ' || *command == '\t')
121 /* If the string has a series of tokens with "=" in them at them, set
122 `after_vars' to point into the string after those tokens and any
123 trailing whitespace. Otherwise, after_vars == command.
125 after_vars = command;
126 for (s = command; *s; s++)
128 if (*s == '=') got_eq = 1;
133 while (*s == ' ' || *s == '\t')
144 strncat (command2, command, after_vars - command);
145 strcat (command2, "exec ");
146 strcat (command2, after_vars);
148 /* We have now done these transformations:
149 "foo -x -y" ==> "exec foo -x -y"
150 "BLAT=foop foo -x" ==> "BLAT=foop exec foo -x"
151 "BLAT=foop A=b foo -x" ==> "BLAT=foop A=b exec foo -x"
155 /* Invoke the shell as "/bin/sh -c 'exec prog -arg -arg ...'" */
156 av [ac++] = (char *) shell;
158 av [ac++] = command2;
161 execvp (av[0], av); /* shouldn't return. */
167 exec_vms_command (const char *command)
172 exit (1); /* Note that this only exits a child fork. */
179 exec_command (const char *shell, const char *command, int nice_level)
184 nice_process (nice_level);
186 hairy_p = !!strpbrk (command, "*?$&!<>[];`'\\\"=");
187 /* note: = is in the above because of the sh syntax "FOO=bar cmd". */
189 if (getuid() == (uid_t) 0 || geteuid() == (uid_t) 0)
191 /* If you're thinking of commenting this out, think again.
192 If you do so, you will open a security hole. Mail jwz
193 so that he may enlighten you as to the error of your ways.
195 fprintf (stderr, "%s: we're still running as root! Disaster!\n",
201 /* If it contains any shell metacharacters, do it the hard way,
202 and fork a shell to parse the arguments for us. */
203 exec_complex_command (shell, command);
205 /* Otherwise, we can just exec the program directly. */
206 exec_simple_command (command);
209 exec_vms_command (command);
214 /* Setting process priority
218 nice_process (int nice_level)
223 #if defined(HAVE_NICE)
225 int old_nice = nice (0);
226 int n = nice_level - old_nice;
228 if (nice (n) == -1 && errno != 0)
231 sprintf (buf, "%s: nice(%d) failed", blurb(), n);
235 #elif defined(HAVE_SETPRIORITY) && defined(PRIO_PROCESS)
236 if (setpriority (PRIO_PROCESS, getpid(), nice_level) != 0)
239 sprintf (buf, "%s: setpriority(PRIO_PROCESS, %lu, %d) failed",
240 blurb(), (unsigned long) getpid(), nice_level);
245 "%s: don't know how to change process priority on this system.\n",
252 /* Whether the given command exists on $PATH.
253 (Anything before the first space is considered to be the program name.)
256 on_path_p (const char *program)
260 char *cmd = strdup (program);
261 char *token = strchr (cmd, ' ');
265 if (token) *token = 0;
268 if (strchr (cmd, '/'))
270 result = (0 == stat (cmd, &st));
274 path = getenv("PATH");
279 path = strdup (path);
280 token = strtok (path, ":");
284 char *p2 = (char *) malloc (strlen (token) + L + 3);
288 result = (0 == stat (p2, &st));
291 token = strtok (0, ":");
296 if (path) free (path);