mirror of
https://git.proxmox.com/git/mirror_lxc
synced 2025-08-15 08:05:20 +00:00
Merge pull request #1934 from brauner/2017-11-21/implement_do_lxc_reboot_correctly
commands: improve and simplify locking + lxccontainer: add reboot2() API extension
This commit is contained in:
commit
49be8a144a
@ -128,6 +128,9 @@ static int lxc_cmd_rsp_recv(int sock, struct lxc_cmd_rr *cmd)
|
||||
if (ret < 0) {
|
||||
WARN("%s - Failed to receive response for command \"%s\"",
|
||||
strerror(errno), lxc_cmd_str(cmd->req.cmd));
|
||||
if (errno == ECONNRESET)
|
||||
return -ECONNRESET;
|
||||
|
||||
return -1;
|
||||
}
|
||||
TRACE("Command \"%s\" received response", lxc_cmd_str(cmd->req.cmd));
|
||||
@ -318,6 +321,8 @@ static int lxc_cmd(const char *name, struct lxc_cmd_rr *cmd, int *stopped,
|
||||
}
|
||||
|
||||
ret = lxc_cmd_rsp_recv(client_fd, cmd);
|
||||
if (ret == -ECONNRESET)
|
||||
*stopped = 1;
|
||||
out:
|
||||
if (!stay_connected || ret <= 0)
|
||||
close(client_fd);
|
||||
@ -893,9 +898,8 @@ int lxc_cmd_add_state_client(const char *name, const char *lxcpath,
|
||||
lxc_state_t states[MAX_STATE],
|
||||
int *state_client_fd)
|
||||
{
|
||||
int stopped;
|
||||
int state, stopped;
|
||||
ssize_t ret;
|
||||
int state = -1;
|
||||
struct lxc_cmd_rr cmd = {
|
||||
.req = {
|
||||
.cmd = LXC_CMD_ADD_STATE_CLIENT,
|
||||
@ -904,46 +908,10 @@ int lxc_cmd_add_state_client(const char *name, const char *lxcpath,
|
||||
},
|
||||
};
|
||||
|
||||
/* Lock the whole lxc_cmd_add_state_client_callback() call to ensure
|
||||
* that lxc_set_state() doesn't cause us to miss a state.
|
||||
*/
|
||||
process_lock();
|
||||
/* Check if already in requested state. */
|
||||
state = lxc_getstate(name, lxcpath);
|
||||
if (state < 0) {
|
||||
process_unlock();
|
||||
TRACE("%s - Failed to retrieve state of container", strerror(errno));
|
||||
return -1;
|
||||
} else if (states[state]) {
|
||||
process_unlock();
|
||||
TRACE("Container is %s state", lxc_state2str(state));
|
||||
return state;
|
||||
}
|
||||
|
||||
if ((state == STARTING) && !states[RUNNING] && !states[STOPPING] && !states[STOPPED]) {
|
||||
process_unlock();
|
||||
TRACE("Container is in %s state and caller requested to be "
|
||||
"informed about a previous state", lxc_state2str(state));
|
||||
return state;
|
||||
} else if ((state == RUNNING) && !states[STOPPING] && !states[STOPPED]) {
|
||||
process_unlock();
|
||||
TRACE("Container is in %s state and caller requested to be "
|
||||
"informed about a previous state", lxc_state2str(state));
|
||||
return state;
|
||||
} else if ((state == STOPPING) && !states[STOPPED]) {
|
||||
process_unlock();
|
||||
TRACE("Container is in %s state and caller requested to be "
|
||||
"informed about a previous state", lxc_state2str(state));
|
||||
return state;
|
||||
} else if ((state == STOPPED) || (state == ABORTING)) {
|
||||
process_unlock();
|
||||
TRACE("Container is in %s state and caller requested to be "
|
||||
"informed about a previous state", lxc_state2str(state));
|
||||
return state;
|
||||
}
|
||||
|
||||
ret = lxc_cmd(name, &cmd, &stopped, lxcpath, NULL);
|
||||
process_unlock();
|
||||
if (states[STOPPED] != 0 && stopped != 0)
|
||||
return STOPPED;
|
||||
|
||||
if (ret < 0) {
|
||||
ERROR("%s - Failed to execute command", strerror(errno));
|
||||
return -1;
|
||||
@ -952,37 +920,55 @@ int lxc_cmd_add_state_client(const char *name, const char *lxcpath,
|
||||
/* We should now be guaranteed to get an answer from the state sending
|
||||
* function.
|
||||
*/
|
||||
|
||||
if (cmd.rsp.ret < 0) {
|
||||
ERROR("Failed to receive socket fd");
|
||||
ERROR("%s - Failed to receive socket fd", strerror(-cmd.rsp.ret));
|
||||
return -1;
|
||||
}
|
||||
|
||||
state = PTR_TO_INT(cmd.rsp.data);
|
||||
if (state < MAX_STATE) {
|
||||
TRACE("Container is already in requested state %s", lxc_state2str(state));
|
||||
close(cmd.rsp.ret);
|
||||
return state;
|
||||
}
|
||||
|
||||
*state_client_fd = cmd.rsp.ret;
|
||||
TRACE("Added state client %d to state client list", cmd.rsp.ret);
|
||||
return MAX_STATE;
|
||||
}
|
||||
|
||||
static int lxc_cmd_add_state_client_callback(int fd, struct lxc_cmd_req *req,
|
||||
struct lxc_handler *handler)
|
||||
{
|
||||
int ret;
|
||||
struct lxc_cmd_rsp rsp = {0};
|
||||
|
||||
if (req->datalen < 0)
|
||||
return -1;
|
||||
goto reap_client_fd;
|
||||
|
||||
if (req->datalen > (sizeof(lxc_state_t) * MAX_STATE))
|
||||
return -1;
|
||||
goto reap_client_fd;
|
||||
|
||||
if (!req->data)
|
||||
return -1;
|
||||
goto reap_client_fd;
|
||||
|
||||
rsp.ret = lxc_add_state_client(fd, handler, (lxc_state_t *)req->data);
|
||||
if (rsp.ret < 0)
|
||||
ERROR("Failed to add state client %d to state client list", fd);
|
||||
else
|
||||
TRACE("Added state client %d to state client list", fd);
|
||||
goto reap_client_fd;
|
||||
|
||||
return lxc_cmd_rsp_send(fd, &rsp);
|
||||
rsp.data = INT_TO_PTR(rsp.ret);
|
||||
|
||||
ret = lxc_cmd_rsp_send(fd, &rsp);
|
||||
if (ret < 0)
|
||||
goto reap_client_fd;
|
||||
|
||||
return 0;
|
||||
|
||||
reap_client_fd:
|
||||
/* Special indicator to lxc_cmd_handler() to close the fd and do related
|
||||
* cleanup.
|
||||
*/
|
||||
return 1;
|
||||
}
|
||||
|
||||
int lxc_cmd_console_log(const char *name, const char *lxcpath,
|
||||
@ -1155,7 +1141,7 @@ static void lxc_cmd_fd_cleanup(int fd, struct lxc_handler *handler,
|
||||
struct lxc_epoll_descr *descr,
|
||||
const lxc_cmd_t cmd)
|
||||
{
|
||||
struct state_client *client;
|
||||
struct lxc_state_client *client;
|
||||
struct lxc_list *cur, *next;
|
||||
|
||||
lxc_console_free(handler->conf, fd);
|
||||
@ -1166,7 +1152,7 @@ static void lxc_cmd_fd_cleanup(int fd, struct lxc_handler *handler,
|
||||
}
|
||||
|
||||
process_lock();
|
||||
lxc_list_for_each_safe(cur, &handler->state_clients, next) {
|
||||
lxc_list_for_each_safe(cur, &handler->conf->state_clients, next) {
|
||||
client = cur->elem;
|
||||
if (client->clientfd != fd)
|
||||
continue;
|
||||
@ -1176,6 +1162,10 @@ static void lxc_cmd_fd_cleanup(int fd, struct lxc_handler *handler,
|
||||
lxc_list_del(cur);
|
||||
free(cur->elem);
|
||||
free(cur);
|
||||
/* No need to walk the whole list. If we found the state client
|
||||
* fd there can't be a second one.
|
||||
*/
|
||||
break;
|
||||
}
|
||||
process_unlock();
|
||||
}
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include "commands_utils.h"
|
||||
#include "initutils.h"
|
||||
#include "log.h"
|
||||
#include "lxclock.h"
|
||||
#include "monitor.h"
|
||||
#include "state.h"
|
||||
#include "utils.h"
|
||||
@ -192,7 +193,8 @@ int lxc_cmd_connect(const char *name, const char *lxcpath,
|
||||
int lxc_add_state_client(int state_client_fd, struct lxc_handler *handler,
|
||||
lxc_state_t states[MAX_STATE])
|
||||
{
|
||||
struct state_client *newclient;
|
||||
int state;
|
||||
struct lxc_state_client *newclient;
|
||||
struct lxc_list *tmplist;
|
||||
|
||||
newclient = malloc(sizeof(*newclient));
|
||||
@ -209,10 +211,19 @@ int lxc_add_state_client(int state_client_fd, struct lxc_handler *handler,
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
lxc_list_add_elem(tmplist, newclient);
|
||||
lxc_list_add_tail(&handler->state_clients, tmplist);
|
||||
process_lock();
|
||||
state = handler->state;
|
||||
if (states[state] != 1) {
|
||||
lxc_list_add_elem(tmplist, newclient);
|
||||
lxc_list_add_tail(&handler->conf->state_clients, tmplist);
|
||||
process_unlock();
|
||||
} else {
|
||||
process_unlock();
|
||||
free(newclient);
|
||||
free(tmplist);
|
||||
return state;
|
||||
}
|
||||
|
||||
TRACE("added state client %d to state client list", state_client_fd);
|
||||
|
||||
return 0;
|
||||
return MAX_STATE;
|
||||
}
|
||||
|
@ -2419,6 +2419,7 @@ struct lxc_conf *lxc_conf_init(void)
|
||||
for (i = 0; i < NUM_LXC_HOOKS; i++)
|
||||
lxc_list_init(&new->hooks[i]);
|
||||
lxc_list_init(&new->groups);
|
||||
lxc_list_init(&new->state_clients);
|
||||
new->lsm_aa_profile = NULL;
|
||||
new->lsm_se_context = NULL;
|
||||
new->tmp_umount_proc = 0;
|
||||
|
@ -248,6 +248,11 @@ enum lxchooks {
|
||||
|
||||
extern char *lxchook_names[NUM_LXC_HOOKS];
|
||||
|
||||
struct lxc_state_client {
|
||||
int clientfd;
|
||||
lxc_state_t states[MAX_STATE];
|
||||
};
|
||||
|
||||
struct lxc_conf {
|
||||
int is_execute;
|
||||
char *fstab;
|
||||
@ -363,6 +368,8 @@ struct lxc_conf {
|
||||
/* init working directory */
|
||||
char* init_cwd;
|
||||
|
||||
/* A list of clients registered to be informed about a container state. */
|
||||
struct lxc_list state_clients;
|
||||
};
|
||||
|
||||
int write_id_mapping(enum idtype idtype, pid_t pid, const char *buf,
|
||||
|
@ -77,6 +77,11 @@ static int execute_start(struct lxc_handler *handler, void* data)
|
||||
argv[i++] = (char *)lxc_log_priority_to_string(lxc_log_get_level());
|
||||
}
|
||||
|
||||
if (handler->conf->logfile) {
|
||||
argv[i++] = "-o";
|
||||
argv[i++] = (char *)handler->conf->logfile;
|
||||
}
|
||||
|
||||
if (my_args->quiet)
|
||||
argv[i++] = "--quiet";
|
||||
|
||||
|
@ -828,10 +828,6 @@ static bool do_lxcapi_start(struct lxc_container *c, int useinit, char * const a
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Is this app meant to be run through lxcinit, as in lxc-execute? */
|
||||
if (useinit && !argv)
|
||||
return false;
|
||||
|
||||
if (container_mem_lock(c))
|
||||
return false;
|
||||
conf = c->lxc_conf;
|
||||
@ -843,15 +839,20 @@ static bool do_lxcapi_start(struct lxc_container *c, int useinit, char * const a
|
||||
if (!handler)
|
||||
return false;
|
||||
|
||||
/* If no argv was passed in, use lxc.init_cmd if provided in the
|
||||
* configuration
|
||||
*/
|
||||
if (!argv)
|
||||
argv = init_cmd = split_init_cmd(conf->init_cmd);
|
||||
if (!argv) {
|
||||
if (useinit && conf->execute_cmd)
|
||||
argv = init_cmd = split_init_cmd(conf->execute_cmd);
|
||||
else
|
||||
argv = init_cmd = split_init_cmd(conf->init_cmd);
|
||||
}
|
||||
|
||||
/* ... otherwise use default_args. */
|
||||
if (!argv)
|
||||
argv = default_args;
|
||||
if (!argv) {
|
||||
if (useinit)
|
||||
return false;
|
||||
else
|
||||
argv = default_args;
|
||||
}
|
||||
|
||||
/* I'm not sure what locks we want here.Any? Is liblxc's locking enough
|
||||
* here to protect the on disk container? We don't want to exclude
|
||||
@ -1793,12 +1794,11 @@ static bool do_lxcapi_reboot(struct lxc_container *c)
|
||||
|
||||
WRAP_API(bool, lxcapi_reboot)
|
||||
|
||||
static bool do_lxcapi_shutdown(struct lxc_container *c, int timeout)
|
||||
static bool do_lxcapi_reboot2(struct lxc_container *c, int timeout)
|
||||
{
|
||||
int ret, state_client_fd = -1;
|
||||
bool retv = false;
|
||||
int killret, ret;
|
||||
pid_t pid;
|
||||
int haltsignal = SIGPWR;
|
||||
int rebootsignal = SIGINT, state_client_fd = -1;
|
||||
lxc_state_t states[MAX_STATE] = {0};
|
||||
|
||||
if (!c)
|
||||
@ -1806,6 +1806,74 @@ static bool do_lxcapi_shutdown(struct lxc_container *c, int timeout)
|
||||
|
||||
if (!do_lxcapi_is_running(c))
|
||||
return true;
|
||||
|
||||
pid = do_lxcapi_init_pid(c);
|
||||
if (pid <= 0)
|
||||
return true;
|
||||
|
||||
if (c->lxc_conf && c->lxc_conf->rebootsignal)
|
||||
rebootsignal = c->lxc_conf->rebootsignal;
|
||||
|
||||
/* Add a new state client before sending the shutdown signal so that we
|
||||
* don't miss a state.
|
||||
*/
|
||||
if (timeout != 0) {
|
||||
states[RUNNING] = 2;
|
||||
ret = lxc_cmd_add_state_client(c->name, c->config_path, states,
|
||||
&state_client_fd);
|
||||
if (ret < 0)
|
||||
return false;
|
||||
|
||||
if (state_client_fd < 0)
|
||||
return false;
|
||||
|
||||
if (ret == RUNNING)
|
||||
return true;
|
||||
|
||||
if (ret < MAX_STATE)
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Send reboot signal to container. */
|
||||
killret = kill(pid, rebootsignal);
|
||||
if (killret < 0) {
|
||||
WARN("Could not send signal %d to pid %d", rebootsignal, pid);
|
||||
if (state_client_fd >= 0)
|
||||
close(state_client_fd);
|
||||
return false;
|
||||
}
|
||||
TRACE("Sent signal %d to pid %d", rebootsignal, pid);
|
||||
|
||||
if (timeout == 0)
|
||||
return true;
|
||||
|
||||
ret = lxc_cmd_sock_rcv_state(state_client_fd, timeout);
|
||||
close(state_client_fd);
|
||||
if (ret < 0)
|
||||
return false;
|
||||
|
||||
TRACE("Received state \"%s\"", lxc_state2str(ret));
|
||||
if (ret != RUNNING)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
WRAP_API_1(bool, lxcapi_reboot2, int)
|
||||
|
||||
static bool do_lxcapi_shutdown(struct lxc_container *c, int timeout)
|
||||
{
|
||||
int killret, ret;
|
||||
pid_t pid;
|
||||
int haltsignal = SIGPWR, state_client_fd = -1;
|
||||
lxc_state_t states[MAX_STATE] = {0};
|
||||
|
||||
if (!c)
|
||||
return false;
|
||||
|
||||
if (!do_lxcapi_is_running(c))
|
||||
return true;
|
||||
|
||||
pid = do_lxcapi_init_pid(c);
|
||||
if (pid <= 0)
|
||||
return true;
|
||||
@ -1817,37 +1885,49 @@ static bool do_lxcapi_shutdown(struct lxc_container *c, int timeout)
|
||||
if (c->lxc_conf && c->lxc_conf->haltsignal)
|
||||
haltsignal = c->lxc_conf->haltsignal;
|
||||
|
||||
INFO("Using signal number '%d' as halt signal", haltsignal);
|
||||
|
||||
/* Add a new state client before sending the shutdown signal so that we
|
||||
* don't miss a state.
|
||||
*/
|
||||
states[STOPPED] = 1;
|
||||
ret = lxc_cmd_add_state_client(c->name, c->config_path, states,
|
||||
&state_client_fd);
|
||||
|
||||
/* Send shutdown signal to container. */
|
||||
if (kill(pid, haltsignal) < 0)
|
||||
WARN("Could not send signal %d to pid %d", haltsignal, pid);
|
||||
|
||||
/* Retrieve the state. */
|
||||
if (state_client_fd >= 0) {
|
||||
int state;
|
||||
state = lxc_cmd_sock_rcv_state(state_client_fd, timeout);
|
||||
close(state_client_fd);
|
||||
TRACE("Received state \"%s\"", lxc_state2str(state));
|
||||
if (state != STOPPED)
|
||||
if (timeout != 0) {
|
||||
states[STOPPED] = 1;
|
||||
ret = lxc_cmd_add_state_client(c->name, c->config_path, states,
|
||||
&state_client_fd);
|
||||
if (ret < 0)
|
||||
return false;
|
||||
|
||||
if (state_client_fd < 0)
|
||||
return false;
|
||||
|
||||
if (ret == STOPPED)
|
||||
return true;
|
||||
|
||||
if (ret < MAX_STATE)
|
||||
return false;
|
||||
retv = true;
|
||||
} else if (ret == STOPPED) {
|
||||
TRACE("Container is already stopped");
|
||||
retv = true;
|
||||
} else {
|
||||
TRACE("Received state \"%s\" instead of expected \"STOPPED\"",
|
||||
lxc_state2str(ret));
|
||||
}
|
||||
|
||||
return retv;
|
||||
/* Send shutdown signal to container. */
|
||||
killret = kill(pid, haltsignal);
|
||||
if (killret < 0) {
|
||||
WARN("Could not send signal %d to pid %d", haltsignal, pid);
|
||||
if (state_client_fd >= 0)
|
||||
close(state_client_fd);
|
||||
return false;
|
||||
}
|
||||
TRACE("Sent signal %d to pid %d", haltsignal, pid);
|
||||
|
||||
if (timeout == 0)
|
||||
return true;
|
||||
|
||||
ret = lxc_cmd_sock_rcv_state(state_client_fd, timeout);
|
||||
close(state_client_fd);
|
||||
if (ret < 0)
|
||||
return false;
|
||||
|
||||
TRACE("Received state \"%s\"", lxc_state2str(ret));
|
||||
if (ret != STOPPED)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
WRAP_API_1(bool, lxcapi_shutdown, int)
|
||||
@ -4595,6 +4675,7 @@ struct lxc_container *lxc_container_new(const char *name, const char *configpath
|
||||
c->createl = lxcapi_createl;
|
||||
c->shutdown = lxcapi_shutdown;
|
||||
c->reboot = lxcapi_reboot;
|
||||
c->reboot2 = lxcapi_reboot2;
|
||||
c->clear_config = lxcapi_clear_config;
|
||||
c->clear_config_item = lxcapi_clear_config_item;
|
||||
c->get_config_item = lxcapi_get_config_item;
|
||||
|
@ -846,6 +846,17 @@ struct lxc_container {
|
||||
* \return \c 0 on success, nonzero on failure.
|
||||
*/
|
||||
int (*console_log)(struct lxc_container *c, struct lxc_console_log *log);
|
||||
|
||||
/*!
|
||||
* \brief Request the container reboot by sending it \c SIGINT.
|
||||
*
|
||||
* \param c Container.
|
||||
* \param timeout Seconds to wait before returning false.
|
||||
* (-1 to wait forever, 0 to avoid waiting).
|
||||
*
|
||||
* \return \c true if the container was rebooted successfully, else \c false.
|
||||
*/
|
||||
bool (*reboot2)(struct lxc_container *c, int timeout);
|
||||
};
|
||||
|
||||
/*!
|
||||
|
@ -200,6 +200,9 @@ restart:
|
||||
fddir = dirfd(dir);
|
||||
|
||||
while ((direntp = readdir(dir))) {
|
||||
struct lxc_list *cur;
|
||||
bool matched = false;
|
||||
|
||||
if (!direntp)
|
||||
break;
|
||||
|
||||
@ -222,6 +225,20 @@ restart:
|
||||
(i < len_fds && fd == fds_to_ignore[i]))
|
||||
continue;
|
||||
|
||||
/* Keep state clients that wait on reboots. */
|
||||
lxc_list_for_each(cur, &conf->state_clients) {
|
||||
struct lxc_state_client *client = cur->elem;
|
||||
|
||||
if (client->clientfd != fd)
|
||||
continue;
|
||||
|
||||
matched = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (matched)
|
||||
continue;
|
||||
|
||||
if (current_config && fd == current_config->logfd)
|
||||
continue;
|
||||
|
||||
@ -338,18 +355,14 @@ static int lxc_serve_state_clients(const char *name,
|
||||
{
|
||||
ssize_t ret;
|
||||
struct lxc_list *cur, *next;
|
||||
struct state_client *client;
|
||||
struct lxc_state_client *client;
|
||||
struct lxc_msg msg = {.type = lxc_msg_state, .value = state};
|
||||
|
||||
process_lock();
|
||||
|
||||
/* Only set state under process lock held so that we don't cause
|
||||
* lxc_cmd_add_state_client() to miss a state.
|
||||
*/
|
||||
handler->state = state;
|
||||
TRACE("Set container state to %s", lxc_state2str(state));
|
||||
|
||||
if (lxc_list_empty(&handler->state_clients)) {
|
||||
if (lxc_list_empty(&handler->conf->state_clients)) {
|
||||
TRACE("No state clients registered");
|
||||
process_unlock();
|
||||
lxc_monitor_send_state(name, state, handler->lxcpath);
|
||||
@ -359,10 +372,10 @@ static int lxc_serve_state_clients(const char *name,
|
||||
strncpy(msg.name, name, sizeof(msg.name));
|
||||
msg.name[sizeof(msg.name) - 1] = 0;
|
||||
|
||||
lxc_list_for_each_safe(cur, &handler->state_clients, next) {
|
||||
lxc_list_for_each_safe(cur, &handler->conf->state_clients, next) {
|
||||
client = cur->elem;
|
||||
|
||||
if (!client->states[state]) {
|
||||
if (client->states[state] == 0) {
|
||||
TRACE("State %s not registered for state client %d",
|
||||
lxc_state2str(state), client->clientfd);
|
||||
continue;
|
||||
@ -531,7 +544,8 @@ struct lxc_handler *lxc_init_handler(const char *name, struct lxc_conf *conf,
|
||||
handler->lxcpath = lxcpath;
|
||||
handler->pinfd = -1;
|
||||
handler->state_socket_pair[0] = handler->state_socket_pair[1] = -1;
|
||||
lxc_list_init(&handler->state_clients);
|
||||
if (handler->conf->reboot == 0)
|
||||
lxc_list_init(&handler->conf->state_clients);
|
||||
|
||||
for (i = 0; i < LXC_NS_MAX; i++)
|
||||
handler->nsfd[i] = -1;
|
||||
@ -554,10 +568,12 @@ struct lxc_handler *lxc_init_handler(const char *name, struct lxc_conf *conf,
|
||||
handler->state_socket_pair[1]);
|
||||
}
|
||||
|
||||
handler->conf->maincmd_fd = lxc_cmd_init(name, lxcpath, "command");
|
||||
if (handler->conf->maincmd_fd < 0) {
|
||||
ERROR("Failed to set up command socket");
|
||||
goto on_error;
|
||||
if (handler->conf->reboot == 0) {
|
||||
handler->conf->maincmd_fd = lxc_cmd_init(name, lxcpath, "command");
|
||||
if (handler->conf->maincmd_fd < 0) {
|
||||
ERROR("Failed to set up command socket");
|
||||
goto on_error;
|
||||
}
|
||||
}
|
||||
TRACE("Unix domain socket %d for command server is ready",
|
||||
handler->conf->maincmd_fd);
|
||||
@ -715,9 +731,11 @@ void lxc_fini(const char *name, struct lxc_handler *handler)
|
||||
|
||||
lxc_set_state(name, handler, STOPPED);
|
||||
|
||||
/* close command socket */
|
||||
close(handler->conf->maincmd_fd);
|
||||
handler->conf->maincmd_fd = -1;
|
||||
if (handler->conf->reboot == 0) {
|
||||
/* close command socket */
|
||||
close(handler->conf->maincmd_fd);
|
||||
handler->conf->maincmd_fd = -1;
|
||||
}
|
||||
|
||||
if (run_lxc_hooks(name, "post-stop", handler->conf, handler->lxcpath, NULL)) {
|
||||
ERROR("Failed to run lxc.hook.post-stop for container \"%s\".", name);
|
||||
@ -739,8 +757,13 @@ void lxc_fini(const char *name, struct lxc_handler *handler)
|
||||
/* The command socket is now closed, no more state clients can register
|
||||
* themselves from now on. So free the list of state clients.
|
||||
*/
|
||||
lxc_list_for_each_safe(cur, &handler->state_clients, next) {
|
||||
struct state_client *client = cur->elem;
|
||||
lxc_list_for_each_safe(cur, &handler->conf->state_clients, next) {
|
||||
struct lxc_state_client *client = cur->elem;
|
||||
|
||||
/* Keep state clients that want to be notified about reboots. */
|
||||
if ((handler->conf->reboot > 0) && (client->states[RUNNING] == 2))
|
||||
continue;
|
||||
|
||||
/* close state client socket */
|
||||
close(client->clientfd);
|
||||
lxc_list_del(cur);
|
||||
@ -801,9 +824,8 @@ static int do_start(void *data)
|
||||
lxc_sync_fini_parent(handler);
|
||||
|
||||
/* Don't leak the pinfd to the container. */
|
||||
if (handler->pinfd >= 0) {
|
||||
if (handler->pinfd >= 0)
|
||||
close(handler->pinfd);
|
||||
}
|
||||
|
||||
if (lxc_sync_wait_parent(handler, LXC_SYNC_STARTUP))
|
||||
return -1;
|
||||
|
@ -85,9 +85,6 @@ struct lxc_handler {
|
||||
/* The container's in-memory configuration. */
|
||||
struct lxc_conf *conf;
|
||||
|
||||
/* A list of clients registered to be informed about a container state. */
|
||||
struct lxc_list state_clients;
|
||||
|
||||
/* A set of operations to be performed at various stages of the
|
||||
* container's life.
|
||||
*/
|
||||
@ -110,11 +107,6 @@ struct lxc_operations {
|
||||
int (*post_start)(struct lxc_handler *, void *);
|
||||
};
|
||||
|
||||
struct state_client {
|
||||
int clientfd;
|
||||
lxc_state_t states[MAX_STATE];
|
||||
};
|
||||
|
||||
extern int lxc_poll(const char *name, struct lxc_handler *handler);
|
||||
extern int lxc_set_state(const char *name, struct lxc_handler *handler, lxc_state_t state);
|
||||
extern void lxc_abort(const char *name, struct lxc_handler *handler);
|
||||
|
@ -45,9 +45,9 @@
|
||||
|
||||
lxc_log_define(lxc_state, lxc);
|
||||
|
||||
static const char * const strstate[] = {
|
||||
"STOPPED", "STARTING", "RUNNING", "STOPPING",
|
||||
"ABORTING", "FREEZING", "FROZEN", "THAWED",
|
||||
static const char *const strstate[] = {
|
||||
"STOPPED", "STARTING", "RUNNING", "STOPPING",
|
||||
"ABORTING", "FREEZING", "FROZEN", "THAWED",
|
||||
};
|
||||
|
||||
const char *lxc_state2str(lxc_state_t state)
|
||||
|
@ -94,62 +94,6 @@ Options :\n\
|
||||
.timeout = -2,
|
||||
};
|
||||
|
||||
/* returns -1 on failure, 0 on success */
|
||||
static int do_reboot_and_check(struct lxc_arguments *a, struct lxc_container *c)
|
||||
{
|
||||
int ret;
|
||||
pid_t pid;
|
||||
pid_t newpid;
|
||||
int timeout = a->timeout;
|
||||
|
||||
pid = c->init_pid(c);
|
||||
if (pid == -1)
|
||||
return -1;
|
||||
if (!c->reboot(c))
|
||||
return -1;
|
||||
if (a->nowait)
|
||||
return 0;
|
||||
if (timeout == 0)
|
||||
goto out;
|
||||
|
||||
for (;;) {
|
||||
/* can we use c-> wait for this, assuming it will
|
||||
* re-enter RUNNING? For now just sleep */
|
||||
int elapsed_time, curtime = 0;
|
||||
struct timeval tv;
|
||||
|
||||
newpid = c->init_pid(c);
|
||||
if (newpid != -1 && newpid != pid)
|
||||
return 0;
|
||||
|
||||
if (timeout != -1) {
|
||||
ret = gettimeofday(&tv, NULL);
|
||||
if (ret)
|
||||
break;
|
||||
curtime = tv.tv_sec;
|
||||
}
|
||||
|
||||
sleep(1);
|
||||
if (timeout != -1) {
|
||||
ret = gettimeofday(&tv, NULL);
|
||||
if (ret)
|
||||
break;
|
||||
elapsed_time = tv.tv_sec - curtime;
|
||||
if (timeout - elapsed_time <= 0)
|
||||
break;
|
||||
timeout -= elapsed_time;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
newpid = c->init_pid(c);
|
||||
if (newpid == -1 || newpid == pid) {
|
||||
printf("Reboot did not complete before timeout\n");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct lxc_container *c;
|
||||
@ -259,7 +203,11 @@ int main(int argc, char *argv[])
|
||||
|
||||
/* reboot */
|
||||
if (my_args.reboot) {
|
||||
ret = do_reboot_and_check(&my_args, c) < 0 ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
ret = c->reboot2(c, my_args.timeout);
|
||||
if (ret < 0)
|
||||
ret = EXIT_FAILURE;
|
||||
else
|
||||
ret = EXIT_SUCCESS;
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,7 @@ lxc_test_snapshot_SOURCES = snapshot.c
|
||||
lxc_test_concurrent_SOURCES = concurrent.c
|
||||
lxc_test_may_control_SOURCES = may_control.c
|
||||
lxc_test_reboot_SOURCES = reboot.c
|
||||
lxc_test_api_reboot_SOURCES = api_reboot.c
|
||||
lxc_test_list_SOURCES = list.c
|
||||
lxc_test_attach_SOURCES = attach.c
|
||||
lxc_test_device_add_remove_SOURCES = device_add_remove.c
|
||||
@ -29,6 +30,7 @@ lxc_test_parse_config_file_SOURCES = parse_config_file.c lxctest.h
|
||||
lxc_test_config_jump_table_SOURCES = config_jump_table.c lxctest.h
|
||||
lxc_test_shortlived_SOURCES = shortlived.c
|
||||
lxc_test_livepatch_SOURCES = livepatch.c lxctest.h
|
||||
lxc_test_state_server_SOURCES = state_server.c lxctest.h
|
||||
|
||||
AM_CFLAGS=-DLXCROOTFSMOUNT=\"$(LXCROOTFSMOUNT)\" \
|
||||
-DLXCPATH=\"$(LXCPATH)\" \
|
||||
@ -57,7 +59,8 @@ bin_PROGRAMS = lxc-test-containertests lxc-test-locktests lxc-test-startone \
|
||||
lxc-test-snapshot lxc-test-concurrent lxc-test-may-control \
|
||||
lxc-test-reboot lxc-test-list lxc-test-attach lxc-test-device-add-remove \
|
||||
lxc-test-apparmor lxc-test-utils lxc-test-parse-config-file \
|
||||
lxc-test-config-jump-table lxc-test-shortlived lxc-test-livepatch
|
||||
lxc-test-config-jump-table lxc-test-shortlived lxc-test-livepatch \
|
||||
lxc-test-api-reboot lxc-test-state-server
|
||||
|
||||
bin_SCRIPTS = lxc-test-automount \
|
||||
lxc-test-autostart \
|
||||
@ -117,7 +120,8 @@ EXTRA_DIST = \
|
||||
shortlived.c \
|
||||
shutdowntest.c \
|
||||
snapshot.c \
|
||||
startone.c
|
||||
startone.c \
|
||||
state_server.c
|
||||
|
||||
clean-local:
|
||||
rm -f lxc-test-utils-*
|
||||
|
125
src/tests/api_reboot.c
Normal file
125
src/tests/api_reboot.c
Normal file
@ -0,0 +1,125 @@
|
||||
/* liblxcapi
|
||||
*
|
||||
* Copyright © 2017 Christian Brauner <christian.brauner@ubuntu.com>.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <alloca.h>
|
||||
#include <stdio.h>
|
||||
#include <sched.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <sys/reboot.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include "lxc/lxccontainer.h"
|
||||
#include "lxctest.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int i;
|
||||
struct lxc_container *c;
|
||||
int ret = EXIT_FAILURE;
|
||||
|
||||
/* Test that the reboot() API function properly waits for containers to
|
||||
* restart.
|
||||
*/
|
||||
c = lxc_container_new("reboot", NULL);
|
||||
if (!c) {
|
||||
lxc_error("%s", "Failed to create container \"reboot\"");
|
||||
exit(ret);
|
||||
}
|
||||
|
||||
if (c->is_defined(c)) {
|
||||
lxc_error("%s\n", "Container \"reboot\" is defined");
|
||||
goto on_error_put;
|
||||
}
|
||||
|
||||
if (!c->createl(c, "busybox", NULL, NULL, 0, NULL)) {
|
||||
lxc_error("%s\n", "Failed to create busybox container \"reboot\"");
|
||||
goto on_error_put;
|
||||
}
|
||||
|
||||
if (!c->is_defined(c)) {
|
||||
lxc_error("%s\n", "Container \"reboot\" is not defined");
|
||||
goto on_error_put;
|
||||
}
|
||||
|
||||
c->clear_config(c);
|
||||
|
||||
if (!c->load_config(c, NULL)) {
|
||||
lxc_error("%s\n", "Failed to load config for container \"reboot\"");
|
||||
goto on_error_stop;
|
||||
}
|
||||
|
||||
if (!c->want_daemonize(c, true)) {
|
||||
lxc_error("%s\n", "Failed to mark container \"reboot\" daemonized");
|
||||
goto on_error_stop;
|
||||
}
|
||||
|
||||
if (!c->startl(c, 0, NULL)) {
|
||||
lxc_error("%s\n", "Failed to start container \"reboot\" daemonized");
|
||||
goto on_error_stop;
|
||||
}
|
||||
|
||||
/* reboot 10 times */
|
||||
for (i = 0; i < 10; i++) {
|
||||
/* Give the init system some time to setup it's signal handlers
|
||||
* otherwise we will hang indefinitely.
|
||||
*/
|
||||
sleep(5);
|
||||
|
||||
if (!c->reboot2(c, -1)) {
|
||||
lxc_error("%s\n", "Failed to reboot container \"reboot\"");
|
||||
goto on_error_stop;
|
||||
}
|
||||
|
||||
if (!c->is_running(c)) {
|
||||
lxc_error("%s\n", "Failed to reboot container \"reboot\"");
|
||||
goto on_error_stop;
|
||||
}
|
||||
lxc_debug("%s\n", "Container \"reboot\" rebooted successfully");
|
||||
}
|
||||
|
||||
/* Give the init system some time to setup it's signal handlers
|
||||
* otherwise we will hang indefinitely.
|
||||
*/
|
||||
sleep(5);
|
||||
|
||||
/* Test non-blocking reboot2() */
|
||||
if (!c->reboot2(c, 0)) {
|
||||
lxc_error("%s\n", "Failed to request non-blocking reboot of container \"reboot\"");
|
||||
goto on_error_stop;
|
||||
}
|
||||
lxc_debug("%s\n", "Non-blocking reboot of container \"reboot\" succeeded");
|
||||
|
||||
ret = EXIT_SUCCESS;
|
||||
|
||||
on_error_stop:
|
||||
if (c->is_running(c) && !c->stop(c))
|
||||
lxc_error("%s\n", "Failed to stop container \"reboot\"");
|
||||
|
||||
if (!c->destroy(c))
|
||||
lxc_error("%s\n", "Failed to destroy container \"reboot\"");
|
||||
|
||||
on_error_put:
|
||||
lxc_container_put(c);
|
||||
if (ret == EXIT_SUCCESS)
|
||||
lxc_debug("%s\n", "All reboot tests passed");
|
||||
exit(ret);
|
||||
}
|
@ -203,14 +203,11 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
free(value);
|
||||
|
||||
if (!c->reboot(c)) {
|
||||
if (!c->reboot2(c, -1)) {
|
||||
lxc_error("%s", "Failed to create container \"livepatch\"");
|
||||
goto on_error_stop;
|
||||
}
|
||||
|
||||
/* Busybox shouldn't take long to reboot. Sleep for 5s. */
|
||||
sleep(5);
|
||||
|
||||
if (!c->is_running(c)) {
|
||||
lxc_error("%s\n", "Failed to reboot container \"livepatch\"");
|
||||
goto on_error_destroy;
|
||||
|
@ -28,6 +28,9 @@
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "lxctest.h"
|
||||
#include "utils.h"
|
||||
|
||||
#define MYNAME "shortlived"
|
||||
|
||||
static int destroy_container(void)
|
||||
@ -92,12 +95,33 @@ again:
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct lxc_container *c;
|
||||
int i;
|
||||
const char *s;
|
||||
bool b;
|
||||
int i;
|
||||
struct lxc_container *c;
|
||||
struct lxc_log log;
|
||||
char template[sizeof(P_tmpdir"/shortlived_XXXXXX")];
|
||||
int ret = 0;
|
||||
|
||||
strcpy(template, P_tmpdir"/shortlived_XXXXXX");
|
||||
i = lxc_make_tmpfile(template, false);
|
||||
if (i < 0) {
|
||||
lxc_error("Failed to create temporary log file for container %s\n", MYNAME);
|
||||
exit(EXIT_FAILURE);
|
||||
} else {
|
||||
lxc_debug("Using \"%s\" as temporary log file for container %s\n", template, MYNAME);
|
||||
close(i);
|
||||
}
|
||||
|
||||
log.name = MYNAME;
|
||||
log.file = template;
|
||||
log.level = "TRACE";
|
||||
log.prefix = "shortlived";
|
||||
log.quiet = false;
|
||||
log.lxcpath = NULL;
|
||||
if (lxc_log_init(&log))
|
||||
exit(EXIT_FAILURE);
|
||||
|
||||
ret = 1;
|
||||
/* test a real container */
|
||||
c = lxc_container_new(MYNAME, NULL);
|
||||
@ -141,16 +165,51 @@ int main(int argc, char *argv[])
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!c->set_config_item(c, "lxc.execute.cmd", "echo hello")) {
|
||||
fprintf(stderr, "%d: failed setting lxc.init.cmd\n", __LINE__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
c->want_daemonize(c, true);
|
||||
|
||||
/* Test whether we can start a really short-lived daemonized container.
|
||||
*/
|
||||
/* Test whether we can start a really short-lived daemonized container. */
|
||||
for (i = 0; i < 10; i++) {
|
||||
if (!c->startl(c, 0, NULL)) {
|
||||
fprintf(stderr, "%d: %s failed to start\n", __LINE__, c->name);
|
||||
fprintf(stderr, "%d: %s failed to start on %dth iteration\n", __LINE__, c->name, i);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* The container needs to exit with a successful error code. */
|
||||
if (c->error_num != 0) {
|
||||
fprintf(stderr, "%d: %s exited successfully on %dth iteration\n", __LINE__, c->name, i);
|
||||
goto out;
|
||||
}
|
||||
fprintf(stderr, "%d: %s exited correctly with error code %d on %dth iteration\n", __LINE__, c->name, c->error_num, i);
|
||||
|
||||
if (!c->wait(c, "STOPPED", 30)) {
|
||||
fprintf(stderr, "%d: %s failed to wait on %dth iteration\n", __LINE__, c->name, i);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* Test whether we can start a really short-lived daemonized container with lxc-init. */
|
||||
for (i = 0; i < 10; i++) {
|
||||
if (!c->startl(c, 1, NULL)) {
|
||||
fprintf(stderr, "%d: %s failed to start on %dth iteration\n", __LINE__, c->name, i);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* The container needs to exit with a successful error code. */
|
||||
if (c->error_num != 0) {
|
||||
fprintf(stderr, "%d: %s exited successfully on %dth iteration\n", __LINE__, c->name, i);
|
||||
goto out;
|
||||
}
|
||||
fprintf(stderr, "%d: %s exited correctly with error code %d on %dth iteration\n", __LINE__, c->name, c->error_num, i);
|
||||
|
||||
if (!c->wait(c, "STOPPED", 30)) {
|
||||
fprintf(stderr, "%d: %s failed to wait on %dth iteration\n", __LINE__, c->name, i);
|
||||
goto out;
|
||||
}
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
if (!c->set_config_item(c, "lxc.init.cmd", "you-shall-fail")) {
|
||||
@ -158,15 +217,52 @@ int main(int argc, char *argv[])
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Test whether we catch the start failure of a really short-lived
|
||||
* daemonized container.
|
||||
*/
|
||||
if (!c->set_config_item(c, "lxc.execute.cmd", "you-shall-fail")) {
|
||||
fprintf(stderr, "%d: failed setting lxc.init.cmd\n", __LINE__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Test whether we can start a really short-lived daemonized container. */
|
||||
for (i = 0; i < 10; i++) {
|
||||
if (c->startl(c, 0, NULL)) {
|
||||
fprintf(stderr, "%d: %s failed to start\n", __LINE__, c->name);
|
||||
fprintf(stderr, "%d: %s failed to start on %dth iteration\n", __LINE__, c->name, i);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* The container needs to exit with an error code.*/
|
||||
if (c->error_num == 0) {
|
||||
fprintf(stderr, "%d: %s exited successfully on %dth iteration\n", __LINE__, c->name, i);
|
||||
goto out;
|
||||
}
|
||||
fprintf(stderr, "%d: %s exited correctly with error code %d on %dth iteration\n", __LINE__, c->name, c->error_num, i);
|
||||
|
||||
if (!c->wait(c, "STOPPED", 30)) {
|
||||
fprintf(stderr, "%d: %s failed to wait on %dth iteration\n", __LINE__, c->name, i);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* Test whether we can start a really short-lived daemonized container with lxc-init. */
|
||||
for (i = 0; i < 10; i++) {
|
||||
/* An container started with lxc-init will always start
|
||||
* succesfully unless lxc-init has a bug.
|
||||
*/
|
||||
if (!c->startl(c, 1, NULL)) {
|
||||
fprintf(stderr, "%d: %s failed to start on %dth iteration\n", __LINE__, c->name, i);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* The container needs to exit with an error code.*/
|
||||
if (c->error_num == 0) {
|
||||
fprintf(stderr, "%d: %s exited successfully on %dth iteration\n", __LINE__, c->name, i);
|
||||
goto out;
|
||||
}
|
||||
fprintf(stderr, "%d: %s exited correctly with error code %d on %dth iteration\n", __LINE__, c->name, c->error_num, i);
|
||||
|
||||
if (!c->wait(c, "STOPPED", 30)) {
|
||||
fprintf(stderr, "%d: %s failed to wait on %dth iteration\n", __LINE__, c->name, i);
|
||||
goto out;
|
||||
}
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
c->stop(c);
|
||||
@ -180,5 +276,6 @@ out:
|
||||
destroy_container();
|
||||
}
|
||||
lxc_container_put(c);
|
||||
unlink(template);
|
||||
exit(ret);
|
||||
}
|
||||
|
153
src/tests/state_server.c
Normal file
153
src/tests/state_server.c
Normal file
@ -0,0 +1,153 @@
|
||||
/* liblxcapi
|
||||
*
|
||||
* Copyright © 2017 Christian Brauner <christian.brauner@ubuntu.com>.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <alloca.h>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/reboot.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include "lxc/lxccontainer.h"
|
||||
#include "lxctest.h"
|
||||
|
||||
struct thread_args {
|
||||
int thread_id;
|
||||
int timeout;
|
||||
bool success;
|
||||
struct lxc_container *c;
|
||||
};
|
||||
|
||||
void *state_wrapper(void *data)
|
||||
{
|
||||
struct thread_args *args = data;
|
||||
|
||||
lxc_debug("Starting state server thread %d\n", args->thread_id);
|
||||
|
||||
args->success = args->c->shutdown(args->c, args->timeout);
|
||||
|
||||
lxc_debug("State server thread %d with shutdown timeout %d returned \"%s\"\n",
|
||||
args->thread_id, args->timeout, args->success ? "SUCCESS" : "FAILED");
|
||||
|
||||
pthread_exit(NULL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int i, j;
|
||||
pthread_attr_t attr;
|
||||
pthread_t threads[10];
|
||||
struct thread_args args[10];
|
||||
struct lxc_container *c;
|
||||
int ret = EXIT_FAILURE;
|
||||
|
||||
c = lxc_container_new("state-server", NULL);
|
||||
if (!c) {
|
||||
lxc_error("%s", "Failed to create container \"state-server\"");
|
||||
exit(ret);
|
||||
}
|
||||
|
||||
if (c->is_defined(c)) {
|
||||
lxc_error("%s\n", "Container \"state-server\" is defined");
|
||||
goto on_error_put;
|
||||
}
|
||||
|
||||
if (!c->createl(c, "busybox", NULL, NULL, 0, NULL)) {
|
||||
lxc_error("%s\n", "Failed to create busybox container \"state-server\"");
|
||||
goto on_error_put;
|
||||
}
|
||||
|
||||
if (!c->is_defined(c)) {
|
||||
lxc_error("%s\n", "Container \"state-server\" is not defined");
|
||||
goto on_error_put;
|
||||
}
|
||||
|
||||
c->clear_config(c);
|
||||
|
||||
if (!c->load_config(c, NULL)) {
|
||||
lxc_error("%s\n", "Failed to load config for container \"state-server\"");
|
||||
goto on_error_stop;
|
||||
}
|
||||
|
||||
if (!c->want_daemonize(c, true)) {
|
||||
lxc_error("%s\n", "Failed to mark container \"state-server\" daemonized");
|
||||
goto on_error_stop;
|
||||
}
|
||||
|
||||
pthread_attr_init(&attr);
|
||||
|
||||
for (j = 0; j < 10; j++) {
|
||||
lxc_debug("Starting state server test iteration %d\n", j);
|
||||
|
||||
if (!c->startl(c, 0, NULL)) {
|
||||
lxc_error("%s\n", "Failed to start container \"state-server\" daemonized");
|
||||
goto on_error_stop;
|
||||
}
|
||||
|
||||
sleep(5);
|
||||
|
||||
for (i = 0; i < 10; i++) {
|
||||
int ret;
|
||||
|
||||
args[i].thread_id = i;
|
||||
args[i].c = c;
|
||||
args[i].timeout = -1;
|
||||
/* test non-blocking shutdown request */
|
||||
if (i == 0)
|
||||
args[i].timeout = 0;
|
||||
|
||||
ret = pthread_create(&threads[i], &attr, state_wrapper, (void *) &args[i]);
|
||||
if (ret != 0)
|
||||
goto on_error_stop;
|
||||
}
|
||||
|
||||
for (i = 0; i < 10; i++) {
|
||||
int ret;
|
||||
|
||||
ret = pthread_join(threads[i], NULL);
|
||||
if (ret != 0)
|
||||
goto on_error_stop;
|
||||
|
||||
if (!args[i].success) {
|
||||
lxc_error("State server thread %d failed\n", args[i].thread_id);
|
||||
goto on_error_stop;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret = EXIT_SUCCESS;
|
||||
|
||||
on_error_stop:
|
||||
if (c->is_running(c) && !c->stop(c))
|
||||
lxc_error("%s\n", "Failed to stop container \"state-server\"");
|
||||
|
||||
if (!c->destroy(c))
|
||||
lxc_error("%s\n", "Failed to destroy container \"state-server\"");
|
||||
|
||||
on_error_put:
|
||||
lxc_container_put(c);
|
||||
if (ret == EXIT_SUCCESS)
|
||||
lxc_debug("%s\n", "All state server tests passed");
|
||||
exit(ret);
|
||||
}
|
Loading…
Reference in New Issue
Block a user