|
Packit |
8ea169 |
/*
|
|
Packit |
8ea169 |
Copyright (C) 2015 ABRT Team
|
|
Packit |
8ea169 |
Copyright (C) 2015 RedHat inc.
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
This program is free software; you can redistribute it and/or modify
|
|
Packit |
8ea169 |
it under the terms of the GNU General Public License as published by
|
|
Packit |
8ea169 |
the Free Software Foundation; either version 2 of the License, or
|
|
Packit |
8ea169 |
(at your option) any later version.
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
This program is distributed in the hope that it will be useful,
|
|
Packit |
8ea169 |
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
8ea169 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit |
8ea169 |
GNU General Public License for more details.
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
You should have received a copy of the GNU General Public License along
|
|
Packit |
8ea169 |
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
Packit |
8ea169 |
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
Packit |
8ea169 |
*/
|
|
Packit |
8ea169 |
#include "libabrt.h"
|
|
Packit |
8ea169 |
#include <json.h>
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
void dump_docker_info(struct dump_dir *dd, const char *root_dir)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
if (!dd_exist(dd, FILENAME_CONTAINER))
|
|
Packit |
8ea169 |
dd_save_text(dd, FILENAME_CONTAINER, "docker");
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
json_object *json = NULL;
|
|
Packit |
8ea169 |
char *mntnf_path = concat_path_file(dd->dd_dirname, FILENAME_MOUNTINFO);
|
|
Packit |
8ea169 |
FILE *mntnf_file = fopen(mntnf_path, "r");
|
|
Packit |
8ea169 |
free(mntnf_path);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
struct mount_point {
|
|
Packit |
8ea169 |
const char *name;
|
|
Packit |
8ea169 |
enum mountinfo_fields {
|
|
Packit |
8ea169 |
MOUNTINFO_ROOT,
|
|
Packit |
8ea169 |
MOUNTINFO_SOURCE,
|
|
Packit |
8ea169 |
} field;
|
|
Packit |
8ea169 |
} mount_points[] = {
|
|
Packit |
8ea169 |
{ "/sys/fs/cgroup/memory", MOUNTINFO_ROOT },
|
|
Packit |
8ea169 |
{ "/", MOUNTINFO_SOURCE },
|
|
Packit |
8ea169 |
};
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
char *container_id = NULL;
|
|
Packit |
8ea169 |
char *output = NULL;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* initialized to 0 because we call mountinfo_destroy below */
|
|
Packit |
8ea169 |
struct mountinfo mntnf = {0};
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
for (size_t i = 0; i < ARRAY_SIZE(mount_points); ++i)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
log_debug("Parsing container ID from mount point '%s'", mount_points[i].name);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
rewind(mntnf_file);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* get_mountinfo_for_mount_point() re-initializes &mntnf */
|
|
Packit |
8ea169 |
mountinfo_destroy(&mntnf);
|
|
Packit |
8ea169 |
int r = get_mountinfo_for_mount_point(mntnf_file, &mntnf, mount_points[i].name);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (r != 0)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
log_debug("Mount poin not found");
|
|
Packit |
8ea169 |
continue;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
const char *mnt_info = NULL;
|
|
Packit |
8ea169 |
switch(mount_points[i].field)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
case MOUNTINFO_ROOT:
|
|
Packit |
8ea169 |
mnt_info = MOUNTINFO_ROOT(mntnf);
|
|
Packit |
8ea169 |
break;
|
|
Packit |
8ea169 |
case MOUNTINFO_SOURCE:
|
|
Packit |
8ea169 |
mnt_info = MOUNTINFO_MOUNT_SOURCE(mntnf);
|
|
Packit |
8ea169 |
break;
|
|
Packit |
8ea169 |
default:
|
|
Packit |
8ea169 |
error_msg("BUG: forgotten MOUNTINFO field type");
|
|
Packit |
8ea169 |
abort();
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
const char *last = strrchr(mnt_info, '/');
|
|
Packit |
8ea169 |
if (last == NULL || strncmp("/docker-", last, strlen("/docker-")) != 0)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
log_debug("Mounted source is not a docker mount source: '%s'", mnt_info);
|
|
Packit |
8ea169 |
continue;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
last = strrchr(last, '-');
|
|
Packit |
8ea169 |
if (last == NULL)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
log_debug("The docker mount point has unknown format");
|
|
Packit |
8ea169 |
continue;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
++last;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* Why we copy only 12 bytes here?
|
|
Packit |
8ea169 |
* Because only the first 12 characters are used by docker as ID of the
|
|
Packit |
8ea169 |
* container. */
|
|
Packit |
8ea169 |
container_id = xstrndup(last, 12);
|
|
Packit |
8ea169 |
if (strlen(container_id) != 12)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
log_debug("Failed to get container ID");
|
|
Packit |
8ea169 |
continue;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
char *docker_inspect_cmdline = NULL;
|
|
Packit |
8ea169 |
if (root_dir != NULL)
|
|
Packit |
8ea169 |
docker_inspect_cmdline = xasprintf("chroot %s /bin/sh -c \"docker inspect %s\"", root_dir, container_id);
|
|
Packit |
8ea169 |
else
|
|
Packit |
8ea169 |
docker_inspect_cmdline = xasprintf("docker inspect %s", container_id);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
log_debug("Executing: '%s'", docker_inspect_cmdline);
|
|
Packit |
8ea169 |
output = run_in_shell_and_save_output(0, docker_inspect_cmdline, "/", NULL);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
free(docker_inspect_cmdline);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (output == NULL || strcmp(output, "[]\n") == 0)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
log_debug("Unsupported container ID: '%s'", container_id);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
free(container_id);
|
|
Packit |
8ea169 |
container_id = NULL;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
free(output);
|
|
Packit |
8ea169 |
output = NULL;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
continue;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
break;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
fclose(mntnf_file);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (container_id == NULL)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
error_msg("Could not inspect the container");
|
|
Packit |
8ea169 |
goto dump_docker_info_cleanup;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
dd_save_text(dd, FILENAME_CONTAINER_ID, container_id);
|
|
Packit |
8ea169 |
dd_save_text(dd, FILENAME_DOCKER_INSPECT, output);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
json = json_tokener_parse(output);
|
|
Packit |
8ea169 |
free(output);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (json == NULL)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
error_msg("Unable parse response from docker");
|
|
Packit |
8ea169 |
goto dump_docker_info_cleanup;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
json_object *container = json_object_array_get_idx(json, 0);
|
|
Packit |
8ea169 |
if (container == NULL)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
error_msg("docker does not contain array of containers");
|
|
Packit |
8ea169 |
goto dump_docker_info_cleanup;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
json_object *config = NULL;
|
|
Packit |
8ea169 |
if (!json_object_object_get_ex(container, "Config", &config))
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
error_msg("container does not have 'Config' member");
|
|
Packit |
8ea169 |
goto dump_docker_info_cleanup;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
json_object *image = NULL;
|
|
Packit |
8ea169 |
if (!json_object_object_get_ex(config, "Image", &image))
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
error_msg("Config does not have 'Image' member");
|
|
Packit |
8ea169 |
goto dump_docker_info_cleanup;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
char *name = strtrimch(xstrdup(json_object_to_json_string(image)), '"');
|
|
Packit |
8ea169 |
dd_save_text(dd, FILENAME_CONTAINER_IMAGE, name);
|
|
Packit |
8ea169 |
free(name);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
dump_docker_info_cleanup:
|
|
Packit |
8ea169 |
if (json != NULL)
|
|
Packit |
8ea169 |
json_object_put(json);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
mountinfo_destroy(&mntnf);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
return;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
void dump_lxc_info(struct dump_dir *dd, const char *lxc_cmd)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
if (!dd_exist(dd, FILENAME_CONTAINER))
|
|
Packit |
8ea169 |
dd_save_text(dd, FILENAME_CONTAINER, "lxc");
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
char *mntnf_path = concat_path_file(dd->dd_dirname, FILENAME_MOUNTINFO);
|
|
Packit |
8ea169 |
FILE *mntnf_file = fopen(mntnf_path, "r");
|
|
Packit |
8ea169 |
free(mntnf_path);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
struct mountinfo mntnf;
|
|
Packit |
8ea169 |
int r = get_mountinfo_for_mount_point(mntnf_file, &mntnf, "/");
|
|
Packit |
8ea169 |
fclose(mntnf_file);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (r != 0)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
error_msg("lxc processes must have re-mounted root");
|
|
Packit |
8ea169 |
goto dump_lxc_info_cleanup;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
const char *mnt_root = MOUNTINFO_ROOT(mntnf);
|
|
Packit |
8ea169 |
const char *last_slash = strrchr(mnt_root, '/');
|
|
Packit |
8ea169 |
if (last_slash == NULL || (strcmp("rootfs", last_slash +1) != 0))
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
error_msg("Invalid root path '%s'", mnt_root);
|
|
Packit |
8ea169 |
goto dump_lxc_info_cleanup;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (last_slash == mnt_root)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
error_msg("root path misses container id: '%s'", mnt_root);
|
|
Packit |
8ea169 |
goto dump_lxc_info_cleanup;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
const char *tmp = strrchr(last_slash - 1, '/');
|
|
Packit |
8ea169 |
if (tmp == NULL)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
error_msg("root path misses first /: '%s'", mnt_root);
|
|
Packit |
8ea169 |
goto dump_lxc_info_cleanup;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
char *container_id = xstrndup(tmp + 1, (last_slash - tmp) - 1);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
dd_save_text(dd, FILENAME_CONTAINER_ID, container_id);
|
|
Packit |
8ea169 |
dd_save_text(dd, FILENAME_CONTAINER_UUID, container_id);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
free(container_id);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* TODO: make a copy of 'config' */
|
|
Packit |
8ea169 |
/* get mount point for MOUNTINFO_MOUNT_SOURCE(mntnf) + MOUNTINFO_ROOT(mntnf) */
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
dump_lxc_info_cleanup:
|
|
Packit |
8ea169 |
mountinfo_destroy(&mntnf);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
int main(int argc, char **argv)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
/* I18n */
|
|
Packit |
8ea169 |
setlocale(LC_ALL, "");
|
|
Packit |
8ea169 |
#if ENABLE_NLS
|
|
Packit |
8ea169 |
bindtextdomain(PACKAGE, LOCALEDIR);
|
|
Packit |
8ea169 |
textdomain(PACKAGE);
|
|
Packit |
8ea169 |
#endif
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
abrt_init(argv);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
const char *dump_dir_name = ".";
|
|
Packit |
8ea169 |
const char *root_dir = NULL;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* Can't keep these strings/structs static: _() doesn't support that */
|
|
Packit |
8ea169 |
const char *program_usage_string = _(
|
|
Packit |
8ea169 |
"& [-v] -d DIR\n"
|
|
Packit |
8ea169 |
"\n"
|
|
Packit |
8ea169 |
"Save container metadata"
|
|
Packit |
8ea169 |
);
|
|
Packit |
8ea169 |
enum {
|
|
Packit |
8ea169 |
OPT_v = 1 << 0,
|
|
Packit |
8ea169 |
OPT_d = 1 << 1,
|
|
Packit |
8ea169 |
};
|
|
Packit |
8ea169 |
/* Keep enum above and order of options below in sync! */
|
|
Packit |
8ea169 |
struct options program_options[] = {
|
|
Packit |
8ea169 |
OPT__VERBOSE(&g_verbose),
|
|
Packit |
8ea169 |
OPT_STRING('d', NULL, &dump_dir_name, "DIR" , _("Problem directory")),
|
|
Packit |
8ea169 |
OPT_STRING('r', NULL, &root_dir, "ROOTDIR" , _("Root directory for running container commands")),
|
|
Packit |
8ea169 |
OPT_END()
|
|
Packit |
8ea169 |
};
|
|
Packit |
8ea169 |
/*unsigned opts =*/ parse_opts(argc, argv, program_options, program_usage_string);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
export_abrt_envvars(0);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
struct dump_dir *dd = dd_opendir(dump_dir_name, /* for writing */0);
|
|
Packit |
8ea169 |
if (dd == NULL)
|
|
Packit |
8ea169 |
xfunc_die();
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
char *container_cmdline = dd_load_text_ext(dd, FILENAME_CONTAINER_CMDLINE, DD_LOAD_TEXT_RETURN_NULL_ON_FAILURE);
|
|
Packit |
8ea169 |
if (container_cmdline == NULL)
|
|
Packit |
8ea169 |
error_msg_and_die("The crash didn't occur in container");
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (strstr(container_cmdline, "/docker ") != 0 || prefixcmp(container_cmdline, "/usr/libexec/docker") == 0)
|
|
Packit |
8ea169 |
dump_docker_info(dd, root_dir);
|
|
Packit |
8ea169 |
else if (strstr(container_cmdline, "/lxc-") != 0)
|
|
Packit |
8ea169 |
dump_lxc_info(dd, container_cmdline);
|
|
Packit |
8ea169 |
else
|
|
Packit |
8ea169 |
error_msg_and_die("Unsupported container technology");
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
free(container_cmdline);
|
|
Packit |
8ea169 |
dd_close(dd);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
return 0;
|
|
Packit |
8ea169 |
}
|