Project

General

Profile

Download (33.3 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($extra_env = array()) {
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 (!empty($config['system']['proxyurl'])) {
113
		$http_proxy = $config['system']['proxyurl'];
114
		if (!empty($config['system']['proxyport'])) {
115
			$http_proxy .= ':' . $config['system']['proxyport'];
116
		}
117
		$pkg_env_vars['HTTP_PROXY'] = $http_proxy;
118
	}
119

    
120
	if ($g['platform'] == "nanobsd" ||
121
	    isset($config['system']['use_mfs_tmpvar'])) {
122
		$pkg_env_vars['PKG_DBDIR'] = '/root/var/db/pkg';
123
		$pkg_env_vars['PKG_CACHEDIR'] = '/root/var/cache/pkg';
124
	}
125

    
126
	foreach ($extra_env as $key => $value) {
127
		$pkg_env_vars[$key] = $value;
128
	}
129

    
130
	return $pkg_env_vars;
131
}
132

    
133
/* Execute a pkg call */
134
function pkg_call($params, $mute = false, $extra_env = array()) {
135
	global $g, $config;
136

    
137
	if (empty($params)) {
138
		return false;
139
	}
140

    
141
	$user_agent = $g['product_name'] . '/' . $g['product_version'];
142
	if (!isset($config['system']['do_not_send_host_uuid'])) {
143
		$user_agent .= ' : ' . get_single_sysctl('kern.hostuuid');
144
	}
145

    
146
	$descriptorspec = array(
147
		1 => array("pipe", "w"), /* stdout */
148
		2 => array("pipe", "w")	 /* stderr */
149
	);
150

    
151
	conf_mount_rw();
152

    
153
	pkg_debug("pkg_call(): {$params}\n");
154
	$process = proc_open("/usr/sbin/pkg {$params}", $descriptorspec, $pipes,
155
	    '/', pkg_env($extra_env));
156

    
157
	if (!is_resource($process)) {
158
		conf_mount_ro();
159
		return false;
160
	}
161

    
162
	stream_set_blocking($pipes[1], 0);
163
	stream_set_blocking($pipes[2], 0);
164

    
165
	/* XXX: should be a tunnable? */
166
	$timeout = 300; // seconds
167
	$error_log = '';
168

    
169
	do {
170
		$write = array();
171
		$read = array($pipes[1], $pipes[2]);
172
		$except = array();
173

    
174
		$stream = stream_select($read, $write, $except, null, $timeout);
175
		if ($stream !== FALSE && $stream > 0) {
176
			foreach ($read as $pipe) {
177
				$content = stream_get_contents($pipe);
178
				if ($content == '') {
179
					continue;
180
				}
181
				if ($pipe === $pipes[1]) {
182
					if (!$mute) {
183
						update_status($content);
184
					}
185
					flush();
186
				} else if ($pipe === $pipes[2]) {
187
					$error_log .= $content;
188
				}
189
			}
190
		}
191

    
192
		$status = proc_get_status($process);
193
	} while ($status['running']);
194

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

    
199
	conf_mount_ro();
200

    
201
	$rc = $status['exitcode'];
202

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

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

    
215
	return false;
216
}
217

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

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

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

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

    
236
	conf_mount_rw();
237

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

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

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

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

    
259
	conf_mount_ro();
260

    
261
	return proc_close($process);
262
}
263

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

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

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

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

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

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

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

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

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

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

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

    
315
	return $result;
316
}
317

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

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

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

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

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

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

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

    
353
	return -1;
354
}
355

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
451
	return $result;
452
}
453

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

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

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

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

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

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

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

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

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

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

    
502
	conf_mount_rw();
503

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
648
	return $pkg_config;
649
}
650

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

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

    
658
	return '';
659
}
660

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
825
	return true;
826
}
827

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

    
831
	conf_mount_rw();
832

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

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

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

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

    
961
	conf_mount_ro();
962
}
963

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

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

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

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

    
991
	$pkg_info = get_pkg_info();
992

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

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

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

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

    
1021
	return true;
1022
}
1023

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

    
1034
	global $config, $g;
1035

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1145
/* List available repos */
1146
function pkg_list_repos() {
1147
	global $g;
1148

    
1149
	$path = "/usr/local/share/{$g['product_name']}/pkg/repos";
1150

    
1151
	$default_descr = @file_get_contents($path . "/{$g['product_name']}-repo.descr");
1152

    
1153
	$default = array(
1154
	    'name' => 'Default',
1155
	    'path' => $path . "/{$g['product_name']}-repo.conf",
1156
	    'descr' => $default_descr
1157
	);
1158

    
1159
	$result = array($default);
1160

    
1161
	$conf_files = glob("{$path}/{$g['product_name']}-repo-*.conf");
1162
	foreach ($conf_files as $conf_file) {
1163
		$descr_file = preg_replace('/.conf$/', '.descr', $conf_file);
1164
		if (file_exists($descr_file)) {
1165
			$descr_content = file($descr_file);
1166
			$descr = chop($descr_content[0]);
1167
		} else {
1168
			$descr = 'Unknown';
1169
		}
1170
		if (!preg_match('/-repo-(.*).conf/', $conf_file, $matches)) {
1171
			continue;
1172
		}
1173
		$entry = array(
1174
		    'name' => ucfirst(strtolower($matches[1])),
1175
		    'path' => $conf_file,
1176
		    'descr' => $descr
1177
		);
1178
		$result[] = $entry;
1179
	}
1180

    
1181
	return $result;
1182
}
1183

    
1184
/* Switch between stable and devel repos */
1185
function pkg_switch_repo($path) {
1186
	global $g;
1187

    
1188
	safe_mkdir("/usr/local/etc/pkg/repos");
1189
	@unlink("/usr/local/etc/pkg/repos/{$g['product_name']}.conf");
1190
	@symlink($path, "/usr/local/etc/pkg/repos/{$g['product_name']}.conf");
1191

    
1192
	return pkg_update(true);
1193
}
1194

    
1195
?>
(40-40/65)