sysv-generator.c revision c279613f861636c816f2f7df051b02c2f55a5134
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2014 Thomas H.P. Andersen
Copyright 2010 Lennart Poettering
Copyright 2011 Michal Schmidt
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include "alloc-util.h"
#include "dirent-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "hashmap.h"
#include "hexdecoct.h"
#include "install.h"
#include "log.h"
#include "mkdir.h"
#include "path-lookup.h"
#include "path-util.h"
#include "set.h"
#include "special.h"
#include "stat-util.h"
#include "string-util.h"
#include "strv.h"
#include "unit-name.h"
#include "util.h"
typedef enum RunlevelType {
} RunlevelType;
static const struct {
const char *path;
const char *target;
const RunlevelType type;
} rcnd_table[] = {
/* Standard SysV runlevels for start-up */
/* Standard SysV runlevels for shutdown */
/* Note that the order here matters, as we read the
directories in this order, and we want to make sure that
sysv_start_priority is known when we first load the
unit. And that value we only know from S links. Hence
UP must be read before DOWN */
};
const char *arg_dest = "/tmp";
typedef struct SysvStub {
char *name;
char *path;
char *description;
int sysv_start_priority;
char *pid_file;
char **before;
char **after;
char **wants;
char **wanted_by;
char **conflicts;
bool has_lsb;
bool reload;
bool loaded;
} SysvStub;
static void free_sysvstub(SysvStub *s) {
if (!s)
return;
free(s->description);
free(s);
}
static void free_sysvstub_hashmapp(Hashmap **h) {
while ((stub = hashmap_steal_first(*h)))
hashmap_free(*h);
}
int r;
if (r < 0) {
return 0;
return -errno;
}
return 1;
}
const char *link;
int r;
if (r < 0) {
return 0;
return -errno;
}
return 1;
}
static int generate_unit_file(SysvStub *s) {
const char *unit;
char **p;
int r;
assert(s);
if (!s->loaded)
return 0;
return log_oom();
/* We might already have a symlink with the same name from a Provides:,
* so remove an existing link */
if (is_symlink(unit) > 0) {
}
if (!f)
fprintf(f,
"# Automatically generated by systemd-sysv-generator\n\n"
"[Unit]\n"
"Documentation=man:systemd-sysv-generator(8)\n"
"SourcePath=%s\n",
s->path);
if (s->description)
fprintf(f,
"\n[Service]\n"
"Type=forking\n"
"Restart=no\n"
"TimeoutSec=5min\n"
"IgnoreSIGPIPE=no\n"
"KillMode=process\n"
"GuessMainPID=no\n"
"RemainAfterExit=%s\n",
if (s->pid_file)
fprintf(f,
"ExecStart=%s start\n"
"ExecStop=%s stop\n",
if (s->reload)
r = fflush_and_check(f);
if (r < 0)
STRV_FOREACH(p, s->wanted_by) {
r = add_symlink(s->name, *p);
if (r < 0)
log_warning_errno(r, "Failed to create 'Wants' symlink to %s, ignoring: %m", *p);
}
return 1;
}
static bool usage_contains_reload(const char *line) {
}
static char *sysv_translate_name(const char *name) {
_cleanup_free_ char *c = NULL;
char *res;
if (!c)
return NULL;
if (res)
*res = 0;
return NULL;
return res;
}
/* We silently ignore the $ prefix here. According to the LSB
* spec it simply indicates whether something is a
* standardized name or a distribution-specific one. Since we
* just follow what already exists and do not introduce new
* uses or names we don't care who introduced a new name. */
static const char * const table[] = {
/* LSB defined facilities */
"local_fs", NULL,
"network", SPECIAL_NETWORK_ONLINE_TARGET,
"named", SPECIAL_NSS_LOOKUP_TARGET,
"portmap", SPECIAL_RPCBIND_TARGET,
"remote_fs", SPECIAL_REMOTE_FS_TARGET,
"syslog", NULL,
"time", SPECIAL_TIME_SYNC_TARGET,
};
char *filename_no_sh, *e, *m;
const char *n;
unsigned i;
int r;
continue;
if (!table[i+1])
return 0;
if (!m)
return log_oom();
*ret = m;
return 1;
}
/* If we don't know this name, fallback heuristics to figure
* out whether something is a target or a service alias. */
/* Facilities starting with $ are most likely targets */
if (*name == '$') {
if (r < 0)
return log_error_errno(r, "Failed to build name: %m");
return r;
}
/* Strip ".sh" suffix from file name for comparison */
if (e) {
*e = '\0';
}
/* Names equaling the file name of the services are redundant */
return 0;
/* Everything else we assume to be normal service names */
m = sysv_translate_name(n);
if (!m)
return log_oom();
*ret = m;
return 1;
}
int r;
assert(s);
for (;;) {
if (r < 0)
return log_error_errno(r, "Failed to parse word from provides string: %m");
if (r == 0)
break;
if (r <= 0) /* continue on error */
continue;
switch (unit_name_to_type(m)) {
case UNIT_SERVICE:
if (r < 0)
break;
case UNIT_TARGET:
/* NB: SysV targets which are provided by a
* service are pulled in by the services, as
* an indication that the generic service is
* now available. This is strictly one-way.
* The targets do NOT pull in SysV services! */
r = strv_extend(&s->before, m);
if (r < 0)
return log_oom();
r = strv_extend(&s->wants, m);
if (r < 0)
return log_oom();
if (streq(m, SPECIAL_NETWORK_ONLINE_TARGET)) {
if (r < 0)
return log_oom();
}
break;
case _UNIT_TYPE_INVALID:
log_warning("Unit name '%s' is invalid", m);
break;
default:
log_warning("Unknown unit type for unit '%s'", m);
}
}
return 0;
}
static int handle_dependencies(SysvStub *s, unsigned line, const char *full_text, const char *text) {
int r;
assert(s);
for (;;) {
bool is_before;
if (r < 0)
return log_error_errno(r, "Failed to parse word from provides string: %m");
if (r == 0)
break;
if (r <= 0) /* continue on error */
continue;
/* the network-online target is special, as it needs to be actively pulled in */
r = strv_extend(&s->after, m);
if (r < 0)
return log_oom();
r = strv_extend(&s->wants, m);
} else
if (r < 0)
return log_oom();
}
return 0;
}
_cleanup_fclose_ FILE *f;
unsigned line = 0;
int r;
enum {
LSB,
_cleanup_free_ char *short_description = NULL, *long_description = NULL, *chkconfig_description = NULL;
char *description;
bool supports_reload = false;
char l[LINE_MAX];
assert(s);
if (!f) {
return 0;
}
FOREACH_LINE(l, f, goto fail) {
char *t;
line++;
t = strstrip(l);
if (*t != '#') {
/* Try to figure out whether this init script supports
* the reload operation. This heuristic looks for
* "Usage" lines which include the reload option. */
if ( state == USAGE_CONTINUATION ||
if (usage_contains_reload(t)) {
supports_reload = true;
else
}
continue;
}
s->has_lsb = true;
continue;
}
continue;
}
t++;
t += strspn(t, WHITESPACE);
/* Try to parse Red Hat style description */
if (startswith_no_case(t, "description:")) {
size_t k;
const const char *j;
k = strlen(t);
if (k > 0 && t[k-1] == '\\') {
state = DESCRIPTION;
t[k-1] = 0;
}
j = strstrip(t+12);
if (isempty(j))
j = NULL;
r = free_and_strdup(&chkconfig_description, j);
if (r < 0)
return log_oom();
} else if (startswith_no_case(t, "pidfile:")) {
const char *fn;
if (!path_is_absolute(fn)) {
continue;
}
if (r < 0)
return log_oom();
}
} else if (state == DESCRIPTION) {
/* Try to parse Red Hat style description
* continuation */
size_t k;
char *j;
k = strlen(t);
if (k > 0 && t[k-1] == '\\')
t[k-1] = 0;
else
j = strstrip(t);
if (!isempty(j)) {
char *d = NULL;
else
d = strdup(j);
if (!d)
return log_oom();
}
if (startswith_no_case(t, "Provides:")) {
if (r < 0)
return r;
} else if (startswith_no_case(t, "Required-Start:") ||
startswith_no_case(t, "Should-Start:") ||
startswith_no_case(t, "X-Start-Before:") ||
startswith_no_case(t, "X-Start-After:")) {
if (r < 0)
return r;
} else if (startswith_no_case(t, "Description:")) {
const char *j;
j = strstrip(t+12);
if (isempty(j))
j = NULL;
r = free_and_strdup(&long_description, j);
if (r < 0)
return log_oom();
} else if (startswith_no_case(t, "Short-Description:")) {
const char *j;
j = strstrip(t+18);
if (isempty(j))
j = NULL;
r = free_and_strdup(&short_description, j);
if (r < 0)
return log_oom();
} else if (state == LSB_DESCRIPTION) {
const char *j;
j = strstrip(t);
if (!isempty(j)) {
char *d = NULL;
if (long_description)
else
d = strdup(j);
if (!d)
return log_oom();
long_description = d;
}
} else
}
}
}
s->reload = supports_reload;
/* We use the long description only if
* no short description is set. */
if (short_description)
else if (chkconfig_description)
else if (long_description)
else
description = NULL;
if (description) {
char *d;
if (!d)
return log_oom();
s->description = d;
}
s->loaded = true;
return 0;
fail:
}
Iterator j;
int r;
assert(s);
if (!s->loaded)
return 0;
if (s->sysv_start_priority < 0)
return 0;
if (s == other)
continue;
continue;
if (other->sysv_start_priority < 0)
continue;
/* If both units have modern headers we don't care
* about the priorities */
continue;
if (r < 0)
return log_oom();
if (r < 0)
return log_oom();
} else
continue;
/* FIXME: Maybe we should compare the name here lexicographically? */
}
return 0;
}
char **path;
if (!d) {
continue;
}
FOREACH_DIRENT(de, d, log_error_errno(errno, "Failed to enumerate directory %s, ignoring: %m", *path)) {
int r;
continue;
}
continue;
continue;
if (!name)
return log_oom();
continue;
continue;
}
if (!fpath)
return log_oom();
if (!service)
return log_oom();
if (r < 0)
return log_oom();
}
}
return 0;
}
unsigned i;
Iterator j;
char **p;
int r;
for (i = 0; i < ELEMENTSOF(rcnd_table); i ++) {
if (!path) {
r = log_oom();
goto finish;
}
if (!d) {
continue;
}
FOREACH_DIRENT(de, d, log_error_errno(errno, "Failed to enumerate directory %s, ignoring: %m", path)) {
int a, b;
continue;
continue;
if (a < 0 || b < 0)
continue;
if (!fpath) {
r = log_oom();
goto finish;
}
if (!name) {
r = log_oom();
goto finish;
}
if (!service){
continue;
}
if (r < 0) {
log_oom();
goto finish;
}
if (r < 0) {
log_oom();
goto finish;
}
if (r < 0) {
log_oom();
goto finish;
}
if (r < 0) {
log_oom();
goto finish;
}
}
}
}
}
for (i = 0; i < ELEMENTSOF(rcnd_table); i ++)
if (r < 0) {
log_oom();
goto finish;
}
if (r < 0) {
log_oom();
goto finish;
}
}
if (r < 0) {
log_oom();
goto finish;
}
if (r < 0) {
log_oom();
goto finish;
}
}
r = 0;
for (i = 0; i < ELEMENTSOF(rcnd_table); i++)
return r;
}
Iterator j;
int r;
log_error("This program takes three or no arguments.");
return EXIT_FAILURE;
}
if (argc > 1)
log_open();
umask(0022);
if (r < 0) {
log_error_errno(r, "Failed to find lookup paths: %m");
goto finish;
}
if (!all_services) {
r = log_oom();
goto finish;
}
if (r < 0)
goto finish;
if (r < 0)
goto finish;
(void) generate_unit_file(service);
}
r = 0;
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}