MDL-23590 refactoring cli install before adding install_database.php + fixing problem...
[moodle.git] / admin / cli / install.php
CommitLineData
3b596dbf 1<?php
2
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/>.
17
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 *
b7315f35 27 * @package core
3b596dbf 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 */
32
33if (isset($_SERVER['REMOTE_ADDR'])) {
34 error_log("admin/cli/install.php can not be called from web server!");
35 exit;
36}
37
5a411e81 38$help =
39"Command line Moodle installer, creates config.php and initializes database.
40Please note you must execute this script with the same uid as apache
41or use chmod/chown after installation.
42
43Site defaults may be changed via local/defaults.php.
44
45Options:
7be67681
DM
46--chmod=OCTAL-MODE Permissions of new directories created within dataroot.
47 Default is 2777. You may want to change it to 2770
48 or 2750 or 750. See chmod man page for details.
5a411e81 49--lang=CODE Installation and default site language.
50--wwwroot=URL Web address for the Moodle site,
51 required in non-interactive mode.
52--dataroot=DIR Location of the moodle data folder,
59ba2bb0
DM
53 must not be web accessible. Default is moodledata
54 in the parent directory.
5a411e81 55--dbtype=TYPE Database type. Default is mysqli
56--dbhost=HOST Database host. Default is localhost
57--dbname=NAME Database name. Default is moodle
58--dbuser=USERNAME Database user. Default is root
59--dbpass=PASSWORD Database password. Default is blank
60--dbsocket Use database sockets. Available for some databases only.
61--prefix=STRING Table prefix for above database tables. Default is mdl_
7217676b
DM
62--adminuser=USERNAME Username for the moodle admin account. Default is admin
63--adminpass=PASSWORD Password for the moodle admin account,
5a411e81 64 required in non-interactive mode.
65--non-interactive No interactive questions, installation fails if any
66 problem encountered.
c31d94e8 67--agree-license Indicates agreement with software license,
5a411e81 68 required in non-interactive mode.
69-h, --help Print out this help
70
71Example: \$sudo -u wwwrun /usr/bin/php admin/cli/install.php --lang=cs
c31d94e8 72"; //TODO: localize, mark as needed in install - to be translated later when everything is finished
3b596dbf 73
3b596dbf 74
75// Nothing to do if config.php exists
76$configfile = dirname(dirname(dirname(__FILE__))).'/config.php';
77if (file_exists($configfile)) {
5a411e81 78 require($configfile);
c31d94e8 79 require_once($CFG->libdir.'/clilib.php');
80 list($options, $unrecognized) = cli_get_params(array('help'=>false), array('h'=>'help'));
81
82 if ($options['help']) {
83 echo $help;
84 echo "\n\n";
85 }
86
87 cli_error(get_string('clialreadyinstalled', 'install'));
3b596dbf 88}
89
5a411e81 90$olddir = getcwd();
91
92// change directory so that includes bellow work properly
93chdir(dirname($_SERVER['argv'][0]));
94
3b596dbf 95// make sure PHP errors are displayed - helps with diagnosing of problems
96@error_reporting(E_ALL);
97@ini_set('display_errors', '1');
98// we need a lot of memory
99@ini_set('memory_limit', '128M');
100
460ebd6c
PS
101/** Used by library scripts to check they are being called by Moodle */
102define('MOODLE_INTERNAL', true);
103
3b596dbf 104// Check that PHP is of a sufficient version
105if (version_compare(phpversion(), "5.2.8") < 0) {
106 $phpversion = phpversion();
107 // do NOT localise - lang strings would not work here and we CAN NOT move it after installib
108 echo "Sorry, Moodle 2.0 requires PHP 5.2.8 or later (currently using version $phpversion).\n";
109 echo "Please upgrade your server software or install latest Moodle 1.9.x instead.";
110 die;
111}
112
113// set up configuration
114$CFG = new stdClass();
3a915b06 115$CFG->lang = 'en';
3b5ff37f 116$CFG->dirroot = dirname(dirname(dirname(__FILE__)));
3b596dbf 117$CFG->libdir = "$CFG->dirroot/lib";
118$CFG->wwwroot = "http://localhost";
119$CFG->httpswwwroot = $CFG->wwwroot;
59ba2bb0 120$CFG->dataroot = str_replace('\\', '/', dirname(dirname(dirname(dirname(__FILE__)))).'/moodledata');
3b596dbf 121$CFG->docroot = 'http://docs.moodle.org';
fb344c2d 122$CFG->running_installer = true;
3a915b06
PS
123$CFG->early_install_lang = true;
124
3b596dbf 125$parts = explode('/', str_replace('\\', '/', dirname(dirname(__FILE__))));
126$CFG->admin = array_pop($parts);
127
128require($CFG->dirroot.'/version.php');
129$CFG->target_release = $release;
130
131//point pear include path to moodles lib/pear so that includes and requires will search there for files before anywhere else
132//the problem is that we need specific version of quickforms and hacked excel files :-(
133ini_set('include_path', $CFG->libdir.'/pear' . PATH_SEPARATOR . ini_get('include_path'));
134
135require_once($CFG->libdir.'/installlib.php');
136require_once($CFG->libdir.'/clilib.php');
137require_once($CFG->libdir.'/setuplib.php');
138require_once($CFG->libdir.'/textlib.class.php');
139require_once($CFG->libdir.'/weblib.php');
140require_once($CFG->libdir.'/dmllib.php');
141require_once($CFG->libdir.'/moodlelib.php');
142require_once($CFG->libdir.'/deprecatedlib.php');
143require_once($CFG->libdir.'/adminlib.php');
3b596dbf 144require_once($CFG->libdir.'/componentlib.class.php');
3b596dbf 145
146//Database types
147$databases = array('mysqli' => moodle_database::get_driver_instance('mysqli', 'native'),
148 'pgsql' => moodle_database::get_driver_instance('pgsql', 'native'),
149 'oci' => moodle_database::get_driver_instance('oci', 'native'),
7e60d0d6
EL
150 'sqlsrv' => moodle_database::get_driver_instance('sqlsrv', 'native'), // MS SQL*Server PHP driver
151 'mssql' => moodle_database::get_driver_instance('mssql', 'native'), // FreeTDS driver
3b596dbf 152 );
153foreach ($databases as $type=>$database) {
154 if ($database->driver_installed() !== true) {
155 unset($databases[$type]);
156 }
157}
158if (empty($databases)) {
c31d94e8 159 $defaultdb = '';
160} else {
161 reset($databases);
162 $defaultdb = key($databases);
3b596dbf 163}
164
3b596dbf 165// now get cli options
7217676b
DM
166list($options, $unrecognized) = cli_get_params(
167 array(
b7315f35 168 'chmod' => '2777',
7217676b
DM
169 'lang' => $CFG->lang,
170 'wwwroot' => '',
171 'dataroot' => $CFG->dataroot,
172 'dbtype' => $defaultdb,
173 'dbhost' => 'localhost',
174 'dbname' => 'moodle',
175 'dbuser' => 'root',
176 'dbpass' => '',
177 'dbsocket' => false,
178 'prefix' => 'mdl_',
179 'adminuser' => 'admin',
180 'adminpass' => '',
181 'non-interactive' => false,
182 'agree-license' => false,
183 'help' => false
184 ),
185 array(
186 'h' => 'help'
187 )
188);
3b596dbf 189
190$interactive = empty($options['non-interactive']);
191
192// set up language
193$lang = clean_param($options['lang'], PARAM_SAFEDIR);
194if (file_exists($CFG->dirroot.'/install/lang/'.$lang)) {
195 $CFG->lang = $lang;
3b596dbf 196}
197
198if ($unrecognized) {
c31d94e8 199 $unrecognized = implode("\n ", $unrecognized);
1494616f 200 cli_error(get_string('cliunknowoption', 'admin', $unrecognized));
3b596dbf 201}
202
203if ($options['help']) {
3b596dbf 204 echo $help;
205 die;
206}
207
3b596dbf 208//Print header
c31d94e8 209echo get_string('cliinstallheader', 'install', $CFG->target_release)."\n";
3b596dbf 210
211//Fist select language
212if ($interactive) {
c31d94e8 213 cli_separator();
1f96e907 214 $languages = get_string_manager()->get_list_of_translations();
c31d94e8 215 // format the langs nicely - 3 per line
3b596dbf 216 $c = 0;
217 $langlist = '';
218 foreach ($languages as $key=>$lang) {
219 $c++;
220 $length = iconv_strlen($lang, 'UTF-8');
221 $padded = $lang.str_repeat(' ', 28-$length);
222 $langlist .= $padded;
223 if ($c % 3 == 0) {
224 $langlist .= "\n";
225 }
226 }
3a915b06 227 $default = $CFG->lang;
c31d94e8 228 cli_heading(get_string('availablelangs', 'install'));
229 echo $langlist."\n";
3a915b06 230 $prompt = get_string('clitypevaluedefault', 'admin', $CFG->lang);
3b596dbf 231 $error = '';
232 do {
233 echo $error;
234 $input = cli_input($prompt, $default);
235 $input = clean_param($input, PARAM_SAFEDIR);
236
3a915b06 237 if (!file_exists($CFG->dirroot.'/install/lang/'.$input)) {
c31d94e8 238 $error = get_string('cliincorrectvalueretry', 'admin')."\n";
3b596dbf 239 } else {
240 $error = '';
241 }
242 } while ($error !== '');
3a915b06 243 $CFG->lang = $input;
3b596dbf 244} else {
c31d94e8 245 // already selected and verified
3b596dbf 246}
247
7be67681
DM
248// Set directorypermissions first
249$chmod = octdec(clean_param($options['chmod'], PARAM_INT));
250if ($interactive) {
251 cli_separator();
252 cli_heading('Data directories permission'); // todo localize
253 $prompt = get_string('clitypevaluedefault', 'admin', decoct($chmod));
254 $error = '';
255 do {
256 echo $error;
b7315f35 257 $input = cli_input($prompt, decoct($chmod));
7be67681
DM
258 $input = octdec(clean_param($input, PARAM_INT));
259 if (empty($input)) {
260 $error = get_string('cliincorrectvalueretry', 'admin')."\n";
261 } else {
262 $error = '';
263 }
b7315f35 264 } while ($error !== '');
7be67681
DM
265 $chmod = $input;
266
267} else {
268 if (empty($chmod)) {
269 $a = (object)array('option' => 'chmod', 'value' => decoct($chmod));
270 cli_error(get_string('cliincorrectvalueerror', 'admin', $a));
271 }
272}
273$CFG->directorypermissions = $chmod;
3b596dbf 274
275//We need wwwroot before we test dataroot
276$wwwroot = clean_param($options['wwwroot'], PARAM_URL);
277$wwwroot = trim($wwwroot, '/');
278if ($interactive) {
c31d94e8 279 cli_separator();
280 cli_heading(get_string('wwwroot', 'install'));
3b596dbf 281 if (strpos($wwwroot, 'http') === 0) {
c31d94e8 282 $prompt = get_string('clitypevaluedefault', 'admin', $wwwroot);
3b596dbf 283 } else {
c31d94e8 284 $wwwroot = null;
285 $prompt = get_string('clitypevalue', 'admin');
3b596dbf 286 }
287 $error = '';
288 do {
289 echo $error;
290 $input = cli_input($prompt, $wwwroot);
291 $input = clean_param($input, PARAM_URL);
292 $input = trim($input, '/');
293 if (strpos($input, 'http') !== 0) {
c31d94e8 294 $error = get_string('cliincorrectvalueretry', 'admin')."\n";
3b596dbf 295 } else {
296 $error = '';
297 }
298 } while ($error !== '');
299 $wwwroot = $input;
300
301} else {
302 if (strpos($wwwroot, 'http') !== 0) {
c31d94e8 303 $a = (object)array('option'=>'wwwroot', 'value'=>$wwwroot);
304 cli_error(get_string('cliincorrectvalueerror', 'admin', $a));
3b596dbf 305 }
306}
307$CFG->wwwroot = $wwwroot;
308$CFG->httpswwwroot = $CFG->wwwroot;
3b596dbf 309
310
311//We need dataroot before lang download
d8030e11 312if (!empty($options['dataroot'])) {
c31aa73c 313 $CFG->dataroot = $options['dataroot'];
d8030e11 314}
3b596dbf 315if ($interactive) {
c31d94e8 316 cli_separator();
3b596dbf 317 $i=0;
318 while(is_dataroot_insecure()) {
319 $parrent = dirname($CFG->dataroot);
320 $i++;
321 if ($parrent == '/' or $parrent == '.' or preg_match('/^[a-z]:\\\?$/i', $parrent) or ($i > 100)) {
322 $CFG->dataroot = ''; //can not find secure location for dataroot
323 break;
324 }
325 $CFG->dataroot = dirname($parrent).'/moodledata';
326 }
c31d94e8 327 cli_heading(get_string('dataroot', 'install'));
3b596dbf 328 $error = '';
329 do {
330 if ($CFG->dataroot !== '') {
c31d94e8 331 $prompt = get_string('clitypevaluedefault', 'admin', $CFG->dataroot);
3b596dbf 332 } else {
c31d94e8 333 $prompt = get_string('clitypevalue', 'admin');
3b596dbf 334 }
335 echo $error;
336 $CFG->dataroot = cli_input($prompt, $CFG->dataroot);
337 if ($CFG->dataroot === '') {
c31d94e8 338 $error = get_string('cliincorrectvalueretry', 'admin')."\n";
3b596dbf 339 } else if (is_dataroot_insecure()) {
340 $CFG->dataroot = '';
a18d577d 341 $error = get_string('pathsunsecuredataroot', 'install')."\n";
3b596dbf 342 } else {
343 if (make_upload_directory('lang', false)) {
344 $error = '';
345 } else {
59ba2bb0
DM
346 $a = (object)array('dataroot' => $CFG->dataroot);
347 $error = get_string('pathserrcreatedataroot', 'install', $a)."\n";
3b596dbf 348 }
349 }
350
351 } while ($error !== '');
352
353} else {
354 if (is_dataroot_insecure()) {
59ba2bb0 355 cli_error(get_string('pathsunsecuredataroot', 'install'));
c31d94e8 356 }
357 if (!make_upload_directory('lang', false)) {
59ba2bb0
DM
358 $a = (object)array('dataroot' => $CFG->dataroot);
359 cli_error(get_string('pathserrcreatedataroot', 'install', $a));
3b596dbf 360 }
361}
362
363//download lang pack with optional notification
3a915b06 364if ($CFG->lang != 'en') {
c4f8fd0c 365 if ($cd = new component_installer('http://download.moodle.org', 'langpack/2.0', $CFG->lang.'.zip', 'languages.md5', 'lang')) {
3b596dbf 366 if ($cd->install() == COMPONENT_ERROR) {
367 if ($cd->get_error() == 'remotedownloaderror') {
368 $a = new stdClass();
c4f8fd0c 369 $a->url = 'http://download.moodle.org/langpack/2.0/'.$CFG->lang.'.zip';
3b596dbf 370 $a->dest = $CFG->dataroot.'/lang';
371 cli_problem(get_string($cd->get_error(), 'error', $a));
372 } else {
373 cli_problem(get_string($cd->get_error(), 'error'));
374 }
375 } else {
376 // install parent lang if defined
377 if ($parentlang = get_parent_language()) {
c4f8fd0c 378 if ($cd = new component_installer('http://download.moodle.org', 'langpack/2.0', $parentlang.'.zip', 'languages.md5', 'lang')) {
3b596dbf 379 $cd->install();
380 }
381 }
382 }
383 }
3b596dbf 384}
3b596dbf 385
7d73574c 386// switch the string_manager instance to stop using install/lang/
3a915b06 387$CFG->early_install_lang = false;
877a172d
DM
388$CFG->langotherroot = $CFG->dataroot.'/lang';
389$CFG->langlocalroot = $CFG->dataroot.'/lang';
7d73574c 390get_string_manager(true);
3a915b06 391
3b596dbf 392// ask for db type - show only drivers available
393if ($interactive) {
394 $options['dbtype'] = strtolower($options['dbtype']);
c31d94e8 395 cli_separator();
396 cli_heading(get_string('databasetypehead', 'install'));
3b596dbf 397 foreach ($databases as $type=>$database) {
c31d94e8 398 echo " $type \n";
3b596dbf 399 }
3b596dbf 400 if (!empty($databases[$options['dbtype']])) {
c31d94e8 401 $prompt = get_string('clitypevaluedefault', 'admin', $options['dbtype']);
3b596dbf 402 } else {
c31d94e8 403 $prompt = get_string('clitypevalue', 'admin');
3b596dbf 404 }
3b596dbf 405 $CFG->dbtype = cli_input($prompt, $options['dbtype'], array_keys($databases));
406
407} else {
408 if (empty($databases[$options['dbtype']])) {
c31d94e8 409 $a = (object)array('option'=>'dbtype', 'value'=>$options['dbtype']);
410 cli_error(get_string('cliincorrectvalueerror', 'admin', $a));
3b596dbf 411 }
412 $CFG->dbtype = $options['dbtype'];
413}
414$database = $databases[$CFG->dbtype];
415
416
417// ask for db host
418if ($interactive) {
c31d94e8 419 cli_separator();
420 cli_heading(get_string('databasehost', 'install'));
3b596dbf 421 if ($options['dbhost'] !== '') {
c31d94e8 422 $prompt = get_string('clitypevaluedefault', 'admin', $options['dbhost']);
3b596dbf 423 } else {
c31d94e8 424 $prompt = get_string('clitypevalue', 'admin');
3b596dbf 425 }
3b596dbf 426 $CFG->dbhost = cli_input($prompt, $options['dbhost']);
427
428} else {
429 $CFG->dbhost = $options['dbhost'];
430}
431
432// ask for db name
433if ($interactive) {
c31d94e8 434 cli_separator();
435 cli_heading(get_string('databasename', 'install'));
3b596dbf 436 if ($options['dbname'] !== '') {
c31d94e8 437 $prompt = get_string('clitypevaluedefault', 'admin', $options['dbname']);
3b596dbf 438 } else {
c31d94e8 439 $prompt = get_string('clitypevalue', 'admin');
3b596dbf 440 }
3b596dbf 441 $CFG->dbname = cli_input($prompt, $options['dbname']);
442
443} else {
444 $CFG->dbname = $options['dbname'];
445}
446
447// ask for db prefix
448if ($interactive) {
c31d94e8 449 cli_separator();
450 cli_heading(get_string('dbprefix', 'install'));
3b596dbf 451 //TODO: solve somehow the prefix trouble for oci
452 if ($options['prefix'] !== '') {
c31d94e8 453 $prompt = get_string('clitypevaluedefault', 'admin', $options['prefix']);
3b596dbf 454 } else {
c31d94e8 455 $prompt = get_string('clitypevalue', 'admin');
3b596dbf 456 }
3b596dbf 457 $CFG->prefix = cli_input($prompt, $options['prefix']);
458
459} else {
460 $CFG->prefix = $options['prefix'];
461}
462
463// ask for db user
464if ($interactive) {
c31d94e8 465 cli_separator();
466 cli_heading(get_string('databaseuser', 'install'));
3b596dbf 467 if ($options['dbuser'] !== '') {
c31d94e8 468 $prompt = get_string('clitypevaluedefault', 'admin', $options['dbuser']);
3b596dbf 469 } else {
c31d94e8 470 $prompt = get_string('clitypevalue', 'admin');
3b596dbf 471 }
3b596dbf 472 $CFG->dbuser = cli_input($prompt, $options['dbuser']);
473
474} else {
475 $CFG->dbuser = $options['dbuser'];
476}
477
478// ask for db password
479if ($interactive) {
c31d94e8 480 cli_separator();
481 cli_heading(get_string('databasepass', 'install'));
3b596dbf 482 do {
483 if ($options['dbpass'] !== '') {
c31d94e8 484 $prompt = get_string('clitypevaluedefault', 'admin', $options['dbpass']);
3b596dbf 485 } else {
c31d94e8 486 $prompt = get_string('clitypevalue', 'admin');
3b596dbf 487 }
488
489 $CFG->dbpass = cli_input($prompt, $options['dbpass']);
a0b7200d 490 $hint_database = install_db_validate($database, $CFG->dbhost, $CFG->dbuser, $CFG->dbpass, $CFG->dbname, $CFG->prefix, array('dbpersist'=>0, 'dbsocket'=>$options['dbsocket']));
3b596dbf 491 } while ($hint_database !== '');
492
493} else {
494 $CFG->dbpass = $options['dbpass'];
a0b7200d 495 $hint_database = install_db_validate($database, $CFG->dbhost, $CFG->dbuser, $CFG->dbpass, $CFG->dbname, $CFG->prefix, array('dbpersist'=>0, 'dbsocket'=>$options['dbsocket']));
3b596dbf 496 if ($hint_database !== '') {
c31d94e8 497 cli_error(get_string('dbconnectionerror', 'install'));
3b596dbf 498 }
499}
500
7217676b
DM
501// ask for admin user name
502if ($interactive) {
503 cli_separator();
504 cli_heading(get_string('cliadminusername', 'install'));
505 if (!empty($options['adminuser'])) {
506 $prompt = get_string('clitypevaluedefault', 'admin', $options['adminuser']);
507 } else {
508 $prompt = get_string('clitypevalue', 'admin');
509 }
510 do {
511 $options['adminuser'] = cli_input($prompt, $options['adminuser']);
512 } while (empty($options['adminuser']) or $options['adminuser'] === 'guest');
513} else {
514 if (empty($options['adminuser']) or $options['adminuser'] === 'guest') {
515 $a = (object)array('option'=>'adminuser', 'value'=>$options['adminuser']);
516 cli_error(get_string('cliincorrectvalueerror', 'admin', $a));
517 }
518}
519
3b596dbf 520// ask for admin user password
521if ($interactive) {
c31d94e8 522 cli_separator();
523 cli_heading(get_string('cliadminpassword', 'install'));
524 $prompt = get_string('clitypevalue', 'admin');
3b596dbf 525 do {
7217676b
DM
526 $options['adminpass'] = cli_input($prompt);
527 } while (empty($options['adminpass']) or $options['adminpass'] === 'admin');
3b596dbf 528} else {
7217676b
DM
529 if (empty($options['adminpass']) or $options['adminpass'] === 'admin') {
530 $a = (object)array('option'=>'adminpass', 'value'=>$options['adminpass']);
c31d94e8 531 cli_error(get_string('cliincorrectvalueerror', 'admin', $a));
3b596dbf 532 }
533}
534
535if ($interactive) {
c31d94e8 536 if (!$options['agree-license']) {
fb344c2d 537 cli_separator();
538 cli_heading(get_string('copyrightnotice'));
05736058 539 echo "Moodle - Modular Object-Oriented Dynamic Learning Environment\n";
852a1f66 540 echo get_string('gpl3')."\n\n";
fb344c2d 541 echo get_string('doyouagree')."\n";
b8523467 542 $prompt = get_string('cliyesnoprompt', 'admin');
05736058 543 $input = cli_input($prompt, '', array(get_string('clianswerno', 'admin'), get_string('cliansweryes', 'admin')));
b8523467 544 if ($input == get_string('clianswerno', 'admin')) {
545 exit(1);
546 }
3b596dbf 547 }
548} else {
c31d94e8 549 if (!$options['agree-license']) {
b8523467 550 cli_error(get_string('climustagreelicense', 'install'));
3b596dbf 551 }
552}
553
554// Finally we have all info needed for config.php
555$configphp = install_generate_configphp($database, $CFG);
556umask(0137);
557if (($fh = fopen($configfile, 'w')) !== false) {
558 fwrite($fh, $configphp);
559 fclose($fh);
560}
561
562if (!file_exists($configfile)) {
563 cli_error('Can not create config file.');
3b596dbf 564}
565
b7315f35 566// return back to original dir before executing setup.php which changes the dir again
3b596dbf 567chdir($olddir);
568// We have config.php, it is a real php script from now on :-)
569require($configfile);
570
b7315f35 571install_cli_database($options, $interactive);
3b596dbf 572
b8523467 573echo get_string('cliinstallfinished', 'install')."\n";
574exit(0); // 0 means success