53a2299230
start-stop-daemon man page: -b|--background Typically used with programs that don't detach on their own. This option will force start-stop-daemon to fork before starting the process, and force it into the background. WARNING: start-stop-daemon cannot check the exit status if the process fails to execute for any reason. This is a last resort, and is only meant for programs that either make no sense forking on their own, or where it's not feasible to add the code for it to do this itself. This is usefull for applets like watchdog
271 lines
4.5 KiB
C
271 lines
4.5 KiB
C
/* vi: set sw=4 ts=4: */
|
|
/*
|
|
* Mini start-stop-daemon implementation(s) for busybox
|
|
*
|
|
* Written by Marek Michalkiewicz <marekm@i17linuxb.ists.pwr.wroc.pl>,
|
|
* public domain.
|
|
* Adapted for busybox David Kimdon <dwhedon@gordian.com>
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdarg.h>
|
|
#include <signal.h>
|
|
#include <errno.h>
|
|
#include <sys/stat.h>
|
|
#include <dirent.h>
|
|
#include <unistd.h>
|
|
#include "pwd.h"
|
|
|
|
#include "busybox.h"
|
|
|
|
static int start = 0;
|
|
static int stop = 0;
|
|
static int fork_before_exec = 0;
|
|
static int signal_nr = 15;
|
|
static int user_id = -1;
|
|
static const char *userspec = NULL;
|
|
static const char *cmdname = NULL;
|
|
static char *execname = NULL;
|
|
static char *startas = NULL;
|
|
|
|
typedef struct pid_list {
|
|
struct pid_list *next;
|
|
int pid;
|
|
} pid_list;
|
|
|
|
static pid_list *found = NULL;
|
|
|
|
static inline void
|
|
push(int pid)
|
|
{
|
|
pid_list *p;
|
|
|
|
p = xmalloc(sizeof(*p));
|
|
p->next = found;
|
|
p->pid = pid;
|
|
found = p;
|
|
}
|
|
|
|
|
|
static void
|
|
parse_options(int argc, char * const *argv)
|
|
{
|
|
|
|
int c;
|
|
|
|
for (;;) {
|
|
c = getopt (argc, argv, "a:n:s:u:x:KSb");
|
|
if (c == EOF)
|
|
break;
|
|
switch (c) {
|
|
case 'K':
|
|
stop = 1;
|
|
break;
|
|
case 'S':
|
|
start = 1;
|
|
break;
|
|
case 'a':
|
|
startas = optarg;
|
|
break;
|
|
case 'n':
|
|
cmdname = optarg;
|
|
break;
|
|
case 's':
|
|
if (sscanf(optarg, "%d", &signal_nr) != 1)
|
|
error_msg_and_die ("-s takes a numeric argument");
|
|
break;
|
|
case 'u':
|
|
userspec = optarg;
|
|
break;
|
|
case 'x':
|
|
execname = optarg;
|
|
break;
|
|
case 'b':
|
|
fork_before_exec = 1;
|
|
break;
|
|
default:
|
|
show_usage();
|
|
}
|
|
}
|
|
|
|
if (start == stop)
|
|
error_msg_and_die ("need one of -S or -K");
|
|
|
|
if (!execname && !userspec)
|
|
error_msg_and_die ("need at least one of -x or -u");
|
|
|
|
if (!startas)
|
|
startas = execname;
|
|
|
|
if (start && !startas)
|
|
error_msg_and_die ("-S needs -x or -a");
|
|
}
|
|
|
|
|
|
static int
|
|
pid_is_exec(int pid, const char *exec)
|
|
{
|
|
char buf[PATH_MAX];
|
|
FILE *fp;
|
|
|
|
sprintf(buf, "/proc/%d/cmdline", pid);
|
|
fp = fopen(buf, "r");
|
|
if (fp && fgets (buf, sizeof (buf), fp) ) {
|
|
if (strncmp (buf, exec, strlen(exec)) == 0)
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
pid_is_user(int pid, int uid)
|
|
{
|
|
struct stat sb;
|
|
char buf[32];
|
|
|
|
sprintf(buf, "/proc/%d", pid);
|
|
if (stat(buf, &sb) != 0)
|
|
return 0;
|
|
return (sb.st_uid == uid);
|
|
}
|
|
|
|
|
|
static int
|
|
pid_is_cmd(int pid, const char *name)
|
|
{
|
|
char buf[32];
|
|
FILE *f;
|
|
int c;
|
|
|
|
sprintf(buf, "/proc/%d/stat", pid);
|
|
f = fopen(buf, "r");
|
|
if (!f)
|
|
return 0;
|
|
while ((c = getc(f)) != EOF && c != '(')
|
|
;
|
|
if (c != '(') {
|
|
fclose(f);
|
|
return 0;
|
|
}
|
|
/* this hopefully handles command names containing ')' */
|
|
while ((c = getc(f)) != EOF && c == *name)
|
|
name++;
|
|
fclose(f);
|
|
return (c == ')' && *name == '\0');
|
|
}
|
|
|
|
|
|
static void
|
|
check(int pid)
|
|
{
|
|
if (execname && !pid_is_exec(pid, execname)) {
|
|
return;
|
|
}
|
|
if (userspec && !pid_is_user(pid, user_id)) {
|
|
return;
|
|
}
|
|
if (cmdname && !pid_is_cmd(pid, cmdname)) {
|
|
return;
|
|
}
|
|
push(pid);
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
do_procfs(void)
|
|
{
|
|
DIR *procdir;
|
|
struct dirent *entry;
|
|
int foundany, pid;
|
|
|
|
procdir = opendir("/proc");
|
|
if (!procdir)
|
|
perror_msg_and_die ("opendir /proc");
|
|
|
|
foundany = 0;
|
|
while ((entry = readdir(procdir)) != NULL) {
|
|
if (sscanf(entry->d_name, "%d", &pid) != 1)
|
|
continue;
|
|
foundany++;
|
|
check(pid);
|
|
}
|
|
closedir(procdir);
|
|
if (!foundany)
|
|
error_msg_and_die ("nothing in /proc - not mounted?");
|
|
}
|
|
|
|
|
|
static void
|
|
do_stop(void)
|
|
{
|
|
char what[1024];
|
|
pid_list *p;
|
|
int killed = 0;
|
|
|
|
if (cmdname)
|
|
strcpy(what, cmdname);
|
|
else if (execname)
|
|
strcpy(what, execname);
|
|
else if (userspec)
|
|
sprintf(what, "process(es) owned by `%s'", userspec);
|
|
else
|
|
error_msg_and_die ("internal error, please report");
|
|
|
|
if (!found) {
|
|
printf("no %s found; none killed.\n", what);
|
|
return;
|
|
}
|
|
for (p = found; p; p = p->next) {
|
|
if (kill(p->pid, signal_nr) == 0) {
|
|
p->pid = -p->pid;
|
|
killed++;
|
|
} else {
|
|
perror_msg("warning: failed to kill %d:", p->pid);
|
|
}
|
|
}
|
|
if (killed) {
|
|
printf("stopped %s (pid", what);
|
|
for (p = found; p; p = p->next)
|
|
if(p->pid < 0)
|
|
printf(" %d", -p->pid);
|
|
printf(").\n");
|
|
}
|
|
}
|
|
|
|
|
|
int
|
|
start_stop_daemon_main(int argc, char **argv)
|
|
{
|
|
parse_options(argc, argv);
|
|
argc -= optind;
|
|
argv += optind;
|
|
|
|
if (userspec && sscanf(userspec, "%d", &user_id) != 1)
|
|
user_id = my_getpwnam(userspec);
|
|
|
|
do_procfs();
|
|
|
|
if (stop) {
|
|
do_stop();
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
if (found) {
|
|
printf("%s already running.\n%d\n", execname ,found->pid);
|
|
return EXIT_SUCCESS;
|
|
}
|
|
*--argv = startas;
|
|
if (fork_before_exec) {
|
|
if (daemon(0, 0) == -1)
|
|
perror_msg_and_die ("unable to fork");
|
|
}
|
|
setsid();
|
|
execv(startas, argv);
|
|
perror_msg_and_die ("unable to start %s", startas);
|
|
}
|
|
|