Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added a panel to show the status of the apt packages #75

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ In its [Web](http://www.ezservermonitor.com/esm-web/features) version eSM is a P
- **Last login** : display last 5 user connections
- **Ping** : ping the hosts defined in the configuration file
- **Services** : displays the status (up or down) services defined in the configuration file
- **Package Update Status** : displays the status of pending package updates for `apt` based systems

Several themes are available !

Expand Down
6 changes: 5 additions & 1 deletion conf/esm.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,5 +67,9 @@
"protocol": "tcp"
}
]
}
},
"package_management": {
"apt": false,
"apt_update_before_check": false
}
}
17 changes: 16 additions & 1 deletion index.php
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,22 @@

</div>


<?php if ($Config->get('package_management:apt') == true): ?>
<div class="box column-left column-33" id="esm-apt">
<div class="box-header">
<h1>Package Update Status</h1>
<ul>
<li><a href="#" class="reload" onclick="esm.reloadBlock('apt');"><span class="icon-cycle"></span></a></li>
</ul>
</div>

<div class="box-content">
<table>
<tbody></tbody>
</table>
</div>
</div>
<?php endif; ?>

<div class="cls"></div>

Expand Down
44 changes: 42 additions & 2 deletions js/esm.js
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,44 @@ esm.getServices = function() {

}

esm.getAptStatus = function() {
var module = 'apt';

esm.reloadBlock_spin(module);

$.get('libs/'+module+'.php', function(data) {
var $box = $('.box#esm-'+module+' .box-content tbody');
$box.empty();

var html = '';

if( data.status === 0 ) {
console.log("apt-status", data);

var package_color = data.standard > 0 ? 'label success' : '';
var security_color = data.security > 0 ? 'label error' : '';

html += '<tr>';
html += '<td>Available Package Updates</td>';
html += '<td class="w5p"><span class="'+package_color+'">'+data.standard+'</span></td>';
html += '</tr>';
html += '<tr>';
html += '<td>Available Security Updates</td>';
html += '<td class="w5p"><span class="'+security_color+'">'+data.security+'</span></td>';
html += '</tr>';
} else {
// If the module isn't disabled, something else went wrong
if( data.status !== 1 ) {
console.error("Unable to retrieve package updates", data);
}
}

$box.append(html);

esm.reloadBlock_spin(module);
}, 'json');
}


