Attempt to make s-s-d simpler by not enforcing the need for a full path and maybe working better with interpreted scripts.

This commit is contained in:
Roy Marples 2008-07-27 11:30:49 +00:00
parent 46a74f244e
commit cea206014d
6 changed files with 251 additions and 238 deletions

View File

@ -22,7 +22,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE. .\" SUCH DAMAGE.
.\" .\"
.Dd Feb 24, 2008 .Dd Jul 08, 2008
.Dt START-STOP-DAEMON 8 SMM .Dt START-STOP-DAEMON 8 SMM
.Os OpenRC .Os OpenRC
.Sh NAME .Sh NAME
@ -44,6 +44,11 @@
.Sh DESCRIPTION .Sh DESCRIPTION
.Nm .Nm
provides a consistent method of starting, stopping and signalling daemons. provides a consistent method of starting, stopping and signalling daemons.
If neither
.Fl K , -stop
nor
.Fl s , -signal
are provided, then we assume we are starting the daemon.
If a daemon cannot background by itself, nor create a pidfile, If a daemon cannot background by itself, nor create a pidfile,
.Nm .Nm
can do it for the daemon in a secure fashion. can do it for the daemon in a secure fashion.
@ -67,6 +72,8 @@ Here are the options to specify the daemon and how it should start or stop:
The The
.Ar daemon .Ar daemon
we start or stop. we start or stop.
If this option is not specified, then the first non option argument
is used.
If the If the
.Ar daemon .Ar daemon
is a script and you are not using the pidfile or process name options, is a script and you are not using the pidfile or process name options,
@ -76,7 +83,7 @@ with the interpreter and pass
.Ar daemon .Ar daemon
as an argument. Below is an example: as an argument. Below is an example:
.Pp .Pp
start-stop-daemon -Sx /usr/bin/perl -- /usr/bin/daemon.pl start-stop-daemon perl -- /usr/bin/daemon.pl
.It Fl p , -pidfile Ar pidfile .It Fl p , -pidfile Ar pidfile
When starting, we expect the daemon to create a valid When starting, we expect the daemon to create a valid
.Ar pidfile .Ar pidfile
@ -101,6 +108,10 @@ The return value is set as if the command was taken and worked.
.Pp .Pp
These options are only used for starting daemons: These options are only used for starting daemons:
.Bl -tag -width indent .Bl -tag -width indent
.It Fl a , -startas Ar name
Change the process name of the daemon to
.Ar name .
This just changes the first argument passed to the daemon.
.It Fl b , -background .It Fl b , -background
Force the daemon into the background. Some daemons don't create pidfiles, so a Force the daemon into the background. Some daemons don't create pidfiles, so a
good trick is to get the daemon to run in the foreground, and use the this good trick is to get the daemon to run in the foreground, and use the this

View File

