. * Copyright (C) 2009 Entity Cyber, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ // Include table of user-configurable iPXE options require_once "flag-table.php"; // Include user-shadowable globals require_once "globals.php"; // Allow user to shadow globals if ( is_file ( 'local-config.php' ) ) { include_once "local-config.php"; } //// // General utility functions //// /** * Remove undesirable characters from a given string * * Certain characters have the potential to be used for * malicious purposes by web-based attackers. This routine * filters out such characters. * * @param string $s supplied string * * @return string returned string with unwanted characters * removed */ function cleanstring ( $s ) { $len = strlen ( $s ); if ( $len > 80 ) { $s = substr ( $s, 0, 80 ); } $s = trim ( $s ); $pos = 0; $result = ""; while ( $pos < $len ) { $ltr = ord ( ucfirst ( $s[$pos] ) ); if ( ( $ltr >= ord ( "A" ) ) && ( $ltr <= ord ( "Z" ) ) || ( $ltr >= ord ( "0" ) ) && ( $ltr <= ord ( "9" ) ) || ( $ltr == ord ( "." ) ) && ( strlen ( $result ) > 0 ) || ( $ltr == ord ( "_" ) ) || ( $ltr == ord ( "+" ) ) || ( $ltr == ord ( ":" ) ) || ( $ltr == ord ( "/" ) ) || ( $ltr == ord ( "-" ) ) ) { $result .= $s[$pos]; } $pos++; } return $result; } /** * Return URL of the currently running script, minus the filename * * @return string the URL of the currently running script, minus the filename */ function curDirURL () { $dir = dirname ( $_SERVER['PHP_SELF'] ); if ( $dir == "." || $dir == "/" ) { $dir = ""; } $isHTTPS = ( isset ( $_SERVER["HTTPS"] ) && $_SERVER["HTTPS"] == "on" ); $port = ( isset($_SERVER["SERVER_PORT"] ) && ( ( !$isHTTPS && $_SERVER["SERVER_PORT"] != "80" ) || ( $isHTTPS && $_SERVER["SERVER_PORT"] != "443" ) ) ); $port = ( $port ) ? ':' . $_SERVER["SERVER_PORT"] : ''; $dest = ( $isHTTPS ? 'https://' : 'http://' ) . $_SERVER["SERVER_NAME"] . $dir . "/"; return $dest; } /** * Extract NIC families and associated ROM PCI IDs from the src/bin/NIC file. * * $src_dir must contain the path of the iPXE src directory for this build * * @return array[0] array $new_nics * @return array[1] array $roms */ function parse_nic_file () { global $src_dir; $fd = fopen ( "$src_dir/bin/NIC", "r" ); if ( ! $fd ) { die ( "Missing src/bin/NIC file. 'make bin/NIC'" ); } $nics = array (); $roms = array (); $nic = ""; while ( !feof ( $fd ) ) { $line = trim ( fgets ( $fd, 200 ) ); $first_eight_chars = substr ( $line, 0, 8 ); settype ( $first_eight_chars, "string" ); if ( strpos ( $first_eight_chars, "family" ) === 0 ) { // get pathname of NIC driver list ( $dummy, $nic ) = split( "[ \t]+", $line ); settype ( $nic, "string" ); // extract filename name of driver from pathname $nic = substr ( $nic, strrpos ( $nic, "/" ) + 1, strlen ( $nic ) - strrpos ( $nic, "/" ) + 1 ); $nics[$nic] = $nic; // For each ISA NIC, there can only be one ROM variant $roms[$nic] = $nic; } // If the first 8 digits of the line are hex digits // add this rom to the current nic family. if ( ( strlen ( $first_eight_chars ) == 8 ) && ( ctype_xdigit ( $first_eight_chars ) ) && ( $nic != "" ) ) { $roms[$first_eight_chars] = $nic; } } fclose ( $fd ); // put most NICs in nice alpha order for menu ksort ( $nics ); // add special cases to the top $new_nics = array ( "all-drivers" => "ipxe", "undionly" => "undionly", "undi" => "undi", ); foreach ( $nics as $key => $value ) { // skip the undi driver if ( $key != "undi" ) { $new_nics[$key] = $value; } } return array ( $new_nics, $roms ); } //// // HTML form utility functions //// /** * Return html code to create hidden form input fields * * @param string $flag name of form variable to set * @param string $value value to give form variable * * @return string html code for given hidden form input field */ function hidden ( $flag, $value ) { $value = htmlentities ( $value ); return ""; } /** * Return html code to create checkbox form input fields * * @param string $flag name of form variable to set * @param string $value "on" means box should be checked * * @return string html code for given hidden form input field */ function checkbox ( $flag, $value ) { return "" : ">" ); } /** * Return html code to create text form input fields * * @param string $flag name of form variable to set * @param string $value initial contents of field * @param string $size size in characters of text box * * @return string html code for given text input field */ function textbox ( $flag, $value, $size ) { $value = htmlentities ( $value ); return ""; } /** * Return html code to create textarea form fields * * @param string $flag name of form variable to set * @param string $value initial contents of textarea * @param string $rows height of text area in rows * @param string $cols width of text area in columns * * @return string html code for given textarea input field */ function textarea ( $flag, $value, $rows, $cols ) { $value = htmlentities ( $value ); return ""; } /** * Return html code to create select (menu) form fields * * Use array of strings as menu choices * * @param string $flag name of form variable to set * @param array $options array of strings representing choices * @param string $value value of choice to select in menu * * @return string html code for given select (menu) input field */ function menubox ( $name, $options, $value ) { $s=""; } /** * Return html code to create select (menu) form fields * * Use indices of array of strings as menu choices rather than * the values pointed to by the indicies. * * @param string $flag name of form variable to set * @param array $options array of strings representing choices * @param string $value value of choice to select in menu * * @return string html code for given select (menu) input field */ function keys_menubox ( $name, $options, $value ) { $s=""; } //// // Flag (compile option) handling functions //// /** * Return default compile options (flags) * * Initial compile options are in a global called $flag_table. * Create and return an array containing the ones we want. * * @return array default compile options (flags) */ function default_flags () { global $flag_table; $flags = array (); foreach ( $flag_table as $key => $props ) { $flag = $props["flag"]; $type = $props["type"]; // Fields like headers have no "value" property if ( isset ( $props["value"] ) ) { $flags[$flag] = $props["value"]; } } return $flags; } /** * Return combination of default and user compile options (flags) * * Initial compile options are in a global called $flag_table. * Compile options may have been changed via form input. We return * an array with either the default value of each option or a user * supplied value from form input. * * @return array combined default and user supplied compile options (flags) */ function get_flags () { global $flag_table; $flags = default_flags (); if ( ! isset ( $_POST["use_flags"] ) ) return $flags; foreach ( $flag_table as $key => $props ) { $flag = $props["flag"]; $type = $props["type"]; if ( isset ( $_POST["$flag"] ) ) { $flags[$flag] = $_POST["$flag"]; if ( $type == "integer-hex" ) { if ( strtolower ( substr ( $flags[$flag], 0, 2 ) ) != "0x" ) { $flags[$flag] = "0x" . $flags[$flag]; } } } else if ( $type == "on/off" ) { // Unchecked checkboxes don't pass any POST value // so we must check for them specially. At this // point we know that there is no $_POST value set // for this option. If it is a checkbox, this means // it is unchecked, so record that in $flags so we // can later generate an #undef for this option. $flags[$flag] = "off"; } } return $flags; } /** * Output given value in appropriate format for iPXE config file * * iPXE config/*.h files use C pre-processor syntax. Output the given * compile option in a format appropriate to its type * * @param string $key index into $flag_table for given compile option * @param string $value value we wish to set compile option to * * @return string code to set compile option to given value */ function pprint_flag ( $key, $value ) { global $flag_table; // Determine type of given compile option (flag) $type = $flag_table[$key]["type"]; $s = ""; if ( $type == "on/off" && $value == "on" ) { $s = "#define $key"; } else if ( $type == "on/off" && $value != "on" ) { $s = "#undef $key"; } else if ( $type == "string" ) { $s = ( "#define $key \"" . cleanstring ( $value ) . "\"" ); } else if ($type == "qstring" ) { $s = ( "#define $key \\\"" . cleanstring ( $value ) . "\\\"" ); } else { $s = "#define $key " . cleanstring ( $value ); } return $s; } /** * Output html code to display all compile options as a table * * @param array $flags array of compile options * * @return void */ function echo_flags ( $flags ) { global $flag_table; echo "\n"; foreach ( $flag_table as $key => $props ) { // Hide parameters from users that should not be changed. $hide_from_user = isset ( $props["hide_from_user"] ) ? $props["hide_from_user"] : "no"; $flag = $props["flag"]; $type = $props["type"]; $value = isset ( $flags[$flag] ) ? $flags[$flag] : ''; if ( $hide_from_user == "yes" ) { // Hidden flags cannot not be set by the user. We use hidden form // fields to keep them at their default values. if ( $type != "header" ) { echo hidden ( $flag, $value ); } } else { // Flag (iPXE compile option) should be displayed to user if ( $type == "header" ) { $label = $props["label"]; echo ""; } else if ($type == "on/off" ) { echo ""; } else { // don't display checkbox for non-on/off flags echo ""; } echo "\n"; if ( $type != "header" ) { echo ""; echo "\n"; } } } echo "

