/* * 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; }
/* * 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; }
/* * 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; }