3 Finds PID for given daemon criteria
7 * Copyright (c) 2007-2009 Roy Marples <roy@marples.name>
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 #if defined(__linux__)
35 pid_is_exec(pid_t pid
, const char *exec
)
42 exec
= basename_c(exec
);
43 snprintf(buffer
, sizeof(buffer
), "/proc/%d/stat", pid
);
44 if ((fp
= fopen(buffer
, "r"))) {
45 while ((c
= getc(fp
)) != EOF
&& c
!= '(')
48 while ((c
= getc(fp
)) != EOF
&& c
== *exec
)
50 if (c
== ')' && *exec
== '\0')
59 pid_is_argv(pid_t pid
, const char *const *argv
)
63 char buffer
[PATH_MAX
];
67 snprintf(cmdline
, sizeof(cmdline
), "/proc/%u/cmdline", pid
);
68 if ((fd
= open(cmdline
, O_RDONLY
)) < 0)
70 bytes
= read(fd
, buffer
, sizeof(buffer
));
78 if (strcmp(*argv
, p
) != 0)
82 if ((unsigned)(p
- buffer
) > sizeof(buffer
))
89 rc_find_pids(const char *exec
, const char *const *argv
, uid_t uid
, pid_t pid
)
94 char buffer
[PATH_MAX
];
96 pid_t runscript_pid
= 0;
98 RC_PIDLIST
*pids
= NULL
;
101 if ((procdir
= opendir("/proc")) == NULL
)
105 We never match RC_RUNSCRIPT_PID if present so we avoid the below
108 /etc/init.d/ntpd stop does
109 start-stop-daemon --stop --name ntpd
110 catching /etc/init.d/ntpd stop
115 if ((pp
= getenv("RC_RUNSCRIPT_PID"))) {
116 if (sscanf(pp
, "%d", &runscript_pid
) != 1)
120 while ((entry
= readdir(procdir
)) != NULL
) {
121 if (sscanf(entry
->d_name
, "%d", &p
) != 1)
123 if (runscript_pid
!= 0 && runscript_pid
== p
)
125 if (pid
!= 0 && pid
!= p
)
128 snprintf(buffer
, sizeof(buffer
), "/proc/%d", p
);
129 if (stat(buffer
, &sb
) != 0 || sb
.st_uid
!= uid
)
132 if (exec
&& !pid_is_exec(p
, exec
))
135 !pid_is_argv(p
, (const char *const *)argv
))
138 pids
= xmalloc(sizeof(*pids
));
141 pi
= xmalloc(sizeof(*pi
));
143 LIST_INSERT_HEAD(pids
, pi
, entries
);
148 librc_hidden_def(rc_find_pids
)
152 # if defined(__NetBSD__) || defined(__OpenBSD__)
153 # define _KVM_GETPROC2
154 # define _KINFO_PROC kinfo_proc2
155 # define _KVM_GETARGV kvm_getargv2
156 # define _GET_KINFO_UID(kp) (kp.p_ruid)
157 # define _GET_KINFO_COMM(kp) (kp.p_comm)
158 # define _GET_KINFO_PID(kp) (kp.p_pid)
159 # define _KVM_PATH NULL
160 # define _KVM_FLAGS KVM_NO_FILES
162 # ifndef KERN_PROC_PROC
163 # define KERN_PROC_PROC KERN_PROC_ALL
165 # define _KINFO_PROC kinfo_proc
166 # define _KVM_GETARGV kvm_getargv
167 # define _GET_KINFO_UID(kp) (kp.ki_ruid)
168 # define _GET_KINFO_COMM(kp) (kp.ki_comm)
169 # define _GET_KINFO_PID(kp) (kp.ki_pid)
170 # define _KVM_PATH _PATH_DEVNULL
171 # define _KVM_FLAGS O_RDONLY
175 rc_find_pids(const char *exec
, const char *const *argv
, uid_t uid
, pid_t pid
)
177 static kvm_t
*kd
= NULL
;
178 char errbuf
[_POSIX2_LINE_MAX
];
179 struct _KINFO_PROC
*kp
;
184 RC_PIDLIST
*pids
= NULL
;
187 const char *const *arg
;
190 if ((kd
= kvm_openfiles(_KVM_PATH
, _KVM_PATH
,
191 NULL
, _KVM_FLAGS
, errbuf
)) == NULL
)
193 fprintf(stderr
, "kvm_open: %s\n", errbuf
);
198 kp
= kvm_getproc2(kd
, KERN_PROC_ALL
, 0, sizeof(*kp
), &processes
);
200 kp
= kvm_getprocs(kd
, KERN_PROC_PROC
, 0, &processes
);
202 if ((kp
== NULL
&& processes
> 0) || (kp
!= NULL
&& processes
< 0)) {
203 fprintf(stderr
, "kvm_getprocs: %s\n", kvm_geterr(kd
));
209 exec
= basename_c(exec
);
210 for (i
= 0; i
< processes
; i
++) {
211 p
= _GET_KINFO_PID(kp
[i
]);
212 if (pid
!= 0 && pid
!= p
)
214 if (uid
!= 0 && uid
!= _GET_KINFO_UID(kp
[i
]))
217 if (!_GET_KINFO_COMM(kp
[i
]) ||
218 strcmp(exec
, _GET_KINFO_COMM(kp
[i
])) != 0)
222 pargv
= _KVM_GETARGV(kd
, &kp
[i
], pargc
);
223 if (!pargv
|| !*pargv
)
227 while (*arg
&& *pargv
)
228 if (strcmp(*arg
++, *pargv
++) != 0) {
236 pids
= xmalloc(sizeof(*pids
));
239 pi
= xmalloc(sizeof(*pi
));
241 LIST_INSERT_HEAD(pids
, pi
, entries
);
247 librc_hidden_def(rc_find_pids
)
250 # error "Platform not supported!"
254 _match_daemon(const char *path
, const char *file
, RC_STRINGLIST
*match
)
258 char ffile
[PATH_MAX
];
262 snprintf(ffile
, sizeof(ffile
), "%s/%s", path
, file
);
263 fp
= fopen(ffile
, "r");
268 while ((rc_getline(&line
, &len
, fp
))) {
269 TAILQ_FOREACH(m
, match
, entries
)
270 if (strcmp(line
, m
->value
) == 0) {
271 TAILQ_REMOVE(match
, m
, entries
);
274 if (!TAILQ_FIRST(match
))
279 if (TAILQ_FIRST(match
))
284 static RC_STRINGLIST
*
285 _match_list(const char *exec
, const char *const *argv
, const char *pidfile
)
287 RC_STRINGLIST
*match
= rc_stringlist_new();
293 l
= strlen(exec
) + 6;
294 m
= xmalloc(sizeof(char) * l
);
295 snprintf(m
, l
, "exec=%s", exec
);
296 rc_stringlist_add(match
, m
);
300 while (argv
&& argv
[i
]) {
301 l
= strlen(*argv
) + strlen("argv_=") + 16;
302 m
= xmalloc(sizeof(char) * l
);
303 snprintf(m
, l
, "argv_0=%s", argv
[i
++]);
304 rc_stringlist_add(match
, m
);
309 l
= strlen(pidfile
) + 9;
310 m
= xmalloc(sizeof(char) * l
);
311 snprintf(m
, l
, "pidfile=%s", pidfile
);
312 rc_stringlist_add(match
, m
);
320 rc_service_daemon_set(const char *service
, const char *exec
,
321 const char *const *argv
,
322 const char *pidfile
, bool started
)
324 char dirpath
[PATH_MAX
];
327 char oldfile
[PATH_MAX
] = { '\0' };
331 RC_STRINGLIST
*match
;
335 if (!exec
&& !pidfile
) {
340 snprintf(dirpath
, sizeof(dirpath
), RC_SVCDIR
"/daemons/%s",
341 basename_c(service
));
343 /* Regardless, erase any existing daemon info */
344 if ((dp
= opendir(dirpath
))) {
345 match
= _match_list(exec
, argv
, pidfile
);
346 while ((d
= readdir(dp
))) {
347 if (d
->d_name
[0] == '.')
350 snprintf(file
, sizeof(file
), "%s/%s",
355 if (_match_daemon(dirpath
, d
->d_name
, match
)) {
357 strlcpy(oldfile
, file
, sizeof(oldfile
));
361 rename(file
, oldfile
);
362 strlcpy(oldfile
, file
, sizeof(oldfile
));
366 rc_stringlist_free(match
);
369 /* Now store our daemon info */
371 if (mkdir(dirpath
, 0755) == 0 || errno
== EEXIST
) {
372 snprintf(file
, sizeof(file
), "%s/%03d",
373 dirpath
, nfiles
+ 1);
374 if ((fp
= fopen(file
, "w"))) {
375 fprintf(fp
, "exec=");
377 fprintf(fp
, "%s", exec
);
378 while (argv
&& argv
[i
]) {
379 fprintf(fp
, "\nargv_%d=%s", i
, argv
[i
]);
382 fprintf(fp
, "\npidfile=");
384 fprintf(fp
, "%s", pidfile
);
395 librc_hidden_def(rc_service_daemon_set
)
398 rc_service_started_daemon(const char *service
,
399 const char *exec
, const char *const *argv
, int indx
)
401 char dirpath
[PATH_MAX
];
403 RC_STRINGLIST
*match
;
408 if (!service
|| !exec
)
411 snprintf(dirpath
, sizeof(dirpath
), RC_SVCDIR
"/daemons/%s",
412 basename_c(service
));
413 match
= _match_list(exec
, argv
, NULL
);
416 snprintf(file
, sizeof(file
), "%03d", indx
);
417 retval
= _match_daemon(dirpath
, file
, match
);
419 if ((dp
= opendir(dirpath
))) {
420 while ((d
= readdir(dp
))) {
421 if (d
->d_name
[0] == '.')
423 retval
= _match_daemon(dirpath
, d
->d_name
, match
);
431 rc_stringlist_free(match
);
434 librc_hidden_def(rc_service_started_daemon
)
437 rc_service_daemons_crashed(const char *service
)
439 char dirpath
[PATH_MAX
];
442 char *path
= dirpath
;
449 char *pidfile
= NULL
;
457 RC_STRINGLIST
*list
= NULL
;
461 path
+= snprintf(dirpath
, sizeof(dirpath
), RC_SVCDIR
"/daemons/%s",
462 basename_c(service
));
464 if (!(dp
= opendir(dirpath
)))
467 while ((d
= readdir(dp
))) {
468 if (d
->d_name
[0] == '.')
471 snprintf(path
, sizeof(dirpath
) - (path
- dirpath
), "/%s",
473 fp
= fopen(dirpath
, "r");
477 while ((rc_getline(&line
, &len
, fp
))) {
479 if ((token
= strsep(&p
, "=")) == NULL
|| !p
)
485 if (strcmp(token
, "exec") == 0) {
489 } else if (strncmp(token
, "argv_", 5) == 0) {
491 list
= rc_stringlist_new();
492 rc_stringlist_add(list
, p
);
493 } else if (strcmp(token
, "name") == 0) {
497 } else if (strcmp(token
, "pidfile") == 0) {
498 pidfile
= xstrdup(p
);
507 if ((fp
= fopen(pidfile
, "r"))) {
508 if (fscanf(fp
, "%d", &pid
) == 1)
515 /* We have the pid, so no need to match
524 list
= rc_stringlist_new();
525 if (!TAILQ_FIRST(list
))
526 rc_stringlist_add(list
, exec
);
533 /* We need to flatten our linked list
536 TAILQ_FOREACH(s
, list
, entries
)
538 argv
= xmalloc(sizeof(char *) * (i
+ 1));
540 TAILQ_FOREACH(s
, list
, entries
)
541 argv
[i
++] = s
->value
;
548 if (kill(pid
, 0) == -1 && errno
== ESRCH
)
550 } else if ((pids
= rc_find_pids(exec
,
551 (const char *const *)argv
,
554 p1
= LIST_FIRST(pids
);
556 p2
= LIST_NEXT(p1
, entries
);
564 rc_stringlist_free(list
);
580 librc_hidden_def(rc_service_daemons_crashed
)