mirror of
https://github.com/pygos/init.git
synced 2024-12-21 23:00:56 +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