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

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