/* * Copyright (c) 2008, XenSource Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of XenSource Inc. nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include "tap-ctl.h" #include "blktap2.h" #include "list.h" static void free_list(tap_list_t *entry) { if (entry->type) { free(entry->type); entry->type = NULL; } if (entry->path) { free(entry->path); entry->path = NULL; } free(entry); } int _parse_params(const char *params, char **type, char **path) { char *ptr; size_t len; ptr = strchr(params, ':'); if (!ptr) return -EINVAL; len = ptr - params; *type = strndup(params, len); *path = strdup(params + len + 1); if (!*type || !*path) { free(*type); *type = NULL; free(*path); *path = NULL; return -errno; } return 0; } static int init_list(tap_list_t *entry, int tap_id, pid_t tap_pid, int vbd_minor, int vbd_state, const char *params) { int err = 0; entry->id = tap_id; entry->pid = tap_pid; entry->minor = vbd_minor; entry->state = vbd_state; if (params) err = _parse_params(params, &entry->type, &entry->path); return err; } void tap_ctl_free_list(tap_list_t **list) { tap_list_t **_entry; for (_entry = list; *_entry != NULL; ++_entry) free_list(*_entry); free(list); } static tap_list_t** tap_ctl_alloc_list(int n) { tap_list_t **list, *entry; size_t size; int i; size = sizeof(tap_list_t*) * (n+1); list = malloc(size); if (!list) goto fail; memset(list, 0, size); for (i = 0; i < n; ++i) { tap_list_t *entry; entry = malloc(sizeof(tap_list_t)); if (!entry) goto fail; memset(entry, 0, sizeof(tap_list_t)); list[i] = entry; } return list; fail: if (list) tap_ctl_free_list(list); return NULL; } static int tap_ctl_list_length(const tap_list_t **list) { const tap_list_t **_entry; int n; n = 0; for (_entry = list; *_entry != NULL; ++_entry) n++; return n; } static int _tap_minor_cmp(const void *a, const void *b) { return *(int*)a - *(int*)b; } int _tap_ctl_find_minors(int **_minorv) { glob_t glbuf = { 0 }; const char *pattern, *format; int *minorv = NULL, n_minors = 0; int err, i; pattern = BLKTAP2_SYSFS_DIR"/blktap*"; format = BLKTAP2_SYSFS_DIR"/blktap%d"; n_minors = 0; minorv = NULL; err = glob(pattern, 0, NULL, &glbuf); switch (err) { case GLOB_NOMATCH: goto done; case GLOB_ABORTED: case GLOB_NOSPACE: err = -errno; EPRINTF("%s: glob failed, err %d", pattern, err); goto fail; } minorv = malloc(sizeof(int) * glbuf.gl_pathc); if (!minorv) { err = -errno; goto fail; } for (i = 0; i < glbuf.gl_pathc; ++i) { int n; n = sscanf(glbuf.gl_pathv[i], format, &minorv[n_minors]); if (n != 1) continue; n_minors++; } qsort(minorv, n_minors, sizeof(int), _tap_minor_cmp); done: *_minorv = minorv; err = 0; out: if (glbuf.gl_pathv) globfree(&glbuf); return err ? : n_minors; fail: if (minorv) free(minorv); goto out; } struct tapdisk { int id; pid_t pid; struct list_head list; }; static int _tap_tapdisk_cmp(const void *a, const void *b) { return ((struct tapdisk*)a)->id - ((struct tapdisk*)b)->id; } int _tap_ctl_find_tapdisks(struct tapdisk **_tapv) { glob_t glbuf = { 0 }; const char *pattern, *format; struct tapdisk *tapv = NULL; int err, i, n_taps = 0; pattern = BLKTAP2_CONTROL_DIR"/"BLKTAP2_CONTROL_SOCKET"*"; format = BLKTAP2_CONTROL_DIR"/"BLKTAP2_CONTROL_SOCKET"%d"; n_taps = 0; tapv = NULL; err = glob(pattern, 0, NULL, &glbuf); switch (err) { case GLOB_NOMATCH: goto done; case GLOB_ABORTED: case GLOB_NOSPACE: err = -errno; EPRINTF("%s: glob failed, err %d", pattern, err); goto fail; } tapv = malloc(sizeof(struct tapdisk) * glbuf.gl_pathc); if (!tapv) { err = -errno; goto fail; } for (i = 0; i < glbuf.gl_pathc; ++i) { struct tapdisk *tap; int n; tap = &tapv[n_taps]; err = sscanf(glbuf.gl_pathv[i], format, &tap->id); if (err != 1) continue; tap->pid = tap_ctl_get_pid(tap->id); if (tap->pid < 0) continue; n_taps++; } qsort(tapv, n_taps, sizeof(struct tapdisk), _tap_tapdisk_cmp); for (i = 0; i < n_taps; ++i) INIT_LIST_HEAD(&tapv[i].list); done: *_tapv = tapv; err = 0; out: if (glbuf.gl_pathv) globfree(&glbuf); return err ? : n_taps; fail: if (tapv) free(tapv); goto out; } struct tapdisk_list { int minor; int state; char *params; struct list_head entry; }; int _tap_ctl_list_tapdisk(int id, struct list_head *_list) { tapdisk_message_t message; struct list_head list; struct tapdisk_list *tl, *next; int err, sfd; err = tap_ctl_connect_id(id, &sfd); if (err) return err; memset(&message, 0, sizeof(message)); message.type = TAPDISK_MESSAGE_LIST; message.cookie = -1; err = tap_ctl_write_message(sfd, &message, 2); if (err) return err; INIT_LIST_HEAD(&list); do { err = tap_ctl_read_message(sfd, &message, 2); if (err) { err = -EPROTO; break; } if (message.u.list.count == 0) break; tl = malloc(sizeof(struct tapdisk_list)); if (!tl) { err = -ENOMEM; break; } tl->minor = message.u.list.minor; tl->state = message.u.list.state; if (message.u.list.path[0] != 0) { tl->params = strndup(message.u.list.path, sizeof(message.u.list.path)); if (!tl->params) { err = -errno; break; } } else tl->params = NULL; list_add(&tl->entry, &list); } while (1); if (err) list_for_each_entry_safe(tl, next, &list, entry) { list_del(&tl->entry); free(tl->params); free(tl); } close(sfd); list_splice(&list, _list); return err; } void _tap_ctl_free_tapdisks(struct tapdisk *tapv, int n_taps) { struct tapdisk *tap; for (tap = tapv; tap < &tapv[n_taps]; ++tap) { struct tapdisk_list *tl, *next; list_for_each_entry_safe(tl, next, &tap->list, entry) { free(tl->params); free(tl); } } free(tapv); } int _tap_list_join3(int n_minors, int *minorv, int n_taps, struct tapdisk *tapv, tap_list_t ***_list) { tap_list_t **list, **_entry, *entry; int i, _m, err; list = tap_ctl_alloc_list(n_minors + n_taps); if (!list) { err = -ENOMEM; goto fail; } _entry = list; for (i = 0; i < n_taps; ++i) { struct tapdisk *tap = &tapv[i]; struct tapdisk_list *tl; /* orphaned tapdisk */ if (list_empty(&tap->list)) { err = init_list(*_entry++, tap->id, tap->pid, -1, -1, NULL); if (err) goto fail; continue; } list_for_each_entry(tl, &tap->list, entry) { err = init_list(*_entry++, tap->id, tap->pid, tl->minor, tl->state, tl->params); if (err) goto fail; if (tl->minor >= 0) { /* clear minor */ for (_m = 0; _m < n_minors; ++_m) { if (minorv[_m] == tl->minor) { minorv[_m] = -1; break; } } } } } /* orphaned minors */ for (_m = 0; _m < n_minors; ++_m) { int minor = minorv[_m]; if (minor >= 0) { err = init_list(*_entry++, -1, -1, minor, -1, NULL); if (err) goto fail; } } /* free extraneous list entries */ for (; *_entry != NULL; ++entry) { free_list(*_entry); *_entry = NULL; } *_list = list; return 0; fail: if (list) tap_ctl_free_list(list); return err; } int tap_ctl_list(tap_list_t ***list) { int n_taps, n_minors, err, *minorv; struct tapdisk *tapv, *tap; n_taps = -1; n_minors = -1; err = n_minors = _tap_ctl_find_minors(&minorv); if (err < 0) goto out; err = n_taps = _tap_ctl_find_tapdisks(&tapv); if (err < 0) goto out; for (tap = tapv; tap < &tapv[n_taps]; ++tap) { err = _tap_ctl_list_tapdisk(tap->id, &tap->list); if (err) goto out; } err = _tap_list_join3(n_minors, minorv, n_taps, tapv, list); out: if (n_taps > 0) _tap_ctl_free_tapdisks(tapv, n_taps); if (n_minors > 0) free(minorv); return err; } int tap_ctl_find(const char *type, const char *path, tap_list_t *tap) { tap_list_t **list, **_entry; int ret = -ENOENT, err; err = tap_ctl_list(&list); if (err) return err; for (_entry = list; *_entry != NULL; ++_entry) { tap_list_t *entry = *_entry; if (type && (!entry->type || strcmp(entry->type, type))) continue; if (path && (!entry->path || strcmp(entry->path, path))) continue; *tap = *entry; tap->type = tap->path = NULL; ret = 0; break; } tap_ctl_free_list(list); return ret; }