Initial commit

Signed-off-by: David Oberhollenzer <david.oberhollenzer@tele2.at>
This commit is contained in:
David Oberhollenzer 2018-02-25 14:33:19 +01:00
commit 9a88f7da45
44 changed files with 2248 additions and 0 deletions

26
.gitignore vendored Normal file
View 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
View 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
View File

@ -0,0 +1,3 @@
#!/bin/sh
autoreconf --force --install --symlink

13
cmd/Makemodule.am Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View File

@ -0,0 +1,4 @@
name = "sysinit"
description = "basic system initialization"
type = once
target = boot