Project

General

Profile

Download (34.9 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
 * Licensed under the Apache License, Version 2.0 (the "License");
11
 * you may not use this file except in compliance with the License.
12
 * You may obtain a copy of the License at
13
 *
14
 * http://www.apache.org/licenses/LICENSE-2.0
15
 *
16
 * Unless required by applicable law or agreed to in writing, software
17
 * distributed under the License is distributed on an "AS IS" BASIS,
18
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19
 * See the License for the specific language governing permissions and
20
 * limitations under the License.
21
 */
22

    
23
require_once("globals.inc");
24
require_once("service-utils.inc");
25

    
26
if (file_exists("/cf/conf/use_xmlreader")) {
27
	require_once("xmlreader.inc");
28
} else {
29
	require_once("xmlparse.inc");
30
}
31

    
32
require_once("pfsense-utils.inc");
33

    
34
if (!function_exists("pkg_debug")) {
35
	/* set up logging if needed */
36
	function pkg_debug($msg) {
37
		global $g, $debug, $fd_log;
38

    
39
		if (!$debug) {
40
			return;
41
		}
42

    
43
		if (!$fd_log) {
44
			if (!$fd_log = fopen("{$g['tmp_path']}/pkg_mgr_debug.log", "w")) {
45
				update_status(gettext("Warning, could not open log for writing.") . "\n");
46
			}
47
		}
48
		@fwrite($fd_log, $msg);
49
	}
50
}
51

    
52
/* Validate if pkg name is valid */
53
function pkg_valid_name($pkgname) {
54
	global $g;
55

    
56
	$pattern = "/^{$g['pkg_prefix']}[a-zA-Z0-9\.\-_]+$/";
57
	return preg_match($pattern, $pkgname);
58
}
59

    
60
/* Remove pkg_prefix from package name if it's present */
61
function pkg_remove_prefix(&$pkg_name) {
62
	global $g;
63

    
64
	if (substr($pkg_name, 0, strlen($g['pkg_prefix'])) == $g['pkg_prefix']) {
65
		$pkg_name = substr($pkg_name, strlen($g['pkg_prefix']));
66
	}
67
}
68

    
69
/* Execute pkg update when it's necessary */
70
function pkg_update($force = false) {
71
	global $g;
72

    
73
	return pkg_call("update" . ($force ? " -f" : ""));
74
}
75

    
76
/* return an array with necessary environment vars for pkg */
77
function pkg_env($extra_env = array()) {
78
	global $config, $g;
79

    
80
	$pkg_env_vars = array(
81
		"LANG" => "C",
82
		"HTTP_USER_AGENT" => $user_agent,
83
		"ASSUME_ALWAYS_YES" => "true",
84
		"FETCH_TIMEOUT" => 5,
85
		"FETCH_RETRY" => 2
86
	);
87

    
88
	if (!empty($config['system']['proxyurl'])) {
89
		$http_proxy = $config['system']['proxyurl'];
90
		if (!empty($config['system']['proxyport'])) {
91
			$http_proxy .= ':' . $config['system']['proxyport'];
92
		}
93
		$pkg_env_vars['HTTP_PROXY'] = $http_proxy;
94

    
95
		if (!empty($config['system']['proxyuser']) && !empty($config['system']['proxypass'])) {
96
			$pkg_env_vars['HTTP_PROXY_AUTH'] = "basic:*:{$config['system']['proxyuser']}:{$config['system']['proxypass']}";
97
		}
98
	}
99

    
100
	if (isset($config['system']['use_mfs_tmpvar'])) {
101
		$pkg_env_vars['PKG_DBDIR'] = '/root/var/db/pkg';
102
		$pkg_env_vars['PKG_CACHEDIR'] = '/root/var/cache/pkg';
103
	}
104

    
105
	foreach ($extra_env as $key => $value) {
106
		$pkg_env_vars[$key] = $value;
107
	}
108

    
109
	return $pkg_env_vars;
110
}
111

    
112
/* Execute a pkg call */
113
function pkg_call($params, $mute = false, $extra_env = array()) {
114
	global $g, $config;
115

    
116
	if (empty($params)) {
117
		return false;
118
	}
119

    
120
	$user_agent = $g['product_name'] . '/' . $g['product_version'];
121
	if (!isset($config['system']['do_not_send_host_uuid'])) {
122
		$user_agent .= ' : ' . get_single_sysctl('kern.hostuuid');
123
	}
124

    
125
	$descriptorspec = array(
126
		1 => array("pipe", "w"), /* stdout */
127
		2 => array("pipe", "w")	 /* stderr */
128
	);
129

    
130

    
131
	pkg_debug("pkg_call(): {$params}\n");
132
	$process = proc_open("/usr/sbin/pkg {$params}", $descriptorspec, $pipes,
133
	    '/', pkg_env($extra_env));
134

    
135
	if (!is_resource($process)) {
136
		return false;
137
	}
138

    
139
	stream_set_blocking($pipes[1], 0);
140
	stream_set_blocking($pipes[2], 0);
141

    
142
	/* XXX: should be a tunnable? */
143
	$timeout = 300; // seconds
144
	$error_log = '';
145

    
146
	do {
147
		$write = array();
148
		$read = array($pipes[1], $pipes[2]);
149
		$except = array();
150

    
151
		$stream = stream_select($read, $write, $except, null, $timeout);
152
		if ($stream !== FALSE && $stream > 0) {
153
			foreach ($read as $pipe) {
154
				$content = stream_get_contents($pipe);
155
				if ($content == '') {
156
					continue;
157
				}
158
				if ($pipe === $pipes[1]) {
159
					if (!$mute) {
160
						update_status($content);
161
					}
162
					flush();
163
				} else if ($pipe === $pipes[2]) {
164
					$error_log .= $content;
165
				}
166
			}
167
		}
168

    
169
		$status = proc_get_status($process);
170
	} while ($status['running']);
171

    
172
	fclose($pipes[1]);
173
	fclose($pipes[2]);
174
	proc_close($process);
175

    
176

    
177
	$rc = $status['exitcode'];
178

    
179
	pkg_debug("pkg_call(): rc = {$rc}\n");
180
	if ($rc == 0) {
181
		return true;
182
	}
183

    
184
	pkg_debug("pkg_call(): error_log\n{$error_log}\n");
185
	if (!$mute) {
186
		update_status("\n\n" .  sprintf(gettext(
187
		    "ERROR!!! An error occurred on pkg execution (rc = %d) with parameters '%s':"),
188
		    $rc, $params) . "\n" . $error_log . "\n");
189
	}
190

    
191
	return false;
192
}
193

    
194
/* Execute pkg with $params, fill stdout and stderr and return pkg rc */
195
function pkg_exec($params, &$stdout, &$stderr, $extra_env = array()) {
196
	global $g, $config;
197

    
198
	if (empty($params)) {
199
		return -1;
200
	}
201

    
202
	$user_agent = $g['product_name'] . '/' . $g['product_version'];
203
	if (!isset($config['system']['do_not_send_host_uuid'])) {
204
		$user_agent .= ' : ' . get_single_sysctl('kern.hostuuid');
205
	}
206

    
207
	$descriptorspec = array(
208
		1 => array("pipe", "w"), /* stdout */
209
		2 => array("pipe", "w")	 /* stderr */
210
	);
211

    
212

    
213
	pkg_debug("pkg_exec(): {$params}\n");
214
	$process = proc_open("/usr/sbin/pkg {$params}", $descriptorspec, $pipes,
215
	    '/', pkg_env($extra_env));
216

    
217
	if (!is_resource($process)) {
218
		return -1;
219
	}
220

    
221
	$stdout = '';
222
	while (($l = fgets($pipes[1])) !== FALSE) {
223
		$stdout .= $l;
224
	}
225
	fclose($pipes[1]);
226

    
227
	$stderr = '';
228
	while (($l = fgets($pipes[2])) !== FALSE) {
229
		$stderr .= $l;
230
	}
231
	fclose($pipes[2]);
232

    
233

    
234
	return proc_close($process);
235
}
236

    
237
/* Compare 2 pkg versions and return:
238
 * '=' - versions are the same
239
 * '>' - $v1 > $v2
240
 * '<' - $v1 < $v2
241
 * '?' - Error
242
 */
243
function pkg_version_compare($v1, $v2) {
244
	if (empty($v1) || empty($v2)) {
245
		return '?';
246
	}
247

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

    
250
	if ($rc != 0) {
251
		return '?';
252
	}
253

    
254
	return str_replace("\n", "", $stdout);
255
}
256

    
257
/* Check if package is installed */
258
function is_pkg_installed($pkg_name) {
259
	global $g;
260

    
261
	if (empty($pkg_name)) {
262
		return false;
263
	}
264

    
265
	return pkg_call("info -e " . $pkg_name, true);
266
}
267

    
268
/* Install package, $pkg_name should not contain prefix */
269
function pkg_install($pkg_name, $force = false) {
270
	global $g;
271
	$result = false;
272

    
273
	$shortname = $pkg_name;
274
	pkg_remove_prefix($shortname);
275

    
276
	$pkg_force = "";
277
	if ($force) {
278
		$pkg_force = "-f ";
279
	}
280

    
281
	pkg_debug("Installing package {$shortname}\n");
282
	if ($force || !is_pkg_installed($pkg_name)) {
283
		$result = pkg_call("install -y " . $pkg_force . $pkg_name);
284
		/* Cleanup cacke to free disk space */
285
		pkg_call("clean -y");
286
	}
287

    
288
	return $result;
289
}
290

    
291
/* Delete package from FreeBSD, $pkg_name should not contain prefix */
292
function pkg_delete($pkg_name) {
293
	global $g;
294

    
295
	$shortname = $pkg_name;
296
	pkg_remove_prefix($shortname);
297

    
298
	pkg_debug("Removing package {$shortname}\n");
299
	if (is_pkg_installed($pkg_name)) {
300
		pkg_call("delete -y " . $pkg_name);
301
		/* Cleanup unecessary dependencies */
302
		pkg_call("autoremove -y");
303
	}
304
}
305

    
306
/* Check if package is present in config.xml */
307
function is_package_installed($package_name) {
308
	return (get_package_id($package_name) != -1);
309
}
310

    
311
/* Find package array index */
312
function get_package_id($package_name) {
313
	global $config;
314

    
315
	if (!is_array($config['installedpackages']['package'])) {
316
		return -1;
317
	}
318

    
319
	foreach ($config['installedpackages']['package'] as $idx => $pkg) {
320
		if ($pkg['name'] == $package_name ||
321
		    get_package_internal_name($pkg) == $package_name) {
322
			return $idx;
323
		}
324
	}
325

    
326
	return -1;
327
}
328

    
329
/* Return internal_name when it's defined, otherwise, returns name */
330
function get_package_internal_name($package_data) {
331
	if (isset($package_data['internal_name']) && ($package_data['internal_name'] != "")) {
332
		/* e.g. name is Ipguard-dev, internal name is ipguard */
333
		return $package_data['internal_name'];
334
	} else {
335
		return $package_data['name'];
336
	}
337
}
338

    
339
// Get information about packages.
340
function get_pkg_info($pkgs = 'all', $remote_repo_usage_disabled = false, $installed_pkgs_only = false) {
341

    
342
	global $g, $input_errors;
343

    
344
	$out = $err = $extra_param = '';
345
	$rc = 0;
346

    
347
	unset($pkg_filter);
348

    
349
	if (is_array($pkgs)) {
350
		$pkg_filter = $pkgs;
351
		$pkgs = $g['pkg_prefix'] . '*';
352
	} elseif ($pkgs == 'all') {
353
		$pkgs = $g['pkg_prefix'] . '*';
354
	}
355

    
356
	
357
	if (!function_exists('is_subsystem_dirty')) {
358
		require_once("util.inc");
359
	}
360

    
361
	/* Do not run remote operations if pkg has a lock */
362
	if (is_subsystem_dirty('pkg')) {
363
		$remote_repo_usage_disabled = true;
364
		$lock = false;
365
	} else {
366
		$lock = true;
367
	}
368

    
369
	if ($lock) {
370
		mark_subsystem_dirty('pkg');
371
	}
372

    
373
	if ($remote_repo_usage_disabled) {
374
		$extra_param = "-U ";
375
	}
376

    
377
	if (!$installed_pkgs_only) {
378
		$rc = pkg_exec("search {$extra_param}-R --raw-format json-compact " . $pkgs, $out, $err);
379
	}
380
	if (($installed_pkgs_only || ($rc != 0 && $remote_repo_usage_disabled)) && is_package_installed($pkgs)) {
381
		/* Fall back on pkg info to return locally installed matching pkgs instead, if 
382
		 *
383
		 *   (1) only installed pkgs needed, or
384
		 *       we tried to check the local catalog copy (implying that we would have accepted incomplete/outdated pkg info)
385
		 *       but it didn't have any contents, or for other reasons returned an error. 
386
		 *   AND
387
		 *   (2) at least some pkgs matching <pattern> are installed
388
		 *
389
		 * Following an unsuccessful attempt to access a remote repo catalog, the local copy is wiped clear. Thereafter any
390
		 * "pkg search" will return an error until online+updated again. If the calling code would have accepted local copy info
391
		 * (which could be incomplete/out of date), then it makes sense to fall back on pkg info to at least return the known
392
		 * info about installed pkgs (pkg info should still work), instead of failing and returning no info at all. 
393
		 * For example, this at least enables offline view + management of installed pkgs in GUI/console.
394
		 *
395
		 * We skip this step if no matching pkgs are installed, because then pkg info would return a "no matching pkgs"
396
		 * RC code, even though this wouldn't be considered an "error" (and $out+$err would be correct empty strings if none match).
397
		 * Note that is_package_installed() is a wrapper for pkg info -e <pattern> which is what we need here.
398
		*/
399
		
400
		// ok, 1 or more packages match, so pkg info can be safely called to get the pkg list  
401
		$rc = pkg_exec("info -R --raw-format json-compact " . $pkgs, $out, $err);  
402
	}
403

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

    
408
	if ($rc != 0) {
409
		update_status("\n" . gettext(
410
		    "ERROR: Error trying to get packages list. Aborting...")
411
		    . "\n");
412
		update_status($err);
413
		$input_errors[] =  gettext("ERROR: Error trying to get packages list. Aborting...") . "\n";
414
		$input_errors[] =  $err;
415
		return array();
416
	}
417

    
418
	$result = array();
419
	$pkgs_info = explode("\n", $out);
420
	foreach ($pkgs_info as $pkg_info_json) {
421
		$pkg_info = json_decode($pkg_info_json, true);
422
		if (!isset($pkg_info['name'])) {
423
			continue;
424
		}
425

    
426
		if (isset($pkg_filter) && !in_array($pkg_info['name'], $pkg_filter)) {
427
			continue;
428
		}
429

    
430
		$pkg_info['shortname'] = $pkg_info['name'];
431
		pkg_remove_prefix($pkg_info['shortname']);
432

    
433
		/* XXX: Add it to globals.inc? */
434
		$pkg_info['changeloglink'] =
435
		    "https://github.com/pfsense/FreeBSD-ports/commits/devel/" .
436
		    $pkg_info['categories'][0] . '/' . $pkg_info['name'];
437

    
438
		if (is_pkg_installed($pkg_info['name'])) {
439
			$pkg_info['installed'] = true;
440

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

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

    
453
			$pkg_info['installed_version'] = str_replace("\n", "", $out);
454
		} else if (is_package_installed($pkg_info['shortname'])) {
455
			$pkg_info['broken'] = true;
456
		}
457

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

    
460
		$result[] = $pkg_info;
461
		unset($pkg_info);
462
	}
463

    
464
	/* Sort result alphabetically */
465
	usort($result, function($a, $b) {
466
		return(strcasecmp ($a['name'], $b['name']));
467
	});
468

    
469
	return $result;
470
}
471

    
472
/*
473
 * If binary pkg is installed but post-install tasks were not
474
 * executed yet, do it now.
475
 * This scenario can happen when a pkg is pre-installed during
476
 * build phase, and at this point, cannot find a running system
477
 * to register itself in config.xml and also execute custom
478
 * install functions
479
 */
480
function register_all_installed_packages() {
481
	global $g, $config, $pkg_interface;
482

    
483
	$pkg_info = get_pkg_info('all', true, true);
484

    
485

    
486
	foreach ($pkg_info as $pkg) {
487
		if (!isset($pkg['installed'])) {
488
			continue;
489
		}
490

    
491
		pkg_remove_prefix($pkg['name']);
492

    
493
		if (is_package_installed($pkg['name'])) {
494
			continue;
495
		}
496

    
497
		update_status(sprintf(gettext(
498
		    "Running last steps of %s installation.") . "\n",
499
		    $pkg['name']));
500
		install_package_xml($pkg['name']);
501
	}
502
}
503

    
504
/*
505
 * resync_all_package_configs() Force packages to setup their configuration and rc.d files.
506
 * This function may also print output to the terminal indicating progress.
507
 */
508
function resync_all_package_configs($show_message = false) {
509
	global $config, $pkg_interface, $g;
510

    
511
	log_error(gettext("Resyncing configuration for all packages."));
512

    
513
	if (!is_array($config['installedpackages']['package'])) {
514
		return;
515
	}
516

    
517
	if ($show_message == true) {
518
		echo "Syncing packages:";
519
	}
520

    
521

    
522
	foreach ($config['installedpackages']['package'] as $idx => $package) {
523
		if (empty($package['name'])) {
524
			continue;
525
		}
526
		if ($show_message == true) {
527
			echo " " . $package['name'];
528
		}
529
		if (platform_booting() != true) {
530
			stop_service(get_package_internal_name($package));
531
		}
532
		sync_package($package['name']);
533
		update_status(gettext("Syncing packages...") . "\n");
534
	}
535

    
536
	if ($show_message == true) {
537
		echo " done.\n";
538
	}
539

    
540
	@unlink("/conf/needs_package_sync");
541
}
542

    
543
function uninstall_package($package_name) {
544
	global $config;
545

    
546
	$internal_name = $package_name;
547
	$id = get_package_id($package_name);
548
	if ($id >= 0) {
549
		$internal_name = get_package_internal_name($config['installedpackages']['package'][$id]);
550
		stop_service($internal_name);
551
	}
552
	$pkg_name = $g['pkg_prefix'] . $internal_name;
553

    
554
	if (is_pkg_installed($pkg_name)) {
555
		update_status(gettext("Removing package...") . "\n");
556
		pkg_delete($pkg_name);
557
	} else {
558
		delete_package_xml($package_name);
559
	}
560

    
561
	update_status(gettext("done.") . "\n");
562
}
563

    
564
/* Run <custom_php_resync_config_command> */
565
function sync_package($package_name) {
566
	global $config, $builder_package_install;
567

    
568
	// If this code is being called by pfspkg_installer
569
	// which the builder system uses then return (ignore).
570
	if ($builder_package_install) {
571
		return;
572
	}
573

    
574
	if (empty($config['installedpackages']['package'])) {
575
		return;
576
	}
577

    
578
	if (($pkg_id = get_package_id($package_name)) == -1) {
579
		return; // This package doesn't really exist - exit the function.
580
	}
581

    
582
	if (!is_array($config['installedpackages']['package'][$pkg_id])) {
583
		return;	 // No package belongs to the pkg_id passed to this function.
584
	}
585

    
586
	$package =& $config['installedpackages']['package'][$pkg_id];
587
	if (!file_exists("/usr/local/pkg/" . $package['configurationfile'])) {
588
		log_error(sprintf(gettext("The %s package is missing its configuration file and must be reinstalled."), $package['name']));
589
		delete_package_xml($package['name']);
590
		return;
591
	}
592

    
593
	$pkg_config = parse_xml_config_pkg("/usr/local/pkg/" . $package['configurationfile'], "packagegui");
594
	if (isset($pkg_config['nosync'])) {
595
		return;
596
	}
597

    
598
	/* Bring in package include files */
599
	if (!empty($pkg_config['include_file'])) {
600
		$include_file = $pkg_config['include_file'];
601
		if (file_exists($include_file)) {
602
			require_once($include_file);
603
		} else {
604
			log_error(sprintf(gettext('Reinstalling package %1$s because its include file(%2$s) is missing!'), $package['name'], $include_file));
605
			uninstall_package($package['name']);
606
			if (install_package($package['name']) != 0) {
607
				log_error(sprintf(gettext("Reinstalling package %s failed. Take appropriate measures!!!"), $package['name']));
608
				return;
609
			}
610
			if (file_exists($include_file)) {
611
				require_once($include_file);
612
			} else {
613
				return;
614
			}
615
		}
616
	}
617

    
618
	if (!empty($pkg_config['custom_php_global_functions'])) {
619
		eval($pkg_config['custom_php_global_functions']);
620
	}
621
	if (!empty($pkg_config['custom_php_resync_config_command'])) {
622
		eval($pkg_config['custom_php_resync_config_command']);
623
	}
624
}
625

    
626
/* Read info.xml installed by package and return an array */
627
function read_package_config($package_name) {
628
	global $g;
629

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

    
632
	if (!file_exists($pkg_info_xml)) {
633
		return false;
634
	}
635

    
636
	$pkg_info = parse_xml_config_pkg($pkg_info_xml, 'pfsensepkgs');
637

    
638
	if (empty($pkg_info)) {
639
		return false;
640
	}
641

    
642
	/* it always returns an array with 1 item */
643
	return $pkg_info['package'][0];
644
}
645

    
646
/* Read package configurationfile and return an array */
647
function read_package_configurationfile($package_name) {
648
	global $config, $g;
649

    
650
	$pkg_config = array();
651
	$id = get_package_id($package_name);
652

    
653
	if ($id < 0 || !isset($config['installedpackages']['package'][$id]['configurationfile'])) {
654
		return $pkg_config;
655
	}
656

    
657
	$pkg_configurationfile = $config['installedpackages']['package'][$id]['configurationfile'];
658

    
659
	if (empty($pkg_configurationfile) || !file_exists('/usr/local/pkg/' . $pkg_configurationfile)) {
660
		return $pkg_config;
661
	}
662

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

    
665
	return $pkg_config;
666
}
667

    
668
function get_after_install_info($package_name) {
669
	$pkg_config = read_package_config($package_name);
670

    
671
	if (isset($pkg_config['after_install_info'])) {
672
		return $pkg_config['after_install_info'];
673
	}
674

    
675
	return '';
676
}
677

    
678
function eval_once($toeval) {
679
	global $evaled;
680
	if (!$evaled) {
681
		$evaled = array();
682
	}
683
	$evalmd5 = md5($toeval);
684
	if (!in_array($evalmd5, $evaled)) {
685
		@eval($toeval);
686
		$evaled[] = $evalmd5;
687
	}
688
	return;
689
}
690

    
691
function install_package_xml($package_name) {
692
	global $g, $config, $pkg_interface;
693

    
694
	if (($pkg_info = read_package_config($package_name)) == false) {
695
		return false;
696
	}
697

    
698
	/* safe side. Write config below will send to ro again. */
699

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

    
703
	/* add package information to config.xml */
704
	$pkgid = get_package_id($pkg_info['name']);
705
	update_status(gettext("Saving updated package information...") . "\n");
706
	if ($pkgid == -1) {
707
		$config['installedpackages']['package'][] = $pkg_info;
708
		$changedesc = sprintf(gettext("Installed %s package."), $pkg_info['name']);
709
		$to_output = gettext("done.") . "\n";
710
	} else {
711
		$config['installedpackages']['package'][$pkgid] = $pkg_info;
712
		$changedesc = sprintf(gettext("Overwrote previous installation of %s."), $pkg_info['name']);
713
		$to_output = gettext("overwrite!") . "\n";
714
	}
715
	unlink_if_exists('/conf/needs_package_sync');
716
	write_config(sprintf(gettext("Intermediate config write during package install for %s."), $pkg_info['name']));
717
	update_status($to_output);
718

    
719
	if (($pkgid = get_package_id($package_name)) == -1) {
720
		update_status(sprintf(gettext('The %1$s package is not installed.%2$sInstallation aborted.'), $package_name, "\n\n"));
721

    
722
		uninstall_package($package_name);
723
		write_config($changedesc);
724
		log_error(sprintf(gettext("Failed to install package: %s."), $pkg_info['name']));
725
		update_status(gettext("Failed to install package.") . "\n");
726
		return false;
727
	}
728

    
729
	if (file_exists("/usr/local/pkg/" . $pkg_info['configurationfile'])) {
730
		update_status(gettext("Loading package configuration... "));
731
		$pkg_config = parse_xml_config_pkg("/usr/local/pkg/" . $pkg_info['configurationfile'], "packagegui");
732
		update_status(gettext("done.") . "\n");
733
		update_status(gettext("Configuring package components...") . "\n");
734
		if (!empty($pkg_config['filter_rules_needed'])) {
735
			$config['installedpackages']['package'][$pkgid]['filter_rule_function'] = $pkg_config['filter_rules_needed'];
736
		}
737
		/* modify system files */
738

    
739
		/* if a require exists, include it.  this will
740
		 * show us where an error exists in a package
741
		 * instead of making us blindly guess
742
		 */
743
		$missing_include = false;
744
		if ($pkg_config['include_file'] <> "") {
745
			update_status(gettext("Loading package instructions...") . "\n");
746
			if (file_exists($pkg_config['include_file'])) {
747
				pkg_debug("require_once('{$pkg_config['include_file']}')\n");
748
				require_once($pkg_config['include_file']);
749
			} else {
750
				pkg_debug("Missing include {$pkg_config['include_file']}\n");
751
				$missing_include = true;
752
				update_status(sprintf(gettext("Include %s is missing!"), basename($pkg_config['include_file'])) . "\n");
753

    
754
				uninstall_package($package_name);
755
				write_config($changedesc);
756
				log_error(sprintf(gettext("Failed to install package: %s."), $pkg_info['name']));
757
				update_status(gettext("Failed to install package.") . "\n");
758
				return false;
759
			}
760
		}
761

    
762
		/* custom commands */
763
		update_status(gettext("Custom commands...") . "\n");
764
		if ($missing_include == false) {
765
			if ($pkg_config['custom_php_global_functions'] <> "") {
766
				update_status(gettext("Executing custom_php_global_functions()..."));
767
				eval_once($pkg_config['custom_php_global_functions']);
768
				update_status(gettext("done.") . "\n");
769
			}
770
			if ($pkg_config['custom_php_install_command']) {
771
				update_status(gettext("Executing custom_php_install_command()..."));
772
				eval_once($pkg_config['custom_php_install_command']);
773
				update_status(gettext("done.") . "\n");
774
			}
775
			if ($pkg_config['custom_php_resync_config_command'] <> "") {
776
				update_status(gettext("Executing custom_php_resync_config_command()..."));
777
				eval_once($pkg_config['custom_php_resync_config_command']);
778
				update_status(gettext("done.") . "\n");
779
			}
780
		}
781
		/* sidebar items */
782
		if (is_array($pkg_config['menu'])) {
783
			update_status(gettext("Menu items... "));
784
			foreach ($pkg_config['menu'] as $menu) {
785
				if (is_array($config['installedpackages']['menu'])) {
786
					foreach ($config['installedpackages']['menu'] as $amenu) {
787
						if ($amenu['name'] == $menu['name']) {
788
							continue 2;
789
						}
790
					}
791
				} else {
792
					$config['installedpackages']['menu'] = array();
793
				}
794
				$config['installedpackages']['menu'][] = $menu;
795
			}
796
			update_status(gettext("done.") . "\n");
797
		}
798
		/* services */
799
		if (is_array($pkg_config['service'])) {
800
			update_status(gettext("Services... "));
801
			foreach ($pkg_config['service'] as $service) {
802
				if (is_array($config['installedpackages']['service'])) {
803
					foreach ($config['installedpackages']['service'] as $aservice) {
804
						if ($aservice['name'] == $service['name']) {
805
							continue 2;
806
						}
807
					}
808
				} else {
809
					$config['installedpackages']['service'] = array();
810
				}
811
				$config['installedpackages']['service'][] = $service;
812
			}
813
			update_status(gettext("done.") . "\n");
814
		}
815
	} else {
816
		pkg_debug("Unable to find config file\n");
817
		update_status(gettext("Loading package configuration... failed!") . "\n\n" . gettext("Installation aborted."));
818
		pkg_debug(gettext("Unable to load package configuration. Installation aborted.") ."\n");
819

    
820
		uninstall_package($package_name);
821
		write_config($changedesc);
822
		log_error(sprintf(gettext("Failed to install package: %s."), $pkg_info['name']));
823
		update_status(gettext("Failed to install package.") . "\n");
824
		return false;
825
	}
826

    
827
	/* set up package logging streams */
828
	if ($pkg_info['logging']) {
829
		system_syslogd_start();
830
	}
831

    
832
	update_status(gettext("Writing configuration... "));
833
	write_config($changedesc);
834
	log_error(sprintf(gettext("Successfully installed package: %s."), $pkg_info['name']));
835
	update_status(gettext("done.") . "\n");
836
	if ($pkg_info['after_install_info']) {
837
		update_status($pkg_info['after_install_info']);
838
	}
839

    
840
	return true;
841
}
842

    
843
function delete_package_xml($package_name, $when = "post-deinstall") {
844
	global $g, $config, $pkg_interface;
845

    
846

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

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

    
960
	if ($when == "post-deinstall") {
961
		/* remove config.xml entries */
962
		update_status(gettext("Configuration... "));
963
		unset($config['installedpackages']['package'][$pkgid]);
964
		update_status(gettext("done.") . "\n");
965
		write_config(sprintf(gettext("Removed %s package."), $package_name));
966
	}
967

    
968
	/* remove package entry from /etc/syslog.conf if needed */
969
	/* this must be done after removing the entries from config.xml */
970
	if ($need_syslog_restart) {
971
		system_syslogd_start();
972
	}
973

    
974
}
975

    
976
/*
977
 * Used during upgrade process or retore backup process, verify all
978
 * packages installed in config.xml and install pkg accordingly
979
 */
980
function package_reinstall_all() {
981
	global $g, $config, $pkg_interface;
982

    
983
	$upgrade = (file_exists('/conf/needs_package_sync') && platform_booting());
984

    
985
	if ((!isset($config['installedpackages']['package']) ||
986
	    !is_array($config['installedpackages']['package'])) && !$upgrade) {
987
		return true;
988
	}
989

    
990
	/* During boot after upgrade, wait for internet connection */
991
	if ($upgrade) {
992
		update_status(gettext("Waiting for Internet connection to update pkg metadata and finish package reinstallation"));
993
		$ntries = 3;
994
		while ($ntries > 0) {
995
			if (pkg_update(true)) {
996
				break;
997
			}
998
			update_status('.');
999
			sleep(1);
1000
			$ntries--;
1001
		}
1002
		update_status("\n");
1003

    
1004
		if ($ntries == 0) {
1005
			file_notice(gettext("Package reinstall"),
1006
			    gettext("Package reinstall process was ABORTED due to lack of internet connectivity"));
1007
			return false;
1008
		}
1009
	}
1010

    
1011
	$pkg_info = get_pkg_info();
1012

    
1013
	if ($upgrade &&
1014
	    file_exists("{$g['cf_conf_path']}/packages_to_reinstall_after_upgrade.txt")) {
1015
		$package_list = file("{$g['cf_conf_path']}/packages_to_reinstall_after_upgrade.txt",
1016
		    FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
1017
		unlink_if_exists("{$g['cf_conf_path']}/packages_to_reinstall_after_upgrade.txt");
1018
	} else {
1019
		if (!isset($config['installedpackages']['package']) || !is_array($config['installedpackages']['package'])) {
1020
			return true;
1021
		}
1022
		$package_list = array();
1023
		foreach ($config['installedpackages']['package'] as $package) {
1024
			$package_list[] = get_package_internal_name($package);
1025
		}
1026
	}
1027

    
1028
	foreach ($package_list as $package) {
1029
		$found = false;
1030
		foreach ($pkg_info as $pkg) {
1031
			pkg_remove_prefix($pkg['name']);
1032
			if ($pkg['name'] == $package) {
1033
				pkg_install($g['pkg_prefix'] . $package, true);
1034
				$found = true;
1035
				break;
1036
			}
1037
		}
1038

    
1039
		if (!$found) {
1040
			if (!function_exists("file_notice")) {
1041
				require_once("notices.inc");
1042
			}
1043

    
1044
			file_notice(gettext("Package reinstall"),
1045
			    sprintf(gettext("Package %s does not exist in current %s version and it has been removed."),
1046
			    $package, $g['product_name']));
1047
			uninstall_package($package);
1048
		}
1049
	}
1050

    
1051
	return true;
1052
}
1053

    
1054
function stop_packages() {
1055
	require_once("config.inc");
1056
	require_once("functions.inc");
1057
	require_once("filter.inc");
1058
	require_once("shaper.inc");
1059
	require_once("captiveportal.inc");
1060
	require_once("pkg-utils.inc");
1061
	require_once("pfsense-utils.inc");
1062
	require_once("service-utils.inc");
1063

    
1064
	global $config, $g;
1065

    
1066
	log_error(gettext("Stopping all packages."));
1067

    
1068
	$rcfiles = glob(RCFILEPREFIX . "*.sh");
1069
	if (!$rcfiles) {
1070
		$rcfiles = array();
1071
	} else {
1072
		$rcfiles = array_flip($rcfiles);
1073
		if (!$rcfiles) {
1074
			$rcfiles = array();
1075
		}
1076
	}
1077

    
1078
	if (is_array($config['installedpackages']['package'])) {
1079
		foreach ($config['installedpackages']['package'] as $package) {
1080
			echo " Stopping package {$package['name']}...";
1081
			$internal_name = get_package_internal_name($package);
1082
			stop_service($internal_name);
1083
			unset($rcfiles[RCFILEPREFIX . strtolower($internal_name) . ".sh"]);
1084
			echo "done.\n";
1085
		}
1086
	}
1087

    
1088
	foreach ($rcfiles as $rcfile => $number) {
1089
		$shell = @popen("/bin/sh", "w");
1090
		if ($shell) {
1091
			echo " Stopping {$rcfile}...";
1092
			if (!@fwrite($shell, "{$rcfile} stop >>/tmp/bootup_messages 2>&1")) {
1093
				if ($shell) {
1094
					pclose($shell);
1095
				}
1096
				$shell = @popen("/bin/sh", "w");
1097
			}
1098
			echo "done.\n";
1099
			pclose($shell);
1100
		}
1101
	}
1102
}
1103

    
1104
/* Identify which meta package is installed */
1105
function get_meta_pkg_name() {
1106
	global $g;
1107

    
1108
	/* XXX: Use pkg annotation */
1109
	if (is_pkg_installed($g['product_name'])) {
1110
		return $g['product_name'];
1111
	} else if (is_pkg_installed($g['product_name'] . '-vmware')) {
1112
		return $g['product_name'] . '-vmware';
1113
	}
1114
	return false;
1115
}
1116

    
1117
/* Identify which base package is installed */
1118
function get_base_pkg_name() {
1119
	global $g;
1120

    
1121
	/* XXX: Use pkg annotation */
1122
	if (is_pkg_installed($g['product_name'] . '-base-' . $g['platform'])) {
1123
		return $g['product_name'] . '-base-' . $g['platform'];
1124
	} else if (is_pkg_installed($g['product_name'] . '-base')) {
1125
		return $g['product_name'] . '-base';
1126
	}
1127
	return false;
1128
}
1129

    
1130
/* Verify if system needs upgrade (meta package or base) */
1131
function get_system_pkg_version($baseonly = false) {
1132
	global $g;
1133

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

    
1137
	/* pfSense-upgrade returns 2 when there is a new version */
1138
	if ($rc == 2) {
1139
		$new_version = explode(' ', $output)[0];
1140
	}
1141

    
1142
	$base_pkg = get_base_pkg_name();
1143
	$meta_pkg = get_meta_pkg_name();
1144

    
1145
	if (!$base_pkg || !$meta_pkg) {
1146
		return false;
1147
	}
1148

    
1149
	$info = get_pkg_info($base_pkg, true);
1150

    
1151
	$pkg_info = array();
1152
	foreach ($info as $item) {
1153
		if ($item['name'] == $base_pkg) {
1154
			$pkg_info = $item;
1155
			break;
1156
		}
1157
	}
1158

    
1159
	if (empty($pkg_info) || (!$baseonly && ($pkg_info['version'] ==
1160
	    $pkg_info['installed_version']))) {
1161
		$info = get_pkg_info($meta_pkg, true);
1162

    
1163
		foreach ($info as $item) {
1164
			if ($item['name'] == $meta_pkg) {
1165
				$pkg_info = $item;
1166
				break;
1167
			}
1168
		}
1169
	}
1170

    
1171
	if (empty($pkg_info)) {
1172
		return false;
1173
	}
1174

    
1175
	return array(
1176
	    'version'           => $new_version ?: $pkg_info['version'],
1177
	    'installed_version' => $pkg_info['installed_version']
1178
	);
1179
}
1180

    
1181
/* List available repos */
1182
function pkg_list_repos() {
1183
	global $g;
1184

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

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

    
1189
	$default = array(
1190
	    'name' => 'Default',
1191
	    'path' => $path . "/{$g['product_name']}-repo.conf",
1192
	    'descr' => $default_descr
1193
	);
1194

    
1195
	$result = array($default);
1196

    
1197
	$conf_files = glob("{$path}/{$g['product_name']}-repo-*.conf");
1198
	foreach ($conf_files as $conf_file) {
1199
		$descr_file = preg_replace('/.conf$/', '.descr', $conf_file);
1200
		if (file_exists($descr_file)) {
1201
			$descr_content = file($descr_file);
1202
			$descr = chop($descr_content[0]);
1203
		} else {
1204
			$descr = 'Unknown';
1205
		}
1206
		if (!preg_match('/-repo-(.*).conf/', $conf_file, $matches)) {
1207
			continue;
1208
		}
1209
		$entry = array(
1210
		    'name' => ucfirst(strtolower($matches[1])),
1211
		    'path' => $conf_file,
1212
		    'descr' => $descr
1213
		);
1214
		$result[] = $entry;
1215
	}
1216

    
1217
	return $result;
1218
}
1219

    
1220
/* Switch between stable and devel repos */
1221
function pkg_switch_repo($path) {
1222
	global $g;
1223

    
1224
	safe_mkdir("/usr/local/etc/pkg/repos");
1225
	@unlink("/usr/local/etc/pkg/repos/{$g['product_name']}.conf");
1226
	@symlink($path, "/usr/local/etc/pkg/repos/{$g['product_name']}.conf");
1227

    
1228
	$abi_file = str_replace('.conf', '.abi', $path);
1229
	$altabi_file = str_replace('.conf', '.altabi', $path);
1230

    
1231
	if (file_exists($abi_file) && file_exists($altabi_file)) {
1232
		$abi = file_get_contents($abi_file);
1233
		$altabi = file_get_contents($altabi_file);
1234

    
1235
		$pkg_conf = array(
1236
			"ABI={$abi}",
1237
			"ALTABI={$altabi}"
1238
		);
1239

    
1240
		file_put_contents("/usr/local/etc/pkg.conf", $pkg_conf);
1241
	}
1242

    
1243
	return pkg_update(true);
1244
}
1245

    
1246
?>
(31-31/51)