aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2008-05-18 21:17:52 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2008-05-18 21:17:52 +0000
commite125a683a77d14401644d15f38b7f578db723924 (patch)
tree2beb60ca4b40bb96c017c02d4d9cefff85df1db9
parentfaf334aeb87ccc6680987bbd1fae8faf81969fed (diff)
downloadbusybox-w32-e125a683a77d14401644d15f38b7f578db723924.tar.gz
busybox-w32-e125a683a77d14401644d15f38b7f578db723924.tar.bz2
busybox-w32-e125a683a77d14401644d15f38b7f578db723924.zip
start_stop_daemon: add -test, fix -x to not match by inode,
vastly improve hext text. (mostly by Roy Marples <roy AT marples.name>) function old new delta packed_usage 24124 24190 +66 start_stop_daemon_main 959 991 +32 start_stop_daemon_longopts 149 156 +7 check 1632 1589 -43 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 3/1 up/down: 105/-43) Total: 62 bytes
-rw-r--r--debianutils/start_stop_daemon.c134
-rw-r--r--include/usage.h83
2 files changed, 137 insertions, 80 deletions
diff --git a/debianutils/start_stop_daemon.c b/debianutils/start_stop_daemon.c
index 094b3b18a..524259979 100644
--- a/debianutils/start_stop_daemon.c
+++ b/debianutils/start_stop_daemon.c
@@ -23,15 +23,18 @@ If --start if given, start a new process unless a matching process was found.
23 23
24Options controlling process matching: 24Options controlling process matching:
25 -u,--user USERNAME|UID Only consider this user's processes 25 -u,--user USERNAME|UID Only consider this user's processes
26 -n,--name PROCESS_NAME Look for processes with matching argv[0] 26 -n,--name PROCESS_NAME Look for processes by matching PROCESS_NAME
27 or /proc/$PID/exe or /proc/$PID/stat (comm field). 27 with comm field in /proc/$PID/stat.
28 Only basename is compared: 28 Only basename is compared:
29 "ntpd" == "./ntpd" == "/path/to/ntpd". 29 "ntpd" == "./ntpd" == "/path/to/ntpd".
30[TODO: can PROCESS_NAME be a full pathname? Should we require full match then 30[TODO: can PROCESS_NAME be a full pathname? Should we require full match then
31with /proc/$PID/exe or argv[0] (comm can't be matched, it never contains path)] 31with /proc/$PID/exe or argv[0] (comm can't be matched, it never contains path)]
32 -x,--exec EXECUTABLE Look for processes with matching /proc/$PID/exe. 32 -x,--exec EXECUTABLE Look for processes that were started with this
33 Match is performed using device+inode. 33 command in /proc/$PID/cmdline.
34 Unlike -n, we match against the full path:
35 "ntpd" != "./ntpd" != "/path/to/ntpd"
34 -p,--pidfile PID_FILE Look for processes with PID from this file 36 -p,--pidfile PID_FILE Look for processes with PID from this file
37If multiple conditions are specified, all must match.
35 38
36Options which are valid for --start only: 39Options which are valid for --start only:
37 -x,--exec EXECUTABLE Program to run (1st arg of execvp). Mandatory. 40 -x,--exec EXECUTABLE Program to run (1st arg of execvp). Mandatory.
@@ -41,8 +44,13 @@ Options which are valid for --start only:
41 -c,--chuid USER[:[GRP]] Change to specified user [and group] 44 -c,--chuid USER[:[GRP]] Change to specified user [and group]
42 -m,--make-pidfile Write PID to the pidfile 45 -m,--make-pidfile Write PID to the pidfile
43 (both -m and -p must be given!) 46 (both -m and -p must be given!)
44Misc options: 47
48Options which are valid for --stop only:
45 -s,--signal SIG Signal to send (default:TERM) 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
53Misc options:
46 -o,--oknodo Exit with status 0 if nothing is done 54 -o,--oknodo Exit with status 0 if nothing is done
47 -q,--quiet Quiet 55 -q,--quiet Quiet
48 -v,--verbose Verbose 56 -v,--verbose Verbose
@@ -64,19 +72,21 @@ enum {
64 CTX_START = (1 << 1), 72 CTX_START = (1 << 1),
65 OPT_BACKGROUND = (1 << 2), // -b 73 OPT_BACKGROUND = (1 << 2), // -b
66 OPT_QUIET = (1 << 3), // -q 74 OPT_QUIET = (1 << 3), // -q
67 OPT_MAKEPID = (1 << 4), // -m 75 OPT_TEST = (1 << 4), // -t
68 OPT_a = (1 << 5), // -a 76 OPT_MAKEPID = (1 << 5), // -m
69 OPT_n = (1 << 6), // -n 77 OPT_a = (1 << 6), // -a
70 OPT_s = (1 << 7), // -s 78 OPT_n = (1 << 7), // -n
71 OPT_u = (1 << 8), // -u 79 OPT_s = (1 << 8), // -s
72 OPT_c = (1 << 9), // -c 80 OPT_u = (1 << 9), // -u
73 OPT_x = (1 << 10), // -x 81 OPT_c = (1 << 10), // -c
74 OPT_p = (1 << 11), // -p 82 OPT_x = (1 << 11), // -x
75 OPT_OKNODO = (1 << 12) * ENABLE_FEATURE_START_STOP_DAEMON_FANCY, // -o 83 OPT_p = (1 << 12), // -p
76 OPT_VERBOSE = (1 << 13) * ENABLE_FEATURE_START_STOP_DAEMON_FANCY, // -v 84 OPT_OKNODO = (1 << 13) * ENABLE_FEATURE_START_STOP_DAEMON_FANCY, // -o
77 OPT_NICELEVEL = (1 << 14) * ENABLE_FEATURE_START_STOP_DAEMON_FANCY, // -N 85 OPT_VERBOSE = (1 << 14) * ENABLE_FEATURE_START_STOP_DAEMON_FANCY, // -v
86 OPT_NICELEVEL = (1 << 15) * ENABLE_FEATURE_START_STOP_DAEMON_FANCY, // -N
78}; 87};
79#define QUIET (option_mask32 & OPT_QUIET) 88#define QUIET (option_mask32 & OPT_QUIET)
89#define TEST (option_mask32 & OPT_TEST)
80 90
81struct globals { 91struct globals {
82 struct pid_list *found; 92 struct pid_list *found;
@@ -86,7 +96,6 @@ struct globals {
86 char *pidfile; 96 char *pidfile;
87 int user_id; 97 int user_id;
88 smallint signal_nr; 98 smallint signal_nr;
89 struct stat execstat;
90}; 99};
91#define G (*(struct globals*)&bb_common_bufsiz1) 100#define G (*(struct globals*)&bb_common_bufsiz1)
92#define found (G.found ) 101#define found (G.found )
@@ -96,20 +105,23 @@ struct globals {
96#define pidfile (G.pidfile ) 105#define pidfile (G.pidfile )
97#define user_id (G.user_id ) 106#define user_id (G.user_id )
98#define signal_nr (G.signal_nr ) 107#define signal_nr (G.signal_nr )
99#define execstat (G.execstat )
100#define INIT_G() \ 108#define INIT_G() \
101 do { \ 109 do { \
102 user_id = -1; \ 110 user_id = -1; \
103 signal_nr = 15; \ 111 signal_nr = 15; \
104 } while (0) 112 } while (0)
105 113
106 114#ifdef OLDER_VERSION_OF_X
115/* -x,--exec EXECUTABLE
116 * Look for processes with matching /proc/$PID/exe.
117 * Match is performed using device+inode.
118 */
107static int pid_is_exec(pid_t pid) 119static int pid_is_exec(pid_t pid)
108{ 120{
109 struct stat st; 121 struct stat st;
110 char buf[sizeof("/proc//exe") + sizeof(int)*3]; 122 char buf[sizeof("/proc//exe") + sizeof(int)*3];
111 123
112 sprintf(buf, "/proc/%u/exe", pid); 124 sprintf(buf, "/proc/%u/exe", (unsigned)pid);
113 if (stat(buf, &st) < 0) 125 if (stat(buf, &st) < 0)
114 return 0; 126 return 0;
115 if (st.st_dev == execstat.st_dev 127 if (st.st_dev == execstat.st_dev
@@ -117,24 +129,29 @@ static int pid_is_exec(pid_t pid)
117 return 1; 129 return 1;
118 return 0; 130 return 0;
119} 131}
132#endif
120 133
121static int pid_is_user(int pid) 134static int pid_is_exec(pid_t pid)
122{ 135{
123 struct stat sb; 136 ssize_t bytes;
124 char buf[sizeof("/proc/") + sizeof(int)*3]; 137 char buf[PATH_MAX];
125 138
126 sprintf(buf, "/proc/%u", pid); 139 sprintf(buf, "/proc/%u/cmdline", (unsigned)pid);
127 if (stat(buf, &sb) != 0) 140 bytes = open_read_close(buf, buf, sizeof(buf) - 1);
128 return 0; 141 if (bytes > 0) {
129 return (sb.st_uid == (uid_t)user_id); 142 buf[bytes] = '\0';
143 return strcmp(buf, execname) == 0;
144 }
145 return 0;
130} 146}
131 147
132static int pid_is_cmd(pid_t pid) 148static int pid_is_name(pid_t pid)
133{ 149{
134 char buf[256]; /* is it big enough? */ 150 /* /proc/PID/stat is "PID (comm_15_bytes_max) ..." */
151 char buf[32]; /* should be enough */
135 char *p, *pe; 152 char *p, *pe;
136 153
137 sprintf(buf, "/proc/%u/stat", pid); 154 sprintf(buf, "/proc/%u/stat", (unsigned)pid);
138 if (open_read_close(buf, buf, sizeof(buf) - 1) < 0) 155 if (open_read_close(buf, buf, sizeof(buf) - 1) < 0)
139 return 0; 156 return 0;
140 buf[sizeof(buf) - 1] = '\0'; /* paranoia */ 157 buf[sizeof(buf) - 1] = '\0'; /* paranoia */
@@ -145,7 +162,23 @@ static int pid_is_cmd(pid_t pid)
145 if (!pe) 162 if (!pe)
146 return 0; 163 return 0;
147 *pe = '\0'; 164 *pe = '\0';
148 return !strcmp(p, cmdname); 165 /* we require comm to match and to not be truncated */
166 /* in Linux, if comm is 15 chars, it may be a truncated
167 * name, so we don't allow that to match */
168 if (strlen(p) >= COMM_LEN - 1) /* COMM_LEN is 16 */
169 return 0;
170 return strcmp(p, cmdname) == 0;
171}
172
173static int pid_is_user(int pid)
174{
175 struct stat sb;
176 char buf[sizeof("/proc/") + sizeof(int)*3];
177
178 sprintf(buf, "/proc/%u", (unsigned)pid);
179 if (stat(buf, &sb) != 0)
180 return 0;
181 return (sb.st_uid == (uid_t)user_id);
149} 182}
150 183
151static void check(int pid) 184static void check(int pid)
@@ -155,10 +188,10 @@ static void check(int pid)
155 if (execname && !pid_is_exec(pid)) { 188 if (execname && !pid_is_exec(pid)) {
156 return; 189 return;
157 } 190 }
158 if (userspec && !pid_is_user(pid)) { 191 if (cmdname && !pid_is_name(pid)) {
159 return; 192 return;
160 } 193 }
161 if (cmdname && !pid_is_cmd(pid)) { 194 if (userspec && !pid_is_user(pid)) {
162 return; 195 return;
163 } 196 }
164 p = xmalloc(sizeof(*p)); 197 p = xmalloc(sizeof(*p));
@@ -195,10 +228,11 @@ static void do_procinit(void)
195 procdir = xopendir("/proc"); 228 procdir = xopendir("/proc");
196 229
197 pid = 0; 230 pid = 0;
198 while(1) { 231 while (1) {
199 errno = 0; /* clear any previous error */ 232 errno = 0; /* clear any previous error */
200 entry = readdir(procdir); 233 entry = readdir(procdir);
201// TODO: check for exact errno(s) which mean that we got stale entry 234// TODO: this check is too generic, it's better
235// to check for exact errno(s) which mean that we got stale entry
202 if (errno) /* Stale entry, process has died after opendir */ 236 if (errno) /* Stale entry, process has died after opendir */
203 continue; 237 continue;
204 if (!entry) /* EOF, no more entries */ 238 if (!entry) /* EOF, no more entries */
@@ -225,12 +259,13 @@ static int do_stop(void)
225 } else if (execname) { 259 } else if (execname) {
226 if (ENABLE_FEATURE_CLEAN_UP) what = xstrdup(execname); 260 if (ENABLE_FEATURE_CLEAN_UP) what = xstrdup(execname);
227 if (!ENABLE_FEATURE_CLEAN_UP) what = execname; 261 if (!ENABLE_FEATURE_CLEAN_UP) what = execname;
228 } else if (pidfile) 262 } else if (pidfile) {
229 what = xasprintf("process in pidfile '%s'", pidfile); 263 what = xasprintf("process in pidfile '%s'", pidfile);
230 else if (userspec) 264 } else if (userspec) {
231 what = xasprintf("process(es) owned by '%s'", userspec); 265 what = xasprintf("process(es) owned by '%s'", userspec);
232 else 266 } else {
233 bb_error_msg_and_die("internal error, please report"); 267 bb_error_msg_and_die("internal error, please report");
268 }
234 269
235 if (!found) { 270 if (!found) {
236 if (!QUIET) 271 if (!QUIET)
@@ -239,18 +274,18 @@ static int do_stop(void)
239 goto ret; 274 goto ret;
240 } 275 }
241 for (p = found; p; p = p->next) { 276 for (p = found; p; p = p->next) {
242 if (kill(p->pid, signal_nr) == 0) { 277 if (TEST || kill(p->pid, signal_nr) == 0) {
243 p->pid = - p->pid;
244 killed++; 278 killed++;
245 } else { 279 } else {
246 bb_perror_msg("warning: killing process %u", p->pid); 280 p->pid = 0;
281 bb_perror_msg("warning: killing process %u", (unsigned)p->pid);
247 } 282 }
248 } 283 }
249 if (!QUIET && killed) { 284 if (!QUIET && killed) {
250 printf("stopped %s (pid", what); 285 printf("stopped %s (pid", what);
251 for (p = found; p; p = p->next) 286 for (p = found; p; p = p->next)
252 if (p->pid < 0) 287 if (p->pid)
253 printf(" %u", - p->pid); 288 printf(" %u", (unsigned)p->pid);
254 puts(")"); 289 puts(")");
255 } 290 }
256 ret: 291 ret:
@@ -265,6 +300,7 @@ static const char start_stop_daemon_longopts[] ALIGN1 =
265 "start\0" No_argument "S" 300 "start\0" No_argument "S"
266 "background\0" No_argument "b" 301 "background\0" No_argument "b"
267 "quiet\0" No_argument "q" 302 "quiet\0" No_argument "q"
303 "test\0" No_argument "t"
268 "make-pidfile\0" No_argument "m" 304 "make-pidfile\0" No_argument "m"
269#if ENABLE_FEATURE_START_STOP_DAEMON_FANCY 305#if ENABLE_FEATURE_START_STOP_DAEMON_FANCY
270 "oknodo\0" No_argument "o" 306 "oknodo\0" No_argument "o"
@@ -291,6 +327,7 @@ int start_stop_daemon_main(int argc ATTRIBUTE_UNUSED, char **argv)
291 char *signame; 327 char *signame;
292 char *startas; 328 char *startas;
293 char *chuid; 329 char *chuid;
330 struct stat execstat;
294#if ENABLE_FEATURE_START_STOP_DAEMON_FANCY 331#if ENABLE_FEATURE_START_STOP_DAEMON_FANCY
295// char *retry_arg = NULL; 332// char *retry_arg = NULL;
296// int retries = -1; 333// int retries = -1;
@@ -310,7 +347,7 @@ int start_stop_daemon_main(int argc ATTRIBUTE_UNUSED, char **argv)
310 /* -q turns off -v */ 347 /* -q turns off -v */
311 opt_complementary = "K:S:K--S:S--K:m?p:K?xpun:S?xa" 348 opt_complementary = "K:S:K--S:S--K:m?p:K?xpun:S?xa"
312 USE_FEATURE_START_STOP_DAEMON_FANCY("q-v"); 349 USE_FEATURE_START_STOP_DAEMON_FANCY("q-v");
313 opt = getopt32(argv, "KSbqma:n:s:u:c:x:p:" 350 opt = getopt32(argv, "KSbqtma:n:s:u:c:x:p:"
314 USE_FEATURE_START_STOP_DAEMON_FANCY("ovN:"), 351 USE_FEATURE_START_STOP_DAEMON_FANCY("ovN:"),
315// USE_FEATURE_START_STOP_DAEMON_FANCY("ovN:R:"), 352// USE_FEATURE_START_STOP_DAEMON_FANCY("ovN:R:"),
316 &startas, &cmdname, &signame, &userspec, &chuid, &execname, &pidfile 353 &startas, &cmdname, &signame, &userspec, &chuid, &execname, &pidfile
@@ -338,9 +375,6 @@ int start_stop_daemon_main(int argc ATTRIBUTE_UNUSED, char **argv)
338 if (errno) 375 if (errno)
339 user_id = xuname2uid(userspec); 376 user_id = xuname2uid(userspec);
340 } 377 }
341 if (execname)
342 xstat(execname, &execstat);
343
344 do_procinit(); /* Both start and stop needs to know current processes */ 378 do_procinit(); /* Both start and stop needs to know current processes */
345 379
346 if (opt & CTX_STOP) { 380 if (opt & CTX_STOP) {
@@ -353,6 +387,10 @@ int start_stop_daemon_main(int argc ATTRIBUTE_UNUSED, char **argv)
353 printf("%s already running\n%d\n", execname, found->pid); 387 printf("%s already running\n%d\n", execname, found->pid);
354 return !(opt & OPT_OKNODO); 388 return !(opt & OPT_OKNODO);
355 } 389 }
390
391 if (execname)
392 xstat(execname, &execstat);
393
356 *--argv = startas; 394 *--argv = startas;
357 if (opt & OPT_BACKGROUND) { 395 if (opt & OPT_BACKGROUND) {
358#if BB_MMU 396#if BB_MMU
diff --git a/include/usage.h b/include/usage.h
index f912d7b88..56198cfa5 100644
--- a/include/usage.h
+++ b/include/usage.h
@@ -3613,46 +3613,65 @@
3613 USE_GETOPT_LONG("--start|--stop") SKIP_GETOPT_LONG("-S|-K") \ 3613 USE_GETOPT_LONG("--start|--stop") SKIP_GETOPT_LONG("-S|-K") \
3614 "] ... [-- arguments...]" 3614 "] ... [-- arguments...]"
3615#define start_stop_daemon_full_usage "\n\n" \ 3615#define start_stop_daemon_full_usage "\n\n" \
3616 "Start and stop services\n" \ 3616 "Search for matching processes, and then\n" \
3617 "\nOptions:" \ 3617 "-S: stop all matching processes.\n" \
3618 "-K: start a process unless a matching process is found.\n" \
3618 USE_GETOPT_LONG( \ 3619 USE_GETOPT_LONG( \
3619 "\n -S,--start Start" \ 3620 "\nProcess matching:" \
3620 "\n -K,--stop Stop" \ 3621 "\n -u,--user USERNAME|UID Match only this user's processes" \
3621 "\n -a,--startas pathname Start process specified by pathname" \ 3622 "\n -n,--name NAME Match processes with NAME" \
3622 "\n -b,--background Put process into background" \ 3623 "\n in comm field in /proc/PID/stat" \
3623 "\n -u,--user username|uid Stop this user's processes" \ 3624 "\n -x,--exec EXECUTABLE Match processes with this command" \
3624 "\n -x,--exec executable Program to either start or check" \ 3625 "\n in /proc/PID/cmdline" \
3625 "\n -n,--name process-name Stop processes with this name" \ 3626 "\n -p,--pidfile FILE Match a process with PID from the file" \
3626 "\n -p,--pidfile pid-file Save or load pid using a pid-file" \ 3627 "\n All specified conditions must match" \
3627 "\n -m,--make-pidfile Create the -p file and enter pid in it" \ 3628 "\n-K only:" \
3628 "\n -q,--quiet Quiet" \ 3629 "\n -x,--exec EXECUTABLE Program to run" \
3630 "\n -a,--startas NAME Zeroth argument" \
3631 "\n -b,--background Background" \
3629 USE_FEATURE_START_STOP_DAEMON_FANCY( \ 3632 USE_FEATURE_START_STOP_DAEMON_FANCY( \
3630 "\n -o,--oknodo Exit status 0 if nothing done" \ 3633 "\n -N,--nicelevel N Change nice level" \
3631 "\n -v,--verbose Verbose" \
3632 "\n -N,--nicelevel N Add N to process's nice level" \
3633 ) \ 3634 ) \
3634 "\n -s,--signal signal Signal to send (default TERM)" \ 3635 "\n -c,--chuid USER[:[GRP]] Change to user/group" \
3635 "\n -c,--chuid user[:[grp]] Change to specified user/group" \ 3636 "\n -m,--make-pidfile Write PID to the pidfile specified by -p" \
3637 "\n-S only:" \
3638 "\n -s,--signal SIG Signal to send" \
3639 "\n -t,--test Match only, exit with 0 if a process is found" \
3640 "\nOther:" \
3641 USE_FEATURE_START_STOP_DAEMON_FANCY( \
3642 "\n -o,--oknodo Exit with status 0 if nothing is done" \
3643 "\n -q,--quiet Quiet" \
3644 ) \
3645 "\n -v,--verbose Verbose" \
3636 ) \ 3646 ) \
3637 SKIP_GETOPT_LONG( \ 3647 SKIP_GETOPT_LONG( \
3638 "\n -S Start" \ 3648 "\nProcess matching:" \
3639 "\n -K Stop" \ 3649 "\n -u USERNAME|UID Match only this user's processes" \
3640 "\n -a pathname Start process specified by pathname" \ 3650 "\n -n NAME Match processes with NAME" \
3641 "\n -b Put process into background" \ 3651 "\n in comm field in /proc/PID/stat" \
3642 "\n -u username|uid Stop this user's processes" \ 3652 "\n -x EXECUTABLE Match processes with this command" \
3643 "\n -x executable Program to either start or check" \ 3653 "\n command in /proc/PID/cmdline" \
3644 "\n -n process-name Stop processes with this name" \ 3654 "\n -p FILE Match a process with PID from the file" \
3645 "\n -p pid-file Save or load pid using a pid-file" \ 3655 "\n All specified conditions must match" \
3646 "\n -m Create the -p file and enter pid in it" \ 3656 "\n-K only:" \
3647 "\n -q Quiet" \ 3657 "\n -x EXECUTABLE Program to run" \
3658 "\n -a NAME Zeroth argument" \
3659 "\n -b Background" \
3660 USE_FEATURE_START_STOP_DAEMON_FANCY( \
3661 "\n -N N Change nice level" \
3662 ) \
3663 "\n -c USER[:[GRP]] Change to user/group" \
3664 "\n -m Write PID to the pidfile specified by -p" \
3665 "\n-S only:" \
3666 "\n -s SIG Signal to send" \
3667 "\n -t Match only, exit with 0 if a process is found" \
3668 "\nOther:" \
3648 USE_FEATURE_START_STOP_DAEMON_FANCY( \ 3669 USE_FEATURE_START_STOP_DAEMON_FANCY( \
3649 "\n -o Exit status 0 if nothing done" \ 3670 "\n -o Exit with status 0 if nothing is done" \
3671 "\n -q Quiet" \
3672 ) \
3650 "\n -v Verbose" \ 3673 "\n -v Verbose" \
3651 "\n -N N Add N to process's nice level" \
3652 ) \ 3674 ) \
3653 "\n -s signal Signal to send (default TERM)" \
3654 "\n -c user[:[grp]] Change to specified user/group" \
3655 )
3656 3675
3657#define stat_trivial_usage \ 3676#define stat_trivial_usage \
3658 "[OPTION] FILE..." 3677 "[OPTION] FILE..."