commit e6c2dc0fb35fc1c0196a585d5c9d0c787bf19c23 Author: Thomas Orozco Date: Sun Feb 22 13:23:44 2015 -0800 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ccc4718 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +sinit.o +sinit diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..691535c --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Thoams Orozco + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..01729c3 --- /dev/null +++ b/Makefile @@ -0,0 +1,31 @@ +include config.mk + +OBJ = sinit.o +BIN = sinit + +all: $(BIN) + +$(BIN): $(OBJ) + $(CC) $(LDFLAGS) -o $@ $(OBJ) $(LDLIBS) + +$(OBJ): + +install: all + mkdir -p $(DESTDIR)$(PREFIX)/bin + cp -f $(BIN) $(DESTDIR)$(PREFIX)/bin + +uninstall: + rm -f $(DESTDIR)$(PREFIX)/bin/$(BIN) + +dist: clean + mkdir -p sinit-$(VERSION) + cp LICENSE Makefile README config.def.h config.mk sinit.c sinit-$(VERSION) + tar -cf sinit-$(VERSION).tar sinit-$(VERSION) + gzip sinit-$(VERSION).tar + rm -rf sinit-$(VERSION) + +clean: + rm -f $(BIN) $(OBJ) sinit-$(VERSION).tar.gz + +.PHONY: + all install uninstall dist clean diff --git a/README b/README new file mode 100644 index 0000000..cdde5b6 --- /dev/null +++ b/README @@ -0,0 +1 @@ +Simple but correct init for Docker containers diff --git a/config.mk b/config.mk new file mode 100644 index 0000000..92980c2 --- /dev/null +++ b/config.mk @@ -0,0 +1,12 @@ +# sinit version +VERSION = 0.1.0 + +# paths +PREFIX = /usr/local +MANPREFIX = $(PREFIX)/share/man + +CC = cc +LD = $(CC) +CPPFLAGS = +CFLAGS = -Wextra -Wall -Os +LDFLAGS = -s -static diff --git a/sinit.c b/sinit.c new file mode 100644 index 0000000..7b9e5e6 --- /dev/null +++ b/sinit.c @@ -0,0 +1,131 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +static sigset_t set; + +pid_t spawn(char *const argv[]) { + pid_t pid; + + pid = fork(); + if (pid < 0) { + perror("[FATAL] Fork failed"); + _exit(1); + } else if (pid == 0) { + sigprocmask(SIG_UNBLOCK, &set, NULL); + execvp(argv[0], argv); + perror("[ERROR] Executing child process failed"); + _exit(1); + } else { + return pid; + } +} + +int main(int argc, char *argv[]) { + siginfo_t sig; + + pid_t child_pid; + + pid_t current_pid; + int current_status; + + struct timespec ts; + ts.tv_sec = 1; + ts.tv_nsec = 0; + + /* Prepare signals */ + if (sigfillset(&set)) { + perror("sigfillset"); + return 1; + } + if (sigprocmask(SIG_BLOCK, &set, NULL)) { + perror("sigprocmask"); + return 1; + } + + /* Spawn the main command */ + child_pid = spawn(argv+1); + printf("[INFO ] Spawned child process\n"); + + /* Loop forever: + * - Reap zombies + * - Forward signals + */ + while (1) { + if (sigtimedwait(&set, &sig, &ts) == -1) { + switch (errno) { + case EAGAIN: + break; + case EINTR: + break; + case EINVAL: + perror("[ERROR] Fatal!"); + return 2; + } + } else { + /* There is a signal to handle here */ + switch (sig.si_signo) { + case SIGCHLD: + // Special-cased, as we don't forward SIGCHLD. Instead, we'll + // fallthrough to reaping processes. + printf("[INFO ] Received SIGCHLD\n"); + break; + default: + printf("[INFO ] Passing signal: %s\n", strsignal(sig.si_signo)); + // Forward anything else + kill(child_pid, sig.si_signo); + break; + } + } + + /* Now, reap zombies */ + while (1) { + current_pid = waitpid(-1, ¤t_status, WNOHANG); + switch (current_pid) { + case -1: + // An error occured. Print it and exit. + perror("waitpids returned -1"); + return 1; + case 0: + // No child to reap. We'll break out of the loop here. + break; + default: + // A child was reaped. Check whether it's the main one, + // and go for another iteration otherwise. + if (current_pid == child_pid) { + printf("[INFO ] Main child has exited\n"); + if (WIFEXITED(current_status)) { + // Our process exited normally. + printf("[DEBUG] Main child exited normally\n"); + return WEXITSTATUS(current_status); + } else if (WIFSIGNALED(current_status)) { + // Our process was terminated. Emulate what sh / bash + // would do, which is to return 128 + signal number. + printf("[DEBUG] Main child exited with signal\n"); + return 128 + WTERMSIG(current_status); + } else { + printf("[WARNING] Main child exited for unknown reason\n"); + return 1; + } + } + continue; + } + + // If we make it here, that's because we did not continue in + // the switch case. + break; + } + + } + /* not reachable */ + return 0; +} +