mirror of
https://git.proxmox.com/git/mirror_lxc
synced 2025-08-16 09:01:09 +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) {
|
if (ret < 0) {
|
||||||
WARN("%s - Failed to receive response for command \"%s\"",
|
WARN("%s - Failed to receive response for command \"%s\"",
|
||||||
strerror(errno), lxc_cmd_str(cmd->req.cmd));
|
strerror(errno), lxc_cmd_str(cmd->req.cmd));
|
||||||
|
if (errno == ECONNRESET)
|
||||||
|
return -ECONNRESET;
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
TRACE("Command \"%s\" received response", lxc_cmd_str(cmd->req.cmd));
|
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);
|
ret = lxc_cmd_rsp_recv(client_fd, cmd);
|
||||||
|
if (ret == -ECONNRESET)
|
||||||
|
*stopped = 1;
|
||||||
out:
|
out:
|
||||||
if (!stay_connected || ret <= 0)
|
if (!stay_connected || ret <= 0)
|
||||||
close(client_fd);
|
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],
|
lxc_state_t states[MAX_STATE],
|
||||||
int *state_client_fd)
|
int *state_client_fd)
|
||||||
{
|
{
|
||||||
int stopped;
|
int state, stopped;
|
||||||
ssize_t ret;
|
ssize_t ret;
|
||||||
int state = -1;
|
|
||||||
struct lxc_cmd_rr cmd = {
|
struct lxc_cmd_rr cmd = {
|
||||||
.req = {
|
.req = {
|
||||||
.cmd = LXC_CMD_ADD_STATE_CLIENT,
|
.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);
|
ret = lxc_cmd(name, &cmd, &stopped, lxcpath, NULL);
|
||||||
process_unlock();
|
if (states[STOPPED] != 0 && stopped != 0)
|
||||||
|
return STOPPED;
|
||||||
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
ERROR("%s - Failed to execute command", strerror(errno));
|
ERROR("%s - Failed to execute command", strerror(errno));
|
||||||
return -1;
|
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
|
/* We should now be guaranteed to get an answer from the state sending
|
||||||
* function.
|
* function.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (cmd.rsp.ret < 0) {
|
if (cmd.rsp.ret < 0) {
|
||||||
ERROR("Failed to receive socket fd");
|
ERROR("%s - Failed to receive socket fd", strerror(-cmd.rsp.ret));
|
||||||
return -1;
|
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;
|
*state_client_fd = cmd.rsp.ret;
|
||||||
|
TRACE("Added state client %d to state client list", cmd.rsp.ret);
|
||||||
return MAX_STATE;
|
return MAX_STATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int lxc_cmd_add_state_client_callback(int fd, struct lxc_cmd_req *req,
|
static int lxc_cmd_add_state_client_callback(int fd, struct lxc_cmd_req *req,
|
||||||
struct lxc_handler *handler)
|
struct lxc_handler *handler)
|
||||||
{
|
{
|
||||||
|
int ret;
|
||||||
struct lxc_cmd_rsp rsp = {0};
|
struct lxc_cmd_rsp rsp = {0};
|
||||||
|
|
||||||
if (req->datalen < 0)
|
if (req->datalen < 0)
|
||||||
return -1;
|
goto reap_client_fd;
|
||||||
|
|
||||||
if (req->datalen > (sizeof(lxc_state_t) * MAX_STATE))
|
if (req->datalen > (sizeof(lxc_state_t) * MAX_STATE))
|
||||||
return -1;
|
goto reap_client_fd;
|
||||||
|
|
||||||
if (!req->data)
|
if (!req->data)
|
||||||
return -1;
|
goto reap_client_fd;
|
||||||
|
|
||||||
rsp.ret = lxc_add_state_client(fd, handler, (lxc_state_t *)req->data);
|
rsp.ret = lxc_add_state_client(fd, handler, (lxc_state_t *)req->data);
|
||||||
if (rsp.ret < 0)
|
if (rsp.ret < 0)
|
||||||
ERROR("Failed to add state client %d to state client list", fd);
|
goto reap_client_fd;
|
||||||
else
|
|
||||||
TRACE("Added state client %d to state client list", 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,
|
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,
|
struct lxc_epoll_descr *descr,
|
||||||
const lxc_cmd_t cmd)
|
const lxc_cmd_t cmd)
|
||||||
{
|
{
|
||||||
struct state_client *client;
|
struct lxc_state_client *client;
|
||||||
struct lxc_list *cur, *next;
|
struct lxc_list *cur, *next;
|
||||||
|
|
||||||
lxc_console_free(handler->conf, fd);
|
lxc_console_free(handler->conf, fd);
|
||||||
@ -1166,7 +1152,7 @@ static void lxc_cmd_fd_cleanup(int fd, struct lxc_handler *handler,
|
|||||||
}
|
}
|
||||||
|
|
||||||
process_lock();
|
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;
|
client = cur->elem;
|
||||||
if (client->clientfd != fd)
|
if (client->clientfd != fd)
|
||||||
continue;
|
continue;
|
||||||
@ -1176,6 +1162,10 @@ static void lxc_cmd_fd_cleanup(int fd, struct lxc_handler *handler,
|
|||||||
lxc_list_del(cur);
|
lxc_list_del(cur);
|
||||||
free(cur->elem);
|
free(cur->elem);
|
||||||
free(cur);
|
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();
|
process_unlock();
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,7 @@
|
|||||||
#include "commands_utils.h"
|
#include "commands_utils.h"
|
||||||
#include "initutils.h"
|
#include "initutils.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
#include "lxclock.h"
|
||||||
#include "monitor.h"
|
#include "monitor.h"
|
||||||
#include "state.h"
|
#include "state.h"
|
||||||
#include "utils.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,
|
int lxc_add_state_client(int state_client_fd, struct lxc_handler *handler,
|
||||||
lxc_state_t states[MAX_STATE])
|
lxc_state_t states[MAX_STATE])
|
||||||
{
|
{
|
||||||
struct state_client *newclient;
|
int state;
|
||||||
|
struct lxc_state_client *newclient;
|
||||||
struct lxc_list *tmplist;
|
struct lxc_list *tmplist;
|
||||||
|
|
||||||
newclient = malloc(sizeof(*newclient));
|
newclient = malloc(sizeof(*newclient));
|
||||||
@ -209,10 +211,19 @@ int lxc_add_state_client(int state_client_fd, struct lxc_handler *handler,
|
|||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
lxc_list_add_elem(tmplist, newclient);
|
process_lock();
|
||||||
lxc_list_add_tail(&handler->state_clients, tmplist);
|
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);
|
TRACE("added state client %d to state client list", state_client_fd);
|
||||||
|
return MAX_STATE;
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
@ -2419,6 +2419,7 @@ struct lxc_conf *lxc_conf_init(void)
|
|||||||
for (i = 0; i < NUM_LXC_HOOKS; i++)
|
for (i = 0; i < NUM_LXC_HOOKS; i++)
|
||||||
lxc_list_init(&new->hooks[i]);
|
lxc_list_init(&new->hooks[i]);
|
||||||
lxc_list_init(&new->groups);
|
lxc_list_init(&new->groups);
|
||||||
|
lxc_list_init(&new->state_clients);
|
||||||
new->lsm_aa_profile = NULL;
|
new->lsm_aa_profile = NULL;
|
||||||
new->lsm_se_context = NULL;
|
new->lsm_se_context = NULL;
|
||||||
new->tmp_umount_proc = 0;
|
new->tmp_umount_proc = 0;
|
||||||
|
@ -248,6 +248,11 @@ enum lxchooks {
|
|||||||
|
|
||||||
extern char *lxchook_names[NUM_LXC_HOOKS];
|
extern char *lxchook_names[NUM_LXC_HOOKS];
|
||||||
|
|
||||||
|
struct lxc_state_client {
|
||||||
|
int clientfd;
|
||||||
|
lxc_state_t states[MAX_STATE];
|
||||||
|
};
|
||||||
|
|
||||||
struct lxc_conf {
|
struct lxc_conf {
|
||||||
int is_execute;
|
int is_execute;
|
||||||
char *fstab;
|
char *fstab;
|
||||||
@ -363,6 +368,8 @@ struct lxc_conf {
|
|||||||
/* init working directory */
|
/* init working directory */
|
||||||
char* init_cwd;
|
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,
|
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());
|
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)
|
if (my_args->quiet)
|
||||||
argv[i++] = "--quiet";
|
argv[i++] = "--quiet";
|
||||||
|
|
||||||
|
@ -828,10 +828,6 @@ static bool do_lxcapi_start(struct lxc_container *c, int useinit, char * const a
|
|||||||
return false;
|
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))
|
if (container_mem_lock(c))
|
||||||
return false;
|
return false;
|
||||||
conf = c->lxc_conf;
|
conf = c->lxc_conf;
|
||||||
@ -843,15 +839,20 @@ static bool do_lxcapi_start(struct lxc_container *c, int useinit, char * const a
|
|||||||
if (!handler)
|
if (!handler)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
/* If no argv was passed in, use lxc.init_cmd if provided in the
|
if (!argv) {
|
||||||
* configuration
|
if (useinit && conf->execute_cmd)
|
||||||
*/
|
argv = init_cmd = split_init_cmd(conf->execute_cmd);
|
||||||
if (!argv)
|
else
|
||||||
argv = init_cmd = split_init_cmd(conf->init_cmd);
|
argv = init_cmd = split_init_cmd(conf->init_cmd);
|
||||||
|
}
|
||||||
|
|
||||||
/* ... otherwise use default_args. */
|
/* ... otherwise use default_args. */
|
||||||
if (!argv)
|
if (!argv) {
|
||||||
argv = default_args;
|
if (useinit)
|
||||||
|
return false;
|
||||||
|
else
|
||||||
|
argv = default_args;
|
||||||
|
}
|
||||||
|
|
||||||
/* I'm not sure what locks we want here.Any? Is liblxc's locking enough
|
/* 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
|
* 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)
|
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;
|
int killret, ret;
|
||||||
bool retv = false;
|
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
int haltsignal = SIGPWR;
|
int rebootsignal = SIGINT, state_client_fd = -1;
|
||||||
lxc_state_t states[MAX_STATE] = {0};
|
lxc_state_t states[MAX_STATE] = {0};
|
||||||
|
|
||||||
if (!c)
|
if (!c)
|
||||||
@ -1806,6 +1806,74 @@ static bool do_lxcapi_shutdown(struct lxc_container *c, int timeout)
|
|||||||
|
|
||||||
if (!do_lxcapi_is_running(c))
|
if (!do_lxcapi_is_running(c))
|
||||||
return true;
|
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);
|
pid = do_lxcapi_init_pid(c);
|
||||||
if (pid <= 0)
|
if (pid <= 0)
|
||||||
return true;
|
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)
|
if (c->lxc_conf && c->lxc_conf->haltsignal)
|
||||||
haltsignal = 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
|
/* Add a new state client before sending the shutdown signal so that we
|
||||||
* don't miss a state.
|
* don't miss a state.
|
||||||
*/
|
*/
|
||||||
states[STOPPED] = 1;
|
if (timeout != 0) {
|
||||||
ret = lxc_cmd_add_state_client(c->name, c->config_path, states,
|
states[STOPPED] = 1;
|
||||||
&state_client_fd);
|
ret = lxc_cmd_add_state_client(c->name, c->config_path, states,
|
||||||
|
&state_client_fd);
|
||||||
/* Send shutdown signal to container. */
|
if (ret < 0)
|
||||||
if (kill(pid, haltsignal) < 0)
|
return false;
|
||||||
WARN("Could not send signal %d to pid %d", haltsignal, pid);
|
|
||||||
|
if (state_client_fd < 0)
|
||||||
/* Retrieve the state. */
|
return false;
|
||||||
if (state_client_fd >= 0) {
|
|
||||||
int state;
|
if (ret == STOPPED)
|
||||||
state = lxc_cmd_sock_rcv_state(state_client_fd, timeout);
|
return true;
|
||||||
close(state_client_fd);
|
|
||||||
TRACE("Received state \"%s\"", lxc_state2str(state));
|
if (ret < MAX_STATE)
|
||||||
if (state != STOPPED)
|
|
||||||
return false;
|
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)
|
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->createl = lxcapi_createl;
|
||||||
c->shutdown = lxcapi_shutdown;
|
c->shutdown = lxcapi_shutdown;
|
||||||
c->reboot = lxcapi_reboot;
|
c->reboot = lxcapi_reboot;
|
||||||
|
c->reboot2 = lxcapi_reboot2;
|
||||||
c->clear_config = lxcapi_clear_config;
|
c->clear_config = lxcapi_clear_config;
|
||||||
c->clear_config_item = lxcapi_clear_config_item;
|
c->clear_config_item = lxcapi_clear_config_item;
|
||||||
c->get_config_item = lxcapi_get_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.
|
* \return \c 0 on success, nonzero on failure.
|
||||||
*/
|
*/
|
||||||
int (*console_log)(struct lxc_container *c, struct lxc_console_log *log);
|
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);
|
fddir = dirfd(dir);
|
||||||
|
|
||||||
while ((direntp = readdir(dir))) {
|
while ((direntp = readdir(dir))) {
|
||||||
|
struct lxc_list *cur;
|
||||||
|
bool matched = false;
|
||||||
|
|
||||||
if (!direntp)
|
if (!direntp)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -222,6 +225,20 @@ restart:
|
|||||||
(i < len_fds && fd == fds_to_ignore[i]))
|
(i < len_fds && fd == fds_to_ignore[i]))
|
||||||
continue;
|
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)
|
if (current_config && fd == current_config->logfd)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@ -338,18 +355,14 @@ static int lxc_serve_state_clients(const char *name,
|
|||||||
{
|
{
|
||||||
ssize_t ret;
|
ssize_t ret;
|
||||||
struct lxc_list *cur, *next;
|
struct lxc_list *cur, *next;
|
||||||
struct state_client *client;
|
struct lxc_state_client *client;
|
||||||
struct lxc_msg msg = {.type = lxc_msg_state, .value = state};
|
struct lxc_msg msg = {.type = lxc_msg_state, .value = state};
|
||||||
|
|
||||||
process_lock();
|
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;
|
handler->state = state;
|
||||||
TRACE("Set container state to %s", lxc_state2str(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");
|
TRACE("No state clients registered");
|
||||||
process_unlock();
|
process_unlock();
|
||||||
lxc_monitor_send_state(name, state, handler->lxcpath);
|
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));
|
strncpy(msg.name, name, sizeof(msg.name));
|
||||||
msg.name[sizeof(msg.name) - 1] = 0;
|
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;
|
client = cur->elem;
|
||||||
|
|
||||||
if (!client->states[state]) {
|
if (client->states[state] == 0) {
|
||||||
TRACE("State %s not registered for state client %d",
|
TRACE("State %s not registered for state client %d",
|
||||||
lxc_state2str(state), client->clientfd);
|
lxc_state2str(state), client->clientfd);
|
||||||
continue;
|
continue;
|
||||||
@ -531,7 +544,8 @@ struct lxc_handler *lxc_init_handler(const char *name, struct lxc_conf *conf,
|
|||||||
handler->lxcpath = lxcpath;
|
handler->lxcpath = lxcpath;
|
||||||
handler->pinfd = -1;
|
handler->pinfd = -1;
|
||||||
handler->state_socket_pair[0] = handler->state_socket_pair[1] = -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++)
|
for (i = 0; i < LXC_NS_MAX; i++)
|
||||||
handler->nsfd[i] = -1;
|
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->state_socket_pair[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
handler->conf->maincmd_fd = lxc_cmd_init(name, lxcpath, "command");
|
if (handler->conf->reboot == 0) {
|
||||||
if (handler->conf->maincmd_fd < 0) {
|
handler->conf->maincmd_fd = lxc_cmd_init(name, lxcpath, "command");
|
||||||
ERROR("Failed to set up command socket");
|
if (handler->conf->maincmd_fd < 0) {
|
||||||
goto on_error;
|
ERROR("Failed to set up command socket");
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
TRACE("Unix domain socket %d for command server is ready",
|
TRACE("Unix domain socket %d for command server is ready",
|
||||||
handler->conf->maincmd_fd);
|
handler->conf->maincmd_fd);
|
||||||
@ -715,9 +731,11 @@ void lxc_fini(const char *name, struct lxc_handler *handler)
|
|||||||
|
|
||||||
lxc_set_state(name, handler, STOPPED);
|
lxc_set_state(name, handler, STOPPED);
|
||||||
|
|
||||||
/* close command socket */
|
if (handler->conf->reboot == 0) {
|
||||||
close(handler->conf->maincmd_fd);
|
/* close command socket */
|
||||||
handler->conf->maincmd_fd = -1;
|
close(handler->conf->maincmd_fd);
|
||||||
|
handler->conf->maincmd_fd = -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (run_lxc_hooks(name, "post-stop", handler->conf, handler->lxcpath, NULL)) {
|
if (run_lxc_hooks(name, "post-stop", handler->conf, handler->lxcpath, NULL)) {
|
||||||
ERROR("Failed to run lxc.hook.post-stop for container \"%s\".", name);
|
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
|
/* The command socket is now closed, no more state clients can register
|
||||||
* themselves from now on. So free the list of state clients.
|
* themselves from now on. So free the list of state clients.
|
||||||
*/
|
*/
|
||||||
lxc_list_for_each_safe(cur, &handler->state_clients, next) {
|
lxc_list_for_each_safe(cur, &handler->conf->state_clients, next) {
|
||||||
struct state_client *client = cur->elem;
|
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 state client socket */
|
||||||
close(client->clientfd);
|
close(client->clientfd);
|
||||||
lxc_list_del(cur);
|
lxc_list_del(cur);
|
||||||
@ -801,9 +824,8 @@ static int do_start(void *data)
|
|||||||
lxc_sync_fini_parent(handler);
|
lxc_sync_fini_parent(handler);
|
||||||
|
|
||||||
/* Don't leak the pinfd to the container. */
|
/* Don't leak the pinfd to the container. */
|
||||||
if (handler->pinfd >= 0) {
|
if (handler->pinfd >= 0)
|
||||||
close(handler->pinfd);
|
close(handler->pinfd);
|
||||||
}
|
|
||||||
|
|
||||||
if (lxc_sync_wait_parent(handler, LXC_SYNC_STARTUP))
|
if (lxc_sync_wait_parent(handler, LXC_SYNC_STARTUP))
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -85,9 +85,6 @@ struct lxc_handler {
|
|||||||
/* The container's in-memory configuration. */
|
/* The container's in-memory configuration. */
|
||||||
struct lxc_conf *conf;
|
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
|
/* A set of operations to be performed at various stages of the
|
||||||
* container's life.
|
* container's life.
|
||||||
*/
|
*/
|
||||||
@ -110,11 +107,6 @@ struct lxc_operations {
|
|||||||
int (*post_start)(struct lxc_handler *, void *);
|
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_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 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);
|
extern void lxc_abort(const char *name, struct lxc_handler *handler);
|
||||||
|
@ -45,9 +45,9 @@
|
|||||||
|
|
||||||
lxc_log_define(lxc_state, lxc);
|
lxc_log_define(lxc_state, lxc);
|
||||||
|
|
||||||
static const char * const strstate[] = {
|
static const char *const strstate[] = {
|
||||||
"STOPPED", "STARTING", "RUNNING", "STOPPING",
|
"STOPPED", "STARTING", "RUNNING", "STOPPING",
|
||||||
"ABORTING", "FREEZING", "FROZEN", "THAWED",
|
"ABORTING", "FREEZING", "FROZEN", "THAWED",
|
||||||
};
|
};
|
||||||
|
|
||||||
const char *lxc_state2str(lxc_state_t state)
|
const char *lxc_state2str(lxc_state_t state)
|
||||||
|
@ -94,62 +94,6 @@ Options :\n\
|
|||||||
.timeout = -2,
|
.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[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
struct lxc_container *c;
|
struct lxc_container *c;
|
||||||
@ -259,7 +203,11 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
/* reboot */
|
/* reboot */
|
||||||
if (my_args.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;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ lxc_test_snapshot_SOURCES = snapshot.c
|
|||||||
lxc_test_concurrent_SOURCES = concurrent.c
|
lxc_test_concurrent_SOURCES = concurrent.c
|
||||||
lxc_test_may_control_SOURCES = may_control.c
|
lxc_test_may_control_SOURCES = may_control.c
|
||||||
lxc_test_reboot_SOURCES = reboot.c
|
lxc_test_reboot_SOURCES = reboot.c
|
||||||
|
lxc_test_api_reboot_SOURCES = api_reboot.c
|
||||||
lxc_test_list_SOURCES = list.c
|
lxc_test_list_SOURCES = list.c
|
||||||
lxc_test_attach_SOURCES = attach.c
|
lxc_test_attach_SOURCES = attach.c
|
||||||
lxc_test_device_add_remove_SOURCES = device_add_remove.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_config_jump_table_SOURCES = config_jump_table.c lxctest.h
|
||||||
lxc_test_shortlived_SOURCES = shortlived.c
|
lxc_test_shortlived_SOURCES = shortlived.c
|
||||||
lxc_test_livepatch_SOURCES = livepatch.c lxctest.h
|
lxc_test_livepatch_SOURCES = livepatch.c lxctest.h
|
||||||
|
lxc_test_state_server_SOURCES = state_server.c lxctest.h
|
||||||
|
|
||||||
AM_CFLAGS=-DLXCROOTFSMOUNT=\"$(LXCROOTFSMOUNT)\" \
|
AM_CFLAGS=-DLXCROOTFSMOUNT=\"$(LXCROOTFSMOUNT)\" \
|
||||||
-DLXCPATH=\"$(LXCPATH)\" \
|
-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-snapshot lxc-test-concurrent lxc-test-may-control \
|
||||||
lxc-test-reboot lxc-test-list lxc-test-attach lxc-test-device-add-remove \
|
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-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 \
|
bin_SCRIPTS = lxc-test-automount \
|
||||||
lxc-test-autostart \
|
lxc-test-autostart \
|
||||||
@ -117,7 +120,8 @@ EXTRA_DIST = \
|
|||||||
shortlived.c \
|
shortlived.c \
|
||||||
shutdowntest.c \
|
shutdowntest.c \
|
||||||
snapshot.c \
|
snapshot.c \
|
||||||
startone.c
|
startone.c \
|
||||||
|
state_server.c
|
||||||
|
|
||||||
clean-local:
|
clean-local:
|
||||||
rm -f lxc-test-utils-*
|
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);
|
free(value);
|
||||||
|
|
||||||
if (!c->reboot(c)) {
|
if (!c->reboot2(c, -1)) {
|
||||||
lxc_error("%s", "Failed to create container \"livepatch\"");
|
lxc_error("%s", "Failed to create container \"livepatch\"");
|
||||||
goto on_error_stop;
|
goto on_error_stop;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Busybox shouldn't take long to reboot. Sleep for 5s. */
|
|
||||||
sleep(5);
|
|
||||||
|
|
||||||
if (!c->is_running(c)) {
|
if (!c->is_running(c)) {
|
||||||
lxc_error("%s\n", "Failed to reboot container \"livepatch\"");
|
lxc_error("%s\n", "Failed to reboot container \"livepatch\"");
|
||||||
goto on_error_destroy;
|
goto on_error_destroy;
|
||||||
|
@ -28,6 +28,9 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#include "lxctest.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
#define MYNAME "shortlived"
|
#define MYNAME "shortlived"
|
||||||
|
|
||||||
static int destroy_container(void)
|
static int destroy_container(void)
|
||||||
@ -92,12 +95,33 @@ again:
|
|||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
struct lxc_container *c;
|
int i;
|
||||||
const char *s;
|
const char *s;
|
||||||
bool b;
|
bool b;
|
||||||
int i;
|
struct lxc_container *c;
|
||||||
|
struct lxc_log log;
|
||||||
|
char template[sizeof(P_tmpdir"/shortlived_XXXXXX")];
|
||||||
int ret = 0;
|
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;
|
ret = 1;
|
||||||
/* test a real container */
|
/* test a real container */
|
||||||
c = lxc_container_new(MYNAME, NULL);
|
c = lxc_container_new(MYNAME, NULL);
|
||||||
@ -141,16 +165,51 @@ int main(int argc, char *argv[])
|
|||||||
goto out;
|
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);
|
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++) {
|
for (i = 0; i < 10; i++) {
|
||||||
if (!c->startl(c, 0, NULL)) {
|
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;
|
goto out;
|
||||||
}
|
}
|
||||||
sleep(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!c->set_config_item(c, "lxc.init.cmd", "you-shall-fail")) {
|
if (!c->set_config_item(c, "lxc.init.cmd", "you-shall-fail")) {
|
||||||
@ -158,15 +217,52 @@ int main(int argc, char *argv[])
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Test whether we catch the start failure of a really short-lived
|
if (!c->set_config_item(c, "lxc.execute.cmd", "you-shall-fail")) {
|
||||||
* daemonized container.
|
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++) {
|
for (i = 0; i < 10; i++) {
|
||||||
if (c->startl(c, 0, NULL)) {
|
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;
|
goto out;
|
||||||
}
|
}
|
||||||
sleep(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
c->stop(c);
|
c->stop(c);
|
||||||
@ -180,5 +276,6 @@ out:
|
|||||||
destroy_container();
|
destroy_container();
|
||||||
}
|
}
|
||||||
lxc_container_put(c);
|
lxc_container_put(c);
|
||||||
|
unlink(template);
|
||||||
exit(ret);
|
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