Project

General

Profile

Download (42.3 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-2024 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 from package name if it's present */
63
function pkg_remove_prefix(&$pkg_name) {
64
	global $g;
65

    
66
	if (substr($pkg_name, 0, strlen(g_get('pkg_prefix'))) ==
67
	    g_get('pkg_prefix')) {
68
		$pkg_name = substr($pkg_name, strlen(g_get('pkg_prefix')));
69
	}
70
}
71

    
72
/* Execute pkg update when it's necessary */
73
function pkg_update($force = false) {
74
	global $g;
75

    
76
	return pkg_call("update" . ($force ? " -f" : ""));
77
}
78

    
79
/* return an array with necessary environment vars for pkg */
80
function pkg_env($extra_env = array()) {
81
	global $g;
82

    
83
	$user_agent = g_get('product_label') . '/' . g_get('product_version');
84
	if (!config_path_enabled('system','do_not_send_uniqueid')) {
85
		$user_agent .= ':' . system_get_uniqueid();
86
	}
87

    
88
	$pkg_env_vars = array(
89
		"LANG" => "C",
90
		"HTTP_USER_AGENT" => $user_agent,
91
		"ASSUME_ALWAYS_YES" => "true",
92
		"FETCH_TIMEOUT" => 5,
93
		"FETCH_RETRY" => 2
94
	);
95

    
96
	$http_proxy = config_get_path('system/proxyurl');
97
	$http_proxyport = config_get_path('system/proxyport');
98
	if (!empty($http_proxy)) {
99
		if (!empty($http_proxyport)) {
100
			$http_proxy .= ':' . $http_proxyport;
101
		}
102
		$pkg_env_vars['HTTP_PROXY'] = $http_proxy;
103

    
104
		$proxyuser = config_get_path('system/proxyuser');
105
		$proxypass = config_get_path('system/proxypass');
106
		if (!empty($proxyuser) && !empty($proxypass)) {
107
			$pkg_env_vars['HTTP_PROXY_AUTH'] = $proxyuser . ":" . $proxypass;
108
		}
109
	}
110

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

    
117
	foreach ($extra_env as $key => $value) {
118
		$pkg_env_vars[$key] = $value;
119
	}
120

    
121
	return $pkg_env_vars;
122
}
123

    
124
/* Execute a pkg call */
125
function pkg_call($params, $mute = false, $extra_env = array()) {
126
	if (empty($params)) {
127
		return false;
128
	}
129

    
130
	$descriptorspec = array(
131
		1 => array("pipe", "w"), /* stdout */
132
		2 => array("pipe", "w")	 /* stderr */
133
	);
134

    
135
	pkg_debug("pkg_call(): {$params}\n");
136
	$process = proc_open("/usr/local/sbin/pkg-static {$params}",
137
	    $descriptorspec, $pipes, '/', pkg_env($extra_env));
138

    
139
	if (!is_resource($process)) {
140
		return false;
141
	}
142

    
143
	stream_set_blocking($pipes[1], 0);
144
	stream_set_blocking($pipes[2], 0);
145

    
146
	/* XXX: should be a tunable? */
147
	$timeout = 60; // seconds
148
	$error_log = '';
149

    
150
	do {
151
		$write = array();
152
		$read = array($pipes[1], $pipes[2]);
153
		$except = array();
154

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

    
173
		$status = proc_get_status($process);
174
	} while ($status['running']);
175

    
176
	fclose($pipes[1]);
177
	fclose($pipes[2]);
178
	proc_close($process);
179

    
180

    
181
	$rc = $status['exitcode'];
182

    
183
	pkg_debug("pkg_call(): rc = {$rc}\n");
184
	if ($rc == 0) {
185
		return true;
186
	}
187

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

    
195
	return false;
196
}
197

    
198
/* Execute pkg with $params, fill stdout and stderr and return pkg rc */
199
function pkg_exec($params, &$stdout, &$stderr, $extra_env = array()) {
200
	if (empty($params)) {
201
		return -1;
202
	}
203

    
204
	$descriptorspec = array(
205
		1 => array("pipe", "w"), /* stdout */
206
		2 => array("pipe", "w")	 /* stderr */
207
	);
208

    
209

    
210
	pkg_debug("pkg_exec(): {$params}\n");
211
	$process = proc_open("/usr/local/sbin/pkg-static {$params}",
212
	    $descriptorspec, $pipes, '/', pkg_env($extra_env));
213

    
214
	if (!is_resource($process)) {
215
		return -1;
216
	}
217

    
218
	$stdout = '';
219
	while (($l = fgets($pipes[1])) !== FALSE) {
220
		$stdout .= $l;
221
	}
222
	fclose($pipes[1]);
223

    
224
	$stderr = '';
225
	while (($l = fgets($pipes[2])) !== FALSE) {
226
		$stderr .= $l;
227
	}
228
	fclose($pipes[2]);
229

    
230

    
231
	return proc_close($process);
232
}
233

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

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

    
247
	if ($rc != 0) {
248
		return '?';
249
	}
250

    
251
	return str_replace("\n", "", $stdout);
252
}
253

    
254
/* Check if package is installed */
255
function is_pkg_installed($pkg_name) {
256
	global $g;
257

    
258
	if (empty($pkg_name)) {
259
		return false;
260
	}
261

    
262
	return pkg_call("info -e " . $pkg_name, true);
263
}
264

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

    
270
	$shortname = $pkg_name;