$label


", checkbox ( $flag, $value ), "$flag $flag: "; if ($type == "choice" ) { $options = $props["options"]; echo menubox($flag, $options, $value); } else { echo textbox($flag, $value, ($type == "integer" || $type == "integer-hex" ? 7 : 25)); } echo "
 \n"; if ( is_file ( "doc/$flag.html" ) ) { include_once "doc/$flag.html"; } echo "\n
"; } /** * Return an array of configuration sections used in all compile options * * $flag_table, the global list of compile options contains a 'cfgsec' * property for each flag we are interested in. We return a list of * all the unique cfgsec options we find in $flag_table. * * @return array an array of strings representing all unique cfgsec values * found in $flag_table */ function get_flag_cfgsecs () { global $flag_table; $cfgsecs = array (); foreach ( $flag_table as $key => $props ) { if ( isset ( $props['cfgsec'] ) ) { $cfgsec = $props["cfgsec"]; $cfgsecs[$cfgsec] = $cfgsec; } } return $cfgsecs; } //// // File and directory handling functions //// /** * Create a copy of a given source directory to a given destination * * Since we are going to modify the source directory, we create a copy * of the directory with a unique name in the given destination directory. * We supply a prefix for the tempnam call to prepend to the random filename * it generates. * * @param string $src source directory * @param string $dst destination directory * @param string $prefix string to append to directory created * * @return string absolute path to destination directory */ function mktempcopy ( $src, $dst, $prefix ) { if ( $src[0] != "/" ) { $src = dirname ( $_SERVER['SCRIPT_FILENAME'] ) . "/" . $src; } // Create a file in the given destination directory with a unique name $dir = tempnam ( $dst, $prefix ); // Delete the file just created, since it would interfere with the copy we // are about to do. We only care that the dir name we copy to is unique. unlink ( $dir ); exec ( "/bin/cp -a '$src' '$dir' 2>&1", $cpytxt, $status ); if ( $status != 0 ) { die ( "src directory copy failed!" ); } return $dir; } /** * Write iPXE config files based on value of given flags * * iPXE compile options are stored in src/config/*.h . * We write out a config file for each set of options. * * @param string $config_dir directory to write .h files to * @param array $flags array of compile options for this build * * @return void */ function write_ipxe_config_files ( $config_dir, $flags ) { global $flag_table; $cfgsecs = get_flag_cfgsecs (); foreach ( $cfgsecs as $cfgsec ) { $fname = $config_dir . "/" . $cfgsec . ".h"; $fp = fopen ( $fname, "wb" ); if ( $fp <= 0 ) { die ( "Unable to open $fname file for output!" ); } $ifdef_secname = "CONFIG_" . strtoupper ( $cfgsec ) . "_H"; fwrite ( $fp, "#ifndef ${ifdef_secname}\n" ); fwrite ( $fp, "#define ${ifdef_secname}\n" ); fwrite ( $fp, "#include \n" ); foreach ( $flags as $key => $value ) { // When the flag matches this section name, write it out if ( $flag_table[$key]["cfgsec"] == $cfgsec ) { fwrite ( $fp, pprint_flag ( $key, $value ) . "\n" ); } } fwrite ( $fp, "#endif /* ${ifdef_secname} */\n" ); fclose ( $fp ); } } /** * Output a string to a file * * Output a given string to a given pathname. The file will be created if * necessary, and the string will replace the file's contents in all cases. * * @param string $fname pathname of file to output string to * @param string $ftext text to output to file * * @return void */ function write_file_from_string ( $fname, $ftext ) { $fp = fopen ( $fname, "wb" ); if ( ! $fp ) { die ( "Unable to open $fname file for output!" ); } fwrite ( $fp, $ftext ); fclose ( $fp ); } /** * Delete a file or recursively delete a directory tree * * @param string $file_or_dir_name name of file or directory to delete * @return bool Returns TRUE on success, FALSE on failure */ function rm_file_or_dir ( $file_or_dir_name ) { if ( ! file_exists ( $file_or_dir_name ) ) { return false; } if ( is_file ( $file_or_dir_name ) || is_link ( $file_or_dir_name ) ) { return unlink ( $file_or_dir_name ); } $dir = dir ( $file_or_dir_name ); while ( ( $dir_entry = $dir->read () ) !== false ) { if ( $dir_entry == '.' || $dir_entry == '..') { continue; } rm_file_or_dir ( $file_or_dir_name . '/' . $dir_entry ); } $dir->close(); return rmdir ( $file_or_dir_name ); } //// // Debugging functions //// /** * Emit html code to display given array of compile options (flags) * * @param array $flags array of compile options for this build * * @return void */ function show_flags ( $flags ) { echo ( "\$flags contains " . count ( $flags ) . " elements:" . "
" ); foreach ( $flags as $key => $flag ) { echo ( "\$flags[" . $key . "]=" . "\"$flag\"" . "
" ); } } /** * Emit HTML code to display default array of compile options (flags) * * $flag_table contains default compile options and properties. This * routine outputs HTML code to display all properties of $flag_table. * * @return void */ function dump_flag_table () { global $flag_table; echo ( "\$flag_table contains " . count ( $flag_table ) . " elements:" . "
" ); foreach ( $flag_table as $key => $props ) { print ( "flag_table[" . $key . "] = " . "
" ); foreach ( $props as $key2 => $props2 ) { print ( "   " . $key2 . " = " . $props2 . "
" ); } } } // Parse src/bin/NIC file list ( $nics, $roms ) = parse_nic_file (); // For emacs: // Local variables: // c-basic-offset: 4 // c-indent-level: 4 // tab-width: 4 // End: ?>