Subject: Re: signal stuff...
To: Christos Zoulas <christos@zoulas.com>
From: Ignatios Souvatzis <ignatios@cs.uni-bonn.de>
List: tech-kern
Date: 01/08/1999 12:35:00
--pAwQNkOnpTn9IO2O
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename=mutta00834

On Fri, Jan 08, 1999 at 11:10:40AM +0000, Christos Zoulas wrote:
> In article <19990108115308.D834@cs.uni-bonn.de> ignatios@cs.uni-bonn.de (Ignatios Souvatzis) writes:
> 
> >  act.sa_handler = (void (*)(int)) &chkpt_restore;
> >  act.sa_flags |= SA_ONSTACK;
> 
> add:
>    sigemptyset(&act.sa_mask);

Uhm, I forgot the comment:

/* We use the same mask as above */

also flags are already initialized (to SA_RESTART, if I looked right).
Maybe I should have cited more... but you better look at the complete
original.

	-is

--pAwQNkOnpTn9IO2O
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="chkpt.c"

/*
 * Copyright (c) 1998 Alexandre Wennmacher (wennmach@geo.Uni-Koeln.DE)
 * 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.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 * This product includes software developed by Alexandre Wennmacher.
 * 4. The name of Alexandre Wennmacher may not be used to endorse or promote
 *    products derived from this software without specific prior written
 *    permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY ALEXANDRE WENNMACHER AND CONTRIBUTORS ``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 ALEXANDRE WENNMACHER OR CONTRIBUTORS 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.
 *
 * The Chkpt Library is not an official product of the University of Cologne
 * (Universitaet zu Koeln).
 */

#include <machine/vmparam.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/syscall.h>
#include <sys/uio.h>
#include <stddef.h>
#include <stdlib.h>
#include <fcntl.h> 
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <setjmp.h>
#include <time.h>

#include "machine.h"
#include "fpu.h"
#include "chkpt_lib.h"
#include "chkpt.h"

#if defined(OS_DIGITAL_UNIX)
#include <alloca.h>
#endif

#if defined(HAVE_MD5)
#include <md5.h>
#endif

#if defined(OS_NETBSD)
extern caddr_t etext;
#endif

#if defined(OS_DIGITAL_UNIX)
extern unsigned long _ftext;
extern unsigned long _etext;
extern unsigned long _fdata;
#endif

static char chkpt_file[MAXPATHLEN];
static char chkpt_file_tmp[MAXPATHLEN];
static char chkpt_file_lock[MAXPATHLEN];
static jmp_buf procenv;
static int handler_installed = 0;
static struct header chkpt_hdr;
static size_t s_header = sizeof(chkpt_hdr);
static struct iovec iov[2];
static int sglen;
static int fd;
static int f_lock;

#if defined(SAVE_FPU)
static fpu_regs fpu;
#endif

