Project

General

Profile

Download (36.8 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/****h* pfSense/config
3
 * NAME
4
 *   config.inc - Functions to manipulate config.xml
5
 * DESCRIPTION
6
 *   This include contains various config.xml specific functions.
7
 * HISTORY
8
 * $Id$
9
 ******
10

    
11
	config.inc
12
	Copyright (C) 2004-2006 Scott Ullrich
13
	All rights reserved.
14

    
15
	originally part of m0n0wall (http://m0n0.ch/wall)
16
	Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>.
17
	All rights reserved.
18

    
19
	Redistribution and use in source and binary forms, with or without
20
	modification, are permitted provided that the following conditions are met:
21

    
22
	1. Redistributions of source code must retain the above copyright notice,
23
	   this list of conditions and the following disclaimer.
24

    
25
	2. Redistributions in binary form must reproduce the above copyright
26
	   notice, this list of conditions and the following disclaimer in the
27
	   documentation and/or other materials provided with the distribution.
28

    
29
	THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
30
	INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
31
	AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
32
	AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
33
	OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
34
	SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
35
	INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
36
	CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
37
	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
38
	POSSIBILITY OF SUCH DAMAGE.
39
*/
40
/*
41
 * XXX: Hack around the cvs syntax checks. 
42
 * DISABLE_PHP_LINT_CHECKING
43
 */
44
 
45

    
46
if($g['booting']) echo ".";
47

    
48
/* do not load this file twice. */
49
if($config_inc_loaded == true)
50
	return;
51
else
52
	$config_inc_loaded = true;
53

    
54
/* include globals from notices.inc /utility/XML parser files */
55
require_once("notices.inc");
56
if($g['booting']) echo ".";
57
require_once("util.inc");
58
if($g['booting']) echo ".";
59
require_once("xmlparse.inc");
60
if($g['booting']) echo ".";
61
require_once("crypt.inc");
62

    
63
/* read platform */
64
if($g['booting']) echo ".";
65
if (file_exists("{$g['etc_path']}/platform")) {
66
	$g['platform'] = chop(file_get_contents("{$g['etc_path']}/platform"));
67
} else {
68
	$g['platform'] = "unknown";
69
}
70

    
71
/* if /debugging exists, lets set $debugging
72
   so we can output more information */
73
if(file_exists("/debugging")) {
74
	$debugging = true;
75
	$g['debug'] = true;
76
}
77

    
78
if($g['booting']) echo ".";
79
if(file_exists("/cf/conf/config.xml")) {
80
	$config_contents = file_get_contents("/cf/conf/config.xml");
81
	if(stristr($config_contents, "<m0n0wall>") == true) {
82
		if($g['booting']) echo ".";
83
		/* user has just upgraded to m0n0wall, replace root xml tags */
84
		log_error("Upgrading m0n0wall configuration to pfSense... ");
85
		$config_contents = str_replace("m0n0wall","pfsense", $config_contents);
86
		if (!config_validate("{$g['conf_path']}/config.xml"))
87
			log_error("ERROR!  Could not convert m0n0wall -> pfsense in config.xml");
88
		conf_mount_rw();
89
		$fd = fopen("/cf/conf/config.xml", "w");
90
		fwrite($fd, $config_contents);
91
		fclose($fd);
92
		conf_mount_ro();
93
	}
94
}
95

    
96
/* if our config file exists bail out, we're already set. */
97
if ($g['booting'] and !file_exists($g['cf_conf_path'] . "/config.xml")  ) {
98
	if($g['booting']) echo ".";
99
	/* find the device where config.xml resides and write out an fstab */
100
	unset($cfgdevice);
101
	if($g['booting']) echo ".";
102
	/* check if there's already an fstab (NFS booting?) */
103
	if (!file_exists("{$g['etc_path']}/fstab")) {
104
		if($g['booting']) echo ".";
105
		if (strstr($g['platform'], "cdrom")) {
106
			/* config is on floppy disk for CD-ROM version */
107
			$cfgdevice = $cfgpartition = "fd0";
108
			$dmesg = `dmesg -a`;
109
			if(ereg("da0", $dmesg) == true) {
110
				$cfgdevice = $cfgpartition = "da0" ;
111
				if (mwexec("/sbin/mount -r /dev/{$cfgdevice} /cf")) {
112
					/* could not mount, fallback to floppy */
113
					$cfgdevice = $cfgpartition = "fd0";
114
				}
115
			}
116
			$cfgfstype = "msdosfs";
117
			echo "CDROM build\n";
118
			echo "   CFG: {$cfgpartition}\n";
119
			echo "  TYPE: {$cfgfstype}\n";
120
		} else {
121
			if($g['booting']) echo ".";
122
			/* probe kernel known disks until we find one with config.xml */
123
			$disks = explode(" ", trim(preg_replace("/kern.disks: /", "", exec("/sbin/sysctl kern.disks"))));
124
			foreach ($disks as $mountdisk) {
125
				/* skip mfs mounted filesystems */
126
				if (strstr($mountdisk, "md"))
127
					continue;
128
				if (mwexec("/sbin/mount -r /dev/{$mountdisk}a {$g['cf_path']}") == 0) {
129
					if (file_exists("{$g['cf_conf_path']}/config.xml")) {
130
						/* found it */
131
						$cfgdevice = $mountdisk;
132
						$cfgpartition = $cfgdevice . "a";
133
						$cfgfstype = "ufs";
134
						echo "Found configuration on $cfgdevice.\n";
135
					}
136

    
137
					mwexec("/sbin/umount -f {$g['cf_path']}");
138

    
139
					if ($cfgdevice)
140
						break;
141
				}
142
				if (mwexec("/sbin/mount -r /dev/{$mountdisk}d {$g['cf_path']}") == 0) {
143
					if($g['booting']) echo ".";
144
					if (file_exists("{$g['cf_conf_path']}/config.xml")) {
145
						/* found it */
146
						$cfgdevice = $mountdisk;
147
						$cfgpartition = $cfgdevice . "d";
148
						$cfgfstype = "ufs";
149
						echo "Found configuration on $cfgdevice.\n";
150
					}
151

    
152
					mwexec("/sbin/umount -f {$g['cf_path']}");
153

    
154
					if ($cfgdevice)
155
						break;
156
				}
157
			}
158
		}
159
		if($g['booting']) echo ".";
160
		if (!$cfgdevice) {
161
			$last_backup = discover_last_backup();
162
			if($last_backup) {
163
				log_error("No config.xml found, attempting last known config restore.");
164
				file_notice("config.xml", "No config.xml found, attempting last known config restore.", "pfSenseConfigurator", "");
165
				restore_backup("/cf/conf/backup/{$last_backup}");
166
			} else {
167
				log_error("No config.xml or config backups found, resetting to factory defaults.");
168
				restore_backup('/conf.default/config.xml');
169
			}
170
		}
171

    
172
		/* write device name to a file for rc.firmware */
173
		$fd = fopen("{$g['varetc_path']}/cfdevice", "w");
174
		fwrite($fd, $cfgdevice . "\n");
175
		fclose($fd);
176

    
177
		/* write out an fstab */
178
		$fd = fopen("{$g['etc_path']}/fstab", "w");
179

    
180
		$fstab = "/dev/{$cfgpartition} {$g['cf_path']} {$cfgfstype} ro 1 1\n";
181
		$fstab .= "proc /proc procfs rw 0 0\n";
182

    
183
		fwrite($fd, $fstab);
184
		fclose($fd);
185
	}
186
	if($g['booting']) echo ".";
187
	/* mount all filesystems */
188
	mwexec("/sbin/mount -a");
189
}
190

    
191
/****f* config/encrypted_configxml
192
 * NAME
193
 *   encrypted_configxml - Checks to see if config.xml is encrypted and if so, prompts to unlock.
194
 * INPUTS
195
 *   None
196
 * RESULT
197
 *   $config 	- rewrites config.xml without encryption
198
 ******/
199
function encrypted_configxml() {
200
	global $g, $config;
201
	if(file_exists($g['conf_path'] . "/config.xml")) {
202
		if($g['booting']) {
203
			$configtxt = file_get_contents($g['conf_path'] . "/config.xml");			
204
			if(tagfile_deformat($configtxt, $configtxt, "config.xml")) {
205
				$fp = fopen('php://stdin', 'r');
206
				$data = "";
207
				echo "\n\n*** Encrypted config.xml detected ***\n";
208
				while($data == "") {
209
					echo "\nEnter the password to decrypt config.xml: ";
210
					$decrypt_password = chop(fgets($fp));
211
					$data = decrypt_data($configtxt, $decrypt_password);
212
					if(!strstr($data, "<pfsense>"))
213
						$data = "";
214
					if($data) {
215
						$fd = fopen($g['conf_path'] . "/config.xml", "w");
216
						fwrite($fd, $data);
217
						fclose($fd);
218
						echo "\nConfig.xml unlocked.\n";
219
						fclose($fp);
220
					} else {
221
						echo "\nInvalid password entered.  Please try again.\n";
222
					}
223
				}
224
			}
225
		}
226
	}
227
}
228

    
229
/****f* config/parse_config
230
 * NAME
231
 *   parse_config - Read in config.cache or config.xml if needed and return $config array
232
 * INPUTS
233
 *   $parse       - boolean to force parse_config() to read config.xml and generate config.cache
234
 * RESULT
235
 *   $config      - array containing all configuration variables
236
 ******/
237
function parse_config($parse = false) {
238
	global $g;
239
	
240
	$lockkey = lock('config');
241
	if (filesize("{$g['conf_path']}/config.xml") == 0) {
242
		$last_backup = discover_last_backup();
243
		if($last_backup) {
244
			log_error("No config.xml found, attempting last known config restore.");
245
			file_notice("config.xml", "No config.xml found, attempting last known config restore.", "pfSenseConfigurator", "");
246
			restore_backup("{$g['conf_path']}/backup/{$last_backup}");
247
		} else {
248
			unlock($lockkey);
249
			die("Config.xml is corrupted and is 0 bytes.  Could not restore a previous backup.");
250
		}
251
	}
252
	if($g['booting']) echo ".";
253
	// Check for encrypted config.xml
254
	encrypted_configxml();
255
	if(!$parse) {
256
		if(file_exists($g['tmp_path'] . '/config.cache')) {
257
			$config = unserialize(file_get_contents($g['tmp_path'] . '/config.cache'));
258
			if(is_null($config)) {
259
				unlock($lockkey);
260
				parse_config(true);
261
				$lockkey = lock('config');
262
			}
263
		} else {
264
			if(!file_exists($g['conf_path'] . "/config.xml")) {
265
				log_error("No config.xml found, attempting last known config restore.");
266
				file_notice("config.xml", "No config.xml found, attempting last known config restore.", "pfSenseConfigurator", "");
267
				$last_backup = discover_last_backup();
268
				if ($last_backup)
269
					restore_backup("/cf/conf/backup/{$last_backup}");
270
				else
271
					log_error("Could not restore config.xml.");
272
			}
273
			unlock($lockkey);
274
			$config = parse_config(true);
275
			$lockkey = lock('config');
276
		}
277
	} else {
278
		if(!file_exists($g['conf_path'] . "/config.xml")) {
279
			if($g['booting']) echo ".";
280
			log_error("No config.xml found, attempting last known config restore.");
281
			file_notice("config.xml", "No config.xml found, attempting last known config restore.", "pfSenseConfigurator", "");
282
			$last_backup = discover_last_backup();
283
			if ($last_backup)
284
				restore_backup("/cf/conf/backup/{$last_backup}");
285
			else
286
				log_error("Could not restore config.xml.");
287
		}
288
		$config = parse_xml_config($g['conf_path'] . '/config.xml', $g['xml_rootobj']);
289
		if($config == "-1") {
290
			$last_backup = discover_last_backup();
291
			if ($last_backup)
292
				restore_backup("/cf/conf/backup/{$last_backup}");
293
			else
294
				log_error(gettext("Could not restore config.xml."));
295
		}
296
		generate_config_cache($config);
297
	}
298
	if($g['booting']) echo ".";
299
	alias_make_table($config);
300
	unlock($lockkey);
301

    
302
	/* process packager manager custom rules */
303
	if(is_dir("/usr/local/pkg/parse_config")) {
304
		update_filter_reload_status("Running plugins (parse_config)");
305
		run_plugins("/usr/local/pkg/parse_config/");
306
		update_filter_reload_status("Plugins completed.");
307
	}
308

    
309
	return $config;
310
}
311

    
312
/****f* config/generate_config_cache
313
 * NAME
314
 *   generate_config_cache - Write serialized configuration to cache.
315
 * INPUTS
316
 *   $config	- array containing current firewall configuration
317
 * RESULT
318
 *   boolean	- true on completion
319
 ******/
320
function generate_config_cache($config) {
321
	global $g;
322

    
323
	$configcache = fopen($g['tmp_path'] . '/config.cache', "w");
324
	fwrite($configcache, serialize($config));
325
	fclose($configcache);
326
}
327

    
328
function discover_last_backup() {
329
        $backups = split("\n", `cd /cf/conf/backup && ls -ltr *.xml | awk '{print \$9}'`);
330
	$last_backup = "";
331
        foreach($backups as $backup)
332
        	if($backup)
333
	        	$last_backup = $backup;
334

    
335
        return $last_backup;
336
}
337

    
338
function restore_backup($file) {
339
	if (file_exists($file)) {
340
		conf_mount_rw();
341
		copy("$file","/cf/conf/config.xml");
342
		unlink_if_exists("/tmp/config.cache");
343
		log_error("{$g['product_name']} is restoring the configuration $file");
344
		file_notice("config.xml", "{$g['product_name']} is restoring the configuration $file", "pfSenseConfigurator", "");
345
		conf_mount_ro();
346
	}
347
}
348

    
349
/****f* config/parse_config_bootup
350
 * NAME
351
 *   parse_config_bootup - Bootup-specific configuration checks.
352
 * RESULT
353
 *   null
354
 ******/
355
function parse_config_bootup() {
356
	global $config, $g, $noparseconfig;
357

    
358
	if($g['booting']) echo ".";
359

    
360
	$lockkey = lock('config');
361
	if (!$noparseconfig) {
362
		if (!file_exists("{$g['conf_path']}/config.xml")) {
363
			if ($g['booting']) {
364
				if (strstr($g['platform'], "cdrom")) {
365
					/* try copying the default config. to the floppy */
366
					echo "Resetting factory defaults...\n";
367
					reset_factory_defaults(true);
368
					if (file_exists("{$g['conf_path']}/config.xml")) {
369
						/* do nothing, we have a file. */
370
					} else {
371
						echo "No XML configuration file found - using factory defaults.\n";
372
						echo "Make sure that the configuration floppy disk with the conf/config.xml\n";
373
						echo "file is inserted. If it isn't, your configuration changes will be lost\n";
374
						echo "on reboot.\n";
375
					}
376
				} else {
377
					$last_backup = discover_last_backup();
378
					if($last_backup) {
379
						log_error("No config.xml found, attempting last known config restore.");
380
						file_notice("config.xml", "No config.xml found, attempting last known config restore.", "pfSenseConfigurator", "");
381
						restore_backup("/cf/conf/backup/{$last_backup}");
382
					}
383
					if(!file_exists("{$g['conf_path']}/config.xml")) {
384
						echo "XML configuration file not found.  {$g['product_name']} cannot continue booting.\n";
385
						mwexec("/sbin/halt");
386
						exit;
387
					}
388
					log_error("Last known config found and restored.  Please double check your configuration file for accuracy.");
389
					file_notice("config.xml", "Last known config found and restored.  Please double check your configuration file for accuracy.", "pfSenseConfigurator", "");
390
				}
391
			} else {
392
				unlock($lockkey);
393
				exit(0);
394
			}
395
		}
396
	}
397
	if (filesize("{$g['conf_path']}/config.xml") == 0) {
398
		$last_backup = discover_last_backup();
399
		if($last_backup) {
400
			log_error("No config.xml found, attempting last known config restore.");
401
			file_notice("config.xml", "No config.xml found, attempting last known config restore.", "pfSenseConfigurator", "");
402
			restore_backup("{$g['conf_path']}/backup/{$last_backup}");
403
		} else {
404
			unlock($lockkey);
405
			die("Config.xml is corrupted and is 0 bytes.  Could not restore a previous backup.");
406
		}
407
	}
408
	unlock($lockkey);
409
	parse_config(true);
410

    
411
	if ((float)$config['version'] > (float)$g['latest_config']) {
412
		echo <<<EOD
413

    
414

    
415
*******************************************************************************
416
* WARNING!                                                                    *
417
* The current configuration has been created with a newer version of {$g['product_name']}  *
418
* than this one! This can lead to serious misbehavior and even security       *
419
* holes! You are urged to either upgrade to a newer version of {$g['product_name']} or     *
420
* revert to the default configuration immediately!                            *
421
*******************************************************************************
422

    
423

    
424
EOD;
425
		}
426

    
427
	/* make alias table (for faster lookups) */
428
	alias_make_table($config);
429
}
430

    
431
/****f* config/conf_mount_rw
432
 * NAME
433
 *   conf_mount_rw - Mount filesystems read/write.
434
 * RESULT
435
 *   null
436
 ******/
437
/* mount flash card read/write */
438
function conf_mount_rw() {
439
	global $g;
440

    
441
	/* do not mount on cdrom platform */
442
	if($g['platform'] == "cdrom" or $g['platform'] == "pfSense")
443
		return;
444
		
445
	if (is_subsystem_dirty('mount'))
446
		return;
447

    
448
	$status = mwexec("/sbin/mount -u -w {$g['cf_path']}");
449
	if($status <> 0) {
450
		if($g['booting'])
451
			echo "Disk is dirty.  Running fsck -y\n";
452
		mwexec("/sbin/fsck -y {$g['cf_path']}");
453
		$status = mwexec("/sbin/mount -u -w {$g['cf_path']}");
454
	}
455

    
456
	/*    if the platform is soekris or wrap or pfSense, lets mount the
457
	 *    compact flash cards root.
458
         */
459
	if($g['platform'] == "wrap" or $g['platform'] == "net45xx"
460
	   or $g['platform'] == "embedded" or $g['platform'] == "nanobsd") {
461
		$status = mwexec("/sbin/mount -u -w /");
462
		/* we could not mount this correctly.  kick off fsck */
463
		if($status <> 0) {
464
			log_error("File system is dirty.  Launching FSCK for /");
465
			mwexec("/sbin/fsck -y /");
466
			$status = mwexec("/sbin/mount -u -w /");
467
		}
468
	}
469
	
470
	mark_subsystem_dirty('mount');
471
}
472

    
473
/****f* config/conf_mount_ro
474
 * NAME
475
 *   conf_mount_ro - Mount filesystems readonly.
476
 * RESULT
477
 *   null
478
 ******/
479
function conf_mount_ro() {
480
	global $g;
481

    
482
	if($g['booting'] == true)
483
		return;
484

    
485
	/* firmare upgrade in progress */
486
	if (is_subsystem_dirty('firmwarelock'))
487
		return;
488

    
489
	/* do not umount if generating ssh keys */
490
	if (is_subsystem_dirty('sshdkeys'))
491
		return;
492

    
493
	/* do not umount on cdrom or pfSense platforms */
494
	if($g['platform'] == "cdrom" or $g['platform'] == "pfSense")
495
		return;
496

    
497
	clear_subsystem_dirty('mount');
498
	/* sync data, then force a remount of /cf */
499
	mwexec("/bin/sync");
500
	mwexec("/bin/sync");
501
	mwexec("/sbin/mount -u -r -f {$g['cf_path']}");
502
	mwexec("/sbin/mount -u -r -f /");
503
}
504

    
505
/****f* config/convert_config
506
 * NAME
507
 *   convert_config - Attempt to update config.xml.
508
 * DESCRIPTION
509
 *   convert_config() reads the current global configuration
510
 *   and attempts to convert it to conform to the latest
511
 *   config.xml version. This allows major formatting changes
512
 *   to be made with a minimum of breakage.
513
 * RESULT
514
 *   null
515
 ******/
516
/* convert configuration, if necessary */
517
function convert_config() {
518
	global $config, $g;
519
	$now = date("H:i:s");
520
	log_error("Start Configuration upgrade at $now, set execution timeout to 15 minutes");
521
	ini_set("max_execution_time", "900");
522

    
523
	/* special case upgrades */
524
	/* fix every minute crontab bogons entry */
525
	$cron_item_count = count($config['cron']['item']);
526
	for($x=0; $x<$cron_item_count; $x++) {
527
		if(stristr($config['cron']['item'][$x]['command'], "rc.update_bogons.sh")) {
528
			if($config['cron']['item'][$x]['hour'] == "*" ) {
529
		        $config['cron']['item'][$x]['hour'] = "3";
530
		 		write_config("Updated bogon update frequency to 3am");
531
		 		log_error("Updated bogon update frequency to 3am");
532
		 	}       
533
		}
534
	}
535
	if ($config['version'] == $g['latest_config'])
536
		return;		/* already at latest version */
537

    
538
	// Save off config version
539
	$prev_version = $config['version'];
540
	
541
	include_once('upgrade_config.inc');
542
	/* Loop and run upgrade_VER_to_VER() until we're at current version */
543
	while ($config['version'] < $g['latest_config']) {
544
		$cur = $config['version'] * 10;
545
		$next = $cur + 1;
546
		$migration_function = sprintf('upgrade_%03d_to_%03d', $cur, $next);
547
		$migration_function();
548
		$config['version'] = sprintf('%.1f', $next / 10);
549
		echo ".";
550
	}
551

    
552
	$now = date("H:i:s");
553
	log_error("Ended Configuration upgrade at $now");
554

    
555
	if ($prev_version != $config['version'])
556
		write_config("Upgraded config version level from {$prev_version} to {$config['version']}");
557
}
558

    
559
/****f* config/write_config
560
 * NAME
561
 *   write_config - Backup and write the firewall configuration.
562
 * DESCRIPTION
563
 *   write_config() handles backing up the current configuration,
564
 *   applying changes, and regenerating the configuration cache.
565
 * INPUTS
566
 *   $desc	- string containing the a description of configuration changes
567
 *   $backup	- boolean: do not back up current configuration if false.
568
 * RESULT
569
 *   null
570
 ******/
571
/* save the system configuration */
572
function write_config($desc="Unknown", $backup = true) {
573
	global $config, $g;
574

    
575
	if($g['bootup']) 
576
		log_error("WARNING! Configuration written on bootup.  This can cause stray openvpn and load balancing items in config.xml");
577

    
578
	if($backup)
579
		backup_config();
580

    
581
	if (time() > mktime(0, 0, 0, 9, 1, 2004))       /* make sure the clock settings are plausible */
582
		$changetime = time();
583

    
584
	/* Log the running script so it's not entirely unlogged what changed */
585
    if ($desc == "Unknown")
586
		$desc = "{$_SERVER['SCRIPT_NAME']} made unknown change";
587

    
588
	$config['revision']['description'] = $desc;
589
	$config['revision']['time'] = $changetime;
590

    
591
	$lockkey = lock('config');
592

    
593
	/* generate configuration XML */
594
	$xmlconfig = dump_xml_config($config, $g['xml_rootobj']);
595

    
596
	conf_mount_rw();
597

    
598
	/* write new configuration */
599
	$fd = fopen("{$g['cf_conf_path']}/config.xml", "w");
600
        if (!$fd) {
601
                // Unable to open temporary file for writing
602
		log_error("WARNING: Config contents could not be save. Could not open file!");
603
		unlock($lockkey);
604
                return false;
605
        }
606
        if (!fwrite($fd, $xmlconfig)) {
607
                // Unable to write to temporary file
608
		log_error("WARNING: Config contents could not be written on file.");
609
                fclose($fd);
610
		unlock($lockkey);
611
                return false;
612
        }
613
        fclose($fd);
614

    
615
	if($g['platform'] == "embedded" or $g['platform'] == "nanobsd") {
616
		cleanup_backupcache(5, true);
617
	} else {
618
		cleanup_backupcache(30, true);
619
	}
620

    
621
	/* re-read configuration */
622
	$config = parse_xml_config("{$g['conf_path']}/config.xml", $g['xml_rootobj']);
623

    
624
	/* write config cache */
625
	$fd = @fopen("{$g['tmp_path']}/config.cache", "wb");
626
	if ($fd) {
627
		fwrite($fd, serialize($config));
628
		fclose($fd);
629
	}
630

    
631
	/* tell kernel to sync fs data */
632
	if (!$g['booting'])
633
		conf_mount_ro();
634
	unlock($lockkey);
635

    
636
	unlink_if_exists("/usr/local/pkg/pf/carp_sync_client.php");
637
	/* sync carp entries to other firewalls */
638
        carp_sync_client();
639

    
640
	if(is_dir("/usr/local/pkg/write_config")) {
641
		/* process packager manager custom rules */
642
		update_filter_reload_status("Running plugins");
643
		run_plugins("/usr/local/pkg/write_config/");
644
		update_filter_reload_status("Plugins completed.");
645
	}
646

    
647
	return $config;
648
}
649

    
650
/****f* config/reset_factory_defaults
651
 * NAME
652
 *   reset_factory_defaults - Reset the system to its default configuration.
653
 * RESULT
654
 *   integer	- indicates completion
655
 ******/
656
function reset_factory_defaults($lock = false) {
657
	global $g;
658

    
659
	if (!$lock)
660
		$lockkey = lock('config');
661
	conf_mount_rw();
662

    
663
	/* create conf directory, if necessary */
664
	safe_mkdir("{$g['cf_conf_path']}");
665

    
666
	/* clear out /conf */
667
	$dh = opendir($g['conf_path']);
668
	while ($filename = readdir($dh)) {
669
		if (($filename != ".") && ($filename != "..")) {
670
			unlink_if_exists($g['conf_path'] . "/" . $filename);
671
		}
672
	}
673
	closedir($dh);
674

    
675
	/* copy default configuration */
676
	copy("{$g['conf_default_path']}/config.xml", "{$g['conf_path']}/config.xml");
677

    
678
	/* call the wizard */
679
	touch("/conf/trigger_initial_wizard");
680
	conf_mount_ro();
681
	if (!$lock)
682
		unlock($lockkey);
683

    
684
	return 0;
685
}
686

    
687
function config_restore($conffile) {
688
	global $config, $g;
689

    
690
	if (!file_exists($conffile))
691
		return 1;
692

    
693
	$lockkey = lock('config');
694
	conf_mount_rw();
695

    
696
	backup_config();
697
	copy($conffile, "{$g['cf_conf_path']}/config.xml");
698
	unlock($lockkey);
699
	$config = parse_config(true);
700
	$lockkey = lock('config');
701
	write_config("Reverted to " . array_pop(explode("/", $conffile)) . ".", false);
702

    
703
	conf_mount_ro();
704
	unlock($lockkey);
705

    
706
	return 0;
707
}
708

    
709
function config_install($conffile) {
710
	global $config, $g;
711

    
712
	if (!file_exists($conffile))
713
		return 1;
714

    
715
	if (!config_validate("{$g['conf_path']}/config.xml"))
716
		return 1;
717

    
718
	if($g['booting'] == true)
719
		echo "Installing configuration...\n";
720

    
721
	$lockkey = lock('config');
722
	conf_mount_rw();
723

    
724
	copy($conffile, "{$g['conf_path']}/config.xml");
725

    
726
	/* unlink cache file if it exists */
727
	if(file_exists("{$g['tmp_path']}/config.cache"))
728
		unlink("{$g['tmp_path']}/config.cache");
729

    
730
	conf_mount_ro();
731
	unlock($lockkey);
732

    
733
    return 0;
734
}
735

    
736
function config_validate($conffile) {
737

    
738
	global $g, $xmlerr;
739

    
740
	$xml_parser = xml_parser_create();
741

    
742
	if (!($fp = fopen($conffile, "r"))) {
743
		$xmlerr = "XML error: unable to open file";
744
		return false;
745
	}
746

    
747
	while ($data = fread($fp, 4096)) {
748
		if (!xml_parse($xml_parser, $data, feof($fp))) {
749
			$xmlerr = sprintf("%s at line %d",
750
						xml_error_string(xml_get_error_code($xml_parser)),
751
						xml_get_current_line_number($xml_parser));
752
			return false;
753
		}
754
	}
755
	xml_parser_free($xml_parser);
756

    
757
	fclose($fp);
758

    
759
	return true;
760
}
761

    
762
function set_networking_interfaces_ports() {
763
	global $noreboot;
764
	global $config;
765
	global $g;
766
	global $fp;
767

    
768
	$fp = fopen('php://stdin', 'r');
769

    
770
	$memory = get_memory();
771
	$avail = $memory[0];
772

    
773
	if($avail < $g['minimum_ram_warning']) {
774
		echo "\n\n\n";
775
		echo "DANGER!  WARNING!  ACHTUNG!\n\n";
776
		echo "{$g['product_name']} requires *AT LEAST* {$g['minimum_ram_warning_text']} RAM to function correctly.\n";
777
		echo "Only ({$avail}) MB RAM has been detected.\n";
778
		echo "\nPress ENTER to continue. ";
779
		fgets($fp);
780
		echo "\n";
781
	}
782

    
783
	$iflist = get_interface_list();
784

    
785
	echo <<<EOD
786

    
787
Valid interfaces are:
788

    
789

    
790
EOD;
791

    
792
	if(!is_array($iflist)) {
793
		echo "No interfaces found!\n";
794
		$iflist = array();
795
	} else {
796
		foreach ($iflist as $iface => $ifa) {
797
			echo sprintf("% -8s%s%s\t%s\n", $iface, $ifa['mac'],
798
				$ifa['up'] ? "   (up)" : "   (down)", $ifa['dmesg']);
799
		}
800
	}
801

    
802
	echo <<<EOD
803

    
804
Do you want to set up VLANs first?
805
If you are not going to use VLANs, or only for optional interfaces, you should
806
say no here and use the webConfigurator to configure VLANs later, if required.
807

    
808
Do you want to set up VLANs now [y|n]?
809
EOD;
810

    
811
	if (strcasecmp(chop(fgets($fp)), "y") == 0)
812
		vlan_setup();
813

    
814
	if (is_array($config['vlans']['vlan']) && count($config['vlans']['vlan'])) {
815

    
816
		echo "\n\nVLAN interfaces:\n\n";
817
		foreach ($config['vlans']['vlan'] as $vlan) {
818

    
819
			echo sprintf("% -16s%s\n", "{$vlan['if']}_vlan{$vlan['tag']}",
820
				"VLAN tag {$vlan['tag']}, parent interface {$vlan['if']}");
821

    
822
			$iflist[$vlan['if'] . '_vlan' . $vlan['tag']] = array();
823
		}
824
	}
825

    
826
	echo <<<EOD
827

    
828
*NOTE*  {$g['product_name']} requires {$g['minimum_nic_count_text']} assigned interfaces to function.
829
        If you do not have {$g['minimum_nic_count_text']} interfaces you CANNOT continue. 
830

    
831
        If you do not have at least {$g['minimum_nic_count']} *REAL* network interface cards
832
        or one interface with multiple VLANs then {$g['product_name']}
833
        *WILL NOT* function correctly.
834

    
835
If you do not know the names of your interfaces, you may choose to use
836
auto-detection. In that case, disconnect all interfaces now before
837
hitting 'a' to initiate auto detection.
838

    
839
EOD;
840

    
841
	do {
842
		echo "\nEnter the WAN interface name or 'a' for auto-detection: ";
843
		$wanif = chop(fgets($fp));
844
		if ($wanif === "") {
845
			return;
846
		}
847
		if ($wanif === "a")
848
			$wanif = autodetect_interface("WAN", $fp);
849
		else if (!array_key_exists($wanif, $iflist)) {
850
			echo "\nInvalid interface name '{$wanif}'\n";
851
			unset($wanif);
852
			continue;
853
		}
854
	} while (!$wanif);
855

    
856
	do {
857
		echo "\nEnter the LAN interface name or 'a' for auto-detection \n" .
858
		    "NOTE: this enables full Firewalling/NAT mode.\n" .
859
			"(or nothing if finished): ";
860

    
861
		$lanif = chop(fgets($fp));
862
		
863
		if($lanif == "exit") {
864
			exit;
865
		}
866
		
867
		if($lanif == "") {
868
			if($g['minimum_nic_count'] < 2) {
869
				break;	
870
			} else {
871
				fclose($fp);
872
				return;
873
			}
874
		}
875

    
876
		if ($lanif === "a")
877
			$lanif = autodetect_interface("LAN", $fp);
878
		else if (!array_key_exists($lanif, $iflist)) {
879
			echo "\nInvalid interface name '{$lanif}'\n";
880
			unset($lanif);
881
			continue;
882
		}
883
	} while (!$lanif);
884

    
885
	/* optional interfaces */
886
	$i = 0;
887
	$optif = array();
888

    
889
	if($lanif <> "") {
890
		while (1) {
891
			if ($optif[$i])
892
				$i++;
893
			$i1 = $i + 1;
894
	
895
			if($config['interfaces']['opt' . $i1]['descr'])
896
				echo "\nOptional interface {$i1} description found: {$config['interfaces']['opt' . $i1]['descr']}";
897

    
898
			echo "\nEnter the Optional {$i1} interface name or 'a' for auto-detection\n" .
899
				"(or nothing if finished): ";
900
	
901
			$optif[$i] = chop(fgets($fp));
902
	
903
			if ($optif[$i]) {
904
				if ($optif[$i] === "a") {
905
					$ad = autodetect_interface("Optional " . $i1, $fp);
906
					if ($ad)
907
						$optif[$i] = $ad;
908
					else
909
						unset($optif[$i]);
910
				} else if (!array_key_exists($optif[$i], $iflist)) {
911
					echo "\nInvalid interface name '{$optif[$i]}'\n";
912
					unset($optif[$i]);
913
					continue;
914
				}
915
			} else {
916
				unset($optif[$i]);
917
				break;
918
			}
919
		}
920
	}
921
	
922
	/* check for double assignments */
923
	$ifarr = array_merge(array($lanif, $wanif), $optif);
924
	
925
	for ($i = 0; $i < (count($ifarr)-1); $i++) {
926
	for ($j = ($i+1); $j < count($ifarr); $j++) {
927
		if ($ifarr[$i] == $ifarr[$j]) {
928
			echo <<<EOD
929

    
930
Error: you cannot assign the same interface name twice!
931

    
932
EOD;
933
				fclose($fp);
934
				return;
935
			}
936
		}
937
	}
938

    
939
	echo "\nThe interfaces will be assigned as follows: \n\n";
940

    
941
	if ($lanif != "")
942
		echo "LAN  ->" . $lanif . "\n";
943
	echo "WAN  ->" . $wanif . "\n";
944
	for ($i = 0; $i < count($optif); $i++) {
945
		echo "OPT" . ($i+1) . " -> " . $optif[$i] . "\n";
946
	}
947

    
948
echo <<<EOD
949

    
950
Do you want to proceed [y|n]?
951
EOD;
952

    
953
	if (strcasecmp(chop(fgets($fp)), "y") == 0) {
954
		if($lanif) {
955
			$config['interfaces']['lan']['if'] = $lanif;
956
		} elseif (!$g['booting']) {
957

    
958
echo <<<EODD
959

    
960
You have chosen to remove the LAN interface.
961

    
962
Would you like to remove the LAN IP address and
963
unload the interface now? [y|n]? 
964
EODD;
965

    
966
				if (strcasecmp(chop(fgets($fp)), "y") == 0) {
967
					if($config['interfaces']['lan']['if'])
968
						mwexec("/sbin/ifconfig delete " . $config['interfaces']['lan']['if']);
969
				}
970
				if(isset($config['interfaces']['lan']))
971
					unset($config['interfaces']['lan']);
972
				if(isset($config['dhcpd']['lan']))
973
					unset($config['dhcpd']['lan']);
974
				if(isset($config['interfaces']['lan']['if']))
975
					unset($config['interfaces']['lan']['if']);
976
				if(isset($config['interfaces']['wan']['blockpriv']))
977
					unset($config['interfaces']['wan']['blockpriv']);
978
				if(isset($config['shaper']))
979
					unset($config['shaper']);
980
				if(isset($config['ezshaper']))
981
					unset($config['ezshaper']);
982
				if(isset($config['nat']))
983
					unset($config['nat']);				
984
		} else {
985
			if(isset($config['interfaces']['lan']['if']))
986
				mwexec("/sbin/ifconfig delete " . $config['interfaces']['lan']['if']);
987
			if(isset($config['interfaces']['lan']))
988
				unset($config['interfaces']['lan']);
989
			if(isset($config['dhcpd']['lan']))
990
				unset($config['dhcpd']['lan']);
991
			if(isset($config['interfaces']['lan']['if']))
992
				unset($config['interfaces']['lan']['if']);
993
			if(isset($config['interfaces']['wan']['blockpriv']))
994
				unset($config['interfaces']['wan']['blockpriv']);
995
			if(isset($config['shaper']))
996
				unset($config['shaper']);
997
			if(isset($config['ezshaper']))
998
				unset($config['ezshaper']);
999
			if(isset($config['nat']))
1000
				unset($config['nat']);				
1001
		}
1002
		if (preg_match($g['wireless_regex'], $lanif)) {
1003
			if (!is_array($config['interfaces']['lan']['wireless']))
1004
				$config['interfaces']['lan']['wireless'] = array();
1005
		} else {
1006
			unset($config['interfaces']['lan']['wireless']);
1007
		}
1008

    
1009
		$config['interfaces']['wan']['if'] = $wanif;
1010
		if (preg_match($g['wireless_regex'], $wanif)) {
1011
			if (!is_array($config['interfaces']['wan']['wireless']))
1012
				$config['interfaces']['wan']['wireless'] = array();
1013
		} else {
1014
			unset($config['interfaces']['wan']['wireless']);
1015
		}
1016

    
1017
		for ($i = 0; $i < count($optif); $i++) {
1018
			if (!is_array($config['interfaces']['opt' . ($i+1)]))
1019
				$config['interfaces']['opt' . ($i+1)] = array();
1020

    
1021
			$config['interfaces']['opt' . ($i+1)]['if'] = $optif[$i];
1022

    
1023
			/* wireless interface? */
1024
			if (preg_match($g['wireless_regex'], $optif[$i])) {
1025
				if (!is_array($config['interfaces']['opt' . ($i+1)]['wireless']))
1026
					$config['interfaces']['opt' . ($i+1)]['wireless'] = array();
1027
			} else {
1028
				unset($config['interfaces']['opt' . ($i+1)]['wireless']);
1029
			}
1030

    
1031
			unset($config['interfaces']['opt' . ($i+1)]['enable']);
1032
			$config['interfaces']['opt' . ($i+1)]['descr'] = "OPT" . ($i+1);
1033
		}
1034

    
1035
		/* remove all other (old) optional interfaces */
1036
		for (; isset($config['interfaces']['opt' . ($i+1)]); $i++)
1037
			unset($config['interfaces']['opt' . ($i+1)]);
1038

    
1039
		echo "\nWriting configuration...";
1040
		write_config();
1041
		echo "done.\n";
1042

    
1043
		echo <<<EOD
1044

    
1045

    
1046

    
1047
EOD;
1048

    
1049
		fclose($fp);
1050
		if($g['booting'])
1051
			return;
1052

    
1053
		echo "One moment while we reload the settings...";
1054

    
1055
		$g['booting'] = false;
1056

    
1057
		/* XXX: ermal - disable it for now this is used during bootup at best so shouldn't be needed.
1058
		 * 		For now just comment it out and later remove it completely.
1059
		 * resync everything 
1060
			reload_all_sync();
1061
		 */
1062

    
1063
		echo " done!\n";
1064

    
1065
		touch("{$g['tmp_path']}/assign_complete");
1066

    
1067
	}
1068
}
1069

    
1070
function autodetect_interface($ifname, $fp) {
1071
	$iflist_prev = get_interface_list("media");
1072
	echo <<<EOD
1073

    
1074
Connect the {$ifname} interface now and make sure that the link is up.
1075
Then press ENTER to continue.
1076

    
1077
EOD;
1078
	fgets($fp);
1079
	$iflist = get_interface_list("media");
1080

    
1081
	foreach ($iflist_prev as $ifn => $ifa) {
1082
		if (!$ifa['up'] && $iflist[$ifn]['up']) {
1083
			echo "Detected link-up on interface {$ifn}.\n";
1084
			return $ifn;
1085
		}
1086
	}
1087

    
1088
	echo "No link-up detected.\n";
1089

    
1090
	return null;
1091
}
1092

    
1093
function vlan_setup() {
1094
	global $iflist, $config, $g, $fp;
1095

    
1096
	$iflist = get_interface_list();
1097

    
1098
	if (is_array($config['vlans']['vlan']) && count($config['vlans']['vlan'])) {
1099

    
1100
	echo <<<EOD
1101

    
1102
WARNING: all existing VLANs will be cleared if you proceed!
1103

    
1104
Do you want to proceed [y|n]?
1105
EOD;
1106

    
1107
	if (strcasecmp(chop(fgets($fp)), "y") != 0)
1108
		return;
1109
	}
1110

    
1111
	$config['vlans']['vlan'] = array();
1112
	echo "\n";
1113

    
1114
	$vlanif = 0;
1115

    
1116
	while (1) {
1117
		$vlan = array();
1118

    
1119
		echo "\n\nVLAN Capable interfaces:\n\n";
1120
		if(!is_array($iflist)) {
1121
			echo "No interfaces found!\n";
1122
		} else {
1123
			$vlan_capable=0;
1124
			foreach ($iflist as $iface => $ifa) {
1125
				if (is_jumbo_capable($iface)) {
1126
					echo sprintf("% -8s%s%s\n", $iface, $ifa['mac'],
1127
						$ifa['up'] ? "   (up)" : "");
1128
					$vlan_capable++;
1129
				}
1130
			}
1131
		}
1132

    
1133
		if($vlan_capable == 0) {
1134
			echo "No VLAN capable interfaces detected.\n";
1135
			return;
1136
		}
1137

    
1138
		echo "\nEnter the parent interface name for the new VLAN (or nothing if finished): ";
1139
		$vlan['if'] = chop(fgets($fp));
1140

    
1141
		if ($vlan['if']) {
1142
			if (!array_key_exists($vlan['if'], $iflist) or
1143
			    !is_jumbo_capable($vlan['if'])) {
1144
				echo "\nInvalid interface name '{$vlan['if']}'\n";
1145
				continue;
1146
			}
1147
		} else {
1148
			break;
1149
		}
1150

    
1151
		echo "Enter the VLAN tag (1-4094): ";
1152
		$vlan['tag'] = chop(fgets($fp));
1153
		$vlan['vlanif'] = "{$vlan['if']}_vlan{$vlan['tag']}";
1154
		if (!is_numericint($vlan['tag']) || ($vlan['tag'] < 1) || ($vlan['tag'] > 4094)) {
1155
			echo "\nInvalid VLAN tag '{$vlan['tag']}'\n";
1156
			continue;
1157
		}
1158
		
1159
		$config['vlans']['vlan'][] = $vlan;
1160
		$vlanif++;
1161
	}
1162
}
1163

    
1164
function cleanup_backupcache($revisions = 30, $lock = false) {
1165
	global $g;
1166
	$i = false;
1167
	
1168
	if (!$lock)
1169
		$lockkey = lock('config');
1170
	if(file_exists($g['cf_conf_path'] . '/backup/backup.cache')) {
1171
		conf_mount_rw();
1172
		$backups = get_backups();
1173
		$newbaks = array();
1174
		$bakfiles = glob($g['cf_conf_path'] . "/backup/config-*");
1175
		$baktimes = $backups['versions'];
1176
		$tocache = array();
1177
		unset($backups['versions']);
1178
   		foreach($bakfiles as $backup) { // Check for backups in the directory not represented in the cache.
1179
   			if(filesize($backup) == 0) {
1180
   				unlink($backup);
1181
   				continue;
1182
   			}
1183
			$tocheck = array_shift(explode('.', array_pop(explode('-', $backup))));
1184
            if(!in_array($tocheck, $baktimes)) {
1185
				$i = true;
1186
				if($g['booting'])
1187
					echo ".";
1188
				$newxml = parse_xml_config($backup, $g['xml_rootobj']);
1189
				if($newxml == "-1") {
1190
					log_error("The backup cache file $backup is corrupted.  Unlinking.");
1191
					unlink($backup);
1192
					log_error("The backup cache file $backup is corrupted.  Unlinking.");
1193
					continue;
1194
				}
1195
				if($newxml['revision']['description'] == "")
1196
					$newxml['revision']['description'] = "Unknown";
1197
				$tocache[$tocheck] = array('description' => $newxml['revision']['description']);
1198
			}
1199
    	}
1200
		foreach($backups as $checkbak) {
1201

    
1202
			if(count(preg_grep('/' . $checkbak['time'] . '/i', $bakfiles)) != 0) {
1203
				$newbaks[] = $checkbak;
1204
			} else {
1205
				$i = true;
1206
				if($g['booting']) print " " . $tocheck . "r";
1207
			}
1208
		}
1209
		foreach($newbaks as $todo) $tocache[$todo['time']] = array('description' => $todo['description']);
1210
		if(is_int($revisions) and (count($tocache) > $revisions)) {
1211
			$toslice = array_slice(array_keys($tocache), 0, $revisions);
1212
			foreach($toslice as $sliced)
1213
				$newcache[$sliced] = $tocache[$sliced];
1214
			foreach($tocache as $version => $versioninfo) {
1215
				if(!in_array($version, array_keys($newcache))) {
1216
					unlink_if_exists($g['conf_path'] . '/backup/config-' . $version . '.xml');
1217
					if($g['booting']) print " " . $tocheck . "d";
1218
				}
1219
			}
1220
			$tocache = $newcache;
1221
		}
1222
		$bakout = fopen($g['cf_conf_path'] . '/backup/backup.cache', "w");
1223
        fwrite($bakout, serialize($tocache));
1224
		fclose($bakout);
1225
		conf_mount_ro();
1226
	}
1227
	if($g['booting'] && $i)
1228
		print "done.\n";
1229
	if (!$lock)
1230
		unlock($lockkey);
1231
}
1232

    
1233
function get_backups() {
1234
	global $g;
1235
	if(file_exists("{$g['cf_conf_path']}/backup/backup.cache")) {
1236
		$confvers = unserialize(file_get_contents("{$g['cf_conf_path']}/backup/backup.cache"));
1237
		$bakvers = array_keys($confvers);
1238
		$toreturn = array();
1239
		sort($bakvers);
1240
		// 	$bakvers = array_reverse($bakvers);
1241
		foreach(array_reverse($bakvers) as $bakver)
1242
			$toreturn[] = array('time' => $bakver, 'description' => $confvers[$bakver]['description']);
1243
	} else {
1244
		return false;
1245
	}
1246
	$toreturn['versions'] = $bakvers;
1247
	return $toreturn;
1248
}
1249

    
1250
function backup_config() {
1251
	global $config, $g;
1252

    
1253
	if($g['platform'] == "cdrom")
1254
		return;
1255

    
1256
	conf_mount_rw();
1257

    
1258
	/* Create backup directory if needed */
1259
	safe_mkdir("{$g['cf_conf_path']}/backup");
1260

    
1261
    if($config['revision']['time'] == "") {
1262
            $baktime = 0;
1263
    } else {
1264
            $baktime = $config['revision']['time'];
1265
    }
1266
    if($config['revision']['description'] == "") {
1267
            $bakdesc = "Unknown";
1268
    } else {
1269
            $bakdesc = $config['revision']['description'];
1270
    }
1271
    copy($g['cf_conf_path'] . '/config.xml', $g['cf_conf_path'] . '/backup/config-' . $baktime . '.xml');
1272
    if(file_exists($g['cf_conf_path'] . '/backup/backup.cache')) {
1273
            $backupcache = unserialize(file_get_contents($g['cf_conf_path'] . '/backup/backup.cache'));
1274
    } else {
1275
            $backupcache = array();
1276
    }
1277
    $backupcache[$baktime] = array('description' => $bakdesc);
1278
    $bakout = fopen($g['cf_conf_path'] . '/backup/backup.cache', "w");
1279
    fwrite($bakout, serialize($backupcache));
1280
    fclose($bakout);
1281

    
1282
	conf_mount_ro();
1283

    
1284
	return true;
1285
}
1286

    
1287
function set_device_perms() {
1288
	$devices = array(
1289
		'pf'	=> array(	'user'	=> 'proxy',
1290
					'group'	=> 'proxy',
1291
					'mode'	=> 0660),
1292
		);
1293

    
1294
	foreach ($devices as $name => $attr) {
1295
		$path = "/dev/$name";
1296
		if (file_exists($path)) {
1297
			chown($path, $attr['user']);
1298
			chgrp($path, $attr['group']);
1299
			chmod($path, $attr['mode']);
1300
		}
1301
	}
1302
}
1303

    
1304
if($g['booting']) echo ".";
1305
$config = parse_config();
1306

    
1307
?>
(9-9/41)