Merge branch 'master' of github.com:thepowersgang/acess2
[tpg/acess2.git] / Usermode / Applications / init_src / main.c
1 /*
2  * Acess2 System Init Task
3  */
4 #include <acess/sys.h>
5 #include <stdlib.h>
6 #include <stdio.h>
7 #include <string.h>
8 #include "common.h"
9 #include <ctype.h>
10
11 // === CONSTANTS ===
12 #define DEFAULT_SHELL   "/Acess/SBin/login"
13 #define INITTAB_FILE    "/Acess/Conf/inittab"
14
15 #define ARRAY_SIZE(x)   ((sizeof(x))/(sizeof((x)[0])))
16
17 // === PROTOTYPES ===
18  int    ProcessInittab(const char *Path);
19 char    *ReadQuotedString(FILE *FP);
20 char    **ReadCommand(FILE *FP);
21 void    FreeCommand(char **Command);
22
23 tInitProgram    *AllocateProgram(char **Command, enum eProgType Type, size_t ExtraSpace);
24 void    RespawnProgam(tInitProgram *Program);
25
26  int    AddKTerminal(int TerminalSpec, char **Command);
27  int    AddSerialTerminal(const char *DevPathSegment, const char *ModeStr, char **Command);
28  int    AddDaemon(char *StdoutPath, char *StderrPath, char **Command);
29
30  int    SpawnCommand(int c_stdin, int c_stdout, int c_stderr, char **ArgV);
31  int    SpawnKTerm(tInitProgram *Program);
32  int    SpawnSTerm(tInitProgram *Program);
33  int    SpawnDaemon(tInitProgram *Program);
34
35 // === GLOBALS ===
36 const char      *gsInittabPath = INITTAB_FILE;
37 tInitProgram    *gpInitPrograms;
38
39 // === CODE ===
40 /**
41  * \fn int main(int argc, char *argv[])
42  * \brief Entrypoint
43  */
44 int main(int argc, char *argv[])
45 {
46         // Parse commandline args
47         // TODO: cmdline
48
49         // TODO: Behave differently if not invoked as first process?
50
51         // Begin init process
52         if( ProcessInittab(gsInittabPath) != 0 )
53         {
54                 // No inittab file found, default to:
55                 _SysDebug("inittab '%s' is invalid, falling back to one VT", gsInittabPath);
56                 
57                 #if 1
58                 for( ;; )
59                 {
60                         int pid = SpawnCommand(0, 1, 1, (char *[]){DEFAULT_SHELL, NULL});
61                         // TODO: Detect errors
62                         _SysWaitTID(pid, NULL);
63                 }
64                 #else
65                 return 41;
66                 #endif
67         }
68
69         // TODO: Implement message watching
70         for(;;)
71         {
72                  int    pid, status;
73                 pid = _SysWaitTID(-1, &status);
74                 _SysDebug("PID %i died, looking for respawn entry", pid);
75         }
76         
77         return 42;
78 }
79
80 char *ReadQuotedString(FILE *FP)
81 {
82         char    ch;
83         
84         while( isblank(ch = fgetc(FP)) )
85                 ;
86
87         if( ch == '\n' ) {
88                 return NULL;
89         }
90
91         char    *retstr = NULL;
92          int    mode = 0;
93          int    pos = 0, space = 0;
94         for( ; !feof(FP); ch = fgetc(FP) )
95         {
96                  int    skip;
97                 skip = 1;
98                 if( mode & 4 ) {
99                         // Add escaped/special character
100                         skip = 0;
101                         mode &= 3;
102                 }
103                 else if( mode == 0 )
104                 {
105                         if( isspace(ch) ) {
106                                 fseek(FP, -1, SEEK_CUR);
107                                 break;
108                         }
109                         else if( ch == '\\' )
110                                 mode |= 4;
111                         else if( ch == '"' )
112                                 mode = 1;
113                         else if( ch == '\'')
114                                 mode = 2;
115                         else {
116                                 // Add character
117                                 skip = 0;
118                         }
119                 }
120                 // Double-quoted string
121                 else if( mode == 1 )
122                 {
123                         if( ch == '"' )
124                                 mode = 0;
125                         else if( ch == '\\' )
126                                 mode |= 4;
127                         else {
128                                 // Add character
129                                 skip = 0;
130                         }
131                 }
132                 // Single-quoted string
133                 else if( mode == 2 )
134                 {
135                         if( ch == '\'' )
136                                 mode = 0;
137                         else if( ch == '\\' )
138                                 mode |= 4;
139                         else {
140                                 // Add character
141                                 skip = 0;
142                         }
143                 }
144         
145                 if( !skip )
146                 {
147                         if( pos == space ) {
148                                 space += 32;
149                                 void *tmp = realloc(retstr, space+1);
150                                 if( !tmp ) {
151                                         _SysDebug("ReadQuotedString - realloc(%i) failure", space+1);
152                                         free(retstr);
153                                         return NULL;
154                                 }
155                                 retstr = tmp;
156                         }
157                         retstr[pos++] = ch;
158                 }
159         }
160         if( retstr )
161                 retstr[pos] = '\0';
162         return retstr;
163 }
164
165 char **ReadCommand(FILE *FP)
166 {
167         const int       space = 8;
168          int    pos = 0;
169         char    **ret = malloc(space*sizeof(char*));
170         char    *arg;
171         do {
172                 arg = ReadQuotedString(FP);
173                 if(arg == NULL)
174                         break;
175                 if( pos == space - 1 ) {
176                         _SysDebug("Too many arguments %i", pos);
177                         ret[pos] = NULL;
178                         FreeCommand(ret);
179                         return NULL;
180                 }
181                 ret[pos++] = arg;
182         } while(arg != NULL);
183         
184         if( pos == 0 )
185         {
186                 free(ret);
187                 return NULL;
188         }
189         ret[pos] = NULL;
190         return ret;
191 }
192
193 void FreeCommand(char **Command)
194 {
195         int pos = 0;
196         while( Command[pos] )
197         {
198                 free(Command[pos]);
199                 pos ++;
200         }
201         free(Command);
202 }
203
204 int ProcessInittab(const char *Path)
205 {
206          int    line_num = 0;
207         FILE    *fp = fopen(Path, "r");
208
209         if( !fp )
210                 return 1;       
211
212         while(!feof(fp))
213         {
214                 char cmdbuf[64+1];
215                 
216                 line_num ++;
217
218                  int    rv;
219                 if( (rv = fscanf(fp, "%64s%*[ \t]", cmdbuf)) != 1 ) {
220                         _SysDebug("fscanf rv %i != exp 1", rv);
221                         break;
222                 }
223                 
224                 // Clear comments
225                 if( cmdbuf[0] == '#' ) {
226                         while( !feof(fp) && fgetc(fp) != '\n' )
227                                 ;
228                         continue ;
229                 }
230                 if( cmdbuf[0] == '\0' ) {
231                         char    ch = fgetc(fp);
232                         if( ch != '\n' && ch != -1 ) {
233                                 fclose(fp);
234                                 _SysDebug("Unexpected char 0x%x, expecting EOL", ch);
235                                 return 2;       // Unexpected character?
236                         }
237                         continue ;
238                 }
239
240                 // Check commands
241                 if( strcmp(cmdbuf, "ktty") == 0 ) {
242                         // ktty <ID> <command...>
243                         // - Spins off a console on the specified kernel TTY
244                          int    id = 0;
245                         if( fscanf(fp, "%d ", &id) != 1 ) {
246                                 _SysDebug("init[ktty] - TTY ID read failed");
247                                 goto lineError;
248                         }
249                         char    **command = ReadCommand(fp);
250                         if( !command ) {
251                                 _SysDebug("init[ktty] - Command read failure");
252                                 goto lineError;
253                         }
254                         AddKTerminal(id, command);
255                         free(command);
256                 }
257                 else if(strcmp(cmdbuf, "stty") == 0 ) {
258                         // stty <devpath> [78][NOE][012][bB]<baud> <command...>
259                         char    path_seg[32+1];
260                         char    modespec[4+6+1];
261                         if( fscanf(fp, "%32s %10s ", path_seg, modespec) != 2 ) {
262                                 goto lineError;
263                         }
264                         char **command = ReadCommand(fp);
265                         if( !command )
266                                 goto lineError;
267                         AddSerialTerminal(path_seg, modespec, command);
268                 }
269                 else if(strcmp(cmdbuf, "daemon") == 0 ) {
270                         // daemon <stdout> <stderr> <command...>
271                         // - Runs a daemon (respawning) that logs to the specified files
272                         // - Will append a header whenever the daemon starts
273                         char    *stdout_path = ReadQuotedString(fp);
274                         if( !stdout_path )
275                                 goto lineError;
276                         char    *stderr_path = ReadQuotedString(fp);
277                         if( !stderr_path )
278                                 goto lineError;
279                         char    **command = ReadCommand(fp);
280                         if( !command )
281                                 goto lineError;
282                         
283                         AddDaemon(stdout_path, stderr_path, command);
284                 }
285                 else if(strcmp(cmdbuf, "exec") == 0 ) {
286                         // exec <command...>
287                         // - Runs a command and waits for it to complete before continuing
288                         // - NOTE: No other commands will respawn while this is running
289                         char **command = ReadCommand(fp);
290                         if(!command)
291                                 goto lineError;
292
293                         int handles[] = {0, 1, 2};
294                         int pid = _SysSpawn(command[0], (const char **)command, NULL, 3, handles, NULL);
295                         int retstatus;
296                         _SysWaitTID(pid, &retstatus);
297                         _SysDebug("Command '%s' returned %i", command[0], retstatus);
298
299                         FreeCommand(command);
300                 }
301                 else {
302                         // Unknown command.
303                         _SysDebug("Unknown command '%s'", cmdbuf);
304                         goto lineError;
305                 }
306                 fscanf(fp, " ");
307                 continue;
308         lineError:
309                 _SysDebug("label lineError: goto'd - line %i, cmdbuf='%s'", line_num, cmdbuf);
310                 while( !feof(fp) && fgetc(fp) != '\n' )
311                         ;
312                 continue ;
313         }
314
315         fclose(fp);
316
317         if( gpInitPrograms == NULL )
318                 return 2;
319
320         return 0;
321 }
322
323 tInitProgram *AllocateProgram(char **Command, enum eProgType Type, size_t ExtraSpace)
324 {
325         tInitProgram    *ret;
326         
327         ret = malloc( sizeof(tInitProgram) - sizeof(union uProgTypes) + ExtraSpace );
328         ret->Next = NULL;
329         ret->CurrentPID = 0;
330         ret->Type = Type;
331         ret->Command = Command;
332         
333         // Append
334         ret->Next = gpInitPrograms;
335         gpInitPrograms = ret;
336         
337         return ret;
338 }
339
340 void RespawnProgam(tInitProgram *Program)
341 {
342          int    rv = 0;
343         switch(Program->Type)
344         {
345         case PT_KTERM:  rv = SpawnKTerm(Program);       break;
346         case PT_STERM:  rv = SpawnSTerm(Program);       break;
347         case PT_DAEMON: rv = SpawnDaemon(Program);      break;
348         default:
349                 _SysDebug("BUGCHECK - Program Type %i unknown", Program->Type);
350                 break;
351         }
352         if( !rv ) {
353                 _SysDebug("Respawn failure!");
354                 // TODO: Remove from list?
355         }
356 }
357
358 int AddKTerminal(int TerminalID, char **Command)
359 {
360         // TODO: Smarter validation
361         if( TerminalID < 0 || TerminalID > 7 )
362                 return -1;
363         
364         tInitProgram    *ent = AllocateProgram(Command, PT_KTERM, sizeof(struct sKTerm));
365         ent->TypeInfo.KTerm.ID = TerminalID;
366
367         RespawnProgam(ent);
368         return 0;
369 }
370
371 int AddSerialTerminal(const char *DevPathSegment, const char *ModeStr, char **Command)
372 {
373         char    dbit, parity, sbit;
374          int    baud;
375
376         // Parse mode string
377         if( sscanf(ModeStr, "%1[78]%1[NOE]%1[012]%*1[bB]%d", &dbit, &parity, &sbit, &baud) != 4 ) {
378                 // Oops?
379                 _SysDebug("Serial mode string is invalid ('%s')", ModeStr);
380                 return -1;
381         }
382         
383         // Validate baud rate / build mode word
384         // TODO: Does baud rate need validation?
385         uint32_t        modeword = 0;
386         modeword |= (dbit == '7' ? 1 : 0) << 0;
387         modeword |= (parity == 'O' ? 1 : (parity == 'E' ? 2 : 0)) << 1;
388         modeword |= (sbit - '0') << 3;
389         
390         // Create info
391         const char DEVPREFIX[] = "/Devices/";
392         int pathlen = sizeof(DEVPREFIX) + strlen(DevPathSegment);
393         tInitProgram    *ent = AllocateProgram(Command, PT_STERM, sizeof(struct sSTerm)+pathlen);
394         ent->TypeInfo.STerm.FormatBits = modeword;
395         ent->TypeInfo.STerm.BaudRate = baud;
396         strcpy(ent->TypeInfo.STerm.Path, DEVPREFIX);
397         strcat(ent->TypeInfo.STerm.Path, DevPathSegment);
398
399         RespawnProgam(ent);
400         return 0;
401 }
402
403 int AddDaemon(char *StdoutPath, char *StderrPath, char **Command)
404 {
405         tInitProgram    *ent = AllocateProgram(Command, PT_DAEMON, sizeof(struct sDaemon));
406         ent->TypeInfo.Daemon.StdoutPath = StdoutPath;
407         ent->TypeInfo.Daemon.StderrPath = StderrPath;
408         
409         RespawnProgam(ent);
410         return 0;
411 }
412
413 int SpawnCommand(int c_stdin, int c_stdout, int c_stderr, char **ArgV)
414 {
415          int    handles[] = {c_stdin, c_stdout, c_stderr};
416
417         int rv = _SysSpawn(ArgV[0], (const char **)ArgV, NULL, 3, handles, NULL);
418
419         _SysClose(c_stdin);
420         if( c_stdout != c_stdin )
421                 _SysClose(c_stdout);
422         if( c_stderr != c_stdin && c_stderr != c_stdout )
423                 _SysClose(c_stderr);
424
425         return rv;
426 }
427
428 int SpawnKTerm(tInitProgram *Program)
429 {
430         const char fmt[] = "/Devices/VTerm/%i";
431         char    path[sizeof(fmt)];
432         
433         snprintf(path, sizeof(path), fmt, Program->TypeInfo.KTerm.ID);
434         
435          int    in = _SysOpen(path, OPENFLAG_READ);
436          int    out = _SysOpen(path, OPENFLAG_WRITE);
437         
438         return SpawnCommand(in, out, out, Program->Command);
439 }
440
441 int SpawnSTerm(tInitProgram *Program)
442 {
443          int    in = _SysOpen(Program->TypeInfo.STerm.Path, OPENFLAG_READ);
444          int    out = _SysOpen(Program->TypeInfo.STerm.Path, OPENFLAG_WRITE);
445
446         if(in == -1 || out == -1 ) {
447                 _SysDebug("Unable to open serial '%s' for '%s'", Program->TypeInfo.STerm.Path, Program->Command);
448                 return -1;
449         }
450
451         #if 0
452         if( _SysIOCtl(in, 0, NULL) != DRV_TYPE_SERIAL )
453         {
454                 // Oops?
455                 return -2;
456         }
457         _SysIOCtl(in, SERIAL_IOCTL_GETSETBAUD, &Program->TypeInfo.STerm.BaudRate);
458         _SysIOCtl(in, SERIAL_IOCTL_GETSETFORMAT, &Program->TypeInfo.STerm.FormatBits);
459         #endif
460
461         return SpawnCommand(in, out, out, Program->Command);
462 }
463
464 int SpawnDaemon(tInitProgram *Program)
465 {
466          int    in = _SysOpen("/Devices/null", OPENFLAG_READ);
467          int    out = _SysOpen(Program->TypeInfo.Daemon.StdoutPath, OPENFLAG_WRITE);
468          int    err = _SysOpen(Program->TypeInfo.Daemon.StderrPath, OPENFLAG_WRITE);
469         
470         if( in == -1 || out == -1 || err == -1 ) {
471                 _SysClose(in);
472                 _SysClose(out);
473                 _SysClose(err);
474                 return -2;
475         }
476         
477         // Log spawn header
478         {
479                 char    buffer[101];
480                 size_t len = snprintf(buffer, 100, "[%i] init spawning '%s'\n", _SysTimestamp(), Program->Command);
481                 _SysWrite(out, buffer, len);
482         }
483         
484         return SpawnCommand(in, out, err, Program->Command);
485 }
486

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