X-Git-Url: https://git.ucc.asn.au/?a=blobdiff_plain;f=Usermode%2FApplications%2Finit_src%2Fmain.c;h=73411b9345be1ca5362278ae834212ad07d87831;hb=4239ff15de51d3eb299bbebf4f63077edcc0d2db;hp=50057089ef5524675a562b7c68b1ecca056b1207;hpb=685d1f5c4d865e814a4640f5f3270a82ea10e4b0;p=tpg%2Facess2.git diff --git a/Usermode/Applications/init_src/main.c b/Usermode/Applications/init_src/main.c index 50057089..73411b93 100644 --- a/Usermode/Applications/init_src/main.c +++ b/Usermode/Applications/init_src/main.c @@ -2,12 +2,40 @@ * Acess2 System Init Task */ #include +#include +#include +#include +#include "common.h" +#include +#include // === CONSTANTS === -#define NULL ((void*)0) -#define NUM_TERMS 4 -#define DEFAULT_TERMINAL "/Devices/VTerm/0" #define DEFAULT_SHELL "/Acess/SBin/login" +#define INITTAB_FILE "/Acess/Conf/inittab" + +#define ARRAY_SIZE(x) ((sizeof(x))/(sizeof((x)[0]))) + +// === PROTOTYPES === + int ProcessInittab(const char *Path); +char *ReadQuotedString(FILE *FP); +char **ReadCommand(FILE *FP); +void FreeCommand(char **Command); + +tInitProgram *AllocateProgram(char **Command, enum eProgType Type, size_t ExtraSpace); +void RespawnProgam(tInitProgram *Program); + + int AddKTerminal(int TerminalSpec, char **Command); + int AddSerialTerminal(const char *DevPathSegment, const char *ModeStr, char **Command); + int AddDaemon(char *StdoutPath, char *StderrPath, char **Command); + + int SpawnCommand(int c_stdin, int c_stdout, int c_stderr, char **ArgV, const char **EnvP); + int SpawnKTerm(tInitProgram *Program); + int SpawnSTerm(tInitProgram *Program); + int SpawnDaemon(tInitProgram *Program); + +// === GLOBALS === +const char *gsInittabPath = INITTAB_FILE; +tInitProgram *gpInitPrograms; // === CODE === /** @@ -16,30 +44,473 @@ */ int main(int argc, char *argv[]) { - int tid; - int i; - char termpath[sizeof(DEFAULT_TERMINAL)] = DEFAULT_TERMINAL; - char *child_argv[2] = {DEFAULT_SHELL, 0}; + // Parse commandline args + // TODO: cmdline + + // TODO: Behave differently if not invoked as first process? + + // Begin init process + if( ProcessInittab(gsInittabPath) != 0 ) + { + // No inittab file found, default to: + _SysDebug("inittab '%s' is invalid, falling back to one VT", gsInittabPath); + + #if 1 + for( ;; ) + { + char *args[] = {DEFAULT_SHELL, NULL}; + const char *env[] = {"TERM=screen", NULL}; + int pid = SpawnCommand(0, 1, 1, args, env); + // TODO: Detect errors + _SysWaitTID(pid, NULL); + } + #else + return 41; + #endif + } + + // TODO: Implement message watching + for(;;) + { + int pid, status; + pid = _SysWaitTID(-1, &status); + _SysDebug("PID %i died, looking for respawn entry", pid); + } + + return 42; +} + +char *ReadQuotedString(FILE *FP) +{ + char ch; - for( i = 0; i < NUM_TERMS; i++ ) - { - tid = clone(CLONE_VM, 0); - if(tid == 0) + while( isblank(ch = fgetc(FP)) ) + ; + + if( ch == '\n' ) { + return NULL; + } + + char *retstr = NULL; + int mode = 0; + int pos = 0, space = 0; + for( ; !feof(FP); ch = fgetc(FP) ) + { + int skip; + skip = 1; + if( mode & 4 ) { + // Add escaped/special character + skip = 0; + mode &= 3; + } + else if( mode == 0 ) { - termpath[sizeof(DEFAULT_TERMINAL)-2] = '0' + i; - - //__asm__ __volatile__ ("int $0xAC" :: "a" (256), "b" ("%s"), "c" (termpath)); + if( isspace(ch) ) { + fseek(FP, -1, SEEK_CUR); + break; + } + else if( ch == '\\' ) + mode |= 4; + else if( ch == '"' ) + mode = 1; + else if( ch == '\'') + mode = 2; + else { + // Add character + skip = 0; + } + } + // Double-quoted string + else if( mode == 1 ) + { + if( ch == '"' ) + mode = 0; + else if( ch == '\\' ) + mode |= 4; + else { + // Add character + skip = 0; + } + } + // Single-quoted string + else if( mode == 2 ) + { + if( ch == '\'' ) + mode = 0; + else if( ch == '\\' ) + mode |= 4; + else { + // Add character + skip = 0; + } + } + + if( !skip ) + { + if( pos == space ) { + space += 32; + void *tmp = realloc(retstr, space+1); + if( !tmp ) { + _SysDebug("ReadQuotedString - realloc(%i) failure", space+1); + free(retstr); + return NULL; + } + retstr = tmp; + } + retstr[pos++] = ch; + } + } + if( retstr ) + retstr[pos] = '\0'; + return retstr; +} + +char **ReadCommand(FILE *FP) +{ + const int space = 8; + int pos = 0; + char **ret = malloc(space*sizeof(char*)); + char *arg; + do { + arg = ReadQuotedString(FP); + if(arg == NULL) + break; + if( pos == space - 1 ) { + _SysDebug("Too many arguments %i", pos); + ret[pos] = NULL; + FreeCommand(ret); + return NULL; + } + ret[pos++] = arg; + } while(arg != NULL); + + if( pos == 0 ) + { + free(ret); + return NULL; + } + ret[pos] = NULL; + return ret; +} + +void FreeCommand(char **Command) +{ + int pos = 0; + while( Command[pos] ) + { + free(Command[pos]); + pos ++; + } + free(Command); +} + +int ProcessInittab(const char *Path) +{ + int line_num = 0; + FILE *fp = fopen(Path, "r"); + + if( !fp ) + return 1; + + while(!feof(fp)) + { + char cmdbuf[64+1]; + + line_num ++; + + int rv; + if( (rv = fscanf(fp, "%64s%*[ \t]", cmdbuf)) != 1 ) { + _SysDebug("fscanf rv %i != exp 1", rv); + break; + } + + // Clear comments + if( cmdbuf[0] == '#' ) { + while( !feof(fp) && fgetc(fp) != '\n' ) + ; + continue ; + } + if( cmdbuf[0] == '\0' ) { + char ch = fgetc(fp); + if( ch != '\n' && ch != -1 ) { + fclose(fp); + _SysDebug("Unexpected char 0x%x, expecting EOL", ch); + return 2; // Unexpected character? + } + continue ; + } + + // Check commands + if( strcmp(cmdbuf, "ktty") == 0 ) { + // ktty + // - Spins off a console on the specified kernel TTY + int id = 0; + if( fscanf(fp, "%d ", &id) != 1 ) { + _SysDebug("init[ktty] - TTY ID read failed"); + goto lineError; + } + char **command = ReadCommand(fp); + if( !command ) { + _SysDebug("init[ktty] - Command read failure"); + goto lineError; + } + AddKTerminal(id, command); + } + else if(strcmp(cmdbuf, "stty") == 0 ) { + // stty [78][NOE][012][bB] + char path_seg[32+1]; + char modespec[4+6+1]; + if( fscanf(fp, "%32s %10s ", path_seg, modespec) != 2 ) { + goto lineError; + } + char **command = ReadCommand(fp); + if( !command ) + goto lineError; + AddSerialTerminal(path_seg, modespec, command); + } + else if(strcmp(cmdbuf, "daemon") == 0 ) { + // daemon + // - Runs a daemon (respawning) that logs to the specified files + // - Will append a header whenever the daemon starts + char *stdout_path = ReadQuotedString(fp); + if( !stdout_path ) + goto lineError; + char *stderr_path = ReadQuotedString(fp); + if( !stderr_path ) + goto lineError; + char **command = ReadCommand(fp); + if( !command ) + goto lineError; - open(termpath, OPENFLAG_READ); // Stdin - open(termpath, OPENFLAG_WRITE); // Stdout - open(termpath, OPENFLAG_WRITE); // Stderr - execve(DEFAULT_SHELL, child_argv, NULL); - for(;;) sleep(); + AddDaemon(stdout_path, stderr_path, command); } + else if(strcmp(cmdbuf, "exec") == 0 ) { + // exec + // - Runs a command and waits for it to complete before continuing + // - NOTE: No other commands will respawn while this is running + char **command = ReadCommand(fp); + if(!command) + goto lineError; + + int handles[] = {0, 1, 1}; + int pid = _SysSpawn(command[0], (const char **)command, NULL, 3, handles, NULL); + int retstatus; + _SysWaitTID(pid, &retstatus); + _SysDebug("Command '%s' returned %i", command[0], retstatus); + + FreeCommand(command); + } + else { + // Unknown command. + _SysDebug("Unknown command '%s'", cmdbuf); + goto lineError; + } + fscanf(fp, " "); + continue; + lineError: + _SysDebug("label lineError: goto'd - line %i, cmdbuf='%s'", line_num, cmdbuf); + while( !feof(fp) && fgetc(fp) != '\n' ) + ; + continue ; + } + + fclose(fp); + + if( gpInitPrograms == NULL ) + return 2; + + return 0; +} + +tInitProgram *AllocateProgram(char **Command, enum eProgType Type, size_t ExtraSpace) +{ + tInitProgram *ret; + + ret = malloc( sizeof(tInitProgram) - sizeof(union uProgTypes) + ExtraSpace ); + ret->Next = NULL; + ret->CurrentPID = 0; + ret->Type = Type; + ret->Command = Command; + + // Append + ret->Next = gpInitPrograms; + gpInitPrograms = ret; + + return ret; +} + +void RespawnProgam(tInitProgram *Program) +{ + int rv = 0; + switch(Program->Type) + { + case PT_KTERM: rv = SpawnKTerm(Program); break; + case PT_STERM: rv = SpawnSTerm(Program); break; + case PT_DAEMON: rv = SpawnDaemon(Program); break; + default: + _SysDebug("BUGCHECK - Program Type %i unknown", Program->Type); + break; + } + if( !rv ) { + _SysDebug("Respawn failure!"); + // TODO: Remove from list? } +} + +int AddKTerminal(int TerminalID, char **Command) +{ + // TODO: Smarter validation + if( TerminalID < 0 || TerminalID > 7 ) + return -1; - // TODO: Implement message watching - for(;;) sleep(); + tInitProgram *ent = AllocateProgram(Command, PT_KTERM, sizeof(struct sKTerm)); + ent->TypeInfo.KTerm.ID = TerminalID; + + RespawnProgam(ent); + return 0; +} + +int AddSerialTerminal(const char *DevPathSegment, const char *ModeStr, char **Command) +{ + char dbit, parity, sbit; + int baud; + + // Parse mode string + if( sscanf(ModeStr, "%1[78]%1[NOE]%1[012]%*1[bB]%d", &dbit, &parity, &sbit, &baud) != 4 ) { + // Oops? + _SysDebug("Serial mode string is invalid ('%s')", ModeStr); + return -1; + } - return 42; + // Validate baud rate / build mode word + // TODO: Does baud rate need validation? + uint32_t modeword = 0; + modeword |= (dbit == '7' ? 1 : 0) << 0; + modeword |= (parity == 'O' ? 1 : (parity == 'E' ? 2 : 0)) << 1; + modeword |= (sbit - '0') << 3; + + // Create info + const char DEVPREFIX[] = "/Devices/pts/serial"; + int pathlen = sizeof(DEVPREFIX) + strlen(DevPathSegment); + tInitProgram *ent = AllocateProgram(Command, PT_STERM, sizeof(struct sSTerm)+pathlen); + ent->TypeInfo.STerm.FormatBits = modeword; + ent->TypeInfo.STerm.BaudRate = baud; + strcpy(ent->TypeInfo.STerm.Path, DEVPREFIX); + strcat(ent->TypeInfo.STerm.Path, DevPathSegment); + + RespawnProgam(ent); + return 0; +} + +int AddDaemon(char *StdoutPath, char *StderrPath, char **Command) +{ + tInitProgram *ent = AllocateProgram(Command, PT_DAEMON, sizeof(struct sDaemon)); + ent->TypeInfo.Daemon.StdoutPath = StdoutPath; + ent->TypeInfo.Daemon.StderrPath = StderrPath; + + RespawnProgam(ent); + return 0; } + +int SpawnCommand(int c_stdin, int c_stdout, int c_stderr, char **ArgV, const char **EnvP) +{ + int handles[] = {c_stdin, c_stdout, c_stderr}; + + _SysDebug("Spawning '%s'", ArgV[0]); + + if( c_stdin < 0 ) { + _SysDebug("SpawnCommand: stdin is invalid"); + return 1; + } + if( c_stdout < 0 ) { + _SysDebug("SpawnCommand: stdout is invalid"); + return 1; + } + if( c_stderr < 0 ) { + _SysDebug("SpawnCommand: stderr is invalid"); + return 1; + } + + int rv = _SysSpawn(ArgV[0], (const char **)ArgV, EnvP, 3, handles, NULL); + + _SysClose(c_stdin); + if( c_stdout != c_stdin ) + _SysClose(c_stdout); + if( c_stderr != c_stdin && c_stderr != c_stdout ) + _SysClose(c_stderr); + + return rv; +} + +int SpawnKTerm(tInitProgram *Program) +{ + const char fmt[] = "/Devices/pts/vt%i"; + const char *env[] = {"TERM=screen", NULL}; + char path[sizeof(fmt)]; + + snprintf(path, sizeof(path), fmt, Program->TypeInfo.KTerm.ID); + + int in = _SysOpen(path, OPENFLAG_READ); + int out = _SysOpen(path, OPENFLAG_WRITE); + + _SysDebug("Spawning virtual terminal '%s' with term '%s'", + path, Program->Command[0]); + return SpawnCommand(in, out, out, Program->Command, env); +} + +int SpawnSTerm(tInitProgram *Program) +{ + int in = _SysOpen(Program->TypeInfo.STerm.Path, OPENFLAG_READ); + int out = _SysOpen(Program->TypeInfo.STerm.Path, OPENFLAG_WRITE); + + if(in == -1 || out == -1 ) { + _SysDebug("Unable to open serial port '%s'", Program->TypeInfo.STerm.Path); + return -1; + } + + #if 0 + if( _SysIOCtl(in, 0, NULL) != DRV_TYPE_SERIAL ) + { + // Oops? + return -2; + } + _SysIOCtl(in, SERIAL_IOCTL_GETSETBAUD, &Program->TypeInfo.STerm.BaudRate); + _SysIOCtl(in, SERIAL_IOCTL_GETSETFORMAT, &Program->TypeInfo.STerm.FormatBits); + #endif + + _SysDebug("Spawning serial terminal '%s' with term '%s'", + Program->TypeInfo.STerm.Path, Program->Command[0]); + return SpawnCommand(in, out, out, Program->Command, NULL); +} + +int SpawnDaemon(tInitProgram *Program) +{ + int in = _SysOpen("/Devices/null", OPENFLAG_READ); + int out = _SysOpen(Program->TypeInfo.Daemon.StdoutPath, OPENFLAG_WRITE); + int err = _SysOpen(Program->TypeInfo.Daemon.StderrPath, OPENFLAG_WRITE); + + if( in < 0 || out < 0 || err < 0 ) { + perror("SpawnDaemon"); + _SysClose(in); + _SysClose(out); + _SysClose(err); + return -2; + } + + // Log spawn header + { + char buffer[101]; + size_t len = snprintf(buffer, 100, "[%"PRIi64"] init spawning ", _SysTimestamp()); + _SysWrite(out, buffer, len); + for( int i = 0; Program->Command[i]; i ++ ) + { + _SysWrite(out, "'", 1); + _SysWrite(out, Program->Command[i], strlen(Program->Command[i])); + _SysWrite(out, "'", 1); + } + _SysWrite(out, "\n", 1); + } + + return SpawnCommand(in, out, err, Program->Command, NULL); +} +