a58ba15cf4824189a715c29c35530ef225f27139
[proj/openrc.git] / src / librc / librc-daemon.c
1 /*
2 librc-daemon
3 Finds PID for given daemon criteria
4 */
5
6 /*
7 * Copyright 2007-2008 Roy Marples <roy@marples.name>
8 * All rights reserved
9
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 #include "librc.h"
33
34 #ifdef __GLIBC__
35 # if ! defined (__UCLIBC__) && ! defined (__dietlibc__)
36 static size_t strlcpy(char *dst, const char *src, size_t size)
37 {
38 const char *s = src;
39 size_t n = size;
40
41 if (n && --n)
42 do {
43 if (! (*dst++ = *src++))
44 break;
45 } while (--n);
46
47 if (! n) {
48 if (size)
49 *dst = '\0';
50 while (*src++);
51 }
52
53 return src - s - 1;
54 }
55 # endif
56 #endif
57
58 #if defined(__linux__)
59 static bool pid_is_cmd(pid_t pid, const char *cmd)
60 {
61 char buffer[32];
62 FILE *fp;
63 int c;
64
65 snprintf(buffer, sizeof(buffer), "/proc/%d/stat", pid);
66 if ((fp = fopen(buffer, "r")) == NULL)
67 return false;
68
69 while ((c = getc(fp)) != EOF && c != '(')
70 ;
71
72 if (c != '(') {
73 fclose(fp);
74 return false;
75 }
76
77 while ((c = getc(fp)) != EOF && c == *cmd)
78 cmd++;
79
80 fclose(fp);
81
82 return (c == ')' && *cmd == '\0') ? true : false;
83 }
84
85 static bool pid_is_exec(pid_t pid, const char *const *argv)
86 {
87 char cmdline[32];
88 char buffer[PATH_MAX];
89 char *p;
90 int fd = -1;
91 int r;
92
93 /* Check it's the right binary */
94 snprintf (cmdline, sizeof (cmdline), "/proc/%u/exe", pid);
95 memset (buffer, 0, sizeof (buffer));
96 if (readlink(cmdline, buffer, sizeof(buffer)) != -1) {
97 if (strcmp(*argv, buffer) == 0)
98 return true;
99
100 /* We should cater for deleted binaries too */
101 if (strlen(buffer) > 10) {
102 p = buffer + (strlen(buffer) - 10);
103 if (strcmp(p, " (deleted)") == 0) {
104 *p = 0;
105 if (strcmp(buffer, *argv) == 0)
106 return true;
107 }
108 }
109 }
110
111 snprintf(cmdline, sizeof(cmdline), "/proc/%u/cmdline", pid);
112 if ((fd = open(cmdline, O_RDONLY)) < 0)
113 return false;
114
115 r = read(fd, buffer, sizeof(buffer));
116 close(fd);
117
118 if (r == -1)
119 return 0;
120
121 buffer[r] = 0;
122 p = buffer;
123 while (*argv) {
124 if (strcmp(*argv, p) != 0)
125 return false;
126 argv++;
127 p += strlen(p) + 1;
128 if ((unsigned) (p - buffer) > sizeof (buffer))
129 return false;
130 }
131 return true;
132 }
133
134 RC_PIDLIST *rc_find_pids(const char *const *argv, const char *cmd,
135 uid_t uid, pid_t pid)
136 {
137 DIR *procdir;
138 struct dirent *entry;
139 pid_t p;
140 char buffer[PATH_MAX];
141 struct stat sb;
142 pid_t runscript_pid = 0;
143 char *pp;
144 RC_PIDLIST *pids = NULL;
145 RC_PID *pi;
146
147 if ((procdir = opendir("/proc")) == NULL)
148 return NULL;
149
150 /*
151 We never match RC_RUNSCRIPT_PID if present so we avoid the below
152 scenario
153
154 /etc/init.d/ntpd stop does
155 start-stop-daemon --stop --name ntpd
156 catching /etc/init.d/ntpd stop
157
158 nasty
159 */
160
161 if ((pp = getenv("RC_RUNSCRIPT_PID"))) {
162 if (sscanf(pp, "%d", &runscript_pid) != 1)
163 runscript_pid = 0;
164 }
165
166 while ((entry = readdir(procdir)) != NULL) {
167 if (sscanf(entry->d_name, "%d", &p) != 1)
168 continue;
169
170 if (runscript_pid != 0 && runscript_pid == p)
171 continue;
172
173 if (pid != 0 && pid != p)
174 continue;
175
176 if (uid) {
177 snprintf(buffer, sizeof(buffer), "/proc/%d", p);
178 if (stat(buffer, &sb) != 0 || sb.st_uid != uid)
179 continue;
180 }
181
182 if (cmd && ! pid_is_cmd(p, cmd))
183 continue;
184
185 if (argv && ! cmd && !
186 pid_is_exec(p, (const char *const *)argv))
187 continue;
188
189 if (! pids) {
190 pids = xmalloc(sizeof(*pids));
191 LIST_INIT(pids);
192 }
193 pi = xmalloc(sizeof(*pi));
194 pi->pid = p;
195 LIST_INSERT_HEAD(pids, pi, entries);
196 }
197 closedir(procdir);
198
199 return pids;
200 }
201 librc_hidden_def(rc_find_pids)
202
203 #elif BSD
204
205 # if defined(__NetBSD__) || defined(__OpenBSD__)
206 # define _KVM_GETPROC2
207 # define _KINFO_PROC kinfo_proc2
208 # define _KVM_GETARGV kvm_getargv2
209 # define _GET_KINFO_UID(kp) (kp.p_ruid)
210 # define _GET_KINFO_COMM(kp) (kp.p_comm)
211 # define _GET_KINFO_PID(kp) (kp.p_pid)
212 # define _KVM_PATH NULL
213 # define _KVM_FLAGS KVM_NO_FILES
214 # else
215 # ifndef KERN_PROC_PROC
216 # define KERN_PROC_PROC KERN_PROC_ALL
217 # endif
218 # define _KINFO_PROC kinfo_proc
219 # define _KVM_GETARGV kvm_getargv
220 # define _GET_KINFO_UID(kp) (kp.ki_ruid)
221 # define _GET_KINFO_COMM(kp) (kp.ki_comm)
222 # define _GET_KINFO_PID(kp) (kp.ki_pid)
223 # define _KVM_PATH _PATH_DEVNULL
224 # define _KVM_FLAGS O_RDONLY
225 # endif
226
227 RC_PIDLIST *rc_find_pids(const char *const *argv, const char *cmd,
228 uid_t uid, pid_t pid)
229 {
230 static kvm_t *kd = NULL;
231 char errbuf[_POSIX2_LINE_MAX];
232 struct _KINFO_PROC *kp;
233 int i;
234 int processes = 0;
235 int pargc = 0;
236 char **pargv;
237 RC_PIDLIST *pids = NULL;
238 RC_PID *pi;
239 pid_t p;
240 const char *const *arg;
241 int match;
242
243 if ((kd = kvm_openfiles(_KVM_PATH, _KVM_PATH,
244 NULL, _KVM_FLAGS, errbuf)) == NULL)
245 {
246 fprintf(stderr, "kvm_open: %s\n", errbuf);
247 return NULL;
248 }
249
250 #ifdef _KVM_GETPROC2
251 kp = kvm_getproc2(kd, KERN_PROC_ALL, 0, sizeof(*kp), &processes);
252 #else
253 kp = kvm_getprocs(kd, KERN_PROC_PROC, 0, &processes);
254 #endif
255 if ((kp == NULL && processes > 0) || (kp != NULL && processes < 0)) {
256 fprintf(stderr, "kvm_getprocs: %s\n", kvm_geterr(kd));
257 kvm_close(kd);
258 return NULL;
259 }
260
261 for (i = 0; i < processes; i++) {
262 p = _GET_KINFO_PID(kp[i]);
263 if (pid != 0 && pid != p)
264 continue;
265
266 if (uid != 0 && uid != _GET_KINFO_UID(kp[i]))
267 continue;
268
269 if (cmd) {
270 if (! _GET_KINFO_COMM(kp[i]) ||
271 strcmp(cmd, _GET_KINFO_COMM(kp[i])) != 0)
272 continue;
273 }
274
275 if (argv && *argv && ! cmd) {
276 pargv = _KVM_GETARGV(kd, &kp[i], pargc);
277 if (! pargv || ! *pargv)
278 continue;
279
280 arg = argv;
281 match = 1;
282
283 while (*arg && *pargv)
284 if (strcmp(*arg++, *pargv++) != 0) {
285 match = 0;
286 break;
287 }
288
289 if (! match)
290 continue;
291 }
292
293 if (! pids) {
294 pids = xmalloc(sizeof(*pids));
295 LIST_INIT(pids);
296 }
297 pi = xmalloc(sizeof(*pi));
298 pi->pid = p;
299 LIST_INSERT_HEAD(pids, pi, entries);
300 }
301 kvm_close(kd);
302
303 return pids;
304 }
305 librc_hidden_def(rc_find_pids)
306
307 #else
308 # error "Platform not supported!"
309 #endif
310
311 static bool _match_daemon(const char *path, const char *file,
312 RC_STRINGLIST *match)
313 {
314 char *line = NULL;
315 size_t len = 0;
316 char ffile[PATH_MAX];
317 FILE *fp;
318 RC_STRING *m;
319
320 snprintf(ffile, sizeof(ffile), "%s/%s", path, file);
321 fp = fopen(ffile, "r");
322
323 if (! fp)
324 return false;
325
326 while ((rc_getline(&line, &len, fp))) {
327 TAILQ_FOREACH(m, match, entries)
328 if (strcmp(line, m->value) == 0) {
329 TAILQ_REMOVE(match, m, entries);
330 break;
331 }
332 if (! TAILQ_FIRST(match))
333 break;
334 }
335 fclose(fp);
336 free(line);
337 if (TAILQ_FIRST(match))
338 return false;
339 return true;
340 }
341
342 static RC_STRINGLIST *_match_list(const char* const* argv,
343 const char *name, const char *pidfile)
344 {
345 RC_STRINGLIST *match = rc_stringlist_new();
346 int i = 0;
347 size_t l;
348 char *m;
349
350 while (argv && argv[i]) {
351 l = strlen(*argv) + strlen("argv_=") + 16;
352 m = xmalloc(sizeof(char) * l);
353 snprintf(m, l, "argv_0=%s", argv[i++]);
354 rc_stringlist_add(match, m);
355 free(m);
356 }
357
358 if (name) {
359 l = strlen(name) + 6;
360 m = xmalloc(sizeof (char) * l);
361 snprintf(m, l, "name=%s", name);
362 rc_stringlist_add(match, m);
363 free(m);
364 }
365
366 if (pidfile) {
367 l = strlen(pidfile) + 9;
368 m = xmalloc(sizeof (char) * l);
369 snprintf(m, l, "pidfile=%s", pidfile);
370 rc_stringlist_add(match, m);
371 free (m);
372 }
373
374 return match;
375 }
376
377 bool rc_service_daemon_set(const char *service, const char *const *argv,
378 const char *name, const char *pidfile, bool started)
379 {
380 char dirpath[PATH_MAX];
381 char file[PATH_MAX];
382 int nfiles = 0;
383 char oldfile[PATH_MAX] = { '\0' };
384 bool retval = false;
385 DIR *dp;
386 struct dirent *d;
387 RC_STRINGLIST *match;
388 int i = 0;
389 FILE *fp;
390
391 if (!(argv && *argv) && ! name && ! pidfile) {
392 errno = EINVAL;
393 return false;
394 }
395
396 snprintf(dirpath, sizeof(dirpath), RC_SVCDIR "/daemons/%s",
397 basename_c(service));
398
399 /* Regardless, erase any existing daemon info */
400 if ((dp = opendir(dirpath))) {
401 match = _match_list(argv, name, pidfile);
402 while ((d = readdir(dp))) {
403 if (d->d_name[0] == '.')
404 continue;
405
406 snprintf(file, sizeof(file), "%s/%s",
407 dirpath, d->d_name);
408 nfiles++;
409
410 if (! *oldfile) {
411 if (_match_daemon(dirpath, d->d_name, match)) {
412 unlink(file);
413 strlcpy(oldfile, file, sizeof(oldfile));
414 nfiles--;
415 }
416 } else {
417 rename(file, oldfile);
418 strlcpy(oldfile, file, sizeof(oldfile));
419 }
420 }
421 closedir(dp);
422 rc_stringlist_free(match);
423 }
424
425 /* Now store our daemon info */
426 if (started) {
427 if (mkdir(dirpath, 0755) == 0 || errno == EEXIST) {
428 snprintf(file, sizeof(file), "%s/%03d",
429 dirpath, nfiles + 1);
430 if ((fp = fopen(file, "w"))) {
431 while (argv && argv[i]) {
432 fprintf(fp, "argv_%d=%s\n", i, argv[i]);
433 i++;
434 }
435 fprintf(fp, "name=");
436 if (name)
437 fprintf(fp, "%s", name);
438 fprintf(fp, "\npidfile=");
439 if (pidfile)
440 fprintf(fp, "%s", pidfile);
441 fprintf(fp, "\n");
442 fclose(fp);
443 retval = true;
444 }
445 }
446 } else
447 retval = true;
448
449 return retval;
450 }
451 librc_hidden_def(rc_service_daemon_set)
452
453 bool rc_service_started_daemon(const char *service, const char *const *argv,
454 int indx)
455 {
456 char dirpath[PATH_MAX];
457 char file[16];
458 RC_STRINGLIST *match;
459 bool retval = false;
460 DIR *dp;
461 struct dirent *d;
462
463 if (!service || !(argv && *argv))
464 return false;
465
466 snprintf(dirpath, sizeof(dirpath), RC_SVCDIR "/daemons/%s",
467 basename_c(service));
468 match = _match_list(argv, NULL, NULL);
469
470 if (indx > 0) {
471 snprintf(file, sizeof(file), "%03d", indx);
472 retval = _match_daemon(dirpath, file, match);
473 } else {
474 if ((dp = opendir(dirpath))) {
475 while ((d = readdir(dp))) {
476 if (d->d_name[0] == '.')
477 continue;
478 retval = _match_daemon(dirpath, d->d_name, match);
479 if (retval)
480 break;
481 }
482 closedir(dp);
483 }
484 }
485
486 rc_stringlist_free(match);
487 return retval;
488 }
489 librc_hidden_def(rc_service_started_daemon)
490
491 bool rc_service_daemons_crashed(const char *service)
492 {
493 char dirpath[PATH_MAX];
494 DIR *dp;
495 struct dirent *d;
496 char *path = dirpath;
497 FILE *fp;
498 char *line = NULL;
499 size_t len = 0;
500 char **argv = NULL;
501 char *exec = NULL;
502 char *name = NULL;
503 char *pidfile = NULL;
504 pid_t pid = 0;
505 RC_PIDLIST *pids;
506 RC_PID *p1;
507 RC_PID *p2;
508 char *p;
509 char *token;
510 bool retval = false;
511 RC_STRINGLIST *list = NULL;
512 RC_STRING *s;
513 size_t i;
514
515 path += snprintf(dirpath, sizeof(dirpath), RC_SVCDIR "/daemons/%s",
516 basename_c(service));
517
518 if (! (dp = opendir(dirpath)))
519 return false;
520
521 while ((d = readdir(dp))) {
522 if (d->d_name[0] == '.')
523 continue;
524
525 snprintf(path, sizeof(dirpath) - (path - dirpath), "/%s",
526 d->d_name);
527 fp = fopen(dirpath, "r");
528 if (! fp)
529 break;
530
531 while ((rc_getline(&line, &len, fp))) {
532 p = line;
533 if ((token = strsep(&p, "=")) == NULL || ! p)
534 continue;
535
536 if (! *p)
537 continue;
538
539 if (strncmp(token, "argv_", 5) == 0) {
540 if (! list)
541 list = rc_stringlist_new();
542 rc_stringlist_add(list, p);
543 } else if (strcmp(token, "exec") == 0) {
544 if (exec)
545 free(exec);
546 exec = xstrdup(p);
547 } else if (strcmp(token, "name") == 0) {
548 if (name)
549 free(name);
550 name = xstrdup(p);
551 } else if (strcmp(token, "pidfile") == 0) {
552 pidfile = xstrdup(p);
553 break;
554 }
555 }
556 fclose(fp);
557
558 pid = 0;
559 if (pidfile) {
560 retval = true;
561 if ((fp = fopen(pidfile, "r"))) {
562 if (fscanf(fp, "%d", &pid) == 1)
563 retval = false;
564
565 fclose (fp);
566 }
567 free(pidfile);
568 pidfile = NULL;
569
570 /* We have the pid, so no need to match on name */
571 free (name);
572 name = NULL;
573 } else {
574 if (exec) {
575 if (! list)
576 list = rc_stringlist_new();
577 if (! TAILQ_FIRST(list))
578 rc_stringlist_add(list, exec);
579
580 free(exec);
581 exec = NULL;
582 }
583
584 if (list) {
585 /* We need to flatten our linked list into an array */
586 i = 0;
587 TAILQ_FOREACH(s, list, entries)
588 i++;
589 argv = xmalloc(sizeof(char *) * (i + 1));
590 i = 0;
591 TAILQ_FOREACH(s, list, entries)
592 argv[i++] = s->value;
593 argv[i] = '\0';
594 }
595 }
596
597 if (! retval) {
598 if ((pids = rc_find_pids((const char *const *)argv,
599 name, 0, pid)))
600 {
601 p1 = LIST_FIRST(pids);
602 while (p1) {
603 p2 = LIST_NEXT(p1, entries);
604 free(p1);
605 p1 = p2;
606 }
607 free(pids);
608 } else
609 retval = true;
610 }
611 rc_stringlist_free(list);
612 list = NULL;
613 free(argv);
614 argv = NULL;
615 free(exec);
616 exec = NULL;
617 free(name);
618 name = NULL;
619 if (retval)
620 break;
621 }
622 closedir(dp);
623 free(line);
624
625 return retval;
626 }
627 librc_hidden_def(rc_service_daemons_crashed)