Logo Search packages:      
Sourcecode: mailutils version File versions

main.c

/* GNU Mailutils -- a suite of utilities for electronic mail
   Copyright (C) 1999, 2000, 2001, 2002, 2005 Free Software Foundation, Inc.

   GNU Mailutils is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   GNU Mailutils is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with GNU Mailutils; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA  */

#include <mail.local.h>

int multiple_delivery;     /* Don't return errors when delivering to multiple
                        recipients */
int ex_quota_tempfail;     /* Return temporary failure if mailbox quota is
                        exceeded. If this variable is not set, mail.local
                        will return "service unavailable" */
int exit_code = EX_OK;     /* Exit code to be used */
uid_t uid;                 /* Current user name */
char *quotadbname = NULL;  /* Name of mailbox quota database */
char *quota_query = NULL;  /* SQL query to retrieve mailbox quota */

/* Debuggig options */
int debug_level;           /* General debugging level */ 
int debug_flags;           /* Mailutils debugging flags */
int sieve_debug_flags;     /* Sieve debugging flags */
int sieve_enable_log;      /* Enables logging of executed Sieve actions */
char *message_id_header;   /* Use the value of this header as message
                        identifier when logging Sieve actions */
mu_debug_t mudebug;        /* Mailutils debugging object */


#define MAXFD 64
#define EX_QUOTA() (ex_quota_tempfail ? EX_TEMPFAIL : EX_UNAVAILABLE)

void close_fds __P((void));
int make_tmp __P((const char *from, mailbox_t *mbx));
void deliver __P((mailbox_t msg, char *name));
void guess_retval __P((int ec));
void mailer_err __P((char *fmt, ...));
void notify_biff __P((mailbox_t mbox, char *name, size_t size));

const char *program_version = "mail.local (" PACKAGE_STRING ")";
static char doc[] =
/* TRANSLATORS: Please, preserve the vertical tabulation (^K character)
   in this message */
N_("GNU mail.local -- the local MDA\
\v\
Debug flags are:\n\
  g - guimb stack traces\n\
  T - mailutils traces (MU_DEBUG_TRACE)\n\
  P - network protocols (MU_DEBUG_PROT)\n\
  t - sieve trace (MU_SIEVE_DEBUG_TRACE)\n\
  i - sieve instructions trace (MU_SIEVE_DEBUG_INSTR)\n\
  l - sieve action logs\n\
  0-9 - Set mail.local debugging level\n");

static char args_doc[] = N_("recipient [recipient ...]");

#define ARG_MULTIPLE_DELIVERY 1
#define ARG_QUOTA_TEMPFAIL    2
#define ARG_MESSAGE_ID_HEADER 3
#define ARG_QUOTA_QUERY       4

static struct argp_option options[] = 
{
  { "ex-multiple-delivery-success", ARG_MULTIPLE_DELIVERY, NULL, 0,
    N_("Do not return errors when delivering to multiple recipients"), 0 },
  { "ex-quota-tempfail", ARG_QUOTA_TEMPFAIL, NULL, 0,
    N_("Return temporary failure if disk or mailbox quota is exceeded"), 0 },
  { "from", 'f', N_("EMAIL"), 0,
    N_("Specify the sender's name") },
  { NULL, 'r', NULL, OPTION_ALIAS, NULL },
#ifdef USE_DBM
  { "quota-db", 'q', N_("FILE"), 0,
    N_("Specify path to quota DBM database"), 0 },
#endif
#ifdef USE_SQL
  { "quota-query", ARG_QUOTA_QUERY, N_("STRING"), 0,
    N_("SQL query to retrieve mailbox quota"), 0 },
#endif
  { "sieve", 'S', N_("PATTERN"), 0,
    N_("Set name pattern for user-defined Sieve mail filters"), 0 },
  { "message-id-header", ARG_MESSAGE_ID_HEADER, N_("STRING"), 0,
    N_("Identify messages by the value of this header when logging Sieve actions"), 0 },
#ifdef WITH_GUILE
  { "source", 's', N_("PATTERN"), 0,
    N_("Set name pattern for user-defined Scheme mail filters"), 0 },
#endif
  { "debug", 'x', N_("FLAGS"), 0,
    N_("Enable debugging"), 0 },
  { NULL,      0, NULL, 0, NULL, 0 }
};

