Project

General

Profile

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

    
55
require_once("globals.inc");
56
require_once("service-utils.inc");
57

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

    
64
require_once("pfsense-utils.inc");
65

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

    
71
		if (!$debug) {
72
			return;
73
		}
74

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

    
84
/* Validate if pkg name is valid */
85
function pkg_valid_name($pkgname) {
86
	global $g;
87

    
88
	$pattern = "/^{$g['pkg_prefix']}[a-zA-Z0-9\.\-_]+$/";
89
	return preg_match($pattern, $pkgname);
90
}
91

    
92
/* Remove pkg_prefix from package name if it's present */
93
function pkg_remove_prefix(&$pkg_name) {
94
	global $g;
95

    
96
	if (substr($pkg_name, 0, strlen($g['pkg_prefix'])) == $g['pkg_prefix']) {
97
		$pkg_name = substr($pkg_name, strlen($g['pkg_prefix']));
98
	}
99
}
100

    
101
/* Execute pkg update when it's necessary */
102
function pkg_update($force = false) {
103
	global $g;
104

    
105
	return pkg_call("update" . ($force ? " -f" : ""));
106
}
107

    
108
/* return an array with necessary environment vars for pkg */
109
function pkg_env($extra_env = array()) {
110
	global $config, $g;
111

    
112
	$pkg_env_vars = array(
113
		"LANG" => "C",
114
		"HTTP_USER_AGENT" => $user_agent,
115
		"ASSUME_ALWAYS_YES" => "true",
116
		"FETCH_TIMEOUT" => 5,
117
		"FETCH_RETRY" => 2
118
	);
119

    
120
	if (!empty($config['system']['proxyurl'])) {
121
		$http_proxy = $config['system']['proxyurl'];
122
		if (!empty($config['system']['proxyport'])) {
123
			$http_proxy .= ':' . $config['system']['proxyport'];
124
		}
125
		$pkg_env_vars['HTTP_PROXY'] = $http_proxy;
126
	}
127

    
128
	if ($g['platform'] == "nanobsd" ||
129
	    isset($config['system']['use_mfs_tmpvar'])) {
130
		$pkg_env_vars['PKG_DBDIR'] = '/root/var/db/pkg';
131
		$pkg_env_vars['PKG_CACHEDIR'] = '/root/var/cache/pkg';
132
	}
133

    
134
	foreach ($extra_env as $key => $value) {
135
		$pkg_env_vars[$key] = $value;
136
	}
137

    
138
	return $pkg_env_vars;
139
}
140

    
141
/* Execute a pkg call */
142
function pkg_call($params, $mute = false, $extra_env = array()) {
143
	global $g, $config;
144

    
145
	if (empty($params)) {
146
		return false;
147
	}
148

    
149
	$user_agent = $g['product_name'] . '/' . $g['product_version'];
150
	if (!isset($config['system']['do_not_send_host_uuid'])) {
151
		$user_agent .= ' : ' . get_single_sysctl('kern.hostuuid');
152
	}
153

    
154
	$descriptorspec = array(
155
		1 => array("pipe", "w"), /* stdout */
156
		2 => array("pipe", "w")	 /* stderr */
157
	);
158

    
159
	conf_mount_rw();
160

    
161
	pkg_debug("pkg_call(): {$params}\n");
162
	$process = proc_open("/usr/sbin/pkg {$params}", $descriptorspec, $pipes,
163
	    '/', pkg_env($extra_env));
164

    
165
	if (!is_resource($process)) {
166
		conf_mount_ro();
167
		return false;
168
	}
169

    
170
	stream_set_blocking($pipes[1], 0);
171
	stream_set_blocking($pipes[2], 0);
172

    
173
	/* XXX: should be a tunnable? */
174
	$timeout = 60; // seconds
175
	$error_log = '';
176

    
177
	do {
178
		$write = array();
179
		$read = array($pipes[1], $pipes[2]);
180
		$except = array();
181

    
182
		$stream = stream_select($read, $write, $except, $timeout);
183
		if ($stream !== FALSE && $stream > 0) {
184
			foreach ($read as $pipe) {
185
				$content = stream_get_contents($pipe);
186
				if ($content == '') {
187
					continue;
188
				}
189
				if ($pipe === $pipes[1]) {
190
					if (!$mute) {
191
						update_status($content);
192
					}
193
					flush();
194
				} else if ($pipe === $pipes[2]) {
195
					$error_log .= $content;
196
				}
197
			}
198
		}
199

    
200
		$status = proc_get_status($process);
201
	} while ($status['running']);
202

    
203
	fclose($pipes[1]);
204
	fclose($pipes[2]);
205
	proc_close($process);
206

    
207
	conf_mount_ro();
208

    
209
	$rc = $status['exitcode'];
210

    
211
	pkg_debug("pkg_call(): rc = {$rc}\n");
212
	if ($rc == 0) {
213
		return true;
214
	}
215

    
216
	pkg_debug("pkg_call(): error_log\n{$error_log}\n");
217
	if (!$mute) {
218
		update_status("\n\n" .  sprintf(gettext(
219
		    "ERROR!!! An error occurred on pkg execution (rc = %d) with parameters '%s':"),
220
		    $rc, $params) . "\n" . $error_log . "\n");
221
	}
222

    
223
	return false;
224
}
225

    
226
/* Execute pkg with $params, fill stdout and stderr and return pkg rc */
227
function pkg_exec($params, &$stdout, &$stderr, $extra_env = array()) {
228
	global $g, $config;
229

    
230
	if (empty($params)) {
231
		return -1;
232
	}
233

    
234
	$user_agent = $g['product_name'] . '/' . $g['product_version'];
235
	if (!isset($config['system']['do_not_send_host_uuid'])) {
236
		$user_agent .= ' : ' . get_single_sysctl('kern.hostuuid');
237
	}
238

    
239
	$descriptorspec = array(
240
		1 => array("pipe", "w"), /* stdout */
241
		2 => array("pipe", "w")	 /* stderr */
242
	);
243

    
244
	conf_mount_rw();
245

    
246
	pkg_debug("pkg_exec(): {$params}\n");
247
	$process = proc_open("/usr/sbin/pkg {$params}", $descriptorspec, $pipes,
248
	    '/', pkg_env($extra_env));
249

    
250
	if (!is_resource($process)) {
251
		conf_mount_ro();
252
		return -1;
253
	}
254

    
255
	$stdout = '';
256
	while (($l = fgets($pipes[1])) !== FALSE) {
257
		$stdout .= $l;
258
	}
259
	fclose($pipes[1]);
260

    
261
	$stderr = '';
262
	while (($l = fgets($pipes[2])) !== FALSE) {
263
		$stderr .= $l;
264
	}
265
	fclose($pipes[2]);
266

    
267
	conf_mount_ro();
268

    
269
	return proc_close($process);
270
}
271

    
272
/* Compare 2 pkg versions and return:
273
 * '=' - versions are the same
274
 * '>' - $v1 > $v2
275
 * '<' - $v1 < $v2
276
 * '?' - Error
277
 */
