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

mailq.c

/*
 *    Copyright 1988 by Rayan S. Zachariassen, all rights reserved.
 *    This will be free software, but only when it is finished.
 */
/*
 *    Lots of modifications (new guts, more or less..) by
 *    Matti Aarnio <mea@nic.funet.fi>  (copyright) 1992-2003
 */

/*
 * Rayan 1988:
 *  This program must be installed suid to the uid the scheduler runs as
 *  (usually root).  Unfortunately.
 *
 * mea 1990:
 *  This program can be run without suid-root -- depending on what one
 *  needs of special features, e.g. if one aspires to see the verbose
 *  queue printout along with exact message source and destination
 *  addresses, message-ids, sizes, ...  Either run as root, or suid-root.
 *
 * mea 2001:
 *  What has been true for 10+ years is still true, several things can
 *  now be done without any sort of suid-privileges, others may need access
 *  to the actual message files and need e.g. root powers.
 *  Autentication for using 'MAILQv2' is orthogonal from suid:ing this.
 */


#include "hostenv.h"
#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include <sysexits.h>
#include <signal.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <pwd.h>
#include <stdlib.h>
#include "mail.h"
#include "scheduler.h"
#include "zmalloc.h"
#include "mailer.h"

#include "md5.h"

#ifdef HAVE_DIRENT_H
# include <dirent.h>
#else /* not HAVE_DIRENT_H */
# define dirent direct
# ifdef HAVE_SYS_NDIR_H
#  include <sys/ndir.h>
# endif /* HAVE_SYS_NDIR_H */
# ifdef HAVE_SYS_DIR_H
#  include <sys/dir.h>
# endif /* HAVE_SYS_DIR_H */
# ifdef HAVE_NDIR_H
#  include <ndir.h>
# endif /* HAVE_NDIR_H */
#endif /* HAVE_DIRENT_H */

#ifdef HAVE_SYS_LOADAVG_H
#include <sys/loadavg.h>
#endif

#include <netdb.h>
#ifndef EAI_AGAIN
# include "netdb6.h"
#endif
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <sys/file.h>

#ifdef HAVE_SYS_UN_H
#include <sys/un.h>
#endif

#ifdef      MALLOC_TRACE
struct conshell *envarlist = NULL;
#endif      /* MALLOC_TRACE */
int   D_alloc = 0;


#include "prototypes.h"
#include "memtypes.h"
#include "token.h"
#include "libz.h"
#include "libc.h"

#include "ta.h"

extern int errno, pokedaemon();

static void print_shm __((void));

#ifndef strchr
extern char *strchr(), *strrchr();
#endif

const char  *progname;
const char  *postoffice;

static char *port = NULL;

char * v2username = "nobody";
char * v2password = "nobody";

int   debug, verbose, summary, user, status, onlyuser, nonlocal, schedq;
int   sawcore, othern;

time_t      now;

extern char *optarg;
extern int   optind;
char  path[MAXPATHLEN];

#define  ISDIGIT(cc) ('0' <= cc && cc <= '9')
#define  ISSPACE(cc) (cc == ' ' || cc == '\t')


typedef struct threadtype {
  const char *channel;
  const char *host;
  char *line;
} threadtype;


const char *host = NULL;

const char *channel_opt = NULL;
const char *host_opt    = NULL;