static error_t parse_opt (int key, char *arg, struct argp_state *state);

static struct argp argp = {
  options,
  parse_opt,
  args_doc, 
  doc,
  NULL,
  NULL, NULL
};

static const char *argp_capa[] = {
  "auth",
  "common",
  "license",
  "logging",
  "mailbox",
  "mailer",
  "sieve",
  NULL
};

char *from = NULL;
char *progfile_pattern = NULL;
char *sieve_pattern = NULL;

#define D_DEFAULT "9s"

static error_t
parse_opt (int key, char *arg, struct argp_state *state)
{
  switch (key)
    {
    case ARG_MULTIPLE_DELIVERY:
      multiple_delivery = 1;
      break;

    case ARG_QUOTA_TEMPFAIL:
      ex_quota_tempfail = 1;
      break;

    case ARG_MESSAGE_ID_HEADER:
      message_id_header = arg;
      break;

    case ARG_QUOTA_QUERY:
      quota_query = arg;
      break;
      
    case 'r':
    case 'f':
      if (from != NULL)
      {
        argp_error (state, _("Multiple --from options"));
        return EX_USAGE;
      }
      from = arg;
      break;
      
#ifdef USE_DBM
    case 'q':
      quotadbname = arg;
      break;
#endif

#ifdef WITH_GUILE 
    case 's':
      progfile_pattern = arg;
      break;
#endif

    case 'S':
      sieve_pattern = arg;
      break;
      
    case 'x':
      do
      {
        if (!arg)
          arg = D_DEFAULT;
        switch (*arg)
          {
          case 'g':
#ifdef WITH_GUILE
            debug_guile = 1;
#endif
            break;

          case 't':
            sieve_debug_flags |= MU_SIEVE_DEBUG_TRACE;
            break;

          case 'i':
            sieve_debug_flags |= MU_SIEVE_DEBUG_INSTR;
            break;
            
          case 'l':
            sieve_enable_log = 1;
            break;
            
          case 'T':
            debug_flags |= MU_DEBUG_TRACE;
            break;

          case 'P':
            debug_flags |= MU_DEBUG_PROT;
            break;

          default:
            if (isdigit (*arg))
            debug_level = *arg - '0';
            else
            argp_error (state, _("%c is not a valid debug flag"), *arg);
            break;
          }
      }
      while (*++arg);
      break;
      
    default:
      return ARGP_ERR_UNKNOWN;

    case ARGP_KEY_ERROR:
      exit (EX_USAGE);
    }
  return 0;
}


static int
_mu_debug_printer (mu_debug_t unused, size_t level, const char *fmt,
               va_list ap)
{
  vsyslog ((level == MU_DEBUG_ERROR) ? LOG_ERR : LOG_DEBUG, fmt, ap);
  return 0;
}

static int
_sieve_debug_printer (void *unused, const char *fmt, va_list ap)
{
  vsyslog (LOG_DEBUG, fmt, ap);
  return 0;
}

