Project

General

Profile

Download (32.5 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
 * pkg-utils.inc
4
 */
5
/* ====================================================================
6
 *	Copyright (c)  2004-2015  Electric Sheep Fencing, LLC. All rights reserved.
7
 *	Copyright (c)  2005-2006 Colin Smith (ethethlay@gmail.com)
8
 *
9
 *	Redistribution and use in source and binary forms, with or without modification,
10
 *	are permitted provided that the following conditions are met:
11
 *
12
 *	1. Redistributions of source code must retain the above copyright notice,
13
 *		this list of conditions and the following disclaimer.
14
 *
15
 *	2. Redistributions in binary form must reproduce the above copyright
16
 *		notice, this list of conditions and the following disclaimer in
17
 *		the documentation and/or other materials provided with the
18
 *		distribution.
19
 *
20
 *	3. All advertising materials mentioning features or use of this software
21
 *		must display the following acknowledgment:
22
 *		"This product includes software developed by the pfSense Project
23
 *		 for use in the pfSense software distribution. (http://www.pfsense.org/).
24
 *
25
 *	4. The names "pfSense" and "pfSense Project" must not be used to
26
 *		 endorse or promote products derived from this software without
27
 *		 prior written permission. For written permission, please contact
28
 *		 coreteam@pfsense.org.
29
 *
30
 *	5. Products derived from this software may not be called "pfSense"
31
 *		nor may "pfSense" appear in their names without prior written
32
 *		permission of the Electric Sheep Fencing, LLC.
33
 *
34
 *	6. Redistributions of any form whatsoever must retain the following
35
 *		acknowledgment:
36
 *
37
 *	"This product includes software developed by the pfSense Project
38
 *	for use in the pfSense software distribution (http://www.pfsense.org/).
39
 *
40
 *	THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY
41
 *	EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
42
 *	IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
43
 *	PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR
44
 *	ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
45
 *	SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
46
 *	NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
47
 *	LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
48
 *	HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
49
 *	STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
50
 *	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
51
 *	OF THE POSSIBILITY OF SUCH DAMAGE.
52
 *
53
 *	====================================================================
54
 *
55
 */
56

    
57
require_once("globals.inc");
58
require_once("service-utils.inc");
59

    
60
if (file_exists("/cf/conf/use_xmlreader")) {
61
	require_once("xmlreader.inc");
62
} else {
63
	require_once("xmlparse.inc");
64
}
65

    
66
require_once("pfsense-utils.inc");
67

    
68
if (!function_exists("pkg_debug")) {
69
	/* set up logging if needed */
70
	function pkg_debug($msg) {
71
		global $g, $debug, $fd_log;
72

    
73
		if (!$debug) {
74
			return;
75
		}
76

    
77
		if (!$fd_log) {
78
			if (!$fd_log = fopen("{$g['tmp_path']}/pkg_mgr_debug.log", "w")) {
79
				update_status(gettext("Warning, could not open log for writing.") . "\n");
80
			}
81
		}
82
		@fwrite($fd_log, $msg);
83
	}
84
}
85

    
86
/* Remove pkg_prefix from package name if it's present */
87
function pkg_remove_prefix(&$pkg_name) {
88
	global $g;
89

    
90
	if (substr($pkg_name, 0, strlen($g['pkg_prefix'])) == $g['pkg_prefix']) {
91
		$pkg_name = substr($pkg_name, strlen($g['pkg_prefix']));
92
	}
93
}
94

    
95
/* Execute pkg update when it's necessary */
96
function pkg_update($force = false) {
97
	global $g;
98

    
99
	return pkg_call("update" . ($force ? " -f" : ""));
100
}
101

    
102
/* return an array with necessary environment vars for pkg */
103
function pkg_env() {
104
	global $config, $g;
105

    
106
	$pkg_env_vars = array(
107
		"LANG" => "C",
108
		"HTTP_USER_AGENT" => $user_agent,
109
		"ASSUME_ALWAYS_YES" => "true"
110
	);
111

    
112
	if ($g['platform'] == "nanobsd" ||
113
	    isset($config['system']['use_mfs_tmpvar'])) {
114
		$pkg_env_vars['PKG_DBDIR'] = '/root/var/db/pkg';
115
		$pkg_env_vars['PKG_CACHEDIR'] = '/root/var/cache/pkg';
116
	}
117

    
118
	return $pkg_env_vars;
119
}
120

    
121
/* Execute a pkg call */
122
function pkg_call($params, $mute = false) {
123
	global $g, $config;
124

    
125
	if (empty($params)) {
126
		return false;
127
	}
128

    
129
	$user_agent = $g['product_name'] . '/' . $g['product_version'];
130
	if (!isset($config['system']['do_not_send_host_uuid'])) {
131
		$user_agent .= ' : ' . get_single_sysctl('kern.hostuuid');
132
	}
133

    
134
	$descriptorspec = array(
135
		1 => array("pipe", "w"), /* stdout */
136
		2 => array("pipe", "w")	 /* stderr */
137
	);
138

    
139
	conf_mount_rw();
140

    
141
	pkg_debug("pkg_call(): {$params}\n");
142
	$process = proc_open("/usr/sbin/pkg {$params}", $descriptorspec, $pipes,
143
	    '/', pkg_env());
144

    
145
	if (!is_resource($process)) {
146
		conf_mount_ro();
147
		return false;
148
	}
149

    
150
	stream_set_blocking($pipes[1], 0);
151
	stream_set_blocking($pipes[2], 0);
152

    
153
	/* XXX: should be a tunnable? */
154
	$timeout = 300; // seconds
155
	$error_log = '';
156
	$started = time();
157
	$maxwaittime = 10; // Number of seconds to wait for a response fromteh pacakge we are calling
158

    
159
	do {
160
		$write = array();
161
		$read = array($pipes[1], $pipes[2]);
162
		$except = array();
163

    
164
		$stream = stream_select($read, $write, $except, null, $timeout);
165
		if ($stream !== FALSE && $stream > 0) {
166
			foreach ($read as $pipe) {
167
				$content = stream_get_contents($pipe);
168
				if ($content == '') {
169
					continue;
170
				}
171
				if ($pipe === $pipes[1]) {
172
					if (!$mute) {
173
						update_status($content);
174
					}
175
					flush();
176
				} else if ($pipe === $pipes[2]) {
177
					$error_log .= $content;
178
				}
179
			}
180
		}
181

    
182
		$status = proc_get_status($process);
183

    
184
		$now = time();
185

    
186
		if (($now - $started) >= $maxwaittime) {
187
			$rc = -1;
188
			proc_terminate($process);
189
			break;
190
		}
191

    
192
	} while ($status['running']);
193

    
194
	fclose($pipes[1]);
195
	fclose($pipes[2]);
196
	proc_close($process);
197

    
198
	conf_mount_ro();
199

    
200
	if (!isset($rc)) {
201
		$rc = $status['exitcode'];
202
	}
203

    
204
	pkg_debug("pkg_call(): rc = {$rc}\n");
205
	if ($rc == 0) {
206
		return true;
207
	}
208

    
209
	pkg_debug("pkg_call(): error_log\n{$error_log}\n");
210
	if (!$mute) {
211
		update_status("\n\n" .  sprintf(gettext(
212
		    "ERROR!!! An error occurred on pkg execution (rc = %d) with parameters '%s':"),
213
		    $rc, $params) . "\n" . $error_log . "\n");
214
	}
215

    
216
	return false;
217
}
218

    
219
/* Execute pkg with $params, fill stdout and stderr and return pkg rc */
220
function pkg_exec($params, &$stdout, &$stderr) {
221
	global $g, $config;
222

    
223
	if (empty($params)) {
224
		return -1;
225
	}
226

    
227
	$user_agent = $g['product_name'] . '/' . $g['product_version'];
228
	if (!isset($config['system']['do_not_send_host_uuid'])) {
229
		$user_agent .= ' : ' . get_single_sysctl('kern.hostuuid');
230
	}
231

    
232
	$descriptorspec = array(
233
		1 => array("pipe", "w"), /* stdout */
234
		2 => array("pipe", "w")	 /* stderr */
235
	);
236

    
237
	conf_mount_rw();
238

    
239
	pkg_debug("pkg_exec(): {$params}\n");
240
	$process = proc_open("/usr/sbin/pkg {$params}", $descriptorspec, $pipes,
241
	    '/', pkg_env());
242

    
243
	if (!is_resource($process)) {
244
		conf_mount_ro();
245
		return -1;
246
	}
247

    
248
	$stdout = '';
249
	while (($l = fgets($pipes[1])) !== FALSE) {
250
		$stdout .= $l;
251
	}
252
	fclose($pipes[1]);
253

    
254
	$stderr = '';
255
	while (($l = fgets($pipes[2])) !== FALSE) {
256
		$stderr .= $l;
257
	}
258
	fclose($pipes[2]);
259

    
260
	conf_mount_ro();
261

    
262
	return proc_close($process);
263
}
264

    
265
/* Compare 2 pkg versions and return:
266
 * '=' - versions are the same
267
 * '>' - $v1 > $v2
268
 * '<' - $v1 < $v2
269
 * '?' - Error
270
 */
271
function pkg_version_compare($v1, $v2) {
272
	if (empty($v1) || empty($v2)) {
273
		return '?';
274
	}
275

    
276
	$rc = pkg_exec("version -t '{$v1}' '{$v2}'", $stdout, $stderr);
277

    
278
	if ($rc != 0) {
279
		return '?';
280
	}
281

    
282
	return str_replace("\n", "", $stdout);
283
}
284

    
285
/* Check if package is installed */
286
function is_pkg_installed($pkg_name) {
287
	global $g;
288

    
289
	if (empty($pkg_name)) {
290
		return false;
291
	}
292

    
293
	return pkg_call("info -e " . $pkg_name, true);
294
}
295

    
296
/* Install package, $pkg_name should not contain prefix */
297
function pkg_install($pkg_name, $force = false) {
298
	global $g;
299
	$result = false;
300

    
301
	$shortname = $pkg_name;
302
	pkg_remove_prefix($shortname);
303

    
304
	$pkg_force = "";
305
	if ($force) {
306
		$pkg_force = "-f ";
307
	}
308

    
309
	pkg_debug("Installing package {$shortname}\n");
310
	if ($force || !is_pkg_installed($pkg_name)) {
311
		$result = pkg_call("install -y " . $pkg_force . $pkg_name);
312
		/* Cleanup cacke to free disk space */
313
		pkg_call("clean -y");
314
	}
315

    
316
	return $result;
317
}
318

    
319
/* Delete package from FreeBSD, $pkg_name should not contain prefix */
320
function pkg_delete($pkg_name) {
321
	global $g;
322

    
323
	$shortname = $pkg_name;
324
	pkg_remove_prefix($shortname);
325

    
326
	pkg_debug("Removing package {$shortname}\n");
327
	if (is_pkg_installed($pkg_name)) {
328
		pkg_call("delete -y " . $pkg_name);
329
		/* Cleanup unecessary dependencies */
330
		pkg_call("autoremove -y");
331
	}
332
}
333

    
334
/* Check if package is present in config.xml */
335
function is_package_installed($package_name) {
336
	return (get_package_id($package_name) != -1);
337
}
338

    
339
/* Find package array index */
340
function get_package_id($package_name) {
341
	global $config;
342

    
343
	if (!is_array($config['installedpackages']['package'])) {
344
		return -1;
345
	}
346

    
347
	foreach ($config['installedpackages']['package'] as $idx => $pkg) {
348
		if ($pkg['name'] == $package_name ||
349
		    get_package_internal_name($pkg) == $package_name) {
350
			return $idx;
351
		}
352
	}
353

    
354
	return -1;
355
}
356

    
357
/* Return internal_name when it's defined, otherwise, returns name */
358
function get_package_internal_name($package_data) {
359
	if (isset($package_data['internal_name']) && ($package_data['internal_name'] != "")) {
360
		/* e.g. name is Ipguard-dev, internal name is ipguard */
361
		return $package_data['internal_name'];
362
	} else {
363
		return $package_data['name'];
364
	}
365
}
366

    
367
// Get information about packages.
368
function get_pkg_info($pkgs = 'all', $info = 'all', $only_local = false) {
369
	global $g, $input_errors;
370

    
371
	$out = '';
372
	$err = '';
373

    
374
	unset($pkg_filter);
375
	if (is_array($pkgs)) {
376
		$pkg_filter = $pkgs;
377
		$pkgs = 'all';
378
	}
379

    
380
	if ($pkgs == 'all') {
381
		$pkgs = $g['pkg_prefix'];
382
	}
383

    
384
	$extra_param = "";
385
	if ($only_local) {
386
		$extra_param = "-U ";
387
	}
388

    
389
	$rc = pkg_exec("search {$extra_param}--raw-format json-compact " . $pkgs, $out, $err);
390

    
391
	if ($rc != 0) {
392
		update_status("\n" . gettext(
393
		    "ERROR: Error trying to get packages list. Aborting...")
394
		    . "\n");
395
		update_status($err);
396
		$input_errors[] =  gettext("ERROR: Error trying to get packages list. Aborting...") . "\n";
397
		$input_errors[] =  $err;
398
		return array();
399
	}
400

    
401
	$result = array();
402
	$pkgs_info = explode("\n", $out);
403
	foreach ($pkgs_info as $pkg_info_json) {
404
		$pkg_info = json_decode($pkg_info_json, true);
405
		if (!isset($pkg_info['name'])) {
406
			continue;
407
		}
408

    
409
		if (isset($pkg_filter) && !in_array($pkg_info['name'], $pkg_filter)) {
410
			continue;
411
		}
412

    
413
		$pkg_info['shortname'] = $pkg_info['name'];
414
		pkg_remove_prefix($pkg_info['shortname']);
415

    
416
		/* XXX: Add it to globals.inc? */
417
		$pkg_info['changeloglink'] =
418
		    "https://github.com/pfsense/FreeBSD-ports/commits/devel/" .
419
		    $pkg_info['categories'][0] . '/' . $pkg_info['name'];
420

    
421
		if (is_pkg_installed($pkg_info['name'])) {
422
			$pkg_info['installed'] = true;
423

    
424
			$rc = pkg_exec("query %v {$pkg_info['name']}", $out, $err);
425

    
426
			if ($rc != 0) {
427
				update_status("\n" . gettext(
428
				    "ERROR: Error trying to get package version. Aborting...")
429
				    . "\n");
430
				update_status($err);
431
				$input_errors[] =  gettext("ERROR: Error trying to get package version. Aborting...") . "\n";
432
				$input_errors[] =  $err;
433
				return array();
434
			}
435

    
436
			$pkg_info['installed_version'] = str_replace("\n", "", $out);
437
		} else if (is_package_installed($pkg_info['shortname'])) {
438
			$pkg_info['broken'] = true;
439
		}
440

    
441
		$pkg_info['desc'] = preg_replace('/\n+WWW:.*$/', '', $pkg_info['desc']);
442

    
443
		$result[] = $pkg_info;
444
		unset($pkg_info);
445
	}
446

    
447
	/* Sort result alphabetically */
448
	usort($result, function($a, $b) {
449
		return(strcasecmp ($a['name'], $b['name']));
450
	});
451

    
452
	return $result;
453
}
454

    
455
/*
456
 * If binary pkg is installed but post-install tasks were not
457
 * executed yet, fo it now.
458
 * This scenario can happen when a pkg is pre-installed during
459
 * build phase, and at this point, cannot find a running system
460
 * to register itself in config.xml and also execute custom
461
 * install functions
462
 */
463
function register_all_installed_packages() {
464
	global $g, $config, $pkg_interface;
465

    
466
	$pkg_info = get_pkg_info('all', 'all', true);
467

    
468
	foreach ($pkg_info as $pkg) {
469
		if (!isset($pkg['installed'])) {
470
			continue;
471
		}
472

    
473
		pkg_remove_prefix($pkg['name']);
474

    
475
		if (is_package_installed($pkg['name'])) {
476
			continue;
477
		}
478

    
479
		update_status(sprintf(gettext(
480
		    "Running last steps of %s installation.") . "\n",
481
		    $pkg['name']));
482
		install_package_xml($pkg['name']);
483
	}
484
}
485

    
486
/*
487
 * resync_all_package_configs() Force packages to setup their configuration and rc.d files.
488
 * This function may also print output to the terminal indicating progress.
489
 */
490
function resync_all_package_configs($show_message = false) {
491
	global $config, $pkg_interface, $g;
492

    
493
	log_error(gettext("Resyncing configuration for all packages."));
494

    
495
	if (!is_array($config['installedpackages']['package'])) {
496
		return;
497
	}
498

    
499
	if ($show_message == true) {
500
		echo "Syncing packages:";
501
	}
502

    
503
	conf_mount_rw();
504

    
505
	foreach ($config['installedpackages']['package'] as $idx => $package) {
506
		if (empty($package['name'])) {
507
			continue;
508
		}
509
		if ($show_message == true) {
510
			echo " " . $package['name'];
511
		}
512
		if (platform_booting() != true) {
513
			stop_service(get_package_internal_name($package));
514
		}
515
		sync_package($package['name']);
516
		update_status(gettext("Syncing packages...") . "\n");
517
	}
518

    
519
	if ($show_message == true) {
520
		echo " done.\n";
521
	}
522

    
523
	@unlink("/conf/needs_package_sync");
524
	conf_mount_ro();
525
}
526

    
527
function uninstall_package($package_name) {
528
	global $config;
529

    
530
	$internal_name = $package_name;
531
	$id = get_package_id($package_name);
532
	if ($id >= 0) {
533
		$internal_name = get_package_internal_name($config['installedpackages']['package'][$id]);
534
		stop_service($internal_name);
535
	}
536
	$pkg_name = $g['pkg_prefix'] . $internal_name;
537

    
538
	if (is_pkg_installed($pkg_name)) {
539
		update_status(gettext("Removing package...") . "\n");
540
		pkg_delete($pkg_name);
541
	} else {
542
		delete_package_xml($package_name);
543
	}
544

    
545
	update_status(gettext("done.") . "\n");
546
}
547

    
548
/* Run <custom_php_resync_config_command> */
549
function sync_package($package_name) {
550
	global $config, $builder_package_install;
551

    
552
	// If this code is being called by pfspkg_installer
553
	// which the builder system uses then return (ignore).
554
	if ($builder_package_install) {
555
		return;
556
	}
557

    
558
	if (empty($config['installedpackages']['package'])) {
559
		return;
560
	}
561

    
562
	if (($pkg_id = get_package_id($package_name)) == -1) {
563
		return; // This package doesn't really exist - exit the function.
564
	}
565

    
566
	if (!is_array($config['installedpackages']['package'][$pkg_id])) {
567
		return;	 // No package belongs to the pkg_id passed to this function.
568
	}
569

    
570
	$package =& $config['installedpackages']['package'][$pkg_id];
571
	if (!file_exists("/usr/local/pkg/" . $package['configurationfile'])) {
572
		log_error(sprintf(gettext("The %s package is missing its configuration file and must be reinstalled."), $package['name']));
573
		delete_package_xml($package['name']);
574
		return;
575
	}
576

    
577
	$pkg_config = parse_xml_config_pkg("/usr/local/pkg/" . $package['configurationfile'], "packagegui");
578
	if (isset($pkg_config['nosync'])) {
579
		return;
580
	}
581

    
582
	/* Bring in package include files */
583
	if (!empty($pkg_config['include_file'])) {
584
		$include_file = $pkg_config['include_file'];
585
		if (file_exists($include_file)) {
586
			require_once($include_file);
587
		} else {
588
			log_error(sprintf(gettext('Reinstalling package %1$s because its include file(%2$s) is missing!'), $package['name'], $include_file));
589
			uninstall_package($package['name']);
590
			if (install_package($package['name']) != 0) {
591
				log_error(sprintf(gettext("Reinstalling package %s failed. Take appropriate measures!!!"), $package['name']));
592
				return;
593
			}
594
			if (file_exists($include_file)) {
595
				require_once($include_file);
596
			} else {
597
				return;
598
			}
599
		}
600
	}
601

    
602
	if (!empty($pkg_config['custom_php_global_functions'])) {
603
		eval($pkg_config['custom_php_global_functions']);
604
	}
605
	if (!empty($pkg_config['custom_php_resync_config_command'])) {
606
		eval($pkg_config['custom_php_resync_config_command']);
607
	}
608
}
609

    
610
/* Read info.xml installed by package and return an array */
611
function read_package_config($package_name) {
612
	global $g;
613

    
614
	$pkg_info_xml = '/usr/local/share/' . $g['pkg_prefix'] . $package_name . '/info.xml';
615

    
616
	if (!file_exists($pkg_info_xml)) {
617
		return false;
618
	}
619

    
620
	$pkg_info = parse_xml_config_pkg($pkg_info_xml, 'pfsensepkgs');
621

    
622
	if (empty($pkg_info)) {
623
		return false;
624
	}
625

    
626
	/* it always returns an array with 1 item */
627
	return $pkg_info['package'][0];
628
}
629

    
630
/* Read package configurationfile and return an array */
631
function read_package_configurationfile($package_name) {
632
	global $config, $g;
633

    
634
	$pkg_config = array();
635
	$id = get_package_id($package_name);
636

    
637
	if ($id < 0 || !isset($config['installedpackages']['package'][$id]['configurationfile'])) {
638
		return $pkg_config;
639
	}
640

    
641
	$pkg_configurationfile = $config['installedpackages']['package'][$id]['configurationfile'];
642

    
643
	if (empty($pkg_configurationfile) || !file_exists('/usr/local/pkg/' . $pkg_configurationfile)) {
644
		return $pkg_config;
645
	}
646

    
647
	$pkg_config = parse_xml_config_pkg('/usr/local/pkg/' . $pkg_configurationfile, "packagegui");
648

    
649
	return $pkg_config;
650
}
651

    
652
function get_after_install_info($package_name) {
653
	$pkg_config = read_package_config($package_name);
654

    
655
	if (isset($pkg_config['after_install_info'])) {
656
		return $pkg_config['after_install_info'];
657
	}
658

    
659
	return '';
660
}
661

    
662
function eval_once($toeval) {
663
	global $evaled;
664
	if (!$evaled) {
665
		$evaled = array();
666
	}
667
	$evalmd5 = md5($toeval);
668
	if (!in_array($evalmd5, $evaled)) {
669
		@eval($toeval);
670
		$evaled[] = $evalmd5;
671
	}
672
	return;
673
}
674

    
675
function install_package_xml($package_name) {
676
	global $g, $config, $pkg_interface;
677

    
678
	if (($pkg_info = read_package_config($package_name)) == false) {
679
		return false;
680
	}
681

    
682
	/* safe side. Write config below will send to ro again. */
683
	conf_mount_rw();
684

    
685
	pkg_debug(gettext("Beginning package installation.") . "\n");
686
	log_error(sprintf(gettext('Beginning package installation for %s .'), $pkg_info['name']));
687

    
688
	/* add package information to config.xml */
689
	$pkgid = get_package_id($pkg_info['name']);
690
	update_status(gettext("Saving updated package information...") . "\n");
691
	if ($pkgid == -1) {
692
		$config['installedpackages']['package'][] = $pkg_info;
693
		$changedesc = sprintf(gettext("Installed %s package."), $pkg_info['name']);
694
		$to_output = gettext("done.") . "\n";
695
	} else {
696
		$config['installedpackages']['package'][$pkgid] = $pkg_info;
697
		$changedesc = sprintf(gettext("Overwrote previous installation of %s."), $pkg_info['name']);
698
		$to_output = gettext("overwrite!") . "\n";
699
	}
700
	unlink_if_exists('/conf/needs_package_sync');
701
	write_config(sprintf(gettext("Intermediate config write during package install for %s."), $pkg_info['name']));
702
	conf_mount_ro();
703
	update_status($to_output);
704

    
705
	if (($pkgid = get_package_id($package_name)) == -1) {
706
		update_status(sprintf(gettext("The %s package is not installed.%sInstallation aborted."), $package_name, "\n\n"));
707

    
708
		uninstall_package($package_name);
709
		write_config($changedesc);
710
		log_error(sprintf(gettext("Failed to install package: %s."), $pkg_info['name']));
711
		update_status(gettext("Failed to install package.") . "\n");
712
		return false;
713
	}
714

    
715
	if (file_exists("/usr/local/pkg/" . $pkg_info['configurationfile'])) {
716
		update_status(gettext("Loading package configuration... "));
717
		$pkg_config = parse_xml_config_pkg("/usr/local/pkg/" . $pkg_info['configurationfile'], "packagegui");
718
		update_status(gettext("done.") . "\n");
719
		update_status(gettext("Configuring package components...") . "\n");
720
		if (!empty($pkg_config['filter_rules_needed'])) {
721
			$config['installedpackages']['package'][$pkgid]['filter_rule_function'] = $pkg_config['filter_rules_needed'];
722
		}
723
		/* modify system files */
724

    
725
		/* if a require exists, include it.  this will
726
		 * show us where an error exists in a package
727
		 * instead of making us blindly guess
728
		 */
729
		$missing_include = false;
730
		if ($pkg_config['include_file'] <> "") {
731
			update_status(gettext("Loading package instructions...") . "\n");
732
			if (file_exists($pkg_config['include_file'])) {
733
				pkg_debug("require_once('{$pkg_config['include_file']}')\n");
734
				require_once($pkg_config['include_file']);
735
			} else {
736
				pkg_debug("Missing include {$pkg_config['include_file']}\n");
737
				$missing_include = true;
738
				update_status(sprintf(gettext("Include %s is missing!"), basename($pkg_config['include_file'])) . "\n");
739

    
740
				uninstall_package($package_name);
741
				write_config($changedesc);
742
				log_error(sprintf(gettext("Failed to install package: %s."), $pkg_info['name']));
743
				update_status(gettext("Failed to install package.") . "\n");
744
				return false;
745
			}
746
		}
747

    
748
		/* custom commands */
749
		update_status(gettext("Custom commands...") . "\n");
750
		if ($missing_include == false) {
751
			if ($pkg_config['custom_php_global_functions'] <> "") {
752
				update_status(gettext("Executing custom_php_global_functions()..."));
753
				eval_once($pkg_config['custom_php_global_functions']);
754
				update_status(gettext("done.") . "\n");
755
			}
756
			if ($pkg_config['custom_php_install_command']) {
757
				update_status(gettext("Executing custom_php_install_command()..."));
758
				eval_once($pkg_config['custom_php_install_command']);
759
				update_status(gettext("done.") . "\n");
760
			}
761
			if ($pkg_config['custom_php_resync_config_command'] <> "") {
762
				update_status(gettext("Executing custom_php_resync_config_command()..."));
763
				eval_once($pkg_config['custom_php_resync_config_command']);
764
				update_status(gettext("done.") . "\n");
765
			}
766
		}
767
		/* sidebar items */
768
		if (is_array($pkg_config['menu'])) {
769
			update_status(gettext("Menu items... "));
770
			foreach ($pkg_config['menu'] as $menu) {
771
				if (is_array($config['installedpackages']['menu'])) {
772
					foreach ($config['installedpackages']['menu'] as $amenu) {
773
						if ($amenu['name'] == $menu['name']) {
774
							continue 2;
775
						}
776
					}
777
				} else {
778
					$config['installedpackages']['menu'] = array();
779
				}
780
				$config['installedpackages']['menu'][] = $menu;
781
			}
782
			update_status(gettext("done.") . "\n");
783
		}
784
		/* services */
785
		if (is_array($pkg_config['service'])) {
786
			update_status(gettext("Services... "));
787
			foreach ($pkg_config['service'] as $service) {
788
				if (is_array($config['installedpackages']['service'])) {
789
					foreach ($config['installedpackages']['service'] as $aservice) {
790
						if ($aservice['name'] == $service['name']) {
791
							continue 2;
792
						}
793
					}
794
				} else {
795
					$config['installedpackages']['service'] = array();
796
				}
797
				$config['installedpackages']['service'][] = $service;
798
			}
799
			update_status(gettext("done.") . "\n");
800
		}
801
	} else {
802
		pkg_debug("Unable to find config file\n");
803
		update_status(gettext("Loading package configuration... failed!") . "\n\n" . gettext("Installation aborted."));
804
		pkg_debug(gettext("Unable to load package configuration. Installation aborted.") ."\n");
805

    
806
		uninstall_package($package_name);
807
		write_config($changedesc);
808
		log_error(sprintf(gettext("Failed to install package: %s."), $pkg_info['name']));
809
		update_status(gettext("Failed to install package.") . "\n");
810
		return false;
811
	}
812

    
813
	/* set up package logging streams */
814
	if ($pkg_info['logging']) {
815
		system_syslogd_start();
816
	}
817

    
818
	update_status(gettext("Writing configuration... "));
819
	write_config($changedesc);
820
	log_error(sprintf(gettext("Successfully installed package: %s."), $pkg_info['name']));
821
	update_status(gettext("done.") . "\n");
822
	if ($pkg_info['after_install_info']) {
823
		update_status($pkg_info['after_install_info']);
824
	}
825

    
826
	return true;
827
}
828

    
829
function delete_package_xml($package_name, $when = "post-deinstall") {
830
	global $g, $config, $pkg_interface;
831

    
832
	conf_mount_rw();
833

    
834
	$pkgid = get_package_id($package_name);
835
	if ($pkgid == -1) {
836
		update_status(sprintf(gettext("The %s package is not installed.%sDeletion aborted."), $package_name, "\n\n"));
837
		ob_flush();
838
		sleep(1);
839
		conf_mount_ro();
840
		return;
841
	}
842
	pkg_debug(sprintf(gettext("Removing %s package... "), $package_name));
843
	update_status(sprintf(gettext("Removing %s components..."), $package_name) . "\n");
844
	/* parse package configuration */
845
	$packages = &$config['installedpackages']['package'];
846
	$menus =& $config['installedpackages']['menu'];
847
	$services = &$config['installedpackages']['service'];
848
	$pkg_info =& $packages[$pkgid];
849
	if (file_exists("/usr/local/pkg/" . $pkg_info['configurationfile'])) {
850
		$pkg_config = parse_xml_config_pkg("/usr/local/pkg/" . $packages[$pkgid]['configurationfile'], "packagegui");
851
		/* remove menu items */
852
		if (is_array($pkg_config['menu'])) {
853
			update_status(gettext("Menu items... "));
854
			if (is_array($pkg_config['menu']) && is_array($menus)) {
855
				foreach ($pkg_config['menu'] as $menu) {
856
					foreach ($menus as $key => $instmenu) {
857
						if ($instmenu['name'] == $menu['name']) {
858
							unset($menus[$key]);
859
							break;
860
						}
861
					}
862
				}
863
			}
864
			update_status(gettext("done.") . "\n");
865
		}
866
		/* remove services */
867
		if (is_array($pkg_config['service'])) {
868
			update_status(gettext("Services... "));
869
			if (is_array($pkg_config['service']) && is_array($services)) {
870
				foreach ($pkg_config['service'] as $service) {
871
					foreach ($services as $key => $instservice) {
872
						if ($instservice['name'] == $service['name']) {
873
							if (platform_booting() != true) {
874
								stop_service($service['name']);
875
							}
876
							if ($service['rcfile']) {
877
								$prefix = RCFILEPREFIX;
878
								if (!empty($service['prefix'])) {
879
									$prefix = $service['prefix'];
880
								}
881
								if (file_exists("{$prefix}{$service['rcfile']}")) {
882
									@unlink("{$prefix}{$service['rcfile']}");
883
								}
884
							}
885
							unset($services[$key]);
886
						}
887
					}
888
				}
889
			}
890
			update_status(gettext("done.") . "\n");
891
		}
892
		/*
893
		 * XXX: Otherwise inclusion of config.inc again invalidates actions taken.
894
		 *	Same is done during installation.
895
		 */
896
		write_config(sprintf(gettext("Intermediate config write during package removal for %s."), $package_name));
897

    
898
		/*
899
		 * If a require exists, include it.	 this will
900
		 * show us where an error exists in a package
901
		 * instead of making us blindly guess
902
		 */
903
		$missing_include = false;
904
		if ($pkg_config['include_file'] <> "") {
905
			update_status(gettext("Loading package instructions...") . "\n");
906
			if (file_exists($pkg_config['include_file'])) {
907
				pkg_debug("require_once(\"{$pkg_config['include_file']}\")\n");
908
				require_once($pkg_config['include_file']);
909
			} else {
910
				pkg_debug("Missing include {$pkg_config['include_file']}\n");
911
				$missing_include = true;
912
				update_status(sprintf(gettext("Include file %s could not be found for inclusion."), basename($pkg_config['include_file'])) . "\n");
913
			}
914
		}
915
		/* ermal
916
		 * NOTE: It is not possible to handle parse errors on eval.
917
		 * So we prevent it from being run at all to not interrupt all the other code.
918
		 */
919
		if ($when == "deinstall" && $missing_include == false) {
920
			/* evaluate this package's global functions and pre deinstall commands */
921
			if ($pkg_config['custom_php_global_functions'] <> "") {
922
				eval_once($pkg_config['custom_php_global_functions']);
923
			}
924
			if ($pkg_config['custom_php_pre_deinstall_command'] <> "") {
925
				eval_once($pkg_config['custom_php_pre_deinstall_command']);
926
			}
927
		}
928
		/* deinstall commands */
929
		if ($when == "post-deinstall" && $pkg_config['custom_php_deinstall_command'] <> "") {
930
			update_status(gettext("Deinstall commands... "));
931
			if ($missing_include == false) {
932
				eval_once($pkg_config['custom_php_deinstall_command']);
933
				update_status(gettext("done.") . "\n");
934
			} else {
935
				update_status("\n". gettext("Not executing custom deinstall hook because an include is missing.") . "\n");
936
			}
937
		}
938
	}
939
	/* syslog */
940
	$need_syslog_restart = false;
941
	if (is_array($pkg_info['logging']) && $pkg_info['logging']['logfilename'] <> "") {
942
		update_status(gettext("Syslog entries... "));
943
		@unlink("{$g['varlog_path']}/{$pkg_info['logging']['logfilename']}");
944
		update_status("done.\n");
945
		$need_syslog_restart = true;
946
	}
947

    
948
	if ($when == "post-deinstall") {
949
		/* remove config.xml entries */
950
		update_status(gettext("Configuration... "));
951
		unset($config['installedpackages']['package'][$pkgid]);
952
		update_status(gettext("done.") . "\n");
953
		write_config(sprintf(gettext("Removed %s package."), $package_name));
954
	}
955

    
956
	/* remove package entry from /etc/syslog.conf if needed */
957
	/* this must be done after removing the entries from config.xml */
958
	if ($need_syslog_restart) {
959
		system_syslogd_start();
960
	}
961

    
962
	conf_mount_ro();
963
}
964

    
965
/*
966
 * Used during upgrade process or retore backup process, verify all
967
 * packages installed in config.xml and install pkg accordingly
968
 */
969
function package_reinstall_all() {
970
	global $g, $config, $pkg_interface;
971

    
972
	if (!isset($config['installedpackages']['package']) ||
973
	    !is_array($config['installedpackages']['package'])) {
974
		return true;
975
	}
976

    
977
	$upgrade = (file_exists('/conf/needs_package_sync') && platform_booting());
978

    
979
	/* During boot after upgrade, wait for internet connection */
980
	if ($upgrade) {
981
		update_status(gettext("Waiting for internet connection to update pkg metadata and finish package reinstallation"));
982
		while (true) {
983
			if (pkg_update(true)) {
984
				break;
985
			}
986
			update_status('.');
987
			sleep(1);
988
		}
989
		update_status("\n");
990
	}
991

    
992
	$pkg_info = get_pkg_info();
993

    
994
	foreach ($config['installedpackages']['package'] as $package) {
995
		$found = false;
996
		$internal_name = get_package_internal_name($package);
997
		foreach ($pkg_info as $pkg) {
998
			pkg_remove_prefix($pkg['name']);
999
			if ($pkg['name'] == $internal_name) {
1000
				$found = true;
1001
				break;
1002
			}
1003
		}
1004

    
1005
		if (!$found) {
1006
			if (!function_exists("file_notice")) {
1007
				require_once("notices.inc");
1008
			}
1009

    
1010
			file_notice(gettext("Package reinstall"),
1011
			    sprintf(gettext("Package %s does not exist in current %s version and it has been removed."), $package['name'], $g['product_name']));
1012
			uninstall_package($package['name']);
1013
		}
1014
	}
1015

    
1016
	/* Obsoleted packages were removed, lets reinstall all remaining */
1017
	foreach ($config['installedpackages']['package'] as $package) {
1018
		$internal_name = get_package_internal_name($package);
1019
		pkg_install($g['pkg_prefix'] . $internal_name, true);
1020
	}
1021

    
1022
	return true;
1023
}
1024

    
1025
function stop_packages() {
1026
	require_once("config.inc");
1027
	require_once("functions.inc");
1028
	require_once("filter.inc");
1029
	require_once("shaper.inc");
1030
	require_once("captiveportal.inc");
1031
	require_once("pkg-utils.inc");
1032
	require_once("pfsense-utils.inc");
1033
	require_once("service-utils.inc");
1034

    
1035
	global $config, $g;
1036

    
1037
	log_error(gettext("Stopping all packages."));
1038

    
1039
	$rcfiles = glob(RCFILEPREFIX . "*.sh");
1040
	if (!$rcfiles) {
1041
		$rcfiles = array();
1042
	} else {
1043
		$rcfiles = array_flip($rcfiles);
1044
		if (!$rcfiles) {
1045
			$rcfiles = array();
1046
		}
1047
	}
1048

    
1049
	if (is_array($config['installedpackages']['package'])) {
1050
		foreach ($config['installedpackages']['package'] as $package) {
1051
			echo " Stopping package {$package['name']}...";
1052
			$internal_name = get_package_internal_name($package);
1053
			stop_service($internal_name);
1054
			unset($rcfiles[RCFILEPREFIX . strtolower($internal_name) . ".sh"]);
1055
			echo "done.\n";
1056
		}
1057
	}
1058

    
1059
	foreach ($rcfiles as $rcfile => $number) {
1060
		$shell = @popen("/bin/sh", "w");
1061
		if ($shell) {
1062
			echo " Stopping {$rcfile}...";
1063
			if (!@fwrite($shell, "{$rcfile} stop >>/tmp/bootup_messages 2>&1")) {
1064
				if ($shell) {
1065
					pclose($shell);
1066
				}
1067
				$shell = @popen("/bin/sh", "w");
1068
			}
1069
			echo "done.\n";
1070
			pclose($shell);
1071
		}
1072
	}
1073
}
1074

    
1075
/* Identify which meta package is installed */
1076
function get_meta_pkg_name() {
1077
	global $g;
1078

    
1079
	/* XXX: Use pkg annotation */
1080
	if (is_pkg_installed($g['product_name'])) {
1081
		return $g['product_name'];
1082
	} else if (is_pkg_installed($g['product_name'] . '-vmware')) {
1083
		return $g['product_name'] . '-vmware';
1084
	}
1085
	return false;
1086
}
1087

    
1088
/* Identify which base package is installed */
1089
function get_base_pkg_name() {
1090
	global $g;
1091

    
1092
	/* XXX: Use pkg annotation */
1093
	if (is_pkg_installed($g['product_name'] . '-base-' . $g['platform'])) {
1094
		return $g['product_name'];
1095
		return $g['product_name'] . '-base-' . $g['platform'];
1096
	} else if (is_pkg_installed($g['product_name'] . '-base')) {
1097
		return $g['product_name'] . '-base';
1098
	}
1099
	return false;
1100
}
1101

    
1102
/* Verify if system needs upgrade (meta package or base) */
1103
function get_system_pkg_version() {
1104
	global $g;
1105

    
1106
	$base_pkg = get_base_pkg_name();
1107
	$meta_pkg = get_meta_pkg_name();
1108

    
1109
	if (!$base_pkg || !$meta_pkg) {
1110
		return false;
1111
	}
1112

    
1113
	$info = get_pkg_info($base_pkg);
1114
	$pkg_name = $base_pkg;
1115

    
1116
	$pkg_info = array();
1117
	foreach ($info as $item) {
1118
		if ($item['name'] == $base_pkg) {
1119
			$pkg_info = $item;
1120
		}
1121
	}
1122

    
1123
	if (empty($pkg_info) ||
1124
	    $pkg_info['version'] == $pkg_info['installed_version']) {
1125
		$info = get_pkg_info($meta_pkg);
1126
		$pkg_name = $meta_pkg;
1127

    
1128
		foreach ($info as $item) {
1129
			if ($item['name'] == $meta_pkg) {
1130
				$pkg_info = $item;
1131
			}
1132
		}
1133
	}
1134

    
1135
	if (empty($pkg_info)) {
1136
		return false;
1137
	}
1138

    
1139
	return array(
1140
	    'pkg_name'          => $pkg_name,
1141
	    'version'           => $pkg_info['version'],
1142
	    'installed_version' => $pkg_info['installed_version']
1143
	);
1144
}
1145

    
1146
/* Switch between stable and devel repos */
1147
function pkg_switch_repo($devel = false) {
1148
	global $g;
1149

    
1150
	$repo_stable = $g['product_name'] . '-repo';
1151
	$repo_devel = $g['product_name'] . '-repo-devel';
1152

    
1153
	if ($devel) {
1154
		$repo_target = $repo_devel;
1155
	} else {
1156
		$repo_target = $repo_stable;
1157
	}
1158

    
1159
	if (is_pkg_installed($repo_target)) {
1160
		/* It's already installed */
1161
		return true;
1162
	}
1163

    
1164
	/*
1165
	 * Since both install files in the same place, just
1166
	 * call pkg_install for target and current one will
1167
	 * be replaced
1168
	 */
1169
	return pkg_install($repo_target, true);
1170
}
1171

    
1172
?>
(40-40/65)