diff options
Diffstat (limited to 'source3')
-rw-r--r-- | source3/include/util_tdb.h | 10 | ||||
-rw-r--r-- | source3/lib/util_tdb.c | 203 | ||||
-rw-r--r-- | source3/nsswitch/winbindd_cache.c | 244 |
3 files changed, 238 insertions, 219 deletions
diff --git a/source3/include/util_tdb.h b/source3/include/util_tdb.h index 803d18c5eb..5fd05f59fc 100644 --- a/source3/include/util_tdb.h +++ b/source3/include/util_tdb.h @@ -41,6 +41,16 @@ struct tdb_wrap { struct tdb_wrap *next, *prev; }; +struct tdb_validation_status { + BOOL tdb_error; + BOOL bad_freelist; + BOOL bad_entry; + BOOL unknown_key; + BOOL success; +}; + +typedef int (*tdb_validate_data_func)(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state); + TDB_LIST_NODE *tdb_search_keys(struct tdb_context*, const char*); void tdb_search_list_free(TDB_LIST_NODE*); int32 tdb_change_int32_atomic(struct tdb_context *tdb, const char *keystr, int32 *oldval, int32 change_val); diff --git a/source3/lib/util_tdb.c b/source3/lib/util_tdb.c index 6d783198d6..cb53b7f2e3 100644 --- a/source3/lib/util_tdb.c +++ b/source3/lib/util_tdb.c @@ -980,3 +980,206 @@ NTSTATUS map_nt_error_from_tdb(enum TDB_ERROR err) return NT_STATUS_INTERNAL_ERROR; } + + +/********************************************************************* + * the following is a generic validation mechanism for tdbs. + *********************************************************************/ + +/* + * internal validation function, executed by the child. + */ +static int tdb_validate_child(const char *tdb_path, + tdb_validate_data_func validate_fn, + int pfd) +{ + int ret = -1; + int tfd = -1; + int num_entries = 0; + TDB_CONTEXT *tdb = NULL; + struct tdb_validation_status v_status; + + v_status.tdb_error = False; + v_status.bad_freelist = False; + v_status.bad_entry = False; + v_status.unknown_key = False; + v_status.success = True; + + tdb = tdb_open_log(tdb_path, + WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, + lp_winbind_offline_logon() + ? TDB_DEFAULT + : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST), + O_RDWR|O_CREAT, 0600); + if (!tdb) { + v_status.tdb_error = True; + v_status.success = False; + goto out; + } + + tfd = tdb_fd(tdb); + + /* Check the cache freelist is good. */ + if (tdb_validate_freelist(tdb, &num_entries) == -1) { + DEBUG(0,("tdb_validate_child: bad freelist in cache %s\n", + tdb_path)); + v_status.bad_freelist = True; + v_status.success = False; + goto out; + } + + DEBUG(10,("tdb_validate_child: cache %s freelist has %d entries\n", + tdb_path, num_entries)); + + /* Now traverse the cache to validate it. */ + num_entries = tdb_traverse(tdb, validate_fn, (void *)&v_status); + if (num_entries == -1 || !(v_status.success)) { + DEBUG(0,("tdb_validate_child: cache %s traverse failed\n", + tdb_path)); + if (!(v_status.success)) { + if (v_status.bad_entry) { + DEBUGADD(0, (" -> bad entry found\n")); + } + if (v_status.unknown_key) { + DEBUGADD(0, (" -> unknown key encountered\n")); + } + } + goto out; + } + + DEBUG(10,("tdb_validate_child: cache %s is good " + "with %d entries\n", tdb_path, num_entries)); + ret = 0; /* Cache is good. */ + +out: + if (tdb) { + if (ret == 0) { + tdb_close(tdb); + } + else if (tfd != -1) { + close(tfd); + } + } + + DEBUG(10, ("tdb_validate_child: writing status to pipe\n")); + write (pfd, (const char *)&v_status, sizeof(v_status)); + close(pfd); + + return ret; +} + +int tdb_validate(const char *tdb_path, tdb_validate_data_func validate_fn) +{ + pid_t child_pid = -1; + int child_status = 0; + int wait_pid = 0; + int ret = -1; + int pipe_fds[2]; + struct tdb_validation_status v_status; + int bytes_read = 0; + + /* fork and let the child do the validation. + * benefit: no need to twist signal handlers and panic functions. + * just let the child panic. we catch the signal. + * communicate the extended status struct over a pipe. */ + + if (pipe(pipe_fds) != 0) { + DEBUG(0, ("tdb_validate: unable to create pipe, " + "error %s", strerror(errno))); + smb_panic("winbind_validate_cache: unable to create pipe."); + } + + DEBUG(10, ("tdb_validate: forking to let child do validation.\n")); + child_pid = sys_fork(); + if (child_pid == 0) { + DEBUG(10, ("tdb_validate (validation child): created\n")); + close(pipe_fds[0]); /* close reading fd */ + DEBUG(10, ("tdb_validate (validation child): " + "calling tdb_validate_child\n")); + exit(tdb_validate_child(tdb_path, validate_fn, pipe_fds[1])); + } + else if (child_pid < 0) { + smb_panic("tdb_validate: fork for validation failed."); + } + + /* parent */ + + DEBUG(10, ("tdb_validate: fork succeeded, child PID = %d\n", + child_pid)); + close(pipe_fds[1]); /* close writing fd */ + + v_status.success = True; + v_status.bad_entry = False; + v_status.unknown_key = False; + + DEBUG(10, ("tdb_validate: reading from pipe.\n")); + bytes_read = read(pipe_fds[0], (void *)&v_status, sizeof(v_status)); + close(pipe_fds[0]); + + if (bytes_read != sizeof(v_status)) { + DEBUG(10, ("tdb_validate: read %d bytes from pipe " + "but expected %d", bytes_read, (int)sizeof(v_status))); + DEBUGADD(10, (" -> assuming child crashed\n")); + v_status.success = False; + } + else { + DEBUG(10, ("tdb_validate: read status from child\n")); + DEBUGADD(10, (" * tdb error: %s\n", v_status.tdb_error ? "yes" : "no")); + DEBUGADD(10, (" * bad freelist: %s\n", v_status.bad_freelist ? "yes" : "no")); + DEBUGADD(10, (" * bad entry: %s\n", v_status.bad_entry ? "yes" : "no")); + DEBUGADD(10, (" * unknown key: %s\n", v_status.unknown_key ? "yes" : "no")); + DEBUGADD(10, (" => overall success: %s\n", v_status.success ? "yes" : "no")); + } + + if (!v_status.success) { + DEBUG(10, ("tdb_validate: validation not successful.\n")); + DEBUGADD(10, ("removing tdb %s.\n", tdb_path)); + unlink(tdb_path); + } + + DEBUG(10, ("tdb_validate: waiting for child to finish...\n")); + while ((wait_pid = sys_waitpid(child_pid, &child_status, 0)) < 0) { + if (errno == EINTR) { + DEBUG(10, ("tdb_validate: got signal during " + "waitpid, retrying\n")); + errno = 0; + continue; + } + DEBUG(0, ("tdb_validate: waitpid failed with " + "errno %s\n", strerror(errno))); + smb_panic("tdb_validate: waitpid failed."); + } + if (wait_pid != child_pid) { + DEBUG(0, ("tdb_validate: waitpid returned pid %d, " + "but %d was expexted\n", wait_pid, child_pid)); + smb_panic("tdb_validate: waitpid returned " + "unexpected PID."); + } + + + DEBUG(10, ("tdb_validate: validating child returned.\n")); + if (WIFEXITED(child_status)) { + DEBUG(10, ("tdb_validate: child exited, code %d.\n", + WEXITSTATUS(child_status))); + ret = WEXITSTATUS(child_status); + } + if (WIFSIGNALED(child_status)) { + DEBUG(10, ("tdb_validate: child terminated " + "by signal %d\n", WTERMSIG(child_status))); +#ifdef WCOREDUMP + if (WCOREDUMP(child_status)) { + DEBUGADD(10, ("core dumped\n")); + } +#endif + ret = WTERMSIG(child_status); + } + if (WIFSTOPPED(child_status)) { + DEBUG(10, ("tdb_validate: child was stopped " + "by signal %d\n", + WSTOPSIG(child_status))); + ret = WSTOPSIG(child_status); + } + + return ret; +} + diff --git a/source3/nsswitch/winbindd_cache.c b/source3/nsswitch/winbindd_cache.c index 017a2ebfeb..122432d95b 100644 --- a/source3/nsswitch/winbindd_cache.c +++ b/source3/nsswitch/winbindd_cache.c @@ -2749,16 +2749,8 @@ BOOL get_global_winbindd_state_offline(void) Validate functions for all possible cache tdb keys. ***********************************************************************/ -struct validation_status { - BOOL tdb_error; - BOOL bad_freelist; - BOOL bad_entry; - BOOL unknown_key; - BOOL success; -}; - static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data, - struct validation_status *state) + struct tdb_validation_status *state) { struct cache_entry *centry; @@ -2786,7 +2778,7 @@ static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA dat } static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, - struct validation_status *state) + struct tdb_validation_status *state) { if (dbuf.dsize != 8) { DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n", @@ -2798,7 +2790,7 @@ static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbu } static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, - struct validation_status *state) + struct tdb_validation_status *state) { struct cache_entry *centry = create_centry_validate(keystr, dbuf, state); if (!centry) { @@ -2821,7 +2813,7 @@ static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, } static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, - struct validation_status *state) + struct tdb_validation_status *state) { struct cache_entry *centry = create_centry_validate(keystr, dbuf, state); if (!centry) { @@ -2844,7 +2836,7 @@ static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, } static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, - struct validation_status *state) + struct tdb_validation_status *state) { struct cache_entry *centry = create_centry_validate(keystr, dbuf, state); DOM_SID sid; @@ -2871,7 +2863,7 @@ static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, } static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, - struct validation_status *state) + struct tdb_validation_status *state) { struct cache_entry *centry = create_centry_validate(keystr, dbuf, state); @@ -2893,7 +2885,7 @@ static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA db } static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, - struct validation_status *state) + struct tdb_validation_status *state) { struct cache_entry *centry = create_centry_validate(keystr, dbuf, state); @@ -2917,7 +2909,7 @@ static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA db } static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, - struct validation_status *state) + struct tdb_validation_status *state) { struct cache_entry *centry = create_centry_validate(keystr, dbuf, state); @@ -2943,7 +2935,7 @@ static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, } static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, - struct validation_status *state) + struct tdb_validation_status *state) { struct cache_entry *centry = create_centry_validate(keystr, dbuf, state); int32 num_entries, i; @@ -2974,7 +2966,7 @@ static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, } static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, - struct validation_status *state) + struct tdb_validation_status *state) { struct cache_entry *centry = create_centry_validate(keystr, dbuf, state); int32 num_entries, i; @@ -3001,7 +2993,7 @@ static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, } static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, - struct validation_status *state) + struct tdb_validation_status *state) { struct cache_entry *centry = create_centry_validate(keystr, dbuf, state); int32 num_groups, i; @@ -3027,7 +3019,7 @@ static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, } static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, - struct validation_status *state) + struct tdb_validation_status *state) { struct cache_entry *centry = create_centry_validate(keystr, dbuf, state); int32 num_aliases, i; @@ -3052,7 +3044,7 @@ static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, } static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, - struct validation_status *state) + struct tdb_validation_status *state) { struct cache_entry *centry = create_centry_validate(keystr, dbuf, state); int32 num_names, i; @@ -3080,7 +3072,7 @@ static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, } static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, - struct validation_status *state) + struct tdb_validation_status *state) { /* Can't say anything about this other than must be nonzero. */ if (dbuf.dsize == 0) { @@ -3096,7 +3088,7 @@ static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, } static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, - struct validation_status *state) + struct tdb_validation_status *state) { /* Can't say anything about this other than must be nonzero. */ if (dbuf.dsize == 0) { @@ -3112,7 +3104,7 @@ static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, } static int validate_trustdoms(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, - struct validation_status *state) + struct tdb_validation_status *state) { struct cache_entry *centry = create_centry_validate(keystr, dbuf, state); int32 num_domains, i; @@ -3141,7 +3133,7 @@ static int validate_trustdoms(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, - struct validation_status *state) + struct tdb_validation_status *state) { if (dbuf.dsize == 0) { DEBUG(0, ("validate_trustdomcache: Corrupt cache for " @@ -3157,7 +3149,7 @@ static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr, } static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, - struct validation_status *state) + struct tdb_validation_status *state) { if (dbuf.dsize != 4) { DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n", @@ -3171,7 +3163,7 @@ static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA db } static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, - struct validation_status *state) + struct tdb_validation_status *state) { if (dbuf.dsize != 4) { DEBUG(0, ("validate_cache_version: Corrupt cache for " @@ -3193,7 +3185,7 @@ static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_D struct key_val_struct { const char *keyname; - int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct validation_status* state); + int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state); } key_val[] = { {"SEQNUM/", validate_seqnum}, {"NS/", validate_ns}, @@ -3224,7 +3216,7 @@ struct key_val_struct { static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state) { int i; - struct validation_status *v_state = (struct validation_status *)state; + struct tdb_validation_status *v_state = (struct tdb_validation_status *)state; /* Paranoia check. */ if (kbuf.dsize > 1024) { @@ -3285,201 +3277,15 @@ static void validate_panic(const char *const why) function) will restart us as we don't know if we crashed or not. ***********************************************************************/ -/* - * internal validation function, executed by the child. - */ -static int winbindd_validate_cache_child(const char *cache_path, int pfd) -{ - int ret = -1; - int tfd = -1; - int num_entries = 0; - TDB_CONTEXT *tdb = NULL; - struct validation_status v_status; - - v_status.tdb_error = False; - v_status.bad_freelist = False; - v_status.bad_entry = False; - v_status.unknown_key = False; - v_status.success = True; - - tdb = tdb_open_log(cache_path, - WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, - lp_winbind_offline_logon() - ? TDB_DEFAULT - : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST), - O_RDWR|O_CREAT, 0600); - if (!tdb) { - v_status.tdb_error = True; - v_status.success = False; - goto out; - } - - tfd = tdb_fd(tdb); - - /* Check the cache freelist is good. */ - if (tdb_validate_freelist(tdb, &num_entries) == -1) { - DEBUG(0,("winbindd_validate_cache_child: bad freelist in cache %s\n", - cache_path)); - v_status.bad_freelist = True; - v_status.success = False; - goto out; - } - - DEBUG(10,("winbindd_validate_cache_child: cache %s freelist has %d entries\n", - cache_path, num_entries)); - - /* Now traverse the cache to validate it. */ - num_entries = tdb_traverse(tdb, cache_traverse_validate_fn, (void *)&v_status); - if (num_entries == -1 || !(v_status.success)) { - DEBUG(0,("winbindd_validate_cache_child: cache %s traverse failed\n", - cache_path)); - if (!(v_status.success)) { - if (v_status.bad_entry) { - DEBUGADD(0, (" -> bad entry found\n")); - } - if (v_status.unknown_key) { - DEBUGADD(0, (" -> unknown key encountered\n")); - } - } - goto out; - } - - DEBUG(10,("winbindd_validate_cache_child: cache %s is good " - "with %d entries\n", cache_path, num_entries)); - ret = 0; /* Cache is good. */ - -out: - if (tdb) { - if (ret == 0) { - tdb_close(tdb); - } - else if (tfd != -1) { - close(tfd); - } - } - - DEBUG(10, ("winbindd_validate_cache_child: writing status to pipe\n")); - write (pfd, (const char *)&v_status, sizeof(v_status)); - close(pfd); - - return ret; -} - int winbindd_validate_cache(void) { - pid_t child_pid = -1; - int child_status = 0; - int wait_pid = 0; - int ret = -1; - int pipe_fds[2]; - struct validation_status v_status; - int bytes_read = 0; - const char *cache_path = lock_path("winbindd_cache.tdb"); - + int ret; + DEBUG(10, ("winbindd_validate_cache: replacing panic function\n")); smb_panic_fn = validate_panic; - /* fork and let the child do the validation. - * benefit: no need to twist signal handlers and panic functions. - * just let the child panic. we catch the signal. - * communicate the extended status struct over a pipe. */ - - if (pipe(pipe_fds) != 0) { - DEBUG(0, ("winbindd_validate_cache: unable to create pipe, " - "error %s", strerror(errno))); - smb_panic("winbind_validate_cache: unable to create pipe."); - } - - DEBUG(10, ("winbindd_validate_cache: forking to let child do validation.\n")); - child_pid = sys_fork(); - if (child_pid == 0) { - DEBUG(10, ("winbindd_validate_cache (validation child): created\n")); - close(pipe_fds[0]); /* close reading fd */ - DEBUG(10, ("winbindd_validate_cache (validation child): " - "calling winbindd_validate_cache_child\n")); - exit(winbindd_validate_cache_child(cache_path, pipe_fds[1])); - } - else if (child_pid < 0) { - smb_panic("winbindd_validate_cache: fork for validation failed."); - } - - /* parent */ - - DEBUG(10, ("winbindd_validate_cache: fork succeeded, child PID = %d\n", - child_pid)); - close(pipe_fds[1]); /* close writing fd */ - - v_status.success = True; - v_status.bad_entry = False; - v_status.unknown_key = False; - - DEBUG(10, ("winbindd_validate_cache: reading from pipe.\n")); - bytes_read = read(pipe_fds[0], (void *)&v_status, sizeof(v_status)); - close(pipe_fds[0]); - - if (bytes_read != sizeof(v_status)) { - DEBUG(10, ("winbindd_validate_cache: read %d bytes from pipe " - "but expected %d", bytes_read, (int)sizeof(v_status))); - DEBUGADD(10, (" -> assuming child crashed\n")); - v_status.success = False; - } - else { - DEBUG(10, ("winbindd_validate_cache: read status from child\n")); - DEBUGADD(10, (" * tdb error: %s\n", v_status.tdb_error ? "yes" : "no")); - DEBUGADD(10, (" * bad freelist: %s\n", v_status.bad_freelist ? "yes" : "no")); - DEBUGADD(10, (" * bad entry: %s\n", v_status.bad_entry ? "yes" : "no")); - DEBUGADD(10, (" * unknown key: %s\n", v_status.unknown_key ? "yes" : "no")); - DEBUGADD(10, (" => overall success: %s\n", v_status.success ? "yes" : "no")); - } - - if (!v_status.success) { - DEBUG(10, ("winbindd_validate_cache: validation not successful.\n")); - DEBUGADD(10, ("removing tdb %s.\n", cache_path)); - unlink(cache_path); - } - - DEBUG(10, ("winbindd_validate_cache: waiting for child to finish...\n")); - while ((wait_pid = sys_waitpid(child_pid, &child_status, 0)) < 0) { - if (errno == EINTR) { - DEBUG(10, ("winbindd_validate_cache: got signal during " - "waitpid, retrying\n")); - errno = 0; - continue; - } - DEBUG(0, ("winbindd_validate_cache: waitpid failed with " - "errno %s\n", strerror(errno))); - smb_panic("winbindd_validate_cache: waitpid failed."); - } - if (wait_pid != child_pid) { - DEBUG(0, ("winbindd_validate_cache: waitpid returned pid %d, " - "but %d was expexted\n", wait_pid, child_pid)); - smb_panic("winbindd_validate_cache: waitpid returned " - "unexpected PID."); - } - - - DEBUG(10, ("winbindd_validate_cache: validating child returned.\n")); - if (WIFEXITED(child_status)) { - DEBUG(10, ("winbindd_validate_cache: child exited, code %d.\n", - WEXITSTATUS(child_status))); - ret = WEXITSTATUS(child_status); - } - if (WIFSIGNALED(child_status)) { - DEBUG(10, ("winbindd_validate_cache: child terminated " - "by signal %d\n", WTERMSIG(child_status))); -#ifdef WCOREDUMP - if (WCOREDUMP(child_status)) { - DEBUGADD(10, ("core dumped\n")); - } -#endif - ret = WTERMSIG(child_status); - } - if (WIFSTOPPED(child_status)) { - DEBUG(10, ("winbindd_validate_cache: child was stopped " - "by signal %d\n", - WSTOPSIG(child_status))); - ret = WSTOPSIG(child_status); - } + ret = tdb_validate(lock_path("winbindd_cache.tdb"), + cache_traverse_validate_fn); DEBUG(10, ("winbindd_validate_cache: restoring panic function\n")); smb_panic_fn = smb_panic; |