@ -40,11 +40,11 @@ static size_t strlcpy(char *dst, const char *src, size_t size)
if (n && --n) if (n && --n)
do { do {
if (! (*dst++ = *src++)) if (!(*dst++ = *src++))
break; break;
} while (--n); } while (--n);
if (! n) { if (!n) {
if (size) if (size)
*dst = '\0'; *dst = '\0';
while (*src++); while (*src++);
@ -57,21 +57,22 @@ static size_t strlcpy(char *dst, const char *src, size_t size)
#if defined(__linux__) #if defined(__linux__)
static bool pid_is_cmd(pid_t pid, const char *cmd) static bool pid_is_exec(pid_t pid, const char *exec)
{ {
char buffer[32]; char buffer[32];
FILE *fp; FILE *fp;
int c; int c;
bool retval = false; bool retval = false;
exec = basename_c(exec);
snprintf(buffer, sizeof(buffer), "/proc/%d/stat", pid); snprintf(buffer, sizeof(buffer), "/proc/%d/stat", pid);
if ((fp = fopen(buffer, "r"))) { if ((fp = fopen(buffer, "r"))) {
while ((c = getc(fp)) != EOF && c != '(') while ((c = getc(fp)) != EOF && c != '(')
; ;
if (c == '(') { if (c == '(') {
while ((c = getc(fp)) != EOF && c == *cmd) while ((c = getc(fp)) != EOF && c == *exec)
cmd++; exec++;
if (c == ')' && *cmd == '\0') if (c == ')' && *exec == '\0')
retval = true; retval = true;
} }
fclose(fp); fclose(fp);
@ -79,7 +80,7 @@ static bool pid_is_cmd(pid_t pid, const char *cmd)
return retval; return retval;
} }
static bool pid_is_exec(pid_t pid, const char *const *argv) static bool pid_is_argv(pid_t pid, const char *const *argv)
{ {
char cmdline[32]; char cmdline[32];
int fd; int fd;
@ -108,7 +109,7 @@ static bool pid_is_exec(pid_t pid, const char *const *argv)
return true; return true;
} }
RC_PIDLIST *rc_find_pids(const char *const *argv, const char *cmd, RC_PIDLIST *rc_find_pids(const char *exec, const char *const *argv,
uid_t uid, pid_t pid) uid_t uid, pid_t pid)
{ {
DIR *procdir; DIR *procdir;
@ -143,27 +144,21 @@ RC_PIDLIST *rc_find_pids(const char *const *argv, const char *cmd,
while ((entry = readdir(procdir)) != NULL) { while ((entry = readdir(procdir)) != NULL) {
if (sscanf(entry->d_name, "%d", &p) != 1) if (sscanf(entry->d_name, "%d", &p) != 1)
continue; continue;
if (runscript_pid != 0 && runscript_pid == p) if (runscript_pid != 0 && runscript_pid == p)
continue; continue;
if (pid != 0 && pid != p) if (pid != 0 && pid != p)
continue; continue;
if (uid) { if (uid) {
snprintf(buffer, sizeof(buffer), "/proc/%d", p); snprintf(buffer, sizeof(buffer), "/proc/%d", p);
if (stat(buffer, &sb) != 0 || sb.st_uid != uid) if (stat(buffer, &sb) != 0 || sb.st_uid != uid)
continue; continue;
} }
if (exec && !pid_is_exec(p, exec))
if (cmd && ! pid_is_cmd(p, cmd))
continue; continue;
if (argv &&
if (argv && ! cmd && ! !pid_is_argv(p, (const char *const *)argv))
pid_is_exec(p, (const char *const *)argv))
continue; continue;
if (!pids) {
if (! pids) {
pids = xmalloc(sizeof(*pids)); pids = xmalloc(sizeof(*pids));
LIST_INIT(pids); LIST_INIT(pids);
} }
@ -172,7 +167,6 @@ RC_PIDLIST *rc_find_pids(const char *const *argv, const char *cmd,
LIST_INSERT_HEAD(pids, pi, entries); LIST_INSERT_HEAD(pids, pi, entries);
} }
closedir(procdir); closedir(procdir);
return pids; return pids;
} }
librc_hidden_def(rc_find_pids) librc_hidden_def(rc_find_pids)
@ -201,7 +195,7 @@ librc_hidden_def(rc_find_pids)
# define _KVM_FLAGS O_RDONLY # define _KVM_FLAGS O_RDONLY
# endif # endif
RC_PIDLIST *rc_find_pids(const char *const *argv, const char *cmd, RC_PIDLIST *rc_find_pids(const char *exec, const char *const *argv,
uid_t uid, pid_t pid) uid_t uid, pid_t pid)
{ {
static kvm_t *kd = NULL; static kvm_t *kd = NULL;
@ -235,39 +229,34 @@ RC_PIDLIST *rc_find_pids(const char *const *argv, const char *cmd,
return NULL; return NULL;
} }
if (exec)
exec = basename_c(exec);
for (i = 0; i < processes; i++) { for (i = 0; i < processes; i++) {
p = _GET_KINFO_PID(kp[i]); p = _GET_KINFO_PID(kp[i]);
if (pid != 0 && pid != p) if (pid != 0 && pid != p)
continue; continue;
if (uid != 0 && uid != _GET_KINFO_UID(kp[i])) if (uid != 0 && uid != _GET_KINFO_UID(kp[i]))
continue; continue;
if (exec) {
if (cmd) { if (!_GET_KINFO_COMM(kp[i]) ||
if (! _GET_KINFO_COMM(kp[i]) || strcmp(exec, _GET_KINFO_COMM(kp[i])) != 0)
strcmp(cmd, _GET_KINFO_COMM(kp[i])) != 0)
continue; continue;
} }
if (argv && *argv) {
if (argv && *argv && ! cmd) {
pargv = _KVM_GETARGV(kd, &kp[i], pargc); pargv = _KVM_GETARGV(kd, &kp[i], pargc);
if (! pargv || ! *pargv) if (!pargv || !*pargv)
continue; continue;
arg = argv; arg = argv;
match = 1; match = 1;
while (*arg && *pargv) while (*arg && *pargv)
if (strcmp(*arg++, *pargv++) != 0) { if (strcmp(*arg++, *pargv++) != 0) {
match = 0; match = 0;
break; break;
} }
if (!match)
if (! match)
continue; continue;
} }
if (!pids) {
if (! pids) {
pids = xmalloc(sizeof(*pids)); pids = xmalloc(sizeof(*pids));
LIST_INIT(pids); LIST_INIT(pids);
} }
@ -297,7 +286,7 @@ static bool _match_daemon(const char *path, const char *file,
snprintf(ffile, sizeof(ffile), "%s/%s", path, file); snprintf(ffile, sizeof(ffile), "%s/%s", path, file);
fp = fopen(ffile, "r"); fp = fopen(ffile, "r");
if (! fp) if (!fp)
return false; return false;
while ((rc_getline(&line, &len, fp))) { while ((rc_getline(&line, &len, fp))) {
@ -306,7 +295,7 @@ static bool _match_daemon(const char *path, const char *file,
TAILQ_REMOVE(match, m, entries); TAILQ_REMOVE(match, m, entries);
break; break;
} }
if (! TAILQ_FIRST(match)) if (!TAILQ_FIRST(match))
break; break;
} }
fclose(fp); fclose(fp);
@ -316,26 +305,26 @@ static bool _match_daemon(const char *path, const char *file,
return true; return true;
} }
static RC_STRINGLIST *_match_list(const char* const* argv, static RC_STRINGLIST *_match_list(const char *exec, const char* const* argv,
const char *name, const char *pidfile) const char *pidfile)
{ {
RC_STRINGLIST *match = rc_stringlist_new(); RC_STRINGLIST *match = rc_stringlist_new();
int i = 0; int i = 0;
size_t l; size_t l;
char *m; char *m;
while (argv && argv[i]) { if (exec) {
l = strlen(*argv) + strlen("argv_=") + 16; l = strlen(exec) + 6;
m = xmalloc(sizeof(char) * l); m = xmalloc(sizeof(char) * l);
snprintf(m, l, "argv_0=%s", argv[i++]); snprintf(m, l, "exec=%s", exec);
rc_stringlist_add(match, m); rc_stringlist_add(match, m);
free(m); free(m);
} }
if (name) { while (argv && argv[i]) {
l = strlen(name) + 6; l = strlen(*argv) + strlen("argv_=") + 16;
m = xmalloc(sizeof (char) * l); m = xmalloc(sizeof(char) * l);
snprintf(m, l, "name=%s", name); snprintf(m, l, "argv_0=%s", argv[i++]);
rc_stringlist_add(match, m); rc_stringlist_add(match, m);
free(m); free(m);
} }
@ -351,8 +340,9 @@ static RC_STRINGLIST *_match_list(const char* const* argv,
return match; return match;
} }
bool rc_service_daemon_set(const char *service, const char *const *argv, bool rc_service_daemon_set(const char *service, const char *exec,
const char *name, const char *pidfile, bool started) const char *const *argv,
const char *pidfile, bool started)
{ {
char dirpath[PATH_MAX]; char dirpath[PATH_MAX];
char file[PATH_MAX]; char file[PATH_MAX];
@ -365,7 +355,7 @@ bool rc_service_daemon_set(const char *service, const char *const *argv,
int i = 0; int i = 0;
FILE *fp; FILE *fp;
if (!(argv && *argv) && ! name && ! pidfile) { if (!exec && !pidfile) {
errno = EINVAL; errno = EINVAL;
return false; return false;
} }
@ -375,7 +365,7 @@ bool rc_service_daemon_set(const char *service, const char *const *argv,
/* Regardless, erase any existing daemon info */ /* Regardless, erase any existing daemon info */
if ((dp = opendir(dirpath))) { if ((dp = opendir(dirpath))) {
match = _match_list(argv, name, pidfile); match = _match_list(exec, argv, pidfile);
while ((d = readdir(dp))) { while ((d = readdir(dp))) {
if (d->d_name[0] == '.') if (d->d_name[0] == '.')
continue; continue;
@ -384,7 +374,7 @@ bool rc_service_daemon_set(const char *service, const char *const *argv,
dirpath, d->d_name); dirpath, d->d_name);
nfiles++; nfiles++;
if (! *oldfile) { if (!*oldfile) {
if (_match_daemon(dirpath, d->d_name, match)) { if (_match_daemon(dirpath, d->d_name, match)) {
unlink(file); unlink(file);
strlcpy(oldfile, file, sizeof(oldfile)); strlcpy(oldfile, file, sizeof(oldfile));
@ -405,13 +395,13 @@ bool rc_service_daemon_set(const char *service, const char *const *argv,
snprintf(file, sizeof(file), "%s/%03d", snprintf(file, sizeof(file), "%s/%03d",
dirpath, nfiles + 1); dirpath, nfiles + 1);
if ((fp = fopen(file, "w"))) { if ((fp = fopen(file, "w"))) {
fprintf(fp, "exec=");
if (exec)
fprintf(fp, "%s", exec);
while (argv && argv[i]) { while (argv && argv[i]) {
fprintf(fp, "argv_%d=%s\n", i, argv[i]); fprintf(fp, "\nargv_%d=%s", i, argv[i]);
i++; i++;
} }
fprintf(fp, "name=");
if (name)
fprintf(fp, "%s", name);
fprintf(fp, "\npidfile="); fprintf(fp, "\npidfile=");
if (pidfile) if (pidfile)
fprintf(fp, "%s", pidfile); fprintf(fp, "%s", pidfile);
@ -427,7 +417,8 @@ bool rc_service_daemon_set(const char *service, const char *const *argv,
} }
librc_hidden_def(rc_service_daemon_set) librc_hidden_def(rc_service_daemon_set)
bool rc_service_started_daemon(const char *service, const char *const *argv, bool rc_service_started_daemon(const char *service,
const char *exec, const char *const *argv,
int indx) int indx)
{ {
char dirpath[PATH_MAX]; char dirpath[PATH_MAX];
@ -442,7 +433,7 @@ bool rc_service_started_daemon(const char *service, const char *const *argv,
snprintf(dirpath, sizeof(dirpath), RC_SVCDIR "/daemons/%s", snprintf(dirpath, sizeof(dirpath), RC_SVCDIR "/daemons/%s",
basename_c(service)); basename_c(service));
match = _match_list(argv, NULL, NULL); match = _match_list(exec, argv, NULL);
if (indx > 0) { if (indx > 0) {
snprintf(file, sizeof(file), "%03d", indx); snprintf(file, sizeof(file), "%03d", indx);
@ -492,7 +483,7 @@ bool rc_service_daemons_crashed(const char *service)
path += snprintf(dirpath, sizeof(dirpath), RC_SVCDIR "/daemons/%s", path += snprintf(dirpath, sizeof(dirpath), RC_SVCDIR "/daemons/%s",
basename_c(service)); basename_c(service));
if (! (dp = opendir(dirpath))) if (!(dp = opendir(dirpath)))
return false; return false;
while ((d = readdir(dp))) { while ((d = readdir(dp))) {
@ -502,25 +493,25 @@ bool rc_service_daemons_crashed(const char *service)
snprintf(path, sizeof(dirpath) - (path - dirpath), "/%s", snprintf(path, sizeof(dirpath) - (path - dirpath), "/%s",
d->d_name); d->d_name);
fp = fopen(dirpath, "r"); fp = fopen(dirpath, "r");
if (! fp) if (!fp)
break; break;
while ((rc_getline(&line, &len, fp))) { while ((rc_getline(&line, &len, fp))) {
p = line; p = line;
if ((token = strsep(&p, "=")) == NULL || ! p) if ((token = strsep(&p, "=")) == NULL || !p)
continue; continue;
if (! *p) if (!*p)
continue; continue;
if (strncmp(token, "argv_", 5) == 0) { if (strcmp(token, "exec") == 0) {
if (! list)
list = rc_stringlist_new();
rc_stringlist_add(list, p);
} else if (strcmp(token, "exec") == 0) {
if (exec) if (exec)
free(exec); free(exec);
exec = xstrdup(p); exec = xstrdup(p);
} else if (strncmp(token, "argv_", 5) == 0) {
if (!list)
list = rc_stringlist_new();
rc_stringlist_add(list, p);
} else if (strcmp(token, "name") == 0) { } else if (strcmp(token, "name") == 0) {
if (name) if (name)
free(name); free(name);
@ -548,9 +539,9 @@ bool rc_service_daemons_crashed(const char *service)
name = NULL; name = NULL;
} else { } else {
if (exec) { if (exec) {
if (! list) if (!list)
list = rc_stringlist_new(); list = rc_stringlist_new();
if (! TAILQ_FIRST(list)) if (!TAILQ_FIRST(list))
rc_stringlist_add(list, exec); rc_stringlist_add(list, exec);
free(exec); free(exec);
@ -570,9 +561,10 @@ bool rc_service_daemons_crashed(const char *service)
} }
} }
if (! retval) { if (!retval) {
if ((pids = rc_find_pids((const char *const *)argv, if ((pids = rc_find_pids(exec,
name, 0, pid))) (const char *const *)argv,
0, pid)))
{ {
p1 = LIST_FIRST(pids); p1 = LIST_FIRST(pids);
while (p1) { while (p1) {

View File

@ -142,7 +142,7 @@ bool rc_service_delete(const char *, const char *);
* @param name of the process (optional) * @param name of the process (optional)
* @param pidfile of the process (optional) * @param pidfile of the process (optional)
* @param started if true, add the arguments otherwise remove existing matching arguments */ * @param started if true, add the arguments otherwise remove existing matching arguments */
bool rc_service_daemon_set(const char *, const char *const *, const char *, const char *, bool rc_service_daemon_set(const char *, const char *, const char *const *, const char *,
bool); bool);
/*! Returns a description of what the service and/or option does. /*! Returns a description of what the service and/or option does.
@ -202,9 +202,11 @@ RC_SERVICE rc_service_state(const char *);
/*! Check if the service started the daemon /*! Check if the service started the daemon
* @param service to check * @param service to check
* @param exec to check * @param exec to check
* @param argv to check
* @param indx of the daemon (optional - 1st daemon, 2nd daemon, etc) * @param indx of the daemon (optional - 1st daemon, 2nd daemon, etc)
* @return true if started by this service, otherwise false */ * @return true if started by this service, otherwise false */
bool rc_service_started_daemon(const char *, const char *const *, int); bool rc_service_started_daemon(const char *, const char *,
const char *const *, int);
/*! Return a saved value for a service /*! Return a saved value for a service
* @param service to check * @param service to check
@ -463,11 +465,11 @@ typedef LIST_HEAD(rc_pidlist, rc_pid) RC_PIDLIST;
* pid overrides anything else. * pid overrides anything else.
* If both exec and cmd are given then we ignore exec. * If both exec and cmd are given then we ignore exec.
* @param exec to check for * @param exec to check for
* @param cmd to check for * @param argv to check for
* @param uid to check for * @param uid to check for
* @param pid to check for * @param pid to check for
* @return NULL terminated list of pids */ * @return NULL terminated list of pids */
RC_PIDLIST *rc_find_pids(const char *const *, const char *, uid_t, pid_t); RC_PIDLIST *rc_find_pids(const char *, const char *const *, uid_t, pid_t);
__END_DECLS __END_DECLS
#endif #endif

View File

@ -30,15 +30,14 @@ RC_SBINLINKS= mark_service_starting mark_service_started \
ALL_LINKS= ${BINLINKS} ${SBINLINKS} ${RC_BINLINKS} ${RC_SBINLINKS} ALL_LINKS= ${BINLINKS} ${SBINLINKS} ${RC_BINLINKS} ${RC_SBINLINKS}
CLEANFILES+= ${ALL_LINKS} CLEANFILES+= ${ALL_LINKS}
CPPFLAGS+= -I../includes -I../librc -I../libeinfo
LDFLAGS+= -L../librc -L../libeinfo LDFLAGS+= -L../librc -L../libeinfo
LDADD+= -lutil -lrc -leinfo LDADD+= -lutil -lrc -leinfo
MK= ../../mk MK= ../../mk
include ${MK}/debug.mk
include ${MK}/prog.mk include ${MK}/prog.mk
include ${MK}/cc.mk include ${MK}/cc.mk
include ${MK}/debug.mk
CPPFLAGS+= -I../includes -I../librc -I../libeinfo
include ${MK}/${MKTERMCAP}.mk include ${MK}/${MKTERMCAP}.mk
LDADD+= ${LIBDL} ${LIBKVM} LDADD+= ${LIBDL} ${LIBKVM}

View File

@ -243,9 +243,9 @@ static int do_e(int argc, char **argv)
static int do_service(int argc, char **argv) static int do_service(int argc, char **argv)
{ {
bool ok = false; bool ok = false;
char *service = NULL; char *service;
char *exec;
int idx = 0; int idx = 0;
char *d[] = { NULL, NULL };
if (argc > 1) if (argc > 1)
service = argv[1]; service = argv[1];
@ -270,21 +270,19 @@ static int do_service(int argc, char **argv)
else if (strcmp(applet, "service_wasinactive") == 0) else if (strcmp(applet, "service_wasinactive") == 0)
ok = (rc_service_state(service) & RC_SERVICE_WASINACTIVE); ok = (rc_service_state(service) & RC_SERVICE_WASINACTIVE);
else if (strcmp(applet, "service_started_daemon") == 0) { else if (strcmp(applet, "service_started_daemon") == 0) {
d[0] = argv[1];
service = getenv("RC_SVCNAME"); service = getenv("RC_SVCNAME");
exec = argv[1];
if (argc > 3) { if (argc > 3) {
service = argv[1]; service = argv[1];
d[0] = argv[2]; exec = argv[2];
sscanf(argv[3], "%d", &idx); sscanf(argv[3], "%d", &idx);
} else if (argc == 3) { } else if (argc == 3) {
if (sscanf(argv[2], "%d", &idx) != 1) { if (sscanf(argv[2], "%d", &idx) != 1) {
service = argv[1]; service = argv[1];
d[0] = argv[2]; exec = argv[2];
} }
} }
ok = rc_service_started_daemon(service, ok = rc_service_started_daemon(service, exec, NULL, idx);
(const char * const *)d, idx);
} else } else
eerrorx("%s: unknown applet", applet); eerrorx("%s: unknown applet", applet);

View File

@ -157,7 +157,7 @@ static int parse_signal(const char *sig)
unsigned int i = 0; unsigned int i = 0;
const char *s; const char *s;
if (! sig || *sig == '\0') if (!sig || *sig == '\0')
return -1; return -1;
if (sscanf(sig, "%u", &i) == 1) { if (sscanf(sig, "%u", &i) == 1) {
@ -289,13 +289,13 @@ static pid_t get_pid(const char *pidfile, bool quiet)
return -1; return -1;
if ((fp = fopen(pidfile, "r")) == NULL) { if ((fp = fopen(pidfile, "r")) == NULL) {
if (! quiet) if (!quiet)
eerror("%s: fopen `%s': %s", applet, pidfile, strerror(errno)); eerror("%s: fopen `%s': %s", applet, pidfile, strerror(errno));
return -1; return -1;
} }
if (fscanf(fp, "%d", &pid) != 1) { if (fscanf(fp, "%d", &pid) != 1) {
if (! quiet) if (!quiet)
eerror("%s: no pid found in `%s'", applet, pidfile); eerror("%s: no pid found in `%s'", applet, pidfile);
fclose(fp); fclose(fp);
return -1; return -1;
@ -307,7 +307,7 @@ static pid_t get_pid(const char *pidfile, bool quiet)
} }
/* return number of processed killed, -1 on error */ /* return number of processed killed, -1 on error */
static int do_stop(const char *const *argv, const char *cmd, static int do_stop(const char *exec, const char *const *argv,
pid_t pid, uid_t uid,int sig, pid_t pid, uid_t uid,int sig,
bool quiet, bool verbose, bool test) bool quiet, bool verbose, bool test)
{ {
@ -320,14 +320,14 @@ static int do_stop(const char *const *argv, const char *cmd,
if (pid) if (pid)
pids = rc_find_pids(NULL, NULL, 0, pid); pids = rc_find_pids(NULL, NULL, 0, pid);
else else
pids = rc_find_pids(argv, cmd, uid, pid); pids = rc_find_pids(exec, argv, uid, pid);
if (! pids) if (!pids)
return 0; return 0;
LIST_FOREACH_SAFE(pi, pids, entries, np) { LIST_FOREACH_SAFE(pi, pids, entries, np) {
if (test) { if (test) {
if (! quiet) if (!quiet)
einfo("Would send signal %d to PID %d", einfo("Would send signal %d to PID %d",
sig, pi->pid); sig, pi->pid);
nkilled++; nkilled++;
@ -342,7 +342,7 @@ static int do_stop(const char *const *argv, const char *cmd,
eend(killed ? 0 : 1, eend(killed ? 0 : 1,
"%s: failed to send signal %d to PID %d: %s", "%s: failed to send signal %d to PID %d: %s",
applet, sig, pi->pid, strerror(errno)); applet, sig, pi->pid, strerror(errno));
if (! killed) { if (!killed) {
nkilled = -1; nkilled = -1;
} else { } else {
if (nkilled != -1) if (nkilled != -1)
@ -356,7 +356,7 @@ static int do_stop(const char *const *argv, const char *cmd,
return nkilled; return nkilled;
} }
static int run_stop_schedule(const char *const *argv, const char *cmd, static int run_stop_schedule(const char *exec, const char *const *argv,
const char *pidfile, uid_t uid, const char *pidfile, uid_t uid,
bool quiet, bool verbose, bool test) bool quiet, bool verbose, bool test)
{ {
@ -369,14 +369,14 @@ static int run_stop_schedule(const char *const *argv, const char *cmd,
pid_t pid = 0; pid_t pid = 0;
if (verbose) { if (verbose) {
if (exec)
einfo ("Will stop %s\n", exec);
if (pidfile) if (pidfile)
einfo("Will stop PID in pidfile `%s'", pidfile); einfo("Will stop PID in pidfile `%s'", pidfile);
if (uid) if (uid)
einfo("Will stop processes owned by UID %d", uid); einfo("Will stop processes owned by UID %d", uid);
if (argv && *argv) if (argv && *argv)
einfo("Will stop processes of `%s'", *argv); einfo("Will stop processes of `%s'", *argv);
if (cmd)
einfo("Will stop processes called `%s'", cmd);
} }
if (pidfile) { if (pidfile) {
@ -393,7 +393,7 @@ static int run_stop_schedule(const char *const *argv, const char *cmd,
case SC_SIGNAL: case SC_SIGNAL:
nrunning = 0; nrunning = 0;
nkilled = do_stop(argv, cmd, pid, uid, item->value, nkilled = do_stop(exec, argv, pid, uid, item->value,
quiet, verbose, test); quiet, verbose, test);
if (nkilled == 0) { if (nkilled == 0) {
if (tkilled == 0) { if (tkilled == 0) {
@ -419,7 +419,7 @@ static int run_stop_schedule(const char *const *argv, const char *cmd,
ts.tv_nsec = POLL_INTERVAL; ts.tv_nsec = POLL_INTERVAL;
while (nloops) { while (nloops) {
if ((nrunning = do_stop(argv, cmd, pid, if ((nrunning = do_stop(exec, argv, pid,
uid, 0, true, false, true)) == 0) uid, 0, true, false, true)) == 0)
return 0; return 0;
@ -466,15 +466,15 @@ static void handle_signal(int sig)
switch (sig) { switch (sig) {
case SIGINT: case SIGINT:
if (! signame[0]) if (!signame[0])
snprintf(signame, sizeof(signame), "SIGINT"); snprintf(signame, sizeof(signame), "SIGINT");
/* FALLTHROUGH */ /* FALLTHROUGH */
case SIGTERM: case SIGTERM:
if (! signame[0]) if (!signame[0])
snprintf(signame, sizeof(signame), "SIGTERM"); snprintf(signame, sizeof(signame), "SIGTERM");
/* FALLTHROUGH */ /* FALLTHROUGH */
case SIGQUIT: case SIGQUIT:
if (! signame[0]) if (!signame[0])
snprintf(signame, sizeof(signame), "SIGQUIT"); snprintf(signame, sizeof(signame), "SIGQUIT");
eerrorx("%s: caught %s, aborting", applet, signame); eerrorx("%s: caught %s, aborting", applet, signame);
/* NOTREACHED */ /* NOTREACHED */
@ -499,7 +499,7 @@ static void handle_signal(int sig)
#include "_usage.h" #include "_usage.h"
#define getoptstring "KN:R:Sbc:d:g:mn:op:s:tu:r:x:1:2:" getoptstring_COMMON #define getoptstring "KN:R:Sbc:d:e:g:mn:op:s:tu:r:x:1:2:" getoptstring_COMMON
static const struct option longopts[] = { static const struct option longopts[] = {
{ "stop", 0, NULL, 'K'}, { "stop", 0, NULL, 'K'},
{ "nicelevel", 1, NULL, 'N'}, { "nicelevel", 1, NULL, 'N'},
@ -529,7 +529,7 @@ static const char * const longopts_help[] = {
"Set a nicelevel when starting", "Set a nicelevel when starting",
"Retry schedule to use when stopping", "Retry schedule to use when stopping",
"Start daemon", "Start daemon",
"deprecated, use --exec", "deprecated, use --exec or --name",
"Force daemon to background", "Force daemon to background",
"deprecated, use --user", "deprecated, use --user",
"Change the PWD", "Change the PWD",
@ -563,14 +563,14 @@ int start_stop_daemon(int argc, char **argv)
#endif #endif
int opt; int opt;
bool start = false; bool start = true;
bool stop = false;
bool oknodo = false; bool oknodo = false;
bool test = false; bool test = false;
bool quiet; bool quiet;
bool verbose = false; bool verbose = false;
char *exec = NULL; char *exec = NULL;
char *cmd = NULL; char *startas = NULL;
char *name = NULL;
char *pidfile = NULL; char *pidfile = NULL;
int sig = SIGTERM; int sig = SIGTERM;
int nicelevel = 0; int nicelevel = 0;
@ -585,16 +585,16 @@ int start_stop_daemon(int argc, char **argv)
char *redirect_stdout = NULL; char *redirect_stdout = NULL;
int stdout_fd; int stdout_fd;
int stderr_fd; int stderr_fd;
pid_t pid; pid_t pid, spid;
int i; int i;
char *svcname = getenv("RC_SVCNAME"); char *svcname = getenv("RC_SVCNAME");
RC_STRINGLIST *env_list; RC_STRINGLIST *env_list;
RC_STRING *env; RC_STRING *env;
char *path; char *tmp, *newpath, *np;
bool sethome = false; bool sethome = false;
bool setuser = false; bool setuser = false;
char *p; char *p;
char *tmp; char *token;
char exec_file[PATH_MAX]; char exec_file[PATH_MAX];
struct passwd *pw; struct passwd *pw;
struct group *gr; struct group *gr;
@ -609,16 +609,16 @@ int start_stop_daemon(int argc, char **argv)
signal_setup(SIGQUIT, handle_signal); signal_setup(SIGQUIT, handle_signal);
signal_setup(SIGTERM, handle_signal); signal_setup(SIGTERM, handle_signal);
if ((path = getenv("SSD_NICELEVEL"))) if ((tmp = getenv("SSD_NICELEVEL")))
if (sscanf(path, "%d", &nicelevel) != 1) if (sscanf(tmp, "%d", &nicelevel) != 1)
eerror("%s: invalid nice level `%s' (SSD_NICELEVEL)", eerror("%s: invalid nice level `%s' (SSD_NICELEVEL)",
applet, path); applet, tmp);
while ((opt = getopt_long(argc, argv, "e:" getoptstring, longopts, while ((opt = getopt_long(argc, argv, getoptstring, longopts,
(int *) 0)) != -1) (int *) 0)) != -1)
switch (opt) { switch (opt) {
case 'K': /* --stop */ case 'K': /* --stop */
stop = true; start = false;
break; break;
case 'N': /* --nice */ case 'N': /* --nice */
@ -701,7 +701,7 @@ int start_stop_daemon(int argc, char **argv)
break; break;
case 'n': /* --name <process-name> */ case 'n': /* --name <process-name> */
cmd = optarg; name = optarg;
break; break;
case 'o': /* --oknodo */ case 'o': /* --oknodo */
@ -724,7 +724,9 @@ int start_stop_daemon(int argc, char **argv)
ch_root = optarg; ch_root = optarg;
break; break;
case 'a': case 'a': /* --startas <name> */
startas = optarg;
break;
case 'x': /* --exec <executable> */ case 'x': /* --exec <executable> */
exec = optarg; exec = optarg;
break; break;
@ -740,65 +742,118 @@ int start_stop_daemon(int argc, char **argv)
case_RC_COMMON_GETOPT case_RC_COMMON_GETOPT
} }
argc -= optind;
argv += optind;
quiet = rc_yesno(getenv("EINFO_QUIET")); quiet = rc_yesno(getenv("EINFO_QUIET"));
verbose = rc_yesno(getenv("EINFO_VERBOSE")); verbose = rc_yesno(getenv("EINFO_VERBOSE"));
/* Allow start-stop-daemon --signal HUP --exec /usr/sbin/dnsmasq /* Allow start-stop-daemon --signal HUP --exec /usr/sbin/dnsmasq
* instead of forcing --stop --oknodo as well */ * instead of forcing --stop --oknodo as well */
if (! start && ! stop) if (!start &&
if (sig != SIGINT && sig != SIGINT &&
sig != SIGTERM && sig != SIGTERM &&
sig != SIGQUIT && sig != SIGQUIT &&
sig != SIGKILL) sig != SIGKILL)
{
oknodo = true; oknodo = true;
stop = true;
}
if (start == stop) if (!exec)
eerrorx("%s: need one of --start or --stop", applet); exec = startas;
else if (!name)
name = startas;
if (start && ! exec) if (!exec) {
eerrorx("%s: --start needs --exec", applet); exec = *argv;
if (name)
*argv = name;
} else if (name)
*--argv = name;
else
*--argv = exec;
if (stop && ! exec && ! pidfile && ! cmd && ! uid)
if (start && !exec)
eerrorx("%s: nothing to start", applet);
if (!start && !*argv && !pidfile && !name && !uid)
eerrorx("%s: --stop needs --exec, --pidfile, --name or --user", applet); eerrorx("%s: --stop needs --exec, --pidfile, --name or --user", applet);
if (makepidfile && ! pidfile) if (makepidfile && !pidfile)
eerrorx("%s: --make-pidfile is only relevant with --pidfile", applet); eerrorx("%s: --make-pidfile is only relevant with --pidfile", applet);
if (background && ! start) if (background && !start)
eerrorx("%s: --background is only relevant with --start", applet); eerrorx("%s: --background is only relevant with --start", applet);
if ((redirect_stdout || redirect_stderr) && ! background) if ((redirect_stdout || redirect_stderr) && !background)
eerrorx("%s: --stdout and --stderr are only relevant with --background", eerrorx("%s: --stdout and --stderr are only relevant with --background",
applet); applet);
argc -= optind; if (!start) {
argv += optind; if (!TAILQ_FIRST(&schedule)) {
if (test || oknodo)
parse_schedule("0", sig);
else
parse_schedule(NULL, sig);
}
i = run_stop_schedule(exec, (const char *const *)argv,
pidfile, uid, quiet, verbose, test);
if (i < 0)
/* We failed to stop something */
exit(EXIT_FAILURE);
if (test || oknodo)
return i > 0 ? EXIT_SUCCESS : EXIT_FAILURE;
/* Even if we have not actually killed anything, we should
* remove information about it as it may have unexpectedly
* crashed out. We should also return success as the end
* result would be the same. */
if (pidfile && exists(pidfile))
unlink(pidfile);
if (svcname)
rc_service_daemon_set(svcname, exec,
(const char *const *)argv,
pidfile, false);
exit(EXIT_SUCCESS);
}
/* Validate that the binary exists if we are starting */ /* Validate that the binary exists if we are starting */
if (exec) { if (*exec == '/' || *exec == '.') {
if (ch_root) { /* Full or relative path */
if (ch_root)
snprintf(exec_file, sizeof(exec_file), "%s/%s", ch_root, exec); snprintf(exec_file, sizeof(exec_file), "%s/%s", ch_root, exec);
tmp = exec_file; else
} else snprintf(exec_file, sizeof(exec_file), "%s", exec);
tmp = exec; } else {
if (start && ! exists(tmp)) { /* Something in $PATH */
eerror("%s: %s does not exist", applet, tmp); p = tmp = xstrdup(getenv("PATH"));
*exec_file = '\0';
while ((token = strsep(&p, ":"))) {
if (ch_root)
snprintf(exec_file, sizeof(exec_file), "%s/%s/%s", ch_root, token, exec);
else
snprintf(exec_file, sizeof(exec_file), "%s/%s", token, exec);
if (exists(exec_file))
break;
*exec_file = '\0';
}
free(tmp);
}
if (!exists(exec_file)) {
eerror("%s: %s does not exist", applet,
*exec_file ? exec_file : exec);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
/* If we don't have a pidfile or name, check it's not /* If we don't have a pidfile or name, check it's not
* interpreted, otherwise we should fail */ * interpreted, otherwise we should fail */
if (! pidfile && ! cmd) { if (!pidfile && !name) {
fp = fopen (tmp, "r"); fp = fopen(tmp, "r");
if (fp) { if (fp) {
fgets(line, sizeof(line), fp); fgets(line, sizeof(line), fp);
fclose(fp); fclose(fp);
if (line[0] == '#' && line[1] == '!') { if (line[0] == '#' && line[1] == '!') {
len = strlen (line) - 1; len = strlen(line) - 1;
/* Remove the trailing newline */ /* Remove the trailing newline */
if (line[len] == '\n') if (line[len] == '\n')
@ -816,52 +871,13 @@ int start_stop_daemon(int argc, char **argv)
} }
} }
} }
}
/* Add exec to our arguments */ if (pidfile)
*--argv = exec;
if (stop) {
int result;
if (! TAILQ_FIRST(&schedule)) {
if (test || oknodo)
parse_schedule("0", sig);
else
parse_schedule(NULL, sig);
}
result = run_stop_schedule((const char *const *)argv, cmd,
pidfile, uid, quiet, verbose, test);
if (result < 0)
/* We failed to stop something */
exit(EXIT_FAILURE);
if (test || oknodo)
return result > 0 ? EXIT_SUCCESS : EXIT_FAILURE;
/* Even if we have not actually killed anything, we should
* remove information about it as it may have unexpectedly
* crashed out. We should also return success as the end
* result would be the same. */
if (pidfile && exists(pidfile))
unlink(pidfile);
if (svcname)
rc_service_daemon_set(svcname,
(const char *const *)argv,
cmd, pidfile, false);
exit(EXIT_SUCCESS);
}
if (pidfile) {
pid = get_pid(pidfile, true); pid = get_pid(pidfile, true);
} else else
pid = 0; pid = 0;
if (do_stop((const char * const *)argv, cmd, pid, uid, if (do_stop(exec, (const char * const *)argv, pid, uid,
0, true, false, true) > 0) 0, true, false, true) > 0)
eerrorx("%s: %s is already running", applet, exec); eerrorx("%s: %s is already running", applet, exec);
@ -884,6 +900,8 @@ int start_stop_daemon(int argc, char **argv)
einfo("in dir `%s'", ch_dir); einfo("in dir `%s'", ch_dir);
if (nicelevel != 0) if (nicelevel != 0)
einfo("with a priority of %d", nicelevel); einfo("with a priority of %d", nicelevel);
if (name)
einfo ("with a process name of %s", name);
eoutdent(); eoutdent();
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
} }
@ -918,7 +936,7 @@ int start_stop_daemon(int argc, char **argv)
if (ch_root && chroot(ch_root) < 0) if (ch_root && chroot(ch_root) < 0)
eerrorx("%s: chroot `%s': %s", applet, ch_root, strerror(errno)); eerrorx("%s: chroot `%s': %s", applet, ch_root, strerror(errno));
if (ch_dir && chdir (ch_dir) < 0) if (ch_dir && chdir(ch_dir) < 0)
eerrorx("%s: chdir `%s': %s", applet, ch_dir, strerror(errno)); eerrorx("%s: chdir `%s': %s", applet, ch_dir, strerror(errno));
if (makepidfile && pidfile) { if (makepidfile && pidfile) {
@ -955,12 +973,12 @@ int start_stop_daemon(int argc, char **argv)
else { else {
pw = getpwuid(uid); pw = getpwuid(uid);
if (pw) { if (pw) {
if (! sethome) { if (!sethome) {
unsetenv("HOME"); unsetenv("HOME");
if (pw->pw_dir) if (pw->pw_dir)
setenv("HOME", pw->pw_dir, 1); setenv("HOME", pw->pw_dir, 1);
} }
if (! setuser) { if (!setuser) {
unsetenv("USER"); unsetenv("USER");
if (pw->pw_name) if (pw->pw_name)
setenv("USER", pw->pw_name, 1); setenv("USER", pw->pw_name, 1);
@ -996,26 +1014,22 @@ int start_stop_daemon(int argc, char **argv)
rc_stringlist_free(env_list); rc_stringlist_free(env_list);
/* For the path, remove the rcscript bin dir from it */ /* For the path, remove the rcscript bin dir from it */
if ((path = getenv("PATH"))) { if ((tmp = xstrdup(getenv("PATH")))) {
size_t mx = strlen(path); len = strlen(tmp);
char *newpath = xmalloc(mx); newpath = np = xmalloc(len);
char *token; p = tmp;
char *np = newpath; while ((token = strsep(&p, ":"))) {
size_t l; if (strcmp(token, RC_LIBDIR "/bin") == 0 ||
strcmp(token, RC_LIBDIR "/sbin") == 0)
p = path;
while ((token = strsep (&p, ":"))) {
if (strcmp (token, RC_LIBDIR "/bin") == 0 ||
strcmp (token, RC_LIBDIR "/sbin") == 0)
continue; continue;
len = strlen(token);
l = strlen (token);
if (np != newpath) if (np != newpath)
*np++ = ':'; *np++ = ':';
memcpy (np, token, l); memcpy(np, token, len);
np += l; np += len;
*np = '\0'; *np = '\0';
} }
free(tmp);
unsetenv("PATH"); unsetenv("PATH");
setenv("PATH", newpath, 1); setenv("PATH", newpath, 1);
} }
@ -1047,7 +1061,7 @@ int start_stop_daemon(int argc, char **argv)
close(i); close(i);
setsid(); setsid();
execv(exec, argv); execvp(exec, argv);
#ifdef HAVE_PAM #ifdef HAVE_PAM
if (pamr == PAM_SUCCESS) if (pamr == PAM_SUCCESS)
pam_close_session(pamh, PAM_SILENT); pam_close_session(pamh, PAM_SILENT);
@ -1056,27 +1070,24 @@ int start_stop_daemon(int argc, char **argv)
} }
/* Parent process */ /* Parent process */
if (! background) { if (!background) {
/* As we're not backgrounding the process, wait for our pid to return */ /* As we're not backgrounding the process, wait for our pid to return */
int status = 0; i = 0;
int savepid = pid; spid = pid;
errno = 0;
do { do {
pid = waitpid(savepid, &status, 0); pid = waitpid(spid, &i, 0);
if (pid < 1) { if (pid < 1) {
eerror("waitpid %d: %s", savepid, strerror(errno)); eerror("waitpid %d: %s", spid, strerror(errno));
return -1; return -1;
} }
} while (! WIFEXITED(status) && ! WIFSIGNALED(status)); } while (!WIFEXITED(i) && !WIFSIGNALED(i));
if (!WIFEXITED(i) || WEXITSTATUS(i) != 0) {
if (! WIFEXITED(status) || WEXITSTATUS(status) != 0) { if (!quiet)
if (! quiet)
eerrorx("%s: failed to start `%s'", applet, exec); eerrorx("%s: failed to start `%s'", applet, exec);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
pid = spid;
pid = savepid;
} }
/* Wait a little bit and check that process is still running /* Wait a little bit and check that process is still running
@ -1104,10 +1115,10 @@ int start_stop_daemon(int argc, char **argv)
* created. Once everything is in place we then wait some more * created. Once everything is in place we then wait some more
* to ensure that the daemon really is running and won't abort due * to ensure that the daemon really is running and won't abort due
* to a config error. */ * to a config error. */
if (! background && pidfile && nloopsp) if (!background && pidfile && nloopsp)
nloopsp --; nloopsp--;
else else
nloops --; nloops--;
/* This is knarly. /* This is knarly.
If we backgrounded then we know the exact pid. If we backgrounded then we know the exact pid.
@ -1131,19 +1142,19 @@ int start_stop_daemon(int argc, char **argv)
nloopsp = 0; nloopsp = 0;
} else } else
pid = 0; pid = 0;
if (do_stop((const char *const *)argv, cmd, if (do_stop(exec, (const char *const *)argv,
pid, uid, 0, true, false, true) > 0) pid, uid, 0, true, false, true) > 0)
alive = true; alive = true;
} }
if (! alive) if (!alive)
eerrorx("%s: %s died", applet, exec); eerrorx("%s: %s died", applet, exec);
} }
} }
if (svcname) if (svcname)
rc_service_daemon_set(svcname, (const char *const *)argv, rc_service_daemon_set(svcname, exec, (const char *const *)argv,
cmd, pidfile, true); pidfile, true);
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
/* NOTREACHED */ /* NOTREACHED */