Commit ef6e34ee by Dwight Engen Committed by Serge Hallyn

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: 's avatarDwight Engen <dwight.engen@oracle.com> Signed-off-by: 's avatarSerge Hallyn <serge.hallyn@ubuntu.com>
parent 9c83a661
......@@ -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;
......@@ -215,55 +215,9 @@ fail:
}
/*
* 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
......@@ -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_request {
int type;
int data;
struct lxc_cmd_req {
lxc_cmd_t cmd;
int datalen;
const void *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);
extern int lxc_command(const char *name, struct lxc_command *command,
int *stopped, const char *lxcpath);
struct lxc_cmd_console_rsp_data {
int fd;
int ttynum;
};
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;
......
......@@ -52,14 +52,6 @@ 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
* @argv : an array of char * corresponding to the commande line
......
......@@ -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;
......
/*
* 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);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment