notify: add minimal readiness/status protocol for spawned daemons

This commit is contained in:
Lennart Poettering 2010-06-16 05:10:31 +02:00
parent 17586c16ba
commit 8c47c7325f
18 changed files with 516 additions and 25 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
test-daemon
systemd-install
org.freedesktop.systemd1.*.xml
test-ns

View File

@ -67,7 +67,8 @@ noinst_PROGRAMS = \
test-engine \
test-job-type \
test-ns \
test-loopback
test-loopback \
test-daemon
dist_dbuspolicy_DATA = \
src/org.freedesktop.systemd1.conf
@ -316,8 +317,10 @@ test_loopback_SOURCES = \
src/test-loopback.c \
src/loopback-setup.c
test_loopback_CFLAGS = $(systemd_CFLAGS)
test_loopback_LDADD = $(systemd_LDADD)
test_daemon_SOURCES = \
$(BASIC_SOURCES) \
src/test-daemon.c \
src/sd-daemon.c
systemd_logger_SOURCES = \
$(BASIC_SOURCES) \

4
fixme
View File

@ -1,4 +1,4 @@
* timer
* calendar time support in timer
* enforce max number of concurrent connection limit in sockets.
@ -49,8 +49,6 @@
- bluetoothd (/var/run/sdp! @/org/bluez/audio!)
- distccd
* regnerate unit/sysv search paths on daemon reload
* write utmp record a la upstart for processes
* run PAM session stuff

View File

