0152283ad5458703600ad235686970f3ca28dac4
[oweals/busybox.git] / debianutils / start_stop_daemon.c
1 /* 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
23 static int start = 0;
24 static int stop = 0;
25 static int signal_nr = 15;
26 static int user_id = -1;
27 static const char *userspec = NULL;
28 static const char *cmdname = NULL;
29 static char *execname = NULL;
30 static char *startas = NULL;
31 static const char *progname = "";
32
33 struct pid_list {
34         struct pid_list *next;
35         int pid;
36 };
37
38 static struct pid_list *found = NULL;
39 static struct pid_list *killed = NULL;
40
41 static void
42 push(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
53 static void
54 parse_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
106 static int
107 pid_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
122 static int
123 pid_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
135 static int
136 pid_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
160 static void
161 check(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
177 static void
178 do_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
201 static void
202 do_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
236 int
237 start_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