0dd5102e67860ddebe9ef550276461f782ada162
[moodle.git] / admin / cli / install.php
1 <?php
3 // This file is part of Moodle - http://moodle.org/
4 //
5 // Moodle is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // Moodle is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
18 /**
19  * This script creates config.php file and prepares database.
20  *
21  * This script is not intended for beginners!
22  * Potential problems:
23  * - environment check is not present yet
24  * - su to apache account or sudo before execution
25  * - not compatible with Windows platform
26  *
27  * @package    core
28  * @subpackage cli
29  * @copyright  2009 Petr Skoda (http://skodak.org)
30  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
31  */
33 define('CLI_SCRIPT', true);
35 // extra execution prevention - we can not just require config.php here
36 if (isset($_SERVER['REMOTE_ADDR'])) {
37     exit(1);
38 }
40 $help =
41 "Command line Moodle installer, creates config.php and initializes database.
42 Please note you must execute this script with the same uid as apache
43 or use chmod/chown after installation.
45 Site defaults may be changed via local/defaults.php.
47 Options:
48 --chmod=OCTAL-MODE    Permissions of new directories created within dataroot.
49                       Default is 2777. You may want to change it to 2770
50                       or 2750 or 750. See chmod man page for details.
51 --lang=CODE           Installation and default site language.
52 --wwwroot=URL         Web address for the Moodle site,
53                       required in non-interactive mode.
54 --dataroot=DIR        Location of the moodle data folder,
55                       must not be web accessible. Default is moodledata
56                       in the parent directory.
57 --dbtype=TYPE         Database type. Default is mysqli
58 --dbhost=HOST         Database host. Default is localhost
59 --dbname=NAME         Database name. Default is moodle
60 --dbuser=USERNAME     Database user. Default is root
61 --dbpass=PASSWORD     Database password. Default is blank
62 --dbsocket            Use database sockets. Available for some databases only.
63 --prefix=STRING       Table prefix for above database tables. Default is mdl_
64 --fullname=STRING     The fullname of the site
65 --shortname=STRING    The shortname of the site
66 --adminuser=USERNAME  Username for the moodle admin account. Default is admin
67 --adminpass=PASSWORD  Password for the moodle admin account,
68                       required in non-interactive mode.
69 --non-interactive     No interactive questions, installation fails if any
70                       problem encountered.
71 --agree-license       Indicates agreement with software license,
72                       required in non-interactive mode.
73 --allow-unstable      Install even if the version is not marked as stable yet,
74                       required in non-interactive mode.
75 -h, --help            Print out this help
77 Example:
78 \$sudo -u www-data /usr/bin/php admin/cli/install.php --lang=cs
79 "; //TODO: localize, mark as needed in install - to be translated later when everything is finished
82 // Nothing to do if config.php exists
83 $configfile = dirname(dirname(dirname(__FILE__))).'/config.php';
84 if (file_exists($configfile)) {
85     require($configfile);
86     require_once($CFG->libdir.'/clilib.php');
87     list($options, $unrecognized) = cli_get_params(array('help'=>false), array('h'=>'help'));
89     if ($options['help']) {
90         echo $help;
91         echo "\n\n";
92     }
94     cli_error(get_string('clialreadyinstalled', 'install'));
95 }
97 $olddir = getcwd();
99 // change directory so that includes bellow work properly
100 chdir(dirname($_SERVER['argv'][0]));
102 // Servers should define a default timezone in php.ini, but if they don't then make sure something is defined.
103 // This is a quick hack.  Ideally we should ask the admin for a value.  See MDL-22625 for more on this.
104 if (function_exists('date_default_timezone_set') and function_exists('date_default_timezone_get')) {
105     @date_default_timezone_set(@date_default_timezone_get());
108 // make sure PHP errors are displayed - helps with diagnosing of problems
109 @error_reporting(E_ALL);
110 @ini_set('display_errors', '1');
111 // we need a lot of memory
112 @ini_set('memory_limit', '128M');
114 /** Used by library scripts to check they are being called by Moodle */
115 define('MOODLE_INTERNAL', true);
117 // Check that PHP is of a sufficient version
118 if (version_compare(phpversion(), "5.3.2") < 0) {
119     $phpversion = phpversion();
120     // do NOT localise - lang strings would not work here and we CAN NOT move it after installib
121     fwrite(STDERR, "Moodle 2.1 or later requires at least PHP 5.3.2 (currently using version $phpversion).\n");
122     fwrite(STDERR, "Please upgrade your server software or install older Moodle version.\n");
123     exit(1);
126 // set up configuration
127 $CFG = new stdClass();
128 $CFG->lang                 = 'en';
129 $CFG->dirroot              = dirname(dirname(dirname(__FILE__)));
130 $CFG->libdir               = "$CFG->dirroot/lib";
131 $CFG->wwwroot              = "http://localhost";
132 $CFG->httpswwwroot         = $CFG->wwwroot;
133 $CFG->dataroot             = str_replace('\\', '/', dirname(dirname(dirname(dirname(__FILE__)))).'/moodledata');
134 $CFG->docroot              = 'http://docs.moodle.org';
135 $CFG->running_installer    = true;
136 $CFG->early_install_lang   = true;
138 $parts = explode('/', str_replace('\\', '/', dirname(dirname(__FILE__))));
139 $CFG->admin                = array_pop($parts);
141 //point pear include path to moodles lib/pear so that includes and requires will search there for files before anywhere else
142 //the problem is that we need specific version of quickforms and hacked excel files :-(
143 ini_set('include_path', $CFG->libdir.'/pear' . PATH_SEPARATOR . ini_get('include_path'));
145 require_once($CFG->libdir.'/installlib.php');
146 require_once($CFG->libdir.'/clilib.php');
147 require_once($CFG->libdir.'/setuplib.php');
148 require_once($CFG->libdir.'/textlib.class.php');
149 require_once($CFG->libdir.'/weblib.php');
150 require_once($CFG->libdir.'/dmllib.php');
151 require_once($CFG->libdir.'/moodlelib.php');
152 require_once($CFG->libdir.'/deprecatedlib.php');
153 require_once($CFG->libdir.'/adminlib.php');
154 require_once($CFG->libdir.'/componentlib.class.php');
156 require($CFG->dirroot.'/version.php');
157 $CFG->target_release = $release;
159 //Database types
160 $databases = array('mysqli' => moodle_database::get_driver_instance('mysqli', 'native'),
161                    'pgsql'  => moodle_database::get_driver_instance('pgsql',  'native'),
162                    'oci'    => moodle_database::get_driver_instance('oci',    'native'),
163                    'sqlsrv' => moodle_database::get_driver_instance('sqlsrv', 'native'), // MS SQL*Server PHP driver
164                    'mssql'  => moodle_database::get_driver_instance('mssql',  'native'), // FreeTDS driver
165                   );
166 foreach ($databases as $type=>$database) {
167     if ($database->driver_installed() !== true) {
168         unset($databases[$type]);
169     }
171 if (empty($databases)) {
172     $defaultdb = '';
173 } else {
174     reset($databases);
175     $defaultdb = key($databases);
178 // now get cli options
179 list($options, $unrecognized) = cli_get_params(
180     array(
181         'chmod'             => '2777',
182         'lang'              => $CFG->lang,
183         'wwwroot'           => '',
184         'dataroot'          => $CFG->dataroot,
185         'dbtype'            => $defaultdb,
186         'dbhost'            => 'localhost',
187         'dbname'            => 'moodle',
188         'dbuser'            => 'root',
189         'dbpass'            => '',
190         'dbsocket'          => false,
191         'prefix'            => 'mdl_',
192         'fullname'          => '',
193         'shortname'         => '',
194         'adminuser'         => 'admin',
195         'adminpass'         => '',
196         'non-interactive'   => false,
197         'agree-license'     => false,
198         'allow-unstable'    => false,
199         'help'              => false
200     ),
201     array(
202         'h' => 'help'
203     )
204 );
206 $interactive = empty($options['non-interactive']);
208 // set up language
209 $lang = clean_param($options['lang'], PARAM_SAFEDIR);
210 if (file_exists($CFG->dirroot.'/install/lang/'.$lang)) {
211     $CFG->lang = $lang;
214 if ($unrecognized) {
215     $unrecognized = implode("\n  ", $unrecognized);
216     cli_error(get_string('cliunknowoption', 'admin', $unrecognized));
219 if ($options['help']) {
220     echo $help;
221     die;
224 //Print header
225 echo get_string('cliinstallheader', 'install', $CFG->target_release)."\n";
227 //Fist select language
228 if ($interactive) {
229     cli_separator();
230     $languages = get_string_manager()->get_list_of_translations();
231     // format the langs nicely - 3 per line
232     $c = 0;
233     $langlist = '';
234     foreach ($languages as $key=>$lang) {
235         $c++;
236         $length = iconv_strlen($lang, 'UTF-8');
237         $padded = $lang.str_repeat(' ', 38-$length);
238         $langlist .= $padded;
239         if ($c % 3 == 0) {
240             $langlist .= "\n";
241         }
242     }
243     $default = $CFG->lang;
244     cli_heading(get_string('availablelangs', 'install'));
245     echo $langlist."\n";
246     $prompt = get_string('clitypevaluedefault', 'admin', $CFG->lang);
247     $error = '';
248     do {
249         echo $error;
250         $input = cli_input($prompt, $default);
251         $input = clean_param($input, PARAM_SAFEDIR);
253         if (!file_exists($CFG->dirroot.'/install/lang/'.$input)) {
254             $error = get_string('cliincorrectvalueretry', 'admin')."\n";
255         } else {
256             $error = '';
257         }
258     } while ($error !== '');
259     $CFG->lang = $input;
260 } else {
261     // already selected and verified
264 // Set directorypermissions first
265 $chmod = octdec(clean_param($options['chmod'], PARAM_INT));
266 if ($interactive) {
267     cli_separator();
268     cli_heading('Data directories permission'); // todo localize
269     $prompt = get_string('clitypevaluedefault', 'admin', decoct($chmod));
270     $error = '';
271     do {
272         echo $error;
273         $input = cli_input($prompt, decoct($chmod));
274         $input = octdec(clean_param($input, PARAM_INT));
275         if (empty($input)) {
276             $error = get_string('cliincorrectvalueretry', 'admin')."\n";
277         } else {
278             $error = '';
279         }
280     } while ($error !== '');
281     $chmod = $input;
283 } else {
284     if (empty($chmod)) {
285         $a = (object)array('option' => 'chmod', 'value' => decoct($chmod));
286         cli_error(get_string('cliincorrectvalueerror', 'admin', $a));
287     }
289 $CFG->directorypermissions = $chmod;
291 //We need wwwroot before we test dataroot
292 $wwwroot = clean_param($options['wwwroot'], PARAM_URL);
293 $wwwroot = trim($wwwroot, '/');
294 if ($interactive) {
295     cli_separator();
296     cli_heading(get_string('wwwroot', 'install'));
297     if (strpos($wwwroot, 'http') === 0) {
298         $prompt = get_string('clitypevaluedefault', 'admin', $wwwroot);
299     } else {
300         $wwwroot = null;
301         $prompt = get_string('clitypevalue', 'admin');
302     }
303     $error = '';
304     do {
305         echo $error;
306         $input = cli_input($prompt, $wwwroot);
307         $input = clean_param($input, PARAM_URL);
308         $input = trim($input, '/');
309         if (strpos($input, 'http') !== 0) {
310             $error = get_string('cliincorrectvalueretry', 'admin')."\n";
311         } else {
312             $error = '';
313         }
314     } while ($error !== '');
315     $wwwroot = $input;
317 } else {
318     if (strpos($wwwroot, 'http') !== 0) {
319         $a = (object)array('option'=>'wwwroot', 'value'=>$wwwroot);
320         cli_error(get_string('cliincorrectvalueerror', 'admin', $a));
321     }
323 $CFG->wwwroot       = $wwwroot;
324 $CFG->httpswwwroot  = $CFG->wwwroot;
327 //We need dataroot before lang download
328 if (!empty($options['dataroot'])) {
329     $CFG->dataroot = $options['dataroot'];
331 if ($interactive) {
332     cli_separator();
333     $i=0;
334     while(is_dataroot_insecure()) {
335         $parrent = dirname($CFG->dataroot);
336         $i++;
337         if ($parrent == '/' or $parrent == '.' or preg_match('/^[a-z]:\\\?$/i', $parrent) or ($i > 100)) {
338             $CFG->dataroot = ''; //can not find secure location for dataroot
339             break;
340         }
341         $CFG->dataroot = dirname($parrent).'/moodledata';
342     }
343     cli_heading(get_string('dataroot', 'install'));
344     $error = '';
345     do {
346         if ($CFG->dataroot !== '') {
347             $prompt = get_string('clitypevaluedefault', 'admin', $CFG->dataroot);
348         } else {
349             $prompt = get_string('clitypevalue', 'admin');
350         }
351         echo $error;
352         $CFG->dataroot = cli_input($prompt, $CFG->dataroot);
353         if ($CFG->dataroot === '') {
354             $error = get_string('cliincorrectvalueretry', 'admin')."\n";
355         } else if (is_dataroot_insecure()) {
356             $CFG->dataroot = '';
357             $error = get_string('pathsunsecuredataroot', 'install')."\n";
358         } else {
359             if (install_init_dataroot($CFG->dataroot, $CFG->directorypermissions)) {
360                 $error = '';
361             } else {
362                 $a = (object)array('dataroot' => $CFG->dataroot);
363                 $error = get_string('pathserrcreatedataroot', 'install', $a)."\n";
364             }
365         }
367     } while ($error !== '');
369 } else {
370     if (is_dataroot_insecure()) {
371         cli_error(get_string('pathsunsecuredataroot', 'install'));
372     }
373     if (!install_init_dataroot($CFG->dataroot, $CFG->directorypermissions)) {
374         $a = (object)array('dataroot' => $CFG->dataroot);
375         cli_error(get_string('pathserrcreatedataroot', 'install', $a));
376     }
379 // download required lang packs
380 if ($CFG->lang !== 'en') {
381     $installer = new lang_installer($CFG->lang);
382     $results = $installer->run();
383     foreach ($results as $langcode => $langstatus) {
384         if ($langstatus === lang_installer::RESULT_DOWNLOADERROR) {
385             $a       = new stdClass();
386             $a->url  = $installer->lang_pack_url($langcode);
387             $a->dest = $CFG->dataroot.'/lang';
388             cli_problem(get_string('remotedownloaderror', 'error', $a));
389         }
390     }
393 // switch the string_manager instance to stop using install/lang/
394 $CFG->early_install_lang = false;
395 $CFG->langotherroot      = $CFG->dataroot.'/lang';
396 $CFG->langlocalroot      = $CFG->dataroot.'/lang';
397 get_string_manager(true);
399 // make sure we are installing stable release or require a confirmation
400 if (isset($maturity)) {
401     if (($maturity < MATURITY_STABLE) and !$options['allow-unstable']) {
402         $maturitylevel = get_string('maturity'.$maturity, 'admin');
404         if ($interactive) {
405             cli_separator();
406             cli_heading(get_string('notice'));
407             echo get_string('maturitycorewarning', 'admin', $maturitylevel) . PHP_EOL;
408             echo get_string('morehelp') . ': ' . get_docs_url('admin/versions') . PHP_EOL;
409             echo get_string('continue') . PHP_EOL;
410             $prompt = get_string('cliyesnoprompt', 'admin');
411             $input = cli_input($prompt, '', array(get_string('clianswerno', 'admin'), get_string('cliansweryes', 'admin')));
412             if ($input == get_string('clianswerno', 'admin')) {
413                 exit(1);
414             }
415         } else {
416             cli_error(get_string('maturitycorewarning', 'admin'));
417         }
418     }
421 // ask for db type - show only drivers available
422 if ($interactive) {
423     $options['dbtype'] = strtolower($options['dbtype']);
424     cli_separator();
425     cli_heading(get_string('databasetypehead', 'install'));
426     foreach ($databases as $type=>$database) {
427         echo " $type \n";
428     }
429     if (!empty($databases[$options['dbtype']])) {
430         $prompt = get_string('clitypevaluedefault', 'admin', $options['dbtype']);
431     } else {
432         $prompt = get_string('clitypevalue', 'admin');
433     }
434     $CFG->dbtype = cli_input($prompt, $options['dbtype'], array_keys($databases));
436 } else {
437     if (empty($databases[$options['dbtype']])) {
438         $a = (object)array('option'=>'dbtype', 'value'=>$options['dbtype']);
439         cli_error(get_string('cliincorrectvalueerror', 'admin', $a));
440     }
441     $CFG->dbtype = $options['dbtype'];
443 $database = $databases[$CFG->dbtype];
446 // ask for db host
447 if ($interactive) {
448     cli_separator();
449     cli_heading(get_string('databasehost', 'install'));
450     if ($options['dbhost'] !== '') {
451         $prompt = get_string('clitypevaluedefault', 'admin', $options['dbhost']);
452     } else {
453         $prompt = get_string('clitypevalue', 'admin');
454     }
455     $CFG->dbhost = cli_input($prompt, $options['dbhost']);
457 } else {
458     $CFG->dbhost = $options['dbhost'];
461 // ask for db name
462 if ($interactive) {
463     cli_separator();
464     cli_heading(get_string('databasename', 'install'));
465     if ($options['dbname'] !== '') {
466         $prompt = get_string('clitypevaluedefault', 'admin', $options['dbname']);
467     } else {
468         $prompt = get_string('clitypevalue', 'admin');
469     }
470     $CFG->dbname = cli_input($prompt, $options['dbname']);
472 } else {
473     $CFG->dbname = $options['dbname'];
476 // ask for db prefix
477 if ($interactive) {
478     cli_separator();
479     cli_heading(get_string('dbprefix', 'install'));
480     //TODO: solve somehow the prefix trouble for oci
481     if ($options['prefix'] !== '') {
482         $prompt = get_string('clitypevaluedefault', 'admin', $options['prefix']);
483     } else {
484         $prompt = get_string('clitypevalue', 'admin');
485     }
486     $CFG->prefix = cli_input($prompt, $options['prefix']);
488 } else {
489     $CFG->prefix = $options['prefix'];
492 // ask for db user
493 if ($interactive) {
494     cli_separator();
495     cli_heading(get_string('databaseuser', 'install'));
496     if ($options['dbuser'] !== '') {
497         $prompt = get_string('clitypevaluedefault', 'admin', $options['dbuser']);
498     } else {
499         $prompt = get_string('clitypevalue', 'admin');
500     }
501     $CFG->dbuser = cli_input($prompt, $options['dbuser']);
503 } else {
504     $CFG->dbuser = $options['dbuser'];
507 // ask for db password
508 if ($interactive) {
509     cli_separator();
510     cli_heading(get_string('databasepass', 'install'));
511     do {
512         if ($options['dbpass'] !== '') {
513             $prompt = get_string('clitypevaluedefault', 'admin', $options['dbpass']);
514         } else {
515             $prompt = get_string('clitypevalue', 'admin');
516         }
518         $CFG->dbpass = cli_input($prompt, $options['dbpass']);
519         $hint_database = install_db_validate($database, $CFG->dbhost, $CFG->dbuser, $CFG->dbpass, $CFG->dbname, $CFG->prefix, array('dbpersist'=>0, 'dbsocket'=>$options['dbsocket']));
520     } while ($hint_database !== '');
522 } else {
523     $CFG->dbpass = $options['dbpass'];
524     $hint_database = install_db_validate($database, $CFG->dbhost, $CFG->dbuser, $CFG->dbpass, $CFG->dbname, $CFG->prefix, array('dbpersist'=>0, 'dbsocket'=>$options['dbsocket']));
525     if ($hint_database !== '') {
526         cli_error(get_string('dbconnectionerror', 'install'));
527     }
530 // ask for fullname
531 if ($interactive) {
532     cli_separator();
533     cli_heading(get_string('fullsitename', 'moodle'));
535     if ($options['fullname'] !== '') {
536         $prompt = get_string('clitypevaluedefault', 'admin', $options['fullname']);
537     } else {
538         $prompt = get_string('clitypevalue', 'admin');
539     }
541     do {
542         $options['fullname'] = cli_input($prompt, $options['fullname']);
543     } while (empty($options['fullname']));
544 } else {
545     if (empty($options['fullname'])) {
546         $a = (object)array('option'=>'fullname', 'value'=>$options['fullname']);
547         cli_error(get_string('cliincorrectvalueerror', 'admin', $a));
548     }
551 // ask for shortname
552 if ($interactive) {
553     cli_separator();
554     cli_heading(get_string('shortsitename', 'moodle'));
556     if ($options['shortname'] !== '') {
557         $prompt = get_string('clitypevaluedefault', 'admin', $options['shortname']);
558     } else {
559         $prompt = get_string('clitypevalue', 'admin');
560     }
562     do {
563         $options['shortname'] = cli_input($prompt, $options['shortname']);
564     } while (empty($options['shortname']));
565 } else {
566     if (empty($options['shortname'])) {
567         $a = (object)array('option'=>'shortname', 'value'=>$options['shortname']);
568         cli_error(get_string('cliincorrectvalueerror', 'admin', $a));
569     }
572 // ask for admin user name
573 if ($interactive) {
574     cli_separator();
575     cli_heading(get_string('cliadminusername', 'install'));
576     if (!empty($options['adminuser'])) {
577         $prompt = get_string('clitypevaluedefault', 'admin', $options['adminuser']);
578     } else {
579         $prompt = get_string('clitypevalue', 'admin');
580     }
581     do {
582         $options['adminuser'] = cli_input($prompt, $options['adminuser']);
583     } while (empty($options['adminuser']) or $options['adminuser'] === 'guest');
584 } else {
585     if (empty($options['adminuser']) or $options['adminuser'] === 'guest') {
586         $a = (object)array('option'=>'adminuser', 'value'=>$options['adminuser']);
587         cli_error(get_string('cliincorrectvalueerror', 'admin', $a));
588     }
591 // ask for admin user password
592 if ($interactive) {
593     cli_separator();
594     cli_heading(get_string('cliadminpassword', 'install'));
595     $prompt = get_string('clitypevalue', 'admin');
596     do {
597         $options['adminpass'] = cli_input($prompt);
598     } while (empty($options['adminpass']) or $options['adminpass'] === 'admin');
599 } else {
600     if (empty($options['adminpass']) or $options['adminpass'] === 'admin') {
601         $a = (object)array('option'=>'adminpass', 'value'=>$options['adminpass']);
602         cli_error(get_string('cliincorrectvalueerror', 'admin', $a));
603     }
606 if ($interactive) {
607     if (!$options['agree-license']) {
608         cli_separator();
609         cli_heading(get_string('copyrightnotice'));
610         echo "Moodle  - Modular Object-Oriented Dynamic Learning Environment\n";
611         echo get_string('gpl3')."\n\n";
612         echo get_string('doyouagree')."\n";
613         $prompt = get_string('cliyesnoprompt', 'admin');
614         $input = cli_input($prompt, '', array(get_string('clianswerno', 'admin'), get_string('cliansweryes', 'admin')));
615         if ($input == get_string('clianswerno', 'admin')) {
616             exit(1);
617         }
618     }
619 } else {
620     if (!$options['agree-license']) {
621         cli_error(get_string('climustagreelicense', 'install'));
622     }
625 // Finally we have all info needed for config.php
626 $configphp = install_generate_configphp($database, $CFG);
627 umask(0137);
628 if (($fh = fopen($configfile, 'w')) !== false) {
629     fwrite($fh, $configphp);
630     fclose($fh);
633 if (!file_exists($configfile)) {
634     cli_error('Can not create config file.');
637 // remember selected language
638 $installlang = $CFG->lang;
639 // return back to original dir before executing setup.php which changes the dir again
640 chdir($olddir);
641 // We have config.php, it is a real php script from now on :-)
642 require($configfile);
644 // use selected language
645 $CFG->lang = $installlang;
646 $SESSION->lang = $CFG->lang;
648 install_cli_database($options, $interactive);
650 echo get_string('cliinstallfinished', 'install')."\n";
651 exit(0); // 0 means success