static void
_sieve_action_log (void *user_name,
               const sieve_locus_t *locus, size_t msgno, message_t msg,
               const char *action, const char *fmt, va_list ap)
{
  char *text = NULL;
  
  if (message_id_header)
    {
      header_t hdr = NULL;
      char *val = NULL;
      message_get_header (msg, &hdr);
      if (header_aget_value (hdr, message_id_header, &val) == 0
        || header_aget_value (hdr, MU_HEADER_MESSAGE_ID, &val) == 0)
      {
        asprintf (&text, _("%s:%lu: %s on msg %s"),
                locus->source_file,
                (unsigned long) locus->source_line,
                action, val);
        free (val);
      }
    }
  if (text == NULL)
    {
      size_t uid = 0;
      message_get_uid (msg, &uid);
      asprintf (&text, _("%s:%lu: %s on msg uid %d"),
            locus->source_file,
            (unsigned long)   locus->source_line,
            action, uid);
    }
  
  if (fmt && strlen (fmt))
    {
      char *diag = NULL;
      vasprintf (&diag, fmt, ap);
      syslog (LOG_NOTICE, _("(user %s) %s: %s"), (char*) user_name, text, diag);
      free (diag);
    }
  else
    syslog (LOG_NOTICE, _("(user %s) %s"), (char*) user_name, text);
  free (text);
}

static int
_sieve_parse_error (void *user_name, const char *filename, int lineno,
                const char *fmt, va_list ap)
{
  char *text;
  vasprintf (&text, fmt, ap);
  if (filename)
    {
      char *loc;
      asprintf (&loc, "%s:%d: ", filename, lineno);
      syslog (LOG_ERR, "%s: %s", loc, text);
      free (loc);
    }
  else
    syslog (LOG_ERR, _("(user %s) %s"), (char*)user_name, text);
  free (text);
  return 0;
}

int
main (int argc, char *argv[])
{
  mailbox_t mbox = NULL;
  int arg_index;
  
  /* Preparative work: close inherited fds, force a reasonable umask
     and prepare a logging. */
  close_fds ();
  umask (0077);

  /* Native Language Support */
  mu_init_nls ();

  /* Default locker settings */
  locker_set_default_flags (MU_LOCKER_PID|MU_LOCKER_RETRY,
                      mu_locker_set_flags);
  locker_set_default_retry_timeout (1);
  locker_set_default_retry_count (300);

  /* Default error code for command line errors */
  mu_argp_error_code = EX_CONFIG;
  /* Register needed modules */
  MU_AUTH_REGISTER_ALL_MODULES();
  mu_argp_init (program_version, NULL);
  sieve_argp_init ();
  /* Parse command line */
  mu_argp_parse (&argp, &argc, &argv, 0, argp_capa, &arg_index, NULL);
  
  openlog ("mail.local", LOG_PID, log_facility);
  mu_error_set_print (mu_syslog_error_printer);
  if (debug_flags)
    {
      int rc;
      
      if ((rc = mu_debug_create (&mudebug, NULL)))
      {
        mu_error (_("mu_debug_create failed: %s\n"), mu_strerror (rc));
        exit (EX_TEMPFAIL);
      }
      if ((rc = mu_debug_set_level (mudebug, debug_flags)))
      {
        mu_error (_("mu_debug_set_level failed: %s\n"),
                mu_strerror (rc));
        exit (EX_TEMPFAIL);
      }
      if ((rc = mu_debug_set_print (mudebug, _mu_debug_printer, NULL)))
      {
        mu_error (_("mu_debug_set_print failed: %s\n"),
                mu_strerror (rc));
        exit (EX_TEMPFAIL);
      }
    }
  
  uid = getuid ();

  argc -= arg_index;
  argv += arg_index;

  if (!argc)
    {
      mu_error (_("Missing arguments. Try --help for more info."));
      return EX_USAGE;
    }

  /* Register local mbox formats. */
  {
    list_t bookie;
    registrar_get_list (&bookie);
    list_append (bookie, mbox_record); 
    list_append (bookie, path_record);
    /* Possible supported mailers.  */
    list_append (bookie, sendmail_record);
    list_append (bookie, smtp_record);
  }

  if (make_tmp (from, &mbox))
    exit (exit_code);
  
  if (multiple_delivery)
    multiple_delivery = argc > 1;

#ifdef WITH_GUILE
  if (progfile_pattern)
    {
      struct mda_data mda_data;
      
      memset (&mda_data, 0, sizeof mda_data);
      mda_data.mbox = mbox;
      mda_data.argv = argv;
      mda_data.progfile_pattern = progfile_pattern;
      return prog_mda (&mda_data);
    }
#endif
  
  for (; *argv; argv++)
    mda (mbox, *argv);
  return exit_code;
}

