Logo Search packages:      
Sourcecode: slurm-llnl version File versions  Download package

log.c

/*****************************************************************************\
 *  log.c - slurm logging facilities
 *  $Id: log.c 10574 2006-12-15 23:38:29Z jette $
 *****************************************************************************
 *  Copyright (C) 2002 The Regents of the University of California.
 *  Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
 *  Written by Mark Grondona <mgrondona@llnl.gov>
 *  UCRL-CODE-226842.
 *  
 *  Much of this code was derived or adapted from the log.c component of 
 *  openssh which contains the following notices:
 *****************************************************************************
 * Author: Tatu Ylonen <ylo@cs.hut.fi>
 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
 *                    All rights reserved
 *
 * As far as I am concerned, the code I have written for this software
 * can be used freely for any purpose.  Any derived versions of this
 * software must be clearly marked as such, and if the derived work is
 * incompatible with the protocol description in the RFC file, it must be
 * called by a name other than "ssh" or "Secure Shell".
 *****************************************************************************
 * Copyright (c) 2000 Markus Friedl.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\*****************************************************************************/

/*
** MT safe
*/

#if HAVE_CONFIG_H
#  include "config.h"
#endif

#include <stdio.h>

#if HAVE_STRING_H
#  include <string.h>
#endif

#include <stdarg.h>
#include <errno.h>
#include <syslog.h>

#ifdef WITH_PTHREADS
#  include <pthread.h>
#endif /* WITH_PTHREADS */


#if HAVE_STDLIB_H
#  include <stdlib.h>   /* for abort() */
#endif

#include <slurm/slurm_errno.h>

#include "src/common/log.h"
#include "src/common/fd.h"
#include "src/common/macros.h"
#include "src/common/safeopen.h"
#include "src/common/xassert.h"
#include "src/common/xmalloc.h"
#include "src/common/xstring.h"

#ifndef LINEBUFSIZE
#  define LINEBUFSIZE 256
#endif

/*
** Define slurm-specific aliases for use by plugins, see slurm_xlator.h 
** for details. 
 */
strong_alias(log_init,        slurm_log_init);
strong_alias(log_reinit,      slurm_log_reinit);
strong_alias(log_fini,        slurm_log_fini);
strong_alias(log_alter,       slurm_log_alter);
strong_alias(log_set_fpfx,    slurm_log_set_fpfx);
strong_alias(log_fp,          slurm_log_fp);
strong_alias(log_has_data,    slurm_log_has_data);
strong_alias(log_flush,       slurm_log_flush);
strong_alias(dump_cleanup_list,     slurm_dump_cleanup_list);
strong_alias(fatal_add_cleanup,     slurm_fatal_add_cleanup);
strong_alias(fatal_add_cleanup_job, slurm_fatal_add_cleanup_job);
strong_alias(fatal_remove_cleanup,  slurm_fatal_remove_cleanup);
strong_alias(fatal_remove_cleanup_job,    slurm_fatal_remove_cleanup_job);
strong_alias(fatal_cleanup,   slurm_fatal_cleanup);
strong_alias(fatal,           slurm_fatal);
strong_alias(error,           slurm_error);
strong_alias(info,            slurm_info);
strong_alias(verbose,         slurm_verbose);
strong_alias(debug,           slurm_debug);
strong_alias(debug2,          slurm_debug2);
strong_alias(debug3,          slurm_debug3);

/* 
** struct defining a "log" type
*/
typedef struct {
      char *argv0;
      char *fpfx;              /* optional prefix for logfile entries */
      FILE *logfp;             /* log file pointer                    */
      cbuf_t buf;              /* stderr data buffer                  */
      cbuf_t fbuf;             /* logfile data buffer                 */
      log_facility_t facility;
      log_options_t opt;
      unsigned initialized:1;
}     log_t;

/* static variables */
#ifdef WITH_PTHREADS
  static pthread_mutex_t  log_lock = PTHREAD_MUTEX_INITIALIZER;
