1 /* exec.c --- executes a program in *this* pid, without an intervening process.
2 * xscreensaver, Copyright (c) 1991-2005 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 */
81 extern const char *blurb (void);
83 static void nice_process (int nice_level);
89 exec_simple_command (const char *command)
93 char *token = strtok (strdup(command), " \t");
97 token = strtok(0, " \t");
101 execvp (av[0], av); /* shouldn't return. */
106 exec_complex_command (const char *shell, const char *command)
110 char *command2 = (char *) malloc (strlen (command) + 10);
113 const char *after_vars;
115 /* Skip leading whitespace.
117 while (*command == ' ' || *command == '\t')
120 /* If the string has a series of tokens with "=" in them at them, set
121 `after_vars' to point into the string after those tokens and any
122 trailing whitespace. Otherwise, after_vars == command.
124 after_vars = command;
125 for (s = command; *s; s++)
127 if (*s == '=') got_eq = 1;
132 while (*s == ' ' || *s == '\t')
143 strncat (command2, command, after_vars - command);
144 strcat (command2, "exec ");
145 strcat (command2, after_vars);
147 /* We have now done these transformations:
148 "foo -x -y" ==> "exec foo -x -y"
149 "BLAT=foop foo -x" ==> "BLAT=foop exec foo -x"
150 "BLAT=foop A=b foo -x" ==> "BLAT=foop A=b exec foo -x"
154 /* Invoke the shell as "/bin/sh -c 'exec prog -arg -arg ...'" */
155 av [ac++] = (char *) shell;
157 av [ac++] = command2;
160 execvp (av[0], av); /* shouldn't return. */
166 exec_vms_command (const char *command)
171 exit (1); /* Note that this only exits a child fork. */
178 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);