Logo Search packages:      
Sourcecode: dcc version File versions  Download package

clnt_threaded.c

/* Distributed Checksum Clearinghouse
 *
 * threaded version of client locking
 *
 * Copyright (c) 2005 by Rhyolite Software
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE DISCLAIMS ALL
 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE
 * BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 * SOFTWARE.
 *
 * Rhyolite Software DCC 1.2.74-1.45 $Revision$
 */

#include "dcc_ck.h"
#include "dcc_clnt.h"
#ifdef HAVE_PTHREAD_H
#include <pthread.h>
#else
#include <sys/pthread.h>
#endif
#include <signal.h>

u_char grey_on;
u_char grey_query_only;

DCC_WF cmn_wf;


/* many POSIX thread implementations have unexpected side effects on
 * ordinary system calls, so don't use the threaded version unless
 * necessary */

/* protect the links among contexts and the miscellaneous global
 * variables in the DCC client library */
static pthread_mutex_t ctxts_mutex;

/* make syslog() thread-safe */
static pthread_mutex_t syslog_mutex;
static u_char syslog_threaded;

#ifdef DEBUG_HEAP
static pthread_mutex_t malloc_mutex;
static u_char malloc_threaded;
#endif

/* make gethostbyname() thread-safe */
static pthread_mutex_t host_mutex;

/* Only one process (not just one thread in every process) can
 * resolve server host names or send a burst of NOPs to measure RTTs.
 * These locks only keep more than one thread in a process from trying to
 * resolve server names to addresses.  A lock on the map file is used
 * to resolve the lock among processes. */
static pthread_mutex_t resolve_mutex;
static pthread_t resolve_tid;
static pthread_cond_t resolve_cond;

static u_char resolve_stopping;

/* The threaded DNS blacklist support uses fork() to create helper processes
 * to wait for the typical single-threaded DNS resolver library. */
static pthread_mutex_t dnsbl_mutex;
static pthread_cond_t dnsbl_cond;


void
dcc_ctxts_lock(void)
{
      int error = pthread_mutex_lock(&ctxts_mutex);
      if (error)
            dcc_logbad(EX_SOFTWARE, "pthread_mutex_lock(ctxts): %s",
                     ERROR_STR1(error));
}



void
dcc_ctxts_unlock(void)
{
      int error = pthread_mutex_unlock(&ctxts_mutex);
      if (error)
            dcc_logbad(EX_SOFTWARE, "pthread_mutex_unlock(ctxts): %s",
                     ERROR_STR1(error));
}



void
dcc_syslog_lock(void)
{
      int error;

      if (!syslog_threaded)
            return;
      error = pthread_mutex_lock(&syslog_mutex);
      if (error)
            dcc_logbad(EX_SOFTWARE, "pthread_mutex_lock(syslog): %s",
                     ERROR_STR1(error));
}



void
dcc_syslog_unlock(void)
{
      int error;

      if (!syslog_threaded)
            return;
      error = pthread_mutex_unlock(&syslog_mutex);
      if (error)
            dcc_logbad(EX_SOFTWARE, "pthread_mutex_unlock(syslog): %s",
                     ERROR_STR1(error));
}



u_char dcc_host_locked = 1;
static u_char dcc_host_threaded = 0;

void
dcc_host_lock(void)
{
      int error;

      if (!dcc_host_threaded)
            return;

      error = pthread_mutex_lock(&host_mutex);
      if (error)
            dcc_logbad(EX_SOFTWARE, "pthread_mutex_lock(host): %s",
                     ERROR_STR1(error));
      dcc_host_locked = 1;
}



void
dcc_host_unlock(void)
{
      int error;

      if (!dcc_host_threaded)
            return;

      dcc_host_locked = 0;
      error = pthread_mutex_unlock(&host_mutex);
      if (error)
            dcc_logbad(EX_SOFTWARE, "pthread_mutex_unlock(host): %s",
                     ERROR_STR1(error));
}



#ifdef DEBUG_HEAP
void
dcc_malloc_lock(void)
{
      int error;

      if (!malloc_threaded)
            return;
      error = pthread_mutex_lock(&malloc_mutex);
      if (error)
            dcc_logbad(EX_SOFTWARE, "pthread_mutex_lock(malloc): %s",
                     ERROR_STR1(error));
}


void
dcc_malloc_unlock(void)
{
      int error;

      if (!malloc_threaded)
            return;
      error = pthread_mutex_unlock(&malloc_mutex);
      if (error)
            dcc_logbad(EX_SOFTWARE, "pthread_mutex_unlock(malloc): %s",
                     ERROR_STR1(error));
}
#endif /* DEBUG_HEAP */



#ifndef HAVE_LOCALTIME_R
/* make localtime() thread safe */
static pthread_mutex_t syslog_mutex;
static u_char localtime_threaded;