int
main(argc, argv)
      int argc;
      char *argv[];
{
      int fd, c, errflg, eval;
      struct passwd *pw;
#ifndef     AF_INET
      const char *rendezvous = NULL;
      FILE *fp;
      struct stat stbuf;
      int r, pid, dsflag;
#endif      /* AF_INET */
      int prefer_4 = 0, prefer_6 = 0;
      char *expn = NULL;

      progname = argv[0];
      verbose = debug = errflg = status = user = onlyuser = summary = 0;
      
      while (1) {
        c = getopt(argc, argv, "46c:dE:h:iK:Mp:Qr:sStu:U:vVZ:");
        if (c == EOF)
          break;
        switch (c) {
        case '4':
          prefer_4 = 1;
          prefer_6 = 0;
          break;
        case '6':
          prefer_4 = 0;
          prefer_6 = 1;
          break;
        case 'c':
          channel_opt = optarg;
          break;
        case 'd':
          ++debug;
          break;
        case 'E':
          expn = optarg;
          break;
        case 'h':
          host_opt = optarg;
          if (!channel_opt) channel_opt = "smtp";
          break;
        case 'i':
          user = getuid();
          onlyuser = 1;
          if (verbose == 0)
            ++verbose;
          break;
        case 'M':
          print_shm();
          break;
#if defined(AF_INET) || defined(AF_UNIX)
        case 'p':
          port = optarg;
          break;
#else  /* !AF_INET */
        case 'r':
          rendezvous = optarg;
          break;
#endif /* AF_INET */
        case 's':
          ++status;
          break;
        case 't':
          verbose = 0;
          break;
        case 'u':
          if (optarg == NULL) {
            ++errflg;
            break;
          }
          if ((pw = getpwnam(optarg)) == NULL) {
            fprintf(stderr, "%s: unknown user '%s'\n", progname, optarg);
            ++errflg;
            break;
          }
          user = pw->pw_uid;
          onlyuser = 1;
          if (verbose == 0)
            ++verbose;
          break;
        case 'v':
          ++verbose;
          break;
        case 'S':
          ++summary;
          break;
        case 'Q':
          ++schedq;
          break;
        case 'U':
          v2username = optarg;
          v2password = strchr(v2username,'/');
          if (v2password) *v2password++ = 0;
          else {
            v2password = strchr(v2username,':');
            if (v2password) *v2password++ = 0;
            else {
            v2password = strchr(v2username,',');
            if (v2password) *v2password++ = 0;
            else {
              v2password = "nobody";
            }
            }
          }
          break;
        case 'V':
          prversion("mailq");
          exit(0);
          break;
        case 'Z':
          if (readzenv(optarg) == 0)
            ++errflg;
          break;
        default:
          ++errflg;
          break;
        }
      }
      time(&now);
      if (optind < argc) {
#ifdef      AF_INET
        if (optind != argc - 1) {
          fprintf(stderr, "%s: too many hosts\n", progname);
          ++errflg;
        } else
          host = argv[optind];
#else  /* !AF_INET */
        fprintf(stderr, "%s: not compiled with AF_INET\n", progname);
        ++errflg;
#endif /* AF_INET */
      }
      if (errflg) {
#ifdef      AF_INET
        fprintf(stderr, "Usage: %s [-46isSvt] [-Z zenvcfgfile] [-cchannel -hhost] [-p#] [host]\n", progname);
#else  /* !AF_INET */
        fprintf(stderr, "Usage: %s [-isSvt] [-Z zenvcfgfile] [-cchannel -hhost]\n", progname);
#endif /* AF_INET */
        exit(EX_USAGE);
      }
      if ((postoffice = getzenv("POSTOFFICE")) == NULL)
        postoffice = POSTOFFICE;

      sprintf(path, "%s/%s", postoffice, PID_SCHEDULER);

      errno = 0;

#if defined(AF_UNIX) && defined(HAVE_SYS_UN_H)
      if (port && *port == '/') {
        struct sockaddr_un sad;

        if (status) {
          checkrouter();
          checkscheduler();
          if (status > 1 && !summary)
            exit(0);
        }

        /* try grabbing a port */
        fd = socket(PF_UNIX, SOCK_STREAM, 0);
        if (fd < 0) {
          fprintf(stderr, "%s: ", progname);
          perror("socket");
          exit(EX_UNAVAILABLE);
        }

        sad.sun_family = AF_UNIX;
        strncpy(sad.sun_path, port, sizeof(sad.sun_path));
        sad.sun_path[ sizeof(sad.sun_path) ] = 0;

        if (connect(fd, (void*)&sad, sizeof sad) < 0) {
          fprintf(stderr,"%s: connect failed to path: '%s'\n",progname,sad.sun_path);
          exit(EX_UNAVAILABLE);
        }

        docat((char *)NULL, fd);
      }
#endif
#ifdef      AF_INET
      if (!port || (port && *port != '/')) {


        typedef union {
          struct sockaddr_in  v4;
#if defined(AF_INET6) && defined(INET6)
          struct sockaddr_in6 v6;
#endif
        } Usockaddr;


        struct addrinfo *ai, req;
        int rc;

        struct servent *serv = NULL;

        int portnum = 174;
        nonlocal = 0; /* Claim it to be: "localhost" */

        if (status < 2 || summary) {

          if (port && ISDIGIT(*port)) {
            portnum = atol(port);
          } else if (port == NULL &&
                   (serv = getservbyname(port ? port : "mailq", "tcp")) == NULL) {

            fprintf(stderr,"%s: cannot find 'mailq' tcp service\n",progname);

          } else if (port == 0)
            
            portnum = ntohs(serv->s_port);

          if (host == NULL) {
            host = getzenv("MAILSERVER");
            if ((host == NULL || *host == '\n')
              && (host = whathost(path)) == NULL) {
            if (status > 0) {
              host = "127.0.0.1"; /* "localhost" */
              nonlocal = 0;
            } else {
              if (whathost(postoffice)) {
                fprintf(stderr, "%s: %s is not active", progname, postoffice);
                fprintf(stderr, " (\"%s\" does not exist)\n", path);
              } else
                fprintf(stderr, "%s: cannot find postoffice host\n", progname);
              exit(EX_OSFILE);
            }
            }
          }

          memset(&req, 0, sizeof(req));
          req.ai_socktype = SOCK_STREAM;
          req.ai_protocol = IPPROTO_TCP;
          req.ai_flags    = AI_CANONNAME;
          req.ai_family   = AF_INET;
          ai = NULL;

#ifdef HAVE_GETADDRINFO
          rc = getaddrinfo(host, "0", &req, &ai);
#else
          rc = _getaddrinfo_(host, "0", &req, &ai,
                         (debug ? stderr : NULL));
#endif
#if defined(AF_INET6) && defined(INET6)
          {
            struct addrinfo *ai6;
            memset(&req, 0, sizeof(req));
            req.ai_socktype = SOCK_STREAM;
            req.ai_protocol = IPPROTO_TCP;
            req.ai_flags    = AI_CANONNAME;
            req.ai_family   = AF_INET6;
            ai6 = NULL;
            
#ifdef HAVE_GETADDRINFO
            rc = getaddrinfo(host, "0", &req, &ai6);
#else
            rc = _getaddrinfo_(host, "0", &req, &ai6,
                         (debug ? stderr : NULL));
#endif
            if (!ai && rc == 0)
            /* No IPv4, but have IPv6! */
            ai = ai6;
            else if (ai && ai6) {
            struct addrinfo **aip;
            if (prefer_4) {
              /* Catenate them, FIRST IPv4, then IPv6 things. */
              aip = &ai->ai_next;
              while (*aip) aip = &(*aip)->ai_next;
              *aip = ai6;
            } else {
              /* Catenate them, FIRST IPv6, then IPv4 things. */
              aip = &ai6->ai_next;
              while (*aip) aip = &(*aip)->ai_next;
              *aip = ai;
              ai = ai6;
            }
            }
          }
#endif
          if (! ai) {
            fprintf(stderr, "%s: cannot find address of %s\n", progname, host);
            exit(EX_UNAVAILABLE);
          }
          stashmyaddresses(NULL);
          if (ai && matchmyaddresses(ai) == 0) {
            /* BSD systems can yield ai_canonname member NULL! */
            fprintf(stdout, "[%s]\n", ai->ai_canonname ? ai->ai_canonname : host);
            nonlocal = 1;
          } else
            nonlocal = 0;     /* "localhost" is per default a "local" */
        }
        if (status) {
          checkrouter();
          checkscheduler();
          if (status > 1 && !summary)
            exit(0);
        }

        fd = -1;

        for (; ai; ai = ai->ai_next) {

          Usockaddr *sa = (Usockaddr *)ai->ai_addr;
          int addrsiz = sizeof(sa->v4);

#if defined(AF_INET6) && defined(INET6)
          if (ai->ai_family == AF_INET6) {
            addrsiz = sizeof(sa->v6);
            sa->v6.sin6_port = htons(portnum);
          } else
#endif
            sa->v4.sin_port = htons(portnum);

          /* try grabbing a port */
          fd = socket(ai->ai_family, SOCK_STREAM, 0);
          if (fd < 0) {
            fprintf(stderr, "%s: ", progname);
            perror("socket");
            continue;
          }
          while ((rc = connect(fd, (struct sockaddr *)sa, addrsiz)) < 0 &&
               (errno == EINTR || errno == EAGAIN));

          if (rc < 0) {
            eval = errno;
            close(fd);
            fprintf(stderr, "%s: connect failed to %s\n",
                  progname, ai->ai_canonname ? ai->ai_canonname : host);
            fd = -1;
            continue;
          }
        }
        if (fd >= 0)
          docat((char *)NULL, fd);
        else {
          fprintf(stderr, "%s: connect failed to %s\n",
                progname, host);
        }
      }
#else /* !AF_INET */
      if (strcmp(host, "localhost") == 0 ||
          strcmp(host, "127.0.0.1") == 0) {
        nonlocal = 0;   /* "localhost" is per default a "local" */
      if (status) {
        checkrouter();
        checkscheduler();
        if (status > 1 && !summary)
          exit(0);
      }
      r = isalive(PID_SCHEDULER, &pid, &fp);
      if (r == EX_OSFILE)
        exit(r);
      else if (r == EX_UNAVAILABLE) {
        fprintf(stderr, "%s: no active scheduler process\n", progname);
        exit(r);
      } else if (fp != NULL)
        fclose(fp);
      if (rendezvous == NULL && (rendezvous=getzenv("RENDEZVOUS")) == NULL) {
        rendezvous = qoutputfile;
      }
#ifdef      S_IFIFO
      if (stat(rendezvous, &stbuf) < 0) {
        unlink(rendezvous);
        if (mknod(rendezvous, S_IFIFO|0666, 0) < 0) {
          fprintf(stderr, "%s: mknod: %s\n", progname, strerror(errno));
          exit(EX_UNAVAILABLE);
        }
        stbuf.st_mode |= S_IFIFO; /* cheat on the next test... */
      }
      if (stbuf.st_mode & S_IFIFO) {
        if ((fd = open(rendezvous, O_RDONLY|O_NDELAY, 0)) < 0) {
          fprintf(stderr, "%s: %s: %s\n", progname, rendezvous, strerror(errno));
          exit(EX_OSFILE);
        }
        dsflag = fcntl(fd, F_GETFL, 0);
        dsflag &= ~O_NDELAY;
        fcntl(fd, F_SETFL, dsflag);
        pokedaemon(pid);
        /* XX: reset uid in case we are suid - we need to play games */
        sleep(1);       /* this makes it work reliably. WHY ?! */
        docat((char *)NULL, fd);
      } else
#endif      /* S_IFIFO */
      {
        pokedaemon(pid);
        /* XX: reset uid in case we are suid */
        /* sleep until mtime < ctime */
        do {
          sleep(1);
          if (stat(rendezvous, &stbuf) < 0)
            continue;
          if (stbuf.st_mtime < stbuf.st_ctime)
            break;
        } while (1);
        docat(rendezvous, -1);
      }
#endif      /* AF_INET */
      exit(EX_OK);
      /* NOTREACHED */
      return 0;
}


/* Lifted from BIND res/res_debug.c */
/*
 * Return a mnemonic for a time to live
 */
char *
saytime(value, buf, shortform)
      long value;
      char *buf;
      int shortform;
{
      int secs, mins, hours, fields = 0;
      register char *p;

      p = buf;

      while (*p) ++p;
      if (value < 0) {
        *p++ = '-'; *p = 0;
        value = -value;
      }

      if (value == 0) {
        if (shortform)
          strcpy(p,"0s");
        else
          strcpy(p,"0 sec");
        return buf;
      }

      secs = value % 60;
      value /= 60;
      mins = value % 60;
      value /= 60;
      hours = value % 24;
      value /= 24;

#define     PLURALIZE(x)      x, (x == 1) ? "" : "s"
      if (value) {
        if (shortform)
          sprintf(p, "%ldd", value);
        else
          sprintf(p, "%ld day%s", PLURALIZE(value));
        ++fields;
        while (*++p);
      }
      if (hours) {
        if (shortform)
          sprintf(p, "%dh", hours);
        else {
          if (value && p != buf)
            *p++ = ' ';
          sprintf(p, "%d hour%s", PLURALIZE(hours));
        }
        ++fields;
        while (*++p);
      }
      if (mins && fields < 2) {
        if (shortform)
          sprintf(p, "%dm", mins);
        else {
          if ((hours || value) && p != buf)
            *p++ = ' ';
          sprintf(p, "%d min%s", PLURALIZE(mins));
        }
        while (*++p);
      }
      if (secs && fields < 2) {
        if (shortform)
          sprintf(p, "%ds", secs);
        else {
          if ((mins || hours || value) && p != buf)
            *p++ = ' ';
          sprintf(p, "%d sec%s", PLURALIZE(secs));
        }
        while (*++p);
      }
      *p = '\0';
      return buf;
}

void
docat(file, fd)
      const char *file;
      int fd;
{
      FILE *fpi = NULL, *fpo = NULL;

      if (fd < 0 && (fpi = fopen(file, "r")) == NULL) {
        fprintf(stderr, "%s: %s: %s\n", progname, file, strerror(errno));
        exit(EX_OSFILE);
        /* NOTREACHED */
      } else if (fd >= 0) {
        fpi = fdopen(fd, "r");
        fpo = fdopen(fd, "w");
      }
#if 0
      if (debug && fpi) {
        char buf[BUFSIZ];
        int n;
        while ((n = fread(buf, 1, sizeof buf, fpi)) > 0)
          fwrite(buf, sizeof buf[0], n, stdout);
      } else
#endif
        if (fpi && fpo)
          report(fpi, fpo);
      if (fpi) fclose(fpi);
      if (fpo) fclose(fpo);
}

int countfiles __((const char *));
int countfiles(dirpath)
const char *dirpath;
{
      char dpath[512];

      struct dirent *dp;
      DIR *dirp;
      int n = 0;

      dirp = opendir(dirpath);
      if (dirp == NULL) {
        fprintf(stderr, "%s: opendir(%s): %s\n",
              progname, dirpath, strerror(errno));
        return -1;
      }
      for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
        if (dp->d_name[0] == '.' &&
            (dp->d_name[1] == 0 || (dp->d_name[1] == '.' &&
                              dp->d_name[2] == 0)))
          continue; /* . and .. */
        if (ISDIGIT(dp->d_name[0]))
          ++n;
        else if (strcmp("core",dp->d_name)==0)
          sawcore = 1, ++othern;
        else {
          if (dp->d_name[0] >= 'A' && dp->d_name[0] <= 'Z' &&
            dp->d_name[1] == 0) {
            struct stat stbuf;
            sprintf(dpath, "%s/%s", dirpath, dp->d_name);
            if (lstat(dpath,&stbuf) != 0 ||
              !S_ISDIR(stbuf.st_mode)) {
            ++othern;
            } else {
            n += countfiles(dpath);
            }
          } else {
            ++othern;
          }
        }
      }
#ifdef      BUGGY_CLOSEDIR
      /*
       * Major serious bug time here;  some closedir()'s
       * free dirp before referring to dirp->dd_fd. GRRR.
       * XX: remove this when bug is eradicated from System V's.
       */
      close(dirp->dd_fd);
#endif
      closedir(dirp);
      return n;
}

