mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2025-01-06 04:55:22 +00:00
eeff3cdcbc
iSCSI volumes. This is implemented in the virStorageBackendUpdateVolInfoFD function, so all future callers will automatically benefit. This is a somewhat large patch because the conversion of the virStorageBackendPartTableTypeToString necessitated a change to the formatToString and formatFromString function pointers, which caused fallout in other places in the storage stuff. The good news is that most of these callers are now converted over to the VIR_ENUM_IMPL, which means a lot of redundant code is now gone. Signed-off-by: Chris Lalancette <clalance@redhat.com>
730 lines
22 KiB
C
730 lines
22 KiB
C
/*
|
|
* storage_backend.c: internal storage driver backend contract
|
|
*
|
|
* Copyright (C) 2007-2008 Red Hat, Inc.
|
|
* Copyright (C) 2007-2008 Daniel P. Berrange
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*
|
|
* Author: Daniel P. Berrange <berrange@redhat.com>
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#include <string.h>
|
|
#if HAVE_REGEX_H
|
|
#include <regex.h>
|
|
#endif
|
|
#include <sys/types.h>
|
|
#if HAVE_SYS_WAIT_H
|
|
#include <sys/wait.h>
|
|
#endif
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <stdint.h>
|
|
#include <sys/stat.h>
|
|
#include <dirent.h>
|
|
|
|
#if HAVE_SELINUX
|
|
#include <selinux/selinux.h>
|
|
#endif
|
|
|
|
#include "internal.h"
|
|
#include "util.h"
|
|
#include "memory.h"
|
|
|
|
#include "storage_backend.h"
|
|
|
|
#if WITH_STORAGE_LVM
|
|
#include "storage_backend_logical.h"
|
|
#endif
|
|
#if WITH_STORAGE_ISCSI
|
|
#include "storage_backend_iscsi.h"
|
|
#endif
|
|
#if WITH_STORAGE_DISK
|
|
#include "storage_backend_disk.h"
|
|
#endif
|
|
#if WITH_STORAGE_DIR
|
|
#include "storage_backend_fs.h"
|
|
#endif
|
|
|
|
VIR_ENUM_IMPL(virStorageBackendPartTable,
|
|
VIR_STORAGE_POOL_DISK_LAST,
|
|
"unknown", "dos", "dvh", "gpt",
|
|
"mac", "bsd", "pc98", "sun", "lvm2");
|
|
|
|
static virStorageBackendPtr backends[] = {
|
|
#if WITH_STORAGE_DIR
|
|
&virStorageBackendDirectory,
|
|
#endif
|
|
#if WITH_STORAGE_FS
|
|
&virStorageBackendFileSystem,
|
|
&virStorageBackendNetFileSystem,
|
|
#endif
|
|
#if WITH_STORAGE_LVM
|
|
&virStorageBackendLogical,
|
|
#endif
|
|
#if WITH_STORAGE_ISCSI
|
|
&virStorageBackendISCSI,
|
|
#endif
|
|
#if WITH_STORAGE_DISK
|
|
&virStorageBackendDisk,
|
|
#endif
|
|
};
|
|
|
|
|
|
virStorageBackendPtr
|
|
virStorageBackendForType(int type) {
|
|
unsigned int i;
|
|
for (i = 0 ; i < (sizeof(backends)/sizeof(backends[0])) ; i++)
|
|
if (backends[i]->type == type)
|
|
return backends[i];
|
|
|
|
virStorageReportError(NULL, VIR_ERR_INTERNAL_ERROR,
|
|
_("missing backend for pool type %d"), type);
|
|
return NULL;
|
|
}
|
|
|
|
virStorageBackendPoolOptionsPtr
|
|
virStorageBackendPoolOptionsForType(int type) {
|
|
virStorageBackendPtr backend = virStorageBackendForType(type);
|
|
if (backend == NULL)
|
|
return NULL;
|
|
return &backend->poolOptions;
|
|
}
|
|
|
|
virStorageBackendVolOptionsPtr
|
|
virStorageBackendVolOptionsForType(int type) {
|
|
virStorageBackendPtr backend = virStorageBackendForType(type);
|
|
if (backend == NULL)
|
|
return NULL;
|
|
return &backend->volOptions;
|
|
}
|
|
|
|
|
|
int
|
|
virStorageBackendFromString(const char *type) {
|
|
if (STREQ(type, "dir"))
|
|
return VIR_STORAGE_POOL_DIR;
|
|
#if WITH_STORAGE_FS
|
|
if (STREQ(type, "fs"))
|
|
return VIR_STORAGE_POOL_FS;
|
|
if (STREQ(type, "netfs"))
|
|
return VIR_STORAGE_POOL_NETFS;
|
|
#endif
|
|
#if WITH_STORAGE_LVM
|
|
if (STREQ(type, "logical"))
|
|
return VIR_STORAGE_POOL_LOGICAL;
|
|
#endif
|
|
#if WITH_STORAGE_ISCSI
|
|
if (STREQ(type, "iscsi"))
|
|
return VIR_STORAGE_POOL_ISCSI;
|
|
#endif
|
|
#if WITH_STORAGE_DISK
|
|
if (STREQ(type, "disk"))
|
|
return VIR_STORAGE_POOL_DISK;
|
|
#endif
|
|
|
|
virStorageReportError(NULL, VIR_ERR_INTERNAL_ERROR,
|
|
_("unknown storage backend type %s"), type);
|
|
return -1;
|
|
}
|
|
|
|
const char *
|
|
virStorageBackendToString(int type) {
|
|
switch (type) {
|
|
case VIR_STORAGE_POOL_DIR:
|
|
return "dir";
|
|
#if WITH_STORAGE_FS
|
|
case VIR_STORAGE_POOL_FS:
|
|
return "fs";
|
|
case VIR_STORAGE_POOL_NETFS:
|
|
return "netfs";
|
|
#endif
|
|
#if WITH_STORAGE_LVM
|
|
case VIR_STORAGE_POOL_LOGICAL:
|
|
return "logical";
|
|
#endif
|
|
#if WITH_STORAGE_ISCSI
|
|
case VIR_STORAGE_POOL_ISCSI:
|
|
return "iscsi";
|
|
#endif
|
|
#if WITH_STORAGE_DISK
|
|
case VIR_STORAGE_POOL_DISK:
|
|
return "disk";
|
|
#endif
|
|
}
|
|
|
|
virStorageReportError(NULL, VIR_ERR_INTERNAL_ERROR,
|
|
_("unknown storage backend type %d"), type);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
int
|
|
virStorageBackendUpdateVolInfo(virConnectPtr conn,
|
|
virStorageVolDefPtr vol,
|
|
int withCapacity)
|
|
{
|
|
int ret, fd;
|
|
|
|
if ((fd = open(vol->target.path, O_RDONLY)) < 0) {
|
|
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
|
_("cannot open volume '%s': %s"),
|
|
vol->target.path, strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
ret = virStorageBackendUpdateVolInfoFD(conn,
|
|
vol,
|
|
fd,
|
|
withCapacity);
|
|
|
|
close(fd);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static struct diskType const disk_types[] = {
|
|
{ VIR_STORAGE_POOL_DISK_LVM2, 0x218, 8, 0x31303020324D564CULL },
|
|
{ VIR_STORAGE_POOL_DISK_GPT, 0x200, 8, 0x5452415020494645ULL },
|
|
{ VIR_STORAGE_POOL_DISK_DVH, 0x0, 4, 0x41A9E50BULL },
|
|
{ VIR_STORAGE_POOL_DISK_MAC, 0x0, 2, 0x5245ULL },
|
|
{ VIR_STORAGE_POOL_DISK_BSD, 0x40, 4, 0x82564557ULL },
|
|
{ VIR_STORAGE_POOL_DISK_SUN, 0x1fc, 2, 0xBEDAULL },
|
|
/*
|
|
* NOTE: pc98 is funky; the actual signature is 0x55AA (just like dos), so
|
|
* we can't use that. At the moment I'm relying on the "dummy" IPL
|
|
* bootloader data that comes from parted. Luckily, the chances of running
|
|
* into a pc98 machine running libvirt are approximately nil.
|
|
*/
|
|
/*{ 0x1fe, 2, 0xAA55UL },*/
|
|
{ VIR_STORAGE_POOL_DISK_PC98, 0x0, 8, 0x314C5049000000CBULL },
|
|
/*
|
|
* NOTE: the order is important here; some other disk types (like GPT and
|
|
* and PC98) also have 0x55AA at this offset. For that reason, the DOS
|
|
* one must be the last one.
|
|
*/
|
|
{ VIR_STORAGE_POOL_DISK_DOS, 0x1fe, 2, 0xAA55ULL },
|
|
{ -1, 0x0, 0, 0x0ULL },
|
|
};
|
|
|
|
int
|
|
virStorageBackendUpdateVolInfoFD(virConnectPtr conn,
|
|
virStorageVolDefPtr vol,
|
|
int fd,
|
|
int withCapacity)
|
|
{
|
|
struct stat sb;
|
|
#if HAVE_SELINUX
|
|
security_context_t filecon = NULL;
|
|
#endif
|
|
|
|
if (fstat(fd, &sb) < 0) {
|
|
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
|
_("cannot stat file '%s': %s"),
|
|
vol->target.path, strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
if (!S_ISREG(sb.st_mode) &&
|
|
!S_ISCHR(sb.st_mode) &&
|
|
!S_ISBLK(sb.st_mode))
|
|
return -2;
|
|
|
|
if (S_ISREG(sb.st_mode)) {
|
|
#ifndef __MINGW32__
|
|
vol->allocation = (unsigned long long)sb.st_blocks *
|
|
(unsigned long long)sb.st_blksize;
|
|
#else
|
|
vol->allocation = sb.st_size;
|
|
#endif
|
|
/* Regular files may be sparse, so logical size (capacity) is not same
|
|
* as actual allocation above
|
|
*/
|
|
if (withCapacity)
|
|
vol->capacity = sb.st_size;
|
|
} else {
|
|
off_t end;
|
|
/* XXX this is POSIX compliant, but doesn't work for for CHAR files,
|
|
* only BLOCK. There is a Linux specific ioctl() for getting
|
|
* size of both CHAR / BLOCK devices we should check for in
|
|
* configure
|
|
*/
|
|
end = lseek(fd, 0, SEEK_END);
|
|
if (end == (off_t)-1) {
|
|
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
|
_("cannot seek to end of file '%s':%s"),
|
|
vol->target.path, strerror(errno));
|
|
return -1;
|
|
}
|
|
vol->allocation = end;
|
|
if (withCapacity) vol->capacity = end;
|
|
}
|
|
|
|
/* make sure to set the target format "unknown" to begin with */
|
|
vol->target.format = VIR_STORAGE_POOL_DISK_UNKNOWN;
|
|
|
|
if (S_ISBLK(sb.st_mode)) {
|
|
off_t start;
|
|
int i;
|
|
unsigned char buffer[1024];
|
|
ssize_t bytes;
|
|
|
|
start = lseek(fd, 0, SEEK_SET);
|
|
if (start < 0) {
|
|
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
|
_("cannot seek to beginning of file '%s':%s"),
|
|
vol->target.path, strerror(errno));
|
|
return -1;
|
|
}
|
|
bytes = saferead(fd, buffer, sizeof(buffer));
|
|
if (bytes < 0) {
|
|
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
|
_("cannot read beginning of file '%s':%s"),
|
|
vol->target.path, strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0; disk_types[i].part_table_type != -1; i++) {
|
|
if (disk_types[i].offset + disk_types[i].length > bytes)
|
|
continue;
|
|
if (memcmp(buffer+disk_types[i].offset, &disk_types[i].magic,
|
|
disk_types[i].length) == 0) {
|
|
vol->target.format = disk_types[i].part_table_type;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
vol->target.perms.mode = sb.st_mode;
|
|
vol->target.perms.uid = sb.st_uid;
|
|
vol->target.perms.gid = sb.st_gid;
|
|
|
|
VIR_FREE(vol->target.perms.label);
|
|
|
|
#if HAVE_SELINUX
|
|
if (fgetfilecon(fd, &filecon) == -1) {
|
|
if (errno != ENODATA && errno != ENOTSUP) {
|
|
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
|
_("cannot get file context of %s: %s"),
|
|
vol->target.path, strerror(errno));
|
|
return -1;
|
|
} else {
|
|
vol->target.perms.label = NULL;
|
|
}
|
|
} else {
|
|
vol->target.perms.label = strdup(filecon);
|
|
if (vol->target.perms.label == NULL) {
|
|
virStorageReportError(conn, VIR_ERR_NO_MEMORY, "%s", _("context"));
|
|
return -1;
|
|
}
|
|
freecon(filecon);
|
|
}
|
|
#else
|
|
vol->target.perms.label = NULL;
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Given a volume path directly in /dev/XXX, iterate over the
|
|
* entries in the directory pool->def->target.path and find the
|
|
* first symlink pointing to the volume path.
|
|
*
|
|
* If, the target.path is /dev/, then return the original volume
|
|
* path.
|
|
*
|
|
* If no symlink is found, then return the original volume path
|
|
*
|
|
* Typically target.path is one of the /dev/disk/by-XXX dirs
|
|
* with stable paths.
|
|
*/
|
|
char *
|
|
virStorageBackendStablePath(virConnectPtr conn,
|
|
virStoragePoolObjPtr pool,
|
|
char *devpath)
|
|
{
|
|
DIR *dh;
|
|
struct dirent *dent;
|
|
|
|
/* Short circuit if pool has no target, or if its /dev */
|
|
if (pool->def->target.path == NULL ||
|
|
STREQ(pool->def->target.path, "/dev") ||
|
|
STREQ(pool->def->target.path, "/dev/"))
|
|
return devpath;
|
|
|
|
/* The pool is pointing somewhere like /dev/disk/by-path
|
|
* or /dev/disk/by-id, so we need to check all symlinks in
|
|
* the target directory and figure out which one points
|
|
* to this device node
|
|
*/
|
|
if ((dh = opendir(pool->def->target.path)) == NULL) {
|
|
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
|
_("cannot read dir %s: %s"),
|
|
pool->def->target.path,
|
|
strerror(errno));
|
|
return NULL;
|
|
}
|
|
|
|
while ((dent = readdir(dh)) != NULL) {
|
|
char *stablepath;
|
|
if (dent->d_name[0] == '.')
|
|
continue;
|
|
|
|
if (VIR_ALLOC_N(stablepath, strlen(pool->def->target.path) +
|
|
1 + strlen(dent->d_name) + 1) < 0) {
|
|
virStorageReportError(conn, VIR_ERR_NO_MEMORY, "%s", _("path"));
|
|
closedir(dh);
|
|
return NULL;
|
|
}
|
|
|
|
strcpy(stablepath, pool->def->target.path);
|
|
strcat(stablepath, "/");
|
|
strcat(stablepath, dent->d_name);
|
|
|
|
if (virFileLinkPointsTo(stablepath, devpath)) {
|
|
closedir(dh);
|
|
return stablepath;
|
|
}
|
|
|
|
VIR_FREE(stablepath);
|
|
}
|
|
|
|
closedir(dh);
|
|
|
|
/* Couldn't find any matching stable link so give back
|
|
* the original non-stable dev path
|
|
*/
|
|
return devpath;
|
|
}
|
|
|
|
|
|
#ifndef __MINGW32__
|
|
/*
|
|
* Run an external program.
|
|
*
|
|
* Read its output and apply a series of regexes to each line
|
|
* When the entire set of regexes has matched consecutively
|
|
* then run a callback passing in all the matches
|
|
*/
|
|
int
|
|
virStorageBackendRunProgRegex(virConnectPtr conn,
|
|
virStoragePoolObjPtr pool,
|
|
const char *const*prog,
|
|
int nregex,
|
|
const char **regex,
|
|
int *nvars,
|
|
virStorageBackendListVolRegexFunc func,
|
|
void *data,
|
|
int *outexit)
|
|
{
|
|
int child = 0, fd = -1, exitstatus, err, failed = 1;
|
|
FILE *list = NULL;
|
|
regex_t *reg;
|
|
regmatch_t *vars = NULL;
|
|
char line[1024];
|
|
int maxReg = 0, i, j;
|
|
int totgroups = 0, ngroup = 0, maxvars = 0;
|
|
char **groups;
|
|
|
|
/* Compile all regular expressions */
|
|
if (VIR_ALLOC_N(reg, nregex) < 0) {
|
|
virStorageReportError(conn, VIR_ERR_NO_MEMORY, "%s", _("regex"));
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0 ; i < nregex ; i++) {
|
|
err = regcomp(®[i], regex[i], REG_EXTENDED);
|
|
if (err != 0) {
|
|
char error[100];
|
|
regerror(err, ®[i], error, sizeof(error));
|
|
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
|
_("Failed to compile regex %s"), error);
|
|
for (j = 0 ; j <= i ; j++)
|
|
regfree(®[j]);
|
|
VIR_FREE(reg);
|
|
return -1;
|
|
}
|
|
|
|
totgroups += nvars[i];
|
|
if (nvars[i] > maxvars)
|
|
maxvars = nvars[i];
|
|
|
|
}
|
|
|
|
/* Storage for matched variables */
|
|
if (VIR_ALLOC_N(groups, totgroups) < 0) {
|
|
virStorageReportError(conn, VIR_ERR_NO_MEMORY,
|
|
"%s", _("regex groups"));
|
|
goto cleanup;
|
|
}
|
|
if (VIR_ALLOC_N(vars, maxvars+1) < 0) {
|
|
virStorageReportError(conn, VIR_ERR_NO_MEMORY,
|
|
"%s", _("regex groups"));
|
|
goto cleanup;
|
|
}
|
|
|
|
|
|
/* Run the program and capture its output */
|
|
if (virExec(conn, prog, NULL, NULL,
|
|
&child, -1, &fd, NULL, VIR_EXEC_NONE) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if ((list = fdopen(fd, "r")) == NULL) {
|
|
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
|
"%s", _("cannot read fd"));
|
|
goto cleanup;
|
|
}
|
|
|
|
while (fgets(line, sizeof(line), list) != NULL) {
|
|
/* Strip trailing newline */
|
|
int len = strlen(line);
|
|
if (len && line[len-1] == '\n')
|
|
line[len-1] = '\0';
|
|
|
|
for (i = 0 ; i <= maxReg && i < nregex ; i++) {
|
|
if (regexec(®[i], line, nvars[i]+1, vars, 0) == 0) {
|
|
maxReg++;
|
|
|
|
if (i == 0)
|
|
ngroup = 0;
|
|
|
|
/* NULL terminate each captured group in the line */
|
|
for (j = 0 ; j < nvars[i] ; j++) {
|
|
/* NB vars[0] is the full pattern, so we offset j by 1 */
|
|
line[vars[j+1].rm_eo] = '\0';
|
|
if ((groups[ngroup++] =
|
|
strdup(line + vars[j+1].rm_so)) == NULL) {
|
|
virStorageReportError(conn, VIR_ERR_NO_MEMORY,
|
|
"%s", _("regex groups"));
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
/* We're matching on the last regex, so callback time */
|
|
if (i == (nregex-1)) {
|
|
if (((*func)(conn, pool, groups, data)) < 0)
|
|
goto cleanup;
|
|
|
|
/* Release matches & restart to matching the first regex */
|
|
for (j = 0 ; j < totgroups ; j++)
|
|
VIR_FREE(groups[j]);
|
|
maxReg = 0;
|
|
ngroup = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
failed = 0;
|
|
|
|
cleanup:
|
|
if (groups) {
|
|
for (j = 0 ; j < totgroups ; j++)
|
|
VIR_FREE(groups[j]);
|
|
VIR_FREE(groups);
|
|
}
|
|
VIR_FREE(vars);
|
|
|
|
for (i = 0 ; i < nregex ; i++)
|
|
regfree(®[i]);
|
|
|
|
VIR_FREE(reg);
|
|
|
|
if (list)
|
|
fclose(list);
|
|
else
|
|
close(fd);
|
|
|
|
while ((err = waitpid(child, &exitstatus, 0) == -1) && errno == EINTR);
|
|
|
|
/* Don't bother checking exit status if we already failed */
|
|
if (failed)
|
|
return -1;
|
|
|
|
if (err == -1) {
|
|
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
|
_("failed to wait for command: %s"),
|
|
strerror(errno));
|
|
return -1;
|
|
} else {
|
|
if (WIFEXITED(exitstatus)) {
|
|
if (outexit != NULL)
|
|
*outexit = WEXITSTATUS(exitstatus);
|
|
} else {
|
|
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
|
"%s", _("command did not exit cleanly"));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Run an external program and read from its standard output
|
|
* a stream of tokens from IN_STREAM, applying FUNC to
|
|
* each successive sequence of N_COLUMNS tokens.
|
|
* If FUNC returns < 0, stop processing input and return -1.
|
|
* Return -1 if N_COLUMNS == 0.
|
|
* Return -1 upon memory allocation error.
|
|
* If the number of input tokens is not a multiple of N_COLUMNS,
|
|
* then the final FUNC call will specify a number smaller than N_COLUMNS.
|
|
* If there are no input tokens (empty input), call FUNC with N_COLUMNS == 0.
|
|
*/
|
|
int
|
|
virStorageBackendRunProgNul(virConnectPtr conn,
|
|
virStoragePoolObjPtr pool,
|
|
const char **prog,
|
|
size_t n_columns,
|
|
virStorageBackendListVolNulFunc func,
|
|
void *data)
|
|
{
|
|
size_t n_tok = 0;
|
|
int child = 0, fd = -1, exitstatus;
|
|
FILE *fp = NULL;
|
|
char **v;
|
|
int err = -1;
|
|
int w_err;
|
|
int i;
|
|
|
|
if (n_columns == 0)
|
|
return -1;
|
|
|
|
if (VIR_ALLOC_N(v, n_columns) < 0) {
|
|
virStorageReportError(conn, VIR_ERR_NO_MEMORY,
|
|
"%s", _("n_columns too large"));
|
|
return -1;
|
|
}
|
|
for (i = 0; i < n_columns; i++)
|
|
v[i] = NULL;
|
|
|
|
/* Run the program and capture its output */
|
|
if (virExec(conn, prog, NULL, NULL,
|
|
&child, -1, &fd, NULL, VIR_EXEC_NONE) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if ((fp = fdopen(fd, "r")) == NULL) {
|
|
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
|
"%s", _("cannot read fd"));
|
|
goto cleanup;
|
|
}
|
|
|
|
while (1) {
|
|
char *buf = NULL;
|
|
size_t buf_len = 0;
|
|
/* Be careful: even when it returns -1,
|
|
this use of getdelim allocates memory. */
|
|
ssize_t tok_len = getdelim (&buf, &buf_len, 0, fp);
|
|
v[n_tok] = buf;
|
|
if (tok_len < 0) {
|
|
/* Maybe EOF, maybe an error.
|
|
If n_tok > 0, then we know it's an error. */
|
|
if (n_tok && func (conn, pool, n_tok, v, data) < 0)
|
|
goto cleanup;
|
|
break;
|
|
}
|
|
++n_tok;
|
|
if (n_tok == n_columns) {
|
|
if (func (conn, pool, n_tok, v, data) < 0)
|
|
goto cleanup;
|
|
n_tok = 0;
|
|
for (i = 0; i < n_columns; i++) {
|
|
free (v[i]);
|
|
v[i] = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (feof (fp))
|
|
err = 0;
|
|
else
|
|
virStorageReportError (conn, VIR_ERR_INTERNAL_ERROR,
|
|
_("read error: %s"), strerror (errno));
|
|
|
|
cleanup:
|
|
for (i = 0; i < n_columns; i++)
|
|
free (v[i]);
|
|
free (v);
|
|
|
|
if (fp)
|
|
fclose (fp);
|
|
else
|
|
close (fd);
|
|
|
|
while ((w_err = waitpid (child, &exitstatus, 0) == -1) && errno == EINTR)
|
|
/* empty */ ;
|
|
|
|
/* Don't bother checking exit status if we already failed */
|
|
if (err < 0)
|
|
return -1;
|
|
|
|
if (w_err == -1) {
|
|
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
|
_("failed to wait for command: %s"),
|
|
strerror(errno));
|
|
return -1;
|
|
} else {
|
|
if (WIFEXITED(exitstatus)) {
|
|
if (WEXITSTATUS(exitstatus) != 0) {
|
|
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
|
_("non-zero exit status from command %d"),
|
|
WEXITSTATUS(exitstatus));
|
|
return -1;
|
|
}
|
|
} else {
|
|
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
|
"%s", _("command did not exit cleanly"));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#else
|
|
|
|
int
|
|
virStorageBackendRunProgRegex(virConnectPtr conn,
|
|
virStoragePoolObjPtr pool ATTRIBUTE_UNUSED,
|
|
const char *const*prog ATTRIBUTE_UNUSED,
|
|
int nregex ATTRIBUTE_UNUSED,
|
|
const char **regex ATTRIBUTE_UNUSED,
|
|
int *nvars ATTRIBUTE_UNUSED,
|
|
virStorageBackendListVolRegexFunc func ATTRIBUTE_UNUSED,
|
|
void *data ATTRIBUTE_UNUSED,
|
|
int *outexit ATTRIBUTE_UNUSED)
|
|
{
|
|
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, _("%s not implemented on Win32"), __FUNCTION__);
|
|
return -1;
|
|
}
|
|
|
|
int
|
|
virStorageBackendRunProgNul(virConnectPtr conn,
|
|
virStoragePoolObjPtr pool ATTRIBUTE_UNUSED,
|
|
const char **prog ATTRIBUTE_UNUSED,
|
|
size_t n_columns ATTRIBUTE_UNUSED,
|
|
virStorageBackendListVolNulFunc func ATTRIBUTE_UNUSED,
|
|
void *data ATTRIBUTE_UNUSED)
|
|
{
|
|
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, _("%s not implemented on Win32"), __FUNCTION__);
|
|
return -1;
|
|
}
|
|
#endif
|