예제 #1
0
파일: commands.c 프로젝트: stgraber/lxc
/*
 * 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 request 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 on its side of the socket in its mainloop select and
 * then free the slot with lxc_cmd_fd_cleanup(). The socket fd will be
 * returned in the cmd response structure.
 */
static int lxc_cmd(const char *name, struct lxc_cmd_rr *cmd, int *stopped,
		   const char *lxcpath, const char *hashed_sock_name)
{
	__do_close_prot_errno int client_fd = -EBADF;
	int ret = -1;
	bool stay_connected = false;

	if (cmd->req.cmd == LXC_CMD_CONSOLE ||
	    cmd->req.cmd == LXC_CMD_ADD_STATE_CLIENT)
		stay_connected = true;

	*stopped = 0;

	client_fd = lxc_cmd_send(name, cmd, lxcpath, hashed_sock_name);
	if (client_fd < 0) {
		SYSTRACE("Command \"%s\" failed to connect command socket",
		         lxc_cmd_str(cmd->req.cmd));

		if (errno == ECONNREFUSED || errno == EPIPE)
			*stopped = 1;

		return -1;
	}

	ret = lxc_cmd_rsp_recv(client_fd, cmd);
	if (ret < 0 && errno == ECONNRESET)
		*stopped = 1;

	if (stay_connected && ret > 0)
		cmd->rsp.ret = move_fd(client_fd);

	return ret;
}
예제 #2
0
파일: commands.c 프로젝트: adrianreber/lxc
/*
 * 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 request 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 on its side of the socket in its mainloop select and
 * then free the slot with lxc_cmd_fd_cleanup(). The socket fd will be
 * returned in the cmd response structure.
 */
static int lxc_cmd(const char *name, struct lxc_cmd_rr *cmd, int *stopped,
		   const char *lxcpath, const char *hashed_sock_name)
{
	int client_fd, saved_errno;
	int ret = -1;
	bool stay_connected = false;

	if (cmd->req.cmd == LXC_CMD_CONSOLE ||
	    cmd->req.cmd == LXC_CMD_ADD_STATE_CLIENT)
		stay_connected = true;

	*stopped = 0;

	client_fd = lxc_cmd_send(name, cmd, lxcpath, hashed_sock_name);
	if (client_fd < 0) {
		SYSTRACE("Command \"%s\" failed to connect command socket",
		         lxc_cmd_str(cmd->req.cmd));

		if (errno == ECONNREFUSED)
			*stopped = 1;

		if (errno == EPIPE) {
			*stopped = 1;
			client_fd = 0;
		}

		return -1;
	}

	ret = lxc_cmd_rsp_recv(client_fd, cmd);
	if (ret < 0 && errno == ECONNRESET)
		*stopped = 1;

	if (!stay_connected || ret <= 0)
		if (client_fd >= 0) {
			saved_errno = errno;
			close(client_fd);
			errno = saved_errno;
		}

	if (stay_connected && ret > 0)
		cmd->rsp.ret = client_fd;

	return ret;
}
예제 #3
0
파일: commands.c 프로젝트: adrianreber/lxc
/*
 * 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 for the master pty 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_abstract_unix_recv_fds(sock, &rspfd, 1, rsp, sizeof(*rsp));
	if (ret < 0) {
		SYSWARN("Failed to receive response for command \"%s\"",
		        lxc_cmd_str(cmd->req.cmd));

		if (errno == ECONNRESET)
			return -1;

		return -1;
	}
	TRACE("Command \"%s\" received response", lxc_cmd_str(cmd->req.cmd));

	if (cmd->req.cmd == LXC_CMD_CONSOLE) {
		struct lxc_cmd_console_rsp_data *rspdata;

		/* recv() returns 0 bytes when a tty cannot be allocated,
		 * rsp->ret is < 0 when the peer permission check failed
		 */
		if (ret == 0 || rsp->ret < 0)
			return 0;

		rspdata = malloc(sizeof(*rspdata));
		if (!rspdata) {
			errno = ENOMEM;
			ERROR("Failed to allocate response buffer for command \"%s\"",
			      lxc_cmd_str(cmd->req.cmd));
			return -1;
		}

		rspdata->masterfd = rspfd;
		rspdata->ttynum = PTR_TO_INT(rsp->data);
		rsp->data = rspdata;
	}

	if (rsp->datalen == 0) {
		DEBUG("Response data length for command \"%s\" is 0",
		      lxc_cmd_str(cmd->req.cmd));
		return ret;
	}

	if ((rsp->datalen > LXC_CMD_DATA_MAX) &&
	    (cmd->req.cmd != LXC_CMD_CONSOLE_LOG)) {
		ERROR("Response data for command \"%s\" is too long: %d bytes > %d",
		      lxc_cmd_str(cmd->req.cmd), rsp->datalen, LXC_CMD_DATA_MAX);
		return -1;
	}

	if (cmd->req.cmd == LXC_CMD_CONSOLE_LOG) {
		rsp->data = malloc(rsp->datalen + 1);
		((char *)rsp->data)[rsp->datalen] = '\0';
	} else {
		rsp->data = malloc(rsp->datalen);
	}
	if (!rsp->data) {
		errno = ENOMEM;
		ERROR("Failed to allocate response buffer for command \"%s\"",
		      lxc_cmd_str(cmd->req.cmd));
		return -1;
	}

	ret = lxc_recv_nointr(sock, rsp->data, rsp->datalen, 0);
	if (ret != rsp->datalen) {
		SYSERROR("Failed to receive response data for command \"%s\"",
		         lxc_cmd_str(cmd->req.cmd));
		return -1;
	}

	return ret;
}