Move virStorageBackendRun to vircommand

The only storage-specific parameter is the pool object, which
is only used for passing to the callback function.
This commit is contained in:
Ján Tomko 2014-03-18 15:35:01 +01:00
parent 845255a430
commit f2cc42868e
9 changed files with 360 additions and 347 deletions

View File

@ -1113,6 +1113,8 @@ virCommandRawStatus;
virCommandRequireHandshake; virCommandRequireHandshake;
virCommandRun; virCommandRun;
virCommandRunAsync; virCommandRunAsync;
virCommandRunNul;
virCommandRunRegex;
virCommandSetAppArmorProfile; virCommandSetAppArmorProfile;
virCommandSetDryRun; virCommandSetDryRun;
virCommandSetErrorBuffer; virCommandSetErrorBuffer;

View File

@ -1632,252 +1632,3 @@ virStorageBackendStablePath(virStoragePoolObjPtr pool,
return stablepath; return stablepath;
} }
#ifndef WIN32
/*
* 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(virStoragePoolObjPtr pool,
virCommandPtr cmd,
int nregex,
const char **regex,
int *nvars,
virStorageBackendListVolRegexFunc func,
void *data, const char *prefix)
{
int fd = -1, err, ret = -1;
FILE *list = NULL;
regex_t *reg;
regmatch_t *vars = NULL;
char line[1024];
int maxReg = 0;
size_t i, j;
int totgroups = 0, ngroup = 0, maxvars = 0;
char **groups;
/* Compile all regular expressions */
if (VIR_ALLOC_N(reg, nregex) < 0)
return -1;
for (i = 0; i < nregex; i++) {
err = regcomp(&reg[i], regex[i], REG_EXTENDED);
if (err != 0) {
char error[100];
regerror(err, &reg[i], error, sizeof(error));
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Failed to compile regex %s"), error);
for (j = 0; j < i; j++)
regfree(&reg[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)
goto cleanup;
if (VIR_ALLOC_N(vars, maxvars+1) < 0)
goto cleanup;
virCommandSetOutputFD(cmd, &fd);
if (virCommandRunAsync(cmd, NULL) < 0) {
goto cleanup;
}
if ((list = VIR_FDOPEN(fd, "r")) == NULL) {
virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("cannot read fd"));
goto cleanup;
}
while (fgets(line, sizeof(line), list) != NULL) {
char *p = NULL;
/* Strip trailing newline */
int len = strlen(line);
if (len && line[len-1] == '\n')
line[len-1] = '\0';
/* ignore any command prefix */
if (prefix)
p = STRSKIP(line, prefix);
if (!p)
p = line;
for (i = 0; i <= maxReg && i < nregex; i++) {
if (regexec(&reg[i], p, 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 */
p[vars[j+1].rm_eo] = '\0';
if (VIR_STRDUP(groups[ngroup++], p + vars[j+1].rm_so) < 0)
goto cleanup;
}
/* We're matching on the last regex, so callback time */
if (i == (nregex-1)) {
if (((*func)(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;
}
}
}
}
ret = virCommandWait(cmd, NULL);
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(&reg[i]);
VIR_FREE(reg);
VIR_FORCE_FCLOSE(list);
VIR_FORCE_CLOSE(fd);
return ret;
}
/*
* 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(virStoragePoolObjPtr pool,
virCommandPtr cmd,
size_t n_columns,
virStorageBackendListVolNulFunc func,
void *data)
{
size_t n_tok = 0;
int fd = -1;
FILE *fp = NULL;
char **v;
int ret = -1;
size_t i;
if (n_columns == 0)
return -1;
if (VIR_ALLOC_N(v, n_columns) < 0)
return -1;
for (i = 0; i < n_columns; i++)
v[i] = NULL;
virCommandSetOutputFD(cmd, &fd);
if (virCommandRunAsync(cmd, NULL) < 0) {
goto cleanup;
}
if ((fp = VIR_FDOPEN(fd, "r")) == NULL) {
virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("cannot open file using 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(pool, n_tok, v, data) < 0)
goto cleanup;
break;
}
++n_tok;
if (n_tok == n_columns) {
if (func(pool, n_tok, v, data) < 0)
goto cleanup;
n_tok = 0;
for (i = 0; i < n_columns; i++) {
VIR_FREE(v[i]);
}
}
}
if (feof(fp) < 0) {
virReportSystemError(errno, "%s",
_("read error on pipe"));
goto cleanup;
}
ret = virCommandWait(cmd, NULL);
cleanup:
for (i = 0; i < n_columns; i++)
VIR_FREE(v[i]);
VIR_FREE(v);
VIR_FORCE_FCLOSE(fp);
VIR_FORCE_CLOSE(fd);
return ret;
}
#else /* WIN32 */
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)
{
virReportError(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)
{
virReportError(VIR_ERR_INTERNAL_ERROR,
_("%s not implemented on Win32"), __FUNCTION__);
return -1;
}
#endif /* WIN32 */

View File

@ -159,28 +159,6 @@ char *virStorageBackendStablePath(virStoragePoolObjPtr pool,
const char *devpath, const char *devpath,
bool loop); bool loop);
typedef int (*virStorageBackendListVolRegexFunc)(virStoragePoolObjPtr pool,
char **const groups,
void *data);
typedef int (*virStorageBackendListVolNulFunc)(virStoragePoolObjPtr pool,
size_t n_tokens,
char **const groups,
void *data);
int virStorageBackendRunProgRegex(virStoragePoolObjPtr pool,
virCommandPtr cmd,
int nregex,
const char **regex,
int *nvars,
virStorageBackendListVolRegexFunc func,
void *data, const char *cmd_to_ignore);
int virStorageBackendRunProgNul(virStoragePoolObjPtr pool,
virCommandPtr cmd,
size_t n_columns,
virStorageBackendListVolNulFunc func,
void *data);
virCommandPtr virCommandPtr
virStorageBackendCreateQemuImgCmd(virConnectPtr conn, virStorageBackendCreateQemuImgCmd(virConnectPtr conn,
virStoragePoolObjPtr pool, virStoragePoolObjPtr pool,

View File

@ -188,12 +188,18 @@ virStorageBackendDiskMakeFreeExtent(virStoragePoolObjPtr pool,
} }
struct virStorageBackendDiskPoolVolData {
virStoragePoolObjPtr pool;
virStorageVolDefPtr vol;
};
static int static int
virStorageBackendDiskMakeVol(virStoragePoolObjPtr pool, virStorageBackendDiskMakeVol(size_t ntok ATTRIBUTE_UNUSED,
size_t ntok ATTRIBUTE_UNUSED,
char **const groups, char **const groups,
void *data) void *opaque)
{ {
struct virStorageBackendDiskPoolVolData *data = opaque;
virStoragePoolObjPtr pool = data->pool;
/* /*
* Ignore normal+metadata, and logical+metadata partitions * Ignore normal+metadata, and logical+metadata partitions
* since they're basically internal book-keeping regions * since they're basically internal book-keeping regions
@ -209,7 +215,7 @@ virStorageBackendDiskMakeVol(virStoragePoolObjPtr pool,
/* Remaining data / metadata parts get turn into volumes... */ /* Remaining data / metadata parts get turn into volumes... */
if (STREQ(groups[2], "metadata") || if (STREQ(groups[2], "metadata") ||
STREQ(groups[2], "data")) { STREQ(groups[2], "data")) {
virStorageVolDefPtr vol = data; virStorageVolDefPtr vol = data->vol;
if (vol) { if (vol) {
/* We're searching for a specific vol only */ /* We're searching for a specific vol only */
@ -234,7 +240,6 @@ virStorageBackendDiskMakeVol(virStoragePoolObjPtr pool,
} }
} }
/* To get a list of partitions we run an external helper /* To get a list of partitions we run an external helper
* tool which then uses parted APIs. This is because * tool which then uses parted APIs. This is because
* parted's API is not compatible with libvirt's license * parted's API is not compatible with libvirt's license
@ -259,25 +264,28 @@ virStorageBackendDiskReadPartitions(virStoragePoolObjPtr pool,
virCommandPtr cmd = virCommandNewArgList(PARTHELPER, virCommandPtr cmd = virCommandNewArgList(PARTHELPER,
pool->def->source.devices[0].path, pool->def->source.devices[0].path,
NULL); NULL);
struct virStorageBackendDiskPoolVolData cbdata = {
.pool = pool,
.vol = vol,
};
int ret; int ret;
pool->def->allocation = pool->def->capacity = pool->def->available = 0; pool->def->allocation = pool->def->capacity = pool->def->available = 0;
ret = virStorageBackendRunProgNul(pool, ret = virCommandRunNul(cmd,
cmd, 6,
6, virStorageBackendDiskMakeVol,
virStorageBackendDiskMakeVol, &cbdata);
vol);
virCommandFree(cmd); virCommandFree(cmd);
return ret; return ret;
} }
static int static int
virStorageBackendDiskMakePoolGeometry(virStoragePoolObjPtr pool, virStorageBackendDiskMakePoolGeometry(size_t ntok ATTRIBUTE_UNUSED,
size_t ntok ATTRIBUTE_UNUSED,
char **const groups, char **const groups,
void *data ATTRIBUTE_UNUSED) void *data)
{ {
virStoragePoolObjPtr pool = data;
virStoragePoolSourceDevicePtr device = &(pool->def->source.devices[0]); virStoragePoolSourceDevicePtr device = &(pool->def->source.devices[0]);
if (virStrToLong_i(groups[0], NULL, 0, &device->geometry.cylinders) < 0 || if (virStrToLong_i(groups[0], NULL, 0, &device->geometry.cylinders) < 0 ||
virStrToLong_i(groups[1], NULL, 0, &device->geometry.heads) < 0 || virStrToLong_i(groups[1], NULL, 0, &device->geometry.heads) < 0 ||
@ -299,11 +307,10 @@ virStorageBackendDiskReadGeometry(virStoragePoolObjPtr pool)
NULL); NULL);
int ret; int ret;
ret = virStorageBackendRunProgNul(pool, ret = virCommandRunNul(cmd,
cmd, 3,
3, virStorageBackendDiskMakePoolGeometry,
virStorageBackendDiskMakePoolGeometry, pool);
NULL);
virCommandFree(cmd); virCommandFree(cmd);
return ret; return ret;
} }

View File

@ -202,8 +202,7 @@ struct _virNetfsDiscoverState {
typedef struct _virNetfsDiscoverState virNetfsDiscoverState; typedef struct _virNetfsDiscoverState virNetfsDiscoverState;
static int static int
virStorageBackendFileSystemNetFindPoolSourcesFunc(virStoragePoolObjPtr pool ATTRIBUTE_UNUSED, virStorageBackendFileSystemNetFindPoolSourcesFunc(char **const groups,
char **const groups,
void *data) void *data)
{ {
virNetfsDiscoverState *state = data; virNetfsDiscoverState *state = data;
@ -301,9 +300,9 @@ virStorageBackendFileSystemNetFindPoolSources(virConnectPtr conn ATTRIBUTE_UNUSE
source->hosts[0].name, source->hosts[0].name,
NULL); NULL);
if (virStorageBackendRunProgRegex(NULL, cmd, 1, regexes, vars, if (virCommandRunRegex(cmd, 1, regexes, vars,
virStorageBackendFileSystemNetFindPoolSourcesFunc, virStorageBackendFileSystemNetFindPoolSourcesFunc,
&state, NULL) < 0) &state, NULL) < 0)
goto cleanup; goto cleanup;
retval = virStoragePoolSourceListFormat(&state.list); retval = virStoragePoolSourceListFormat(&state.list);

View File

@ -79,16 +79,19 @@ virStorageBackendISCSIPortal(virStoragePoolSourcePtr source)
return portal; return portal;
} }
struct virStorageBackendISCSISessionData {
char *session;
const char *devpath;
};
static int static int
virStorageBackendISCSIExtractSession(virStoragePoolObjPtr pool, virStorageBackendISCSIExtractSession(char **const groups,
char **const groups, void *opaque)
void *data)
{ {
char **session = data; struct virStorageBackendISCSISessionData *data = opaque;
if (STREQ(groups[1], pool->def->source.devices[0].path)) if (STREQ(groups[1], data->devpath))
return VIR_STRDUP(*session, groups[0]); return VIR_STRDUP(data->session, groups[0]);
return 0; return 0;
} }
@ -109,21 +112,22 @@ virStorageBackendISCSISession(virStoragePoolObjPtr pool,
int vars[] = { int vars[] = {
2, 2,
}; };
char *session = NULL; struct virStorageBackendISCSISessionData cbdata = {
.session = NULL,
.devpath = pool->def->source.devices[0].path
};
virCommandPtr cmd = virCommandNewArgList(ISCSIADM, "--mode", "session", NULL); virCommandPtr cmd = virCommandNewArgList(ISCSIADM, "--mode", "session", NULL);
if (virStorageBackendRunProgRegex(pool, if (virCommandRunRegex(cmd,
cmd, 1,
1, regexes,
regexes, vars,
vars, virStorageBackendISCSIExtractSession,
virStorageBackendISCSIExtractSession, &cbdata, NULL) < 0)
&session, NULL) < 0)
goto cleanup; goto cleanup;
if (session == NULL && if (cbdata.session == NULL && !probe) {
!probe) {
virReportError(VIR_ERR_INTERNAL_ERROR, virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("cannot find session")); "%s", _("cannot find session"));
goto cleanup; goto cleanup;
@ -131,7 +135,7 @@ virStorageBackendISCSISession(virStoragePoolObjPtr pool,
cleanup: cleanup:
virCommandFree(cmd); virCommandFree(cmd);
return session; return cbdata.session;
} }
@ -439,8 +443,7 @@ struct virStorageBackendISCSITargetList {
}; };
static int static int
virStorageBackendISCSIGetTargets(virStoragePoolObjPtr pool ATTRIBUTE_UNUSED, virStorageBackendISCSIGetTargets(char **const groups,
char **const groups,
void *data) void *data)
{ {
struct virStorageBackendISCSITargetList *list = data; struct virStorageBackendISCSITargetList *list = data;
@ -503,13 +506,12 @@ virStorageBackendISCSIScanTargets(const char *portal,
memset(&list, 0, sizeof(list)); memset(&list, 0, sizeof(list));
if (virStorageBackendRunProgRegex(NULL, /* No pool for callback */ if (virCommandRunRegex(cmd,
cmd, 1,
1, regexes,
regexes, vars,
vars, virStorageBackendISCSIGetTargets,
virStorageBackendISCSIGetTargets, &list, NULL) < 0)
&list, NULL) < 0)
goto cleanup; goto cleanup;
for (i = 0; i < list.ntargets; i++) { for (i = 0; i < list.ntargets; i++) {

View File

@ -66,11 +66,17 @@ virStorageBackendLogicalSetActive(virStoragePoolObjPtr pool,
#define VIR_STORAGE_VOL_LOGICAL_SEGTYPE_STRIPED "striped" #define VIR_STORAGE_VOL_LOGICAL_SEGTYPE_STRIPED "striped"
struct virStorageBackendLogicalPoolVolData {
virStoragePoolObjPtr pool;
virStorageVolDefPtr vol;
};
static int static int
virStorageBackendLogicalMakeVol(virStoragePoolObjPtr pool, virStorageBackendLogicalMakeVol(char **const groups,
char **const groups, void *opaque)
void *data)
{ {
struct virStorageBackendLogicalPoolVolData *data = opaque;
virStoragePoolObjPtr pool = data->pool;
virStorageVolDefPtr vol = NULL; virStorageVolDefPtr vol = NULL;
bool is_new_vol = false; bool is_new_vol = false;
unsigned long long offset, size, length; unsigned long long offset, size, length;
@ -96,8 +102,8 @@ virStorageBackendLogicalMakeVol(virStoragePoolObjPtr pool,
return 0; return 0;
/* See if we're only looking for a specific volume */ /* See if we're only looking for a specific volume */
if (data != NULL) { if (data->vol != NULL) {
vol = data; vol = data->vol;
if (STRNEQ(vol->name, groups[0])) if (STRNEQ(vol->name, groups[0]))
return 0; return 0;
} }
@ -299,6 +305,10 @@ virStorageBackendLogicalFindLVs(virStoragePoolObjPtr pool,
}; };
int ret = -1; int ret = -1;
virCommandPtr cmd; virCommandPtr cmd;
struct virStorageBackendLogicalPoolVolData cbdata = {
.pool = pool,
.vol = vol,
};
cmd = virCommandNewArgList(LVS, cmd = virCommandNewArgList(LVS,
"--separator", "#", "--separator", "#",
@ -310,13 +320,13 @@ virStorageBackendLogicalFindLVs(virStoragePoolObjPtr pool,
"lv_name,origin,uuid,devices,segtype,stripes,seg_size,vg_extent_size,size,lv_attr", "lv_name,origin,uuid,devices,segtype,stripes,seg_size,vg_extent_size,size,lv_attr",
pool->def->source.name, pool->def->source.name,
NULL); NULL);
if (virStorageBackendRunProgRegex(pool, if (virCommandRunRegex(cmd,
cmd, 1,
1, regexes,
regexes, vars,
vars, virStorageBackendLogicalMakeVol,
virStorageBackendLogicalMakeVol, &cbdata,
vol, "lvs") < 0) "lvs") < 0)
goto cleanup; goto cleanup;
ret = 0; ret = 0;
@ -326,10 +336,10 @@ cleanup:
} }
static int static int
virStorageBackendLogicalRefreshPoolFunc(virStoragePoolObjPtr pool ATTRIBUTE_UNUSED, virStorageBackendLogicalRefreshPoolFunc(char **const groups,
char **const groups, void *data)
void *data ATTRIBUTE_UNUSED)
{ {
virStoragePoolObjPtr pool = data;
if (virStrToLong_ull(groups[0], NULL, 10, &pool->def->capacity) < 0) if (virStrToLong_ull(groups[0], NULL, 10, &pool->def->capacity) < 0)
return -1; return -1;
if (virStrToLong_ull(groups[1], NULL, 10, &pool->def->available) < 0) if (virStrToLong_ull(groups[1], NULL, 10, &pool->def->available) < 0)
@ -341,8 +351,7 @@ virStorageBackendLogicalRefreshPoolFunc(virStoragePoolObjPtr pool ATTRIBUTE_UNUS
static int static int
virStorageBackendLogicalFindPoolSourcesFunc(virStoragePoolObjPtr pool ATTRIBUTE_UNUSED, virStorageBackendLogicalFindPoolSourcesFunc(char **const groups,
char **const groups,
void *data) void *data)
{ {
virStoragePoolSourceListPtr sourceList = data; virStoragePoolSourceListPtr sourceList = data;
@ -432,9 +441,9 @@ virStorageBackendLogicalFindPoolSources(virConnectPtr conn ATTRIBUTE_UNUSED,
"--noheadings", "--noheadings",
"-o", "pv_name,vg_name", "-o", "pv_name,vg_name",
NULL); NULL);
if (virStorageBackendRunProgRegex(NULL, cmd, 1, regexes, vars, if (virCommandRunRegex(cmd, 1, regexes, vars,
virStorageBackendLogicalFindPoolSourcesFunc, virStorageBackendLogicalFindPoolSourcesFunc,
&sourceList, "pvs") < 0) { &sourceList, "pvs") < 0) {
virCommandFree(cmd); virCommandFree(cmd);
return NULL; return NULL;
} }
@ -593,13 +602,13 @@ virStorageBackendLogicalRefreshPool(virConnectPtr conn ATTRIBUTE_UNUSED,
NULL); NULL);
/* Now get basic volgrp metadata */ /* Now get basic volgrp metadata */
if (virStorageBackendRunProgRegex(pool, if (virCommandRunRegex(cmd,
cmd, 1,
1, regexes,
regexes, vars,
vars, virStorageBackendLogicalRefreshPoolFunc,
virStorageBackendLogicalRefreshPoolFunc, pool,
NULL, "vgs") < 0) "vgs") < 0)
goto cleanup; goto cleanup;
ret = 0; ret = 0;

View File

@ -22,6 +22,7 @@
#include <config.h> #include <config.h>
#include <poll.h> #include <poll.h>
#include <regex.h>
#include <signal.h> #include <signal.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdlib.h> #include <stdlib.h>
@ -2760,3 +2761,247 @@ virCommandSetDryRun(virBufferPtr buf,
dryRunCallback = cb; dryRunCallback = cb;
dryRunOpaque = opaque; dryRunOpaque = opaque;
} }
#ifndef WIN32
/*
* 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
virCommandRunRegex(virCommandPtr cmd,
int nregex,
const char **regex,
int *nvars,
virCommandRunRegexFunc func,
void *data,
const char *prefix)
{
int fd = -1, err, ret = -1;
FILE *list = NULL;
regex_t *reg;
regmatch_t *vars = NULL;
char line[1024];
int maxReg = 0;
size_t i, j;
int totgroups = 0, ngroup = 0, maxvars = 0;
char **groups;
/* Compile all regular expressions */
if (VIR_ALLOC_N(reg, nregex) < 0)
return -1;
for (i = 0; i < nregex; i++) {
err = regcomp(&reg[i], regex[i], REG_EXTENDED);
if (err != 0) {
char error[100];
regerror(err, &reg[i], error, sizeof(error));
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Failed to compile regex %s"), error);
for (j = 0; j < i; j++)
regfree(&reg[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)
goto cleanup;
if (VIR_ALLOC_N(vars, maxvars+1) < 0)
goto cleanup;
virCommandSetOutputFD(cmd, &fd);
if (virCommandRunAsync(cmd, NULL) < 0) {
goto cleanup;
}
if ((list = VIR_FDOPEN(fd, "r")) == NULL) {
virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("cannot read fd"));
goto cleanup;
}
while (fgets(line, sizeof(line), list) != NULL) {
char *p = NULL;
/* Strip trailing newline */
int len = strlen(line);
if (len && line[len-1] == '\n')
line[len-1] = '\0';
/* ignore any command prefix */
if (prefix)
p = STRSKIP(line, prefix);
if (!p)
p = line;
for (i = 0; i <= maxReg && i < nregex; i++) {
if (regexec(&reg[i], p, 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 */
p[vars[j+1].rm_eo] = '\0';
if (VIR_STRDUP(groups[ngroup++], p + vars[j+1].rm_so) < 0)
goto cleanup;
}
/* We're matching on the last regex, so callback time */
if (i == (nregex-1)) {
if (((*func)(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;
}
}
}
}
ret = virCommandWait(cmd, NULL);
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(&reg[i]);
VIR_FREE(reg);
VIR_FORCE_FCLOSE(list);
VIR_FORCE_CLOSE(fd);
return ret;
}
/*
* 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
virCommandRunNul(virCommandPtr cmd,
size_t n_columns,
virCommandRunNulFunc func,
void *data)
{
size_t n_tok = 0;
int fd = -1;
FILE *fp = NULL;
char **v;
int ret = -1;
size_t i;
if (n_columns == 0)
return -1;
if (VIR_ALLOC_N(v, n_columns) < 0)
return -1;
for (i = 0; i < n_columns; i++)
v[i] = NULL;
virCommandSetOutputFD(cmd, &fd);
if (virCommandRunAsync(cmd, NULL) < 0) {
goto cleanup;
}
if ((fp = VIR_FDOPEN(fd, "r")) == NULL) {
virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("cannot open file using 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(n_tok, v, data) < 0)
goto cleanup;
break;
}
++n_tok;
if (n_tok == n_columns) {
if (func(n_tok, v, data) < 0)
goto cleanup;
n_tok = 0;
for (i = 0; i < n_columns; i++) {
VIR_FREE(v[i]);
}
}
}
if (feof(fp) < 0) {
virReportSystemError(errno, "%s",
_("read error on pipe"));
goto cleanup;
}
ret = virCommandWait(cmd, NULL);
cleanup:
for (i = 0; i < n_columns; i++)
VIR_FREE(v[i]);
VIR_FREE(v);
VIR_FORCE_FCLOSE(fp);
VIR_FORCE_CLOSE(fd);
return ret;
}
#else /* WIN32 */
int
virCommandRunRegex(virCommandPtr cmd ATTRIBUTE_UNUSED,
int nregex ATTRIBUTE_UNUSED,
const char **regex ATTRIBUTE_UNUSED,
int *nvars ATTRIBUTE_UNUSED,
virCommandRunRegexFunc func ATTRIBUTE_UNUSED,
void *data ATTRIBUTE_UNUSED,
const char *prefix ATTRIBUTE_UNUSED)
{
virReportError(VIR_ERR_INTERNAL_ERROR,
_("%s not implemented on Win32"), __FUNCTION__);
return -1;
}
int
virCommandRunNul(virCommandPtr cmd ATTRIBUTE_UNUSED,
size_t n_columns ATTRIBUTE_UNUSED,
virCommandRunNulFunc func ATTRIBUTE_UNUSED,
void *data ATTRIBUTE_UNUSED)
{
virReportError(VIR_ERR_INTERNAL_ERROR,
_("%s not implemented on Win32"), __FUNCTION__);
return -1;
}
#endif /* WIN32 */

View File

@ -187,4 +187,24 @@ void virCommandFree(virCommandPtr cmd);
void virCommandDoAsyncIO(virCommandPtr cmd); void virCommandDoAsyncIO(virCommandPtr cmd);
typedef int (*virCommandRunRegexFunc)(char **const groups,
void *data);
typedef int (*virCommandRunNulFunc)(size_t n_tokens,
char **const groups,
void *data);
int virCommandRunRegex(virCommandPtr cmd,
int nregex,
const char **regex,
int *nvars,
virCommandRunRegexFunc func,
void *data,
const char *cmd_to_ignore);
int virCommandRunNul(virCommandPtr cmd,
size_t n_columns,
virCommandRunNulFunc func,
void *data);
#endif /* __VIR_COMMAND_H__ */ #endif /* __VIR_COMMAND_H__ */