esm.getAll = function() {
esm.getSystem();
Expand All @@ -306,6 +344,7 @@ esm.getAll = function() {
esm.getNetwork();
esm.getPing();
esm.getServices();
esm.getAptStatus();
}

esm.reloadBlock = function(block) {
Expand Down Expand Up @@ -367,5 +406,6 @@ esm.mapping = {
last_login: esm.getLast_login,
network: esm.getNetwork,
ping: esm.getPing,
services: esm.getServices
};
services: esm.getServices,
apt: esm.getAptStatus
};
149 changes: 149 additions & 0 deletions libs/apt.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
<?php
require '../autoload.php';
$Config = new Config();

/*
Apt package status - This lib checks the status of the packages installed on the system and
returns the number of packages that can be upgraded and how many are security updates.

This lib is specific to Linux distributions that use the APT Package Management System such as
Debian and Ubuntu.

'apt-check' notes:
* apt-check is the utility used by apt to determine if there are packages available in Ubuntu
distributions.
* If called with no parameters, it returns with a tuple of numbers in the format: <standard>;<security>
- 'standard' is an int representing the upgrade packages available
- 'security' is an int representing the security upgrade packages available
* At least on Ubuntu 20.x, the path of the 'apt-check' utility is
'/usr/lib/update-notifier/apt-check'. The utility and it's path will need to be validated
on other Ubuntu versions.
* The results of apt-check are cached on the OS side of things and thus, if we can, we should
prefer the use of this command.

'apt-get' notes:
* The command `apt-get update` **must** be run before this will report the correct number of
packages. As this will need to be run with super user privileges, it is recommended that a
simple cron job or timer script be configured for this job.
* It is possible to enable it directly by putting 'apt_update_before_check' parameter to true.
Note that you'll then need to grant sudo apt-get rights to www-data:
`sudo visudo`
`www-data ALL=(ALL) NOPASSWD: /bin/apt-get`
<!> ACTIVATING IT MAY HAVE IMPACT ON SECURITY OF YOUR COMPUTER SO USE IT WITH CAUTION
<!> THIS APPROACH WILL ALSO SLOW DOWN PAGE LOAD
* The `apt-get` approach is used if the `apt-check` command cannot be found. Most likely, it
means that this script is not running in an Ubuntu environment.
* Basically, this calls and filters 'apt-get --simulate dist-upgrade'. If this call is
successful, the results are filtered with php commands to get the number of standard and
security updates.
* This call is not cached and can take a bit of time to complete.
* In the grand scheme of things, this is basically running these two CLI commands:
- 'apt-get --simulate dist-upgrade |grep "^Inst" |grep --ignore-case securi |wc --lines'
- 'apt-get --simulate dist-upgrade |grep "^Inst" |wc --lines'

Configuration / Usage
* The property 'package_management:apt' must be in the 'esm.config.json' file with the property
of 'true' for this library to execute correctly. See the example 'esm.config.json' for the
syntax.
* Status Values & Messages:
- `0` - 'Success' will be set if everything was successful.
- `1` - 'Disabled' will be set if this library is disabled.
- `2` - 'Failure' will be set if apt-check failed to execute. Can have additional
information appended to the message.
- `3` - 'Not Available' will be set if apt-check could not be located.
- `-1` - 'Unknown' or other messages will be set for any other reason.
* The JSON return structure schema:
- (int) `status` - the status of the call, can be negative.
- (string) `message` - the result of the call in freetext form.
- (int) `standard` - the number of packages awaiting upgrade. Optional
- (int) `security` - on success, the number of security packages awaiting upgrade. Optional
*/

$configKey = 'package_management:apt';
$optionalUpdateKey = 'package_management:apt_update_before_check';

// The command paths. Intentionally not configurable to prevent remote execution bugs.
$apt_get_root_path = '/bin/apt-get';
$apt_get_usr_path = '/usr/bin/apt-get';
$apt_get_path = '';
$apt_check_path = '/usr/lib/update-notifier/apt-check';

// Quickly find the apt-get path, just in case.
if(file_exists($apt_get_root_path)) {
$apt_get_path = $apt_get_root_path;
} else if( file_exists($apt_get_usr_path)) {
$apt_get_path = $apt_get_usr_path;
}

$datas = array();

// TODO Determine how to test for the existence of a key before trying to access it. If you load a
// non-existent key with $Config->get($configKey), you'll get the entire configuration back. If you
// try to load a partially existing key (e.g. the 'package_management' header exists but not the
// 'apt' key) you'll get nothing back. Return a status of -1, message of 'not configured'.
if ($Config->get($configKey) == false ) {
$datas['status'] = 1;
$datas['message'] = 'Disabled';
} elseif ($Config->get($configKey) == true ) {
// Check each command path for existance & if it's executable.
if( file_exists($apt_check_path) && is_executable($apt_check_path) ) {
$command_path = $apt_check_path;
// apt-check outputs to stderr, 2>&1 concat's stderr to stdout
$options = '2>&1';

$command = $command_path . " " . $options;

$execresult = exec($command, $output, $retval);

if( $retval == 0 ) {
$items = explode(';', $output[0]);
$datas['status'] = 0;
$datas['message'] = 'Success';
$datas['standard'] = $items[0];
$datas['security'] = $items[1];
} else {
$datas['status'] = 2;
$datas['message'] = 'apt-check failure - error code ' . $retval;
}
} else if ( $apt_get_path != '' && file_exists($apt_get_path) && is_executable($apt_get_path) ) {
// If requested in config, update apt (getting latest infos from apt server) before doing an apt.
// WARNING (security potential issue): sudo apt-get will then need to be allowed for www-data user
if ($Config->get($optionalUpdateKey) == true) {
$updateCommand = 'sudo --non-interactive ' . $apt_get_path . ' --quiet --yes update';
$execresult = exec($updateCommand, $output, $retval);
if ( $retval != 0 ) {
error_log("Failed to execute '$updateCommand' from php script");
}
}

$command_path = $apt_get_path;

$options = "--simulate dist-upgrade";
$command = $command_path . " " . $options;

$execresult = exec($command, $output, $retval);

if( $retval == 0 ) {
// Success - now filter the results
$standard = preg_grep('/^Inst/', $output);
$security = preg_grep('/securi/i', $standard);

$datas['status'] = 0;
$datas['message'] = 'Success';
$datas['standard'] = sizeof($standard);
$datas['security'] = sizeof($security);
} else {
$datas['status'] = 2;
$datas['message'] = 'apt-get failure - error code ' . $retval;
}
} else {
$datas['status'] = 3;
$datas['message'] = 'Not Available';
}
} else {
// Not sure what's going on here....
$datas['status'] = -1;
$datas['message'] = 'Unknown';
}

echo json_encode($datas);