From a85df70436ee5efee23357d68cf2b6cc1627ef18 Mon Sep 17 00:00:00 2001 From: Jakub Hrozek Date: Wed, 20 Apr 2016 17:06:03 +0200 Subject: [PATCH 03/14] SYSDB: Refactor database connection Changes the functions in sysdb_init.c so that the functions are usable to initialize a second cache to store timestamps. In particular, functions that operated on sysdb->ldb now operate on a generic ldb context so that a new ldb_ts context can be used later. Existing functions that initialize the sysdb cache call the generic functions with sysdb->ldb as a parameter. Splits out a function to initialize an empty ldb database with a generic LDIF. Splits out the sysdb upgrade function because the upgrade will only be used by the sysdb cache, the timestamp cache will start with a different version and might receive separate upgrade functions in the future. The ldb connection function accepts ldb flags parameter, currently unused. --- src/db/sysdb_init.c | 519 +++++++++++++++++++++++++++++++------------------ src/db/sysdb_private.h | 4 +- src/db/sysdb_upgrade.c | 4 +- 3 files changed, 337 insertions(+), 190 deletions(-) diff --git a/src/db/sysdb_init.c b/src/db/sysdb_init.c index 637b41a650f23437d8b5fd5d8bde2f5a5310ef2b..6426b940a98e9efd3a9e50a97ce14af9daa78699 100644 --- a/src/db/sysdb_init.c +++ b/src/db/sysdb_init.c @@ -30,7 +30,9 @@ #define LDB_MODULES_PATH "LDB_MODULES_PATH" -errno_t sysdb_ldb_connect(TALLOC_CTX *mem_ctx, const char *filename, +errno_t sysdb_ldb_connect(TALLOC_CTX *mem_ctx, + const char *filename, + int flags, struct ldb_context **_ldb) { int ret; @@ -57,7 +59,7 @@ errno_t sysdb_ldb_connect(TALLOC_CTX *mem_ctx, const char *filename, ldb_set_modules_dir(ldb, mod_path); } - ret = ldb_connect(ldb, filename, 0, NULL); + ret = ldb_connect(ldb, filename, flags, NULL); if (ret != LDB_SUCCESS) { return EIO; } @@ -67,6 +69,22 @@ errno_t sysdb_ldb_connect(TALLOC_CTX *mem_ctx, const char *filename, return EOK; } +static errno_t sysdb_ldb_reconnect(TALLOC_CTX *mem_ctx, + const char *ldb_file, + int flags, + struct ldb_context **ldb) +{ + errno_t ret; + + talloc_zfree(*ldb); + ret = sysdb_ldb_connect(mem_ctx, ldb_file, flags, ldb); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_ldb_connect failed.\n"); + } + + return ret; +} + int sysdb_get_db_file(TALLOC_CTX *mem_ctx, const char *provider, const char *name, const char *base_path, char **_ldb_file) @@ -89,7 +107,8 @@ int sysdb_get_db_file(TALLOC_CTX *mem_ctx, return EOK; } -errno_t sysdb_domain_create(struct sysdb_ctx *sysdb, const char *domain_name) +static errno_t sysdb_domain_create_int(struct ldb_context *ldb, + const char *domain_name) { struct ldb_message *msg; TALLOC_CTX *tmp_ctx; @@ -108,7 +127,7 @@ errno_t sysdb_domain_create(struct sysdb_ctx *sysdb, const char *domain_name) ret = ENOMEM; goto done; } - msg->dn = ldb_dn_new_fmt(msg, sysdb->ldb, SYSDB_DOM_BASE, domain_name); + msg->dn = ldb_dn_new_fmt(msg, ldb, SYSDB_DOM_BASE, domain_name); if (!msg->dn) { ret = ENOMEM; goto done; @@ -119,11 +138,11 @@ errno_t sysdb_domain_create(struct sysdb_ctx *sysdb, const char *domain_name) goto done; } /* do a synchronous add */ - ret = ldb_add(sysdb->ldb, msg); + ret = ldb_add(ldb, msg); if (ret != LDB_SUCCESS) { DEBUG(SSSDBG_FATAL_FAILURE, "Failed to initialize DB (%d, [%s]) " "for domain %s!\n", - ret, ldb_errstring(sysdb->ldb), + ret, ldb_errstring(ldb), domain_name); ret = EIO; goto done; @@ -137,7 +156,7 @@ errno_t sysdb_domain_create(struct sysdb_ctx *sysdb, const char *domain_name) ret = ENOMEM; goto done; } - msg->dn = ldb_dn_new_fmt(msg, sysdb->ldb, + msg->dn = ldb_dn_new_fmt(msg, ldb, SYSDB_TMPL_USER_BASE, domain_name); if (!msg->dn) { ret = ENOMEM; @@ -149,11 +168,11 @@ errno_t sysdb_domain_create(struct sysdb_ctx *sysdb, const char *domain_name) goto done; } /* do a synchronous add */ - ret = ldb_add(sysdb->ldb, msg); + ret = ldb_add(ldb, msg); if (ret != LDB_SUCCESS) { DEBUG(SSSDBG_FATAL_FAILURE, "Failed to initialize DB (%d, [%s]) " "for domain %s!\n", - ret, ldb_errstring(sysdb->ldb), + ret, ldb_errstring(ldb), domain_name); ret = EIO; goto done; @@ -167,7 +186,7 @@ errno_t sysdb_domain_create(struct sysdb_ctx *sysdb, const char *domain_name) ret = ENOMEM; goto done; } - msg->dn = ldb_dn_new_fmt(msg, sysdb->ldb, + msg->dn = ldb_dn_new_fmt(msg, ldb, SYSDB_TMPL_GROUP_BASE, domain_name); if (!msg->dn) { ret = ENOMEM; @@ -179,11 +198,11 @@ errno_t sysdb_domain_create(struct sysdb_ctx *sysdb, const char *domain_name) goto done; } /* do a synchronous add */ - ret = ldb_add(sysdb->ldb, msg); + ret = ldb_add(ldb, msg); if (ret != LDB_SUCCESS) { DEBUG(SSSDBG_FATAL_FAILURE, "Failed to initialize DB (%d, [%s]) for " "domain %s!\n", - ret, ldb_errstring(sysdb->ldb), + ret, ldb_errstring(ldb), domain_name); ret = EIO; goto done; @@ -197,6 +216,11 @@ done: return ret; } +errno_t sysdb_domain_create(struct sysdb_ctx *sysdb, const char *domain_name) +{ + return sysdb_domain_create_int(sysdb->ldb, domain_name); +} + /* Compare versions of sysdb, returns ERRNO accordingly */ static errno_t sysdb_version_check(const char *expected, @@ -205,6 +229,10 @@ sysdb_version_check(const char *expected, int ret; unsigned int exp_major, exp_minor, recv_major, recv_minor; + if (strcmp(received, expected) == 0) { + return EOK; + } + ret = sscanf(expected, "%u.%u", &exp_major, &exp_minor); if (ret != 2) { return EINVAL; @@ -229,41 +257,202 @@ sysdb_version_check(const char *expected, return EOK; } -int sysdb_domain_init_internal(TALLOC_CTX *mem_ctx, - struct sss_domain_info *domain, - const char *db_path, - bool allow_upgrade, - struct sysdb_ctx **_ctx) +static errno_t sysdb_cache_add_base_ldif(struct ldb_context *ldb, + const char *base_ldif, + const char *domain_name) { - TALLOC_CTX *tmp_ctx = NULL; - struct sysdb_ctx *sysdb; - const char *base_ldif; + int ret; struct ldb_ldif *ldif; + + while ((ldif = ldb_ldif_read_string(ldb, &base_ldif))) { + ret = ldb_add(ldb, ldif->msg); + if (ret != LDB_SUCCESS) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Failed to initialize DB (%d, [%s]) for domain %s!\n", + ret, ldb_errstring(ldb), domain_name); + return EIO; + } + ldb_ldif_read_free(ldb, ldif); + } + + return EOK; +} + +static errno_t sysdb_cache_create_empty(struct ldb_context *ldb, + const char *base_ldif, + struct sss_domain_info *domain) +{ + int ret; + + ret = sysdb_cache_add_base_ldif(ldb, base_ldif, domain->name); + if (ret != EOK) { + return ret; + } + + ret = sysdb_domain_create_int(ldb, domain->name); + if (ret != EOK) { + return ret; + } + + return EOK; +} + +static errno_t sysdb_domain_cache_upgrade(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *sysdb, + struct ldb_context *ldb, + struct sss_domain_info *domain, + const char *cur_version, + const char **_new_version) +{ + errno_t ret; + TALLOC_CTX *tmp_ctx; + const char *version; + struct ldb_context *save_ldb; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + /* The upgrade process depends on having ldb around, yet the upgrade + * function shouldn't set the ldb pointer, only the connect function + * should after it's successful. To avoid hard refactoring, save the + * ldb pointer here and restore in the 'done' handler + */ + save_ldb = sysdb->ldb; + + version = talloc_strdup(tmp_ctx, cur_version); + if (version == NULL) { + ret = ENOMEM; + goto done; + } + + DEBUG(SSSDBG_CONF_SETTINGS, + "Upgrading DB [%s] from version: %s\n", + domain->name, version); + + if (strcmp(version, SYSDB_VERSION_0_3) == 0) { + ret = sysdb_upgrade_03(sysdb, &version); + if (ret != EOK) { + goto done; + } + } + + if (strcmp(version, SYSDB_VERSION_0_4) == 0) { + ret = sysdb_upgrade_04(sysdb, &version); + if (ret != EOK) { + goto done; + } + } + + if (strcmp(version, SYSDB_VERSION_0_5) == 0) { + ret = sysdb_upgrade_05(sysdb, &version); + if (ret != EOK) { + goto done; + } + } + + if (strcmp(version, SYSDB_VERSION_0_6) == 0) { + ret = sysdb_upgrade_06(sysdb, &version); + if (ret != EOK) { + goto done; + } + } + + if (strcmp(version, SYSDB_VERSION_0_7) == 0) { + ret = sysdb_upgrade_07(sysdb, &version); + if (ret != EOK) { + goto done; + } + } + + if (strcmp(version, SYSDB_VERSION_0_8) == 0) { + ret = sysdb_upgrade_08(sysdb, &version); + if (ret != EOK) { + goto done; + } + } + + if (strcmp(version, SYSDB_VERSION_0_9) == 0) { + ret = sysdb_upgrade_09(sysdb, &version); + if (ret != EOK) { + goto done; + } + } + + if (strcmp(version, SYSDB_VERSION_0_10) == 0) { + ret = sysdb_upgrade_10(sysdb, domain, &version); + if (ret != EOK) { + goto done; + } + } + + if (strcmp(version, SYSDB_VERSION_0_11) == 0) { + ret = sysdb_upgrade_11(sysdb, domain, &version); + if (ret != EOK) { + goto done; + } + } + + if (strcmp(version, SYSDB_VERSION_0_12) == 0) { + ret = sysdb_upgrade_12(sysdb, &version); + if (ret != EOK) { + goto done; + } + } + + if (strcmp(version, SYSDB_VERSION_0_13) == 0) { + ret = sysdb_upgrade_13(sysdb, &version); + if (ret != EOK) { + goto done; + } + } + + if (strcmp(version, SYSDB_VERSION_0_14) == 0) { + ret = sysdb_upgrade_14(sysdb, &version); + if (ret != EOK) { + goto done; + } + } + + if (strcmp(version, SYSDB_VERSION_0_15) == 0) { + ret = sysdb_upgrade_15(sysdb, &version); + if (ret != EOK) { + goto done; + } + } + + if (strcmp(version, SYSDB_VERSION_0_16) == 0) { + ret = sysdb_upgrade_16(sysdb, &version); + if (ret != EOK) { + goto done; + } + } + + ret = EOK; +done: + sysdb->ldb = save_ldb; + *_new_version = talloc_steal(mem_ctx, version); + talloc_free(tmp_ctx); + return ret; +} + +static errno_t sysdb_cache_connect(TALLOC_CTX *mem_ctx, + struct sss_domain_info *domain, + const char *ldb_file, + int flags, + const char *exp_version, + const char *base_ldif, + struct ldb_context **_ldb, + const char **_version) +{ + TALLOC_CTX *tmp_ctx = NULL; struct ldb_message_element *el; struct ldb_result *res; struct ldb_dn *verdn; const char *version = NULL; int ret; - - sysdb = talloc_zero(mem_ctx, struct sysdb_ctx); - if (!sysdb) { - return ENOMEM; - } - - ret = sysdb_get_db_file(sysdb, domain->provider, - domain->name, db_path, - &sysdb->ldb_file); - if (ret != EOK) { - goto done; - } - DEBUG(SSSDBG_FUNC_DATA, - "DB File for %s: %s\n", domain->name, sysdb->ldb_file); - - ret = sysdb_ldb_connect(sysdb, sysdb->ldb_file, &sysdb->ldb); - if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_ldb_connect failed.\n"); - goto done; - } + struct ldb_context *ldb; tmp_ctx = talloc_new(NULL); if (!tmp_ctx) { @@ -271,13 +460,19 @@ int sysdb_domain_init_internal(TALLOC_CTX *mem_ctx, goto done; } - verdn = ldb_dn_new(tmp_ctx, sysdb->ldb, SYSDB_BASE); + ret = sysdb_ldb_connect(tmp_ctx, ldb_file, flags, &ldb); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_ldb_connect failed.\n"); + goto done; + } + + verdn = ldb_dn_new(tmp_ctx, ldb, SYSDB_BASE); if (!verdn) { ret = EIO; goto done; } - ret = ldb_search(sysdb->ldb, tmp_ctx, &res, + ret = ldb_search(ldb, tmp_ctx, &res, verdn, LDB_SCOPE_BASE, NULL, NULL); if (ret != LDB_SUCCESS) { @@ -308,119 +503,65 @@ int sysdb_domain_init_internal(TALLOC_CTX *mem_ctx, goto done; } - if (strcmp(version, SYSDB_VERSION) == 0) { - /* all fine, return */ - ret = EOK; - goto done; - } + ret = sysdb_version_check(exp_version, version); + /* This is not the latest version. Return what version it is + * and appropriate error + */ + *_version = talloc_steal(mem_ctx, version); + goto done; + } - if (!allow_upgrade) { + /* SYSDB_BASE does not exists, means db is empty, populate */ + ret = sysdb_cache_create_empty(ldb, base_ldif, domain); + if (ret != EOK) { + return ret; + } + + /* The cache has been newly created. + * We need to reopen the LDB to ensure that + * all of the special values take effect + * (such as enabling the memberOf plugin and + * the various indexes). + */ + ret = sysdb_ldb_reconnect(tmp_ctx, ldb_file, flags, &ldb); +done: + if (ret == EOK) { + *_ldb = talloc_steal(mem_ctx, ldb); + } + talloc_free(tmp_ctx); + return ret; +} + +static int sysdb_domain_cache_connect(struct sysdb_ctx *sysdb, + struct sss_domain_info *domain, + bool allow_upgrade) +{ + errno_t ret; + const char *version; + TALLOC_CTX *tmp_ctx; + struct ldb_context *ldb; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + ret = sysdb_cache_connect(tmp_ctx, domain, sysdb->ldb_file, 0, + SYSDB_VERSION, SYSDB_BASE_LDIF, + &ldb, &version); + switch (ret) { + case ERR_SYSDB_VERSION_TOO_OLD: + if (allow_upgrade == false) { DEBUG(SSSDBG_FATAL_FAILURE, - "Wrong DB version (got %s expected %s)\n", - version, SYSDB_VERSION); - ret = sysdb_version_check(SYSDB_VERSION, version); + "DB version too old [%s], expected [%s] for domain %s!\n", + version, SYSDB_VERSION, domain->name); goto done; } - DEBUG(SSSDBG_CONF_SETTINGS, "Upgrading DB [%s] from version: %s\n", - domain->name, version); - - if (strcmp(version, SYSDB_VERSION_0_3) == 0) { - ret = sysdb_upgrade_03(sysdb, &version); - if (ret != EOK) { - goto done; - } - } - - if (strcmp(version, SYSDB_VERSION_0_4) == 0) { - ret = sysdb_upgrade_04(sysdb, &version); - if (ret != EOK) { - goto done; - } - } - - if (strcmp(version, SYSDB_VERSION_0_5) == 0) { - ret = sysdb_upgrade_05(sysdb, &version); - if (ret != EOK) { - goto done; - } - } - - if (strcmp(version, SYSDB_VERSION_0_6) == 0) { - ret = sysdb_upgrade_06(sysdb, &version); - if (ret != EOK) { - goto done; - } - } - - if (strcmp(version, SYSDB_VERSION_0_7) == 0) { - ret = sysdb_upgrade_07(sysdb, &version); - if (ret != EOK) { - goto done; - } - } - - if (strcmp(version, SYSDB_VERSION_0_8) == 0) { - ret = sysdb_upgrade_08(sysdb, &version); - if (ret != EOK) { - goto done; - } - } - - if (strcmp(version, SYSDB_VERSION_0_9) == 0) { - ret = sysdb_upgrade_09(sysdb, &version); - if (ret != EOK) { - goto done; - } - } - - if (strcmp(version, SYSDB_VERSION_0_10) == 0) { - ret = sysdb_upgrade_10(sysdb, domain, &version); - if (ret != EOK) { - goto done; - } - } - - if (strcmp(version, SYSDB_VERSION_0_11) == 0) { - ret = sysdb_upgrade_11(sysdb, domain, &version); - if (ret != EOK) { - goto done; - } - } - - if (strcmp(version, SYSDB_VERSION_0_12) == 0) { - ret = sysdb_upgrade_12(sysdb, &version); - if (ret != EOK) { - goto done; - } - } - - if (strcmp(version, SYSDB_VERSION_0_13) == 0) { - ret = sysdb_upgrade_13(sysdb, &version); - if (ret != EOK) { - goto done; - } - } - - if (strcmp(version, SYSDB_VERSION_0_14) == 0) { - ret = sysdb_upgrade_14(sysdb, &version); - if (ret != EOK) { - goto done; - } - } - - if (strcmp(version, SYSDB_VERSION_0_15) == 0) { - ret = sysdb_upgrade_15(sysdb, &version); - if (ret != EOK) { - goto done; - } - } - - if (strcmp(version, SYSDB_VERSION_0_16) == 0) { - ret = sysdb_upgrade_16(sysdb, &version); - if (ret != EOK) { - goto done; - } + ret = sysdb_domain_cache_upgrade(tmp_ctx, sysdb, ldb, domain, version, + &version); + if (ret != EOK) { + goto done; } /* The version should now match SYSDB_VERSION. @@ -429,65 +570,69 @@ int sysdb_domain_init_internal(TALLOC_CTX *mem_ctx, * corrupt or generated by a newer version of * SSSD. */ - if (strcmp(version, SYSDB_VERSION) == 0) { + ret = sysdb_version_check(SYSDB_VERSION, version); + if (ret == EOK) { /* The cache has been upgraded. * We need to reopen the LDB to ensure that * any changes made above take effect. */ - talloc_zfree(sysdb->ldb); - ret = sysdb_ldb_connect(sysdb, sysdb->ldb_file, &sysdb->ldb); - if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_ldb_connect failed.\n"); - } + ret = sysdb_ldb_reconnect(tmp_ctx, sysdb->ldb_file, 0, &ldb); goto done; } - + break; + case ERR_SYSDB_VERSION_TOO_NEW: DEBUG(SSSDBG_FATAL_FAILURE, - "Unknown DB version [%s], expected [%s] for domain %s!\n", + "DB version too new [%s], expected [%s] for domain %s!\n", version, SYSDB_VERSION, domain->name); - ret = sysdb_version_check(SYSDB_VERSION, version); - goto done; + break; + default: + break; + } + +done: + if (ret == EOK) { + sysdb->ldb = talloc_steal(sysdb, ldb); } + talloc_free(tmp_ctx); + return ret; +} - /* SYSDB_BASE does not exists, means db is empty, populate */ +int sysdb_domain_init_internal(TALLOC_CTX *mem_ctx, + struct sss_domain_info *domain, + const char *db_path, + bool allow_upgrade, + struct sysdb_ctx **_ctx) +{ + TALLOC_CTX *tmp_ctx = NULL; + struct sysdb_ctx *sysdb; + int ret; - base_ldif = SYSDB_BASE_LDIF; - while ((ldif = ldb_ldif_read_string(sysdb->ldb, &base_ldif))) { - ret = ldb_add(sysdb->ldb, ldif->msg); - if (ret != LDB_SUCCESS) { - DEBUG(SSSDBG_FATAL_FAILURE, - "Failed to initialize DB (%d, [%s]) for domain %s!\n", - ret, ldb_errstring(sysdb->ldb), domain->name); - ret = EIO; - goto done; - } - ldb_ldif_read_free(sysdb->ldb, ldif); + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ENOMEM; } - ret = sysdb_domain_create(sysdb, domain->name); - if (ret != EOK) { + sysdb = talloc_zero(mem_ctx, struct sysdb_ctx); + if (!sysdb) { + ret = ENOMEM; goto done; } - /* The cache has been newly created. - * We need to reopen the LDB to ensure that - * all of the special values take effect - * (such as enabling the memberOf plugin and - * the various indexes). - */ - talloc_zfree(sysdb->ldb); - ret = sysdb_ldb_connect(sysdb, sysdb->ldb_file, &sysdb->ldb); + ret = sysdb_get_db_file(sysdb, domain->provider, + domain->name, db_path, + &sysdb->ldb_file); if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_ldb_connect failed.\n"); + goto done; } + DEBUG(SSSDBG_FUNC_DATA, + "DB File for %s: %s\n", domain->name, sysdb->ldb_file); + ret = sysdb_domain_cache_connect(sysdb, domain, allow_upgrade); done: - talloc_free(tmp_ctx); if (ret == EOK) { - *_ctx = sysdb; - } else { - talloc_free(sysdb); + *_ctx = talloc_steal(mem_ctx, sysdb); } + talloc_free(tmp_ctx); return ret; } diff --git a/src/db/sysdb_private.h b/src/db/sysdb_private.h index d0837fe232425a61a932ce81ff8ba1b0fe4bafc6..a78a9be2f780cf04181032745e13ab609bfa3f5d 100644 --- a/src/db/sysdb_private.h +++ b/src/db/sysdb_private.h @@ -96,7 +96,9 @@ struct sysdb_ctx { int sysdb_get_db_file(TALLOC_CTX *mem_ctx, const char *provider, const char *name, const char *base_path, char **_ldb_file); -errno_t sysdb_ldb_connect(TALLOC_CTX *mem_ctx, const char *filename, +errno_t sysdb_ldb_connect(TALLOC_CTX *mem_ctx, + const char *filename, + int flags, struct ldb_context **_ldb); int sysdb_domain_init_internal(TALLOC_CTX *mem_ctx, struct sss_domain_info *domain, diff --git a/src/db/sysdb_upgrade.c b/src/db/sysdb_upgrade.c index 113f24644e3e3de1d4c46d375492c2fe1e6b2f83..873c8eedede63e7089edfb18bffd9eee86ed4f69 100644 --- a/src/db/sysdb_upgrade.c +++ b/src/db/sysdb_upgrade.c @@ -289,7 +289,7 @@ int sysdb_check_upgrade_02(struct sss_domain_info *domains, goto exit; } - ret = sysdb_ldb_connect(tmp_ctx, ldb_file, &ldb); + ret = sysdb_ldb_connect(tmp_ctx, ldb_file, 0, &ldb); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_ldb_connect failed.\n"); return ret; @@ -380,7 +380,7 @@ int sysdb_check_upgrade_02(struct sss_domain_info *domains, } /* reopen */ - ret = sysdb_ldb_connect(tmp_ctx, ldb_file, &ldb); + ret = sysdb_ldb_connect(tmp_ctx, ldb_file, 0, &ldb); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_ldb_connect failed.\n"); return ret; -- 2.4.11