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

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