/*
 * Determine if the Router is alive, how many entries are in the queue,
 * and whether the router dumped core last time it died.
 */
 
void
checkrouter()
{
      int pid, n, r;
      FILE *fp;
      struct stat pidbuf, corebuf;
      struct dirent *dp;
      DIR *dirp;

      if (postoffice == NULL)
        return;
      sprintf(path, "%s/%s", postoffice, ROUTERDIR);
      n = countfiles(path);
      fprintf(stdout,"%d entr%s in %s/ directory: ", n, n != 1 ? "ies" : "y", path);

      if (nonlocal)
        r = -2;
      else
        r = isalive(PID_ROUTER, &pid, &fp);

      switch (r) {
      case EX_UNAVAILABLE:
        /* if the .router.pid file is younger than any core file,
           then the router dumped core... so let'em know about it. */
        sprintf(path, "%s/%s/core",postoffice,ROUTERDIR);
        if (fstat(FILENO(fp), &pidbuf) < 0) {
          fprintf(stderr, "\n%s: fstat: %s", progname, strerror(errno));
        } else if (stat(path, &corebuf) == 0 && pidbuf.st_mtime < corebuf.st_mtime)
          fprintf(stdout,"core dumped\n");
        else
          fprintf(stdout,"no daemon\n");
        fclose(fp);
        break;
      case EX_OK:
        if (n)
          fprintf(stdout,"processing\n");
        else
          fprintf(stdout,"idle\n");
        fclose(fp);
        break;
      case -2:
        fprintf(stdout,"non-local\n");
        break;
      default:
        fprintf(stdout,"never started\n");
        break;
      }

      sprintf(path, "%s/%s", postoffice, DEFERREDDIR);
      dirp = opendir(path);
      if (dirp == NULL) {
        fprintf(stderr, "%s: opendir(%s): %s\n",
              progname, path, strerror(errno));
        return;
      }
      for (dp = readdir(dirp), n = 0; dp != NULL; dp = readdir(dirp)) {
        if (ISDIGIT(dp->d_name[0]))
          ++n;
      }
#ifdef      BUGGY_CLOSEDIR
      /*
       * Major serious bug time here;  some closedir()'s
       * free dirp before referring to dirp->dd_fd. GRRR.
       * XX: remove this when bug is eradicated from System V's.
       */
      close(dirp->dd_fd);
#endif
      closedir(dirp);
      if (n)
        fprintf(stdout,"%d message%s deferred\n", n, n != 1 ? "s" : "");
}


