3 Finds PID for given daemon criteria
7 * Copyright 2007-2008 Roy Marples <roy@marples.name>
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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.
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
35 # if ! defined (__UCLIBC__) && ! defined (__dietlibc__)
36 static size_t strlcpy(char *dst
, const char *src
, size_t size
)
43 if (! (*dst
++ = *src
++))
58 #if defined(__linux__)
59 static bool pid_is_cmd(pid_t pid
, const char *cmd
)
65 snprintf(buffer
, sizeof(buffer
), "/proc/%d/stat", pid
);
66 if ((fp
= fopen(buffer
, "r")) == NULL
)
69 while ((c
= getc(fp
)) != EOF
&& c
!= '(')
77 while ((c
= getc(fp
)) != EOF
&& c
== *cmd
)
82 return (c
== ')' && *cmd
== '\0') ?
true : false;
85 static bool pid_is_exec(pid_t pid
, const char *const *argv
)
88 char buffer
[PATH_MAX
];
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)
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) {
105 if (strcmp(buffer
, *argv
) == 0)
111 snprintf(cmdline
, sizeof(cmdline
), "/proc/%u/cmdline", pid
);
112 if ((fd
= open(cmdline
, O_RDONLY
)) < 0)
115 r
= read(fd
, buffer
, sizeof(buffer
));
124 if (strcmp(*argv
, p
) != 0)
128 if ((unsigned) (p
- buffer
) > sizeof (buffer
))
134 RC_PIDLIST
*rc_find_pids(const char *const *argv
, const char *cmd
,
135 uid_t uid
, pid_t pid
)
138 struct dirent
*entry
;
140 char buffer
[PATH_MAX
];
142 pid_t runscript_pid
= 0;
144 RC_PIDLIST
*pids
= NULL
;
147 if ((procdir
= opendir("/proc")) == NULL
)
151 We never match RC_RUNSCRIPT_PID if present so we avoid the below
154 /etc/init.d/ntpd stop does
155 start-stop-daemon --stop --name ntpd
156 catching /etc/init.d/ntpd stop
161 if ((pp
= getenv("RC_RUNSCRIPT_PID"))) {
162 if (sscanf(pp
, "%d", &runscript_pid
) != 1)
166 while ((entry
= readdir(procdir
)) != NULL
) {
167 if (sscanf(entry
->d_name
, "%d", &p
) != 1)
170 if (runscript_pid
!= 0 && runscript_pid
== p
)
173 if (pid
!= 0 && pid
!= p
)
177 snprintf(buffer
, sizeof(buffer
), "/proc/%d", p
);
178 if (stat(buffer
, &sb
) != 0 || sb
.st_uid
!= uid
)
182 if (cmd
&& ! pid_is_cmd(p
, cmd
))
185 if (argv
&& ! cmd
&& !
186 pid_is_exec(p
, (const char *const *)argv
))
190 pids
= xmalloc(sizeof(*pids
));
193 pi
= xmalloc(sizeof(*pi
));
195 LIST_INSERT_HEAD(pids
, pi
, entries
);
201 librc_hidden_def(rc_find_pids
)
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
215 # ifndef KERN_PROC_PROC
216 # define KERN_PROC_PROC KERN_PROC_ALL
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
227 RC_PIDLIST
*rc_find_pids(const char *const *argv
, const char *cmd
,
228 uid_t uid
, pid_t pid
)
230 static kvm_t
*kd
= NULL
;
231 char errbuf
[_POSIX2_LINE_MAX
];
232 struct _KINFO_PROC
*kp
;
237 RC_PIDLIST
*pids
= NULL
;
240 const char *const *arg
;
243 if ((kd
= kvm_openfiles(_KVM_PATH
, _KVM_PATH
,
244 NULL
, _KVM_FLAGS
, errbuf
)) == NULL
)
246 fprintf(stderr
, "kvm_open: %s\n", errbuf
);
251 kp
= kvm_getproc2(kd
, KERN_PROC_ALL
, 0, sizeof(*kp
), &processes
);
253 kp
= kvm_getprocs(kd
, KERN_PROC_PROC
, 0, &processes
);
255 if ((kp
== NULL
&& processes
> 0) || (kp
!= NULL
&& processes
< 0)) {
256 fprintf(stderr
, "kvm_getprocs: %s\n", kvm_geterr(kd
));
261 for (i
= 0; i
< processes
; i
++) {
262 p
= _GET_KINFO_PID(kp
[i
]);
263 if (pid
!= 0 && pid
!= p
)
266 if (uid
!= 0 && uid
!= _GET_KINFO_UID(kp
[i
]))
270 if (! _GET_KINFO_COMM(kp
[i
]) ||
271 strcmp(cmd
, _GET_KINFO_COMM(kp
[i
])) != 0)
275 if (argv
&& *argv
&& ! cmd
) {
276 pargv
= _KVM_GETARGV(kd
, &kp
[i
], pargc
);
277 if (! pargv
|| ! *pargv
)
283 while (*arg
&& *pargv
)
284 if (strcmp(*arg
++, *pargv
++) != 0) {
294 pids
= xmalloc(sizeof(*pids
));
297 pi
= xmalloc(sizeof(*pi
));
299 LIST_INSERT_HEAD(pids
, pi
, entries
);
305 librc_hidden_def(rc_find_pids
)
308 # error "Platform not supported!"
311 static bool _match_daemon(const char *path
, const char *file
,
312 RC_STRINGLIST
*match
)
316 char ffile
[PATH_MAX
];
320 snprintf(ffile
, sizeof(ffile
), "%s/%s", path
, file
);
321 fp
= fopen(ffile
, "r");
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
);
332 if (! TAILQ_FIRST(match
))
337 if (TAILQ_FIRST(match
))
342 static RC_STRINGLIST
*_match_list(const char* const* argv
,
343 const char *name
, const char *pidfile
)
345 RC_STRINGLIST
*match
= rc_stringlist_new();
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
);
359 l
= strlen(name
) + 6;
360 m
= xmalloc(sizeof (char) * l
);
361 snprintf(m
, l
, "name=%s", name
);
362 rc_stringlist_add(match
, m
);
367 l
= strlen(pidfile
) + 9;
368 m
= xmalloc(sizeof (char) * l
);
369 snprintf(m
, l
, "pidfile=%s", pidfile
);
370 rc_stringlist_add(match
, m
);
377 bool rc_service_daemon_set(const char *service
, const char *const *argv
,
378 const char *name
, const char *pidfile
, bool started
)
380 char dirpath
[PATH_MAX
];
383 char oldfile
[PATH_MAX
] = { '\0' };
387 RC_STRINGLIST
*match
;
391 if (!(argv
&& *argv
) && ! name
&& ! pidfile
) {
396 snprintf(dirpath
, sizeof(dirpath
), RC_SVCDIR
"/daemons/%s",
397 basename_c(service
));
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] == '.')
406 snprintf(file
, sizeof(file
), "%s/%s",
411 if (_match_daemon(dirpath
, d
->d_name
, match
)) {
413 strlcpy(oldfile
, file
, sizeof(oldfile
));
417 rename(file
, oldfile
);
418 strlcpy(oldfile
, file
, sizeof(oldfile
));
422 rc_stringlist_free(match
);
425 /* Now store our daemon info */
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
]);
435 fprintf(fp
, "name=");
437 fprintf(fp
, "%s", name
);
438 fprintf(fp
, "\npidfile=");
440 fprintf(fp
, "%s", pidfile
);
451 librc_hidden_def(rc_service_daemon_set
)
453 bool rc_service_started_daemon(const char *service
, const char *const *argv
,
456 char dirpath
[PATH_MAX
];
458 RC_STRINGLIST
*match
;
463 if (!service
|| !(argv
&& *argv
))
466 snprintf(dirpath
, sizeof(dirpath
), RC_SVCDIR
"/daemons/%s",
467 basename_c(service
));
468 match
= _match_list(argv
, NULL
, NULL
);
471 snprintf(file
, sizeof(file
), "%03d", indx
);
472 retval
= _match_daemon(dirpath
, file
, match
);
474 if ((dp
= opendir(dirpath
))) {
475 while ((d
= readdir(dp
))) {
476 if (d
->d_name
[0] == '.')
478 retval
= _match_daemon(dirpath
, d
->d_name
, match
);
486 rc_stringlist_free(match
);
489 librc_hidden_def(rc_service_started_daemon
)
491 bool rc_service_daemons_crashed(const char *service
)
493 char dirpath
[PATH_MAX
];
496 char *path
= dirpath
;
503 char *pidfile
= NULL
;
511 RC_STRINGLIST
*list
= NULL
;
515 path
+= snprintf(dirpath
, sizeof(dirpath
), RC_SVCDIR
"/daemons/%s",
516 basename_c(service
));
518 if (! (dp
= opendir(dirpath
)))
521 while ((d
= readdir(dp
))) {
522 if (d
->d_name
[0] == '.')
525 snprintf(path
, sizeof(dirpath
) - (path
- dirpath
), "/%s",
527 fp
= fopen(dirpath
, "r");
531 while ((rc_getline(&line
, &len
, fp
))) {
533 if ((token
= strsep(&p
, "=")) == NULL
|| ! p
)
539 if (strncmp(token
, "argv_", 5) == 0) {
541 list
= rc_stringlist_new();
542 rc_stringlist_add(list
, p
);
543 } else if (strcmp(token
, "exec") == 0) {
547 } else if (strcmp(token
, "name") == 0) {
551 } else if (strcmp(token
, "pidfile") == 0) {
552 pidfile
= xstrdup(p
);
561 if ((fp
= fopen(pidfile
, "r"))) {
562 if (fscanf(fp
, "%d", &pid
) == 1)
570 /* We have the pid, so no need to match on name */
576 list
= rc_stringlist_new();
577 if (! TAILQ_FIRST(list
))
578 rc_stringlist_add(list
, exec
);
585 /* We need to flatten our linked list into an array */
587 TAILQ_FOREACH(s
, list
, entries
)
589 argv
= xmalloc(sizeof(char *) * (i
+ 1));
591 TAILQ_FOREACH(s
, list
, entries
)
592 argv
[i
++] = s
->value
;
598 if ((pids
= rc_find_pids((const char *const *)argv
,
601 p1
= LIST_FIRST(pids
);
603 p2
= LIST_NEXT(p1
, entries
);
611 rc_stringlist_free(list
);
627 librc_hidden_def(rc_service_daemons_crashed
)