Project

General

Profile

Download (42.5 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-2013 BSD Perimeter
8
 * Copyright (c) 2013-2016 Electric Sheep Fencing
9
 * Copyright (c) 2014-2025 Rubicon Communications, LLC (Netgate)
10
 * All rights reserved.
11
 *
12
 * Licensed under the Apache License, Version 2.0 (the "License");
13
 * you may not use this file except in compliance with the License.
14
 * You may obtain a copy of the License at
15
 *
16
 * http://www.apache.org/licenses/LICENSE-2.0
17
 *
18
 * Unless required by applicable law or agreed to in writing, software
19
 * distributed under the License is distributed on an "AS IS" BASIS,
20
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21
 * See the License for the specific language governing permissions and
22
 * limitations under the License.
23
 */
24

    
25
require_once("globals.inc");
26
require_once("service-utils.inc");
27
require_once("/usr/local/www/includes/functions.inc.php");
28
require_once("xmlparse.inc");
29
require_once("pfsense-utils.inc");
30

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

    
36
		if (!$debug) {
37
			return;
38
		}
39

    
40
		if (!$fd_log) {
41
			$fd_log = fopen("{$g['tmp_path']}/pkg_mgr_debug.log",
42
			    "w");
43
		}
44

    
45
		if (!$fd_log) {
46
			update_status(gettext("Warning, could not open log " .
47
			    "for writing.") . "\n");
48
			return;
49
		}
50
		@fwrite($fd_log, $msg);
51
	}
52
}
53

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

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

    
62
/* Remove pkg_prefix or flavor_prefix from package name if it's present */
63
function pkg_remove_prefix(&$pkg_name) {
64
	$pres = [g_get('pkg_prefix')];
65
	if (!is_null(g_get('flavor_prefix'))) {
66
		$pres[] = g_get('flavor_prefix');
67
	}
68
	foreach ( $pres as $pre) {
69
		if (str_starts_with($pkg_name, $pre)) {
70
			$pkg_name = substr($pkg_name, strlen($pre));
71
			break;
72
		}
73
	}
74
}
75

    
76
/* Execute pkg update when it's necessary */
77
function pkg_update($force = false) {
78
	global $g;
79

    
80
	return pkg_call("update" . ($force ? " -f" : ""));
81
}
82

    
83
/* return an array with necessary environment vars for pkg */
84
function pkg_env($extra_env = array()) {
85
	global $g;
86

    
87
	$user_agent = g_get('product_label') . '/' . g_get('product_version');
88
	if (!config_path_enabled('system','do_not_send_uniqueid')) {
89
		$user_agent .= ':' . system_get_uniqueid();
90
	}
91

    
92
	$pkg_env_vars = array(
93
		"LANG" => "C",
94
		"HTTP_USER_AGENT" => $user_agent,
95
		"ASSUME_ALWAYS_YES" => "true",
96
		"FETCH_TIMEOUT" => 5,
97
		"FETCH_RETRY" => 2
98
	);
99

    
100
	$http_proxy = config_get_path('system/proxyurl');
101
	$http_proxyport = config_get_path('system/proxyport');
102
	if (!empty($http_proxy)) {
103
		if (!empty($http_proxyport)) {
104
			$http_proxy .= ':' . $http_proxyport;
105
		}
106
		$pkg_env_vars['HTTP_PROXY'] = $http_proxy;
107

    
108
		$proxyuser = config_get_path('system/proxyuser');
109
		$proxypass = config_get_path('system/proxypass');
110
		if (!empty($proxyuser) && !empty($proxypass)) {
111
			$pkg_env_vars['HTTP_PROXY_AUTH'] = $proxyuser . ":" . $proxypass;
112
		}
113
	}
114

    
115
#	if (config_path_enabled('system','use_mfs_tmpvar') &&
116
#	    !file_exists("/conf/ram_disks_failed")) {
117
#		$pkg_env_vars['PKG_DBDIR'] = '/root/var/db/pkg';
118
#		$pkg_env_vars['PKG_CACHEDIR'] = '/root/var/cache/pkg';
119
#	}
120

    
121
	foreach ($extra_env as $key => $value) {
122
		$pkg_env_vars[$key] = $value;
123
	}
124

    
125
	return $pkg_env_vars;
126
}
127

    
128
/* Execute a pkg call */
129
function pkg_call($params, $mute = false, $extra_env = array()) {
130
	if (empty($params)) {
131
		return false;
132
	}
133

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

    
139
	pkg_debug("pkg_call(): {$params}\n");
140
	$process = proc_open("/usr/local/sbin/pkg-static {$params}",
141
	    $descriptorspec, $pipes, '/', pkg_env($extra_env));
142

    
143
	if (!is_resource($process)) {
144
		return false;
145
	}
146

    
147
	stream_set_blocking($pipes[1], 0);
148
	stream_set_blocking($pipes[2], 0);
149

    
150
	/* XXX: should be a tunable? */
151
	$timeout = 60; // seconds
152
	$error_log = '';
153

    
154
	do {
155
		$write = array();
156
		$read = array($pipes[1], $pipes[2]);
157
		$except = array();
158

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

    
177
		$status = proc_get_status($process);
178
	} while ($status['running']);
179

    
180
	fclose($pipes[1]);
181
	fclose($pipes[2]);
182
	proc_close($process);
183

    
184

    
185
	$rc = $status['exitcode'];
186

    
187
	pkg_debug("pkg_call(): rc = {$rc}\n");
188
	if ($rc == 0) {
189
		return true;
190
	}
191

    
192
	pkg_debug("pkg_call(): error_log\n{$error_log}\n");
193
	if (!$mute) {
194
		update_status("\n\n" . sprintf(gettext("ERROR!!! An error " .
195
		    "occurred on pkg execution (rc = %d) with parameters " .
196
		    "'%s':"), $rc, $params) . "\n" . $error_log . "\n");
197
	}
198

    
199
	return false;
200
}
201

    
202
/* Execute pkg with $params, fill stdout and stderr and return pkg rc */
203
function pkg_exec($params, &$stdout, &$stderr, $extra_env = array()) {
204
	if (empty($params)) {
205
		return -1;
206
	}
207

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

    
213

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

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

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

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

    
234

    
235
	return proc_close($process);
236
}
237

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

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

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

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

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

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

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

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

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

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

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

    
289
	return $result;
