blob: 0152283ad5458703600ad235686970f3ca28dac4 [file] [log] [blame]
Eric Andersenc2af1ee2001-10-18 19:33:06 +00001/* vi: set sw=4 ts=4: */
2/*
3 * Mini start-stop-daemon implementation(s) for busybox
4 *
5 * Written by Marek Michalkiewicz <marekm@i17linuxb.ists.pwr.wroc.pl>,
6 * public domain.
7 * Adapted for busybox David Kimdon <dwhedon@gordian.com>
8 */
9
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13#include <stdarg.h>
14#include <signal.h>
15#include <errno.h>
16#include <sys/stat.h>
17#include <dirent.h>
18#include <unistd.h>
19#include <pwd.h>
20
21#include "busybox.h"
22
23static int start = 0;
24static int stop = 0;
25static int signal_nr = 15;
26static int user_id = -1;
27static const char *userspec = NULL;
28static const char *cmdname = NULL;
29static char *execname = NULL;
30static char *startas = NULL;
31static const char *progname = "";
32
33struct pid_list {
34 struct pid_list *next;
35 int pid;
36};
37
38static struct pid_list *found = NULL;
39static struct pid_list *killed = NULL;
40
41static void
42push(struct pid_list **list, int pid)
43{
44 struct pid_list *p;
45
46 p = xmalloc(sizeof(*p));
47 p->next = *list;
48 p->pid = pid;
49 *list = p;
50}
51
52
53static void
54parse_options(int argc, char * const *argv)
55{
56
57 int c;
58
59 for (;;) {
60 c = getopt (argc, argv, "a:n:s:u:x:KS");
61 if (c == EOF)
62 break;
63 switch (c) {
64 case 'K':
65 stop = 1;
66 break;
67 case 'S':
68 start = 1;
69 break;
70 case 'a':
71 startas = optarg;
72 break;
73 case 'n':
74 cmdname = optarg;
75 break;
76 case 's':
77 if (sscanf(optarg, "%d", &signal_nr) != 1)
78 error_msg_and_die ("-s takes a numeric argument");
79 break;
80 case 'u':
81 userspec = optarg;
82 break;
83 case 'x':
84 execname = optarg;
85 break;
86 default:
87 show_usage();
88 exit(1);
89 }
90 }
91
92 if (start == stop)
93 error_msg_and_die ("need one of -S or -K");
94
95 if (!execname && !userspec)
96 error_msg_and_die ("need at least one of -x or -u");
97
98 if (!startas)
99 startas = execname;
100
101 if (start && !startas)
102 error_msg_and_die ("-S needs -x or -a");
103}
104
105
106static int
107pid_is_exec(int pid, const char *exec)
108{
109 char buf[PATH_MAX];
110 FILE *fp;
111
112 sprintf(buf, "/proc/%d/cmdline", pid);
113 fp = fopen(buf, "r");
114 if (fp && fgets (buf, sizeof (buf), fp) ) {
115 if (strncmp (buf, exec, strlen(exec)) == 0)
116 return 1;
117 }
118 return 0;
119}
120
121
122static int
123pid_is_user(int pid, int uid)
124{
125 struct stat sb;
126 char buf[32];
127
128 sprintf(buf, "/proc/%d", pid);
129 if (stat(buf, &sb) != 0)
130 return 0;
131 return (sb.st_uid == uid);
132}
133
134
135static int
136pid_is_cmd(int pid, const char *name)
137{
138 char buf[32];
139 FILE *f;
140 int c;
141
142 sprintf(buf, "/proc/%d/stat", pid);
143 f = fopen(buf, "r");
144 if (!f)
145 return 0;
146 while ((c = getc(f)) != EOF && c != '(')
147 ;
148 if (c != '(') {
149 fclose(f);
150 return 0;
151 }
152 /* this hopefully handles command names containing ')' */
153 while ((c = getc(f)) != EOF && c == *name)
154 name++;
155 fclose(f);
156 return (c == ')' && *name == '\0');
157}
158
159
160static void
161check(int pid)
162{
163 if (execname && !pid_is_exec(pid, execname)) {
164 return;
165 }
166 if (userspec && !pid_is_user(pid, user_id)) {
167 return;
168 }
169 if (cmdname && !pid_is_cmd(pid, cmdname)) {
170 return;
171 }
172 push(&found, pid);
173}
174
175
176
177static void
178do_procfs(void)
179{
180 DIR *procdir;
181 struct dirent *entry;
182 int foundany, pid;
183
184 procdir = opendir("/proc");
185 if (!procdir)
186 perror_msg_and_die ("opendir /proc");
187
188 foundany = 0;
189 while ((entry = readdir(procdir)) != NULL) {
190 if (sscanf(entry->d_name, "%d", &pid) != 1)
191 continue;
192 foundany++;
193 check(pid);
194 }
195 closedir(procdir);
196 if (!foundany)
197 error_msg_and_die ("nothing in /proc - not mounted?");
198}
199
200
201static void
202do_stop(void)
203{
204 char what[1024];
205 struct pid_list *p;
206
207 if (cmdname)
208 strcpy(what, cmdname);
209 else if (execname)
210 strcpy(what, execname);
211 else if (userspec)
212 sprintf(what, "process(es) owned by `%s'", userspec);
213 else
214 error_msg_and_die ("internal error, please report");
215
216 if (!found) {
217 printf("no %s found; none killed.\n", what);
218 exit(0);
219 }
220 for (p = found; p; p = p->next) {
221 if (kill(p->pid, signal_nr) == 0)
222 push(&killed, p->pid);
223 else
224 printf("%s: warning: failed to kill %d: %s\n",
225 progname, p->pid, strerror(errno));
226 }
227 if (killed) {
228 printf("stopped %s (pid", what);
229 for (p = killed; p; p = p->next)
230 printf(" %d", p->pid);
231 printf(").\n");
232 }
233}
234
235
236int
237start_stop_daemon_main(int argc, char **argv)
238{
239 progname = argv[0];
240
241 parse_options(argc, argv);
242 argc -= optind;
243 argv += optind;
244
245 if (userspec && sscanf(userspec, "%d", &user_id) != 1) {
246 struct passwd *pw;
247
248 pw = getpwnam(userspec);
249 if (!pw)
250 error_msg_and_die ("user `%s' not found\n", userspec);
251
252 user_id = pw->pw_uid;
253 }
254
255 do_procfs();
256
257 if (stop) {
258 do_stop();
259 exit(0);
260 }
261
262 if (found) {
263 printf("%s already running.\n", execname);
264 printf("%d\n",found->pid);
265 exit(0);
266 }
267 *--argv = startas;
268 execv(startas, argv);
269 perror_msg_and_die ("unable to start %s", startas);
270}
271