#else
  static int              log_lock;
#endif /* WITH_PTHREADS */
static log_t            *log = NULL;

#define LOG_INITIALIZED ((log != NULL) && (log->initialized))

/* define a default argv0 */
#if HAVE_PROGRAM_INVOCATION_NAME
/* This used to use program_invocation_short_name, but on some systems 
 * that gets truncated at 16 bytes, too short for our needs. */
extern char * program_invocation_name;
#  define default_name  program_invocation_name
#else 
#  define default_name ""
#endif


/* 
 * pthread_atfork handlers:
 */
#ifdef WITH_PTHREADS
static void _atfork_prep()   { slurm_mutex_lock(&log_lock);   }
static void _atfork_parent() { slurm_mutex_unlock(&log_lock); }
static void _atfork_child()  { slurm_mutex_unlock(&log_lock); }
static bool at_forked = false;
#  define atfork_install_handlers()                                           \
          while (!at_forked) {                                                \
                pthread_atfork(_atfork_prep, _atfork_parent, _atfork_child);  \
            at_forked = true;                                             \
        }
#else 
#  define atfork_install_handlers() (NULL)
#endif
static void _log_flush();

/*
 * Initialize log with 
 * prog = program name to tag error messages with
 * opt  = log_options_t specifying max log levels for syslog, stderr, and file
 * fac  = log facility for syslog (unused if syslog level == LOG_QUIET)
 * logfile =
 *        logfile name if logfile level > LOG_QUIET
 */
static int 
_log_init(char *prog, log_options_t opt, log_facility_t fac, char *logfile )
{
      int rc = 0;
      
      if (!log)  {
            log = (log_t *)xmalloc(sizeof(log_t));
            log->logfp = NULL;
            log->argv0 = NULL;
            log->buf   = NULL;
            log->fbuf  = NULL;
            log->fpfx  = NULL; 
            atfork_install_handlers();
      }

      if (prog) {
            if (log->argv0)
                  xfree(log->argv0);
            log->argv0 = xstrdup(xbasename(prog));
      } else if (!log->argv0) {
            char *short_name = strrchr(default_name, '/');
            if (short_name)
                  short_name++;
            else
                  short_name = default_name;
            log->argv0 = xstrdup(short_name);
      }

      if (!log->fpfx)
            log->fpfx = xstrdup("");

      log->opt = opt;

      if (log->buf) 
            cbuf_destroy(log->buf);
      if (log->fbuf)
            cbuf_destroy(log->fbuf);

      if (log->opt.buffered) {
            log->buf  = cbuf_create(128, 8192);
            log->fbuf = cbuf_create(128, 8192);
      }

      if (log->opt.syslog_level > LOG_LEVEL_QUIET)
            log->facility = fac;

      if (logfile && (log->opt.logfile_level > LOG_LEVEL_QUIET)) {
            FILE *fp; 

            fp = safeopen(logfile, "a", SAFEOPEN_LINK_OK);

            if (!fp) {
                  char *errmsg = NULL;
                  slurm_mutex_unlock(&log_lock);
                  xslurm_strerrorcat(errmsg);
                  fprintf(stderr, 
                        "%s: log_init(): Unable to open logfile"
                          "`%s': %s\n", prog, logfile, errmsg);
                  xfree(errmsg);
                  rc = errno;
                  goto out;
            } 

            if (log->logfp)
                  fclose(log->logfp); /* Ignore errors */

            log->logfp = fp;
      }

      if (log->logfp) {
            int fd;
            if ((fd = fileno(log->logfp)) < 0)
                  log->logfp = NULL;
            else
                  fd_set_close_on_exec(fd);
      }

      log->initialized = 1;
 out:
      return rc;
}


/* initialize log mutex, then initialize log data structures
 */
