From f2cc42868e440c795516ec2fd6a07cd759381492 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A1n=20Tomko?= Date: Tue, 18 Mar 2014 15:35:01 +0100 Subject: [PATCH] Move virStorageBackendRun to vircommand The only storage-specific parameter is the pool object, which is only used for passing to the callback function. --- src/libvirt_private.syms | 2 + src/storage/storage_backend.c | 249 -------------------------- src/storage/storage_backend.h | 22 --- src/storage/storage_backend_disk.c | 43 +++-- src/storage/storage_backend_fs.c | 9 +- src/storage/storage_backend_iscsi.c | 54 +++--- src/storage/storage_backend_logical.c | 63 ++++--- src/util/vircommand.c | 245 +++++++++++++++++++++++++ src/util/vircommand.h | 20 +++ 9 files changed, 360 insertions(+), 347 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 3baf766bf8..c7e024d942 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1113,6 +1113,8 @@ virCommandRawStatus; virCommandRequireHandshake; virCommandRun; virCommandRunAsync; +virCommandRunNul; +virCommandRunRegex; virCommandSetAppArmorProfile; virCommandSetDryRun; virCommandSetErrorBuffer; diff --git a/src/storage/storage_backend.c b/src/storage/storage_backend.c index d14e633e70..b1421ecf5f 100644 --- a/src/storage/storage_backend.c +++ b/src/storage/storage_backend.c @@ -1632,252 +1632,3 @@ virStorageBackendStablePath(virStoragePoolObjPtr pool, 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(®[i], regex[i], REG_EXTENDED); - if (err != 0) { - char error[100]; - regerror(err, ®[i], error, sizeof(error)); - virReportError(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) - 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(®[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(®[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 */ diff --git a/src/storage/storage_backend.h b/src/storage/storage_backend.h index 5314411ed3..aaa17a05e9 100644 --- a/src/storage/storage_backend.h +++ b/src/storage/storage_backend.h @@ -159,28 +159,6 @@ char *virStorageBackendStablePath(virStoragePoolObjPtr pool, const char *devpath, 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 virStorageBackendCreateQemuImgCmd(virConnectPtr conn, virStoragePoolObjPtr pool, diff --git a/src/storage/storage_backend_disk.c b/src/storage/storage_backend_disk.c index 05799a3d85..81201fd8ea 100644 --- a/src/storage/storage_backend_disk.c +++ b/src/storage/storage_backend_disk.c @@ -188,12 +188,18 @@ virStorageBackendDiskMakeFreeExtent(virStoragePoolObjPtr pool, } +struct virStorageBackendDiskPoolVolData { + virStoragePoolObjPtr pool; + virStorageVolDefPtr vol; +}; + static int -virStorageBackendDiskMakeVol(virStoragePoolObjPtr pool, - size_t ntok ATTRIBUTE_UNUSED, +virStorageBackendDiskMakeVol(size_t ntok ATTRIBUTE_UNUSED, char **const groups, - void *data) + void *opaque) { + struct virStorageBackendDiskPoolVolData *data = opaque; + virStoragePoolObjPtr pool = data->pool; /* * Ignore normal+metadata, and logical+metadata partitions * since they're basically internal book-keeping regions @@ -209,7 +215,7 @@ virStorageBackendDiskMakeVol(virStoragePoolObjPtr pool, /* Remaining data / metadata parts get turn into volumes... */ if (STREQ(groups[2], "metadata") || STREQ(groups[2], "data")) { - virStorageVolDefPtr vol = data; + virStorageVolDefPtr vol = data->vol; if (vol) { /* 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 * tool which then uses parted APIs. This is because * parted's API is not compatible with libvirt's license @@ -259,25 +264,28 @@ virStorageBackendDiskReadPartitions(virStoragePoolObjPtr pool, virCommandPtr cmd = virCommandNewArgList(PARTHELPER, pool->def->source.devices[0].path, NULL); + struct virStorageBackendDiskPoolVolData cbdata = { + .pool = pool, + .vol = vol, + }; int ret; pool->def->allocation = pool->def->capacity = pool->def->available = 0; - ret = virStorageBackendRunProgNul(pool, - cmd, - 6, - virStorageBackendDiskMakeVol, - vol); + ret = virCommandRunNul(cmd, + 6, + virStorageBackendDiskMakeVol, + &cbdata); virCommandFree(cmd); return ret; } static int -virStorageBackendDiskMakePoolGeometry(virStoragePoolObjPtr pool, - size_t ntok ATTRIBUTE_UNUSED, +virStorageBackendDiskMakePoolGeometry(size_t ntok ATTRIBUTE_UNUSED, char **const groups, - void *data ATTRIBUTE_UNUSED) + void *data) { + virStoragePoolObjPtr pool = data; virStoragePoolSourceDevicePtr device = &(pool->def->source.devices[0]); if (virStrToLong_i(groups[0], NULL, 0, &device->geometry.cylinders) < 0 || virStrToLong_i(groups[1], NULL, 0, &device->geometry.heads) < 0 || @@ -299,11 +307,10 @@ virStorageBackendDiskReadGeometry(virStoragePoolObjPtr pool) NULL); int ret; - ret = virStorageBackendRunProgNul(pool, - cmd, - 3, - virStorageBackendDiskMakePoolGeometry, - NULL); + ret = virCommandRunNul(cmd, + 3, + virStorageBackendDiskMakePoolGeometry, + pool); virCommandFree(cmd); return ret; } diff --git a/src/storage/storage_backend_fs.c b/src/storage/storage_backend_fs.c index 722193ba87..a9271e26b4 100644 --- a/src/storage/storage_backend_fs.c +++ b/src/storage/storage_backend_fs.c @@ -202,8 +202,7 @@ struct _virNetfsDiscoverState { typedef struct _virNetfsDiscoverState virNetfsDiscoverState; static int -virStorageBackendFileSystemNetFindPoolSourcesFunc(virStoragePoolObjPtr pool ATTRIBUTE_UNUSED, - char **const groups, +virStorageBackendFileSystemNetFindPoolSourcesFunc(char **const groups, void *data) { virNetfsDiscoverState *state = data; @@ -301,9 +300,9 @@ virStorageBackendFileSystemNetFindPoolSources(virConnectPtr conn ATTRIBUTE_UNUSE source->hosts[0].name, NULL); - if (virStorageBackendRunProgRegex(NULL, cmd, 1, regexes, vars, - virStorageBackendFileSystemNetFindPoolSourcesFunc, - &state, NULL) < 0) + if (virCommandRunRegex(cmd, 1, regexes, vars, + virStorageBackendFileSystemNetFindPoolSourcesFunc, + &state, NULL) < 0) goto cleanup; retval = virStoragePoolSourceListFormat(&state.list); diff --git a/src/storage/storage_backend_iscsi.c b/src/storage/storage_backend_iscsi.c index 0feeb5f436..20fc0e6e48 100644 --- a/src/storage/storage_backend_iscsi.c +++ b/src/storage/storage_backend_iscsi.c @@ -79,16 +79,19 @@ virStorageBackendISCSIPortal(virStoragePoolSourcePtr source) return portal; } +struct virStorageBackendISCSISessionData { + char *session; + const char *devpath; +}; static int -virStorageBackendISCSIExtractSession(virStoragePoolObjPtr pool, - char **const groups, - void *data) +virStorageBackendISCSIExtractSession(char **const groups, + void *opaque) { - char **session = data; + struct virStorageBackendISCSISessionData *data = opaque; - if (STREQ(groups[1], pool->def->source.devices[0].path)) - return VIR_STRDUP(*session, groups[0]); + if (STREQ(groups[1], data->devpath)) + return VIR_STRDUP(data->session, groups[0]); return 0; } @@ -109,21 +112,22 @@ virStorageBackendISCSISession(virStoragePoolObjPtr pool, int vars[] = { 2, }; - char *session = NULL; + struct virStorageBackendISCSISessionData cbdata = { + .session = NULL, + .devpath = pool->def->source.devices[0].path + }; virCommandPtr cmd = virCommandNewArgList(ISCSIADM, "--mode", "session", NULL); - if (virStorageBackendRunProgRegex(pool, - cmd, - 1, - regexes, - vars, - virStorageBackendISCSIExtractSession, - &session, NULL) < 0) + if (virCommandRunRegex(cmd, + 1, + regexes, + vars, + virStorageBackendISCSIExtractSession, + &cbdata, NULL) < 0) goto cleanup; - if (session == NULL && - !probe) { + if (cbdata.session == NULL && !probe) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("cannot find session")); goto cleanup; @@ -131,7 +135,7 @@ virStorageBackendISCSISession(virStoragePoolObjPtr pool, cleanup: virCommandFree(cmd); - return session; + return cbdata.session; } @@ -439,8 +443,7 @@ struct virStorageBackendISCSITargetList { }; static int -virStorageBackendISCSIGetTargets(virStoragePoolObjPtr pool ATTRIBUTE_UNUSED, - char **const groups, +virStorageBackendISCSIGetTargets(char **const groups, void *data) { struct virStorageBackendISCSITargetList *list = data; @@ -503,13 +506,12 @@ virStorageBackendISCSIScanTargets(const char *portal, memset(&list, 0, sizeof(list)); - if (virStorageBackendRunProgRegex(NULL, /* No pool for callback */ - cmd, - 1, - regexes, - vars, - virStorageBackendISCSIGetTargets, - &list, NULL) < 0) + if (virCommandRunRegex(cmd, + 1, + regexes, + vars, + virStorageBackendISCSIGetTargets, + &list, NULL) < 0) goto cleanup; for (i = 0; i < list.ntargets; i++) { diff --git a/src/storage/storage_backend_logical.c b/src/storage/storage_backend_logical.c index 667fb063f5..907b9b0d6e 100644 --- a/src/storage/storage_backend_logical.c +++ b/src/storage/storage_backend_logical.c @@ -66,11 +66,17 @@ virStorageBackendLogicalSetActive(virStoragePoolObjPtr pool, #define VIR_STORAGE_VOL_LOGICAL_SEGTYPE_STRIPED "striped" +struct virStorageBackendLogicalPoolVolData { + virStoragePoolObjPtr pool; + virStorageVolDefPtr vol; +}; + static int -virStorageBackendLogicalMakeVol(virStoragePoolObjPtr pool, - char **const groups, - void *data) +virStorageBackendLogicalMakeVol(char **const groups, + void *opaque) { + struct virStorageBackendLogicalPoolVolData *data = opaque; + virStoragePoolObjPtr pool = data->pool; virStorageVolDefPtr vol = NULL; bool is_new_vol = false; unsigned long long offset, size, length; @@ -96,8 +102,8 @@ virStorageBackendLogicalMakeVol(virStoragePoolObjPtr pool, return 0; /* See if we're only looking for a specific volume */ - if (data != NULL) { - vol = data; + if (data->vol != NULL) { + vol = data->vol; if (STRNEQ(vol->name, groups[0])) return 0; } @@ -299,6 +305,10 @@ virStorageBackendLogicalFindLVs(virStoragePoolObjPtr pool, }; int ret = -1; virCommandPtr cmd; + struct virStorageBackendLogicalPoolVolData cbdata = { + .pool = pool, + .vol = vol, + }; cmd = virCommandNewArgList(LVS, "--separator", "#", @@ -310,13 +320,13 @@ virStorageBackendLogicalFindLVs(virStoragePoolObjPtr pool, "lv_name,origin,uuid,devices,segtype,stripes,seg_size,vg_extent_size,size,lv_attr", pool->def->source.name, NULL); - if (virStorageBackendRunProgRegex(pool, - cmd, - 1, - regexes, - vars, - virStorageBackendLogicalMakeVol, - vol, "lvs") < 0) + if (virCommandRunRegex(cmd, + 1, + regexes, + vars, + virStorageBackendLogicalMakeVol, + &cbdata, + "lvs") < 0) goto cleanup; ret = 0; @@ -326,10 +336,10 @@ cleanup: } static int -virStorageBackendLogicalRefreshPoolFunc(virStoragePoolObjPtr pool ATTRIBUTE_UNUSED, - char **const groups, - void *data ATTRIBUTE_UNUSED) +virStorageBackendLogicalRefreshPoolFunc(char **const groups, + void *data) { + virStoragePoolObjPtr pool = data; if (virStrToLong_ull(groups[0], NULL, 10, &pool->def->capacity) < 0) return -1; if (virStrToLong_ull(groups[1], NULL, 10, &pool->def->available) < 0) @@ -341,8 +351,7 @@ virStorageBackendLogicalRefreshPoolFunc(virStoragePoolObjPtr pool ATTRIBUTE_UNUS static int -virStorageBackendLogicalFindPoolSourcesFunc(virStoragePoolObjPtr pool ATTRIBUTE_UNUSED, - char **const groups, +virStorageBackendLogicalFindPoolSourcesFunc(char **const groups, void *data) { virStoragePoolSourceListPtr sourceList = data; @@ -432,9 +441,9 @@ virStorageBackendLogicalFindPoolSources(virConnectPtr conn ATTRIBUTE_UNUSED, "--noheadings", "-o", "pv_name,vg_name", NULL); - if (virStorageBackendRunProgRegex(NULL, cmd, 1, regexes, vars, - virStorageBackendLogicalFindPoolSourcesFunc, - &sourceList, "pvs") < 0) { + if (virCommandRunRegex(cmd, 1, regexes, vars, + virStorageBackendLogicalFindPoolSourcesFunc, + &sourceList, "pvs") < 0) { virCommandFree(cmd); return NULL; } @@ -593,13 +602,13 @@ virStorageBackendLogicalRefreshPool(virConnectPtr conn ATTRIBUTE_UNUSED, NULL); /* Now get basic volgrp metadata */ - if (virStorageBackendRunProgRegex(pool, - cmd, - 1, - regexes, - vars, - virStorageBackendLogicalRefreshPoolFunc, - NULL, "vgs") < 0) + if (virCommandRunRegex(cmd, + 1, + regexes, + vars, + virStorageBackendLogicalRefreshPoolFunc, + pool, + "vgs") < 0) goto cleanup; ret = 0; diff --git a/src/util/vircommand.c b/src/util/vircommand.c index 79bb20c4d9..3f98eb8ca6 100644 --- a/src/util/vircommand.c +++ b/src/util/vircommand.c @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -2760,3 +2761,247 @@ virCommandSetDryRun(virBufferPtr buf, dryRunCallback = cb; 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(®[i], regex[i], REG_EXTENDED); + if (err != 0) { + char error[100]; + regerror(err, ®[i], error, sizeof(error)); + virReportError(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) + 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(®[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(®[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 */ diff --git a/src/util/vircommand.h b/src/util/vircommand.h index 929375b3cc..8cdb31cf5f 100644 --- a/src/util/vircommand.h +++ b/src/util/vircommand.h @@ -187,4 +187,24 @@ void virCommandFree(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__ */