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