int
sieve_test (struct mu_auth_data *auth, mailbox_t mbx)
{
  int rc = 1;
  char *progfile;
    
  if (!sieve_pattern)
    return 1;

  progfile = mu_expand_path_pattern (sieve_pattern, auth->name);
  if (access (progfile, R_OK))
    {
      if (debug_level > 2)
      syslog (LOG_DEBUG, _("Access to %s failed: %m"), progfile);
    }
  else
    {
      sieve_machine_t mach;
      rc = sieve_machine_init (&mach, auth->name);
      if (rc)
      {
        mu_error (_("Cannot initialize sieve machine: %s"),
                mu_strerror (rc));
      }
      else
      {
        sieve_set_debug (mach, _sieve_debug_printer);
        sieve_set_debug_level (mach, mudebug, sieve_debug_flags);
        sieve_set_parse_error (mach, _sieve_parse_error);
        if (sieve_enable_log)
          sieve_set_logger (mach, _sieve_action_log);
        
        rc = sieve_compile (mach, progfile);
        if (rc == 0)
          {
            attribute_t attr;
            message_t msg = NULL;
            
            mailbox_get_message (mbx, 1, &msg);
            message_get_attribute (msg, &attr);
            attribute_unset_deleted (attr);
            if (switch_user_id (auth, 1) == 0)
            {
              chdir (auth->dir);
            
              rc = sieve_message (mach, msg);
              if (rc == 0)
                rc = attribute_is_deleted (attr) == 0;

              switch_user_id (auth, 0);
              chdir ("/");
            }
            sieve_machine_destroy (&mach);
          }
      }
    }
  free (progfile);
  return rc;
}

int
mda (mailbox_t mbx, char *username)
{
  deliver (mbx, username);

  if (multiple_delivery)
    exit_code = EX_OK;

  return exit_code;
}

void
close_fds ()
{
  int i;
  long fdlimit = MAXFD;

#if defined (HAVE_SYSCONF) && defined (_SC_OPEN_MAX)
  fdlimit = sysconf (_SC_OPEN_MAX);
#elif defined (HAVE_GETDTABLESIZE)
  fdlimit = getdtablesize ();
#endif

  for (i = 3; i < fdlimit; i++)
    close (i);
}

int
switch_user_id (struct mu_auth_data *auth, int user)
{
  int rc;
  uid_t uid;
  
  if (!auth || auth->change_uid == 0)
    return 0;
  
  if (user)
    uid = auth->uid;
  else
    uid = 0;
  
#if defined(HAVE_SETREUID)
  rc = setreuid (0, uid);
#elif defined(HAVE_SETRESUID)
  rc = setresuid (-1, uid, -1);
#elif defined(HAVE_SETEUID)
  rc = seteuid (uid);
#else
# error "No way to reset user privileges?"
#endif
  if (rc < 0)
    mailer_err ("setreuid(0, %d): %s (r=%d, e=%d)",
            uid, strerror (errno), getuid (), geteuid ());
  return rc;
}

static int
tmp_write (stream_t stream, off_t *poffset, char *buf, size_t len)
{
  size_t n = 0;
  int status = stream_write (stream, buf, len, *poffset, &n);
  if (status == 0 && n != len)
    status = EIO;
  *poffset += n;
  return status;
}

