示例#1
0
static int
nwfilterListNWFilters(virConnectPtr conn,
                      char **const names,
                      int nnames) {
    virNWFilterDriverStatePtr driver = conn->nwfilterPrivateData;
    int got = 0, i;

    nwfilterDriverLock(driver);
    for (i = 0 ; i < driver->nwfilters.count && got < nnames ; i++) {
        virNWFilterObjLock(driver->nwfilters.objs[i]);
        if (!(names[got] = strdup(driver->nwfilters.objs[i]->def->name))) {
             virNWFilterObjUnlock(driver->nwfilters.objs[i]);
             virReportOOMError();
             goto cleanup;
        }
        got++;
        virNWFilterObjUnlock(driver->nwfilters.objs[i]);
    }
    nwfilterDriverUnlock(driver);
    return got;

 cleanup:
    nwfilterDriverUnlock(driver);
    for (i = 0 ; i < got ; i++)
        VIR_FREE(names[i]);
    memset(names, 0, nnames * sizeof(*names));
    return -1;
}
示例#2
0
int
virNWFilterObjListGetNames(virNWFilterObjListPtr nwfilters,
                           virConnectPtr conn,
                           virNWFilterObjListFilter filter,
                           char **const names,
                           int maxnames)
{
    int nnames = 0;
    size_t i;
    virNWFilterDefPtr def;

    for (i = 0; i < nwfilters->count && nnames < maxnames; i++) {
        virNWFilterObjPtr obj = nwfilters->objs[i];
        virNWFilterObjLock(obj);
        def = obj->def;
        if (!filter || filter(conn, def)) {
            if (VIR_STRDUP(names[nnames], def->name) < 0) {
                virNWFilterObjUnlock(obj);
                goto failure;
            }
            nnames++;
        }
        virNWFilterObjUnlock(obj);
    }

    return nnames;

 failure:
    while (--nnames >= 0)
        VIR_FREE(names[nnames]);

    return -1;
}
示例#3
0
int
virNWFilterObjListExport(virConnectPtr conn,
                         virNWFilterObjListPtr nwfilters,
                         virNWFilterPtr **filters,
                         virNWFilterObjListFilter filter)
{
    virNWFilterPtr *tmp_filters = NULL;
    int nfilters = 0;
    virNWFilterPtr nwfilter = NULL;
    virNWFilterObjPtr obj = NULL;
    virNWFilterDefPtr def;
    size_t i;
    int ret = -1;

    if (!filters) {
        ret = nwfilters->count;
        goto cleanup;
    }

    if (VIR_ALLOC_N(tmp_filters, nwfilters->count + 1) < 0)
        goto cleanup;

    for (i = 0; i < nwfilters->count; i++) {
        obj = nwfilters->objs[i];
        virNWFilterObjLock(obj);
        def = obj->def;
        if (!filter || filter(conn, def)) {
            if (!(nwfilter = virGetNWFilter(conn, def->name, def->uuid))) {
                virNWFilterObjUnlock(obj);
                goto cleanup;
            }
            tmp_filters[nfilters++] = nwfilter;
        }
        virNWFilterObjUnlock(obj);
    }

    *filters = tmp_filters;
    tmp_filters = NULL;
    ret = nfilters;

 cleanup:
    if (tmp_filters) {
        for (i = 0; i < nfilters; i ++)
            virObjectUnref(tmp_filters[i]);
    }
    VIR_FREE(tmp_filters);

    return ret;
}
示例#4
0
static char *
nwfilterGetXMLDesc(virNWFilterPtr obj,
                   int flags) {
    virNWFilterDriverStatePtr driver = obj->conn->nwfilterPrivateData;
    virNWFilterObjPtr nwfilter;
    char *ret = NULL;

    virCheckFlags(0, NULL);

    nwfilterDriverLock(driver);
    nwfilter = virNWFilterObjFindByUUID(&driver->nwfilters, obj->uuid);
    nwfilterDriverUnlock(driver);

    if (!nwfilter) {
        virNWFilterReportError(VIR_ERR_NO_NWFILTER,
                               "%s", _("no nwfilter with matching uuid"));
        goto cleanup;
    }

    ret = virNWFilterDefFormat(nwfilter->def);

cleanup:
    if (nwfilter)
        virNWFilterObjUnlock(nwfilter);
    return ret;
}
示例#5
0
int
virNWFilterObjListLoadAllConfigs(virNWFilterObjListPtr nwfilters,
                                 const char *configDir)
{
    DIR *dir;
    struct dirent *entry;
    int ret = -1;
    int rc;

    if ((rc = virDirOpenIfExists(&dir, configDir)) <= 0)
        return rc;

    while ((ret = virDirRead(dir, &entry, configDir)) > 0) {
        virNWFilterObjPtr obj;

        if (!virFileStripSuffix(entry->d_name, ".xml"))
            continue;

        obj = virNWFilterObjListLoadConfig(nwfilters, configDir, entry->d_name);
        if (obj)
            virNWFilterObjUnlock(obj);
    }

    VIR_DIR_CLOSE(dir);
    return ret;
}
示例#6
0
void
virNWFilterObjListRemove(virNWFilterObjListPtr nwfilters,
                         virNWFilterObjPtr obj)
{
    size_t i;

    virNWFilterObjUnlock(obj);

    for (i = 0; i < nwfilters->count; i++) {
        virNWFilterObjLock(nwfilters->objs[i]);
        if (nwfilters->objs[i] == obj) {
            virNWFilterObjUnlock(nwfilters->objs[i]);
            virNWFilterObjFree(nwfilters->objs[i]);

            VIR_DELETE_ELEMENT(nwfilters->objs, i, nwfilters->count);
            break;
        }
        virNWFilterObjUnlock(nwfilters->objs[i]);
    }
}
示例#7
0
int
virNWFilterObjListNumOfNWFilters(virNWFilterObjListPtr nwfilters,
                                 virConnectPtr conn,
                                 virNWFilterObjListFilter filter)
{
    size_t i;
    int nfilters = 0;

    for (i = 0; i < nwfilters->count; i++) {
        virNWFilterObjPtr obj = nwfilters->objs[i];
        virNWFilterObjLock(obj);
        if (!filter || filter(conn, obj->def))
            nfilters++;
        virNWFilterObjUnlock(obj);
    }

    return nfilters;
}
示例#8
0
static int
nwfilterUndefine(virNWFilterPtr obj) {
    virNWFilterDriverStatePtr driver = obj->conn->nwfilterPrivateData;
    virNWFilterObjPtr nwfilter;
    int ret = -1;

    nwfilterDriverLock(driver);
    virNWFilterCallbackDriversLock();

    virNWFilterLockFilterUpdates();

    nwfilter = virNWFilterObjFindByUUID(&driver->nwfilters, obj->uuid);
    if (!nwfilter) {
        virNWFilterReportError(VIR_ERR_NO_NWFILTER,
                               "%s", _("no nwfilter with matching uuid"));
        goto cleanup;
    }

    if (virNWFilterTestUnassignDef(obj->conn, nwfilter)) {
        virNWFilterReportError(VIR_ERR_OPERATION_INVALID,
                               "%s",
                               _("nwfilter is in use"));
        goto cleanup;
    }

    if (virNWFilterObjDeleteDef(nwfilter) < 0)
        goto cleanup;

    VIR_FREE(nwfilter->configFile);

    virNWFilterObjRemove(&driver->nwfilters, nwfilter);
    nwfilter = NULL;
    ret = 0;

cleanup:
    if (nwfilter)
        virNWFilterObjUnlock(nwfilter);

    virNWFilterUnlockFilterUpdates();

    virNWFilterCallbackDriversUnlock();
    nwfilterDriverUnlock(driver);
    return ret;
}
示例#9
0
virNWFilterObjPtr
virNWFilterObjListFindByUUID(virNWFilterObjListPtr nwfilters,
                             const unsigned char *uuid)
{
    size_t i;
    virNWFilterObjPtr obj;
    virNWFilterDefPtr def;

    for (i = 0; i < nwfilters->count; i++) {
        obj = nwfilters->objs[i];
        virNWFilterObjLock(obj);
        def = obj->def;
        if (!memcmp(def->uuid, uuid, VIR_UUID_BUFLEN))
            return obj;
        virNWFilterObjUnlock(obj);
    }

    return NULL;
}
示例#10
0
virNWFilterObjPtr
virNWFilterObjListFindByName(virNWFilterObjListPtr nwfilters,
                             const char *name)
{
    size_t i;
    virNWFilterObjPtr obj;
    virNWFilterDefPtr def;

    for (i = 0; i < nwfilters->count; i++) {
        obj = nwfilters->objs[i];
        virNWFilterObjLock(obj);
        def = obj->def;
        if (STREQ_NULLABLE(def->name, name))
            return obj;
        virNWFilterObjUnlock(obj);
    }

    return NULL;
}
示例#11
0
virNWFilterObjPtr
virNWFilterObjListFindInstantiateFilter(virNWFilterObjListPtr nwfilters,
                                        const char *filtername)
{
    virNWFilterObjPtr obj;

    if (!(obj = virNWFilterObjListFindByName(nwfilters, filtername))) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("referenced filter '%s' is missing"), filtername);
        return NULL;
    }

    if (virNWFilterObjWantRemoved(obj)) {
        virReportError(VIR_ERR_NO_NWFILTER,
                       _("Filter '%s' is in use."), filtername);
        virNWFilterObjUnlock(obj);
        return NULL;
    }

    return obj;
}
示例#12
0
static virNWFilterPtr
nwfilterLookupByName(virConnectPtr conn,
                     const char *name) {
    virNWFilterDriverStatePtr driver = conn->nwfilterPrivateData;
    virNWFilterObjPtr nwfilter;
    virNWFilterPtr ret = NULL;

    nwfilterDriverLock(driver);
    nwfilter = virNWFilterObjFindByName(&driver->nwfilters, name);
    nwfilterDriverUnlock(driver);

    if (!nwfilter) {
        virNWFilterReportError(VIR_ERR_NO_NWFILTER,
                               _("no nwfilter with matching name '%s'"), name);
        goto cleanup;
    }

    ret = virGetNWFilter(conn, nwfilter->def->name, nwfilter->def->uuid);

cleanup:
    if (nwfilter)
        virNWFilterObjUnlock(nwfilter);
    return ret;
}
示例#13
0
static virNWFilterPtr
nwfilterDefine(virConnectPtr conn,
               const char *xml)
{
    virNWFilterDriverStatePtr driver = conn->nwfilterPrivateData;
    virNWFilterDefPtr def;
    virNWFilterObjPtr nwfilter = NULL;
    virNWFilterPtr ret = NULL;

    nwfilterDriverLock(driver);
    virNWFilterCallbackDriversLock();

    if (!(def = virNWFilterDefParseString(conn, xml)))
        goto cleanup;

    if (!(nwfilter = virNWFilterObjAssignDef(conn, &driver->nwfilters, def)))
        goto cleanup;

    if (virNWFilterObjSaveDef(driver, nwfilter, def) < 0) {
        virNWFilterObjRemove(&driver->nwfilters, nwfilter);
        def = NULL;
        goto cleanup;
    }
    def = NULL;

    ret = virGetNWFilter(conn, nwfilter->def->name, nwfilter->def->uuid);

cleanup:
    virNWFilterDefFree(def);
    if (nwfilter)
        virNWFilterObjUnlock(nwfilter);

    virNWFilterCallbackDriversUnlock();
    nwfilterDriverUnlock(driver);
    return ret;
}
示例#14
0
static int
_virNWFilterObjListDefLoopDetect(virNWFilterObjListPtr nwfilters,
                                 virNWFilterDefPtr def,
                                 const char *filtername)
{
    int rc = 0;
    size_t i;
    virNWFilterEntryPtr entry;
    virNWFilterObjPtr obj;

    if (!def)
        return 0;

    for (i = 0; i < def->nentries; i++) {
        entry = def->filterEntries[i];
        if (entry->include) {

            if (STREQ(filtername, entry->include->filterref)) {
                rc = -1;
                break;
            }

            obj = virNWFilterObjListFindByName(nwfilters,
                                               entry->include->filterref);
            if (obj) {
                rc = _virNWFilterObjListDefLoopDetect(nwfilters, obj->def,
                                                      filtername);
                virNWFilterObjUnlock(obj);
                if (rc < 0)
                    break;
            }
        }
    }

    return rc;
}
示例#15
0
/**
 * _virNWFilterInstantiateRec:
 * @conn: pointer to virConnect object
 * @techdriver: The driver to use for instantiation
 * @filter: The filter to instantiate
 * @ifname: The name of the interface to apply the rules to
 * @vars: A map holding variable names and values used for instantiating
 *  the filter and its subfilters.
 * @nEntries: number of virNWFilterInst objects collected
 * @insts: pointer to array for virNWFilterIns object pointers
 * @useNewFilter: instruct whether to use a newDef pointer rather than a
 *  def ptr which is useful during a filter update
 * @foundNewFilter: pointer to int indivating whether a newDef pointer was
 *  ever used; variable expected to be initialized to 0 by caller
 *
 * Returns 0 on success, a value otherwise.
 *
 * Recursively instantiate a filter by instantiating the given filter along
 * with all its subfilters in a depth-first traversal of the tree of
 * referenced filters. The name of the interface to which the rules belong
 * must be provided. Apply the values of variables as needed. Terminate with
 * error when a referenced filter is missing or a variable could not be
 * resolved -- among other reasons.
 */