271
	pkg_remove_prefix($shortname);
272

    
273
	$pkg_force = "";
274
	if ($force) {
275
		$pkg_force = "-f ";
276
	}
277

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

    
285
	return $result;
286
}
287

    
288
/* Delete package from FreeBSD, $pkg_name should not contain prefix */
289
function pkg_delete($pkg_name) {
290
	global $g;
291

    
292
	$shortname = $pkg_name;
293
	pkg_remove_prefix($shortname);
294

    
295
	pkg_debug("Removing package {$shortname}\n");
296
	if (is_pkg_installed($pkg_name)) {
297
		pkg_call("delete -y " . $pkg_name);
298
		/* Cleanup unnecessary dependencies */
299
		pkg_call("autoremove -y");
300
	}
301
}
302

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

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

    
317
	return -1;
318
}
319

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

    
331
// Get information about packages.
332
function get_pkg_info($pkgs = 'all', $remote_repo_usage_disabled = false,
333
    $installed_pkgs_only = false) {
334
	global $g, $input_errors;
335

    
336
	$out = $err = $extra_param = '';
337
	$rc = 0;
338

    
339
	unset($pkg_filter);
340

    
341
	if (is_array($pkgs)) {
342
		$pkg_filter = $pkgs;
343
		$pkgs = g_get('pkg_prefix') . '*';
344
	} elseif ($pkgs == 'all') {
345
		$pkgs = g_get('pkg_prefix') . '*';
346
	}
347

    
348
	$base_packages = (substr($pkgs, 0, strlen(g_get('pkg_prefix'))) !=
349
	    g_get('pkg_prefix'));
350

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

    
360
	if (!function_exists('is_subsystem_dirty')) {
361
		require_once("util.inc");
362
	}
363

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

    
372
	if ($lock) {
373
		mark_subsystem_dirty('pkg');
374
	}
375

    
376
	if ($remote_repo_usage_disabled) {
377
		$extra_param = "-U ";
378
	}
379

    
380
	$did_search = false;
381
	$search_rc = 0;
382
	$info_rc = 0;
383
	$search_items = array();
384
	$info_items = array();
385

    
386
	if ($base_packages) {
387
		$repo_param = "";
388
	} else {
389
		$repo_param = "-r {$g['product_name']}";
390
	}
391

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

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

    
429
	if ($lock) {
430
		clear_subsystem_dirty('pkg');
431
	}
432

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

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

    
454
			if (array_search($pkg_info['name'], array_column(
455
			    $search_items, 'name')) === FALSE) {
456
				$pkg_info['obsolete'] = true;
457
				$search_items[] = $pkg_info;
458
			}
459
		}