290
}
291

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

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

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

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

    
312
/* Find package array index */
313
function get_package_id($package_name) {
314
	foreach (config_get_path('installedpackages/package', []) as $idx => $pkg) {
315
		if ($pkg['name'] == $package_name ||
316
		    get_package_internal_name($pkg) == $package_name) {
317
			return $idx;
318
		}
319
	}
320

    
321
	return -1;
322
}
323

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

    
335
// Get information about packages.
336
function get_pkg_info($pkgs = 'all', $remote_repo_usage_disabled = false,
337
    $installed_pkgs_only = false) {
338
	global $g, $input_errors;
339

    
340
	$out = $err = $extra_param = '';
341
	$rc = 0;
342

    
343
	unset($pkg_filter);
344

    
345
	if (is_array($pkgs)) {
346
		$pkg_filter = $pkgs;
347
		$pkgs = g_get('pkg_prefix') . '*';
348
	} elseif ($pkgs == 'all') {
349
		$pkgs = g_get('pkg_prefix') . '*';
350
	}
351

    
352
	$base_packages = (substr($pkgs, 0, strlen(g_get('pkg_prefix'))) !=
353
	    g_get('pkg_prefix'));
354

    
355
	if ($installed_pkgs_only && !is_pkg_installed($pkgs)) {
356
		/*
357
		 * Return early if the caller wants just installed packages
358
		 * and there are none.  Saves doing any calls that might
359
		 * access a remote package repo.
360
		 */
361
		return array();
362
	}
363

    
364
	if (!function_exists('is_subsystem_dirty')) {
365
		require_once("util.inc");
366
	}
367

    
368
	/* Do not run remote operations if pkg has a lock */
369
	if (is_subsystem_dirty('pkg')) {
370
		$remote_repo_usage_disabled = true;
371
		$lock = false;
372
	} else {
373
		$lock = true;
374
	}
375

    
376
	if ($lock) {
377
		mark_subsystem_dirty('pkg');
378
	}
379

    
380
	if ($remote_repo_usage_disabled) {
381
		$extra_param = "-U ";
382
	}
383

    
384
	$did_search = false;
385
	$search_rc = 0;
386
	$info_rc = 0;
387
	$search_items = array();
388
	$info_items = array();
389

    
390
	if ($base_packages) {
391
		$repo_param = "";
392
	} else {
393
		$repo_param = "-r {$g['product_name']}";
394
	}
395

    
396
	/*
397
	 * If we want more than just the currently installed packages or
398
	 * we want up-to-date remote repo info then do a full pkg search
399
	 */
400
	if (!$installed_pkgs_only || !$remote_repo_usage_disabled) {
401
		$did_search = true;
402
		/* Update the repository access credentials. */
403
		mwexec("/usr/local/sbin/{$g['product_name']}-repo-setup");
404
		$search_rc = pkg_exec("search {$repo_param} " .
405
		    "{$extra_param}-R --raw-format json-compact " .
406
		    $pkgs, $search_out, $search_err);
407
		if ($search_rc == 0) {
408
			$search_items = explode("\n", chop($search_out));
409
			array_walk($search_items, function(&$v, &$k) {
410
				$v = json_decode($v, true);
411
			});
412
		}
413
	}
414

    
415
	/*
416
	 * We always should look for local items to detect packages that
417
	 * were removed from remote repo but are already installed locally
418
	 *
419
	 * Take pkg search return code into consideration to fallback to local
420
	 * information when remote repo is not accessible
421
	 */
422
	if (is_pkg_installed($pkgs) || $search_rc != 0) {
423
		$info_rc = pkg_exec("info -R --raw-format json-compact " .
424
		    $pkgs, $info_out, $info_err);
425
		if ($info_rc == 0) {
426
			$info_items = explode("\n", chop($info_out));
427
			array_walk($info_items, function(&$v, &$k) {
428
				$v = json_decode($v, true);
429
			});
430
		}
431
	}
432

    
433
	if ($lock) {
434
		clear_subsystem_dirty('pkg');
435
	}
436

    
437
	if ($search_rc != 0 && $info_rc != 0) {
438
		update_status("\n" . gettext(
439
		    "ERROR: Error trying to get packages list. Aborting...")
440
		    . "\n");
441
		update_status($search_err . "\n" . $info_err);
442
		$input_errors[] = gettext(
443
		    "ERROR: Error trying to get packages list. Aborting...") .
444
		    "\n";
445
		$input_errors[] = $search_err . "\n" . $info_err;
446
		return array();
447
	}
448

    
449
	/* It was not possible to search, use local information only */
450
	if ($search_rc != 0 || !$did_search) {
451
		$search_items = $info_items;
452
	} else {
453
		foreach ($info_items as $pkg_info) {
454
			if (empty($pkg_info['name'])) {
455
				continue;
456
			}
457

    
458
			if (array_search($pkg_info['name'], array_column(
459
			    $search_items, 'name')) === FALSE) {
460
				$pkg_info['obsolete'] = true;
461
				$search_items[] = $pkg_info;
462
			}
463
		}
464
	}
465

    
466
	$result = array();
467
	foreach ($search_items as $pkg_info) {
468
		if (empty($pkg_info['name'])) {
469
			continue;
470
		}
471

    
472
		if (isset($pkg_filter) && !in_array($pkg_info['name'],
473
		    $pkg_filter)) {
474
			continue;
475
		}
476

    
477
		$pkg_info['shortname'] = $pkg_info['name'];
478
		pkg_remove_prefix($pkg_info['shortname']);
479

    
480
		/* XXX: Add it to globals.inc? */
481
		$pkg_info['changeloglink'] =
482
		    "https://github.com/pfsense/FreeBSD-ports/commits/devel/" .
483
		    $pkg_info['categories'][0] . '/' . $pkg_info['name'];
484

    
485
		$pkg_is_installed = false;
486

    
487
		if (is_pkg_installed($pkg_info['name'])) {
488
			$rc = pkg_exec("query %R {$pkg_info['name']}", $out,
489
			    $err);
490
			if (!$base_packages &&
491
			    rtrim($out) != g_get('product_name')) {
492
				continue;
493
			}
494

    
495
			$pkg_info['installed'] = true;
496
			$pkg_is_installed = true;
497

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

    
501
			if ($rc != 0) {
502
				update_status("\n" . gettext("ERROR: Error " .
503
				    "trying to get package version. " .
504
				    "Aborting...") . "\n");
505
				update_status($err);
506
				$input_errors[] = gettext("ERROR: Error " .
507
				    "trying to get package version. " .
508
				    "Aborting...") . "\n";
509
				$input_errors[] = $err;
510
				return array();
511
			}
512

    
513
			$pkg_info['installed_version'] = str_replace("\n", "",
514
			    $out);
515

    
516
			/*
517
			 * We used pkg info to collect pkg data so remote
518
			 * version is not available. Lets try to collect it
519
			 * using rquery if possible
520
			 */
521
			if ($search_rc != 0 || !$did_search) {
522
				$rc = pkg_exec(
523
				    "rquery -U %v {$pkg_info['name']}", $out,
524
				    $err);
525

    
526
				if ($rc == 0) {
527
					/*
528
					 * just consider the first line of output from rquery
529
					 * ref: https://github.com/freebsd/pkg/issues/2164
530
					 */
531
					$pkg_info['version'] = explode("\n", $out)[0];
532
				}
533
			}
534

    
535
		} else if (is_package_installed($pkg_info['shortname'])) {
536
			$pkg_info['broken'] = true;
537
			$pkg_is_installed = true;
538
		}
539

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

    
543
		if (!$installed_pkgs_only || $pkg_is_installed) {
544
			$result[] = $pkg_info;
545
		}
546
		unset($pkg_info);
547
	}
548

    
549
	/* Sort result alphabetically */
550
	usort($result, function($a, $b) {
551
		return(strcasecmp ($a['name'], $b['name']));
552
	});
553

    
554
	return $result;
555
}
556

    
557
/*
558
 * If binary pkg is installed but post-install tasks were not
559
 * executed yet, do it now.
560
 * This scenario can happen when a pkg is pre-installed during
561
 * build phase, and at this point, cannot find a running system
562
 * to register itself in config.xml and also execute custom
563
 * install functions
564
 */
