Project

General

Profile

Download (36.4 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
 * pkg-utils.inc
4
 *
5
 * part of pfSense (https://www.pfsense.org)
6
 * Copyright (c) 2005-2006 Colin Smith (ethethlay@gmail.com)
7
 * Copyright (c) 2004-2016 Rubicon Communications, LLC (Netgate)
8
 * All rights reserved.
9
 *
10
 * Licensed under the Apache License, Version 2.0 (the "License");
11
 * you may not use this file except in compliance with the License.
12
 * You may obtain a copy of the License at
13
 *
14
 * http://www.apache.org/licenses/LICENSE-2.0
15
 *
16
 * Unless required by applicable law or agreed to in writing, software
17
 * distributed under the License is distributed on an "AS IS" BASIS,
18
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19
 * See the License for the specific language governing permissions and
20
 * limitations under the License.
21
 */
22

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

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

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

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

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

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

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

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

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

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

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

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

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

    
80
	$user_agent = $g['product_name'] . '/' . $g['product_version'];
81
	if (!isset($config['system']['do_not_send_uniqueid'])) {
82
		$user_agent .= ':' . system_get_uniqueid();
83
	}
84

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

    
93
	if (!empty($config['system']['proxyurl'])) {
94
		$http_proxy = $config['system']['proxyurl'];
95
		if (!empty($config['system']['proxyport'])) {
96
			$http_proxy .= ':' . $config['system']['proxyport'];
97
		}
98
		$pkg_env_vars['HTTP_PROXY'] = $http_proxy;
99

    
100
		if (!empty($config['system']['proxyuser']) && !empty($config['system']['proxypass'])) {
101
			$pkg_env_vars['HTTP_PROXY_AUTH'] = "basic:*:{$config['system']['proxyuser']}:{$config['system']['proxypass']}";
102
		}
103
	}
104

    
105
	if (isset($config['system']['use_mfs_tmpvar'])) {
106
		$pkg_env_vars['PKG_DBDIR'] = '/root/var/db/pkg';
107
		$pkg_env_vars['PKG_CACHEDIR'] = '/root/var/cache/pkg';
108
	}
109

    
110
	foreach ($extra_env as $key => $value) {
111
		$pkg_env_vars[$key] = $value;
112
	}
113

    
114
	return $pkg_env_vars;
115
}
116

    
117
/* Execute a pkg call */
118
function pkg_call($params, $mute = false, $extra_env = array()) {
119
	global $g, $config;
120

    
121
	if (empty($params)) {
122
		return false;
123
	}
124

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

    
130

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

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

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

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

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

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

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

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

    
176

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

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

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

    
191
	return false;
192
}
193

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

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

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

    
207

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

    
212
	if (!is_resource($process)) {
213
		return -1;
214
	}
215

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

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

    
228

    
229
	return proc_close($process);
230
}
231

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

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

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

    
249
	return str_replace("\n", "", $stdout);
250
}
251

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

    
256
	if (empty($pkg_name)) {
257
		return false;
258
	}
259

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

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

    
268
	$shortname = $pkg_name;
269
	pkg_remove_prefix($shortname);
270

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

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

    
283
	return $result;
284
}
285

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

    
290
	$shortname = $pkg_name;
291
	pkg_remove_prefix($shortname);
292

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

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

    
306
/* Find package array index */
307
function get_package_id($package_name) {
308
	global $config;
309

    
310
	if (!is_array($config['installedpackages']['package'])) {
311
		return -1;
312
	}
313

    
314
	foreach ($config['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']) && ($package_data['internal_name'] != "")) {
327
		/* e.g. name is Ipguard-dev, internal name is ipguard */
328
		return $package_data['internal_name'];
329
	} else {
330
		return $package_data['name'];
331
	}
332
}
333

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

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

    
342
	unset($pkg_filter);
343

    
344
	if (is_array($pkgs)) {
345
		$pkg_filter = $pkgs;
346
		$pkgs = $g['pkg_prefix'] . '*';
347
	} elseif ($pkgs == 'all') {
348
		$pkgs = $g['pkg_prefix'] . '*';
349
	}
350

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

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

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

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

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

    
378
	// If we want more than just the currently installed packages or
379
	//    we want up-to-date remote repo info
380
	// then do a full pkg search
381
	if (!$installed_pkgs_only || !$remote_repo_usage_disabled) {
382
		$rc = pkg_exec(
383
		    "search {$extra_param}-R --raw-format json-compact " .
384
		    $pkgs, $out, $err);
385
	}
386

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

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

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

    
445
	$result = array();
446
	$pkgs_info = explode("\n", $out);
