Handle case in build.php when $A is not set
[people/meteger/rom-o-matic/.git] / utils.php
1 <? // -*- Mode: PHP; -*-
2
3 /**
4  * Copyright (C) 2009 Marty Connor <mdc@etherboot.org>.
5  * Copyright (C) 2009 Entity Cyber, Inc.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License as
9  * published by the Free Software Foundation; either version 2 of the
10  * License, or any later version.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20  */
21
22 // Include table of user-configurable gPXE options
23 require_once "flag-table.php";
24
25 // Include user-shadowable globals
26 require_once "globals.php";
27
28 // Allow user to shadow globals
29 if ( is_file ( 'local-config.php' ) ) { 
30     include_once "local-config.php";
31 }
32
33 ////
34 // General utility functions
35 ////
36
37 /**
38  * Remove undesirable characters from a given string
39  *
40  * Certain characters have the potential to be used for
41  * malicious purposes by web-based attackers.  This routine
42  * filters out such characters.
43  *
44  * @param string $s supplied string
45  *
46  * @return string returned string with unwanted characters
47  *                removed
48  */
49 function cleanstring ( $s ) 
50 {
51     $len = strlen ( $s );
52     if ( $len > 80 ) {
53         $s = substr ( $s, 0, 80 );
54     }
55
56     $s      = trim ( $s );
57     $pos    = 0;
58     $result = "";
59
60     while ( $pos < $len ) {
61         $ltr = ord ( ucfirst ( $s[$pos] ) );
62         if ( ( $ltr >= ord ( "A" ) ) && ( $ltr <= ord ( "Z" ) ) || 
63              ( $ltr >= ord ( "0" ) ) && ( $ltr <= ord ( "9" ) ) || 
64              ( $ltr == ord ( "." ) ) && ( strlen ( $result ) > 0 ) ||
65              ( $ltr == ord ( "_" ) ) ||
66              ( $ltr == ord ( "+" ) ) ||
67              ( $ltr == ord ( ":" ) ) ||
68              ( $ltr == ord ( "/" ) ) ||
69              ( $ltr == ord ( "-" ) ) ) {
70             $result .= $s[$pos];
71         }
72         $pos++;
73     }
74     return $result;
75 }
76
77 /**
78  * Return URL of the currently running script, minus the filename
79  *
80  * @return string the URL of the currently running script, minus the filename
81  */
82 function curDirURL () 
83 {
84         $dir = dirname ( $_SERVER['PHP_SELF'] );
85
86         if ( $dir == "." || $dir == "/" ) {
87                 $dir = "";
88         }
89
90         $isHTTPS = ( isset ( $_SERVER["HTTPS"] ) && $_SERVER["HTTPS"] == "on" );
91         $port = ( isset($_SERVER["SERVER_PORT"] ) && 
92                           ( ( !$isHTTPS && $_SERVER["SERVER_PORT"] != "80" ) || 
93                                 ( $isHTTPS  && $_SERVER["SERVER_PORT"] != "443" ) ) );
94
95         $port = ( $port ) ? ':' . $_SERVER["SERVER_PORT"] : '';
96
97         $dest = ( $isHTTPS ? 'https://' : 'http://' ) .
98                 $_SERVER["SERVER_NAME"] . $dir . "/";
99
100         return $dest;
101 }
102
103 /**
104  * Extract NIC families and associated ROM PCI IDs from the src/bin/NIC file.
105  *
106  * $src_dir must contain the path of the gPXE src directory for this build
107  *
108  * @return array[0] array $new_nics
109  * @return array[1] array $roms
110  */
111 function parse_nic_file ()
112 {
113     global $src_dir;
114
115     $fd = fopen ( "$src_dir/bin/NIC", "r" );
116     if ( ! $fd ) {
117         die ( "Missing src/bin/NIC file.  'make bin/NIC'" );
118     }
119
120     $nics = array ();
121     $roms = array ();
122     $nic = "";
123
124     while ( !feof ( $fd ) ) {
125
126         $line = trim ( fgets ( $fd, 200 ) );
127
128         $first_eight_chars = substr ( $line, 0, 8 );
129         settype ( $first_eight_chars, "string" );
130
131         if ( strpos ( $first_eight_chars, "family" ) === 0 ) {
132
133             // get pathname of NIC driver
134             list ( $dummy, $nic ) = split( "[ \t]+", $line );
135             settype ( $nic, "string" );
136             
137             // extract filename name of driver from pathname
138             $nic = substr ( $nic, strrpos ( $nic, "/" ) + 1, 
139                            strlen ( $nic ) - strrpos ( $nic, "/" ) + 1 );
140
141             $nics[$nic] = $nic;
142
143             // For each ISA NIC, there can only be one ROM variant
144             $roms[$nic] = $nic;
145         }
146
147         // If the first 8 digits of the line are hex digits
148         // add this rom to the current nic family.
149
150         if (    ( strlen ( $first_eight_chars ) == 8 ) 
151              && ( ctype_xdigit ( $first_eight_chars ) )
152              && ( $nic != "" ) ) {
153         
154             $roms[$first_eight_chars] = $nic;
155         }
156     }
157     fclose ( $fd );
158
159     // put most NICs in nice alpha order for menu
160     ksort ( $nics );
161
162     // add special cases to the top
163
164         $new_nics = array ( "all-drivers" => "gpxe",
165                                                 "undionly" => "undionly",
166                                                 "undi" => "undi",
167     );
168
169         foreach ( $nics as $key => $value ) {
170                 // skip the undi driver
171                 if ( $key != "undi" ) {
172                         $new_nics[$key] = $value;
173                 }
174         }
175
176         return array ( $new_nics, $roms );
177 }
178
179 ////
180 // HTML form utility functions
181 ////
182
183 /**
184  * Return html code to create hidden form input fields
185  *
186  * @param string $flag  name of form variable to set
187  * @param string $value value to give form variable
188  *
189  * @return string html code for given hidden form input field
190  */
191 function hidden ( $flag, $value ) 
192 {
193     $value = htmlentities ( $value );
194     return "<input type=\"hidden\" value=\"$value\" name=\"$flag\"></input>";
195 }
196
197 /**
198  * Return html code to create checkbox form input fields
199  *
200  * @param string $flag  name of form variable to set
201  * @param string $value "on" means box should be checked
202  *
203  * @return string html code for given hidden form input field
204  */
205 function checkbox ( $flag, $value ) 
206 {
207     return "<input type=\"checkbox\" value=\"on\" name=\"$flag\"" .
208         ($value == "on" ? " checked>" : ">" );
209 }
210
211 /**
212  * Return html code to create text form input fields
213  *
214  * @param string $flag  name of form variable to set
215  * @param string $value initial contents of field
216  * @param string $size  size in characters of text box
217  *
218  * @return string html code for given text input field
219  */
220 function textbox ( $flag, $value, $size ) 
221 {
222     $value = htmlentities ( $value );
223     return "<input type=\"text\" size=\"$size\" value=\"$value\" name=\"$flag\">";
224 }
225
226 /**
227  * Return html code to create textarea form fields
228  *
229  * @param string $flag  name of form variable to set
230  * @param string $value initial contents of textarea
231  * @param string $rows  height of text area in rows
232  * @param string $cols  width of text area in columns
233  *
234  * @return string html code for given textarea input field
235  */
236 function textarea ( $flag, $value, $rows, $cols ) 
237 {
238     $value = htmlentities ( $value );
239     return "<textarea name=\"$flag\" rows=\"$rows\" cols=\"$cols\">"
240             . $value . "</textarea>";
241 }
242
243 /**
244  * Return html code to create select (menu) form fields
245  *
246  * Use array of strings as menu choices
247  *
248  * @param string $flag    name of form variable to set
249  * @param array  $options array of strings representing choices
250  * @param string $value   value of choice to select in menu
251  *
252  * @return string html code for given select (menu) input field
253  */
254 function menubox ( $name, $options, $value ) 
255 {
256     $s="<select name=\"$name\">";
257
258         foreach ( $options as $ignore => $option ) {
259         if ( !$value ) $value = $option;
260         $s .= "<option" . ( $option == $value ? " selected>" : ">" ) .
261             htmlentities ( $option ) . "</option>";
262     }
263     return $s . "</select>";
264 }
265
266 /**
267  * Return html code to create select (menu) form fields
268  *
269  * Use indices of array of strings as menu choices rather than
270  * the values pointed to by the indicies.
271  *
272  * @param string $flag    name of form variable to set
273  * @param array  $options array of strings representing choices
274  * @param string $value   value of choice to select in menu
275  *
276  * @return string html code for given select (menu) input field
277  */
278 function keys_menubox ( $name, $options, $value ) 
279 {
280     $s="<select name=\"$name\">";
281     
282     foreach ( $options as $option => $ignore ) {
283         if ( !$value ) $value = $option;
284         $s .= "<option" . ( $option == $value ? " selected>" : ">" ) .
285             htmlentities ( $option ) . "</option>";
286     }
287     return $s . "</select>";
288 }  
289
290 ////
291 // Flag (compile option) handling functions
292 ////
293
294 /**
295  * Return default compile options (flags)
296  *
297  * Initial compile options are in a global called $flag_table.
298  * Create and return an array containing the ones we want.
299  *
300  * @return array default compile options (flags)
301  */
302 function default_flags () 
303 {
304     global $flag_table;
305     
306     $flags = array ();
307
308     foreach ( $flag_table as $key => $props ) {
309
310         $flag  = $props["flag"];
311         $type  = $props["type"];
312
313         // Fields like headers have no "value" property
314         if ( isset ( $props["value"] ) ) {
315             $flags[$flag] = $props["value"];
316         }
317     }
318     return $flags;
319 }
320
321 /**
322  * Return combination of default and user compile options (flags)
323  *
324  * Initial compile options are in a global called $flag_table.
325  * Compile options may have been changed via form input. We return
326  * an array with either the default value of each option or a user
327  * supplied value from form input.
328  *
329  * @return array combined default and user supplied compile options (flags)
330  */
331 function get_flags () 
332 {
333     global $flag_table;
334     
335     $flags = default_flags ();
336
337     if ( ! isset ( $_POST["use_flags"] ) )
338         return $flags;
339
340     foreach ( $flag_table as $key => $props ) {
341
342         $flag = $props["flag"];
343         $type = $props["type"];
344         
345         if ( isset ( $_POST["$flag"] ) ) {
346             $flags[$flag] = $_POST["$flag"];
347         } else if ( $type == "on/off" ) {
348                         // Unchecked checkboxes don't pass any POST value
349                         // so we must check for them specially.  At this 
350                         // point we know that there is no $_POST value set
351                         // for this option.  If it is a checkbox, this means
352                         // it is unchecked, so record that in $flags so we
353                         // can later generate an #undef for this option.
354             $flags[$flag] = "off";
355         }            
356     }
357     return $flags;
358 }
359
360 /**
361  * Output given value in appropriate format for gPXE config file
362  *
363  * gPXE config/*.h files use C pre-processor syntax.  Output the given
364  * compile option in a format appropriate to its type
365  *
366  * @param string $key   index into $flag_table for given compile option
367  * @param string $value value we wish to set compile option to
368  * 
369  * @return string code to set compile option to given value
370  */
371 function pprint_flag ( $key, $value )
372 {
373     global $flag_table;
374
375     // Determine type of given compile option (flag)
376     $type = $flag_table[$key]["type"];
377     $s = "";
378
379     if ( $type == "on/off" && $value == "on" ) {
380         $s = "#define $key";
381     } else if ( $type == "on/off" && $value != "on" ) {
382         $s = "#undef $key";
383     } else if ( $type == "string" ) {
384         $s = ( "#define $key \"" . cleanstring ( $value ) . "\"" );
385     } else if ($type == "qstring" ) {
386         $s = ( "#define $key \\\"" . cleanstring ( $value ) . "\\\"" );
387     } else {
388         $s = "#define $key " . cleanstring ( $value );
389     }
390     
391     return $s;
392 }
393
394 /**
395  * Output html code to display all compile options as a table 
396  *
397  * @param array $flags array of compile options
398  * 
399  * @return void
400  */
401 function echo_flags ( $flags )
402 {
403     global $flag_table;
404
405     echo "<table>\n";
406
407         foreach ( $flag_table as $key => $props ) {
408
409         // Hide parameters from users that should not be changed.
410         $hide_from_user = isset ( $props["hide_from_user"] ) ? $props["hide_from_user"] : "no";
411
412         $flag = $props["flag"];
413         $type = $props["type"];
414
415         $value = isset ( $flags[$flag] ) ? $flags[$flag] : '';
416
417         if ( $hide_from_user == "yes" ) {
418
419             // Hidden flags cannot not be set by the user.  We use hidden form
420             // fields to keep them at their default values.
421             if ( $type != "header" ) {
422                 echo hidden ( $flag, $value );
423             }
424             
425         } else {
426             
427             // Flag (gPXE compile option) should be displayed to user
428
429             if ( $type == "header" ) {
430
431                 $label = $props["label"];
432                 echo "<td colspan=2><hr><h3>$label</h3><hr></td>";
433
434             } else if ($type == "on/off" ) {
435
436                 echo "<td>", checkbox ( $flag, $value ), "</td><td><strong>$flag</strong></td>";
437
438             } else {   // don't display checkbox for non-on/off flags
439
440                 echo "<td>&nbsp;</td><td><strong>$flag: </strong>";
441
442                 if ($type == "choice" ) {
443                     $options = $props["options"];
444                     echo menubox($flag, $options, $value);
445
446                 } else {
447
448                     echo textbox($flag, $value, ($type == "integer" ? 7 : 25));
449                 }
450                 echo "</td>";
451             }
452             echo "</tr>\n";
453
454             if ( $type != "header" ) {
455                                 echo "<tr><td>&nbsp;</td>";
456                                 echo "<td>\n";
457                                 if ( is_file ( "doc/$flag.html" ) ) { 
458                                         include_once "doc/$flag.html";
459                                 }
460                                 echo "\n</td></tr>\n";
461             }
462         }
463     }
464     echo "</table>";
465 }
466
467 /**
468  * Return an array of configuration sections used in all compile options
469  *
470  * $flag_table, the global list of compile options contains a 'cfgsec'
471  * property for each flag we are interested in.  We return a list of 
472  * all the unique cfgsec options we find in $flag_table.
473  *
474  * @return array an array of strings representing all unique cfgsec values
475  *               found in $flag_table
476  */
477 function get_flag_cfgsecs ()
478 {
479     global $flag_table;
480     $cfgsecs = array ();
481
482     foreach ( $flag_table as $key => $props ) {
483         if ( isset ( $props['cfgsec'] ) ) {
484             $cfgsec = $props["cfgsec"];
485             $cfgsecs[$cfgsec] = $cfgsec;
486         }
487     }
488     return $cfgsecs;
489 }
490
491 ////
492 // File and directory handling functions
493 ////
494
495 /**
496  * Create a copy of a given source directory to a given destination
497  *
498  * Since we are going to modify the source directory, we create a copy
499  * of the directory with a unique name in the given destination directory.
500  * We supply a prefix for the tempnam call to prepend to the random filename
501  * it generates.
502  *
503  * @param string $src    source directory
504  * @param string $dst    destination directory
505  * @param string $prefix string to append to directory created
506  * 
507  * @return string absolute path to destination directory
508  */
509 function mktempcopy ( $src, $dst, $prefix ) 
510 {
511     if ( $src[0] != "/" ) {
512         $src = dirname ( $_SERVER['SCRIPT_FILENAME'] ) . "/" . $src;
513     }
514
515     // Create a file in the given destination directory with a unique name
516     $dir = tempnam ( $dst, $prefix );
517     
518     // Delete the file just created, since it would interfere with the copy we
519     // are about to do.  We only care that the dir name we copy to is unique.
520     unlink ( $dir );
521
522     exec ( "/bin/cp -a '$src' '$dir' 2>&1", $cpytxt, $status );
523
524     if ( $status != 0 ) {
525         die ( "src directory copy failed!" );
526     }
527     return $dir;
528 }
529
530 /**
531  * Write gPXE config files based on value of given flags
532  *
533  * gPXE compile options are stored in src/config/*.h .
534  * We write out a config file for each set of options.
535  * 
536  * @param string $config_dir directory to write .h files to
537  * @param array  $flags array of compile options for this build
538  * 
539  * @return void
540  */
541 function write_gpxe_config_files ( $config_dir, $flags ) 
542 {
543     global $flag_table;
544
545     $cfgsecs = get_flag_cfgsecs ();
546
547     foreach ( $cfgsecs as $cfgsec ) {
548
549         $fname = $config_dir . "/" . $cfgsec . ".h";
550
551         $fp = fopen ( $fname, "wb" );
552         if ( $fp <= 0 ) {
553             die ( "Unable to open $fname file for output!" );
554         }
555
556         $ifdef_secname = "CONFIG_" . strtoupper ( $cfgsec ) . "_H";
557
558         fwrite ( $fp, "#ifndef ${ifdef_secname}\n" );
559         fwrite ( $fp, "#define ${ifdef_secname}\n" );
560         fwrite ( $fp, "#include <config/defaults.h>\n" );
561
562         foreach ( $flags as $key => $value ) {
563             // When the flag matches this section name, write it out
564             if ( $flag_table[$key]["cfgsec"] == $cfgsec ) {
565                 fwrite ( $fp, pprint_flag ( $key, $value ) . "\n" );
566             } 
567         }
568         fwrite ( $fp, "#endif /* ${ifdef_secname} */\n" );
569         fclose ( $fp );
570     }
571 }
572
573 /**
574  * Output a string to a file
575  *
576  * Output a given string to a given pathname. The file will be created if 
577  * necessary, and the string will replace the file's contents in all cases.
578  *
579  * @param string $fname pathname of file to output string to
580  * @param string $ftext text to output to file
581  *
582  * @return void
583  */
584 function write_file_from_string ( $fname, $ftext )
585 {
586         $fp = fopen ( $fname, "wb" );
587         if ( ! $fp ) {
588             die ( "Unable to open $fname file for output!" );
589         }
590         fwrite ( $fp, $ftext );
591         fclose ( $fp );
592 }
593
594 /**
595  * Delete a file or recursively delete a directory tree
596  *
597  * @param   string   $file_or_dir_name  name of file or directory to delete
598  * @return  bool     Returns TRUE on success, FALSE on failure
599  */
600 function rm_file_or_dir ( $file_or_dir_name )
601 {
602     if ( ! file_exists ( $file_or_dir_name ) ) {
603         return false;
604     }
605
606     if ( is_file ( $file_or_dir_name ) || is_link ( $file_or_dir_name ) ) {
607         return unlink ( $file_or_dir_name );
608     }
609
610     $dir = dir ( $file_or_dir_name );
611     while ( ( $dir_entry = $dir->read () ) !== false ) {
612
613         if ( $dir_entry == '.' || $dir_entry == '..') {
614             continue;
615         }
616         rm_file_or_dir ( $file_or_dir_name . '/' . $dir_entry );
617     }
618     $dir->close();
619     
620     return rmdir ( $file_or_dir_name );
621 }
622
623 ////
624 // Debugging functions
625 ////
626
627 /**
628  * Emit html code to display given array of compile options (flags)
629  *
630  * @param array  $flags array of compile options for this build
631  *
632  * @return void
633  */
634 function show_flags ( $flags ) 
635 {
636     echo ( "\$flags contains " . count ( $flags ) . " elements:" . "<br>" );
637
638         foreach ( $flags as $key => $flag ) {
639         echo ( "\$flags[" . $key . "]=" . "\"$flag\"" . "<br>" );
640     }
641 }
642
643 /**
644  * Emit HTML code to display default array of compile options (flags)
645  * 
646  * $flag_table contains default compile options and properties.  This
647  * routine outputs HTML code to display all properties of $flag_table.
648  *
649  * @return void
650  */
651 function dump_flag_table () 
652 {
653     global $flag_table;
654
655     echo ( "\$flag_table contains " . count ( $flag_table ) . " elements:" . "<br>" );
656
657         foreach ( $flag_table as $key => $props ) {
658         print ( "flag_table[" . $key . "] = " . "<br>" );
659
660                 foreach ( $props as $key2 => $props2 ) {
661             print ( "&nbsp;&nbsp;&nbsp;" . $key2 . " = " . $props2 . "<br>" );
662         }
663     }
664 }
665
666 // Parse src/bin/NIC file
667 list ( $nics, $roms ) = parse_nic_file ();
668
669 // For emacs:
670 // Local variables:
671 //  c-basic-offset: 4
672 //  c-indent-level: 4
673 //  tab-width: 4
674 // End:
675
676 ?>