Commit 3146f583 authored by Cyrill Gorcunov's avatar Cyrill Gorcunov Committed by Pavel Emelyanov

plugin: Rework plugins API, v2

Here we define new api to be used in plugins.

 - Plugin should provide a descriptor with help of
   CR_PLUGIN_REGISTER macro, or in case if plugin require
   no init/exit functions -- with CR_PLUGIN_REGISTER_DUMMY.

 - Plugin should define a plugin hook with help of
   CR_PLUGIN_REGISTER_HOOK macro.

 - Now init/exit functions of plugins takes @stage
   argument which tells plugin which stage of criu
   it's been called on dump/restore. For exit it
   also takes @ret which allows plugin to know if
   something went wrong and it needs to cleanup
   own resources.

The idea behind is to not limit plugins authors with names
of functions they might need to use for particular hook.

Such new API deprecates olds plugins structure but to keep
backward compatibility we will provide a tiny layer of
additional code to support old plugins for at least a couple
of release cycles.

For example a trivial plugin might look like

 | #include <sys/types.h>
 | #include <sys/stat.h>
 | #include <fcntl.h>
 | #include <libgen.h>
 | #include <errno.h>
 |
 | #include <sys/socket.h>
 | #include <linux/un.h>
 |
 | #include <stdio.h>
 | #include <stdlib.h>
 | #include <string.h>
 | #include <unistd.h>
 |
 | #include "criu-plugin.h"
 | #include "criu-log.h"
 |
 | static int dump_ext_file(int fd, int id)
 | {
 |	pr_info("dump_ext_file: fd %d id %d\n", fd, id);
 |	return 0;
 | }
 |
 | CR_PLUGIN_REGISTER_DUMMY("trivial")
 | CR_PLUGIN_REGISTER_HOOK(CR_PLUGIN_HOOK__DUMP_EXT_FILE, dump_ext_file)
Signed-off-by: 's avatarCyrill Gorcunov <gorcunov@openvz.org>
Acked-by: 's avatarAndrew Vagin <avagin@parallels.com>
Signed-off-by: 's avatarPavel Emelyanov <xemul@parallels.com>
parent b8587117
......@@ -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) {
/*
......
......@@ -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;
}
......
......@@ -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;
......
......@@ -23,21 +23,109 @@
#include <limits.h>
#include <stdbool.h>
#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
#define CRIU_PLUGIN_VERSION_OLD CRIU_PLUGIN_GEN_VERSION(0,1,0)
#define CRIU_PLUGIN_VERSION \
CRIU_PLUGIN_GEN_VERSION(CRIU_PLUGIN_VERSION_MAJOR, \
CRIU_PLUGIN_VERSION_MINOR, \
CRIU_PLUGIN_VERSION_SUBLEVEL)
/*
* Plugin hook points and their arguments in hooks.
*/
enum {
CR_PLUGIN_HOOK__DUMP_UNIX_SK,
CR_PLUGIN_HOOK__RESTORE_UNIX_SK,
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);
/* Public API */
extern int criu_get_image_dir(void);
#endif /* __CRIU_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);
int cr_plugin_dump_unix_sk(int fd, int id);
int cr_plugin_restore_unix_sk(int id);
int cr_plugin_dump_file(int fd, int id);
int cr_plugin_restore_file(int id);
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);
int cr_plugin_dump_ext_link(int index, int type, char *kind);
void cr_plugin_fini(int stage, int err);
int cr_plugin_init(int stage);
typedef struct {
struct list_head head;
struct list_head hook_chain[CR_PLUGIN_HOOK__MAX];
} cr_plugin_ctl_t;
extern cr_plugin_ctl_t cr_plugin_ctl;
typedef struct {
cr_plugin_desc_t *d;
struct list_head list;
void *dlhandle;
struct list_head link[CR_PLUGIN_HOOK__MAX];
} plugin_desc_t;
#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
......@@ -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;
......
......@@ -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);
......
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <errno.h>
#include <stdio.h>
#include <errno.h>
#include <dlfcn.h>
#include <unistd.h>
#include "cr_options.h"
#include "compiler.h"
#include "xmalloc.h"
#include "plugin.h"
#include "list.h"
#include "log.h"
#include "xmalloc.h"
struct cr_plugin_entry {
union {
cr_plugin_fini_t *cr_fini;
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;
};
struct cr_plugin_entry *next;
};
struct cr_plugins {
struct cr_plugin_entry *cr_fini;
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;
};
struct cr_plugins cr_plugins;
#define add_plugin_func(name) \
cr_plugin_ctl_t cr_plugin_ctl;
/*
* 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;
d = xzalloc(sizeof(*d));
if (!d)
return NULL;
d->name = strdup(path);
d->max_hooks = CR_PLUGIN_HOOK__MAX;
d->version = CRIU_PLUGIN_VERSION_OLD;
pr_warn("Generating dynamic descriptor for plugin `%s'."
"Won't work in next version of the program."
"Please update your plugin.\n", path);
#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; \
}) \
int cr_plugin_dump_unix_sk(int fd, int id)
{
return run_plugin_funcs(cr_plugin_dump_unix_sk, fd, id);
}
__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_restore_unix_sk(int id)
{
return run_plugin_funcs(cr_plugin_restore_unix_sk, id);
}
#undef __assign_hook
int cr_plugin_dump_file(int fd, int id)
{
return run_plugin_funcs(cr_plugin_dump_file, fd, id);
}
d->init = dlsym(h, "cr_plugin_init");
d->exit = dlsym(h, "cr_plugin_fini");
int cr_plugin_restore_file(int id)
{
return run_plugin_funcs(cr_plugin_restore_file, id);
return d;
}
int cr_plugin_dump_ext_mount(char *mountpoint, int id)
static void show_plugin_desc(cr_plugin_desc_t *d)
{
return run_plugin_funcs(cr_plugin_dump_ext_mount, mountpoint, id);
}
size_t i;
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);
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_ext_link(int index, int type, char *kind)
static int verify_plugin(cr_plugin_desc_t *d)
{
return run_plugin_funcs(cr_plugin_dump_ext_link, index, type, kind);
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;
}
static int cr_lib_load(char *path)
static int cr_lib_load(int stage, 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);
/*
* 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;
}
this = xzalloc(sizeof(*this));
if (!this) {
dlclose(h);
return -1;
}
add_plugin_func(cr_plugin_dump_file);
add_plugin_func(cr_plugin_restore_file);
if (verify_plugin(d)) {
pr_err("Corrupted plugin %s\n", path);
xfree(this);
dlclose(h);
return -1;
}
add_plugin_func(cr_plugin_dump_ext_mount);
add_plugin_func(cr_plugin_restore_ext_mount);
this->d = d;
this->dlhandle = h;
INIT_LIST_HEAD(&this->list);
add_plugin_func(cr_plugin_dump_ext_link);
for (i = 0; i < d->max_hooks; i++)
INIT_LIST_HEAD(&this->link[i]);
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;
}
list_add_tail(&this->list, &cr_plugin_ctl.head);
show_plugin_desc(d);
f_init = dlsym(h, "cr_plugin_init");
if (f_init && f_init()) {
xfree(ce);
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;
}
if (ce) {
ce->next = cr_plugins.cr_fini;
cr_plugins.cr_fini = ce;
/*
* 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;
}
......@@ -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;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment