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

shutdown.c

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

#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <mntent.h>
#include <netdb.h>
#include <paths.h>
#include <setjmp.h>
#include <signal.h>
#include <string.h>
#include <stdlib.h>
#include <utmp.h>
#include <sys/mman.h>
#include <sys/param.h>
#include <sys/stat.h>

#include "watch_err.h"
#include "extern.h"

#if defined __GLIBC__
#include "ext2_mnt.h"
#include <sys/quota.h>
#include <sys/swap.h>
#include <sys/reboot.h>
#else                   /* __GLIBC__ */
#include <linux/quota.h>
#endif                        /* __GLIBC__ */

#include <unistd.h>

#if USE_SYSLOG
#include <syslog.h>
#endif                        /* USE_SYSLOG */

#ifndef NSIG
#define NSIG _NSIG
#endif

extern void umount_all(void *);
extern int ifdown(void);
#if 0
extern int mount_one(char *, char *, char *, char *, int, int);
static struct mntent rootfs;
#endif

#if defined(_POSIX_MEMLOCK)
extern int mlocked, realtime;
#endif

jmp_buf ret2dog;

/* Info about a process. */
typedef struct _proc_ {
    pid_t pid;                /* Process ID.                    */
    int sid;                  /* Session ID.                    */
    struct _proc_ *next;      /* Pointer to next struct.        */
} PROC;

/* write a log entry on exit */
static void log_end()
{
#if USE_SYSLOG
    /* Log the closinging message */
    syslog(LOG_INFO, "stopping daemon (%d.%d)", MAJOR_VERSION, MINOR_VERSION);
    closelog();

    sleep(5);                 /* make sure log is written */
#endif                        /* USE_SYSLOG */
    return;
}

/* close the device and check for error */
static void close_all()
{
    if (watchdog != -1) {
        if ( write(watchdog, "V", 1) < 0 ) {
            int err = errno;
#if USE_SYSLOG
            syslog(LOG_ERR, "write watchdog device gave error %d = '%m'!", err);
#else                   /* USE_SYSLOG */
            perror(progname);
#endif                        /* USE_SYSLOG */
      }

        if (close(watchdog) == -1) {
#if USE_SYSLOG
            syslog(LOG_ALERT, "cannot close %s (errno = %d)", devname, errno);
#else                   /* USE_SYSLOG */
            perror(progname);
#endif                        /* USE_SYSLOG */
        }
    }
    
    if (load != -1 && close(load) == -1) {
#if USE_SYSLOG
      syslog(LOG_ALERT, "cannot close /proc/loadavg (errno = %d)", errno);
#else                   /* USE_SYSLOG */
      perror(progname);
#endif                        /* USE_SYSLOG */
    }
    
    if (mem != -1 && close(mem) == -1) {
#if USE_SYSLOG
      syslog(LOG_ALERT, "cannot close /proc/meminfo (errno = %d)", errno);
#else                   /* USE_SYSLOG */
      perror(progname);
#endif                        /* USE_SYSLOG */
    }
    
    if (temp != -1 && close(temp) == -1) {
#if USE_SYSLOG
      syslog(LOG_ALERT, "cannot close /dev/temperature (errno = %d)", errno);
#else                   /* USE_SYSLOG */
      perror(progname);
#endif                        /* USE_SYSLOG */
    }

    if (hb != NULL && fclose(hb) == -1) {
#if USE_SYSLOG
      syslog(LOG_ALERT, "cannot close %s (errno = %d)", heartbeat, errno);
#else                   /* USE_SYSLOG */
      perror(progname);
#endif                        /* USE_SYSLOG */
    }
}

/* on exit we close the device and log that we stop */
void terminate(int arg)
{
#if defined(_POSIX_MEMLOCK)
    if (realtime == TRUE && mlocked == TRUE)
    {
      /* unlock all locked pages */
      if (munlockall() != 0) {
#if USE_SYSLOG
            syslog(LOG_ERR, "cannot unlock realtime memory (errno = %d)", errno);
#else                   /* USE_SYSLOG */
            perror(progname);
#endif
      }
    }
#endif
    close_all();
    log_end();
    if (timestamps != NULL)
          free(timestamps);
    exit(0);
}

