From 6e8f21df8e01889395b38e5993a3813f49cd386c Mon Sep 17 00:00:00 2001 From: Mike Lowis Date: Tue, 26 Apr 2016 16:15:18 -0400 Subject: [PATCH] Initial commit of functioning init and 50% completed getty --- LICENSE.md | 21 ++++ Makefile | 45 ++++++++ README.md | 0 include/util.h | 285 +++++++++++++++++++++++++++++++++++++++++++++++++ source/getty.c | 120 +++++++++++++++++++++ source/init.c | 42 ++++++++ 6 files changed, 513 insertions(+) create mode 100644 LICENSE.md create mode 100644 Makefile create mode 100644 README.md create mode 100644 include/util.h create mode 100644 source/getty.c create mode 100644 source/init.c diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 00000000..fa01cbf9 --- /dev/null +++ b/LICENSE.md @@ -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 index 00000000..5080417f --- /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 index 00000000..e69de29b diff --git a/include/util.h b/include/util.h new file mode 100644 index 00000000..d5689cea --- /dev/null +++ b/include/util.h @@ -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 +#include +#include +#include +#include +#include + +/* Useful Standard Functions */ +#include +#include +#include +#include +#include + +/* 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 index 00000000..1499129b --- /dev/null +++ b/source/getty.c @@ -0,0 +1,120 @@ +/** + @brief + @author Michael D. Lowis + @license BSD 2-clause License +*/ +#include "util.h" +#include +#include +#include + +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 index 00000000..43be77e2 --- /dev/null +++ b/source/init.c @@ -0,0 +1,42 @@ +/** + @brief + @author Michael D. Lowis + @license BSD 2-clause License +*/ +#include +#include +#include + +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; +} -- 2.54.0