diff --git a/cr-dump.c b/cr-dump.c index 42c151c38..0d28403b8 100644 --- a/cr-dump.c +++ b/cr-dump.c @@ -1759,7 +1759,7 @@ int cr_dump_tasks(pid_t pid) if (init_stats(DUMP_STATS)) goto err; - if (cr_plugin_init()) + if (cr_plugin_init(CR_PLUGIN_STAGE__DUMP)) goto err; if (kerndat_init()) @@ -1859,7 +1859,7 @@ err: close_cr_fdset(&glob_fdset); - cr_plugin_fini(); + cr_plugin_fini(CR_PLUGIN_STAGE__DUMP, ret); if (!ret) { /* diff --git a/cr-restore.c b/cr-restore.c index 2735d0dea..05efbda16 100644 --- a/cr-restore.c +++ b/cr-restore.c @@ -1792,7 +1792,7 @@ int cr_restore_tasks(void) { int ret = -1; - if (cr_plugin_init()) + if (cr_plugin_init(CR_PLUGIN_STAGE__RESTORE)) return -1; if (check_img_inventory() < 0) @@ -1831,7 +1831,7 @@ int cr_restore_tasks(void) err: fini_cgroup(); - cr_plugin_fini(); + cr_plugin_fini(CR_PLUGIN_STAGE__RESTORE, ret); return ret; } diff --git a/files-ext.c b/files-ext.c index d9640c49c..a5ca5945d 100644 --- a/files-ext.c +++ b/files-ext.c @@ -15,7 +15,7 @@ static int dump_one_ext_file(int lfd, u32 id, const struct fd_parms *p) ExtFileEntry xfe = EXT_FILE_ENTRY__INIT; - ret = cr_plugin_dump_file(lfd, id); + ret = run_plugins(DUMP_EXT_FILE, lfd, id); if (ret < 0) return ret; @@ -44,7 +44,7 @@ static int open_fd(struct file_desc *d) xfi = container_of(d, struct ext_file_info, d); - fd = cr_plugin_restore_file(xfi->xfe->id); + fd = run_plugins(RESTORE_EXT_FILE, xfi->xfe->id); if (fd < 0) { pr_err("Unable to restore %#x\n", xfi->xfe->id); return -1; diff --git a/include/criu-plugin.h b/include/criu-plugin.h index cda8c8eb9..3f76b8fe6 100644 --- a/include/criu-plugin.h +++ b/include/criu-plugin.h @@ -23,21 +23,109 @@ #include #include -typedef int (cr_plugin_init_t)(void); -typedef void (cr_plugin_fini_t)(void); +#define CRIU_PLUGIN_GEN_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) +#define CRIU_PLUGIN_VERSION_MAJOR 0 +#define CRIU_PLUGIN_VERSION_MINOR 2 +#define CRIU_PLUGIN_VERSION_SUBLEVEL 0 -typedef int (cr_plugin_dump_unix_sk_t)(int fd, int id); -typedef int (cr_plugin_restore_unix_sk_t)(int id); +#define CRIU_PLUGIN_VERSION_OLD CRIU_PLUGIN_GEN_VERSION(0,1,0) -typedef int (cr_plugin_dump_file_t)(int fd, int id); -typedef int (cr_plugin_restore_file_t)(int id); +#define CRIU_PLUGIN_VERSION \ + CRIU_PLUGIN_GEN_VERSION(CRIU_PLUGIN_VERSION_MAJOR, \ + CRIU_PLUGIN_VERSION_MINOR, \ + CRIU_PLUGIN_VERSION_SUBLEVEL) -typedef int (cr_plugin_dump_ext_mount_t)(char *mountpoint, int id); -typedef int (cr_plugin_restore_ext_mount_t)(int id, char *mountpoint, char *old_root, int *is_file); +/* + * Plugin hook points and their arguments in hooks. + */ +enum { + CR_PLUGIN_HOOK__DUMP_UNIX_SK, + CR_PLUGIN_HOOK__RESTORE_UNIX_SK, -typedef int (cr_plugin_dump_ext_link_t)(int index, int type, char *kind); + CR_PLUGIN_HOOK__DUMP_EXT_FILE, + CR_PLUGIN_HOOK__RESTORE_EXT_FILE, + + CR_PLUGIN_HOOK__DUMP_EXT_MOUNT, + CR_PLUGIN_HOOK__RESTORE_EXT_MOUNT, + + CR_PLUGIN_HOOK__DUMP_EXT_LINK, + + CR_PLUGIN_HOOK__MAX +}; + +#define DECLARE_PLUGIN_HOOK_ARGS(__hook, ...) \ + typedef int (__hook ##_t)(__VA_ARGS__) + +DECLARE_PLUGIN_HOOK_ARGS(CR_PLUGIN_HOOK__DUMP_UNIX_SK, int fd, int id); +DECLARE_PLUGIN_HOOK_ARGS(CR_PLUGIN_HOOK__RESTORE_UNIX_SK, int id); +DECLARE_PLUGIN_HOOK_ARGS(CR_PLUGIN_HOOK__DUMP_EXT_FILE, int fd, int id); +DECLARE_PLUGIN_HOOK_ARGS(CR_PLUGIN_HOOK__RESTORE_EXT_FILE, int id); +DECLARE_PLUGIN_HOOK_ARGS(CR_PLUGIN_HOOK__DUMP_EXT_MOUNT, char *mountpoint, int id); +DECLARE_PLUGIN_HOOK_ARGS(CR_PLUGIN_HOOK__RESTORE_EXT_MOUNT, int id, char *mountpoint, char *old_root, int *is_file); +DECLARE_PLUGIN_HOOK_ARGS(CR_PLUGIN_HOOK__DUMP_EXT_LINK, int index, int type, char *kind); + +enum { + CR_PLUGIN_STAGE__DUMP, + CR_PLUGIN_STAGE__RESTORE, + + CR_PLUGIN_STAGE_MAX +}; + +/* + * Plugin descriptor. + */ +typedef struct { + const char *name; + int (*init)(int stage); + void (*exit)(int stage, int ret); + unsigned int version; + unsigned int max_hooks; + void *hooks[CR_PLUGIN_HOOK__MAX]; +} cr_plugin_desc_t; + +extern cr_plugin_desc_t CR_PLUGIN_DESC; + +#define CR_PLUGIN_REGISTER(___name, ___init, ___exit) \ + cr_plugin_desc_t CR_PLUGIN_DESC = { \ + .name = ___name, \ + .init = ___init, \ + .exit = ___exit, \ + .version = CRIU_PLUGIN_VERSION, \ + .max_hooks = CR_PLUGIN_HOOK__MAX, \ + }; + +static inline int cr_plugin_dummy_init(int stage) { return 0; } +static inline void cr_plugin_dummy_exit(int stage, int ret) { } + +#define CR_PLUGIN_REGISTER_DUMMY(___name) \ + cr_plugin_desc_t CR_PLUGIN_DESC = { \ + .name = ___name, \ + .init = cr_plugin_dummy_init, \ + .exit = cr_plugin_dummy_exit, \ + .version = CRIU_PLUGIN_VERSION, \ + .max_hooks = CR_PLUGIN_HOOK__MAX, \ + }; + +#define CR_PLUGIN_REGISTER_HOOK(__hook, __func) \ +static void __attribute__((constructor)) cr_plugin_register_hook_##__func (void) \ +{ \ + CR_PLUGIN_DESC.hooks[__hook] = (void *)__func; \ +} /* Public API */ extern int criu_get_image_dir(void); +/* + * Deprecated, will be removed in next version. + */ +typedef int (cr_plugin_init_t)(void); +typedef void (cr_plugin_fini_t)(void); +typedef int (cr_plugin_dump_unix_sk_t)(int fd, int id); +typedef int (cr_plugin_restore_unix_sk_t)(int id); +typedef int (cr_plugin_dump_file_t)(int fd, int id); +typedef int (cr_plugin_restore_file_t)(int id); +typedef int (cr_plugin_dump_ext_mount_t)(char *mountpoint, int id); +typedef int (cr_plugin_restore_ext_mount_t)(int id, char *mountpoint, char *old_root, int *is_file); +typedef int (cr_plugin_dump_ext_link_t)(int index, int type, char *kind); + #endif /* __CRIU_PLUGIN_H__ */ diff --git a/include/plugin.h b/include/plugin.h index 3c53e3003..285583620 100644 --- a/include/plugin.h +++ b/include/plugin.h @@ -2,21 +2,45 @@ #define __CR_PLUGIN_H__ #include "criu-plugin.h" +#include "compiler.h" +#include "list.h" #define CR_PLUGIN_DEFAULT "/var/lib/criu/" -void cr_plugin_fini(void); -int cr_plugin_init(void); +void cr_plugin_fini(int stage, int err); +int cr_plugin_init(int stage); -int cr_plugin_dump_unix_sk(int fd, int id); -int cr_plugin_restore_unix_sk(int id); +typedef struct { + struct list_head head; + struct list_head hook_chain[CR_PLUGIN_HOOK__MAX]; +} cr_plugin_ctl_t; -int cr_plugin_dump_file(int fd, int id); -int cr_plugin_restore_file(int id); +extern cr_plugin_ctl_t cr_plugin_ctl; -int cr_plugin_dump_ext_mount(char *mountpoint, int id); -int cr_plugin_restore_ext_mount(int id, char *mountpoint, char *old_root, int *is_file); +typedef struct { + cr_plugin_desc_t *d; + struct list_head list; + void *dlhandle; + struct list_head link[CR_PLUGIN_HOOK__MAX]; +} plugin_desc_t; -int cr_plugin_dump_ext_link(int index, int type, char *kind); +#define run_plugins(__hook, ...) \ +({ \ + plugin_desc_t *this; \ + int __ret = -ENOTSUP; \ + \ + list_for_each_entry(this, &cr_plugin_ctl.hook_chain[CR_PLUGIN_HOOK__ ##__hook], \ + link[CR_PLUGIN_HOOK__ ##__hook]) { \ + pr_debug("plugin: `%s' hook %u -> %p\n", \ + this->d->name, CR_PLUGIN_HOOK__ ##__hook, \ + this->d->hooks[CR_PLUGIN_HOOK__ ##__hook]); \ + __ret = ((CR_PLUGIN_HOOK__ ##__hook ##_t *) \ + this->d->hooks[CR_PLUGIN_HOOK__ ##__hook])(__VA_ARGS__); \ + if (__ret == -ENOTSUP) \ + continue; \ + break; \ + } \ + __ret; \ +}) #endif diff --git a/mount.c b/mount.c index 9bb8a1744..f7bb5f074 100644 --- a/mount.c +++ b/mount.c @@ -426,7 +426,7 @@ static int validate_mounts(struct mount_info *info, bool for_dump) int ret; if (for_dump) { - ret = cr_plugin_dump_ext_mount(m->mountpoint + 1, m->mnt_id); + ret = run_plugins(DUMP_EXT_MOUNT, m->mountpoint, m->mnt_id); if (ret == 0) m->need_plugin = true; else if (ret == -ENOTSUP) @@ -1302,7 +1302,7 @@ static int restore_ext_mount(struct mount_info *mi) int ret; pr_debug("Restoring external bind mount %s\n", mi->mountpoint); - ret = cr_plugin_restore_ext_mount(mi->mnt_id, mi->mountpoint, "/", NULL); + ret = run_plugins(RESTORE_EXT_MOUNT, mi->mnt_id, mi->mountpoint, "/", NULL); if (ret) pr_err("Can't restore ext mount (%d)\n", ret); return ret; diff --git a/net.c b/net.c index 206736ad7..177b7592f 100644 --- a/net.c +++ b/net.c @@ -106,7 +106,7 @@ static int dump_unknown_device(struct ifinfomsg *ifi, char *kind, { int ret; - ret = cr_plugin_dump_ext_link(ifi->ifi_index, ifi->ifi_type, kind); + ret = run_plugins(DUMP_EXT_LINK, ifi->ifi_index, ifi->ifi_type, kind); if (ret == 0) return dump_one_netdev(ND_TYPE__EXTLINK, ifi, tb, fds, NULL); diff --git a/plugin.c b/plugin.c index 367f9fda3..c4584657f 100644 --- a/plugin.c +++ b/plugin.c @@ -1,117 +1,97 @@ +#include #include #include #include -#include #include +#include #include -#include #include "cr_options.h" -#include "plugin.h" -#include "log.h" +#include "compiler.h" #include "xmalloc.h" +#include "plugin.h" +#include "list.h" +#include "log.h" -struct cr_plugin_entry { - union { - cr_plugin_fini_t *cr_fini; +cr_plugin_ctl_t cr_plugin_ctl; - cr_plugin_dump_unix_sk_t *cr_plugin_dump_unix_sk; - cr_plugin_restore_unix_sk_t *cr_plugin_restore_unix_sk; - cr_plugin_dump_file_t *cr_plugin_dump_file; - cr_plugin_restore_file_t *cr_plugin_restore_file; - cr_plugin_dump_ext_mount_t *cr_plugin_dump_ext_mount; - cr_plugin_restore_ext_mount_t *cr_plugin_restore_ext_mount; - cr_plugin_dump_ext_link_t *cr_plugin_dump_ext_link; - }; +/* + * If we met old version of a plugin, selfgenerate a plugin descriptor for it. + */ +static cr_plugin_desc_t *cr_gen_plugin_desc(void *h, char *path) +{ + cr_plugin_desc_t *d; - struct cr_plugin_entry *next; -}; + d = xzalloc(sizeof(*d)); + if (!d) + return NULL; -struct cr_plugins { - struct cr_plugin_entry *cr_fini; + d->name = strdup(path); + d->max_hooks = CR_PLUGIN_HOOK__MAX; + d->version = CRIU_PLUGIN_VERSION_OLD; - struct cr_plugin_entry *cr_plugin_dump_unix_sk; - struct cr_plugin_entry *cr_plugin_restore_unix_sk; - struct cr_plugin_entry *cr_plugin_dump_file; - struct cr_plugin_entry *cr_plugin_restore_file; - struct cr_plugin_entry *cr_plugin_dump_ext_mount; - struct cr_plugin_entry *cr_plugin_restore_ext_mount; - struct cr_plugin_entry *cr_plugin_dump_ext_link; -}; + pr_warn("Generating dynamic descriptor for plugin `%s'." + "Won't work in next version of the program." + "Please update your plugin.\n", path); -struct cr_plugins cr_plugins; - -#define add_plugin_func(name) \ +#define __assign_hook(__hook, __name) \ do { \ - name ## _t *name; \ - name = dlsym(h, #name); \ - if (name) { \ - struct cr_plugin_entry *__ce; \ - __ce = xmalloc(sizeof(*__ce)); \ - if (__ce == NULL) \ - goto nomem; \ - __ce->name = name; \ - __ce->next = cr_plugins.name; \ - cr_plugins.name = __ce; \ - } \ + void *name; \ + name = dlsym(h, __name); \ + if (name) \ + d->hooks[CR_PLUGIN_HOOK__ ##__hook] = name; \ } while (0) -#define run_plugin_funcs(name, ...) ({ \ - struct cr_plugin_entry *__ce = cr_plugins.name; \ - int __ret = -ENOTSUP; \ - \ - while (__ce) { \ - __ret = __ce->name(__VA_ARGS__); \ - if (__ret == -ENOTSUP) { \ - __ce = __ce->next; \ - continue; \ - } \ - break; \ - } \ - \ - __ret; \ - }) \ + __assign_hook(DUMP_UNIX_SK, "cr_plugin_dump_unix_sk"); + __assign_hook(RESTORE_UNIX_SK, "cr_plugin_restore_unix_sk"); + __assign_hook(DUMP_EXT_FILE, "cr_plugin_dump_file"); + __assign_hook(RESTORE_EXT_FILE, "cr_plugin_restore_file"); + __assign_hook(DUMP_EXT_MOUNT, "cr_plugin_dump_ext_mount"); + __assign_hook(RESTORE_EXT_MOUNT, "cr_plugin_restore_ext_mount"); + __assign_hook(DUMP_EXT_LINK, "cr_plugin_dump_ext_link"); -int cr_plugin_dump_unix_sk(int fd, int id) -{ - return run_plugin_funcs(cr_plugin_dump_unix_sk, fd, id); +#undef __assign_hook + + d->init = dlsym(h, "cr_plugin_init"); + d->exit = dlsym(h, "cr_plugin_fini"); + + return d; } -int cr_plugin_restore_unix_sk(int id) +static void show_plugin_desc(cr_plugin_desc_t *d) { - return run_plugin_funcs(cr_plugin_restore_unix_sk, id); + size_t i; + + pr_debug("Plugin \"%s\" (version %u hooks %u)\n", + d->name, d->version, d->max_hooks); + for (i = 0; i < d->max_hooks; i++) { + if (d->hooks[i]) + pr_debug("\t%4zu -> %p\n", i, d->hooks[i]); + } } -int cr_plugin_dump_file(int fd, int id) +static int verify_plugin(cr_plugin_desc_t *d) { - return run_plugin_funcs(cr_plugin_dump_file, fd, id); + if (d->version > CRIU_PLUGIN_VERSION) { + pr_debug("Plugin %s has version %x while max %x supported\n", + d->name, d->version, CRIU_PLUGIN_VERSION); + return -1; + } + + if (d->max_hooks > CR_PLUGIN_HOOK__MAX) { + pr_debug("Plugin %s has %u assigned while max %u supported\n", + d->name, d->max_hooks, CR_PLUGIN_HOOK__MAX); + return -1; + } + + return 0; } -int cr_plugin_restore_file(int id) +static int cr_lib_load(int stage, char *path) { - return run_plugin_funcs(cr_plugin_restore_file, id); -} - -int cr_plugin_dump_ext_mount(char *mountpoint, int id) -{ - return run_plugin_funcs(cr_plugin_dump_ext_mount, mountpoint, id); -} - -int cr_plugin_restore_ext_mount(int id, char *mountpoint, char *old_root, int *is_file) -{ - return run_plugin_funcs(cr_plugin_restore_ext_mount, id, mountpoint, old_root, is_file); -} - -int cr_plugin_dump_ext_link(int index, int type, char *kind) -{ - return run_plugin_funcs(cr_plugin_dump_ext_link, index, type, kind); -} - -static int cr_lib_load(char *path) -{ - struct cr_plugin_entry *ce; - cr_plugin_init_t *f_init; - cr_plugin_fini_t *f_fini; + cr_plugin_desc_t *d; + plugin_desc_t *this; + size_t i; void *h; h = dlopen(path, RTLD_LAZY); @@ -120,77 +100,98 @@ static int cr_lib_load(char *path) return -1; } - add_plugin_func(cr_plugin_dump_unix_sk); - add_plugin_func(cr_plugin_restore_unix_sk); - - add_plugin_func(cr_plugin_dump_file); - add_plugin_func(cr_plugin_restore_file); - - add_plugin_func(cr_plugin_dump_ext_mount); - add_plugin_func(cr_plugin_restore_ext_mount); - - add_plugin_func(cr_plugin_dump_ext_link); - - ce = NULL; - f_fini = dlsym(h, "cr_plugin_fini"); - if (f_fini) { - ce = xmalloc(sizeof(*ce)); - if (ce == NULL) - goto nomem; - ce->cr_fini = f_fini; - } - - f_init = dlsym(h, "cr_plugin_init"); - if (f_init && f_init()) { - xfree(ce); + /* + * Load plugin descriptor. If plugin is too old -- create + * dynamic plugin descriptor. In most cases this won't + * be a common operation and plugins are not supposed to + * be changing own format frequently. + */ + d = dlsym(h, "CR_PLUGIN_DESC"); + if (!d) + d = cr_gen_plugin_desc(h, path); + if (!d) { + pr_err("Can't load plugin %s\n", path); + dlclose(h); return -1; } - if (ce) { - ce->next = cr_plugins.cr_fini; - cr_plugins.cr_fini = ce; + this = xzalloc(sizeof(*this)); + if (!this) { + dlclose(h); + return -1; + } + + if (verify_plugin(d)) { + pr_err("Corrupted plugin %s\n", path); + xfree(this); + dlclose(h); + return -1; + } + + this->d = d; + this->dlhandle = h; + INIT_LIST_HEAD(&this->list); + + for (i = 0; i < d->max_hooks; i++) + INIT_LIST_HEAD(&this->link[i]); + + list_add_tail(&this->list, &cr_plugin_ctl.head); + show_plugin_desc(d); + + if (d->init && d->init(stage)) { + pr_err("Failed in init(%d) of \"%s\"\n", stage, d->name); + list_del(&this->list); + xfree(this); + dlclose(h); + return -1; + } + + /* + * Chain hooks into appropriate places for + * fast handler access. + */ + for (i = 0; i < d->max_hooks; i++) { + if (!d->hooks[i]) + continue; + list_add_tail(&this->link[i], &cr_plugin_ctl.hook_chain[i]); } return 0; - -nomem: - return -1; } -#define cr_plugin_free(name) do { \ - while (cr_plugins.name) { \ - ce = cr_plugins.name; \ - cr_plugins.name = cr_plugins.name->next; \ - xfree(ce); \ - } \ -} while (0) \ - -void cr_plugin_fini(void) +void cr_plugin_fini(int stage, int ret) { - struct cr_plugin_entry *ce; + plugin_desc_t *this, *tmp; - cr_plugin_free(cr_plugin_dump_unix_sk); - cr_plugin_free(cr_plugin_restore_unix_sk); + list_for_each_entry_safe(this, tmp, &cr_plugin_ctl.head, list) { + void *h = this->dlhandle; + size_t i; - cr_plugin_free(cr_plugin_dump_file); - cr_plugin_free(cr_plugin_restore_file); + list_del(&this->list); + if (this->d->exit) + this->d->exit(stage, ret); - while (cr_plugins.cr_fini) { - ce = cr_plugins.cr_fini; - cr_plugins.cr_fini = cr_plugins.cr_fini->next; + for (i = 0; i < this->d->max_hooks; i++) { + if (!list_empty(&this->link[i])) + list_del(&this->link[i]); + } - ce->cr_fini(); - xfree(ce); + if (this->d->version == CRIU_PLUGIN_VERSION_OLD) + xfree(this->d); + dlclose(h); } } -int cr_plugin_init(void) +int cr_plugin_init(int stage) { int exit_code = -1; char *path; + size_t i; DIR *d; - memset(&cr_plugins, 0, sizeof(cr_plugins)); + INIT_LIST_HEAD(&cr_plugin_ctl.head); + for (i = 0; i < ARRAY_SIZE(cr_plugin_ctl.hook_chain); i++) + INIT_LIST_HEAD(&cr_plugin_ctl.hook_chain[i]); if (opts.libdir == NULL) { path = getenv("CRIU_LIBS_DIR"); @@ -231,7 +232,7 @@ int cr_plugin_init(void) snprintf(path, sizeof(path), "%s/%s", opts.libdir, de->d_name); - if (cr_lib_load(path)) + if (cr_lib_load(stage, path)) goto err; } @@ -240,7 +241,7 @@ err: closedir(d); if (exit_code) - cr_plugin_fini(); + cr_plugin_fini(stage, exit_code); return exit_code; } diff --git a/sk-unix.c b/sk-unix.c index f205d8f46..dad07505f 100644 --- a/sk-unix.c +++ b/sk-unix.c @@ -550,7 +550,7 @@ static int dump_external_sockets(struct unix_sk_desc *peer) while (!list_empty(&peer->peer_list)) { sk = list_first_entry(&peer->peer_list, struct unix_sk_desc, peer_node); - ret = cr_plugin_dump_unix_sk(sk->fd, sk->sd.ino); + ret = run_plugins(DUMP_UNIX_SK, sk->fd, sk->sd.ino); if (ret == -ENOTSUP) { if (!opts.ext_unix_sk) { show_one_unix("Runaway socket", peer); @@ -917,7 +917,7 @@ static int open_unixsk_standalone(struct unix_sk_info *ui) sk = sks[0]; } else { if (ui->ue->uflags & USK_CALLBACK) { - sk = cr_plugin_restore_unix_sk(ui->ue->ino); + sk = run_plugins(RESTORE_UNIX_SK, ui->ue->ino); if (sk >= 0) goto out; }