460
	}
461

    
462
	$result = array();
463
	foreach ($search_items as $pkg_info) {
464
		if (empty($pkg_info['name'])) {
465
			continue;
466
		}
467

    
468
		if (isset($pkg_filter) && !in_array($pkg_info['name'],
469
		    $pkg_filter)) {
470
			continue;
471
		}
472

    
473
		$pkg_info['shortname'] = $pkg_info['name'];
474
		pkg_remove_prefix($pkg_info['shortname']);
475

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

    
481
		$pkg_is_installed = false;
482

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

    
491
			$pkg_info['installed'] = true;
492
			$pkg_is_installed = true;
493

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

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

    
509
			$pkg_info['installed_version'] = str_replace("\n", "",
510
			    $out);
511

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

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

    
531
		} else if (is_package_installed($pkg_info['shortname'])) {
532
			$pkg_info['broken'] = true;
533
			$pkg_is_installed = true;
534
		}
535

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

    
539
		if (!$installed_pkgs_only || $pkg_is_installed) {
540
			$result[] = $pkg_info;
541
		}
542
		unset($pkg_info);
543
	}
544

    
545
	/* Sort result alphabetically */
546
	usort($result, function($a, $b) {
547
		return(strcasecmp ($a['name'], $b['name']));
548
	});
549

    
550
	return $result;
551
}
552

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

    
564
	foreach ($pkg_info as $pkg) {
565
		pkg_remove_prefix($pkg['name']);
566

    
567
		if (!$force && is_package_installed($pkg['name'])) {
568
			continue;
569
		}
570

    
571
		update_status(sprintf(gettext(
572
		    "Running last steps of %s installation.") . "\n",
573
		    $pkg['name']));
574
		install_package_xml($pkg['name']);
575
	}
576
}
577

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

    
586
	if ($show_message == true) {
587
		echo "Syncing packages:";
588
	}
589

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

    
604
	if ($show_message == true) {
605
		echo " done.\n";
606
	}