static int
_virNWFilterInstantiateRec(virConnectPtr conn,
                           virNWFilterTechDriverPtr techdriver,
                           enum virDomainNetType nettype,
                           virNWFilterDefPtr filter,
                           const char *ifname,
                           virNWFilterHashTablePtr vars,
                           int *nEntries,
                           virNWFilterRuleInstPtr **insts,
                           enum instCase useNewFilter, bool *foundNewFilter,
                           virNWFilterDriverStatePtr driver)
{
    virNWFilterObjPtr obj;
    int rc = 0;
    int i;
    virNWFilterRuleInstPtr inst;
    virNWFilterDefPtr next_filter;

    for (i = 0; i < filter->nentries; i++) {
        virNWFilterRuleDefPtr    rule = filter->filterEntries[i]->rule;
        virNWFilterIncludeDefPtr inc  = filter->filterEntries[i]->include;
        if (rule) {
            inst = virNWFilterRuleInstantiate(conn,
                                              techdriver,
                                              nettype,
                                              filter,
                                              rule,
                                              ifname,
                                              vars);
            if (!inst) {
                rc = 1;
                break;
            }

            if (VIR_REALLOC_N(*insts, (*nEntries)+1) < 0) {
                virReportOOMError();
                rc = 1;
                break;
            }

            (*insts)[(*nEntries)++] = inst;

        } else if (inc) {
            VIR_DEBUG("Instantiating filter %s", inc->filterref);
            obj = virNWFilterObjFindByName(&driver->nwfilters, inc->filterref);
            if (obj) {

                if (obj->wantRemoved) {
                    virNWFilterReportError(VIR_ERR_NO_NWFILTER,
                                           _("Filter '%s' is in use."),
                                           inc->filterref);
                    rc = 1;
                    virNWFilterObjUnlock(obj);
                    break;
                }

                /* create a temporary hashmap for depth-first tree traversal */
                virNWFilterHashTablePtr tmpvars =
                                      virNWFilterCreateVarsFrom(inc->params,
                                                                vars);
                if (!tmpvars) {
                    virReportOOMError();
                    rc = 1;
                    virNWFilterObjUnlock(obj);
                    break;
                }

                next_filter = obj->def;

                switch (useNewFilter) {
                case INSTANTIATE_FOLLOW_NEWFILTER:
                    if (obj->newDef) {
                        next_filter = obj->newDef;
                        *foundNewFilter = true;
                    }
                break;
                case INSTANTIATE_ALWAYS:
                break;
                }

                rc = _virNWFilterInstantiateRec(conn,
                                                techdriver,
                                                nettype,
                                                next_filter,
                                                ifname,
                                                tmpvars,
                                                nEntries, insts,
                                                useNewFilter,
                                                foundNewFilter,
                                                driver);

                virNWFilterHashTableFree(tmpvars);

                virNWFilterObjUnlock(obj);
                if (rc)
                    break;
            } else {
                virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
                                       _("referenced filter '%s' is missing"),
                                       inc->filterref);
                rc = 1;
                break;
            }
        }
    }
    return rc;
}
示例#16
0
static int
virNWFilterDetermineMissingVarsRec(virConnectPtr conn,
                                   virNWFilterDefPtr filter,
                                   virNWFilterHashTablePtr vars,
                                   virNWFilterHashTablePtr missing_vars,
                                   int useNewFilter,
                                   virNWFilterDriverStatePtr driver)
{
    virNWFilterObjPtr obj;
    int rc = 0;
    int i, j;
    virNWFilterDefPtr next_filter;

    for (i = 0; i < filter->nentries; i++) {
        virNWFilterRuleDefPtr    rule = filter->filterEntries[i]->rule;
        virNWFilterIncludeDefPtr inc  = filter->filterEntries[i]->include;
        if (rule) {
            /* check all variables of this rule */
            for (j = 0; j < rule->nvars; j++) {
                if (!virHashLookup(vars->hashTable, rule->vars[j])) {
                    virNWFilterHashTablePut(missing_vars, rule->vars[j],
                                            strdup("1"), 1);
                }
            }
        } else if (inc) {
            VIR_DEBUG("Following filter %s\n", inc->filterref);
            obj = virNWFilterObjFindByName(&driver->nwfilters, inc->filterref);
            if (obj) {

                if (obj->wantRemoved) {
                    virNWFilterReportError(VIR_ERR_NO_NWFILTER,
                                           _("Filter '%s' is in use."),
                                           inc->filterref);
                    rc = 1;
                    virNWFilterObjUnlock(obj);
                    break;
                }

                /* create a temporary hashmap for depth-first tree traversal */
                virNWFilterHashTablePtr tmpvars =
                                      virNWFilterCreateVarsFrom(inc->params,
                                                                vars);
                if (!tmpvars) {
                    virReportOOMError();
                    rc = 1;
                    virNWFilterObjUnlock(obj);
                    break;
                }

                next_filter = obj->def;

                switch (useNewFilter) {
                case INSTANTIATE_FOLLOW_NEWFILTER:
                    if (obj->newDef) {
                        next_filter = obj->newDef;
                    }
                break;
                case INSTANTIATE_ALWAYS:
                break;
                }

                rc = virNWFilterDetermineMissingVarsRec(conn,
                                                        next_filter,
                                                        tmpvars,
                                                        missing_vars,
                                                        useNewFilter,
                                                        driver);

                virNWFilterHashTableFree(tmpvars);

                virNWFilterObjUnlock(obj);
                if (rc)
                    break;
            } else {
                virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
                                       _("referenced filter '%s' is missing"),
                                       inc->filterref);
                rc = 1;
                break;
            }
        }
    }
    return rc;
}
示例#17
0
virNWFilterObjPtr
virNWFilterObjListAssignDef(virNWFilterObjListPtr nwfilters,
                            virNWFilterDefPtr def)
{
    virNWFilterObjPtr obj;
    virNWFilterDefPtr objdef;

    if ((obj = virNWFilterObjListFindByUUID(nwfilters, def->uuid))) {
        objdef = obj->def;

        if (STRNEQ(def->name, objdef->name)) {
            virReportError(VIR_ERR_OPERATION_FAILED,
                           _("filter with same UUID but different name "
                             "('%s') already exists"),
                           objdef->name);
            virNWFilterObjUnlock(obj);
            return NULL;
        }
        virNWFilterObjUnlock(obj);
    } else {
        if ((obj = virNWFilterObjListFindByName(nwfilters, def->name))) {
            char uuidstr[VIR_UUID_STRING_BUFLEN];

            objdef = obj->def;
            virUUIDFormat(objdef->uuid, uuidstr);
            virReportError(VIR_ERR_OPERATION_FAILED,
                           _("filter '%s' already exists with uuid %s"),
                           def->name, uuidstr);
            virNWFilterObjUnlock(obj);
            return NULL;
        }
    }

    if (virNWFilterObjListDefLoopDetect(nwfilters, def) < 0) {
        virReportError(VIR_ERR_OPERATION_FAILED,
                       "%s", _("filter would introduce a loop"));
        return NULL;
    }


    if ((obj = virNWFilterObjListFindByName(nwfilters, def->name))) {

        objdef = obj->def;
        if (virNWFilterDefEqual(def, objdef, false)) {
            virNWFilterDefFree(objdef);
            obj->def = def;
            return obj;
        }

        obj->newDef = def;
        /* trigger the update on VMs referencing the filter */
        if (virNWFilterTriggerVMFilterRebuild() < 0) {
            obj->newDef = NULL;
            virNWFilterObjUnlock(obj);
            return NULL;
        }

        virNWFilterDefFree(objdef);
        obj->def = def;
        obj->newDef = NULL;
        return obj;
    }

    if (!(obj = virNWFilterObjNew()))
        return NULL;

    if (VIR_APPEND_ELEMENT_COPY(nwfilters->objs,
                                nwfilters->count, obj) < 0) {
        virNWFilterObjUnlock(obj);
        virNWFilterObjFree(obj);
        return NULL;
    }
    obj->def = def;

    return obj;
}
示例#18
0
/*
 * Call this function while holding the NWFilter filter update lock
 */
