Bit of debug cleanup, fixing bugs found on GrUB2
[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 = 0; 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 = 0; i < argc; i++ )
156         {
157                 Log("argv[%i] = '%s'", i, argv[i]);
158                 switch(argv[i][0])
159                 {
160                 // --- VFS ---
161                 // Mount    /System=ext2:/Devices/ATA/A1
162                 // Symlink  /Acess=/System/Acess2
163                 case '/':
164                         System_ParseVFS( argv[i] );
165                         break;
166                 }
167         }
168 }
169
170 /**
171  * \fn void System_ParseVFS(char *Arg)
172  */
173 void System_ParseVFS(char *Arg)
174 {
175         char    *value;
176          int    fd;
177         
178         value = Arg;
179         // Search for the '=' token
180         while( *value && *value != '=' )
181                 value++;
182         
183         // Check if the equals was found
184         if( *value == '\0' ) {
185                 Log_Warning("Config", "Expected '=' in the string '%s'", Arg);
186                 return ;
187         }
188         
189         // Edit string
190         *value = '\0';  value ++;
191         
192         // Check assignment type
193         // - Symbolic Link <link>=<destination>
194         if(value[0] == '/')
195         {
196                 Log_Log("Config", "Symbolic link '%s' pointing to '%s'", Arg, value);
197                 VFS_Symlink(Arg, value);
198         }
199         // - Mount <mountpoint>=<fs>:<device>
200         else
201         {
202                 char    *dev = value;
203                 // Find colon
204                 while(*dev && *dev != ':')      dev++;
205                 if(*dev) {
206                         *dev = '\0';
207                         dev++;  // Eat ':'
208                 }
209                 // Create Mountpoint
210                 if( (fd = VFS_Open(Arg, 0)) == -1 ) {
211                         Log_Log("Config", "Creating directory '%s'", Arg, value);
212                         VFS_MkDir( Arg );
213                 } else {
214                         VFS_Close(fd);
215                 }
216                 // Mount
217                 Log_Log("Config", "Mounting '%s' to '%s' ('%s')", dev, Arg, value);
218                 VFS_Mount(dev, Arg, value, "");
219         }
220 }
221
222 /**
223  * \biref Parse a module argument string
224  */
225 void System_ParseModuleArgs(char *Arg)
226 {
227         char    *name, *args;
228          int    i;
229         
230         // Remove '-'   
231         name = Arg + 1;
232         
233         // Find the start of the args
234         i = strpos(name, ':');
235         if( i == -1 ) {
236                 Log_Warning("Config", "Module spec with no arguments");
237                 #if 1
238                 return ;
239                 #else
240                 i = strlen(name);
241                 args = name + i;
242                 #endif
243         }
244         else {
245                 name[i] = '\0';
246                 args = name + i + 1;
247         }
248         
249         Log_Log("Config", "Setting boot parameters for '%s' to '%s'", name, args);
250         Modules_SetBuiltinParams(name, args);
251 }
252
253 /**
254  * \fn void System_ParseSetting(char *Arg)
255  */
256 void System_ParseSetting(char *Arg)
257 {
258         char    *value;
259         value = Arg;
260
261         // Search for the '=' token
262         while( *value && *value != '=' )
263                 value++;
264         
265         // Check for boolean/flag (no '=')
266         if(*value == '\0')
267         {
268                 //if(strcmp(Arg, "") == 0) {
269                 //} else {
270                         Log_Warning("Config", "Kernel flag '%s' is not recognised", Arg);
271                 //}
272         }
273         else
274         {
275                 *value = '\0';  // Remove '='
276                 value ++;       // and eat it's position
277                 
278                 if(strcmp(Arg, "SCRIPT") == 0) {
279                         Log_Log("Config", "Config Script: '%s'", value);
280                         if(strlen(value) == 0)
281                                 gsConfigScript = NULL;
282                         else
283                                 gsConfigScript = value;
284                 } else {
285                         Log_Warning("Config", "Kernel config setting '%s' is not recognised", Arg);
286                 }
287                 
288         }
289 }
290
291 /**
292  * \fn void System_ExecuteScript()
293  * \brief Reads and parses the boot configuration script
294  */
295 void System_ExecuteScript(void)
296 {
297          int    fp;
298          int    fLen = 0;
299          int    i, j, k;
300          int    val;
301          int    result = 0;
302          int    variables[N_VARIABLES];
303          int    bReplaced[N_MAX_ARGS];
304         char    *fData;
305         char    *jmpTarget;
306         tConfigFile     *file;
307         tConfigLine     *line;
308         
309         // Open Script
310         fp = VFS_Open(gsConfigScript, VFS_OPENFLAG_READ);
311         if(fp == -1) {
312                 Log_Warning("Config", "Passed script '%s' does not exist", gsConfigScript);
313                 return;
314         }
315         
316         // Get length
317         VFS_Seek(fp, 0, SEEK_END);
318         fLen = VFS_Tell(fp);
319         VFS_Seek(fp, 0, SEEK_SET);
320         // Read into memory buffer
321         fData = malloc(fLen+1);
322         VFS_Read(fp, fLen, fData);
323         fData[fLen] = '\0';
324         VFS_Close(fp);
325         
326         
327         // Parse File
328         file = System_Int_ParseFile(fData);
329         
330         // Parse each line
331         for( i = 0; i < file->nLines; i++ )
332         {
333                 line = &file->Lines[i];
334                 if( line->nParts == 0 ) continue;       // Skip blank
335                 
336                 if(line->Parts[0][0] == ':')    continue;       // Ignore labels
337                 
338                 // Prescan and eliminate variables
339                 for( j = 1; j < line->nParts; j++ ) {
340                         Log_Debug("Config", "Arg #%i is '%s'", j, line->Parts[j]);
341                         bReplaced[j] = 0;
342                         if( line->Parts[j][0] != '$' )  continue;
343                         if( line->Parts[j][1] == '?' ) {
344                                 val = result;
345                         }
346                         else {
347                                 val = atoi( &line->Parts[j][1] );
348                                 if( val < 0 || val > N_VARIABLES )      continue;
349                                 val = variables[ val ];
350                         }
351                         Log_Debug("Config", "Replaced arg %i ('%s') with 0x%x", j, line->Parts[j], val);
352                         line->Parts[j] = malloc( BITS/8+2+1 );
353                         sprintf(line->Parts[j], "0x%x", val);
354                         bReplaced[j] = 1;
355                 }
356                 
357                 for( j = 0; j < NUM_CONFIG_COMMANDS; j++ )
358                 {
359                         Uint    args[N_MAX_ARGS];
360                         if(strcmp(line->Parts[0], caConfigCommands[j].Name) != 0)       continue;
361                         
362                         Log_Debug("Config", "Command '%s', %i args passed", line->Parts[0], line->nParts-1);
363                         
364                         if( line->nParts - 1 < caConfigCommands[j].MinArgs ) {
365                                 Log_Warning("Config",
366                                         "Configuration command '%s' requires at least %i arguments, %i given",
367                                         caConfigCommands[j].Name, caConfigCommands[j].MinArgs, line->nParts-1
368                                         );
369                                 break;
370                         }
371                         
372                         if( line->nParts - 1 > caConfigCommands[j].MaxArgs ) {
373                                 Log_Warning("Config",
374                                         "Configuration command '%s' takes at most %i arguments, %i given",
375                                         caConfigCommands[j].Name, caConfigCommands[j].MaxArgs, line->nParts-1
376                                         );
377                                 break;
378                         }
379                         
380                         for( k = caConfigCommands[j].MaxArgs-1; k > line->nParts - 1; k-- ) {
381                                 args[k] = caConfigCommands[j].OptDefaults[k];
382                         }
383                         
384                         for( k = line->nParts-1; k--; )
385                         {
386                                 if( caConfigCommands[j].IntArgs & (1 << k) ) {
387                                         args[k] = atoi(line->Parts[k+1]);
388                                 }
389                                 else {
390                                         args[k] = (Uint)line->Parts[k+1];
391                                 }
392                                 Log_Debug("Config", "args[%i] = 0x%x", k, args[k]);
393                         }
394                         result = CallWithArgArray(caConfigCommands[j].Func, caConfigCommands[j].MaxArgs, args);
395                         Log_Debug("Config", "result = %i", result);
396                         break;
397                 }
398                 if( j < NUM_CONFIG_COMMANDS )   continue;
399                         
400                 // --- State and Variables ---
401                 if(strcmp(line->Parts[0], "set") == 0)
402                 {
403                          int    to, value;
404                         if( line->nParts-1 != 2 ) {
405                                 Log_Warning("Config", "Configuration command 'set' requires 2 arguments, %i given",
406                                         line->nParts-1);
407                                 continue;
408                         }
409                         
410                         to = atoi(line->Parts[1]);
411                         value = atoi(line->Parts[2]);
412                         
413                         variables[to] = value;
414                         result = value;
415                 }
416                 // if <val1> <op> <val2> <dest>
417                 else if(strcmp(line->Parts[0], "if") == 0)
418                 {
419                         if( line->nParts-1 != 4 ) {
420                                 Log_Warning("Config", "Configuration command 'if' requires 4 arguments, %i given",
421                                         line->nParts-1);
422                         }
423                         
424                         result = atoi(line->Parts[1]);
425                         val = atoi(line->Parts[3]);
426                         
427                         jmpTarget = line->Parts[4];
428                         
429                         Log_Log("Config", "IF 0x%x %s 0x%x THEN GOTO %s",
430                                 result, line->Parts[2], val, jmpTarget);
431                         
432                         if( strcmp(line->Parts[2], "<" ) == 0 ) {
433                                 if( result < val )      goto jumpToLabel;
434                         }
435                         else if( strcmp(line->Parts[2], "<=") == 0 ) {
436                                 if( result <= val )     goto jumpToLabel;
437                         }
438                         else if( strcmp(line->Parts[2], ">" ) == 0 ) {
439                                 if (result > val )      goto jumpToLabel;
440                         }
441                         else if( strcmp(line->Parts[2], ">=") == 0 ) {
442                                 if( result >= val )     goto jumpToLabel;
443                         }
444                         else if( strcmp(line->Parts[2],  "=") == 0 ) {
445                                 if( result == val )     goto jumpToLabel;
446                         }
447                         else if( strcmp(line->Parts[2], "!=") == 0 ) {
448                                 if( result != val )     goto jumpToLabel;
449                         }
450                         else {
451                                 Log_Warning("Config", "Unknown comparision '%s' in `if`", line->Parts[2]);
452                         }
453                         
454                 }
455                 else if(strcmp(line->Parts[0], "goto") == 0) {
456                         if( line->nParts-1 != 1 ) {
457                                 Log_Warning("Config", "Configuration command 'goto' requires 1 arguments, %i given",
458                                         line->nParts-1);
459                         }
460                         jmpTarget = line->Parts[1];
461                 
462                 jumpToLabel:
463                         for( j = 0; j < file->nLines; j ++ )
464                         {
465                                 if(file->Lines[j].nParts == 0)
466                                         continue;
467                                 if(file->Lines[j].Parts[0][0] != ':')
468                                         continue;
469                                 if( strcmp(file->Lines[j].Parts[0]+1, jmpTarget) == 0)
470                                         break;
471                         }
472                         if( j == file->nLines )
473                                 Log_Warning("Config", "Unable to find label '%s'", jmpTarget);
474                         else
475                                 i = j;
476                 }
477                 else {
478                         Log_Warning("Config", "Unknown configuration command '%s' on line %i",
479                                 line->Parts[0],
480                                 line->TrueLine
481                                 );
482                 }
483         }
484         
485         // Clean up after ourselves
486         for( i = 0; i < file->nLines; i++ ) {
487                 if( file->Lines[i].nParts == 0 )        continue;       // Skip blank
488                 for( j = 0; j < file->Lines[i].nParts; j++ ) {
489                         if(IsHeap(file->Lines[i].Parts[j]))
490                                 free(file->Lines[i].Parts[j]);
491                 }
492                 free( file->Lines[i].Parts );
493         }
494         
495         // Free data
496         free( file );
497         free( fData );
498 }
499
500 /**
501  * \brief Parses a config file
502  * \param FileData      Read/Write buffer containing the config file data
503  *                  (will be modified)
504  * \return ::tConfigFile structure that represents the original contents
505  *         of \a FileData
506  */
507 tConfigFile     *System_Int_ParseFile(char *FileData)
508 {
509         char    *ptr;
510         char    *start;
511          int    nLines = 1;
512          int    i, j;
513         tConfigFile     *ret;
514         
515         ENTER("pFileData", FileData);
516         
517         // Prescan and count the number of lines
518         for(ptr = FileData; *ptr; ptr++)
519         {               
520                 if(*ptr != '\n')        continue;
521                 
522                 if(ptr == FileData) {
523                         nLines ++;
524                         continue;
525                 }
526                 
527                 // Escaped EOL
528                 if(ptr[-1] == '\\')     continue;
529                 
530                 nLines ++;
531         }
532         
533         LOG("nLines = %i", nLines);
534         
535         // Ok so we have `nLines` lines, now to allocate our return
536         ret = malloc( sizeof(tConfigFile) + sizeof(tConfigLine)*nLines );
537         ret->nLines = nLines;
538         
539         // Read the file for real
540         for(
541                 ptr = FileData, i = 0;
542                 *ptr;
543                 i++
544                 )
545         {
546                 start = ptr;
547                 
548                 ret->Lines[i].nParts = 0;
549                 
550                 // Count parts
551                 for(;;)
552                 {
553                         // Read leading whitespace
554                         while( *ptr == '\t' || *ptr == ' ' )    ptr++;
555                         
556                         // End of line/file
557                         if( *ptr == '\0' || *ptr == '\n' ) {
558                                 if(*ptr == '\n')        ptr ++;
559                                 break;
560                         }
561                         // Comment
562                         if( *ptr == '#' || *ptr == ';' ) {
563                                 while( *ptr && *ptr != '\n' )   ptr ++;
564                                 if(*ptr == '\n')        ptr ++;
565                                 break;
566                         }
567                         
568                         ret->Lines[i].nParts ++;
569                         // Quoted
570                         if( *ptr == '"' ) {
571                                 ptr ++;
572                                 while( *ptr && !(*ptr == '"' && ptr[-1] == '\\') && *ptr != '\n' )
573                                         ptr++;
574                                 continue;
575                         }
576                         // Unquoted
577                         while( *ptr && !(*ptr == '\t' || *ptr == ' ') && *ptr != '\n' )
578                                 ptr++;
579                 }
580                 
581                 LOG("ret->Lines[%i].nParts = %i", i, ret->Lines[i].nParts);
582                 
583                 if( ret->Lines[i].nParts == 0 ) {
584                         ret->Lines[i].Parts = NULL;
585                         continue;
586                 }
587                 
588                 // Allocate part list
589                 ret->Lines[i].Parts = malloc( sizeof(char*) * ret->Lines[i].nParts );
590                 
591                 // Fill list
592                 for( ptr = start, j = 0; ; j++ )
593                 {
594                         // Read leading whitespace
595                         while( *ptr == '\t' || *ptr == ' ' )    ptr++;
596                         
597                         // End of line/file
598                         if( *ptr == '\0' || *ptr == '\n' ) {
599                                 if(*ptr == '\n')        ptr ++;
600                                 break;
601                         }
602                         // Comment
603                         if( *ptr == '#' || *ptr == ';' ) {
604                                 while( *ptr && *ptr != '\n' )   ptr ++;
605                                 if(*ptr == '\n')        ptr ++;
606                                 break;
607                         }
608                         
609                         ret->Lines[i].Parts[j] = ptr;
610                         
611                         // Quoted
612                         if( *ptr == '"' ) {
613                                 ptr ++;
614                                 ret->Lines[i].Parts[j] = ptr;
615                                 while( *ptr && !(*ptr == '"' && ptr[-1] == '\\') && *ptr != '\n' )
616                                         ptr++;
617                         }
618                         // Unquoted
619                         else {
620                                 while( *ptr != '\t' && *ptr != ' ' && *ptr != '\n' )
621                                         ptr++;
622                         }
623                         
624                         // Break if we have reached NULL
625                         if( *ptr == '\0' ) {
626                                 LOG("ret->Lines[%i].Parts[%i] = '%s'", i, j, ret->Lines[i].Parts[j]);
627                                 break;
628                         }
629                         if( *ptr == '\n' ) {
630                                 *ptr = '\0';
631                                 LOG("ret->Lines[%i].Parts[%i] = '%s'", i, j, ret->Lines[i].Parts[j]);
632                                 ptr ++;
633                                 break;
634                         }
635                         *ptr = '\0';    // Cap off string
636                         LOG("ret->Lines[%i].Parts[%i] = '%s'", i, j, ret->Lines[i].Parts[j]);
637                         ptr ++; // And increment for the next round
638                 }
639         }
640         
641         LEAVE('p', ret);
642         return ret;
643 }

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