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
	pkg_debug("pkg_call(): {$params}\n");
131
	$process = proc_open("/usr/local/sbin/pkg-static {$params}",
132
	    $descriptorspec, $pipes, '/', pkg_env($extra_env));
133

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

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

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

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

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

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

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

    
175

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

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

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

    
190
	return false;
191
}
192

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

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

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

    
206

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

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

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

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

    
227

    
228
	return proc_close($process);
229
}
230

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

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

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

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

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

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

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

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

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

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

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

    
282
	return $result;
283
}
284

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

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

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

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

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

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

    
313
	foreach ($config['installedpackages']['package'] as $idx => $pkg) {
314
		if ($pkg['name'] == $package_name ||
315
		    get_package_internal_name($pkg) == $package_name) {
316
			return $idx;
317
		}
318
	}
319

    
320
	return -1;
321
}
322

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

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

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

    
341
	unset($pkg_filter);
342

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
465
		$pkg_is_installed = false;
466

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

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

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

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

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

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

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

    
507
	return $result;
508
}
509

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

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

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

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

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

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

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

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

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

    
554

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
698
	return $pkg_config;
699
}
700

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

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

    
708
	return '';
709
}
710

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
883
	return true;
884
}
885

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

    
889

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

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

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

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

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

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

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

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

    
1052
	$pkg_info = get_pkg_info();
1053

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

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

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

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

    
1092
	return true;
1093
}
1094

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

    
1105
	global $config, $g;
1106

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1256
	$result = array($default);
1257

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

    
1278
	return $result;
1279
}
1280

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

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

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

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

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

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

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

    
1309
?>
(34-34/54)