utils.php 19KB

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