int log_init(char *prog, log_options_t opt, log_facility_t fac, char *logfile)
{
      int rc = 0;

      slurm_mutex_lock(&log_lock);
      rc = _log_init(prog, opt, fac, logfile);
      slurm_mutex_unlock(&log_lock);
      return rc;
}

void log_fini()
{
      if (!log)
            return;

      slurm_mutex_lock(&log_lock);
      _log_flush();
      xfree(log->argv0);
      xfree(log->fpfx);
      if (log->buf)
            cbuf_destroy(log->buf);
      if (log->fbuf)
            cbuf_destroy(log->fbuf);
      if (log->logfp)
            fclose(log->logfp);
      xfree(log);
      log = NULL;
      slurm_mutex_unlock(&log_lock);
}

void log_reinit()
{
      slurm_mutex_init(&log_lock);
}

void log_set_fpfx(char *prefix)
{
      slurm_mutex_lock(&log_lock);
      xfree(log->fpfx);
      if (!prefix)
            log->fpfx = xstrdup("");
      else {
            log->fpfx = xstrdup(prefix);
            xstrcatchar(log->fpfx, ' ');
      }
      slurm_mutex_unlock(&log_lock);
}

void log_set_argv0(char *argv0)
{
      slurm_mutex_lock(&log_lock);
      if (log->argv0)
            xfree(log->argv0);
      if (!argv0)
            log->argv0 = xstrdup("");
      else 
            log->argv0 = xstrdup(argv0);
      slurm_mutex_unlock(&log_lock);
}

/* reinitialize log data structures. Like log_init, but do not init
 * the log mutex
 */
int log_alter(log_options_t opt, log_facility_t fac, char *logfile)
{     
      int rc = 0;
      slurm_mutex_lock(&log_lock);
      rc = _log_init(NULL, opt, fac, logfile);
      slurm_mutex_unlock(&log_lock);
      return rc;
}

/* return the FILE * of the current logfile (stderr if logging to stderr)
 */
FILE *log_fp(void)
{
      FILE *fp;
      slurm_mutex_lock(&log_lock);
      if (log->logfp)
            fp = log->logfp;
      else
            fp = stderr;
      slurm_mutex_unlock(&log_lock);
      return fp;
}

/* return a heap allocated string formed from fmt and ap arglist
 * returned string is allocated with xmalloc, so must free with xfree.
 * 
 * args are like printf, with the addition of the following format chars:
 * - %m expands to strerror(errno)
 * - %t expands to strftime("%x %X") [ locally preferred short date/time ]
 * - %T expands to rfc822 date time  [ "dd Mon yyyy hh:mm:ss GMT offset" ]
 *
 * simple format specifiers are handled explicitly to avoid calls to 
 * vsnprintf and allow dynamic sizing of the message buffer. If a call
 * is made to vsnprintf, however, the message will be limited to 1024 bytes.
 * (inc. newline)
 *
 */