@ -535,6 +535,37 @@ int cgroup_notify_empty(Manager *m, const char *group) {
return 0;
}
Unit* cgroup_unit_by_pid(Manager *m, pid_t pid) {
CGroupBonding *l, *b;
char *group = NULL;
int r;
assert(m);
if (pid <= 1)
return NULL;
if ((r = cgroup_get_current_controller_path(pid, m->cgroup_controller, &group)))
return NULL;
l = hashmap_get(m->cgroup_bondings, group);
free(group);
if (!l)
return NULL;
LIST_FOREACH(by_path, b, l) {
if (!b->unit)
continue;
if (b->only_us)
return b->unit;
}
return NULL;
}
CGroupBonding *cgroup_bonding_find_list(CGroupBonding *first, const char *controller) {
CGroupBonding *b;

View File

@ -76,4 +76,6 @@ int manager_shutdown_cgroup(Manager *m, bool delete);
int cgroup_notify_empty(Manager *m, const char *group);
Unit* cgroup_unit_by_pid(Manager *m, pid_t pid);
#endif

View File

@ -41,7 +41,8 @@
" <property name=\"ControlPID\" type=\"u\" access=\"read\"/>\n" \
" <property name=\"SysVPath\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"BusName\" type=\"s\" access=\"read\"/>\n" \
" </interface>\n"
" <property name=\"StatusText\" type=\"s\" access=\"read\"/>\n" \
" </interface>\n"
#define INTROSPECTION \
DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
@ -76,6 +77,7 @@ DBusHandlerResult bus_service_message_handler(Unit *u, DBusMessage *message) {
{ "org.freedesktop.systemd1.Service", "ControlPID", bus_property_append_pid, "u", &u->service.control_pid },
{ "org.freedesktop.systemd1.Service", "SysVPath", bus_property_append_string, "s", u->service.sysv_path },
{ "org.freedesktop.systemd1.Service", "BusName", bus_property_append_string, "s", u->service.bus_name },
{ "org.freedesktop.systemd1.Service", "StatusText", bus_property_append_string, "s", u->service.status_text },
{ NULL, NULL, NULL, NULL, NULL }
};

View File

@ -354,6 +354,10 @@ int main(int argc, char *argv[]) {
if (server_init(&server, (unsigned) n) < 0)
return 2;
sd_notify(false,
"READY=1\n"
"STATUS=Processing requests...");
for (;;) {
struct epoll_event event;
int k;
@ -378,6 +382,9 @@ int main(int argc, char *argv[]) {
r = 0;
fail:
sd_notify(false,
"STATUS=Shutting down...");
server_done(&server);
log_info("systemd-initctl stopped as pid %llu", (unsigned long long) getpid());

View File

@ -547,6 +547,10 @@ int main(int argc, char *argv[]) {
if (server_init(&server, (unsigned) n) < 0)
return 3;
sd_notify(false,
"READY=1\n"
"STATUS=Processing requests...");
for (;;) {
struct epoll_event event;
int k;
@ -571,6 +575,9 @@ int main(int argc, char *argv[]) {
r = 0;
fail:
sd_notify(false,
"STATUS=Shutting down...");
server_done(&server);
log_info("systemd-logger stopped as pid %llu", (unsigned long long) getpid());

View File

@ -60,6 +60,67 @@
/* As soon as 5s passed since a unit was added to our GC queue, make sure to run a gc sweep */
#define GC_QUEUE_USEC_MAX (10*USEC_PER_SEC)
/* Where clients shall send notification messages to */
#define NOTIFY_SOCKET "/org/freedesktop/systemd1/notify"
static int manager_setup_notify(Manager *m) {
union {
struct sockaddr sa;
struct sockaddr_un un;
} sa;
struct epoll_event ev;
char *ne[2], **t;
int one = 1;
assert(m);
m->notify_watch.type = WATCH_NOTIFY;
if ((m->notify_watch.fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0)) < 0) {
log_error("Failed to allocate notification socket: %m");
return -errno;
}
zero(sa);
sa.sa.sa_family = AF_UNIX;
if (m->running_as == MANAGER_SESSION)
snprintf(sa.un.sun_path+1, sizeof(sa.un.sun_path)-1, NOTIFY_SOCKET "/%llu", random_ull());
else
strncpy(sa.un.sun_path+1, NOTIFY_SOCKET, sizeof(sa.un.sun_path)-1);
if (bind(m->notify_watch.fd, &sa.sa, sizeof(sa)) < 0) {
log_error("bind() failed: %m");
return -errno;
}
if (setsockopt(m->notify_watch.fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0) {
log_error("SO_PASSCRED failed: %m");
return -errno;
}
zero(ev);
ev.events = EPOLLIN;
ev.data.ptr = &m->notify_watch;
if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->notify_watch.fd, &ev) < 0)
return -errno;
if (asprintf(&ne[0], "NOTIFY_SOCKET=@%s", sa.un.sun_path+1) < 0)
return -ENOMEM;
ne[1] = NULL;
t = strv_env_merge(m->environment, ne, NULL);
free(ne[0]);
if (!t)
return -ENOMEM;
strv_free(m->environment);
m->environment = t;
return 0;
}
static int enable_special_signals(Manager *m) {
char fd;
@ -177,6 +238,9 @@ int manager_new(ManagerRunningAs running_as, bool confirm_spawn, Manager **_m) {
if ((r = manager_setup_cgroup(m)) < 0)
goto fail;
if ((r = manager_setup_notify(m)) < 0)
goto fail;
/* Try to connect to the busses, if possible. */
if ((r = bus_init_system(m)) < 0 ||
(r = bus_init_api(m)) < 0)
@ -364,6 +428,8 @@ void manager_free(Manager *m) {
close_nointr_nofail(m->epoll_fd);
if (m->signal_watch.fd >= 0)
close_nointr_nofail(m->signal_watch.fd);
if (m->notify_watch.fd >= 0)
close_nointr_nofail(m->notify_watch.fd);
lookup_paths_free(&m->lookup_paths);
strv_free(m->environment);
@ -1521,12 +1587,82 @@ unsigned manager_dispatch_dbus_queue(Manager *m) {
return n;
}
static int manager_process_notify_fd(Manager *m) {
ssize_t n;
assert(m);
for (;;) {
char buf[4096];
struct msghdr msghdr;
struct iovec iovec;
struct ucred *ucred;
union {
struct cmsghdr cmsghdr;
uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
} control;
Unit *u;
char **tags;
zero(iovec);
iovec.iov_base = buf;
iovec.iov_len = sizeof(buf)-1;
zero(control);
zero(msghdr);
msghdr.msg_iov = &iovec;
msghdr.msg_iovlen = 1;
msghdr.msg_control = &control;
msghdr.msg_controllen = sizeof(control);
if ((n = recvmsg(m->notify_watch.fd, &msghdr, MSG_DONTWAIT)) <= 0) {
if (n >= 0)
return -EIO;
if (errno == EAGAIN)
break;
return -errno;
}
if (msghdr.msg_controllen < CMSG_LEN(sizeof(struct ucred)) ||
control.cmsghdr.cmsg_level != SOL_SOCKET ||
control.cmsghdr.cmsg_type != SCM_CREDENTIALS ||
control.cmsghdr.cmsg_len != CMSG_LEN(sizeof(struct ucred))) {
log_warning("Received notify message without credentials. Ignoring.");
continue;
}
ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr);
if (!(u = hashmap_get(m->watch_pids, UINT32_TO_PTR(ucred->pid))))
if (!(u = cgroup_unit_by_pid(m, ucred->pid))) {
log_warning("Cannot find unit for notify message of PID %lu.", (unsigned long) ucred->pid);
continue;
}
char_array_0(buf);
if (!(tags = strv_split(buf, "\n\r")))
return -ENOMEM;
log_debug("Got notification message for unit %s", u->meta.id);
if (UNIT_VTABLE(u)->notify_message)
UNIT_VTABLE(u)->notify_message(u, tags);
strv_free(tags);
}
return 0;
}
static int manager_dispatch_sigchld(Manager *m) {
assert(m);
for (;;) {
siginfo_t si;
Unit *u;
int r;
zero(si);
@ -1555,6 +1691,17 @@ static int manager_dispatch_sigchld(Manager *m) {
free(name);
}
/* Let's flush any message the dying child might still
* have queued for us. This ensures that the process
* still exists in /proc so that we can figure out
* which cgroup and hence unit it belongs to. */
if ((r = manager_process_notify_fd(m)) < 0)
return r;
/* And now figure out the unit this belongs to */
if (!(u = hashmap_get(m->watch_pids, UINT32_TO_PTR(si.si_pid))))
u = cgroup_unit_by_pid(m, si.si_pid);
/* And now, we actually reap the zombie. */
if (waitid(P_PID, si.si_pid, &si, WEXITED) < 0) {
if (errno == EINTR)
@ -1572,11 +1719,12 @@ static int manager_dispatch_sigchld(Manager *m) {
si.si_status,
strna(si.si_code == CLD_EXITED ? exit_status_to_string(si.si_status) : strsignal(si.si_status)));
if (!(u = hashmap_remove(m->watch_pids, UINT32_TO_PTR(si.si_pid))))
if (!u)
continue;
log_debug("Child %llu belongs to %s", (long long unsigned) si.si_pid, u->meta.id);
hashmap_remove(m->watch_pids, UINT32_TO_PTR(si.si_pid));
UNIT_VTABLE(u)->sigchld_event(u, si.si_pid, si.si_code, si.si_status);
}
@ -1738,6 +1886,17 @@ static int process_event(Manager *m, struct epoll_event *ev) {
break;
case WATCH_NOTIFY:
/* An incoming daemon notification event? */
if (ev->events != EPOLLIN)
return -EINVAL;
if ((r = manager_process_notify_fd(m)) < 0)
return r;
break;
case WATCH_FD:
/* Some fd event, to be dispatched to the units */

View File

@ -56,6 +56,7 @@ typedef enum ManagerRunningAs {
enum WatchType {
WATCH_INVALID,
WATCH_SIGNAL,
WATCH_NOTIFY,
WATCH_FD,
WATCH_TIMER,
WATCH_MOUNT,
@ -171,6 +172,7 @@ struct Manager {
Hashmap *watch_pids; /* pid => Unit object n:1 */
Watch notify_watch;
Watch signal_watch;
int epoll_fd;
@ -215,7 +217,6 @@ struct Manager {
char *cgroup_hierarchy;
usec_t gc_queue_timestamp;
int gc_marker;
unsigned n_in_gc_queue;

View File

@ -921,12 +921,14 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) {
assert(m);
assert(pid >= 0);
if (pid != m->control_pid)
return;
m->control_pid = 0;
success = is_clean_exit(code, status);
m->failure = m->failure || !success;
assert(m->control_pid == pid);
m->control_pid = 0;
if (m->control_command) {
exec_status_fill(&m->control_command->exec_status, pid, code, status);
m->control_command = NULL;

View File

@ -24,6 +24,10 @@
SOFTWARE.
***/
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
@ -34,12 +38,14 @@
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
#include "sd-daemon.h"
int sd_listen_fds(int unset_environment) {
#ifdef DISABLE_SYSTEMD
#if defined(DISABLE_SYSTEMD) || !defined(__linux__)
return 0;
#else
int r, fd;
@ -317,3 +323,108 @@ int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t
return 1;
}
int sd_notify(int unset_environment, const char *state) {
#if defined(DISABLE_SYSTEMD) || !defined(__linux__)
return 0;
#else
int fd = -1, r;
struct msghdr msghdr;
struct iovec iovec;
union sockaddr_union sockaddr;
struct ucred *ucred;
union {
struct cmsghdr cmsghdr;
uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
} control;
const char *e;
if (!state) {
r = -EINVAL;
goto finish;
}
if (!(e = getenv("NOTIFY_SOCKET"))) {
r = 0;
goto finish;
}
/* Must be an abstract socket, or an absolute path */
if ((e[0] != '@' && e[0] != '/') || e[1] == 0) {
r = -EINVAL;
goto finish;
}
if ((fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0)) < 0) {
r = -errno;
goto finish;
}
memset(&sockaddr, 0, sizeof(sockaddr));
sockaddr.sa.sa_family = AF_UNIX;
strncpy(sockaddr.un.sun_path, e, sizeof(sockaddr.un.sun_path));
if (sockaddr.un.sun_path[0] == '@')
sockaddr.un.sun_path[0] = 0;
memset(&iovec, 0, sizeof(iovec));
iovec.iov_base = (char*) state;
iovec.iov_len = strlen(state);
memset(&control, 0, sizeof(control));
control.cmsghdr.cmsg_level = SOL_SOCKET;
control.cmsghdr.cmsg_type = SCM_CREDENTIALS;
control.cmsghdr.cmsg_len = CMSG_LEN(sizeof(struct ucred));
ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr);
ucred->pid = getpid();
ucred->uid = getuid();
ucred->gid = getgid();
memset(&msghdr, 0, sizeof(msghdr));
msghdr.msg_name = &sockaddr;
msghdr.msg_namelen = sizeof(struct sockaddr_un);
msghdr.msg_iov = &iovec;
msghdr.msg_iovlen = 1;
msghdr.msg_control = &control;
msghdr.msg_controllen = control.cmsghdr.cmsg_len;
if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) < 0) {
r = -errno;
goto finish;
}
r = 0;
finish:
if (unset_environment)
unsetenv("NOTIFY_SOCKET");
if (fd >= 0)
close(fd);
return r;
#endif
}
int sd_notifyf(int unset_environment, const char *format, ...) {
#if defined(DISABLE_SYSTEMD) || !defined(__linux__)
return 0;
#else
va_list ap;
char *p = NULL;
int r;
va_start(ap, format);
r = vasprintf(&p, format, ap);
va_end(ap);
if (r < 0 || !p)
return -ENOMEM;
r = sd_notify(unset_environment, p);
free(p);
return r;
#endif
}

View File

@ -27,8 +27,13 @@
SOFTWARE.
***/
#include <sys/types.h>
#include <inttypes.h>
#ifdef __cplusplus
extern "C" {
#endif
/* Reference implementation of a few systemd related interfaces for
* writing daemons. These interfaces are trivial to implement. To
* simplify porting we provide this reference
@ -111,4 +116,58 @@ int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port
* errno style error code on failure. */
int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length);
/* Informs systemd about changed daemon state. This takes a numeber of
* newline seperated environment-style variable assignments in a
* string. The following strings are known:
*
* READY=1 Tells systemd that daemon startup is finished (only
* relevant for services of Type=notify). The passed
* argument is a boolean "1" or "0". Since there is
* little value in signalling non-readiness the only
* value daemons should send is "READY=1".
*
* STATUS=... Passes a status string back to systemd that
* describes the daemon state. This is free-from and
* can be used for various purposes: general state
* feedback, fsck-like programs could pass completion
* percentages and failing programs could pass a human
* readable error message. Example: "STATUS=Completed
* 66% of file system check..."
*
* ERRNO=... If a daemon fails, the errno-style error code,
* formatted as string. Example: "ERRNO=2" for ENOENT.
*
* BUSERROR=... If a daemon fails, the D-Bus error-style error
* code. Example: "BUSERROR=org.freedesktop.DBus.Error.TimedOut"
*
* MAINPID=... The main pid of a daemon, in case systemd did not
* fork off the process itself. Example: "MAINPID=4711"
*
* See sd_notifyf() for more complete examples.
*/
int sd_notify(int unset_environment, const char *state);
/* Similar to sd_send_state() but takes a format string.
*
* Example 1: A daemon could send the following after initialization:
*
* sd_notifyf(0, "READY=1\n"
* "STATUS=Processing requests...\n"
* "MAINPID=%lu",
* (unsigned long) getpid());
*
* Example 2: A daemon could send the following shortly before
* exiting, on failure:
*
* sd_notifyf(0, "STATUS=Failed to start up: %s\n"
* "ERRNO=%i",
* strerror(errno),
* errno);
*/
int sd_notifyf(int unset_environment, const char *format, ...);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -149,6 +149,9 @@ static void service_done(Unit *u) {
free(s->sysv_runlevels);
s->sysv_runlevels = NULL;
free(s->status_text);
s->status_text = NULL;
exec_context_done(&s->exec_context);
exec_command_free_array(s->exec_command, _SERVICE_EXEC_COMMAND_MAX);
s->control_command = NULL;
@ -907,6 +910,10 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) {
fprintf(f, "%sSysVRunLevels: %s\n",
prefix, s->sysv_runlevels);
if (s->status_text)
fprintf(f, "%sStatus Text: %s\n",
prefix, s->status_text);
free(p2);
}
@ -1120,7 +1127,9 @@ static int service_coldplug(Unit *u) {
if ((s->deserialized_state == SERVICE_START &&
(s->type == SERVICE_FORKING ||
s->type == SERVICE_DBUS)) ||
s->type == SERVICE_DBUS ||
s->type == SERVICE_FINISH ||
s->type == SERVICE_NOTIFY)) ||
s->deserialized_state == SERVICE_START_POST ||
s->deserialized_state == SERVICE_RUNNING ||
s->deserialized_state == SERVICE_RELOAD ||
@ -1541,7 +1550,7 @@ static void service_enter_start(Service *s) {
if ((r = service_spawn(s,
s->exec_command[SERVICE_EXEC_START],
s->type == SERVICE_FORKING || s->type == SERVICE_DBUS,
s->type == SERVICE_FORKING || s->type == SERVICE_DBUS || s->type == SERVICE_NOTIFY,
true,
true,
true,
@ -1569,13 +1578,15 @@ static void service_enter_start(Service *s) {
service_set_state(s, SERVICE_START);
} else if (s->type == SERVICE_FINISH ||
s->type == SERVICE_DBUS) {
s->type == SERVICE_DBUS ||
s->type == SERVICE_NOTIFY) {
/* For finishing services we wait until the start
* process exited, too, but it is our main process. */
/* For D-Bus services we know the main pid right away,
* but wait for the bus name to appear on the bus. */
* but wait for the bus name to appear on the
* bus. Notify services are similar. */
s->main_pid = pid;
s->main_pid_known = true;
@ -1946,7 +1957,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
exec_status_fill(&s->main_exec_status, pid, code, status);
s->main_pid = 0;
if (s->type == SERVICE_SIMPLE || s->type == SERVICE_FINISH) {
if (s->type != SERVICE_FORKING) {
assert(s->exec_command[SERVICE_EXEC_START]);
s->exec_command[SERVICE_EXEC_START]->exec_status = s->main_exec_status;
}
@ -1974,7 +1985,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
service_enter_signal(s, SERVICE_FINAL_SIGTERM, false);
break;
} else {
assert(s->type == SERVICE_DBUS);
assert(s->type == SERVICE_DBUS || s->type == SERVICE_NOTIFY);
/* Fall through */
}
@ -2101,8 +2112,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
assert_not_reached("Uh, control process died at wrong time.");
}
}
} else
assert_not_reached("Got SIGCHLD for unkown PID");
}
}
static void service_timer_event(Unit *u, uint64_t elapsed, Watch* w) {
@ -2195,6 +2205,57 @@ static void service_cgroup_notify_event(Unit *u) {
}
}
static void service_notify_message(Unit *u, char **tags) {
Service *s = SERVICE(u);
const char *e;
assert(u);
log_debug("%s: Got message", u->meta.id);
/* Interpret MAINPID= */
if ((e = strv_find_prefix(tags, "MAINPID=")) &&
(s->state == SERVICE_START ||
s->state == SERVICE_START_POST ||
s->state == SERVICE_RUNNING ||
s->state == SERVICE_RELOAD)) {
unsigned long pid;
if (safe_atolu(e + 8, &pid) < 0 ||
(unsigned long) (pid_t) pid != pid ||
pid <= 1)
log_warning("Failed to parse %s", e);
else {
log_debug("%s: got %s", u->meta.id, e);
s->main_pid = (pid_t) pid;
}
}
/* Interpret READY= */
if (s->type == SERVICE_NOTIFY &&
s->state == SERVICE_START &&
strv_find(tags, "READY=1")) {
log_debug("%s: got READY=1", u->meta.id);
service_enter_start_post(s);
}
/* Interpret STATUS= */
if ((e = strv_find_prefix(tags, "STATUS="))) {
char *t;
if (!(t = strdup(e+7))) {
log_error("Failed to allocate string.");
return;
}
log_debug("%s: got %s", u->meta.id, e);
free(s->status_text);
s->status_text = t;
}
}
static int service_enumerate(Manager *m) {
char **p;
unsigned i;
@ -2456,7 +2517,8 @@ static const char* const service_type_table[_SERVICE_TYPE_MAX] = {
[SERVICE_FORKING] = "forking",
[SERVICE_SIMPLE] = "simple",
[SERVICE_FINISH] = "finish",
[SERVICE_DBUS] = "dbus"
[SERVICE_DBUS] = "dbus",
[SERVICE_NOTIFY] = "notify"
};
DEFINE_STRING_TABLE_LOOKUP(service_type, ServiceType);
@ -2502,6 +2564,7 @@ const UnitVTable service_vtable = {
.timer_event = service_timer_event,
.cgroup_notify_empty = service_cgroup_notify_event,
.notify_message = service_notify_message,
.bus_name_owner_change = service_bus_name_owner_change,
.bus_query_pid_done = service_bus_query_pid_done,

View File

@ -60,6 +60,7 @@ typedef enum ServiceType {
SERVICE_FORKING, /* forks by itself (i.e. traditional daemons) */
SERVICE_FINISH, /* we fork and wait until the program finishes (i.e. programs like fsck which run and need to finish before we continue) */
SERVICE_DBUS, /* we fork and wait until a specific D-Bus name appears on the bus */
SERVICE_NOTIFY, /* we fork and wait until a daemon sends us a ready message with sd_notify() */
_SERVICE_TYPE_MAX,
_SERVICE_TYPE_INVALID = -1
} ServiceType;
@ -121,6 +122,8 @@ struct Service {
char *bus_name;
char *status_text;
RateLimit ratelimit;
int socket_fd;

View File

@ -1228,12 +1228,14 @@ static void socket_sigchld_event(Unit *u, pid_t pid, int code, int status) {
assert(s);
assert(pid >= 0);
if (pid != s->control_pid)
return;
s->control_pid = 0;
success = is_clean_exit(code, status);
s->failure = s->failure || !success;
assert(s->control_pid == pid);
s->control_pid = 0;
if (s->control_command)
exec_status_fill(&s->control_command->exec_status, pid, code, status);

37
src/test-daemon.c Normal file
View File

@ -0,0 +1,37 @@
/*-*- Mode: C; c-basic-offset: 8 -*-*/
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <unistd.h>
#include "sd-daemon.h"
int main(int argc, char*argv[]) {
sd_notify(0, "STATUS=Starting up");
sleep(5);
sd_notify(0,
"STATUS=Running\n"
"READY=1");
sleep(10);
sd_notify(0, "STATUS=Quitting");
return 0;
}

View File

@ -285,6 +285,9 @@ struct UnitVTable {
* ran empty */
void (*cgroup_notify_empty)(Unit *u);
/* Called whenever a process of this unit sends us a message */
void (*notify_message)(Unit *u, char **tags);
/* Called whenever a name thus Unit registered for comes or
* goes away. */
void (*bus_name_owner_change)(Unit *u, const char *name, const char *old_owner, const char *new_owner);