MDL-29351 add missing temp and cache dirs to installers
[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->tempdir              = $CFG->dataroot.'/temp';
135 $CFG->cachedir             = $CFG->dataroot.'/temp';
136 $CFG->docroot              = 'http://docs.moodle.org';
137 $CFG->running_installer    = true;
138 $CFG->early_install_lang   = true;
140 $parts = explode('/', str_replace('\\', '/', dirname(dirname(__FILE__))));
141 $CFG->admin                = array_pop($parts);
143 //point pear include path to moodles lib/pear so that includes and requires will search there for files before anywhere else
144 //the problem is that we need specific version of quickforms and hacked excel files :-(
145 ini_set('include_path', $CFG->libdir.'/pear' . PATH_SEPARATOR . ini_get('include_path'));
147 require_once($CFG->libdir.'/installlib.php');
148 require_once($CFG->libdir.'/clilib.php');
149 require_once($CFG->libdir.'/setuplib.php');
150 require_once($CFG->libdir.'/textlib.class.php');
151 require_once($CFG->libdir.'/weblib.php');
152 require_once($CFG->libdir.'/dmllib.php');
153 require_once($CFG->libdir.'/moodlelib.php');
154 require_once($CFG->libdir.'/deprecatedlib.php');
155 require_once($CFG->libdir.'/adminlib.php');
156 require_once($CFG->libdir.'/componentlib.class.php');
158 require($CFG->dirroot.'/version.php');
159 $CFG->target_release = $release;
161 //Database types
162 $databases = array('mysqli' => moodle_database::get_driver_instance('mysqli', 'native'),
163                    'pgsql'  => moodle_database::get_driver_instance('pgsql',  'native'),
164                    'oci'    => moodle_database::get_driver_instance('oci',    'native'),
165                    'sqlsrv' => moodle_database::get_driver_instance('sqlsrv', 'native'), // MS SQL*Server PHP driver
166                    'mssql'  => moodle_database::get_driver_instance('mssql',  'native'), // FreeTDS driver
167                   );
168 foreach ($databases as $type=>$database) {
169     if ($database->driver_installed() !== true) {
170         unset($databases[$type]);
171     }
173 if (empty($databases)) {
174     $defaultdb = '';
175 } else {
176     reset($databases);
177     $defaultdb = key($databases);
180 // now get cli options
181 list($options, $unrecognized) = cli_get_params(
182     array(
183         'chmod'             => '2777',
184         'lang'              => $CFG->lang,
185         'wwwroot'           => '',
186         'dataroot'          => $CFG->dataroot,
187         'dbtype'            => $defaultdb,
188         'dbhost'            => 'localhost',
189         'dbname'            => 'moodle',
190         'dbuser'            => 'root',
191         'dbpass'            => '',
192         'dbsocket'          => false,
193         'prefix'            => 'mdl_',
194         'fullname'          => '',
195         'shortname'         => '',
196         'adminuser'         => 'admin',
197         'adminpass'         => '',
198         'non-interactive'   => false,
199         'agree-license'     => false,
200         'allow-unstable'    => false,
201         'help'              => false
202     ),
203     array(
204         'h' => 'help'
205     )
206 );
208 $interactive = empty($options['non-interactive']);
210 // set up language
211 $lang = clean_param($options['lang'], PARAM_SAFEDIR);
212 if (file_exists($CFG->dirroot.'/install/lang/'.$lang)) {
213     $CFG->lang = $lang;
216 if ($unrecognized) {
217     $unrecognized = implode("\n  ", $unrecognized);
218     cli_error(get_string('cliunknowoption', 'admin', $unrecognized));
221 if ($options['help']) {
222     echo $help;
223     die;
226 //Print header
227 echo get_string('cliinstallheader', 'install', $CFG->target_release)."\n";
229 //Fist select language
230 if ($interactive) {
231     cli_separator();
232     $languages = get_string_manager()->get_list_of_translations();
233     // format the langs nicely - 3 per line
234     $c = 0;
235     $langlist = '';
236     foreach ($languages as $key=>$lang) {
237         $c++;
238         $length = iconv_strlen($lang, 'UTF-8');
239         $padded = $lang.str_repeat(' ', 38-$length);
240         $langlist .= $padded;
241         if ($c % 3 == 0) {
242             $langlist .= "\n";
243         }
244     }
245     $default = $CFG->lang;
246     cli_heading(get_string('availablelangs', 'install'));
247     echo $langlist."\n";
248     $prompt = get_string('clitypevaluedefault', 'admin', $CFG->lang);
249     $error = '';
250     do {
251         echo $error;
252         $input = cli_input($prompt, $default);
253         $input = clean_param($input, PARAM_SAFEDIR);
255         if (!file_exists($CFG->dirroot.'/install/lang/'.$input)) {
256             $error = get_string('cliincorrectvalueretry', 'admin')."\n";
257         } else {
258             $error = '';
259         }
260     } while ($error !== '');
261     $CFG->lang = $input;
262 } else {
263     // already selected and verified
266 // Set directorypermissions first
267 $chmod = octdec(clean_param($options['chmod'], PARAM_INT));
268 if ($interactive) {
269     cli_separator();
270     cli_heading('Data directories permission'); // todo localize
271     $prompt = get_string('clitypevaluedefault', 'admin', decoct($chmod));
272     $error = '';
273     do {
274         echo $error;
275         $input = cli_input($prompt, decoct($chmod));
276         $input = octdec(clean_param($input, PARAM_INT));
277         if (empty($input)) {
278             $error = get_string('cliincorrectvalueretry', 'admin')."\n";
279         } else {
280             $error = '';
281         }
282     } while ($error !== '');
283     $chmod = $input;
285 } else {
286     if (empty($chmod)) {
287         $a = (object)array('option' => 'chmod', 'value' => decoct($chmod));
288         cli_error(get_string('cliincorrectvalueerror', 'admin', $a));
289     }
291 $CFG->directorypermissions = $chmod;
293 //We need wwwroot before we test dataroot
294 $wwwroot = clean_param($options['wwwroot'], PARAM_URL);
295 $wwwroot = trim($wwwroot, '/');
296 if ($interactive) {
297     cli_separator();
298     cli_heading(get_string('wwwroot', 'install'));
299     if (strpos($wwwroot, 'http') === 0) {
300         $prompt = get_string('clitypevaluedefault', 'admin', $wwwroot);
301     } else {
302         $wwwroot = null;
303         $prompt = get_string('clitypevalue', 'admin');
304     }
305     $error = '';
306     do {
307         echo $error;
308         $input = cli_input($prompt, $wwwroot);
309         $input = clean_param($input, PARAM_URL);
310         $input = trim($input, '/');
311         if (strpos($input, 'http') !== 0) {
312             $error = get_string('cliincorrectvalueretry', 'admin')."\n";
313         } else {
314             $error = '';
315         }
316     } while ($error !== '');
317     $wwwroot = $input;
319 } else {
320     if (strpos($wwwroot, 'http') !== 0) {
321         $a = (object)array('option'=>'wwwroot', 'value'=>$wwwroot);
322         cli_error(get_string('cliincorrectvalueerror', 'admin', $a));
323     }
325 $CFG->wwwroot       = $wwwroot;
326 $CFG->httpswwwroot  = $CFG->wwwroot;
329 //We need dataroot before lang download
330 if (!empty($options['dataroot'])) {
331     $CFG->dataroot = $options['dataroot'];
333 if ($interactive) {
334     cli_separator();
335     $i=0;
336     while(is_dataroot_insecure()) {
337         $parrent = dirname($CFG->dataroot);
338         $i++;
339         if ($parrent == '/' or $parrent == '.' or preg_match('/^[a-z]:\\\?$/i', $parrent) or ($i > 100)) {
340             $CFG->dataroot = ''; //can not find secure location for dataroot
341             break;
342         }
343         $CFG->dataroot = dirname($parrent).'/moodledata';
344     }
345     cli_heading(get_string('dataroot', 'install'));
346     $error = '';
347     do {
348         if ($CFG->dataroot !== '') {
349             $prompt = get_string('clitypevaluedefault', 'admin', $CFG->dataroot);
350         } else {
351             $prompt = get_string('clitypevalue', 'admin');
352         }
353         echo $error;
354         $CFG->dataroot = cli_input($prompt, $CFG->dataroot);
355         if ($CFG->dataroot === '') {
356             $error = get_string('cliincorrectvalueretry', 'admin')."\n";
357         } else if (is_dataroot_insecure()) {
358             $CFG->dataroot = '';
359             $error = get_string('pathsunsecuredataroot', 'install')."\n";
360         } else {
361             if (install_init_dataroot($CFG->dataroot, $CFG->directorypermissions)) {
362                 $error = '';
363             } else {
364                 $a = (object)array('dataroot' => $CFG->dataroot);
365                 $error = get_string('pathserrcreatedataroot', 'install', $a)."\n";
366             }
367         }
369     } while ($error !== '');
371 } else {
372     if (is_dataroot_insecure()) {
373         cli_error(get_string('pathsunsecuredataroot', 'install'));
374     }
375     if (!install_init_dataroot($CFG->dataroot, $CFG->directorypermissions)) {
376         $a = (object)array('dataroot' => $CFG->dataroot);
377         cli_error(get_string('pathserrcreatedataroot', 'install', $a));
378     }
381 // download required lang packs
382 if ($CFG->lang !== 'en') {
383     $installer = new lang_installer($CFG->lang);
384     $results = $installer->run();
385     foreach ($results as $langcode => $langstatus) {
386         if ($langstatus === lang_installer::RESULT_DOWNLOADERROR) {
387             $a       = new stdClass();
388             $a->url  = $installer->lang_pack_url($langcode);
389             $a->dest = $CFG->dataroot.'/lang';
390             cli_problem(get_string('remotedownloaderror', 'error', $a));
391         }
392     }
395 // switch the string_manager instance to stop using install/lang/
396 $CFG->early_install_lang = false;
397 $CFG->langotherroot      = $CFG->dataroot.'/lang';
398 $CFG->langlocalroot      = $CFG->dataroot.'/lang';
399 get_string_manager(true);
401 // make sure we are installing stable release or require a confirmation
402 if (isset($maturity)) {
403     if (($maturity < MATURITY_STABLE) and !$options['allow-unstable']) {
404         $maturitylevel = get_string('maturity'.$maturity, 'admin');
406         if ($interactive) {
407             cli_separator();
408             cli_heading(get_string('notice'));
409             echo get_string('maturitycorewarning', 'admin', $maturitylevel) . PHP_EOL;
410             echo get_string('morehelp') . ': ' . get_docs_url('admin/versions') . PHP_EOL;
411             echo get_string('continue') . PHP_EOL;
412             $prompt = get_string('cliyesnoprompt', 'admin');
413             $input = cli_input($prompt, '', array(get_string('clianswerno', 'admin'), get_string('cliansweryes', 'admin')));
414             if ($input == get_string('clianswerno', 'admin')) {
415                 exit(1);
416             }
417         } else {
418             cli_error(get_string('maturitycorewarning', 'admin'));
419         }
420     }
423 // ask for db type - show only drivers available
424 if ($interactive) {
425     $options['dbtype'] = strtolower($options['dbtype']);
426     cli_separator();
427     cli_heading(get_string('databasetypehead', 'install'));
428     foreach ($databases as $type=>$database) {
429         echo " $type \n";
430     }
431     if (!empty($databases[$options['dbtype']])) {
432         $prompt = get_string('clitypevaluedefault', 'admin', $options['dbtype']);
433     } else {
434         $prompt = get_string('clitypevalue', 'admin');
435     }
436     $CFG->dbtype = cli_input($prompt, $options['dbtype'], array_keys($databases));
438 } else {
439     if (empty($databases[$options['dbtype']])) {
440         $a = (object)array('option'=>'dbtype', 'value'=>$options['dbtype']);
441         cli_error(get_string('cliincorrectvalueerror', 'admin', $a));
442     }
443     $CFG->dbtype = $options['dbtype'];
445 $database = $databases[$CFG->dbtype];
448 // ask for db host
449 if ($interactive) {
450     cli_separator();
451     cli_heading(get_string('databasehost', 'install'));
452     if ($options['dbhost'] !== '') {
453         $prompt = get_string('clitypevaluedefault', 'admin', $options['dbhost']);
454     } else {
455         $prompt = get_string('clitypevalue', 'admin');
456     }
457     $CFG->dbhost = cli_input($prompt, $options['dbhost']);
459 } else {
460     $CFG->dbhost = $options['dbhost'];
463 // ask for db name
464 if ($interactive) {
465     cli_separator();
466     cli_heading(get_string('databasename', 'install'));
467     if ($options['dbname'] !== '') {
468         $prompt = get_string('clitypevaluedefault', 'admin', $options['dbname']);
469     } else {
470         $prompt = get_string('clitypevalue', 'admin');
471     }
472     $CFG->dbname = cli_input($prompt, $options['dbname']);
474 } else {
475     $CFG->dbname = $options['dbname'];
478 // ask for db prefix
479 if ($interactive) {
480     cli_separator();
481     cli_heading(get_string('dbprefix', 'install'));
482     //TODO: solve somehow the prefix trouble for oci
483     if ($options['prefix'] !== '') {
484         $prompt = get_string('clitypevaluedefault', 'admin', $options['prefix']);
485     } else {
486         $prompt = get_string('clitypevalue', 'admin');
487     }
488     $CFG->prefix = cli_input($prompt, $options['prefix']);
490 } else {
491     $CFG->prefix = $options['prefix'];
494 // ask for db user
495 if ($interactive) {
496     cli_separator();
497     cli_heading(get_string('databaseuser', 'install'));
498     if ($options['dbuser'] !== '') {
499         $prompt = get_string('clitypevaluedefault', 'admin', $options['dbuser']);
500     } else {
501         $prompt = get_string('clitypevalue', 'admin');
502     }
503     $CFG->dbuser = cli_input($prompt, $options['dbuser']);
505 } else {
506     $CFG->dbuser = $options['dbuser'];
509 // ask for db password
510 if ($interactive) {
511     cli_separator();
512     cli_heading(get_string('databasepass', 'install'));
513     do {
514         if ($options['dbpass'] !== '') {
515             $prompt = get_string('clitypevaluedefault', 'admin', $options['dbpass']);
516         } else {
517             $prompt = get_string('clitypevalue', 'admin');
518         }
520         $CFG->dbpass = cli_input($prompt, $options['dbpass']);
521         $hint_database = install_db_validate($database, $CFG->dbhost, $CFG->dbuser, $CFG->dbpass, $CFG->dbname, $CFG->prefix, array('dbpersist'=>0, 'dbsocket'=>$options['dbsocket']));
522     } while ($hint_database !== '');
524 } else {
525     $CFG->dbpass = $options['dbpass'];
526     $hint_database = install_db_validate($database, $CFG->dbhost, $CFG->dbuser, $CFG->dbpass, $CFG->dbname, $CFG->prefix, array('dbpersist'=>0, 'dbsocket'=>$options['dbsocket']));
527     if ($hint_database !== '') {
528         cli_error(get_string('dbconnectionerror', 'install'));
529     }
532 // ask for fullname
533 if ($interactive) {
534     cli_separator();
535     cli_heading(get_string('fullsitename', 'moodle'));
537     if ($options['fullname'] !== '') {
538         $prompt = get_string('clitypevaluedefault', 'admin', $options['fullname']);
539     } else {
540         $prompt = get_string('clitypevalue', 'admin');
541     }
543     do {
544         $options['fullname'] = cli_input($prompt, $options['fullname']);
545     } while (empty($options['fullname']));
546 } else {
547     if (empty($options['fullname'])) {
548         $a = (object)array('option'=>'fullname', 'value'=>$options['fullname']);
549         cli_error(get_string('cliincorrectvalueerror', 'admin', $a));
550     }
553 // ask for shortname
554 if ($interactive) {
555     cli_separator();
556     cli_heading(get_string('shortsitename', 'moodle'));
558     if ($options['shortname'] !== '') {
559         $prompt = get_string('clitypevaluedefault', 'admin', $options['shortname']);
560     } else {
561         $prompt = get_string('clitypevalue', 'admin');
562     }
564     do {
565         $options['shortname'] = cli_input($prompt, $options['shortname']);
566     } while (empty($options['shortname']));
567 } else {
568     if (empty($options['shortname'])) {
569         $a = (object)array('option'=>'shortname', 'value'=>$options['shortname']);
570         cli_error(get_string('cliincorrectvalueerror', 'admin', $a));
571     }
574 // ask for admin user name
575 if ($interactive) {
576     cli_separator();
577     cli_heading(get_string('cliadminusername', 'install'));
578     if (!empty($options['adminuser'])) {
579         $prompt = get_string('clitypevaluedefault', 'admin', $options['adminuser']);
580     } else {
581         $prompt = get_string('clitypevalue', 'admin');
582     }
583     do {
584         $options['adminuser'] = cli_input($prompt, $options['adminuser']);
585     } while (empty($options['adminuser']) or $options['adminuser'] === 'guest');
586 } else {
587     if (empty($options['adminuser']) or $options['adminuser'] === 'guest') {
588         $a = (object)array('option'=>'adminuser', 'value'=>$options['adminuser']);
589         cli_error(get_string('cliincorrectvalueerror', 'admin', $a));
590     }
593 // ask for admin user password
594 if ($interactive) {
595     cli_separator();
596     cli_heading(get_string('cliadminpassword', 'install'));
597     $prompt = get_string('clitypevalue', 'admin');
598     do {
599         $options['adminpass'] = cli_input($prompt);
600     } while (empty($options['adminpass']) or $options['adminpass'] === 'admin');
601 } else {
602     if (empty($options['adminpass']) or $options['adminpass'] === 'admin') {
603         $a = (object)array('option'=>'adminpass', 'value'=>$options['adminpass']);
604         cli_error(get_string('cliincorrectvalueerror', 'admin', $a));
605     }
608 if ($interactive) {
609     if (!$options['agree-license']) {
610         cli_separator();
611         cli_heading(get_string('copyrightnotice'));
612         echo "Moodle  - Modular Object-Oriented Dynamic Learning Environment\n";
613         echo get_string('gpl3')."\n\n";
614         echo get_string('doyouagree')."\n";
615         $prompt = get_string('cliyesnoprompt', 'admin');
616         $input = cli_input($prompt, '', array(get_string('clianswerno', 'admin'), get_string('cliansweryes', 'admin')));
617         if ($input == get_string('clianswerno', 'admin')) {
618             exit(1);
619         }
620     }
621 } else {
622     if (!$options['agree-license']) {
623         cli_error(get_string('climustagreelicense', 'install'));
624     }
627 // Finally we have all info needed for config.php
628 $configphp = install_generate_configphp($database, $CFG);
629 umask(0137);
630 if (($fh = fopen($configfile, 'w')) !== false) {
631     fwrite($fh, $configphp);
632     fclose($fh);
635 if (!file_exists($configfile)) {
636     cli_error('Can not create config file.');
639 // remember selected language
640 $installlang = $CFG->lang;
641 // return back to original dir before executing setup.php which changes the dir again
642 chdir($olddir);
643 // We have config.php, it is a real php script from now on :-)
644 require($configfile);
646 // use selected language
647 $CFG->lang = $installlang;
648 $SESSION->lang = $CFG->lang;
650 install_cli_database($options, $interactive);
652 echo get_string('cliinstallfinished', 'install')."\n";
653 exit(0); // 0 means success