278
function pkg_version_compare($v1, $v2) {
279
	if (empty($v1) || empty($v2)) {
280
		return '?';
281
	}
282

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

    
285
	if ($rc != 0) {
286
		return '?';
287
	}
288

    
289
	return str_replace("\n", "", $stdout);
290
}
291

    
292
/* Check if package is installed */
293
function is_pkg_installed($pkg_name) {
294
	global $g;
295

    
296
	if (empty($pkg_name)) {
297
		return false;
298
	}
299

    
300
	return pkg_call("info -e " . $pkg_name, true);
301
}
302

    
303
/* Install package, $pkg_name should not contain prefix */
304
function pkg_install($pkg_name, $force = false) {
305
	global $g;
306
	$result = false;
307

    
308
	$shortname = $pkg_name;
309
	pkg_remove_prefix($shortname);
310

    
311
	$pkg_force = "";
312
	if ($force) {
313
		$pkg_force = "-f ";
314
	}
315

    
316
	pkg_debug("Installing package {$shortname}\n");
317
	if ($force || !is_pkg_installed($pkg_name)) {
318
		$result = pkg_call("install -y " . $pkg_force . $pkg_name);
319
		/* Cleanup cacke to free disk space */
320
		pkg_call("clean -y");
321
	}
322

    
323
	return $result;
324
}
325

    
326
/* Delete package from FreeBSD, $pkg_name should not contain prefix */
327
function pkg_delete($pkg_name) {
328
	global $g;
329

    
330
	$shortname = $pkg_name;
331
	pkg_remove_prefix($shortname);
332

    
333
	pkg_debug("Removing package {$shortname}\n");
334
	if (is_pkg_installed($pkg_name)) {
335
		pkg_call("delete -y " . $pkg_name);
336
		/* Cleanup unecessary dependencies */
337
		pkg_call("autoremove -y");
338
	}
339
}
340

    
341
/* Check if package is present in config.xml */
342
function is_package_installed($package_name) {
343
	return (get_package_id($package_name) != -1);
344
}
345

    
346
/* Find package array index */
347
function get_package_id($package_name) {
348
	global $config;
349

    
350
	if (!is_array($config['installedpackages']['package'])) {
351
		return -1;
352
	}
353

    
354
	foreach ($config['installedpackages']['package'] as $idx => $pkg) {
355
		if ($pkg['name'] == $package_name ||
356
		    get_package_internal_name($pkg) == $package_name) {
357
			return $idx;
358
		}
359
	}
360

    
361
	return -1;
362
}
363

    
364
/* Return internal_name when it's defined, otherwise, returns name */
365
function get_package_internal_name($package_data) {
366
	if (isset($package_data['internal_name']) && ($package_data['internal_name'] != "")) {
367
		/* e.g. name is Ipguard-dev, internal name is ipguard */
368
		return $package_data['internal_name'];
369
	} else {
370
		return $package_data['name'];
371
	}
372
}
373

    
374
// Get information about packages.
375
function get_pkg_info($pkgs = 'all', $remote_repo_usage_disabled = false, $installed_pkgs_only = false) {
376

    
377
	global $g, $input_errors;
378

    
379
	$out = $err = $extra_param = '';
380
	$rc = 0;
381

    
382
	unset($pkg_filter);
383

    
384
	if (is_array($pkgs)) {
385
		$pkg_filter = $pkgs;
386
		$pkgs = $g['pkg_prefix'] . '*';
387
	} elseif ($pkgs == 'all') {
388
		$pkgs = $g['pkg_prefix'] . '*';
389
	}
390

    
391
	
392
	if (!function_exists('is_subsystem_dirty')) {
393
		require_once("util.inc");
394
	}
395

    
396
	/* Do not run remote operations if pkg has a lock */
397
	if (is_subsystem_dirty('pkg')) {
398
		$remote_repo_usage_disabled = true;
399
		$lock = false;
400
	} else {
401
		$lock = true;
402
	}
403

    
404
	if ($lock) {
405
		mark_subsystem_dirty('pkg');
406
	}
407

    
408
	if ($remote_repo_usage_disabled) {
409
		$extra_param = "-U ";
410
	}
411

    
412
	if (!$installed_pkgs_only) {
413
		$rc = pkg_exec("search {$extra_param}-R --raw-format json-compact " . $pkgs, $out, $err);
414
	}
415
	if (($installed_pkgs_only || ($rc != 0 && $remote_repo_usage_disabled)) && is_package_installed($pkgs)) {
416
		/* Fall back on pkg info to return locally installed matching pkgs instead, if 
417
		 *
418
		 *   (1) only installed pkgs needed, or
419
		 *       we tried to check the local catalog copy (implying that we would have accepted incomplete/outdated pkg info)
420
		 *       but it didn't have any contents, or for other reasons returned an error. 
421
		 *   AND
422
		 *   (2) at least some pkgs matching <pattern> are installed
423
		 *
424
		 * Following an unsuccessful attempt to access a remote repo catalog, the local copy is wiped clear. Thereafter any
425
		 * "pkg search" will return an error until online+updated again. If the calling code would have accepted local copy info
426
		 * (which could be incomplete/out of date), then it makes sense to fall back on pkg info to at least return the known
427
		 * info about installed pkgs (pkg info should still work), instead of failing and returning no info at all. 
428
		 * For example, this at least enables offline view + management of installed pkgs in GUI/console.
429
		 *
430
		 * We skip this step if no matching pkgs are installed, because then pkg info would return a "no matching pkgs"
431
		 * RC code, even though this wouldn't be considered an "error" (and $out+$err would be correct empty strings if none match).
432
		 * Note that is_package_installed() is a wrapper for pkg info -e <pattern> which is what we need here.
433
		*/
434
		
435
		// ok, 1 or more packages match, so pkg info can be safely called to get the pkg list  
436
		$rc = pkg_exec("info -R --raw-format json-compact " . $pkgs, $out, $err);  
437
	}
438

    
439
	if ($lock) {
440
		clear_subsystem_dirty('pkg');
441
	}
442

    
443
	if ($rc != 0) {
444
		update_status("\n" . gettext(
445
		    "ERROR: Error trying to get packages list. Aborting...")
446
		    . "\n");
447
		update_status($err);
448
		$input_errors[] =  gettext("ERROR: Error trying to get packages list. Aborting...") . "\n";
449
		$input_errors[] =  $err;
450
		return array();
451
	}
452

    
453
	$result = array();
454
	$pkgs_info = explode("\n", $out);
455
	foreach ($pkgs_info as $pkg_info_json) {
456
		$pkg_info = json_decode($pkg_info_json, true);
457
		if (!isset($pkg_info['name'])) {
458
			continue;
459
		}
460

    
461
		if (isset($pkg_filter) && !in_array($pkg_info['name'], $pkg_filter)) {
462
			continue;
463
		}
464

    
465
		$pkg_info['shortname'] = $pkg_info['name'];
466
		pkg_remove_prefix($pkg_info['shortname']);
467

    
468
		/* XXX: Add it to globals.inc? */
469
		$pkg_info['changeloglink'] =
470
		    "https://github.com/pfsense/FreeBSD-ports/commits/devel/" .
471
		    $pkg_info['categories'][0] . '/' . $pkg_info['name'];
472

    
473
		if (is_pkg_installed($pkg_info['name'])) {
474
			$pkg_info['installed'] = true;
475

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

    
478
			if ($rc != 0) {
479
				update_status("\n" . gettext(
480
				    "ERROR: Error trying to get package version. Aborting...")
481
				    . "\n");
482
				update_status($err);
483
				$input_errors[] =  gettext("ERROR: Error trying to get package version. Aborting...") . "\n";
484
				$input_errors[] =  $err;
485
				return array();
486
			}
487

    
488
			$pkg_info['installed_version'] = str_replace("\n", "", $out);
489
		} else if (is_package_installed($pkg_info['shortname'])) {
490
			$pkg_info['broken'] = true;
491
		}
492

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

    
495
		$result[] = $pkg_info;
496
		unset($pkg_info);
497
	}
498

    
499
	/* Sort result alphabetically */
500
	usort($result, function($a, $b) {
501
		return(strcasecmp ($a['name'], $b['name']));
502
	});
503

    
504
	return $result;
505
}
506

    
507
/*
508
 * If binary pkg is installed but post-install tasks were not
509
 * executed yet, do it now.
510
 * This scenario can happen when a pkg is pre-installed during
511
 * build phase, and at this point, cannot find a running system
512
 * to register itself in config.xml and also execute custom
513
 * install functions
514
 */
