X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Fnspawn%2Fnspawn.c;h=588a8ae8ac8c4ff230d704eb25a7ef0342a35184;hb=4bbfe7ad22b0666e82719e39e40be1c6cbb5cc91;hp=2387c151bafc2311e633c3d6713de68f17759ba0;hpb=aceac2f0b652dff701e5815c51c2e372e8fee84d;p=elogind.git diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 2387c151b..588a8ae8a 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -43,6 +43,8 @@ #include #include #include +#include +#include #ifdef HAVE_SELINUX #include @@ -178,6 +180,7 @@ static bool arg_register = true; static bool arg_keep_unit = false; static char **arg_network_interfaces = NULL; static char **arg_network_macvlan = NULL; +static char **arg_network_ipvlan = NULL; static bool arg_network_veth = false; static const char *arg_network_bridge = NULL; static unsigned long arg_personality = 0xffffffffLU; @@ -209,6 +212,9 @@ static void help(void) { " --network-macvlan=INTERFACE\n" " Create a macvlan network interface based on an\n" " existing network interface to the container\n" + " --network-ipvlan=INTERFACE\n" + " Create a ipvlan network interface based on an\n" + " existing network interface to the container\n" " -n --network-veth Add a virtual ethernet connection between host\n" " and container\n" " --network-bridge=INTERFACE\n" @@ -283,6 +289,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_KEEP_UNIT, ARG_NETWORK_INTERFACE, ARG_NETWORK_MACVLAN, + ARG_NETWORK_IPVLAN, ARG_NETWORK_BRIDGE, ARG_PERSONALITY, ARG_VOLATILE, @@ -317,6 +324,7 @@ static int parse_argv(int argc, char *argv[]) { { "keep-unit", no_argument, NULL, ARG_KEEP_UNIT }, { "network-interface", required_argument, NULL, ARG_NETWORK_INTERFACE }, { "network-macvlan", required_argument, NULL, ARG_NETWORK_MACVLAN }, + { "network-ipvlan", required_argument, NULL, ARG_NETWORK_IPVLAN }, { "network-veth", no_argument, NULL, 'n' }, { "network-bridge", required_argument, NULL, ARG_NETWORK_BRIDGE }, { "personality", required_argument, NULL, ARG_PERSONALITY }, @@ -399,6 +407,13 @@ static int parse_argv(int argc, char *argv[]) { if (strv_extend(&arg_network_macvlan, optarg) < 0) return log_oom(); + arg_private_network = true; + break; + + case ARG_NETWORK_IPVLAN: + if (strv_extend(&arg_network_ipvlan, optarg) < 0) + return log_oom(); + /* fall through */ case ARG_PRIVATE_NETWORK: @@ -1029,7 +1044,7 @@ static int mount_cgroup(const char *dest) { return r; if (symlink(combined, target) < 0) - return log_error_errno(errno, "Failed to create symlink for combined hiearchy: %m"); + return log_error_errno(errno, "Failed to create symlink for combined hierarchy: %m"); } } @@ -2379,6 +2394,87 @@ static int setup_macvlan(pid_t pid) { return 0; } +static int setup_ipvlan(pid_t pid) { + _cleanup_udev_unref_ struct udev *udev = NULL; + _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL; + char **i; + int r; + + if (!arg_private_network) + return 0; + + if (strv_isempty(arg_network_ipvlan)) + return 0; + + r = sd_rtnl_open(&rtnl, 0); + if (r < 0) + return log_error_errno(r, "Failed to connect to netlink: %m"); + + udev = udev_new(); + if (!udev) { + log_error("Failed to connect to udev."); + return -ENOMEM; + } + + STRV_FOREACH(i, arg_network_ipvlan) { + _cleanup_rtnl_message_unref_ sd_rtnl_message *m = NULL; + _cleanup_free_ char *n = NULL; + int ifi; + + ifi = parse_interface(udev, *i); + if (ifi < 0) + return ifi; + + r = sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINK, 0); + if (r < 0) + return log_error_errno(r, "Failed to allocate netlink message: %m"); + + r = sd_rtnl_message_append_u32(m, IFLA_LINK, ifi); + if (r < 0) + return log_error_errno(r, "Failed to add netlink interface index: %m"); + + n = strappend("iv-", *i); + if (!n) + return log_oom(); + + strshorten(n, IFNAMSIZ-1); + + r = sd_rtnl_message_append_string(m, IFLA_IFNAME, n); + if (r < 0) + return log_error_errno(r, "Failed to add netlink interface name: %m"); + + r = sd_rtnl_message_append_u32(m, IFLA_NET_NS_PID, pid); + if (r < 0) + return log_error_errno(r, "Failed to add netlink namespace field: %m"); + + r = sd_rtnl_message_open_container(m, IFLA_LINKINFO); + if (r < 0) + return log_error_errno(r, "Failed to open netlink container: %m"); + + r = sd_rtnl_message_open_container_union(m, IFLA_INFO_DATA, "ipvlan"); + if (r < 0) + return log_error_errno(r, "Failed to open netlink container: %m"); + + r = sd_rtnl_message_append_u16(m, IFLA_IPVLAN_MODE, IPVLAN_MODE_L2); + if (r < 0) + return log_error_errno(r, "Failed to add ipvlan mode: %m"); + + r = sd_rtnl_message_close_container(m); + if (r < 0) + return log_error_errno(r, "Failed to close netlink container: %m"); + + r = sd_rtnl_message_close_container(m); + if (r < 0) + return log_error_errno(r, "Failed to close netlink container: %m"); + + r = sd_rtnl_call(rtnl, m, 0, NULL); + if (r < 0) + return log_error_errno(r, "Failed to add new ipvlan interfaces: %m"); + } + + return 0; +} + static int setup_seccomp(void) { #ifdef HAVE_SECCOMP @@ -2557,9 +2653,67 @@ static int setup_image(char **device_path, int *loop_nr) { return r; } +static int wait_for_block_device(struct udev *udev, dev_t devnum, struct udev_device **ret) { + _cleanup_udev_monitor_unref_ struct udev_monitor *monitor = NULL; + int r; + + assert(udev); + assert(ret); + + for (;;) { + _cleanup_udev_device_unref_ struct udev_device *d = NULL; + struct pollfd pfd = { + .events = POLLIN + }; + + d = udev_device_new_from_devnum(udev, 'b', devnum); + if (!d) + return log_oom(); + + r = udev_device_get_is_initialized(d); + if (r < 0) + return log_error_errno(r, "Failed to check if device is initialized: %m"); + if (r > 0) { + *ret = d; + d = NULL; + return 0; + } + d = udev_device_unref(d); + + if (!monitor) { + monitor = udev_monitor_new_from_netlink(udev, "udev"); + if (!monitor) + return log_oom(); + + r = udev_monitor_filter_add_match_subsystem_devtype(monitor, "block", NULL); + if (r < 0) + return log_error_errno(r, "Failed to add block match: %m"); + + r = udev_monitor_enable_receiving(monitor); + if (r < 0) + return log_error_errno(r, "Failed to turn on monitor: %m"); + + continue; + } + + pfd.fd = udev_monitor_get_fd(monitor); + if (pfd.fd < 0) + return log_error_errno(r, "Failed to get udev monitor fd: %m"); + + r = poll(&pfd, 1, -1); + if (r < 0) + return log_error_errno(errno, "Failed to wait for device initialization: %m"); + + d = udev_monitor_receive_device(monitor); + } + + return 0; +} + #define PARTITION_TABLE_BLURB \ "Note that the disk image needs to either contain only a single MBR partition of\n" \ - "type 0x83 that is marked bootable, or follow\n" \ + "type 0x83 that is marked bootable, or a sinlge GPT partition of type" \ + "0FC63DAF-8483-4772-8E79-3D69D8477DE4 or follow\n" \ " http://www.freedesktop.org/wiki/Specifications/DiscoverablePartitionsSpec/\n" \ "to be bootable with systemd-nspawn." @@ -2578,19 +2732,18 @@ static int dissect_image( #ifdef GPT_ROOT_SECONDARY int secondary_root_nr = -1; #endif - - _cleanup_free_ char *home = NULL, *root = NULL, *secondary_root = NULL, *srv = NULL; + _cleanup_free_ char *home = NULL, *root = NULL, *secondary_root = NULL, *srv = NULL, *generic = NULL; _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL; _cleanup_udev_device_unref_ struct udev_device *d = NULL; _cleanup_blkid_free_probe_ blkid_probe b = NULL; _cleanup_udev_unref_ struct udev *udev = NULL; struct udev_list_entry *first, *item; - bool home_rw = true, root_rw = true, secondary_root_rw = true, srv_rw = true; + bool home_rw = true, root_rw = true, secondary_root_rw = true, srv_rw = true, generic_rw = true; const char *pttype = NULL; blkid_partlist pl; struct stat st; int r; - bool is_gpt, is_mbr; + bool is_gpt, is_mbr, multiple_generic = false; assert(fd >= 0); assert(root_device); @@ -2659,9 +2812,9 @@ static int dissect_image( if (fstat(fd, &st) < 0) return log_error_errno(errno, "Failed to stat block device: %m"); - d = udev_device_new_from_devnum(udev, 'b', st.st_rdev); - if (!d) - return log_oom(); + r = wait_for_block_device(udev, st.st_rdev, &d); + if (r < 0) + return r; e = udev_enumerate_new(udev); if (!e) @@ -2710,10 +2863,6 @@ static int dissect_image( continue; flags = blkid_partition_get_flags(pp); - if (is_gpt && (flags & GPT_FLAG_NO_AUTO)) - continue; - if (is_mbr && (flags != 0x80)) /* Bootable flag */ - continue; nr = blkid_partition_get_partno(pp); if (nr < 0) @@ -2723,6 +2872,9 @@ static int dissect_image( sd_id128_t type_id; const char *stype; + if (flags & GPT_FLAG_NO_AUTO) + continue; + stype = blkid_partition_get_type_string(pp); if (!stype) continue; @@ -2782,48 +2934,41 @@ static int dissect_image( return log_oom(); } #endif + else if (sd_id128_equal(type_id, GPT_LINUX_GENERIC)) { + + if (generic) + multiple_generic = true; + else { + generic_rw = !(flags & GPT_FLAG_READ_ONLY); + + r = free_and_strdup(&generic, node); + if (r < 0) + return log_oom(); + } + } } else if (is_mbr) { int type; + if (flags != 0x80) /* Bootable flag */ + continue; + type = blkid_partition_get_type(pp); if (type != 0x83) /* Linux partition */ continue; - /* Note that there's a certain, intended - * asymmetry here: while for GPT we simply - * take the first valid partition and ignore - * all others of the same type, for MBR we - * fail if there are multiple suitable - * partitions. This is because the GPT - * partition types are defined by us, and - * hence we can define their lookup semantics, - * while for the MBR logic we reuse existing - * definitions, and simply don't want to make - * out the situation. */ - - if (root) { - log_error("Identified multiple bootable Linux 0x83 partitions on\n" - " %s\n" - PARTITION_TABLE_BLURB, arg_image); - return -EINVAL; - } - - root_nr = nr; + if (generic) + multiple_generic = true; + else { + generic_rw = true; - r = free_and_strdup(&root, node); - if (r < 0) - return log_oom(); + r = free_and_strdup(&root, node); + if (r < 0) + return log_oom(); + } } } - if (!root && !secondary_root) { - log_error("Failed to identify root partition in disk image\n" - " %s\n" - PARTITION_TABLE_BLURB, arg_image); - return -EINVAL; - } - if (root) { *root_device = root; root = NULL; @@ -2836,6 +2981,31 @@ static int dissect_image( *root_device_rw = secondary_root_rw; *secondary = true; + } else if (generic) { + + /* There were no partitions with precise meanings + * around, but we found generic partitions. In this + * case, if there's only one, we can go ahead and boot + * it, otherwise we bail out, because we really cannot + * make any sense of it. */ + + if (multiple_generic) { + log_error("Identified multiple bootable Linux partitions on\n" + " %s\n" + PARTITION_TABLE_BLURB, arg_image); + return -EINVAL; + } + + *root_device = generic; + generic = NULL; + + *root_device_rw = generic_rw; + *secondary = false; + } else { + log_error("Failed to identify root partition in disk image\n" + " %s\n" + PARTITION_TABLE_BLURB, arg_image); + return -EINVAL; } if (home) { @@ -3490,7 +3660,7 @@ int main(int argc, char *argv[]) { if (!arg_quiet) log_info("Directory %s already exists, not populating from template %s.", arg_directory, arg_template); } else if (r < 0) { - log_error_errno(r, "Couldn't create snapshort %s from %s: %m", arg_directory, arg_template); + log_error_errno(r, "Couldn't create snapshot %s from %s: %m", arg_directory, arg_template); goto finish; } else { if (!arg_quiet) @@ -3968,6 +4138,10 @@ int main(int argc, char *argv[]) { if (r < 0) goto finish; + r = setup_ipvlan(pid); + if (r < 0) + goto finish; + r = register_machine(pid, ifi); if (r < 0) goto finish; @@ -4125,6 +4299,7 @@ finish: strv_free(arg_setenv); strv_free(arg_network_interfaces); strv_free(arg_network_macvlan); + strv_free(arg_network_ipvlan); strv_free(arg_bind); strv_free(arg_bind_ro); strv_free(arg_tmpfs);