/* * * (c) Copyright 1989 OPEN SOFTWARE FOUNDATION, INC. * (c) Copyright 1989 HEWLETT-PACKARD COMPANY * (c) Copyright 1989 DIGITAL EQUIPMENT CORPORATION * To anyone who acknowledges that this file is provided "AS IS" * without any express or implied warranty: * permission to use, copy, modify, and distribute this * file for any purpose is hereby granted without fee, provided that * the above copyright notices and this notice appears in all source * code copies, and that none of the names of Open Software * Foundation, Inc., Hewlett-Packard Company, or Digital Equipment * Corporation be used in advertising or publicity pertaining to * distribution of the software without specific, written prior * permission. Neither Open Software Foundation, Inc., Hewlett- * Packard Company, nor Digital Equipment Corporation makes any * representations about the suitability of this software for any * purpose. * */ /* */ /* ** ** NAME: ** ** rpcddb.c ** ** FACILITY: ** ** RPC Daemon Basic Database Routines ** ** ABSTRACT: ** ** Hash table management, list management, context handle management, ** database concurrency control (lock/unlock) routines. Basic database ** open and update routine. ** ** */ #include #include #include #include #include #include typedef struct { struct db *db_handle; db_list_type_t list_type; db_lists_t *lp; unsigned32 pass; } db_contexth_t, *db_contexth_p_t; INTERNAL void list_add _DCE_PROTOTYPE_(( db_list_t *list, db_list_t *elp )); INTERNAL void list_remove _DCE_PROTOTYPE_(( db_list_t *list, db_list_t *elp )); INTERNAL boolean32 db_bad_context _DCE_PROTOTYPE_ (( struct db *h, ept_lookup_handle_t *entry_handle )); /* * Setup the persistent database image. * If no database exists on disk, create it. */ PRIVATE void db_open(h, database_file, version, status) struct db *h; unsigned char *database_file; unsigned32 version; error_status_t *status; { struct db_file_hdr hdr; error_status_t tmp_st; SET_STATUS_OK(status); dsm_open(database_file, (dsm_handle_t *) &h->dsh, status); if (STATUS_OK(status)) { /* read and check the file header info */ dsm_get_info(h->dsh,&hdr,sizeof(hdr),status); if (! STATUS_OK(status)) { if (dflag) show_st("Error reading endpoint database", status); db_to_ept_ecode(status); return; } if (hdr.version != version) { /* Bad database version * Close database * If earlier version, just delete database, * we'll try to create a new one later. * If later version, return an error */ dsm_close((dsm_handle_t *) &h->dsh, &tmp_st); h->dsh = NULL; if (hdr.version < version) { remove((char *)database_file); } else { SET_STATUS(status, ept_s_database_invalid); if (dflag) show_st("Newer persistent database version", status); return; } } else h->object = hdr.object; } else if (*status != dsm_err_open_failed) { /* file exists but couldn't open it */ if (dflag) show_st("Error opening endpoint database", status); db_to_ept_ecode(status); return; } if (h->dsh == NULL) { /* Create and initialize file */ dsm_create(database_file, (dsm_handle_t *) &h->dsh, status); if (! STATUS_OK(status)) { if (dflag) show_st("Error creating endpoint database", status); db_to_ept_ecode(status); return; } uuid_create(&h->object, &tmp_st); hdr.version = version; hdr.object = h->object; dsm_set_info(h->dsh, &hdr, sizeof(hdr), status); if (! STATUS_OK(status)) { if (dflag) show_st("Error writing to endpoint database", status); db_to_ept_ecode(status); return; } } return; } /* db_update_entry * Update an existing record in the disk copy * of a database. * DSM update is really a delete and add so * if the process crashes between dsm_detach * and dsm_write the record is lost. * * dsm_detach marks an entry as free on disk, used * in volatile memory (so it won't be given out to anyone * else during this process's lifetime but it will be * lost in a crash) */ PRIVATE void db_update_entry(h, entp, status) struct db *h; db_entry_p_t entp; error_status_t *status; { dsm_detach(h->dsh, (void *)entp, status); if (! STATUS_OK(status)) return; dsm_write(h->dsh, (void *)entp, status); if (! STATUS_OK(status)) return; } /* * Each entry is on 3 lists: the entry list (of all entries), a list that is * keyed by the entry's object uuid, and a list that is keyed by the entry's * interface uuid. An entry's object list and interface list are accessed * via uuid hash into a hash table which marks the beginning of a list for * all uuids that hash to the same value. Separate hash tables are used for * the object lists and the interface lists. * * The entry lists are not stably stored. They are recreated at startup * time. * * A database's lists are accessed via its struct db.lists_mgmt field. * The lists contain forward and back pointers with the last entry on * a list having its fwd ptr = NULL. * * The procedures that traverse lists (db_list_first, db_list_next) return a pointer * to the beginning of an entry (assuming that db_lists_t is at the beginning of * db_entry_t). To get a pointer to the beginning of the entry, these procedures * subtract a pre-inited offset from the entry's object or interface list ptr. */ INTERNAL unsigned32 db_c_object_list_offset; INTERNAL unsigned32 db_c_interface_list_offset; PRIVATE void db_init_lists(h) struct db *h; { int i; db_lists_mgmt_t *lists_mgmt; db_lists_t lists; lists_mgmt = &h->lists_mgmt; lists_mgmt->entry_list.fwd = NULL; lists_mgmt->entry_list.back = &lists_mgmt->entry_list; for (i = 0; i < db_c_nbucket; i++) { lists_mgmt->object_table[i].fwd = NULL; lists_mgmt->object_table[i].back = &lists_mgmt->object_table[i]; lists_mgmt->interface_table[i].fwd = NULL; lists_mgmt->interface_table[i].back = &lists_mgmt->interface_table[i]; } db_c_object_list_offset = ((char *) &lists.object_list) - ((char *) &lists.entry_list); db_c_interface_list_offset = ((char *) &lists.interface_list) - ((char *) &lists.entry_list); } PRIVATE void db_lists_add(h, entp) struct db *h; db_entry_t *entp; { db_list_add(&h->lists_mgmt.entry_list, db_c_entry_list, (db_lists_t *) entp); db_htable_add(h->lists_mgmt.object_table, db_c_object_list, &entp->object, (db_lists_t *) entp); db_htable_add(h->lists_mgmt.interface_table, db_c_interface_list, &entp->interface.uuid, (db_lists_t *) entp); } PRIVATE void db_lists_remove(h, entp) struct db *h; db_entry_t *entp; { db_list_remove(&h->lists_mgmt.entry_list, db_c_entry_list, (db_lists_t *) entp); db_htable_remove(h->lists_mgmt.object_table, db_c_object_list, &entp->object, (db_lists_t *) entp); db_htable_remove(h->lists_mgmt.interface_table, db_c_interface_list, &entp->interface.uuid, (db_lists_t *) entp); } PRIVATE void db_htable_add(htable, table_type, id, entp) db_hash_table_t htable; db_list_type_t table_type; uuid_p_t id; db_lists_t *entp; { unsigned16 bucket; error_status_t tmp_st; bucket = uuid_hash(id, &tmp_st); bucket = bucket % db_c_nbucket; db_list_add(&htable[bucket], table_type, entp); } PRIVATE void db_htable_remove(htable, table_type, id, entp) db_hash_table_t htable; db_list_type_t table_type; uuid_p_t id; db_lists_t *entp; { unsigned16 bucket; error_status_t tmp_st; bucket = uuid_hash(id, &tmp_st); bucket = bucket % db_c_nbucket; db_list_remove(&htable[bucket], table_type, entp); } /* * Add an entry (entp) to the beginning of list. * The entry is being added to one of 3 lists * which is defined by list_type */ PRIVATE void db_list_add(list, list_type, entp) db_list_t *list; db_list_type_t list_type; db_lists_t *entp; { db_list_t *elp; error_status_t tmp_st; switch(list_type) { case db_c_entry_list: elp = &entp->entry_list; break; case db_c_object_list: elp = &entp->object_list; break; case db_c_interface_list: elp = &entp->interface_list; break; default: tmp_st = ept_s_database_invalid; show_st("db_list_add - bad list type", &tmp_st); return; } list_add(list, elp); } PRIVATE void db_list_remove(list, list_type, entp) db_list_t *list; db_list_type_t list_type; db_lists_t *entp; { db_list_t *elp; error_status_t tmp_st; switch(list_type) { case db_c_entry_list: elp = &entp->entry_list; break; case db_c_object_list: elp = &entp->object_list; break; case db_c_interface_list: elp = &entp->interface_list; break; default: tmp_st = ept_s_database_invalid; show_st("db_list_remove - bad list type", &tmp_st); return; } list_remove(list, elp); } /* * Add entry elp to the end of list */ INTERNAL void list_add(list, elp) db_list_t *list; db_list_t *elp; { elp->fwd = NULL; elp->back = list->back; elp->back->fwd = elp; list->back = elp; } /* Remove entry elp from a list */ INTERNAL void list_remove(list, elp) db_list_t *list; db_list_t *elp; { elp->back->fwd = elp->fwd; if (elp->fwd != NULL) elp->fwd->back = elp->back; else list->back = elp->back; /* remove from end of list */ elp->fwd = NULL; elp->back = NULL; } /* Return a pointer to the first entry on list list_type */ PRIVATE db_lists_t *db_list_first(lists_mgmt, list_type, id) db_lists_mgmt_t *lists_mgmt; db_list_type_t list_type; uuid_p_t id; { unsigned16 bucket; error_status_t tmp_st; db_list_t *elp; db_lists_t *entp; entp = NULL; switch(list_type) { case db_c_entry_list: entp = (db_lists_t *) lists_mgmt->entry_list.fwd; break; case db_c_object_list: bucket = uuid_hash(id, &tmp_st); bucket = bucket % db_c_nbucket; elp = lists_mgmt->object_table[bucket].fwd; if (elp != NULL) entp = (db_lists_t *) (((char *) elp) - db_c_object_list_offset); break; case db_c_interface_list: bucket = uuid_hash(id, &tmp_st); bucket = bucket % db_c_nbucket; elp = lists_mgmt->interface_table[bucket].fwd; if (elp != NULL) entp = (db_lists_t *) (((char *) elp) - db_c_interface_list_offset); break; default: tmp_st = ept_s_database_invalid; show_st("db_list_first - bad list type", &tmp_st); break; } return(entp); } /* Return a pointer to the next entry (after xentp) on list list_type */ PRIVATE db_lists_t *db_list_next(list_type, xentp) db_list_type_t list_type; db_lists_t *xentp; { db_list_t *elp; db_lists_t *entp; error_status_t tmp_st; entp = NULL; switch(list_type) { case db_c_entry_list: entp = (db_lists_t *) xentp->entry_list.fwd; break; case db_c_object_list: elp = xentp->object_list.fwd; if (elp != NULL) entp = (db_lists_t *) (((char *) elp) - db_c_object_list_offset); break; case db_c_interface_list: elp = xentp->interface_list.fwd; if (elp != NULL) entp = (db_lists_t *) (((char *) elp) - db_c_interface_list_offset); break; default: tmp_st = ept_s_database_invalid; show_st("db_list_next - bad list type", &tmp_st); break; } return(entp); } /* Save context * epdb_lookup, fwd, map take an ept_entry_handle_t context handle * argument which supports iterative search of the database. The list * type, list entry pointer, and pass number are saved so the search * can resume where it left off. */ PRIVATE void db_save_context(h, entry_handle, list_type, lp, pass) struct db *h; ept_lookup_handle_t *entry_handle; db_list_type_t list_type; db_lists_t *lp; unsigned32 pass; { db_contexth_t *chp; db_entry_t *entp; /* * If the entry list pointer is NULL assume that the * job is done - just delete the context (if one has been * established) and return */ if (lp == NULL) { db_delete_context(h, entry_handle); return; } /* Check that context handle is ok */ if (db_bad_context(h, entry_handle)) return; /* If the context already exists, decrement the number of * read references in the entry to which the old context * points; this context won't point to the entry any more. * If the context does not exist, malloc space for it. * * Set chp and *entry_handle to the context's address. */ if (*entry_handle != NULL) { chp = (db_contexth_t *) (*entry_handle); entp = (db_entry_t *) chp->lp; entp->read_nrefs--; } else { *entry_handle = (ept_lookup_handle_t *) malloc(sizeof(db_contexth_t)); if (*entry_handle == NULL) return; chp = (db_contexth_t *) (*entry_handle); } /* Increment read ref count of the entry to which context will point. * Check for read refs overflow */ entp = (db_entry_t *) lp; entp->read_nrefs++; if (entp->read_nrefs >= db_c_max_read_nrefs) { db_delete_context(h, entry_handle); return; } /* Save new context info */ chp->db_handle = h; chp->list_type = list_type; chp->lp = lp; chp->pass = pass; } PRIVATE void db_delete_context(h, entry_handle) struct db *h; ept_lookup_handle_t *entry_handle; { db_contexth_t *chp; db_entry_t *entp; if (db_bad_context(h, entry_handle) || (*entry_handle == NULL)) return; chp = (db_contexth_t *) (*entry_handle); entp = (db_entry_t *) chp->lp; entp->read_nrefs--; free(chp); *entry_handle = NULL; } PRIVATE void db_get_context(h, entry_handle, list_type, lp, pass, status) struct db *h; ept_lookup_handle_t *entry_handle; db_list_type_t *list_type; db_lists_t **lp; unsigned32 *pass; error_status_t *status; { db_contexth_t *chp; if (db_bad_context(h, entry_handle) || (*entry_handle == NULL)) { SET_STATUS(status, ept_s_invalid_context); return; } chp = (db_contexth_t *) *entry_handle; *list_type = chp->list_type; *lp = chp->lp; *pass = chp->pass; SET_STATUS_OK(status); } /* Check whether this entry_handle's context * points to the dbase denoted by h. * Return true and status = ept_s_invalid_context * if entry_handle's db_handle field != h arg. * Otherwise return false. */ PRIVATE boolean32 db_different_context(h, entry_handle, status) struct db *h; ept_lookup_handle_t *entry_handle; error_status_t *status; { db_contexth_t *chp; SET_STATUS_OK(status); if (entry_handle == NULL) return(false); if (*entry_handle != NULL) { chp = (db_contexth_t *) *entry_handle; if (chp->db_handle != h) { SET_STATUS(status, ept_s_invalid_context); return(true); } } return(false); } /* return true if entry_handle is a "bad" context * handle - ie. * there's no place to pass the handle back * to the caller (ie. he really didn't want to * save context) * the context handle doesn't point to this database * return false if entry_handle appears to be ok */ INTERNAL boolean32 db_bad_context(h, entry_handle) struct db *h; ept_lookup_handle_t *entry_handle; { db_contexth_t *chp; if (entry_handle == NULL) return(true); if (*entry_handle != NULL) { chp = (db_contexth_t *) *entry_handle; if (chp->db_handle != h) return(true); } return(false); } /* * Note that the rpcddb package must protect a database from * concurrent access since there can be a call executor thread, * a dg forward map callback thread, and a check server liveness * thread. */ PRIVATE void db_lock(h) struct db *h; { dcethread_mutex_lock_throw(&h->lock); } PRIVATE void db_unlock(h) struct db *h; { dcethread_mutex_unlock_throw(&h->lock); } PRIVATE void db_init_lock(h) struct db *h; { dcethread_mutex_init_throw(&h->lock, NULL); } /* Map dsm error codes to ept error codes */ PRIVATE void db_to_ept_ecode(status) error_status_t *status; { switch ((int)*status) { case dsm_err_create_failed: *status = ept_s_cant_create; break; case dsm_err_file_io_error: *status = ept_s_update_failed; break; case dsm_err_open_failed: *status = ept_s_cant_access; break; case dsm_err_version: *status = ept_s_database_invalid; break; case dsm_err_no_memory: *status = ept_s_no_memory; break; case dsm_err_duplicate_write: *status = ept_s_update_failed; break; case dsm_err_header_too_long: *status = ept_s_database_invalid; break; case dsm_err_no_more_entries: *status = ept_s_not_registered; break; case dsm_err_invalid_handle: *status = ept_s_cant_perform_op; break; case dsm_err_invalid_pointer: *status = ept_s_cant_perform_op; break; case dsm_err_info_too_long: *status = ept_s_cant_perform_op; break; case dsm_err_file_busy: *status = ept_s_database_already_open; break; case dsm_err_invalid_marker: *status = ept_s_cant_perform_op; break; default: break; } }