static char *vxstrfmt(const char *fmt, va_list ap)
{
      char        *buf = NULL;
      char        *p   = NULL;
      size_t      len = (size_t) 0;
      char        tmp[LINEBUFSIZE];
      int         unprocessed = 0;


      while (*fmt != '\0') {

            if ((p = (char *)strchr(fmt, '%')) == NULL) {  
                  /* no more format chars */
                  xstrcat(buf, fmt);
                  break;

            } else {        /* *p == '%' */

                  /* take difference from fmt to just before `%' */
                  len = (size_t) ((long)(p) - (long)fmt);

                  /* append from fmt to p into buf if there's 
                   * anythere there
                   */
                  if (len > 0) {
                        memcpy(tmp, fmt, len);
                        tmp[len] = '\0';
                        xstrcat(buf, tmp);
                  }

                  switch (*(++p)) {
                    case '%': /* "%%" => "%" */
                        xstrcatchar(buf, '%');
                        break;

                  case 'm':   /* "%m" => strerror(errno) */
                        xslurm_strerrorcat(buf);
                        break;

                  case 't':   /* "%t" => locally preferred date/time*/ 
                        xstrftimecat(buf, "%x %X");
                        break;
                  case 'T':   /* "%T" => "dd Mon yyyy hh:mm:ss off" */
                        xstrftimecat(buf, "%a %d %b %Y %H:%M:%S %z");   
                        break;
                  case 'M':       /* "%M" => "Mon DD hh:mm:ss"          */
                        xstrftimecat(buf, "%b %d %T");
                        break;
                  case 's':   /* "%s" => append string */
                        /* we deal with this case for efficiency */
                        if (unprocessed == 0) 
                              xstrcat(buf, va_arg(ap, char *));
                        else
                              xstrcat(buf, "%s");
                        break;
                  case 'f':   /* "%f" => append double */
                        /* again, we only handle this for efficiency */
                        if (unprocessed == 0) {
                              snprintf(tmp, sizeof(tmp), "%f",
                                     va_arg(ap, double));
                              xstrcat(buf, tmp);
                        } else
                              xstrcat(buf, "%f");
                        break;
                  case 'd':
                        if (unprocessed == 0) {
                              snprintf(tmp, sizeof(tmp), "%d",
                                     va_arg(ap, int));
                              xstrcat(buf, tmp);
                        } else
                              xstrcat(buf, "%d");
                        break;
                  case 'u':
                        if (unprocessed == 0) {
                              snprintf(tmp, sizeof(tmp), "%u",
                                     va_arg(ap, int));
                              xstrcat(buf, tmp);
                        } else
                              xstrcat(buf, "%u");
                        break;
                  case 'l':
                        if ((unprocessed == 0) && (*(p+1) == 'u')) {
                              snprintf(tmp, sizeof(tmp), "%lu",
                                    va_arg(ap, long unsigned));
                              xstrcat(buf, tmp);
                              p++;
                        } else if ((unprocessed==0) && (*(p+1)=='d')) {
                              snprintf(tmp, sizeof(tmp), "%ld",
                                    va_arg(ap, long int));
                              xstrcat(buf, tmp);
                              p++;
                        } else if ((unprocessed==0) && (*(p+1)=='x')) {
                              snprintf(tmp, sizeof(tmp), "%lx",
                                    va_arg(ap, long int));
                              xstrcat(buf, tmp);
                              p++;
                        } else
                              xstrcat(buf, "%l");
                        break; 
                  default:    /* try to handle the rest  */
                        xstrcatchar(buf, '%');
                        xstrcatchar(buf, *p);
                        unprocessed++;
                        break;
                  }

            }

            fmt = p + 1;
      }

      if (unprocessed > 0) {
            vsnprintf(tmp, sizeof(tmp)-1, buf, ap);
            xfree(buf);
            return xstrdup(tmp);
      }

      return buf;
}

/*
 * concatenate result of xstrfmt() to dst, expanding dst if necessary
 */
static void xlogfmtcat(char **dst, const char *fmt, ...)
{
      va_list ap;
      char *buf = NULL;

      va_start(ap, fmt);
      buf = vxstrfmt(fmt, ap);
      va_end(ap);

      xstrcat(*dst, buf);

      xfree(buf);

}

static void
_log_printf(cbuf_t cb, FILE *stream, const char *fmt, ...) 
{
      va_list ap;

      xassert(fileno(stream) >= 0);

      va_start(ap, fmt);
      if (log->opt.buffered && (cb != NULL)) {
            char *buf = vxstrfmt(fmt, ap);
            int   len = strlen(buf);
            int   dropped;
            cbuf_write(cb, buf, len, &dropped); 
            cbuf_read_to_fd(cb, fileno(stream), -1);
            xfree(buf);
      } else  {
            vfprintf(stream, fmt, ap);
      }
      va_end(ap);

}

/*
 * log a message at the specified level to facilities that have been 
 * configured to receive messages at that level
 */
