X-Git-Url: http://git.samba.org/?a=blobdiff_plain;f=source3%2Fwinbindd%2Fwinbindd.c;h=bbd602ee8c8b853fee86e6f6efae6a1c46a64157;hb=f970d084dd46b4c1a380d2c8cd0879d39081e745;hp=e881ab412ecafac1ca22fe1f3e950efd810e2c05;hpb=759a3bcc88230c5232b748bb8f5a0c6e0cce128c;p=obnox%2Fsamba-ctdb.git diff --git a/source3/winbindd/winbindd.c b/source3/winbindd/winbindd.c index e881ab412e..bbd602ee8c 100644 --- a/source3/winbindd/winbindd.c +++ b/source3/winbindd/winbindd.c @@ -28,7 +28,7 @@ #undef DBGC_CLASS #define DBGC_CLASS DBGC_WINBIND -bool opt_nocache = False; +static bool opt_nocache = False; static bool interactive = False; extern bool override_logfile; @@ -160,6 +160,8 @@ static void terminate(bool is_parent) trustdom_cache_shutdown(); + gencache_stabilize(); + #if 0 if (interactive) { TALLOC_CTX *mem_ctx = talloc_init("end_description"); @@ -170,39 +172,184 @@ static void terminate(bool is_parent) } #endif + if (is_parent) { + pidfile_unlink(); + } + exit(0); } -static SIG_ATOMIC_T do_sigterm = 0; +static void winbindd_sig_term_handler(struct tevent_context *ev, + struct tevent_signal *se, + int signum, + int count, + void *siginfo, + void *private_data) +{ + bool *is_parent = talloc_get_type_abort(private_data, bool); + + DEBUG(0,("Got sig[%d] terminate (is_parent=%d)\n", + signum, (int)*is_parent)); + + /* if the parent killed us due to a timeout log where we were */ + if (signum == SIGXCPU) { + log_stack_trace_with_level(1); + } + + terminate(*is_parent); +} -static void termination_handler(int signum) +bool winbindd_setup_sig_term_handler(bool parent) { - do_sigterm = 1; - sys_select_signal(signum); + struct tevent_signal *se; + bool *is_parent; + + is_parent = talloc(winbind_event_context(), bool); + if (!is_parent) { + return false; + } + + *is_parent = parent; + + se = tevent_add_signal(winbind_event_context(), + is_parent, + SIGTERM, 0, + winbindd_sig_term_handler, + is_parent); + if (!se) { + DEBUG(0,("failed to setup SIGTERM handler")); + talloc_free(is_parent); + return false; + } + + se = tevent_add_signal(winbind_event_context(), + is_parent, + SIGINT, 0, + winbindd_sig_term_handler, + is_parent); + if (!se) { + DEBUG(0,("failed to setup SIGINT handler")); + talloc_free(is_parent); + return false; + } + + se = tevent_add_signal(winbind_event_context(), + is_parent, + SIGQUIT, 0, + winbindd_sig_term_handler, + is_parent); + if (!se) { + DEBUG(0,("failed to setup SIGINT handler")); + talloc_free(is_parent); + return false; + } + + se = tevent_add_signal(winbind_event_context(), + is_parent, + SIGXCPU, 0, + winbindd_sig_term_handler, + is_parent); + if (!se) { + DEBUG(0,("failed to setup SIGXCPU handler")); + talloc_free(is_parent); + return false; + } + return true; } -static SIG_ATOMIC_T do_sigusr2 = 0; +static void winbindd_sig_hup_handler(struct tevent_context *ev, + struct tevent_signal *se, + int signum, + int count, + void *siginfo, + void *private_data) +{ + const char *file = (const char *)private_data; + + DEBUG(1,("Reloading services after SIGHUP\n")); + flush_caches(); + reload_services_file(file); +} -static void sigusr2_handler(int signum) +bool winbindd_setup_sig_hup_handler(const char *lfile) { - do_sigusr2 = 1; - sys_select_signal(SIGUSR2); + struct tevent_signal *se; + char *file = NULL; + + if (lfile) { + file = talloc_strdup(winbind_event_context(), + lfile); + if (!file) { + return false; + } + } + + se = tevent_add_signal(winbind_event_context(), + winbind_event_context(), + SIGHUP, 0, + winbindd_sig_hup_handler, + file); + if (!se) { + return false; + } + + return true; } -static SIG_ATOMIC_T do_sighup = 0; +static void winbindd_sig_chld_handler(struct tevent_context *ev, + struct tevent_signal *se, + int signum, + int count, + void *siginfo, + void *private_data) +{ + pid_t pid; + + while ((pid = sys_waitpid(-1, NULL, WNOHANG)) > 0) { + winbind_child_died(pid); + } +} -static void sighup_handler(int signum) +static bool winbindd_setup_sig_chld_handler(void) { - do_sighup = 1; - sys_select_signal(SIGHUP); + struct tevent_signal *se; + + se = tevent_add_signal(winbind_event_context(), + winbind_event_context(), + SIGCHLD, 0, + winbindd_sig_chld_handler, + NULL); + if (!se) { + return false; + } + + return true; } -static SIG_ATOMIC_T do_sigchld = 0; +static void winbindd_sig_usr2_handler(struct tevent_context *ev, + struct tevent_signal *se, + int signum, + int count, + void *siginfo, + void *private_data) +{ + print_winbindd_status(); +} -static void sigchld_handler(int signum) +static bool winbindd_setup_sig_usr2_handler(void) { - do_sigchld = 1; - sys_select_signal(SIGCHLD); + struct tevent_signal *se; + + se = tevent_add_signal(winbind_event_context(), + winbind_event_context(), + SIGUSR2, 0, + winbindd_sig_usr2_handler, + NULL); + if (!se) { + return false; + } + + return true; } /* React on 'smbcontrol winbindd reload-config' in the same way as on SIGHUP*/ @@ -224,7 +371,9 @@ static void msg_shutdown(struct messaging_context *msg, struct server_id server_id, DATA_BLOB *data) { - do_sigterm = 1; + /* only the parent waits for this message */ + DEBUG(0,("Got shutdown message\n")); + terminate(true); } @@ -236,8 +385,6 @@ static void winbind_msg_validate_cache(struct messaging_context *msg_ctx, { uint8 ret; pid_t child_pid; - struct sigaction act; - struct sigaction oldact; DEBUG(10, ("winbindd_msg_validate_cache: got validate-cache " "message.\n")); @@ -247,7 +394,6 @@ static void winbind_msg_validate_cache(struct messaging_context *msg_ctx, * so we don't block the main winbindd and the validation * code can safely use fork/waitpid... */ - CatchChild(); child_pid = sys_fork(); if (child_pid == -1) { @@ -259,22 +405,18 @@ static void winbind_msg_validate_cache(struct messaging_context *msg_ctx, if (child_pid != 0) { /* parent */ DEBUG(5, ("winbind_msg_validate_cache: child created with " - "pid %d.\n", child_pid)); + "pid %d.\n", (int)child_pid)); return; } /* child */ + if (!winbindd_reinit_after_fork(NULL)) { + _exit(0); + } + /* install default SIGCHLD handler: validation code uses fork/waitpid */ - ZERO_STRUCT(act); - act.sa_handler = SIG_DFL; -#ifdef SA_RESTART - /* We *want* SIGALRM to interrupt a system call. */ - act.sa_flags = SA_RESTART; -#endif - sigemptyset(&act.sa_mask); - sigaddset(&act.sa_mask,SIGCHLD); - sigaction(SIGCHLD,&act,&oldact); + CatchSignal(SIGCHLD, SIG_DFL); ret = (uint8)winbindd_validate_cache_nobackup(); DEBUG(10, ("winbindd_msg_validata_cache: got return value %d\n", ret)); @@ -293,6 +435,7 @@ static struct winbindd_dispatch_table { { WINBINDD_GETPWNAM, winbindd_getpwnam, "GETPWNAM" }, { WINBINDD_GETPWUID, winbindd_getpwuid, "GETPWUID" }, + { WINBINDD_GETPWSID, winbindd_getpwsid, "GETPWSID" }, { WINBINDD_SETPWENT, winbindd_setpwent, "SETPWENT" }, { WINBINDD_ENDPWENT, winbindd_endpwent, "ENDPWENT" }, @@ -302,6 +445,8 @@ static struct winbindd_dispatch_table { { WINBINDD_GETUSERSIDS, winbindd_getusersids, "GETUSERSIDS" }, { WINBINDD_GETUSERDOMGROUPS, winbindd_getuserdomgroups, "GETUSERDOMGROUPS" }, + { WINBINDD_GETSIDALIASES, winbindd_getsidaliases, + "LOOKUPUSERALIASES" }, /* Group functions */ @@ -342,19 +487,18 @@ static struct winbindd_dispatch_table { { WINBINDD_GID_TO_SID, winbindd_gid_to_sid, "GID_TO_SID" }, { WINBINDD_ALLOCATE_UID, winbindd_allocate_uid, "ALLOCATE_UID" }, { WINBINDD_ALLOCATE_GID, winbindd_allocate_gid, "ALLOCATE_GID" }, - { WINBINDD_SET_MAPPING, winbindd_set_mapping, "SET_MAPPING" }, - { WINBINDD_REMOVE_MAPPING, winbindd_remove_mapping, "REMOVE_MAPPING" }, - { WINBINDD_SET_HWM, winbindd_set_hwm, "SET_HWMS" }, /* Miscellaneous */ { WINBINDD_CHECK_MACHACC, winbindd_check_machine_acct, "CHECK_MACHACC" }, + { WINBINDD_PING_DC, winbindd_ping_dc, "PING_DC" }, { WINBINDD_PING, winbindd_ping, "PING" }, { WINBINDD_INFO, winbindd_info, "INFO" }, { WINBINDD_INTERFACE_VERSION, winbindd_interface_version, "INTERFACE_VERSION" }, { WINBINDD_DOMAIN_NAME, winbindd_domain_name, "DOMAIN_NAME" }, { WINBINDD_DOMAIN_INFO, winbindd_domain_info, "DOMAIN_INFO" }, + { WINBINDD_DC_INFO, winbindd_dc_info, "DC_INFO" }, { WINBINDD_NETBIOS_NAME, winbindd_netbios_name, "NETBIOS_NAME" }, { WINBINDD_PRIV_PIPE_DIR, winbindd_priv_pipe_dir, "WINBINDD_PRIV_PIPE_DIR" }, @@ -415,16 +559,16 @@ static void process_request(struct winbindd_cli_state *state) /* * A list of file descriptors being monitored by select in the main processing - * loop. fd_event->handler is called whenever the socket is readable/writable. + * loop. winbindd_fd_event->handler is called whenever the socket is readable/writable. */ -static struct fd_event *fd_events = NULL; +static struct winbindd_fd_event *fd_events = NULL; -void add_fd_event(struct fd_event *ev) +void add_fd_event(struct winbindd_fd_event *ev) { - struct fd_event *match; + struct winbindd_fd_event *match; - /* only add unique fd_event structs */ + /* only add unique winbindd_fd_event structs */ for (match=fd_events; match; match=match->next ) { #ifdef DEVELOPER @@ -438,17 +582,17 @@ void add_fd_event(struct fd_event *ev) DLIST_ADD(fd_events, ev); } -void remove_fd_event(struct fd_event *ev) +void remove_fd_event(struct winbindd_fd_event *ev) { DLIST_REMOVE(fd_events, ev); } /* - * Handler for fd_events to complete a read/write request, set up by + * Handler for winbindd_fd_events to complete a read/write request, set up by * setup_async_read/setup_async_write. */ -static void rw_callback(struct fd_event *event, int flags) +static void rw_callback(struct winbindd_fd_event *event, int flags) { size_t todo; ssize_t done = 0; @@ -489,11 +633,11 @@ static void rw_callback(struct fd_event *event, int flags) } /* - * Request an async read/write on a fd_event structure. (*finished) is called + * Request an async read/write on a winbindd_fd_event structure. (*finished) is called * when the request is completed or an error had occurred. */ -void setup_async_read(struct fd_event *event, void *data, size_t length, +void setup_async_read(struct winbindd_fd_event *event, void *data, size_t length, void (*finished)(void *private_data, bool success), void *private_data) { @@ -507,7 +651,7 @@ void setup_async_read(struct fd_event *event, void *data, size_t length, event->flags = EVENT_FD_READ; } -void setup_async_write(struct fd_event *event, void *data, size_t length, +void setup_async_write(struct winbindd_fd_event *event, void *data, size_t length, void (*finished)(void *private_data, bool success), void *private_data) { @@ -737,11 +881,13 @@ static void remove_client(struct winbindd_cli_state *state) return; } - /* tell client, we are closing ... */ - nwritten = write(state->sock, &c, sizeof(c)); - if (nwritten == -1) { - DEBUG(2, ("final write to client failed: %s\n", - strerror(errno))); + if (!state->finished) { + /* tell client, we are closing ... */ + nwritten = write(state->sock, &c, sizeof(c)); + if (nwritten == -1) { + DEBUG(2, ("final write to client failed: %s\n", + strerror(errno))); + } } /* Close socket */ @@ -778,6 +924,7 @@ static bool remove_idle_client(void) for (state = winbindd_client_list(); state; state = state->next) { if (state->response.result != WINBINDD_PENDING && + state->fd_event.flags == EVENT_FD_READ && !state->getpwent_state && !state->getgrent_state) { nidle++; if (!last_access || state->last_access < last_access) { @@ -797,25 +944,95 @@ static bool remove_idle_client(void) return False; } -/* check if HUP has been received and reload files */ -void winbind_check_sighup(const char *lfile) +struct winbindd_listen_state { + bool privileged; + int fd; + struct tevent_fd *fde; +}; + +static void winbindd_listen_fde_handler(struct tevent_context *ev, + struct tevent_fd *fde, + uint16_t flags, + void *private_data) { - if (do_sighup) { + struct winbindd_listen_state *s = talloc_get_type_abort(private_data, + struct winbindd_listen_state); + + while (winbindd_num_clients() > + WINBINDD_MAX_SIMULTANEOUS_CLIENTS - 1) { + DEBUG(5,("winbindd: Exceeding %d client " + "connections, removing idle " + "connection.\n", + WINBINDD_MAX_SIMULTANEOUS_CLIENTS)); + if (!remove_idle_client()) { + DEBUG(0,("winbindd: Exceeding %d " + "client connections, no idle " + "connection found\n", + WINBINDD_MAX_SIMULTANEOUS_CLIENTS)); + break; + } + } - DEBUG(3, ("got SIGHUP\n")); + /* new, non-privileged connection */ + new_connection(s->fd, s->privileged); +} - flush_caches(); - reload_services_file(lfile); +static bool winbindd_setup_listeners(void) +{ + struct winbindd_listen_state *pub_state = NULL; + struct winbindd_listen_state *priv_state = NULL; - do_sighup = 0; + pub_state = talloc(winbind_event_context(), + struct winbindd_listen_state); + if (!pub_state) { + goto failed; } -} -/* check if TERM has been received */ -void winbind_check_sigterm(bool is_parent) -{ - if (do_sigterm) - terminate(is_parent); + pub_state->privileged = false; + pub_state->fd = open_winbindd_socket(); + if (pub_state->fd == -1) { + goto failed; + } + + pub_state->fde = tevent_add_fd(winbind_event_context(), + pub_state, pub_state->fd, + TEVENT_FD_READ, + winbindd_listen_fde_handler, + pub_state); + if (!pub_state->fde) { + close(pub_state->fd); + goto failed; + } + tevent_fd_set_auto_close(pub_state->fde); + + priv_state = talloc(winbind_event_context(), + struct winbindd_listen_state); + if (!priv_state) { + goto failed; + } + + priv_state->privileged = true; + priv_state->fd = open_winbindd_priv_socket(); + if (priv_state->fd == -1) { + goto failed; + } + + priv_state->fde = tevent_add_fd(winbind_event_context(), + priv_state, priv_state->fd, + TEVENT_FD_READ, + winbindd_listen_fde_handler, + priv_state); + if (!priv_state->fde) { + close(priv_state->fd); + goto failed; + } + tevent_fd_set_auto_close(priv_state->fde); + + return true; +failed: + TALLOC_FREE(pub_state); + TALLOC_FREE(priv_state); + return false; } /* Process incoming clients on listen_sock. We use a tricky non-blocking, @@ -825,67 +1042,35 @@ void winbind_check_sigterm(bool is_parent) static void process_loop(void) { - struct winbindd_cli_state *state; - struct fd_event *ev; + struct winbindd_fd_event *ev; fd_set r_fds, w_fds; - int maxfd, listen_sock, listen_priv_sock, selret; + int maxfd = 0, selret; struct timeval timeout, ev_timeout; - /* Open Sockets here to get stuff going ASAP */ - listen_sock = open_winbindd_socket(); - listen_priv_sock = open_winbindd_priv_socket(); - - if (listen_sock == -1 || listen_priv_sock == -1) { - perror("open_winbind_socket"); - exit(1); + if (run_events(winbind_event_context(), 0, NULL, NULL)) { + return; } - /* We'll be doing this a lot */ - - /* Handle messages */ - - message_dispatch(winbind_messaging_context()); - - run_events(winbind_event_context(), 0, NULL, NULL); - - /* refresh the trusted domain cache */ - - rescan_trusted_domains(); - /* Initialise fd lists for select() */ - maxfd = MAX(listen_sock, listen_priv_sock); - FD_ZERO(&r_fds); FD_ZERO(&w_fds); - FD_SET(listen_sock, &r_fds); - FD_SET(listen_priv_sock, &r_fds); timeout.tv_sec = WINBINDD_ESTABLISH_LOOP; timeout.tv_usec = 0; /* Check for any event timeouts. */ + { + struct timeval now; + GetTimeOfDay(&now); + + event_add_to_select_args(winbind_event_context(), &now, + &r_fds, &w_fds, &ev_timeout, &maxfd); + } if (get_timed_events_timeout(winbind_event_context(), &ev_timeout)) { timeout = timeval_min(&timeout, &ev_timeout); } - /* Set up client readers and writers */ - - state = winbindd_client_list(); - - while (state) { - - struct winbindd_cli_state *next = state->next; - - /* Dispose of client connection if it is marked as - finished */ - - if (state->finished) - remove_client(state); - - state = next; - } - for (ev = fd_events; ev; ev = ev->next) { if (ev->flags & EVENT_FD_READ) { FD_SET(ev->fd, &r_fds); @@ -918,82 +1103,44 @@ static void process_loop(void) /* selret > 0 */ + if (run_events(winbind_event_context(), selret, &r_fds, &w_fds)) { + return; + } + ev = fd_events; while (ev != NULL) { - struct fd_event *next = ev->next; + struct winbindd_fd_event *next = ev->next; int flags = 0; if (FD_ISSET(ev->fd, &r_fds)) flags |= EVENT_FD_READ; if (FD_ISSET(ev->fd, &w_fds)) flags |= EVENT_FD_WRITE; - if (flags) + if (flags) { ev->handler(ev, flags); - ev = next; - } - - if (FD_ISSET(listen_sock, &r_fds)) { - while (winbindd_num_clients() > - WINBINDD_MAX_SIMULTANEOUS_CLIENTS - 1) { - DEBUG(5,("winbindd: Exceeding %d client " - "connections, removing idle " - "connection.\n", - WINBINDD_MAX_SIMULTANEOUS_CLIENTS)); - if (!remove_idle_client()) { - DEBUG(0,("winbindd: Exceeding %d " - "client connections, no idle " - "connection found\n", - WINBINDD_MAX_SIMULTANEOUS_CLIENTS)); - break; - } + return; } - /* new, non-privileged connection */ - new_connection(listen_sock, False); + ev = next; } - if (FD_ISSET(listen_priv_sock, &r_fds)) { - while (winbindd_num_clients() > - WINBINDD_MAX_SIMULTANEOUS_CLIENTS - 1) { - DEBUG(5,("winbindd: Exceeding %d client " - "connections, removing idle " - "connection.\n", - WINBINDD_MAX_SIMULTANEOUS_CLIENTS)); - if (!remove_idle_client()) { - DEBUG(0,("winbindd: Exceeding %d " - "client connections, no idle " - "connection found\n", - WINBINDD_MAX_SIMULTANEOUS_CLIENTS)); - break; - } - } - /* new, privileged connection */ - new_connection(listen_priv_sock, True); - } + return; no_fds_ready: + run_events(winbind_event_context(), selret, &r_fds, &w_fds); + #if 0 winbindd_check_cache_size(time(NULL)); #endif +} - /* Check signal handling things */ - - winbind_check_sigterm(true); - winbind_check_sighup(NULL); - - if (do_sigusr2) { - print_winbindd_status(); - do_sigusr2 = 0; - } - - if (do_sigchld) { - pid_t pid; - - do_sigchld = 0; +bool winbindd_use_idmap_cache(void) +{ + return !opt_nocache; +} - while ((pid = sys_waitpid(-1, NULL, WNOHANG)) > 0) { - winbind_child_died(pid); - } - } +bool winbindd_use_cache(void) +{ + return !opt_nocache; } /* Main function */ @@ -1112,7 +1259,7 @@ int main(int argc, char **argv, char **envp) setup_logging("winbindd", log_stdout); reopen_logs(); - DEBUG(0,("winbindd version %s started.\n", SAMBA_VERSION_STRING)); + DEBUG(0,("winbindd version %s started.\n", samba_version_string())); DEBUGADD(0,("%s\n", COPYRIGHT_STARTUP_MESSAGE)); if (!lp_load_initial_only(get_dyn_CONFIGFILE())) { @@ -1162,18 +1309,7 @@ int main(int argc, char **argv, char **envp) BlockSignals(False, SIGUSR2); BlockSignals(False, SIGHUP); BlockSignals(False, SIGCHLD); - - /* Setup signal handlers */ - - CatchSignal(SIGINT, termination_handler); /* Exit on these sigs */ - CatchSignal(SIGQUIT, termination_handler); - CatchSignal(SIGTERM, termination_handler); - CatchSignal(SIGCHLD, sigchld_handler); - - CatchSignal(SIGPIPE, SIG_IGN); /* Ignore sigpipe */ - - CatchSignal(SIGUSR2, sigusr2_handler); /* Debugging sigs */ - CatchSignal(SIGHUP, sighup_handler); + BlockSignals(False, SIGXCPU); if (!interactive) become_daemon(Fork, no_process_group); @@ -1191,11 +1327,31 @@ int main(int argc, char **argv, char **envp) TimeInit(); - if (!reinit_after_fork(winbind_messaging_context(), false)) { + /* Don't use winbindd_reinit_after_fork here as + * we're just starting up and haven't created any + * winbindd-specific resources we must free yet. JRA. + */ + + if (!NT_STATUS_IS_OK(reinit_after_fork(winbind_messaging_context(), + winbind_event_context(), + false))) { DEBUG(0,("reinit_after_fork() failed\n")); exit(1); } + /* Setup signal handlers */ + + if (!winbindd_setup_sig_term_handler(true)) + exit(1); + if (!winbindd_setup_sig_hup_handler(NULL)) + exit(1); + if (!winbindd_setup_sig_chld_handler()) + exit(1); + if (!winbindd_setup_sig_usr2_handler()) + exit(1); + + CatchSignal(SIGPIPE, SIG_IGN); /* Ignore sigpipe */ + /* * Ensure all cache and idmap caches are consistent * and initialized before we startup. @@ -1205,7 +1361,12 @@ int main(int argc, char **argv, char **envp) } /* get broadcast messages */ - claim_connection(NULL,"",FLAG_MSG_GENERAL|FLAG_MSG_DBWRAP); + + if (!serverid_register(procid_self(), + FLAG_MSG_GENERAL|FLAG_MSG_DBWRAP)) { + DEBUG(1, ("Could not register myself in serverid.tdb\n")); + exit(1); + } /* React on 'smbcontrol winbindd reload-config' in the same way as to SIGHUP signal */ @@ -1233,6 +1394,10 @@ int main(int argc, char **argv, char **envp) MSG_WINBIND_DUMP_DOMAIN_LIST, winbind_msg_dump_domain_list); + messaging_register(winbind_messaging_context(), NULL, + MSG_WINBIND_IP_DROPPED, + winbind_msg_ip_dropped_parent); + /* Register handler for MSG_DEBUG. */ messaging_register(winbind_messaging_context(), NULL, MSG_DEBUG, @@ -1255,12 +1420,39 @@ int main(int argc, char **argv, char **envp) smb_nscd_flush_user_cache(); smb_nscd_flush_group_cache(); - /* Loop waiting for requests */ + /* setup listen sockets */ + + if (!winbindd_setup_listeners()) { + DEBUG(0,("winbindd_setup_listeners() failed\n")); + exit(1); + } TALLOC_FREE(frame); + /* Loop waiting for requests */ while (1) { + struct winbindd_cli_state *state; + frame = talloc_stackframe(); + + /* refresh the trusted domain cache */ + + rescan_trusted_domains(); + + /* Dispose of client connection if it is marked as + finished */ + state = winbindd_client_list(); + while (state) { + struct winbindd_cli_state *next = state->next; + + if (state->finished) { + remove_client(state); + } + + state = next; + } + process_loop(); + TALLOC_FREE(frame); }