It's been too long since I committed
[tpg/acess2.git] / Kernel / system.c
1 /*
2  * Acess 2
3  * Architecture Independent System Init
4  * system.c
5  */
6 #define DEBUG   0
7 #include <acess.h>
8
9 #define N_VARIABLES     16
10 #define N_MAX_ARGS      BITS
11
12 // === TYPES ===
13 typedef struct
14 {
15          int    TrueLine;
16          int    nParts;
17         char    **Parts;
18 }       tConfigLine;
19 typedef struct
20 {
21          int    nLines;
22         tConfigLine     Lines[];
23 }       tConfigFile;
24 typedef struct
25 {
26         char    *Name;          // Name
27          int    MinArgs;        // Minimum number of arguments
28          int    MaxArgs;        // Maximum number of arguments
29         Uint    IntArgs;        // Bitmap of arguments that should be treated as integers
30         void    *Func;          // Function pointer
31         Uint    OptDefaults[N_MAX_ARGS];        // Default values for optional arguments
32 }       tConfigCommand;
33
34 // === IMPORTS ===
35 extern void     Arch_LoadBootModules(void);
36 extern int      Modules_LoadBuiltins(void);
37 extern void     Modules_SetBuiltinParams(char *Name, char *ArgString);
38 extern void     Debug_SetKTerminal(char *File);
39
40 // === PROTOTYPES ===
41 void    System_Init(char *Commandline);
42 void    System_ParseCommandLine(char *ArgString);
43 void    System_ExecuteCommandLine(void);
44 void    System_ParseVFS(char *Arg);
45 void    System_ParseModuleArgs(char *Arg);
46 void    System_ParseSetting(char *Arg);
47 void    System_ExecuteScript(void);
48 tConfigFile     *System_Int_ParseFile(char *File);
49
50 // === CONSTANTS ===
51 const tConfigCommand    caConfigCommands[] = {
52         {"module",  1,2, 00, Module_LoadFile, {(Uint)"",0}},    // Load a module from a file
53         {"spawn",   1,1, 00, Proc_Spawn, {0}},          // Spawn a process
54         // --- VFS ---
55         {"mount",   3,4, 00, VFS_Mount, {(Uint)"",0}},          // Mount a device
56         {"symlink", 2,2, 00, VFS_Symlink, {0}}, // Create a Symbolic Link
57         {"mkdir",   1,1, 00, VFS_MkDir, {0}},           // Create a Directory
58         {"open",    1,2, 00, VFS_Open,  {VFS_OPENFLAG_READ,0}}, // Open a file
59         {"close",   1,1, 01, VFS_Close, {0}},   // Close an open file
60         {"ioctl",   3,3, 03, VFS_IOCtl, {0}},   // Call an IOCtl
61         
62         {"", 0,0, 0, NULL, {0}}
63 };
64 #define NUM_CONFIG_COMMANDS     (sizeof(caConfigCommands)/sizeof(caConfigCommands[0]))
65
66 // === GLOBALS ===
67 char    *gsConfigScript = "/Acess/Conf/BootConf.cfg";
68 char    *argv[32];
69  int    argc;
70
71 // === CODE ===
72 void System_Init(char *CommandLine)
73 {
74         // Parse Kernel's Command Line
75         System_ParseCommandLine(CommandLine);
76         
77         // Initialise modules
78         Log_Log("Config", "Initialising builtin modules...");
79         Modules_LoadBuiltins();
80         Arch_LoadBootModules();
81         
82         System_ExecuteCommandLine();
83         
84         // - Execute the Config Script
85         Log_Log("Config", "Executing config script...");
86         System_ExecuteScript();
87         
88         // Set the debug to be echoed to the terminal
89         Log_Log("Config", "Kernel now echoes to VT7 (Ctrl-Alt-F8)");
90         Debug_SetKTerminal("/Devices/VTerm/7");
91 }
92
93 /**
94  * \fn void System_ParseCommandLine(char *ArgString)
95  * \brief Parses the kernel's command line and sets the environment
96  */
97 void System_ParseCommandLine(char *ArgString)
98 {
99          int    i;
100         char    *str;
101         
102         Log_Log("Config", "Kernel Invocation \"%s\"", ArgString);
103         
104         // --- Get Arguments ---
105         str = ArgString;
106         for( argc = 0; argc < 32; argc++ )
107         {
108                 // Eat Whitespace
109                 while(*str == ' ')      str++;
110                 // Check for the end of the string
111                 if(*str == '\0') {      argc--; break;} 
112                 argv[argc] = str;
113                 if(*str == '"') {
114                         while(*str && !(*str == '"' && str[-1] != '\\'))
115                                 str ++;
116                 }
117                 else {
118                         while(*str && *str != ' ')
119                                 str++;
120                 }
121                 if(*str == '\0')        break;  // Check for EOS
122                 *str = '\0';    // Cap off argument string
123                 str ++; // and increment the string pointer
124         }
125         if(argc < 32)
126                 argc ++;        // Count last argument
127         
128         // --- Parse Arguments (Pass 1) ---
129         for( i = 1; i < argc; i++ )
130         {
131                 switch(argv[i][0])
132                 {
133                 // --- VFS ---
134                 // Ignored on this pass
135                 case '/':
136                         break;
137                 
138                 // --- Module Paramaters ---
139                 // -VTerm:Width=640,Height=480,Scrollback=2
140                 case '-':
141                         System_ParseModuleArgs( argv[i] );
142                         break;
143                 // --- Config Options ---
144                 // SCRIPT=/Acess/Conf/BootConf.cfg
145                 default:
146                         System_ParseSetting( argv[i] );
147                         break;
148                 }
149         }
150 }
151
152 void System_ExecuteCommandLine(void)
153 {
154          int    i;
155         for( i = 1; i < argc; i++ )
156         {
157                 switch(argv[i][0])
158                 {
159                 // --- VFS ---
160                 // Mount    /System=ext2:/Devices/ATA/A1
161                 // Symlink  /Acess=/System/Acess2
162                 case '/':
163                         System_ParseVFS( argv[i] );
164                         break;
165                 }
166         }
167 }
168
169 /**
170  * \fn void System_ParseVFS(char *Arg)
171  */
172 void System_ParseVFS(char *Arg)
173 {
174         char    *value;
175          int    fd;
176         
177         value = Arg;
178         // Search for the '=' token
179         while( *value && *value != '=' )
180                 value++;
181         
182         // Check if the equals was found
183         if( *value == '\0' ) {
184                 Log_Warning("Config", "Expected '=' in the string '%s'", Arg);
185                 return ;
186         }
187         
188         // Edit string
189         *value = '\0';  value ++;
190         
191         // Check assignment type
192         // - Symbolic Link <link>=<destination>
193         if(value[0] == '/')
194         {
195                 Log_Log("Config", "Symbolic link '%s' pointing to '%s'", Arg, value);
196                 VFS_Symlink(Arg, value);
197         }
198         // - Mount <mountpoint>=<fs>:<device>
199         else
200         {
201                 char    *dev = value;
202                 // Find colon
203                 while(*dev && *dev != ':')      dev++;
204                 if(*dev) {
205                         *dev = '\0';
206                         dev++;  // Eat ':'
207                 }
208                 // Create Mountpoint
209                 if( (fd = VFS_Open(Arg, 0)) == -1 ) {
210                         Log_Log("Config", "Creating directory '%s'", Arg, value);
211                         VFS_MkDir( Arg );
212                 } else {
213                         VFS_Close(fd);
214                 }
215                 // Mount
216                 Log_Log("Config", "Mounting '%s' to '%s' ('%s')", dev, Arg, value);
217                 VFS_Mount(dev, Arg, value, "");
218         }
219 }
220
221 /**
222  * \biref Parse a module argument string
223  */
224 void System_ParseModuleArgs(char *Arg)
225 {
226         char    *name, *args;
227          int    i;
228         
229         // Remove '-'   
230         name = Arg + 1;
231         
232         // Find the start of the args
233         i = strpos(name, ':');
234         if( i == -1 ) {
235                 Log_Warning("Config", "Module spec with no arguments");
236                 #if 1
237                 return ;
238                 #else
239                 i = strlen(name);
240                 args = name + i;
241                 #endif
242         }
243         else {
244                 name[i] = '\0';
245                 args = name + i + 1;
246         }
247         
248         Log_Log("Config", "Setting boot parameters for '%s' to '%s'", name, args);
249         Modules_SetBuiltinParams(name, args);
250 }
251
252 /**
253  * \fn void System_ParseSetting(char *Arg)
254  */
255 void System_ParseSetting(char *Arg)
256 {
257         char    *value;
258         value = Arg;
259
260         // Search for the '=' token
261         while( *value && *value != '=' )
262                 value++;
263         
264         // Check for boolean/flag (no '=')
265         if(*value == '\0')
266         {
267                 //if(strcmp(Arg, "") == 0) {
268                 //} else {
269                         Log_Warning("Config", "Kernel flag '%s' is not recognised", Arg);
270                 //}
271         }
272         else
273         {
274                 *value = '\0';  // Remove '='
275                 value ++;       // and eat it's position
276                 
277                 if(strcmp(Arg, "SCRIPT") == 0) {
278                         Log_Log("Config", "Config Script: '%s'", value);
279                         if(strlen(value) == 0)
280                                 gsConfigScript = NULL;
281                         else
282                                 gsConfigScript = value;
283                 } else {
284                         Log_Warning("Config", "Kernel config setting '%s' is not recognised", Arg);
285                 }
286                 
287         }
288 }
289
290 /**
291  * \fn void System_ExecuteScript()
292  * \brief Reads and parses the boot configuration script
293  */
294 void System_ExecuteScript(void)
295 {
296          int    fp;
297          int    fLen = 0;
298          int    i, j, k;
299          int    val;
300          int    result = 0;
301          int    variables[N_VARIABLES];
302          int    bReplaced[N_MAX_ARGS];
303         char    *fData;
304         char    *jmpTarget;
305         tConfigFile     *file;
306         tConfigLine     *line;
307         
308         // Open Script
309         fp = VFS_Open(gsConfigScript, VFS_OPENFLAG_READ);
310         if(fp == -1) {
311                 Log_Warning("Config", "Passed script '%s' does not exist", gsConfigScript);
312                 return;
313         }
314         
315         // Get length
316         VFS_Seek(fp, 0, SEEK_END);
317         fLen = VFS_Tell(fp);
318         VFS_Seek(fp, 0, SEEK_SET);
319         // Read into memory buffer
320         fData = malloc(fLen+1);
321         VFS_Read(fp, fLen, fData);
322         fData[fLen] = '\0';
323         VFS_Close(fp);
324         
325         
326         // Parse File
327         file = System_Int_ParseFile(fData);
328         
329         // Parse each line
330         for( i = 0; i < file->nLines; i++ )
331         {
332                 line = &file->Lines[i];
333                 if( line->nParts == 0 ) continue;       // Skip blank
334                 
335                 if(line->Parts[0][0] == ':')    continue;       // Ignore labels
336                 
337                 // Prescan and eliminate variables
338                 for( j = 1; j < line->nParts; j++ ) {
339                         Log_Debug("Config", "Arg #%i is '%s'", j, line->Parts[j]);
340                         bReplaced[j] = 0;
341                         if( line->Parts[j][0] != '$' )  continue;
342                         if( line->Parts[j][1] == '?' ) {
343                                 val = result;
344                         }
345                         else {
346                                 val = atoi( &line->Parts[j][1] );
347                                 if( val < 0 || val > N_VARIABLES )      continue;
348                                 val = variables[ val ];
349                         }
350                         Log_Debug("Config", "Replaced arg %i ('%s') with 0x%x", j, line->Parts[j], val);
351                         line->Parts[j] = malloc( BITS/8+2+1 );
352                         sprintf(line->Parts[j], "0x%x", val);
353                         bReplaced[j] = 1;
354                 }
355                 
356                 for( j = 0; j < NUM_CONFIG_COMMANDS; j++ )
357                 {
358                         Uint    args[N_MAX_ARGS];
359                         if(strcmp(line->Parts[0], caConfigCommands[j].Name) != 0)       continue;
360                         
361                         Log_Debug("Config", "Command '%s', %i args passed", line->Parts[0], line->nParts-1);
362                         
363                         if( line->nParts - 1 < caConfigCommands[j].MinArgs ) {
364                                 Log_Warning("Config",
365                                         "Configuration command '%s' requires at least %i arguments, %i given",
366                                         caConfigCommands[j].Name, caConfigCommands[j].MinArgs, line->nParts-1
367                                         );
368                                 break;
369                         }
370                         
371                         if( line->nParts - 1 > caConfigCommands[j].MaxArgs ) {
372                                 Log_Warning("Config",
373                                         "Configuration command '%s' takes at most %i arguments, %i given",
374                                         caConfigCommands[j].Name, caConfigCommands[j].MaxArgs, line->nParts-1
375                                         );
376                                 break;
377                         }
378                         
379                         for( k = caConfigCommands[j].MaxArgs-1; k > line->nParts - 1; k-- ) {
380                                 args[k] = caConfigCommands[j].OptDefaults[k];
381                         }
382                         
383                         for( k = line->nParts-1; k--; )
384                         {
385                                 if( caConfigCommands[j].IntArgs & (1 << k) ) {
386                                         args[k] = atoi(line->Parts[k+1]);
387                                 }
388                                 else {
389                                         args[k] = (Uint)line->Parts[k+1];
390                                 }
391                                 Log_Debug("Config", "args[%i] = 0x%x", k, args[k]);
392                         }
393                         result = CallWithArgArray(caConfigCommands[j].Func, caConfigCommands[j].MaxArgs, args);
394                         Log_Debug("Config", "result = %i", result);
395                         break;
396                 }
397                 if( j < NUM_CONFIG_COMMANDS )   continue;
398                         
399                 // --- State and Variables ---
400                 if(strcmp(line->Parts[0], "set") == 0)
401                 {
402                          int    to, value;
403                         if( line->nParts-1 != 2 ) {
404                                 Log_Warning("Config", "Configuration command 'set' requires 2 arguments, %i given",
405                                         line->nParts-1);
406                                 continue;
407                         }
408                         
409                         to = atoi(line->Parts[1]);
410                         value = atoi(line->Parts[2]);
411                         
412                         variables[to] = value;
413                         result = value;
414                 }
415                 // if <val1> <op> <val2> <dest>
416                 else if(strcmp(line->Parts[0], "if") == 0)
417                 {
418                         if( line->nParts-1 != 4 ) {
419                                 Log_Warning("Config", "Configuration command 'if' requires 4 arguments, %i given",
420                                         line->nParts-1);
421                         }
422                         
423                         result = atoi(line->Parts[1]);
424                         val = atoi(line->Parts[3]);
425                         
426                         jmpTarget = line->Parts[4];
427                         
428                         Log_Log("Config", "IF 0x%x %s 0x%x THEN GOTO %s",
429                                 result, line->Parts[2], val, jmpTarget);
430                         
431                         if( strcmp(line->Parts[2], "<" ) == 0 ) {
432                                 if( result < val )      goto jumpToLabel;
433                         }
434                         else if( strcmp(line->Parts[2], "<=") == 0 ) {
435                                 if( result <= val )     goto jumpToLabel;
436                         }
437                         else if( strcmp(line->Parts[2], ">" ) == 0 ) {
438                                 if (result > val )      goto jumpToLabel;
439                         }
440                         else if( strcmp(line->Parts[2], ">=") == 0 ) {
441                                 if( result >= val )     goto jumpToLabel;
442                         }
443                         else if( strcmp(line->Parts[2],  "=") == 0 ) {
444                                 if( result == val )     goto jumpToLabel;
445                         }
446                         else if( strcmp(line->Parts[2], "!=") == 0 ) {
447                                 if( result != val )     goto jumpToLabel;
448                         }
449                         else {
450                                 Log_Warning("Config", "Unknown comparision '%s' in `if`", line->Parts[2]);
451                         }
452                         
453                 }
454                 else if(strcmp(line->Parts[0], "goto") == 0) {
455                         if( line->nParts-1 != 1 ) {
456                                 Log_Warning("Config", "Configuration command 'goto' requires 1 arguments, %i given",
457                                         line->nParts-1);
458                         }
459                         jmpTarget = line->Parts[1];
460                 
461                 jumpToLabel:
462                         for( j = 0; j < file->nLines; j ++ )
463                         {
464                                 if(file->Lines[j].nParts == 0)
465                                         continue;
466                                 if(file->Lines[j].Parts[0][0] != ':')
467                                         continue;
468                                 if( strcmp(file->Lines[j].Parts[0]+1, jmpTarget) == 0)
469                                         break;
470                         }
471                         if( j == file->nLines )
472                                 Log_Warning("Config", "Unable to find label '%s'", jmpTarget);
473                         else
474                                 i = j;
475                 }
476                 else {
477                         Log_Warning("Config", "Unknown configuration command '%s' on line %i",
478                                 line->Parts[0],
479                                 line->TrueLine
480                                 );
481                 }
482         }
483         
484         // Clean up after ourselves
485         for( i = 0; i < file->nLines; i++ ) {
486                 if( file->Lines[i].nParts == 0 )        continue;       // Skip blank
487                 for( j = 0; j < file->Lines[i].nParts; j++ ) {
488                         if(IsHeap(file->Lines[i].Parts[j]))
489                                 free(file->Lines[i].Parts[j]);
490                 }
491                 free( file->Lines[i].Parts );
492         }
493         
494         // Free data
495         free( file );
496         free( fData );
497 }
498
499 /**
500  * \brief Parses a config file
501  * \param FileData      Read/Write buffer containing the config file data
502  *                  (will be modified)
503  * \return ::tConfigFile structure that represents the original contents
504  *         of \a FileData
505  */
506 tConfigFile     *System_Int_ParseFile(char *FileData)
507 {
508         char    *ptr;
509         char    *start;
510          int    nLines = 1;
511          int    i, j;
512         tConfigFile     *ret;
513         
514         ENTER("pFileData", FileData);
515         
516         // Prescan and count the number of lines
517         for(ptr = FileData; *ptr; ptr++)
518         {               
519                 if(*ptr != '\n')        continue;
520                 
521                 if(ptr == FileData) {
522                         nLines ++;
523                         continue;
524                 }
525                 
526                 // Escaped EOL
527                 if(ptr[-1] == '\\')     continue;
528                 
529                 nLines ++;
530         }
531         
532         LOG("nLines = %i", nLines);
533         
534         // Ok so we have `nLines` lines, now to allocate our return
535         ret = malloc( sizeof(tConfigFile) + sizeof(tConfigLine)*nLines );
536         ret->nLines = nLines;
537         
538         // Read the file for real
539         for(
540                 ptr = FileData, i = 0;
541                 *ptr;
542                 i++
543                 )
544         {
545                 start = ptr;
546                 
547                 ret->Lines[i].nParts = 0;
548                 
549                 // Count parts
550                 for(;;)
551                 {
552                         // Read leading whitespace
553                         while( *ptr == '\t' || *ptr == ' ' )    ptr++;
554                         
555                         // End of line/file
556                         if( *ptr == '\0' || *ptr == '\n' ) {
557                                 if(*ptr == '\n')        ptr ++;
558                                 break;
559                         }
560                         // Comment
561                         if( *ptr == '#' || *ptr == ';' ) {
562                                 while( *ptr && *ptr != '\n' )   ptr ++;
563                                 if(*ptr == '\n')        ptr ++;
564                                 break;
565                         }
566                         
567                         ret->Lines[i].nParts ++;
568                         // Quoted
569                         if( *ptr == '"' ) {
570                                 ptr ++;
571                                 while( *ptr && !(*ptr == '"' && ptr[-1] == '\\') && *ptr != '\n' )
572                                         ptr++;
573                                 continue;
574                         }
575                         // Unquoted
576                         while( *ptr && !(*ptr == '\t' || *ptr == ' ') && *ptr != '\n' )
577                                 ptr++;
578                 }
579                 
580                 LOG("ret->Lines[%i].nParts = %i", i, ret->Lines[i].nParts);
581                 
582                 if( ret->Lines[i].nParts == 0 ) {
583                         ret->Lines[i].Parts = NULL;
584                         continue;
585                 }
586                 
587                 // Allocate part list
588                 ret->Lines[i].Parts = malloc( sizeof(char*) * ret->Lines[i].nParts );
589                 
590                 // Fill list
591                 for( ptr = start, j = 0; ; j++ )
592                 {
593                         // Read leading whitespace
594                         while( *ptr == '\t' || *ptr == ' ' )    ptr++;
595                         
596                         // End of line/file
597                         if( *ptr == '\0' || *ptr == '\n' ) {
598                                 if(*ptr == '\n')        ptr ++;
599                                 break;
600                         }
601                         // Comment
602                         if( *ptr == '#' || *ptr == ';' ) {
603                                 while( *ptr && *ptr != '\n' )   ptr ++;
604                                 if(*ptr == '\n')        ptr ++;
605                                 break;
606                         }
607                         
608                         ret->Lines[i].Parts[j] = ptr;
609                         
610                         // Quoted
611                         if( *ptr == '"' ) {
612                                 ptr ++;
613                                 ret->Lines[i].Parts[j] = ptr;
614                                 while( *ptr && !(*ptr == '"' && ptr[-1] == '\\') && *ptr != '\n' )
615                                         ptr++;
616                         }
617                         // Unquoted
618                         else {
619                                 while( *ptr != '\t' && *ptr != ' ' && *ptr != '\n' )
620                                         ptr++;
621                         }
622                         
623                         // Break if we have reached NULL
624                         if( *ptr == '\0' ) {
625                                 LOG("ret->Lines[%i].Parts[%i] = '%s'", i, j, ret->Lines[i].Parts[j]);
626                                 break;
627                         }
628                         if( *ptr == '\n' ) {
629                                 *ptr = '\0';
630                                 LOG("ret->Lines[%i].Parts[%i] = '%s'", i, j, ret->Lines[i].Parts[j]);
631                                 ptr ++;
632                                 break;
633                         }
634                         *ptr = '\0';    // Cap off string
635                         LOG("ret->Lines[%i].Parts[%i] = '%s'", i, j, ret->Lines[i].Parts[j]);
636                         ptr ++; // And increment for the next round
637                 }
638         }
639         
640         LEAVE('p', ret);
641         return ret;
642 }

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