diff options
Diffstat (limited to 'usr/src')
-rw-r--r-- | usr/src/cmd/mdb/common/modules/libuutil/libuutil.c | 20 | ||||
-rw-r--r-- | usr/src/cmd/svc/configd/backend.c | 215 | ||||
-rw-r--r-- | usr/src/cmd/svc/configd/client.c | 78 | ||||
-rw-r--r-- | usr/src/cmd/svc/configd/configd.c | 42 | ||||
-rw-r--r-- | usr/src/cmd/svc/configd/configd.h | 14 | ||||
-rw-r--r-- | usr/src/cmd/svc/configd/file_object.c | 276 | ||||
-rw-r--r-- | usr/src/cmd/svc/configd/restore_repository.sh | 55 | ||||
-rw-r--r-- | usr/src/lib/libscf/common/lowlevel.c | 156 | ||||
-rw-r--r-- | usr/src/lib/libscf/common/lowlevel_impl.h | 8 | ||||
-rw-r--r-- | usr/src/lib/libuutil/common/libuutil_impl.h | 34 | ||||
-rw-r--r-- | usr/src/lib/libuutil/common/uu_avl.c | 45 | ||||
-rw-r--r-- | usr/src/lib/libuutil/common/uu_list.c | 32 |
12 files changed, 773 insertions, 202 deletions
diff --git a/usr/src/cmd/mdb/common/modules/libuutil/libuutil.c b/usr/src/cmd/mdb/common/modules/libuutil/libuutil.c index d9444cc3ce..aa890302ec 100644 --- a/usr/src/cmd/mdb/common/modules/libuutil/libuutil.c +++ b/usr/src/cmd/mdb/common/modules/libuutil/libuutil.c @@ -20,7 +20,7 @@ * CDDL HEADER END */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -106,8 +106,8 @@ uutil_list(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) "ADDR", "POOL", "PARENT", "NODES", "FLAGS"); mdb_printf("%0?p %0?p %0?p %6u %c%c\n", - addr, ul.ul_pool, ul.ul_parent, ul.ul_numnodes, - ul.ul_sorted ? 'S' : ' ', ul.ul_debug? 'D' : ' '); + addr, ul.ul_pool, UU_PTR_DECODE(ul.ul_parent_enc), + ul.ul_numnodes, ul.ul_sorted ? 'S' : ' ', ul.ul_debug? 'D' : ' '); return (DCMD_OK); } @@ -185,21 +185,23 @@ typedef struct uutil_list_walk { int uutil_list_walk_init(mdb_walk_state_t *wsp) { - uu_list_t null_list; uutil_list_walk_t *ulw; uu_list_pool_t ulp; - bzero(&null_list, sizeof (uu_list_t)); - if (mdb_vread(&ulp, sizeof (uu_list_pool_t), wsp->walk_addr) == -1) { mdb_warn("failed to read uu_list_pool_t at given address\n"); return (WALK_ERR); } + if (UU_LIST_PTR(ulp.ulp_null_list.ul_next_enc) == + &((uu_list_pool_t *)wsp->walk_addr)->ulp_null_list) + return (WALK_DONE); + ulw = mdb_alloc(sizeof (uutil_list_walk_t), UM_SLEEP); - ulw->ulw_final = (uintptr_t)ulp.ulp_null_list.ul_prev; - ulw->ulw_current = (uintptr_t)ulp.ulp_null_list.ul_next; + ulw->ulw_final = (uintptr_t)UU_LIST_PTR(ulp.ulp_null_list.ul_prev_enc); + ulw->ulw_current = + (uintptr_t)UU_LIST_PTR(ulp.ulp_null_list.ul_next_enc); wsp->walk_data = ulw; return (WALK_NEXT); @@ -222,7 +224,7 @@ uutil_list_walk_step(mdb_walk_state_t *wsp) if (ulw->ulw_current == ulw->ulw_final) return (WALK_DONE); - ulw->ulw_current = (uintptr_t)ul.ul_next; + ulw->ulw_current = (uintptr_t)UU_LIST_PTR(ul.ul_next_enc); return (status); } diff --git a/usr/src/cmd/svc/configd/backend.c b/usr/src/cmd/svc/configd/backend.c index 141df0c168..8589e82b7d 100644 --- a/usr/src/cmd/svc/configd/backend.c +++ b/usr/src/cmd/svc/configd/backend.c @@ -20,12 +20,20 @@ * CDDL HEADER END */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" +/* + * sqlite is not compatible with _FILE_OFFSET_BITS=64, but we need to + * be able to statvfs(2) possibly large systems. This define gives us + * access to the transitional interfaces. See lfcompile64(5) for how + * _LARGEFILE64_SOURCE works. + */ +#define _LARGEFILE64_SOURCE + #include <assert.h> #include <door.h> #include <dirent.h> @@ -37,6 +45,8 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <sys/stat.h> +#include <sys/statvfs.h> #include <unistd.h> #include <zone.h> @@ -72,9 +82,10 @@ typedef struct sqlite_backend { pthread_t be_thread; /* thread holding lock */ struct sqlite *be_db; const char *be_path; /* path to db */ - int be_readonly; /* backend is read-only */ + int be_readonly; /* readonly at start, and still is */ int be_writing; /* held for writing */ backend_type_t be_type; /* type of db */ + hrtime_t be_lastcheck; /* time of last read-only check */ backend_totals_t be_totals[2]; /* one for reading, one for writing */ } sqlite_backend_t; @@ -119,6 +130,9 @@ int backend_do_trace = 0; /* invoke tracing callback */ int backend_print_trace = 0; /* tracing callback prints SQL */ int backend_panic_abort = 0; /* abort when panicking */ +/* interval between read-only checks while starting up */ +#define BACKEND_READONLY_CHECK_INTERVAL (2 * (hrtime_t)NANOSEC) + /* * Any change to the below schema should bump the version number */ @@ -319,18 +333,49 @@ backend_fail_if_seen(void *arg, int columns, char **vals, char **names) return (BACKEND_CALLBACK_ABORT); } +/* + * check to see if we can successfully start a transaction; if not, the + * filesystem is mounted read-only. + */ static int -backend_is_readonly(struct sqlite *db, char **errp) +backend_is_readonly(struct sqlite *db, const char *path) { - int r = sqlite_exec(db, + int r; + statvfs64_t stat; + + if (statvfs64(path, &stat) == 0 && (stat.f_flag & ST_RDONLY)) + return (SQLITE_READONLY); + + r = sqlite_exec(db, "BEGIN TRANSACTION; " "UPDATE schema_version SET schema_version = schema_version; ", - NULL, NULL, errp); - + NULL, NULL, NULL); (void) sqlite_exec(db, "ROLLBACK TRANSACTION", NULL, NULL, NULL); return (r); } +/* + * Check to see if the administrator has removed the writable bits on the + * repository file. If they have, we don't allow modifications. + * + * Since we normally run with PRIV_FILE_DAC_WRITE, we have to use a separate + * check. + */ +static int +backend_check_perm(const char *path) +{ + struct stat64 stat; + + if (access(path, W_OK) < 0) + return (SQLITE_READONLY); + + if (stat64(path, &stat) == 0 && + !(stat.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH))) + return (SQLITE_READONLY); + + return (SQLITE_OK); +} + static void backend_trace_sql(void *arg, const char *sql) { @@ -655,6 +700,68 @@ backend_backup_base(sqlite_backend_t *be, const char *name, } /* + * See if a backup is needed. We do a backup unless both files are + * byte-for-byte identical. + */ +static int +backend_check_backup_needed(const char *rep_name, const char *backup_name) +{ + int repfd = open(rep_name, O_RDONLY); + int fd = open(backup_name, O_RDONLY); + struct stat s_rep, s_backup; + int c1, c2; + + FILE *f_rep = NULL; + FILE *f_backup = NULL; + + if (repfd < 0 || fd < 0) + goto fail; + + if (fstat(repfd, &s_rep) < 0 || fstat(fd, &s_backup) < 0) + goto fail; + + /* + * if they are the same file, we need to do a backup to break the + * hard link or symlink involved. + */ + if (s_rep.st_ino == s_backup.st_ino && s_rep.st_dev == s_backup.st_dev) + goto fail; + + if (s_rep.st_size != s_backup.st_size) + goto fail; + + if ((f_rep = fdopen(repfd, "r")) == NULL || + (f_backup = fdopen(fd, "r")) == NULL) + goto fail; + + do { + c1 = getc(f_rep); + c2 = getc(f_backup); + if (c1 != c2) + goto fail; + } while (c1 != EOF); + + if (!ferror(f_rep) && !ferror(f_backup)) { + (void) fclose(f_rep); + (void) fclose(f_backup); + (void) close(repfd); + (void) close(fd); + return (0); + } + +fail: + if (f_rep != NULL) + (void) fclose(f_rep); + if (f_backup != NULL) + (void) fclose(f_backup); + if (repfd >= 0) + (void) close(repfd); + if (fd >= 0) + (void) close(fd); + return (1); +} + +/* * Can return: * _BAD_REQUEST name is not valid * _TRUNCATED name is too long for current repository path @@ -693,6 +800,10 @@ backend_create_backup_locked(sqlite_backend_t *be, const char *name) if (result != REP_PROTOCOL_SUCCESS) return (result); + if (!backend_check_backup_needed(be->be_path, finalpath)) { + return (REP_PROTOCOL_SUCCESS); + } + /* * remember the original length, and the basename location */ @@ -819,6 +930,58 @@ fail: return (result); } +static int +backend_check_readonly(sqlite_backend_t *be, int writing, hrtime_t t) +{ + char *errp; + struct sqlite *new; + int r; + + assert(be->be_readonly); + assert(be == bes[BACKEND_TYPE_NORMAL]); + + /* + * If we don't *need* to be writable, only check every once in a + * while. + */ + if (!writing) { + if ((uint64_t)(t - be->be_lastcheck) < + BACKEND_READONLY_CHECK_INTERVAL) + return (REP_PROTOCOL_SUCCESS); + be->be_lastcheck = t; + } + + new = sqlite_open(be->be_path, 0600, &errp); + if (new == NULL) { + backend_panic("reopening %s: %s\n", be->be_path, errp); + /*NOTREACHED*/ + } + r = backend_is_readonly(new, be->be_path); + + if (r != SQLITE_OK) { + sqlite_close(new); + if (writing) + return (REP_PROTOCOL_FAIL_BACKEND_READONLY); + return (REP_PROTOCOL_SUCCESS); + } + + /* + * We can write! Swap the db handles, mark ourself writable, + * and make a backup. + */ + sqlite_close(be->be_db); + be->be_db = new; + be->be_readonly = 0; + + if (backend_create_backup_locked(be, REPOSITORY_BOOT_BACKUP) != + REP_PROTOCOL_SUCCESS) { + configd_critical( + "unable to create \"%s\" backup of \"%s\"\n", + REPOSITORY_BOOT_BACKUP, be->be_path); + } + + return (REP_PROTOCOL_SUCCESS); +} /* * If t is not BACKEND_TYPE_NORMAL, can fail with @@ -859,41 +1022,23 @@ backend_lock(backend_type_t t, int writing, sqlite_backend_t **bep) } be->be_thread = pthread_self(); - if (writing && be->be_readonly) { - char *errp; - struct sqlite *new; + if (be->be_readonly) { int r; - assert(t == BACKEND_TYPE_NORMAL); - new = sqlite_open(be->be_path, 0600, &errp); - if (new == NULL) { - backend_panic("reopening %s: %s\n", be->be_path, errp); - /*NOTREACHED*/ - } - r = backend_is_readonly(new, &errp); - if (r != SQLITE_OK) { - free(errp); - sqlite_close(new); + r = backend_check_readonly(be, writing, ts); + if (r != REP_PROTOCOL_SUCCESS) { be->be_thread = 0; (void) pthread_mutex_unlock(&be->be_lock); - return (REP_PROTOCOL_FAIL_BACKEND_READONLY); + return (r); } + } - /* - * We can write! Swap our db handles, mark ourself writable, - * and make a backup. - */ - sqlite_close(be->be_db); - be->be_db = new; - be->be_readonly = 0; - - if (backend_create_backup_locked(be, REPOSITORY_BOOT_BACKUP) != - REP_PROTOCOL_SUCCESS) { - configd_critical( - "unable to create \"%s\" backup of \"%s\"\n", - REPOSITORY_BOOT_BACKUP, be->be_path); - } + if (writing && t == BACKEND_TYPE_NORMAL && + backend_check_perm(be->be_path) != SQLITE_OK) { + be->be_thread = 0; + (void) pthread_mutex_unlock(&be->be_lock); + return (REP_PROTOCOL_FAIL_BACKEND_READONLY); } if (backend_do_trace) @@ -1192,7 +1337,7 @@ integrity_fail: /* * check if we are writable */ - r = backend_is_readonly(be->be_db, &errp); + r = backend_is_readonly(be->be_db, be->be_path); if (r == SQLITE_BUSY || r == SQLITE_LOCKED) { free(errp); diff --git a/usr/src/cmd/svc/configd/client.c b/usr/src/cmd/svc/configd/client.c index 49aed805d1..72aa30d12e 100644 --- a/usr/src/cmd/svc/configd/client.c +++ b/usr/src/cmd/svc/configd/client.c @@ -72,8 +72,8 @@ #pragma align 64(client_hash) static client_bucket_t client_hash[CLIENT_HASH_SIZE]; -static uu_list_pool_t *entity_pool; -static uu_list_pool_t *iter_pool; +static uu_avl_pool_t *entity_pool; +static uu_avl_pool_t *iter_pool; static uu_list_pool_t *client_pool; #define CLIENT_HASH(id) (&client_hash[((id) & (CLIENT_HASH_SIZE - 1))]) @@ -228,14 +228,14 @@ client_hash_init(void) int x; assert_nolint(offsetof(repcache_entity_t, re_id) == 0); - entity_pool = uu_list_pool_create("repcache_entitys", + entity_pool = uu_avl_pool_create("repcache_entitys", sizeof (repcache_entity_t), offsetof(repcache_entity_t, re_link), - entity_compare, UU_LIST_POOL_DEBUG); + entity_compare, UU_AVL_POOL_DEBUG); assert_nolint(offsetof(repcache_iter_t, ri_id) == 0); - iter_pool = uu_list_pool_create("repcache_iters", + iter_pool = uu_avl_pool_create("repcache_iters", sizeof (repcache_iter_t), offsetof(repcache_iter_t, ri_link), - iter_compare, UU_LIST_POOL_DEBUG); + iter_compare, UU_AVL_POOL_DEBUG); assert_nolint(offsetof(repcache_client_t, rc_id) == 0); client_pool = uu_list_pool_create("repcache_clients", @@ -266,12 +266,12 @@ client_alloc(void) if (cp == NULL) return (NULL); - cp->rc_entity_list = uu_list_create(entity_pool, cp, UU_LIST_SORTED); - if (cp->rc_entity_list == NULL) + cp->rc_entities = uu_avl_create(entity_pool, cp, 0); + if (cp->rc_entities == NULL) goto fail; - cp->rc_iter_list = uu_list_create(iter_pool, cp, UU_LIST_SORTED); - if (cp->rc_iter_list == NULL) + cp->rc_iters = uu_avl_create(iter_pool, cp, 0); + if (cp->rc_iters == NULL) goto fail; uu_list_node_init(cp, &cp->rc_link, client_pool); @@ -286,10 +286,10 @@ client_alloc(void) return (cp); fail: - if (cp->rc_iter_list != NULL) - uu_list_destroy(cp->rc_iter_list); - if (cp->rc_entity_list != NULL) - uu_list_destroy(cp->rc_entity_list); + if (cp->rc_iters != NULL) + uu_avl_destroy(cp->rc_iters); + if (cp->rc_entities != NULL) + uu_avl_destroy(cp->rc_entities); uu_free(cp); return (NULL); } @@ -301,10 +301,10 @@ client_free(repcache_client_t *cp) assert(cp->rc_refcnt == 0); assert(cp->rc_doorfd == -1); assert(cp->rc_doorid == INVALID_DOORID); - assert(uu_list_first(cp->rc_entity_list) == NULL); - assert(uu_list_first(cp->rc_iter_list) == NULL); - uu_list_destroy(cp->rc_entity_list); - uu_list_destroy(cp->rc_iter_list); + assert(uu_avl_first(cp->rc_entities) == NULL); + assert(uu_avl_first(cp->rc_iters) == NULL); + uu_avl_destroy(cp->rc_entities); + uu_avl_destroy(cp->rc_iters); uu_list_node_fini(cp, &cp->rc_link, client_pool); (void) pthread_mutex_destroy(&cp->rc_lock); uu_free(cp); @@ -398,7 +398,7 @@ entity_alloc(repcache_client_t *cp) { repcache_entity_t *ep = uu_zalloc(sizeof (repcache_entity_t)); if (ep != NULL) { - uu_list_node_init(ep, &ep->re_link, entity_pool); + uu_avl_node_init(ep, &ep->re_link, entity_pool); } return (ep); } @@ -406,13 +406,13 @@ entity_alloc(repcache_client_t *cp) static void entity_add(repcache_client_t *cp, repcache_entity_t *ep) { - uu_list_index_t idx; + uu_avl_index_t idx; (void) pthread_mutex_lock(&cp->rc_lock); assert(cp->rc_insert_thr == pthread_self()); - (void) uu_list_find(cp->rc_entity_list, ep, NULL, &idx); - uu_list_insert(cp->rc_entity_list, ep, idx); + (void) uu_avl_find(cp->rc_entities, ep, NULL, &idx); + uu_avl_insert(cp->rc_entities, ep, idx); (void) pthread_mutex_unlock(&cp->rc_lock); } @@ -423,7 +423,7 @@ entity_find(repcache_client_t *cp, uint32_t id) repcache_entity_t *ep; (void) pthread_mutex_lock(&cp->rc_lock); - ep = uu_list_find(cp->rc_entity_list, &id, NULL, NULL); + ep = uu_avl_find(cp->rc_entities, &id, NULL, NULL); if (ep != NULL) { add_log_ptr(get_log(), RC_PTR_TYPE_ENTITY, id, ep); (void) pthread_mutex_lock(&ep->re_lock); @@ -449,8 +449,8 @@ entity_find2(repcache_client_t *cp, uint32_t id1, repcache_entity_t **out1, return (REP_PROTOCOL_FAIL_DUPLICATE_ID); (void) pthread_mutex_lock(&cp->rc_lock); - e1 = uu_list_find(cp->rc_entity_list, &id1, NULL, NULL); - e2 = uu_list_find(cp->rc_entity_list, &id2, NULL, NULL); + e1 = uu_avl_find(cp->rc_entities, &id1, NULL, NULL); + e2 = uu_avl_find(cp->rc_entities, &id2, NULL, NULL); if (e1 == NULL || e2 == NULL) { (void) pthread_mutex_unlock(&cp->rc_lock); return (REP_PROTOCOL_FAIL_UNKNOWN_ID); @@ -496,7 +496,7 @@ entity_destroy(repcache_entity_t *entity) rc_node_clear(&entity->re_node, 0); (void) pthread_mutex_unlock(&entity->re_lock); - uu_list_node_fini(entity, &entity->re_link, entity_pool); + uu_avl_node_fini(entity, &entity->re_link, entity_pool); (void) pthread_mutex_destroy(&entity->re_lock); uu_free(entity); } @@ -507,9 +507,9 @@ entity_remove(repcache_client_t *cp, uint32_t id) repcache_entity_t *entity; (void) pthread_mutex_lock(&cp->rc_lock); - entity = uu_list_find(cp->rc_entity_list, &id, NULL, NULL); + entity = uu_avl_find(cp->rc_entities, &id, NULL, NULL); if (entity != NULL) - uu_list_remove(cp->rc_entity_list, entity); + uu_avl_remove(cp->rc_entities, entity); (void) pthread_mutex_unlock(&cp->rc_lock); if (entity != NULL) @@ -523,7 +523,7 @@ entity_cleanup(repcache_client_t *cp) void *cookie = NULL; (void) pthread_mutex_lock(&cp->rc_lock); - while ((ep = uu_list_teardown(cp->rc_entity_list, &cookie)) != NULL) { + while ((ep = uu_avl_teardown(cp->rc_entities, &cookie)) != NULL) { (void) pthread_mutex_unlock(&cp->rc_lock); entity_destroy(ep); (void) pthread_mutex_lock(&cp->rc_lock); @@ -538,7 +538,7 @@ iter_alloc(repcache_client_t *cp) repcache_iter_t *iter; iter = uu_zalloc(sizeof (repcache_iter_t)); if (iter != NULL) - uu_list_node_init(iter, &iter->ri_link, iter_pool); + uu_avl_node_init(iter, &iter->ri_link, iter_pool); return (iter); } @@ -550,8 +550,8 @@ iter_add(repcache_client_t *cp, repcache_iter_t *iter) (void) pthread_mutex_lock(&cp->rc_lock); assert(cp->rc_insert_thr == pthread_self()); - (void) uu_list_find(cp->rc_iter_list, iter, NULL, &idx); - uu_list_insert(cp->rc_iter_list, iter, idx); + (void) uu_avl_find(cp->rc_iters, iter, NULL, &idx); + uu_avl_insert(cp->rc_iters, iter, idx); (void) pthread_mutex_unlock(&cp->rc_lock); } @@ -563,7 +563,7 @@ iter_find(repcache_client_t *cp, uint32_t id) (void) pthread_mutex_lock(&cp->rc_lock); - iter = uu_list_find(cp->rc_iter_list, &id, NULL, NULL); + iter = uu_avl_find(cp->rc_iters, &id, NULL, NULL); if (iter != NULL) { add_log_ptr(get_log(), RC_PTR_TYPE_ITER, id, iter); (void) pthread_mutex_lock(&iter->ri_lock); @@ -586,8 +586,8 @@ iter_find_w_entity(repcache_client_t *cp, uint32_t iter_id, request_log_entry_t *rlp; (void) pthread_mutex_lock(&cp->rc_lock); - iter = uu_list_find(cp->rc_iter_list, &iter_id, NULL, NULL); - ep = uu_list_find(cp->rc_entity_list, &entity_id, NULL, NULL); + iter = uu_avl_find(cp->rc_iters, &iter_id, NULL, NULL); + ep = uu_avl_find(cp->rc_entities, &entity_id, NULL, NULL); assert(iter == NULL || !MUTEX_HELD(&iter->ri_lock)); assert(ep == NULL || !MUTEX_HELD(&ep->re_lock)); @@ -626,7 +626,7 @@ iter_destroy(repcache_iter_t *iter) rc_iter_destroy(&iter->ri_iter); (void) pthread_mutex_unlock(&iter->ri_lock); - uu_list_node_fini(iter, &iter->ri_link, iter_pool); + uu_avl_node_fini(iter, &iter->ri_link, iter_pool); (void) pthread_mutex_destroy(&iter->ri_lock); uu_free(iter); } @@ -637,9 +637,9 @@ iter_remove(repcache_client_t *cp, uint32_t id) repcache_iter_t *iter; (void) pthread_mutex_lock(&cp->rc_lock); - iter = uu_list_find(cp->rc_iter_list, &id, NULL, NULL); + iter = uu_avl_find(cp->rc_iters, &id, NULL, NULL); if (iter != NULL) - uu_list_remove(cp->rc_iter_list, iter); + uu_avl_remove(cp->rc_iters, iter); (void) pthread_mutex_unlock(&cp->rc_lock); if (iter != NULL) @@ -653,7 +653,7 @@ iter_cleanup(repcache_client_t *cp) void *cookie = NULL; (void) pthread_mutex_lock(&cp->rc_lock); - while ((iter = uu_list_teardown(cp->rc_iter_list, &cookie)) != NULL) { + while ((iter = uu_avl_teardown(cp->rc_iters, &cookie)) != NULL) { (void) pthread_mutex_unlock(&cp->rc_lock); iter_destroy(iter); (void) pthread_mutex_lock(&cp->rc_lock); diff --git a/usr/src/cmd/svc/configd/configd.c b/usr/src/cmd/svc/configd/configd.c index 32f6f318c6..5632735834 100644 --- a/usr/src/cmd/svc/configd/configd.c +++ b/usr/src/cmd/svc/configd/configd.c @@ -20,7 +20,7 @@ * CDDL HEADER END */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -30,6 +30,7 @@ #include <door.h> #include <errno.h> #include <fcntl.h> +#include <limits.h> #include <priv.h> #include <procfs.h> #include <pthread.h> @@ -162,7 +163,8 @@ thread_exiting(void *arg) { thread_info_t *ti = arg; - log_enter(&ti->ti_log); + if (ti != NULL) + log_enter(&ti->ti_log); (void) pthread_mutex_lock(&thread_lock); if (ti != NULL) { @@ -471,6 +473,23 @@ daemonize_ready(void) (void) close(pipe_fd); } +const char * +regularize_path(const char *dir, const char *base, char *tmpbuf) +{ + if (base == NULL) + return (NULL); + if (base[0] == '/') + return (base); + + if (snprintf(tmpbuf, PATH_MAX, "%s/%s", dir, base) >= PATH_MAX) { + (void) fprintf(stderr, "svc.configd: %s/%s: path too long\n", + dir, base); + exit(CONFIGD_EXIT_BAD_ARGS); + } + + return (tmpbuf); +} + int main(int argc, char *argv[]) { @@ -485,6 +504,12 @@ main(int argc, char *argv[]) int c; int ret; int fd; + + char curdir[PATH_MAX]; + char dbtmp[PATH_MAX]; + char npdbtmp[PATH_MAX]; + char doortmp[PATH_MAX]; + const char *dbpath = NULL; const char *npdbpath = NULL; const char *doorpath = REPOSITORY_DOOR_NAME; @@ -495,13 +520,20 @@ main(int argc, char *argv[]) closefrom(3); /* get rid of extraneous fds */ + if (getcwd(curdir, sizeof (curdir)) == NULL) { + (void) fprintf(stderr, + "%s: unable to get current directory: %s\n", + argv[0], strerror(errno)); + exit(CONFIGD_EXIT_INIT_FAILED); + } + while ((c = getopt(argc, argv, "Dnpd:r:t:")) != -1) { switch (c) { case 'n': daemonize = 0; break; case 'd': - doorpath = optarg; + doorpath = regularize_path(curdir, optarg, doortmp); is_main_repository = 0; have_npdb = 0; /* default to no non-persist */ break; @@ -527,11 +559,11 @@ main(int argc, char *argv[]) privileged_psinfo_fd = fd; break; case 'r': - dbpath = optarg; + dbpath = regularize_path(curdir, optarg, dbtmp); is_main_repository = 0; break; case 't': - npdbpath = optarg; + npdbpath = regularize_path(curdir, optarg, npdbtmp); is_main_repository = 0; break; default: diff --git a/usr/src/cmd/svc/configd/configd.h b/usr/src/cmd/svc/configd/configd.h index f00bb6467b..7d01a9005e 100644 --- a/usr/src/cmd/svc/configd/configd.h +++ b/usr/src/cmd/svc/configd/configd.h @@ -20,7 +20,7 @@ * CDDL HEADER END */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -404,7 +404,7 @@ enum repcache_txstate { typedef struct repcache_entity { uint32_t re_id; - uu_list_node_t re_link; + uu_avl_node_t re_link; uint32_t re_changeid; pthread_mutex_t re_lock; @@ -415,7 +415,7 @@ typedef struct repcache_entity { typedef struct repcache_iter { uint32_t ri_id; - uu_list_node_t ri_link; + uu_avl_node_t ri_link; uint32_t ri_type; /* result type */ @@ -452,10 +452,10 @@ typedef struct repcache_client { rc_node_ptr_t rc_notify_ptr; /* - * register lists, protected by rc_lock + * register sets, protected by rc_lock */ - uu_list_t *rc_entity_list; /* entities */ - uu_list_t *rc_iter_list; /* iters */ + uu_avl_t *rc_entities; + uu_avl_t *rc_iters; /* * Variables, protected by rc_lock @@ -660,7 +660,7 @@ int object_create_pg(rc_node_t *, uint32_t, const char *, const char *, int object_delete(rc_node_t *); void object_free_values(const char *, uint32_t, size_t, size_t); -int object_fill_snapshot(rc_snapshot_t *sp); +int object_fill_snapshot(rc_snapshot_t *); int object_snapshot_take_new(rc_node_t *, const char *, const char *, const char *, rc_node_t **); diff --git a/usr/src/cmd/svc/configd/file_object.c b/usr/src/cmd/svc/configd/file_object.c index 52bff4858f..b4f177f1eb 100644 --- a/usr/src/cmd/svc/configd/file_object.c +++ b/usr/src/cmd/svc/configd/file_object.c @@ -94,6 +94,14 @@ typedef struct object_info { int (*obj_delete_start)(rc_node_t *, delete_info_t *); } object_info_t; +static void +string_to_id(const char *str, uint32_t *output, const char *fieldname) +{ + if (uu_strtouint(str, output, sizeof (*output), 0, 0, 0) == -1) + backend_panic("invalid integer \"%s\" in field \"%s\"", + str, fieldname); +} + #define NUM_NEEDED 50 static int @@ -191,10 +199,8 @@ push_delete_callback(void *data, int columns, char **vals, char **names) assert(columns == 2); - if (uu_strtouint(id_str, &id, sizeof (id), 0, 0, 0) == -1) - backend_panic("invalid integer in database"); - if (uu_strtouint(gen_str, &gen, sizeof (gen), 0, 0, 0) == -1) - backend_panic("invalid integer in database"); + string_to_id(id_str, &id, "id"); + string_to_id(gen_str, &gen, "gen_id"); info->dci_result = delete_stack_push(info->dci_dip, info->dci_be, info->dci_cb, id, gen); @@ -536,8 +542,7 @@ fill_child_callback(void *data, int columns, char **vals, char **names) cur = *vals++; columns--; - if (uu_strtouint(cur, &main_id, sizeof (main_id), 0, 0, 0) == -1) - backend_panic("invalid integer in database"); + string_to_id(cur, &main_id, "id"); lp->rl_main_id = main_id; @@ -572,9 +577,9 @@ fill_snapshot_callback(void *data, int columns, char **vals, char **names) columns--; snap = *vals++; columns--; - if (uu_strtouint(cur, &main_id, sizeof (main_id), 0, 0, 0) == -1 || - uu_strtouint(snap, &snap_id, sizeof (snap_id), 0, 0, 0) == -1) - backend_panic("invalid integer in database"); + + string_to_id(cur, &main_id, "lnk_id"); + string_to_id(snap, &snap_id, "lnk_snap_id"); lp->rl_main_id = main_id; @@ -609,23 +614,20 @@ fill_pg_callback(void *data, int columns, char **vals, char **names) cur = *vals++; /* pg_id */ columns--; - if (uu_strtouint(cur, &main_id, sizeof (main_id), 0, 0, 0) == -1) - backend_panic("invalid integer in database"); + string_to_id(cur, &main_id, "pg_id"); lp->rl_main_id = main_id; cur = *vals++; /* pg_gen_id */ columns--; - if (uu_strtouint(cur, &gen_id, sizeof (gen_id), 0, 0, 0) == -1) - backend_panic("invalid integer in database"); + string_to_id(cur, &gen_id, "pg_gen_id"); type = *vals++; /* pg_type */ columns--; cur = *vals++; /* pg_flags */ columns--; - if (uu_strtouint(cur, &flags, sizeof (flags), 0, 0, 0) == -1) - backend_panic("invalid integer in database"); + string_to_id(cur, &flags, "pg_flags"); if ((newnode = rc_node_alloc()) == NULL) return (BACKEND_CALLBACK_ABORT); @@ -723,8 +725,7 @@ fill_property_callback(void *data, int columns, char **vals, char **names) name = *vals++; cur = *vals++; - if (uu_strtouint(cur, &main_id, sizeof (main_id), 0, 0, 0) == -1) - backend_panic("invalid integer in database"); + string_to_id(cur, &main_id, "lnk_prop_id"); cur = *vals++; assert(('a' <= cur[0] && 'z' >= cur[0]) || @@ -1764,17 +1765,12 @@ fill_snapshot_cb(void *data, int columns, char **vals, char **names) lvl->rsl_next = sp->rs_levels; sp->rs_levels = lvl; - if (uu_strtouint(num, &lvl->rsl_level_num, - sizeof (lvl->rsl_level_num), 0, 0, 0) == -1 || - uu_strtouint(id, &lvl->rsl_level_id, - sizeof (lvl->rsl_level_id), 0, 0, 0) == -1 || - uu_strtouint(service_id, &lvl->rsl_service_id, - sizeof (lvl->rsl_level_num), 0, 0, 0) == -1 || - (instance_id != NULL && - uu_strtouint(instance_id, &lvl->rsl_instance_id, - sizeof (lvl->rsl_instance_id), 0, 0, 0) == -1)) { - backend_panic("invalid integer in database"); - } + string_to_id(num, &lvl->rsl_level_num, "snap_level_num"); + string_to_id(id, &lvl->rsl_level_id, "snap_level_id"); + string_to_id(service_id, &lvl->rsl_service_id, "snap_level_service_id"); + if (instance_id != NULL) + string_to_id(instance_id, &lvl->rsl_instance_id, + "snap_level_instance_id"); lvl->rsl_scope = (const char *)"localhost"; lvl->rsl_service = strdup(service); @@ -1837,6 +1833,226 @@ object_fill_snapshot(rc_snapshot_t *sp) return (result); } +/* + * This represents a property group in a snapshot. + */ +typedef struct check_snapshot_elem { + uint32_t cse_parent; + uint32_t cse_pg_id; + uint32_t cse_pg_gen; + char cse_seen; +} check_snapshot_elem_t; + +#define CSI_MAX_PARENTS COMPOSITION_DEPTH +typedef struct check_snapshot_info { + size_t csi_count; + size_t csi_array_size; + check_snapshot_elem_t *csi_array; + size_t csi_nparents; + uint32_t csi_parent_ids[CSI_MAX_PARENTS]; +} check_snapshot_info_t; + +/*ARGSUSED*/ +static int +check_snapshot_fill_cb(void *data, int columns, char **vals, char **names) +{ + check_snapshot_info_t *csip = data; + check_snapshot_elem_t *cur; + const char *parent; + const char *pg_id; + const char *pg_gen_id; + + if (columns == 1) { + uint32_t *target; + + if (csip->csi_nparents >= CSI_MAX_PARENTS) + backend_panic("snaplevel table has too many elements"); + + target = &csip->csi_parent_ids[csip->csi_nparents++]; + string_to_id(vals[0], target, "snap_level_*_id"); + + return (BACKEND_CALLBACK_CONTINUE); + } + + assert(columns == 3); + + parent = vals[0]; + pg_id = vals[1]; + pg_gen_id = vals[2]; + + if (csip->csi_count == csip->csi_array_size) { + size_t newsz = (csip->csi_array_size > 0) ? + csip->csi_array_size * 2 : 8; + check_snapshot_elem_t *new = uu_zalloc(newsz * sizeof (*new)); + + if (new == NULL) + return (BACKEND_CALLBACK_ABORT); + + (void) memcpy(new, csip->csi_array, + sizeof (*new) * csip->csi_array_size); + uu_free(csip->csi_array); + csip->csi_array = new; + csip->csi_array_size = newsz; + } + + cur = &csip->csi_array[csip->csi_count++]; + + string_to_id(parent, &cur->cse_parent, "snap_level_*_id"); + string_to_id(pg_id, &cur->cse_pg_id, "snaplvl_pg_id"); + string_to_id(pg_gen_id, &cur->cse_pg_gen, "snaplvl_gen_id"); + cur->cse_seen = 0; + + return (BACKEND_CALLBACK_CONTINUE); +} + +static int +check_snapshot_elem_cmp(const void *lhs_arg, const void *rhs_arg) +{ + const check_snapshot_elem_t *lhs = lhs_arg; + const check_snapshot_elem_t *rhs = rhs_arg; + + if (lhs->cse_parent < rhs->cse_parent) + return (-1); + if (lhs->cse_parent > rhs->cse_parent) + return (1); + + if (lhs->cse_pg_id < rhs->cse_pg_id) + return (-1); + if (lhs->cse_pg_id > rhs->cse_pg_id) + return (1); + + if (lhs->cse_pg_gen < rhs->cse_pg_gen) + return (-1); + if (lhs->cse_pg_gen > rhs->cse_pg_gen) + return (1); + + return (0); +} + +/*ARGSUSED*/ +static int +check_snapshot_check_cb(void *data, int columns, char **vals, char **names) +{ + check_snapshot_info_t *csip = data; + check_snapshot_elem_t elem; + check_snapshot_elem_t *cur; + + const char *parent = vals[0]; + const char *pg_id = vals[1]; + const char *pg_gen_id = vals[2]; + + assert(columns == 3); + + string_to_id(parent, &elem.cse_parent, "snap_level_*_id"); + string_to_id(pg_id, &elem.cse_pg_id, "snaplvl_pg_id"); + string_to_id(pg_gen_id, &elem.cse_pg_gen, "snaplvl_gen_id"); + + if ((cur = bsearch(&elem, csip->csi_array, csip->csi_count, + sizeof (*csip->csi_array), check_snapshot_elem_cmp)) == NULL) + return (BACKEND_CALLBACK_ABORT); + + if (cur->cse_seen) + backend_panic("duplicate property group reported"); + cur->cse_seen = 1; + return (BACKEND_CALLBACK_CONTINUE); +} + +/* + * Check that a snapshot matches up with the latest in the repository. + * Returns: + * REP_PROTOCOL_SUCCESS if it is up-to-date, + * REP_PROTOCOL_DONE if it is out-of-date, or + * REP_PROTOCOL_FAIL_NO_RESOURCES if we ran out of memory. + */ +static int +object_check_snapshot(uint32_t snap_id) +{ + check_snapshot_info_t csi; + backend_query_t *q; + int result; + size_t idx; + + /* if the snapshot has never been taken, it must be out of date. */ + if (snap_id == 0) + return (REP_PROTOCOL_DONE); + + (void) memset(&csi, '\0', sizeof (csi)); + + q = backend_query_alloc(); + backend_query_add(q, + "SELECT\n" + " CASE snap_level_instance_id\n" + " WHEN 0 THEN snap_level_service_id\n" + " ELSE snap_level_instance_id\n" + " END\n" + "FROM snaplevel_tbl\n" + "WHERE snap_id = %d;\n" + "\n" + "SELECT\n" + " CASE snap_level_instance_id\n" + " WHEN 0 THEN snap_level_service_id\n" + " ELSE snap_level_instance_id\n" + " END,\n" + " snaplvl_pg_id,\n" + " snaplvl_gen_id\n" + "FROM snaplevel_tbl, snaplevel_lnk_tbl\n" + "WHERE\n" + " (snaplvl_level_id = snap_level_id AND\n" + " snap_id = %d);", + snap_id, snap_id); + + result = backend_run(BACKEND_TYPE_NORMAL, q, check_snapshot_fill_cb, + &csi); + if (result == REP_PROTOCOL_DONE) + result = REP_PROTOCOL_FAIL_NO_RESOURCES; + backend_query_free(q); + + if (result != REP_PROTOCOL_SUCCESS) + goto fail; + + if (csi.csi_count > 0) { + qsort(csi.csi_array, csi.csi_count, sizeof (*csi.csi_array), + check_snapshot_elem_cmp); + } + +#if COMPOSITION_DEPTH == 2 + if (csi.csi_nparents != COMPOSITION_DEPTH) { + result = REP_PROTOCOL_DONE; + goto fail; + } + + q = backend_query_alloc(); + backend_query_add(q, + "SELECT " + " pg_parent_id, pg_id, pg_gen_id " + "FROM " + " pg_tbl " + "WHERE (pg_parent_id = %d OR pg_parent_id = %d)", + csi.csi_parent_ids[0], csi.csi_parent_ids[1]); + + result = backend_run(BACKEND_TYPE_NORMAL, q, check_snapshot_check_cb, + &csi); +#else +#error This code must be updated +#endif + /* + * To succeed, the callback must not have aborted, and we must have + * found all of the items. + */ + if (result == REP_PROTOCOL_SUCCESS) { + for (idx = 0; idx < csi.csi_count; idx++) { + if (csi.csi_array[idx].cse_seen == 0) { + result = REP_PROTOCOL_DONE; + goto fail; + } + } + } + +fail: + uu_free(csi.csi_array); + return (result); +} + /*ARGSUSED*/ static int object_copy_string(void *data_arg, int columns, char **vals, char **names) @@ -2115,6 +2331,10 @@ object_snapshot_attach(rc_node_lookup_t *snapi, uint32_t *snapid_ptr, return (REP_PROTOCOL_FAIL_TYPE_MISMATCH); if (takesnap) { + /* first, check that we're actually out of date */ + if (object_check_snapshot(snapid) == REP_PROTOCOL_SUCCESS) + return (REP_PROTOCOL_SUCCESS); + result = object_snapshot_do_take(instid, NULL, svcid, NULL, &tx, &snapid); if (result != REP_PROTOCOL_SUCCESS) diff --git a/usr/src/cmd/svc/configd/restore_repository.sh b/usr/src/cmd/svc/configd/restore_repository.sh index 08e916b0f9..8ba38ce70f 100644 --- a/usr/src/cmd/svc/configd/restore_repository.sh +++ b/usr/src/cmd/svc/configd/restore_repository.sh @@ -21,28 +21,24 @@ # CDDL HEADER END # # -# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Copyright 2005 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" PATH=/sbin:/usr/bin:/usr/sbin +LC_ALL=C +export PATH LC_ALL . /lib/svc/share/smf_include.sh . /lib/svc/share/fs_include.sh -echo >&2 " -Repository Restore utility - -See http://sun.com/msg/SMF-8000-MY for more information on the use of -this script to restore backup copies of the smf(5) repository. - -If there are any problems which need human intervention, this script will -give instructions and then exit back to your shell." - usage() { echo "usage: $0 [-r rootdir]" >&2 + echo " +See http://sun.com/msg/SMF-8000-MY for more information on the use of +this script." exit 2; } @@ -79,6 +75,23 @@ if [ $OPTIND -le $# ]; then usage fi +# +# Note that the below test is carefully constructed to fail *open*; if +# anything goes wrong, it will drive onward. +# +if [ -x /usr/bin/id -a -x /usr/bin/grep ] && + /usr/bin/id 2>/dev/null | /usr/bin/grep -v '^[^=]*=0(' >/dev/null 2>&1; then + echo "$0: may only be invoked by root" >&2 + exit 2 +fi + +echo >&2 " +See http://sun.com/msg/SMF-8000-MY for more information on the use of +this script to restore backup copies of the smf(5) repository. + +If there are any problems which need human intervention, this script will +give instructions and then exit back to your shell." + if [ "$myroot" -eq / ]; then system="system" [ "`/sbin/zonename`" != global ] && system="zone" @@ -99,6 +112,7 @@ rootro=false if [ ! -x /usr/bin/pgrep ]; then nouser=true fi + if [ ! -w "$myroot" ]; then rootro=true fi @@ -142,7 +156,8 @@ if [ -z "$oldreps" ]; then cat >&2 <<EOF There are no available backups of $myroot$repositorydir/$repository.db. The only available repository is "-seed-". Note that restoring the seed -will lose all customizations, and XXX other issues? +will lose all customizations, including those made by the system during +the installation and/or upgrade process. EOF prompt="Enter -seed- to restore from the seed, or -quit- to exit: \c" @@ -160,13 +175,17 @@ the repository after system boot. Backups beginning with "manifest_import" are made after svc:/system/manifest-import:default finishes its processing. The time of backup is given in YYYYMMDD_HHMMSS format. -Please enter one of: - 1) boot, for the most recent post-boot backup - 2) manifest_import, for the most recent manifest_import backup. - 3) a specific backup repository from the above list - 4) -seed-, the initial starting repository. (All customizations - will be lost.) - 5) -quit-, to cancel. +Please enter either a specific backup repository from the above list to +restore it, or one of the following choices: + + CHOICE ACTION + ---------------- ---------------------------------------------- + boot restore the most recent post-boot backup + manifest_import restore the most recent manifest_import backup + -seed- restore the initial starting repository (All + customizations will be lost, including those + made by the install/upgrade process.) + -quit- cancel script and quit EOF prompt="Enter response [boot]: \c" diff --git a/usr/src/lib/libscf/common/lowlevel.c b/usr/src/lib/libscf/common/lowlevel.c index ae9108ab95..16c375d5e2 100644 --- a/usr/src/lib/libscf/common/lowlevel.c +++ b/usr/src/lib/libscf/common/lowlevel.c @@ -20,7 +20,7 @@ * CDDL HEADER END */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -177,7 +177,35 @@ transaction_entry_compare(const void *l_arg, const void *r_arg, void *private) ret = strcmp(l_prop, r_prop); if (ret > 0) return (1); - else if (ret < 0) + if (ret < 0) + return (-1); + return (0); +} + +static int +datael_compare(const void *l_arg, const void *r_arg, void *private) +{ + uint32_t l_id = ((scf_datael_t *)l_arg)->rd_entity; + uint32_t r_id = (r_arg != NULL) ? ((scf_datael_t *)r_arg)->rd_entity : + *(uint32_t *)private; + + if (l_id > r_id) + return (1); + if (l_id < r_id) + return (-1); + return (0); +} + +static int +iter_compare(const void *l_arg, const void *r_arg, void *private) +{ + uint32_t l_id = ((scf_iter_t *)l_arg)->iter_id; + uint32_t r_id = (r_arg != NULL) ? ((scf_iter_t *)r_arg)->iter_id : + *(uint32_t *)private; + + if (l_id > r_id) + return (1); + if (l_id < r_id) return (-1); return (0); } @@ -209,11 +237,11 @@ lowlevel_init(void) datael_pool = uu_list_pool_create("SUNW,libscf_datael", sizeof (scf_datael_t), offsetof(scf_datael_t, rd_node), - NULL, UU_LIST_POOL_DEBUG); + datael_compare, UU_LIST_POOL_DEBUG); iter_pool = uu_list_pool_create("SUNW,libscf_iter", sizeof (scf_iter_t), offsetof(scf_iter_t, iter_node), - NULL, UU_LIST_POOL_DEBUG); + iter_compare, UU_LIST_POOL_DEBUG); assert_nolint(offsetof(scf_transaction_entry_t, entry_property) == 0); @@ -506,14 +534,22 @@ handle_is_bound(scf_handle_t *h) } static int -handle_has_server(scf_handle_t *h) +handle_has_server_locked(scf_handle_t *h) { door_info_t i; + assert(MUTEX_HELD(&h->rh_lock)); + + return (handle_is_bound(h) && door_info(h->rh_doorfd, &i) != -1 && + i.di_target != -1); +} + +static int +handle_has_server(scf_handle_t *h) +{ int ret; (void) pthread_mutex_lock(&h->rh_lock); - ret = (handle_is_bound(h) && door_info(h->rh_doorfd, &i) != -1 && - i.di_target != -1); + ret = handle_has_server_locked(h); (void) pthread_mutex_unlock(&h->rh_lock); return (ret); @@ -1267,24 +1303,91 @@ scf_myname(scf_handle_t *h, char *out, size_t len) } static uint32_t -handle_alloc_entityid(scf_handle_t *handle) +handle_alloc_entityid(scf_handle_t *h) { - assert(MUTEX_HELD(&handle->rh_lock)); - return (++handle->rh_nextentity); + uint32_t nextid; + + assert(MUTEX_HELD(&h->rh_lock)); + + if (uu_list_numnodes(h->rh_dataels) == UINT32_MAX) + return (0); /* no ids available */ + + /* + * The following loop assumes that there are not a huge number of + * outstanding entities when we've wrapped. If that ends up not + * being the case, the O(N^2) nature of this search will hurt a lot, + * and the data structure should be switched to an AVL tree. + */ + nextid = h->rh_nextentity + 1; + for (;;) { + scf_datael_t *cur; + + if (nextid == 0) { + nextid++; + h->rh_flags |= HANDLE_WRAPPED_ENTITY; + } + if (!(h->rh_flags & HANDLE_WRAPPED_ENTITY)) + break; + + cur = uu_list_find(h->rh_dataels, NULL, &nextid, NULL); + if (cur == NULL) + break; /* not in use */ + + if (nextid == h->rh_nextentity) + return (0); /* wrapped around; no ids available */ + nextid++; + } + + h->rh_nextentity = nextid; + return (nextid); } static uint32_t -handle_alloc_iterid(scf_handle_t *handle) +handle_alloc_iterid(scf_handle_t *h) { - assert(MUTEX_HELD(&handle->rh_lock)); - return (++handle->rh_nextiter); + uint32_t nextid; + + assert(MUTEX_HELD(&h->rh_lock)); + + if (uu_list_numnodes(h->rh_iters) == UINT32_MAX) + return (0); /* no ids available */ + + /* see the comment in handle_alloc_entityid */ + nextid = h->rh_nextiter + 1; + for (;;) { + scf_iter_t *cur; + + if (nextid == 0) { + nextid++; + h->rh_flags |= HANDLE_WRAPPED_ITER; + } + if (!(h->rh_flags & HANDLE_WRAPPED_ITER)) + break; /* not yet wrapped */ + + cur = uu_list_find(h->rh_iters, NULL, &nextid, NULL); + if (cur == NULL) + break; /* not in use */ + + if (nextid == h->rh_nextiter) + return (0); /* wrapped around; no ids available */ + nextid++; + } + + h->rh_nextiter = nextid; + return (nextid); } static uint32_t -handle_alloc_changeid(scf_handle_t *handle) +handle_next_changeid(scf_handle_t *handle) { + uint32_t nextid; + assert(MUTEX_HELD(&handle->rh_lock)); - return (++handle->rh_nextchangeid); + + nextid = ++handle->rh_nextchangeid; + if (nextid == 0) + nextid = ++handle->rh_nextchangeid; + return (nextid); } /* @@ -1320,6 +1423,11 @@ datael_init(scf_datael_t *dp, scf_handle_t *h, uint32_t type) return (scf_set_error(SCF_ERROR_HANDLE_DESTROYED)); } dp->rd_entity = handle_alloc_entityid(h); + if (dp->rd_entity == 0) { + (void) pthread_mutex_unlock(&h->rh_lock); + uu_list_node_fini(dp, &dp->rd_node, datael_pool); + return (scf_set_error(SCF_ERROR_NO_MEMORY)); + } ret = datael_attach(dp); if (ret == 0) { @@ -1767,7 +1875,7 @@ datael_add_child(const scf_datael_t *dp, const char *name, uint32_t type, request.rpr_childid = cp->rd_entity; datael_finish_reset(dp); - request.rpr_changeid = handle_alloc_changeid(h); + request.rpr_changeid = handle_next_changeid(h); r = make_door_call(h, &request, sizeof (request), &response, sizeof (response)); (void) pthread_mutex_unlock(&h->rh_lock); @@ -1831,7 +1939,7 @@ datael_add_pg(const scf_datael_t *dp, const char *name, const char *type, datael_finish_reset(dp); datael_finish_reset(cp); - request.rpr_changeid = handle_alloc_changeid(h); + request.rpr_changeid = handle_next_changeid(h); r = make_door_call(h, &request, sizeof (request), &response, sizeof (response)); (void) pthread_mutex_unlock(&h->rh_lock); @@ -1867,7 +1975,7 @@ datael_delete(const scf_datael_t *dp) request.rpr_entityid = dp->rd_entity; datael_finish_reset(dp); - request.rpr_changeid = handle_alloc_changeid(h); + request.rpr_changeid = handle_next_changeid(h); r = make_door_call(h, &request, sizeof (request), &response, sizeof (response)); (void) pthread_mutex_unlock(&h->rh_lock); @@ -1913,6 +2021,12 @@ scf_iter_create(scf_handle_t *h) (void) pthread_mutex_lock(&h->rh_lock); iter->iter_id = handle_alloc_iterid(h); + if (iter->iter_id == 0) { + (void) pthread_mutex_unlock(&h->rh_lock); + uu_list_node_fini(iter, &iter->iter_node, iter_pool); + (void) scf_set_error(SCF_ERROR_NO_MEMORY); + return (NULL); + } if (iter_attach(iter) == -1) { uu_list_node_fini(iter, &iter->iter_node, iter_pool); (void) pthread_mutex_unlock(&h->rh_lock); @@ -2030,7 +2144,7 @@ scf_iter_handle_scopes(scf_iter_t *iter, const scf_handle_t *handle) return (scf_set_error(SCF_ERROR_NOT_BOUND)); } - if (!handle_has_server(h)) { + if (!handle_has_server_locked(h)) { (void) pthread_mutex_unlock(&h->rh_lock); return (scf_set_error(SCF_ERROR_CONNECTION_BROKEN)); } @@ -2894,7 +3008,7 @@ datael_update(scf_datael_t *dp) request.rpr_entityid = dp->rd_entity; datael_finish_reset(dp); - request.rpr_changeid = handle_alloc_changeid(h); + request.rpr_changeid = handle_next_changeid(h); r = make_door_call(h, &request, sizeof (request), &response, sizeof (response)); @@ -6606,7 +6720,7 @@ _scf_request_backup(scf_handle_t *h, const char *name) (void) pthread_mutex_lock(&h->rh_lock); request.rpr_request = REP_PROTOCOL_BACKUP; - request.rpr_changeid = handle_alloc_changeid(h); + request.rpr_changeid = handle_next_changeid(h); r = make_door_call(h, &request, sizeof (request), &response, sizeof (response)); diff --git a/usr/src/lib/libscf/common/lowlevel_impl.h b/usr/src/lib/libscf/common/lowlevel_impl.h index 5137eaf8ad..a3bc40aed9 100644 --- a/usr/src/lib/libscf/common/lowlevel_impl.h +++ b/usr/src/lib/libscf/common/lowlevel_impl.h @@ -20,7 +20,7 @@ * CDDL HEADER END */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -101,8 +101,10 @@ struct scf_handle { scf_property_t *rh_property; scf_value_t *rh_value; }; -#define HANDLE_DEAD 0x0001 -#define HANDLE_UNREFED 0x0002 +#define HANDLE_DEAD 0x0001 +#define HANDLE_UNREFED 0x0002 +#define HANDLE_WRAPPED_ENTITY 0x0004 +#define HANDLE_WRAPPED_ITER 0x0008 #define RH_HOLD_ITER 0x0001 #define RH_HOLD_SCOPE 0x0002 diff --git a/usr/src/lib/libuutil/common/libuutil_impl.h b/usr/src/lib/libuutil/common/libuutil_impl.h index 101671095a..9466e59745 100644 --- a/usr/src/lib/libuutil/common/libuutil_impl.h +++ b/usr/src/lib/libuutil/common/libuutil_impl.h @@ -20,7 +20,7 @@ * CDDL HEADER END */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -33,6 +33,7 @@ #include <pthread.h> #include <sys/avl_impl.h> +#include <sys/byteorder.h> #ifdef __cplusplus extern "C" { @@ -52,6 +53,21 @@ struct uu_dprintf { }; /* + * For debugging purposes, libuutil keeps around linked lists of all uu_lists + * and uu_avls, along with pointers to their parents. These can cause false + * negatives when looking for memory leaks, so we encode the pointers by + * storing them with swapped endianness; this is not perfect, but it's about + * the best we can do without wasting a lot of space. + */ +#ifdef _LP64 +#define UU_PTR_ENCODE(ptr) BSWAP_64((uintptr_t)(void *)(ptr)) +#else +#define UU_PTR_ENCODE(ptr) BSWAP_32((uintptr_t)(void *)(ptr)) +#endif + +#define UU_PTR_DECODE(ptr) ((void *)UU_PTR_ENCODE(ptr)) + +/* * uu_list structures */ typedef struct uu_list_node_impl { @@ -70,11 +86,11 @@ struct uu_list_walk { }; struct uu_list { - uu_list_t *ul_next; - uu_list_t *ul_prev; + uintptr_t ul_next_enc; + uintptr_t ul_prev_enc; uu_list_pool_t *ul_pool; - void *ul_parent; + uintptr_t ul_parent_enc; /* encoded parent pointer */ size_t ul_offset; size_t ul_numnodes; uint8_t ul_debug; @@ -85,6 +101,8 @@ struct uu_list { uu_list_walk_t ul_null_walk; /* for robust walkers */ }; +#define UU_LIST_PTR(ptr) ((uu_list_t *)UU_PTR_DECODE(ptr)) + #define UU_LIST_POOL_MAXNAME 64 struct uu_list_pool { @@ -117,11 +135,11 @@ struct uu_avl_walk { }; struct uu_avl { - uu_avl_t *ua_next; - uu_avl_t *ua_prev; + uintptr_t ua_next_enc; + uintptr_t ua_prev_enc; uu_avl_pool_t *ua_pool; - void *ua_parent; + uintptr_t ua_parent_enc; uint8_t ua_debug; uint8_t ua_index; /* mark for uu_avl_index_ts */ @@ -129,6 +147,8 @@ struct uu_avl { uu_avl_walk_t ua_null_walk; }; +#define UU_AVL_PTR(x) ((uu_avl_t *)UU_PTR_DECODE(x)) + #define UU_AVL_POOL_MAXNAME 64 struct uu_avl_pool { diff --git a/usr/src/lib/libuutil/common/uu_avl.c b/usr/src/lib/libuutil/common/uu_avl.c index 8538fcebbc..504b413a3a 100644 --- a/usr/src/lib/libuutil/common/uu_avl.c +++ b/usr/src/lib/libuutil/common/uu_avl.c @@ -20,7 +20,7 @@ * CDDL HEADER END */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -98,8 +98,8 @@ uu_avl_pool_create(const char *name, size_t objsize, size_t nodeoffset, (void) pthread_mutex_init(&pp->uap_lock, NULL); - pp->uap_null_avl.ua_next = &pp->uap_null_avl; - pp->uap_null_avl.ua_prev = &pp->uap_null_avl; + pp->uap_null_avl.ua_next_enc = UU_PTR_ENCODE(&pp->uap_null_avl); + pp->uap_null_avl.ua_prev_enc = UU_PTR_ENCODE(&pp->uap_null_avl); (void) pthread_mutex_lock(&uu_apool_list_lock); pp->uap_next = next = &uu_null_apool; @@ -115,8 +115,10 @@ void uu_avl_pool_destroy(uu_avl_pool_t *pp) { if (pp->uap_debug) { - if (pp->uap_null_avl.ua_next != &pp->uap_null_avl || - pp->uap_null_avl.ua_prev != &pp->uap_null_avl) { + if (pp->uap_null_avl.ua_next_enc != + UU_PTR_ENCODE(&pp->uap_null_avl) || + pp->uap_null_avl.ua_prev_enc != + UU_PTR_ENCODE(&pp->uap_null_avl)) { uu_panic("uu_avl_pool_destroy: Pool \"%.*s\" (%p) has " "outstanding avls, or is corrupt.\n", sizeof (pp->uap_name), pp->uap_name, pp); @@ -209,7 +211,7 @@ uu_avl_create(uu_avl_pool_t *pp, void *parent, uint32_t flags) { uu_avl_t *ap, *next, *prev; - if (flags & UU_AVL_DEBUG) { + if (flags & ~UU_AVL_DEBUG) { uu_set_error(UU_ERROR_UNKNOWN_FLAG); return (NULL); } @@ -221,7 +223,7 @@ uu_avl_create(uu_avl_pool_t *pp, void *parent, uint32_t flags) } ap->ua_pool = pp; - ap->ua_parent = parent; + ap->ua_parent_enc = UU_PTR_ENCODE(parent); ap->ua_debug = pp->uap_debug || (flags & UU_AVL_DEBUG); ap->ua_index = (pp->uap_last_index = INDEX_NEXT(pp->uap_last_index)); @@ -232,10 +234,12 @@ uu_avl_create(uu_avl_pool_t *pp, void *parent, uint32_t flags) ap->ua_null_walk.uaw_prev = &ap->ua_null_walk; (void) pthread_mutex_lock(&pp->uap_lock); - ap->ua_next = next = &pp->uap_null_avl; - ap->ua_prev = prev = next->ua_prev; - next->ua_prev = ap; - prev->ua_next = ap; + next = &pp->uap_null_avl; + prev = UU_PTR_DECODE(next->ua_prev_enc); + ap->ua_next_enc = UU_PTR_ENCODE(next); + ap->ua_prev_enc = UU_PTR_ENCODE(prev); + next->ua_prev_enc = UU_PTR_ENCODE(ap); + prev->ua_next_enc = UU_PTR_ENCODE(ap); (void) pthread_mutex_unlock(&pp->uap_lock); return (ap); @@ -257,11 +261,11 @@ uu_avl_destroy(uu_avl_t *ap) } } (void) pthread_mutex_lock(&pp->uap_lock); - ap->ua_next->ua_prev = ap->ua_prev; - ap->ua_prev->ua_next = ap->ua_next; + UU_AVL_PTR(ap->ua_next_enc)->ua_prev_enc = ap->ua_prev_enc; + UU_AVL_PTR(ap->ua_prev_enc)->ua_next_enc = ap->ua_next_enc; (void) pthread_mutex_unlock(&pp->uap_lock); - ap->ua_prev = NULL; - ap->ua_next = NULL; + ap->ua_prev_enc = UU_PTR_ENCODE(NULL); + ap->ua_next_enc = UU_PTR_ENCODE(NULL); ap->ua_pool = NULL; avl_destroy(&ap->ua_tree); @@ -451,7 +455,16 @@ uu_avl_remove(uu_avl_t *ap, void *elem) void * uu_avl_teardown(uu_avl_t *ap, void **cookie) { - return (avl_destroy_nodes(&ap->ua_tree, cookie)); + void *elem = avl_destroy_nodes(&ap->ua_tree, cookie); + + if (elem != NULL) { + uu_avl_pool_t *pp = ap->ua_pool; + uintptr_t *na = NODE_ARRAY(pp, elem); + + na[0] = POOL_TO_MARKER(pp); + na[1] = NULL; + } + return (elem); } void * diff --git a/usr/src/lib/libuutil/common/uu_list.c b/usr/src/lib/libuutil/common/uu_list.c index cf7485c905..d9dc86fed4 100644 --- a/usr/src/lib/libuutil/common/uu_list.c +++ b/usr/src/lib/libuutil/common/uu_list.c @@ -20,7 +20,7 @@ * CDDL HEADER END */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -94,8 +94,8 @@ uu_list_pool_create(const char *name, size_t objsize, (void) pthread_mutex_init(&pp->ulp_lock, NULL); - pp->ulp_null_list.ul_next = &pp->ulp_null_list; - pp->ulp_null_list.ul_prev = &pp->ulp_null_list; + pp->ulp_null_list.ul_next_enc = UU_PTR_ENCODE(&pp->ulp_null_list); + pp->ulp_null_list.ul_prev_enc = UU_PTR_ENCODE(&pp->ulp_null_list); (void) pthread_mutex_lock(&uu_lpool_list_lock); pp->ulp_next = next = &uu_null_lpool; @@ -111,8 +111,10 @@ void uu_list_pool_destroy(uu_list_pool_t *pp) { if (pp->ulp_debug) { - if (pp->ulp_null_list.ul_next != &pp->ulp_null_list || - pp->ulp_null_list.ul_prev != &pp->ulp_null_list) { + if (pp->ulp_null_list.ul_next_enc != + UU_PTR_ENCODE(&pp->ulp_null_list) || + pp->ulp_null_list.ul_prev_enc != + UU_PTR_ENCODE(&pp->ulp_null_list)) { uu_panic("uu_list_pool_destroy: Pool \"%.*s\" (%p) has " "outstanding lists, or is corrupt.\n", sizeof (pp->ulp_name), pp->ulp_name, pp); @@ -200,7 +202,7 @@ uu_list_create(uu_list_pool_t *pp, void *parent, uint32_t flags) } lp->ul_pool = pp; - lp->ul_parent = parent; + lp->ul_parent_enc = UU_PTR_ENCODE(parent); lp->ul_offset = pp->ulp_nodeoffset; lp->ul_debug = pp->ulp_debug || (flags & UU_LIST_DEBUG); lp->ul_sorted = (flags & UU_LIST_SORTED); @@ -214,10 +216,12 @@ uu_list_create(uu_list_pool_t *pp, void *parent, uint32_t flags) lp->ul_null_walk.ulw_prev = &lp->ul_null_walk; (void) pthread_mutex_lock(&pp->ulp_lock); - lp->ul_next = next = &pp->ulp_null_list; - lp->ul_prev = prev = next->ul_prev; - next->ul_prev = lp; - prev->ul_next = lp; + next = &pp->ulp_null_list; + prev = UU_PTR_DECODE(next->ul_prev_enc); + lp->ul_next_enc = UU_PTR_ENCODE(next); + lp->ul_prev_enc = UU_PTR_ENCODE(prev); + next->ul_prev_enc = UU_PTR_ENCODE(lp); + prev->ul_next_enc = UU_PTR_ENCODE(lp); (void) pthread_mutex_unlock(&pp->ulp_lock); return (lp); @@ -246,11 +250,11 @@ uu_list_destroy(uu_list_t *lp) } (void) pthread_mutex_lock(&pp->ulp_lock); - lp->ul_next->ul_prev = lp->ul_prev; - lp->ul_prev->ul_next = lp->ul_next; + UU_LIST_PTR(lp->ul_next_enc)->ul_prev_enc = lp->ul_prev_enc; + UU_LIST_PTR(lp->ul_prev_enc)->ul_next_enc = lp->ul_next_enc; (void) pthread_mutex_unlock(&pp->ulp_lock); - lp->ul_prev = NULL; - lp->ul_next = NULL; + lp->ul_prev_enc = UU_PTR_ENCODE(NULL); + lp->ul_next_enc = UU_PTR_ENCODE(NULL); lp->ul_pool = NULL; uu_free(lp); } |