void
dcc_localtime_lock(void)
{
      int error;

      if (!localtime_threaded)
            return;
      error = pthread_mutex_lock(&localtime_mutex);
      if (error)
            dcc_logbad(EX_SOFTWARE, "pthread_mutex_lock(localtime): %s",
                     ERROR_STR1(error));
}



void
dcc_localtime_unlock(void)
{
      int error;

      if (!localtime_threaded)
            return;
      error = pthread_mutex_unlock(&localtime_mutex);
      if (error)
            dcc_logbad(EX_SOFTWARE, "pthread_mutex_unlock(localtime): %s",
                     ERROR_STR1(error));
}
#endif /* HAVE_LOCALTIME_R */



u_char                              /* 0=lock busy, 1=got it */
dcc_resolve_mutex_lock(u_char nowait)
{
      int error;

      /* if getting the lock is optional, merely try it */
      if (nowait) {
            error = pthread_mutex_trylock(&resolve_mutex);
            if (error) {
                  if (error == EBUSY)
                        return 0;
                  dcc_logbad(EX_SOFTWARE,
                           "pthread_mutex_trylock(resolve): %s",
                           ERROR_STR1(error));
            }
      } else {
            error = pthread_mutex_lock(&resolve_mutex);
            if (error)
                  dcc_logbad(EX_SOFTWARE,
                           "pthread_mutex_lock(resolve): %s",
                           ERROR_STR1(error));
      }
      return 1;
}



void
dcc_resolve_mutex_unlock(void)
{
      int error;

      error = pthread_mutex_unlock(&resolve_mutex);
      if (error)
            dcc_logbad(EX_SOFTWARE,
                     "pthread_mutex_unlock(resolve): %s",
                     ERROR_STR1(error));
}



static void *
resolve_thread(void *arg UATTRIB)
{
      DCC_EMSG emsg;
      DCC_CLNT_CTXT *ctxt;
      sigset_t sigsoff;
      DCC_WHITE_RESULT result;
      int error;

      sigemptyset(&sigsoff);
      sigaddset(&sigsoff, SIGHUP);
      sigaddset(&sigsoff, SIGINT);
      sigaddset(&sigsoff, SIGTERM);
      error = pthread_sigmask(SIG_BLOCK, &sigsoff, 0);
      if (error)
            dcc_logbad(EX_SOFTWARE, "pthread_sigmask(resolve): %s",
                     ERROR_STR1(error));

      dcc_ctxts_lock();
      ctxt = dcc_alloc_ctxt();
      dcc_ctxts_unlock();
      for (;;) {
            dcc_ctxts_lock();
            error = pthread_cond_wait(&resolve_cond, &ctxts_mutex);
            if (error != 0)
                  dcc_logbad(EX_SOFTWARE,
                           "pthread_cond_wait(resolve): %s",
                           ERROR_STR1(error));

            if (resolve_stopping) {
                  dcc_ctxts_unlock();
                  pthread_exit(0);
            }

            emsg[0] = '\0';
            if (!dcc_info_lock(emsg))
                  dcc_logbad(dcc_ex_code, "%s", emsg);
            if (!dcc_clnt_rdy(emsg, ctxt, DCC_CLNT_FG_NO_FAIL))
                  dcc_error_msg("%s", emsg);
            else if (!dcc_info_unlock(emsg))
                  dcc_logbad(dcc_ex_code, "%s", emsg);
            if (grey_on) {
                  emsg[0] = '\0';
                  if (!dcc_info_lock(emsg))
                        dcc_logbad(dcc_ex_code, "%s", emsg);
                  if (!dcc_clnt_rdy(emsg, ctxt, (DCC_CLNT_FG_GREY
                                           | DCC_CLNT_FG_NO_FAIL)))
                        dcc_error_msg("%s", emsg);
                  else if (!dcc_info_unlock(emsg))
                        dcc_logbad(dcc_ex_code, "%s", emsg);
            }
            dcc_ctxts_unlock();

            if (!dcc_white_rdy(emsg, &cmn_wf, 0,
                           DCC_WHITE_RDY_LOCK_NEED_MUTEX, &result)
                && (result == DCC_WHITE_ERROR
                  || dcc_clnt_debug))
                  dcc_error_msg("%s", emsg);
            dcc_wf_unlock(&cmn_wf);
      }
}



u_char                              /* 1=awoke the resolver thread */
dcc_clnt_wake_resolve(void)
{
      int error;

      /* we cannot awaken ourself */
      if (pthread_equal(pthread_self(), resolve_tid))
            return 0;

      error = pthread_cond_signal(&resolve_cond);
      if (error != 0)
            dcc_logbad(EX_SOFTWARE, "pthread_cond_signal(resolve): %s",
                     ERROR_STR1(error));
      return 1;
}



