// Copyright © 2013, 2014, The Go-LXC Authors. All rights reserved.
// Use of this source code is governed by a LGPLv2.1
// license that can be found in the LICENSE file.
// +build linux,cgo
#include <errno.h>
#include <stdbool.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <lxc/lxccontainer.h>
#include <lxc/attach_options.h>
#include <lxc/version.h>
#include "lxc-binding.h"
#ifndef LXC_DEVEL
#define LXC_DEVEL 0
#endif
#define ret_errno(__errno__) \
({ \
errno = (__errno__); \
-(__errno__); \
})
bool go_lxc_defined(struct lxc_container *c) {
return c->is_defined(c);
}
const char* go_lxc_state(struct lxc_container *c) {
return c->state(c);
}
bool go_lxc_running(struct lxc_container *c) {
return c->is_running(c);
}
bool go_lxc_freeze(struct lxc_container *c) {
return c->freeze(c);
}
bool go_lxc_unfreeze(struct lxc_container *c) {
return c->unfreeze(c);
}
pid_t go_lxc_init_pid(struct lxc_container *c) {
return c->init_pid(c);
}
int go_lxc_init_pidfd(struct lxc_container *c) {
#if VERSION_AT_LEAST(4, 0, 0)
return c->init_pidfd(c);
#else
return ret_errno(ENOSYS);
#endif
}
int go_lxc_seccomp_notify_fd(struct lxc_container *c) {
#if VERSION_AT_LEAST(4, 0, 0)
return c->seccomp_notify_fd(c);
#else
return ret_errno(ENOSYS);
#endif
}
int go_lxc_seccomp_notify_fd_active(struct lxc_container *c) {
#if VERSION_AT_LEAST(4, 0, 5)
return c->seccomp_notify_fd_active(c);
#else
return ret_errno(ENOSYS);
#endif
}
int go_lxc_devpts_fd(struct lxc_container *c) {
#if VERSION_AT_LEAST(4, 0, 5)
return c->devpts_fd(c);
#else
return ret_errno(ENOSYS);
#endif
}
bool go_lxc_want_daemonize(struct lxc_container *c, bool state) {
return c->want_daemonize(c, state);
}
bool go_lxc_want_close_all_fds(struct lxc_container *c, bool state) {
return c->want_close_all_fds(c, state);
}
bool go_lxc_create(struct lxc_container *c, const char *t, const char *bdevtype, struct bdev_specs *specs, int flags, char * const argv[]) {
if (strncmp(t, "none", strlen(t)) == 0) {
return c->create(c, NULL, bdevtype, specs, !!(flags & LXC_CREATE_QUIET), argv);
}
return c->create(c, t, bdevtype, specs, !!(flags & LXC_CREATE_QUIET), argv);
}
bool go_lxc_start(struct lxc_container *c, int useinit, char * const argv[]) {
return c->start(c, useinit, argv);
}
bool go_lxc_stop(struct lxc_container *c) {
return c->stop(c);
}
bool go_lxc_reboot(struct lxc_container *c) {
return c->reboot(c);
}
bool go_lxc_shutdown(struct lxc_container *c, int timeout) {
return c->shutdown(c, timeout);
}
char* go_lxc_config_file_name(struct lxc_container *c) {
return c->config_file_name(c);
}
bool go_lxc_destroy(struct lxc_container *c) {
return c->destroy(c);
}
bool go_lxc_destroy_with_snapshots(struct lxc_container *c) {
#if VERSION_AT_LEAST(1, 1, 0)
return c->destroy_with_snapshots(c);
#else
return false;
#endif
}
bool go_lxc_wait(struct lxc_container *c, const char *state, int timeout) {
return c->wait(c, state, timeout);
}
char *go_lxc_get_config_item(struct lxc_container *c, const char *key)
{
char *value = NULL;
int len = c->get_config_item(c, key, NULL, 0);
if (len <= 0)
return NULL;
again:
value = (char *)malloc(sizeof(char) * len + 1);
if (value == NULL)
goto again;
if (c->get_config_item(c, key, value, len + 1) != len) {
free(value);
return NULL;
}
return value;
}
bool go_lxc_set_config_item(struct lxc_container *c, const char *key, const char *value) {
return c->set_config_item(c, key, value);
}
void go_lxc_clear_config(struct lxc_container *c) {
c->clear_config(c);
}
bool go_lxc_clear_config_item(struct lxc_container *c, const char *key) {
return c->clear_config_item(c, key);
}
char* go_lxc_get_running_config_item(struct lxc_container *c, const char *key) {
return c->get_running_config_item(c, key);
}
char *go_lxc_get_keys(struct lxc_container *c, const char *key)
{
char *value = NULL;
int len = c->get_keys(c, key, NULL, 0);
if (len <= 0)
return NULL;
again:
value = (char *)malloc(sizeof(char) * len + 1);
if (value == NULL)
goto again;
if (c->get_keys(c, key, value, len + 1) != len) {
free(value);
return NULL;
}
return value;
}
char *go_lxc_get_cgroup_item(struct lxc_container *c, const char *key)
{
char *value = NULL;
int len = c->get_cgroup_item(c, key, NULL, 0);
if (len <= 0)
return NULL;
again:
value = (char *)malloc(sizeof(char) * len + 1);
if (value == NULL)
goto again;
if (c->get_cgroup_item(c, key, value, len + 1) != len) {
free(value);
return NULL;
}
return value;
}
bool go_lxc_set_cgroup_item(struct lxc_container *c, const char *key, const char *value) {
return c->set_cgroup_item(c, key, value);
}
const char* go_lxc_get_config_path(struct lxc_container *c) {
return c->get_config_path(c);
}
bool go_lxc_set_config_path(struct lxc_container *c, const char *path) {
return c->set_config_path(c, path);
}
bool go_lxc_load_config(struct lxc_container *c, const char *alt_file) {
return c->load_config(c, alt_file);
}
bool go_lxc_save_config(struct lxc_container *c, const char *alt_file) {
return c->save_config(c, alt_file);
}
bool go_lxc_clone(struct lxc_container *c, const char *newname, const char *lxcpath, int flags, const char *bdevtype) {
struct lxc_container *c2 = c->clone(c, newname, lxcpath, flags, bdevtype, NULL, 0, NULL);
if (c2 == NULL) {
return false;
}
lxc_container_put(c2);
return true;
}
int go_lxc_console_getfd(struct lxc_container *c, int ttynum) {
int mainfd;
int ret = 0;
ret = c->console_getfd(c, &ttynum, &mainfd);
if (ret < 0)
return ret;
return mainfd;
}
bool go_lxc_console(struct lxc_container *c, int ttynum, int stdinfd, int stdoutfd, int stderrfd, int escape) {
if (c->console(c, ttynum, stdinfd, stdoutfd, stderrfd, escape) == 0) {
return true;
}
return false;
}
char** go_lxc_get_interfaces(struct lxc_container *c) {
return c->get_interfaces(c);
}
char** go_lxc_get_ips(struct lxc_container *c, const char *interface, const char *family, int scope) {
return c->get_ips(c, interface, family, scope);
}
int wait_for_pid_status(pid_t pid)
{
int status, ret;
again:
ret = waitpid(pid, &status, 0);
if (ret == -1) {
if (errno == EINTR)
goto again;
return -1;
}
if (ret != pid)
goto again;
return status;
}
int go_lxc_attach_no_wait(struct lxc_container *c,
bool clear_env,
int namespaces,
long personality,
uid_t uid, gid_t gid, lxc_groups_t groups,
int stdinfd, int stdoutfd, int stderrfd,
char *initial_cwd,
char **extra_env_vars,
char **extra_keep_env,
const char * const argv[],
pid_t *attached_pid,
int attach_flags) {
int ret;
lxc_attach_options_t attach_options = LXC_ATTACH_OPTIONS_DEFAULT;
attach_options.attach_flags = attach_flags;
lxc_attach_command_t command = (lxc_attach_command_t){.program = NULL};
attach_options.env_policy = LXC_ATTACH_KEEP_ENV;
if (clear_env) {
attach_options.env_policy = LXC_ATTACH_CLEAR_ENV;
}
attach_options.namespaces = namespaces;
attach_options.personality = personality;
attach_options.uid = uid;
attach_options.gid = gid;
#if VERSION_AT_LEAST(4, 0, 9)
if ( groups.size > 0 ) {
attach_options.groups = groups;
attach_options.attach_flags &= LXC_ATTACH_SETGROUPS;
}
#endif
attach_options.stdin_fd = stdinfd;
attach_options.stdout_fd = stdoutfd;
attach_options.stderr_fd = stderrfd;
attach_options.initial_cwd = initial_cwd;
attach_options.extra_env_vars = extra_env_vars;
attach_options.extra_keep_env = extra_keep_env;
command.program = (char *)argv[0];
command.argv = (char **)argv;
ret = c->attach(c, lxc_attach_run_command, &command, &attach_options, attached_pid);
if (ret < 0)
return ret;
return 0;
}
int go_lxc_attach(struct lxc_container *c,
bool clear_env,
int namespaces,
long personality,
uid_t uid, gid_t gid, lxc_groups_t groups,
int stdinfd, int stdoutfd, int stderrfd,
char *initial_cwd,
char **extra_env_vars,
char **extra_keep_env,
int attach_flags) {
int ret;
pid_t pid;
lxc_attach_options_t attach_options = LXC_ATTACH_OPTIONS_DEFAULT;
attach_options.attach_flags = attach_flags;
attach_options.env_policy = LXC_ATTACH_KEEP_ENV;
if (clear_env) {
attach_options.env_policy = LXC_ATTACH_CLEAR_ENV;
}
attach_options.namespaces = namespaces;
attach_options.personality = personality;
attach_options.uid = uid;
attach_options.gid = gid;
#if VERSION_AT_LEAST(4, 0, 9)
if ( groups.size > 0 ) {
attach_options.groups = groups;
attach_options.attach_flags &= LXC_ATTACH_SETGROUPS;
}
#endif
attach_options.stdin_fd = stdinfd;
attach_options.stdout_fd = stdoutfd;
attach_options.stderr_fd = stderrfd;
attach_options.initial_cwd = initial_cwd;
attach_options.extra_env_vars = extra_env_vars;
attach_options.extra_keep_env = extra_keep_env;
ret = c->attach(c, lxc_attach_run_shell, NULL, &attach_options, &pid);
if (ret < 0)
return ret;
ret = wait_for_pid_status(pid);
if (ret < 0)
return ret;
if (WIFEXITED(ret))
return WEXITSTATUS(ret);
return ret;
}
int go_lxc_attach_run_wait(struct lxc_container *c,
bool clear_env,
int namespaces,
long personality,
uid_t uid, gid_t gid, lxc_groups_t groups,
int stdinfd, int stdoutfd, int stderrfd,
char *initial_cwd,
char **extra_env_vars,
char **extra_keep_env,
const char * const argv[],
int attach_flags) {
int ret;
lxc_attach_options_t attach_options = LXC_ATTACH_OPTIONS_DEFAULT;
attach_options.attach_flags = attach_flags;
attach_options.env_policy = LXC_ATTACH_KEEP_ENV;
if (clear_env) {
attach_options.env_policy = LXC_ATTACH_CLEAR_ENV;
}
attach_options.namespaces = namespaces;
attach_options.personality = personality;
attach_options.uid = uid;
attach_options.gid = gid;
#if VERSION_AT_LEAST(4, 0, 9)
if ( groups.size > 0 ) {
attach_options.groups = groups;
attach_options.attach_flags &= LXC_ATTACH_SETGROUPS;
}
#endif
attach_options.stdin_fd = stdinfd;
attach_options.stdout_fd = stdoutfd;
attach_options.stderr_fd = stderrfd;
attach_options.initial_cwd = initial_cwd;
attach_options.extra_env_vars = extra_env_vars;
attach_options.extra_keep_env = extra_keep_env;
ret = c->attach_run_wait(c, &attach_options, argv[0], argv);
if (WIFEXITED(ret) && WEXITSTATUS(ret) == 255)
return -1;
return ret;
}
bool go_lxc_may_control(struct lxc_container *c) {
return c->may_control(c);
}
int go_lxc_snapshot(struct lxc_container *c) {
return c->snapshot(c, NULL);
}
int go_lxc_snapshot_list(struct lxc_container *c, struct lxc_snapshot **ret) {
return c->snapshot_list(c, ret);
}
bool go_lxc_snapshot_restore(struct lxc_container *c, const char *snapname, const char *newname) {
return c->snapshot_restore(c, snapname, newname);
}
bool go_lxc_snapshot_destroy(struct lxc_container *c, const char *snapname) {
return c->snapshot_destroy(c, snapname);
}
bool go_lxc_snapshot_destroy_all(struct lxc_container *c) {
#if VERSION_AT_LEAST(1, 1, 0)
return c->snapshot_destroy_all(c);
#else
return false;
#endif
}
bool go_lxc_add_device_node(struct lxc_container *c, const char *src_path, const char *dest_path) {
return c->add_device_node(c, src_path, dest_path);
}
bool go_lxc_remove_device_node(struct lxc_container *c, const char *src_path, const char *dest_path) {
return c->remove_device_node(c, src_path, dest_path);
}
bool go_lxc_rename(struct lxc_container *c, const char *newname) {
return c->rename(c, newname);
}
bool go_lxc_checkpoint(struct lxc_container *c, char *directory, bool stop, bool verbose) {
#if VERSION_AT_LEAST(1, 1, 0)
return c->checkpoint(c, directory, stop, verbose);
#else
return false;
#endif
}
bool go_lxc_restore(struct lxc_container *c, char *directory, bool verbose) {
#if VERSION_AT_LEAST(1, 1, 0)
return c->restore(c, directory, verbose);
#else
return false;
#endif
}
int go_lxc_migrate(struct lxc_container *c, unsigned int cmd, struct migrate_opts *opts, struct extra_migrate_opts *extras) {
#if VERSION_AT_LEAST(3, 0, 0)
opts->features_to_check = extras->features_to_check;
#endif
#if VERSION_AT_LEAST(2, 0, 4)
opts->action_script = extras->action_script;
opts->ghost_limit = extras->ghost_limit;
#endif
#if VERSION_AT_LEAST(2, 0, 1)
opts->preserves_inodes = extras->preserves_inodes;
#endif
#if VERSION_AT_LEAST(2, 0, 0)
return c->migrate(c, cmd, opts, sizeof(*opts));
#else
return -EINVAL;
#endif
}
bool go_lxc_attach_interface(struct lxc_container *c, const char *dev, const char *dst_dev) {
#if VERSION_AT_LEAST(1, 1, 0)
return c->attach_interface(c, dev, dst_dev);
#else
return false;
#endif
}
bool go_lxc_detach_interface(struct lxc_container *c, const char *dev, const char *dst_dev) {
#if VERSION_AT_LEAST(1, 1, 0)
return c->detach_interface(c, dev, dst_dev);
#else
return false;
#endif
}
bool go_lxc_config_item_is_supported(const char *key)
{
#if VERSION_AT_LEAST(2, 1, 0)
return lxc_config_item_is_supported(key);
#else
return false;
#endif
}
int go_lxc_error_num(struct lxc_container *c)
{
return c->error_num;
}
int go_lxc_console_log(struct lxc_container *c, struct lxc_console_log *log) {
#if VERSION_AT_LEAST(3, 0, 0)
return c->console_log(c, log);
#else
return false;
#endif
}
bool go_lxc_has_api_extension(const char *extension)
{
#if VERSION_AT_LEAST(3, 1, 0)
return lxc_has_api_extension(extension);
#else
return false;
#endif
}