static void log_msg(log_level_t level, const char *fmt, va_list args)
{
      char *pfx = "";
      char *buf = NULL;
      char *msgbuf = NULL;
      int priority = LOG_INFO;

      slurm_mutex_lock(&log_lock);
      if (!LOG_INITIALIZED) {
            log_options_t opts = LOG_OPTS_STDERR_ONLY;
            _log_init(NULL, opts, 0, NULL);
      }

      if (level > log->opt.syslog_level  && 
          level > log->opt.logfile_level && 
          level > log->opt.stderr_level) {
            slurm_mutex_unlock(&log_lock);
            return;
      }

      if (log->opt.prefix_level || log->opt.syslog_level > level) {
            switch (level) {
            case LOG_LEVEL_FATAL:
                  priority = LOG_CRIT;
                  pfx = "fatal: ";
                  break;

            case LOG_LEVEL_ERROR:
                  priority = LOG_ERR;
                  pfx = "error: ";
                  break;

            case LOG_LEVEL_INFO:
            case LOG_LEVEL_VERBOSE:
                  priority = LOG_INFO;
                  break;

            case LOG_LEVEL_DEBUG:
                  priority = LOG_DEBUG;
                  pfx = "debug:  ";
                  break;

            case LOG_LEVEL_DEBUG2:
                  priority = LOG_DEBUG;
                  pfx = "debug2: ";
                  break;

            case LOG_LEVEL_DEBUG3:
                  priority = LOG_DEBUG;
                  pfx = "debug3: ";
                  break;

            case LOG_LEVEL_DEBUG4:
                  priority = LOG_DEBUG;
                  pfx = "debug4: ";
                  break;

            case LOG_LEVEL_DEBUG5:
                  priority = LOG_DEBUG;
                  pfx = "debug5: ";
                  break;

            default:
                  priority = LOG_ERR;
                  pfx = "internal error: ";
                  break;
            }

      }

      /* format the basic message */
      buf = vxstrfmt(fmt, args);

      if (level <= log->opt.stderr_level) {
            fflush(stdout);
            if (strlen(buf) > 0 && buf[strlen(buf) - 1] == '\n')
                  _log_printf( log->buf, stderr, "%s: %s%s", 
                             log->argv0, pfx, buf);
            else
                  _log_printf( log->buf, stderr, "%s: %s%s\n", 
                             log->argv0, pfx, buf);
            fflush(stderr);
      }

      if (level <= log->opt.logfile_level && log->logfp != NULL) {
            xlogfmtcat(&msgbuf, "[%M] %s%s%s", 
                            log->fpfx, pfx, buf);

            if (strlen(buf) > 0 && buf[strlen(buf) - 1] == '\n')
                  _log_printf(log->fbuf, log->logfp, "%s", msgbuf);
            else
                  _log_printf(log->fbuf, log->logfp, "%s\n", msgbuf);
            fflush(log->logfp);

            xfree(msgbuf);
      }

      if (level <=  log->opt.syslog_level) {
            xlogfmtcat(&msgbuf, "%s%s", pfx, buf);

            openlog(log->argv0, LOG_PID, log->facility);
            syslog(priority, "%.500s", msgbuf);
            closelog();

            xfree(msgbuf);
      }

      slurm_mutex_unlock(&log_lock);

      xfree(buf);
}

bool
log_has_data()
{
      bool rc = false;
      slurm_mutex_lock(&log_lock);
      if (log->opt.buffered)
            rc = (cbuf_used(log->buf) > 0);
      slurm_mutex_unlock(&log_lock);
      return rc;
}

static void
_log_flush()
{
      if (!log->opt.buffered)
            return;

      if (log->opt.stderr_level) 
            cbuf_read_to_fd(log->buf, fileno(stderr), -1);
      else if (log->logfp && (fileno(log->logfp) > 0))
            cbuf_read_to_fd(log->fbuf, fileno(log->logfp), -1);
}

