From: Serge Hallyn Date: Mon, 12 Jan 2015 23:54:36 +0000 (+0000) Subject: autodev: switch strategies (v3) X-Git-Tag: lxc-1.1.0.rc1~14 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=87da4ec3371b975df8387360524c76cb4d4b6a4a;p=thirdparty%2Flxc.git autodev: switch strategies (v3) Do not keep container devs under /dev/.lxc. Instead, always keep them in a small tmpfs mounted at $(mounted_root)/dev. The tmpfs is mounted in the container monitor's namespace. This means that at every reboot it will get re-created. It seems to me this better replicates what happens on a real host. If we want devices persisting across reboots, then perhaps we can implement a $lxcpath/$name/keepdev directory containing devices to bind into the container at each startup. Changelog (v2): don't bother with the $lxcpath/$name/rootfs.dev directory, just mount the tmpfs straight into the container. Changelog (v3): Don't create /dev if it doesn't exist Signed-off-by: Serge Hallyn Acked-by: Stéphane Graber --- diff --git a/src/lxc/conf.c b/src/lxc/conf.c index 907200279..28c108181 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -94,10 +94,7 @@ lxc_log_define(lxc_conf, lxc); -#define MAXHWLEN 18 -#define MAXINDEXLEN 20 -#define MAXMTULEN 16 -#define MAXLINELEN 128 +#define LINELEN 4096 #if HAVE_SYS_CAPABILITY_H #ifndef CAP_SETFCAP @@ -295,9 +292,6 @@ static struct caps_opt caps_opt[] = { static struct caps_opt caps_opt[] = {}; #endif -const char *dev_base_path = "/dev/.lxc"; -const char *dev_user_path = "/dev/.lxc/user"; - static int run_buffer(char *buffer) { struct lxc_popen_FILE *f; @@ -1092,247 +1086,47 @@ fail: } /* - * Check to see if a directory has something mounted on it and, - * if it does, return the fstype. - * - * Code largely based on detect_shared_rootfs below - * - * Returns: # of matching entries in /proc/self/mounts - * if != 0 fstype is filled with the last filesystem value. - * if == 0 no matches found, fstype unchanged. - * - * ToDo: Maybe return the mount options in another parameter... - */ - -#define LINELEN 4096 -#define MAX_FSTYPE_LEN 128 -static int mount_check_fs( const char *dir, char *fstype ) -{ - char buf[LINELEN], *p; - struct stat s; - FILE *f; - int found_fs = 0; - char *p2; - - DEBUG("entering mount_check_fs for %s", dir); - - if ( 0 != access(dir, F_OK) || 0 != stat(dir, &s) || 0 == S_ISDIR(s.st_mode) ) { - return 0; - } - - f = fopen("/proc/self/mounts", "r"); - if (!f) - return 0; - while (fgets(buf, LINELEN, f)) { - p = index(buf, ' '); - if( !p ) - continue; - *p = '\0'; - p2 = p + 1; - - p = index(p2, ' '); - if( !p ) - continue; - *p = '\0'; - - /* Compare the directory in the entry to desired */ - if( strcmp( p2, dir ) ) { - continue; - } - - p2 = p + 1; - p = index( p2, ' '); - if( !p ) - continue; - *p = '\0'; - - ++found_fs; - - if( fstype ) { - strncpy( fstype, p2, MAX_FSTYPE_LEN - 1 ); - fstype [ MAX_FSTYPE_LEN - 1 ] = '\0'; - } - } - - fclose(f); - - DEBUG("mount_check_fs returning %d last %s", found_fs, fstype); - - return found_fs; -} - -/* - * Locate a devtmpfs mount (should be on /dev) and create a container - * subdirectory on it which we can then bind mount to the container - * /dev instead of mounting a tmpfs there. - * If we fail, return NULL. - * Else return the pointer to the name buffer with the string to - * the devtmpfs subdirectory. + * Just create a path for /dev under $lxcpath/$name and in rootfs + * If we hit an error, log it but don't fail yet. */ - -static char *mk_devtmpfs(const char *name, char *path, const char *lxcpath) +static int mount_autodev(const char *name, char *root, const char *lxcpath) { int ret; - struct stat s; - char tmp_path[MAXPATHLEN]; - char fstype[MAX_FSTYPE_LEN]; - uint64_t hash; - - if ( 0 != access(dev_base_path, F_OK) || 0 != stat(dev_base_path, &s) || 0 == S_ISDIR(s.st_mode) ) { - /* This is just making /dev/.lxc it better work or we're done */ - ret = mkdir(dev_base_path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); - if ( ret ) { - SYSERROR( "Unable to create /dev/.lxc for autodev" ); - return NULL; - } - } + size_t clen; + char *path; - /* - * Programmers notes: - * We can not do mounts in this area of code that we want - * to be visible in the host. Consequently, /dev/.lxc must - * be set up earlier if we need a tmpfs mounted there. - * That only affects the rare cases where autodev is enabled - * for a container and devtmpfs is not mounted on /dev in the - * host. In that case, we'll fall back to the old method - * of mounting a tmpfs in the container and have no visibility - * into the container /dev. - */ - if( ! mount_check_fs( "/dev", fstype ) - || strcmp( "devtmpfs", fstype ) ) { - /* Either /dev was not mounted or was not devtmpfs */ + INFO("Mounting /dev under %s", root); - if ( ! mount_check_fs( "/dev/.lxc", NULL ) ) { - /* - * /dev/.lxc is not already mounted - * Doing a mount here does no good, since - * it's not visible in the host. - */ + /* $(root) + "/dev/pts" + '\0' */ + clen = strlen(root) + 9; + path = alloca(clen); - ERROR("/dev/.lxc is not setup - taking fallback" ); - return NULL; - } - } + ret = snprintf(path, clen, "%s/dev", root); + if (ret < 0 || ret >= clen) + return -1; - if ( 0 != access(dev_user_path, F_OK) || 0 != stat(dev_user_path, &s) || 0 == S_ISDIR(s.st_mode) ) { - /* - * This is making /dev/.lxc/user path for non-priv users. - * If this doesn't work, we'll have to fall back in the - * case of non-priv users. It's mode 1777 like /tmp. - */ - ret = mkdir(dev_user_path, S_IRWXU | S_IRWXG | S_IRWXO | S_ISVTX); - if ( ret ) { - /* Issue an error but don't fail yet! */ - ERROR("Unable to create /dev/.lxc/user"); - } - /* Umask tends to screw us up here */ - chmod(dev_user_path, S_IRWXU | S_IRWXG | S_IRWXO | S_ISVTX); + if (!dir_exists(path)) { + WARN("No /dev on container rootfs."); + WARN("Proceeding without autodev setup"); + return 0; } - /* - * Since the container name must be unique within a given - * lxcpath, we're going to use a hash of the path - * /lxcpath/name as our hash name in /dev/.lxc/ - */ - - ret = snprintf(tmp_path, MAXPATHLEN, "%s/%s", lxcpath, name); - if (ret < 0 || ret >= MAXPATHLEN) - return NULL; - - hash = fnv_64a_buf(tmp_path, ret, FNV1A_64_INIT); - - ret = snprintf(tmp_path, MAXPATHLEN, "%s/%s.%016" PRIx64, dev_base_path, name, hash); - if (ret < 0 || ret >= MAXPATHLEN) - return NULL; - - if ( 0 != access(tmp_path, F_OK) || 0 != stat(tmp_path, &s) || 0 == S_ISDIR(s.st_mode) ) { - ret = mkdir(tmp_path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); - if ( ret ) { - /* Something must have failed with the dev_base_path... - * Maybe unpriv user. Try dev_user_path now... */ - INFO("Setup in /dev/.lxc failed. Trying /dev/.lxc/user." ); - - ret = snprintf(tmp_path, MAXPATHLEN, "%s/%s.%016" PRIx64, dev_user_path, name, hash); - if (ret < 0 || ret >= MAXPATHLEN) - return NULL; - - if ( 0 != access(tmp_path, F_OK) || 0 != stat(tmp_path, &s) || 0 == S_ISDIR(s.st_mode) ) { - ret = mkdir(tmp_path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); - if ( ret ) { - ERROR("Container /dev setup in host /dev failed - taking fallback" ); - return NULL; - } - } - } + if (mount("none", path, "tmpfs", 0, "size=100000,mode=755")) { + SYSERROR("Failed mounting tmpfs onto %s\n", path); + return false; } - strcpy( path, tmp_path ); - return path; -} - -/* - * Do we want to add options for max size of /dev and a file to - * specify which devices to create? - */ -static int mount_autodev(const char *name, char *root, const char *lxcpath) -{ - int ret; - struct stat s; - char path[MAXPATHLEN]; - char host_path[MAXPATHLEN]; - char devtmpfs_path[MAXPATHLEN]; - - INFO("Mounting /dev under %s", root); - - ret = snprintf(host_path, MAXPATHLEN, "%s/%s/rootfs.dev", lxcpath, name); - if (ret < 0 || ret > MAXPATHLEN) - return -1; + INFO("Mounted tmpfs onto %s", path); - ret = snprintf(path, MAXPATHLEN, "%s/dev", root); - if (ret < 0 || ret > MAXPATHLEN) + ret = snprintf(path, clen, "%s/dev/pts", root); + if (ret < 0 || ret >= clen) return -1; - if (mk_devtmpfs( name, devtmpfs_path, lxcpath ) ) { - /* - * Get rid of old links and directoriess - * This could be either a symlink and we remove it, - * or an empty directory and we remove it, - * or non-existent and we don't care, - * or a non-empty directory, and we will then emit an error - * but we will not fail out the process. - */ - unlink( host_path ); - rmdir( host_path ); - ret = symlink(devtmpfs_path, host_path); - - if ( ret < 0 ) { - SYSERROR("WARNING: Failed to create symlink '%s'->'%s'", host_path, devtmpfs_path); - } - DEBUG("Bind mounting %s to %s", devtmpfs_path , path ); - ret = mount(devtmpfs_path, path, NULL, MS_BIND, 0 ); - } else { - /* Only mount a tmpfs on here if we don't already a mount */ - if ( ! mount_check_fs( host_path, NULL ) ) { - DEBUG("Mounting tmpfs to %s", host_path ); - ret = mount("none", path, "tmpfs", 0, "size=100000,mode=755"); - } else { - /* This allows someone to manually set up a mount */ - DEBUG("Bind mounting %s to %s", host_path, path ); - ret = mount(host_path , path, NULL, MS_BIND, 0 ); - } - } - if (ret) { - SYSERROR("Failed to mount /dev at %s", root); - return -1; - } - ret = snprintf(path, MAXPATHLEN, "%s/dev/pts", root); - if (ret < 0 || ret >= MAXPATHLEN) - return -1; /* * If we are running on a devtmpfs mapping, dev/pts may already exist. * If not, then create it and exit if that fails... */ - if ( 0 != access(path, F_OK) || 0 != stat(path, &s) || 0 == S_ISDIR(s.st_mode) ) { + if (!dir_exists(path)) { ret = mkdir(path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); if (ret) { SYSERROR("Failed to create /dev/pts in container"); @@ -1395,64 +1189,6 @@ static int setup_autodev(const char *root) return 0; } -/* - * Locate allocated devtmpfs mount and purge it. - * path lookup mostly taken from mk_devtmpfs - */ -int lxc_delete_autodev(struct lxc_handler *handler) -{ - int ret; - struct stat s; - struct lxc_conf *lxc_conf = handler->conf; - const char *name = handler->name; - const char *lxcpath = handler->lxcpath; - char tmp_path[MAXPATHLEN]; - uint64_t hash; - - if ( lxc_conf->autodev <= 0 ) - return 0; - - /* don't clean on reboot */ - if ( lxc_conf->reboot == 1 ) - return 0; - - /* - * Use the same logic as mk_devtmpfs to compute candidate - * path for cleanup. - */ - - ret = snprintf(tmp_path, MAXPATHLEN, "%s/%s", lxcpath, name); - if (ret < 0 || ret >= MAXPATHLEN) - return -1; - - hash = fnv_64a_buf(tmp_path, ret, FNV1A_64_INIT); - - /* Probe /dev/.lxc/. */ - ret = snprintf(tmp_path, MAXPATHLEN, "%s/%s.%016" PRIx64, dev_base_path, name, hash); - if (ret < 0 || ret >= MAXPATHLEN) - return -1; - - if ( 0 != access(tmp_path, F_OK) || 0 != stat(tmp_path, &s) || 0 == S_ISDIR(s.st_mode) ) { - /* Probe /dev/.lxc/user/. */ - ret = snprintf(tmp_path, MAXPATHLEN, "%s/%s.%016" PRIx64, dev_user_path, name, hash); - if (ret < 0 || ret >= MAXPATHLEN) - return -1; - - if ( 0 != access(tmp_path, F_OK) || 0 != stat(tmp_path, &s) || 0 == S_ISDIR(s.st_mode) ) { - WARN("Failed to locate autodev /dev/.lxc and /dev/.lxc/user." ); - return -1; - } - } - - /* Do the cleanup */ - INFO("Cleaning %s", tmp_path ); - if ( 0 != lxc_rmdir_onedev(tmp_path, NULL) ) { - ERROR("Failed to cleanup autodev" ); - } - - return 0; -} - static int setup_rootfs(struct lxc_conf *conf) { const struct lxc_rootfs *rootfs = &conf->rootfs; diff --git a/src/lxc/start.c b/src/lxc/start.c index f9bff5192..161e4c019 100644 --- a/src/lxc/start.c +++ b/src/lxc/start.c @@ -489,7 +489,6 @@ void lxc_fini(const char *name, struct lxc_handler *handler) lxc_console_delete(&handler->conf->console); lxc_delete_tty(&handler->conf->tty_info); - lxc_delete_autodev(handler); close(handler->conf->maincmd_fd); handler->conf->maincmd_fd = -1; free(handler->name);