void
checkscheduler()
{
      int pid, n, n2, r;
      FILE *fp;

      if (postoffice == NULL)
        return;

      sawcore = 0;
      othern  = 0;

      sprintf(path, "%s/%s", postoffice, TRANSPORTDIR);
      n = countfiles(path);
      fprintf(stdout,"%d message%s in %s/ directory: ",
             n, n != 1 ? "s" : "", path);

      if (nonlocal)
        r = -2;
      else
        r = isalive(PID_SCHEDULER, &pid, &fp);

      switch (r) {
      case EX_UNAVAILABLE:
        fprintf(stdout,"no scheduler daemon");
        fclose(fp);
        break;
      case EX_OK:
        if (n == 0)
          fprintf(stdout,"idle");
        else
          fprintf(stdout,"working");
        break;
      case -2:
        fprintf(stdout,"non-local");
        break;
      default:
        fprintf(stdout,"never started");
        if (n > 0)
          fprintf(stdout," \"%s/%s\" polluted", postoffice, TRANSPORTDIR);
        break;
      }
      if (sawcore)
        fprintf(stdout," (core exists)");
      fprintf(stdout,"\n");

      sprintf(path, "%s/%s", postoffice, QUEUEDIR);
      n2 = countfiles(path);
      if (n != n2)
        fprintf(stdout,"%d message%s in %s/ directory\n",
              n2, n2 != 1 ? "s" : "", path);
}

int
isalive(pidfil, pidp, fpp)
      const char *pidfil;
      int *pidp;
      FILE **fpp;
{
      if (postoffice == NULL)
            return 0;
      sprintf(path, "%s/%s", postoffice, pidfil);
      
      if ((*fpp = fopen(path, "r")) == NULL) {
        /* fprintf(stderr, "%s: cannot open %s (%s)\n",
           progname, path, strerror(errno)); */
        return EX_OSFILE;
      }
      if (fscanf(*fpp, "%d", pidp) != 1) {
        fprintf(stderr, "%s: cannot read process id\n", progname);
        fclose(*fpp);
        *fpp = NULL;
        return EX_OSFILE;
      }
      if (kill(*pidp, 0) < 0 && errno == ESRCH)
        return EX_UNAVAILABLE;
      return EX_OK;
}

#define     MAGIC_PREAMBLE          "version "
#define     LEN_MAGIC_PREAMBLE      (sizeof MAGIC_PREAMBLE - 1)
#define     VERSION_ID        "zmailer 1.0"
#define     VERSION_ID2       "zmailer 2.0"

static int _getline(buf, bufsize, bufspace, fp)
     char **buf;
     int *bufsize;
     int *bufspace;
     FILE *fp;
{
  int c;

  if (!*buf) {
    *bufsize = 0;
    *bufspace = 110;
    *buf = malloc(*bufspace+3);
  }

  while ((c = fgetc(fp)) != EOF) {
    if (c == '\n')
      break;

    if (*bufsize >= *bufspace) {
      *bufspace *= 2;
      *buf = realloc(*buf, *bufspace+3);
    }
    (*buf)[*bufsize] = c;
    *bufsize += 1;
  }
  (*buf)[*bufsize] = 0;

  if (c == EOF && *bufsize != 0) {
    fprintf(stderr, "%s: no input from scheduler\n", progname);
    (*buf)[0] = '\0';
    return -1;
  }

  if (debug && *buf)
    fprintf(stderr, "- %s\n",*buf);

  return 0; /* Got something */
}


#define GETLINE(buf, bufsize, bufspace, fp) _getline(&buf, &bufsize, &bufspace, fp)


const char *names[SIZE_L+2];

#define     L_VERTEX    SIZE_L
#define L_END           SIZE_L+1
struct sptree *spt_ids [SIZE_L+2];
struct sptree *spt_syms[SIZE_L+2];

#define     EQNSTR(a,b) (!strncmp(a,b,strlen(b)))

