1 /*
2 * Copyright (c) 2008, XenSource Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of XenSource Inc. nor the names of its contributors
13 * may be used to endorse or promote products derived from this software
14 * without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
20 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <stdio.h>
30 #include <errno.h>
31 #include <stdlib.h>
32 #include <unistd.h>
33 #include <string.h>
34 #include <glob.h>
35
36 #include "tap-ctl.h"
37 #include "blktap2.h"
38 #include "list.h"
39
40 static void
free_list(tap_list_t * entry)41 free_list(tap_list_t *entry)
42 {
43 if (entry->type) {
44 free(entry->type);
45 entry->type = NULL;
46 }
47
48 if (entry->path) {
49 free(entry->path);
50 entry->path = NULL;
51 }
52
53 free(entry);
54 }
55
56 int
_parse_params(const char * params,char ** type,char ** path)57 _parse_params(const char *params, char **type, char **path)
58 {
59 char *ptr;
60 size_t len;
61
62 ptr = strchr(params, ':');
63 if (!ptr)
64 return -EINVAL;
65
66 len = ptr - params;
67
68 *type = strndup(params, len);
69 *path = strdup(params + len + 1);
70
71 if (!*type || !*path) {
72 free(*type);
73 *type = NULL;
74
75 free(*path);
76 *path = NULL;
77
78 return -errno;
79 }
80
81 return 0;
82 }
83
84 static int
init_list(tap_list_t * entry,int tap_id,pid_t tap_pid,int vbd_minor,int vbd_state,const char * params)85 init_list(tap_list_t *entry,
86 int tap_id, pid_t tap_pid, int vbd_minor, int vbd_state,
87 const char *params)
88 {
89 int err = 0;
90
91 entry->id = tap_id;
92 entry->pid = tap_pid;
93 entry->minor = vbd_minor;
94 entry->state = vbd_state;
95
96 if (params)
97 err = _parse_params(params, &entry->type, &entry->path);
98
99 return err;
100 }
101
102 void
tap_ctl_free_list(tap_list_t ** list)103 tap_ctl_free_list(tap_list_t **list)
104 {
105 tap_list_t **_entry;
106
107 for (_entry = list; *_entry != NULL; ++_entry)
108 free_list(*_entry);
109
110 free(list);
111 }
112
113 static tap_list_t**
tap_ctl_alloc_list(int n)114 tap_ctl_alloc_list(int n)
115 {
116 tap_list_t **list, *entry;
117 size_t size;
118 int i;
119
120 size = sizeof(tap_list_t*) * (n+1);
121 list = malloc(size);
122 if (!list)
123 goto fail;
124
125 memset(list, 0, size);
126
127 for (i = 0; i < n; ++i) {
128 tap_list_t *entry;
129
130 entry = malloc(sizeof(tap_list_t));
131 if (!entry)
132 goto fail;
133
134 memset(entry, 0, sizeof(tap_list_t));
135
136 list[i] = entry;
137 }
138
139 return list;
140
141 fail:
142 if (list)
143 tap_ctl_free_list(list);
144
145 return NULL;
146 }
147
148 static int
tap_ctl_list_length(const tap_list_t ** list)149 tap_ctl_list_length(const tap_list_t **list)
150 {
151 const tap_list_t **_entry;
152 int n;
153
154 n = 0;
155 for (_entry = list; *_entry != NULL; ++_entry)
156 n++;
157
158 return n;
159 }
160
161 static int
_tap_minor_cmp(const void * a,const void * b)162 _tap_minor_cmp(const void *a, const void *b)
163 {
164 return *(int*)a - *(int*)b;
165 }
166
167 int
_tap_ctl_find_minors(int ** _minorv)168 _tap_ctl_find_minors(int **_minorv)
169 {
170 glob_t glbuf = { 0 };
171 const char *pattern, *format;
172 int *minorv = NULL, n_minors = 0;
173 int err, i;
174
175 pattern = BLKTAP2_SYSFS_DIR"/blktap*";
176 format = BLKTAP2_SYSFS_DIR"/blktap%d";
177
178 n_minors = 0;
179 minorv = NULL;
180
181 err = glob(pattern, 0, NULL, &glbuf);
182 switch (err) {
183 case GLOB_NOMATCH:
184 goto done;
185
186 case GLOB_ABORTED:
187 case GLOB_NOSPACE:
188 err = -errno;
189 EPRINTF("%s: glob failed, err %d", pattern, err);
190 goto fail;
191 }
192
193 minorv = malloc(sizeof(int) * glbuf.gl_pathc);
194 if (!minorv) {
195 err = -errno;
196 goto fail;
197 }
198
199 for (i = 0; i < glbuf.gl_pathc; ++i) {
200 int n;
201
202 n = sscanf(glbuf.gl_pathv[i], format, &minorv[n_minors]);
203 if (n != 1)
204 continue;
205
206 n_minors++;
207 }
208
209 qsort(minorv, n_minors, sizeof(int), _tap_minor_cmp);
210
211 done:
212 *_minorv = minorv;
213 err = 0;
214
215 out:
216 if (glbuf.gl_pathv)
217 globfree(&glbuf);
218
219 return err ? : n_minors;
220
221 fail:
222 if (minorv)
223 free(minorv);
224
225 goto out;
226 }
227
228 struct tapdisk {
229 int id;
230 pid_t pid;
231 struct list_head list;
232 };
233
234 static int
_tap_tapdisk_cmp(const void * a,const void * b)235 _tap_tapdisk_cmp(const void *a, const void *b)
236 {
237 return ((struct tapdisk*)a)->id - ((struct tapdisk*)b)->id;
238 }
239
240 int
_tap_ctl_find_tapdisks(struct tapdisk ** _tapv)241 _tap_ctl_find_tapdisks(struct tapdisk **_tapv)
242 {
243 glob_t glbuf = { 0 };
244 const char *pattern, *format;
245 struct tapdisk *tapv = NULL;
246 int err, i, n_taps = 0;
247
248 pattern = BLKTAP2_CONTROL_DIR"/"BLKTAP2_CONTROL_SOCKET"*";
249 format = BLKTAP2_CONTROL_DIR"/"BLKTAP2_CONTROL_SOCKET"%d";
250
251 n_taps = 0;
252 tapv = NULL;
253
254 err = glob(pattern, 0, NULL, &glbuf);
255 switch (err) {
256 case GLOB_NOMATCH:
257 goto done;
258
259 case GLOB_ABORTED:
260 case GLOB_NOSPACE:
261 err = -errno;
262 EPRINTF("%s: glob failed, err %d", pattern, err);
263 goto fail;
264 }
265
266 tapv = malloc(sizeof(struct tapdisk) * glbuf.gl_pathc);
267 if (!tapv) {
268 err = -errno;
269 goto fail;
270 }
271
272 for (i = 0; i < glbuf.gl_pathc; ++i) {
273 struct tapdisk *tap;
274 int n;
275
276 tap = &tapv[n_taps];
277
278 err = sscanf(glbuf.gl_pathv[i], format, &tap->id);
279 if (err != 1)
280 continue;
281
282 tap->pid = tap_ctl_get_pid(tap->id);
283 if (tap->pid < 0)
284 continue;
285
286 n_taps++;
287 }
288
289 qsort(tapv, n_taps, sizeof(struct tapdisk), _tap_tapdisk_cmp);
290
291 for (i = 0; i < n_taps; ++i)
292 INIT_LIST_HEAD(&tapv[i].list);
293
294 done:
295 *_tapv = tapv;
296 err = 0;
297
298 out:
299 if (glbuf.gl_pathv)
300 globfree(&glbuf);
301
302 return err ? : n_taps;
303
304 fail:
305 if (tapv)
306 free(tapv);
307
308 goto out;
309 }
310
311 struct tapdisk_list {
312 int minor;
313 int state;
314 char *params;
315 struct list_head entry;
316 };
317
318 int
_tap_ctl_list_tapdisk(int id,struct list_head * _list)319 _tap_ctl_list_tapdisk(int id, struct list_head *_list)
320 {
321 tapdisk_message_t message;
322 struct list_head list;
323 struct tapdisk_list *tl, *next;
324 int err, sfd;
325
326 err = tap_ctl_connect_id(id, &sfd);
327 if (err)
328 return err;
329
330 memset(&message, 0, sizeof(message));
331 message.type = TAPDISK_MESSAGE_LIST;
332 message.cookie = -1;
333
334 err = tap_ctl_write_message(sfd, &message, 2);
335 if (err)
336 return err;
337
338 INIT_LIST_HEAD(&list);
339 do {
340 err = tap_ctl_read_message(sfd, &message, 2);
341 if (err) {
342 err = -EPROTO;
343 break;
344 }
345
346 if (message.u.list.count == 0)
347 break;
348
349 tl = malloc(sizeof(struct tapdisk_list));
350 if (!tl) {
351 err = -ENOMEM;
352 break;
353 }
354
355 tl->minor = message.u.list.minor;
356 tl->state = message.u.list.state;
357 if (message.u.list.path[0] != 0) {
358 tl->params = strndup(message.u.list.path,
359 sizeof(message.u.list.path));
360 if (!tl->params) {
361 err = -errno;
362 break;
363 }
364 } else
365 tl->params = NULL;
366
367 list_add(&tl->entry, &list);
368 } while (1);
369
370 if (err)
371 list_for_each_entry_safe(tl, next, &list, entry) {
372 list_del(&tl->entry);
373 free(tl->params);
374 free(tl);
375 }
376
377 close(sfd);
378 list_splice(&list, _list);
379 return err;
380 }
381
382 void
_tap_ctl_free_tapdisks(struct tapdisk * tapv,int n_taps)383 _tap_ctl_free_tapdisks(struct tapdisk *tapv, int n_taps)
384 {
385 struct tapdisk *tap;
386
387 for (tap = tapv; tap < &tapv[n_taps]; ++tap) {
388 struct tapdisk_list *tl, *next;
389
390 list_for_each_entry_safe(tl, next, &tap->list, entry) {
391 free(tl->params);
392 free(tl);
393 }
394 }
395
396 free(tapv);
397 }
398
399 int
_tap_list_join3(int n_minors,int * minorv,int n_taps,struct tapdisk * tapv,tap_list_t *** _list)400 _tap_list_join3(int n_minors, int *minorv, int n_taps, struct tapdisk *tapv,
401 tap_list_t ***_list)
402 {
403 tap_list_t **list, **_entry;
404 int i, _m, err;
405
406 list = tap_ctl_alloc_list(n_minors + n_taps);
407 if (!list) {
408 err = -ENOMEM;
409 goto fail;
410 }
411
412 _entry = list;
413
414 for (i = 0; i < n_taps; ++i) {
415 struct tapdisk *tap = &tapv[i];
416 struct tapdisk_list *tl;
417
418 /* orphaned tapdisk */
419 if (list_empty(&tap->list)) {
420 err = init_list(*_entry++, tap->id, tap->pid, -1, -1, NULL);
421 if (err)
422 goto fail;
423 continue;
424 }
425
426 list_for_each_entry(tl, &tap->list, entry) {
427
428 err = init_list(*_entry++,
429 tap->id, tap->pid,
430 tl->minor, tl->state, tl->params);
431 if (err)
432 goto fail;
433
434 if (tl->minor >= 0) {
435 /* clear minor */
436 for (_m = 0; _m < n_minors; ++_m) {
437 if (minorv[_m] == tl->minor) {
438 minorv[_m] = -1;
439 break;
440 }
441 }
442 }
443 }
444 }
445
446 /* orphaned minors */
447 for (_m = 0; _m < n_minors; ++_m) {
448 int minor = minorv[_m];
449 if (minor >= 0) {
450 err = init_list(*_entry++, -1, -1, minor, -1, NULL);
451 if (err)
452 goto fail;
453 }
454 }
455
456 /* free extraneous list entries */
457 for (; *_entry != NULL; ++_entry) {
458 free_list(*_entry);
459 *_entry = NULL;
460 }
461
462 *_list = list;
463
464 return 0;
465
466 fail:
467 if (list)
468 tap_ctl_free_list(list);
469
470 return err;
471 }
472
473 int
tap_ctl_list(tap_list_t *** list)474 tap_ctl_list(tap_list_t ***list)
475 {
476 int n_taps, n_minors, err, *minorv;
477 struct tapdisk *tapv, *tap;
478
479 n_taps = -1;
480 n_minors = -1;
481
482 err = n_minors = _tap_ctl_find_minors(&minorv);
483 if (err < 0)
484 goto out;
485
486 err = n_taps = _tap_ctl_find_tapdisks(&tapv);
487 if (err < 0)
488 goto out;
489
490 for (tap = tapv; tap < &tapv[n_taps]; ++tap) {
491 err = _tap_ctl_list_tapdisk(tap->id, &tap->list);
492 if (err)
493 goto out;
494 }
495
496 err = _tap_list_join3(n_minors, minorv, n_taps, tapv, list);
497
498 out:
499 if (n_taps > 0)
500 _tap_ctl_free_tapdisks(tapv, n_taps);
501
502 if (n_minors > 0)
503 free(minorv);
504
505 return err;
506 }
507
508 int
tap_ctl_find(const char * type,const char * path,tap_list_t * tap)509 tap_ctl_find(const char *type, const char *path, tap_list_t *tap)
510 {
511 tap_list_t **list, **_entry;
512 int ret = -ENOENT, err;
513
514 err = tap_ctl_list(&list);
515 if (err)
516 return err;
517
518 for (_entry = list; *_entry != NULL; ++_entry) {
519 tap_list_t *entry = *_entry;
520
521 if (type && (!entry->type || strcmp(entry->type, type)))
522 continue;
523
524 if (path && (!entry->path || strcmp(entry->path, path)))
525 continue;
526
527 *tap = *entry;
528 tap->type = tap->path = NULL;
529 ret = 0;
530 break;
531 }
532
533 tap_ctl_free_list(list);
534
535 return ret;
536 }
537