int
make_tmp (const char *from, mailbox_t *mbox)
{
  stream_t stream;
  char *buf = NULL;
  size_t n = 0;
  off_t offset = 0;
  size_t line;
  int status;
  static char *newline = "\n";
  char *tempfile;
  
  tempfile = mu_tempname (NULL);
  if ((status = file_stream_create (&stream, tempfile, MU_STREAM_RDWR)))
    {
      mailer_err (_("Unable to open temporary file: %s"), mu_strerror (status));
      exit (exit_code);
    }

  if ((status = stream_open (stream)))
    {
      mailer_err (_("unable to open temporary file: %s"), mu_strerror (status));
      exit (exit_code);
    }
  
  line = 0;
  while (getline (&buf, &n, stdin) > 0)
    {
      line++;
      if (line == 1)
      {
        if (memcmp (buf, "From ", 5))
          {
            struct mu_auth_data *auth = NULL;
            if (!from)
            {
              auth = mu_get_auth_by_uid (uid);
              if (auth)
                from = auth->name;
            }
            if (from)
            {
              time_t t;
              char *ptr;
              
              time (&t);
              asprintf (&ptr, "From %s %s", from, ctime (&t));
              status = tmp_write (stream, &offset, ptr, strlen (ptr));
              free (ptr);
            }
            else
            {
              mailer_err (_("Cannot determine sender address"));
              exit (EX_UNAVAILABLE);
            }
            if (auth)
            mu_auth_data_free (auth);
          }
      }
      else if (!memcmp (buf, "From ", 5))
      {
        static char *escape = ">";
        status = tmp_write (stream, &offset, escape, 1);
      }

      if (!status)
      status = tmp_write (stream, &offset, buf, strlen (buf));
      
      if (status)
      {
        mailer_err (_("Error writing temporary file: %s"), mu_strerror (status));
        stream_destroy (&stream, stream_get_owner (stream));
        return status;
      }
    }
  
  if (buf && strchr (buf, '\n') == NULL)
    status = tmp_write (stream, &offset, newline, 1);

  status = tmp_write (stream, &offset, newline, 1);
  free (buf);
  unlink (tempfile);
  free (tempfile);
  
  if (status)
    {
      errno = status;
      mailer_err (_("Error writing temporary file: %s"), mu_strerror (status));
      stream_destroy (&stream, stream_get_owner (stream));
      return status;
    }

  stream_flush (stream);
  if ((status = mailbox_create (mbox, "/dev/null")) 
      || (status = mailbox_open (*mbox, MU_STREAM_READ))
      || (status = mailbox_set_stream (*mbox, stream)))
    {
      mailer_err (_("Error opening temporary file: %s"), mu_strerror (status));
      stream_destroy (&stream, stream_get_owner (stream));
      return status;
    }

  status = mailbox_messages_count (*mbox, &n);
  if (status)
    {
      errno = status;
      mailer_err (_("Error creating temporary message: %s"),
              mu_strerror (status));
      stream_destroy (&stream, stream_get_owner (stream));
      return status;
    }

  return status;
}