extern int parse __((FILE *));
int
parse(fp)
      FILE *fp;
{
      register char *cp;
      register struct vertex *v;
      register struct web *w;
      register struct ctlfile *cfp;
      register int      i;
      u_long      list, key;
      struct spblk *spl;
      int bufsize, bufspace;
      char  *buf = NULL, *ocp;

      names[L_CTLFILE] = "Vertices:";
      names[L_HOST]    = "Hosts:";
      names[L_CHANNEL] = "Channels:";
      names[L_END]     = "End:";

      bufsize = 0;
      if (GETLINE(buf,bufsize,bufspace,fp))
        return 0;

      if (EQNSTR(buf, MAGIC_PREAMBLE) &&
          EQNSTR(buf+LEN_MAGIC_PREAMBLE, VERSION_ID2))
        return 2; /* We have version 2 scheduler! */

      if (!(EQNSTR(buf, MAGIC_PREAMBLE)
            && EQNSTR(buf+LEN_MAGIC_PREAMBLE, VERSION_ID))) {
        fprintf(stderr, "%s: version mismatch, input is \"%s\".\n", progname, buf);
        return 0;
      }

      if (schedq) {
        /* We ignore the classical mailq data, just read it fast */
        while (1) {
          bufsize = 0;
          if (GETLINE(buf, bufsize, bufspace, fp))
            return 1; /* EOF ? */
          if (memcmp(buf,"End:",4) == 0)
            return 1;
        }
        /* NOT REACHED */
      }

      bufsize = 0;
      if (GETLINE(buf,bufsize,bufspace,fp))
        return 0;
      if (!EQNSTR(buf, names[L_CTLFILE]))
        return 0;
      list = L_CTLFILE;
      spt_ids [L_CTLFILE] = sp_init();
      spt_ids [L_VERTEX ] = sp_init();
      spt_ids [L_CHANNEL] = sp_init();
      spt_ids [L_HOST   ] = sp_init();
      spt_syms[L_CTLFILE] = sp_init();
      spt_syms[L_VERTEX ] = sp_init();
      spt_syms[L_CHANNEL] = sp_init();
      spt_syms[L_HOST   ] = sp_init();
      while (1) {

        bufsize = 0;
        if (GETLINE(buf, bufsize, bufspace, fp))
          break;

        switch ((int)list) {
        case L_CTLFILE:
          /* decid:\tfile\tnaddr; off1[,off2,...][\t#message] */
          if (!ISDIGIT(buf[0])) {
            if (EQNSTR(buf, names[L_CHANNEL])) {
            list = L_CHANNEL;
            break;
            }
            if (EQNSTR(buf, names[L_END])) {
            return 1;
            }
          }
          if (!ISDIGIT(buf[0]) ||
            (cp = strchr(buf, ':')) == NULL) {
            fprintf(stderr, "%s: %s: orphaned pending recovery\n", progname, buf);
            break;
          }
          *cp++ = '\0';
          key = atol(buf);
          while ( ISSPACE(*cp)) ++cp;
          ocp = cp;
          while (!ISSPACE(*cp)) ++cp;
          *cp++ = '\0';
if (debug)
  fprintf(stderr," - '%s'\n",ocp);

          spl = sp_lookup(symbol_db(ocp,spt_syms[L_CTLFILE]),
                      spt_ids[L_CTLFILE]);
          if (spl == NULL || (cfp = (struct ctlfile *)spl->data) == NULL) {
            cfp = (struct ctlfile *)emalloc(sizeof (struct ctlfile));
            memset((void*)cfp,0,sizeof(struct ctlfile));
            cfp->fd = -1;
            cfp->haderror = 0;
            cfp->head = NULL;
            cfp->nlines        = 0;
            cfp->msgbodyoffset = 0;
            cfp->contents = NULL;
            cfp->logident = NULL;
            cfp->id = 0;
            cfp->mid = strsave(ocp);
            cfp->mark = 0;
            sp_install(symbol_db(ocp,spt_syms[L_CTLFILE]),
                   (void *)cfp, 0, spt_ids[L_CTLFILE]);
          }
          while (*cp == ' ' || *cp == '\t')
            ++cp;
          ocp = cp;
          while ('0' <= *cp && *cp <= '9')
            ++cp;
          *cp++ = '\0';

if (debug)
  fprintf(stderr," - '%s'\n",ocp);

          if ((i = atoi(ocp)) < 1) {
            fprintf(stderr, "%s: bad number of addresses: '%s'\n", progname, ocp);
            break;
          }
          v = (struct vertex *)emalloc(sizeof(struct vertex)+((i-1)*sizeof(long)));
          memset((void*)v,0,sizeof (struct vertex)+(i-1)*sizeof(long));
          v->ngroup = i;
          v->cfp = cfp;
          while (ISSPACE(*cp)) ++cp;
          for (i = 0; ISDIGIT(*cp); ++cp) {
            ocp = cp;
            while (ISDIGIT(*cp)) ++cp;
            *cp = '\0';
            v->index[i++] = atol(ocp);

if (debug)
  fprintf(stderr," - '%s'\n",ocp);

          }
          while (*cp != '\0' && *cp != '\n' && *cp != '#')
            ++cp;
          if (*cp == '#') {
            ocp = ++cp;
            while (*cp != '\0' && *cp != '\n')
            ++cp;
            *cp = '\0';
            v->message = strsave(ocp);

if (debug)
  fprintf(stderr," - '%s'\n",ocp);

          } else
            v->message = NULL;
          v->next[L_CTLFILE] = cfp->head;
          if (cfp->head == NULL)
            cfp->head = v;
          else
            cfp->head->prev[L_CTLFILE] = v;
          v->prev[L_CTLFILE] = NULL;
          cfp->head = v;
          v->orig[L_CTLFILE] = v->orig[L_CHANNEL] = v->orig[L_HOST] = NULL;
          v->next[L_CHANNEL] = v->next[L_HOST] = NULL;
          v->prev[L_CHANNEL] = v->prev[L_HOST] = NULL;
          sp_install(key, (void *)v, 0, spt_ids[L_VERTEX]);
          break;
        case L_CHANNEL:
          /* (channel|host):\tdecid[>decid...] */
          if (EQNSTR(buf, names[L_HOST])) {
            list = L_HOST;
            break;
          }
          if (EQNSTR(buf, names[L_END])) {
            return 1;
          }
          /* FALL THROUGH */
        case L_HOST:
          if (EQNSTR(buf, names[L_END])) {
            return 1;
          }
          cp = buf-1;
          do {
            cp = strchr(cp+1, ':');
          } while (cp != 0 && (*(cp+1) != '\t' || *(cp+2) != '>'));

          if (cp == NULL) {
            fprintf(stderr, "%s: %s: orphaned pending recovery\n", progname, buf);
            break;
          }
          *cp++ = '\0';

if (debug)
  fprintf(stderr," - '%s'\n",buf);


          /* Look for channel/host identifier splay-tree */
          spl = sp_lookup(symbol_db(buf,spt_syms[list]), spt_ids[list]);
          if (spl == NULL || (w = (struct web *)spl->data) == NULL) {
            w = (struct web *)emalloc(sizeof (struct web));
            memset((void*)w,0,sizeof(struct web));
            w->name = strsave(buf);
            w->kids = 0;
            w->link = w->lastlink = NULL;
            sp_install(symbol_db(buf,spt_syms[list]),
                   (void *)w, 0, spt_ids[list]);
          }
          while (*cp == ' ' || *cp == '\t')
            ++cp;

          /* Pick each vertex reference */

          ++cp;         /* skip the first '>' */
          while (ISDIGIT(*cp)) {
            int c;
            ocp = cp;
            while (ISDIGIT(*cp))
            ++cp;
            c = *cp;
            *cp = '\0';
            if (c) ++cp;

if (debug)
  fprintf(stderr," - '%s'\n",ocp);

            spl = sp_lookup((u_long)atol(ocp), spt_ids[L_VERTEX]);
            if (spl == NULL || (v = (struct vertex *)spl->data)==NULL) {
            fprintf(stderr, "%s: unknown key %s\n", progname, ocp);
            } else {
            if (w->link)
              w->link->prev[list] = v;
            else
              w->lastlink = v;
            v->next[list] = w->link;
            w->link = v;
            if (v->orig[list] == NULL)
              v->orig[list] = w;
            }
          }
          break;
      default:
          break;
        }
      }
      return 1;
}

static int r_i;

extern int repscan_v1 __((struct spblk *));
int
repscan_v1(spl)
      struct spblk *spl;
{
      register struct vertex *v, *vv;
      struct web *w;
      int fd, flag = 0;
      struct stat stbuf;
      long filecnt, filesizesum;

