2 |
2 |
/* $Id$ */
|
3 |
3 |
/*
|
4 |
4 |
pkg_mgr_install.php
|
5 |
|
part of pfSense (https://www.pfsense.org)
|
6 |
|
Copyright (C) 2013-2015 Electric Sheep Fencing, LP
|
7 |
|
Copyright (C) 2004-2010 Scott Ullrich <sullrich@gmail.com>
|
8 |
|
Copyright (C) 2005 Colin Smith
|
9 |
|
All rights reserved.
|
10 |
|
|
11 |
|
Redistribution and use in source and binary forms, with or without
|
12 |
|
modification, are permitted provided that the following conditions are met:
|
13 |
|
|
14 |
|
1. Redistributions of source code must retain the above copyright notice,
|
15 |
|
this list of conditions and the following disclaimer.
|
16 |
|
|
17 |
|
2. Redistributions in binary form must reproduce the above copyright
|
18 |
|
notice, this list of conditions and the following disclaimer in the
|
19 |
|
documentation and/or other materials provided with the distribution.
|
20 |
|
|
21 |
|
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
22 |
|
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
23 |
|
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
24 |
|
AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
25 |
|
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
26 |
|
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
27 |
|
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
28 |
|
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
29 |
|
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
30 |
|
POSSIBILITY OF SUCH DAMAGE.
|
31 |
5 |
*/
|
|
6 |
/* ====================================================================
|
|
7 |
* Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved.
|
|
8 |
* Copyright (c) 2004, 2005 Scott Ullrich
|
|
9 |
* Copyright (c) 2005 Colin Smith
|
|
10 |
*
|
|
11 |
* Redistribution and use in source and binary forms, with or without modification,
|
|
12 |
* are permitted provided that the following conditions are met:
|
|
13 |
*
|
|
14 |
* 1. Redistributions of source code must retain the above copyright notice,
|
|
15 |
* this list of conditions and the following disclaimer.
|
|
16 |
*
|
|
17 |
* 2. Redistributions in binary form must reproduce the above copyright
|
|
18 |
* notice, this list of conditions and the following disclaimer in
|
|
19 |
* the documentation and/or other materials provided with the
|
|
20 |
* distribution.
|
|
21 |
*
|
|
22 |
* 3. All advertising materials mentioning features or use of this software
|
|
23 |
* must display the following acknowledgment:
|
|
24 |
* "This product includes software developed by the pfSense Project
|
|
25 |
* for use in the pfSense software distribution. (http://www.pfsense.org/).
|
|
26 |
*
|
|
27 |
* 4. The names "pfSense" and "pfSense Project" must not be used to
|
|
28 |
* endorse or promote products derived from this software without
|
|
29 |
* prior written permission. For written permission, please contact
|
|
30 |
* coreteam@pfsense.org.
|
|
31 |
*
|
|
32 |
* 5. Products derived from this software may not be called "pfSense"
|
|
33 |
* nor may "pfSense" appear in their names without prior written
|
|
34 |
* permission of the Electric Sheep Fencing, LLC.
|
|
35 |
*
|
|
36 |
* 6. Redistributions of any form whatsoever must retain the following
|
|
37 |
* acknowledgment:
|
|
38 |
*
|
|
39 |
* "This product includes software developed by the pfSense Project
|
|
40 |
* for use in the pfSense software distribution (http://www.pfsense.org/).
|
|
41 |
*
|
|
42 |
* THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY
|
|
43 |
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
44 |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
45 |
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR
|
|
46 |
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
47 |
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
48 |
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
49 |
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
50 |
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
51 |
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
52 |
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
|
53 |
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
54 |
*
|
|
55 |
* ====================================================================
|
|
56 |
*
|
|
57 |
*/
|
32 |
58 |
/*
|
33 |
59 |
pfSense_BUILDER_BINARIES: /bin/rm
|
34 |
60 |
pfSense_MODULE: pkgs
|
... | ... | |
55 |
81 |
$static_status = "";
|
56 |
82 |
$sendto = "output";
|
57 |
83 |
|
|
84 |
//---------------------------------------------------------------------------------------------------------------------
|
|
85 |
// After an installation or removal has been started (mwexec(/usr/local/sbin/pfSense-upgrade-GUI.sh . . . )) AJAX calls
|
|
86 |
// are made to get status.
|
|
87 |
// The log file is read, the newest progress record retrieved, and the PID status obtained. The data is formatted
|
|
88 |
// as JSON before being returned to the AJAX caller (at the bottom of this file)
|
|
89 |
//
|
|
90 |
// Arguments received here:
|
|
91 |
// logfilename = Passed to installation script to tell it how to name the log file we will parse
|
|
92 |
// pid = PID of the background install/remove process
|
|
93 |
// next_log_line = Send log file entries that come after this line number
|
|
94 |
//
|
|
95 |
// JSON items returned
|
|
96 |
// log:
|
|
97 |
// exitcode:
|
|
98 |
// data:{current:, total}
|
|
99 |
// pid:
|
|
100 |
//
|
|
101 |
// Todo:
|
|
102 |
// Respect next_log_line and append log to output window rather than writing it
|
|
103 |
|
|
104 |
if($_REQUEST['ajax']) {
|
|
105 |
$response = "";
|
|
106 |
$code = 0;
|
|
107 |
|
|
108 |
// Process log file -----------------------------------------------------------------------------------------------
|
|
109 |
$logfile = fopen($_REQUEST['logfilename'] . '.txt', "r") or die("Unable to open " . $_REQUEST['logfilename']);
|
|
110 |
|
|
111 |
if($logfile != FALSE) {
|
|
112 |
$resparray = array();
|
|
113 |
$statusarray = array('exitstatus' => 'success');
|
|
114 |
|
|
115 |
// Log file is read a line at a time so that we can detect/modify certain entries
|
|
116 |
while (($logline = fgets($logfile)) !== false) {
|
|
117 |
// Check for return codes and replace with suitable strings
|
|
118 |
if(strpos($logline, "_RC=") != false) {
|
|
119 |
$code = str_replace("__RC=", "", $logline);
|
|
120 |
|
|
121 |
if($code == 0) {
|
|
122 |
$logline = gettext("Success"). "\n";
|
|
123 |
} else {
|
|
124 |
$logline = gettext("Failed") . "\n";
|
|
125 |
}
|
|
126 |
|
|
127 |
$response .= $logline;
|
|
128 |
$statusarray = array('exitstatus' => $code);
|
|
129 |
} else {
|
|
130 |
$response .= htmlspecialchars($logline);
|
|
131 |
}
|
|
132 |
}
|
|
133 |
|
|
134 |
fclose($logfile);
|
|
135 |
$resparray['log'] = $response;
|
|
136 |
} else {
|
|
137 |
$resparray['log'] = "not_ready";
|
|
138 |
}
|
|
139 |
|
|
140 |
// Process progress file ------------------------------------------------------------------------------------------
|
|
141 |
$progress = "";
|
|
142 |
|
|
143 |
$JSONfile = fopen($_REQUEST['logfilename'] . '.json', "r") or die("Unable to open " . $_REQUEST['logfilename']);
|
|
144 |
|
|
145 |
if($JSONfile != FALSE) {
|
|
146 |
while (($logline = fgets($JSONfile)) !== false) {
|
|
147 |
if(strpos($logline, 'INFO_PROGRESS_TICK')) {
|
|
148 |
$progress = $logline;
|
|
149 |
}
|
|
150 |
}
|
|
151 |
|
|
152 |
fclose($JSONfile);
|
|
153 |
|
|
154 |
$progarray = array();
|
|
155 |
if(strlen($progress) > 0)
|
|
156 |
$progarray = json_decode($progress, true);
|
|
157 |
|
|
158 |
if(!$progarray)
|
|
159 |
$progarray = array();
|
|
160 |
}
|
|
161 |
|
|
162 |
// Check to see if our process is still running
|
|
163 |
$pidarray = array('pid' => (file_exists("/proc/" . $_REQUEST['pid']) ? "running":"stopped"));
|
|
164 |
|
|
165 |
// Glob all the arrays we have made togethr, and convert to JSON
|
|
166 |
print(json_encode($progarray + $resparray + $pidarray + $statusarray));
|
|
167 |
exit;
|
|
168 |
}
|
|
169 |
|
58 |
170 |
if ($_POST) {
|
59 |
171 |
if (empty($_POST['id']) && $_POST['mode'] != 'reinstallall') {
|
60 |
172 |
header("Location: pkg_mgr_installed.php");
|
... | ... | |
71 |
183 |
case 'showlog':
|
72 |
184 |
break;
|
73 |
185 |
case 'installedinfo':
|
74 |
|
case 'reinstallxml':
|
75 |
186 |
case 'reinstallpkg':
|
76 |
187 |
case 'delete':
|
77 |
188 |
if (empty($_GET['pkg'])) {
|
... | ... | |
96 |
207 |
$tab_array[] = array(gettext("Installed packages"), false, "pkg_mgr_installed.php");
|
97 |
208 |
$tab_array[] = array(gettext("Package Installer"), true, "");
|
98 |
209 |
display_top_tabs($tab_array);
|
|
210 |
|
|
211 |
$start_polling = false;
|
|
212 |
|
99 |
213 |
?>
|
100 |
214 |
<form action="pkg_mgr_install.php" method="post" class="form-horizontal">
|
101 |
215 |
<h2>Add / remove package</h2>
|
... | ... | |
141 |
255 |
<input type="submit" class="btn btn-default" name="pkgcancel" id="pkgcancel" value="Cancel"/>
|
142 |
256 |
</div>
|
143 |
257 |
</div>
|
144 |
|
<?php endif;?>
|
|
258 |
<?php endif;
|
145 |
259 |
|
146 |
|
<?php if (!empty($_POST['id']) || $_GET['mode'] == 'showlog' || ($_GET['mode'] == 'installedinfo' && !empty($_GET['pkg']))):?>
|
|
260 |
if($_POST['mode'] == 'delete') {
|
|
261 |
$modetxt = gettext("removal");
|
|
262 |
} else if($_POST['mode'] == 'reinstallpkg') {
|
|
263 |
$modetxt = gettext("reinstallation");
|
|
264 |
} else {
|
|
265 |
$modetxt = gettext("installation");
|
|
266 |
}
|
|
267 |
|
|
268 |
if (!empty($_POST['id']) || $_GET['mode'] == 'showlog' || ($_GET['mode'] == 'installedinfo' && !empty($_GET['pkg']))):?>
|
|
269 |
|
|
270 |
<div class="progress" style="display: none;">
|
|
271 |
<div id="progressbar" class="progress-bar progress-bar-striped" role="progressbar" aria-valuemin="0" aria-valuemax="100" style="width: 1%"></div>
|
|
272 |
</div>
|
|
273 |
<br />
|
147 |
274 |
<div class="panel panel-default">
|
148 |
275 |
<div class="panel-heading">
|
149 |
|
<h4 id="status"><?=gettext("Beginning package installation.")?></h4>
|
|
276 |
<h2 class="panel-title" id="status"><?=gettext("Beginning package ") . $modetxt?>.</h2>
|
150 |
277 |
</div>
|
151 |
278 |
|
152 |
279 |
<div class="panel-body">
|
153 |
|
<div class="content">
|
154 |
|
<textarea rows="15" class="form-control" id="output"></textarea>
|
155 |
|
<!--
|
156 |
|
<div class="progress">
|
157 |
|
<div id="progressbar" class="progress-bar progress-bar-striped" role="progressbar" aria-valuemin="0" aria-valuemax="100" style="width: 1%"><span class="sr-only">1% Complete</span></div>
|
158 |
|
</div>
|
159 |
|
-->
|
160 |
|
</div>
|
|
280 |
<textarea rows="15" class="form-control" id="output"></textarea>
|
161 |
281 |
</div>
|
162 |
282 |
</div>
|
|
283 |
|
|
284 |
<div id="final" class="alert" role="alert" style=":display: none;"></div>
|
163 |
285 |
<?php endif?>
|
164 |
286 |
</form>
|
165 |
287 |
<?php
|
... | ... | |
200 |
322 |
|
201 |
323 |
switch ($_POST['mode']) {
|
202 |
324 |
case 'delete':
|
203 |
|
uninstall_package($pkgid);
|
204 |
|
update_status(gettext("Package deleted."));
|
205 |
|
$static_output .= "\n" . gettext("Package deleted.");
|
206 |
|
update_output_window($static_output);
|
|
325 |
$pid = mwexec('/usr/local/sbin/pfSense-upgrade-GUI.sh -r ' . $pkgid, false, false, true);
|
|
326 |
$start_polling = true;
|
207 |
327 |
filter_configure();
|
208 |
328 |
break;
|
209 |
|
case 'reinstallxml':
|
210 |
|
// pkg_fetch_config_file($pkgid);
|
211 |
|
// pkg_fetch_additional_files($pkgid);
|
212 |
|
break;
|
|
329 |
|
213 |
330 |
case 'reinstallpkg':
|
214 |
331 |
delete_package_xml($pkgid);
|
215 |
332 |
if (install_package($pkgid) != 0) {
|
... | ... | |
252 |
369 |
break;
|
253 |
370 |
case 'installed':
|
254 |
371 |
default:
|
255 |
|
$status = install_package($pkgid);
|
|
372 |
$pid = mwexec('/usr/local/sbin/pfSense-upgrade-GUI.sh -i ' . $pkgid, false, false, true);
|
|
373 |
$start_polling = true;
|
|
374 |
|
256 |
375 |
if ($status != 0) {
|
257 |
376 |
update_status(gettext("Installation of") . " {$pkgid} " . gettext("FAILED! "));
|
258 |
377 |
$static_output .= "\n" . gettext("Installation halted.");
|
259 |
378 |
update_output_window($static_output);
|
260 |
379 |
} else {
|
261 |
380 |
$status_a = gettext(sprintf("Installation of %s completed.", $pkgid));
|
262 |
|
update_status($status_a);
|
263 |
|
$status = get_after_install_info($pkgid);
|
264 |
|
if ($status) {
|
265 |
|
$static_output .= "\n" . gettext("Installation completed.") . "\n{$pkgid} " . gettext("setup instructions") . ":\n{$status}";
|
266 |
|
} else {
|
267 |
|
$static_output .= "\n" . gettext("Installation completed. Please check to make sure that the package is configured from the respective menu then start the package.");
|
268 |
|
}
|
269 |
|
|
270 |
|
@file_put_contents("/tmp/{$pkgid}.info", $static_output);
|
271 |
|
echo "<script type='text/javascript'>document.location=\"pkg_mgr_install.php?mode=installedinfo&pkg={$pkgid}\";</script>";
|
272 |
381 |
}
|
|
382 |
|
273 |
383 |
filter_configure();
|
274 |
384 |
break;
|
275 |
385 |
}
|
... | ... | |
287 |
397 |
conf_mount_ro();
|
288 |
398 |
}
|
289 |
399 |
|
290 |
|
include('foot.inc')?>
|
|
400 |
include('foot.inc')?>
|
|
401 |
|
|
402 |
<script>
|
|
403 |
|
|
404 |
// Update the progress indicator
|
|
405 |
function setProgress(barName, percent) {
|
|
406 |
$('.progress').show()
|
|
407 |
$('#' + barName).css('width', percent + '%').attr('aria-valuenow', percent);
|
|
408 |
}
|
|
409 |
|
|
410 |
// Display a success banner
|
|
411 |
function show_success() {
|
|
412 |
$('#final').removeClass("alert-info").addClass("alert-success");
|
|
413 |
$('#final').html("<?=$pkgid?>" + " " + "<?=$modetxt?>" + " " + "<?=gettext(' successfully completed')?>");
|
|
414 |
$('#final').show();
|
|
415 |
}
|
|
416 |
|
|
417 |
// Display a failure banner
|
|
418 |
function show_failure() {
|
|
419 |
$('#final').addClass("alert-danger");
|
|
420 |
$('#final').html("<?=$pkgid?>" + " " + "<?=$modetxt?>" + " " + "<?=gettext(' failed!')?>");
|
|
421 |
$('#final').show();
|
|
422 |
}
|
|
423 |
|
|
424 |
// Ask the user to wait a bit
|
|
425 |
function show_info() {
|
|
426 |
$('#final').addClass("alert-info");
|
|
427 |
$('#final').html("Please wait while the " + "<?=$modetxt?>" + " of " + "<?=$pkgid?>" + " " + "completes." + "<br />(Some packages may take several minutes!)");
|
|
428 |
$('#final').show();
|
|
429 |
}
|
|
430 |
|
|
431 |
function getLogsStatus() {
|
|
432 |
var ajaxRequest;
|
|
433 |
var repeat;
|
|
434 |
var progress;
|
|
435 |
|
|
436 |
repeat = true;
|
|
437 |
|
|
438 |
ajaxRequest = $.ajax({
|
|
439 |
url: "pkg_mgr_install.php",
|
|
440 |
type: "post",
|
|
441 |
data: { ajax: "ajax",
|
|
442 |
pid: "<?=$pid?>",
|
|
443 |
logfilename: "/cf/conf/webgui-log",
|
|
444 |
next_log_line: "0"
|
|
445 |
}
|
|
446 |
});
|
|
447 |
|
|
448 |
// Deal with the results of the above ajax call
|
|
449 |
ajaxRequest.done(function (response, textStatus, jqXHR) {
|
|
450 |
var json = new Object;
|
|
451 |
json = jQuery.parseJSON(response);
|
|
452 |
|
|
453 |
if(json.log != "not ready") {
|
|
454 |
// Write the log file to the "output" textarea
|
|
455 |
$('#output').html(json.log);
|
|
456 |
$('#output').scrollTop($('#output')[0].scrollHeight);
|
|
457 |
|
|
458 |
// Update the progress bar
|
|
459 |
progress = 0;
|
|
460 |
if(json.data) {
|
|
461 |
setProgress('progressbar', ((json.data.current * 100) / json.data.total));
|
|
462 |
progress = json.data.total - json.data.current
|
|
463 |
}
|
|
464 |
|
|
465 |
// Now we need to determine if the installation/removal was successful, and tell the user. Not as easy as it sounds :)
|
|
466 |
if((json.pid == "stopped") && (progress == 0) && (json.exitstatus == 0)) {
|
|
467 |
show_success();
|
|
468 |
repeat = false;
|
|
469 |
}
|
|
470 |
|
|
471 |
if((json.pid == "stopped") && ((progress != 0) || (json.exitstatus != 0))) {
|
|
472 |
show_failure();
|
|
473 |
repeat = false;
|
|
474 |
}
|
|
475 |
|
|
476 |
if((json.pid == "stopped") && ((progress != 0) || (json.exitstatus != 0))) {
|
|
477 |
show_failure();
|
|
478 |
repeat = false;
|
|
479 |
}
|
|
480 |
|
|
481 |
// ToDo: THere are more end conditions we need to catch
|
|
482 |
}
|
|
483 |
|
|
484 |
// And maybe do it again
|
|
485 |
if(repeat)
|
|
486 |
setTimeout(getLogsStatus, 500);
|
|
487 |
});
|
|
488 |
}
|
|
489 |
|
|
490 |
if("<?=$start_polling?>") {
|
|
491 |
setTimeout(getLogsStatus, 500);
|
|
492 |
show_info();
|
|
493 |
}
|
|
494 |
|
|
495 |
</script>
|
Multiple changes to clean up the package installaion system by causing the installer to run in the background
whilst monitoring its output via AJAX to provide a better GUI display.
Work in progress !