447
	foreach ($pkgs_info as $pkg_info_json) {
448
		$pkg_info = json_decode($pkg_info_json, true);
449
		if (!isset($pkg_info['name'])) {
450
			continue;
451
		}
452

    
453
		if (isset($pkg_filter) && !in_array($pkg_info['name'],
454
		    $pkg_filter)) {
455
			continue;
456
		}
457

    
458
		$pkg_info['shortname'] = $pkg_info['name'];
459
		pkg_remove_prefix($pkg_info['shortname']);
460

    
461
		/* XXX: Add it to globals.inc? */
462
		$pkg_info['changeloglink'] =
463
		    "https://github.com/pfsense/FreeBSD-ports/commits/devel/" .
464
		    $pkg_info['categories'][0] . '/' . $pkg_info['name'];
465

    
466
		$pkg_is_installed = false;
467

    
468
		if (is_pkg_installed($pkg_info['name'])) {
469
			$pkg_info['installed'] = true;
470
			$pkg_is_installed = true;
471

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

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

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

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

    
497
		if (!$installed_pkgs_only || $pkg_is_installed) {
498
			$result[] = $pkg_info;
499
		}
500
		unset($pkg_info);
501
	}
502

    
503
	/* Sort result alphabetically */
504
	usort($result, function($a, $b) {
505
		return(strcasecmp ($a['name'], $b['name']));
506
	});
507

    
508
	return $result;
509
}
510

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

    
522
	$pkg_info = get_pkg_info('all', true, true);
523

    
524
	foreach ($pkg_info as $pkg) {
525
		pkg_remove_prefix($pkg['name']);
526

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

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

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

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

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

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

    
555

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

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

    
574
	@unlink("/conf/needs_package_sync");
575
}
576

    
577
function uninstall_package($package_name) {
578
	global $config;
579

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

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

    
595
	update_status(gettext("done.") . "\n");
596
}
597

    
598
/* Run <custom_php_resync_config_command> */
599
function sync_package($package_name) {
600
	global $config, $builder_package_install;
601

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

    
608
	if (empty($config['installedpackages']['package'])) {
609
		return;
610
	}
611

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

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

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

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

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

    
652
	if (!empty($pkg_config['custom_php_global_functions'])) {
653
		eval($pkg_config['custom_php_global_functions']);
654
	}
655
	if (!empty($pkg_config['custom_php_resync_config_command'])) {
656
		eval($pkg_config['custom_php_resync_config_command']);
657
	}
658
}
659

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

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

    
666
	if (!file_exists($pkg_info_xml)) {
667
		return false;
668
	}
669

    
670
	$pkg_info = parse_xml_config_pkg($pkg_info_xml, 'pfsensepkgs');
671

    
672
	if (empty($pkg_info)) {
673
		return false;
674
	}
675

    
676
	/* it always returns an array with 1 item */
677
	return $pkg_info['package'][0];
678
}
679

    
680
/* Read package configurationfile and return an array */
681
function read_package_configurationfile($package_name) {
682
	global $config, $g;
683

    
684
	$pkg_config = array();
685
	$id = get_package_id($package_name);
686

    
687
	if ($id < 0 || !isset($config['installedpackages']['package'][$id]['configurationfile'])) {
688
		return $pkg_config;
689
	}
690

    
691
	$pkg_configurationfile = $config['installedpackages']['package'][$id]['configurationfile'];
692

    
693
	if (empty($pkg_configurationfile) || !file_exists('/usr/local/pkg/' . $pkg_configurationfile)) {
694
		return $pkg_config;
695
	}
696

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

    
699
	return $pkg_config;
700
}
701

    
702
function get_after_install_info($package_name) {
703
	$pkg_config = read_package_config($package_name);
704

    
705
	if (isset($pkg_config['after_install_info'])) {
706
		return $pkg_config['after_install_info'];
707
	}
708

    
709
	return '';
710
}
711

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

    
725
function install_package_xml($package_name) {
726
	global $g, $config, $pkg_interface;
727

    
728
	if (($pkg_info = read_package_config($package_name)) == false) {
729
		return false;
730
	}
731

    
732
	/* safe side. Write config below will send to ro again. */
733

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

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

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

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

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

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

    
788
				uninstall_package($package_name);
789
				write_config($changedesc);
790
				log_error(sprintf(gettext("Failed to install package: %s."), $pkg_info['name']));
791
				update_status(gettext("Failed to install package.") . "\n");
792
				return false;
793
			}
794
		}
795

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

    
864
		uninstall_package($package_name);
865
		write_config($changedesc);
866
		log_error(sprintf(gettext("Failed to install package: %s."), $pkg_info['name']));
867
		update_status(gettext("Failed to install package.") . "\n");
868
		return false;
869
	}
870

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

    
879
	/* set up package logging streams */
880
	if ($pkg_info['logging']) {
881
		system_syslogd_start(true);
882
	}
883

    
884
	return true;
885
}
886

    
887
function delete_package_xml($package_name, $when = "post-deinstall") {
888
	global $g, $config, $pkg_interface;
889

    
890

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

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

    
1004
	if ($when == "post-deinstall") {
1005
		/* remove config.xml entries */
1006
		update_status(gettext("Configuration... "));
1007
		unset($config['installedpackages']['package'][$pkgid]);
1008
		update_status(gettext("done.") . "\n");
1009
		write_config(sprintf(gettext("Removed %s package."), $package_name));
1010
		/* remove package entry from /etc/syslog.conf if needed */
1011
		/* this must be done after removing the entries from config.xml */
1012
		if ($need_syslog_restart) {
1013
			system_syslogd_start(true);
1014
		}
1015
	}
1016
}
1017

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

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

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

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

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

    
1053
	$pkg_info = get_pkg_info();