515
function register_all_installed_packages() {
516
	global $g, $config, $pkg_interface;
517

    
518
	$pkg_info = get_pkg_info('all', true, true);
519

    
520

    
521
	foreach ($pkg_info as $pkg) {
522
		if (!isset($pkg['installed'])) {
523
			continue;
524
		}
525

    
526
		pkg_remove_prefix($pkg['name']);
527

    
528
		if (is_package_installed($pkg['name'])) {
529
			continue;
530
		}
531

    
532
		update_status(sprintf(gettext(
533
		    "Running last steps of %s installation.") . "\n",
534
		    $pkg['name']));
535
		install_package_xml($pkg['name']);
536
	}
537
}
538

    
539
/*
540
 * resync_all_package_configs() Force packages to setup their configuration and rc.d files.
541
 * This function may also print output to the terminal indicating progress.
542
 */
543
function resync_all_package_configs($show_message = false) {
544
	global $config, $pkg_interface, $g;
545

    
546
	log_error(gettext("Resyncing configuration for all packages."));
547

    
548
	if (!is_array($config['installedpackages']['package'])) {
549
		return;
550
	}
551

    
552
	if ($show_message == true) {
553
		echo "Syncing packages:";
554
	}
555

    
556
	conf_mount_rw();
557

    
558
	foreach ($config['installedpackages']['package'] as $idx => $package) {
559
		if (empty($package['name'])) {
560
			continue;
561
		}
562
		if ($show_message == true) {
563
			echo " " . $package['name'];
564
		}
565
		if (platform_booting() != true) {
566
			stop_service(get_package_internal_name($package));
567
		}
568
		sync_package($package['name']);
569
		update_status(gettext("Syncing packages...") . "\n");
570
	}
571

    
572
	if ($show_message == true) {
573
		echo " done.\n";
574
	}
575

    
576
	@unlink("/conf/needs_package_sync");
577
	conf_mount_ro();
578
}
579

    
580
function uninstall_package($package_name) {
581
	global $config;
582

    
583
	$internal_name = $package_name;
584
	$id = get_package_id($package_name);
585
	if ($id >= 0) {
586
		$internal_name = get_package_internal_name($config['installedpackages']['package'][$id]);
587
		stop_service($internal_name);
588
	}
589
	$pkg_name = $g['pkg_prefix'] . $internal_name;
590

    
591
	if (is_pkg_installed($pkg_name)) {
592
		update_status(gettext("Removing package...") . "\n");
593
		pkg_delete($pkg_name);
594
	} else {
595
		delete_package_xml($package_name);
596
	}
597

    
598
	update_status(gettext("done.") . "\n");
599
}
600

    
601
/* Run <custom_php_resync_config_command> */
602
function sync_package($package_name) {
603
	global $config, $builder_package_install;
604

    
605
	// If this code is being called by pfspkg_installer
606
	// which the builder system uses then return (ignore).
607
	if ($builder_package_install) {
608
		return;
609
	}
610

    
611
	if (empty($config['installedpackages']['package'])) {
612
		return;
613
	}
614

    
615
	if (($pkg_id = get_package_id($package_name)) == -1) {
616
		return; // This package doesn't really exist - exit the function.
617
	}
618

    
619
	if (!is_array($config['installedpackages']['package'][$pkg_id])) {
620
		return;	 // No package belongs to the pkg_id passed to this function.
621
	}
622

    
623
	$package =& $config['installedpackages']['package'][$pkg_id];
624
	if (!file_exists("/usr/local/pkg/" . $package['configurationfile'])) {
625
		log_error(sprintf(gettext("The %s package is missing its configuration file and must be reinstalled."), $package['name']));
626
		delete_package_xml($package['name']);
627
		return;
628
	}
629

    
630
	$pkg_config = parse_xml_config_pkg("/usr/local/pkg/" . $package['configurationfile'], "packagegui");
631
	if (isset($pkg_config['nosync'])) {
632
		return;
633
	}
634

    
635
	/* Bring in package include files */
636
	if (!empty($pkg_config['include_file'])) {
637
		$include_file = $pkg_config['include_file'];
638
		if (file_exists($include_file)) {
639
			require_once($include_file);
640
		} else {
641
			log_error(sprintf(gettext('Reinstalling package %1$s because its include file(%2$s) is missing!'), $package['name'], $include_file));
642
			uninstall_package($package['name']);
643
			if (install_package($package['name']) != 0) {
644
				log_error(sprintf(gettext("Reinstalling package %s failed. Take appropriate measures!!!"), $package['name']));
645
				return;
646
			}
647
			if (file_exists($include_file)) {
648
				require_once($include_file);
649
			} else {
650
				return;
651
			}
652
		}
653
	}
654

    
655
	if (!empty($pkg_config['custom_php_global_functions'])) {
656
		eval($pkg_config['custom_php_global_functions']);
657
	}
658
	if (!empty($pkg_config['custom_php_resync_config_command'])) {
659
		eval($pkg_config['custom_php_resync_config_command']);
660
	}
661
}
662

    
663
/* Read info.xml installed by package and return an array */
664
function read_package_config($package_name) {
665
	global $g;
666

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

    
669
	if (!file_exists($pkg_info_xml)) {
670
		return false;
671
	}
672

    
673
	$pkg_info = parse_xml_config_pkg($pkg_info_xml, 'pfsensepkgs');
674

    
675
	if (empty($pkg_info)) {
676
		return false;
677
	}
678

    
679
	/* it always returns an array with 1 item */
680
	return $pkg_info['package'][0];
681
}
682

    
683
/* Read package configurationfile and return an array */
684
function read_package_configurationfile($package_name) {
685
	global $config, $g;
686

    
687
	$pkg_config = array();
688
	$id = get_package_id($package_name);
689

    
690
	if ($id < 0 || !isset($config['installedpackages']['package'][$id]['configurationfile'])) {
691
		return $pkg_config;
692
	}
693

    
694
	$pkg_configurationfile = $config['installedpackages']['package'][$id]['configurationfile'];
695

    
696
	if (empty($pkg_configurationfile) || !file_exists('/usr/local/pkg/' . $pkg_configurationfile)) {
697
		return $pkg_config;
698
	}
699

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

    
702
	return $pkg_config;
703
}
704

    
705
function get_after_install_info($package_name) {
706
	$pkg_config = read_package_config($package_name);
707

    
708
	if (isset($pkg_config['after_install_info'])) {
709
		return $pkg_config['after_install_info'];
710
	}
711

    
712
	return '';
713
}
714

    
715
function eval_once($toeval) {
716
	global $evaled;
717
	if (!$evaled) {
718
		$evaled = array();
719
	}
720
	$evalmd5 = md5($toeval);
721
	if (!in_array($evalmd5, $evaled)) {
722
		@eval($toeval);
723
		$evaled[] = $evalmd5;
724
	}
725
	return;
726
}
727

    
728
function install_package_xml($package_name) {
729
	global $g, $config, $pkg_interface;
730

    
731
	if (($pkg_info = read_package_config($package_name)) == false) {
732
		return false;
733
	}
734

    
735
	/* safe side. Write config below will send to ro again. */
736
	conf_mount_rw();
737

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

    
741
	/* add package information to config.xml */
742
	$pkgid = get_package_id($pkg_info['name']);
743
	update_status(gettext("Saving updated package information...") . "\n");
744
	if ($pkgid == -1) {
745
		$config['installedpackages']['package'][] = $pkg_info;
746
		$changedesc = sprintf(gettext("Installed %s package."), $pkg_info['name']);
747
		$to_output = gettext("done.") . "\n";
748
	} else {
749
		$config['installedpackages']['package'][$pkgid] = $pkg_info;
750
		$changedesc = sprintf(gettext("Overwrote previous installation of %s."), $pkg_info['name']);
751
		$to_output = gettext("overwrite!") . "\n";
752
	}
753
	unlink_if_exists('/conf/needs_package_sync');
754
	write_config(sprintf(gettext("Intermediate config write during package install for %s."), $pkg_info['name']));
755
	conf_mount_ro();
756
	update_status($to_output);
757

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

    
761
		uninstall_package($package_name);
762
		write_config($changedesc);
763
		log_error(sprintf(gettext("Failed to install package: %s."), $pkg_info['name']));
764
		update_status(gettext("Failed to install package.") . "\n");
765
		return false;
766
	}
