Usermode/AxWin3 - Clean up unneeded (and silly) log message
[tpg/acess2.git] / Usermode / Applications / init_src / main.c
index c18e5f8..73411b9 100644 (file)
@@ -4,16 +4,38 @@
 #include <acess/sys.h>
 #include <stdlib.h>
 #include <stdio.h>
-//#include "common.h"
+#include <string.h>
+#include "common.h"
+#include <ctype.h>
+#include <inttypes.h>
 
 // === CONSTANTS ===
-#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 ===
 /**
  */
 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);
+       }
        
-       // - Parse init script
+       return 42;
+}
+
+char *ReadQuotedString(FILE *FP)
+{
+       char    ch;
        
-       // - Start virtual terminals
-       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 )
+               {
+                       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 )
                {
-                       termpath[sizeof(DEFAULT_TERMINAL)-2] = '0' + i;
+                       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 <ID> <command...>
+                       // - 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 <devpath> [78][NOE][012][bB]<baud> <command...>
+                       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 <stdout> <stderr> <command...>
+                       // - 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 <command...>
+                       // - 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;
        
-       // TODO: Implement message watching
-       for(;;) sleep();
+       ret = malloc( sizeof(tInitProgram) - sizeof(union uProgTypes) + ExtraSpace );
+       ret->Next = NULL;
+       ret->CurrentPID = 0;
+       ret->Type = Type;
+       ret->Command = Command;
        
-       return 42;
+       // 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;
+       
+       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;
+       }
+       
+       // 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);
 }
 

UCC git Repository :: git.ucc.asn.au