static void
#if defined(OS_NETBSD)
chkpt_save(sig, code, scp)
int sig;
int code;
struct sigcontext *scp;
#endif
#if defined(OS_DIGITAL_UNIX)
chkpt_save(sig)
int sig;
#endif
{
  struct sigaction act;
  int f_chkpt;
  void *data_top;

#if defined(EBUG)
  switch(sig) {
    case SIGALRM:
      debug("chkpt_save(): info: Received SIGALRM.\n");
      break;
    case SIGVTALRM:
      debug("chkpt_save(): info: Received SIGVTALRM.\n");
      break;
  }
#endif

/* Save the process environment.
 * If setjmp(procenv) != 0 then we returned just from a restarted program ... */
  if (setjmp(procenv) != 0) {
#if defined(EBUG)
    debug("chkpt_save(): info: Longjmp succeeded.\n");
#endif

/* We clean up. First, We deinstall the restore signal handler (SIGUSR1). */
    act.sa_handler = SIG_DFL;
    act.sa_flags = 0; 
    sigemptyset(&act.sa_mask);

    if (sigaction(SIGUSR1, &act, (struct sigaction *) NULL) == 0) {
#if defined(EBUG)
      debug("chkpt_save(): info: Handler for SIGUSR1 deinstalled.\n");
#endif
    } else {
#if defined(IAGNOSTIC)
      debug("chkpt_save(): warning: Deinstallation of handler SIGUSR1 failed.\n");
#endif
    }

/* Reopen files */
    if (files_restore() != E_CHKPT_SUCCESS) {
      debug("chkpt_save(): error: files_restore() failed.\n");
      abort();
    }

/* Restore signals */
    if (signals_restore() != E_CHKPT_SUCCESS) {
      debug("chkpt_save(): error: signals_restore() failed.\n");
      abort();
    }

/* Restore itimers */
    if (itimers_restore() != E_CHKPT_SUCCESS) {
      debug("chkpt_save(): error: itimers_restore() failed.\n");
      abort();
    }

/* Restore the fpu */
#if defined(SAVE_FPU)
#if defined(EBUG)
    debug("chkpt_save(): info: Restoring FPU state.\n");
#endif
    fpu_restore(&fpu);
#endif

/* Finally, we free the additionally sbrk()'d memory.
 * What comes below is really wierd and may give raise to lots of
 * compiler or lint complaints. We use the variable `data_top' before
 * it is initialized. These are the wonders of checkpointing! */
  if (brk(data_top) != 0)
#if defined(IAGNOSTIC)
    debug("chkpt_save(): warning: Could not brk memory at %p\n", data_top);
/*  If the brk fails, do nothing. We can live with it. */
#endif

#if defined(EBUG)
    debug("chkpt_save(): info: Checkpointed state restored; continuing...\n");
#endif
    return;
  }

/* First, on OSes where neither the kernel saves the FPU when building the
 * new context for the signal handler, nor setjmp() saving the FPU registers,
 * we have to do that ourselves. */
#if defined(SAVE_FPU)
#if defined(EBUG)
  debug("chkpt_save(): info: Saving FPU state.\n");
#endif
  fpu_save(&fpu);
#endif

/* Error checking: if one of the save calls fails, return from the
 * chkpt_save handler. We don't like to write corrupt checkpoint files */

    if (itimers_save() != E_CHKPT_SUCCESS) {
#if defined(IAGNOSTIC)
      debug("chkpt_save(): error: itimers_save() failed.\n");
#endif
      return;
    }

    if (signals_save() != E_CHKPT_SUCCESS) {
#if defined(IAGNOSTIC)
      debug("chkpt_save(): error: signals_save() failed.\n");
#endif
      return;
    }

    if (files_save() != E_CHKPT_SUCCESS) {
#if defined(IAGNOSTIC)
      debug("chkpt_save(): error: files_save() failed.\n");
#endif
      return;
    }

/* The `ptr' of struct segment points to the first valid address of the text
 * or the data segment. In the case of the stack segment, it points to the
 * first address after the end. The `top'-variables are always the first
 * addresses after the end of a segment (see etext(3), sbrk(2)). Thus,
 * `top - base' is always the size (we don't need to add 1). 
 * Is this also true for USRSTACK ???
 */

  data_top = (void *) sbrk(0);
  chkpt_hdr.stack.ptr = (void *) alloca(0);

#if defined(OS_DIGITAL_UNIX) && !defined(__gcc2__)
/* We have to do the casts to ptrdiff_t to make the
 * Digital Unix C compiler happy */
  chkpt_hdr.data.len = (ptrdiff_t) data_top - (ptrdiff_t) chkpt_hdr.data.ptr;
  chkpt_hdr.stack.len = USRSTACK - (ptrdiff_t) chkpt_hdr.stack.ptr;
#else
  chkpt_hdr.data.len = data_top - chkpt_hdr.data.ptr;
  chkpt_hdr.stack.len = (void *) USRSTACK - chkpt_hdr.stack.ptr;
#endif

#if defined(EBUG)
  debug("chkpt_save(): info: text.ptr = %p\n", chkpt_hdr.text.ptr);
  debug("chkpt_save(): info: text.len = %d\n", chkpt_hdr.text.len);
  debug("chkpt_save(): info: data.ptr = %p\n", chkpt_hdr.data.ptr);
  debug("chkpt_save(): info: data.len = %d\n", chkpt_hdr.data.len);
  debug("chkpt_save(): info: stack.ptr = %p\n", chkpt_hdr.stack.ptr);
  debug("chkpt_save(): info: stack.len = %d\n", chkpt_hdr.stack.len);
#endif

  iov[0].iov_base = chkpt_hdr.data.ptr;
  iov[0].iov_len  = chkpt_hdr.data.len;
  iov[1].iov_base = chkpt_hdr.stack.ptr;
  iov[1].iov_len  = chkpt_hdr.stack.len;
  sglen = iov[0].iov_len + iov[1].iov_len;

  f_chkpt = syscall(SYS_open, chkpt_file_tmp, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
  if (f_chkpt == -1) {
#if defined(IAGNOSTIC)
    debug("chkpt_save(): error: Could not open %s.\n", chkpt_file_tmp);
#endif
    return;
  } 

  if (write(f_chkpt, &chkpt_hdr, s_header) != s_header) {
#if defined(IAGNOSTIC)
    debug("chkpt_save(): error: Could not write struct chkpt_hdr to %s.\n", chkpt_file_tmp);
#endif
    (void) syscall(SYS_close, f_chkpt);
    return;
  }

  if (writev(f_chkpt, &iov[0], 2) != sglen) {
#if defined(IAGNOSTIC)
    debug("chkpt_save(): error: Could not write data/stack to %s.\n", chkpt_file_tmp);
#endif
    (void) syscall(SYS_close, f_chkpt);
    return;
  }

#if defined(EBUG)
  debug("chkpt_save(): info: Wrote %d bytes of data/stack to %s.\n", sglen, chkpt_file_tmp);
#endif

  (void) syscall(SYS_close, f_chkpt);
  (void) rename(chkpt_file_tmp, chkpt_file);
  return;
}


static void
#if defined(OS_NETBSD)
chkpt_restore(sig, code, scp)
int sig;
int code;
struct sigcontext *scp;
#endif /* OS_NETBSD */
#if defined(OS_DIGITAL_UNIX)
chkpt_restore(sig)
int sig;
#endif /* OS_DIGITAL_UNIX */
{
  int read_len;
  int fd_on_stack;

#if defined(EBUG)
  debug("chkpt_restore(): info: received SIGUSR1.\n");
#endif

/* Read in the data and stack using scatter/gather IO. The pointer to our save
 * area will be overwritten. We make a local copy (on the stack) and restore
 * save immediately after the read.
 */
  fd_on_stack = fd;
  read_len = readv(fd, &iov[0], 2);
  if (read_len != sglen) {
#if defined(IAGNOSTIC)
    debug("chkpt_restore(): error: Could not read data/stack from %s.\n", chkpt_file);
    if (read_len == -1) 
      debug("errno = %d\n", errno);
    else
      debug("chkpt_restore(): error: ... only got %d bytes.\n", read_len);
#endif
    abort();
  }

#if defined(EBUG)
  debug("chkpt_restore(): info: Read %d bytes of data/stack from %s.\n", read_len, chkpt_file);
#endif

  (void) syscall(SYS_close, fd_on_stack);
  longjmp(procenv, 1);

/* Not reached */
  abort();
}


void
chkpt()
{
  (void) kill(getpid(), SIGALRM);
  return;
}

/* Fortran-C-Interface */
void
#if defined(OS_DIGITAL_UNIX)
chkpt_(void)
#endif /* OS_DIGITAL_UNIX */
#if defined(OS_NETBSD)
chkpt_()
#endif /* OS_NETBSD */
{
  chkpt();
}


int
chkpt_init(filename)
char *filename;
{
  int pagesize;
  struct sigaction act;
  struct header lchkpt_hdr;
  int f_chkpt;
  int allocate;
  void *text_top;
  void *data_top;
  void *ldata_top;
  void *stack_base;
  void *lstack_base;
#if defined(OS_NETBSD)
  struct sigaltstack sigstack;
#endif
#if defined(OS_DIGITAL_UNIX)
  stack_t sigstack;
#endif

  (void) strncpy(chkpt_file, filename, MAXPATHLEN);
  (void) strncpy(chkpt_file_tmp, filename, MAXPATHLEN);
  (void) strncat(chkpt_file_tmp, ".tmp", MAXPATHLEN);
  (void) strncpy(chkpt_file_lock, filename, MAXPATHLEN);
  (void) strncat(chkpt_file_lock, ".lock", MAXPATHLEN);

/* We create a lock file to prevent multiple instances of a process
 * to write to the same checkpoint */

#if defined(OS_NETBSD)
  f_lock = syscall(SYS_open, chkpt_file_lock, O_WRONLY | O_CREAT | O_EXLOCK, S_IRUSR | S_IWUSR);
  if (f_lock == -1) {
#if defined(IAGNOSTIC)
    debug("chkpt_init(): error: Lock file %s can not be created.\n", chkpt_file_lock);
#endif
    return E_CHKPT_LOCK;
  }
#endif /* defined(OS_NETBSD) */
#if defined(OS_DIGITAL_UNIX)
  f_lock = syscall(SYS_open, chkpt_file_lock, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
  if (f_lock == -1) {
#if defined(IAGNOSTIC)
    debug("chkpt_init(): error: Lock file %s can not be created.\n", chkpt_file_lock);
#endif
    return E_CHKPT_LOCK;
  }
  if (syscall(SYS_flock, f_lock, LOCK_EX) != 0) {
#if defined(IAGNOSTIC)
    debug("chkpt_init(): error: Could not obtain lock on lock file %s.\n", chkpt_file_lock);
#endif
    return E_CHKPT_LOCK;
  }
#endif /* defined(OS_DIGITAL_UNIX) */


/* We fill in as much as possible in chkpt_hdr */
  (void) strncpy(chkpt_hdr.version, VERSION, VERSIONLEN);

  pagesize = getpagesize();
#if defined(EBUG)
    debug("chkpt_init(): info: Pagesize = %d\n", pagesize);
#endif

#if defined(OS_NETBSD)
  chkpt_hdr.text.ptr = (void *) USRTEXT;
  chkpt_hdr.data.ptr = (void *) roundup((unsigned long) &etext, pagesize);
  text_top = (void *) &etext;
#endif /* OS_NETBSD  */

#if defined(OS_DIGITAL_UNIX)
  chkpt_hdr.text.ptr = (void *) &_ftext;
  chkpt_hdr.data.ptr = (void *) &_fdata;
  text_top = (void *) &_etext;
#endif /* OS_DIGITAL_UNIX */

#if defined(OS_DIGITAL_UNIX) && !defined(__gcc2__)
  chkpt_hdr.text.len = (ptrdiff_t) text_top - (ptrdiff_t) chkpt_hdr.text.ptr;
#else
  chkpt_hdr.text.len = text_top - chkpt_hdr.text.ptr;
#endif

#if defined(HAVE_MD5)
  (void) MD5Data((unsigned char *) chkpt_hdr.text.ptr, chkpt_hdr.text.len, chkpt_hdr.md5);
#if defined(EBUG)
  debug("chkpt_init(): info: MD5 = %s\n", chkpt_hdr.md5);
#endif
#endif

  f_chkpt = syscall(SYS_open, chkpt_file, O_RDONLY, 0);
  if (f_chkpt == -1) {
/* Checkpoint file does not exist. We already test here if we can open
 * chkpt_file for write. If this fails, we signal this to the user. */
    f_chkpt = syscall(SYS_open, chkpt_file, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
    if (f_chkpt == -1) {
#if defined(IAGNOSTIC)
      debug("chkpt_init(): error: Checkpoint file %s can not be created.\n", chkpt_file);
#endif
      return E_CHKPT_CREAT;
    } else {
      (void) syscall(SYS_close, f_chkpt);
      (void) unlink(chkpt_file);

/* Install signal Handlers */

      act.sa_handler = (void (*)(int)) &chkpt_save;
      sigemptyset(&act.sa_mask);
      sigaddset(&act.sa_mask, SIGALRM);
      sigaddset(&act.sa_mask, SIGVTALRM);
      sigaddset(&act.sa_mask, SIGUSR1);
      act.sa_flags = SA_RESTART; 

      if (sigaction(SIGALRM, &act, (struct sigaction *) NULL) == 0) {
#if defined(EBUG)
        debug("chkpt_init(): info: Handler for SIGALRM installed.\n");
#endif
      } else {
#if defined(IAGNOSTIC)
        debug("chkpt_init(): error: Installation of handler for SIGALRM failed.\n");
#endif
        return E_CHKPT_SYS;
      }

      if (sigaction(SIGVTALRM, &act, (struct sigaction *) NULL) == 0) {
#if defined(EBUG)
        debug("chkpt_init(): info: Handler for SIGVTALRM installed.\n");
#endif
      } else {
#if defined(IAGNOSTIC)
        debug("chkpt_init(): error: Installation of handler for SIGVTALRM failed.\n");
#endif
        return E_CHKPT_SYS;
      }

      handler_installed = 1;

      return E_CHKPT_SUCCESS;
    }
  } 

#if defined(EBUG)
    debug("chkpt_init(): info: Checkpoint file detected; preparing for restart.\n");
#endif

/*
 * O.k., so we have detected a checkpoint file and want to restart the
 * process. We can't simply call chkpt_restore() because this routine then
 * will overwrite its own stack. Instead, we install chkpt_restore() as a
 * signal handler and specify a specific (signal) stack for this handler.
 * This way chkpt_restore() can safely restore the saved stack.
 * 
 * However, before we do so, we perform some simple checks to verify that
 * the detected checkpoint file corresponds to this process.
 */

  if (read(f_chkpt, &lchkpt_hdr, s_header) != s_header) {
#if defined(IAGNOSTIC)
    debug("chkpt_init(): error: Could not read struct lchkpt_hdr from %s.\n", chkpt_file);
#endif
    (void) syscall(SYS_close, f_chkpt);
    return E_CHKPT_READ;
  }

  if (strncmp(lchkpt_hdr.version, chkpt_hdr.version, VERSIONLEN) != 0) {
#if defined(IAGNOSTIC)
    debug("chkpt_init(): error: Checkpoint library version mismatch.\n");
#endif
    return E_CHKPT_INCOMPAT;
  }

  if (lchkpt_hdr.text.ptr != chkpt_hdr.text.ptr) {
#if defined(IAGNOSTIC)
    debug("chkpt_init(): error: Start of text segment in checkpoint file does not match with that of the program.\n");
#endif
    return E_CHKPT_INCOMPAT;
  }

  if (lchkpt_hdr.text.len != chkpt_hdr.text.len) {
#if defined(IAGNOSTIC)
    debug("chkpt_init(): error: Length of text segment in checkpoint file does not match with that of the program.\n");
#endif
    return E_CHKPT_INCOMPAT;
  }

#if defined(HAVE_MD5)
  if (strncmp(lchkpt_hdr.md5, chkpt_hdr.md5, MD5LEN) != 0) {
#if defined(IAGNOSTIC)
    debug("chkpt_init(): error: MD5 checksum of checkpoint file does not match with that of the program.\n");
#endif
    return E_CHKPT_INCOMPAT;
  }
#endif

  if (lchkpt_hdr.data.ptr != chkpt_hdr.data.ptr) {
#if defined(IAGNOSTIC)
    debug("chkpt_init(): error: Start of data segment in checkpoint file does not match with that of the program.\n");
#endif
    return E_CHKPT_INCOMPAT;
  }

#if defined(EBUG)
  debug("chkpt_init(): info: Checkpoint file successfully verified.\n");
  debug("chkpt_init(): info: text.ptr = %p\n", lchkpt_hdr.text.ptr);
  debug("chkpt_init(): info: text.len = %d\n", lchkpt_hdr.text.len);
  debug("chkpt_init(): info: data.ptr = %p\n", lchkpt_hdr.data.ptr);
  debug("chkpt_init(): info: data.len = %d\n", lchkpt_hdr.data.len);
  debug("chkpt_init(): info: stack.ptr = %p\n", lchkpt_hdr.stack.ptr);
  debug("chkpt_init(): info: stack.len = %d\n", lchkpt_hdr.stack.len);
#endif

  data_top = (void *) sbrk(0);
#if defined(EBUG)
  debug("chkpt_init(): info: Current data_top = %p\n", data_top);
#endif

#if defined(OS_DIGITAL_UNIX) && !defined(__gcc2__)
  ldata_top = (void *) ((ptrdiff_t) lchkpt_hdr.data.ptr + lchkpt_hdr.data.len);
#else
  ldata_top = lchkpt_hdr.data.ptr + lchkpt_hdr.data.len;
#endif
  if (ldata_top > data_top) {
    if (brk(ldata_top) != 0) {
#if defined(IAGNOSTIC)
      debug("chkpt_init(): error: Could not brk memory at %p\n", ldata_top);
#endif
      (void) syscall(SYS_close, f_chkpt);
      return E_CHKPT_NOMEM;
    }
#if defined(EBUG)
    debug("chkpt_init(): info: Successfully set new brk at %p\n", ldata_top);
#endif
  }

/* Now that we have set the program brk, we can sbrk() space for the signal
 * stack (here) and for the save structure (further down). */
  sigstack.ss_sp = sbrk(SIGSTACKSIZE);
  if (sigstack.ss_sp == (char *) -1) {
#if defined(IAGNOSTIC)
    debug("chkpt_init(): error: Couldn't sbrk() for sigaltstack().\n");
#endif
    return E_CHKPT_NOMEM;
  }

  sigstack.ss_size = SIGSTACKSIZE;
  sigstack.ss_flags = 0;
  if (sigaltstack(&sigstack, 0) < 0) {
#if defined(IAGNOSTIC)
    debug("chkpt_init(): error: Sigaltstack() failed.\n");
#endif
    return E_CHKPT_SYS;
  }

#if defined(OS_NETBSD)
  act.sa_handler = (void (*)(int)) &chkpt_restore;
#endif
#if defined(OS_DIGITAL_UNIX)
  act.sa_handler = &chkpt_restore;
#endif

  act.sa_flags |= SA_ONSTACK; 
/* We use the same mask as above */

  if (sigaction(SIGUSR1, &act, (struct sigaction *) NULL) == 0) {
#if defined(EBUG)
    debug("chkpt_init(): info: Handler for SIGUSR1 installed.\n");
#endif
 } else {
#if defined(IAGNOSTIC)
    debug("chkpt_init(): error: Installation of handler SIGUSR1 failed.\n");
#endif
    return E_CHKPT_SYS;
  }

/* prepare iov[] for readv() : */
  iov[0].iov_base = lchkpt_hdr.data.ptr;
  iov[0].iov_len  = lchkpt_hdr.data.len;
  iov[1].iov_base = lchkpt_hdr.stack.ptr;
  iov[1].iov_len  = lchkpt_hdr.stack.len;
  fd = f_chkpt;

/* Grow the stack to the old size */
  stack_base = alloca(0);
#if defined(EBUG)
  debug("chkpt_init(): info: Current stack_base = %p\n", stack_base);
#endif

#if defined(OS_DIGITAL_UNIX) && !defined(__gcc2__)
  lstack_base = (void *) ((ptrdiff_t) lchkpt_hdr.stack.ptr - lchkpt_hdr.stack.len);
  allocate = (ptrdiff_t) stack_base - (ptrdiff_t) lstack_base;
#else
  lstack_base = lchkpt_hdr.stack.ptr - lchkpt_hdr.stack.len;
  allocate = stack_base - lstack_base;
#endif
  if (allocate > 0) {
#if defined(EBUG)
    debug("chkpt_init(): info: Allocating %d bytes of stack frame.\n", allocate);
#endif
    if (alloca(allocate) == NULL) {
#if defined(IAGNOSTIC)
      debug("chkpt_init(): error: Could not alloca() %d bytes of stack frame.\n", allocate);
#endif
      (void) syscall(SYS_close, f_chkpt);
      return E_CHKPT_NOMEM;
    }
  }

/* Restore data segment and stack by calling the signal handler chkpt_restore
 * on the signal stack */
  (void) kill(getpid(), SIGUSR1);

/* We don't return from the kill(). 
 * Not reached, guard against fatal (compiler? OS?) errors */
  abort(); 

}

/* Fortran-C-Interface */
void
#if defined(OS_DIGITAL_UNIX)
chkpt_init_(fcharptr, fstatus, flength)
#endif
#if defined(OS_NETBSD)
chkpt_init__(fcharptr, fstatus, flength)
#endif
/* Fortran programs do: call chkpt_init(filename, status) */
char *fcharptr;
int *fstatus;
int flength;
{
  char *buf;
  int ii;

  buf = (char *) malloc(flength);
  if (buf == NULL) {
#if defined(DIAGNOSTIC)
    debug("chkpt_init_(): Error: Could not malloc() for buf.\n");
#endif
    *fstatus = E_CHKPT_NOMEM;
    return;
  }

  (void) strncpy(buf, fcharptr, flength);

/* Make shure, that the filename in buf is properly terminated */
  for (ii = 0; ii <= flength; ii++) {
    if (*(buf + ii) == ' ') {
      *(buf + ii) = '\0';
      break;
    }
    if (ii == flength) *(buf + ii) = '\0';
  }
  *fstatus = chkpt_init(buf);
/* do NOT free buf here! */
}


int
chkpt_timer(which, seconds)
int which;
int seconds;
{
  struct itimerval itimer;
  int iwhich;

  if (which != TIMER_REAL && which != TIMER_VIRTUAL) {
#if defined(IAGNOSTIC)
    debug("chkpt_timer(): error: No such timer %d\n", which);
#endif
    return E_CHKPT_INVAL;
  }

  if (handler_installed == 0) {
#if defined(IAGNOSTIC)
    debug("chkpt_timer(): error: Signal handlers not installed, call chkpt_init() first!\n");
#endif
    return E_CHKPT_NOHANDLER;
  }

  if (seconds <= 0) {
#if defined(IAGNOSTIC)
    debug("chkpt_timer(): error: Invalid value for seconds (must be > 0).\n");
#endif
    return E_CHKPT_INVAL;
  }

  itimer.it_interval.tv_sec = itimer.it_value.tv_sec = seconds;
  itimer.it_interval.tv_usec = itimer.it_value.tv_usec = 0;

/* Map our timers to the OS timers */
  iwhich = -1;
  if (which == TIMER_REAL) iwhich = ITIMER_REAL;
    else if (which == TIMER_VIRTUAL) iwhich = ITIMER_VIRTUAL;

  if (setitimer(iwhich, &itimer, (struct itimerval *) NULL) == 0) {
#if defined(EBUG)
    debug("chkpt_timer(): info: Timer %d set to %d s.\n", which, seconds);
#endif
    return E_CHKPT_SUCCESS;
  } else {
#if defined(IAGNOSTIC)
    debug("chkpt_timer(): error: Failed to set timer %d\n", which);
#endif
    return E_CHKPT_SYS;
  }
/* not reached */
}

/* Fortran-C-Interface */
void
#if defined(OS_DIGITAL_UNIX)
chkpt_timer_(fwhich, fseconds, fstatus)
#endif /* OS_DIGITAL_UNIX */
#if defined(OS_NETBSD)
chkpt_timer__(fwhich, fseconds, fstatus)
#endif /* OS_NETBSD */
int *fwhich;
int *fseconds;
int *fstatus;
{
  *fstatus = chkpt_timer(*fwhich, *fseconds);
}


void
chkpt_exit(status)
int status;
{
  struct stat sb;

  if (stat(chkpt_file, &sb) == 0) {
#if defined(EBUG)
    debug("chkpt_exit: info: Checkpoint file detected; unlinking.\n");
#endif
    (void) unlink(chkpt_file);
  }

/* release lock */
#if defined(OS_NETBSD)
  (void) unlink(chkpt_file_lock);
  (void) syscall(SYS_close, f_lock);
#endif /* defined(OS_NETBSD) */
#if defined(OS_DIGITAL_ALPHA)
  (void) syscall(SYS_flock, f_lock, LOCK_UN);
  (void) unlink(chkpt_file_lock);
  (void) syscall(SYS_close, f_lock);
#endif /* defined(OS_DIGITAL_ALPHA) */
  exit(status);
}

/* Fortran-C-Interface */
void
#if defined(OS_DIGITAL_UNIX)
chkpt_exit_(fstatus)
#endif /* OS_DIGITAL_UNIX */
#if defined(OS_NETBSD)
chkpt_exit__(fstatus)
#endif /* OS_NETBSD */
int *fstatus;
{
  chkpt_exit(*fstatus);
}

--pAwQNkOnpTn9IO2O--