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

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