767

    
768
	if (file_exists("/usr/local/pkg/" . $pkg_info['configurationfile'])) {
769
		update_status(gettext("Loading package configuration... "));
770
		$pkg_config = parse_xml_config_pkg("/usr/local/pkg/" . $pkg_info['configurationfile'], "packagegui");
771
		update_status(gettext("done.") . "\n");
772
		update_status(gettext("Configuring package components...") . "\n");
773
		if (!empty($pkg_config['filter_rules_needed'])) {
774
			$config['installedpackages']['package'][$pkgid]['filter_rule_function'] = $pkg_config['filter_rules_needed'];
775
		}
776
		/* modify system files */
777

    
778
		/* if a require exists, include it.  this will
779
		 * show us where an error exists in a package
780
		 * instead of making us blindly guess
781
		 */
782
		$missing_include = false;
783
		if ($pkg_config['include_file'] <> "") {
784
			update_status(gettext("Loading package instructions...") . "\n");
785
			if (file_exists($pkg_config['include_file'])) {
786
				pkg_debug("require_once('{$pkg_config['include_file']}')\n");
787
				require_once($pkg_config['include_file']);
788
			} else {
789
				pkg_debug("Missing include {$pkg_config['include_file']}\n");
790
				$missing_include = true;
791
				update_status(sprintf(gettext("Include %s is missing!"), basename($pkg_config['include_file'])) . "\n");
792

    
793
				uninstall_package($package_name);
794
				write_config($changedesc);
795
				log_error(sprintf(gettext("Failed to install package: %s."), $pkg_info['name']));
796
				update_status(gettext("Failed to install package.") . "\n");
797
				return false;
798
			}
799
		}
800

    
801
		/* custom commands */
802
		update_status(gettext("Custom commands...") . "\n");
803
		if ($missing_include == false) {
804
			if ($pkg_config['custom_php_global_functions'] <> "") {
805
				update_status(gettext("Executing custom_php_global_functions()..."));
806
				eval_once($pkg_config['custom_php_global_functions']);
807
				update_status(gettext("done.") . "\n");
808
			}
809
			if ($pkg_config['custom_php_install_command']) {
810
				update_status(gettext("Executing custom_php_install_command()..."));
811
				eval_once($pkg_config['custom_php_install_command']);
812
				update_status(gettext("done.") . "\n");
813
			}
814
			if ($pkg_config['custom_php_resync_config_command'] <> "") {
815
				update_status(gettext("Executing custom_php_resync_config_command()..."));
816
				eval_once($pkg_config['custom_php_resync_config_command']);
817
				update_status(gettext("done.") . "\n");
818
			}
819
		}
820
		/* sidebar items */
821
		if (is_array($pkg_config['menu'])) {
822
			update_status(gettext("Menu items... "));
823
			foreach ($pkg_config['menu'] as $menu) {
824
				if (is_array($config['installedpackages']['menu'])) {
825
					foreach ($config['installedpackages']['menu'] as $amenu) {
826
						if ($amenu['name'] == $menu['name']) {
827
							continue 2;
828
						}
829
					}
830
				} else {
831
					$config['installedpackages']['menu'] = array();
832
				}
833
				$config['installedpackages']['menu'][] = $menu;
834
			}
835
			update_status(gettext("done.") . "\n");
836
		}
837
		/* services */
838
		if (is_array($pkg_config['service'])) {
839
			update_status(gettext("Services... "));
840
			foreach ($pkg_config['service'] as $service) {
841
				if (is_array($config['installedpackages']['service'])) {
842
					foreach ($config['installedpackages']['service'] as $aservice) {
843
						if ($aservice['name'] == $service['name']) {
844
							continue 2;
845
						}
846
					}
847
				} else {
848
					$config['installedpackages']['service'] = array();
849
				}
850
				$config['installedpackages']['service'][] = $service;
851
			}
852
			update_status(gettext("done.") . "\n");
853
		}
854
	} else {
855
		pkg_debug("Unable to find config file\n");
856
		update_status(gettext("Loading package configuration... failed!") . "\n\n" . gettext("Installation aborted."));
857
		pkg_debug(gettext("Unable to load package configuration. Installation aborted.") ."\n");
858

    
859
		uninstall_package($package_name);
860
		write_config($changedesc);
861
		log_error(sprintf(gettext("Failed to install package: %s."), $pkg_info['name']));
862
		update_status(gettext("Failed to install package.") . "\n");
863
		return false;
864
	}
