Files
xen/tools/blktap2/control/tap-ctl-list.c
T
2016-09-08 16:15:47 +01:00

537 lines
9.6 KiB
C

/*
* 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 <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <glob.h>
#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;
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;
}