]> git.proxmox.com Git - mirror_lxc.git/blob - src/lxc/commands_utils.c
commands: don't lock the whole command
[mirror_lxc.git] / src / lxc / commands_utils.c
1 /* liblxcapi
2 *
3 * Copyright © 2017 Christian Brauner <christian.brauner@ubuntu.com>.
4 * Copyright © 2017 Canonical Ltd.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2, as
8 * published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #define _GNU_SOURCE
21 #define __STDC_FORMAT_MACROS /* Required for PRIu64 to work. */
22 #include <errno.h>
23 #include <inttypes.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include <sys/socket.h>
29 #include <sys/un.h>
30
31 #include "af_unix.h"
32 #include "commands.h"
33 #include "commands_utils.h"
34 #include "initutils.h"
35 #include "log.h"
36 #include "lxclock.h"
37 #include "monitor.h"
38 #include "state.h"
39 #include "utils.h"
40
41 lxc_log_define(lxc_commands_utils, lxc);
42
43 int lxc_cmd_sock_rcv_state(int state_client_fd, int timeout)
44 {
45 int ret;
46 struct lxc_msg msg;
47 struct timeval out;
48
49 if (timeout >= 0) {
50 memset(&out, 0, sizeof(out));
51 out.tv_sec = timeout;
52 ret = setsockopt(state_client_fd, SOL_SOCKET, SO_RCVTIMEO,
53 (const void *)&out, sizeof(out));
54 if (ret < 0) {
55 SYSERROR("Failed to set %ds timeout on containter "
56 "state socket",
57 timeout);
58 return -1;
59 }
60 }
61
62 memset(&msg, 0, sizeof(msg));
63
64 again:
65 ret = recv(state_client_fd, &msg, sizeof(msg), 0);
66 if (ret < 0) {
67 if (errno == EINTR) {
68 TRACE("Caught EINTR; retrying");
69 goto again;
70 }
71
72 ERROR("Failed to receive message: %s", strerror(errno));
73 return -1;
74 }
75
76 if (ret < 0)
77 return -1;
78
79 TRACE("Received state %s from state client %d",
80 lxc_state2str(msg.value), state_client_fd);
81
82 return msg.value;
83 }
84
85 /* Register a new state client and retrieve state from command socket. */
86 int lxc_cmd_sock_get_state(const char *name, const char *lxcpath,
87 lxc_state_t states[MAX_STATE], int timeout)
88 {
89 int ret;
90 int state_client_fd;
91
92 ret = lxc_cmd_add_state_client(name, lxcpath, states, &state_client_fd);
93 if (ret < 0)
94 return -1;
95
96 if (ret < MAX_STATE)
97 return ret;
98
99 ret = lxc_cmd_sock_rcv_state(state_client_fd, timeout);
100 close(state_client_fd);
101 return ret;
102 }
103
104 int lxc_make_abstract_socket_name(char *path, int len, const char *lxcname,
105 const char *lxcpath,
106 const char *hashed_sock_name,
107 const char *suffix)
108 {
109 const char *name;
110 char *tmppath;
111 size_t tmplen;
112 uint64_t hash;
113 int ret;
114
115 name = lxcname;
116 if (!name)
117 name = "";
118
119 if (hashed_sock_name != NULL) {
120 ret =
121 snprintf(path, len, "lxc/%s/%s", hashed_sock_name, suffix);
122 if (ret < 0 || ret >= len) {
123 ERROR("Failed to create abstract socket name");
124 return -1;
125 }
126 return 0;
127 }
128
129 if (!lxcpath) {
130 lxcpath = lxc_global_config_value("lxc.lxcpath");
131 if (!lxcpath) {
132 ERROR("Failed to allocate memory");
133 return -1;
134 }
135 }
136
137 ret = snprintf(path, len, "%s/%s/%s", lxcpath, name, suffix);
138 if (ret < 0) {
139 ERROR("Failed to create abstract socket name");
140 return -1;
141 }
142 if (ret < len)
143 return 0;
144
145 /* ret >= len; lxcpath or name is too long. hash both */
146 tmplen = strlen(name) + strlen(lxcpath) + 2;
147 tmppath = alloca(tmplen);
148 ret = snprintf(tmppath, tmplen, "%s/%s", lxcpath, name);
149 if (ret < 0 || (size_t)ret >= tmplen) {
150 ERROR("Failed to create abstract socket name");
151 return -1;
152 }
153
154 hash = fnv_64a_buf(tmppath, ret, FNV1A_64_INIT);
155 ret = snprintf(path, len, "lxc/%016" PRIx64 "/%s", hash, suffix);
156 if (ret < 0 || ret >= len) {
157 ERROR("Failed to create abstract socket name");
158 return -1;
159 }
160
161 return 0;
162 }
163
164 int lxc_cmd_connect(const char *name, const char *lxcpath,
165 const char *hashed_sock_name, const char *suffix)
166 {
167 int ret, client_fd;
168 char path[sizeof(((struct sockaddr_un *)0)->sun_path)] = {0};
169 char *offset = &path[1];
170
171 /* -2 here because this is an abstract unix socket so it needs a
172 * leading \0, and we null terminate, so it needs a trailing \0.
173 * Although null termination isn't required by the API, we do it anyway
174 * because we print the sockname out sometimes.
175 */
176 size_t len = sizeof(path) - 2;
177 ret = lxc_make_abstract_socket_name(offset, len, name, lxcpath,
178 hashed_sock_name, suffix);
179 if (ret < 0)
180 return -1;
181
182 /* Get new client fd. */
183 client_fd = lxc_abstract_unix_connect(path);
184 if (client_fd < 0) {
185 if (errno == ECONNREFUSED)
186 return -ECONNREFUSED;
187 return -1;
188 }
189
190 return client_fd;
191 }
192
193 int lxc_add_state_client(int state_client_fd, struct lxc_handler *handler,
194 lxc_state_t states[MAX_STATE])
195 {
196 struct state_client *newclient;
197 struct lxc_list *tmplist;
198
199 newclient = malloc(sizeof(*newclient));
200 if (!newclient)
201 return -ENOMEM;
202
203 /* copy requested states */
204 memcpy(newclient->states, states, sizeof(newclient->states));
205 newclient->clientfd = state_client_fd;
206
207 tmplist = malloc(sizeof(*tmplist));
208 if (!tmplist) {
209 free(newclient);
210 return -ENOMEM;
211 }
212
213 process_lock();
214 lxc_list_add_elem(tmplist, newclient);
215 lxc_list_add_tail(&handler->state_clients, tmplist);
216 process_unlock();
217
218 TRACE("added state client %d to state client list", state_client_fd);
219
220 return 0;
221 }