1054

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

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

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

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

    
1093
	return true;
1094
}
1095

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

    
1106
	global $config, $g;
1107

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

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

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

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

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

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

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

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

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

    
1176
	$cache_file = $g['version_cache_file'];
1177
	$rc_file = $cache_file . '.rc';
1178

    
1179
	$rc = "";
1180
	if ($use_cache && file_exists($rc_file) &&
1181
	    (time()-filemtime($rc_file) < $g['version_cache_refresh'])) {
1182
		$rc = chop(@file_get_contents($rc_file));
1183
	}
1184

    
1185
	if ($rc == "2") {
1186
		$output = @file_get_contents($cache_file);
1187
	} else if ($rc != "0") {
1188
		$output = exec(
1189
		    "/usr/local/sbin/{$g['product_name']}-upgrade -c", $_gc,
1190
		    $rc);
1191

    
1192
		/* Update cache if it succeeded */
1193
		if ($rc == 0 || $rc == 2) {
1194
			@file_put_contents($cache_file, $output);
1195
			@file_put_contents($rc_file, $rc);
1196
		}
1197
	}
1198

    
1199
	/* pfSense-upgrade returns 2 when there is a new version */
1200
	if ($rc == "2") {
1201
		$new_version = explode(' ', $output)[0];
1202
	}
1203

    
1204
	$base_pkg = get_base_pkg_name();
1205
	$meta_pkg = get_meta_pkg_name();
1206

    
1207
	if (!$base_pkg || !$meta_pkg) {
1208
		return false;
1209
	}
1210

    
1211
	$info = get_pkg_info($base_pkg, true, true);
1212

    
1213
	$pkg_info = array();
1214
	foreach ($info as $item) {
1215
		if ($item['name'] == $base_pkg) {
1216
			$pkg_info = $item;
1217
			break;
1218
		}
1219
	}
1220

    
1221
	if (empty($pkg_info) || (!$baseonly && ($pkg_info['version'] ==
1222
	    $pkg_info['installed_version']))) {
1223
		$info = get_pkg_info($meta_pkg, true, true);
1224

    
1225
		foreach ($info as $item) {
1226
			if ($item['name'] == $meta_pkg) {
1227
				$pkg_info = $item;
1228
				break;
1229
			}
1230
		}
1231
	}
1232

    
1233
	if (empty($pkg_info)) {
1234
		return false;
1235
	}
1236

    
1237
	return array(
1238
	    'version'           => $new_version ?: $pkg_info['version'],
1239
	    'installed_version' => $pkg_info['installed_version']
1240
	);
1241
}
1242

    
1243
/* List available repos */
1244
function pkg_list_repos() {
1245
	global $g;
1246

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

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

    
1251
	$default = array(
1252
	    'name' => 'Default',
1253
	    'path' => $path . "/{$g['product_name']}-repo.conf",
1254
	    'descr' => $default_descr
1255
	);
1256

    
1257
	$result = array($default);
1258

    
1259
	$conf_files = glob("{$path}/{$g['product_name']}-repo-*.conf");
1260
	foreach ($conf_files as $conf_file) {
1261
		$descr_file = preg_replace('/.conf$/', '.descr', $conf_file);
1262
		if (file_exists($descr_file)) {
1263
			$descr_content = file($descr_file);
1264
			$descr = chop($descr_content[0]);
1265
		} else {
1266
			$descr = 'Unknown';
1267
		}
1268
		if (!preg_match('/-repo-(.*).conf/', $conf_file, $matches)) {
1269
			continue;
1270
		}
1271
		$entry = array(
1272
		    'name' => ucfirst(strtolower($matches[1])),
1273
		    'path' => $conf_file,
1274
		    'descr' => $descr
1275
		);
1276
		$result[] = $entry;
1277
	}
1278

    
1279
	return $result;
1280
}
1281

    
1282
/* Switch between stable and devel repos */
1283
function pkg_switch_repo($path) {
1284
	global $g;
1285

    
1286
	safe_mkdir("/usr/local/etc/pkg/repos");
1287
	@unlink("/usr/local/etc/pkg/repos/{$g['product_name']}.conf");
1288
	@symlink($path, "/usr/local/etc/pkg/repos/{$g['product_name']}.conf");
1289

    
1290
	$abi_file = str_replace('.conf', '.abi', $path);
1291
	$altabi_file = str_replace('.conf', '.altabi', $path);
1292

    
1293
	if (file_exists($abi_file) && file_exists($altabi_file)) {
1294
		$abi = file_get_contents($abi_file);
1295
		$altabi = file_get_contents($altabi_file);
1296

    
1297
		$pkg_conf = array(
1298
			"ABI={$abi}",
1299
			"ALTABI={$altabi}"
1300
		);
1301

    
1302
		file_put_contents("/usr/local/etc/pkg.conf", $pkg_conf);
1303
	}
1304

    
1305
	/* Update pfSense_version cache */
1306
	mwexec_bg("/etc/rc.update_pkg_metadata now");
1307
	return;
1308
}
1309

    
1310
?>
(34-34/54)