void
log_flush()
{
      slurm_mutex_lock(&log_lock);
      _log_flush();
      slurm_mutex_unlock(&log_lock);
}

/* LLNL Software development Toolbox (LSD-Tools)
 * fatal() and nomem() functions
 */
void
lsd_fatal_error(char *file, int line, char *msg)
{
      error("%s:%d %s: %m", file, line, msg);
}

void *
lsd_nomem_error(char *file, int line, char *msg)
{
      error("%s:%d %s: %m", file, line, msg);
      slurm_seterrno(ENOMEM);
      return NULL;
}

/*
 * attempt to log message and abort()
 */
void fatal(const char *fmt, ...)
{
      va_list ap;

      va_start(ap, fmt);
      log_msg(LOG_LEVEL_FATAL, fmt, ap);
      va_end(ap);
      log_flush();
      fatal_cleanup();

      exit(1);
}

int error(const char *fmt, ...)
{
      va_list ap;

      va_start(ap, fmt);
      log_msg(LOG_LEVEL_ERROR, fmt, ap);
      va_end(ap);

      /*
       *  Return SLURM_ERROR so calling functions can
       *    do "return error (...);"
       */
      return SLURM_ERROR;
}

void info(const char *fmt, ...)
{
      va_list ap;

      va_start(ap, fmt);
      log_msg(LOG_LEVEL_INFO, fmt, ap);
      va_end(ap);
}

void verbose(const char *fmt, ...)
{
      va_list ap;

      va_start(ap, fmt);
      log_msg(LOG_LEVEL_VERBOSE, fmt, ap);
      va_end(ap);
}

void debug(const char *fmt, ...)
{
      va_list ap;

      va_start(ap, fmt);
      log_msg(LOG_LEVEL_DEBUG, fmt, ap);
      va_end(ap);
}

void debug2(const char *fmt, ...)
{
      va_list ap;

      va_start(ap, fmt);
      log_msg(LOG_LEVEL_DEBUG2, fmt, ap);
      va_end(ap);
}

void debug3(const char *fmt, ...)
{
      va_list ap;

      va_start(ap, fmt);
      log_msg(LOG_LEVEL_DEBUG3, fmt, ap);
      va_end(ap);
}

/*
 * Debug levels higher than debug3 are not written to stderr in the
 * slurmstepd process after stderr is connected back to the client (srun).
 */
void debug4(const char *fmt, ...)
{
      va_list ap;

      va_start(ap, fmt);
      log_msg(LOG_LEVEL_DEBUG4, fmt, ap);
      va_end(ap);
}

void debug5(const char *fmt, ...)
{
      va_list ap;

      va_start(ap, fmt);
      log_msg(LOG_LEVEL_DEBUG5, fmt, ap);
      va_end(ap);
}

/* Fatal cleanup */

struct fatal_cleanup {
      pthread_t thread_id;
      struct fatal_cleanup *next;
      void (*proc) (void *);
      void *context;
};

/* static variables */
#ifdef WITH_PTHREADS
  static pthread_mutex_t  fatal_lock = PTHREAD_MUTEX_INITIALIZER;    
#else
  static int      fatal_lock;
#endif /* WITH_PTHREADS */
static struct fatal_cleanup *fatal_cleanups = NULL;

/* Registers a cleanup function to be called by fatal() for this thread 
** before exiting. */
void
fatal_add_cleanup(void (*proc) (void *), void *context)
{
      struct fatal_cleanup *cu;

      slurm_mutex_lock(&fatal_lock);
      cu = xmalloc(sizeof(*cu));
      cu->thread_id = pthread_self();
      cu->proc = proc;
      cu->context = context;
      cu->next = fatal_cleanups;
      fatal_cleanups = cu;
      slurm_mutex_unlock(&fatal_lock);
}

