sm

sysmgr implemented in C
git clone git://git.ckyln.com/sm
Log | Files | Refs | README | LICENSE

commit e0a8cb56f82349216c602ff656e461067a179526
parent d0fd3d05d45b84643313070715d94cfe248a520f
Author: Cem Keylan <cem@ckyln.com>
Date:   Fri,  9 Oct 2020 14:30:35 +0300

sysmgr: fully works now, time for cleanup

Diffstat:
M.gitignore | 8++++++--
MLICENSE | 15+++++++++++++++
MMakefile | 16++++++++++++++--
Mconfig.mk | 5+++--
Mlibutil/proc.c | 12+++++++-----
Mlibutil/rm.c | 2--
Mlibutil/service.c | 40++++++++++++++++++++++++++++++----------
Mrunsyssv.c | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Asvctl.c | 124+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msysmgr.c | 10++++------
Mutil.h | 4+++-
11 files changed, 263 insertions(+), 32 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -1,7 +1,12 @@ +# -*- mode: gitignore; -*- +# Emacs editor files +*~ +\#*\# +.\#* # C Objects/binaries *.a *.o config.h sysmgr runsyssv -svctl- \ No newline at end of file +svctl diff --git a/LICENSE b/LICENSE @@ -1,3 +1,18 @@ +Some of the utility functions here are taken from other places, here are their +licenses. See the files for more information. Most of those are from sbase, but +some of the ones from sbase are taken from other places as well. + +enprintf.c, mkdirp.c + Taken from https://core.suckless.org/sbase. MIT/X Consortium License +rm.c + Taken from https://stackoverflow.com/a/5467788. CC BY-SA 2.5 +strlcpy.c + Taken from sbase. Copyright (c) 1998 Todd C. Miller. ISC License +strtonum.c + Taken from sbase. Copyright (c) 2004 Ted Unangst and Todd Miller. ISC License + +-------------------------------------------------------------------------------- + GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 diff --git a/Makefile b/Makefile @@ -1,3 +1,6 @@ +PREFIX = /usr/local +BINDIR = ${PREFIX}/bin + include config.mk .SUFFIXES: @@ -20,7 +23,8 @@ LIBUTILSRC = \ BIN = \ sysmgr \ - runsyssv + runsyssv \ + svctl SRC = ${BIN:=.c} BINOBJ = ${SRC:.c=.o} @@ -54,4 +58,12 @@ config.h: clean: rm -f ${BIN} ${OBJ} ${LIBUTIL} -.PHONY: all clean +install: all + mkdir -p ${DESTDIR}${BINDIR} + cp ${BIN} ${DESTDIR}${BINDIR} + for bin in ${BIN}; do chmod 755 ${DESTDIR}${BINDIR}/$${bin}; done + +uninstall: + for bin in ${BIN}; do rm -f ${DESTDIR}${BINDIR}/$${bin}; done + +.PHONY: all clean install uninstall diff --git a/config.mk b/config.mk @@ -1,8 +1,9 @@ +VERSION = git + CC = cc AR = ar RANLIB = ranlib -CPPFLAGS = -D_XOPEN_SOURCE=700 -D_GNU_SOURCE +CPPFLAGS = -D_XOPEN_SOURCE=700 -DVERSION=\"${VERSION}\" CFLAGS = -std=c99 -Wpedantic -Wall -Wextra LDFLAGS = -static - diff --git a/libutil/proc.c b/libutil/proc.c @@ -29,8 +29,10 @@ getsyspid(service *sv) { pid_t pid; - if (access(sv->syspidfile, R_OK) == -1) + if (access(sv->syspidfile, R_OK) == -1) { + perror(sv->syspidfile); return -1; + } FILE *pidfile; pidfile = fopen(sv->syspidfile, "r"); @@ -49,10 +51,10 @@ writesvpid(char *file, pid_t pid) FILE *pidfile; pidfile = fopen(file, "w"); - if (pidfile == NULL) - /* perror(file); */ - return -2; - + if (pidfile == NULL) { + perror(file); + return -1; + } fprintf(pidfile, "%d\n", pid); fclose(pidfile); diff --git a/libutil/rm.c b/libutil/rm.c @@ -1,7 +1,5 @@ /* Remove directories * - * This file is part of sysmgr. - * * Function mostly taken from: * https://stackoverflow.com/a/5467788 * Licensed under CC BY-SA 2.5 diff --git a/libutil/service.c b/libutil/service.c @@ -20,17 +20,19 @@ #include <sys/stat.h> #include "../util.h" +#include "../config.h" service* sv_init(service *sv, char *sv_name) { sprintf(sv->name, "%s", sv_name); - sprintf(sv->sysdir, "%s", getenv_fallback("SYSDIR", "/var/sysmgr")); - sprintf(sv->rundir, "%s", getenv_fallback("RUNDIR", "/run/sysmgr")); + sprintf(sv->sysdir, "%s", getenv_fallback("SYSDIR", sysdir_default)); + sprintf(sv->rundir, "%s", getenv_fallback("RUNDIR", rundir_default)); sprintf(sv->pidfile, "%s/%s/pid", sv->rundir, sv->name); sprintf(sv->syspidfile, "%s/%s/syspid", sv->rundir, sv->name); sprintf(sv->svfile, "%s/%s", sv->sysdir, sv_name); sprintf(sv->svrundir, "%s/%s", sv->rundir, sv_name); + sprintf(sv->lockfile, "%s/%s/lock", sv->rundir, sv->name); return sv; } @@ -50,18 +52,21 @@ void sv_start(service *sv) } } -int sv_check(service *sv) +int sv_check(service *sv, int force) { + /* If force is specified '1', this will return the actual service status + * regardless of the lockfile. The lockfile serves the purpose of + * stopping services and making sure they are not restarted. + */ pid_t pid; struct stat sb; - char lockfile[PATH_MAX + sizeof("/lock")]; - /* If a lockfile exists, we will assume that the service was stopped by - * the user. */ - sprintf(lockfile, "%s/lock", sv->svrundir); - - if (lstat(lockfile, &sb) == 0) - return 0; + if (lstat(sv->lockfile, &sb) == 0) { + if (force == 1) + return -1; + else + return 0; + } pid = getsyspid(sv); @@ -70,3 +75,18 @@ int sv_check(service *sv) return 0; } + +int sv_writelock(char *file, int sig) +{ + FILE *lockfile; + + lockfile = fopen(file, "w"); + if (lockfile == NULL) { + perror(file); + return -1; + } + fprintf(lockfile, "%d\n", sig); + fclose(lockfile); + + return 0; +} diff --git a/runsyssv.c b/runsyssv.c @@ -1,15 +1,20 @@ #include <sys/types.h> #include <sys/stat.h> +#include <sys/wait.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <signal.h> +#include <libgen.h> #include <string.h> +#include <dirent.h> #include "util.h" #include "config.h" -static char *argv0; +static char *argv0, *lockfile, *svrundir; +static pid_t syspid, svpid; +static struct service sv; void usage(int exitnum) @@ -18,6 +23,23 @@ usage(int exitnum) exit(exitnum); } +void +term(int sig) +{ + switch(sig) { + case SIGUSR1: + kill(svpid, SIGKILL); + break; + default: + kill(svpid, SIGTERM); + break; + } + if (sv_writelock(lockfile, sig) != 0) + rm_rf(svrundir); + exit(0); +} + + int main(int argc, char *argv[]) { @@ -32,8 +54,41 @@ main(int argc, char *argv[]) } else usage(1); + syspid = getpid(); + struct service sv; - sv_init(&sv, argv[1]); + sv_init(&sv, basename(argv[1])); + + DIR *dir; + if ((dir = opendir(sv.svrundir)) != NULL) { + closedir(dir); + return 1; + } + mkdirp(sv.svrundir); + int sv_signals[] = {SIGTERM, SIGINT, SIGHUP, SIGQUIT, SIGABRT}; + lockfile = sv.lockfile; + svrundir = sv.svrundir; + + svpid = fork(); + switch(svpid) { + case -1: + die("fork:"); + break; + case 0: + execvp(sv.svfile, argv); + perror(sv.svfile); + break; + default: + writesvpid(sv.syspidfile, syspid); + writesvpid(sv.pidfile, svpid); + for (long unsigned int i=0; i < sizeof(sv_signals); i++) + signal(sv_signals[i], term); + wait(NULL); + if (rm_rf(sv.svrundir) != 0) + return 1; + break; + } + return 0; } diff --git a/svctl.c b/svctl.c @@ -0,0 +1,124 @@ +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <signal.h> +#include <string.h> +#include <dirent.h> +#include <libgen.h> + +#include "util.h" +#include "config.h" + +static char *argv0; + +void +usage(int exitnum) +{ + printf("usage: %s [operation] [service...]\n\n", argv0); + fputs(" Operations:\n" + " start/stop/restart Start/stop/restart services\n" + " once Start services once\n" + " status Check service statuses\n" + " up/down Same as start/stop\n\n", stdout); + exit(exitnum); +} + +int check_rundir(char *dir) +{ + DIR *rundirectory; + rundirectory = opendir(dir); + if (rundirectory == NULL) + return -1; + closedir(rundirectory); + return 0; +} + +int handle_service(char *operation, char *name) +{ + struct service sv; + sv_init(&sv, basename(name)); + pid_t pid; + + if (strcmp(operation, "start") == 0 || strcmp(operation, "up") == 0) { + if(sv_check(&sv, 1) == 0) + return 0; + return rm_rf(sv.svrundir); + } else if (strcmp(operation, "stop") == 0 || strcmp(operation, "down") == 0) { + pid = getsyspid(&sv); + switch (pid) { + case -1: + perror(NULL); + return 1; + default: + sv_writelock(sv.lockfile, SIGTERM); + kill(pid, SIGTERM); + unlink(sv.syspidfile); + unlink(sv.pidfile); + } + } else if (strcmp(operation, "kill") == 0) { + pid = getsyspid(&sv); + switch (pid) { + case -1: + perror(NULL); + return 1; + default: + sv_writelock(sv.lockfile, SIGKILL); + kill(pid, SIGUSR1); + unlink(sv.syspidfile); + unlink(sv.pidfile); + } + } else if (strcmp(operation, "once") == 0) { + if(sv_check(&sv, 1) == 0) { + if (sv_writelock(sv.lockfile, 0) != 0) { + perror(sv.lockfile); + die("Failed to write lock."); + } + } else { + rm_rf(sv.svrundir); + sleep(1); + return sv_writelock(sv.lockfile, 0); + } + } else if (strcmp(operation, "restart") == 0) { + handle_service("stop", name); + while (1) { + if (sv_check(&sv, 1) != 0) + break; + } + handle_service("start", name); + + } else if (strcmp(operation, "stat") == 0 || strcmp(operation, "status") == 0) { + if (sv_check(&sv, 1) < 0) { + fprintf(stderr, "%s: DOWN\n", name); + exit(1); + } else + fprintf(stderr, "%s: UP\n", name); + } else + die("Unknown operation: %s", operation); + return 0; +} + +int +main(int argc, char *argv[]) +{ + char *rundir; + argv0 = argv[0]; + int i; + + if (argc < 2 || strncmp(argv[1], "-", 1) == 0) + usage(0); + else if (argc < 3) + usage(1); + + char **sv = argv + 1; + + /* Check if the RUNDIR exists */ + rundir = getenv_fallback("RUNDIR", rundir_default); + if (check_rundir(rundir) != 0) + die("%s could not be found, are you sure sysmgr is running?", rundir); + + for (i=2; i < argc; i++) + if (handle_service(argv[1], argv[i]) != 0) + die("Couldn't %s %s", argv[1], argv[i]); + + return 0; +} diff --git a/sysmgr.c b/sysmgr.c @@ -91,13 +91,12 @@ int main(int argc, char *argv[]) mkdirp(rundir); /* Trap signals */ - int signals[] = {SIGTERM, SIGINT, SIGHUP, SIGQUIT, SIGABRT}; - for (long unsigned int i=0; i < sizeof(signals); i++) - signal(signals[i], term); + int sv_signals[] = {SIGTERM, SIGINT, SIGHUP, SIGQUIT, SIGABRT}; + for (long unsigned int i=0; i < sizeof(sv_signals); i++) + signal(sv_signals[i], term); pid_t pid = getpid(); - printf("RUNDIR: %s\nSYSDIR: %s\nselfpid: %d\n", rundir, sysdir, pid); if (writesvpid(sysmgr_pidfile, pid) != 0) die("%s:", sysmgr_pidfile); @@ -112,8 +111,7 @@ int main(int argc, char *argv[]) continue; struct service sv; sv_init(&sv, ent->d_name); - if (sv_check(&sv) != 0) { - printf("%s\n", ent->d_name); + if (sv_check(&sv, 0) != 0) { sv_start(&sv); } } diff --git a/util.h b/util.h @@ -22,12 +22,14 @@ typedef struct service { char syspidfile[PATH_MAX]; char svfile[PATH_MAX]; char svrundir[PATH_MAX]; + char lockfile[PATH_MAX]; } service; /* service.c */ service *sv_init(service *sv, char *sv_name); void sv_start(service *sv); -int sv_check(service *sv); +int sv_check(service *sv, int force); +int sv_writelock(char *file, int sig); /* proc.c */ int getsvpid(service *sv);