607
}
608

    
609
function uninstall_package($package_name) {
610
	global $g;
611

    
612
	$internal_name = $package_name;
613
	$id = get_package_id($package_name);
614
	if ($id >= 0) {
615
		$internal_name = get_package_internal_name(
616
		    config_get_path("installedpackages/package/{$id}"));
617
		stop_service($internal_name);
618
	}
619
	$pkg_name = g_get('pkg_prefix') . $internal_name;
620

    
621
	if (is_pkg_installed($pkg_name)) {
622
		update_status(gettext("Removing package...") . "\n");
623
		pkg_delete($pkg_name);
624
	} else {
625
		delete_package_xml($package_name);
626
	}
627

    
628
	update_status(gettext("done.") . "\n");
629
}
630

    
631
function reinstall_package($package_name) {
632
	global $g;
633

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

    
644
/* Run <custom_php_resync_config_command> */
645
function sync_package($package_name) {
646
	global $builder_package_install;
647

    
648
	// If this code is being called by pfspkg_installer
649
	// which the builder system uses then return (ignore).
650
	if ($builder_package_install) {
651
		return;
652
	}
653

    
654
	if (empty(config_get_path('installedpackages/package', []))) {
655
		return;
656
	}
657

    
658
	if (($pkg_id = get_package_id($package_name)) == -1) {
659
		// This package doesn't really exist - exit the function.
660
		return;
661
	}
662

    
663
	$package = config_get_path("installedpackages/package/{$pkg_id}");
664
	if (empty($package)) {
665
		// No package belongs to the pkg_id passed to this function.
666
		return;
667
	}
668

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

    
677
	$pkg_config = parse_xml_config_pkg("/usr/local/pkg/" .
678
	    $package['configurationfile'], "packagegui");
679
	if (isset($pkg_config['nosync'])) {
680
		return;
681
	}
682

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

    
707
	if (!empty($pkg_config['custom_php_global_functions'])) {
708
		eval($pkg_config['custom_php_global_functions']);
709
	}
710
	if (!empty($pkg_config['custom_php_resync_config_command'])) {
711
		eval($pkg_config['custom_php_resync_config_command']);
712
	}
713
}
714

    
715
/* Read info.xml installed by package and return an array */
716
function read_package_config($package_name) {
717
	global $g;
718

    
719
	$pkg_info_xml = '/usr/local/share/' . g_get('pkg_prefix') . $package_name .
720
	    '/info.xml';
721

    
722
	if (!file_exists($pkg_info_xml)) {
723
		return false;
724
	}
725

    
726
	$pkg_info = parse_xml_config_pkg($pkg_info_xml, 'pfsensepkgs');
727

    
728
	if (empty($pkg_info)) {
729
		return false;
730
	}
731

    
732
	/* it always returns an array with 1 item */
733
	return $pkg_info['package'][0];
734
}
735

    
736
/* Read package configurationfile and return an array */
737
function read_package_configurationfile($package_name) {
738
	$pkg_config = array();
739
	$id = get_package_id($package_name);
740
	$pkg_data = config_get_path("installedpackages/package/{$id}", []);
741

    
742
	if ($id < 0 || empty($pkg_data)) {
743
		return $pkg_config;
744
	}
745

    
746
	if (empty($pkg_data['configurationfile'])) {
747
		return $pkg_config;
748
	}
749

    
750
	if (!file_exists('/usr/local/pkg/' . $pkg_data['configurationfile'])) {
751
		return $pkg_config;
752
	}
753

    
754
	$pkg_config = parse_xml_config_pkg('/usr/local/pkg/' .
755
	    $pkg_data['configurationfile'], "packagegui");
756

    
757
	return $pkg_config;
758
}
759

    
760
function get_after_install_info($package_name) {
761
	$pkg_config = read_package_config($package_name);
762

    
763
	if (isset($pkg_config['after_install_info'])) {
764
		return $pkg_config['after_install_info'];
765
	}
766

    
767
	return '';
768
}
769

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

    
783
function install_package_xml($package_name) {
784
	if (($pkg_info = read_package_config($package_name)) == false) {
785
		return false;
786
	}
787

    
788
	pkg_debug(gettext("Beginning package installation.") . "\n");
789
	log_error(sprintf(gettext('Beginning package installation for %s .'),
790
	    $pkg_info['name']));
791

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

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

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

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

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

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

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

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

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

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

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

    
972
	return true;
973
}
974

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

    
978

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

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

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

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

    
1142
	$pkgs = config_get_path('installedpackages/package');
1143
	if ($pkgs === null) {
1144
		return true;
1145
	}
1146

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

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

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

    
1175
	if ($ntries == 0) {
1176
		return false;
1177
	}
1178

    
1179
	if (!empty($pkgs)) {
1180
		$pkg_info = get_pkg_info();
1181
	}
1182

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

    
1201
		if (!$found) {
1202
			if (!function_exists("file_notice")) {
1203
				require_once("notices.inc");
1204
			}
1205

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

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

    
1234
	return true;
1235
}
1236

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

    
1247
	log_error(gettext("Stopping all packages."));
1248

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

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

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

    
1286
/* Identify which meta package is installed */
1287
function get_meta_pkg_name() {
1288
	global $g;
1289

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

    
1302
/* Identify which base package is installed */
1303
function get_base_pkg_name() {
1304
	global $g;
1305

    
1306
	/* XXX: Use pkg annotation */
1307
	if (is_pkg_installed(g_get('product_name') . '-base-' . g_get('product_name'))) {
1308
		return g_get('product_name') . '-base-' . g_get('product_name');
1309
	} else if (is_pkg_installed(g_get('product_name') . '-base')) {
1310
		return g_get('product_name') . '-base';
1311
	}
1312
	return false;
1313
}
1314

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

    
1319
	if (!get_dnsavailable()) {
1320
		$result['pkg_version_error'] = "DNS servers not available";
1321
		return ($result);
1322
	}
1323

    
1324
	$cache_file = g_get('version_cache_file');
1325
	$rc_file = $cache_file . '.rc';
1326

    
1327
	$result['pkg_use_cache'] = $use_cache;
1328

    
1329
	$rc = "";
1330
	if ($use_cache && file_exists($rc_file) &&
1331
	    (time()-filemtime($rc_file) < g_get('version_cache_refresh'))) {
1332
		$rc = chop(@file_get_contents($rc_file));
1333
	}
1334

    
1335
	if ($rc == "2") {
1336
		$output = @file_get_contents($cache_file);
1337
	} else if ($rc != "0") {
1338
		$output = exec("/usr/local/sbin/{$g['product_name']}-upgrade ".
1339
		    ($updates_only?'-c':'-C') , $_gc, $rc);
1340
		if ($rc == 75) {
1341
			$result['pkg_busy'] = "1";
1342
		}
1343
		if ($rc != 0 && $rc != 2) {
1344
			$result['pkg_version_error'] = "{$g['product_name']}-upgrade error: $rc";
1345
			return ($result);
1346
		}
1347

    
1348
		/* Update cache if it succeeded */
1349
		if ($update_cache && ($rc == 0 || $rc == 2)) {
1350
			@file_put_contents($cache_file, $output);
1351
			@file_put_contents($rc_file, $rc);
1352
		}
1353
	}
1354

    
1355
	/* pfSense-upgrade returns 2 when there is a new version */
1356
	if ($rc == "2") {
1357
		$new_version = explode(' ', $output)[0];
1358
	}
1359

    
1360
	$base_pkg = get_base_pkg_name();
1361
	$meta_pkg = get_meta_pkg_name();
1362

    
1363
	if (!$base_pkg || !$meta_pkg) {
1364
		$result['pkg_version_error'] = "no base pkg: $base_pkg:$meta_pkg";
1365
		return false;
1366
	}
1367

    
1368
	$info = get_pkg_info($base_pkg, true, true);
1369

    
1370
	$pkg_info = array();
1371
	foreach ($info as $item) {
1372
		if ($item['name'] == $base_pkg) {
1373
			$pkg_info = $item;
1374
			break;
1375
		}
1376
	}
1377

    
1378
	if (empty($pkg_info) || (!$baseonly && ($pkg_info['version'] ==
1379
	    $pkg_info['installed_version']))) {
1380
		$info = get_pkg_info($meta_pkg, true, true);
1381

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

    
1390
	if (empty($pkg_info)) {
1391
		return false;
1392
	}
1393

    
1394
	$result = array(
1395
	    'version'           => $new_version ?: $pkg_info['version'],
1396
	    'installed_version' => $pkg_info['installed_version']
1397
	);
1398

    
1399
	$result['pkg_version_compare'] = pkg_version_compare(
1400
	    $result['installed_version'], $result['version']);
1401

    
1402
	return $result;
1403
}
1404

    
1405
/* List available repos */
1406
function pkg_list_repos() {
1407
	global $g;
1408

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

    
1462
	return $result;
1463
}
1464

    
1465
function pkg_get_default_repo() {
1466
	$repos = pkg_list_repos();
1467

    
1468
	foreach ($repos as $repo) {
1469
		if (isset($repo['default'])) {
1470
			return $repo;
1471
		}
1472
	}
1473

    
1474
	/* No default found, return the first one */
1475
	return ($repos[0]);
1476
}
1477

    
1478
/* List available repos on a format to be used by selectors */
1479
function pkg_build_repo_list() {
1480
	$repos = pkg_list_repos();
1481
	$list = array();
1482

    
1483
	foreach ($repos as $repo) {
1484
		$list[$repo['name']] = $repo['descr'];
1485
	}
1486

    
1487
	return($list);
1488
}
1489

    
1490
/* Find repo by path */
1491
function pkg_get_repo_name($path) {
1492
	$repos = pkg_list_repos();
1493

    
1494
	$default = $repos[0]['name'];
1495
	foreach ($repos as $repo) {
1496
		if (in_array($path, [$repo['path'], $repo['name']])) {
1497
			return $repo['name'];
1498
		}
1499
		if (isset($repo['default'])) {
1500
			$default = $repo['name'];
1501
		}
1502
	}
1503

    
1504
	/* Default */
1505
	return $default;
1506
}
1507

    
1508
/* Find the current or default package help file. */
1509
function pkg_get_repo_help() {
1510
	/* Get the current or the default repo name. */
1511
	$saved_repo = pkg_get_repo_name(config_get_path('system/pkg_repo_conf_path'));
1512

    
1513
	$repos = pkg_list_repos();
1514
	foreach ($repos as $repo) {
1515
		if ($saved_repo == $repo['name']) {
1516
			$repo_conf_path = $repo['path'];
1517
			break;
1518
		}
1519
		if (isset($repo['default'])) {
1520
			$repo_conf_path = $repo['path'];
1521
		} elseif (!isset($repo_conf_path)) {
1522
			$repo_conf_path = $repo['path'];
1523
		}
1524
	}
1525

    
1526
	if (!empty($repo_conf_path)) {
1527
		$repo_ext_pos = strrpos($repo_conf_path, '.conf'); 
1528
		if (($repo_ext_pos !== false) && (substr($repo_conf_path, $repo_ext_pos) == '.conf')) {
1529
			$repo_help_path = substr($repo_conf_path, 0, $repo_ext_pos);
1530
			if (!empty($repo_help_path)) {
1531
				return "{$repo_help_path}.help";
1532
			}
1533
		}
1534
	}
1535

    
1536
	/* Default */
1537
	return '';
1538
}
1539

    
1540
/* Switch between stable and devel repos */
1541
function pkg_switch_repo() {
1542
	global $g;
1543

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

    
1548
	/* Update pfSense_version cache */
1549
	mwexec_bg("/etc/rc.update_pkg_metadata now");
1550
	return;
1551
}
1552

    
1553
/*
1554
 * Update the repository settings.
1555
 */
1556
function update_repos() {
1557
	$product_name = g_get('product_name');
1558
	$pipes = [];
1559
	$descriptorspec = [
1560
		1 => ["pipe", "w"], /* stdout */
1561
		2 => ["pipe", "w"]  /* stderr */
1562
	];
1563
	$rc = -1;
1564
	/* Default error message for failure cases. */
1565
	$fail_error = [
1566
		"error" => 1,
1567
		"messages" => ["Could not update connect to Netgate servers. Please try again later."]
1568
	];
1569

    
1570
	/* Execute repoc with a full pkg style environment, including proxy
1571
	 * configuration. */
1572
	$process = proc_open(sprintf('/usr/local/sbin/%s-repoc -N', $product_name),
1573
	    $descriptorspec, $pipes, '/', pkg_env());
1574

    
1575
	/* Process failed to start */
1576
	if (!is_resource($process)) {
1577
		return $fail_error;
1578
	}
1579

    
1580
	/* Read output */
1581
	$stdout = stream_get_contents($pipes[1]);
1582
	fclose($pipes[1]);
1583
	/* If stdout is boolean false, reading stdout failed. */
1584
	if ($stdout === false) {
1585
		return $fail_error;
1586
	}
1587

    
1588
	/* Read errors */
1589
	$stderr = stream_get_contents($pipes[2]);
1590
	fclose($pipes[2]);
1591

    
1592
	/* Get return code */
1593
	$rc = proc_close($process);
1594
	$result = [
1595
		"error" => $rc,
1596
		"messages" => []
1597
	];
1598

    
1599
	/* Change stdout to be an array of lines */
1600
	$stdout = explode("\n", trim($stdout));
1601

    
1602
	if ((count($stdout) > 1) &&
1603
	    ($stdout[0] === "Messages:")) {
1604
		/* Copy over message content from the output. */
1605
		$result['messages'] = array_merge($result['messages'], array_slice($stdout, 1));
1606
	} elseif (!empty($stderr)) {
1607
		$result['messages'][] = $stderr;
1608
	}
1609

    
1610
	return ($result);
1611
}
1612
?>
(41-41/61)