void
deliver (mailbox_t imbx, char *name)
{
  mailbox_t mbox;
  char *path;
  url_t url = NULL;
  locker_t lock;
  struct mu_auth_data *auth;
  int status;
  stream_t istream, ostream;
  off_t size;
  int failed = 0;
  
  auth = mu_get_auth_by_name (name);
  if (!auth)
    {
      mailer_err (_("%s: no such user"), name);
      exit_code = EX_UNAVAILABLE;
      return;
    }

  if (!sieve_test (auth, imbx))
    {
      exit_code = EX_OK;
      mu_auth_data_free (auth);
      return;
    }

  if ((status = mailbox_get_stream (imbx, &istream)) != 0)
    {
      mailer_err (_("Cannot get input message stream: %s"),
              mu_strerror (status));
      mu_auth_data_free (auth);
      return;
    }
  
  if ((status = mailbox_create (&mbox, auth->mailbox)) != 0)
    {
      mailer_err (_("Cannot open mailbox %s: %s"),
              auth->mailbox, mu_strerror (status));
      mu_auth_data_free (auth);
      return;
    }

  mailbox_get_url (mbox, &url);
  path = (char*) url_to_string (url);

  /* Actually open the mailbox. Switch to the user's euid to make
     sure the maildrop file will have right privileges, in case it
     will be created */
  if (switch_user_id (auth, 1))
    return;
  status = mailbox_open (mbox, MU_STREAM_RDWR|MU_STREAM_CREAT);
  if (switch_user_id (auth, 0))
    return;
  if (status != 0)
    {
      mailer_err (_("Cannot open mailbox %s: %s"), path, mu_strerror (status));
      mailbox_destroy (&mbox);
      return;
    }
  
  mailbox_get_locker (mbox, &lock);

  status = locker_lock (lock);

  if (status)
    {
      mailer_err (_("Cannot lock mailbox `%s': %s"), path, mu_strerror (status));
      mailbox_destroy (&mbox);
      exit_code = EX_TEMPFAIL;
      return;
    }

  if ((status = mailbox_get_stream (mbox, &ostream)))
    {
      mailer_err (_("Cannot get stream for mailbox %s: %s"),
              path, mu_strerror (status));
      mailbox_destroy (&mbox);
      return;
    }

  if ((status = stream_size (ostream, &size)))
    {
      mailer_err (_("Cannot get stream size (mailbox %s): %s"),
              path, mu_strerror (status));
      mailbox_destroy (&mbox);
      return;
    }

#if defined(USE_MAILBOX_QUOTAS)
  {
    size_t n;
    off_t isize;

    switch (check_quota (name, size, &n))
      {
      case MQUOTA_EXCEEDED:
      mailer_err (_("%s: mailbox quota exceeded for this recipient"), name);
      exit_code = EX_QUOTA();
      failed++;
      break;
      
      case MQUOTA_UNLIMITED:
      break;
      
      default:
      if ((status = stream_size (istream, &isize)))
        {
          mailer_err (_("Cannot get stream size (input message): %s"),
                  path, mu_strerror (status));
          exit_code = EX_UNAVAILABLE;
          failed++;
        }
      else if (isize > n)
        {
          mailer_err (_("%s: message would exceed maximum mailbox size for this recipient"),
                  name);
          exit_code = EX_QUOTA();
          failed++;
        }
      break;
      }
  }
#endif
  
  if (!failed && switch_user_id (auth, 1) == 0)
    {
      off_t ioff = 0;
      off_t off = size;
      size_t nwr, nrd;
      char *buf = NULL;
      off_t bufsize = 1024;

      stream_size (istream, &bufsize);
      for (; (buf = malloc (bufsize)) == NULL && bufsize > 1; bufsize /= 2)
      ;
      
      if (!buf)
      {
        status = errno = ENOMEM;
        failed++;
      }
      else
      {
        status = 0;

        while ((status = stream_read (istream, buf, bufsize, ioff, &nrd))
             == 0
             && nrd > 0)
          {
            status = stream_write (ostream, buf, nrd, off, &nwr);
            if (status)
            break;
            ioff += nrd;
            off += nwr;
          }
        
        free (buf);
      }
      
      switch_user_id (auth, 0);

      if (status)
      {
        /* Undo the delivery by truncating the mailbox back to its
           original size */
        int rc = stream_truncate (ostream, size);
        if (rc)
          mailer_err (_("Error writing to mailbox: %s. Mailbox NOT truncated: %s"),
                  mu_strerror (status), mu_strerror (rc));
        else  
          mailer_err (_("Error writing to mailbox: %s"),
                    mu_strerror (status));
      }
    }

  if (!failed)
    notify_biff (mbox, name, size);

  locker_unlock (lock);

  mu_auth_data_free (auth);
  mailbox_close (mbox);
  mailbox_destroy (&mbox);
}