static int
__virNWFilterInstantiateFilter(virConnectPtr conn,
                               bool teardownOld,
                               const char *ifname,
                               int ifindex,
                               const char *linkdev,
                               enum virDomainNetType nettype,
                               const unsigned char *macaddr,
                               const char *filtername,
                               virNWFilterHashTablePtr filterparams,
                               enum instCase useNewFilter,
                               virNWFilterDriverStatePtr driver,
                               bool forceWithPendingReq,
                               bool *foundNewFilter)
{
    int rc;
    const char *drvname = EBIPTABLES_DRIVER_ID;
    virNWFilterTechDriverPtr techdriver;
    virNWFilterObjPtr obj;
    virNWFilterHashTablePtr vars, vars1;
    virNWFilterDefPtr filter;
    char vmmacaddr[VIR_MAC_STRING_BUFLEN] = {0};
    char *str_macaddr = NULL;
    const char *ipaddr;
    char *str_ipaddr = NULL;

    techdriver = virNWFilterTechDriverForName(drvname);

    if (!techdriver) {
        virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
                               _("Could not get access to ACL tech "
                               "driver '%s'"),
                               drvname);
        return 1;
    }

    VIR_DEBUG("filter name: %s", filtername);

    obj = virNWFilterObjFindByName(&driver->nwfilters, filtername);
    if (!obj) {
        virNWFilterReportError(VIR_ERR_NO_NWFILTER,
                               _("Could not find filter '%s'"),
                               filtername);
        return 1;
    }

    if (obj->wantRemoved) {
        virNWFilterReportError(VIR_ERR_NO_NWFILTER,
                               _("Filter '%s' is in use."),
                               filtername);
        rc = 1;
        goto err_exit;
    }

    virFormatMacAddr(macaddr, vmmacaddr);
    str_macaddr = strdup(vmmacaddr);
    if (!str_macaddr) {
        virReportOOMError();
        rc = 1;
        goto err_exit;
    }

    ipaddr = virNWFilterGetIpAddrForIfname(ifname);
    if (ipaddr) {
        str_ipaddr = strdup(ipaddr);
        if (!str_ipaddr) {
            virReportOOMError();
            rc = 1;
            goto err_exit;
        }
    }

    vars1 = virNWFilterCreateVarHashmap(str_macaddr, str_ipaddr);
    if (!vars1) {
        rc = 1;
        goto err_exit;
    }

    str_macaddr = NULL;
    str_ipaddr = NULL;

    vars = virNWFilterCreateVarsFrom(vars1,
                                     filterparams);
    if (!vars) {
        rc = 1;
        goto err_exit_vars1;
    }

    filter = obj->def;

    switch (useNewFilter) {
    case INSTANTIATE_FOLLOW_NEWFILTER:
        if (obj->newDef) {
            filter = obj->newDef;
            *foundNewFilter = true;
        }
    break;

    case INSTANTIATE_ALWAYS:
    break;
    }

    rc = virNWFilterInstantiate(conn,
                                techdriver,
                                nettype,
                                filter,
                                ifname,
                                ifindex,
                                linkdev,
                                vars,
                                useNewFilter, foundNewFilter,
                                teardownOld,
                                macaddr,
                                driver,
                                forceWithPendingReq);

    virNWFilterHashTableFree(vars);

err_exit_vars1:
    virNWFilterHashTableFree(vars1);

err_exit:
    virNWFilterObjUnlock(obj);

    VIR_FREE(str_ipaddr);
    VIR_FREE(str_macaddr);

    return rc;
}