Revamped the config file parsing to be more resiliant to errors.
[tpg/acess2.git] / Kernel / system.c
1 /*
2  * Acess 2
3  * Architecture Independent System Init
4  * system.c
5  */
6 #include <common.h>
7
8 // === TYPES ===
9 typedef struct
10 {
11          int    TrueLine;
12          int    nParts;
13         char    **Parts;
14 }       tConfigLine;
15 typedef struct
16 {
17          int    nLines;
18         tConfigLine     Lines[];
19 }       tConfigFile;
20
21 // === IMPORTS ===
22 extern int      Modules_LoadBuiltins();
23 extern int      PCI_Install();
24 extern void     DMA_Install();
25 extern void     Debug_SetKTerminal(char *File);
26 extern void     StartupPrint(char *Str);
27
28 // === PROTOTYPES ===
29 void    System_Init(char *ArgString);
30 void    System_ParseCommandLine(char *ArgString);
31 void    System_ParseVFS(char *Arg);
32 void    System_ParseSetting(char *Arg);
33 void    System_ExecuteScript();
34 tConfigFile     *System_Int_ParseFile(char *File);
35
36 // === GLOBALS ===
37 char    *gsConfigScript = "/Acess/Conf/BootConf.cfg";
38
39 // === CODE ===
40 void System_Init(char *ArgString)
41 {
42         // - Start Builtin Drivers & Filesystems
43         StartupPrint("Scanning PCI Bus...");
44         PCI_Install();
45         StartupPrint("Loading DMA...");
46         DMA_Install();
47         StartupPrint("Loading staticly compiled modules...");
48         Modules_LoadBuiltins();
49         
50         // Set the debug to be echoed to the terminal
51         StartupPrint("Kernel now echoes to VT6 (Ctrl-Alt-F7)");
52         Debug_SetKTerminal("/Devices/VTerm/6");
53         
54         // - Parse Kernel's Command Line
55         System_ParseCommandLine(ArgString);
56         
57         // - Execute the Config Script
58         Log("Executing config script...");
59         System_ExecuteScript();
60 }
61
62 /**
63  * \fn void System_ParseCommandLine(char *ArgString)
64  * \brief Parses the kernel's command line and sets the environment
65  */
66 void System_ParseCommandLine(char *ArgString)
67 {
68         char    *argv[32];
69          int    argc;
70          int    i;
71         char    *str;
72         
73         Log("Kernel Command Line: \"%s\"", ArgString);
74         
75         // --- Get Arguments ---
76         str = ArgString;
77         for( argc = 0; argc < 32; argc++ )
78         {
79                 while(*str == ' ')      str++;  // Eat Whitespace
80                 if(*str == '\0') {      argc--; break;} // End of string
81                 argv[argc] = str;
82                 while(*str && *str != ' ')
83                 {
84                         /*if(*str == '"') {
85                                 while(*str && !(*str == '"' && str[-1] != '\\'))
86                                         str ++;
87                         }*/
88                         str++;
89                 }
90                 if(*str == '\0')        break;  // End of string
91                 *str = '\0';    // Cap off argument string
92                 str ++; // and increment the string pointer
93         }
94         if(argc < 32)
95                 argc ++;        // Count last argument
96         
97         // --- Parse Arguments ---
98         for( i = 1; i < argc; i++ )
99         {
100                 if( argv[i][0] == '/' )
101                         System_ParseVFS( argv[i] );
102                 else
103                         System_ParseSetting( argv[i] );
104         }
105 }
106
107 /**
108  * \fn void System_ParseVFS(char *Arg)
109  */
110 void System_ParseVFS(char *Arg)
111 {
112         char    *value;
113          int    fd;
114         
115         value = Arg;
116         // Search for the '=' token
117         while( *value && *value != '=' )
118                 value++;
119         
120         // Check if the equals was found
121         if( *value == '\0' ) {
122                 Warning("Expected '=' in the string '%s'", Arg);
123                 return ;
124         }
125         
126         // Edit string
127         *value = '\0';  value ++;
128         
129         // Check assignment type
130         // - Symbolic Link <link>=<destination>
131         if(value[0] == '/')
132         {
133                 Log("Symbolic link '%s' pointing to '%s'", Arg, value);
134                 VFS_Symlink(Arg, value);
135         }
136         // - Mount <mountpoint>=<fs>:<device>
137         else
138         {
139                 char    *dev = value;
140                 // Find colon
141                 while(*dev && *dev != ':')      dev++;
142                 if(*dev) {
143                         *dev = '\0';
144                         dev++;  // Eat ':'
145                 }
146                 // Create Mountpoint
147                 if( (fd = VFS_Open(Arg, 0)) == -1 ) {
148                         Log("Creating directory '%s'", Arg, value);
149                         VFS_MkDir( Arg );
150                 } else {
151                         VFS_Close(fd);
152                 }
153                 // Mount
154                 Log("Mounting '%s' to '%s' ('%s')", dev, Arg, value);
155                 VFS_Mount(dev, Arg, value, "");
156         }
157 }
158
159 /**
160  * \fn void System_ParseSetting(char *Arg)
161  */
162 void System_ParseSetting(char *Arg)
163 {
164         char    *value;
165         value = Arg;
166
167         // Search for the '=' token
168         while( *value && *value != '=' )
169                 value++;
170         
171         // Check for boolean/flag (no '=')
172         if(*value == '\0')
173         {
174                 if(strcmp(Arg, "") == 0) {
175                 } else {
176                         Warning("Kernel flag '%s' is not recognised", Arg);
177                 }
178         }
179         else
180         {
181                 *value = '\0';  // Remove '='
182                 value ++;       // and eat it's position
183                 
184                 if(strcmp(Arg, "SCRIPT") == 0) {
185                         Log("Config Script: '%s'", value);
186                         gsConfigScript = value;
187                 } else {
188                         Warning("Kernel config setting '%s' is not recognised", Arg);
189                 }
190                 
191         }
192 }
193
194 /**
195  * \fn void System_ExecuteScript()
196  */
197 void System_ExecuteScript()
198 {
199          int    fp;
200          int    fLen = 0;
201          int    i = 0;
202         char    *fData;
203         tConfigFile     *file;
204         tConfigLine     *line;
205         
206         // Open Script
207         fp = VFS_Open(gsConfigScript, VFS_OPENFLAG_READ);
208         if(fp == -1) {
209                 Warning("[CFG] Passed script '%s' does not exist", gsConfigScript);
210                 return;
211         }
212         
213         // Read into memory buffer
214         VFS_Seek(fp, 0, SEEK_END);
215         fLen = VFS_Tell(fp);
216         VFS_Seek(fp, 0, SEEK_SET);
217         fData = malloc(fLen+1);
218         VFS_Read(fp, fLen, fData);
219         fData[fLen] = '\0';
220         VFS_Close(fp);
221         
222         
223         
224         // Parse File
225         file = System_Int_ParseFile(fData);
226         
227         // Loop lines
228         for( i = 0; i < file->nLines; i++ )
229         {
230                 line = &file->Lines[i];
231                 if( line->nParts == 0 ) continue;       // Skip blank
232                 
233                 // Mount
234                 if( strcmp(line->Parts[0], "mount") == 0 ) {
235                         if( line->nParts != 4 ) {
236                                 Warning("Configuration command 'mount' requires 3 arguments, %i given",
237                                         line->nParts-1);
238                                 continue;
239                         }
240                         //Log("[CFG ] Mount '%s' to '%s' (%s)",
241                         //      line->Parts[1], line->Parts[2], line->Parts[3]);
242                         //! \todo Use an optional 4th argument for the options string
243                         VFS_Mount(line->Parts[1], line->Parts[2], line->Parts[3], "");
244                 }
245                 // Module
246                 else if(strcmp(line->Parts[0], "module") == 0) {
247                         if( line->nParts < 2 || line->nParts > 3 ) {
248                                 Warning("Configuration command 'module' requires 1 or 2 arguments, %i given",
249                                         line->nParts-1);
250                                 continue;
251                         }
252                         if( line->nParts == 3 )
253                                 Module_LoadFile(line->Parts[1], line->Parts[2]);
254                         else
255                                 Module_LoadFile(line->Parts[1], "");
256                 }
257                 // UDI Module
258                 else if(strcmp(line->Parts[0], "udimod") == 0) {
259                         if( line->nParts != 2 ) {
260                                 Warning("Configuration command 'udimod' requires 1 argument, %i given",
261                                         line->nParts-1);
262                                 continue;
263                         }
264                         Log("[CFG  ] Load UDI Module '%s'", line->Parts[1]);
265                         Module_LoadFile(line->Parts[1], "");
266                 }
267                 // EDI Module
268                 else if(strcmp(line->Parts[0], "edimod") == 0) {
269                         if( line->nParts != 2 ) {
270                                 Warning("Configuration command 'edimod' requires 1 argument, %i given",
271                                         line->nParts-1);
272                                 continue;
273                         }
274                         Log("[CFG  ] Load EDI Module '%s'", line->Parts[1]);
275                         Module_LoadFile(line->Parts[1], "");
276                 }
277                 // Symbolic Link
278                 else if(strcmp(line->Parts[0], "symlink") == 0) {
279                         if( line->nParts != 3 ) {
280                                 Warning("Configuration command 'symlink' requires 2 arguments, %i given",
281                                         line->nParts-1);
282                                 continue;
283                         }
284                         Log("[CFG  ] Symlink '%s' pointing to '%s'",
285                                 line->Parts[1], line->Parts[2]);
286                         VFS_Symlink(line->Parts[1], line->Parts[2]);
287                 }
288                 // Create Directory
289                 else if(strcmp(line->Parts[0], "mkdir") == 0) {
290                         if( line->nParts != 3 ) {
291                                 Warning("Configuration command 'mkdir' requires 1 argument, %i given",
292                                         line->nParts-1);
293                                 continue;
294                         }
295                         Log("[CFG  ] New Directory '%s'", line->Parts[1]);
296                         VFS_MkDir(line->Parts[1]);
297                 }
298                 // Spawn a process
299                 else if(strcmp(line->Parts[0], "spawn") == 0) {
300                         if( line->nParts != 3 ) {
301                                 Warning("Configuration command 'spawn' requires 1 argument, %i given",
302                                         line->nParts-1);
303                                 continue;
304                         }
305                         Log("[CFG  ] Starting '%s' as a new task", line->Parts[1]);
306                         Proc_Spawn(line->Parts[1]);
307                 }
308                 else {
309                         Warning("Unknown configuration command '%s' on line %i",
310                                 line->Parts[0],
311                                 line->TrueLine
312                                 );
313                 }
314         }
315         
316         // Clean up after ourselves
317         for( i = 0; i < file->nLines; i++ ) {
318                 free( file->Lines[i].Parts );
319         }
320         free( file );
321         free( fData );
322 }
323
324 /**
325  * \brief Parses a config file
326  * \param FileData      Read/Write buffer containing the config file data
327  *                  (will be modified)
328  * \return ::tConfigFile structure that represents the original contents
329  *         of \a FileData
330  */
331 tConfigFile     *System_Int_ParseFile(char *FileData)
332 {
333         char    *ptr;
334         char    *start;
335          int    nLines = 0;
336          int    i, j;
337         tConfigFile     *ret;
338         
339         // Prescan and count the number of lines
340         for(ptr = FileData; *ptr; ptr++)
341         {               
342                 if(*ptr != '\n')        continue;
343                 
344                 if(ptr == FileData) {
345                         nLines ++;
346                         continue;
347                 }
348                 
349                 #if 0   // Don't handle windows style EOLs
350                 if(ptr[-1] == '\r')
351                 {
352                         if( &ptr[-1] == FileData ) {
353                                 nLines ++;
354                                 continue;
355                         }
356                         if(ptr[-2] == '\\')     continue;
357                 }
358                 #endif
359                 
360                 // Escaped EOL
361                 if(ptr[-1] == '\\')     continue;
362                 
363                 nLines ++;
364         }
365         
366         // Ok so we have `nLines` lines, now to allocate our return
367         ret = malloc( sizeof(tConfigFile) + sizeof(tConfigLine)*nLines );
368         ret->nLines = nLines;
369         
370         // Read the file for real
371         for(
372                 ptr = FileData, i = 0;
373                 *ptr;
374                 i++
375                 )
376         {
377                 start = ptr;
378                 
379                 ret->Lines[i].nParts = 0;
380                 
381                 // Count parts
382                 for(;;)
383                 {
384                         // Read leading whitespace
385                         while( *ptr && (*ptr == '\t' || *ptr == ' ') )  ptr++;
386                         
387                         // End of line/file
388                         if( *ptr == '\0' || *ptr == '\n' )      break;
389                         // Comment
390                         if( *ptr == '#' || *ptr == ';' )        break;
391                         
392                         ret->Lines[i].nParts ++;
393                         // Quoted
394                         if( *ptr == '"' ) {
395                                 ptr ++;
396                                 while( *ptr && !(*ptr == '"' && ptr[-1] == '\\') && *ptr != '\n' )
397                                         ptr++;
398                                 continue;
399                         }
400                         // Unquoted
401                         while( *ptr && !(*ptr == '\t' || *ptr == ' ') )
402                                 ptr++;
403                 }
404                 
405                 // Allocate part list
406                 ret->Lines[i].Parts = malloc( sizeof(char*) * ret->Lines[i].nParts );
407                 
408                 // Fill list
409                 for( ptr = start, j = 0; ; j++ )
410                 {
411                         // Read leading whitespace
412                         while( *ptr && (*ptr == '\t' || *ptr == ' ') )  ptr++;
413                         
414                         // End of line/file
415                         if( *ptr == '\0' || *ptr == '\n' )      break;
416                         // Comment
417                         if( *ptr == '#' || *ptr == ';' )        break;
418                         
419                         
420                         ret->Lines[i].Parts[j] = ptr;
421                         
422                         // Quoted
423                         if( *ptr == '"' ) {
424                                 ptr ++;
425                                 while( *ptr && !(*ptr == '"' && ptr[-1] == '\\') && *ptr != '\n' )
426                                         ptr++;
427                         }
428                         // Unquoted
429                         else {
430                                 while( *ptr && !(*ptr == '\t' || *ptr == ' ') )
431                                         ptr++;
432                         }
433                         
434                         // Break if we have reached NULL
435                         if( *ptr == '\0' )      break;
436                         *ptr = '\0';    // Cap off string
437                         ptr ++; // And increment for the next round
438                 }
439         }
440         
441         return ret;
442 }

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