r2906 - maintenance->trunk sync of changesets 2886~2894
[racktables] / install.php
1 <?php
2
3 // This script is intended for execution through a web-browser, e.g.:
4 // https://example.com/racktables/install.php
5 // See README file for more information.
6
7 $stepfunc[1] = 'not_already_installed';
8 $stepfunc[2] = 'platform_is_ok';
9 $stepfunc[3] = 'init_config';
10 $stepfunc[4] = 'init_database_static';
11 $stepfunc[5] = 'init_database_dynamic';
12 $stepfunc[6] = 'congrats';
13 $dbxlink = NULL;
14
15 if (isset ($_REQUEST['step']))
16 $step = $_REQUEST['step'];
17 else
18 $step = 1;
19
20 if ($step > count ($stepfunc))
21 {
22 require 'inc/init.php';
23 global $root;
24 header ("Location: " . $root);
25 exit;
26 }
27 $title = "RackTables installation: step ${step} of " . count ($stepfunc);
28 require_once ('inc/dictionary.php');
29 ?>
30 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
31 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
32 <head><title><?php echo $title; ?></title>
33 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
34 <link rel=stylesheet type='text/css' href=pi.css />
35 </head>
36 <body>
37 <center>
38 <?php
39 echo "<h1>${title}</h1><p>";
40
41 echo "</p><form method=post>\n";
42 $testres = $stepfunc[$step] ();
43 if ($testres)
44 {
45 $next_step = $step + 1;
46 echo "<input type=submit value='proceed'>";
47 }
48 else
49 {
50 $next_step = $step;
51 echo "<input type=submit value='retry'>";
52 }
53 echo "<input type=hidden name=step value='${next_step}'>\n";
54
55 ?>
56 </form>
57 </center>
58 </body>
59 </html>
60
61 <?php
62 // Check if the software is already installed.
63 function not_already_installed()
64 {
65 @include ('inc/secret.php');
66 if (isset ($pdo_dsn))
67 {
68 echo 'Your configuration file exists and seems to hold necessary data already.<br>';
69 return FALSE;
70 }
71 else
72 {
73 echo 'There seem to be no existing installation here, I am going to setup one now.<br>';
74 return TRUE;
75 }
76 }
77
78 // Check for PHP extensions.
79 function platform_is_ok ()
80 {
81 $nerrs = 0;
82 echo "<table border=1><tr><th>check item</th><th>result</th></tr>\n";
83
84 echo '<tr><td>PDO extension</td>';
85 if (class_exists ('PDO'))
86 echo '<td class=msg_success>Ok';
87 else
88 {
89 echo '<td class=msg_error>not found';
90 $nerrs++;
91 }
92 echo '</td></tr>';
93
94 echo '<tr><td>PDO-MySQL</td>';
95 if (defined ('PDO::MYSQL_ATTR_READ_DEFAULT_FILE'))
96 echo '<td class=msg_success>Ok';
97 else
98 {
99 echo '<td class=msg_error>not found';
100 $nerrs++;
101 }
102 echo '</td></tr>';
103
104 echo '<tr><td>hash function</td>';
105 if (function_exists ('sha1'))
106 echo '<td class=msg_success>Ok';
107 else
108 {
109 echo '<td class=msg_error>not found';
110 $nerrs++;
111 }
112 echo '</td></tr>';
113
114 echo '<tr><td>SNMP extension</td>';
115 if (defined ('SNMP_NULL'))
116 echo '<td class=msg_success>Ok';
117 else
118 echo '<td class=msg_warning>Not found. Live SNMP tab will not function properly until the extension is installed.';
119 echo '</td></tr>';
120
121 echo '<tr><td>GD functions</td>';
122 if (defined ('IMG_PNG'))
123 echo '<td class=msg_success>Ok';
124 else
125 {
126 echo '<td class=msg_error>not found';
127 $nerrs++;
128 }
129 echo '</td></tr>';
130
131 echo '<tr><td>HTTP scheme</td>';
132 if (!empty($_SERVER['HTTPS']) and $_SERVER['HTTPS'] != 'off')
133 echo '<td class=msg_success>HTTPs';
134 else
135 echo '<td class=msg_warning>HTTP (all your passwords will be transmitted in cleartext)';
136 echo '</td></tr>';
137
138 echo '<tr><td>Multibyte string extension</td>';
139 if (defined ('MB_CASE_LOWER'))
140 echo '<td class=msg_success>Ok';
141 else
142 {
143 echo '<td class=msg_error>not found';
144 $nerrs++;
145 }
146 echo '</td></tr>';
147
148 echo '<tr><td>LDAP extension</td>';
149 if (defined ('LDAP_OPT_DEREF'))
150 echo '<td class=msg_success>Ok';
151 else
152 {
153 echo '<td class=msg_warning>not found, LDAP authentication will not work';
154 }
155 echo '</td></tr>';
156
157 echo "</table>\n";
158 return !$nerrs;
159 }
160
161 // Check that we can write to configuration file.
162 // If so, ask for DB connection paramaters and test
163 // the connection. Neither save the parameters nor allow
164 // going further until we succeed with the given
165 // credentials.
166 function init_config ()
167 {
168 if (!is_writable ('inc/secret.php'))
169 {
170 echo "The inc/secret.php file is not writable by web-server. Make sure it is.";
171 echo "The following commands should suffice:<pre>touch inc/secret.php\nchmod 666 inc/secret.php</pre>";
172 echo 'Fedora Linux with SELinux may require this file to be owned by specific user (apache) and/or executing "setenforce 0" for the time of installation. ';
173 echo 'SELinux may be turned back on with "setenforce 1" command.';
174 return FALSE;
175 }
176 if
177 (
178 !isset ($_REQUEST['save_config']) or
179 empty ($_REQUEST['mysql_host']) or
180 empty ($_REQUEST['mysql_db']) or
181 empty ($_REQUEST['mysql_username']) or
182 empty ($_REQUEST['mysql_password'])
183 )
184 {
185 echo "<input type=hidden name=save_config value=1>\n";
186 echo '<table>';
187 echo "<tr><td><label for=mysql_host>MySQL host:</label></td>";
188 echo "<td><input type=text name=mysql_host id=mysql_host value=localhost></td></tr>\n";
189 echo "<tr><td><label for=mysql_host>database:</label></td>";
190 echo "<td><input type=text name=mysql_db id=mysql_db value=racktables></td></tr>\n";
191 echo "<tr><td><label for=mysql_username>username:</label></td>";
192 echo "<td><input type=text name=mysql_username></td></tr>\n";
193 echo "<tr><td><label for=mysql_password>password:</label></td>";
194 echo "<td><input type=password name=mysql_password></td></tr>\n";
195 echo '</table>';
196 return FALSE;
197 }
198 $pdo_dsn = 'mysql:host=' . $_REQUEST['mysql_host'] . ';dbname=' . $_REQUEST['mysql_db'];
199 try
200 {
201 $dbxlink = new PDO ($pdo_dsn, $_REQUEST['mysql_username'], $_REQUEST['mysql_password']);
202 }
203 catch (PDOException $e)
204 {
205 echo "<input type=hidden name=save_config value=1>\n";
206 echo '<table>';
207 echo "<tr><td><label for=mysql_host>MySQL host:</label></td>";
208 echo "<td><input type=text name=mysql_host id=mysql_host value='" . $_REQUEST['mysql_host'] . "'></td></tr>\n";
209 echo "<tr><td><label for=mysql_host>database:</label></td>";
210 echo "<td><input type=text name=mysql_db id=mysql_db value='" . $_REQUEST['mysql_db'] . "'></td></tr>\n";
211 echo "<tr><td><label for=mysql_username>username:</label></td>";
212 echo "<td><input type=text name=mysql_username value='" . $_REQUEST['mysql_username'] . "'></td></tr>\n";
213 echo "<tr><td><label for=mysql_password>password:</label></td>";
214 echo "<td><input type=password name=mysql_password value='" . $_REQUEST['mysql_password'] . "'></td></tr>\n";
215 echo "<tr><td colspan=2>The above parameters did not work. Check and try again.</td></tr>\n";
216 echo '</table>';
217 return FALSE;
218 }
219
220 // Make sure InnoDB is supported
221 require_once 'inc/database.php';
222 if (!isInnoDBSupported ($dbxlink))
223 {
224 echo 'Error: InnoDB support is disabled. See the README for details.';
225 return FALSE;
226 }
227
228 $conf = fopen ('inc/secret.php', 'w+');
229 if ($conf === FALSE)
230 {
231 echo "Error: failed to open inc/secret.php for writing";
232 return FALSE;
233 }
234 fwrite ($conf, "<?php\n/* This file has been generated automatically by RackTables installer.\n");
235 fwrite ($conf, " * you shouldn't normally edit it unless your database setup has changed.\n");
236 fwrite ($conf, " */\n");
237 fwrite ($conf, "\$pdo_dsn = '${pdo_dsn}';\n");
238 fwrite ($conf, "\$db_username = '" . $_REQUEST['mysql_username'] . "';\n");
239 fwrite ($conf, "\$db_password = '" . $_REQUEST['mysql_password'] . "';\n\n");
240 fwrite ($conf, <<<ENDOFTEXT
241 // Default setting is to authenticate users locally, but it is possible to
242 // employ existing LDAP or Apache userbase. Uncommenting below two lines MAY
243 // help in switching authentication to LDAP completely.
244 // More info: http://racktables.org/trac/wiki/RackTablesUserAuthentication
245 #\$user_auth_src = 'ldap';
246 #\$require_local_account = FALSE;
247
248 // This is only necessary for 'ldap' authentication source
249 \$LDAP_options = array
250 (
251 'server' => 'some.server',
252 'domain' => 'some.domain',
253 # 'search_dn' => 'ou=people,O=YourCompany',
254 'search_attr' => 'uid',
255 # 'displayname_attrs' => 'givenname familyname',
256
257 // LDAP cache, values in seconds. Refresh, retry and expiry values are
258 // treated exactly as those for DNS SOA record. Example values 300-15-600:
259 // unconditionally remeber successful auth for 5 minutes, after that still
260 // permit user access, but try to revalidate username and password on the
261 // server (not more often, than once in 15 seconds). After 10 minutes of
262 // unsuccessful retries give up and deny access, so someone goes to fix
263 // LDAP server.
264 'cache_refresh' => 300,
265 'cache_retry' => 15,
266 'cache_expiry' => 600,
267 );
268
269
270 ENDOFTEXT
271 );
272 fwrite ($conf, "?>\n");
273 fclose ($conf);
274 echo "The configuration file has been written successfully.<br>";
275 return TRUE;
276 }
277
278 function connect_to_db ()
279 {
280 require ('inc/secret.php');
281 global $dbxlink;
282 try
283 {
284 $dbxlink = new PDO ($pdo_dsn, $db_username, $db_password);
285 }
286 catch (PDOException $e)
287 {
288 die ('Error connecting to the database');
289 }
290 }
291
292 function init_database_static ()
293 {
294 connect_to_db ();
295 global $dbxlink;
296 $result = $dbxlink->query ('show tables');
297 $tables = $result->fetchAll (PDO::FETCH_NUM);
298 $result->closeCursor();
299 unset ($result);
300 if (count ($tables))
301 {
302 echo 'Your database is already holding ' . count ($tables);
303 echo ' tables, so I will stop here and let you check it yourself.<br>';
304 echo 'There is some important data there probably.<br>';
305 return FALSE;
306 }
307 echo 'Initializing the database...<br>';
308 echo '<table border=1>';
309 echo "<tr><th>file</th><th>queries</th><th>errors</th></tr>";
310 $errlist = array();
311 foreach (array ('structure', 'dictbase') as $part)
312 {
313 $filename = "install/init-${part}.sql";
314 echo "<tr><td>${filename}</td>";
315 $f = fopen ("install/init-${part}.sql", 'r');
316 if ($f === FALSE)
317 {
318 echo "Failed to open install/init-${part}.sql for reading";
319 return FALSE;
320 }
321 $longq = '';
322 while (!feof ($f))
323 {
324 $line = fgets ($f);
325 if (ereg ('^--', $line))
326 continue;
327 $longq .= $line;
328 }
329 fclose ($f);
330 $nq = $nerrs = 0;
331 foreach (preg_split ("/;\s*\n/", $longq) as $query)
332 {
333 $query = trim($query);
334 if (empty ($query))
335 continue;
336 $nq++;
337 if ($dbxlink->exec ($query) === FALSE)
338 {
339 $nerrs++;
340 $errlist[] = $query;
341 }
342 }
343 echo "<td>${nq}</td><td>${nerrs}</td></tr>\n";
344 }
345 // (re)load dictionary by pure PHP means w/o any external file
346 echo "<tr><th>dictionary</th>";
347 $nq = $nerrs = 0;
348 global $dictreload;
349 $dictq = array();
350 foreach ($dictreload as $tmp)
351 foreach (reloadDictionary ($tmp['from'], $tmp['to']) as $query)
352 {
353 $nq++;
354 if ($dbxlink->exec ($query) === FALSE)
355 {
356 $nerrs++;
357 $errlist[] = $query;
358 }
359 }
360 echo "<td>${nq}</td><td>${nerrs}</td></tr>\n";
361
362 echo '</table>';
363 if (count ($errlist))
364 {
365 echo '<pre>The following queries failed:\n';
366 foreach ($errlist as $q)
367 echo "${q}\n\n";
368 echo '</pre>';
369 return FALSE;
370 }
371 return TRUE;
372 }
373
374 function init_database_dynamic ()
375 {
376 connect_to_db();
377 global $dbxlink;
378 if (!isset ($_REQUEST['password']) or empty ($_REQUEST['password']))
379 {
380 $result = $dbxlink->query ('select count(user_id) from UserAccount where user_id = 1');
381 $row = $result->fetch (PDO::FETCH_NUM);
382 $nrecs = $row[0];
383 $result->closeCursor();
384 if (!$nrecs)
385 {
386 echo '<table border=1>';
387 echo '<caption>Administrator password not set</caption>';
388 echo '<tr><td><input type=password name=password></td></tr>';
389 echo '</table>';
390 }
391 return FALSE;
392 }
393 else
394 {
395 // Never send cleartext password over the wire.
396 $hash = sha1 ($_REQUEST['password']);
397 $query = "INSERT INTO `UserAccount` (`user_id`, `user_name`, `user_password_hash`, `user_realname`) " .
398 "VALUES (1,'admin','${hash}','RackTables Administrator')";
399 $result = $dbxlink->exec ($query);
400 echo "Administrator password has been set successfully.<br>";
401 return TRUE;
402 }
403 }
404
405 function congrats ()
406 {
407 echo 'Congratulations! RackTables installation is complete. After pressing Proceed you will ';
408 echo 'enter the system. Authenticate with <strong>admin</strong> username.<br>';
409 echo "RackTables web-site runs some <a href='http://racktables.org/trac/wiki'>wiki</a> pages ";
410 echo "and <a href='http://racktables.org/trac/report/1'>a bug tracker</a>.<br>We have also got ";
411 echo "a <a href='http://www.freelists.org/list/racktables-users'>mailing list</a> for users. Have fun.<br>";
412 return TRUE;
413 }
414
415 ?>