/* * Opens the backend database. * * ARGUMENTS: * backend Pointer to pointer to backend structure. Shall not be * NULL. Upon successful return, "*backend" will be set. * The client should call "beClose(*backend)" when the * backend is no longer needed. * dir Pathname of the parent directory of the database. * Shall not be NULL. The client can free it upon return. * forWriting Open the database for writing? 0 <=> no * RETURNS: * 0 Success. "*backend" is set. * ENOMEM System error. "log_start()" called. * EIO Backend database error. "log_start()" called. */ RegStatus beOpen( Backend** const backend, const char* const dir, int forWriting) { RegStatus status; Backend* back = (Backend*)malloc(sizeof(Backend)); assert(NULL != dir); if (NULL == back) { log_serror("Couldn't allocate %lu bytes", (long)sizeof(Backend)); status = ENOMEM; } else { DB_ENV* env; DB* db; StringBuf* path; if (0 == (status = sb_new(&path, PATH_MAX))) { if (0 == (status = sb_cat(path, dir, "/", DB_DIRNAME))) { if (0 == (status = createDbHandle(path, &env, &db))) { if (status = db->open(db, NULL, DB_FILENAME, NULL, DB_BTREE, forWriting ? DB_CREATE : DB_RDONLY, 0)) { log_add("Couldn't open database \"%s\" in \"%s\" " "for %s", DB_FILENAME, path, forWriting ? "writing" : "reading"); status = EIO; } else { back->db = db; back->cursor.dbCursor = NULL; *backend = back; /* success */ } /* "db" opened */ /* * According to the documentation on DB->open(), if that * call fails, then DB->close() must be called to discard * the DB handle, so DB->close() is the termination * counterpart of db_create() rather than of DB->open(). */ if (status) { (void)db->close(db, 0); (void)env->close(env, 0); } } /* "env" allocated */ } /* DB directory pathname created */ sb_free(path); } /* "path" allocated */ if (status) free(back); } /* "back" allocated */ return status; }
/** * Allocates memory. Thread safe. * * @param nbytes Number of bytes to allocate. * @param msg Message to print on error. Should complete the sentence * "Couldn't allocate <n> bytes for ...". * @param file Name of the file. * @param line Line number in the file. * @retval NULL Out of memory. \c log_start() called. * @return Pointer to the allocated memory. */ void* log_malloc( const size_t nbytes, const char* const msg, const char* const file, const int line) { void* obj = malloc(nbytes); if (obj == NULL) log_serror(LOG_FMT("Couldn't allocate %lu bytes for %s"), file, line, nbytes, msg); return obj; }
/* * Creates a database environment handle * * ARGUMENTS: * path Pathname of the database directory. Shall not be NULL. * The client can free it upon return. * dbEnv Pointer to a pointer to the database environment. Shall * not be NULL. "*dbEnv" is set upon successful return. * RETURNS: * 0 Success. "*dbEnv" is set. * ENOMEM System error. "log_start()" called. * EIO Backend database error. "log_start()" called. */ static RegStatus createEnvHandle( const char* const path, DB_ENV** const dbEnv) { RegStatus status; DB_ENV* env; assert(NULL != path); log_clear(); if (status = db_env_create(&env, 0)) { log_serror("Couldn't create environment handle for database: %s", db_strerror(status)); status = ENOMEM; } else { env->set_errcall(env, logDbError); if (status = env->set_isalive(env, is_alive)) { log_add("Couldn't register \"is_alive()\" function for " "database \"%s\"", path); status = EIO; } else { static const unsigned threadCount = 256; if (status = env->set_thread_count(env, threadCount)) { log_add("Couldn't set thread count to %u for database \"%s\"", threadCount, path); status = EIO; } else { *dbEnv = env; } } if (status) (void)env->close(env, 0); } /* "env" allocated */ return status; }
/* * Constructs a path name for the database. * * Arguments: * path Pathname of the database directory. Shall not be NULL. * The client can free it upon return. * ext Extension for the database path name. * Returns: * NULL System error. "log_start()" called. * else Pointer to the path name of the database. The * client should call "free()" when the path name is no * longer needed. */ static char* makeDatabasePath( const char* const path, const char* const ext) { static const size_t lenFilename = sizeof(DB_FILENAME) - 1; size_t lenPath = strlen(path); size_t len = strlen(path) + 1 + lenFilename + strlen(ext) + 1; char* buf = (char*)malloc(len); if (NULL == buf) { log_serror("Couldn't allocate %lu bytes", (unsigned long)len); } else { (void)strcpy(strcpy(strcpy(strcpy(buf, path) + lenPath, "/") + 1, DB_FILENAME) + lenFilename, ext); } return buf; }
/* * Sets the cursor to reference the first entry in the backend * database whose key is greater than or equal to a given key. * * ARGUMENTS: * backend Pointer to the backend database. Shall have been * set by beOpen(). Shall not be NULL. * key Pointer to the starting key. Shall not be NULL. The * empty string obtains the first entry in the database, * if it exists. * RETURNS * 0 Success. * EINVAL The cursor is not initialized. * ENOENT The database is empty. * EIO Backend database error. "log_start()" called. * ENOMEM System error. "log_start()" called. */ RegStatus beFirstEntry( Backend* const backend, const char* const key) { RegStatus status; Cursor* cursor; assert(NULL != backend); assert(NULL != key); cursor = &backend->cursor; if (!cursor->dbCursor) { log_start("Cursor for backend database \"%s\" not initialized", getPath(backend->db)); status = EINVAL; } else { char* const dupKey = strdup(key); if (NULL == dupKey) { log_serror("Couldn't allocate %lu bytes", (long)strlen(key)); status = ENOMEM; } else { backend->cursor.key.data = dupKey; backend->cursor.key.size = strlen(dupKey) + 1; if (EIO == (status = setCursor(&backend->cursor, DB_SET_RANGE))) { log_add("Couldn't set cursor for database \"%s\" to first " "entry on or after key \"%s\"", getPath(backend->db), key); } } /* "dupKey" allocated */ } return status; }
/* * Returns a new instance of an LDM proxy. Can take a while because it * establishes a connection to the LDM. * * Arguments: * host Identifier of the host on which an LDM server is * running. * instance Pointer to a pointer to the new instance. "*instance" * is set upon successful return. * Returns: * 0 Success. "*instance" is set. * LP_SYSTEM System error. "log_start()" called. * LP_TIMEDOUT Connection attempt timed-out. "log_start()" called. * LP_HOSTUNREACH Host is unreachable. "log_start()" called. * LP_RPC_ERROR RPC error. "log_start()" called. * LP_LDM_ERROR LDM error. "log_start()" called. */ LdmProxyStatus lp_new( const char* const host, LdmProxy** const instance) { LdmProxyStatus status = 0; /* success */ size_t nbytes = sizeof(LdmProxy); LdmProxy* proxy = (LdmProxy*)malloc(nbytes); if (NULL == proxy) { log_serror("Couldn't allocate %lu bytes for new LdmProxy", nbytes); status = LP_SYSTEM; } else { proxy->host = strdup(host); if (NULL == proxy->host) { LOG_SERROR1("Couldn't duplicate string \"%s\"", host); status = LP_SYSTEM; } else { CLIENT* clnt = NULL; ErrorObj* error = ldm_clnttcp_create_vers(host, LDM_PORT, 6, &clnt, NULL, NULL); if (!error) { proxy->version = 6; proxy->hiya = my_hiya_6; proxy->send = my_send_6; proxy->flush = my_flush_6; } else if (LDM_CLNT_BAD_VERSION == err_code(error)) { /* Couldn't connect due to protocol version. */ err_free(error); error = ldm_clnttcp_create_vers(host, LDM_PORT, 5, &clnt, NULL, NULL); if (!error) { proxy->version = 5; proxy->hiya = my_hiya_5; proxy->send = my_send_5; proxy->flush = my_flush_5; } } if (error) { LOG_START1("%s", err_message(error)); err_free(error); free(proxy->host); status = convertStatus(error); } else { proxy->clnt = clnt; proxy->rpcTimeout = rpcTimeout; } } /* "proxy->host" allocated */ if (LP_OK == status) { *instance = proxy; } else { free(proxy); } } /* "proxy" allocated */ return status; }