#define NEWLINES "\n\r"
#ifdef HAVE_SYSV_COMPAT
+
+#define DEFAULT_SYSV_TIMEOUT_USEC (3*USEC_PER_MINUTE)
+
typedef enum RunlevelType {
RUNLEVEL_UP,
RUNLEVEL_DOWN,
static void service_connection_unref(Service *s) {
assert(s);
- if (!s->socket)
+ if (!s->accept_socket)
return;
- socket_connection_unref(s->socket);
- s->socket = NULL;
+ socket_connection_unref(s->accept_socket);
+ s->accept_socket = NULL;
}
static void service_done(Unit *u) {
service_close_socket_fd(s);
service_connection_unref(s);
+ set_free(s->configured_sockets);
+
unit_unwatch_timer(u, &s->timer_watch);
}
return r;
}
-static int sysv_translate_facility(const char *name, char **_r) {
+static int sysv_translate_facility(const char *name, const char *filename, char **_r) {
/* We silently ignore the $ prefix here. According to the LSB
* spec it simply indicates whether something is a
unsigned i;
char *r;
+ const char *n;
- for (i = 0; i < ELEMENTSOF(table); i += 2)
+ assert(name);
+ assert(_r);
- if (streq(table[i], name) ||
- (*name == '$' && streq(table[i], name+1))) {
+ n = *name == '$' ? name + 1 : name;
- if (!table[i+1])
- return 0;
+ for (i = 0; i < ELEMENTSOF(table); i += 2) {
- if (!(r = strdup(table[i+1])))
- return -ENOMEM;
+ if (!streq(table[i], n))
+ continue;
- goto finish;
- }
+ if (!table[i+1])
+ return 0;
+
+ if (!(r = strdup(table[i+1])))
+ return -ENOMEM;
+
+ goto finish;
+ }
/* If we don't know this name, fallback heuristics to figure
* out whether something is a target or an service alias. */
if (*name == '$')
- r = unit_name_build(name+1, NULL, ".target");
+ /* Facilities starting with $ are most likely targets */
+ r = unit_name_build(n, NULL, ".target");
+ else if (filename && streq(name, filename))
+ /* Names equalling the file name of the services are redundant */
+ return 0;
else
- r = sysv_translate_name(name);
+ /* Everything else we assume to be normal service names */
+ r = sysv_translate_name(n);
if (!r)
return -ENOMEM;
LSB,
LSB_DESCRIPTION
} state = NORMAL;
+ char *short_description = NULL, *long_description = NULL, *chkconfig_description = NULL, *description;
assert(s);
assert(path);
s->sysv_runlevels = d;
}
- } else if (startswith_no_case(t, "description:") &&
- !u->meta.description) {
+ } else if (startswith_no_case(t, "description:")) {
size_t k = strlen(t);
char *d;
+ const char *j;
if (t[k-1] == '\\') {
state = DESCRIPTION;
t[k-1] = 0;
}
- if (!(d = strappend("LSB: ", strstrip(t+12)))) {
- r = -ENOMEM;
- goto finish;
- }
+ if ((j = strstrip(t+12)) && *j) {
+ if (!(d = strdup(j))) {
+ r = -ENOMEM;
+ goto finish;
+ }
+ } else
+ d = NULL;
- free(u->meta.description);
- u->meta.description = d;
+ free(chkconfig_description);
+ chkconfig_description = d;
} else if (startswith_no_case(t, "pidfile:")) {
* continuation */
size_t k = strlen(t);
- char *d;
+ char *j;
if (t[k-1] == '\\')
t[k-1] = 0;
else
state = NORMAL;
- assert(u->meta.description);
- if (asprintf(&d, "%s %s", u->meta.description, strstrip(t)) < 0) {
- r = -ENOMEM;
- goto finish;
- }
+ if ((j = strstrip(t)) && *j) {
+ char *d = NULL;
- free(u->meta.description);
- u->meta.description = d;
+ if (chkconfig_description)
+ asprintf(&d, "%s %s", chkconfig_description, j);
+ else
+ d = strdup(j);
+
+ if (!d) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ free(chkconfig_description);
+ chkconfig_description = d;
+ }
} else if (state == LSB || state == LSB_DESCRIPTION) {
goto finish;
}
- if (streq(n, file_name_from_path(path))) {
- free(n);
- continue;
- }
-
- r = sysv_translate_facility(n, &m);
+ r = sysv_translate_facility(n, file_name_from_path(path), &m);
free(n);
if (r < 0)
goto finish;
}
- if (streq(n, file_name_from_path(path))) {
- free(n);
- continue;
- }
-
- r = sysv_translate_facility(n, &m);
+ r = sysv_translate_facility(n, file_name_from_path(path), &m);
free(n);
if (r < 0)
s->sysv_runlevels = d;
}
- } else if (startswith_no_case(t, "Description:") &&
- !u->meta.description) {
- char *d;
-
- /* We use the long description only if
- * no short description is set. */
+ } else if (startswith_no_case(t, "Description:")) {
+ char *d, *j;
state = LSB_DESCRIPTION;
- if (!(d = strappend("LSB: ", strstrip(t+12)))) {
- r = -ENOMEM;
- goto finish;
- }
+ if ((j = strstrip(t+12)) && *j) {
+ if (!(d = strdup(j))) {
+ r = -ENOMEM;
+ goto finish;
+ }
+ } else
+ d = NULL;
- free(u->meta.description);
- u->meta.description = d;
+ free(long_description);
+ long_description = d;
} else if (startswith_no_case(t, "Short-Description:")) {
- char *d;
+ char *d, *j;
state = LSB;
- if (!(d = strappend("LSB: ", strstrip(t+18)))) {
- r = -ENOMEM;
- goto finish;
- }
+ if ((j = strstrip(t+18)) && *j) {
+ if (!(d = strdup(j))) {
+ r = -ENOMEM;
+ goto finish;
+ }
+ } else
+ d = NULL;
- free(u->meta.description);
- u->meta.description = d;
+ free(short_description);
+ short_description = d;
} else if (startswith_no_case(t, "X-Interactive:")) {
int b;
} else if (state == LSB_DESCRIPTION) {
if (startswith(l, "#\t") || startswith(l, "# ")) {
- char *d;
+ char *j;
- assert(u->meta.description);
- if (asprintf(&d, "%s %s", u->meta.description, t) < 0) {
- r = -ENOMEM;
- goto finish;
+ if ((j = strstrip(t)) && *j) {
+ char *d = NULL;
+
+ if (long_description)
+ asprintf(&d, "%s %s", long_description, t);
+ else
+ d = strdup(j);
+
+ if (!d) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ free(long_description);
+ long_description = d;
}
- free(u->meta.description);
- u->meta.description = d;
} else
state = LSB;
}
/* Don't timeout special services during boot (like fsck) */
s->timeout_usec = 0;
- }
+ } else
+ s->timeout_usec = DEFAULT_SYSV_TIMEOUT_USEC;
/* Special setting for all SysV services */
s->type = SERVICE_FORKING;
s->remain_after_exit = true;
- s->restart = SERVICE_ONCE;
+ s->restart = SERVICE_RESTART_NO;
s->exec_context.std_output =
(s->meta.manager->sysv_console || s->exec_context.std_input == EXEC_INPUT_TTY)
? EXEC_OUTPUT_TTY : EXEC_OUTPUT_NULL;
s->exec_context.kill_mode = KILL_PROCESS_GROUP;
+ /* We use the long description only if
+ * no short description is set. */
+
+ if (short_description)
+ description = short_description;
+ else if (chkconfig_description)
+ description = chkconfig_description;
+ else if (long_description)
+ description = long_description;
+ else
+ description = NULL;
+
+ if (description) {
+ char *d;
+
+ if (!(d = strappend("LSB: ", description))) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ u->meta.description = d;
+ }
+
u->meta.load_state = UNIT_LOADED;
r = 0;
if (f)
fclose(f);
+ free(short_description);
+ free(long_description);
+ free(chkconfig_description);
+
return r;
}
if (s->socket_fd >= 0)
return 0;
+ if (!set_isempty(s->configured_sockets))
+ return 0;
+
/* Collects all Socket objects that belong to this
* service. Note that a service might have multiple sockets
* via multiple names. */
static int service_notify_sockets_dead(Service *s) {
Iterator i;
- Set *set;
+ Set *set, *free_set = NULL;
Socket *sock;
int r;
assert(s);
+ /* Notifies all our sockets when we die */
+
if (s->socket_fd >= 0)
return 0;
- /* Notifies all our sockets when we die */
- if ((r = service_get_sockets(s, &set)) < 0)
- return r;
+ if (!set_isempty(s->configured_sockets))
+ set = s->configured_sockets;
+ else {
+ if ((r = service_get_sockets(s, &free_set)) < 0)
+ return r;
+
+ set = free_set;
+ }
SET_FOREACH(sock, set, i)
socket_notify_service_dead(sock);
- set_free(set);
+ set_free(free_set);
return 0;
}
int r;
int *rfds = NULL;
unsigned rn_fds = 0;
- Set *set;
+ Set *set, *free_set = NULL;
Socket *sock;
assert(s);
if (s->socket_fd >= 0)
return 0;
- if ((r = service_get_sockets(s, &set)) < 0)
- return r;
+ if (!set_isempty(s->configured_sockets))
+ set = s->configured_sockets;
+ else {
+ if ((r = service_get_sockets(s, &free_set)) < 0)
+ return r;
+
+ set = free_set;
+ }
SET_FOREACH(sock, set, i) {
int *cfds;
*fds = rfds;
*n_fds = rn_fds;
- set_free(set);
+ set_free(free_set);
return 0;
return -ECANCELED;
}
- if ((s->exec_context.std_input == EXEC_INPUT_SOCKET ||
- s->exec_context.std_output == EXEC_OUTPUT_SOCKET ||
- s->exec_context.std_error == EXEC_OUTPUT_SOCKET) &&
- s->socket_fd < 0) {
- log_warning("%s can only be started with a per-connection socket.", u->meta.id);
- return -EINVAL;
- }
-
s->failure = false;
s->main_pid_known = false;
s->forbid_restart = false;
STRV_FOREACH(p, arch_daemons_split) {
free(name);
+ name = NULL;
if (**p == '!') /* daemons prefixed with ! are disabled, so ignore them */
continue;
s->socket_fd = fd;
s->got_socket_fd = true;
- s->socket = sock;
+ s->accept_socket = sock;
return 0;
}
DEFINE_STRING_TABLE_LOOKUP(service_state, ServiceState);
static const char* const service_restart_table[_SERVICE_RESTART_MAX] = {
- [SERVICE_ONCE] = "once",
- [SERVICE_RESTART_ON_SUCCESS] = "restart-on-success",
- [SERVICE_RESTART_ALWAYS] = "restart-always",
+ [SERVICE_RESTART_NO] = "no",
+ [SERVICE_RESTART_ON_SUCCESS] = "on-success",
+ [SERVICE_RESTART_ALWAYS] = "always",
};
DEFINE_STRING_TABLE_LOOKUP(service_restart, ServiceRestart);