      w = (struct web *)spl->data;
      /* assert w != NULL */
      for (vv = w->link; vv != NULL; vv = vv->next[L_CHANNEL]) {
        if (vv->ngroup == 0)
          continue;
        if (!onlyuser)
          fprintf(stdout,"%s/%s:\n", w->name, vv->orig[L_HOST]->name);
        else
          flag = 0;
        filecnt = 0;
        filesizesum = 0;
        for (v = vv; v != NULL; v = v->next[L_HOST]) {
          if (v->ngroup == 0)
            continue;
          if (onlyuser && status < 2) {
            sprintf(path, "%s/%s/%s", postoffice, TRANSPORTDIR, v->cfp->mid);
            if ((fd = open(path, O_RDONLY, 0)) < 0) {
            continue;
            }
            if (fstat(fd, &stbuf) < 0 || stbuf.st_uid != user) {
            close(fd);
            continue;
            }
            close(fd);
            if (flag == 0)
            fprintf(stdout,"%s/%s:\n", w->name, vv->orig[L_HOST]->name);
          }
          if (!summary) {
            flag = 1;
            fprintf(stdout,"\t%s", v->cfp->mid);
            if (v->ngroup > 1)
            fprintf(stdout,"/%d", v->ngroup);
            fprintf(stdout,":");
            if (v->message)
            fprintf(stdout,"\t%s\n", v->message);
            else
            fprintf(stdout,"\n");
            if (verbose)
            printaddrs(v);
          } else {
            verbose = 2;
            if (summary < 2)
            printaddrs(v);    /* summary does not print a thing! */
            ++filecnt;  /* however it counts many things.. */
            if (summary < 2)
            filesizesum += v->cfp->offset[0];
          }
          for (r_i = 0; r_i < SIZE_L; ++r_i) {
            if (v->next[r_i] != NULL)
            v->next[r_i]->prev[r_i] = v->prev[r_i];
            if (v->prev[r_i] != NULL)
            v->prev[r_i]->next[r_i] = v->next[r_i];
          }
          /* if we are verbose, space becomes important */
          if (v->next[L_CTLFILE] == NULL && v->prev[L_CTLFILE] == NULL) {
            /* we can free the control file */
            if (v->cfp->contents != NULL)
            free(v->cfp->contents);
            free((char *)v->cfp);
          }
          /* we can't free v! so mark it instead */
          v->ngroup = 0;
        }
        if (summary == 1 && !onlyuser) {
          fprintf(stdout,"\t  %d file%s, ", (int)filecnt, filecnt>1 ? "s":"");
          if (filesizesum == 0)
            fprintf(stdout,"no file size info available\n");
          else
            fprintf(stdout,"%ld bytes total, %d bytes average\n",
                  filesizesum, (int)(filesizesum/filecnt));
        }
        if (summary > 1 && !onlyuser) {
          fprintf(stdout,"\t  %d file%s\n", (int)filecnt, filecnt>1 ? "s":"");
        }
      }
      return 0;
}

static struct ctlfile *readmq2cfp __((const char *fname));
static struct ctlfile *readmq2cfp(fname)
     const char *fname;
{
      struct ctlfile *cfp = NULL;
      int i, fd, once;
      struct stat stbuf;
      char *s, *s0;

      sprintf(path, "%s/%s/%s", postoffice, TRANSPORTDIR, fname);
      if (lstat(path, &stbuf) != 0) return NULL;

      cfp = malloc(sizeof(*cfp) + stbuf.st_size + 20);
      if (!cfp) return NULL;

      fd = open(path,O_RDONLY,0);
      if (fd < 0) {
        /* whatever reason */
        free(cfp);
        return NULL;
      }

      s0 = (char *)(cfp+1);

      i = read(fd, s0, stbuf.st_size);
      close(fd);

      memset(cfp, 0, sizeof(*cfp));
      cfp->contents = s0;
      cfp->nlines = stbuf.st_size; /* reuse the variable .. */

      if (i != stbuf.st_size) {
        /* whatever reason.. */
        free(cfp);
        return NULL;
      }

      sprintf(path, "%s/%s/%s", postoffice, QUEUEDIR, fname);
      if (lstat(path, &stbuf) == 0) {
        cfp->msgbodyoffset = stbuf.st_size;
        cfp->mtime         = stbuf.st_mtime;
      }

      s0[i] = 0;

      once = 1;
      for (s = s0; i > 0; ++s, --i) {

        char c;
        char *p;

        if (*s == '\n') {
          --i; ++s;
        }
        c = *s;
        once = 0;
        --i; ++s;
        --i; ++s;
        if (i > 0)
          p = memchr(s, '\n', i);
        else
          break;
        if (!p) break;
        switch(c) {
        case _CF_FORMAT:
          *p = 0;
          cfp->format = 0;
          sscanf(s, "%i", &cfp->format);
          i -= (p - s);
          s = p;
          break;
        case _CF_LOGIDENT:
          cfp->logident = s;
          *p = 0;
          i -= (p - s);
          s = p;
          break;
        case _CF_MSGHEADERS:
        case _CF_MIMESTRUCT:
          for (;i > 1; ++s, --i) {
            if (s[0] == '\n' && s[1] == '\n') {
            *s = 0;
            break;
            }
          }
          break;
        default:
          *p = 0;
          i -= (p - s);
          s = p;
          break;
        }
      }

      return cfp;
}