/* Registers a cleanup function to be called by fatal() for all threads 
** of the job. */
void
fatal_add_cleanup_job(void (*proc) (void *), void *context)
{
      struct fatal_cleanup *cu;

      slurm_mutex_lock(&fatal_lock);
      cu = xmalloc(sizeof(*cu));
      cu->thread_id = 0;
      cu->proc = proc;
      cu->context = context;
      cu->next = fatal_cleanups;
      fatal_cleanups = cu;
      slurm_mutex_unlock(&fatal_lock);
}

/* Removes a cleanup frunction to be called at fatal() for this thread. */
void
fatal_remove_cleanup(void (*proc) (void *context), void *context)
{
      struct fatal_cleanup **cup, *cu;
      pthread_t my_thread_id = pthread_self();

      slurm_mutex_lock(&fatal_lock);
      for (cup = &fatal_cleanups; *cup; cup = &cu->next) {
            cu = *cup;
            if (cu->thread_id == my_thread_id &&
                cu->proc == proc && 
                cu->context == context) {
                  *cup = cu->next;
                  xfree(cu);
                  slurm_mutex_unlock(&fatal_lock);
                  return;
            }
      }
      slurm_mutex_unlock(&fatal_lock);
      fatal("fatal_remove_cleanup: no such cleanup function: 0x%lx 0x%lx",
          (u_long) proc, (u_long) context);
}

/* Removes a cleanup frunction to be called at fatal() for all threads of 
** the job. */
void
fatal_remove_cleanup_job(void (*proc) (void *context), void *context)
{
      struct fatal_cleanup **cup, *cu;

      slurm_mutex_lock(&fatal_lock);
      for (cup = &fatal_cleanups; *cup; cup = &cu->next) {
            cu = *cup;
            if (cu->thread_id == 0 &&
                cu->proc == proc && 
                cu->context == context) {
                  *cup = cu->next;
                  xfree(cu);
                  slurm_mutex_unlock(&fatal_lock);
                  return;
            }
      }
      slurm_mutex_unlock(&fatal_lock);
      fatal("fatal_remove_cleanup_job: no such cleanup function: "
            "0x%lx 0x%lx", (u_long) proc, (u_long) context);
}

/* Execute cleanup functions, first thread-specific then those for the 
** whole job */
void
fatal_cleanup(void)
{
      struct fatal_cleanup **cup, *cu;
      pthread_t my_thread_id = pthread_self();

      slurm_mutex_lock(&fatal_lock);
      for (cup = &fatal_cleanups; *cup; ) {
            cu = *cup;
            if (cu->thread_id != my_thread_id) {
                  cup = &cu->next;
                  continue;
            }
            debug("Calling cleanup 0x%x(0x%x)",
                  (u_long) cu->proc, (u_long) cu->context);
            (*cu->proc) (cu->context);
            *cup = cu->next;
            xfree(cu);
      }
      for (cup = &fatal_cleanups; *cup; cup = &cu->next) {
            cu = *cup;
            if (cu->thread_id != 0)
                  continue;
            debug("Calling cleanup 0x%x(0x%x)",
                  (u_long) cu->proc, (u_long) cu->context);
            (*cu->proc) (cu->context);
      }
      slurm_mutex_unlock(&fatal_lock);
}

/* Print a list of cleanup frunctions to be called at fatal(). */
void
dump_cleanup_list(void)
{
      struct fatal_cleanup **cup, *cu;

      slurm_mutex_lock(&fatal_lock);
      for (cup = &fatal_cleanups; *cup; cup = &cu->next) {
            cu = *cup;
            info ("loc=%ld thread_id=%ld proc=%ld, context=%ld, next=%ld",
                  (long)cu, (long)cu->thread_id, (long)cu->proc, 
                  (long)cu->context, (long)cu->next);
      }
      slurm_mutex_unlock(&fatal_lock);
}


Generated by  Doxygen 1.6.0   Back to index