From 90bc5ee4867a8690b9053917ec30053d61570999 Mon Sep 17 00:00:00 2001 From: Ben Kibbey Date: Sat, 10 Nov 2012 21:09:53 -0500 Subject: [PATCH] Portability fixes. Mostly for Android. Now uses SIGUSR2 to terminate threads since sigwaitinfo() is not as common (Android) as sigtimedwait() and also pthread_cancel() is not available on Android. SIGCHLD is used in the main thread to wait for all other clients to disconnect before exiting. The drawback of using sigtimedwait() (because mostly of the lack of pthread_cancel()) is that some threads cannot block while waiting for an operation. The (tcp_)accept_thread(), for example, needs to set the file descriptor to O_NONBLOCK to be able to check whether it recieved SIGUSR2 from the main thread (shutdown/reload). Its less fast since there needs to be a wait interval between accept() calls to prevent CPU spikes. It's about twice as slow as sigwaitinfo() with the current wait interval, at least in accept_thread(), but still should be fast enough for most folks. --- configure.ac | 15 ++- src/Makefile.am | 2 +- src/agent.c | 4 + src/commands.c | 4 +- src/common.h | 11 +++ src/mutex.h | 23 +++-- src/pwmd.c | 290 +++++++++++++++++++++++++++++++++++++++++++----------- src/rcfile.c | 6 +- src/rcfile.h | 2 +- src/status.c | 4 +- src/tls.c | 4 +- src/util-misc.c | 8 ++ src/util-string.h | 2 + 13 files changed, 294 insertions(+), 81 deletions(-) diff --git a/configure.ac b/configure.ac index fcff37e7..2ab09b44 100644 --- a/configure.ac +++ b/configure.ac @@ -33,6 +33,7 @@ save_CFLAGS="$CFLAGS" save_LIBS="$LIBS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" LIBS="$PTHREAD_LIBS $LIBS" + acx_pthread_mutex_timedlock_ok=no AC_MSG_CHECKING([for pthread_mutex_timedlock()]) AC_TRY_LINK_FUNC(pthread_mutex_timedlock, acx_pthread_mutex_timedlock_ok=yes) @@ -40,6 +41,15 @@ AC_MSG_RESULT($acx_pthread_mutex_timedlock_ok) if test x"$acx_pthread_mutex_timedlock_ok" = "xyes"; then AC_DEFINE(HAVE_PTHREAD_MUTEX_TIMEDLOCK, 1, [Define if your OS has pthread_mutex_timedlock().]) fi + +acx_pthread_testcancel_ok=no +AC_MSG_CHECKING([for pthread_testcancel()]) +AC_TRY_LINK_FUNC(pthread_testcancel, acx_pthread_testcancel_ok=yes) +AC_MSG_RESULT($acx_pthread_testcancel_ok) +if test x"$acx_pthread_testcancel_ok" = "xyes"; then + AC_DEFINE(HAVE_PTHREAD_TESTCANCEL, 1, [Define if your OS has pthread_testcancel().]) +fi + CFLAGS="$save_CFLAGS" LIBS="$save_LIBS" @@ -48,6 +58,7 @@ PKG_PROG_PKG_CONFIG() AM_PATH_LIBGCRYPT([1:1.5.0],, AC_MSG_ERROR([libgcrypt not found])) AM_PATH_XML2(,, AC_MSG_ERROR([libxml2 not found])) AM_PATH_GPG_ERROR(,, AC_MSG_ERROR([libgpg-error not found])) +AC_SEARCH_LIBS([clock_gettime], [rt]) dnl For data file conversion. AC_CHECK_LIB(z, deflate,, AC_MSG_ERROR([Version 1.2.2.1 or later of zlib is required])) @@ -88,11 +99,11 @@ AC_TYPE_SIGNAL dnl Checks for library functions. AC_FUNC_STAT AC_FUNC_FORK -AC_FUNC_MALLOC AC_FUNC_MEMCMP AC_FUNC_STRFTIME AC_CHECK_FUNCS([memset mkdir munmap setlocale socket strchr strerror strrchr \ - strstr strtol setrlimit mlockall getopt_long backtrace]) + strstr strtol setrlimit mlockall getopt_long backtrace \ + sigtimedwait getpwnam_r getgrnam_r]) AM_CONDITIONAL([NEED_GETOPT_LONG], [test "$ac_cv_func_getopt_long" != "yes"]) AC_DEFUN([AC_DEBUG], diff --git a/src/Makefile.am b/src/Makefile.am index 570844c5..f7e9a65a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -8,7 +8,7 @@ pwmd_SOURCES = pwmd.c xml.c xml.h pwmd-error.c pwmd-error.h commands.c \ util-slist.h util-string.c util-string.h util-misc.c \ util-misc.h gettext.h pwmd_LDFLAGS = @PTHREAD_LIBS@ @XML_LIBS@ @GPG_ERROR_LIBS@ @LIBASSUAN_LIBS@ \ - @LIBGCRYPT_LIBS@ -lrt + @LIBGCRYPT_LIBS@ pwmd_CFLAGS = -DLOCALEDIR=\"${prefix}/share/locale\" @PTHREAD_CFLAGS@ \ @XML_CPPFLAGS@ @GPG_ERROR_CFLAGS@ @LIBASSUAN_CFLAGS@ \ @LIBGCRYPT_CFLAGS@ diff --git a/src/agent.c b/src/agent.c index 255ea26a..91d6bb40 100644 --- a/src/agent.c +++ b/src/agent.c @@ -617,7 +617,9 @@ static gpg_error_t iterate_crypto_once(gcry_cipher_hd_t h, unsigned char *inbuf, if (total >= insize) break; +#ifdef HAVE_PTHREAD_TESTCANCEL pthread_testcancel(); +#endif } pthread_cleanup_pop(1); @@ -1140,7 +1142,9 @@ gpg_error_t encrypt_data_file(assuan_context_t ctx, struct crypto_s *crypto, pthread_cleanup_push(gcry_free, key); key = gcry_random_bytes_secure(keysize, GCRY_STRONG_RANDOM); +#ifdef HAVE_PTHREAD_TESTCANCEL pthread_testcancel(); // may have been a long operation +#endif pthread_cleanup_pop(0); if (!key) return GPG_ERR_ENOMEM; diff --git a/src/commands.c b/src/commands.c index 341e40a4..740069c3 100644 --- a/src/commands.c +++ b/src/commands.c @@ -3213,9 +3213,11 @@ static gpg_error_t bye_notify(assuan_context_t ctx, char *line) int rc; do { + struct timeval tv = { 0, 50000 }; + rc = gnutls_bye(cl->thd->tls->ses, GNUTLS_SHUT_RDWR); if (rc == GNUTLS_E_AGAIN) - usleep(50000); + select(0, NULL, NULL, NULL, &tv); } while (rc == GNUTLS_E_AGAIN); } #endif diff --git a/src/common.h b/src/common.h index 09c8878f..5e18614f 100644 --- a/src/common.h +++ b/src/common.h @@ -31,6 +31,17 @@ #endif #endif +#ifdef HAVE_LIMITS_H +#include +#ifndef LINE_MAX +#ifdef _POSIX2_LINE_MAX +#define LINE_MAX _POSIX2_LINE_MAX +#else +#define LINE_MAX 2048 +#endif +#endif +#endif + #ifndef _ #include "gettext.h" #define _(msgid) gettext(msgid) diff --git a/src/mutex.h b/src/mutex.h index 3de0ec83..16a9628c 100644 --- a/src/mutex.h +++ b/src/mutex.h @@ -32,6 +32,19 @@ #include #include +#define INIT_TIMESPEC(t, ts) do { \ + long s = (t*100000000)/1000000000; \ + long l = (t*100000000)%1000000000; \ + clock_gettime(CLOCK_REALTIME, &ts); \ + ts.tv_sec += s; \ + if (ts.tv_nsec + l >= 1000000000) { \ + ts.tv_sec++; \ + ts.tv_nsec = 0; \ + } \ + else \ + ts.tv_nsec += l; \ + } while (0) + #ifdef MUTEX_DEBUG #define MUTEX_LOCK_DEBUG(m) do { \ log_write("%s(%i): %s(): LOCK %p", __FILE__, __LINE__, __FUNCTION__, m); @@ -57,15 +70,7 @@ #ifdef HAVE_PTHREAD_MUTEX_TIMEDLOCK #define MUTEX_TIMED_LOCK(m, timeout, rc) do { \ struct timespec ts; \ - clock_gettime(CLOCK_REALTIME, &ts); \ - time_t s = (timeout*100000000)/1000000000;\ - long l = (timeout*100000000)%1000000000; \ - ts.tv_sec += s; \ - ts.tv_nsec += l; \ - if (ts.tv_nsec >= 1000000000) {\ - ts.tv_sec++; \ - ts.tv_nsec -= 1000000000; \ - } \ + INIT_TIMESPEC(timeout, ts); \ int n = pthread_mutex_timedlock(m, &ts); \ if (n == ETIMEDOUT) { \ rc = GPG_ERR_LOCKED;\ diff --git a/src/pwmd.c b/src/pwmd.c index c526fd9a..55493573 100644 --- a/src/pwmd.c +++ b/src/pwmd.c @@ -85,6 +85,12 @@ #include "agent.h" #include "convert.h" +/* In tenths of a second. */ +#define SIG_TIMEOUT 1 + +/* For (tcp_)accept_thread (usec). */ +#define ACCEPT_TIMEOUT 30000 + static int quit; static int exiting; static int cmdline; @@ -93,11 +99,14 @@ static int nofork; static pthread_cond_t quit_cond; static pthread_mutex_t quit_mutex; static int no_passphrase_file = 0; +static pthread_key_t signal_thread_key; #ifdef WITH_GNUTLS static int tls_fd; static int tls6_fd; static pthread_t tls_tid; static pthread_t tls6_tid; +static int spawned_tls; +static int spawned_tls6; static int start_stop_tls(int term); #endif @@ -107,6 +116,14 @@ static int signal_loop(sigset_t sigset); GCRY_THREAD_OPTION_PTHREAD_IMPL; +static void catch_thread_signal(int sig) +{ + int *n = (int *)pthread_getspecific(signal_thread_key); + + *n = 1; + pthread_setspecific(signal_thread_key, n); +} + static void cache_push_from_rcfile() { struct crypto_s *crypto; @@ -163,6 +180,12 @@ static void setup_logging() static void *reload_rcfile_thread(void *arg) { + int *n = xmalloc(sizeof(int)); + + *n = 0; + pthread_setspecific(signal_thread_key, n); + signal(SIGUSR2, catch_thread_signal); + #ifdef HAVE_PR_SET_NAME prctl(PR_SET_NAME, "reload rcfile"); #endif @@ -171,16 +194,21 @@ static void *reload_rcfile_thread(void *arg) pthread_cleanup_push(cleanup_mutex_cb, &rcfile_mutex); for (;;) { - int exists; struct slist_s *config; char **users; int b = disable_list_and_dump; #ifdef WITH_GNUTLS + int exists; int tcp_require_key = config_get_bool_param(global_config, "global", "tcp_require_key", &exists); #endif + int *n; pthread_cond_wait(&rcfile_cond, &rcfile_mutex); + n = (int *)pthread_getspecific(signal_thread_key); + if (*n) + break; + users = config_get_list("global", "allowed"); log_write(_("reloading configuration file '%s'"), rcfile); config = config_parse(rcfile); @@ -189,7 +217,7 @@ static void *reload_rcfile_thread(void *arg) global_config = config; setup_logging(); cache_push_from_rcfile(); - config_clear_keyss(); + config_clear_keys(); } disable_list_and_dump = !disable_list_and_dump ? b : 1; @@ -435,19 +463,14 @@ static gpg_error_t setup_crypto() return 0; } -static gpg_error_t validate_peer(struct client_s *cl) +#ifdef HAVE_GETGRNAM_R +static gpg_error_t do_validate_peer(struct client_s *cl, assuan_peercred_t *peer) { char **users; int allowed = 0; - assuan_peercred_t peercred; gpg_error_t rc; -#ifdef WITH_GNUTLS - if (cl->thd->remote) - return 0; -#endif - - rc = assuan_get_peercred(cl->ctx, &peercred); + rc = assuan_get_peercred(cl->ctx, peer); if (rc) return rc; @@ -465,29 +488,26 @@ static gpg_error_t validate_peer(struct client_s *cl) len = 16384; buf = xmalloc(len); - if (!buf) { strv_free(users); return GPG_ERR_ENOMEM; } if (!getgrnam_r(*(p)+1, &gr, buf, len, &gresult) && gresult) { - if (gresult->gr_gid == peercred->gid) { + if (gresult->gr_gid == (*peer)->gid) { xfree(buf); allowed = 1; break; } len = sysconf(_SC_GETPW_R_SIZE_MAX); - if (len == -1) len = 16384; char *tbuf = xmalloc(len); - for (char **t = gresult->gr_mem; *t; t++) { if (!getpwnam_r(*t, &pw, tbuf, len, &result) && result) { - if (result->pw_uid == peercred->uid) { + if (result->pw_uid == (*peer)->uid) { xfree(buf); allowed = 1; break; @@ -515,7 +535,7 @@ static gpg_error_t validate_peer(struct client_s *cl) } if (!getpwnam_r(*p, &pw, buf, len, &result) && result) { - if (result->pw_uid == peercred->uid) { + if (result->pw_uid == (*peer)->uid) { xfree(buf); allowed = 1; break; @@ -529,11 +549,74 @@ static gpg_error_t validate_peer(struct client_s *cl) strv_free(users); } - log_write("peer %s: uid=%i, gid=%i, pid=%i", - allowed ? _("accepted") : _("rejected"), peercred->uid, - peercred->gid, peercred->pid); return allowed ? 0 : GPG_ERR_INV_USER_ID; } +#else +static gpg_error_t do_validate_peer(struct client_s *cl, assuan_peercred_t *peer) +{ + char **users; + int allowed = 0; + gpg_error_t rc; + + rc = assuan_get_peercred(cl->ctx, peer); + if (rc) + return rc; + + users = config_get_list("global", "allowed"); + if (users) { + for (char **p = users; *p; p++) { + struct passwd *result; + struct group *gresult; + + if (*(*p) == '@') { + gresult = getgrnam(*(p)+1); + if (gresult && gresult->gr_gid == (*peer)->gid) { + allowed = 1; + break; + } + + for (char **t = gresult->gr_mem; *t; t++) { + result = getpwnam(*t); + if (result && result->pw_uid == (*peer)->uid) { + allowed = 1; + break; + } + } + } + else{ + result = getpwnam(*p); + if (result && result->pw_uid == (*peer)->uid) { + allowed = 1; + break; + } + } + + if (allowed) + break; + } + + strv_free(users); + } + + return allowed ? 0 : GPG_ERR_INV_USER_ID; +} +#endif + +static gpg_error_t validate_peer(struct client_s *cl) +{ + gpg_error_t rc; + assuan_peercred_t peer; + +#ifdef WITH_GNUTLS + if (cl->thd->remote) + return 0; +#endif + + rc = do_validate_peer(cl, &peer); + log_write("peer %s: uid=%i, gid=%i, pid=%i", + !rc ? _("accepted") : _("rejected"), peer->uid, peer->gid, peer->pid); + return rc; +} static void xml_error_cb(void *data, xmlErrorPtr e) { @@ -1045,7 +1128,7 @@ static int do_cache_push(const char *filename, struct crypto_s *crypto) return 1; } -static gpg_error_t init_client_thread(int fd, const char *addr) +static gpg_error_t init_client(int fd, const char *addr) { gpg_error_t rc = 0; struct client_thread_s *new = xcalloc(1, sizeof(struct client_thread_s)); @@ -1120,18 +1203,30 @@ static void *get_in_addr(struct sockaddr *sa) static void *tcp_accept_thread(void *arg) { int sockfd = *(int*)arg; + int *n = xmalloc(sizeof(int)); + + *n = 0; + pthread_setspecific(signal_thread_key, n); + signal(SIGUSR2, catch_thread_signal); #ifdef HAVE_PR_SET_NAME prctl(PR_SET_NAME, "tcp_accept"); #endif pthread_setspecific(thread_name_key, str_dup(__FUNCTION__)); + fcntl(sockfd, F_SETFL, O_NONBLOCK); for (;;) { struct sockaddr_storage raddr; socklen_t slen = sizeof(raddr); int fd = -1; unsigned long n; + int *sigusr2; char s[INET6_ADDRSTRLEN]; + struct timeval tv = { 0, ACCEPT_TIMEOUT }; + + sigusr2 = (int *)pthread_getspecific(signal_thread_key); + if (*sigusr2) + break; fd = accept(sockfd, (struct sockaddr *)&raddr, &slen); if (fd == -1) { @@ -1144,6 +1239,7 @@ static void *tcp_accept_thread(void *arg) break; } + select(0, NULL, NULL, NULL, &tv); continue; } @@ -1152,14 +1248,15 @@ static void *tcp_accept_thread(void *arg) inet_ntop(raddr.ss_family, get_in_addr((struct sockaddr *)&raddr), s, sizeof s); - (void)init_client_thread(fd, s); + (void)init_client(fd, s); n = config_get_integer("global", "tcp_wait"); - if (n > 0) - usleep(n*100000); + if (n > 0) { + tv.tv_sec = (n*100000)/100000; + tv.tv_usec = (n*100000)%100000; + select(0, NULL, NULL, NULL, &tv); + } } - /* Just in case accept() failed for some reason other than EBADF */ - quit = 1; return NULL; } @@ -1174,19 +1271,27 @@ static int start_stop_tls_with_protocol(int ipv6, int term) if (term || config_get_boolean("global", "enable_tcp") == 0) { if (tls6_fd != -1) { - pthread_cancel(tls6_tid); - pthread_join(tls6_tid, NULL); + if (spawned_tls6) { + pthread_kill(tls6_tid, SIGUSR2); + pthread_join(tls6_tid, NULL); + } + shutdown(tls6_fd, SHUT_RDWR); close(tls6_fd); tls6_fd = -1; + spawned_tls6 = 0; } if (tls_fd != -1) { - pthread_cancel(tls_tid); - pthread_join(tls_tid, NULL); + if (spawned_tls) { + pthread_kill(tls_tid, SIGUSR2); + pthread_join(tls_tid, NULL); + } + shutdown(tls_fd, SHUT_RDWR); close(tls_fd); tls_fd = -1; + spawned_tls = 0; } /* A client may still be connected. */ @@ -1225,14 +1330,14 @@ static int start_stop_tls_with_protocol(int ipv6, int term) if (setsockopt(*fd, SOL_SOCKET, SO_REUSEADDR, &r, sizeof(int)) == -1) { - log_write("setsockopt(): %s", strerror(errno)); + log_write("setsockopt(): %s", pwmd_strerror(gpg_error_from_syserror())); freeaddrinfo(servinfo); goto fail; } if (bind(*fd, p->ai_addr, p->ai_addrlen) == -1) { close(*fd); - log_write("bind(): %s", strerror(errno)); + log_write("bind(): %s", pwmd_strerror(gpg_error_from_syserror())); continue; } @@ -1242,15 +1347,13 @@ static int start_stop_tls_with_protocol(int ipv6, int term) freeaddrinfo(servinfo); - if (!n) { - log_write("%s", _("could not bind")); + if (!n) goto fail; - } #ifdef HAVE_DECL_SO_BINDTODEVICE char *tmp = config_get_string("global", "tcp_interface"); if (tmp && setsockopt(*fd, SOL_SOCKET, SO_BINDTODEVICE, tmp, 1) == -1) { - log_write("setsockopt(): %s", strerror(errno)); + log_write("setsockopt(): %s", pwmd_strerror(gpg_error_from_syserror())); xfree(tmp); goto fail; } @@ -1280,6 +1383,11 @@ static int start_stop_tls_with_protocol(int ipv6, int term) goto fail; } + if (ipv6) + spawned_tls6 = 1; + else + spawned_tls = 1; + return 1; fail: @@ -1323,16 +1431,27 @@ static int start_stop_tls(int term) static void *accept_thread(void *arg) { int sockfd = *(int *)arg; + int *n = xmalloc(sizeof(int)); + + *n = 0; + pthread_setspecific(signal_thread_key, n); + signal(SIGUSR2, catch_thread_signal); #ifdef HAVE_PR_SET_NAME prctl(PR_SET_NAME, "accept"); #endif pthread_setspecific(thread_name_key, str_dup(__FUNCTION__)); + fcntl(sockfd, F_SETFL, O_NONBLOCK); for (;;) { socklen_t slen = sizeof(struct sockaddr_un); struct sockaddr_un raddr; int fd; + struct timeval tv = { 0, ACCEPT_TIMEOUT }; + int *sigusr2 = (int *)pthread_getspecific(signal_thread_key); + + if (*sigusr2) + break; fd = accept(sockfd, (struct sockaddr *)&raddr, &slen); if (fd == -1) { @@ -1344,10 +1463,12 @@ static void *accept_thread(void *arg) break; } + + select(0, NULL, NULL, NULL, &tv); continue; } - (void)init_client_thread(fd, NULL); + (void)init_client(fd, NULL); } /* Just in case accept() failed for some reason other than EBADF */ @@ -1357,13 +1478,26 @@ static void *accept_thread(void *arg) static void *cache_timer_thread(void *arg) { + int *n = xmalloc(sizeof(int)); + + *n = 0; + pthread_setspecific(signal_thread_key, n); + signal(SIGUSR2, catch_thread_signal); + #ifdef HAVE_PR_SET_NAME prctl(PR_SET_NAME, "cache timer"); #endif pthread_setspecific(thread_name_key, str_dup(__FUNCTION__)); for (;;) { - sleep(1); + struct timeval tv = { 1, 0 }; + int *n; + + select(0, NULL, NULL, NULL, &tv); + n = (int *)pthread_getspecific(signal_thread_key); + if (*n) + break; + cache_adjust_timeout(); } @@ -1384,15 +1518,23 @@ static int signal_loop(sigset_t sigset) int siint = 0; do { - int sig = sigwaitinfo(&sigset, NULL); - + struct timespec ts; + int sig; + + INIT_TIMESPEC(SIG_TIMEOUT, ts); +#ifdef HAVE_SIGTIMEDWAIT + sig = sigtimedwait(&sigset, NULL, &ts); +#else // Android + sig = __rt_sigtimedwait(&sigset, NULL, &ts, _NSIG/8); +#endif if (sig == -1) { if (errno != EAGAIN) - log_write("sigwaitinfo(): %s", strerror(errno)); + log_write("sigwaitinfo(): %s", + pwmd_strerror(gpg_error_from_syserror())); continue; } - if (sig != SIGUSR2) + if (sig != SIGCHLD) log_write(_("caught signal %i (%s)"), sig, strsignal(sig)); switch (sig) { @@ -1408,7 +1550,7 @@ static int signal_loop(sigset_t sigset) cache_clear(NULL); send_status_all(STATUS_CACHE, NULL); break; - case SIGUSR2: + case SIGCHLD: done = 1; break; default: @@ -1432,6 +1574,13 @@ static void catchsig(int sig) static void *waiting_for_exit(void *arg) { + int *n = xmalloc(sizeof(int)); + int last = 0; + + *n = 0; + pthread_setspecific(signal_thread_key, n); + signal(SIGUSR2, catch_thread_signal); + #ifdef HAVE_PR_SET_NAME prctl(PR_SET_NAME, "exiting"); #endif @@ -1441,18 +1590,30 @@ static void *waiting_for_exit(void *arg) pthread_cleanup_push(cleanup_mutex_cb, &quit_mutex); for (;;) { + struct timespec ts; + int *s = (int *)pthread_getspecific(signal_thread_key); + int n; + MUTEX_LOCK(&cn_mutex); - int n = slist_length(cn_thread_list); + n = slist_length(cn_thread_list); MUTEX_UNLOCK(&cn_mutex); if (!n) break; - log_write(_("%i clients remain"), n); - pthread_cond_wait(&quit_cond, &quit_mutex); + if (*s) + break; + + if (last != n) { + log_write(_("%i clients remain"), n); + last = n; + } + + INIT_TIMESPEC(SIG_TIMEOUT, ts); + pthread_cond_timedwait(&quit_cond, &quit_mutex, &ts); } + kill(getpid(), SIGCHLD); pthread_cleanup_pop(1); - kill(getpid(), SIGUSR2); return NULL; } @@ -1480,7 +1641,7 @@ static int server_loop(int sockfd, char **socketpath) sigaddset(&sigset, SIGHUP); /* For exiting cleanly. */ - sigaddset(&sigset, SIGUSR2); + sigaddset(&sigset, SIGCHLD); /* Clears the cache and exits when something bad happens. */ signal(SIGABRT, catch_sigabrt); @@ -1534,7 +1695,7 @@ static int server_loop(int sockfd, char **socketpath) } cancel_timeout_thread = 1; - rc = create_thread(accept_thread, &sockfd, &accept_tid, 1); + rc = create_thread(accept_thread, &sockfd, &accept_tid, 0); if (rc) { log_write("%s(%i): pthread_create(): %s", __FILE__, __LINE__, pwmd_strerror(rc)); @@ -1542,7 +1703,6 @@ static int server_loop(int sockfd, char **socketpath) } cancel_accept_thread = 1; - sigdelset(&sigset, SIGUSR2); if (!setjmp(jmp)) signal_loop(sigset); else @@ -1555,8 +1715,11 @@ done: * before exiting but exit immediately if another termination signal is * sent. */ - if (cancel_accept_thread) - pthread_cancel(accept_tid); + if (cancel_accept_thread) { + int n = pthread_kill(accept_tid, SIGUSR2); + if (!n) + pthread_join(accept_tid, NULL); + } shutdown(sockfd, SHUT_RDWR); close(sockfd); @@ -1570,12 +1733,12 @@ done: if (n && !segv) { pthread_t tid; - rc = create_thread(waiting_for_exit, NULL, &tid, 1); + rc = create_thread(waiting_for_exit, NULL, &tid, 0); if (!rc) { - sigaddset(&sigset, SIGUSR2); if (signal_loop(sigset)) { log_write(_("Received second termination request. Exiting.")); - pthread_cancel(tid); + pthread_kill(tid, SIGUSR2); + pthread_join(tid, NULL); } } else @@ -1583,8 +1746,10 @@ done: pwmd_strerror(rc)); } - if (cancel_timeout_thread) - pthread_cancel(cache_timeout_tid); + if (cancel_timeout_thread) { + pthread_kill(cache_timeout_tid, SIGUSR2); + pthread_join(cache_timeout_tid, NULL); + } MUTEX_LOCK(&cn_mutex); i = 0; @@ -2062,6 +2227,7 @@ int main(int argc, char *argv[]) pthread_mutex_init(&cn_mutex, &attr); pthread_mutexattr_destroy(&attr); pthread_key_create(&last_error_key, free_key); + pthread_key_create(&signal_thread_key, free_key); if (!rcfile) rcfile = str_asprintf("%s/config", homedir); @@ -2203,7 +2369,7 @@ int main(int argc, char *argv[]) log_write(!nofork ? _("Done. Daemonizing...") : _("Done. Waiting for connections...")); } - config_clear_keyss(); + config_clear_keys(); /* * bind() doesn't like the full pathname of the socket or any non alphanum @@ -2307,10 +2473,16 @@ do_exit: #ifdef WITH_GNUTLS gnutls_global_deinit(); #endif - pthread_cancel(rcfile_tid); - pthread_join(rcfile_tid, NULL); + if (rcfile_tid) { + pthread_kill(rcfile_tid, SIGUSR2); + pthread_cond_signal(&rcfile_cond); + pthread_join(rcfile_tid, NULL); + } + pthread_cond_destroy(&rcfile_cond); pthread_mutex_destroy(&rcfile_mutex); + pthread_key_delete(last_error_key); + pthread_key_delete(signal_thread_key); if (global_config) config_free(global_config); diff --git a/src/rcfile.c b/src/rcfile.c index 2deaca0e..0256983c 100644 --- a/src/rcfile.c +++ b/src/rcfile.c @@ -29,10 +29,6 @@ #include #include -#ifdef HAVE_LIMITS_H -#include -#endif - #include "pwmd-error.h" #include #include "mutex.h" @@ -150,7 +146,7 @@ static void section_remove_param(struct section_s *section, const char *name) } } -void config_clear_keyss() +void config_clear_keys() { MUTEX_LOCK(&rcfile_mutex); unsigned i, t = slist_length(global_config); diff --git a/src/rcfile.h b/src/rcfile.h index b4f2e880..a5844489 100644 --- a/src/rcfile.h +++ b/src/rcfile.h @@ -41,7 +41,7 @@ pthread_cond_t rcfile_cond; pthread_t rcfile_tid; struct slist_s *config_parse(const char *filename); -void config_clear_keyss(); +void config_clear_keys(); void config_free(struct slist_s *config); char *config_get_value(const char *section, const char *what); diff --git a/src/status.c b/src/status.c index 1cfc0d2a..47e7ac1d 100644 --- a/src/status.c +++ b/src/status.c @@ -36,9 +36,9 @@ gpg_error_t send_status(assuan_context_t ctx, status_msg_t which, const char *fmt, ...) { - char *line = NULL; + const char *line = NULL; char buf[ASSUAN_LINELENGTH+1]; - char *status = NULL; + const char *status = NULL; va_list ap; char *p; diff --git a/src/tls.c b/src/tls.c index 8634f8a8..28f069e2 100644 --- a/src/tls.c +++ b/src/tls.c @@ -206,9 +206,11 @@ ssize_t tls_write_hook(assuan_context_t ctx, assuan_fd_t fd, const void *data, ssize_t ret; do { + struct timeval tv = { 0, 50000 }; + ret = gnutls_record_send(client->thd->tls->ses, data, len); if (ret == GNUTLS_E_AGAIN) - usleep(50000); + select(0, NULL, NULL, NULL, &tv); } while (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN); return ret; diff --git a/src/util-misc.c b/src/util-misc.c index b39ceafa..2e2115a5 100644 --- a/src/util-misc.c +++ b/src/util-misc.c @@ -69,6 +69,7 @@ char *get_username() char *get_home_dir() { +#ifdef HAVE_GETPWNAM_R struct passwd pw, *result; size_t len; char *buf; @@ -88,6 +89,13 @@ char *get_home_dir() home_directory = str_dup(result->pw_dir); xfree(buf); +#else + struct passwd *result = getpwuid(getuid()); + + if (result) + home_directory = str_dup(result->pw_dir); +#endif + return home_directory; } diff --git a/src/util-string.h b/src/util-string.h index c9df21c8..31cb721e 100644 --- a/src/util-string.h +++ b/src/util-string.h @@ -20,6 +20,8 @@ #ifndef UTIL_STRING_H #define UTIL_STRING_H +#include + struct string_s { size_t allocated; size_t len; -- 2.11.4.GIT