void query2 __((FILE *, FILE*));
void query2(fpi, fpo)
      FILE *fpi, *fpo;
{
      int  len, i;
      int bufsize = 0;
      int bufspace = 0;
      char *challenge = NULL;
      char *buf = NULL;
      MD5_CTX CTX;
      unsigned char digbuf[16];
      struct ctlfile *cfp = NULL;

      /* Authenticate the query - get challenge */
      bufsize = 0;
      if (GETLINE(challenge, bufsize, bufspace, fpi))
        return;

      MD5Init(&CTX);
      MD5Update(&CTX, (const void *)challenge,  strlen(challenge));
      MD5Update(&CTX, (const void *)v2password, strlen(v2password));
      MD5Final(digbuf, &CTX);
      
      fprintf(fpo, "AUTH %s ", v2username);
      for (i = 0; i < 16; ++i) fprintf(fpo,"%02x",digbuf[i]);
      fprintf(fpo, "\n");
      if (fflush(fpo) || ferror(fpo)) {
          perror("login to scheduler command interface failed");
          return;
      }

      bufsize = 0;
      if (GETLINE(buf, bufsize, bufspace, fpi))
          return;

      if (*buf != '+') {
        fprintf(stdout,"User '%s' not accepted to server '%s'; err='%s'\n",
              v2username, host ? host : "<NO-HOST-?>", buf+1);
        return;
      }

      if (schedq) {

        switch (schedq) {
        case 4:
          strcpy(buf,"SHOW COUNTERS\n");
          break;
        case 3:
          strcpy(buf,"SHOW SNMP\n");
          break;
        case 2:
          strcpy(buf,"SHOW QUEUE SHORT\n");
          break;
        case 1:
          strcpy(buf,"SHOW QUEUE THREADS\n");
          break;
        default:
          fprintf(stdout, "Bad -Q... command\n");
          return;
        }

        len = strlen(buf);

        if (fwrite(buf,1,len,fpo) != len || fflush(fpo)) {
          perror("write to scheduler command interface failed");
          return;
        }

        bufsize = 0;
        if (GETLINE(buf, bufsize, bufspace, fpi))
          return;

        if (*buf != '+') {

          fprintf(stdout,"Scheduler response: '%s'\n",buf);

        } else {

          for (;;) {
            bufsize = 0;
            if (GETLINE(buf, bufsize, bufspace, fpi))
            break;
            if (buf[0] == '.' && buf[1] == 0)
            break;
            /* Do leading dot duplication suppression */
            fprintf(stdout,"%s\n",((*buf == '.') ? buf+1 : buf));
          }

        }

        fprintf(fpo, "QUIT\n");
        fflush(fpo);

        close(FILENO(fpi));

      } else {

        /* Non -Q* -mode processing */

        int linespace = 256;
        int linecnt   = 0;
        char **lines = (char **) malloc(sizeof(char *) * linespace);
        int threadspace = 256;
        int threadcnt   = 0;
        threadtype *threads = (threadtype *) malloc(sizeof(threadtype) *
                                          threadspace);

        if (channel_opt && host_opt) {

          int i = strlen(channel_opt) + strlen(host_opt);
          lines[linecnt] = malloc(i+2);
          sprintf(lines[linecnt], "%s\t%s", channel_opt, host_opt);
          ++linecnt;

        } else {

          fprintf(fpo, "SHOW QUEUE THREADS2\n");
          fflush(fpo);

          bufsize = 0;
          if (GETLINE(buf, bufsize, bufspace, fpi))
            return;

          if (*buf != '+') {
            fprintf(stdout,"Scheduler response: '%s'\n",buf);
            return;
          }

          for (;;) {
            char *b;
            bufsize = 0;
            if (GETLINE(buf, bufsize, bufspace, fpi))
            break;
            if (buf[0] == '.' && buf[1] == 0)
            break;

            if (linecnt+1 >= linespace) {
            linespace *= 2;
            lines = (char **)realloc((void**)lines,
                               sizeof(char *) * linespace);
            }

            /* Do leading dot duplication suppression */
            b = buf;
            if (*b == '.') {
            --bufsize;
            ++b;
            }

            lines[linecnt] = malloc(bufsize+2);
            memcpy(lines[linecnt], b, bufsize+1);
            ++linecnt;

            /* fprintf(stdout,"%s\n", b); */
          }
        }

        lines[linecnt] = NULL;

        for (i = 0; lines[i] != NULL; ++i) {
          char *channel = lines[i];
          char *host    = strchr(channel, '\t');
          char *rest    = "";
          char *b;

          if (host) {
            *host++ = 0;
            rest = strchr(host,'\t');
            if (rest) *rest++ = 0;
          } else host = "";

          fprintf(fpo, "SHOW THREAD %s %s\n",channel,host);
          fflush(fpo);

          bufsize = 0;
          if (GETLINE(buf, bufsize, bufspace, fpi))
            break; /* Response */

          if (*buf != '+') {
            fprintf(stdout,"Scheduler response: '%s'\n",buf);
            break;
          }

          for (;;) {

            bufsize = 0;
            if (GETLINE(buf, bufsize, bufspace, fpi))
            break;
            if (buf[0] == '.' && buf[1] == 0)
            break;

            /* Do leading dot duplication suppression */
            b = buf;
            if (*b == '.') {
            --bufsize;
            ++b;
            }

            if (threadcnt+2 >= threadspace) {
            threadspace *= 2;
            threads = (threadtype *)realloc((void*)threads,
                                    sizeof(threadtype) *
                                    threadspace);
            }

            threads[threadcnt].channel = channel;
            threads[threadcnt].host    = host;
            threads[threadcnt].line    = malloc(bufsize + 2);
            memcpy(threads[threadcnt].line, b, bufsize+1);
            ++threadcnt;
          }
          
        }

        threads[threadcnt].channel = NULL;
        threads[threadcnt].host    = NULL;
        threads[threadcnt].line    = NULL;

        fprintf(fpo, "QUIT\n");
        fflush(fpo);

        close(FILENO(fpi));

        for (i = 0; threads[i].line != NULL; ++i) {
          static const char *channel = NULL;
          static const char *host    = NULL;

          int j;
          char *split[11], *s, *ocp, *b;
          char timebuf[30];

          if (channel != threads[i].channel ||
            host    != threads[i].host) {

            channel = threads[i].channel;
            host    = threads[i].host;

            printf("%s/%s:\n",channel, host);

          }

          b = threads[i].line;


          /* Array elts:
             0) filepath under $POSTOFFICE/transport/
             1) number WITHIN a group of recipients
             2) error address in brackets
             3) recipient line offset within the control file
             4) message expiry time (time_t)
             5) next wakeup time (time_t)
             6) last feed time (time_t)
             7) count of attempts at the delivery
             8) "retry in NNN" or a pending on "channel"/"thread"
             9) possible diagnostic message from previous delivery attempt
          */

          for (j = 0; b && j < 10; ++j) {
            split[j] = b;
            if (j == 1) {
            /* The 'number within group' got added here
               after the rest of the interface was working. */
            if (!('0' <= *b && *b <= '9')) {
              split[1] = "0";
              ++j;
              split[j] = b;
            }
            }
            b = strchr(b, '\t');
            if (b) *b++ = 0;
          }
            
          if (j != 10) {
            fprintf(stderr,"Communication error! Malformed data entry!\n");
            continue;
          }

          j = atoi(split[1]);

          if (j == 0) {

            printf("\t%s: (", split[0]);

            if (!verbose) {
            /* First recipient in the group */
            printf("%s tries, ", split[7]);

            *timebuf = 0;
            saytime((long)(atol(split[4]) - now), timebuf, 1);

            printf("expires in %s)", timebuf);

            printf(" %s\n", split[9]);
            } /* !verbose */
          }


          if (verbose) {
            if (j == 0) {
            if (cfp) free(cfp);
            cfp = readmq2cfp(split[0]);
            }
            if (cfp) {
            if (j == 0) {
              /* First recipient in the group */

              *timebuf = 0;
              saytime((long)(now - cfp->mtime), timebuf, 1);

              printf("%s tries, age %s, ", split[7], timebuf);

              *timebuf = 0;
              saytime((long)(atol(split[4]) - now), timebuf, 1);

              printf("expires in %s, %ld+%ld bytes)", timebuf,
                   (long)cfp->nlines, (long)cfp->msgbodyoffset);

              printf("\n");

              if (cfp->logident)
                printf("\t  id\t%s\n", cfp->logident);

              printf("\t  from\t%s\n", *split[2] ? split[2] : "<>");
            }

            s = cfp->contents + atoi(split[3]) +2;
            if (s > (cfp->contents + cfp->nlines)) {
              printf("\t\tto-ptr bad; split[3]='%s'\n",split[3]);
              continue; /* BAD! */
            }

            if (*s == ' ' || (*s >= '0' && *s <= '9'))
              s += _CFTAG_RCPTPIDSIZE;

            if ((cfp->format & _CF_FORMAT_DELAY1) || *s == ' ' ||
                (*s >= '0' && *s <= '9')) {
              /* Newer DELAY data slot - _CFTAG_RCPTDELAYSIZE bytes */
              s += _CFTAG_RCPTDELAYSIZE;
            }

            s = skip821address(s); /* skip channel */
            while (*s == ' ' || *s == '\t') ++s;
            s = skip821address(s); /* skip host */
            while (*s == ' ' || *s == '\t') ++s;

            ocp = s;
            s = skip821address(s); /* skip user */
            *s++ = 0;
            fprintf(stdout,"\t  to\t%s",ocp);

            /* XXX: ORCPT data! */
            

            fprintf(stdout,"\n");

            } else /* not have cfp */ {
            /* Can't show 'message-id', nor 'to' addresses,
               but have 'from'! */
            if (j == 0) {
              /* First recipient in the group */
              printf("\t  from\t%s\n", *split[2] ? split[2] : "<>");
            }
            }

            /* remember to show the diagnostics */

            /* Show all CR separated sub-lines as their OWN 'diag' lines! */

            s = split[9];
            while (*s == '\r') ++s;
            while (*s) {
            printf("\t  diag\t");
            for (;*s && *s != '\r'; ++s) putchar(*s);
            putchar('\n');
            while (*s == '\r') ++s;
            }

          } /* verbose */

        } /* all recipients towards each host */
        /* all channel/host pairs */        

      } /* No -Q processing */

      free(cfp);
}

