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