/* panic: we're still alive but shouldn't */
static void panic(void)
{
    /* if we are still alive, we just exit */
    close_all();
    fprintf(stderr, "WATCHDOG PANIC: Still alive after sleeping %d seconds!\n", 4 * TIMER_MARGIN);
#if USE_SYSLOG
    openlog(progname, LOG_PID, LOG_DAEMON);
    syslog(LOG_ALERT, "still alive after sleeping %d seconds", 4 * TIMER_MARGIN);
    closelog();
#endif
    exit(1);
}

static void mnt_off()
{
    FILE *fp;
    struct mntent *mnt;

    fp = setmntent(MNTTAB, "r");
    while ((mnt = getmntent(fp)) != (struct mntent *) 0) {
      /* First check if swap */
      if (!strcmp(mnt->mnt_type, MNTTYPE_SWAP))
          if (swapoff(mnt->mnt_fsname) < 0)
            perror(mnt->mnt_fsname);

      /* quota only if mounted at boot time && filesytem=ext2 */
      if (hasmntopt(mnt, MNTOPT_NOAUTO) || strcmp(mnt->mnt_type, MNTTYPE_EXT2))
          continue;

      /* group quota? */
      if (hasmntopt(mnt, MNTOPT_GRPQUOTA))
          if (quotactl(QCMD(Q_QUOTAOFF, GRPQUOTA), mnt->mnt_fsname, 0, (caddr_t) 0) < 0)
            perror(mnt->mnt_fsname);

      /* user quota */
      if (hasmntopt(mnt, MNTOPT_USRQUOTA))
          if (quotactl(QCMD(Q_QUOTAOFF, USRQUOTA), mnt->mnt_fsname, 0, (caddr_t) 0) < 0)
            perror(mnt->mnt_fsname);

#if 0
      /* not needed anymore */
      /* while we're at it we add the remount option */
      if (strcmp(mnt->mnt_dir, "/") == 0) {
            /* save entry if root partition */
            rootfs.mnt_freq = mnt->mnt_freq;
            rootfs.mnt_passno = mnt->mnt_passno;

            rootfs.mnt_fsname = strdup(mnt->mnt_fsname);
            rootfs.mnt_dir = strdup(mnt->mnt_dir);
            rootfs.mnt_type = strdup(mnt->mnt_type);

            /* did we get enough memory? */
            if (rootfs.mnt_fsname == NULL || rootfs.mnt_dir == NULL || rootfs.mnt_type == NULL) {
#if USE_SYSLOG
                syslog(LOG_ERR, "out of memory");
#else                   /* USE_SYSLOG */
                fprintf(stderr, "%s: out of memory\n", progname);
#endif
            }

            if ((rootfs.mnt_opts = malloc(strlen(mnt->mnt_opts) + strlen("remount,ro") + 2)) == NULL) {
#if USE_SYSLOG
                  syslog(LOG_ERR, "out of memory");
#else                   /* USE_SYSLOG */
                  fprintf(stderr, "%s: out of memory\n", progname);
#endif
            } else
                  sprintf(rootfs.mnt_opts, "%s,remount,ro", mnt->mnt_opts);
      }
#endif
    }
    endmntent(fp);
}

/* Parts of the following two functions are taken from Miquel van */
/* Smoorenburg's killall5 program. */

static PROC *plist;

/* get a list of all processes */
static int readproc()
{
    DIR *dir;
    struct dirent *d;
    pid_t act_pid;
    PROC *p;

    /* Open the /proc directory. */
    if ((dir = opendir("/proc")) == NULL) {
#if USE_SYSLOG
      syslog(LOG_ERR, "cannot opendir /proc");
#else                   /* USE_SYSLOG */
      perror(progname);
#endif
      return (-1);
    }
    plist = NULL;

    /* Walk through the directory. */
    while ((d = readdir(dir)) != NULL) {

      /* See if this is a process */
      if ((act_pid = atoi(d->d_name)) == 0)
          continue;

      /* Get a PROC struct . */
      if ((p = (PROC *) calloc(1, sizeof(PROC))) == NULL) {
#if USE_SYSLOG
          syslog(LOG_ERR, "out of memory");
#else                   /* USE_SYSLOG */
          fprintf(stderr, "%s: out of memory\n", progname);
#endif
          return (-1);
      }
      p->sid = getsid(act_pid);
      p->pid = act_pid;

      /* Link it into the list. */
      p->next = plist;
      plist = p;
    }
    closedir(dir);

    /* Done. */
    return (0);
}

