Branch data Line data Source code
1 : : /*
2 : : * Copyright (c) 2016, Citrix Systems, Inc.
3 : : *
4 : : * All rights reserved.
5 : : *
6 : : * Redistribution and use in source and binary forms, with or without
7 : : * modification, are permitted provided that the following conditions are met:
8 : : *
9 : : * 1. Redistributions of source code must retain the above copyright
10 : : * notice, this list of conditions and the following disclaimer.
11 : : * 2. Redistributions in binary form must reproduce the above copyright
12 : : * notice, this list of conditions and the following disclaimer in the
13 : : * documentation and/or other materials provided with the distribution.
14 : : * 3. Neither the name of the copyright holder nor the names of its
15 : : * contributors may be used to endorse or promote products derived from
16 : : * this software without specific prior written permission.
17 : : *
18 : : * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 : : * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 : : * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 : : * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
22 : : * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23 : : * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 : : * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25 : : * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26 : : * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27 : : * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 : : * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 : : */
30 : :
31 : : #ifdef HAVE_CONFIG_H
32 : : #include "config.h"
33 : : #endif
34 : :
35 : : #include <stdio.h>
36 : : #include <errno.h>
37 : : #include <stdlib.h>
38 : : #include <unistd.h>
39 : : #include <string.h>
40 : : #include <glob.h>
41 : :
42 : : #include "tap-ctl.h"
43 : : #include "blktap2.h"
44 : : #include "list.h"
45 : :
46 : : static tap_list_t*
47 : 9 : _tap_list_alloc(void)
48 : : {
49 : 9 : const size_t sz = sizeof(tap_list_t);
50 : : tap_list_t *tl;
51 : :
52 : 9 : tl = malloc(sz);
53 [ + - ]: 9 : if (!tl)
54 : : return NULL;
55 : :
56 : 9 : tl->pid = -1;
57 : 9 : tl->minor = -1;
58 : 9 : tl->state = -1;
59 : 9 : tl->type = NULL;
60 : 9 : tl->path = NULL;
61 : :
62 : 9 : INIT_LIST_HEAD(&tl->entry);
63 : :
64 : 9 : return tl;
65 : : }
66 : :
67 : : static void
68 : 9 : _tap_list_free(tap_list_t *tl)
69 : : {
70 : 9 : list_del_init(&tl->entry);
71 : :
72 [ + + ]: 9 : if (tl->type) {
73 : 1 : free(tl->type);
74 : 1 : tl->type = NULL;
75 : : }
76 : :
77 [ + + ]: 9 : if (tl->path) {
78 : 1 : free(tl->path);
79 : 1 : tl->path = NULL;
80 : : }
81 : :
82 : 9 : free(tl);
83 : 9 : }
84 : :
85 : : int
86 : 1 : _parse_params(const char *params, char **type, char **path)
87 : : {
88 : : char *ptr;
89 : : size_t len;
90 : :
91 : 1 : ptr = strchr(params, ':');
92 [ + - ]: 1 : if (!ptr)
93 : : return -EINVAL;
94 : :
95 : 1 : len = ptr - params;
96 : :
97 : 1 : *type = strndup(params, len);
98 : 1 : *path = strdup(params + len + 1);
99 : :
100 [ + - ][ - + ]: 1 : if (!*type || !*path) {
101 : 0 : free(*type);
102 : 0 : *type = NULL;
103 : :
104 : 0 : free(*path);
105 : 0 : *path = NULL;
106 : :
107 : 0 : return -errno;
108 : : }
109 : :
110 : : return 0;
111 : : }
112 : :
113 : : void
114 : 4 : tap_ctl_list_free(struct list_head *list)
115 : : {
116 : : tap_list_t *tl, *n;
117 : :
118 [ # # ][ # # ]: 8 : tap_list_for_each_entry_safe(tl, n, list)
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ + + ]
119 : 4 : _tap_list_free(tl);
120 : 4 : }
121 : :
122 : : /**
123 : : * Returns a list running tapdisks. tapdisks are searched for by looking for
124 : : * their control socket.
125 : : */
126 : : static int
127 : 5 : _tap_ctl_find_minors(struct list_head *list)
128 : : {
129 : : const char *pattern;
130 : 5 : glob_t glbuf = { 0 };
131 : : tap_list_t *tl;
132 : : int i, err;
133 : :
134 : : INIT_LIST_HEAD(list);
135 : :
136 : 5 : pattern = BLKTAP2_SYSFS_DIR"/blktap*";
137 : :
138 : 5 : err = glob(pattern, 0, NULL, &glbuf);
139 [ + - + ]: 5 : switch (err) {
140 : : case GLOB_NOMATCH:
141 : : goto done;
142 : :
143 : : case GLOB_ABORTED:
144 : : case GLOB_NOSPACE:
145 : 0 : err = -errno;
146 : : EPRINTF("%s: glob failed, err %d", pattern, err);
147 : : goto fail;
148 : : }
149 : :
150 [ + + ]: 6 : for (i = 0; i < glbuf.gl_pathc; ++i) {
151 : : int n;
152 : :
153 : 3 : tl = _tap_list_alloc();
154 [ + - ]: 3 : if (!tl) {
155 : : err = -ENOMEM;
156 : : goto fail;
157 : : }
158 : :
159 : 3 : n = sscanf(glbuf.gl_pathv[i], BLKTAP2_SYSFS_DIR"/blktap!blktap%d", &tl->minor);
160 [ - + ]: 3 : if (n != 1) {
161 : 0 : _tap_list_free(tl);
162 : 0 : continue;
163 : : }
164 : :
165 : 3 : list_add_tail(&tl->entry, list);
166 : : }
167 : :
168 : : done:
169 : : err = 0;
170 : : out:
171 [ + + ]: 5 : if (glbuf.gl_pathv)
172 : 3 : globfree(&glbuf);
173 : :
174 : 5 : return err;
175 : :
176 : : fail:
177 : 0 : tap_ctl_list_free(list);
178 : : goto out;
179 : : }
180 : :
181 : : int
182 : 5 : _tap_ctl_find_tapdisks(struct list_head *list)
183 : : {
184 : : const char *pattern;
185 : 5 : glob_t glbuf = { 0 };
186 : 5 : int err, i, n_taps = 0;
187 : :
188 : 5 : pattern = BLKTAP2_CONTROL_DIR"/"BLKTAP2_CONTROL_SOCKET"*";
189 : :
190 : : INIT_LIST_HEAD(list);
191 : :
192 : 5 : err = glob(pattern, 0, NULL, &glbuf);
193 [ + - + ]: 5 : switch (err) {
194 : : case GLOB_NOMATCH:
195 : : goto done;
196 : :
197 : : case GLOB_ABORTED:
198 : : case GLOB_NOSPACE:
199 : 0 : err = -errno;
200 : : EPRINTF("%s: glob failed, err %d", pattern, err);
201 : : goto fail;
202 : : }
203 : :
204 [ + + ]: 6 : for (i = 0; i < glbuf.gl_pathc; ++i) {
205 : : tap_list_t *tl;
206 : : int n;
207 : :
208 : 3 : tl = _tap_list_alloc();
209 [ + - ]: 3 : if (!tl) {
210 : : err = -ENOMEM;
211 : : goto fail;
212 : : }
213 : :
214 : 3 : n = sscanf(glbuf.gl_pathv[i],
215 : : BLKTAP2_CONTROL_DIR"/"BLKTAP2_CONTROL_SOCKET"%d",
216 : : &tl->pid);
217 [ + - ]: 3 : if (n != 1)
218 : : goto skip;
219 : :
220 : 3 : tl->pid = tap_ctl_get_pid(tl->pid);
221 [ + - ]: 3 : if (tl->pid < 0)
222 : : goto skip;
223 : :
224 : 3 : list_add_tail(&tl->entry, list);
225 : 3 : n_taps++;
226 : 3 : continue;
227 : :
228 : : skip:
229 : 0 : _tap_list_free(tl);
230 : : }
231 : :
232 : : done:
233 : : err = 0;
234 : : out:
235 [ + + ]: 5 : if (glbuf.gl_pathv)
236 : 3 : globfree(&glbuf);
237 : :
238 [ + - ]: 5 : return err ? : n_taps;
239 : :
240 : : fail:
241 : 0 : tap_ctl_list_free(list);
242 : : goto out;
243 : : }
244 : :
245 : : /**
246 : : * Retrieves all the VBDs a tapdisk is serving.
247 : : *
248 : : * @param pid the process ID of the tapdisk whose VBDs should be retrieved
249 : : * @param list output parameter that receives the list of VBD
250 : : * @returns 0 on success, an error code otherwise
251 : : */
252 : : int
253 : 3 : _tap_ctl_list_tapdisk(pid_t pid, struct list_head *list)
254 : : {
255 : 3 : struct timeval timeout = { .tv_sec = 10, .tv_usec = 0 };
256 : : tapdisk_message_t message;
257 : : tap_list_t *tl;
258 : : int err, sfd;
259 : :
260 : 3 : err = tap_ctl_connect_id(pid, &sfd);
261 [ + - ]: 3 : if (err)
262 : 3 : return err;
263 : :
264 : : memset(&message, 0, sizeof(message));
265 : 3 : message.type = TAPDISK_MESSAGE_LIST;
266 : 3 : message.cookie = -1;
267 : :
268 : 3 : err = tap_ctl_write_message(sfd, &message, &timeout);
269 [ + - ]: 3 : if (err)
270 : : goto out;
271 : :
272 : : INIT_LIST_HEAD(list);
273 : :
274 : : do {
275 : 6 : err = tap_ctl_read_message(sfd, &message, &timeout);
276 [ + - ]: 6 : if (err) {
277 : : err = -EPROTO;
278 : : goto fail;
279 : : }
280 : :
281 [ + + ]: 6 : if (message.u.list.count == 0)
282 : : break;
283 : :
284 : 3 : tl = _tap_list_alloc();
285 [ + - ]: 3 : if (!tl) {
286 : : err = -ENOMEM;
287 : : goto fail;
288 : : }
289 : :
290 : 3 : tl->pid = pid;
291 : 3 : tl->minor = message.u.list.minor;
292 : 3 : tl->state = message.u.list.state;
293 : :
294 [ + + ]: 3 : if (message.u.list.path[0] != 0) {
295 : 1 : err = _parse_params(message.u.list.path,
296 : : &tl->type, &tl->path);
297 [ - + ]: 1 : if (err) {
298 : 0 : _tap_list_free(tl);
299 : : goto fail;
300 : : }
301 : : }
302 : :
303 : 3 : list_add(&tl->entry, list);
304 : : } while (1);
305 : :
306 : : err = 0;
307 : : out:
308 : 3 : close(sfd);
309 : : return err;
310 : :
311 : : fail:
312 : 0 : tap_ctl_list_free(list);
313 : : goto out;
314 : : }
315 : :
316 : : int
317 : 5 : tap_ctl_list(struct list_head *list)
318 : : {
319 : : struct list_head minors, tapdisks, vbds;
320 : : tap_list_t *t, *next_t, *v, *next_v, *m, *next_m;
321 : : int err;
322 : :
323 : : /*
324 : : * Find all minors, find all tapdisks, then list all minors
325 : : * they attached to. Output is a 3-way outer join.
326 : : */
327 : :
328 : 5 : err = _tap_ctl_find_minors(&minors);
329 [ + - ]: 5 : if (err < 0)
330 : : goto fail;
331 : :
332 : 5 : err = _tap_ctl_find_tapdisks(&tapdisks);
333 [ - + ]: 5 : if (err < 0) {
334 : 0 : EPRINTF("error finding tapdisks: %s\n", strerror(-err));
335 : : goto fail;
336 : : }
337 : :
338 : : INIT_LIST_HEAD(list);
339 : :
340 [ + + ]: 8 : tap_list_for_each_entry_safe(t, next_t, &tapdisks) {
341 : :
342 : 3 : err = _tap_ctl_list_tapdisk(t->pid, &vbds);
343 : :
344 [ - + + - ]: 3 : if (err || list_empty(&vbds)) {
345 : 3 : list_move_tail(&t->entry, list);
346 : 0 : continue;
347 : : }
348 : :
349 [ + + ]: 6 : tap_list_for_each_entry_safe(v, next_v, &vbds) {
350 : :
351 [ + + ]: 3 : tap_list_for_each_entry_safe(m, next_m, &minors)
352 [ + - ]: 2 : if (m->minor == v->minor) {
353 : 2 : _tap_list_free(m);
354 : : break;
355 : : }
356 : :
357 : 6 : list_move_tail(&v->entry, list);
358 : : }
359 : :
360 : 3 : _tap_list_free(t);
361 : : }
362 : :
363 : : /* orphaned minors */
364 : : list_splice_tail(&minors, list);
365 : :
366 : 5 : return 0;
367 : :
368 : : fail:
369 : 0 : tap_ctl_list_free(list);
370 : :
371 : 0 : tap_ctl_list_free(&vbds);
372 : 0 : tap_ctl_list_free(&tapdisks);
373 : 0 : tap_ctl_list_free(&minors);
374 : :
375 : : return err;
376 : : }
377 : :
378 : : int
379 : 0 : tap_ctl_list_pid(pid_t pid, struct list_head *list)
380 : : {
381 : : tap_list_t *t;
382 : : int err;
383 : :
384 : 0 : t = _tap_list_alloc();
385 [ # # ]: 0 : if (!t)
386 : : return -ENOMEM;
387 : :
388 : 0 : t->pid = tap_ctl_get_pid(pid);
389 [ # # ]: 0 : if (t->pid < 0) {
390 : 0 : _tap_list_free(t);
391 : 0 : return 0;
392 : : }
393 : :
394 : 0 : err = _tap_ctl_list_tapdisk(t->pid, list);
395 : :
396 [ # # ][ # # ]: 0 : if (err || list_empty(list))
397 : 0 : list_add_tail(&t->entry, list);
398 : :
399 : : return 0;
400 : : }
401 : :
402 : : int
403 : 0 : tap_ctl_find_minor(const char *type, const char *path)
404 : : {
405 : 0 : struct list_head list = LIST_HEAD_INIT(list);
406 : : tap_list_t *entry;
407 : : int minor, err;
408 : :
409 : 0 : err = tap_ctl_list(&list);
410 [ # # ]: 0 : if (err)
411 : 0 : return err;
412 : :
413 : 0 : minor = -1;
414 : :
415 [ # # ]: 0 : tap_list_for_each_entry(entry, &list) {
416 : :
417 [ # # ][ # # ]: 0 : if (type && (!entry->type || strcmp(entry->type, type)))
[ # # ]
418 : 0 : continue;
419 : :
420 [ # # ][ # # ]: 0 : if (path && (!entry->path || strcmp(entry->path, path)))
[ # # ]
421 : 0 : continue;
422 : :
423 : 0 : minor = entry->minor;
424 : 0 : break;
425 : : }
426 : :
427 : 0 : tap_ctl_list_free(&list);
428 : :
429 [ # # ]: 0 : return minor >= 0 ? minor : -ENOENT;
430 : : }
|