mirror of
https://github.com/pygos/init.git
synced 2024-11-22 03:09:46 +01:00
Initial commit
Signed-off-by: David Oberhollenzer <david.oberhollenzer@tele2.at>
This commit is contained in:
commit
9a88f7da45
44 changed files with 2248 additions and 0 deletions
26
.gitignore
vendored
Normal file
26
.gitignore
vendored
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
*.in
|
||||||
|
*.cache
|
||||||
|
.*
|
||||||
|
*.o
|
||||||
|
*.a
|
||||||
|
*~
|
||||||
|
Makefile
|
||||||
|
aclocal.m4
|
||||||
|
compile
|
||||||
|
config.*
|
||||||
|
configure
|
||||||
|
depcomp
|
||||||
|
install-sh
|
||||||
|
missing
|
||||||
|
stamp-h1
|
||||||
|
|
||||||
|
init
|
||||||
|
service
|
||||||
|
reboot
|
||||||
|
shutdown
|
||||||
|
|
||||||
|
services/agetty
|
||||||
|
services/hostname
|
||||||
|
services/loopback
|
||||||
|
services/sysctl
|
||||||
|
services/hwclock
|
14
Makefile.am
Normal file
14
Makefile.am
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
ACLOCAL_AMFLAGS = -I m4
|
||||||
|
|
||||||
|
AM_CPPFLAGS = -D_GNU_SOURCE -I$(top_srcdir)/lib/include
|
||||||
|
AM_CFLAGS = -std=c99 -pedantic -Wall -Wextra
|
||||||
|
|
||||||
|
sbin_PROGRAMS =
|
||||||
|
noinst_LIBRARIES =
|
||||||
|
EXTRA_DIST =
|
||||||
|
|
||||||
|
include lib/Makemodule.am
|
||||||
|
include cmd/Makemodule.am
|
||||||
|
include initd/Makemodule.am
|
||||||
|
include services/Makemodule.am
|
||||||
|
include servicecmd/Makemodule.am
|
3
autogen.sh
Executable file
3
autogen.sh
Executable file
|
@ -0,0 +1,3 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
autoreconf --force --install --symlink
|
13
cmd/Makemodule.am
Normal file
13
cmd/Makemodule.am
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
shutdown_SOURCES = cmd/shutdown.c
|
||||||
|
shutdown_CPPFLAGS = $(AM_CPPFLAGS) -DPROGNAME=shutdown
|
||||||
|
shutdown_CFLAGS = $(AM_CFLAGS)
|
||||||
|
shutdown_LDFLAGS = $(AM_LDFLAGS)
|
||||||
|
shutdown_LDADD = libinit.a
|
||||||
|
|
||||||
|
reboot_SOURCES = cmd/shutdown.c
|
||||||
|
reboot_CPPFLAGS = $(AM_CPPFLAGS) -DPROGNAME=reboot
|
||||||
|
reboot_CFLAGS = $(AM_CFLAGS)
|
||||||
|
reboot_LDFLAGS = $(AM_LDFLAGS)
|
||||||
|
reboot_LDADD = libinit.a
|
||||||
|
|
||||||
|
sbin_PROGRAMS += reboot shutdown
|
139
cmd/shutdown.c
Normal file
139
cmd/shutdown.c
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include <sys/reboot.h>
|
||||||
|
#include <linux/reboot.h>
|
||||||
|
|
||||||
|
#include "telinit.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#define STRINIFY(x) #x
|
||||||
|
#define STRINIFY_VALUE(x) STRINIFY(x)
|
||||||
|
#define PROGRAM_NAME STRINIFY_VALUE(PROGNAME)
|
||||||
|
|
||||||
|
#define FL_FORCE 0x01
|
||||||
|
#define FL_NOSYNC 0x02
|
||||||
|
|
||||||
|
static const struct option options[] = {
|
||||||
|
{ "help", no_argument, NULL, 'h' },
|
||||||
|
{ "version", no_argument, NULL, 'V' },
|
||||||
|
{ "poweroff", no_argument, NULL, 'p' },
|
||||||
|
{ "reboot", no_argument, NULL, 'r' },
|
||||||
|
{ "force", no_argument, NULL, 'f' },
|
||||||
|
{ "no-sync", no_argument, NULL, 'n' },
|
||||||
|
{ NULL, 0, NULL, 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *shortopt = "hVprfn";
|
||||||
|
|
||||||
|
static const char *defact_str = "power-off";
|
||||||
|
static int defact = TI_SHUTDOWN;
|
||||||
|
|
||||||
|
static NORETURN void usage(int status)
|
||||||
|
{
|
||||||
|
fprintf(status == EXIT_SUCCESS ? stdout : stderr,
|
||||||
|
"%s [OPTIONS...]\n\n"
|
||||||
|
"Perform a system shutdown or reboot.\n\n"
|
||||||
|
" -h, --help Display this help text and exit.\n"
|
||||||
|
" -V, --version Display version information and exit.\n"
|
||||||
|
" -p, --poweroff Power-off the machine.\n"
|
||||||
|
" -r, --reboot Reboot the machine.\n"
|
||||||
|
" -f, --force Force immediate power-off or reboot. Do not contact the\n"
|
||||||
|
" init system.\n"
|
||||||
|
" -n, --no-sync Don't sync storage media before power-off or reboot.\n\n"
|
||||||
|
"If no option is specified, the default action is %s.\n",
|
||||||
|
PROGRAM_NAME, defact_str);
|
||||||
|
exit(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
static NORETURN void version(void)
|
||||||
|
{
|
||||||
|
fputs(
|
||||||
|
PROGRAM_NAME " (Pygos init) " PACKAGE_VERSION "\n"
|
||||||
|
"Copyright (C) 2018 David Oberhollenzer\n"
|
||||||
|
"License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.\n"
|
||||||
|
"This is free software: you are free to change and redistribute it.\n"
|
||||||
|
"There is NO WARRANTY, to the extent permitted by law.\n",
|
||||||
|
stdout);
|
||||||
|
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int c, fd, flags = 0;
|
||||||
|
ti_msg_t msg;
|
||||||
|
ssize_t ret;
|
||||||
|
|
||||||
|
if (!strcmp(PROGRAM_NAME, "reboot")) {
|
||||||
|
defact_str = "reboot";
|
||||||
|
defact = TI_REBOOT;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
c = getopt_long(argc, argv, shortopt, options, NULL);
|
||||||
|
if (c == -1)
|
||||||
|
break;
|
||||||
|
|
||||||
|
switch (c) {
|
||||||
|
case 'f':
|
||||||
|
flags |= FL_FORCE;
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
flags |= FL_NOSYNC;
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
defact = TI_SHUTDOWN;
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
defact = TI_REBOOT;
|
||||||
|
break;
|
||||||
|
case 'V':
|
||||||
|
version();
|
||||||
|
case 'h':
|
||||||
|
usage(EXIT_SUCCESS);
|
||||||
|
default:
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & FL_FORCE) {
|
||||||
|
if (!(flags & FL_NOSYNC))
|
||||||
|
sync();
|
||||||
|
|
||||||
|
switch (defact) {
|
||||||
|
case TI_REBOOT:
|
||||||
|
reboot(RB_AUTOBOOT);
|
||||||
|
break;
|
||||||
|
case TI_SHUTDOWN:
|
||||||
|
reboot(RB_POWER_OFF);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
perror("reboot system call");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
fd = opensock();
|
||||||
|
if (fd < 0)
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
|
||||||
|
msg.type = defact;
|
||||||
|
retry:
|
||||||
|
ret = write(fd, &msg, sizeof(msg));
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
if (errno == EINTR)
|
||||||
|
goto retry;
|
||||||
|
perror("write on init socket");
|
||||||
|
close(fd);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
26
configure.ac
Normal file
26
configure.ac
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
AC_PREREQ([2.60])
|
||||||
|
|
||||||
|
AC_INIT([init], 0.1, [david.oberhollenzer@tele2.at], init)
|
||||||
|
AC_CONFIG_MACRO_DIR([m4])
|
||||||
|
AM_INIT_AUTOMAKE([foreign subdir-objects dist-bzip2])
|
||||||
|
AM_SILENT_RULES([yes])
|
||||||
|
AC_PROG_CC
|
||||||
|
AC_PROG_INSTALL
|
||||||
|
AC_PROG_RANLIB
|
||||||
|
|
||||||
|
AC_CONFIG_HEADERS([lib/include/config.h])
|
||||||
|
AC_DEFINE_DIR(SVCDIR, sysconfdir/init.d, [Startup service directory])
|
||||||
|
AC_DEFINE_DIR(TEMPLATEDIR, datadir/init, [Service template directory])
|
||||||
|
AC_DEFINE_DIR(SCRIPTDIR, libexecdir/init, [Helper script directory])
|
||||||
|
AC_DEFINE_DIR(SOCKDIR, localstatedir/run, [Directory for initd socket])
|
||||||
|
|
||||||
|
AC_DEFINE_DIR(BINPATH, bindir, [Fully evaluated bin directory])
|
||||||
|
AC_DEFINE_DIR(SBINPATH, sbindir, [Fully evaluated sbin directory])
|
||||||
|
|
||||||
|
AC_CONFIG_FILES([services/agetty])
|
||||||
|
AC_CONFIG_FILES([services/hostname])
|
||||||
|
AC_CONFIG_FILES([services/loopback])
|
||||||
|
AC_CONFIG_FILES([services/sysctl])
|
||||||
|
AC_CONFIG_FILES([services/hwclock])
|
||||||
|
|
||||||
|
AC_OUTPUT([Makefile])
|
8
initd/Makemodule.am
Normal file
8
initd/Makemodule.am
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
init_SOURCES = initd/main.c initd/runlst.c initd/init.h initd/setup_tty.c
|
||||||
|
init_SOURCES += initd/status.c initd/mksock.c initd/shutdown.c initd/svclist.c
|
||||||
|
init_CPPFLAGS = $(AM_CPPFLAGS)
|
||||||
|
init_CFLAGS = $(AM_CFLAGS)
|
||||||
|
init_LDFLAGS = $(AM_LDFLAGS)
|
||||||
|
init_LDADD = libinit.a
|
||||||
|
|
||||||
|
sbin_PROGRAMS += init
|
36
initd/init.h
Normal file
36
initd/init.h
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
#ifndef INIT_H
|
||||||
|
#define INIT_H
|
||||||
|
|
||||||
|
#include <linux/reboot.h>
|
||||||
|
#include <sys/reboot.h>
|
||||||
|
|
||||||
|
#include "service.h"
|
||||||
|
#include "telinit.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
enum {
|
||||||
|
STATUS_OK = 0,
|
||||||
|
STATUS_FAIL,
|
||||||
|
STATUS_WAIT,
|
||||||
|
};
|
||||||
|
|
||||||
|
int runlst_wait(char **exec, size_t num, const char *ctty);
|
||||||
|
|
||||||
|
pid_t runlst(char **exec, size_t num, const char *ctty);
|
||||||
|
|
||||||
|
int setup_tty(void);
|
||||||
|
|
||||||
|
void print_status(const char *msg, int type, bool update);
|
||||||
|
|
||||||
|
int mksock(void);
|
||||||
|
|
||||||
|
NORETURN void do_shutdown(int type);
|
||||||
|
|
||||||
|
bool svclist_have_singleshot(void);
|
||||||
|
|
||||||
|
void svclist_add(service_t *svc);
|
||||||
|
|
||||||
|
service_t *svclist_remove(pid_t pid);
|
||||||
|
|
||||||
|
#endif /* INIT_H */
|
||||||
|
|
238
initd/main.c
Normal file
238
initd/main.c
Normal file
|
@ -0,0 +1,238 @@
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <poll.h>
|
||||||
|
|
||||||
|
#include <sys/signalfd.h>
|
||||||
|
|
||||||
|
#include "init.h"
|
||||||
|
|
||||||
|
static service_list_t cfg;
|
||||||
|
|
||||||
|
static int target = TGT_BOOT; /* runlevel we are targetting */
|
||||||
|
static int runlevel = -1; /* runlevel we are currently on */
|
||||||
|
|
||||||
|
static void handle_exited(service_t *svc)
|
||||||
|
{
|
||||||
|
switch (svc->type) {
|
||||||
|
case SVC_RESPAWN:
|
||||||
|
if (target == TGT_REBOOT || target == TGT_SHUTDOWN) {
|
||||||
|
delsrv(svc);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
svc->pid = runlst(svc->exec, svc->num_exec, svc->ctty);
|
||||||
|
if (svc->pid == -1) {
|
||||||
|
print_status(svc->desc, STATUS_FAIL, false);
|
||||||
|
delsrv(svc);
|
||||||
|
}
|
||||||
|
|
||||||
|
svclist_add(svc);
|
||||||
|
break;
|
||||||
|
case SVC_ONCE:
|
||||||
|
print_status(svc->desc,
|
||||||
|
svc->status == EXIT_SUCCESS ?
|
||||||
|
STATUS_OK : STATUS_FAIL, false);
|
||||||
|
/* fall-through */
|
||||||
|
default:
|
||||||
|
delsrv(svc);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_signal(int sigfd)
|
||||||
|
{
|
||||||
|
struct signalfd_siginfo info;
|
||||||
|
service_t *svc;
|
||||||
|
int status;
|
||||||
|
pid_t pid;
|
||||||
|
|
||||||
|
if (read(sigfd, &info, sizeof(info)) != sizeof(info)) {
|
||||||
|
perror("read on signal fd");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (info.ssi_signo) {
|
||||||
|
case SIGCHLD:
|
||||||
|
for (;;) {
|
||||||
|
pid = waitpid(-1, &status, WNOHANG);
|
||||||
|
if (pid <= 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
status = WIFEXITED(status) ? WEXITSTATUS(status) :
|
||||||
|
EXIT_FAILURE;
|
||||||
|
|
||||||
|
svc = svclist_remove(pid);
|
||||||
|
|
||||||
|
if (svc != NULL)
|
||||||
|
handle_exited(svc);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SIGINT:
|
||||||
|
/* TODO: ctrl-alt-del */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void start_runlevel(int level)
|
||||||
|
{
|
||||||
|
service_t *svc;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
while (cfg.targets[level] != NULL) {
|
||||||
|
svc = cfg.targets[level];
|
||||||
|
cfg.targets[level] = svc->next;
|
||||||
|
|
||||||
|
if (!svc->num_exec) {
|
||||||
|
print_status(svc->desc, STATUS_OK, false);
|
||||||
|
delsrv(svc);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (svc->type == SVC_WAIT) {
|
||||||
|
print_status(svc->desc, STATUS_WAIT, false);
|
||||||
|
|
||||||
|
status = runlst_wait(svc->exec, svc->num_exec,
|
||||||
|
svc->ctty);
|
||||||
|
|
||||||
|
print_status(svc->desc,
|
||||||
|
status == EXIT_SUCCESS ?
|
||||||
|
STATUS_OK : STATUS_FAIL,
|
||||||
|
true);
|
||||||
|
delsrv(svc);
|
||||||
|
} else {
|
||||||
|
svc->pid = runlst(svc->exec, svc->num_exec, svc->ctty);
|
||||||
|
if (svc->pid == -1) {
|
||||||
|
print_status(svc->desc, STATUS_FAIL, false);
|
||||||
|
delsrv(svc);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
svclist_add(svc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int read_msg(int fd, ti_msg_t *msg)
|
||||||
|
{
|
||||||
|
ssize_t ret;
|
||||||
|
retry:
|
||||||
|
ret = read(fd, msg, sizeof(*msg));
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
if (errno == EINTR)
|
||||||
|
goto retry;
|
||||||
|
perror("read on telinit socket");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((size_t)ret < sizeof(*msg)) {
|
||||||
|
fputs("short read on telinit socket", stderr);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_tellinit(int ti_sock)
|
||||||
|
{
|
||||||
|
ti_msg_t msg;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
fd = accept(ti_sock, NULL, NULL);
|
||||||
|
if (fd == -1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (read_msg(fd, &msg)) {
|
||||||
|
close(fd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (msg.type) {
|
||||||
|
case TI_SHUTDOWN:
|
||||||
|
target = TGT_SHUTDOWN;
|
||||||
|
break;
|
||||||
|
case TI_REBOOT:
|
||||||
|
target = TGT_REBOOT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
int ti_sock, sfd, ret;
|
||||||
|
struct pollfd pfd[2];
|
||||||
|
sigset_t mask;
|
||||||
|
|
||||||
|
if (getpid() != 1) {
|
||||||
|
fputs("init does not have pid 1, terminating!\n", stderr);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reboot(LINUX_REBOOT_CMD_CAD_OFF))
|
||||||
|
perror("cannot disable CTRL+ALT+DEL");
|
||||||
|
|
||||||
|
if (srvscan(SVCDIR, &cfg)) {
|
||||||
|
fputs("Error reading service list from " SVCDIR "\n"
|
||||||
|
"Trying to continue anyway\n", stderr);
|
||||||
|
}
|
||||||
|
|
||||||
|
sigfillset(&mask);
|
||||||
|
if (sigprocmask(SIG_SETMASK, &mask, NULL) == -1) {
|
||||||
|
perror("sigprocmask");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
sfd = signalfd(-1, &mask, SFD_CLOEXEC);
|
||||||
|
if (sfd == -1) {
|
||||||
|
perror("signalfd");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
ti_sock = mksock();
|
||||||
|
if (ti_sock == -1)
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
|
||||||
|
if (setup_tty())
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
|
||||||
|
memset(pfd, 0, sizeof(pfd));
|
||||||
|
pfd[0].fd = sfd;
|
||||||
|
pfd[1].fd = ti_sock;
|
||||||
|
pfd[0].events = pfd[1].events = POLLIN;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
if (!svclist_have_singleshot()) {
|
||||||
|
if (target != runlevel) {
|
||||||
|
start_runlevel(target);
|
||||||
|
runlevel = target;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (runlevel == TGT_SHUTDOWN)
|
||||||
|
do_shutdown(RB_POWER_OFF);
|
||||||
|
|
||||||
|
if (runlevel == TGT_REBOOT)
|
||||||
|
do_shutdown(RB_AUTOBOOT);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = poll(pfd, sizeof(pfd) / sizeof(pfd[0]), -1);
|
||||||
|
|
||||||
|
if (ret > 0) {
|
||||||
|
if (pfd[0].revents & POLLIN)
|
||||||
|
handle_signal(sfd);
|
||||||
|
|
||||||
|
if (pfd[1].revents & POLLIN)
|
||||||
|
handle_tellinit(ti_sock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
65
initd/mksock.c
Normal file
65
initd/mksock.c
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "telinit.h"
|
||||||
|
#include "init.h"
|
||||||
|
|
||||||
|
int mksock(void)
|
||||||
|
{
|
||||||
|
struct sockaddr_un un;
|
||||||
|
int fd, flags;
|
||||||
|
|
||||||
|
fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
|
if (fd < 0) {
|
||||||
|
perror("socket");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
flags = fcntl(fd, F_GETFD);
|
||||||
|
if (flags == -1) {
|
||||||
|
perror("socket F_GETFD");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fcntl(fd, F_SETFD, flags | O_CLOEXEC)) {
|
||||||
|
perror("socket F_SETFD");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&un, 0, sizeof(un));
|
||||||
|
un.sun_family = AF_UNIX;
|
||||||
|
|
||||||
|
strcpy(un.sun_path, INITSOCK);
|
||||||
|
|
||||||
|
if (bind(fd, (struct sockaddr *)&un, sizeof(un))) {
|
||||||
|
perror("bind: " INITSOCK);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chown(INITSOCK, 0, 0)) {
|
||||||
|
perror("chown: " INITSOCK);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chmod(INITSOCK, 0770)) {
|
||||||
|
perror("chmod: " INITSOCK);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (listen(fd, 10)) {
|
||||||
|
perror("listen");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fd;
|
||||||
|
fail:
|
||||||
|
close(fd);
|
||||||
|
unlink(INITSOCK);
|
||||||
|
return -1;
|
||||||
|
}
|
128
initd/runlst.c
Normal file
128
initd/runlst.c
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#include "init.h"
|
||||||
|
|
||||||
|
extern char **environ;
|
||||||
|
|
||||||
|
static NORETURN void split_and_exec(char *cmd)
|
||||||
|
{
|
||||||
|
char *argv[128];
|
||||||
|
size_t i = 0;
|
||||||
|
|
||||||
|
while (*cmd != '\0') {
|
||||||
|
argv[i++] = cmd; /* FIXME: buffer overflow!! */
|
||||||
|
|
||||||
|
while (*cmd != '\0' && !isspace(*cmd))
|
||||||
|
++cmd;
|
||||||
|
|
||||||
|
if (isspace(*cmd)) {
|
||||||
|
*(cmd++) = '\0';
|
||||||
|
|
||||||
|
while (isspace(*cmd))
|
||||||
|
++cmd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
argv[i] = NULL;
|
||||||
|
|
||||||
|
execve(argv[0], argv, environ);
|
||||||
|
perror(argv[0]);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int child_setup(const char *ctty)
|
||||||
|
{
|
||||||
|
sigset_t mask;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
sigemptyset(&mask);
|
||||||
|
sigprocmask(SIG_SETMASK, &mask, NULL);
|
||||||
|
|
||||||
|
if (ctty != NULL) {
|
||||||
|
fd = open(ctty, O_RDWR);
|
||||||
|
if (fd < 0) {
|
||||||
|
perror(ctty);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
close(STDIN_FILENO);
|
||||||
|
close(STDOUT_FILENO);
|
||||||
|
close(STDERR_FILENO);
|
||||||
|
|
||||||
|
setsid();
|
||||||
|
|
||||||
|
dup2(fd, STDIN_FILENO);
|
||||||
|
dup2(fd, STDOUT_FILENO);
|
||||||
|
dup2(fd, STDERR_FILENO);
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int runlst_wait(char **exec, size_t num, const char *ctty)
|
||||||
|
{
|
||||||
|
pid_t ret, pid;
|
||||||
|
int status;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < num; ++i) {
|
||||||
|
pid = fork();
|
||||||
|
|
||||||
|
if (pid == 0) {
|
||||||
|
if (child_setup(ctty))
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
split_and_exec(exec[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pid == -1) {
|
||||||
|
perror("fork");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
ret = waitpid(pid, &status, 0);
|
||||||
|
} while (ret != pid);
|
||||||
|
|
||||||
|
if (!WIFEXITED(status))
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
|
||||||
|
if (WEXITSTATUS(status) != EXIT_SUCCESS)
|
||||||
|
return WEXITSTATUS(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
pid_t runlst(char **exec, size_t num, const char *ctty)
|
||||||
|
{
|
||||||
|
int status;
|
||||||
|
pid_t pid;
|
||||||
|
|
||||||
|
pid = fork();
|
||||||
|
|
||||||
|
if (pid == 0) {
|
||||||
|
if (child_setup(ctty))
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
|
||||||
|
if (num > 1) {
|
||||||
|
status = runlst_wait(exec, num, NULL);
|
||||||
|
exit(status);
|
||||||
|
} else {
|
||||||
|
split_and_exec(exec[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pid == -1)
|
||||||
|
perror("fork");
|
||||||
|
|
||||||
|
return pid;
|
||||||
|
}
|
23
initd/setup_tty.c
Normal file
23
initd/setup_tty.c
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "init.h"
|
||||||
|
|
||||||
|
int setup_tty(void)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
fd = open("/dev/console", O_WRONLY | O_NOCTTY);
|
||||||
|
if (fd < 0) {
|
||||||
|
perror("/dev/console");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
close(STDIN_FILENO);
|
||||||
|
dup2(fd, STDOUT_FILENO);
|
||||||
|
dup2(fd, STDERR_FILENO);
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
return 0;
|
||||||
|
}
|
37
initd/shutdown.c
Normal file
37
initd/shutdown.c
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include "init.h"
|
||||||
|
|
||||||
|
void do_shutdown(int type)
|
||||||
|
{
|
||||||
|
struct timespec req, rem;
|
||||||
|
|
||||||
|
print_status("sending SIGTERM to all processes", STATUS_WAIT, false);
|
||||||
|
kill(-1, SIGTERM);
|
||||||
|
|
||||||
|
memset(&req, 0, sizeof(req));
|
||||||
|
memset(&rem, 0, sizeof(rem));
|
||||||
|
req.tv_sec = 5; /* TODO: make configurable? */
|
||||||
|
|
||||||
|
while (nanosleep(&req, &rem) != 0 && errno == EINTR)
|
||||||
|
req = rem;
|
||||||
|
|
||||||
|
print_status("sending SIGTERM to all processes", STATUS_OK, true);
|
||||||
|
kill(-1, SIGKILL);
|
||||||
|
print_status("sending SIGKILL to remaining processes",
|
||||||
|
STATUS_OK, false);
|
||||||
|
|
||||||
|
print_status("sync", STATUS_WAIT, false);
|
||||||
|
sync();
|
||||||
|
print_status("sync", STATUS_OK, true);
|
||||||
|
|
||||||
|
reboot(type);
|
||||||
|
perror("reboot system call");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
27
initd/status.c
Normal file
27
initd/status.c
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "init.h"
|
||||||
|
|
||||||
|
void print_status(const char *msg, int type, bool update)
|
||||||
|
{
|
||||||
|
const char *str;
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case STATUS_FAIL:
|
||||||
|
str = "\033[22;31mFAIL\033[0m";
|
||||||
|
break;
|
||||||
|
case STATUS_WAIT:
|
||||||
|
str = "\033[22;33m .. \033[0m";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
str = "\033[22;32m OK \033[0m";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (update)
|
||||||
|
fputc('\r', stdout);
|
||||||
|
printf("[%s] %s", str, msg);
|
||||||
|
if (type != STATUS_WAIT)
|
||||||
|
fputc('\n', stdout);
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
43
initd/svclist.c
Normal file
43
initd/svclist.c
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
#include "init.h"
|
||||||
|
|
||||||
|
static service_t *running = NULL; /* currently supervised services */
|
||||||
|
static int singleshot = 0; /* active singleshot services */
|
||||||
|
|
||||||
|
bool svclist_have_singleshot(void)
|
||||||
|
{
|
||||||
|
return singleshot > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void svclist_add(service_t *svc)
|
||||||
|
{
|
||||||
|
svc->next = running;
|
||||||
|
running = svc;
|
||||||
|
|
||||||
|
if (svc->type == SVC_ONCE)
|
||||||
|
singleshot += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
service_t *svclist_remove(pid_t pid)
|
||||||
|
{
|
||||||
|
service_t *prev = NULL, *svc = running;
|
||||||
|
|
||||||
|
while (svc != NULL) {
|
||||||
|
if (svc->pid == pid) {
|
||||||
|
if (prev != NULL) {
|
||||||
|
prev->next = svc->next;
|
||||||
|
} else {
|
||||||
|
running = svc->next;
|
||||||
|
}
|
||||||
|
svc->next = NULL;
|
||||||
|
|
||||||
|
if (svc->type == SVC_ONCE)
|
||||||
|
singleshot -= 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
prev = svc;
|
||||||
|
svc = svc->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return svc;
|
||||||
|
}
|
13
lib/Makemodule.am
Normal file
13
lib/Makemodule.am
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
HEADRS = lib/include/util.h lib/include/service.h lib/include/telinit.h
|
||||||
|
|
||||||
|
libinit_a_SOURCES = lib/src/delsrv.c lib/src/rdline.c
|
||||||
|
libinit_a_SOURCES += lib/src/splitkv.c lib/src/enum_by_name.c
|
||||||
|
libinit_a_SOURCES += lib/src/strexpand.c lib/src/rdsrv.c lib/src/srvscan.c
|
||||||
|
libinit_a_SOURCES += lib/src/del_srv_list.c lib/src/srv_tsort.c
|
||||||
|
libinit_a_SOURCES += lib/src/opensock.c $(HEADRS)
|
||||||
|
libinit_a_CPPFLAGS = $(AM_CPPFLAGS)
|
||||||
|
libinit_a_CFLAGS = $(AM_CFLAGS)
|
||||||
|
|
||||||
|
EXTRA_DIST += $(HEADRS)
|
||||||
|
|
||||||
|
noinst_LIBRARIES += libinit.a
|
62
lib/include/service.h
Normal file
62
lib/include/service.h
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
#ifndef SERVICE_H
|
||||||
|
#define SERVICE_H
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
enum {
|
||||||
|
SVC_ONCE = 0,
|
||||||
|
SVC_WAIT,
|
||||||
|
SVC_RESPAWN,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
TGT_BOOT = 0,
|
||||||
|
TGT_SHUTDOWN,
|
||||||
|
TGT_REBOOT,
|
||||||
|
TGT_CAD,
|
||||||
|
|
||||||
|
TGT_MAX
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct service_t {
|
||||||
|
int type; /* SVC_* service type */
|
||||||
|
int target; /* TGT_* service target */
|
||||||
|
char *name; /* canonical service name */
|
||||||
|
char *desc; /* description string */
|
||||||
|
char **exec; /* command lines to execute */
|
||||||
|
size_t num_exec; /* number of command lines */
|
||||||
|
char *ctty; /* controlling tty or log file */
|
||||||
|
|
||||||
|
char **before;
|
||||||
|
size_t num_before;
|
||||||
|
char **after;
|
||||||
|
size_t num_after;
|
||||||
|
|
||||||
|
pid_t pid;
|
||||||
|
int status; /* process exit status */
|
||||||
|
|
||||||
|
struct service_t *next;
|
||||||
|
} service_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
service_t *targets[TGT_MAX];
|
||||||
|
} service_list_t;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Read a service from a file.
|
||||||
|
*/
|
||||||
|
service_t *rdsrv(int dirfd, const char *filename);
|
||||||
|
|
||||||
|
void delsrv(service_t *srv);
|
||||||
|
|
||||||
|
int srvscan(const char *directory, service_list_t *list);
|
||||||
|
|
||||||
|
void del_srv_list(service_list_t *list);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Sort a list of services by dependencies.
|
||||||
|
*/
|
||||||
|
service_t *srv_tsort(service_t *list);
|
||||||
|
|
||||||
|
#endif /* SERVICE_H */
|
||||||
|
|
20
lib/include/telinit.h
Normal file
20
lib/include/telinit.h
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#ifndef TELINIT_H
|
||||||
|
#define TELINIT_H
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#define INITSOCK SOCKDIR "/" "initd.socket"
|
||||||
|
|
||||||
|
enum {
|
||||||
|
TI_SHUTDOWN = 1,
|
||||||
|
TI_REBOOT = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int type;
|
||||||
|
} ti_msg_t;
|
||||||
|
|
||||||
|
int opensock(void);
|
||||||
|
|
||||||
|
#endif /* TELINIT_H */
|
||||||
|
|
58
lib/include/util.h
Normal file
58
lib/include/util.h
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
#ifndef UTIL_H
|
||||||
|
#define UTIL_H
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#ifdef __GNUC__
|
||||||
|
#define NORETURN __attribute__((noreturn))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const char *name;
|
||||||
|
int value;
|
||||||
|
} enum_map_t;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Read from fd until end-of-file or a line feed is encountered.
|
||||||
|
|
||||||
|
Returns NULL with errno set on failure. Returns NULL with errno
|
||||||
|
cleared if end-of-file is reached.
|
||||||
|
|
||||||
|
The line must be deallocated with free().
|
||||||
|
*/
|
||||||
|
char *rdline(int fd);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Split a line of the shape "key = value" into key and value part.
|
||||||
|
|
||||||
|
The key can contain alphanumeric characters and can be padded with
|
||||||
|
spaces or tabs.
|
||||||
|
|
||||||
|
The value can be either a sequence of alphanumeric characters, period
|
||||||
|
or underscore OR a string in quotation marks. For strings, the
|
||||||
|
quotation marks are removed and escape sequences are processed.
|
||||||
|
|
||||||
|
The value may also be padded with spaces or tabs but the line may not
|
||||||
|
contain anything else after the value, except for spaces, tabs or
|
||||||
|
the '#' symbol which is interpreted as start of a comment.
|
||||||
|
*/
|
||||||
|
int splitkv(char *line, char **key, char **value);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Search through an array of enum_map_t entries to resolve a string to
|
||||||
|
a numeric value. The end of the map is indicated by a sentinel entry
|
||||||
|
with the name set to NULL.
|
||||||
|
*/
|
||||||
|
const enum_map_t *enum_by_name(const enum_map_t *map, const char *name);
|
||||||
|
|
||||||
|
char *strexpand(const char *inp, size_t argc, const char *const *argv);
|
||||||
|
|
||||||
|
#endif /* UTIL_H */
|
||||||
|
|
18
lib/src/del_srv_list.c
Normal file
18
lib/src/del_srv_list.c
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "service.h"
|
||||||
|
|
||||||
|
void del_srv_list(service_list_t *list)
|
||||||
|
{
|
||||||
|
service_t *srv;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < TGT_MAX; ++i) {
|
||||||
|
while (list->targets[i] != NULL) {
|
||||||
|
srv = list->targets[i];
|
||||||
|
list->targets[i] = srv->next;
|
||||||
|
|
||||||
|
delsrv(srv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
25
lib/src/delsrv.c
Normal file
25
lib/src/delsrv.c
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "service.h"
|
||||||
|
|
||||||
|
void delsrv(service_t *srv)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < srv->num_exec; ++i)
|
||||||
|
free(srv->exec[i]);
|
||||||
|
|
||||||
|
for (i = 0; i < srv->num_before; ++i)
|
||||||
|
free(srv->before[i]);
|
||||||
|
|
||||||
|
for (i = 0; i < srv->num_after; ++i)
|
||||||
|
free(srv->after[i]);
|
||||||
|
|
||||||
|
free(srv->before);
|
||||||
|
free(srv->after);
|
||||||
|
free(srv->name);
|
||||||
|
free(srv->desc);
|
||||||
|
free(srv->exec);
|
||||||
|
free(srv->ctty);
|
||||||
|
free(srv);
|
||||||
|
}
|
15
lib/src/enum_by_name.c
Normal file
15
lib/src/enum_by_name.c
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
const enum_map_t *enum_by_name(const enum_map_t *map, const char *name)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; map[i].name != NULL; ++i) {
|
||||||
|
if (!strcmp(map[i].name, name))
|
||||||
|
return map + i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
33
lib/src/opensock.c
Normal file
33
lib/src/opensock.c
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "telinit.h"
|
||||||
|
|
||||||
|
int opensock(void)
|
||||||
|
{
|
||||||
|
struct sockaddr_un un;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
|
if (fd < 0) {
|
||||||
|
perror("socket");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&un, 0, sizeof(un));
|
||||||
|
un.sun_family = AF_UNIX;
|
||||||
|
|
||||||
|
strcpy(un.sun_path, INITSOCK);
|
||||||
|
|
||||||
|
if (connect(fd, (struct sockaddr *)&un, sizeof(un))) {
|
||||||
|
perror("connect: " INITSOCK);
|
||||||
|
close(fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fd;
|
||||||
|
}
|
54
lib/src/rdline.c
Normal file
54
lib/src/rdline.c
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
char *rdline(int fd)
|
||||||
|
{
|
||||||
|
size_t i = 0, bufsiz = 0, newsz;
|
||||||
|
char c, *new, *buffer = NULL;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
switch (read(fd, &c, 1)) {
|
||||||
|
case 0:
|
||||||
|
if (i == 0) {
|
||||||
|
errno = 0;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
c = '\0';
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
if (c == '\n')
|
||||||
|
c = '\0';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (errno == EINTR)
|
||||||
|
continue;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == bufsiz) {
|
||||||
|
newsz = bufsiz ? bufsiz * 2 : 16;
|
||||||
|
new = realloc(buffer, newsz);
|
||||||
|
|
||||||
|
if (new == NULL)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
buffer = new;
|
||||||
|
bufsiz = newsz;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer[i++] = c;
|
||||||
|
if (c == '\0')
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return buffer;
|
||||||
|
fail:
|
||||||
|
ret = errno;
|
||||||
|
free(buffer);
|
||||||
|
errno = ret;
|
||||||
|
return NULL;
|
||||||
|
}
|
249
lib/src/rdsrv.c
Normal file
249
lib/src/rdsrv.c
Normal file
|
@ -0,0 +1,249 @@
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#include "service.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
static const enum_map_t type_map[] = {
|
||||||
|
{ "once", SVC_ONCE },
|
||||||
|
{ "wait", SVC_WAIT },
|
||||||
|
{ "respawn", SVC_RESPAWN },
|
||||||
|
{ NULL, 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const enum_map_t target_map[] = {
|
||||||
|
{ "boot", TGT_BOOT },
|
||||||
|
{ "shutdown", TGT_SHUTDOWN },
|
||||||
|
{ "reboot", TGT_REBOOT },
|
||||||
|
{ "ctrlaltdel", TGT_CAD },
|
||||||
|
{ NULL, 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static int srv_name(service_t *srv, char *arg,
|
||||||
|
const char *filename, size_t lineno)
|
||||||
|
{
|
||||||
|
(void)filename; (void)lineno;
|
||||||
|
srv->name = arg;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int srv_desc(service_t *srv, char *arg,
|
||||||
|
const char *filename, size_t lineno)
|
||||||
|
{
|
||||||
|
(void)filename; (void)lineno;
|
||||||
|
srv->desc = arg;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int srv_tty(service_t *srv, char *arg,
|
||||||
|
const char *filename, size_t lineno)
|
||||||
|
{
|
||||||
|
(void)filename; (void)lineno;
|
||||||
|
srv->ctty = arg;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int srv_exec(service_t *srv, char *arg,
|
||||||
|
const char *filename, size_t lineno)
|
||||||
|
{
|
||||||
|
char **new = realloc(srv->exec, sizeof(char*) * (srv->num_exec + 1));
|
||||||
|
|
||||||
|
if (new == NULL) {
|
||||||
|
fprintf(stderr, "%s: %zu: out of memory\n", filename, lineno);
|
||||||
|
free(arg);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
srv->exec = new;
|
||||||
|
srv->exec[srv->num_exec++] = arg;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int srv_before(service_t *srv, char *arg,
|
||||||
|
const char *filename, size_t lineno)
|
||||||
|
{
|
||||||
|
char **new = realloc(srv->before,
|
||||||
|
sizeof(char*) * (srv->num_before + 1));
|
||||||
|
|
||||||
|
if (new == NULL) {
|
||||||
|
fprintf(stderr, "%s: %zu: out of memory\n", filename, lineno);
|
||||||
|
free(arg);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
srv->before = new;
|
||||||
|
srv->before[srv->num_before++] = arg;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int srv_after(service_t *srv, char *arg,
|
||||||
|
const char *filename, size_t lineno)
|
||||||
|
{
|
||||||
|
char **new = realloc(srv->after, sizeof(char*) * (srv->num_after + 1));
|
||||||
|
|
||||||
|
if (new == NULL) {
|
||||||
|
fprintf(stderr, "%s: %zu: out of memory\n", filename, lineno);
|
||||||
|
free(arg);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
srv->after = new;
|
||||||
|
srv->after[srv->num_after++] = arg;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int srv_type(service_t *srv, char *arg,
|
||||||
|
const char *filename, size_t lineno)
|
||||||
|
{
|
||||||
|
const enum_map_t *ent = enum_by_name(type_map, arg);
|
||||||
|
|
||||||
|
if (ent == NULL) {
|
||||||
|
fprintf(stderr, "%s: %zu: unknown service type '%s'\n",
|
||||||
|
filename, lineno, arg);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
srv->type = ent->value;
|
||||||
|
free(arg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int srv_target(service_t *srv, char *arg,
|
||||||
|
const char *filename, size_t lineno)
|
||||||
|
{
|
||||||
|
const enum_map_t *ent = enum_by_name(target_map, arg);
|
||||||
|
|
||||||
|
if (ent == NULL) {
|
||||||
|
fprintf(stderr, "%s: %zu: unknown service target '%s'\n",
|
||||||
|
filename, lineno, arg);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
srv->target = ent->value;
|
||||||
|
free(arg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static const struct {
|
||||||
|
const char *key;
|
||||||
|
|
||||||
|
int (*handle)(service_t *srv, char *arg,
|
||||||
|
const char *filename, size_t lineno);
|
||||||
|
} srv_params[] = {
|
||||||
|
{ "name", srv_name },
|
||||||
|
{ "description", srv_desc },
|
||||||
|
{ "exec", srv_exec },
|
||||||
|
{ "type", srv_type },
|
||||||
|
{ "target", srv_target },
|
||||||
|
{ "tty", srv_tty },
|
||||||
|
{ "before", srv_before },
|
||||||
|
{ "after", srv_after },
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
service_t *rdsrv(int dirfd, const char *filename)
|
||||||
|
{
|
||||||
|
const char *arg, *args[1];
|
||||||
|
char *line, *key, *value;
|
||||||
|
size_t i, argc, lineno;
|
||||||
|
service_t *srv;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
fd = openat(dirfd, filename, O_RDONLY);
|
||||||
|
if (fd < 0) {
|
||||||
|
perror(filename);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
arg = strchr(filename, '@');
|
||||||
|
if (arg != NULL) {
|
||||||
|
args[0] = arg + 1;
|
||||||
|
argc = 1;
|
||||||
|
} else {
|
||||||
|
argc = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
srv = calloc(1, sizeof(*srv));
|
||||||
|
if (srv == NULL) {
|
||||||
|
fputs("out of memory\n", stderr);
|
||||||
|
close(fd);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (lineno = 1; ; ++lineno) {
|
||||||
|
errno = 0;
|
||||||
|
line = rdline(fd);
|
||||||
|
|
||||||
|
if (line == NULL) {
|
||||||
|
if (errno != 0) {
|
||||||
|
fprintf(stderr, "read: %s: %zu: %s\n",
|
||||||
|
filename, lineno, strerror(errno));
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (splitkv(line, &key, &value)) {
|
||||||
|
if (key == NULL) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"%s: %zu: expected <key> = <value>\n",
|
||||||
|
filename, lineno);
|
||||||
|
} else if (value == NULL) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"%s: %zu: expected value after %s\n",
|
||||||
|
filename, lineno, key);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr,
|
||||||
|
"%s: %zu: unexpected arguments "
|
||||||
|
"after key-value pair\n",
|
||||||
|
filename, lineno);
|
||||||
|
}
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key == NULL) {
|
||||||
|
free(line);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(srv_params); ++i) {
|
||||||
|
if (!strcmp(srv_params[i].key, key))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i >= ARRAY_SIZE(srv_params)) {
|
||||||
|
fprintf(stderr, "%s: %zu: unknown parameter '%s'\n",
|
||||||
|
filename, lineno, key);
|
||||||
|
goto fail_line;
|
||||||
|
}
|
||||||
|
|
||||||
|
value = strexpand(value, argc, args);
|
||||||
|
if (value == NULL) {
|
||||||
|
fputs("out of memory", stderr);
|
||||||
|
goto fail_line;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (srv_params[i].handle(srv, value, filename, lineno)) {
|
||||||
|
free(value);
|
||||||
|
goto fail_line;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
return srv;
|
||||||
|
fail_line:
|
||||||
|
free(line);
|
||||||
|
fail:
|
||||||
|
close(fd);
|
||||||
|
delsrv(srv);
|
||||||
|
return NULL;
|
||||||
|
}
|
124
lib/src/splitkv.c
Normal file
124
lib/src/splitkv.c
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
static char *skpspc(char *ptr)
|
||||||
|
{
|
||||||
|
while (*ptr == ' ' || *ptr == '\t')
|
||||||
|
++ptr;
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int xdigit(int x)
|
||||||
|
{
|
||||||
|
if (isupper(x))
|
||||||
|
return x - 'A' + 0x0A;
|
||||||
|
if (islower(x))
|
||||||
|
return x - 'a' + 0x0A;
|
||||||
|
return x - '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *parse_str(char *src)
|
||||||
|
{
|
||||||
|
char *dst = src;
|
||||||
|
int c;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
c = *(src++);
|
||||||
|
|
||||||
|
switch (c) {
|
||||||
|
case '\\':
|
||||||
|
c = *(src++);
|
||||||
|
|
||||||
|
switch (c) {
|
||||||
|
case 'a': c = '\a'; break;
|
||||||
|
case 'b': c = '\b'; break;
|
||||||
|
case 'f': c = '\f'; break;
|
||||||
|
case 'n': c = '\n'; break;
|
||||||
|
case 't': c = '\t'; break;
|
||||||
|
case '\\': break;
|
||||||
|
case '"': break;
|
||||||
|
case 'x':
|
||||||
|
c = 0;
|
||||||
|
if (isxdigit(*src))
|
||||||
|
c = (c << 4) | xdigit(*(src++));
|
||||||
|
if (isxdigit(*src))
|
||||||
|
c = (c << 4) | xdigit(*(src++));
|
||||||
|
break;
|
||||||
|
case '0':
|
||||||
|
c = 0;
|
||||||
|
if (isdigit(*src) && *src < '8')
|
||||||
|
c = (c << 3) | (*(src++) - '0');
|
||||||
|
if (isdigit(*src) && *src < '8')
|
||||||
|
c = (c << 3) | (*(src++) - '0');
|
||||||
|
if (isdigit(*src) && *src < '8')
|
||||||
|
c = (c << 3) | (*(src++) - '0');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '"':
|
||||||
|
*(dst++) = '\0';
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
*(dst++) = c;
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
return src;
|
||||||
|
}
|
||||||
|
|
||||||
|
int splitkv(char *line, char **key, char **value)
|
||||||
|
{
|
||||||
|
*key = NULL;
|
||||||
|
*value = NULL;
|
||||||
|
|
||||||
|
line = skpspc(line);
|
||||||
|
|
||||||
|
if (*line == '#' || *line == '\0')
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!isalpha(*line))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
*key = line;
|
||||||
|
|
||||||
|
while (isalnum(*line))
|
||||||
|
++line;
|
||||||
|
|
||||||
|
if (*line == ' ' || *line == '\t') {
|
||||||
|
*(line++) = '\0';
|
||||||
|
line = skpspc(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*line != '=')
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
*(line++) = '\0';
|
||||||
|
line = skpspc(line);
|
||||||
|
|
||||||
|
if (*line == '"') {
|
||||||
|
++line;
|
||||||
|
*value = line;
|
||||||
|
|
||||||
|
line = parse_str(line);
|
||||||
|
} else if (isalnum(*line)) {
|
||||||
|
*value = line;
|
||||||
|
|
||||||
|
while (isalnum(*line) || *line == '.' || *line == '_')
|
||||||
|
++line;
|
||||||
|
|
||||||
|
if (*line != '\0')
|
||||||
|
*(line++) = '\0';
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
line = skpspc(line);
|
||||||
|
|
||||||
|
if (*line != '\0' && *line != '#')
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
75
lib/src/srv_tsort.c
Normal file
75
lib/src/srv_tsort.c
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include "service.h"
|
||||||
|
|
||||||
|
static bool has_dependencies(service_t *list, service_t *svc)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
while (list != NULL) {
|
||||||
|
for (i = 0; i < svc->num_after; ++i) {
|
||||||
|
if (!strcmp(svc->after[i], list->name))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < list->num_before; ++i) {
|
||||||
|
if (!strcmp(list->before[i], svc->name))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
list = list->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
service_t *srv_tsort(service_t *list)
|
||||||
|
{
|
||||||
|
service_t *nl = NULL, *end = NULL;
|
||||||
|
service_t *svc, *prev;
|
||||||
|
|
||||||
|
while (list != NULL) {
|
||||||
|
/* remove first service without dependencies */
|
||||||
|
prev = NULL;
|
||||||
|
svc = list;
|
||||||
|
|
||||||
|
while (svc != NULL) {
|
||||||
|
if (has_dependencies(list, svc)) {
|
||||||
|
prev = svc;
|
||||||
|
svc = svc->next;
|
||||||
|
} else {
|
||||||
|
if (prev != NULL) {
|
||||||
|
prev->next = svc->next;
|
||||||
|
} else {
|
||||||
|
list = svc->next;
|
||||||
|
}
|
||||||
|
svc->next = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* cycle! */
|
||||||
|
if (svc == NULL) {
|
||||||
|
if (end == NULL) {
|
||||||
|
nl = list;
|
||||||
|
} else {
|
||||||
|
end->next = list;
|
||||||
|
}
|
||||||
|
errno = ELOOP;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* append to new list */
|
||||||
|
if (end == NULL) {
|
||||||
|
nl = end = svc;
|
||||||
|
} else {
|
||||||
|
end->next = svc;
|
||||||
|
end = svc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nl;
|
||||||
|
}
|
93
lib/src/srvscan.c
Normal file
93
lib/src/srvscan.c
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#include "service.h"
|
||||||
|
|
||||||
|
int srvscan(const char *directory, service_list_t *list)
|
||||||
|
{
|
||||||
|
int i, dfd, type, ret = 0;
|
||||||
|
struct dirent *ent;
|
||||||
|
const char *ptr;
|
||||||
|
service_t *srv;
|
||||||
|
struct stat sb;
|
||||||
|
DIR *dir;
|
||||||
|
|
||||||
|
for (i = 0; i < TGT_MAX; ++i)
|
||||||
|
list->targets[i] = NULL;
|
||||||
|
|
||||||
|
dir = opendir(directory);
|
||||||
|
if (dir == NULL) {
|
||||||
|
perror(directory);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
dfd = dirfd(dir);
|
||||||
|
if (dfd < 0) {
|
||||||
|
perror(directory);
|
||||||
|
closedir(dir);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
errno = 0;
|
||||||
|
ent = readdir(dir);
|
||||||
|
|
||||||
|
if (ent == NULL) {
|
||||||
|
if (errno != 0) {
|
||||||
|
perror(directory);
|
||||||
|
ret = -1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ptr = ent->d_name; isalnum(*ptr) || *ptr == '_'; ++ptr)
|
||||||
|
;
|
||||||
|
|
||||||
|
if (*ptr != '\0' && *ptr != '@')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (fstatat(dfd, ent->d_name, &sb, AT_SYMLINK_NOFOLLOW)) {
|
||||||
|
fprintf(stderr, "stat %s/%s: %s\n",
|
||||||
|
directory, ent->d_name, strerror(errno));
|
||||||
|
ret = -1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
type = (sb.st_mode & S_IFMT);
|
||||||
|
|
||||||
|
if (type != S_IFREG && type != S_IFLNK)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
srv = rdsrv(dfd, ent->d_name);
|
||||||
|
if (srv == NULL) {
|
||||||
|
ret = -1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
srv->next = list->targets[srv->target];
|
||||||
|
list->targets[srv->target] = srv;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < TGT_MAX; ++i) {
|
||||||
|
if (list->targets[i] == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
list->targets[i] = srv_tsort(list->targets[i]);
|
||||||
|
|
||||||
|
if (errno != 0) {
|
||||||
|
fprintf(stderr, "sorting services read from %s: %s\n",
|
||||||
|
directory, strerror(errno));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
closedir(dir);
|
||||||
|
return ret;
|
||||||
|
}
|
55
lib/src/strexpand.c
Normal file
55
lib/src/strexpand.c
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
char *strexpand(const char *inp, size_t argc, const char *const *argv)
|
||||||
|
{
|
||||||
|
char *out, *dst;
|
||||||
|
const char *ptr;
|
||||||
|
size_t i, len;
|
||||||
|
|
||||||
|
ptr = inp;
|
||||||
|
len = 0;
|
||||||
|
|
||||||
|
while (*ptr != '\0') {
|
||||||
|
if (ptr[0] == '%' && isdigit(ptr[1])) {
|
||||||
|
i = ptr[1] - '0';
|
||||||
|
if (i < argc)
|
||||||
|
len += strlen(argv[i]);
|
||||||
|
ptr += 2;
|
||||||
|
} else if (ptr[0] == '%' && ptr[1] == '%') {
|
||||||
|
ptr += 2;
|
||||||
|
len += 1;
|
||||||
|
} else {
|
||||||
|
++ptr;
|
||||||
|
++len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out = calloc(1, len + 1);
|
||||||
|
if (out == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
dst = out;
|
||||||
|
|
||||||
|
while (*inp != '\0') {
|
||||||
|
if (inp[0] == '%' && isdigit(inp[1])) {
|
||||||
|
i = inp[1] - '0';
|
||||||
|
if (i < argc) {
|
||||||
|
len = strlen(argv[i]);
|
||||||
|
memcpy(dst, argv[i], len);
|
||||||
|
dst += len;
|
||||||
|
}
|
||||||
|
inp += 2;
|
||||||
|
} else if (inp[0] == '%' && inp[1] == '%') {
|
||||||
|
*(dst++) = '%';
|
||||||
|
inp += 2;
|
||||||
|
} else {
|
||||||
|
*(dst++) = *(inp++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
35
m4/ac_define_dir.m4
Normal file
35
m4/ac_define_dir.m4
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
dnl @synopsis AC_DEFINE_DIR(VARNAME, DIR [, DESCRIPTION])
|
||||||
|
dnl
|
||||||
|
dnl This macro sets VARNAME to the expansion of the DIR variable,
|
||||||
|
dnl taking care of fixing up ${prefix} and such.
|
||||||
|
dnl
|
||||||
|
dnl VARNAME is then offered as both an output variable and a C
|
||||||
|
dnl preprocessor symbol.
|
||||||
|
dnl
|
||||||
|
dnl Example:
|
||||||
|
dnl
|
||||||
|
dnl AC_DEFINE_DIR([DATADIR], [datadir], [Where data are placed to.])
|
||||||
|
dnl
|
||||||
|
dnl @category Misc
|
||||||
|
dnl @author Stepan Kasal <kasal@ucw.cz>
|
||||||
|
dnl @author Andreas Schwab <schwab@suse.de>
|
||||||
|
dnl @author Guido U. Draheim <guidod@gmx.de>
|
||||||
|
dnl @author Alexandre Oliva
|
||||||
|
dnl @version 2006-10-13
|
||||||
|
dnl @license AllPermissive
|
||||||
|
|
||||||
|
AC_DEFUN([AC_DEFINE_DIR], [
|
||||||
|
prefix_NONE=
|
||||||
|
exec_prefix_NONE=
|
||||||
|
test "x$prefix" = xNONE && prefix_NONE=yes && prefix=$ac_default_prefix
|
||||||
|
test "x$exec_prefix" = xNONE && exec_prefix_NONE=yes && exec_prefix=$prefix
|
||||||
|
dnl In Autoconf 2.60, ${datadir} refers to ${datarootdir}, which in turn
|
||||||
|
dnl refers to ${prefix}. Thus we have to use `eval' twice.
|
||||||
|
eval ac_define_dir="\"[$]$2\""
|
||||||
|
eval ac_define_dir="\"$ac_define_dir\""
|
||||||
|
AC_SUBST($1, "$ac_define_dir")
|
||||||
|
AC_DEFINE_UNQUOTED($1, "$ac_define_dir", [$3])
|
||||||
|
test "$prefix_NONE" && prefix=NONE
|
||||||
|
test "$exec_prefix_NONE" && exec_prefix=NONE
|
||||||
|
])
|
||||||
|
|
12
servicecmd/Makemodule.am
Normal file
12
servicecmd/Makemodule.am
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
SRVHEADERS = servicecmd/servicecmd.h
|
||||||
|
|
||||||
|
service_SOURCES = servicecmd/servicecmd.c servicecmd/help.c servicecmd/list.c
|
||||||
|
service_SOURCES += servicecmd/enable.c servicecmd/disable.c $(SRVHEADERS)
|
||||||
|
service_CPPFLAGS = $(AM_CPPFLAGS)
|
||||||
|
service_CFLAGS = $(AM_CFLAGS)
|
||||||
|
service_LDFLAGS = $(AM_LDFLAGS)
|
||||||
|
service_LDADD = libinit.a
|
||||||
|
|
||||||
|
EXTRA_DIST += $(SRVHEADERS)
|
||||||
|
|
||||||
|
sbin_PROGRAMS += service
|
75
servicecmd/disable.c
Normal file
75
servicecmd/disable.c
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include "servicecmd.h"
|
||||||
|
|
||||||
|
static int cmd_disable(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int ret = EXIT_FAILURE;
|
||||||
|
char *linkname, *ptr;
|
||||||
|
struct stat sb;
|
||||||
|
|
||||||
|
if (argc < 2 || argc > 3) {
|
||||||
|
fputs("Wrong number of arguments for `disable'.\n"
|
||||||
|
"Try `service help disable' for more information.\n",
|
||||||
|
stderr);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ptr = argv[1]; isalnum(*ptr) || *ptr == '_'; ++ptr)
|
||||||
|
;
|
||||||
|
|
||||||
|
if (*ptr != '\0') {
|
||||||
|
fprintf(stderr, "Invalid service name '%s'\n", argv[1]);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argc == 3) {
|
||||||
|
ret = asprintf(&linkname, "%s/%s@%s",
|
||||||
|
SVCDIR, argv[1], argv[2]);
|
||||||
|
} else {
|
||||||
|
ret = asprintf(&linkname, "%s/%s", SVCDIR, argv[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
perror("asprintf");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lstat(linkname, &sb)) {
|
||||||
|
fprintf(stderr, "lstat %s: %s\n", linkname, strerror(errno));
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((sb.st_mode & S_IFMT) != S_IFLNK) {
|
||||||
|
fprintf(stderr, "error: '%s' is not a symlink!", linkname);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unlink(linkname)) {
|
||||||
|
fprintf(stderr, "removing %s: %s\n",
|
||||||
|
linkname, strerror(errno));
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = EXIT_SUCCESS;
|
||||||
|
out:
|
||||||
|
free(linkname);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static command_t disable = {
|
||||||
|
.cmd = "disable",
|
||||||
|
.usage = "<name> [argument]",
|
||||||
|
.s_desc = "disable a service",
|
||||||
|
.l_desc = "This disables a service by removing the coresponding "
|
||||||
|
"symlink in " SVCDIR ".",
|
||||||
|
.run_cmd = cmd_disable,
|
||||||
|
};
|
||||||
|
|
||||||
|
REGISTER_COMMAND(disable)
|
84
servicecmd/enable.c
Normal file
84
servicecmd/enable.c
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include "servicecmd.h"
|
||||||
|
|
||||||
|
static int cmd_enable(int argc, char **argv)
|
||||||
|
{
|
||||||
|
char *target, *linkname, *ptr;
|
||||||
|
int ret = EXIT_FAILURE;
|
||||||
|
struct stat sb;
|
||||||
|
|
||||||
|
if (argc < 2 || argc > 3) {
|
||||||
|
fputs("Wrong number of arguments for `enable'.\n"
|
||||||
|
"Try `service help enable' for more information.\n",
|
||||||
|
stderr);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ptr = argv[1]; isalnum(*ptr) || *ptr == '_'; ++ptr)
|
||||||
|
;
|
||||||
|
|
||||||
|
if (*ptr != '\0') {
|
||||||
|
fprintf(stderr, "Invalid service name '%s'\n", argv[1]);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (asprintf(&target, "%s/%s", TEMPLATEDIR, argv[1]) < 0) {
|
||||||
|
perror("asprintf");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stat(target, &sb)) {
|
||||||
|
fprintf(stderr, "%s: %s\n", target, strerror(errno));
|
||||||
|
goto out_tgt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((sb.st_mode & S_IFMT) != S_IFREG) {
|
||||||
|
fprintf(stderr, "%s: must be a regular file\n", target);
|
||||||
|
goto out_tgt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argc == 3) {
|
||||||
|
ret = asprintf(&linkname, "%s/%s@%s",
|
||||||
|
SVCDIR, argv[1], argv[2]);
|
||||||
|
} else {
|
||||||
|
ret = asprintf(&linkname, "%s/%s", SVCDIR, argv[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
perror("asprintf");
|
||||||
|
goto out_tgt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (symlink(target, linkname)) {
|
||||||
|
fprintf(stderr, "creating symlink '%s' -> '%s: %s\n",
|
||||||
|
linkname, target, strerror(errno));
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = EXIT_SUCCESS;
|
||||||
|
out:
|
||||||
|
free(linkname);
|
||||||
|
out_tgt:
|
||||||
|
free(target);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static command_t enable = {
|
||||||
|
.cmd = "enable",
|
||||||
|
.usage = "<name> [argument]",
|
||||||
|
.s_desc = "enable a service",
|
||||||
|
.l_desc = "This marks a service as enabled by creating a symlink in "
|
||||||
|
SVCDIR " pointing to the template file in " TEMPLATEDIR ". "
|
||||||
|
"An optional argument can be supplied to parameterize the "
|
||||||
|
"template.",
|
||||||
|
.run_cmd = cmd_enable,
|
||||||
|
};
|
||||||
|
|
||||||
|
REGISTER_COMMAND(enable)
|
112
servicecmd/help.c
Normal file
112
servicecmd/help.c
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
#include "servicecmd.h"
|
||||||
|
|
||||||
|
extern char *__progname;
|
||||||
|
|
||||||
|
static void pretty_print(const char *str, int padd, int len)
|
||||||
|
{
|
||||||
|
int i, brk;
|
||||||
|
next:
|
||||||
|
while (isspace(*str) && *str != '\n')
|
||||||
|
++str;
|
||||||
|
|
||||||
|
if (!(*str))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (*str == '\n') {
|
||||||
|
fputc('\n', stdout);
|
||||||
|
++str;
|
||||||
|
len = padd;
|
||||||
|
goto next;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0, brk = 0; str[i]; ++i) {
|
||||||
|
if (str[i] == '<' || str[i] == '[')
|
||||||
|
++brk;
|
||||||
|
if (str[i] == '>' || str[i] == ']')
|
||||||
|
--brk;
|
||||||
|
if (!brk && isspace(str[i]))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((len + i) < 80) {
|
||||||
|
fwrite(str, 1, i, stdout);
|
||||||
|
str += i;
|
||||||
|
len += i;
|
||||||
|
|
||||||
|
if ((len + 1) < 80) {
|
||||||
|
fputc(' ', stdout);
|
||||||
|
++len;
|
||||||
|
goto next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("\n%*s", padd, "");
|
||||||
|
len = padd;
|
||||||
|
goto next;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_cmd_usage(const char *cmd, const char *usage)
|
||||||
|
{
|
||||||
|
int padd;
|
||||||
|
|
||||||
|
padd = printf("Usage: %s %s ", __progname, cmd);
|
||||||
|
|
||||||
|
if ((strlen(usage) + padd) < 80) {
|
||||||
|
fputs(usage, stdout);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pretty_print(usage, padd, padd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmd_help(int argc, char **argv)
|
||||||
|
{
|
||||||
|
const char *help;
|
||||||
|
command_t *cmd;
|
||||||
|
|
||||||
|
if (argc < 2)
|
||||||
|
usage(EXIT_SUCCESS);
|
||||||
|
|
||||||
|
if (argc > 2) {
|
||||||
|
fprintf(stderr, "Too many arguments\n\n"
|
||||||
|
"Usage: %s help <command>", __progname);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (cmd = commands; cmd != NULL; cmd = cmd->next) {
|
||||||
|
if (strcmp(cmd->cmd, argv[1]) != 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
print_cmd_usage(cmd->cmd, cmd->usage);
|
||||||
|
fputs("\n\n", stdout);
|
||||||
|
|
||||||
|
help = cmd->l_desc ? cmd->l_desc : cmd->s_desc;
|
||||||
|
|
||||||
|
if (islower(*help)) {
|
||||||
|
fputc(toupper(*(help++)), stdout);
|
||||||
|
pretty_print(help, 0, 1);
|
||||||
|
} else {
|
||||||
|
pretty_print(help, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
fputc('\n', stdout);
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "Unknown command '%s'\n\n"
|
||||||
|
"Try `%s help' for a list of available commands\n",
|
||||||
|
argv[1], __progname);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static command_t help = {
|
||||||
|
.cmd = "help",
|
||||||
|
.usage = "<command>",
|
||||||
|
.s_desc = "print a help text for a command",
|
||||||
|
.l_desc = "Print a help text for a specified command. If no command "
|
||||||
|
"is specified, print a generic help text and a list of "
|
||||||
|
"available commands.",
|
||||||
|
.run_cmd = cmd_help,
|
||||||
|
};
|
||||||
|
|
||||||
|
REGISTER_COMMAND(help)
|
71
servicecmd/list.c
Normal file
71
servicecmd/list.c
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
#include "servicecmd.h"
|
||||||
|
#include "service.h"
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
static int cmd_list(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int i, ret = EXIT_SUCCESS;
|
||||||
|
service_list_t list;
|
||||||
|
service_t *svc;
|
||||||
|
|
||||||
|
(void)argc; (void)argv;
|
||||||
|
|
||||||
|
if (srvscan(SVCDIR, &list)) {
|
||||||
|
fprintf(stderr, "Error while reading services from %s\n",
|
||||||
|
SVCDIR);
|
||||||
|
ret = EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < TGT_MAX; ++i) {
|
||||||
|
if (list.targets[i] == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
fputs("******** target: ", stdout);
|
||||||
|
|
||||||
|
switch (i) {
|
||||||
|
case TGT_BOOT:
|
||||||
|
fputs("boot", stdout);
|
||||||
|
break;
|
||||||
|
case TGT_SHUTDOWN:
|
||||||
|
fputs("shutdown", stdout);
|
||||||
|
break;
|
||||||
|
case TGT_REBOOT:
|
||||||
|
fputs("reboot", stdout);
|
||||||
|
break;
|
||||||
|
case TGT_CAD:
|
||||||
|
fputs("ctrl-alt-delete", stdout);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
fputs(" ********\n", stdout);
|
||||||
|
|
||||||
|
for (svc = list.targets[i]; svc != NULL; svc = svc->next) {
|
||||||
|
fprintf(stdout, "Name: %s\n", svc->name);
|
||||||
|
fprintf(stdout, "Descrption: %s\n", svc->desc);
|
||||||
|
|
||||||
|
fputs("Type: ", stdout);
|
||||||
|
switch (svc->type) {
|
||||||
|
case SVC_ONCE: fputs("once\n", stdout); break;
|
||||||
|
case SVC_WAIT: fputs("wait\n", stdout); break;
|
||||||
|
case SVC_RESPAWN: fputs("respawn\n", stdout); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
fputc('\n', stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
fputc('\n', stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
del_srv_list(&list);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static command_t list = {
|
||||||
|
.cmd = "list",
|
||||||
|
.usage = "",
|
||||||
|
.s_desc = "print a list of currently enabled services",
|
||||||
|
.l_desc = "Print a list of currently enabled services.",
|
||||||
|
.run_cmd = cmd_list,
|
||||||
|
};
|
||||||
|
|
||||||
|
REGISTER_COMMAND(list)
|
56
servicecmd/servicecmd.c
Normal file
56
servicecmd/servicecmd.c
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "servicecmd.h"
|
||||||
|
#include "service.h"
|
||||||
|
#include "config.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
|
||||||
|
command_t *commands;
|
||||||
|
|
||||||
|
extern char *__progname;
|
||||||
|
|
||||||
|
|
||||||
|
void usage(int status)
|
||||||
|
{
|
||||||
|
FILE *stream = (status == EXIT_SUCCESS ? stdout : stderr);
|
||||||
|
int padd = 0, len;
|
||||||
|
command_t *cmd;
|
||||||
|
|
||||||
|
fprintf(stream, "usage: %s <command> [args...]\n\n"
|
||||||
|
"Available commands:\n\n", __progname);
|
||||||
|
|
||||||
|
for (cmd = commands; cmd != NULL; cmd = cmd->next) {
|
||||||
|
len = strlen(cmd->cmd);
|
||||||
|
|
||||||
|
padd = len > padd ? len : padd;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (cmd = commands; cmd != NULL; cmd = cmd->next) {
|
||||||
|
fprintf(stream, "%*s - %s\n",
|
||||||
|
(int)padd + 1, cmd->cmd, cmd->s_desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stream, "\nTry `%s help <command>' for more information "
|
||||||
|
"on a specific command\n", __progname);
|
||||||
|
|
||||||
|
exit(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
command_t *cmd;
|
||||||
|
|
||||||
|
if (argc < 2)
|
||||||
|
usage(EXIT_SUCCESS);
|
||||||
|
|
||||||
|
for (cmd = commands; cmd != NULL; cmd = cmd->next) {
|
||||||
|
if (!strcmp(cmd->cmd, argv[1])) {
|
||||||
|
return cmd->run_cmd(argc - 1, argv + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "Unknown command '%s'\n\n", argv[1]);
|
||||||
|
usage(EXIT_FAILURE);
|
||||||
|
}
|
36
servicecmd/servicecmd.h
Normal file
36
servicecmd/servicecmd.h
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
#ifndef SERVICECMD_H
|
||||||
|
#define SERVICECMD_H
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
typedef struct command_t {
|
||||||
|
struct command_t *next;
|
||||||
|
|
||||||
|
const char *cmd;
|
||||||
|
const char *usage;
|
||||||
|
const char *s_desc;
|
||||||
|
const char *l_desc;
|
||||||
|
|
||||||
|
int (*run_cmd)(int argc, char **argv);
|
||||||
|
} command_t;
|
||||||
|
|
||||||
|
extern command_t *commands;
|
||||||
|
|
||||||
|
void usage(int status) NORETURN;
|
||||||
|
|
||||||
|
#define REGISTER_COMMAND(cmd) \
|
||||||
|
static void __attribute__((constructor)) register_##cmd(void) \
|
||||||
|
{ \
|
||||||
|
command_t *c = (command_t *)&cmd; \
|
||||||
|
\
|
||||||
|
c->next = commands; \
|
||||||
|
commands = c; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* SERVICECMD_H */
|
||||||
|
|
5
services/Makemodule.am
Normal file
5
services/Makemodule.am
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
initdir = @TEMPLATEDIR@
|
||||||
|
init_DATA = services/agetty services/hostname services/loopback
|
||||||
|
init_DATA += services/sysctl services/hwclock services/sysinit
|
||||||
|
|
||||||
|
EXTRA_DIST += services/sysinit
|
7
services/agetty.in
Normal file
7
services/agetty.in
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
name = "agetty"
|
||||||
|
description = "agetty on %0"
|
||||||
|
exec = "@SBINPATH@/agetty %0 linux"
|
||||||
|
type = respawn
|
||||||
|
target = boot
|
||||||
|
after = sysinit
|
||||||
|
tty = "/dev/%0"
|
7
services/hostname.in
Normal file
7
services/hostname.in
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
name = "hostname"
|
||||||
|
description = "reload hostname"
|
||||||
|
exec = "@BINPATH@/hostname --file /etc/hostname"
|
||||||
|
type = wait
|
||||||
|
target = boot
|
||||||
|
before = sysinit
|
||||||
|
after = hwclock
|
6
services/hwclock.in
Normal file
6
services/hwclock.in
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
name = "hwclock"
|
||||||
|
description = "restore time from RTC"
|
||||||
|
exec = "@SBINPATH@/hwclock --hctosys --utc"
|
||||||
|
type = wait
|
||||||
|
target = boot
|
||||||
|
before = sysinit
|
10
services/loopback.in
Normal file
10
services/loopback.in
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
name = "loopback"
|
||||||
|
description = "configure network loopback device"
|
||||||
|
type = wait
|
||||||
|
target = boot
|
||||||
|
before = sysinit
|
||||||
|
after = hwclock
|
||||||
|
after = hostname
|
||||||
|
|
||||||
|
exec = "@SBINPATH@/ip addr add 127.0.0.1/8 dev lo brd +"
|
||||||
|
exec = "@SBINPATH@/ip link set lo up"
|
8
services/sysctl.in
Executable file
8
services/sysctl.in
Executable file
|
@ -0,0 +1,8 @@
|
||||||
|
name = "sysctl"
|
||||||
|
description = "configure kernel paramters"
|
||||||
|
exec = "@SBINPATH@/sysctl --system"
|
||||||
|
type = wait
|
||||||
|
target = boot
|
||||||
|
before = sysinit
|
||||||
|
after = hwclock
|
||||||
|
after = hostname
|
4
services/sysinit
Normal file
4
services/sysinit
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
name = "sysinit"
|
||||||
|
description = "basic system initialization"
|
||||||
|
type = once
|
||||||
|
target = boot
|
Loading…
Reference in a new issue