void
notify_biff (mailbox_t mbox, char *name, size_t size)
{
  static int fd = -1;
  url_t url = NULL;
  char *buf = NULL;
  static struct sockaddr_in inaddr;
    
  if (fd == -1)
    {
      struct servent *sp;
      
      if ((sp = getservbyname ("biff", "udp")) == NULL)
      return;

      inaddr.sin_family = AF_INET;
      inaddr.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
      inaddr.sin_port = sp->s_port;
      
      fd = socket (PF_INET, SOCK_DGRAM, 0);
      if (fd < 0)
      fd = -2; /* Mark failed initialization */
    }

  if (fd < 0)
    return;
  
  mailbox_get_url (mbox, &url);
  asprintf (&buf, "%s@%ld:%s", name, size, url_to_string (url));
  if (buf)
    {
      sendto (fd, buf, strlen (buf), 0, (struct sockaddr *)&inaddr,
            sizeof inaddr);
      free (buf);
    }
}

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

  guess_retval (errno);
  va_start (ap, fmt);
  vfprintf (stderr, fmt, ap);
  fprintf (stderr, "\n");
  mu_verror (fmt, ap);
  va_end (ap);
}

int temp_errors[] = {
#ifdef EAGAIN
  EAGAIN, /* Try again */
#endif
#ifdef EBUSY
  EBUSY, /* Device or resource busy */
#endif
#ifdef EPROCLIM
  EPROCLIM, /* Too many processes */
#endif
#ifdef EUSERS
  EUSERS, /* Too many users */
#endif
#ifdef ECONNABORTED
  ECONNABORTED, /* Software caused connection abort */
#endif
#ifdef ECONNREFUSED
  ECONNREFUSED, /* Connection refused */
#endif
#ifdef ECONNRESET
  ECONNRESET, /* Connection reset by peer */
#endif
#ifdef EDEADLK
  EDEADLK, /* Resource deadlock would occur */
#endif
#ifdef EDEADLOCK
  EDEADLOCK, /* Resource deadlock would occur */
#endif
#ifdef EFBIG
  EFBIG, /* File too large */
#endif
#ifdef EHOSTDOWN
  EHOSTDOWN, /* Host is down */
#endif
#ifdef EHOSTUNREACH
  EHOSTUNREACH, /* No route to host */
#endif
#ifdef EMFILE
  EMFILE, /* Too many open files */
#endif
#ifdef ENETDOWN
  ENETDOWN, /* Network is down */
#endif
#ifdef ENETUNREACH
  ENETUNREACH, /* Network is unreachable */
#endif
#ifdef ENETRESET
  ENETRESET, /* Network dropped connection because of reset */
#endif
#ifdef ENFILE
  ENFILE, /* File table overflow */
#endif
#ifdef ENOBUFS
  ENOBUFS, /* No buffer space available */
#endif
#ifdef ENOMEM
  ENOMEM, /* Out of memory */
#endif
#ifdef ENOSPC
  ENOSPC, /* No space left on device */
#endif
#ifdef EROFS
  EROFS, /* Read-only file system */
#endif
#ifdef ESTALE
  ESTALE, /* Stale NFS file handle */
#endif
#ifdef ETIMEDOUT
  ETIMEDOUT,  /* Connection timed out */
#endif
#ifdef EWOULDBLOCK
  EWOULDBLOCK, /* Operation would block */
#endif
};
  

void
guess_retval (int ec)
{
  int i;
  /* Temporary failures override hard errors. */
  if (exit_code == EX_TEMPFAIL)
    return;
#ifdef EDQUOT
  if (ec == EDQUOT)
    {
      exit_code = EX_QUOTA();
      return;
    }
#endif

  for (i = 0; i < sizeof (temp_errors)/sizeof (temp_errors[0]); i++)
    if (temp_errors[i] == ec)
      {
      exit_code = EX_TEMPFAIL;
      return;
      }
  exit_code = EX_UNAVAILABLE;
}


Generated by  Doxygen 1.6.0   Back to index