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