path-util: add a function to peek into a container and guess systemd version

This is a bit crude and only works for new systemd versions which
have libsystemd-shared.
This commit is contained in:
Zbigniew Jędrzejewski-Szmek 2016-10-02 15:51:27 +02:00
parent 1644102735
commit 5a46d55fc8
3 changed files with 89 additions and 0 deletions

View file

@ -34,9 +34,11 @@
#include "alloc-util.h"
#include "extract-word.h"
#include "fs-util.h"
#include "glob-util.h"
#include "log.h"
#include "macro.h"
#include "missing.h"
#include "parse-util.h"
#include "path-util.h"
#include "stat-util.h"
#include "string-util.h"
@ -814,3 +816,69 @@ bool is_device_path(const char *path) {
path_startswith(path, "/dev/") ||
path_startswith(path, "/sys/");
}
int systemd_installation_has_version(const char *root, unsigned minimal_version) {
const char *pattern;
int r;
/* Try to guess if systemd installation is later than the specified version. This
* is hacky and likely to yield false negatives, particularly if the installation
* is non-standard. False positives should be relatively rare.
*/
NULSTR_FOREACH(pattern,
/* /lib works for systems without usr-merge, and for systems with a sane
* usr-merge, where /lib is a symlink to /usr/lib. /usr/lib is necessary
* for Gentoo which does a merge without making /lib a symlink.
*/
"lib/systemd/libsystemd-shared-*.so\0"
"usr/lib/systemd/libsystemd-shared-*.so\0") {
_cleanup_strv_free_ char **names = NULL;
_cleanup_free_ char *path = NULL;
char *c, **name;
path = prefix_root(root, pattern);
if (!path)
return -ENOMEM;
r = glob_extend(&names, path);
if (r == -ENOENT)
continue;
if (r < 0)
return r;
assert_se((c = endswith(path, "*.so")));
*c = '\0'; /* truncate the glob part */
STRV_FOREACH(name, names) {
/* This is most likely to run only once, hence let's not optimize anything. */
char *t, *t2;
unsigned version;
t = startswith(*name, path);
if (!t)
continue;
t2 = endswith(t, ".so");
if (!t2)
continue;
t2[0] = '\0'; /* truncate the suffix */
r = safe_atou(t, &version);
if (r < 0) {
log_debug_errno(r, "Found libsystemd shared at \"%s.so\", but failed to parse version: %m", *name);
continue;
}
log_debug("Found libsystemd shared at \"%s.so\", version %u (%s).",
*name, version,
version >= minimal_version ? "OK" : "too old");
if (version >= minimal_version)
return true;
}
}
return false;
}

View file

@ -125,3 +125,5 @@ char *file_in_same_dir(const char *path, const char *filename);
bool hidden_or_backup_file(const char *filename) _pure_;
bool is_device_path(const char *path);
int systemd_installation_has_version(const char *root, unsigned minimal_version);

View file

@ -511,7 +511,24 @@ static void test_hidden_or_backup_file(void) {
assert_se(!hidden_or_backup_file("test.dpkg-old.foo"));
}
static void test_systemd_installation_has_version(const char *path) {
int r;
const unsigned versions[] = {0, 231, atoi(PACKAGE_VERSION), 999};
unsigned i;
for (i = 0; i < ELEMENTSOF(versions); i++) {
r = systemd_installation_has_version(path, versions[i]);
assert_se(r >= 0);
log_info("%s has systemd >= %u: %s",
path ?: "Current installation", versions[i], yes_no(r));
}
}
int main(int argc, char **argv) {
log_set_max_level(LOG_DEBUG);
log_parse_environment();
log_open();
test_path();
test_find_binary(argv[0]);
test_prefixes();
@ -526,5 +543,7 @@ int main(int argc, char **argv) {
test_filename_is_valid();
test_hidden_or_backup_file();
test_systemd_installation_has_version(argv[1]); /* NULL is OK */
return 0;
}