You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

utils.php 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683
  1. <? // -*- 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 gPXE 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 gPXE 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. settype ( $nic, "string" );
  114. // extract filename name of driver from pathname
  115. $nic = substr ( $nic, strrpos ( $nic, "/" ) + 1,
  116. strlen ( $nic ) - strrpos ( $nic, "/" ) + 1 );
  117. $nics[$nic] = $nic;
  118. // For each ISA NIC, there can only be one ROM variant
  119. $roms[$nic] = $nic;
  120. }
  121. // If the first 8 digits of the line are hex digits
  122. // add this rom to the current nic family.
  123. if ( ( strlen ( $first_eight_chars ) == 8 )
  124. && ( ctype_xdigit ( $first_eight_chars ) )
  125. && ( $nic != "" ) ) {
  126. $roms[$first_eight_chars] = $nic;
  127. }
  128. }
  129. fclose ( $fd );
  130. // put most NICs in nice alpha order for menu
  131. ksort ( $nics );
  132. // add special cases to the top
  133. $new_nics = array ( "all-drivers" => "gpxe",
  134. "undionly" => "undionly",
  135. "undi" => "undi",
  136. );
  137. foreach ( $nics as $key => $value ) {
  138. // skip the undi driver
  139. if ( $key != "undi" ) {
  140. $new_nics[$key] = $value;
  141. }
  142. }
  143. return array ( $new_nics, $roms );
  144. }
  145. ////
  146. // HTML form utility functions
  147. ////
  148. /**
  149. * Return html code to create hidden form input fields
  150. *
  151. * @param string $flag name of form variable to set
  152. * @param string $value value to give form variable
  153. *
  154. * @return string html code for given hidden form input field
  155. */
  156. function hidden ( $flag, $value )
  157. {
  158. $value = htmlentities ( $value );
  159. return "<input type=\"hidden\" value=\"$value\" name=\"$flag\"></input>";
  160. }
  161. /**
  162. * Return html code to create checkbox form input fields
  163. *
  164. * @param string $flag name of form variable to set
  165. * @param string $value "on" means box should be checked
  166. *
  167. * @return string html code for given hidden form input field
  168. */
  169. function checkbox ( $flag, $value )
  170. {
  171. return "<input type=\"checkbox\" value=\"on\" name=\"$flag\"" .
  172. ($value == "on" ? " checked>" : ">" );
  173. }
  174. /**
  175. * Return html code to create text form input fields
  176. *
  177. * @param string $flag name of form variable to set
  178. * @param string $value initial contents of field
  179. * @param string $size size in characters of text box
  180. *
  181. * @return string html code for given text input field
  182. */
  183. function textbox ( $flag, $value, $size )
  184. {
  185. $value = htmlentities ( $value );
  186. return "<input type=\"text\" size=\"$size\" value=\"$value\" name=\"$flag\">";
  187. }
  188. /**
  189. * Return html code to create textarea form fields
  190. *
  191. * @param string $flag name of form variable to set
  192. * @param string $value initial contents of textarea
  193. * @param string $rows height of text area in rows
  194. * @param string $cols width of text area in columns
  195. *
  196. * @return string html code for given textarea input field
  197. */
  198. function textarea ( $flag, $value, $rows, $cols )
  199. {
  200. $value = htmlentities ( $value );
  201. return "<textarea name=\"$flag\" rows=\"$rows\" cols=\"$cols\">"
  202. . $value . "</textarea>";
  203. }
  204. /**
  205. * Return html code to create select (menu) form fields
  206. *
  207. * Use array of strings as menu choices
  208. *
  209. * @param string $flag name of form variable to set
  210. * @param array $options array of strings representing choices
  211. * @param string $value value of choice to select in menu
  212. *
  213. * @return string html code for given select (menu) input field
  214. */
  215. function menubox ( $name, $options, $value )
  216. {
  217. $s="<select name=\"$name\">";
  218. foreach ( $options as $ignore => $option ) {
  219. if ( !$value ) $value = $option;
  220. $s .= "<option" . ( $option == $value ? " selected>" : ">" ) .
  221. htmlentities ( $option ) . "</option>";
  222. }
  223. return $s . "</select>";
  224. }
  225. /**
  226. * Return html code to create select (menu) form fields
  227. *
  228. * Use indices of array of strings as menu choices rather than
  229. * the values pointed to by the indicies.
  230. *
  231. * @param string $flag name of form variable to set
  232. * @param array $options array of strings representing choices
  233. * @param string $value value of choice to select in menu
  234. *
  235. * @return string html code for given select (menu) input field
  236. */
  237. function keys_menubox ( $name, $options, $value )
  238. {
  239. $s="<select name=\"$name\">";
  240. foreach ( $options as $option => $ignore ) {
  241. if ( !$value ) $value = $option;
  242. $s .= "<option" . ( $option == $value ? " selected>" : ">" ) .
  243. htmlentities ( $option ) . "</option>";
  244. }
  245. return $s . "</select>";
  246. }
  247. ////
  248. // Flag (compile option) handling functions
  249. ////
  250. /**
  251. * Return default compile options (flags)
  252. *
  253. * Initial compile options are in a global called $flag_table.
  254. * Create and return an array containing the ones we want.
  255. *
  256. * @return array default compile options (flags)
  257. */
  258. function default_flags ()
  259. {
  260. global $flag_table;
  261. $flags = array ();
  262. foreach ( $flag_table as $key => $props ) {
  263. $flag = $props["flag"];
  264. $type = $props["type"];
  265. // Fields like headers have no "value" property
  266. if ( isset ( $props["value"] ) ) {
  267. $flags[$flag] = $props["value"];
  268. }
  269. }
  270. return $flags;
  271. }
  272. /**
  273. * Return combination of default and user compile options (flags)
  274. *
  275. * Initial compile options are in a global called $flag_table.
  276. * Compile options may have been changed via form input. We return
  277. * an array with either the default value of each option or a user
  278. * supplied value from form input.
  279. *
  280. * @return array combined default and user supplied compile options (flags)
  281. */
  282. function get_flags ()
  283. {
  284. global $flag_table;
  285. $flags = default_flags ();
  286. if ( ! isset ( $_POST["use_flags"] ) )
  287. return $flags;
  288. foreach ( $flag_table as $key => $props ) {
  289. $flag = $props["flag"];
  290. $type = $props["type"];
  291. if ( isset ( $_POST["$flag"] ) ) {
  292. $flags[$flag] = $_POST["$flag"];
  293. if ( $type == "integer-hex" ) {
  294. if ( strtolower ( substr ( $flags[$flag], 0, 2 ) ) != "0x" ) {
  295. $flags[$flag] = "0x" . $flags[$flag];
  296. }
  297. }
  298. } else if ( $type == "on/off" ) {
  299. // Unchecked checkboxes don't pass any POST value
  300. // so we must check for them specially. At this
  301. // point we know that there is no $_POST value set
  302. // for this option. If it is a checkbox, this means
  303. // it is unchecked, so record that in $flags so we
  304. // can later generate an #undef for this option.
  305. $flags[$flag] = "off";
  306. }
  307. }
  308. return $flags;
  309. }
  310. /**
  311. * Output given value in appropriate format for gPXE config file
  312. *
  313. * gPXE config/*.h files use C pre-processor syntax. Output the given
  314. * compile option in a format appropriate to its type
  315. *
  316. * @param string $key index into $flag_table for given compile option
  317. * @param string $value value we wish to set compile option to
  318. *
  319. * @return string code to set compile option to given value
  320. */
  321. function pprint_flag ( $key, $value )
  322. {
  323. global $flag_table;
  324. // Determine type of given compile option (flag)
  325. $type = $flag_table[$key]["type"];
  326. $s = "";
  327. if ( $type == "on/off" && $value == "on" ) {
  328. $s = "#define $key";
  329. } else if ( $type == "on/off" && $value != "on" ) {
  330. $s = "#undef $key";
  331. } else if ( $type == "string" ) {
  332. $s = ( "#define $key \"" . cleanstring ( $value ) . "\"" );
  333. } else if ($type == "qstring" ) {
  334. $s = ( "#define $key \\\"" . cleanstring ( $value ) . "\\\"" );
  335. } else {
  336. $s = "#define $key " . cleanstring ( $value );
  337. }
  338. return $s;
  339. }
  340. /**
  341. * Output html code to display all compile options as a table
  342. *
  343. * @param array $flags array of compile options
  344. *
  345. * @return void
  346. */
  347. function echo_flags ( $flags )
  348. {
  349. global $flag_table;
  350. echo "<table>\n";
  351. foreach ( $flag_table as $key => $props ) {
  352. // Hide parameters from users that should not be changed.
  353. $hide_from_user = isset ( $props["hide_from_user"] ) ? $props["hide_from_user"] : "no";
  354. $flag = $props["flag"];
  355. $type = $props["type"];
  356. $value = isset ( $flags[$flag] ) ? $flags[$flag] : '';
  357. if ( $hide_from_user == "yes" ) {
  358. // Hidden flags cannot not be set by the user. We use hidden form
  359. // fields to keep them at their default values.
  360. if ( $type != "header" ) {
  361. echo hidden ( $flag, $value );
  362. }
  363. } else {
  364. // Flag (gPXE compile option) should be displayed to user
  365. if ( $type == "header" ) {
  366. $label = $props["label"];
  367. echo "<td colspan=2><hr><h3>$label</h3><hr></td>";
  368. } else if ($type == "on/off" ) {
  369. echo "<td>", checkbox ( $flag, $value ), "</td><td><strong>$flag</strong></td>";
  370. } else { // don't display checkbox for non-on/off flags
  371. echo "<td>&nbsp;</td><td><strong>$flag: </strong>";
  372. if ($type == "choice" ) {
  373. $options = $props["options"];
  374. echo menubox($flag, $options, $value);
  375. } else {
  376. echo textbox($flag, $value, ($type == "integer" ||
  377. $type == "integer-hex"
  378. ? 7 : 25));
  379. }
  380. echo "</td>";
  381. }
  382. echo "</tr>\n";
  383. if ( $type != "header" ) {
  384. echo "<tr><td>&nbsp;</td>";
  385. echo "<td>\n";
  386. if ( is_file ( "doc/$flag.html" ) ) {
  387. include_once "doc/$flag.html";
  388. }
  389. echo "\n</td></tr>\n";
  390. }
  391. }
  392. }
  393. echo "</table>";
  394. }
  395. /**
  396. * Return an array of configuration sections used in all compile options
  397. *
  398. * $flag_table, the global list of compile options contains a 'cfgsec'
  399. * property for each flag we are interested in. We return a list of
  400. * all the unique cfgsec options we find in $flag_table.
  401. *
  402. * @return array an array of strings representing all unique cfgsec values
  403. * found in $flag_table
  404. */
  405. function get_flag_cfgsecs ()
  406. {
  407. global $flag_table;
  408. $cfgsecs = array ();
  409. foreach ( $flag_table as $key => $props ) {
  410. if ( isset ( $props['cfgsec'] ) ) {
  411. $cfgsec = $props["cfgsec"];
  412. $cfgsecs[$cfgsec] = $cfgsec;
  413. }
  414. }
  415. return $cfgsecs;
  416. }
  417. ////
  418. // File and directory handling functions
  419. ////
  420. /**
  421. * Create a copy of a given source directory to a given destination
  422. *
  423. * Since we are going to modify the source directory, we create a copy
  424. * of the directory with a unique name in the given destination directory.
  425. * We supply a prefix for the tempnam call to prepend to the random filename
  426. * it generates.
  427. *
  428. * @param string $src source directory
  429. * @param string $dst destination directory
  430. * @param string $prefix string to append to directory created
  431. *
  432. * @return string absolute path to destination directory
  433. */
  434. function mktempcopy ( $src, $dst, $prefix )
  435. {
  436. if ( $src[0] != "/" ) {
  437. $src = dirname ( $_SERVER['SCRIPT_FILENAME'] ) . "/" . $src;
  438. }
  439. // Create a file in the given destination directory with a unique name
  440. $dir = tempnam ( $dst, $prefix );
  441. // Delete the file just created, since it would interfere with the copy we
  442. // are about to do. We only care that the dir name we copy to is unique.
  443. unlink ( $dir );
  444. exec ( "/bin/cp -a '$src' '$dir' 2>&1", $cpytxt, $status );
  445. if ( $status != 0 ) {
  446. die ( "src directory copy failed!" );
  447. }
  448. return $dir;
  449. }
  450. /**
  451. * Write gPXE config files based on value of given flags
  452. *
  453. * gPXE compile options are stored in src/config/*.h .
  454. * We write out a config file for each set of options.
  455. *
  456. * @param string $config_dir directory to write .h files to
  457. * @param array $flags array of compile options for this build
  458. *
  459. * @return void
  460. */
  461. function write_gpxe_config_files ( $config_dir, $flags )
  462. {
  463. global $flag_table;
  464. $cfgsecs = get_flag_cfgsecs ();
  465. foreach ( $cfgsecs as $cfgsec ) {
  466. $fname = $config_dir . "/" . $cfgsec . ".h";
  467. $fp = fopen ( $fname, "wb" );
  468. if ( $fp <= 0 ) {
  469. die ( "Unable to open $fname file for output!" );
  470. }
  471. $ifdef_secname = "CONFIG_" . strtoupper ( $cfgsec ) . "_H";
  472. fwrite ( $fp, "#ifndef ${ifdef_secname}\n" );
  473. fwrite ( $fp, "#define ${ifdef_secname}\n" );
  474. fwrite ( $fp, "#include <config/defaults.h>\n" );
  475. foreach ( $flags as $key => $value ) {
  476. // When the flag matches this section name, write it out
  477. if ( $flag_table[$key]["cfgsec"] == $cfgsec ) {
  478. fwrite ( $fp, pprint_flag ( $key, $value ) . "\n" );
  479. }
  480. }
  481. fwrite ( $fp, "#endif /* ${ifdef_secname} */\n" );
  482. fclose ( $fp );
  483. }
  484. }
  485. /**
  486. * Output a string to a file
  487. *
  488. * Output a given string to a given pathname. The file will be created if
  489. * necessary, and the string will replace the file's contents in all cases.
  490. *
  491. * @param string $fname pathname of file to output string to
  492. * @param string $ftext text to output to file
  493. *
  494. * @return void
  495. */
  496. function write_file_from_string ( $fname, $ftext )
  497. {
  498. $fp = fopen ( $fname, "wb" );
  499. if ( ! $fp ) {
  500. die ( "Unable to open $fname file for output!" );
  501. }
  502. fwrite ( $fp, $ftext );
  503. fclose ( $fp );
  504. }
  505. /**
  506. * Delete a file or recursively delete a directory tree
  507. *
  508. * @param string $file_or_dir_name name of file or directory to delete
  509. * @return bool Returns TRUE on success, FALSE on failure
  510. */
  511. function rm_file_or_dir ( $file_or_dir_name )
  512. {
  513. if ( ! file_exists ( $file_or_dir_name ) ) {
  514. return false;
  515. }
  516. if ( is_file ( $file_or_dir_name ) || is_link ( $file_or_dir_name ) ) {
  517. return unlink ( $file_or_dir_name );
  518. }
  519. $dir = dir ( $file_or_dir_name );
  520. while ( ( $dir_entry = $dir->read () ) !== false ) {
  521. if ( $dir_entry == '.' || $dir_entry == '..') {
  522. continue;
  523. }
  524. rm_file_or_dir ( $file_or_dir_name . '/' . $dir_entry );
  525. }
  526. $dir->close();
  527. return rmdir ( $file_or_dir_name );
  528. }
  529. ////
  530. // Debugging functions
  531. ////
  532. /**
  533. * Emit html code to display given array of compile options (flags)
  534. *
  535. * @param array $flags array of compile options for this build
  536. *
  537. * @return void
  538. */
  539. function show_flags ( $flags )
  540. {
  541. echo ( "\$flags contains " . count ( $flags ) . " elements:" . "<br>" );
  542. foreach ( $flags as $key => $flag ) {
  543. echo ( "\$flags[" . $key . "]=" . "\"$flag\"" . "<br>" );
  544. }
  545. }
  546. /**
  547. * Emit HTML code to display default array of compile options (flags)
  548. *
  549. * $flag_table contains default compile options and properties. This
  550. * routine outputs HTML code to display all properties of $flag_table.
  551. *
  552. * @return void
  553. */
  554. function dump_flag_table ()
  555. {
  556. global $flag_table;
  557. echo ( "\$flag_table contains " . count ( $flag_table ) . " elements:" . "<br>" );
  558. foreach ( $flag_table as $key => $props ) {
  559. print ( "flag_table[" . $key . "] = " . "<br>" );
  560. foreach ( $props as $key2 => $props2 ) {
  561. print ( "&nbsp;&nbsp;&nbsp;" . $key2 . " = " . $props2 . "<br>" );
  562. }
  563. }
  564. }
  565. // Parse src/bin/NIC file
  566. list ( $nics, $roms ) = parse_nic_file ();
  567. // For emacs:
  568. // Local variables:
  569. // c-basic-offset: 4
  570. // c-indent-level: 4
  571. // tab-width: 4
  572. // End:
  573. ?>