]> git.mdlowis.com Git - proto/albase.git/commitdiff
Initial commit of functioning init and 50% completed getty
authorMike Lowis <mike.lowis@gentex.com>
Tue, 26 Apr 2016 20:15:18 +0000 (16:15 -0400)
committerMike Lowis <mike.lowis@gentex.com>
Tue, 26 Apr 2016 20:15:18 +0000 (16:15 -0400)
LICENSE.md [new file with mode: 0644]
Makefile [new file with mode: 0644]
README.md [new file with mode: 0644]
include/util.h [new file with mode: 0644]
source/getty.c [new file with mode: 0644]
source/init.c [new file with mode: 0644]

diff --git a/LICENSE.md b/LICENSE.md
new file mode 100644 (file)
index 0000000..fa01cbf
--- /dev/null
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2016 mikedlowis-prototypes
+
+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 (file)
index 0000000..5080417
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,45 @@
+#------------------------------------------------------------------------------
+# Build Configuration
+#------------------------------------------------------------------------------
+# tools
+CC = cc
+
+# flags
+LIBS     =
+INCS     = -Iinclude
+CPPFLAGS =
+CFLAGS   = -O2 ${INCS} ${CPPFLAGS}
+LDFLAGS  = ${LIBS}
+ECLEAN   =
+
+#------------------------------------------------------------------------------
+# Build-Specific Macros
+#------------------------------------------------------------------------------
+# Simple Single-file Binary Template
+define make-bin =
+$1: source/$1.c
+       $(CC) ${CFLAGS} -o $$@ $$<
+ECLEAN += $1
+endef
+
+BINS = init getty
+
+# load user-specific settings
+-include config.mk
+
+#------------------------------------------------------------------------------
+# Phony Targets
+#------------------------------------------------------------------------------
+.PHONY: all
+
+all: $(BINS)
+
+$(eval $(call make-bin,init))
+$(eval $(call make-bin,getty))
+
+clean:
+       $(RM) $(ECLEAN)
+
+# load dependency files
+-include ${DEPS}
+
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/include/util.h b/include/util.h
new file mode 100644 (file)
index 0000000..d5689ce
--- /dev/null
@@ -0,0 +1,285 @@
+/**
+  @brief Collection of useful C types and functions.
+  @author Michael D. Lowis
+  @license BSD 2-clause License
+*/
+#ifndef UTIL_H
+#define UTIL_H
+
+/* Standard Macros and Types */
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <limits.h>
+#include <assert.h>
+
+/* Useful Standard Functions */
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+
+/* Type Definitions
+ *****************************************************************************/
+typedef unsigned short ushort;
+typedef unsigned char uchar;
+typedef unsigned long ulong;
+typedef unsigned int uint;
+typedef signed char schar;
+typedef long long vlong;
+typedef unsigned long long uvlong;
+
+/* Generic Death Function
+ *****************************************************************************/
+static void die(const char* msgfmt, ...)
+{
+    va_list args;
+    va_start(args, msgfmt);
+    #ifdef CLEANUP_HOOK
+    CLEANUP_HOOK();
+    #endif
+    fprintf(stderr, "Error: ");
+    vfprintf(stderr, msgfmt, args);
+    fprintf(stderr, "\n");
+    va_end(args);
+    exit(EXIT_FAILURE);
+}
+
+/* Signal Handling
+ *****************************************************************************/
+static void esignal(int sig, void (*func)(int))
+{
+    errno = 0;
+    func  = signal(sig, func);
+    if (func == SIG_ERR || errno > 0)
+        die("failed to register signal handler for signal %d", sig);
+}
+
+static int eraise(int sig)
+{
+    int ret;
+    if ((ret = raise(sig)) != 0)
+        die("failed to raise signal %d", sig);
+    return ret;
+}
+
+/* Dynamic Allocation
+ *****************************************************************************/
+static void* ecalloc(size_t num, size_t size)
+{
+    void* ret;
+    if (NULL == (ret = calloc(num,size)))
+        die("out of memory");
+    return ret;
+}
+
+static void* emalloc(size_t size)
+{
+    void* ret;
+    if (NULL == (ret = malloc(size)))
+        die("out of memory");
+    return ret;
+}
+
+static void* erealloc(void* ptr, size_t size)
+{
+    void* ret;
+    if (NULL == (ret = realloc(ptr,size)))
+        die("out of memory");
+    return ret;
+}
+
+/* File Handling
+ *****************************************************************************/
+static FILE* efopen(const char* filename, const char* mode)
+{
+    FILE* file;
+    errno = 0;
+    if (NULL == (file = fopen(filename, mode)) || errno != 0)
+        die("failed to open file: %s", filename);
+    return file;
+}
+
+static char* efreadline(FILE* input)
+{
+    size_t size  = 8;
+    size_t index = 0;
+    char*  str   = (char*)emalloc(size);
+    memset(str, 0, 8);
+    if (feof(input)) {
+        free(str);
+        return NULL;
+    }
+    while (true) {
+        char ch = fgetc(input);
+        if (ch == EOF) break;
+        str[index++] = ch;
+        str[index]   = '\0';
+        if (index+1 >= size) {
+            size = size << 1;
+            str  = erealloc(str, size);
+        }
+        if (ch == '\n') break;
+    }
+    return str;
+}
+
+/* String Handling
+ *****************************************************************************/
+static char* estrdup(const char *s)
+{
+    char* ns = (char*)emalloc(strlen(s) + 1);
+    strcpy(ns,s);
+    return ns;
+}
+
+/* Option Parsing
+ *
+ * This following macros implement a simple POSIX-style option parsing strategy.
+ * They are heavily influenced and inspired by the arg.h file from suckless.org
+ * (http://git.suckless.org/libsl/tree/arg.h). That file is in turn inspired by
+ * the corresponding macros defined in plan9.
+ *
+ * The interface assumes that the main function will have the following
+ * prototype:
+ *
+ * int main(int argc, char** argv);
+ *
+ * An example usage of the interface would look something like the follwoing:
+ *
+ * char* ARGV0;
+ * int main(int argc, char** argv) {
+ *     OPTBEGIN {
+ *         case 'a': printf("Simple option\n"); break;
+ *         case 'b': printf("Option with arg: %s\n", OPTARG()); break;
+ *         default:  printf("Unknown option!\n");
+ *     } OPTEND;
+ *     return 0;
+ * }
+ */
+
+/* This variable contains the value of argv[0] so that it can be referenced
+ * again once the option parsing is done. This variable must be defined by the
+ * program.
+ *
+ * NOTE: Ensure that you define this variable with external linkage (i.e. not
+ * static) */
+extern char* ARGV0;
+
+/* This is a helper function used by the following macros to parse the next
+ * option from the command line. */
+static inline char* _getopt_(int* p_argc, char*** p_argv) {
+    if (!(*p_argv)[0][1] && !(*p_argv)[1]) {
+        return (char*)0;
+    } else if ((*p_argv)[0][1]) {
+        return &(*p_argv)[0][1];
+    } else {
+        *p_argv = *p_argv + 1;
+        *p_argc = *p_argc - 1;
+        return (*p_argv)[0];
+    }
+}
+
+/* This macro is almost identical to the ARGBEGIN macro from suckless.org. If
+ * it ain't broke, don't fix it. */
+#define OPTBEGIN                                                              \
+    for (                                                                     \
+        ARGV0 = *argv, argc--, argv++;                                        \
+        argv[0] && argv[0][1] && argv[0][0] == '-';                           \
+        argc--, argv++                                                        \
+    ) {                                                                       \
+        int brk_; char argc_ , **argv_, *optarg_;                             \
+        if (argv[0][1] == '-' && !argv[0][2]) {                               \
+            argv++, argc--; break;                                            \
+        }                                                                     \
+        for (brk_=0, argv[0]++, argv_=argv; argv[0][0] && !brk_; argv[0]++) { \
+            if (argv_ != argv) break;                                         \
+            argc_ = argv[0][0];                                               \
+            switch (argc_)
+
+/* Terminate the option parsing. */
+#define OPTEND }}
+
+/* Get the current option character */
+#define OPTC() (argc_)
+
+/* Get an argument from the command line and return it as a string. If no
+ * argument is available, this macro returns NULL */
+#define OPTARG() \
+    (optarg_ = _getopt_(&argc,&argv), brk_ = (optarg_!=0), optarg_)
+
+/* Get an argument from the command line and return it as a string. If no
+ * argument is available, this macro executes the provided code. If that code
+ * returns, then abort is called. */
+#define EOPTARG(code) \
+    (optarg_ = _getopt_(&argc,&argv), \
+    (!optarg_ ? ((code), abort(), (char*)0) : (brk_ = 1, optarg_)))
+
+/* Helper macro to recognize number options */
+#define OPTNUM \
+    case '0':  \
+    case '1':  \
+    case '2':  \
+    case '3':  \
+    case '4':  \
+    case '5':  \
+    case '6':  \
+    case '7':  \
+    case '8':  \
+    case '9'
+
+/* Helper macro to recognize "long" options ala GNU style. */
+#define OPTLONG \
+    case '-'
+
+/* Error Handling
+ *****************************************************************************/
+#ifdef NDEBUG
+    #define debug(msg, ...) \
+        ((void)0)
+#else
+    #define debug(msg, ...) \
+        fprintf(stderr, "DEBUG %s:%d: " msg "\n", __FILE__, __LINE__, ##__VA_ARGS__)
+#endif
+
+#define errnostr() \
+    (errno == 0 ? "None" : strerror(errno))
+
+#define print_error(msg, ...) \
+    fprintf(stderr, "[ERROR] (%s:%d: errno: %s) " msg "\n", __FILE__, __LINE__, errnostr(), ##__VA_ARGS__)
+
+#define check(expr, msg, ...) \
+    if(!(expr)) { print_error(msg, ##__VA_ARGS__); errno=0; goto error; }
+
+#define sentinel(msg, ...) \
+    { print_error(msg, ##__VA_ARGS__); errno=0; goto error; }
+
+/* Miscellaneous
+ *****************************************************************************/
+#ifndef nelem
+    #define nelem(x) \
+        (sizeof(x)/sizeof((x)[0]))
+#endif
+
+#ifndef container_of
+    #define container_of(obj, type, member) \
+        (type*)((uintptr_t)obj - offsetof(type, member))
+#endif
+
+#define concat(a,b) \
+    a##b
+
+#define ident(a) \
+    concat(id, a)
+
+#define unique_id \
+    ident(__LINE__)
+
+#ifndef static_assert
+    #define static_assert(expr) \
+        typedef char unique_id[( expr )?1:-1]
+#endif
+
+#endif
diff --git a/source/getty.c b/source/getty.c
new file mode 100644 (file)
index 0000000..1499129
--- /dev/null
@@ -0,0 +1,120 @@
+/**
+  @brief
+  @author Michael D. Lowis
+  @license BSD 2-clause License
+*/
+#include "util.h"
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+
+char* ARGV0;
+static char* TTY  = "/dev/tty1";
+static char* TERM = "linux";
+
+static void sigignore(int signal, int ignore) {
+    struct sigaction sigact;
+    sigact.sa_handler = (ignore ? SIG_IGN : SIG_DFL);
+    sigact.sa_flags = 0;
+    sigemptyset(&sigact.sa_mask);
+    sigaction(signal, &sigact, NULL);
+}
+
+static int opentty(void) {
+    int fd;
+    /* First reset the tty and take control if necessary */
+    if ((fd = open(TTY, O_RDWR)) < 0)
+        die("Failed to open %s\n", TTY);
+    if (isatty(fd) == 0)
+        die("%s is not a tty\n", TTY);
+    if (ioctl(fd, TIOCSCTTY, (void *)1) != 0)
+        fprintf(stderr, "Warning: TIOCSCTTY: could not set controlling tty\n");
+    vhangup();
+    close(fd);
+    /* Now open it for real */
+    if ((fd = open(TTY, O_RDWR)) < 0)
+        die("Failed to open %s\n", TTY);
+    return fd;
+}
+
+static int dologin(void) {
+    //if (gethostname(hostname, sizeof(hostname)) == 0)
+    //    printf("%s ", hostname);
+    //printf("login: ");
+    //fflush(stdout);
+
+    ///* Flush pending input */
+    //ioctl(0, TCFLSH, (void *)0);
+    //memset(logname, 0, sizeof(logname));
+    //while (1) {
+    //    n = read(0, &c, 1);
+    //    if (n < 0)
+    //        eprintf("read:");
+    //    if (n == 0)
+    //        return 1;
+    //    if (i >= sizeof(logname) - 1)
+    //        eprintf("login name too long\n");
+    //    if (c == '\n' || c == '\r')
+    //        break;
+    //    logname[i++] = c;
+    //}
+    //if (logname[0] == '-')
+    //    eprintf("login name cannot start with '-'\n");
+    //if (logname[0] == '\0')
+    //    return 1;
+    //return execlp("/bin/login", "login", "-p", logname, NULL);
+    return 0;
+}
+
+int main(int argc, char *argv[]) {
+    /* Print usage and exit if we receive any flags */
+    OPTBEGIN{
+        default:
+            die("Usage: %s [tty] [term] [cmd] [args]\n", ARGV0);
+    } OPTEND;
+    /* Read the arguments into variables */
+    if (argc > 0) TTY  = argv[0];
+    if (argc > 1) TERM = argv[1];
+    /* setup the environment and session */
+    sigignore(SIGHUP, 1); // ignore SIGHUP
+    setenv("TERM", TERM, 1);
+    setsid();
+    /* Open the TTY for normal usage */
+    int fd = opentty();
+    dup2(fd, 0); // Make stdin  the TTY
+    dup2(fd, 1); // Make stdout the TTY
+    dup2(fd, 2); // Make stderr the TTY
+    if (fchown(fd, 0, 0) < 0)
+        fprintf(stderr, "fchown %s:", TTY);
+    if (fchmod(fd, 0600) < 0)
+        fprintf(stderr, "fchmod %s:", TTY);
+    if (fd > 2)
+        close(fd);
+    sigignore(SIGHUP, 0); // stop ignoring SIGHUP
+    /* clear all utmp entries for this TTY */
+//    /* Clear all utmp entries for this tty */
+//    fp = fopen(UTMP_PATH, "r+");
+//    if (fp) {
+//        do {
+//            pos = ftell(fp);
+//            if (fread(&usr, sizeof(usr), 1, fp) != 1)
+//                break;
+//            if (usr.ut_line[0] == '\0')
+//                continue;
+//            if (strcmp(usr.ut_line, tty) != 0)
+//                continue;
+//            memset(&usr, 0, sizeof(usr));
+//            fseek(fp, pos, SEEK_SET);
+//            if (fwrite(&usr, sizeof(usr), 1, fp) != 1)
+//                break;
+//        } while (1);
+//        if (ferror(fp))
+//            weprintf("%s: I/O error:", UTMP_PATH);
+//        fclose(fp);
+//    }
+    /* Start the command or login prompt */
+    if (argc > 2)
+        return execvp(argv[2], &(argv[2]));
+    else
+        return dologin();
+}
diff --git a/source/init.c b/source/init.c
new file mode 100644 (file)
index 0000000..43be77e
--- /dev/null
@@ -0,0 +1,42 @@
+/**
+  @brief
+  @author Michael D. Lowis
+  @license BSD 2-clause License
+*/
+#include <signal.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+static sigset_t set;
+static char* const rcinitcmd[]     = { "/etc/rc.init", 0 };
+static char* const rcrebootcmd[]   = { "/etc/rc.shutdown", "reboot", 0 };
+static char* const rcpoweroffcmd[] = { "/etc/rc.shutdown", "poweroff", 0 };
+
+static void spawn(char* const argv[]) {
+    if (0 == fork()) {
+        sigprocmask(SIG_UNBLOCK, &set, NULL);
+        setsid();
+        execvp(argv[0], argv);
+        _exit(1);
+    }
+}
+
+int main(void) {
+    if (getpid() != 1)  return 1;
+    if (chdir("/") < 0) return 1;
+    sigfillset(&set);
+    sigprocmask(SIG_BLOCK, &set, NULL);
+    spawn(rcinitcmd);
+    while (1) {
+        int sig;
+        sigwait(&set, &sig);
+        switch (sig) {
+            case SIGUSR1: spawn(rcpoweroffcmd); break;
+            case SIGINT:  spawn(rcrebootcmd);   break;
+            case SIGCHLD:
+                while (waitpid(-1, NULL, WNOHANG) > 0);
+                break;
+        }
+    }
+    return 0;
+}