865

    
866
	/* set up package logging streams */
867
	if ($pkg_info['logging']) {
868
		system_syslogd_start();
869
	}
870

    
871
	update_status(gettext("Writing configuration... "));
872
	write_config($changedesc);
873
	log_error(sprintf(gettext("Successfully installed package: %s."), $pkg_info['name']));
874
	update_status(gettext("done.") . "\n");
875
	if ($pkg_info['after_install_info']) {
876
		update_status($pkg_info['after_install_info']);
877
	}
878

    
879
	return true;
880
}
881

    
882
function delete_package_xml($package_name, $when = "post-deinstall") {
883
	global $g, $config, $pkg_interface;
884

    
885
	conf_mount_rw();
886

    
887
	$pkgid = get_package_id($package_name);
888
	if ($pkgid == -1) {
889
		update_status(sprintf(gettext("The %s package is not installed.%sDeletion aborted."), $package_name, "\n\n"));
890
		ob_flush();
891
		sleep(1);
892
		conf_mount_ro();
893
		return;
894
	}
895
	pkg_debug(sprintf(gettext("Removing %s package... "), $package_name));
896
	update_status(sprintf(gettext("Removing %s components..."), $package_name) . "\n");
897
	/* parse package configuration */
898
	$packages = &$config['installedpackages']['package'];
899
	$menus =& $config['installedpackages']['menu'];
900
	$services = &$config['installedpackages']['service'];
901
	$pkg_info =& $packages[$pkgid];
902
	if (file_exists("/usr/local/pkg/" . $pkg_info['configurationfile'])) {
903
		$pkg_config = parse_xml_config_pkg("/usr/local/pkg/" . $packages[$pkgid]['configurationfile'], "packagegui");
904
		/* remove menu items */
905
		if (is_array($pkg_config['menu'])) {
906
			update_status(gettext("Menu items... "));
907
			if (is_array($pkg_config['menu']) && is_array($menus)) {
908
				foreach ($pkg_config['menu'] as $menu) {
909
					foreach ($menus as $key => $instmenu) {
910
						if ($instmenu['name'] == $menu['name']) {
911
							unset($menus[$key]);
912
							break;
913
						}
914
					}
915
				}
916
			}
917
			update_status(gettext("done.") . "\n");
918
		}
919
		/* remove services */
920
		if (is_array($pkg_config['service'])) {
921
			update_status(gettext("Services... "));
922
			if (is_array($pkg_config['service']) && is_array($services)) {
923
				foreach ($pkg_config['service'] as $service) {
924
					foreach ($services as $key => $instservice) {
925
						if ($instservice['name'] == $service['name']) {
926
							if (platform_booting() != true) {
927
								stop_service($service['name']);
928
							}
929
							if ($service['rcfile']) {
930
								$prefix = RCFILEPREFIX;
931
								if (!empty($service['prefix'])) {
932
									$prefix = $service['prefix'];
933
								}
934
								if (file_exists("{$prefix}{$service['rcfile']}")) {
935
									@unlink("{$prefix}{$service['rcfile']}");
936
								}
937
							}
938
							unset($services[$key]);
939
						}
940
					}
941
				}
942
			}
943
			update_status(gettext("done.") . "\n");
944
		}
945
		/*
946
		 * XXX: Otherwise inclusion of config.inc again invalidates actions taken.
947
		 *	Same is done during installation.
948
		 */
949
		write_config(sprintf(gettext("Intermediate config write during package removal for %s."), $package_name));
950

    
951
		/*
952
		 * If a require exists, include it.	 this will
953
		 * show us where an error exists in a package
954
		 * instead of making us blindly guess
955
		 */
956
		$missing_include = false;
957
		if ($pkg_config['include_file'] <> "") {
958
			update_status(gettext("Loading package instructions...") . "\n");
959
			if (file_exists($pkg_config['include_file'])) {
960
				pkg_debug("require_once(\"{$pkg_config['include_file']}\")\n");
961
				require_once($pkg_config['include_file']);
962
			} else {
963
				pkg_debug("Missing include {$pkg_config['include_file']}\n");
964
				$missing_include = true;
965
				update_status(sprintf(gettext("Include file %s could not be found for inclusion."), basename($pkg_config['include_file'])) . "\n");
966
			}
967
		}
968
		/* ermal
969
		 * NOTE: It is not possible to handle parse errors on eval.
970
		 * So we prevent it from being run at all to not interrupt all the other code.
971
		 */
972
		if ($when == "deinstall" && $missing_include == false) {
973
			/* evaluate this package's global functions and pre deinstall commands */
974
			if ($pkg_config['custom_php_global_functions'] <> "") {
975
				eval_once($pkg_config['custom_php_global_functions']);
976
			}
977
			if ($pkg_config['custom_php_pre_deinstall_command'] <> "") {
978
				eval_once($pkg_config['custom_php_pre_deinstall_command']);
979
			}
980
		}
981
		/* deinstall commands */
982
		if ($when == "post-deinstall" && $pkg_config['custom_php_deinstall_command'] <> "") {
983
			update_status(gettext("Deinstall commands... "));
984
			if ($missing_include == false) {
985
				eval_once($pkg_config['custom_php_deinstall_command']);
986
				update_status(gettext("done.") . "\n");
987
			} else {
988
				update_status("\n". gettext("Not executing custom deinstall hook because an include is missing.") . "\n");
989
			}
990
		}
991
	}
992
	/* syslog */
993
	$need_syslog_restart = false;
994
	if (is_array($pkg_info['logging']) && $pkg_info['logging']['logfilename'] <> "") {
995
		update_status(gettext("Syslog entries... "));
996
		@unlink("{$g['varlog_path']}/{$pkg_info['logging']['logfilename']}");
997
		update_status("done.\n");
998
		$need_syslog_restart = true;
999
	}
1000

    
1001
	if ($when == "post-deinstall") {
1002
		/* remove config.xml entries */
1003
		update_status(gettext("Configuration... "));
1004
		unset($config['installedpackages']['package'][$pkgid]);
1005
		update_status(gettext("done.") . "\n");
1006
		write_config(sprintf(gettext("Removed %s package."), $package_name));
1007
	}
1008

    
1009
	/* remove package entry from /etc/syslog.conf if needed */
1010
	/* this must be done after removing the entries from config.xml */
1011
	if ($need_syslog_restart) {
1012
		system_syslogd_start();
1013
	}
1014

    
1015
	conf_mount_ro();
1016
}
1017

    
1018
/*
1019
 * Used during upgrade process or retore backup process, verify all
1020
 * packages installed in config.xml and install pkg accordingly
1021
 */