static void killall5(int sig)
{
    PROC *p;
    int sid = -1;

    /*
     *    Ignoring SIGKILL and SIGSTOP do not make sense, but
     *    someday kill(-1, sig) might kill ourself if we don't
     *    do this. This certainly is a valid concern for SIGTERM-
     *    Linux 2.1 might send the calling process the signal too.
     */

    /* Since we ignore all signals, we don't have to worry here. MM */
    /* Now stop all processes. */
    kill(-1, SIGSTOP);

    /* Find out our own 'sid'. */
    if (readproc() < 0) {
      kill(-1, SIGCONT);
      return;
    }
    for (p = plist; p; p = p->next)
      if (p->pid == pid) {
          sid = p->sid;
          break;
      }
    /* Now kill all processes except our session. */
    for (p = plist; p; p = p->next)
      if (p->pid != pid && p->sid != sid)
          kill(p->pid, sig);

    /* And let them continue. */
    kill(-1, SIGCONT);
}

/* shut down the system */
void do_shutdown(int errorcode)
{
    int i = 0, fd;
    char *seedbck = RANDOM_SEED;

    /* soft-boot the system */
    /* first close the open files */
    close_all();

    /* if we will halt the system we should try to tell a sysadmin */
    if (admin != NULL) {
      /* send mail to the system admin */
      FILE *ph;
      char exe[128];

      /* only can send an email if sendmail binary exists so make shell check
       * that first, or else we will get a broken pipe in pclose */
      sprintf(exe, "[ -x %s ] && %s -i %s", PATH_SENDMAIL, PATH_SENDMAIL, admin);
      ph = popen(exe, "w");
      if (ph == NULL) {
#if USE_SYSLOG
          syslog(LOG_ERR, "cannot start %s (errno = %d)", PATH_SENDMAIL, errno);
#endif                        /* USE_SYSLOG */
      } else {
          char myname[MAXHOSTNAMELEN + 1];
          struct hostent *hp;

          /* get my name */
          gethostname(myname, sizeof(myname));

          fprintf(ph, "To: %s\n", admin);
          if (ferror(ph) != 0) {
#if USE_SYSLOG
            syslog(LOG_ERR, "cannot send mail (errno = %d)", errno);
#endif                        /* USE_SYSLOG */
          }
          /* if possible use the full name including domain */
          if ((hp = gethostbyname(myname)) != NULL)
            fprintf(ph, "Subject: %s is going down!\n\n", hp->h_name);
          else
            fprintf(ph, "Subject: %s is going down!\n\n", myname);
          if (ferror(ph) != 0) {
#if USE_SYSLOG
            syslog(LOG_ERR, "cannot send mail (errno = %d)", errno);
#endif                        /* USE_SYSLOG */
          }

          if (errorcode == ETOOHOT)
            fprintf(ph, "Message from watchdog:\nIt is too hot to keep on working. The system will be halted!\n");
          else
            fprintf(ph, "Message from watchdog:\nThe system will be rebooted because of error %d!\n", errorcode);
          if (ferror(ph) != 0) {
#if USE_SYSLOG
            syslog(LOG_ERR, "cannot send mail (errno = %d)", errno);
#endif                        /* USE_SYSLOG */
          }
          if (pclose(ph) == -1) {
#if USE_SYSLOG
            syslog(LOG_ERR, "cannot finish mail (errno = %d)", errno);
#endif                        /* USE_SYSLOG */
          }
          /* finally give the system a little bit of time to deliver */
      }
    }

#if USE_SYSLOG
    /* now tell syslog what's happening */
    syslog(LOG_ALERT, "shutting down the system because of error %d", errorcode);
    closelog();
#endif                        /* USE_SYSLOG */

    sleep(10);                /* make sure log is written and mail is send */

    /* We cannot start shutdown, since init might not be able to fork. */
    /* That would stop the reboot process. So we try rebooting the system */
    /* ourselves. Note, that it is very likely we cannot start any rc */
    /* script either, so we do it all here. */

    /* Start with closing the files. */
    for (i = 0; i < 3; i++)
      if (!isatty(i))
          close(i);
    for (i = 3; i < 20; i++)
      close(i);
    close(255);

    /* Ignore all signals. */
    for (i = 1; i < NSIG; i++)
      signal(i, SIG_IGN);

    /* Stop init; it is insensitive to the signals sent by the kernel. */
    kill(1, SIGTSTP);

    /* Kill all processes. */
    (void) killall5(SIGTERM);
    sleep(5);
    (void) killall5(SIGKILL);

    /* Record the fact that we're going down */
    if ((fd = open(_PATH_WTMP, O_WRONLY | O_APPEND)) >= 0) {
      time_t t;
      struct utmp wtmp;

      time(&t);
      strcpy(wtmp.ut_user, "shutdown");
      strcpy(wtmp.ut_line, "~");
      strcpy(wtmp.ut_id, "~~");
      wtmp.ut_pid = 0;
      wtmp.ut_type = RUN_LVL;
      wtmp.ut_time = t;
      write(fd, (char *) &wtmp, sizeof(wtmp));
      close(fd);
    }
    
    /* save the random seed if a save location exists */
    /* don't worry about error messages, we react here anyway */
    if (strlen(seedbck) != 0) {
      int fd_seed;

      if ((fd_seed = open("/dev/urandom", O_RDONLY)) >= 0) {
          int fd_bck;

          if ((fd_bck = creat(seedbck, S_IRUSR | S_IWUSR)) >= 0) {
            char buf[512];

            if (read(fd_seed, buf, 512) == 512)
                write(fd_bck, buf, 512);
            close(fd_bck);
          }
          close(fd_seed);
      }
    }
    
    /* Turn off accounting */
    if (acct(NULL) < 0)
      perror(progname);

    /* Turn off quota and swap */
    mnt_off();

    /* umount all partitions */
    if (setjmp(ret2dog) == 0)
      umount_all(NULL);

#if 0
    /* with the more recent version of mount code, this is not needed anymore */
    /* remount / read-only */
    if (setjmp(ret2dog) == 0)
      mount_one(rootfs.mnt_fsname, rootfs.mnt_dir, rootfs.mnt_type,
              rootfs.mnt_opts, rootfs.mnt_freq, rootfs.mnt_passno);
#endif

    /* shut down interfaces (also taken from sysvinit source */
    ifdown();

    /* finally reboot */
    if (errorcode != ETOOHOT) {
#ifdef __GLIBC__
      reboot(RB_AUTOBOOT);
#else                   /* __GLIBC__ */
      reboot(0xfee1dead, 672274793, 0x01234567);
#endif                        /* __GLIBC__ */
    } else {
      /* rebooting makes no sense if it's too hot */
      /* Turn on hard reboot, CTRL-ALT-DEL will reboot now */
#ifdef __GLIBC__
      reboot(RB_ENABLE_CAD);
#else                   /* __GLIBC__ */
      reboot(0xfee1dead, 672274793, 0x89abcdef);
#endif                        /* __GLIBC__ */

      /* And perform the `halt' system call. */
#ifdef __GLIBC__
      reboot(RB_HALT_SYSTEM);
#else                   /* __GLIBC__ */
      reboot(0xfee1dead, 672274793, 0xcdef0123);
#endif
    }

    /* okay we should never reach this point, */
    /* but if we do we will cause the hard reset */

    /* open the device again */
    /* don't check for error, it won't help anyway here */
    if (devname != NULL) {
      open(devname, O_WRONLY);

      sleep(TIMER_MARGIN * 4);
    }

    /* unbelievable: we're still alive */
    panic();
}



Generated by  Doxygen 1.6.0   Back to index