8 |
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details. |
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details. |
9 |
*/ |
*/ |
10 |
|
|
11 |
#include "busybox.h" |
/* |
12 |
#include <getopt.h> |
This is how it is supposed to work: |
13 |
|
|
14 |
|
start-stop-daemon [OPTIONS] [--start|--stop] [[--] arguments...] |
15 |
|
|
16 |
|
One (only) of these must be given: |
17 |
|
-S,--start Start |
18 |
|
-K,--stop Stop |
19 |
|
|
20 |
|
Search for matching processes. |
21 |
|
If --stop is given, stop all matching processes (by sending a signal). |
22 |
|
If --start is given, start a new process unless a matching process was found. |
23 |
|
|
24 |
|
Options controlling process matching |
25 |
|
(if multiple conditions are specified, all must match): |
26 |
|
-u,--user USERNAME|UID Only consider this user's processes |
27 |
|
-n,--name PROCESS_NAME Look for processes by matching PROCESS_NAME |
28 |
|
with comm field in /proc/$PID/stat. |
29 |
|
Only basename is compared: |
30 |
|
"ntpd" == "./ntpd" == "/path/to/ntpd". |
31 |
|
[TODO: can PROCESS_NAME be a full pathname? Should we require full match then |
32 |
|
with /proc/$PID/exe or argv[0] (comm can't be matched, it never contains path)] |
33 |
|
-x,--exec EXECUTABLE Look for processes that were started with this |
34 |
|
command in /proc/$PID/cmdline. |
35 |
|
Unlike -n, we match against the full path: |
36 |
|
"ntpd" != "./ntpd" != "/path/to/ntpd" |
37 |
|
-p,--pidfile PID_FILE Look for processes with PID from this file |
38 |
|
|
39 |
|
Options which are valid for --start only: |
40 |
|
-x,--exec EXECUTABLE Program to run (1st arg of execvp). Mandatory. |
41 |
|
-a,--startas NAME argv[0] (defaults to EXECUTABLE) |
42 |
|
-b,--background Put process into background |
43 |
|
-N,--nicelevel N Add N to process' nice level |
44 |
|
-c,--chuid USER[:[GRP]] Change to specified user [and group] |
45 |
|
-m,--make-pidfile Write PID to the pidfile |
46 |
|
(both -m and -p must be given!) |
47 |
|
|
48 |
|
Options which are valid for --stop only: |
49 |
|
-s,--signal SIG Signal to send (default:TERM) |
50 |
|
-t,--test Exit with status 0 if process is found |
51 |
|
(we don't actually start or stop daemons) |
52 |
|
|
53 |
|
Misc options: |
54 |
|
-o,--oknodo Exit with status 0 if nothing is done |
55 |
|
-q,--quiet Quiet |
56 |
|
-v,--verbose Verbose |
57 |
|
*/ |
58 |
|
|
59 |
#include <sys/resource.h> |
#include <sys/resource.h> |
60 |
|
|
61 |
static int signal_nr = 15; |
/* Override ENABLE_FEATURE_PIDFILE */ |
62 |
static int user_id = -1; |
#define WANT_PIDFILE 1 |
63 |
static int quiet; |
#include "libbb.h" |
|
static char *userspec; |
|
|
static char *chuid; |
|
|
static char *cmdname; |
|
|
static char *execname; |
|
|
static char *pidfile; |
|
64 |
|
|
65 |
struct pid_list { |
struct pid_list { |
66 |
struct pid_list *next; |
struct pid_list *next; |
67 |
pid_t pid; |
pid_t pid; |
68 |
}; |
}; |
69 |
|
|
70 |
static struct pid_list *found; |
enum { |
71 |
|
CTX_STOP = (1 << 0), |
72 |
|
CTX_START = (1 << 1), |
73 |
|
OPT_BACKGROUND = (1 << 2), // -b |
74 |
|
OPT_QUIET = (1 << 3), // -q |
75 |
|
OPT_TEST = (1 << 4), // -t |
76 |
|
OPT_MAKEPID = (1 << 5), // -m |
77 |
|
OPT_a = (1 << 6), // -a |
78 |
|
OPT_n = (1 << 7), // -n |
79 |
|
OPT_s = (1 << 8), // -s |
80 |
|
OPT_u = (1 << 9), // -u |
81 |
|
OPT_c = (1 << 10), // -c |
82 |
|
OPT_x = (1 << 11), // -x |
83 |
|
OPT_p = (1 << 12), // -p |
84 |
|
OPT_OKNODO = (1 << 13) * ENABLE_FEATURE_START_STOP_DAEMON_FANCY, // -o |
85 |
|
OPT_VERBOSE = (1 << 14) * ENABLE_FEATURE_START_STOP_DAEMON_FANCY, // -v |
86 |
|
OPT_NICELEVEL = (1 << 15) * ENABLE_FEATURE_START_STOP_DAEMON_FANCY, // -N |
87 |
|
}; |
88 |
|
#define QUIET (option_mask32 & OPT_QUIET) |
89 |
|
#define TEST (option_mask32 & OPT_TEST) |
90 |
|
|
91 |
static inline void push(pid_t pid) |
struct globals { |
92 |
|
struct pid_list *found; |
93 |
|
char *userspec; |
94 |
|
char *cmdname; |
95 |
|
char *execname; |
96 |
|
char *pidfile; |
97 |
|
int user_id; |
98 |
|
smallint signal_nr; |
99 |
|
}; |
100 |
|
#define G (*(struct globals*)&bb_common_bufsiz1) |
101 |
|
#define found (G.found ) |
102 |
|
#define userspec (G.userspec ) |
103 |
|
#define cmdname (G.cmdname ) |
104 |
|
#define execname (G.execname ) |
105 |
|
#define pidfile (G.pidfile ) |
106 |
|
#define user_id (G.user_id ) |
107 |
|
#define signal_nr (G.signal_nr ) |
108 |
|
#define INIT_G() do { \ |
109 |
|
user_id = -1; \ |
110 |
|
signal_nr = 15; \ |
111 |
|
} while (0) |
112 |
|
|
113 |
|
#ifdef OLDER_VERSION_OF_X |
114 |
|
/* -x,--exec EXECUTABLE |
115 |
|
* Look for processes with matching /proc/$PID/exe. |
116 |
|
* Match is performed using device+inode. |
117 |
|
*/ |
118 |
|
static int pid_is_exec(pid_t pid) |
119 |
{ |
{ |
120 |
struct pid_list *p; |
struct stat st; |
121 |
|
char buf[sizeof("/proc//exe") + sizeof(int)*3]; |
122 |
|
|
123 |
p = xmalloc(sizeof(*p)); |
sprintf(buf, "/proc/%u/exe", (unsigned)pid); |
124 |
p->next = found; |
if (stat(buf, &st) < 0) |
125 |
p->pid = pid; |
return 0; |
126 |
found = p; |
if (st.st_dev == execstat.st_dev |
127 |
|
&& st.st_ino == execstat.st_ino) |
128 |
|
return 1; |
129 |
|
return 0; |
130 |
} |
} |
131 |
|
#endif |
132 |
|
|
133 |
static int pid_is_exec(pid_t pid, const char *name) |
static int pid_is_exec(pid_t pid) |
134 |
{ |
{ |
135 |
char buf[sizeof("/proc//exe") + sizeof(int)*3]; |
ssize_t bytes; |
136 |
char *execbuf; |
char buf[PATH_MAX]; |
|
int sz; |
|
|
int equal; |
|
|
|
|
|
sprintf(buf, "/proc/%d/exe", pid); |
|
|
sz = strlen(name) + 1; |
|
|
execbuf = xzalloc(sz); |
|
|
readlink(buf, execbuf, sz); |
|
137 |
|
|
138 |
/* if readlink fails, execbuf still contains "" */ |
sprintf(buf, "/proc/%u/cmdline", (unsigned)pid); |
139 |
equal = !strcmp(execbuf, name); |
bytes = open_read_close(buf, buf, sizeof(buf) - 1); |
140 |
if (ENABLE_FEATURE_CLEAN_UP) |
if (bytes > 0) { |
141 |
free(execbuf); |
buf[bytes] = '\0'; |
142 |
return equal; |
return strcmp(buf, execname) == 0; |
143 |
|
} |
144 |
|
return 0; |
145 |
} |
} |
146 |
|
|
147 |
static int pid_is_user(int pid, int uid) |
static int pid_is_name(pid_t pid) |
148 |
{ |
{ |
149 |
struct stat sb; |
/* /proc/PID/stat is "PID (comm_15_bytes_max) ..." */ |
150 |
char buf[sizeof("/proc/") + sizeof(int)*3]; |
char buf[32]; /* should be enough */ |
151 |
|
char *p, *pe; |
152 |
|
|
153 |
sprintf(buf, "/proc/%u", pid); |
sprintf(buf, "/proc/%u/stat", (unsigned)pid); |
154 |
if (stat(buf, &sb) != 0) |
if (open_read_close(buf, buf, sizeof(buf) - 1) < 0) |
155 |
|
return 0; |
156 |
|
buf[sizeof(buf) - 1] = '\0'; /* paranoia */ |
157 |
|
p = strchr(buf, '('); |
158 |
|
if (!p) |
159 |
|
return 0; |
160 |
|
pe = strrchr(++p, ')'); |
161 |
|
if (!pe) |
162 |
|
return 0; |
163 |
|
*pe = '\0'; |
164 |
|
/* we require comm to match and to not be truncated */ |
165 |
|
/* in Linux, if comm is 15 chars, it may be a truncated |
166 |
|
* name, so we don't allow that to match */ |
167 |
|
if (strlen(p) >= COMM_LEN - 1) /* COMM_LEN is 16 */ |
168 |
return 0; |
return 0; |
169 |
return (sb.st_uid == uid); |
return strcmp(p, cmdname) == 0; |
170 |
} |
} |
171 |
|
|
172 |
static int pid_is_cmd(pid_t pid, const char *name) |
static int pid_is_user(int pid) |
173 |
{ |
{ |
174 |
char fname[sizeof("/proc//stat") + sizeof(int)*3]; |
struct stat sb; |
175 |
char *buf; |
char buf[sizeof("/proc/") + sizeof(int)*3]; |
|
int r = 0; |
|
|
|
|
|
sprintf(fname, "/proc/%u/stat", pid); |
|
|
buf = xmalloc_open_read_close(fname, NULL); |
|
|
if (buf) { |
|
|
char *p = strchr(buf, '('); |
|
|
if (p) { |
|
|
char *pe = strrchr(++p, ')'); |
|
|
if (pe) { |
|
|
*pe = '\0'; |
|
|
r = !strcmp(p, name); |
|
|
} |
|
|
} |
|
|
free(buf); |
|
|
} |
|
|
return r; |
|
|
} |
|
176 |
|
|
177 |
|
sprintf(buf, "/proc/%u", (unsigned)pid); |
178 |
|
if (stat(buf, &sb) != 0) |
179 |
|
return 0; |
180 |
|
return (sb.st_uid == (uid_t)user_id); |
181 |
|
} |
182 |
|
|
183 |
static void check(int pid) |
static void check(int pid) |
184 |
{ |
{ |
185 |
if (execname && !pid_is_exec(pid, execname)) { |
struct pid_list *p; |
186 |
|
|
187 |
|
if (execname && !pid_is_exec(pid)) { |
188 |
return; |
return; |
189 |
} |
} |
190 |
if (userspec && !pid_is_user(pid, user_id)) { |
if (cmdname && !pid_is_name(pid)) { |
191 |
return; |
return; |
192 |
} |
} |
193 |
if (cmdname && !pid_is_cmd(pid, cmdname)) { |
if (userspec && !pid_is_user(pid)) { |
194 |
return; |
return; |
195 |
} |
} |
196 |
push(pid); |
p = xmalloc(sizeof(*p)); |
197 |
|
p->next = found; |
198 |
|
p->pid = pid; |
199 |
|
found = p; |
200 |
} |
} |
201 |
|
|
|
|
|
202 |
static void do_pidfile(void) |
static void do_pidfile(void) |
203 |
{ |
{ |
204 |
FILE *f; |
FILE *f; |
205 |
pid_t pid; |
unsigned pid; |
206 |
|
|
207 |
f = fopen(pidfile, "r"); |
f = fopen_for_read(pidfile); |
208 |
if (f) { |
if (f) { |
209 |
if (fscanf(f, "%u", &pid) == 1) |
if (fscanf(f, "%u", &pid) == 1) |
210 |
check(pid); |
check(pid); |
217 |
{ |
{ |
218 |
DIR *procdir; |
DIR *procdir; |
219 |
struct dirent *entry; |
struct dirent *entry; |
220 |
int foundany, pid; |
int pid; |
221 |
|
|
222 |
if (pidfile) { |
if (pidfile) { |
223 |
do_pidfile(); |
do_pidfile(); |
226 |
|
|
227 |
procdir = xopendir("/proc"); |
procdir = xopendir("/proc"); |
228 |
|
|
229 |
foundany = 0; |
pid = 0; |
230 |
while ((entry = readdir(procdir)) != NULL) { |
while (1) { |
231 |
|
errno = 0; /* clear any previous error */ |
232 |
|
entry = readdir(procdir); |
233 |
|
// TODO: this check is too generic, it's better |
234 |
|
// to check for exact errno(s) which mean that we got stale entry |
235 |
|
if (errno) /* Stale entry, process has died after opendir */ |
236 |
|
continue; |
237 |
|
if (!entry) /* EOF, no more entries */ |
238 |
|
break; |
239 |
pid = bb_strtou(entry->d_name, NULL, 10); |
pid = bb_strtou(entry->d_name, NULL, 10); |
240 |
if (errno) |
if (errno) /* NaN */ |
241 |
continue; |
continue; |
|
foundany++; |
|
242 |
check(pid); |
check(pid); |
243 |
} |
} |
244 |
closedir(procdir); |
closedir(procdir); |
245 |
if (!foundany) |
if (!pid) |
246 |
bb_error_msg_and_die ("nothing in /proc - not mounted?"); |
bb_error_msg_and_die("nothing in /proc - not mounted?"); |
247 |
} |
} |
248 |
|
|
|
|
|
249 |
static int do_stop(void) |
static int do_stop(void) |
250 |
{ |
{ |
251 |
char *what; |
char *what; |
252 |
struct pid_list *p; |
struct pid_list *p; |
253 |
int killed = 0; |
int killed = 0; |
254 |
|
|
255 |
do_procinit(); |
if (cmdname) { |
256 |
|
if (ENABLE_FEATURE_CLEAN_UP) what = xstrdup(cmdname); |
257 |
if (cmdname) |
if (!ENABLE_FEATURE_CLEAN_UP) what = cmdname; |
258 |
what = xstrdup(cmdname); |
} else if (execname) { |
259 |
else if (execname) |
if (ENABLE_FEATURE_CLEAN_UP) what = xstrdup(execname); |
260 |
what = xstrdup(execname); |
if (!ENABLE_FEATURE_CLEAN_UP) what = execname; |
261 |
else if (pidfile) |
} else if (pidfile) { |
262 |
what = xasprintf("process in pidfile '%s'", pidfile); |
what = xasprintf("process in pidfile '%s'", pidfile); |
263 |
else if (userspec) |
} else if (userspec) { |
264 |
what = xasprintf("process(es) owned by '%s'", userspec); |
what = xasprintf("process(es) owned by '%s'", userspec); |
265 |
else |
} else { |
266 |
bb_error_msg_and_die("internal error, please report"); |
bb_error_msg_and_die("internal error, please report"); |
267 |
|
} |
268 |
|
|
269 |
if (!found) { |
if (!found) { |
270 |
if (!quiet) |
if (!QUIET) |
271 |
printf("no %s found; none killed\n", what); |
printf("no %s found; none killed\n", what); |
272 |
if (ENABLE_FEATURE_CLEAN_UP) |
killed = -1; |
273 |
free(what); |
goto ret; |
|
return -1; |
|
274 |
} |
} |
275 |
for (p = found; p; p = p->next) { |
for (p = found; p; p = p->next) { |
276 |
if (kill(p->pid, signal_nr) == 0) { |
if (TEST || kill(p->pid, signal_nr) == 0) { |
|
p->pid = -p->pid; |
|
277 |
killed++; |
killed++; |
278 |
} else { |
} else { |
279 |
bb_perror_msg("warning: failed to kill %d", p->pid); |
p->pid = 0; |
280 |
|
bb_perror_msg("warning: killing process %u", (unsigned)p->pid); |
281 |
} |
} |
282 |
} |
} |
283 |
if (!quiet && killed) { |
if (!QUIET && killed) { |
284 |
printf("stopped %s (pid", what); |
printf("stopped %s (pid", what); |
285 |
for (p = found; p; p = p->next) |
for (p = found; p; p = p->next) |
286 |
if(p->pid < 0) |
if (p->pid) |
287 |
printf(" %d", -p->pid); |
printf(" %u", (unsigned)p->pid); |
288 |
puts(")"); |
puts(")"); |
289 |
} |
} |
290 |
|
ret: |
291 |
if (ENABLE_FEATURE_CLEAN_UP) |
if (ENABLE_FEATURE_CLEAN_UP) |
292 |
free(what); |
free(what); |
293 |
return killed; |
return killed; |
294 |
} |
} |
295 |
|
|
296 |
#if ENABLE_FEATURE_START_STOP_DAEMON_LONG_OPTIONS |
#if ENABLE_FEATURE_START_STOP_DAEMON_LONG_OPTIONS |
297 |
static const struct option long_options[] = { |
static const char start_stop_daemon_longopts[] ALIGN1 = |
298 |
{ "stop", 0, NULL, 'K' }, |
"stop\0" No_argument "K" |
299 |
{ "start", 0, NULL, 'S' }, |
"start\0" No_argument "S" |
300 |
{ "background", 0, NULL, 'b' }, |
"background\0" No_argument "b" |
301 |
{ "quiet", 0, NULL, 'q' }, |
"quiet\0" No_argument "q" |
302 |
{ "make-pidfile", 0, NULL, 'm' }, |
"test\0" No_argument "t" |
303 |
|
"make-pidfile\0" No_argument "m" |
304 |
#if ENABLE_FEATURE_START_STOP_DAEMON_FANCY |
#if ENABLE_FEATURE_START_STOP_DAEMON_FANCY |
305 |
{ "oknodo", 0, NULL, 'o' }, |
"oknodo\0" No_argument "o" |
306 |
{ "verbose", 0, NULL, 'v' }, |
"verbose\0" No_argument "v" |
307 |
{ "nicelevel", 1, NULL, 'N' }, |
"nicelevel\0" Required_argument "N" |
308 |
#endif |
#endif |
309 |
{ "startas", 1, NULL, 'a' }, |
"startas\0" Required_argument "a" |
310 |
{ "name", 1, NULL, 'n' }, |
"name\0" Required_argument "n" |
311 |
{ "signal", 1, NULL, 's' }, |
"signal\0" Required_argument "s" |
312 |
{ "user", 1, NULL, 'u' }, |
"user\0" Required_argument "u" |
313 |
{ "chuid", 1, NULL, 'c' }, |
"chuid\0" Required_argument "c" |
314 |
{ "exec", 1, NULL, 'x' }, |
"exec\0" Required_argument "x" |
315 |
{ "pidfile", 1, NULL, 'p' }, |
"pidfile\0" Required_argument "p" |
316 |
#if ENABLE_FEATURE_START_STOP_DAEMON_FANCY |
#if ENABLE_FEATURE_START_STOP_DAEMON_FANCY |
317 |
{ "retry", 1, NULL, 'R' }, |
"retry\0" Required_argument "R" |
318 |
#endif |
#endif |
319 |
{ 0, 0, 0, 0 } |
; |
|
}; |
|
320 |
#endif |
#endif |
321 |
|
|
322 |
enum { |
int start_stop_daemon_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
323 |
CTX_STOP = 0x1, |
int start_stop_daemon_main(int argc UNUSED_PARAM, char **argv) |
|
CTX_START = 0x2, |
|
|
OPT_BACKGROUND = 0x4, |
|
|
OPT_QUIET = 0x8, |
|
|
OPT_MAKEPID = 0x10, |
|
|
OPT_OKNODO = 0x20 * ENABLE_FEATURE_START_STOP_DAEMON_FANCY, |
|
|
OPT_VERBOSE = 0x40 * ENABLE_FEATURE_START_STOP_DAEMON_FANCY, |
|
|
OPT_NICELEVEL = 0x80 * ENABLE_FEATURE_START_STOP_DAEMON_FANCY, |
|
|
}; |
|
|
|
|
|
int start_stop_daemon_main(int argc, char **argv) |
|
324 |
{ |
{ |
325 |
unsigned opt; |
unsigned opt; |
326 |
char *signame = NULL; |
char *signame; |
327 |
char *startas = NULL; |
char *startas; |
328 |
|
char *chuid; |
329 |
|
#ifdef OLDER_VERSION_OF_X |
330 |
|
struct stat execstat; |
331 |
|
#endif |
332 |
#if ENABLE_FEATURE_START_STOP_DAEMON_FANCY |
#if ENABLE_FEATURE_START_STOP_DAEMON_FANCY |
333 |
// char *retry_arg = NULL; |
// char *retry_arg = NULL; |
334 |
// int retries = -1; |
// int retries = -1; |
335 |
char *opt_N; |
char *opt_N; |
336 |
#endif |
#endif |
337 |
|
|
338 |
|
INIT_G(); |
339 |
|
|
340 |
#if ENABLE_FEATURE_START_STOP_DAEMON_LONG_OPTIONS |
#if ENABLE_FEATURE_START_STOP_DAEMON_LONG_OPTIONS |
341 |
applet_long_options = long_options; |
applet_long_options = start_stop_daemon_longopts; |
342 |
#endif |
#endif |
343 |
|
|
344 |
/* Check required one context option was given */ |
/* -K or -S is required; they are mutually exclusive */ |
345 |
opt_complementary = "K:S:?:K--S:S--K:m?p:K?xpun:S?xa"; |
/* -p is required if -m is given */ |
346 |
opt = getopt32(argc, argv, "KSbqm" |
/* -xpun (at least one) is required if -K is given */ |
347 |
// USE_FEATURE_START_STOP_DAEMON_FANCY("ovN:R:") |
/* -xa (at least one) is required if -S is given */ |
348 |
USE_FEATURE_START_STOP_DAEMON_FANCY("ovN:") |
/* -q turns off -v */ |
349 |
"a:n:s:u:c:x:p:" |
opt_complementary = "K:S:K--S:S--K:m?p:K?xpun:S?xa" |
350 |
|
USE_FEATURE_START_STOP_DAEMON_FANCY("q-v"); |
351 |
|
opt = getopt32(argv, "KSbqtma:n:s:u:c:x:p:" |
352 |
|
USE_FEATURE_START_STOP_DAEMON_FANCY("ovN:R:"), |
353 |
|
&startas, &cmdname, &signame, &userspec, &chuid, &execname, &pidfile |
354 |
USE_FEATURE_START_STOP_DAEMON_FANCY(,&opt_N) |
USE_FEATURE_START_STOP_DAEMON_FANCY(,&opt_N) |
355 |
// USE_FEATURE_START_STOP_DAEMON_FANCY(,&retry_arg) |
/* We accept and ignore -R <param> / --retry <param> */ |
356 |
,&startas, &cmdname, &signame, &userspec, &chuid, &execname, &pidfile); |
USE_FEATURE_START_STOP_DAEMON_FANCY(,NULL) |
357 |
|
); |
|
quiet = (opt & OPT_QUIET) && !(opt & OPT_VERBOSE); |
|
358 |
|
|
359 |
if (signame) { |
if (opt & OPT_s) { |
360 |
signal_nr = get_signum(signame); |
signal_nr = get_signum(signame); |
361 |
if (signal_nr < 0) bb_show_usage(); |
if (signal_nr < 0) bb_show_usage(); |
362 |
} |
} |
363 |
|
|
364 |
if (!startas) |
if (!(opt & OPT_a)) |
365 |
startas = execname; |
startas = execname; |
366 |
|
if (!execname) /* in case -a is given and -x is not */ |
367 |
|
execname = startas; |
368 |
|
|
369 |
// USE_FEATURE_START_STOP_DAEMON_FANCY( |
// USE_FEATURE_START_STOP_DAEMON_FANCY( |
370 |
// if (retry_arg) |
// if (retry_arg) |
371 |
// retries = xatoi_u(retry_arg); |
// retries = xatoi_u(retry_arg); |
372 |
// ) |
// ) |
373 |
argc -= optind; |
//argc -= optind; |
374 |
argv += optind; |
argv += optind; |
375 |
|
|
376 |
if (userspec) { |
if (userspec) { |
378 |
if (errno) |
if (errno) |
379 |
user_id = xuname2uid(userspec); |
user_id = xuname2uid(userspec); |
380 |
} |
} |
381 |
|
/* Both start and stop need to know current processes */ |
382 |
|
do_procinit(); |
383 |
|
|
384 |
if (opt & CTX_STOP) { |
if (opt & CTX_STOP) { |
385 |
int i = do_stop(); |
int i = do_stop(); |
386 |
return (opt & OPT_OKNODO) ? 0 : (i<=0); |
return (opt & OPT_OKNODO) ? 0 : (i <= 0); |
387 |
} |
} |
388 |
|
|
|
do_procinit(); |
|
|
|
|
389 |
if (found) { |
if (found) { |
390 |
if (!quiet) |
if (!QUIET) |
391 |
printf("%s already running\n%d\n", execname, found->pid); |
printf("%s is already running\n%u\n", execname, (unsigned)found->pid); |
392 |
return !(opt & OPT_OKNODO); |
return !(opt & OPT_OKNODO); |
393 |
} |
} |
394 |
|
|
395 |
|
#ifdef OLDER_VERSION_OF_X |
396 |
|
if (execname) |
397 |
|
xstat(execname, &execstat); |
398 |
|
#endif |
399 |
|
|
400 |
*--argv = startas; |
*--argv = startas; |
401 |
if (opt & OPT_BACKGROUND) { |
if (opt & OPT_BACKGROUND) { |
402 |
setsid(); |
#if BB_MMU |
403 |
bb_daemonize(); |
bb_daemonize(DAEMON_DEVNULL_STDIO + DAEMON_CLOSE_EXTRA_FDS); |
404 |
|
/* DAEMON_DEVNULL_STDIO is superfluous - |
405 |
|
* it's always done by bb_daemonize() */ |
406 |
|
#else |
407 |
|
pid_t pid = vfork(); |
408 |
|
if (pid < 0) /* error */ |
409 |
|
bb_perror_msg_and_die("vfork"); |
410 |
|
if (pid != 0) { |
411 |
|
/* parent */ |
412 |
|
/* why _exit? the child may have changed the stack, |
413 |
|
* so "return 0" may do bad things */ |
414 |
|
_exit(EXIT_SUCCESS); |
415 |
|
} |
416 |
|
/* Child */ |
417 |
|
setsid(); /* detach from controlling tty */ |
418 |
|
/* Redirect stdio to /dev/null, close extra FDs. |
419 |
|
* We do not actually daemonize because of DAEMON_ONLY_SANITIZE */ |
420 |
|
bb_daemonize_or_rexec(DAEMON_DEVNULL_STDIO |
421 |
|
+ DAEMON_CLOSE_EXTRA_FDS |
422 |
|
+ DAEMON_ONLY_SANITIZE, |
423 |
|
NULL /* argv, unused */ ); |
424 |
|
#endif |
425 |
} |
} |
426 |
if (opt & OPT_MAKEPID) { |
if (opt & OPT_MAKEPID) { |
427 |
/* user wants _us_ to make the pidfile */ |
/* User wants _us_ to make the pidfile */ |
428 |
FILE *pidf = xfopen(pidfile, "w"); |
write_pidfile(pidfile); |
|
|
|
|
pid_t pidt = getpid(); |
|
|
fprintf(pidf, "%d\n", pidt); |
|
|
fclose(pidf); |
|
429 |
} |
} |
430 |
if (chuid) { |
if (opt & OPT_c) { |
431 |
user_id = bb_strtou(chuid, NULL, 10); |
struct bb_uidgid_t ugid = { -1, -1 }; |
432 |
if (errno) |
parse_chown_usergroup_or_die(&ugid, chuid); |
433 |
user_id = xuname2uid(chuid); |
if (ugid.gid != (gid_t) -1) xsetgid(ugid.gid); |
434 |
xsetuid(user_id); |
if (ugid.uid != (uid_t) -1) xsetuid(ugid.uid); |
435 |
} |
} |
436 |
#if ENABLE_FEATURE_START_STOP_DAEMON_FANCY |
#if ENABLE_FEATURE_START_STOP_DAEMON_FANCY |
437 |
if (opt & OPT_NICELEVEL) { |
if (opt & OPT_NICELEVEL) { |
442 |
} |
} |
443 |
} |
} |
444 |
#endif |
#endif |
445 |
execv(startas, argv); |
execvp(startas, argv); |
446 |
bb_perror_msg_and_die("cannot start %s", startas); |
bb_perror_msg_and_die("cannot start %s", startas); |
447 |
} |
} |