Merge pull request #7335 from poettering/dissect-meta-info

beef up image dissection, to gather image metadata
This commit is contained in:
Lennart Poettering 2017-11-21 11:58:31 +01:00 committed by GitHub
commit 54c552eae6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 685 additions and 172 deletions

View file

@ -25,6 +25,8 @@
#include <sys/utsname.h>
#include <unistd.h>
#include "alloc-util.h"
#include "def.h"
#include "fd-util.h"
#include "fileio.h"
#include "hostname-util.h"
@ -219,35 +221,55 @@ int sethostname_idempotent(const char *s) {
return 1;
}
int read_hostname_config(const char *path, char **hostname) {
_cleanup_fclose_ FILE *f = NULL;
char l[LINE_MAX];
char *name = NULL;
int read_etc_hostname_stream(FILE *f, char **ret) {
int r;
assert(path);
assert(hostname);
assert(f);
assert(ret);
for (;;) {
_cleanup_free_ char *line = NULL;
char *p;
r = read_line(f, LONG_LINE_MAX, &line);
if (r < 0)
return r;
if (r == 0) /* EOF without any hostname? the file is empty, let's treat that exactly like no file at all: ENOENT */
return -ENOENT;
p = strstrip(line);
/* File may have empty lines or comments, ignore them */
if (!IN_SET(*p, '\0', '#')) {
char *copy;
hostname_cleanup(p); /* normalize the hostname */
if (!hostname_is_valid(p, true)) /* check that the hostname we return is valid */
return -EBADMSG;
copy = strdup(p);
if (!copy)
return -ENOMEM;
*ret = copy;
return 0;
}
}
}
int read_etc_hostname(const char *path, char **ret) {
_cleanup_fclose_ FILE *f = NULL;
assert(ret);
if (!path)
path = "/etc/hostname";
f = fopen(path, "re");
if (!f)
return -errno;
/* may have comments, ignore them */
FOREACH_LINE(l, f, return -errno) {
truncate_nl(l);
if (!IN_SET(l[0], '\0', '#')) {
/* found line with value */
name = hostname_cleanup(l);
name = strdup(name);
if (!name)
return -ENOMEM;
break;
}
}
return read_etc_hostname_stream(f, ret);
if (!name)
/* no non-empty line found */
return -ENOENT;
*hostname = name;
return 0;
}

View file

@ -39,4 +39,5 @@ bool is_gateway_hostname(const char *hostname);
int sethostname_idempotent(const char *s);
int read_hostname_config(const char *path, char **hostname);
int read_etc_hostname_stream(FILE *f, char **ret);
int read_etc_hostname(const char *path, char **ret);

View file

@ -45,11 +45,11 @@ static inline void block_signals_reset(sigset_t *ss) {
assert_se(sigprocmask(SIG_SETMASK, ss, NULL) >= 0);
}
#define BLOCK_SIGNALS(...) \
_cleanup_(block_signals_reset) _unused_ sigset_t _saved_sigset = ({ \
sigset_t t; \
assert_se(sigprocmask_many(SIG_BLOCK, &t, __VA_ARGS__, -1) >= 0); \
t; \
#define BLOCK_SIGNALS(...) \
_cleanup_(block_signals_reset) _unused_ sigset_t _saved_sigset = ({ \
sigset_t _t; \
assert_se(sigprocmask_many(SIG_BLOCK, &_t, __VA_ARGS__, -1) >= 0); \
_t; \
})
static inline bool SIGNAL_VALID(int signo) {

View file

@ -181,3 +181,11 @@ char **strv_skip(char **l, size_t n);
int strv_extend_n(char ***l, const char *value, size_t n);
int fputstrv(FILE *f, char **l, const char *separator, bool *space);
#define strv_free_and_replace(a, b) \
({ \
strv_free(a); \
(a) = (b); \
(b) = NULL; \
0; \
})

View file

@ -37,7 +37,7 @@ int hostname_setup(void) {
const char *hn;
int r;
r = read_hostname_config("/etc/hostname", &b);
r = read_etc_hostname(NULL, &b);
if (r < 0) {
if (r == -ENOENT)
enoent = true;

View file

@ -28,6 +28,7 @@
#include "log.h"
#include "loop-util.h"
#include "string-util.h"
#include "strv.h"
#include "util.h"
static enum {
@ -264,6 +265,36 @@ int main(int argc, char *argv[]) {
putchar('\n');
}
r = dissected_image_acquire_metadata(m);
if (r < 0) {
log_error_errno(r, "Failed to acquire image metadata: %m");
goto finish;
}
if (m->hostname)
printf(" Hostname: %s\n", m->hostname);
if (!sd_id128_is_null(m->machine_id))
printf("Machine ID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(m->machine_id));
if (!strv_isempty(m->machine_info)) {
char **p, **q;
STRV_FOREACH_PAIR(p, q, m->machine_info)
printf("%s %s=%s\n",
p == m->machine_info ? "Mach. Info:" : " ",
*p, *q);
}
if (!strv_isempty(m->os_release)) {
char **p, **q;
STRV_FOREACH_PAIR(p, q, m->os_release)
printf("%s %s=%s\n",
p == m->os_release ? "OS Release:" : " ",
*p, *q);
}
break;
}

View file

@ -96,7 +96,7 @@ static int context_read_data(Context *c) {
if (!c->data[PROP_HOSTNAME])
return -ENOMEM;
r = read_hostname_config("/etc/hostname", &c->data[PROP_STATIC_HOSTNAME]);
r = read_etc_hostname(NULL, &c->data[PROP_STATIC_HOSTNAME]);
if (r < 0 && r != -ENOENT)
return r;

View file

@ -290,124 +290,68 @@ int bus_image_method_set_limit(
return sd_bus_reply_method_return(message, NULL);
}
#define EXIT_NOT_FOUND 2
int bus_image_method_get_hostname(
sd_bus_message *message,
void *userdata,
sd_bus_error *error) {
static int directory_image_get_os_release(Image *image, char ***ret, sd_bus_error *error) {
_cleanup_free_ char *path = NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
Image *image = userdata;
int r;
assert(image);
assert(ret);
r = chase_symlinks("/etc/os-release", image->path, CHASE_PREFIX_ROOT, &path);
if (r == -ENOENT)
r = chase_symlinks("/usr/lib/os-release", image->path, CHASE_PREFIX_ROOT, &path);
if (r == -ENOENT)
return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Image does not contain OS release information");
if (r < 0)
return sd_bus_error_set_errnof(error, r, "Failed to resolve %s: %m", image->path);
r = load_env_file_pairs(NULL, path, NULL, ret);
if (r < 0)
return sd_bus_error_set_errnof(error, r, "Failed to open %s: %m", path);
return 0;
}
static int raw_image_get_os_release(Image *image, char ***ret, sd_bus_error *error) {
_cleanup_(rmdir_and_freep) char *t = NULL;
_cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
_cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
_cleanup_(sigkill_waitp) pid_t child = 0;
_cleanup_close_pair_ int pair[2] = { -1, -1 };
_cleanup_fclose_ FILE *f = NULL;
_cleanup_strv_free_ char **v = NULL;
siginfo_t si;
int r;
assert(image);
assert(ret);
r = mkdtemp_malloc("/tmp/machined-root-XXXXXX", &t);
if (r < 0)
return sd_bus_error_set_errnof(error, r, "Failed to create temporary directory: %m");
r = loop_device_make_by_path(image->path, O_RDONLY, &d);
if (r < 0)
return sd_bus_error_set_errnof(error, r, "Failed to set up loop block device for %s: %m", image->path);
r = dissect_image(d->fd, NULL, 0, DISSECT_IMAGE_REQUIRE_ROOT, &m);
if (r == -ENOPKG)
return sd_bus_error_set_errnof(error, r, "Disk image %s not understood: %m", image->path);
if (r < 0)
return sd_bus_error_set_errnof(error, r, "Failed to dissect image %s: %m", image->path);
if (pipe2(pair, O_CLOEXEC) < 0)
return sd_bus_error_set_errnof(error, errno, "Failed to create communication pipe: %m");
child = raw_clone(SIGCHLD|CLONE_NEWNS);
if (child < 0)
return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m");
if (child == 0) {
int fd;
pair[0] = safe_close(pair[0]);
/* Make sure we never propagate to the host */
if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) < 0)
_exit(EXIT_FAILURE);
r = dissected_image_mount(m, t, DISSECT_IMAGE_READ_ONLY);
if (!image->metadata_valid) {
r = image_read_metadata(image);
if (r < 0)
_exit(EXIT_FAILURE);
r = mount_move_root(t);
if (r < 0)
_exit(EXIT_FAILURE);
fd = open("/etc/os-release", O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (fd < 0 && errno == ENOENT) {
fd = open("/usr/lib/os-release", O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (fd < 0 && errno == ENOENT)
_exit(EXIT_NOT_FOUND);
}
if (fd < 0)
_exit(EXIT_FAILURE);
r = copy_bytes(fd, pair[1], (uint64_t) -1, 0);
if (r < 0)
_exit(EXIT_FAILURE);
_exit(EXIT_SUCCESS);
return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
}
pair[1] = safe_close(pair[1]);
return sd_bus_reply_method_return(message, "s", image->hostname);
}
f = fdopen(pair[0], "re");
if (!f)
return -errno;
int bus_image_method_get_machine_id(
sd_bus_message *message,
void *userdata,
sd_bus_error *error) {
pair[0] = -1;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
Image *image = userdata;
int r;
r = load_env_file_pairs(f, "os-release", NULL, &v);
if (!image->metadata_valid) {
r = image_read_metadata(image);
if (r < 0)
return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
}
r = sd_bus_message_new_method_return(message, &reply);
if (r < 0)
return r;
r = wait_for_terminate(child, &si);
if (sd_id128_is_null(image->machine_id)) /* Add an empty array if the ID is zero */
r = sd_bus_message_append(reply, "ay", 0);
else
r = sd_bus_message_append_array(reply, 'y', image->machine_id.bytes, 16);
if (r < 0)
return sd_bus_error_set_errnof(error, r, "Failed to wait for child: %m");
child = 0;
if (si.si_code == CLD_EXITED && si.si_status == EXIT_NOT_FOUND)
return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Image does not contain OS release information");
if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Child died abnormally.");
return r;
*ret = v;
v = NULL;
return sd_bus_send(NULL, reply, NULL);
}
return 0;
int bus_image_method_get_machine_info(
sd_bus_message *message,
void *userdata,
sd_bus_error *error) {
Image *image = userdata;
int r;
if (!image->metadata_valid) {
r = image_read_metadata(image);
if (r < 0)
return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
}
return bus_reply_pair_array(message, image->machine_info);
}
int bus_image_method_get_os_release(
@ -415,34 +359,16 @@ int bus_image_method_get_os_release(
void *userdata,
sd_bus_error *error) {
_cleanup_release_lock_file_ LockFile tree_global_lock = LOCK_FILE_INIT, tree_local_lock = LOCK_FILE_INIT;
_cleanup_strv_free_ char **v = NULL;
Image *image = userdata;
int r;
r = image_path_lock(image->path, LOCK_SH|LOCK_NB, &tree_global_lock, &tree_local_lock);
if (r < 0)
return sd_bus_error_set_errnof(error, r, "Failed to lock image: %m");
switch (image->type) {
case IMAGE_DIRECTORY:
case IMAGE_SUBVOLUME:
r = directory_image_get_os_release(image, &v, error);
break;
case IMAGE_RAW:
case IMAGE_BLOCK:
r = raw_image_get_os_release(image, &v, error);
break;
default:
assert_not_reached("Unknown image type");
if (!image->metadata_valid) {
r = image_read_metadata(image);
if (r < 0)
return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
}
if (r < 0)
return r;
return bus_reply_pair_array(message, v);
return bus_reply_pair_array(message, image->os_release);
}
const sd_bus_vtable image_vtable[] = {
@ -462,6 +388,9 @@ const sd_bus_vtable image_vtable[] = {
SD_BUS_METHOD("Clone", "sb", NULL, bus_image_method_clone, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("MarkReadOnly", "b", NULL, bus_image_method_mark_read_only, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetLimit", "t", NULL, bus_image_method_set_limit, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("GetHostname", NULL, "s", bus_image_method_get_hostname, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("GetMachineID", NULL, "ay", bus_image_method_get_machine_id, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("GetMachineInfo", NULL, "a{ss}", bus_image_method_get_machine_info, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("GetOSRelease", NULL, "a{ss}", bus_image_method_get_os_release, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_VTABLE_END
};

View file

@ -34,4 +34,7 @@ int bus_image_method_rename(sd_bus_message *message, void *userdata, sd_bus_erro
int bus_image_method_clone(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_image_method_mark_read_only(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_image_method_set_limit(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_image_method_get_hostname(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_image_method_get_machine_id(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_image_method_get_machine_info(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_image_method_get_os_release(sd_bus_message *message, void *userdata, sd_bus_error *error);

View file

@ -900,6 +900,100 @@ static int show_machine(int argc, char *argv[], void *userdata) {
return r;
}
static int print_image_hostname(sd_bus *bus, const char *name) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
const char *hn;
int r;
r = sd_bus_call_method(
bus,
"org.freedesktop.machine1",
"/org/freedesktop/machine1",
"org.freedesktop.machine1.Manager",
"GetImageHostname",
NULL, &reply, "s", name);
if (r < 0)
return r;
r = sd_bus_message_read(reply, "s", &hn);
if (r < 0)
return r;
if (!isempty(hn))
printf("\tHostname: %s\n", hn);
return 0;
}
static int print_image_machine_id(sd_bus *bus, const char *name) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
sd_id128_t id = SD_ID128_NULL;
const void *p;
size_t size;
int r;
r = sd_bus_call_method(
bus,
"org.freedesktop.machine1",
"/org/freedesktop/machine1",
"org.freedesktop.machine1.Manager",
"GetImageMachineID",
NULL, &reply, "s", name);
if (r < 0)
return r;
r = sd_bus_message_read_array(reply, 'y', &p, &size);
if (r < 0)
return r;
if (size == sizeof(sd_id128_t))
memcpy(&id, p, size);
if (!sd_id128_is_null(id))
printf(" Machine ID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(id));
return 0;
}
static int print_image_machine_info(sd_bus *bus, const char *name) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_strv_free_ char **l = NULL;
int r;
r = sd_bus_call_method(
bus,
"org.freedesktop.machine1",
"/org/freedesktop/machine1",
"org.freedesktop.machine1.Manager",
"GetImageMachineInfo",
NULL, &reply, "s", name);
if (r < 0)
return r;
r = sd_bus_message_enter_container(reply, 'a', "{ss}");
if (r < 0)
return r;
for (;;) {
const char *p, *q;
r = sd_bus_message_read(reply, "{ss}", &p, &q);
if (r < 0)
return r;
if (r == 0)
break;
if (streq(p, "DEPLOYMENT"))
printf(" Deployment: %s\n", q);
}
r = sd_bus_message_exit_container(reply);
if (r < 0)
return r;
return 0;
}
typedef struct ImageStatusInfo {
char *name;
char *path;
@ -914,12 +1008,13 @@ typedef struct ImageStatusInfo {
} ImageStatusInfo;
static void image_status_info_clear(ImageStatusInfo *info) {
if (info) {
free(info->name);
free(info->path);
free(info->type);
zero(*info);
}
if (!info)
return;
free(info->name);
free(info->path);
free(info->type);
zero(*info);
}
static void print_image_status_info(sd_bus *bus, ImageStatusInfo *i) {
@ -942,6 +1037,10 @@ static void print_image_status_info(sd_bus *bus, ImageStatusInfo *i) {
if (i->path)
printf("\t Path: %s\n", i->path);
(void) print_image_hostname(bus, i->name);
(void) print_image_machine_id(bus, i->name);
(void) print_image_machine_info(bus, i->name);
print_os_release(bus, "GetImageOSRelease", i->name, "\t OS: ");
printf("\t RO: %s%s%s\n",

View file

@ -846,6 +846,78 @@ static int method_mark_image_read_only(sd_bus_message *message, void *userdata,
return bus_image_method_mark_read_only(message, i, error);
}
static int method_get_image_hostname(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_(image_unrefp) Image *i = NULL;
const char *name;
int r;
assert(message);
r = sd_bus_message_read(message, "s", &name);
if (r < 0)
return r;
if (!image_name_is_valid(name))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", name);
r = image_find(name, &i);
if (r < 0)
return r;
if (r == 0)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_IMAGE, "No image '%s' known", name);
i->userdata = userdata;
return bus_image_method_get_hostname(message, i, error);
}
static int method_get_image_machine_id(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_(image_unrefp) Image *i = NULL;
const char *name;
int r;
assert(message);
r = sd_bus_message_read(message, "s", &name);
if (r < 0)
return r;
if (!image_name_is_valid(name))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", name);
r = image_find(name, &i);
if (r < 0)
return r;
if (r == 0)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_IMAGE, "No image '%s' known", name);
i->userdata = userdata;
return bus_image_method_get_machine_id(message, i, error);
}
static int method_get_image_machine_info(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_(image_unrefp) Image *i = NULL;
const char *name;
int r;
assert(message);
r = sd_bus_message_read(message, "s", &name);
if (r < 0)
return r;
if (!image_name_is_valid(name))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", name);
r = image_find(name, &i);
if (r < 0)
return r;
if (r == 0)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_IMAGE, "No image '%s' known", name);
i->userdata = userdata;
return bus_image_method_get_machine_info(message, i, error);
}
static int method_get_image_os_release(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_(image_unrefp) Image *i = NULL;
const char *name;
@ -1442,6 +1514,9 @@ const sd_bus_vtable manager_vtable[] = {
SD_BUS_METHOD("RenameImage", "ss", NULL, method_rename_image, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("CloneImage", "ssb", NULL, method_clone_image, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("MarkImageReadOnly", "sb", NULL, method_mark_image_read_only, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("GetImageHostname", "s", "s", method_get_image_hostname, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("GetImageMachineID", "s", "ay", method_get_image_machine_id, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("GetImageMachineInfo", "s", "a{ss}", method_get_image_machine_info, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("GetImageOSRelease", "s", "a{ss}", method_get_image_os_release, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetPoolLimit", "t", NULL, method_set_pool_limit, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetImageLimit", "st", NULL, method_set_image_limit, SD_BUS_VTABLE_UNPRIVILEGED),

View file

@ -120,6 +120,18 @@
send_interface="org.freedesktop.machine1.Manager"
send_member="SetImageLimit"/>
<allow send_destination="org.freedesktop.machine1"
send_interface="org.freedesktop.machine1.Manager"
send_member="GetImageHostname"/>
<allow send_destination="org.freedesktop.machine1"
send_interface="org.freedesktop.machine1.Manager"
send_member="GetImageMachineID"/>
<allow send_destination="org.freedesktop.machine1"
send_interface="org.freedesktop.machine1.Manager"
send_member="GetImageMachineInfo"/>
<allow send_destination="org.freedesktop.machine1"
send_interface="org.freedesktop.machine1.Manager"
send_member="GetImageOSRelease"/>
@ -204,6 +216,18 @@
send_interface="org.freedesktop.machine1.Image"
send_member="MarkReadOnly"/>
<allow send_destination="org.freedesktop.machine1"
send_interface="org.freedesktop.machine1.Image"
send_member="GetHostname"/>
<allow send_destination="org.freedesktop.machine1"
send_interface="org.freedesktop.machine1.Image"
send_member="GetMachineID"/>
<allow send_destination="org.freedesktop.machine1"
send_interface="org.freedesktop.machine1.Image"
send_member="GetMachineInfo"/>
<allow send_destination="org.freedesktop.machine1"
send_interface="org.freedesktop.machine1.Image"
send_member="GetOSRelease"/>

View file

@ -25,19 +25,28 @@
#endif
#endif
#include <sys/mount.h>
#include <sys/prctl.h>
#include <sys/wait.h>
#include "architecture.h"
#include "ask-password-api.h"
#include "blkid-util.h"
#include "copy.h"
#include "def.h"
#include "dissect-image.h"
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
#include "gpt.h"
#include "hexdecoct.h"
#include "hostname-util.h"
#include "id128-util.h"
#include "linux-3.13/dm-ioctl.h"
#include "mount-util.h"
#include "path-util.h"
#include "process-util.h"
#include "raw-clone.h"
#include "signal-util.h"
#include "stat-util.h"
#include "stdio-util.h"
#include "string-table.h"
@ -635,6 +644,10 @@ DissectedImage* dissected_image_unref(DissectedImage *m) {
free(m->partitions[i].decrypted_node);
}
free(m->hostname);
strv_free(m->machine_info);
strv_free(m->os_release);
free(m);
return NULL;
}
@ -1193,6 +1206,174 @@ int root_hash_load(const char *image, void **ret, size_t *ret_size) {
return 1;
}
int dissected_image_acquire_metadata(DissectedImage *m) {
enum {
META_HOSTNAME,
META_MACHINE_ID,
META_MACHINE_INFO,
META_OS_RELEASE,
_META_MAX,
};
static const char *const paths[_META_MAX] = {
[META_HOSTNAME] = "/etc/hostname\0",
[META_MACHINE_ID] = "/etc/machine-id\0",
[META_MACHINE_INFO] = "/etc/machine-info\0",
[META_OS_RELEASE] = "/etc/os-release\0/usr/lib/os-release\0",
};
_cleanup_strv_free_ char **machine_info = NULL, **os_release = NULL;
_cleanup_(rmdir_and_freep) char *t = NULL;
_cleanup_(sigkill_waitp) pid_t child = 0;
sd_id128_t machine_id = SD_ID128_NULL;
_cleanup_free_ char *hostname = NULL;
unsigned n_meta_initialized = 0, k;
int fds[2 * _META_MAX], r;
siginfo_t si;
BLOCK_SIGNALS(SIGCHLD);
assert(m);
for (; n_meta_initialized < _META_MAX; n_meta_initialized ++)
if (pipe2(fds + 2*n_meta_initialized, O_CLOEXEC) < 0) {
r = -errno;
goto finish;
}
r = mkdtemp_malloc("/tmp/dissect-XXXXXX", &t);
if (r < 0)
goto finish;
child = raw_clone(SIGCHLD|CLONE_NEWNS);
if (child < 0) {
r = -errno;
goto finish;
}
if (child == 0) {
(void) reset_all_signal_handlers();
(void) reset_signal_mask();
assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
/* Make sure we never propagate to the host */
if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) < 0)
_exit(EXIT_FAILURE);
r = dissected_image_mount(m, t, DISSECT_IMAGE_READ_ONLY);
if (r < 0)
_exit(EXIT_FAILURE);
for (k = 0; k < _META_MAX; k++) {
_cleanup_close_ int fd = -1;
const char *p;
fds[2*k] = safe_close(fds[2*k]);
NULSTR_FOREACH(p, paths[k]) {
_cleanup_free_ char *q = NULL;
r = chase_symlinks(p, t, CHASE_PREFIX_ROOT, &q);
if (r < 0)
continue;
fd = open(q, O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (fd >= 0)
break;
}
if (fd < 0)
continue;
r = copy_bytes(fd, fds[2*k+1], (uint64_t) -1, 0);
if (r < 0)
_exit(EXIT_FAILURE);
fds[2*k+1] = safe_close(fds[2*k+1]);
}
_exit(EXIT_SUCCESS);
}
for (k = 0; k < _META_MAX; k++) {
_cleanup_fclose_ FILE *f = NULL;
fds[2*k+1] = safe_close(fds[2*k+1]);
f = fdopen(fds[2*k], "re");
if (!f) {
r = -errno;
goto finish;
}
fds[2*k] = -1;
switch (k) {
case META_HOSTNAME:
r = read_etc_hostname_stream(f, &hostname);
if (r < 0)
log_debug_errno(r, "Failed to read /etc/hostname: %m");
break;
case META_MACHINE_ID: {
_cleanup_free_ char *line = NULL;
r = read_line(f, LONG_LINE_MAX, &line);
if (r < 0)
log_debug_errno(r, "Failed to read /etc/machine-id: %m");
else if (r == 33) {
r = sd_id128_from_string(line, &machine_id);
if (r < 0)
log_debug_errno(r, "Image contains invalid /etc/machine-id: %s", line);
} else if (r == 0)
log_debug("/etc/machine-id file is empty.");
else
log_debug("/etc/machine-id has unexpected length %i.", r);
break;
}
case META_MACHINE_INFO:
r = load_env_file_pairs(f, "machine-info", NULL, &machine_info);
if (r < 0)
log_debug_errno(r, "Failed to read /etc/machine-info: %m");
break;
case META_OS_RELEASE:
r = load_env_file_pairs(f, "os-release", NULL, &os_release);
if (r < 0)
log_debug_errno(r, "Failed to read OS release file: %m");
break;
}
}
r = wait_for_terminate(child, &si);
if (r < 0)
goto finish;
child = 0;
if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) {
r = -EPROTO;
goto finish;
}
free_and_replace(m->hostname, hostname);
m->machine_id = machine_id;
strv_free_and_replace(m->machine_info, machine_info);
strv_free_and_replace(m->os_release, os_release);
finish:
for (k = 0; k < n_meta_initialized; k++)
safe_close_pair(fds + 2*k);
return r;
}
static const char *const partition_designator_table[] = {
[PARTITION_ROOT] = "root",
[PARTITION_ROOT_SECONDARY] = "root-secondary",

View file

@ -77,7 +77,13 @@ struct DissectedImage {
bool encrypted:1;
bool verity:1; /* verity available and usable */
bool can_verity:1; /* verity available, but not necessarily used */
DissectedPartition partitions[_PARTITION_DESIGNATOR_MAX];
char *hostname;
sd_id128_t machine_id;
char **machine_info;
char **os_release;
};
int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectImageFlags flags, DissectedImage **ret);
@ -89,6 +95,8 @@ int dissected_image_decrypt(DissectedImage *m, const char *passphrase, const voi
int dissected_image_decrypt_interactively(DissectedImage *m, const char *passphrase, const void *root_hash, size_t root_hash_size, DissectImageFlags flags, DecryptedImage **ret);
int dissected_image_mount(DissectedImage *m, const char *dest, DissectImageFlags flags);
int dissected_image_acquire_metadata(DissectedImage *m);
DecryptedImage* decrypted_image_unref(DecryptedImage *p);
DEFINE_TRIVIAL_CLEANUP_FUNC(DecryptedImage*, decrypted_image_unref);
int decrypted_image_relinquish(DecryptedImage *d);

View file

@ -34,12 +34,17 @@
#include "chattr-util.h"
#include "copy.h"
#include "dirent-util.h"
#include "dissect-image.h"
#include "env-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
#include "hashmap.h"
#include "hostname-util.h"
#include "id128-util.h"
#include "lockfile-util.h"
#include "log.h"
#include "loop-util.h"
#include "machine-image.h"
#include "macro.h"
#include "mkdir.h"
@ -65,6 +70,11 @@ Image *image_unref(Image *i) {
free(i->name);
free(i->path);
free(i->hostname);
strv_free(i->machine_info);
strv_free(i->os_release);
return mfree(i);
}
@ -761,6 +771,7 @@ int image_clone(Image *i, const char *new_name, bool read_only) {
int image_read_only(Image *i, bool b) {
_cleanup_release_lock_file_ LockFile global_lock = LOCK_FILE_INIT, local_lock = LOCK_FILE_INIT;
int r;
assert(i);
if (IMAGE_IS_VENDOR(i) || IMAGE_IS_HOST(i))
@ -924,6 +935,118 @@ int image_set_limit(Image *i, uint64_t referenced_max) {
return btrfs_subvol_set_subtree_quota_limit(i->path, 0, referenced_max);
}
int image_read_metadata(Image *i) {
_cleanup_release_lock_file_ LockFile global_lock = LOCK_FILE_INIT, local_lock = LOCK_FILE_INIT;
int r;
assert(i);
r = image_path_lock(i->path, LOCK_SH|LOCK_NB, &global_lock, &local_lock);
if (r < 0)
return r;
switch (i->type) {
case IMAGE_SUBVOLUME:
case IMAGE_DIRECTORY: {
_cleanup_strv_free_ char **machine_info = NULL, **os_release = NULL;
sd_id128_t machine_id = SD_ID128_NULL;
_cleanup_free_ char *hostname = NULL;
_cleanup_free_ char *path = NULL;
r = chase_symlinks("/etc/hostname", i->path, CHASE_PREFIX_ROOT, &path);
if (r < 0 && r != -ENOENT)
log_debug_errno(r, "Failed to chase /etc/hostname in image %s: %m", i->name);
else if (r >= 0) {
r = read_etc_hostname(path, &hostname);
if (r < 0)
log_debug_errno(errno, "Failed to read /etc/hostname of image %s: %m", i->name);
}
path = mfree(path);
r = chase_symlinks("/etc/machine-id", i->path, CHASE_PREFIX_ROOT, &path);
if (r < 0 && r != -ENOENT)
log_debug_errno(r, "Failed to chase /etc/machine-id in image %s: %m", i->name);
else if (r >= 0) {
_cleanup_close_ int fd = -1;
fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (fd < 0)
log_debug_errno(errno, "Failed to open %s: %m", path);
else {
r = id128_read_fd(fd, ID128_PLAIN, &machine_id);
if (r < 0)
log_debug_errno(r, "Image %s contains invalid machine ID.", i->name);
}
}
path = mfree(path);
r = chase_symlinks("/etc/machine-info", i->path, CHASE_PREFIX_ROOT, &path);
if (r < 0 && r != -ENOENT)
log_debug_errno(r, "Failed to chase /etc/machine-info in image %s: %m", i->name);
else if (r >= 0) {
r = load_env_file_pairs(NULL, path, NULL, &machine_info);
if (r < 0)
log_debug_errno(r, "Failed to parse machine-info data of %s: %m", i->name);
}
path = mfree(path);
r = chase_symlinks("/etc/os-release", i->path, CHASE_PREFIX_ROOT, &path);
if (r == -ENOENT)
r = chase_symlinks("/usr/lib/os-release", i->path, CHASE_PREFIX_ROOT, &path);
if (r < 0 && r != -ENOENT)
log_debug_errno(r, "Failed to chase os-release in image: %m");
else if (r >= 0) {
r = load_env_file_pairs(NULL, path, NULL, &os_release);
if (r < 0)
log_debug_errno(r, "Failed to parse os-release data of %s: %m", i->name);
}
free_and_replace(i->hostname, hostname);
i->machine_id = machine_id;
strv_free_and_replace(i->machine_info, machine_info);
strv_free_and_replace(i->os_release, os_release);
break;
}
case IMAGE_RAW:
case IMAGE_BLOCK: {
_cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
_cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
r = loop_device_make_by_path(i->path, O_RDONLY, &d);
if (r < 0)
return r;
r = dissect_image(d->fd, NULL, 0, DISSECT_IMAGE_REQUIRE_ROOT, &m);
if (r < 0)
return r;
r = dissected_image_acquire_metadata(m);
if (r < 0)
return r;
free_and_replace(i->hostname, m->hostname);
i->machine_id = m->machine_id;
strv_free_and_replace(i->machine_info, m->machine_info);
strv_free_and_replace(i->os_release, m->os_release);
break;
}
default:
return -EOPNOTSUPP;
}
i->metadata_valid = true;
return 0;
}
int image_name_lock(const char *name, int operation, LockFile *ret) {
const char *p;

View file

@ -53,6 +53,13 @@ typedef struct Image {
uint64_t limit;
uint64_t limit_exclusive;
char *hostname;
sd_id128_t machine_id;
char **machine_info;
char **os_release;
bool metadata_valid;
void *userdata;
} Image;
@ -80,6 +87,8 @@ int image_name_lock(const char *name, int operation, LockFile *ret);
int image_set_limit(Image *i, uint64_t referenced_max);
int image_read_metadata(Image *i);
static inline bool IMAGE_IS_HIDDEN(const struct Image *i) {
assert(i);

View file

@ -100,7 +100,7 @@ static void test_hostname_cleanup(void) {
assert_se(streq(hostname_cleanup(s), "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"));
}
static void test_read_hostname_config(void) {
static void test_read_etc_hostname(void) {
char path[] = "/tmp/hostname.XXXXXX";
char *hostname;
int fd;
@ -111,27 +111,27 @@ static void test_read_hostname_config(void) {
/* simple hostname */
write_string_file(path, "foo", WRITE_STRING_FILE_CREATE);
assert_se(read_hostname_config(path, &hostname) == 0);
assert_se(read_etc_hostname(path, &hostname) == 0);
assert_se(streq(hostname, "foo"));
hostname = mfree(hostname);
/* with comment */
write_string_file(path, "# comment\nfoo", WRITE_STRING_FILE_CREATE);
assert_se(read_hostname_config(path, &hostname) == 0);
assert_se(read_etc_hostname(path, &hostname) == 0);
assert_se(hostname);
assert_se(streq(hostname, "foo"));
hostname = mfree(hostname);
/* with comment and extra whitespace */
write_string_file(path, "# comment\n\n foo ", WRITE_STRING_FILE_CREATE);
assert_se(read_hostname_config(path, &hostname) == 0);
assert_se(read_etc_hostname(path, &hostname) == 0);
assert_se(hostname);
assert_se(streq(hostname, "foo"));
hostname = mfree(hostname);
/* cleans up name */
write_string_file(path, "!foo/bar.com", WRITE_STRING_FILE_CREATE);
assert_se(read_hostname_config(path, &hostname) == 0);
assert_se(read_etc_hostname(path, &hostname) == 0);
assert_se(hostname);
assert_se(streq(hostname, "foobar.com"));
hostname = mfree(hostname);
@ -139,11 +139,11 @@ static void test_read_hostname_config(void) {
/* no value set */
hostname = (char*) 0x1234;
write_string_file(path, "# nothing here\n", WRITE_STRING_FILE_CREATE);
assert_se(read_hostname_config(path, &hostname) == -ENOENT);
assert_se(read_etc_hostname(path, &hostname) == -ENOENT);
assert_se(hostname == (char*) 0x1234); /* does not touch argument on error */
/* nonexisting file */
assert_se(read_hostname_config("/non/existing", &hostname) == -ENOENT);
assert_se(read_etc_hostname("/non/existing", &hostname) == -ENOENT);
assert_se(hostname == (char*) 0x1234); /* does not touch argument on error */
unlink(path);
@ -155,7 +155,7 @@ int main(int argc, char *argv[]) {
test_hostname_is_valid();
test_hostname_cleanup();
test_read_hostname_config();
test_read_etc_hostname();
return 0;
}