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

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