void
report(fpi,fpo)
     FILE *fpi, *fpo;
{
      int rc = parse(fpi);
      if (rc == 0)
        return;
      if (rc == 2) {
        query2(fpi,fpo);
        return;
      }

      if (schedq) {
        /* Old-style processing */
        int prevc = -1;
        int linesuppress = 0;
        while (!ferror(fpi)) {
          int c = getc(fpi);
          if (c == EOF)
            break;
          if (prevc == '\n') {
            linesuppress = 0;
            if (c == ' ' && schedq > 1)
            linesuppress = 1;
            fflush(stdout);
          }
          if (!linesuppress)
            putc(c,stdout);
          prevc = c;
        }
        fflush(stdout);
        return;
      }

      r_i = 0;
      sp_scan(repscan_v1, (struct spblk *)NULL, spt_ids[L_CHANNEL]);
      if (!r_i) {
        if (onlyuser)
          fprintf(stdout,"No user messages found\n");
        else
          if (schedq == 0)
            fprintf(stdout,"Transport queue is empty -- or scheduler uses -Q -mode\n");
          else
            fprintf(stdout,"Transport queue is empty\n");
      }
}

void
printaddrs(v)
     struct vertex *v;
{
      register char *cp;
      int   i, fd;
      struct stat stbuf;
      char *ocp;

      if (v->cfp->contents == NULL) {
        sprintf(path, "%s/%s/%s", postoffice, TRANSPORTDIR, v->cfp->mid);
        if ((fd = open(path, O_RDONLY, 0)) < 0) {
#if 0
          fprintf(stdout,"\t\t%s: %s\n", path, strerror(errno));
#endif
          return;
        }
        if (fstat(fd, &stbuf) < 0) {
          fprintf(stdout,"\t\tfstat(%s): %s\n", path, strerror(errno));
          close(fd);
          return;
        }
        v->cfp->contents = malloc((u_int)stbuf.st_size);
        if (v->cfp->contents == NULL) {
          fprintf(stdout,"\t\tmalloc(%d): out of memory!\n", (int)stbuf.st_size);
          close(fd);
          return;
        }
        errno = 0;
        if (read(fd, v->cfp->contents, stbuf.st_size) < stbuf.st_size){
          fprintf(stdout,"\t\tread(%d): %s\n", (int)stbuf.st_size,
                errno == 0 ? "failed" : strerror(errno));
          close(fd);
          return;
        }
        close(fd);
        for (cp = v->cfp->contents, i = 0;
             cp < v->cfp->contents + stbuf.st_size - 1; ++cp) {
          if (*cp == '\n') {
            *cp = '\0';
            if (*++cp == _CF_SENDER)
            break;
            switch (*cp) {
            case _CF_FORMAT:
            ++cp;
            v->cfp->format = 0;
            sscanf(cp,"%i",&v->cfp->format);
            if (v->cfp->format & (~_CF_FORMAT_KNOWN_SET))
              fprintf(stdout, "Unsupported SCHEDULER file format flags seen: 0x%x at file '%s'",
                    v->cfp->format, path);
            break;
            case _CF_LOGIDENT:
            v->cfp->logident = cp + 2;
            break;
            case _CF_ERRORADDR:
            /* overload cfp->mark to be from addr*/
            v->cfp->mark = cp+2 - v->cfp->contents;
            break;
            }
          }
        }
        if (verbose > 1 && status < 2) {
          sprintf(path, "%s/%s/%s", postoffice, QUEUEDIR, v->cfp->mid);
          if (stat(path, &stbuf) == 0) {
            /* overload offset[] to be size of message */
            v->cfp->offset[0] = stbuf.st_size;
            v->cfp->mtime     = stbuf.st_mtime;
          } else {
            v->cfp->offset[0] = 0;
            v->cfp->mtime     = 0;
          }
        }
      }
      if (summary)
        return;
      if (v->cfp->logident)
        fprintf(stdout,"\t  id\t%s", v->cfp->logident);
      if (verbose > 1 && v->cfp->offset[0] > 0) {
        long dt = now - v->cfp->mtime;
        int fields = 3;
        fprintf(stdout,", %ld bytes, age ", (long)v->cfp->offset[0]);
        /* age (now-mtime) printout */
        if (dt > (24*3600)) { /* Days */
          fprintf(stdout,"%dd", (int)(dt /(24*3600)));
          dt %= (24*3600);
          --fields;
        }
        if (dt > 3600) {
          fprintf(stdout,"%dh",(int)(dt/3600));
          dt %= 3600;
          --fields;
        }
        if (dt > 60 && fields > 0) {
          fprintf(stdout,"%dm",(int)(dt/60));
          dt %= 60;
          --fields;
        }
        if (fields > 0) {
          fprintf(stdout,"%ds",(int)dt);
        }
      }
      fprintf(stdout,"\n");
      if (v->cfp->mark > 0)
        fprintf(stdout,"\t  from\t%s\n", v->cfp->contents + v->cfp->mark);
      for (i = 0; i < v->ngroup; ++i) {
        cp = v->cfp->contents + v->index[i] + 2;
        if (*cp == ' ' || (*cp >= '0' && *cp <= '9'))
          cp += _CFTAG_RCPTPIDSIZE;

        if ((v->cfp->format & _CF_FORMAT_DELAY1) || *cp == ' ' ||
            (*cp >= '0' && *cp <= '9')) {
          /* Newer DELAY data slot - _CFTAG_RCPTDELAYSIZE bytes */
          cp += _CFTAG_RCPTDELAYSIZE;
        }

        cp = skip821address(cp); /* skip channel */
        while (*cp == ' ' || *cp == '\t') ++cp;
        cp = skip821address(cp); /* skip host */
        while (*cp == ' ' || *cp == '\t') ++cp;

        ocp = cp;
        cp = skip821address(cp); /* skip user */
        *cp++ = 0;
        fprintf(stdout,"\t");
        if (i == 0)
          fprintf(stdout,"  to");
        fprintf(stdout,"\t%s\n",ocp);
      }
}


static void print_shm __((void))
{
  int r, i;

  r = Z_SHM_MIB_Attach (0); /* Attach read-only! */

  if (r < 0) {
    /* Error processing -- magic set of constants: */
    switch (r) {
    case -1:
      fprintf(stderr, "No ZENV variable: SNMPSHAREDFILE\n");
      break;
    case -2:
      perror("Failed to open for exclusively creating of the SHMSHAREDFILE");
      break;
    case -3:
      perror("Failure during creation fill of SGMSHAREDFILE");
      break;
    case -4:
      perror("Failed to open the SHMSHAREDFILE at all");
      break;
    case -5:
      perror("The SHMSHAREDFILE isn't of proper size! ");
      break;
    case -6:
      perror("Failed to mmap() of SHMSHAREDFILE into memory");
      break;
    case -7:
      fprintf(stderr, "The SHMSHAREDFILE  has magic value mismatch!\n");
      break;
    default:
      break;
    }
    return;
  }

  r = (Z_SHM_MIB_is_attached() > 0); /* Attached and WRITABLE ? */


#define sfprintf fprintf
#define fp       stdout

#include "mailq.inc"  /* shared stuff with  mq2.c  module */

      exit(0);
}

Generated by  Doxygen 1.6.0   Back to index