565
function register_all_installed_packages(bool $force = false) {
566
	$pkg_info = get_pkg_info('all', true, true);
567

    
568
	foreach ($pkg_info as $pkg) {
569
		pkg_remove_prefix($pkg['name']);
570

    
571
		if (!$force && is_package_installed($pkg['name'])) {
572
			continue;
573
		}
574

    
575
		update_status(sprintf(gettext(
576
		    "Running last steps of %s installation.") . "\n",
577
		    $pkg['name']));
578
		install_package_xml($pkg['name']);
579
	}
580
}
581

    
582
/*
583
 * resync_all_package_configs() Force packages to setup their configuration
584
 * and rc.d files.  This function may also print output to the terminal
585
 * indicating progress.
586
 */
587
function resync_all_package_configs($show_message = false) {
588
	log_error(gettext("Resyncing configuration for all packages."));
589

    
590
	if ($show_message == true) {
591
		echo "Syncing packages:";
592
	}
593

    
594
	foreach (config_get_path('installedpackages/package', []) as $idx => $package) {
595
		if (empty($package['name'])) {
596
			continue;
597
		}
598
		if ($show_message == true) {
599
			echo " " . $package['name'];
600
		}
601
		if (!is_platform_booting()) {
602
			stop_service(get_package_internal_name($package));
603
		}
604
		sync_package($package['name']);
605
		update_status(gettext("Syncing packages...") . "\n");
606
	}
607

    
608
	if ($show_message == true) {
609
		echo " done.\n";
610
	}
611
}
612

    
613
function uninstall_package($package_name) {
614
	global $g;
615

    
616
	$internal_name = $package_name;
617
	$id = get_package_id($package_name);
618
	if ($id >= 0) {
619
		$internal_name = get_package_internal_name(
620
		    config_get_path("installedpackages/package/{$id}"));
621
		stop_service($internal_name);
622
	}
623
	$pkg_name = g_get('pkg_prefix') . $internal_name;
624

    
625
	if (is_pkg_installed($pkg_name)) {
626
		update_status(gettext("Removing package...") . "\n");
627
		pkg_delete($pkg_name);
628
	} else {
629
		delete_package_xml($package_name);
630
	}
631

    
632
	update_status(gettext("done.") . "\n");
633
}
634

    
635
function reinstall_package($package_name) {
636
	global $g;
637

    
638
	$internal_name = $package_name;
639
	$id = get_package_id($package_name);
640
	if ($id >= 0) {
641
		$internal_name = get_package_internal_name(
642
			config_get_path("installedpackages/package/{$id}"));
643
	}
644
	$pkg_name = g_get('pkg_prefix') . $internal_name;
645
	pkg_install($pkg_name);
646
}
647

    
648
/* Run <custom_php_resync_config_command> */
649
function sync_package($package_name) {
650
	global $builder_package_install;
651

    
652
	// If this code is being called by pfspkg_installer
653
	// which the builder system uses then return (ignore).
654
	if ($builder_package_install) {
655
		return;
656
	}
657

    
658
	if (empty(config_get_path('installedpackages/package', []))) {
659
		return;
660
	}
661

    
662
	if (($pkg_id = get_package_id($package_name)) == -1) {
663
		// This package doesn't really exist - exit the function.
664
		return;
665
	}
666

    
667
	$package = config_get_path("installedpackages/package/{$pkg_id}");
668
	if (empty($package)) {
669
		// No package belongs to the pkg_id passed to this function.
670
		return;
671
	}
672

    
673
	if (!file_exists("/usr/local/pkg/" . $package['configurationfile'])) {
674
		log_error(sprintf(gettext("The %s package is missing its " .
675
		    "configuration file and must be reinstalled."),
676
		    $package['name']));
677
		delete_package_xml($package['name']);
678
		return;
679
	}
680

    
681
	$pkg_config = parse_xml_config_pkg("/usr/local/pkg/" .
682
	    $package['configurationfile'], "packagegui");
683
	if (isset($pkg_config['nosync'])) {
684
		return;
685
	}
686

    
687
	/* Bring in package include files */
688
	if (!empty($pkg_config['include_file'])) {
689
		$include_file = $pkg_config['include_file'];
690
		if (file_exists($include_file)) {
691
			require_once($include_file);
692
		} else {
693
			log_error(sprintf(gettext('Reinstalling package %1$s " .
694
			    "because its include file(%2$s) is missing!'),
695
			    $package['name'], $include_file));
696
			uninstall_package($package['name']);
697
			if (reinstall_package($package['name']) != 0) {
698
				log_error(sprintf(gettext("Reinstalling " .
699
				    "package %s failed. Take appropriate " .
700
				    "measures!!!"), $package['name']));
701
				return;
702
			}
703
			if (file_exists($include_file)) {
704
				require_once($include_file);
705
			} else {
706
				return;
707
			}
708
		}
709
	}
710

    
711
	if (!empty($pkg_config['custom_php_global_functions'])) {
712
		eval($pkg_config['custom_php_global_functions']);
713
	}
714
	if (!empty($pkg_config['custom_php_resync_config_command'])) {
715
		eval($pkg_config['custom_php_resync_config_command']);
716
	}
717
}
718

    
719
/* Read info.xml installed by package and return an array */
720
function read_package_config($package_name) {
721
	global $g;
722

    
723
	$pkg_info_xml = '/usr/local/share/' . g_get('pkg_prefix') . $package_name .
724
	    '/info.xml';
725

    
726
	if (!file_exists($pkg_info_xml)) {
727
		return false;
728
	}
729

    
730
	$pkg_info = parse_xml_config_pkg($pkg_info_xml, 'pfsensepkgs');
731

    
732
	if (empty($pkg_info)) {
733
		return false;
734
	}
735

    
736
	/* it always returns an array with 1 item */
737
	return $pkg_info['package'][0];
738
}
739

    
740
/* Read package configurationfile and return an array */
741
function read_package_configurationfile($package_name) {
742
	$pkg_config = array();
743
	$id = get_package_id($package_name);
744
	$pkg_data = config_get_path("installedpackages/package/{$id}", []);
745

    
746
	if ($id < 0 || empty($pkg_data)) {
747
		return $pkg_config;
748
	}
749

    
750
	if (empty($pkg_data['configurationfile'])) {
751
		return $pkg_config;
752
	}
753

    
754
	if (!file_exists('/usr/local/pkg/' . $pkg_data['configurationfile'])) {
755
		return $pkg_config;
756
	}
757

    
758
	$pkg_config = parse_xml_config_pkg('/usr/local/pkg/' .
759
	    $pkg_data['configurationfile'], "packagegui");
760

    
761
	return $pkg_config;
762
}
763

    
764
function get_after_install_info($package_name) {
765
	$pkg_config = read_package_config($package_name);
766

    
767
	if (isset($pkg_config['after_install_info'])) {
768
		return $pkg_config['after_install_info'];
769
	}
770

    
771
	return '';
772
}
773

    
774
function eval_once($toeval) {
775
	global $evaled;
776
	if (!$evaled) {
777
		$evaled = array();
778
	}
779
	$evalmd5 = md5($toeval);
780
	if (!in_array($evalmd5, $evaled)) {
781
		@eval($toeval);
782
		$evaled[] = $evalmd5;
783
	}
784
	return;
785
}
786

    
787
function install_package_xml($package_name) {
788
	if (($pkg_info = read_package_config($package_name)) == false) {
789
		return false;
790
	}
791

    
792
	pkg_debug(gettext("Beginning package installation.") . "\n");
793
	log_error(sprintf(gettext('Beginning package installation for %s .'),
794
	    $pkg_info['name']));
795

    
796
	/* add package information to config.xml */
797
	$pkgid = get_package_id($pkg_info['name']);
798
	update_status(gettext("Saving updated package information...") . "\n");
799
	$pkgs = config_get_path('installedpackages/package', []);
800
	if ($pkgid == -1) {
801
		$pkgs[] = $pkg_info;
802
		$changedesc = sprintf(gettext("Installed %s package."),
803
		    $pkg_info['name']);
804
		$to_output = gettext("done.") . "\n";
805
	} else {
806
		$pkgs[$pkgid] = $pkg_info;
807
		$changedesc = sprintf(gettext("Overwrote previous " .
808
		    "installation of %s."), $pkg_info['name']);
809
		$to_output = gettext("overwrite!") . "\n";
810
	}
811
	config_set_path('installedpackages/package', $pkgs);
812
	write_config(sprintf(gettext("Intermediate config write during " .
813
	    "package install for %s."), $pkg_info['name']));
814
	update_status($to_output);
815

    
816
	if (($pkgid = get_package_id($package_name)) == -1) {
817
		update_status(sprintf(gettext('The %1$s package is not ' .
818
		    'installed.%2$sInstallation aborted.'), $package_name,
819
		    "\n\n"));
820

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

    
829
	if (!file_exists("/usr/local/pkg/" . $pkg_info['configurationfile'])) {
830
		pkg_debug("Unable to find config file\n");
831
		update_status(gettext("Loading package configuration... " .
832
		    "failed!") .  "\n\n" . gettext("Installation aborted."));
833
		pkg_debug(gettext("Unable to load package configuration. " .
834
		    "Installation aborted.") ."\n");
835

    
836
		uninstall_package($package_name);
837
		write_config($changedesc);
838
		log_error(sprintf(gettext("Failed to install package: %s."),
839
		    $pkg_info['name']));
840
		update_status(gettext("Failed to install package.") . "\n");
841
		return false;
842
	}
843

    
844
	update_status(gettext("Loading package configuration... "));
845
	$pkg_config = parse_xml_config_pkg("/usr/local/pkg/" .
846
	    $pkg_info['configurationfile'], "packagegui");
847
	update_status(gettext("done.") . "\n");
848
	update_status(gettext("Configuring package components...") .
849
	    "\n");
850
	if (!empty($pkg_config['filter_rules_needed'])) {
851
		config_set_path("installedpackages/package/{$pkgid}/filter_rule_function",
852
						$pkg_config['filter_rules_needed']);
853
	}
854
	/* modify system files */
855

    
856
	/* if a require exists, include it.  this will
857
	 * show us where an error exists in a package
858
	 * instead of making us blindly guess
859
	 */
860
	$missing_include = false;
861
	if ($pkg_config['include_file'] <> "") {
862
		update_status(gettext("Loading package instructions...") .
863
		    "\n");
864
		if (file_exists($pkg_config['include_file'])) {
865
			pkg_debug("require_once('" .
866
			    $pkg_config['include_file'] . "')\n");
867
			require_once($pkg_config['include_file']);
868
		} else {
869
			pkg_debug("Missing include " .
870
			    "{$pkg_config['include_file']}\n");
871
			$missing_include = true;
872
			update_status(sprintf(gettext("Include %s is missing!"),
873
			    basename($pkg_config['include_file'])) . "\n");
874

    
875
			uninstall_package($package_name);
876
			write_config($changedesc);
877
			log_error(sprintf(gettext(
878
			    "Failed to install package: %s."),
879
			    $pkg_info['name']));
880
			update_status(gettext("Failed to install package.") .
881
			    "\n");
882
			return false;
883
		}
884
	}
885

    
886
	/* custom commands */
887
	update_status(gettext("Custom commands...") . "\n");
888
	if ($missing_include == false) {
889
		if ($pkg_config['custom_php_global_functions'] <> "") {
890
			update_status(gettext(
891
			    "Executing custom_php_global_functions()..."));
892
			eval_once($pkg_config['custom_php_global_functions']);
893
			update_status(gettext("done.") . "\n");
894
		}
895
		if ($pkg_config['custom_php_install_command']) {
896
			update_status(gettext(
897
			    "Executing custom_php_install_command()..."));
898
			eval_once($pkg_config['custom_php_install_command']);
899
			update_status(gettext("done.") . "\n");
900
		}
901
		if ($pkg_config['custom_php_resync_config_command'] <> "") {
902
			update_status(gettext(
903
			    "Executing custom_php_resync_config_command()..."));
904
			eval_once(
905
			    $pkg_config['custom_php_resync_config_command']);
906
			update_status(gettext("done.") . "\n");
907
		}
908
	}
909
	/* sidebar items */
910
	if (is_array($pkg_config['menu'])) {
911
		$menus = config_get_path('installedpackages/menu', []);
912
		update_status(gettext("Menu items... "));
913
		foreach ($pkg_config['menu'] as $menu) {
914
			foreach ($menus as $amenu) {
915
				if ((trim($amenu['name']) == trim($menu['name'])) &&
916
				    ($amenu['section'] == $menu['section'])) {
917
					continue 2;
918
				}
919
			}
920
			$menus[] = $menu;
921
		}
922
		config_set_path('installedpackages/menu', $menus);
923
		update_status(gettext("done.") . "\n");
924
	}
925
	/* services */
926
	if (is_array($pkg_config['service'])) {
927
		update_status(gettext("Services... "));
928
		$services = config_get_path('installedpackages/service', []);
929
		foreach ($pkg_config['service'] as $service) {
930
			if (empty($service)) {
931
				continue;
932
			}
933
			foreach ($services as $aservice) {
934
				if (empty($aservice)) {
935
					continue;
936
				}
937
				if (trim($aservice['name']) == trim($service['name'])) {
938
					continue 2;
939
				}
940
			}
941
			$services[] = $service;
942
		}
943
		config_set_path('installedpackages/service', $services);
944
		update_status(gettext("done.") . "\n");
945
	}
946
	if (is_array($pkg_config['tabs'])) {
947
		config_set_path("installedpackages/package/{$pkgid}/tabs",$pkg_config['tabs']);
948
	}
949
	/* plugins */
950
	if (isset($pkg_config['include_file'])) {
951
		config_set_path("installedpackages/package/{$pkgid}/include_file", $pkg_config['include_file']);
952
	}
953
	if (is_array($pkg_config['plugins'])) {
954
		config_set_path("installedpackages/package/{$pkgid}/plugins", $pkg_config['plugins']);
955
	}
956

    
957
	update_status(gettext("Writing configuration... "));
958
	write_config($changedesc);
959
	log_error(sprintf(gettext("Successfully installed package: %s."),
960
	    $pkg_info['name']));
961
	update_status(gettext("done.") . "\n");
962
	if ($pkg_info['after_install_info']) {
963
		update_status($pkg_info['after_install_info']);
964
	}
965

    
966
	/* set up package logging streams */
967
	if ($pkg_info['logging']) {
968
		system_syslogd_start(true);
969
	}
970

    
971
	return true;
972
}
973

    
974
function delete_package_xml($package_name, $when = "post-deinstall") {
975
	global $g;
976

    
977

    
978
	$pkgid = get_package_id($package_name);
979
	if ($pkgid == -1) {
980
		update_status(sprintf(gettext('The %1$s package is not ' .
981
		    'installed.%2$sDeletion aborted.'), $package_name, "\n\n"));
982
		ob_flush();
983
		sleep(1);
984
		return;
985
	}
986
	pkg_debug(sprintf(gettext("Removing %s package... "), $package_name));
987
	update_status(sprintf(gettext("Removing %s components..."),
988
	    $package_name) . "\n");
989
	/* parse package configuration */
990
	$pkg_info = config_get_path("installedpackages/package/{$pkgid}",[]);
991
	$services = config_get_path('installedpackages/service', []);
992
	if (file_exists("/usr/local/pkg/" . $pkg_info['configurationfile'])) {
993
		$pkg_config = parse_xml_config_pkg("/usr/local/pkg/" .
994
		    $pkg_info['configurationfile'], "packagegui");
995
		/* remove menu items */
996
		if (is_array($pkg_config['menu'])) {
997
			$menus = config_get_path('installedpackages/menu', []);
998
			update_status(gettext("Menu items... "));
999
			foreach ($pkg_config['menu'] as $menu) {
1000
				foreach ($menus as $key => $amenu) {
1001
					if ((trim($amenu['name']) == trim($menu['name'])) &&
1002
					    ($amenu['section'] == $menu['section'])) {
1003
						unset($menus[$key]);
1004
						break;
1005
					}
1006
				}
1007
			}
1008
			config_set_path('installedpackages/menu', $menus);
1009
			update_status(gettext("done.") . "\n");
1010
		}
1011
		/* remove services */
1012
		if (is_array($pkg_config['service']) && is_array($services)) {
1013
			update_status(gettext("Services... "));
1014
			foreach ($pkg_config['service'] as $service) {
1015
				if (empty($service)) {
1016
					continue;
1017
				}
1018
				foreach ($services as $key => $instservice) {
1019
					if (empty($instservice) ||
1020
					    ($instservice['name'] !=
1021
					    $service['name'])) {
1022
						continue;
1023
					}
1024
					if (!is_platform_booting()) {
1025
						stop_service($service['name']);
1026
					}
1027
					if ($service['rcfile']) {
1028
						if (empty($service['prefix'])) {
1029
							$prefix = RCFILEPREFIX;
1030
						} else {
1031
							$prefix =
1032
							    $service['prefix'];
1033
						}
1034
						unlink_if_exists($prefix .
1035
						    $service['rcfile']);
1036
					}
1037
					config_del_path("installedpackages/service/{$key}");
1038
				}
1039
			}
1040
			update_status(gettext("done.") . "\n");
1041
		}
1042
		/*
1043
		 * XXX: Otherwise inclusion of config.inc again invalidates
1044
		 *      actions taken.
1045
		 *	Same is done during installation.
1046
		 */
1047
		write_config(sprintf(gettext("Intermediate config write " .
1048
		    "during package removal for %s."), $package_name));
1049

    
1050
		/*
1051
		 * If a require exists, include it. this will
1052
		 * show us where an error exists in a package
1053
		 * instead of making us blindly guess
1054
		 */
1055
		$missing_include = false;
1056
		if ($pkg_config['include_file'] <> "") {
1057
			update_status(gettext("Loading package instructions...")
1058
			    . "\n");
1059
			if (file_exists($pkg_config['include_file'])) {
1060
				pkg_debug("require_once(\"" .
1061
				    "{$pkg_config['include_file']}\")\n");
1062
				require_once($pkg_config['include_file']);
1063
			} else {
1064
				pkg_debug("Missing include " .
1065
				    $pkg_config['include_file'] . "\n");
1066
				$missing_include = true;
1067
				update_status(sprintf(gettext("Include file " .
1068
				    "%s could not be found for inclusion."),
1069
				    basename($pkg_config['include_file'])) .
1070
				    "\n");
1071
			}
1072
		}
1073
		/*
1074
		 * NOTE: It is not possible to handle parse errors on eval.
1075
		 * So we prevent it from being run at all to not interrupt all
1076
		 * the other code.
1077
		 */
1078
		if ($when == "deinstall" && $missing_include == false) {
1079
			/*
1080
			 * evaluate this package's global functions and pre
1081
			 * deinstall commands
1082
			 */
1083
			if ($pkg_config['custom_php_global_functions'] <> "") {
1084
				eval_once($pkg_config['custom_php_global_functions']);
1085
			}
1086
			if ($pkg_config['custom_php_pre_deinstall_command'] <> "") {
1087
				eval_once($pkg_config['custom_php_pre_deinstall_command']);
1088
			}
1089
		}
1090
		/* deinstall commands */
1091
		if ($when == "deinstall" &&
1092
		    $pkg_config['custom_php_deinstall_command'] <> "") {
1093
			update_status(gettext("Deinstall commands... "));
1094
			if ($missing_include == false) {
1095
				eval_once($pkg_config['custom_php_deinstall_command']);
1096
				update_status(gettext("done.") . "\n");
1097
			} else {
1098
				update_status("\n". gettext("Not executing " .
1099
				    "custom deinstall hook because an " .
1100
				    "include is missing.") . "\n");
1101
			}
1102
		}
1103
	}
1104
	/* syslog */
1105
	$need_syslog_restart = false;
1106
	if (is_array($pkg_info['logging']) && !empty($pkg_info['logging']['logfilename'])) {
1107
		update_status(gettext("Syslog entries... "));
1108
		/* remove package-related syslog configuration but retain log data,
1109
		 * see https://redmine.pfsense.org/issues/11846 */
1110
		@unlink_if_exists("{$g['varetc_path']}/syslog.d/" . basename($pkg_info['logging']['logfilename']) . ".conf");
1111
		update_status("done.\n");
1112
		$need_syslog_restart = true;
1113
	}
1114

    
1115
	if ($when == "post-deinstall") {
1116
		/* remove config.xml entries */
1117
		update_status(gettext("Configuration... "));
1118
		config_del_path("installedpackages/package/{$pkgid}");
1119
		update_status(gettext("done.") . "\n");
1120
		write_config(sprintf(gettext("Removed %s package."),
1121
		    $package_name));
1122
		/*
1123
		 * remove package entry from /etc/syslog.conf if needed
1124
		 * this must be done after removing the entries from config.xml
1125
		 */
1126
		if ($need_syslog_restart) {
1127
			system_syslogd_start(true);
1128
		}
1129
	}
1130
}
1131

    
1132
/*
1133
 * Used during upgrade process or restore backup process, verify all
1134
 * packages installed in config.xml and install pkg accordingly
1135
 */
1136
function package_reinstall_all() {
1137
	global $g;
1138

    
1139
	$pkgs = config_get_path('installedpackages/package');
1140
	if ($pkgs === null) {
1141
		return true;
1142
	}
1143

    
1144
	/*
1145
	 * Configure default pkg repo for current version instead of
1146
	 * using it from backup, that could be older
1147
	 */
1148
	$default_repo = pkg_get_default_repo();
1149
	$current_repo_path = "";
1150
	if (!empty(config_get_path('system/pkg_repo_conf_path'))) {
1151
		$current_repo_path = config_get_path('system/pkg_repo_conf_path');
1152
	}
1153

    
1154
	if (($current_repo_path != $default_repo['path']) && ($current_repo_path != $default_repo['name'])) {
1155
		config_set_path('system/pkg_repo_conf_path', $default_repo['name']);
1156
		write_config( "Configured default pkg repo after restore");
1157
		pkg_switch_repo();
1158
	}
1159

    
1160
	/* wait for internet connection */
1161
	log_error(gettext("Waiting for Internet connection to update pkg " .
1162
	    "metadata and finish package reinstallation"));
1163
	$ntries = 3;
1164
	while ($ntries > 0) {
1165
		if (pkg_update(true)) {
1166
			break;
1167
		}
1168
		sleep(1);
1169
		$ntries--;
1170
	}
1171

    
1172
	if ($ntries == 0) {
1173
		return false;
1174
	}
1175

    
1176
	if (!empty($pkgs)) {
1177
		$pkg_info = get_pkg_info();
1178
	}
1179

    
1180
	foreach ($pkgs as $package) {
1181
		$found = false;
1182
		foreach ($pkg_info as $pkg) {
1183
			pkg_remove_prefix($pkg['name']);
1184
			if ($pkg['name'] == $package['internal_name']) {
1185
				pkg_install(g_get('pkg_prefix') . $package['internal_name'], true);
1186
				$found = true;
1187
			} elseif ($pkg['name'] == $package['name']) {
1188
				/* some packages use 'name' as the package name,
1189
				 * see https://redmine.pfsense.org/issues/12766 */
1190
				pkg_install(g_get('pkg_prefix') . $package['name'], true);
1191
				$found = true;
1192
			}
1193
			if ($found) {
1194
				break;
1195
			}
1196
		}
1197

    
1198
		if (!$found) {
1199
			if (!function_exists("file_notice")) {
1200
				require_once("notices.inc");
1201
			}
1202

    
1203
			/* Name of the package that cannot be found. If it has
1204
			 * an internal name, include that as well. */
1205
			$pkgname = $package['name'];
1206
			if (!empty($package['internal_name'])) {
1207
				$pkgname .= " ({$package['internal_name']})";
1208
			}
1209
			file_notice(gettext("Package reinstall"),
1210
			    sprintf(gettext("Package %s does not exist in " .
1211
			    "current %s version and it has been removed."),
1212
			    $pkgname, g_get('product_label')));
1213
			uninstall_package($package);
1214
		}
1215
	}
1216

    
1217
	/*
1218
	 * Verify remaining binary packages not present in current config
1219
	 * during backup restore and remove them
1220
	 */
1221
	$installed_packages = get_pkg_info('all', true, true);
1222
	foreach ($installed_packages as $package) {
1223
		$shortname = $package['name'];
1224
		pkg_remove_prefix($shortname);
1225
		if (get_package_id($shortname) != -1) {
1226
			continue;
1227
		}
1228
		pkg_delete($package['name']);
1229
	}
1230

    
1231
	return true;
1232
}
1233

    
1234
function stop_packages() {
1235
	require_once("config.inc");
1236
	require_once("functions.inc");
1237
	require_once("filter.inc");
1238
	require_once("shaper.inc");
1239
	require_once("captiveportal.inc");
1240
	require_once("pkg-utils.inc");
1241
	require_once("pfsense-utils.inc");
1242
	require_once("service-utils.inc");
1243

    
1244
	log_error(gettext("Stopping all packages."));
1245

    
1246
	$rcfiles = glob(RCFILEPREFIX . "*.sh");
1247
	if (!$rcfiles) {
1248
		$rcfiles = array();
1249
	} else {
1250
		$rcfiles = array_flip($rcfiles);
1251
		if (!$rcfiles) {
1252
			$rcfiles = array();
1253
		}
1254
	}
1255

    
1256
	foreach (config_get_path('installedpackages/package', []) as $package) {
1257
		$internal_name = get_package_internal_name($package);
1258
		if (is_service_running($internal_name)) {
1259
			echo " Stopping package {$package['name']}...";
1260
			stop_service($internal_name);
1261
			echo "done.\n";
1262
			unset($rcfiles[RCFILEPREFIX . strtolower($internal_name) . ".sh"]);
1263
		}
1264
	}
1265

    
1266
	foreach ($rcfiles as $rcfile => $number) {
1267
		$shell = @popen("/bin/sh", "w");
1268
		if ($shell) {
1269
			echo " Stopping {$rcfile}...";
1270
			if (!@fwrite($shell,
1271
			    "{$rcfile} stop >>/tmp/bootup_messages 2>&1")) {
1272
				if ($shell) {
1273
					pclose($shell);
1274
				}
1275
				$shell = @popen("/bin/sh", "w");
1276
			}
1277
			echo "done.\n";
1278
			pclose($shell);
1279
		}
1280
	}
1281
}
1282

    
1283
/* Identify which meta package is installed */
1284
function get_meta_pkg_name() {
1285
	global $g;
1286

    
1287
	/* XXX: Use pkg annotation */
1288
	if (is_pkg_installed(g_get('product_name'))) {
1289
		return g_get('product_name');
1290
	}
1291
	foreach (g_get('alternativemetaports') as $suffix) {
1292
		if (is_pkg_installed(g_get('product_name') . '-' . $suffix)) {
1293
			return g_get('product_name') . '-' . $suffix;
1294
		}
1295
	}
1296
	return false;
1297
}
1298

    
1299
/* Identify which package has the product dependencies */
1300
function get_dep_pkg_name() {
1301
	$name = get_meta_pkg_name();
1302

    
1303
	if ($name == false) {
1304
		return false;
1305
	}
1306

    
1307
	// The dependencies have been moved to another package.
1308
	if ($name == g_get('product_name')) {
1309
		$name .= '-system';
1310
	}
1311

    
1312
	return $name;
1313
}
1314

    
1315
/* Identify which base package is installed */
1316
function get_base_pkg_name() {
1317
	global $g;
1318

    
1319
	/* XXX: Use pkg annotation */
1320
	if (is_pkg_installed(g_get('product_name') . '-base-' . g_get('product_name'))) {
1321
		return g_get('product_name') . '-base-' . g_get('product_name');
1322
	} else if (is_pkg_installed(g_get('product_name') . '-base')) {
1323
		return g_get('product_name') . '-base';
1324
	}
1325
	return false;
1326
}
1327

    
1328
/* Verify if system needs updating from selected repo or upgrading from available repos (meta package or base) */
1329
function get_system_pkg_version($baseonly = false, $use_cache = true, $updates_only = true, $update_cache = false) {
1330
	global $g;
1331

    
1332
	if (!get_dnsavailable()) {
1333
		$result['pkg_version_error'] = "DNS servers not available";
1334
		return ($result);
1335
	}
1336

    
1337
	$cache_file = g_get('version_cache_file');
1338
	$rc_file = $cache_file . '.rc';
1339

    
1340
	$result['pkg_use_cache'] = $use_cache;
1341

    
1342
	$rc = "";
1343
	if ($use_cache && file_exists($rc_file) &&
1344
	    (time()-filemtime($rc_file) < g_get('version_cache_refresh'))) {
1345
		$rc = chop(@file_get_contents($rc_file));
1346
	}
1347

    
1348
	if ($rc == "2") {
1349
		$output = @file_get_contents($cache_file);
1350
	} else if ($rc != "0") {
1351
		$output = exec("/usr/local/sbin/{$g['product_name']}-upgrade ".
1352
		    ($updates_only?'-c':'-C') , $_gc, $rc);
1353
		if ($rc == 75) {
1354
			$result['pkg_busy'] = "1";
1355
		}
1356
		if ($rc != 0 && $rc != 2) {
1357
			$result['pkg_version_error'] = "{$g['product_name']}-upgrade error: $rc";
1358
			return ($result);
1359
		}
1360

    
1361
		/* Update cache if it succeeded */
1362
		if ($update_cache && ($rc == 0 || $rc == 2)) {
1363
			@file_put_contents($cache_file, $output);
1364
			@file_put_contents($rc_file, $rc);
1365
		}
1366
	}
1367

    
1368
	/* pfSense-upgrade returns 2 when there is a new version */
1369
	if ($rc == "2") {
1370
		$new_version = explode(' ', $output)[0];
1371
	}
1372

    
1373
	$base_pkg = get_base_pkg_name();
1374
	$dep_pkg = get_dep_pkg_name();
1375

    
1376
	if (!$base_pkg || !$dep_pkg) {
1377
		$result['pkg_version_error'] = "no base pkg: $base_pkg:$dep_pkg";
1378
		return false;
1379
	}
1380

    
1381
	$info = get_pkg_info($base_pkg, true, true);
1382

    
1383
	$pkg_info = array();
1384
	foreach ($info as $item) {
1385
		if ($item['name'] == $base_pkg) {
1386
			$pkg_info = $item;
1387
			break;
1388
		}
1389
	}
1390

    
1391
	if (empty($pkg_info) || (!$baseonly && ($pkg_info['version'] ==
1392
	    $pkg_info['installed_version']))) {
1393
		$info = get_pkg_info($dep_pkg, true, true);
1394

    
1395
		foreach ($info as $item) {
1396
			if ($item['name'] == $dep_pkg) {
1397
				$pkg_info = $item;
1398
				break;
1399
			}
1400
		}
1401
	}
1402

    
1403
	if (empty($pkg_info)) {
1404
		return false;
1405
	}
1406

    
1407
	$result = array(
1408
	    'version'           => $new_version ?: $pkg_info['version'],
1409
	    'installed_version' => $pkg_info['installed_version']
1410
	);
1411

    
1412
	$result['pkg_version_compare'] = pkg_version_compare(
1413
	    $result['installed_version'], $result['version']);
1414

    
1415
	return $result;
1416
}
1417

    
1418
/* List available repos */
1419
function pkg_list_repos() {
1420
	global $g;
1421

    
1422
	$repo_base = "{$g['pkg_repos_path']}/{$g['product_name']}-repo";
1423
	$result = array();
1424
	$name_files = glob("{$repo_base}-*.name");
1425
	foreach ($name_files as $name_file) {
1426
		$repo_name = file_get_contents($name_file);
1427
		if ($repo_name == false || strlen($repo_name) <= 1) {
1428
			continue;
1429
		}
1430
		$old = 0;
1431
		$new = 0;
1432
		$repo_name_base = "{$repo_base}-{$repo_name}";
1433
		if (file_exists("{$repo_name_base}.conf")) {
1434
			$old = 1;
1435
		} else {
1436
			if (sscanf($name_file,
1437
			    "{$repo_base}-%x.name", $repo_id) != 1) {
1438
				continue;
1439
			}
1440
			if ($repo_id < 0 || $repo_id > 0xffff) {
1441
				continue;
1442
			}
1443
			$repo_name_base = sprintf("%s-%04x",
1444
			    $repo_base, $repo_id);
1445
			if (file_exists("{$repo_name_base}.conf")) {
1446
				$new = 1;
1447
			}
1448
		}
1449
		if ($old == 0 && $new == 0) {
1450
			continue;
1451
		}
1452
		$descr_file = "{$repo_name_base}.descr";
1453
		if (file_exists($descr_file)) {
1454
			$descr = file_get_contents($descr_file);
1455
			if ($descr == false) {
1456
				$descr = 'Unknown';
1457
			}
1458
		} else {
1459
			$descr = 'Unknown';
1460
		}
1461
		$entry = array(
1462
		    'name' => $repo_name,
1463
		    'path' => "{$repo_name_base}.conf",
1464
		    'descr' => $descr
1465
		);
1466
		if ($new == 1) {
1467
			$entry['id'] = $repo_id;
1468
		}
1469
		if (file_exists("{$repo_name_base}.default")) {
1470
			$entry['default'] = true;
1471
		}
1472
		$result[] = $entry;
1473
	}
1474

    
1475
	return $result;
1476
}
1477

    
1478
function pkg_get_default_repo() {
1479
	$repos = pkg_list_repos();
1480

    
1481
	foreach ($repos as $repo) {
1482
		if (isset($repo['default'])) {
1483
			return $repo;
1484
		}
1485
	}
1486

    
1487
	/* No default found, return the first one */
1488
	return ($repos[0]);
1489
}
1490

    
1491
/* List available repos on a format to be used by selectors */
1492
function pkg_build_repo_list() {
1493
	$repos = pkg_list_repos();
1494
	$list = array();
1495

    
1496
	foreach ($repos as $repo) {
1497
		$list[$repo['name']] = $repo['descr'];
1498
	}
1499

    
1500
	return($list);
1501
}
1502

    
1503
/* Find repo by path */
1504
function pkg_get_repo_name($path) {
1505
	$repos = pkg_list_repos();
1506

    
1507
	$default = $repos[0]['name'];
1508
	foreach ($repos as $repo) {
1509
		if (in_array($path, [$repo['path'], $repo['name']])) {
1510
			return $repo['name'];
1511
		}
1512
		if (isset($repo['default'])) {
1513
			$default = $repo['name'];
1514
		}
1515
	}
1516

    
1517
	/* Default */
1518
	return $default;
1519
}
1520

    
1521
/* Find the current or default package help file. */
1522
function pkg_get_repo_help() {
1523
	/* Get the current or the default repo name. */
1524
	$saved_repo = pkg_get_repo_name(config_get_path('system/pkg_repo_conf_path'));
1525

    
1526
	$repos = pkg_list_repos();
1527
	foreach ($repos as $repo) {
1528
		if ($saved_repo == $repo['name']) {
1529
			$repo_conf_path = $repo['path'];
1530
			break;
1531
		}
1532
		if (isset($repo['default'])) {
1533
			$repo_conf_path = $repo['path'];
1534
		} elseif (!isset($repo_conf_path)) {
1535
			$repo_conf_path = $repo['path'];
1536
		}
1537
	}
1538

    
1539
	if (!empty($repo_conf_path)) {
1540
		$repo_ext_pos = strrpos($repo_conf_path, '.conf'); 
1541
		if (($repo_ext_pos !== false) && (substr($repo_conf_path, $repo_ext_pos) == '.conf')) {
1542
			$repo_help_path = substr($repo_conf_path, 0, $repo_ext_pos);
1543
			if (!empty($repo_help_path)) {
1544
				return "{$repo_help_path}.help";
1545
			}
1546
		}
1547
	}
1548

    
1549
	/* Default */
1550
	return '';
1551
}
1552

    
1553
/* Switch between stable and devel repos */
1554
function pkg_switch_repo() {
1555
	global $g;
1556

    
1557
	safe_mkdir("/usr/local/etc/pkg/repos");
1558
	/* Do not fetch new settings, only setup the repo pkg.conf. */
1559
	mwexec("/usr/local/sbin/{$g['product_name']}-repo-setup -U");
1560

    
1561
	/* Update pfSense_version cache */
1562
	mwexec_bg("/etc/rc.update_pkg_metadata now");
1563
	return;
1564
}
1565

    
1566
/*
1567
 * Update the repository settings.
1568
 */
1569
function update_repos() {
1570
	$product_name = g_get('product_name');
1571
	$pipes = [];
1572
	$dspec = [
1573
		1 => ['pipe', 'w'], /* stdout */
1574
		2 => ['pipe', 'w']  /* stderr */
1575
	];
1576
	$rc = -1;
1577
	/* Default error message for failure cases. */
1578
	$fail_error = [
1579
		"error" => 1,
1580
		"messages" => [gettext('Could not connect to Netgate servers. Please try again later.')]
1581
	];
1582

    
1583
	/* Execute repoc with a full pkg style environment, including proxy
1584
	 * configuration. */
1585
	$process = proc_open(sprintf('/usr/local/sbin/%s-repoc -N', $product_name),
1586
	    $dspec, $pipes, '/', pkg_env());
1587

    
1588
	/* Process failed to start */
1589
	if (!is_resource($process)) {
1590
		return $fail_error;
1591
	}
1592

    
1593
	$timeout = 5; /* seconds */
1594
	$stdout = '';
1595
	do {
1596
		$start = microtime(true);
1597
		$r = ['stdout' => $pipes[1], 'stderr' => $pipes[2]];
1598
		$w = $e = null;
1599
		if (($n = stream_select($r, $w, $e, 0)) === false) {
1600
			$result = $fail_error;
1601
			goto error;
1602
		} else if ($n > 0) {
1603
			foreach ($r as $var => $fd) {
1604
				$$var .= fread($fd, 4096);
1605
			}
1606
		}
1607
		if (($timeout -= (microtime(true) - $start)) <= 0) {
1608
			$result = $fail_error;
1609
			goto error;
1610
		}
1611
	} while (!feof($pipes[1]) && !feof($pipes[2]));
1612

    
1613
	/* Get return code */
1614
	$result = [
1615
		"error" => proc_close($process),
1616
		"messages" => []
1617
	];
1618

    
1619
	/* Change stdout to be an array of lines */
1620
	$stdout = explode("\n", trim($stdout));
1621

    
1622
	if ((count($stdout) > 1) &&
1623
	    ($stdout[0] === 'Messages:')) {
1624
		/* Copy over message content from the output. */
1625
		$result['messages'] = array_merge($result['messages'], array_slice($stdout, 1));
1626
	} elseif (!empty($stderr)) {
1627
		$result['messages'][] = $stderr;
1628
	}
1629

    
1630
error:
1631
	if (is_resource($process)) {
1632
		proc_close($process);
1633
	}
1634

    
1635
	return $result;
1636
}
1637
?>
(41-41/61)