From 28b12aba4ad1b56c1acfea4e20c1a81b91221d45 Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Mon, 30 Dec 1996 18:27:14 +0000 Subject: Dave Bodenstab's error changes. svn path=/trunk/; revision=705 --- NEWS | 16 ++-- acconfig.h | 4 +- configure.in | 18 +++- driver.c | 24 +++--- fetchmail.c | 34 ++++++-- fetchmail.h | 6 ++ fetchmail.man | 34 +++++++- options.c | 55 +++++++----- report.c | 271 ++++++++++++++++++++++++++++++++++++++++++++++++++++++---- xmalloc.c | 11 +++ 10 files changed, 404 insertions(+), 69 deletions(-) diff --git a/NEWS b/NEWS index f315dbd7..6dae6bf9 100644 --- a/NEWS +++ b/NEWS @@ -1,17 +1,19 @@ To Do: -* The `reading message N ..(M bytes)' messages ideally should go through - error(), but since they are built piecemeal, they go to stderr. What - would be needed is an error() variant that didn't write a message until - it got a trailing \n -- otherwise, if a syslog option were added, these - messages would wind up as separate syslog calls (probably not the desired - result). - * Chris LewisMoss reports that he gets a parse error when the last line of his .fetchmailrc is a comment. Release Notes: +------------------------------------------------------------------------------ +fetchmail-2.7 () + +features -- + +* New --syslog option by Dave Bodenstab. + +bugs -- + ------------------------------------------------------------------------------ fetchmail-2.6 (Fri Dec 27 12:42:56 EST 1996) diff --git a/acconfig.h b/acconfig.h index d6a1399b..9f8434d0 100644 --- a/acconfig.h +++ b/acconfig.h @@ -32,8 +32,8 @@ /* Define if you have GNU's getopt family of functions. */ #undef HAVE_GETOPTLONG -/* Define if you have setlinebuf(3) */ -#undef HAVE_SETLINEBUF +/* Define to enable use of stdio for socket I/O. */ +#undef USE_STDIO /* Leave that blank line there!! Autoheader needs it. diff --git a/configure.in b/configure.in index d988c620..6dea56c5 100644 --- a/configure.in +++ b/configure.in @@ -70,6 +70,22 @@ then LIBS="$LIBS -lkrb -ldes" fi +dnl Configure command line options +opt_enable_stdio=unset + +AC_ARG_ENABLE(stdio, --enable-stdio enable stdio for socket I/O, opt_enable_stdio=$enableval) + +if test $opt_enable_stdio = unset; then + dnl Set USE_STDIO based on $host + case $host in + *-*-*freebsd*) + AC_DEFINE(USE_STDIO) + ;; + esac +elif test $opt_enable_stdio = yes; then + AC_DEFINE(USE_STDIO) +fi + dnl All AC_CHECK_FUNCs must precede the following AC_SUBSTs @@ -77,7 +93,7 @@ AC_SUBST(EXTRASRC) AC_SUBST(EXTRAOBJ) AC_CHECK_FUNCS(tcsetattr stty setsid seteuid gethostbyname res_search herror \ - strrchr strerror setlinebuf) + strrchr strerror setlinebuf syslog snprintf vsnprintf) dnl AC_FUNC_SETVBUF_REVERSED diff --git a/driver.c b/driver.c index 459bfd81..ce09c8e1 100644 --- a/driver.c +++ b/driver.c @@ -336,7 +336,7 @@ struct query *ctl; /* query control record */ while (sizeticker >= SIZETICKER) { if (outlevel > O_SILENT) - fputc('.',stderr); + error_build("."); sizeticker -= SIZETICKER; } } @@ -763,6 +763,9 @@ struct query *ctl; /* query control record */ lines++; } + if (outlevel == O_VERBOSE) + fputc('\n', stderr); + if (ctl->mda[0]) { int rc; @@ -897,7 +900,8 @@ const struct method *proto; /* protocol method table */ if ((sockfp = SockOpen(ctl->servernames->id, ctl->port ? ctl->port : protocol->port)) == NULL) { - error(0, errno, "connecting to host"); + if (errno != EHOSTUNREACH) + error(0, errno, "connecting to host"); ok = PS_SOCKET; goto closeUp; } @@ -1013,9 +1017,9 @@ const struct method *proto; /* protocol method table */ { if (outlevel > O_SILENT) { - fprintf(stderr, "skipping message %d", num); + error_build("skipping message %d", num); if (toolarge) - fprintf(stderr, " (oversized, %d bytes)", msgsizes[num-1]); + error_build(" (oversized, %d bytes)", msgsizes[num-1]); } } else @@ -1028,13 +1032,13 @@ const struct method *proto; /* protocol method table */ if (outlevel > O_SILENT) { - fprintf(stderr, "reading message %d", num); + error_build("reading message %d", num); if (len > 0) - fprintf(stderr, " (%d bytes)", len); + error_build(" (%d bytes)", len); if (outlevel == O_VERBOSE) - fputc('\n', stderr); + error_complete(0, 0, ""); else - fputc(' ', stderr); + error_build(" "); } /* read the message and ship it to the output sink */ @@ -1071,7 +1075,7 @@ const struct method *proto; /* protocol method table */ { deletions++; if (outlevel > O_SILENT) - fprintf(stderr, " flushed\n"); + error_complete(0, 0, " flushed"); ok = (protocol->delete)(sockfp, ctl, num); if (ok != 0) goto cleanUp; @@ -1079,7 +1083,7 @@ const struct method *proto; /* protocol method table */ delete_str(&ctl->newsaved, num); } else if (outlevel > O_SILENT) - fprintf(stderr, " not flushed\n"); + error_complete(0, 0, " not flushed"); /* perhaps this as many as we're ready to handle */ if (ctl->fetchlimit && ctl->fetchlimit <= num) diff --git a/fetchmail.c b/fetchmail.c index 846c4ae7..37ceb0d8 100644 --- a/fetchmail.c +++ b/fetchmail.c @@ -19,6 +19,9 @@ #endif #include #include +#if defined(HAVE_SYSLOG) +#include +#endif #include #include #include @@ -51,6 +54,7 @@ int yydebug; /* enable parse debugging */ int poll_interval; /* poll interval in seconds */ int nodetach; /* if TRUE, don't detach daemon process */ char *logfile; /* log file for daemon mode */ +int use_syslog; /* if --syslog was set */ int quitmode; /* if --quit was set */ int check_only; /* if --probe was set */ int cmd_batchlimit; /* if --batchlimit was set */ @@ -85,7 +89,7 @@ static void unlockit(void) int main (int argc, char **argv) { int st, bkgd = FALSE; - int parsestatus, implicitmode; + int parsestatus, implicitmode, sigwakeup; char *home, *tmpdir, tmpbuf[BUFSIZ]; struct passwd *pw; struct query *ctl; @@ -151,8 +155,15 @@ int main (int argc, char **argv) implicitmode = load_params(argc, argv, optind); /* set up to do lock protocol */ - strcpy(tmpbuf, home); - strcat(tmpbuf, "/.fetchmail"); + if (poll_interval && getuid() == 0) { + sigwakeup = SIGHUP; + strcpy(tmpbuf, "/var/run/fetchmail.pid"); + } + else { + sigwakeup = SIGUSR1; + strcpy(tmpbuf, home); + strcat(tmpbuf, "/.fetchmail"); + } /* perhaps we just want to check options? */ if (versioninfo) { @@ -259,7 +270,7 @@ int main (int argc, char **argv) pid); return(PS_EXCLUDE); } - else if (kill(pid, SIGUSR1) == 0) + else if (kill(pid, sigwakeup) == 0) { fprintf(stderr, "fetchmail: background fetchmail at %d awakened.\n", @@ -270,7 +281,7 @@ int main (int argc, char **argv) { /* * Should never happen -- possible only if a background fetchmail - * croaks after the first kill probe above but before the SIGUSR1 + * croaks after the first kill probe above but before the SIGUSR1/SIGHUP * transmission. */ fprintf(stderr, @@ -301,6 +312,11 @@ int main (int argc, char **argv) /* * Maybe time to go to demon mode... */ +#if defined(HAVE_SYSLOG) + if (use_syslog) + openlog(program_name, LOG_PID, LOG_MAIL); +#endif + if (poll_interval && !nodetach) daemonize(logfile, termhook); @@ -319,7 +335,7 @@ int main (int argc, char **argv) * side effect of interrupting any sleep that may be going on, * forcing fetchmail to re-poll its hosts. */ - signal(SIGUSR1, donothing); + signal(sigwakeup, donothing); /* here's the exclusion lock */ if ( (lockfp = fopen(lockfile,"w")) != NULL ) { @@ -372,9 +388,9 @@ int main (int argc, char **argv) setitimer(ITIMER_REAL,&ntimeout,NULL); signal(SIGALRM, donothing); pause(); - if (lastsig == SIGUSR1) { - signal(SIGALRM, SIG_IGN); - (void) error(0, 0, "awakened by SIGUSR1"); + signal(SIGALRM, SIG_IGN); + if (lastsig == sigwakeup) { + error(0, 0, "awakened by %s", sys_siglist[lastsig]); } } diff --git a/fetchmail.h b/fetchmail.h index 994bc690..905bc211 100644 --- a/fetchmail.h +++ b/fetchmail.h @@ -138,6 +138,7 @@ extern int yydebug; /* enable parse debugging */ extern int poll_interval; /* poll interval in seconds */ extern int nodetach; /* if TRUE, don't detach daemon process */ extern char *logfile; /* log file for daemon mode */ +extern int use_syslog; /* if --syslog was set */ extern int quitmode; /* if --quit was set */ extern int check_only; /* if --check was set */ extern int cmd_batchlimit; /* if --batchlimit was set */ @@ -164,15 +165,20 @@ extern int smtp_response; /* numeric value of SMTP response code */ /* prototypes for globally callable functions */ #if defined(HAVE_STDARG_H) void error (int status, int errnum, const char *format, ...); +void error_build (const char *format, ...); +void error_complete (int status, int errnum, const char *format, ...); void gen_send (FILE *sockfp, char *, ... ); int gen_transact (FILE *sockfp, char *, ... ); #else void error (); +void error_build (); +void error_complete (); void gen_send (); int gen_transact (); #endif void *xmalloc(int); +void *xrealloc(void *, int); char *xstrdup(const char *); int do_protocol(struct query *, const struct method *); diff --git a/fetchmail.man b/fetchmail.man index 1385bf78..a6ea4142 100644 --- a/fetchmail.man +++ b/fetchmail.man @@ -362,6 +362,33 @@ mode into a specified logfile (follow the option with the logfile name). The logfile is opened for append, so previous messages aren't deleted. This is primarily useful for debugging configurations. .PP +The +.B --syslog +option allows you to redirect status and error messages emitted while in +daemon mode to the +.IR syslog (3) +system daemon if available. +Messages are logged with an id of \fBfetchmail\fR, the facility \fBLOG_MAIL\fR, +and priorities \fBLOG_ERR\fR, \fBLOG_ALERT\fR or \fBLOG_INFO\fR. +This option is intended for logging status and error messages which +indicate the status of the daemon and the results while fetching mail +from the server(s). +Error messages for command line options and parsing the \fI.fetchmailrc\fR +file are still written to stderr, or the specified log file if the +.B -L +or +.B --logfile +option was used. +.PP +The \fI/etc/syslog.conf\fR file might contain the following to log +all messages from \fIfetchmail\fR to a single file: +.PP +.RS +!fetchmail +.br +*.* /var/log/fetchmail +.RE +.PP The .B \-N or --nodetach option suppresses detachment of the daemon process @@ -816,8 +843,11 @@ default location of file associating hosts with last message IDs seen (used only with newer RFC1725-compliant POP3 servers supporting the UIDL command). .TP 5 -$~/.fetchmail -lock file to help prevent concurrent runs. +~/.fetchmail +lock file to help prevent concurrent runs (non-root mode). +.TP 5 +/var/run/fetchmail.pid +lock file to help prevent concurrent runs (root mode). .SH ENVIRONMENT For correct initialization, .I fetchmail diff --git a/options.c b/options.c index 4bbc593f..4c02d584 100644 --- a/options.c +++ b/options.c @@ -25,27 +25,28 @@ #define LA_NODETACH 7 #define LA_QUIT 8 #define LA_LOGFILE 9 -#define LA_RCFILE 10 -#define LA_IDFILE 11 -#define LA_PROTOCOL 12 -#define LA_PORT 13 -#define LA_AUTHENTICATE 14 -#define LA_TIMEOUT 15 -#define LA_USERNAME 16 -#define LA_ALL 17 -#define LA_KILL 18 -#define LA_KEEP 19 -#define LA_FLUSH 20 -#define LA_NOREWRITE 21 -#define LA_LIMIT 22 -#define LA_REMOTEFILE 23 -#define LA_SMTPHOST 24 -#define LA_BATCHLIMIT 25 -#define LA_FETCHLIMIT 26 -#define LA_MDA 27 -#define LA_INTERFACE 28 -#define LA_MONITOR 29 -#define LA_YYDEBUG 30 +#define LA_SYSLOG 10 +#define LA_RCFILE 11 +#define LA_IDFILE 12 +#define LA_PROTOCOL 13 +#define LA_PORT 14 +#define LA_AUTHENTICATE 15 +#define LA_TIMEOUT 16 +#define LA_USERNAME 17 +#define LA_ALL 18 +#define LA_KILL 19 +#define LA_KEEP 20 +#define LA_FLUSH 21 +#define LA_NOREWRITE 22 +#define LA_LIMIT 23 +#define LA_REMOTEFILE 24 +#define LA_SMTPHOST 25 +#define LA_BATCHLIMIT 26 +#define LA_FETCHLIMIT 27 +#define LA_MDA 28 +#define LA_INTERFACE 29 +#define LA_MONITOR 30 +#define LA_YYDEBUG 31 static char *shortoptions = "?Vcsvd:NqL:f:i:p:P:A:t:u:akKFnl:r:S:b:B:m:I:M:y"; static struct option longoptions[] = { @@ -58,6 +59,7 @@ static struct option longoptions[] = { {"nodetach", no_argument, (int *) 0, LA_NODETACH }, {"quit", no_argument, (int *) 0, LA_QUIT }, {"logfile", required_argument, (int *) 0, LA_LOGFILE }, + {"syslog", no_argument, (int *) 0, LA_SYSLOG }, {"fetchmailrc",required_argument,(int *) 0, LA_RCFILE }, {"idfile", required_argument, (int *) 0, LA_IDFILE }, #ifdef linux @@ -272,6 +274,10 @@ struct query *ctl; /* option record to be initialized */ yydebug = TRUE; break; + case LA_SYSLOG: + use_syslog = TRUE; + break; + case '?': case LA_HELP: default: @@ -285,6 +291,12 @@ struct query *ctl; /* option record to be initialized */ return(-1); } + if (poll_interval == 0 && use_syslog) + { + fputs("The --syslog option is only valid with the --daemon option.\n", stderr); + return(-1); + } + if (errflag || ocount > 1) { /* squawk if syntax errors were detected */ fputs("usage: fetchmail [options] [server ...]\n", stderr); @@ -299,6 +311,7 @@ struct query *ctl; /* option record to be initialized */ fputs(" -N, --nodetach don't detach daemon process\n", stderr); fputs(" -q, --quit kill daemon process\n", stderr); fputs(" -L, --logfile specify logfile name\n", stderr); + fputs(" --syslog use syslog(3) for most messages when running as a daemon\n", stderr); fputs(" -f, --fetchmailrc specify alternate run control file\n", stderr); fputs(" -i, --idfile specify alternate UIDs file\n", stderr); #ifdef linux diff --git a/report.c b/report.c index 19fb3f45..3e973c84 100644 --- a/report.c +++ b/report.c @@ -22,6 +22,13 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #endif #include +#include +#if defined(HAVE_SYSLOG) +#include +#endif +#if defined(HAVE_ALLOCA_H) +#include +#endif #if HAVE_VPRINTF || HAVE_DOPRNT || _LIBC # if __STDC__ @@ -43,6 +50,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ void exit (); #endif +#include "fetchmail.h" + #ifndef _ # define _(String) String #endif @@ -56,6 +65,11 @@ void (*error_print_progname) ( #endif ); +/* Used by error_build() and error_complete() to accumulate partial messages. */ +static unsigned int partial_message_size = 0; +static unsigned int partial_message_size_used = 0; +static char *partial_message; + /* This variable is incremented each time `error' is called. */ unsigned int error_message_count; @@ -112,40 +126,263 @@ error (status, errnum, message, va_alist) va_list args; #endif - if (error_print_progname) - (*error_print_progname) (); + /* If a partially built message exists, print it now so it's not lost. */ + if (partial_message_size_used != 0) + { + partial_message_size_used = 0; + error (0, 0, "%s (message incomplete)", partial_message); + } + +#if defined(HAVE_SYSLOG) + if (use_syslog) + { + int priority; + +# ifdef VA_START + VA_START (args, message); +# endif + priority = status? LOG_ALERT : errnum? LOG_ERR : LOG_INFO; + + if (errnum) + { + char *msg = alloca (strlen (message) + 5); + + strcpy (msg, message); + strcat (msg, ": %m"); + + errno = errnum; +# ifdef VA_START + vsyslog (priority, msg, args); + va_end (args); +# else + syslog (priority, msg, a1, a2, a3, a4, a5, a6, a7, a8); +# endif + } + else + { +# ifdef VA_START + vsyslog (priority, message, args); + va_end (args); +# else + syslog (priority, message, a1, a2, a3, a4, a5, a6, a7, a8); +# endif + } + } else +#endif { - fflush (stdout); - if ( *message == '\n' ) + if (error_print_progname) + (*error_print_progname) (); + else { - fputc( '\n', stderr ); - ++message; + fflush (stdout); + if ( *message == '\n' ) + { + fputc( '\n', stderr ); + ++message; + } + fprintf (stderr, "%s: ", program_name); } - fprintf (stderr, "%s: ", program_name); - } #ifdef VA_START - VA_START (args, message); + VA_START (args, message); # if HAVE_VPRINTF || _LIBC - vfprintf (stderr, message, args); + vfprintf (stderr, message, args); # else - _doprnt (message, args, stderr); + _doprnt (message, args, stderr); # endif - va_end (args); + va_end (args); #else - fprintf (stderr, message, a1, a2, a3, a4, a5, a6, a7, a8); + fprintf (stderr, message, a1, a2, a3, a4, a5, a6, a7, a8); #endif + if (errnum) + fprintf (stderr, ": %s", strerror (errnum)); + putc ('\n', stderr); + fflush (stderr); + } ++error_message_count; - if (errnum) - fprintf (stderr, ": %s", strerror (errnum)); - putc ('\n', stderr); - fflush (stderr); if (status) exit (status); } +/* Build an error message by appending MESSAGE, which is a printf-style + format string with optional args, to the existing error message (which may + be empty.) The completed error message is finally printed (and reset to + empty) by calling error_complete(). + If an intervening call to error() occurs when a partially constructed + message exists, then, in an attempt to keep the messages in their proper + sequence, the partial message will be printed as-is (with a trailing newline) + before error() prints its message. +/* VARARGS */ + +void +#if defined(VA_START) && __STDC__ +error_build (const char *message, ...) +#else +error_build (message, va_alist) + char *message; + va_dcl +#endif +{ +#ifdef VA_START + va_list args; + int n; +#endif + + /* Make an initial guess for the size of any single message fragment. */ + if (partial_message_size == 0) + { + partial_message_size_used = 0; + partial_message_size = 512; + partial_message = xmalloc (partial_message_size); + } + else + if (partial_message_size - partial_message_size_used < 256) + { + partial_message_size += 512; + partial_message = xrealloc (partial_message, partial_message_size); + } + +#if defined(VA_START) && (HAVE_VSNPRINTF || _LIBC) + VA_START (args, message); + for ( ; ; ) + { + n = vsnprintf (partial_message + partial_message_size_used, + partial_message_size - partial_message_size_used, + message, args); + + if (n < partial_message_size - partial_message_size_used) + { + partial_message_size_used += n; + break; + } + + partial_message_size += 512; + partial_message = xrealloc (partial_message, partial_message_size); + } + va_end (args); +#else +#if HAVE_SNPRINTF + for ( ; ; ) + { + n = snprintf (partial_message + partial_message_size_used, + partial_message_size - partial_message_size_used, + message, a1, a2, a3, a4, a5, a6, a7, a8); + + if (n < partial_message_size - partial_message_size_used) + { + partial_message_size_used += n; + break; + } + + partial_message_size += 512; + partial_message = xrealloc (partial_message, partial_message_size); + } +#else + sprintf (partial_message + partial_message_size_used, message, a1, a2, a3, a4, a5, a6, a7, a8); + + /* Attempt to catch memory overwrites... */ + if ((partial_message_size_used = strlen (partial_message)) >= partial_message_size) + { + partial_message_size_used = 0; + error (PS_UNDEFINED, 0, "partial error message buffer overflow"); + } +#endif +#endif +} + +/* Complete an error message by appending MESSAGE, which is a printf-style + format string with optional args, to the existing error message (which may + be empty.) The completed error message is then printed (and reset to + empty.) +/* VARARGS */ + +void +#if defined(VA_START) && __STDC__ +error_complete (int status, int errnum, const char *message, ...) +#else +error_complete (status, errnum, message, va_alist) + int status; + int errnum; + char *message; + va_dcl +#endif +{ +#ifdef VA_START + va_list args; + int n; +#endif + + /* Make an initial guess for the size of any single message fragment. */ + if (partial_message_size == 0) + { + partial_message_size_used = 0; + partial_message_size = 512; + partial_message = xmalloc (partial_message_size); + } + else + if (partial_message_size - partial_message_size_used < 256) + { + partial_message_size += 512; + partial_message = xrealloc (partial_message, partial_message_size); + } + +#if defined(VA_START) && (HAVE_VSNPRINTF || _LIBC) + VA_START (args, message); + for ( ; ; ) + { + n = vsnprintf (partial_message + partial_message_size_used, + partial_message_size - partial_message_size_used, + message, args); + + if (n < partial_message_size - partial_message_size_used) + { + partial_message_size_used += n; + break; + } + + partial_message_size += 512; + partial_message = xrealloc (partial_message, partial_message_size); + } + va_end (args); +#else +#if HAVE_SNPRINTF + for ( ; ; ) + { + n = snprintf (partial_message + partial_message_size_used, + partial_message_size - partial_message_size_used, + message, a1, a2, a3, a4, a5, a6, a7, a8); + + if (n < partial_message_size - partial_message_size_used) + { + partial_message_size_used += n; + break; + } + + partial_message_size += 512; + partial_message = xrealloc (partial_message, partial_message_size); + } +#else + sprintf (partial_message + partial_message_size_used, message, a1, a2, a3, a4, a5, a6, a7, a8); + + /* Attempt to catch memory overwrites... */ + if ((partial_message_size_used = strlen (partial_message)) >= partial_message_size) + { + partial_message_size_used = 0; + error (PS_UNDEFINED, 0, "partial error message buffer overflow"); + } +#endif +#endif + + /* Finally... print it. */ + if (partial_message_size_used != 0) + { + partial_message_size_used = 0; + error (status, errnum, "%s", partial_message); + } +} + /* Sometimes we want to have at most one error per line. This variable controls whether this mode is selected or not. */ int error_one_per_line; diff --git a/xmalloc.c b/xmalloc.c index 3c231294..8e085b0c 100644 --- a/xmalloc.c +++ b/xmalloc.c @@ -30,6 +30,17 @@ xmalloc (int n) return(p); } +XMALLOCTYPE * +xrealloc (XMALLOCTYPE *p, int n) +{ + if (p == 0) + return xmalloc (n); + p = (XMALLOCTYPE *) realloc(p, n); + if (p == (XMALLOCTYPE *) 0) + error(PS_UNDEFINED, errno, "realloc failed"); + return p; +} + char *xstrdup(const char *s) { char *p; -- cgit v1.2.3