1022
function package_reinstall_all() {
1023
	global $g, $config, $pkg_interface;
1024

    
1025
	$upgrade = (file_exists('/conf/needs_package_sync') && platform_booting());
1026

    
1027
	if ((!isset($config['installedpackages']['package']) ||
1028
	    !is_array($config['installedpackages']['package'])) && !$upgrade) {
1029
		return true;
1030
	}
1031

    
1032
	/* During boot after upgrade, wait for internet connection */
1033
	if ($upgrade) {
1034
		update_status(gettext("Waiting for Internet connection to update pkg metadata and finish package reinstallation"));
1035
		$ntries = 3;
1036
		while ($ntries > 0) {
1037
			if (pkg_update(true)) {
1038
				break;
1039
			}
1040
			update_status('.');
1041
			sleep(1);
1042
			$ntries--;
1043
		}
1044
		update_status("\n");
1045

    
1046
		if ($ntries == 0) {
1047
			file_notice(gettext("Package reinstall"),
1048
			    gettext("Package reinstall process was ABORTED due to lack of internet connectivity"));
1049
			return false;
1050
		}
1051
	}
1052

    
1053
	$pkg_info = get_pkg_info();
1054

    
1055
	if ($upgrade &&
1056
	    file_exists("{$g['cf_conf_path']}/packages_to_reinstall_after_upgrade.txt")) {
1057
		$package_list = file("{$g['cf_conf_path']}/packages_to_reinstall_after_upgrade.txt",
1058
		    FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
1059
		unlink_if_exists("{$g['cf_conf_path']}/packages_to_reinstall_after_upgrade.txt");
1060
	} else {
1061
		if (!isset($config['installedpackages']['package']) || !is_array($config['installedpackages']['package'])) {
1062
			return true;
1063
		}
1064
		$package_list = array();
1065
		foreach ($config['installedpackages']['package'] as $package) {
1066
			$package_list[] = get_package_internal_name($package);
1067
		}
1068
	}
1069

    
1070
	foreach ($package_list as $package) {
1071
		$found = false;
1072
		foreach ($pkg_info as $pkg) {
1073
			pkg_remove_prefix($pkg['name']);
1074
			if ($pkg['name'] == $package) {
1075
				pkg_install($g['pkg_prefix'] . $package, true);
1076
				$found = true;
1077
				break;
1078
			}
1079
		}
1080

    
1081
		if (!$found) {
1082
			if (!function_exists("file_notice")) {
1083
				require_once("notices.inc");
1084
			}
1085

    
1086
			file_notice(gettext("Package reinstall"),
1087
			    sprintf(gettext("Package %s does not exist in current %s version and it has been removed."),
1088
			    $package, $g['product_name']));
1089
			uninstall_package($package);
1090
		}
1091
	}
1092

    
1093
	return true;
1094
}
1095

    
1096
function stop_packages() {
1097
	require_once("config.inc");
1098
	require_once("functions.inc");
1099
	require_once("filter.inc");
1100
	require_once("shaper.inc");
1101
	require_once("captiveportal.inc");
1102
	require_once("pkg-utils.inc");
1103
	require_once("pfsense-utils.inc");
1104
	require_once("service-utils.inc");
1105

    
1106
	global $config, $g;
1107

    
1108
	log_error(gettext("Stopping all packages."));
1109

    
1110
	$rcfiles = glob(RCFILEPREFIX . "*.sh");
1111
	if (!$rcfiles) {
1112
		$rcfiles = array();
1113
	} else {
1114
		$rcfiles = array_flip($rcfiles);
1115
		if (!$rcfiles) {
1116
			$rcfiles = array();
1117
		}
1118
	}
1119

    
1120
	if (is_array($config['installedpackages']['package'])) {
1121
		foreach ($config['installedpackages']['package'] as $package) {
1122
			echo " Stopping package {$package['name']}...";
1123
			$internal_name = get_package_internal_name($package);
1124
			stop_service($internal_name);
1125
			unset($rcfiles[RCFILEPREFIX . strtolower($internal_name) . ".sh"]);
1126
			echo "done.\n";
1127
		}
1128
	}
1129

    
1130
	foreach ($rcfiles as $rcfile => $number) {
1131
		$shell = @popen("/bin/sh", "w");
1132
		if ($shell) {
1133
			echo " Stopping {$rcfile}...";
1134
			if (!@fwrite($shell, "{$rcfile} stop >>/tmp/bootup_messages 2>&1")) {
1135
				if ($shell) {
1136
					pclose($shell);
1137
				}
1138
				$shell = @popen("/bin/sh", "w");
1139
			}
1140
			echo "done.\n";
1141
			pclose($shell);
1142
		}
1143
	}
1144
}
1145

    
1146
/* Identify which meta package is installed */
1147
function get_meta_pkg_name() {
1148
	global $g;
1149

    
1150
	/* XXX: Use pkg annotation */
1151
	if (is_pkg_installed($g['product_name'])) {
1152
		return $g['product_name'];
1153
	} else if (is_pkg_installed($g['product_name'] . '-vmware')) {
1154
		return $g['product_name'] . '-vmware';
1155
	}
1156
	return false;
1157
}
1158

    
1159
/* Identify which base package is installed */
1160
function get_base_pkg_name() {
1161
	global $g;
1162

    
1163
	/* XXX: Use pkg annotation */
1164
	if (is_pkg_installed($g['product_name'] . '-base-' . $g['platform'])) {
1165
		return $g['product_name'] . '-base-' . $g['platform'];
1166
	} else if (is_pkg_installed($g['product_name'] . '-base')) {
1167
		return $g['product_name'] . '-base';
1168
	}
1169
	return false;
1170
}
1171

    
1172
/* Verify if system needs upgrade (meta package or base) */
1173
function get_system_pkg_version($baseonly = false) {
1174
	global $g;
1175

    
1176
	$output = exec("/usr/local/sbin/{$g['product_name']}-upgrade -c", $_gc,
1177
	    $rc);
1178

    
1179
	/* pfSense-upgrade returns 2 when there is a new version */
1180
	if ($rc == 2) {
1181
		$new_version = explode(' ', $output)[0];
1182
	}
1183

    
1184
	$base_pkg = get_base_pkg_name();
1185
	$meta_pkg = get_meta_pkg_name();
1186

    
1187
	if (!$base_pkg || !$meta_pkg) {
1188
		return false;
1189
	}
1190

    
1191
	$info = get_pkg_info($base_pkg, true);
1192

    
1193
	$pkg_info = array();
1194
	foreach ($info as $item) {
1195
		if ($item['name'] == $base_pkg) {
1196
			$pkg_info = $item;
1197
			break;
1198
		}
1199
	}
1200

    
1201
	if (empty($pkg_info) || (!$baseonly && ($pkg_info['version'] ==
1202
	    $pkg_info['installed_version']))) {
1203
		$info = get_pkg_info($meta_pkg, true);
1204

    
1205
		foreach ($info as $item) {
1206
			if ($item['name'] == $meta_pkg) {
1207
				$pkg_info = $item;
1208
				break;
1209
			}
1210
		}
1211
	}
1212

    
1213
	if (empty($pkg_info)) {
1214
		return false;
1215
	}
1216

    
1217
	return array(
1218
	    'version'           => $new_version ?: $pkg_info['version'],
1219
	    'installed_version' => $pkg_info['installed_version']
1220
	);
1221
}
1222

    
1223
/* List available repos */
1224
function pkg_list_repos() {
1225
	global $g;
1226

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

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

    
1231
	$default = array(
1232
	    'name' => 'Default',
1233
	    'path' => $path . "/{$g['product_name']}-repo.conf",
1234
	    'descr' => $default_descr
1235
	);
1236

    
1237
	$result = array($default);
1238

    
1239
	$conf_files = glob("{$path}/{$g['product_name']}-repo-*.conf");
1240
	foreach ($conf_files as $conf_file) {
1241
		$descr_file = preg_replace('/.conf$/', '.descr', $conf_file);
1242
		if (file_exists($descr_file)) {
1243
			$descr_content = file($descr_file);
1244
			$descr = chop($descr_content[0]);
1245
		} else {
1246
			$descr = 'Unknown';
1247
		}
1248
		if (!preg_match('/-repo-(.*).conf/', $conf_file, $matches)) {
1249
			continue;
1250
		}
1251
		$entry = array(
1252
		    'name' => ucfirst(strtolower($matches[1])),
1253
		    'path' => $conf_file,
1254
		    'descr' => $descr
1255
		);
1256
		$result[] = $entry;
1257
	}
1258

    
1259
	return $result;
1260
}
1261

    
1262
/* Switch between stable and devel repos */
1263
function pkg_switch_repo($path) {
1264
	global $g;
1265

    
1266
	safe_mkdir("/usr/local/etc/pkg/repos");
1267
	@unlink("/usr/local/etc/pkg/repos/{$g['product_name']}.conf");
1268
	@symlink($path, "/usr/local/etc/pkg/repos/{$g['product_name']}.conf");
1269

    
1270
	$abi_file = str_replace('.conf', '.abi', $path);
1271
	$altabi_file = str_replace('.conf', '.altabi', $path);
1272

    
1273
	if (file_exists($abi_file) && file_exists($altabi_file)) {
1274
		$abi = file_get_contents($abi_file);
1275
		$altabi = file_get_contents($altabi_file);
1276

    
1277
		$pkg_conf = array(
1278
			"ABI={$abi}",
1279
			"ALTABI={$altabi}"
1280
		);
1281

    
1282
		file_put_contents("/usr/local/etc/pkg.conf", $pkg_conf);
1283
	}
1284

    
1285
	return pkg_update(true);
1286
}
1287

    
1288
?>
(40-40/65)