void
dcc_clnt_stop_resolve(void)
{
      if (resolve_stopping++)
            return;
      if (pthread_equal(pthread_self(), resolve_tid))
            return;
      pthread_cond_signal(&resolve_cond);
}



/* some pthreads implementations (e.g. AIX) don't like static
 * initializations */
void
dcc_mutex_init(void *mutex, const char *nm)
{
      int error = pthread_mutex_init(mutex, 0);
      if (error)
            dcc_logbad(EX_SOFTWARE, "pthread_mutex_init(%s): %s",
                     nm, ERROR_STR1(error));
}



void
dcc_clnt_thread_init(void)
{
      int error;

#ifdef DCC_WIN32
      win32_init();
#endif

      dcc_mutex_init(&ctxts_mutex, 0);
      dcc_mutex_init(&syslog_mutex, 0);
      syslog_threaded = 1;
#ifdef DEBUG_HEAP
      dcc_mutex_init(&malloc_mutex, 0);
      malloc_threaded = 1;
#endif
#ifndef HAVE_LOCALTIME_R
      dcc_mutex_init(&localtime_mutex, 0);
      localtime_threaded = 1;
#endif
      dcc_mutex_init(&host_mutex, 0);
      dcc_mutex_init(&resolve_mutex, 0);
      dcc_host_threaded = 1;
      dcc_host_locked = 0;

      error = pthread_cond_init(&resolve_cond, 0);
      if (error)
            dcc_logbad(EX_SOFTWARE, "phtread_cond_init(resolve): %s",
                     ERROR_STR1(error));
      error = pthread_create(&resolve_tid, 0, resolve_thread, 0);
      if (error)
            dcc_logbad(EX_SOFTWARE, "pthread_create(resolve): %s",
                     ERROR_STR1(error));
      error = pthread_detach(resolve_tid);
      if (error)
            dcc_logbad(EX_SOFTWARE, "pthread_detach(resolve): %s",
                     ERROR_STR1(error));
}



/* protect DNS blacklist helper channels */
u_char
dcc_dnsbl_lock_init(void)
{
      int error;

      dcc_mutex_init(&dnsbl_mutex, 0);
      error = pthread_cond_init(&dnsbl_cond, 0);
      if (error)
            dcc_logbad(EX_SOFTWARE, "phtread_cond_init(dnsbl): %s",
                     ERROR_STR1(error));
      return 1;
}



void
dcc_dnsbl_lock(void)
{
      int error;

      error =  pthread_mutex_lock(&dnsbl_mutex);
      if (error)
            dcc_logbad(EX_SOFTWARE,
                     "pthread_mutex_lock(dnsbl): %s",
                     ERROR_STR1(error));
}



void
dcc_dnsbl_signal(void)
{
      int error;

      error =  pthread_cond_signal(&dnsbl_cond);
      if (error)
            dcc_logbad(EX_SOFTWARE,
                     "pthread_cond_signal(dnsbl): %s",
                     ERROR_STR1(error));
}



void
dcc_dnsbl_wait(time_t stop)
{
      struct timespec abstime;
      int error;

      abstime.tv_sec = stop;
      abstime.tv_nsec = 0;
      error = pthread_cond_timedwait(&dnsbl_cond, &dnsbl_mutex, &abstime);
      if (error != 0 && error != ETIMEDOUT)
            dcc_logbad(EX_SOFTWARE,
                     "pthread_cond_timedwait(dnsbl): %s",
                     ERROR_STR1(error));
}



void
dcc_dnsbl_unlock(void)
{
      int error;

      error = pthread_mutex_unlock(&dnsbl_mutex);
      if (error)
            dcc_logbad(EX_SOFTWARE,
                     "pthread_mutex_unlock(dnsbl): %s",
                     ERROR_STR1(error));
}



/* protect whitelist variables */
void
dcc_wf_init(DCC_WF *wf, void *mutex,
          u_char per_user)          /* 0=global 1=per-user */
{
      dcc_mutex_init(mutex, "");

      /* the following lines must match dcc_wf_init() in clnt_unthreaded.c */
      wf->ht_fd = -1;
      if (per_user == 0)
            wf->flags &= ~DCC_WF_PER_USER;
      else if (per_user == 1)
            wf->flags |= DCC_WF_PER_USER;
      wf->mutex = mutex;
}



void
dcc_wf_lock(DCC_WF *wf)
{
      int error = pthread_mutex_lock(wf->mutex);
      if (error)
            dcc_logbad(EX_SOFTWARE, "pthread_mutex_lock(white): %s",
                     ERROR_STR1(error));
}



void
dcc_wf_unlock(DCC_WF *wf)
{
      int error = pthread_mutex_unlock(wf->mutex);
      if (error)
            dcc_logbad(EX_SOFTWARE, "pthread_mutex_unlock(white): %s",
                     ERROR_STR1(error));
}

Generated by  Doxygen 1.6.0   Back to index