mirror of
https://git.proxmox.com/git/mirror_lxc
synced 2025-08-06 05:17:59 +00:00
extend command processor to handle generic data
Motivation for this change is to have the ability to get the run-time configuration items from a container, which may differ from its current on disk configuration, or might not be available any other way (for example lxc.network.0.veth.pair). In adding this ability it seemed there was room for refactoring improvements. Genericize the command infrastructure so that both command requests and responses can have arbitrary data. Consolidate all commands into command.c and name them consistently. This allows all the callback routines to be made static, reducing exposure. Return the actual allocated tty for the console command. Don't print the init pid in lxc_info if the container isn't actually running. Command processing was made more thread safe by removing the static buffer from receive_answer(). Refactored command response code to a common routine. Signed-off-by: Dwight Engen <dwight.engen@oracle.com> Signed-off-by: Serge Hallyn <serge.hallyn@ubuntu.com>
This commit is contained in:
parent
9c83a66139
commit
ef6e34eec8
@ -40,7 +40,6 @@ liblxc_so_SOURCES = \
|
||||
bdev.c bdev.h \
|
||||
commands.c commands.h \
|
||||
start.c start.h \
|
||||
stop.c \
|
||||
execute.c \
|
||||
monitor.c monitor.h \
|
||||
console.c \
|
||||
|
@ -120,7 +120,7 @@ static int get_cgroup_mount(const char *subsystem, char *mnt)
|
||||
while ((getmntent_r(file, &mntent_r, buf, sizeof(buf)))) {
|
||||
if (strcmp(mntent_r.mnt_type, "cgroup") != 0)
|
||||
continue;
|
||||
|
||||
|
||||
if (subsystem) {
|
||||
if (!hasmntopt(&mntent_r, subsystem))
|
||||
continue;
|
||||
@ -214,56 +214,10 @@ fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate a container's cgroup path for a particular subsystem. This
|
||||
* is the cgroup path relative to the root of the cgroup filesystem.
|
||||
* @path: A char ** into which we copy the char* containing the answer
|
||||
* @subsystem: the cgroup subsystem of interest (i.e. freezer)
|
||||
* @name: container name
|
||||
* @lxcpath: the lxcpath in which the container is running.
|
||||
*
|
||||
* Returns 0 on success, -1 on error.
|
||||
*
|
||||
* Note that the char* copied into *path is a static char[MAXPATHLEN] in
|
||||
* commands.c:receive_answer(). It should not be freed.
|
||||
*/
|
||||
extern int lxc_get_cgpath(const char **path, const char *subsystem, const char *name, const char *lxcpath)
|
||||
{
|
||||
struct lxc_command command = {
|
||||
.request = { .type = LXC_COMMAND_CGROUP },
|
||||
};
|
||||
|
||||
int ret, stopped = 0;
|
||||
|
||||
ret = lxc_command(name, &command, &stopped, lxcpath);
|
||||
if (ret < 0) {
|
||||
if (!stopped)
|
||||
ERROR("failed to send command");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!ret) {
|
||||
WARN("'%s' has stopped before sending its state", name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (command.answer.ret < 0 || command.answer.pathlen < 0) {
|
||||
ERROR("failed to get state for '%s': %s",
|
||||
name, strerror(-command.answer.ret));
|
||||
return -1;
|
||||
}
|
||||
|
||||
*path = command.answer.path;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* lxc_cgroup_path_get: determine full pathname for a cgroup
|
||||
* file for a specific container.
|
||||
* @path: char ** used to return the answer. The char * will point
|
||||
* into the static char* retuf from cgroup_path_get() (so no need
|
||||
* to free it).
|
||||
* @path: char ** used to return the answer.
|
||||
* @subsystem: cgroup subsystem (i.e. "freezer") for which to
|
||||
* return an answer. If NULL, then the first cgroup entry in
|
||||
* mtab will be used.
|
||||
@ -275,12 +229,16 @@ extern int lxc_get_cgpath(const char **path, const char *subsystem, const char *
|
||||
*/
|
||||
int lxc_cgroup_path_get(char **path, const char *subsystem, const char *name, const char *lxcpath)
|
||||
{
|
||||
const char *cgpath;
|
||||
int ret;
|
||||
char *cgpath;
|
||||
|
||||
if (lxc_get_cgpath(&cgpath, subsystem, name, lxcpath) < 0)
|
||||
cgpath = lxc_cmd_get_cgroup_path(subsystem, name, lxcpath);
|
||||
if (!cgpath)
|
||||
return -1;
|
||||
|
||||
return cgroup_path_get(path, subsystem, cgpath);
|
||||
ret = cgroup_path_get(path, subsystem, cgpath);
|
||||
free(cgpath);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -917,13 +875,17 @@ int lxc_cgroup_destroy(const char *cgpath)
|
||||
|
||||
int lxc_cgroup_attach(pid_t pid, const char *name, const char *lxcpath)
|
||||
{
|
||||
const char *dirpath;
|
||||
int ret;
|
||||
char *dirpath;
|
||||
|
||||
if (lxc_get_cgpath(&dirpath, NULL, name, lxcpath) < 0) {
|
||||
dirpath = lxc_cmd_get_cgroup_path(NULL, name, lxcpath);
|
||||
if (!dirpath) {
|
||||
ERROR("Error getting cgroup for container %s: %s", lxcpath, name);
|
||||
return -1;
|
||||
}
|
||||
INFO("joining pid %d to cgroup %s", pid, dirpath);
|
||||
|
||||
return lxc_cgroup_enter(dirpath, pid);
|
||||
ret = lxc_cgroup_enter(dirpath, pid);
|
||||
free(dirpath);
|
||||
return ret;
|
||||
}
|
||||
|
@ -34,5 +34,4 @@ extern char *lxc_cgroup_path_create(const char *lxcgroup, const char *name);
|
||||
extern int lxc_cgroup_enter(const char *cgpath, pid_t pid);
|
||||
extern int lxc_cgroup_attach(pid_t pid, const char *name, const char *lxcpath);
|
||||
extern int cgroup_path_get(char **path, const char *subsystem, const char *cgpath);
|
||||
extern int lxc_get_cgpath(const char **path, const char *subsystem, const char *name, const char *lxcpath);
|
||||
#endif
|
||||
|
@ -34,27 +34,30 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <lxc/log.h>
|
||||
#include <lxc/lxc.h>
|
||||
#include <lxc/conf.h>
|
||||
#include <lxc/start.h> /* for struct lxc_handler */
|
||||
#include <lxc/utils.h>
|
||||
|
||||
#include "commands.h"
|
||||
#include "confile.h"
|
||||
#include "mainloop.h"
|
||||
#include "af_unix.h"
|
||||
#include "config.h"
|
||||
|
||||
/*
|
||||
* This file provides the different functions to have the client
|
||||
* and the server to communicate
|
||||
* This file provides the different functions for clients to
|
||||
* query/command the server. The client is typically some lxc
|
||||
* tool and the server is typically the container (ie. lxc-start).
|
||||
*
|
||||
* Each command is transactional, the client send a request to
|
||||
* the server and the server answer the request with a message
|
||||
* Each command is transactional, the clients send a request to
|
||||
* the server and the server answers the request with a message
|
||||
* giving the request's status (zero or a negative errno value).
|
||||
* Both the request and response may contain addtional data.
|
||||
*
|
||||
* Each command is wrapped in a ancillary message in order to pass
|
||||
* a credential making possible to the server to check if the client
|
||||
* is allowed to ask for this command or not.
|
||||
*
|
||||
*/
|
||||
|
||||
lxc_log_define(lxc_commands, lxc);
|
||||
@ -81,66 +84,183 @@ static int fill_sock_name(char *path, int len, const char *name,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int receive_answer(int sock, struct lxc_answer *answer)
|
||||
static const char *lxc_cmd_str(lxc_cmd_t cmd)
|
||||
{
|
||||
int ret;
|
||||
static char answerpath[MAXPATHLEN];
|
||||
static const char *cmdname[LXC_CMD_MAX] = {
|
||||
[LXC_CMD_CONSOLE] = "console",
|
||||
[LXC_CMD_STOP] = "stop",
|
||||
[LXC_CMD_GET_STATE] = "get_state",
|
||||
[LXC_CMD_GET_INIT_PID] = "get_init_pid",
|
||||
[LXC_CMD_GET_CLONE_FLAGS] = "get_clone_flags",
|
||||
[LXC_CMD_GET_CGROUP] = "get_cgroup",
|
||||
[LXC_CMD_GET_CONFIG_ITEM] = "get_config_item",
|
||||
};
|
||||
|
||||
ret = lxc_af_unix_recv_fd(sock, &answer->fd, answer, sizeof(*answer));
|
||||
if (ret < 0)
|
||||
ERROR("failed to receive answer for the command");
|
||||
if (answer->pathlen == 0)
|
||||
return ret;
|
||||
if (answer->pathlen >= MAXPATHLEN) {
|
||||
ERROR("cgroup path was too long");
|
||||
if (cmd < 0 || cmd >= LXC_CMD_MAX)
|
||||
return "Unknown cmd";
|
||||
return cmdname[cmd];
|
||||
}
|
||||
|
||||
/*
|
||||
* lxc_cmd_rsp_recv: Receive a response to a command
|
||||
*
|
||||
* @sock : the socket connected to the container
|
||||
* @cmd : command to put response in
|
||||
*
|
||||
* Returns the size of the response message or < 0 on failure
|
||||
*
|
||||
* Note that if the command response datalen > 0, then data is
|
||||
* a malloc()ed buffer and should be free()ed by the caller. If
|
||||
* the response data is <= a void * worth of data, it will be
|
||||
* stored directly in data and datalen will be 0.
|
||||
*
|
||||
* As a special case, the response for LXC_CMD_CONSOLE is created
|
||||
* here as it contains an fd passed through the unix socket.
|
||||
*/
|
||||
static int lxc_cmd_rsp_recv(int sock, struct lxc_cmd_rr *cmd)
|
||||
{
|
||||
int ret,rspfd;
|
||||
struct lxc_cmd_rsp *rsp = &cmd->rsp;
|
||||
|
||||
ret = lxc_af_unix_recv_fd(sock, &rspfd, rsp, sizeof(*rsp));
|
||||
if (ret < 0) {
|
||||
ERROR("command %s failed to receive response",
|
||||
lxc_cmd_str(cmd->req.cmd));
|
||||
return -1;
|
||||
}
|
||||
ret = recv(sock, answerpath, answer->pathlen, 0);
|
||||
if (ret != answer->pathlen) {
|
||||
ERROR("failed to receive answer for the command");
|
||||
ret = 0;
|
||||
} else
|
||||
answer->path = answerpath;
|
||||
|
||||
if (cmd->req.cmd == LXC_CMD_CONSOLE) {
|
||||
struct lxc_cmd_console_rsp_data *rspdata;
|
||||
|
||||
rspdata = malloc(sizeof(*rspdata));
|
||||
if (!rspdata) {
|
||||
ERROR("command %s couldn't allocate response buffer",
|
||||
lxc_cmd_str(cmd->req.cmd));
|
||||
return -1;
|
||||
}
|
||||
rspdata->fd = rspfd;
|
||||
rspdata->ttynum = PTR_TO_INT(rsp->data);
|
||||
rsp->data = rspdata;
|
||||
}
|
||||
|
||||
if (rsp->datalen == 0)
|
||||
return ret;
|
||||
if (rsp->datalen > LXC_CMD_DATA_MAX) {
|
||||
ERROR("command %s response data %d too long",
|
||||
lxc_cmd_str(cmd->req.cmd), rsp->datalen);
|
||||
errno = EFBIG;
|
||||
return -1;
|
||||
}
|
||||
|
||||
rsp->data = malloc(rsp->datalen);
|
||||
if (!rsp->data) {
|
||||
ERROR("command %s unable to allocate response buffer",
|
||||
lxc_cmd_str(cmd->req.cmd));
|
||||
return -1;
|
||||
}
|
||||
ret = recv(sock, rsp->data, rsp->datalen, 0);
|
||||
if (ret != rsp->datalen) {
|
||||
ERROR("command %s failed to receive response data",
|
||||
lxc_cmd_str(cmd->req.cmd));
|
||||
if (ret >= 0)
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __lxc_command(const char *name, struct lxc_command *command,
|
||||
int *stopped, int stay_connected, const char *lxcpath)
|
||||
/*
|
||||
* lxc_cmd_rsp_send: Send a command response
|
||||
*
|
||||
* @fd : file descriptor of socket to send response on
|
||||
* @rsp : response to send
|
||||
*
|
||||
* Returns 0 on success, < 0 on failure
|
||||
*/
|
||||
static int lxc_cmd_rsp_send(int fd, struct lxc_cmd_rsp *rsp)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = send(fd, rsp, sizeof(*rsp), 0);
|
||||
if (ret != sizeof(*rsp)) {
|
||||
ERROR("failed to send command response %d %s", ret,
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (rsp->datalen > 0) {
|
||||
ret = send(fd, rsp->data, rsp->datalen, 0);
|
||||
if (ret != rsp->datalen) {
|
||||
WARN("failed to send command response data %d %s", ret,
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* lxc_cmd: Connect to the specified running container, send it a command
|
||||
* request and collect the response
|
||||
*
|
||||
* @name : name of container to connect to
|
||||
* @cmd : command with initialized reqest to send
|
||||
* @stopped : output indicator if the container was not running
|
||||
* @lxcpath : the lxcpath in which the container is running
|
||||
*
|
||||
* Returns the size of the response message on success, < 0 on failure
|
||||
*
|
||||
* Note that there is a special case for LXC_CMD_CONSOLE. For this command
|
||||
* the fd cannot be closed because it is used as a placeholder to indicate
|
||||
* that a particular tty slot is in use. The fd is also used as a signal to
|
||||
* the container that when the caller dies or closes the fd, the container
|
||||
* will notice the fd in its mainloop select and then free the slot with
|
||||
* lxc_cmd_fd_cleanup().
|
||||
*/
|
||||
static int lxc_cmd(const char *name, struct lxc_cmd_rr *cmd, int *stopped,
|
||||
const char *lxcpath)
|
||||
{
|
||||
int sock, ret = -1;
|
||||
char path[sizeof(((struct sockaddr_un *)0)->sun_path)] = { 0 };
|
||||
char *offset = &path[1];
|
||||
int len;
|
||||
int stay_connected = cmd->req.cmd == LXC_CMD_CONSOLE;
|
||||
|
||||
len = sizeof(path)-1;
|
||||
if (fill_sock_name(offset, len, name, lxcpath))
|
||||
return -1;
|
||||
|
||||
sock = lxc_af_unix_connect(path);
|
||||
if (sock < 0 && errno == ECONNREFUSED) {
|
||||
*stopped = 1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (sock < 0) {
|
||||
SYSERROR("failed to connect to '@%s'", offset);
|
||||
if (errno == ECONNREFUSED)
|
||||
*stopped = 1;
|
||||
else
|
||||
SYSERROR("command %s failed to connect to '@%s'",
|
||||
lxc_cmd_str(cmd->req.cmd), offset);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = lxc_af_unix_send_credential(sock, &command->request,
|
||||
sizeof(command->request));
|
||||
if (ret < 0) {
|
||||
SYSERROR("failed to send request to '@%s'", offset);
|
||||
ret = lxc_af_unix_send_credential(sock, &cmd->req, sizeof(cmd->req));
|
||||
if (ret != sizeof(cmd->req)) {
|
||||
SYSERROR("command %s failed to send req to '@%s' %d",
|
||||
lxc_cmd_str(cmd->req.cmd), offset, ret);
|
||||
if (ret >=0)
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (ret != sizeof(command->request)) {
|
||||
SYSERROR("message partially sent to '@%s'", offset);
|
||||
goto out;
|
||||
if (cmd->req.datalen > 0) {
|
||||
ret = send(sock, cmd->req.data, cmd->req.datalen, 0);
|
||||
if (ret != cmd->req.datalen) {
|
||||
SYSERROR("command %s failed to send request data to '@%s' %d",
|
||||
lxc_cmd_str(cmd->req.cmd), offset, ret);
|
||||
if (ret >=0)
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
ret = receive_answer(sock, &command->answer);
|
||||
ret = lxc_cmd_rsp_recv(sock, cmd);
|
||||
out:
|
||||
if (!stay_connected || ret < 0)
|
||||
close(sock);
|
||||
@ -148,114 +268,418 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
extern int lxc_command(const char *name,
|
||||
struct lxc_command *command, int *stopped,
|
||||
const char *lxcpath)
|
||||
{
|
||||
return __lxc_command(name, command, stopped, 0, lxcpath);
|
||||
}
|
||||
/* Implentations of the commands and their callbacks */
|
||||
|
||||
extern int lxc_command_connected(const char *name,
|
||||
struct lxc_command *command, int *stopped,
|
||||
const char *lxcpath)
|
||||
/*
|
||||
* lxc_cmd_get_init_pid: Get pid of the container's init process
|
||||
*
|
||||
* @name : name of container to connect to
|
||||
* @lxcpath : the lxcpath in which the container is running
|
||||
*
|
||||
* Returns the pid on success, < 0 on failure
|
||||
*/
|
||||
pid_t lxc_cmd_get_init_pid(const char *name, const char *lxcpath)
|
||||
{
|
||||
return __lxc_command(name, command, stopped, 1, lxcpath);
|
||||
}
|
||||
|
||||
|
||||
pid_t get_init_pid(const char *name, const char *lxcpath)
|
||||
{
|
||||
struct lxc_command command = {
|
||||
.request = { .type = LXC_COMMAND_PID },
|
||||
int ret, stopped = 0;
|
||||
struct lxc_cmd_rr cmd = {
|
||||
.req = { .cmd = LXC_CMD_GET_INIT_PID },
|
||||
};
|
||||
|
||||
int ret, stopped = 0;
|
||||
ret = lxc_cmd(name, &cmd, &stopped, lxcpath);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = lxc_command(name, &command, &stopped, lxcpath);
|
||||
if (ret < 0 && stopped)
|
||||
return -1;
|
||||
|
||||
if (ret < 0) {
|
||||
ERROR("failed to send command");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (command.answer.ret) {
|
||||
ERROR("failed to retrieve the init pid: %s",
|
||||
strerror(-command.answer.ret));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return command.answer.pid;
|
||||
return PTR_TO_INT(cmd.rsp.data);
|
||||
}
|
||||
|
||||
int lxc_get_clone_flags(const char *name, const char *lxcpath)
|
||||
static int lxc_cmd_get_init_pid_callback(int fd, struct lxc_cmd_req *req,
|
||||
struct lxc_handler *handler)
|
||||
{
|
||||
struct lxc_command command = {
|
||||
.request = { .type = LXC_COMMAND_CLONE_FLAGS },
|
||||
struct lxc_cmd_rsp rsp = { .data = INT_TO_PTR(handler->pid) };
|
||||
|
||||
return lxc_cmd_rsp_send(fd, &rsp);
|
||||
}
|
||||
|
||||
/*
|
||||
* lxc_cmd_get_clone_flags: Get clone flags container was spawned with
|
||||
*
|
||||
* @name : name of container to connect to
|
||||
* @lxcpath : the lxcpath in which the container is running
|
||||
*
|
||||
* Returns the clone flags on success, < 0 on failure
|
||||
*/
|
||||
int lxc_cmd_get_clone_flags(const char *name, const char *lxcpath)
|
||||
{
|
||||
int ret, stopped = 0;
|
||||
struct lxc_cmd_rr cmd = {
|
||||
.req = { .cmd = LXC_CMD_GET_CLONE_FLAGS },
|
||||
};
|
||||
|
||||
int ret, stopped = 0;
|
||||
ret = lxc_cmd(name, &cmd, &stopped, lxcpath);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = lxc_command(name, &command, &stopped, lxcpath);
|
||||
return PTR_TO_INT(cmd.rsp.data);
|
||||
}
|
||||
|
||||
static int lxc_cmd_get_clone_flags_callback(int fd, struct lxc_cmd_req *req,
|
||||
struct lxc_handler *handler)
|
||||
{
|
||||
struct lxc_cmd_rsp rsp = { .data = INT_TO_PTR(handler->clone_flags) };
|
||||
|
||||
return lxc_cmd_rsp_send(fd, &rsp);
|
||||
}
|
||||
|
||||
/*
|
||||
* lxc_cmd_get_cgroup_path: Calculate a container's cgroup path for a
|
||||
* particular subsystem. This is the cgroup path relative to the root
|
||||
* of the cgroup filesystem.
|
||||
*
|
||||
* @subsystem : the cgroup subsystem of interest (i.e. freezer)
|
||||
* @name : name of container to connect to
|
||||
* @lxcpath : the lxcpath in which the container is running
|
||||
*
|
||||
* Returns the path on success, NULL on failure. The caller must free() the
|
||||
* returned path.
|
||||
*/
|
||||
char *lxc_cmd_get_cgroup_path(const char *subsystem, const char *name,
|
||||
const char *lxcpath)
|
||||
{
|
||||
int ret, stopped = 0;
|
||||
struct lxc_cmd_rr cmd = {
|
||||
.req = { .cmd = LXC_CMD_GET_CGROUP },
|
||||
};
|
||||
|
||||
ret = lxc_cmd(name, &cmd, &stopped, lxcpath);
|
||||
if (ret < 0)
|
||||
return NULL;
|
||||
|
||||
if (!ret) {
|
||||
WARN("'%s' has stopped before sending its state", name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (cmd.rsp.ret < 0 || cmd.rsp.datalen < 0) {
|
||||
ERROR("command %s failed for '%s': %s",
|
||||
lxc_cmd_str(cmd.req.cmd), name,
|
||||
strerror(-cmd.rsp.ret));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return cmd.rsp.data;
|
||||
}
|
||||
|
||||
static int lxc_cmd_get_cgroup_callback(int fd, struct lxc_cmd_req *req,
|
||||
struct lxc_handler *handler)
|
||||
{
|
||||
struct lxc_cmd_rsp rsp = {
|
||||
.datalen = strlen(handler->cgroup) + 1,
|
||||
.data = handler->cgroup,
|
||||
};
|
||||
|
||||
return lxc_cmd_rsp_send(fd, &rsp);
|
||||
}
|
||||
|
||||
/*
|
||||
* lxc_cmd_get_config_item: Get config item the running container
|
||||
*
|
||||
* @name : name of container to connect to
|
||||
* @item : the configuration item to retrieve (ex: lxc.network.0.veth.pair)
|
||||
* @lxcpath : the lxcpath in which the container is running
|
||||
*
|
||||
* Returns the item on success, NULL on failure. The caller must free() the
|
||||
* returned item.
|
||||
*/
|
||||
char *lxc_cmd_get_config_item(const char *name, const char *item,
|
||||
const char *lxcpath)
|
||||
{
|
||||
int ret, stopped = 0;
|
||||
struct lxc_cmd_rr cmd = {
|
||||
.req = { .cmd = LXC_CMD_GET_CONFIG_ITEM,
|
||||
.data = item,
|
||||
.datalen = strlen(item)+1,
|
||||
},
|
||||
};
|
||||
|
||||
ret = lxc_cmd(name, &cmd, &stopped, lxcpath);
|
||||
if (ret < 0)
|
||||
return NULL;
|
||||
|
||||
if (cmd.rsp.ret == 0)
|
||||
return cmd.rsp.data;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int lxc_cmd_get_config_item_callback(int fd, struct lxc_cmd_req *req,
|
||||
struct lxc_handler *handler)
|
||||
{
|
||||
int cilen;
|
||||
struct lxc_cmd_rsp rsp;
|
||||
char *cidata;
|
||||
|
||||
memset(&rsp, 0, sizeof(rsp));
|
||||
cilen = lxc_get_config_item(handler->conf, req->data, NULL, 0);
|
||||
if (cilen <= 0)
|
||||
goto err1;
|
||||
|
||||
cidata = alloca(cilen + 1);
|
||||
if (lxc_get_config_item(handler->conf, req->data, cidata, cilen + 1) != cilen)
|
||||
goto err1;
|
||||
cidata[cilen] = '\0';
|
||||
rsp.data = cidata;
|
||||
rsp.datalen = cilen + 1;
|
||||
rsp.ret = 0;
|
||||
goto out;
|
||||
|
||||
err1:
|
||||
rsp.ret = -1;
|
||||
out:
|
||||
return lxc_cmd_rsp_send(fd, &rsp);
|
||||
}
|
||||
|
||||
/*
|
||||
* lxc_cmd_get_state: Get current state of the container
|
||||
*
|
||||
* @name : name of container to connect to
|
||||
* @lxcpath : the lxcpath in which the container is running
|
||||
*
|
||||
* Returns the state on success, < 0 on failure
|
||||
*/
|
||||
lxc_state_t lxc_cmd_get_state(const char *name, const char *lxcpath)
|
||||
{
|
||||
int ret, stopped = 0;
|
||||
struct lxc_cmd_rr cmd = {
|
||||
.req = { .cmd = LXC_CMD_GET_STATE }
|
||||
};
|
||||
|
||||
ret = lxc_cmd(name, &cmd, &stopped, lxcpath);
|
||||
if (ret < 0 && stopped)
|
||||
return STOPPED;
|
||||
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
if (ret < 0) {
|
||||
ERROR("failed to send command");
|
||||
if (!ret) {
|
||||
WARN("'%s' has stopped before sending its state", name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return command.answer.ret;
|
||||
DEBUG("'%s' is in '%s' state", name,
|
||||
lxc_state2str(PTR_TO_INT(cmd.rsp.data)));
|
||||
return PTR_TO_INT(cmd.rsp.data);
|
||||
}
|
||||
|
||||
extern void lxc_console_remove_fd(int, struct lxc_tty_info *);
|
||||
extern int lxc_console_callback(int, struct lxc_request *, struct lxc_handler *);
|
||||
extern int lxc_stop_callback(int, struct lxc_request *, struct lxc_handler *);
|
||||
extern int lxc_state_callback(int, struct lxc_request *, struct lxc_handler *);
|
||||
extern int lxc_pid_callback(int, struct lxc_request *, struct lxc_handler *);
|
||||
extern int lxc_clone_flags_callback(int, struct lxc_request *, struct lxc_handler *);
|
||||
extern int lxc_cgroup_callback(int, struct lxc_request *, struct lxc_handler *);
|
||||
static int lxc_cmd_get_state_callback(int fd, struct lxc_cmd_req *req,
|
||||
struct lxc_handler *handler)
|
||||
{
|
||||
struct lxc_cmd_rsp rsp = { .data = INT_TO_PTR(handler->state) };
|
||||
|
||||
static int trigger_command(int fd, struct lxc_request *request,
|
||||
return lxc_cmd_rsp_send(fd, &rsp);
|
||||
}
|
||||
|
||||
/*
|
||||
* lxc_cmd_stop: Stop the container previously started with lxc_start. All
|
||||
* the processes running inside this container will be killed.
|
||||
*
|
||||
* @name : name of container to connect to
|
||||
* @lxcpath : the lxcpath in which the container is running
|
||||
*
|
||||
* Returns 0 on success, < 0 on failure
|
||||
*/
|
||||
int lxc_cmd_stop(const char *name, const char *lxcpath)
|
||||
{
|
||||
int ret, stopped = 0;
|
||||
struct lxc_cmd_rr cmd = {
|
||||
.req = { .cmd = LXC_CMD_STOP },
|
||||
};
|
||||
|
||||
ret = lxc_cmd(name, &cmd, &stopped, lxcpath);
|
||||
if (ret < 0) {
|
||||
if (stopped) {
|
||||
INFO("'%s' is already stopped", name);
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* we do not expect any answer, because we wait for the connection to be
|
||||
* closed
|
||||
*/
|
||||
if (ret > 0) {
|
||||
ERROR("failed to stop '%s': %s", name, strerror(-cmd.rsp.ret));
|
||||
return -1;
|
||||
}
|
||||
|
||||
INFO("'%s' has stopped", name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lxc_cmd_stop_callback(int fd, struct lxc_cmd_req *req,
|
||||
struct lxc_handler *handler)
|
||||
{
|
||||
struct lxc_cmd_rsp rsp;
|
||||
int ret;
|
||||
int stopsignal = SIGKILL;
|
||||
|
||||
if (handler->conf->stopsignal)
|
||||
stopsignal = handler->conf->stopsignal;
|
||||
memset(&rsp, 0, sizeof(rsp));
|
||||
rsp.ret = kill(handler->pid, stopsignal);
|
||||
if (!rsp.ret) {
|
||||
ret = lxc_unfreeze_bypath(handler->cgroup);
|
||||
if (!ret)
|
||||
return 0;
|
||||
|
||||
ERROR("failed to unfreeze container");
|
||||
rsp.ret = ret;
|
||||
}
|
||||
|
||||
return lxc_cmd_rsp_send(fd, &rsp);
|
||||
}
|
||||
|
||||
/*
|
||||
* lxc_cmd_console: Open an fd to a tty in the container
|
||||
*
|
||||
* @name : name of container to connect to
|
||||
* @ttynum : in: the tty to open or -1 for next available
|
||||
* : out: the tty allocated
|
||||
* @fd : out: file descriptor for master side of pty
|
||||
* @lxcpath : the lxcpath in which the container is running
|
||||
*
|
||||
* Returns 0 on success, < 0 on failure
|
||||
*/
|
||||
int lxc_cmd_console(const char *name, int *ttynum, int *fd, const char *lxcpath)
|
||||
{
|
||||
int ret, stopped = 0;
|
||||
struct lxc_cmd_console_rsp_data *rspdata;
|
||||
struct lxc_cmd_rr cmd = {
|
||||
.req = { .cmd = LXC_CMD_CONSOLE, .data = INT_TO_PTR(*ttynum) },
|
||||
};
|
||||
|
||||
ret = lxc_cmd(name, &cmd, &stopped, lxcpath);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret == 0) {
|
||||
ERROR("console %d invalid or all consoles busy", *ttynum);
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = -1;
|
||||
#if 1 /* FIXME: how can this happen? */
|
||||
if (cmd.rsp.ret) {
|
||||
ERROR("console access denied: %s",
|
||||
strerror(-cmd.rsp.ret));
|
||||
goto out;
|
||||
}
|
||||
#endif
|
||||
|
||||
rspdata = cmd.rsp.data;
|
||||
if (rspdata->fd < 0) {
|
||||
ERROR("unable to allocate fd for tty %d", rspdata->ttynum);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
*fd = rspdata->fd;
|
||||
*ttynum = rspdata->ttynum;
|
||||
INFO("tty %d allocated", rspdata->ttynum);
|
||||
out:
|
||||
free(cmd.rsp.data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int lxc_cmd_console_callback(int fd, struct lxc_cmd_req *req,
|
||||
struct lxc_handler *handler)
|
||||
{
|
||||
int ttynum = PTR_TO_INT(req->data);
|
||||
struct lxc_tty_info *tty_info = &handler->conf->tty_info;
|
||||
struct lxc_cmd_rsp rsp;
|
||||
|
||||
if (ttynum > 0) {
|
||||
if (ttynum > tty_info->nbtty)
|
||||
goto out_close;
|
||||
|
||||
if (tty_info->pty_info[ttynum - 1].busy)
|
||||
goto out_close;
|
||||
|
||||
/* the requested tty is available */
|
||||
goto out_send;
|
||||
}
|
||||
|
||||
/* search for next available tty, fixup index tty1 => [0] */
|
||||
for (ttynum = 1;
|
||||
ttynum <= tty_info->nbtty && tty_info->pty_info[ttynum - 1].busy;
|
||||
ttynum++);
|
||||
|
||||
/* we didn't find any available slot for tty */
|
||||
if (ttynum > tty_info->nbtty)
|
||||
goto out_close;
|
||||
|
||||
out_send:
|
||||
memset(&rsp, 0, sizeof(rsp));
|
||||
rsp.data = INT_TO_PTR(ttynum);
|
||||
if (lxc_af_unix_send_fd(fd, tty_info->pty_info[ttynum - 1].master,
|
||||
&rsp, sizeof(rsp)) < 0) {
|
||||
ERROR("failed to send tty to client");
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
tty_info->pty_info[ttynum - 1].busy = fd;
|
||||
return 0;
|
||||
|
||||
out_close:
|
||||
/* special indicator to lxc_cmd_handler() to close the fd and do
|
||||
* related cleanup
|
||||
*/
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int lxc_cmd_process(int fd, struct lxc_cmd_req *req,
|
||||
struct lxc_handler *handler)
|
||||
{
|
||||
typedef int (*callback)(int, struct lxc_request *, struct lxc_handler *);
|
||||
typedef int (*callback)(int, struct lxc_cmd_req *, struct lxc_handler *);
|
||||
|
||||
callback cb[LXC_COMMAND_MAX] = {
|
||||
[LXC_COMMAND_TTY] = lxc_console_callback,
|
||||
[LXC_COMMAND_STOP] = lxc_stop_callback,
|
||||
[LXC_COMMAND_STATE] = lxc_state_callback,
|
||||
[LXC_COMMAND_PID] = lxc_pid_callback,
|
||||
[LXC_COMMAND_CLONE_FLAGS] = lxc_clone_flags_callback,
|
||||
[LXC_COMMAND_CGROUP] = lxc_cgroup_callback,
|
||||
callback cb[LXC_CMD_MAX] = {
|
||||
[LXC_CMD_CONSOLE] = lxc_cmd_console_callback,
|
||||
[LXC_CMD_STOP] = lxc_cmd_stop_callback,
|
||||
[LXC_CMD_GET_STATE] = lxc_cmd_get_state_callback,
|
||||
[LXC_CMD_GET_INIT_PID] = lxc_cmd_get_init_pid_callback,
|
||||
[LXC_CMD_GET_CLONE_FLAGS] = lxc_cmd_get_clone_flags_callback,
|
||||
[LXC_CMD_GET_CGROUP] = lxc_cmd_get_cgroup_callback,
|
||||
[LXC_CMD_GET_CONFIG_ITEM] = lxc_cmd_get_config_item_callback,
|
||||
};
|
||||
|
||||
if (request->type < 0 || request->type >= LXC_COMMAND_MAX)
|
||||
if (req->cmd < 0 || req->cmd >= LXC_CMD_MAX) {
|
||||
ERROR("bad cmd %d recieved", req->cmd);
|
||||
return -1;
|
||||
|
||||
return cb[request->type](fd, request, handler);
|
||||
}
|
||||
return cb[req->cmd](fd, req, handler);
|
||||
}
|
||||
|
||||
static void command_fd_cleanup(int fd, struct lxc_handler *handler,
|
||||
static void lxc_cmd_fd_cleanup(int fd, struct lxc_handler *handler,
|
||||
struct lxc_epoll_descr *descr)
|
||||
{
|
||||
extern void lxc_console_remove_fd(int, struct lxc_tty_info *);
|
||||
lxc_console_remove_fd(fd, &handler->conf->tty_info);
|
||||
lxc_mainloop_del_handler(descr, fd);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static int command_handler(int fd, void *data, struct lxc_epoll_descr *descr)
|
||||
static int lxc_cmd_handler(int fd, void *data, struct lxc_epoll_descr *descr)
|
||||
{
|
||||
int ret;
|
||||
struct lxc_request request;
|
||||
struct lxc_cmd_req req;
|
||||
struct lxc_handler *handler = data;
|
||||
|
||||
ret = lxc_af_unix_rcv_credential(fd, &request, sizeof(request));
|
||||
ret = lxc_af_unix_rcv_credential(fd, &req, sizeof(req));
|
||||
if (ret == -EACCES) {
|
||||
/* we don't care for the peer, just send and close */
|
||||
struct lxc_answer answer = { .ret = ret };
|
||||
send(fd, &answer, sizeof(answer), 0);
|
||||
struct lxc_cmd_rsp rsp = { .ret = ret };
|
||||
|
||||
lxc_cmd_rsp_send(fd, &rsp);
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
@ -269,12 +693,32 @@ static int command_handler(int fd, void *data, struct lxc_epoll_descr *descr)
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
if (ret != sizeof(request)) {
|
||||
if (ret != sizeof(req)) {
|
||||
WARN("partial request, ignored");
|
||||
ret = -1;
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
ret = trigger_command(fd, &request, handler);
|
||||
if (req.datalen > LXC_CMD_DATA_MAX) {
|
||||
ERROR("cmd data length %d too large", req.datalen);
|
||||
ret = -1;
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
if (req.datalen > 0) {
|
||||
void *reqdata;
|
||||
|
||||
reqdata = alloca(req.datalen);
|
||||
ret = recv(fd, reqdata, req.datalen, 0);
|
||||
if (ret != req.datalen) {
|
||||
WARN("partial request, ignored");
|
||||
ret = -1;
|
||||
goto out_close;
|
||||
}
|
||||
req.data = reqdata;
|
||||
}
|
||||
|
||||
ret = lxc_cmd_process(fd, &req, handler);
|
||||
if (ret) {
|
||||
/* this is not an error, but only a request to close fd */
|
||||
ret = 0;
|
||||
@ -284,12 +728,11 @@ static int command_handler(int fd, void *data, struct lxc_epoll_descr *descr)
|
||||
out:
|
||||
return ret;
|
||||
out_close:
|
||||
command_fd_cleanup(fd, handler, descr);
|
||||
lxc_cmd_fd_cleanup(fd, handler, descr);
|
||||
goto out;
|
||||
}
|
||||
|
||||
static int incoming_command_handler(int fd, void *data,
|
||||
struct lxc_epoll_descr *descr)
|
||||
static int lxc_cmd_accept(int fd, void *data, struct lxc_epoll_descr *descr)
|
||||
{
|
||||
int opt = 1, ret = -1, connection;
|
||||
|
||||
@ -310,7 +753,7 @@ static int incoming_command_handler(int fd, void *data,
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
ret = lxc_mainloop_add_handler(descr, connection, command_handler, data);
|
||||
ret = lxc_mainloop_add_handler(descr, connection, lxc_cmd_handler, data);
|
||||
if (ret) {
|
||||
ERROR("failed to add handler");
|
||||
goto out_close;
|
||||
@ -324,8 +767,8 @@ out_close:
|
||||
goto out;
|
||||
}
|
||||
|
||||
extern int lxc_command_init(const char *name, struct lxc_handler *handler,
|
||||
const char *lxcpath)
|
||||
int lxc_cmd_init(const char *name, struct lxc_handler *handler,
|
||||
const char *lxcpath)
|
||||
{
|
||||
int fd;
|
||||
char path[sizeof(((struct sockaddr_un *)0)->sun_path)] = { 0 };
|
||||
@ -357,14 +800,13 @@ extern int lxc_command_init(const char *name, struct lxc_handler *handler,
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern int lxc_command_mainloop_add(const char *name,
|
||||
struct lxc_epoll_descr *descr,
|
||||
struct lxc_handler *handler)
|
||||
int lxc_cmd_mainloop_add(const char *name,
|
||||
struct lxc_epoll_descr *descr,
|
||||
struct lxc_handler *handler)
|
||||
{
|
||||
int ret, fd = handler->conf->maincmd_fd;
|
||||
|
||||
ret = lxc_mainloop_add_handler(descr, fd, incoming_command_handler,
|
||||
handler);
|
||||
ret = lxc_mainloop_add_handler(descr, fd, lxc_cmd_accept, handler);
|
||||
if (ret) {
|
||||
ERROR("failed to add handler for command socket");
|
||||
close(fd);
|
||||
|
@ -20,52 +20,67 @@
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef __commands_h
|
||||
#define __commands_h
|
||||
|
||||
enum {
|
||||
LXC_COMMAND_TTY,
|
||||
LXC_COMMAND_STOP,
|
||||
LXC_COMMAND_STATE,
|
||||
LXC_COMMAND_PID,
|
||||
LXC_COMMAND_CLONE_FLAGS,
|
||||
LXC_COMMAND_CGROUP,
|
||||
LXC_COMMAND_MAX,
|
||||
#include "state.h"
|
||||
|
||||
#define LXC_CMD_DATA_MAX (MAXPATHLEN*2)
|
||||
|
||||
/* https://developer.gnome.org/glib/2.28/glib-Type-Conversion-Macros.html */
|
||||
#define INT_TO_PTR(n) ((void *) (long) (n))
|
||||
#define PTR_TO_INT(p) ((int) (long) (p))
|
||||
|
||||
typedef enum {
|
||||
LXC_CMD_CONSOLE,
|
||||
LXC_CMD_STOP,
|
||||
LXC_CMD_GET_STATE,
|
||||
LXC_CMD_GET_INIT_PID,
|
||||
LXC_CMD_GET_CLONE_FLAGS,
|
||||
LXC_CMD_GET_CGROUP,
|
||||
LXC_CMD_GET_CONFIG_ITEM,
|
||||
LXC_CMD_MAX,
|
||||
} lxc_cmd_t;
|
||||
|
||||
struct lxc_cmd_req {
|
||||
lxc_cmd_t cmd;
|
||||
int datalen;
|
||||
const void *data;
|
||||
};
|
||||
|
||||
struct lxc_request {
|
||||
int type;
|
||||
int data;
|
||||
};
|
||||
|
||||
struct lxc_answer {
|
||||
int fd;
|
||||
struct lxc_cmd_rsp {
|
||||
int ret; /* 0 on success, -errno on failure */
|
||||
pid_t pid;
|
||||
int pathlen;
|
||||
const char *path;
|
||||
int datalen;
|
||||
void *data;
|
||||
};
|
||||
|
||||
struct lxc_command {
|
||||
struct lxc_request request;
|
||||
struct lxc_answer answer;
|
||||
struct lxc_cmd_rr {
|
||||
struct lxc_cmd_req req;
|
||||
struct lxc_cmd_rsp rsp;
|
||||
};
|
||||
|
||||
extern pid_t get_init_pid(const char *name, const char *lxcpath);
|
||||
extern int lxc_get_clone_flags(const char *name, const char *lxcpath);
|
||||
struct lxc_cmd_console_rsp_data {
|
||||
int fd;
|
||||
int ttynum;
|
||||
};
|
||||
|
||||
extern int lxc_command(const char *name, struct lxc_command *command,
|
||||
int *stopped, const char *lxcpath);
|
||||
|
||||
extern int lxc_command_connected(const char *name, struct lxc_command *command,
|
||||
int *stopped, const char *lxcpath);
|
||||
extern int lxc_cmd_console(const char *name, int *ttynum, int *fd,
|
||||
const char *lxcpath);
|
||||
extern char *lxc_cmd_get_cgroup_path(const char *subsystem,
|
||||
const char *name, const char *lxcpath);
|
||||
extern int lxc_cmd_get_clone_flags(const char *name, const char *lxcpath);
|
||||
extern char *lxc_cmd_get_config_item(const char *name, const char *item, const char *lxcpath);
|
||||
extern pid_t lxc_cmd_get_init_pid(const char *name, const char *lxcpath);
|
||||
extern lxc_state_t lxc_cmd_get_state(const char *name, const char *lxcpath);
|
||||
extern int lxc_cmd_stop(const char *name, const char *lxcpath);
|
||||
|
||||
struct lxc_epoll_descr;
|
||||
struct lxc_handler;
|
||||
|
||||
extern int lxc_command_init(const char *name, struct lxc_handler *handler,
|
||||
extern int lxc_cmd_init(const char *name, struct lxc_handler *handler,
|
||||
const char *lxcpath);
|
||||
extern int lxc_command_mainloop_add(const char *name, struct lxc_epoll_descr *descr,
|
||||
extern int lxc_cmd_mainloop_add(const char *name, struct lxc_epoll_descr *descr,
|
||||
struct lxc_handler *handler);
|
||||
|
||||
#endif
|
||||
#endif /* __commands_h */
|
||||
|
@ -46,49 +46,6 @@
|
||||
|
||||
lxc_log_define(lxc_console, lxc);
|
||||
|
||||
extern int lxc_console(const char *name, int ttynum, int *fd, const char *lxcpath)
|
||||
{
|
||||
int ret, stopped = 0;
|
||||
struct lxc_command command = {
|
||||
.request = { .type = LXC_COMMAND_TTY, .data = ttynum },
|
||||
};
|
||||
|
||||
ret = lxc_command_connected(name, &command, &stopped, lxcpath);
|
||||
if (ret < 0 && stopped) {
|
||||
ERROR("'%s' is stopped", name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
ERROR("failed to send command");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!ret) {
|
||||
ERROR("console denied by '%s'", name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (command.answer.ret) {
|
||||
ERROR("console access denied: %s",
|
||||
strerror(-command.answer.ret));
|
||||
return -1;
|
||||
}
|
||||
|
||||
*fd = command.answer.fd;
|
||||
if (*fd <0) {
|
||||
ERROR("unable to allocate fd for tty %d", ttynum);
|
||||
return -1;
|
||||
}
|
||||
|
||||
INFO("tty %d allocated", ttynum);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
* functions used by lxc-start mainloop
|
||||
* to handle above command request.
|
||||
*--------------------------------------------------------------------------*/
|
||||
extern void lxc_console_remove_fd(int fd, struct lxc_tty_info *tty_info)
|
||||
{
|
||||
int i;
|
||||
@ -104,47 +61,6 @@ extern void lxc_console_remove_fd(int fd, struct lxc_tty_info *tty_info)
|
||||
return;
|
||||
}
|
||||
|
||||
extern int lxc_console_callback(int fd, struct lxc_request *request,
|
||||
struct lxc_handler *handler)
|
||||
{
|
||||
int ttynum = request->data;
|
||||
struct lxc_tty_info *tty_info = &handler->conf->tty_info;
|
||||
|
||||
if (ttynum > 0) {
|
||||
if (ttynum > tty_info->nbtty)
|
||||
goto out_close;
|
||||
|
||||
if (tty_info->pty_info[ttynum - 1].busy)
|
||||
goto out_close;
|
||||
|
||||
goto out_send;
|
||||
}
|
||||
|
||||
/* fixup index tty1 => [0] */
|
||||
for (ttynum = 1;
|
||||
ttynum <= tty_info->nbtty && tty_info->pty_info[ttynum - 1].busy;
|
||||
ttynum++);
|
||||
|
||||
/* we didn't find any available slot for tty */
|
||||
if (ttynum > tty_info->nbtty)
|
||||
goto out_close;
|
||||
|
||||
out_send:
|
||||
if (lxc_af_unix_send_fd(fd, tty_info->pty_info[ttynum - 1].master,
|
||||
&ttynum, sizeof(ttynum)) < 0) {
|
||||
ERROR("failed to send tty to client");
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
tty_info->pty_info[ttynum - 1].busy = fd;
|
||||
|
||||
return 0;
|
||||
|
||||
out_close:
|
||||
/* the close fd and related cleanup will be done by caller */
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int get_default_console(char **console)
|
||||
{
|
||||
int fd;
|
||||
|
@ -51,14 +51,6 @@ struct lxc_arguments;
|
||||
extern int lxc_start(const char *name, char *const argv[], struct lxc_conf *conf,
|
||||
const char *lxcpath);
|
||||
|
||||
/*
|
||||
* Stop the container previously started with lxc_start, all
|
||||
* the processes running inside this container will be killed.
|
||||
* @name : the name of the container
|
||||
* Returns 0 on success, < 0 otherwise
|
||||
*/
|
||||
extern int lxc_stop(const char *name, const char *lxcpath);
|
||||
|
||||
/*
|
||||
* Start the specified command inside an application container
|
||||
* @name : the name of the container
|
||||
|
@ -296,7 +296,7 @@ int main(int argc, char *argv[])
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
init_pid = get_init_pid(my_args.name, my_args.lxcpath[0]);
|
||||
init_pid = lxc_cmd_get_init_pid(my_args.name, my_args.lxcpath[0]);
|
||||
if (init_pid < 0) {
|
||||
ERROR("failed to get the init pid");
|
||||
return -1;
|
||||
@ -314,7 +314,7 @@ int main(int argc, char *argv[])
|
||||
* by asking lxc-start
|
||||
*/
|
||||
if (namespace_flags == -1) {
|
||||
namespace_flags = lxc_get_clone_flags(my_args.name, my_args.lxcpath[0]);
|
||||
namespace_flags = lxc_cmd_get_clone_flags(my_args.name, my_args.lxcpath[0]);
|
||||
/* call failed */
|
||||
if (namespace_flags == -1) {
|
||||
ERROR("failed to automatically determine the "
|
||||
|
@ -43,6 +43,7 @@
|
||||
#include "log.h"
|
||||
#include "mainloop.h"
|
||||
#include "arguments.h"
|
||||
#include "commands.h"
|
||||
|
||||
lxc_log_define(lxc_console_ui, lxc_console);
|
||||
|
||||
@ -202,13 +203,14 @@ int main(int argc, char *argv[])
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = lxc_console(my_args.name, my_args.ttynum, &master, my_args.lxcpath[0]);
|
||||
err = lxc_cmd_console(my_args.name, &my_args.ttynum, &master, my_args.lxcpath[0]);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
fprintf(stderr, "\n\
|
||||
Type <Ctrl+%1$c q> to exit the console, \
|
||||
<Ctrl+%1$c Ctrl+%1$c> to enter Ctrl+%1$c itself\n",
|
||||
Connected to tty %1$d\n\
|
||||
Type <Ctrl+%2$c q> to exit the console, \
|
||||
<Ctrl+%2$c Ctrl+%2$c> to enter Ctrl+%2$c itself\n", my_args.ttynum,
|
||||
'a' + my_args.escape - 1);
|
||||
|
||||
err = setsid();
|
||||
|
@ -22,6 +22,7 @@
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <libgen.h>
|
||||
#include <sys/types.h>
|
||||
@ -96,8 +97,13 @@ int main(int argc, char *argv[])
|
||||
printf("state:%10s\n", lxc_state2str(ret));
|
||||
}
|
||||
|
||||
if (pid)
|
||||
printf("pid:%10d\n", get_init_pid(my_args.name, my_args.lxcpath[0]));
|
||||
if (pid) {
|
||||
pid_t initpid;
|
||||
|
||||
initpid = lxc_cmd_get_init_pid(my_args.name, my_args.lxcpath[0]);
|
||||
if (initpid >= 0)
|
||||
printf("pid:%10d\n", initpid);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -76,7 +76,7 @@ int main(int argc, char *argv[], char *envp[])
|
||||
} else
|
||||
sig=SIGKILL;
|
||||
|
||||
pid = get_init_pid(my_args.name, my_args.lxcpath[0]);
|
||||
pid = lxc_cmd_get_init_pid(my_args.name, my_args.lxcpath[0]);
|
||||
if (pid < 0) {
|
||||
ERROR("failed to get the init pid");
|
||||
return -1;
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include <lxc/log.h>
|
||||
|
||||
#include "arguments.h"
|
||||
#include "commands.h"
|
||||
#include "utils.h"
|
||||
|
||||
static const struct option my_longopts[] = {
|
||||
@ -58,5 +59,5 @@ int main(int argc, char *argv[])
|
||||
my_args.progname, my_args.quiet, my_args.lxcpath[0]))
|
||||
return -1;
|
||||
|
||||
return lxc_stop(my_args.name, my_args.lxcpath[0]);
|
||||
return lxc_cmd_stop(my_args.name, my_args.lxcpath[0]);
|
||||
}
|
||||
|
@ -283,7 +283,7 @@ static pid_t lxcapi_init_pid(struct lxc_container *c)
|
||||
|
||||
if (lxclock(c->slock, 0))
|
||||
return -1;
|
||||
ret = get_init_pid(c->name, c->config_path);
|
||||
ret = lxc_cmd_get_init_pid(c->name, c->config_path);
|
||||
lxcunlock(c->slock);
|
||||
return ret;
|
||||
}
|
||||
@ -521,7 +521,7 @@ static bool lxcapi_stop(struct lxc_container *c)
|
||||
if (!c)
|
||||
return false;
|
||||
|
||||
ret = lxc_stop(c->name, c->config_path);
|
||||
ret = lxc_cmd_stop(c->name, c->config_path);
|
||||
|
||||
return ret == 0;
|
||||
}
|
||||
|
@ -262,90 +262,6 @@ static int signal_handler(int fd, void *data,
|
||||
return 1;
|
||||
}
|
||||
|
||||
int lxc_pid_callback(int fd, struct lxc_request *request,
|
||||
struct lxc_handler *handler)
|
||||
{
|
||||
struct lxc_answer answer;
|
||||
int ret;
|
||||
|
||||
memset(&answer, 0, sizeof(answer));
|
||||
answer.pid = handler->pid;
|
||||
answer.ret = 0;
|
||||
|
||||
ret = send(fd, &answer, sizeof(answer), 0);
|
||||
if (ret < 0) {
|
||||
WARN("failed to send answer to the peer");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ret != sizeof(answer)) {
|
||||
ERROR("partial answer sent");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lxc_cgroup_callback(int fd, struct lxc_request *request,
|
||||
struct lxc_handler *handler)
|
||||
{
|
||||
struct lxc_answer answer;
|
||||
int ret;
|
||||
|
||||
memset(&answer, 0, sizeof(answer));
|
||||
answer.pathlen = strlen(handler->cgroup) + 1;
|
||||
answer.path = handler->cgroup;
|
||||
answer.ret = 0;
|
||||
|
||||
ret = send(fd, &answer, sizeof(answer), 0);
|
||||
if (ret < 0) {
|
||||
WARN("failed to send answer to the peer");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ret != sizeof(answer)) {
|
||||
ERROR("partial answer sent");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = send(fd, answer.path, answer.pathlen, 0);
|
||||
if (ret < 0) {
|
||||
WARN("failed to send answer to the peer");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ret != answer.pathlen) {
|
||||
ERROR("partial answer sent");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lxc_clone_flags_callback(int fd, struct lxc_request *request,
|
||||
struct lxc_handler *handler)
|
||||
{
|
||||
struct lxc_answer answer;
|
||||
int ret;
|
||||
|
||||
memset(&answer, 0, sizeof(answer));
|
||||
answer.pid = 0;
|
||||
answer.ret = handler->clone_flags;
|
||||
|
||||
ret = send(fd, &answer, sizeof(answer), 0);
|
||||
if (ret < 0) {
|
||||
WARN("failed to send answer to the peer");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ret != sizeof(answer)) {
|
||||
ERROR("partial answer sent");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lxc_set_state(const char *name, struct lxc_handler *handler, lxc_state_t state)
|
||||
{
|
||||
handler->state = state;
|
||||
@ -374,7 +290,7 @@ int lxc_poll(const char *name, struct lxc_handler *handler)
|
||||
goto out_mainloop_open;
|
||||
}
|
||||
|
||||
if (lxc_command_mainloop_add(name, &descr, handler)) {
|
||||
if (lxc_cmd_mainloop_add(name, &descr, handler)) {
|
||||
ERROR("failed to add command handler to mainloop");
|
||||
goto out_mainloop_open;
|
||||
}
|
||||
@ -426,7 +342,7 @@ struct lxc_handler *lxc_init(const char *name, struct lxc_conf *conf, const char
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
if (lxc_command_init(name, handler, lxcpath))
|
||||
if (lxc_cmd_init(name, handler, lxcpath))
|
||||
goto out_free_name;
|
||||
|
||||
if (lxc_read_seccomp_config(conf) != 0) {
|
||||
|
@ -103,75 +103,14 @@ fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static lxc_state_t __lxc_getstate(const char *name, const char *lxcpath)
|
||||
{
|
||||
struct lxc_command command = {
|
||||
.request = { .type = LXC_COMMAND_STATE },
|
||||
};
|
||||
|
||||
int ret, stopped = 0;
|
||||
|
||||
ret = lxc_command(name, &command, &stopped, lxcpath);
|
||||
if (ret < 0 && stopped)
|
||||
return STOPPED;
|
||||
|
||||
if (ret < 0) {
|
||||
ERROR("failed to send command");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!ret) {
|
||||
WARN("'%s' has stopped before sending its state", name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (command.answer.ret < 0) {
|
||||
ERROR("failed to get state for '%s': %s",
|
||||
name, strerror(-command.answer.ret));
|
||||
return -1;
|
||||
}
|
||||
|
||||
DEBUG("'%s' is in '%s' state", name, lxc_state2str(command.answer.ret));
|
||||
|
||||
return command.answer.ret;
|
||||
}
|
||||
|
||||
lxc_state_t lxc_getstate(const char *name, const char *lxcpath)
|
||||
{
|
||||
lxc_state_t state = freezer_state(name, lxcpath);
|
||||
if (state != FROZEN && state != FREEZING)
|
||||
state = __lxc_getstate(name, lxcpath);
|
||||
state = lxc_cmd_get_state(name, lxcpath);
|
||||
return state;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
* functions used by lxc-start mainloop
|
||||
* to handle above command request.
|
||||
*--------------------------------------------------------------------------*/
|
||||
extern int lxc_state_callback(int fd, struct lxc_request *request,
|
||||
struct lxc_handler *handler)
|
||||
{
|
||||
struct lxc_answer answer;
|
||||
int ret;
|
||||
|
||||
memset(&answer, 0, sizeof(answer));
|
||||
answer.ret = handler->state;
|
||||
|
||||
ret = send(fd, &answer, sizeof(answer), 0);
|
||||
if (ret < 0) {
|
||||
WARN("failed to send answer to the peer");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (ret != sizeof(answer)) {
|
||||
ERROR("partial answer sent");
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fillwaitedstates(const char *strstates, int *states)
|
||||
{
|
||||
char *token, *saveptr = NULL;
|
||||
|
115
src/lxc/stop.c
115
src/lxc/stop.c
@ -1,115 +0,0 @@
|
||||
/*
|
||||
* lxc: linux Container library
|
||||
*
|
||||
* (C) Copyright IBM Corp. 2007, 2008
|
||||
*
|
||||
* Authors:
|
||||
* Daniel Lezcano <daniel.lezcano at free.fr>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/param.h>
|
||||
#include <signal.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/socket.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <lxc/log.h>
|
||||
#include <lxc/start.h>
|
||||
#include <lxc/conf.h>
|
||||
|
||||
#include "lxc.h"
|
||||
#include "commands.h"
|
||||
|
||||
lxc_log_define(lxc_stop, lxc);
|
||||
|
||||
int lxc_stop(const char *name, const char *lxcpath)
|
||||
{
|
||||
struct lxc_command command = {
|
||||
.request = { .type = LXC_COMMAND_STOP },
|
||||
};
|
||||
|
||||
int ret, stopped = 0;
|
||||
|
||||
ret = lxc_command(name, &command,&stopped, lxcpath);
|
||||
if (ret < 0 && stopped) {
|
||||
INFO("'%s' is already stopped", name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
ERROR("failed to send command");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* we do not expect any answer, because we wait for the connection to be
|
||||
* closed
|
||||
*/
|
||||
if (ret > 0) {
|
||||
ERROR("failed to stop '%s': %s",
|
||||
name, strerror(-command.answer.ret));
|
||||
return -1;
|
||||
}
|
||||
|
||||
INFO("'%s' has stopped", name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
* functions used by lxc-start mainloop
|
||||
* to handle above command request.
|
||||
*--------------------------------------------------------------------------*/
|
||||
extern int lxc_stop_callback(int fd, struct lxc_request *request,
|
||||
struct lxc_handler *handler)
|
||||
{
|
||||
struct lxc_answer answer;
|
||||
int ret;
|
||||
int stopsignal = SIGKILL;
|
||||
|
||||
if (handler->conf->stopsignal)
|
||||
stopsignal = handler->conf->stopsignal;
|
||||
memset(&answer, 0, sizeof(answer));
|
||||
answer.ret = kill(handler->pid, stopsignal);
|
||||
if (!answer.ret) {
|
||||
ret = lxc_unfreeze_bypath(handler->cgroup);
|
||||
if (!ret)
|
||||
return 0;
|
||||
|
||||
ERROR("failed to unfreeze container");
|
||||
answer.ret = ret;
|
||||
}
|
||||
|
||||
ret = send(fd, &answer, sizeof(answer), 0);
|
||||
if (ret < 0) {
|
||||
WARN("failed to send answer to the peer");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (ret != sizeof(answer)) {
|
||||
ERROR("partial answer sent");
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
return -1;
|
||||
}
|
||||
|
@ -137,7 +137,8 @@ int main()
|
||||
}
|
||||
|
||||
const char *dirpath;
|
||||
if (lxc_get_cgpath(&dirpath, NULL, c2->name, c2->config_path) < 0) {
|
||||
dirpath = lxc_cmd_get_cgroup_path(NULL, c2->name, c2->config_path);
|
||||
if (!dirpath) {
|
||||
TSTERR("getting second container's cgpath");
|
||||
goto out;
|
||||
}
|
||||
@ -150,6 +151,8 @@ int main()
|
||||
|
||||
retv = 0;
|
||||
out:
|
||||
if (dirpath)
|
||||
free(dirpath);
|
||||
if (c2) {
|
||||
c2->stop(c2);
|
||||
c2->destroy(c2);
|
||||
|
Loading…
Reference in New Issue
Block a user