From owner-p4-projects@FreeBSD.ORG Tue Nov 21 17:25:44 2006 Return-Path: X-Original-To: p4-projects@freebsd.org Delivered-To: p4-projects@freebsd.org Received: by hub.freebsd.org (Postfix, from userid 32767) id C2D3916B844; Tue, 21 Nov 2006 17:23:51 +0000 (UTC) X-Original-To: perforce@freebsd.org Delivered-To: perforce@freebsd.org Received: from mx1.FreeBSD.org (mx1.freebsd.org [69.147.83.52]) by hub.freebsd.org (Postfix) with ESMTP id 40FBF16B372 for ; Tue, 21 Nov 2006 17:21:51 +0000 (UTC) (envelope-from bushman@freebsd.org) Received: from repoman.freebsd.org (repoman.freebsd.org [69.147.83.41]) by mx1.FreeBSD.org (Postfix) with ESMTP id 4B8B144004 for ; Tue, 21 Nov 2006 17:17:57 +0000 (GMT) (envelope-from bushman@freebsd.org) Received: from repoman.freebsd.org (localhost [127.0.0.1]) by repoman.freebsd.org (8.13.6/8.13.6) with ESMTP id kALHIKDY068679 for ; Tue, 21 Nov 2006 17:18:20 GMT (envelope-from bushman@freebsd.org) Received: (from perforce@localhost) by repoman.freebsd.org (8.13.6/8.13.4/Submit) id kALHIKY8068675 for perforce@freebsd.org; Tue, 21 Nov 2006 17:18:20 GMT (envelope-from bushman@freebsd.org) Date: Tue, 21 Nov 2006 17:18:20 GMT Message-Id: <200611211718.kALHIKY8068675@repoman.freebsd.org> X-Authentication-Warning: repoman.freebsd.org: perforce set sender to bushman@freebsd.org using -f From: Michael Bushkov To: Perforce Change Reviews Cc: Subject: PERFORCE change 110304 for review X-BeenThere: p4-projects@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: p4 projects tree changes List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 21 Nov 2006 17:25:44 -0000 http://perforce.freebsd.org/chv.cgi?CH=110304 Change 110304 by bushman@bushman_nss_ldap_cached on 2006/11/20 15:56:29 Some basic signal-handling added, graceful restart and shutdown (on SIGHUP and SIGTERM signals respectively) implemented. Still need some code to properly handle blocked threads (will be useful with perform-actuall-lookups option set to "yes"). Affected files ... .. //depot/projects/soc2006/nss_ldap_cached/src/usr.sbin/cached/cached.c#12 edit Differences ... ==== //depot/projects/soc2006/nss_ldap_cached/src/usr.sbin/cached/cached.c#12 (text) ==== @@ -91,14 +91,16 @@ #endif static void precache_entries(struct configuration *); static void print_version_info(void); -static void processing_loop(cache, struct runtime_env *, +static int processing_loop(cache, struct runtime_env *, struct configuration *); static void process_socket_event(struct kevent *, struct runtime_env *, struct configuration *); static void process_timer_event(struct kevent *, struct runtime_env *, struct configuration *); static void *processing_thread(void *); +static void tune_runtime_env_kqueue(struct runtime_env *); static void usage(void); +static int wait_for_hup_term(void); void get_time_func(struct timeval *); @@ -175,6 +177,23 @@ TRACE_OUT(destroy_cache_); } +static void +tune_runtime_env_kqueue(struct runtime_env *env) +{ + struct kevent eventlist[3]; + struct timespec timeout; + + EV_SET(&eventlist[0], env->sockfd, EVFILT_READ, EV_ADD | EV_ONESHOT, + 0, 0, 0); + EV_SET(&eventlist[1], SIGTERM, EVFILT_SIGNAL, EV_ADD | EV_ONESHOT, + 0, 0, 0); + EV_SET(&eventlist[2], SIGHUP, EVFILT_SIGNAL, EV_ADD | EV_ONESHOT, + 0, 0, 0); + memset(&timeout, 0, sizeof(struct timespec)); + kevent(env->queue, eventlist, + sizeof(eventlist) / sizeof(struct kevent), NULL, 0, &timeout); +} + /* * Socket and kqueues are prepared here. We have one global queue for both * socket and timers events. @@ -185,9 +204,6 @@ int serv_addr_len; struct sockaddr_un serv_addr; - struct kevent eventlist; - struct timespec timeout; - struct runtime_env *retval; TRACE_IN(init_runtime_env); @@ -238,12 +254,8 @@ TRACE_OUT(init_runtime_env); return (NULL); } + tune_runtime_env_kqueue(retval); - EV_SET(&eventlist, retval->sockfd, EVFILT_READ, EV_ADD | EV_ONESHOT, - 0, 0, 0); - memset(&timeout, 0, sizeof(struct timespec)); - kevent(retval->queue, &eventlist, 1, NULL, 0, &timeout); - LOG_MSG_2("runtime environment", "successfully initialized"); TRACE_OUT(init_runtime_env); return (retval); @@ -259,8 +271,6 @@ duplicate_runtime_env(struct runtime_env *env) { struct runtime_env *retval; - struct timespec timeout; - struct kevent eventlist; TRACE_IN(duplicate_runtime_env); retval = malloc(sizeof(struct runtime_env)); @@ -276,10 +286,7 @@ TRACE_OUT(duplicate_runtime_env); return (NULL); } - EV_SET(&eventlist, retval->sockfd, EVFILT_READ, EV_ADD | EV_ONESHOT, - 0, 0, 0); - memset(&timeout, 0, sizeof(struct timespec)); - kevent(retval->queue, &eventlist, 1, NULL, 0, &timeout); + tune_runtime_env_kqueue(retval); TRACE_OUT(duplicate_runtime_env); return (retval); @@ -600,9 +607,10 @@ /* * Processing loop is the basic processing routine, that forms a body of each - * procssing thread + * procssing thread. It works till its thread receives a signal and returns + * the signal number. */ -static void +static int processing_loop(cache the_cache, struct runtime_env *env, struct configuration *config) { @@ -648,6 +656,8 @@ process_timer_event(event_data, env, config); break; + case EVFILT_SIGNAL: + return (event_data->ident); default: break; } @@ -675,6 +685,8 @@ sigemptyset(&new); sigaddset(&new, SIGPIPE); + sigaddset(&new, SIGTERM); + sigaddset(&new, SIGHUP); if (pthread_sigmask(SIG_BLOCK, &new, NULL) != 0) LOG_ERR_1("processing thread", "thread can't block the SIGPIPE signal"); @@ -700,6 +712,51 @@ time->tv_usec = 0; } +int +wait_for_hup_term() +{ + struct kevent eventlist[2]; + int queue, nevents; + + queue = kqueue(); + if (queue == -1) { + LOG_ERR_2("wait_for_hup_term", "can't create kqueue"); + return (-1); + } + + EV_SET(&eventlist[0], SIGTERM, EVFILT_SIGNAL, EV_ADD | EV_ONESHOT, + 0, 0, 0); + EV_SET(&eventlist[1], SIGHUP, EVFILT_SIGNAL, EV_ADD | EV_ONESHOT, + 0, 0, 0); + + nevents = kevent(queue, eventlist, + sizeof(eventlist) / sizeof(struct kevent), eventlist, + sizeof(eventlist) / sizeof(struct kevent), NULL); + if (nevents == 0) { + LOG_ERR_2("wait_for_hup_term", "kevent() returned 0 events"); + close(queue); + return (-1); + } else { + if (nevents != 1) { + LOG_ERR_2("wait_for_hup_term", "kevent() returned more " + "than 1 event"); + close(queue); + return (-1); + } + + if (eventlist[0].flags & EV_ERROR) { + LOG_ERR_2("wait_for_hup_term", "error while registering" + " event with kevent()"); + close(queue); + return (-1); + } + + assert(eventlist[0].filter == EVFILT_SIGNAL); + close(queue); + return (eventlist[0].ident); + } +} + /* * The idea of _nss_cache_cycle_prevention_function is that nsdispatch will * search for this symbol in the executable. This symbol is the attribute of @@ -721,11 +778,13 @@ struct pidfh *pidfile; pid_t pid; + sigset_t psigmask; char const *config_file; char const *error_str; int error_line; int i, res; + int do_gracerestart; int trace_mode_enabled; int force_single_threaded; @@ -735,7 +794,6 @@ int show_statistics; int daemon_mode, interactive_mode; - /* by default all debug messages are omitted */ TRACE_OFF(); @@ -884,8 +942,14 @@ if (trace_mode_enabled == 1) TRACE_ON(); - /* blocking the main thread from receiving SIGPIPE signal */ - sigblock(sigmask(SIGPIPE)); + sigemptyset(&psigmask); + sigaddset(&psigmask, SIGPIPE); + sigaddset(&psigmask, SIGTERM); + sigaddset(&psigmask, SIGHUP); + if (pthread_sigmask(SIG_BLOCK, &psigmask, NULL) != 0) { + LOG_ERR_1("main", "can't set process signal mask"); + return (-1); + } /* daemonization */ if (do_not_daemonize == 0) { @@ -921,6 +985,9 @@ } LOG_MSG_1("main", "request agents registered successfully"); +gracerestart: + do_gracerestart = 0; + /* configuration initialization */ s_configuration = init_configuration(); if (s_configuration == NULL) { @@ -1029,14 +1096,30 @@ thread_args = NULL; } - for (i = 0; i < s_configuration->threads_num; ++i) { - if (threads[i] != NULL) - pthread_join(threads[i], NULL); + switch (wait_for_hup_term()) { + case SIGHUP: + do_gracerestart = 1; + break; + case SIGTERM: + break; + default: + LOG_ERR_1("main", "wait_for_hup_term() failed"); + break; } } else { LOG_MSG_1("main", "working in single-threaded mode"); precache_entries(s_configuration); - processing_loop(s_cache, s_runtime_env, s_configuration); + res = processing_loop(s_cache, s_runtime_env, s_configuration); + switch (res) { + case SIGHUP: + do_gracerestart = 1; + break; + case SIGTERM: + break; + default: + LOG_ERR_1("main", "processing_loop() returned %d", res); + break; + } } fin: @@ -1048,6 +1131,9 @@ /* configuration destruction */ destroy_configuration(s_configuration); + + if (do_gracerestart != 0) + goto gracerestart; /* agents table destruction */ destroy_agent_table(s_agent_table);