bug-tar
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Bug-tar] GNU Tar 1.28 configuration test for deep directory hierarc


From: Jack Howarth
Subject: Re: [Bug-tar] GNU Tar 1.28 configuration test for deep directory hierarchy failing on Mac OS X 10.11 El Capitan
Date: Mon, 6 Jun 2016 09:54:08 -0400
User-agent: Mutt/1.5.18 (2008-05-17)

On Sun, Jun 05, 2016 at 07:57:08PM -0700, Jonathan Leffler wrote:
> Long slumber — apologies.
> 
> TL;DR — you may close this bug report.  The original report was against Mac
> OS X El Capitan 10.11.  Later versions of OS X 10.11 (definitively from
> 10.11.4, possibly earlier) have the problem fixed.

This issue was due to radr://21483133 which was breakage in the behavior of the 
getcwd() call
in OS X 10.11.0 and 10.11.1. The bug was fixed in the OS X 10.11.2 system 
update.

> 
> Gory details:
> 
> On 10.11.5, I ran the t.c file you provided and it worked cleanly.  It
> exited with status 4, but there was no problem with the directory cleanup.
> 
> I still have a gruesome directory which 'Finder' (the OS X GUI tool) cannot
> clean up when it is in Trash.  I've placed it into a directory
> $HOME/tmp/top-level and renamed 'confdir-14B---' to names such as '
> confdir-000000' .. 'confdir-000335' (using the rename-it.sh script
> attached)  I run into problems with 'confdir-000335'; although its mode
> field is 40700 (a directory with 700 permissions), I get errno 2 'no such
> file or directory' when I execute 'chdir("confdir-000335")' from directory
> "top-level/…/confdir-000334".  I created a program do_cd.c (attached, along
> with the error reporting code I use, stderr.[ch]), loosely related to t.c
> that runs into a hard wall at level 335, whether run from top-level or from
> top-level/…/confdir-000129.  The shell (Bash 3.2.57(1)) gives up (cannot do
> cd) after around top-level/…/confdir-000129, even trying 'cd
> top-level/…/confdir-000061' (successful) followed by 'cd
> confdir-000062/…/confdir-000129' (that succeeds, but 131 fails with 'path
> name too long').
> 
> I finally used 'cd top-level/…/confdir-000061; mv * ~/tmp/top-level' to
> move the hierarchy up the file system — so I had, briefly,
> top-level/confdir-000001 with a path down to confdir-000061, and
> top-level/confdir-000062 with a path down to confdir-000335.  Running 'rm
> -fr confdir-000001' was able to clean that section up.  Using do_cd, there
> were still problems with 'chdir("confdir-000335")' , but 'rm -fr
> confdir-000062' (from the ~/tmp/top-level directory) worked, so I finally
> don't have the wreckage of the attempted build cluttering up my file system.
> 
> I agree that what I found was a bug in OSX, and so apparently does Apple
> since the failure no longer occurs.
> 
> JFTR: the versions of Mac OS X 10.11.x El Capitan so far were announced in
> email on these dates:
> 
> 10.11    — 2015-09-30
> 10.11.1 — 2015-10-21
> 10.11.2 — 2015-12-15
> 10.11.3 — 2016-01-19
> 10.11.4 — 2016-04-21
> 10.11.5 — 2016-05-16
> 
> Since I reported the problem on 2015-10-12, I must have been using 10.11 at
> the time, as I reported.
> 
> 
> On Tue, Oct 13, 2015 at 11:39 AM, Paul Eggert <address@hidden> wrote:
> 
> > On 10/12/2015 02:00 PM, Jonathan Leffler wrote:
> >
> >> 1. Has anyone else seen anything similar on El Capitan?
> >> 2. Does anyone have an idea how to get rid of the trashed directories?
> >> 3. How can we modify the test so it doesn't fail catastrophically like
> >> this on El Capitan?
> >> 4. Do I need to report a bug to Apple separately from this report to GNU
> >> Tar?
> >>
> >
> > For (4), OS X is clearly busted here. A user-mode application shouldn't be
> > able to create a directory that 'rm -fr' can't remove. Please feel free to
> > report the bug to Apple. The problem will occur with many GNU tools'
> > installation procedure, as they're all using Gnulib and Gnulib tries to
> > check for getcwd bugs like this one.
> >
> > For (2) I suggest using coreutils; 'rm -fr directory should do the trick
> > if you're using GNU rm.
> >
> > For (3) we need to figure out why the test doesn't clean up after itself
> > on El Capitan.  It's supposed to.  Please compile and run the attached test
> > program in a place where you don't mind having long directory chains.  On
> > my GNU/Linux host, I can do something like this:
> >
> >   gcc t.c
> >   strace ./a.out
> >
> > and the 'strace' output contains the following, showing that the test
> > program cleans up after itself. Please find out why it's not doing so under
> > El Capitan. OS X lacks strace, but you can use a debugger or dtruss or
> > whatever your favorite tool is. Thanks.
> >
> > mkdir("confdir-14B---", 0700)           = 0
> > chdir("confdir-14B---")                 = 0
> > mkdir("confdir-14B---", 0700)           = 0
> > chdir("confdir-14B---")                 = 0
> > ...
> > mkdir("confdir-14B---", 0700)           = 0
> > chdir("confdir-14B---")                 = 0
> > getcwd(0x1736010, 4096)                 = -1 ENAMETOOLONG (File name too
> > long)
> > [a whole bunch of other stuff, which eventually succeeds. Now comes
> > cleanup time....]
> > rmdir("confdir-14B---")                 = -1 ENOENT (No such file or
> > directory)
> > chdir("..")                             = 0
> > rmdir("confdir-14B---")                 = 0
> > chdir("..")                             = 0
> > ...
> > chdir("..")                             = 0
> > rmdir("confdir-14B---")                 = 0
> > exit_group(0)                           = ?
> >
> >
> 
> 
> -- 
> Jonathan Leffler <address@hidden>  #include <disclaimer.h>
> Guardian of DBD::Informix - v2015.1101 - http://dbi.perl.org
> "Blessed are we who can laugh at ourselves, for we shall never cease to be
> amused."

> #include "stderr.h"
> #include <assert.h>
> #include <dirent.h>
> #include <stdio.h>
> #include <stdlib.h>
> #include <string.h>
> #include <sys/stat.h>
> #include <unistd.h>
> 
> int main(int argc, char **argv)
> {
>     err_setarg0(argv[0]);
> 
>     if (argc != 2)
>         err_usage("directory");
> 
>     struct stat sb;
>     char *name = argv[1];
> 
>     if (stat(name, &sb) != 0)
>         err_syserr("Unable to stat directory '%s': ", name);
>     if (!S_ISDIR(sb.st_mode))
>         err_error("File '%s' is not a directory (mode = %o)\n", name, 
> sb.st_mode);
> 
>     size_t bufmax = 4096;
>     char  *buffer = malloc(bufmax);
>     if (buffer == 0)
>         err_syserr("Failed to malloc %zu bytes: ", bufmax);
>     strcpy(buffer, name);
>     size_t buflen = strlen(buffer);
>     assert(buflen < bufmax);
>     size_t levels = 0;
> 
>     while (buflen < bufmax)
>     {
>         if (chdir(name) != 0)
>         {
>             err_sysrem("Failed to chdir into '%s'\n", name);
>             err_remark("%3zu (%4zu): %s\n", ++levels, buflen, buffer);
>             err_error("Mode = %o\n", sb.st_mode);
>         }
>         printf("%3zu (%4zu): %s\n", ++levels, buflen, buffer);
>         DIR *dp = opendir(".");
>         struct dirent *nm;
>         while ((nm = readdir(dp)) != 0)
>         {
>             if (strcmp(nm->d_name, ".") == 0 || strcmp(nm->d_name, "..") == 0)
>                 continue;
>             if (stat(nm->d_name, &sb) != 0)
>                 err_syserr("Unable to stat name '%s': ", nm->d_name);
>             if (!S_ISDIR(sb.st_mode))
>             {
>                 err_sysrem("Name '%s' is not a directory (mode = %o)\n", 
> nm->d_name, sb.st_mode);
>                 if (unlink(nm->d_name) != 0)
>                     err_syserr("Failed to remove non-directory '%s': ", 
> nm->d_name);
>                 err_remark("Removed '%s' from '%s'\n", nm->d_name, buffer);
>                 continue;
>             }
>             size_t namlen = strlen(nm->d_name);
>             if (buflen + namlen + 1 >= bufmax)
>             {
>                 err_remark("Allocate space - name is too long: additional 
> name '%s' - (%zu levels) %zu '%s'\n",
>                           nm->d_name, levels, buflen, buffer);
>                 size_t newmax = 2 * bufmax;
>                 char  *newbuf = realloc(buffer, newmax);
>                 if (newbuf == 0)
>                     err_syserr("Failed to reallocate %zu bytes memory: ", 
> newmax);
>                 bufmax = newmax;
>                 buffer = newbuf;
>             }
>             buffer[buflen++] = '/';
>             strcpy(&buffer[buflen], nm->d_name);
>             name = &buffer[buflen];
>             buflen += namlen;
>             break;
>         }
>         closedir(dp);
>     }
> 
>     return 0;
> }
> 

> /*
> @(#)File:           $RCSfile: stderr.c,v $
> @(#)Version:        $Revision: 10.14 $
> @(#)Last changed:   $Date: 2015/06/02 03:04:32 $
> @(#)Purpose:        Error reporting routines
> @(#)Author:         J Leffler
> @(#)Copyright:      (C) JLSS 1988-91,1996-99,2001,2003,2005-11,2013,2015
> @(#)Product:        :PRODUCT:
> */
> 
> /*TABSTOP=4*/
> 
> /*
> ** Configuration:
> ** USE_STDERR_SYSLOG   - include syslog functionality
> ** USE_STDERR_FILEDESC - include file descriptor functionality
> ** JLSS_STDERR         - force support for syslog and file descriptors
> */
> 
> #include "posixver.h"
> #include "stderr.h"     /* Includes config.h if available */
> #include <assert.h>
> #include <ctype.h>
> #include <errno.h>
> #include <string.h>
> #include <stdlib.h>
> #include <stdarg.h>
> #include <time.h>
> 
> #ifdef HAVE_UNISTD_H
> #include <unistd.h>
> #else
> extern int getpid(void);
> #endif /* HAVE_UNISTD_H */
> 
> enum { MAX_MSGLEN = 2048 };
> 
> /* Find sub-second timing mechanism */
> #if defined(HAVE_CLOCK_GETTIME)
> /* Uses <time.h> */
> #elif defined(HAVE_GETTIMEOFDAY)
> /* Mac OS X 10.10.3 does not have clock_gettime() yet */
> #include <sys/time.h>
> #else
> /* No sub-second timing */
> #endif
> 
> #if defined(HAVE_SYSLOG_H) && defined(HAVE_SYSLOG) && 
> defined(USE_STDERR_SYSLOG)
> #include <syslog.h>
> extern const char jlss_id_stderr_c_with_syslog[];
> const char jlss_id_stderr_c_with_syslog[] =
>         "@(#)" __FILE__ " configured with USE_STDERR_SYSLOG";
> #else
> #undef USE_STDERR_SYSLOG
> #undef HAVE_SYSLOG_H
> #undef HAVE_SYSLOG
> #endif /* syslog configuration */
> 
> #if defined(USE_STDERR_FILEDESC)
> extern const char jlss_id_stderr_c_with_filedesc[];
> const char jlss_id_stderr_c_with_filedesc[] =
>         "@(#)" __FILE__ " configured with USE_STDERR_FILEDESC";
> #endif /* USE_STDERR_FILEDESC */
> 
> static const char def_format[] = "%Y-%m-%d %H:%M:%S";
> static const char *tm_format = def_format;
> static char arg0[ERR_MAXLEN_ARGV0+1] = "**undefined**";
> 
> /* Permitted default error flags */
> enum { ERR_LOGTIME = ERR_STAMP | ERR_MILLI | ERR_MICRO | ERR_NANO };
> enum { ERR_LOGOPTS = ERR_NOFLUSH | ERR_EXIT | ERR_ABORT | ERR_LOGTIME |
>                      ERR_NOARG0  | ERR_PID  | ERR_ERRNO };
> static int   err_flags = 0;     /* Default error flags (ERR_STAMP, ERR_PID, 
> etc) */
> 
> /* Where do messages go?
> **  if   (defined USE_STDERR_SYSLOG && errlog != 0) ==> syslog
> **  elif (err_fd >= 0)                       ==> file descriptor
> **  else                                     ==> file pointer
> */
> #ifdef USE_STDERR_SYSLOG
> static int   errlog =  0;
> #endif /* USE_STDERR_SYSLOG */
> static FILE *errout =  0;
> #ifdef USE_STDERR_FILEDESC
> static int   err_fd = -1;
> #endif /* USE_STDERR_FILEDESC */
> 
> /*
> ** err_???_print() functions are named systematically, and are all static.
> **
> ** err_[ev][crx][fn]_print():
> ** --   e   takes ellipsis argument
> ** --   v   takes va_list argument
> ** --   c   conditionally exits
> ** --   r   returns (no exit)
> ** --   x   exits (no return)
> ** --   f   takes file pointer
> ** --   n   no file pointer (use errout)
> **
> ** NB: no-return and printf-like can only be attached to declarations, not 
> definitions.
> */
> 
> static NORETURN void err_vxf_print(FILE *fp, int flags, int estat, const char 
> *format, va_list args);
> static NORETURN void err_vxn_print(int flags, int estat, const char *format, 
> va_list args);
> static NORETURN void err_exn_print(int flags, int estat, const char *format, 
> ...)
>                 PRINTFLIKE(3,4);
> static NORETURN void err_terminate(int flags, int estat);
> 
> #ifndef lint
> /* Prevent over-aggressive optimizers from eliminating ID string */
> extern const char jlss_id_stderr_c[];
> const char jlss_id_stderr_c[] = "@(#)$Id: stderr.c,v 10.14 2015/06/02 
> 03:04:32 jleffler Exp $";
> #endif /* lint */
> 
> /*
> ** Set default log options, returning old value.
> ** Setting ERR_EXIT and ERR_ABORT is permitted but not recommended.
> */
> int err_setlogopts(int new_opts)
> {
>     int old_opts = err_flags;
>     err_flags = new_opts & ERR_LOGOPTS;
>     return(old_opts);
> }
> 
> /* Return default log options */
> int err_getlogopts(void)
> {
>     return(err_flags);
> }
> 
> /* Change the definition of 'stderr', reporting on the old one too */
> /* NB: using err_stderr((FILE *)0) simply reports the current 'stderr' */
> FILE *(err_stderr)(FILE *newerr)
> {
>     FILE *old;
> 
>     if (errout == 0)
>         errout = stderr;
>     old = errout;
>     if (newerr != 0)
>         errout = newerr;
>     return(old);
> }
> 
> #if defined(USE_STDERR_FILEDESC)
> /* Change the definition of 'stderr', reporting on the old one too */
> /* NB: using err_use_fd() with a negative value turns off 'errors to file 
> descriptor' */
> int (err_use_fd)(int new_fd)
> {
>     int old_fd = err_fd;
> 
>     if (new_fd < 0)
>         new_fd = -1;
>     err_fd = new_fd;
>     return(old_fd);
> }
> #endif /* USE_STDERR_FILEDESC */
> 
> #if defined(USE_STDERR_SYSLOG)
> /*
> ** Configure the use of syslog
> ** If not configured to use syslog(), this is a no-op.
> ** If configured to use syslog(), the facility argument should be one of
> ** the standard facilities (POSIX defines LOG_USER and LOG_LOCAL0 to
> ** LOG_LOCAL7) to turn on syslog(), or a negative value to turn it off.
> ** The logopts should be the bitwise combination of 0 and the options
> ** LOG_PID, LOG_CONS, LOG_NDELAY, LOG_ODELAY, LOG_NOWAIT.  However, the
> ** STDERR package sets LOG_PID regardless.
> ** The ident used in openlog() corresponds to the value in arg0.
> ** Note that when formatting the message for syslog(), the time, the PID
> ** and arg0 are not needed (and hence not provided).  The downside is
> ** you are stuck with the date formatted by syslog().
> */
> int (err_use_syslog)(int logopts, int facility)
> {
>     if (facility < 0)
>     {
>         /* Turn off syslog() */
>         closelog();
>         errlog = 0;
>     }
>     else
>     {
>         openlog(arg0, LOG_PID|logopts, facility);
>         errlog = 1;
>     }
>     return(errlog);
> }
> #endif /* USE_STDERR_SYSLOG */
> 
> /* Return stored basename of command */
> const char *(err_getarg0)(void)
> {
>     return(arg0);
> }
> 
> /* Store basename of command, excluding trailing slashes */
> void (err_setarg0)(const char *argv0)
> {
>     /* Ignore three pathological program names -- NULL, "/" and "" */
>     if (argv0 != 0 && *argv0 != '\0' && (*argv0 != '/' || *(argv0 + 1) != 
> '\0'))
>     {
>         const char *cp;
>         size_t nbytes = sizeof(arg0) - 1;
> 
>         if ((cp = strrchr(argv0, '/')) == 0)
>         {
>             /* Basename of file only */
>             cp = argv0;
>         }
>         else if (*(cp + 1) != '\0')
>         {
>             /* Regular pathname containing slashes but not trailing slashes */
>             cp++;
>         }
>         else
>         {
>             /* Skip backwards over trailing slashes */
>             const char *ep = cp;
>             while (ep > argv0 && *ep == '/')
>                 ep--;
>             /* Skip backwards over non-slashes */
>             cp = ep;
>             while (cp > argv0 && *cp != '/')
>                 cp--;
>             assert(ep >= cp);
>             cp++;
>             nbytes = (size_t)(ep - cp) + 1;
>             if (nbytes > sizeof(arg0) - 1)
>                 nbytes = sizeof(arg0) - 1;
>         }
>         strncpy(arg0, cp, nbytes);
>         arg0[nbytes] = '\0';
>     }
> }
> 
> const char *(err_rcs_string)(const char *s2, char *buffer, size_t buflen)
> {
>     const char *src = s2;
>     char *dst = buffer;
>     char *end = buffer + buflen - 1;
> 
>     /*
>     ** Bother RCS!  We've probably been given something like:
>     ** "$Revision: 10.14 $ ($Date: 2015/06/02 03:04:32 $)"
>     ** We only want to emit "7.5 (2001/08/11 06:25:48)".
>     ** Skip the components between '$' and ': ', copy up to ' $',
>     ** repeating as necessary.  And we have to test for overflow!
>     ** Also work with the unexpanded forms of keywords ($Keyword$).
>     ** Never needed this with SCCS!
>     */
>     while (*src != '\0' && dst < end)
>     {
>         while (*src != '\0' && *src != '$')
>         {
>             *dst++ = *src++;
>             if (dst >= end)
>                 break;
>         }
>         if (*src == '$')
>             src++;
>         while (*src != '\0' && *src != ':' && *src != '$')
>             src++;
>         if (*src == '\0')
>             break;
>         if (*src == '$')
>         {
>             /* Unexpanded keyword '$Keyword$' notation */
>             src++;
>             continue;
>         }
>         if (*src == ':')
>             src++;
>         if (*src == ' ')
>             src++;
>         while (*src != '\0' && *src != '$')
>         {
>             /* Map / in 2009/02/15 to dash */
>             /* Heuristic - maps slashes surrounded by digits to dashes */
>             char c = *src++;
>             if (c == '/' && isdigit(*src) && isdigit(*(src-2)))
>                 c = '-';
>             *dst++ = c;
>             if (dst >= end)
>                 break;
>         }
>         if (*src == '$')
>         {
>             if (*(dst-1) == ' ')
>                 dst--;
>             src++;
>         }
>     }
>     *dst = '\0';
>     return(buffer);
> }
> 
> /* Similar to, but different from, Time in timer.h */
> typedef struct Time
> {
>     time_t tv_sec;
>     long   tv_nsec;
> } Time;
> 
> static Time now(void)
> {
>     Time clk;
> #if defined(HAVE_CLOCK_GETTIME)
>     struct timespec ts;
>     clock_gettime(CLOCK_REALTIME, &ts);
>     clk.tv_sec = ts.tv_sec;
>     clk.tv_nsec = ts.tv_nsec;
> #elif defined(HAVE_GETTIMEOFDAY)
>     struct timeval tv;
>     gettimeofday(&tv, 0);
>     clk.tv_sec = tv.tv_sec;
>     clk.tv_nsec = 1000 * tv.tv_usec;
> #else
>     clk.tv_sec = time(0);
>     clk.tv_nsec = 0;
> #endif
>     return clk;
> }
> 
> /* Format a time string for now (using ISO8601 format) */
> /* Allow for future settable time format with tm_format */
> static char *err_time(int flags, char *buffer, size_t buflen)
> {
>     Time clk = now();
>     struct tm *tp = localtime(&clk.tv_sec);
>     size_t nb = strftime(buffer, buflen, tm_format, tp);
>     if (flags & (ERR_NANO | ERR_MICRO | ERR_MILLI))
>     {
>         char subsec[12];
>         size_t ss_len;
>         if (flags & ERR_NANO)
>             ss_len = snprintf(subsec, sizeof(subsec), ".%.9ld", clk.tv_nsec);
>         else if (flags & ERR_MICRO)
>             ss_len = snprintf(subsec, sizeof(subsec), ".%.6ld", clk.tv_nsec / 
> 1000);
>         else /* (flags & ERR_MILLI) */
>             ss_len = snprintf(subsec, sizeof(subsec), ".%.3ld", clk.tv_nsec / 
> (1000 * 1000));
>         if (ss_len + nb + 1 < buflen)
>             strcpy(buffer + nb, subsec);
>     }
>     return(buffer);
> }
> 
> /* err_stdio - report error via stdio */
> static void (err_stdio)(FILE *fp, int flags, int errnum, const char *format, 
> va_list args)
> {
>     if ((flags & ERR_NOARG0) == 0)
>         fprintf(fp, "%s: ", arg0);
>     if (flags & ERR_LOGTIME)
>     {
>         char timbuf[48];
>         fprintf(fp, "%s - ", err_time(flags, timbuf, sizeof(timbuf)));
>     }
>     if (flags & ERR_PID)
>         fprintf(fp, "pid=%d: ", (int)getpid());
> #pragma GCC diagnostic push
> #pragma GCC diagnostic ignored "-Wformat-nonliteral"
>     vfprintf(fp, format, args);
> #pragma GCC diagnostic pop
>     if (flags & ERR_ERRNO)
>         fprintf(fp, "error (%d) %s\n", errnum, strerror(errnum));
> }
> 
> #if defined(USE_STDERR_FILEDESC) || defined(USE_STDERR_SYSLOG)
> static char *fmt_string(char *curr, const char *end, const char *format, 
> va_list args)
> {
>     char *new_end = curr;
>     if (curr < end - 1)
>     {
>         size_t size = (size_t)(end - curr);
> #pragma GCC diagnostic push
> #pragma GCC diagnostic ignored "-Wformat-nonliteral"
>         int more = vsnprintf(curr, size, format, args);
> #pragma GCC diagnostic pop
>         if (more >= 0)
>             new_end += ((size_t)more >= size) ? size : (size_t)more;
>     }
>     return(new_end);
> }
> 
> static char *fmt_strdots(char *curr, const char *end, const char *format, ...)
> {
>     va_list args;
>     char *new_end;
>     va_start(args, format);
>     new_end = fmt_string(curr, end, format, args);
>     va_end(args);
>     return new_end;
> }
> #endif /* USE_STDERR_FILEDESC || USE_STDERR_SYSLOG */
> 
> #if defined(USE_STDERR_SYSLOG)
> /* err_syslog() - report error via syslog
> **
> ** syslog() automatically adds PID and program name (configured in openlog()) 
> and time stamp.
> */
> static void (err_syslog)(int flags, int errnum, const char *format, va_list 
> args)
> {
>     char buffer[MAX_MSGLEN];
>     char *curpos = buffer;
>     char *bufend = buffer + sizeof(buffer);
>     int priority;
> 
>     curpos = fmt_string(curpos, bufend, format, args);
>     if (flags & ERR_ERRNO)
>         curpos = fmt_strdots(curpos, bufend,
>                             "error (%d) %s\n", errnum, strerror(errnum));
> 
>     if (flags & ERR_ABORT)
>         priority = LOG_CRIT;
>     else if (flags & ERR_EXIT)
>         priority = LOG_ERR;
>     else
>         priority = LOG_WARNING;
>     syslog(priority, "%s", buffer);
> }
> #endif /* USE_STDERR_SYSLOG */
> 
> #if defined(USE_STDERR_FILEDESC)
> /* err_filedes() - report error via file descriptor */
> static void (err_filedes)(int fd, int flags, int errnum, const char *format, 
> va_list args)
> {
>     char buffer[MAX_MSGLEN];
>     char *curpos = buffer;
>     char *bufend = buffer + sizeof(buffer);
>     ssize_t nbytes;
> 
>     buffer[0] = '\0';   /* Not strictly necessary */
>     if ((flags & ERR_NOARG0) == 0)
>         curpos = fmt_strdots(curpos, bufend, "%s: ", arg0);
>     if (flags & ERR_LOGTIME)
>     {
>         char timbuf[32];
>         curpos = fmt_strdots(curpos, bufend,
>                              "%s - ", err_time(flags, timbuf, 
> sizeof(timbuf)));
>     }
>     if (flags & ERR_PID)
>         curpos = fmt_strdots(curpos, bufend,
>                              "pid=%d: ", (int)getpid());
>     curpos = fmt_string(curpos, bufend, format, args);
>     if (flags & ERR_ERRNO)
>         curpos = fmt_strdots(curpos, bufend,
>                              "error (%d) %s\n", errnum, strerror(errnum));
>     /* There's no sensible way to handle short writes! */
>     nbytes = write(fd, buffer, (size_t)(curpos - buffer));
>     assert(nbytes == curpos - buffer);
> }
> #endif /* USE_STDERR_FILEDESC */
> 
> /* Most fundamental (and flexible) error message printing routine - always 
> returns */
> static void (err_vrf_print)(FILE *fp, int flags, const char *format, va_list 
> args)
> {
>     int errnum = errno;     /* Capture errno before it is damaged! */
> 
>     if ((flags & ERR_NOFLUSH) == 0)
>         fflush(0);
> 
> #if defined(USE_STDERR_SYSLOG)
>     if (errlog)
>         err_syslog(flags, errnum, format, args);
>     else
> #endif /* USE_STDERR_SYSLOG */
> #if defined(USE_STDERR_FILEDESC)
>     if (err_fd >= 0)
>         err_filedes(err_fd, flags, errnum, format, args);
>     else
> #endif /* USE_STDERR_FILEDESC */
>         err_stdio(fp, flags, errnum, format, args);
> 
>     fflush(fp);
> }
> 
> /* Terminate program - abort or exit */
> static void err_terminate(int flags, int estat)
> {
>     assert(flags & (ERR_ABORT|ERR_EXIT));
>     if (flags & ERR_ABORT)
>         abort();
>     exit(estat);
> }
> 
> /* Most fundamental (and flexible) error message printing routine - may 
> return */
> static void (err_vcf_print)(FILE *fp, int flags, int estat, const char 
> *format, va_list args)
> {
>     err_vrf_print(fp, flags, format, args);
>     if (flags & (ERR_ABORT|ERR_EXIT))
>         err_terminate(flags, estat);
> }
> 
> /* Analog of err_vcf_print() which guarantees 'no return' */
> static void (err_vxf_print)(FILE *fp, int flags, int estat, const char 
> *format, va_list args)
> {
>     err_vrf_print(fp, flags, format, args);
>     err_terminate(flags, estat);
> }
> 
> /* External interface to err_vcf_print() - may return */
> void (err_logmsg)(FILE *fp, int flags, int estat, const char *format, ...)
> {
>     va_list         args;
> 
>     va_start(args, format);
>     err_vcf_print(fp, flags, estat, format, args);
>     va_end(args);
> }
> 
> /* Print error message to current error output - no return */
> static void (err_vxn_print)(int flags, int estat, const char *format, va_list 
> args)
> {
>     if (errout == 0)
>         errout = stderr;
>     err_vxf_print(errout, flags, estat, format, args);
> }
> 
> /* Print error message to current error output - no return */
> static void err_exn_print(int flags, int estat, const char *format, ...)
> {
>     va_list args;
> 
>     va_start(args, format);
>     err_vxn_print(flags, estat, format, args);
>     va_end(args);
> }
> 
> /* Print error message to nominated output - always returns */
> static void err_erf_print(FILE *fp, int flags, const char *format, ...)
> {
>     va_list args;
> 
>     va_start(args, format);
>     err_vrf_print(fp, flags, format, args);
>     va_end(args);
> }
> 
> /* Print message using current error file */
> void (err_print)(int flags, int estat, const char *format, va_list args)
> {
>     if (errout == 0)
>         errout = stderr;
>     err_vcf_print(errout, flags, estat, format, args);
> }
> 
> static void err_vrn_print(int flags, const char *format, va_list args)
> {
>     if (errout == 0)
>         errout = stderr;
>     err_vrf_print(errout, flags, format, args);
> }
> 
> /* Report warning including message from errno */
> void (err_sysrem)(const char *format, ...)
> {
>     va_list         args;
> 
>     va_start(args, format);
>     err_vrn_print(ERR_SYSREM | err_getlogopts(), format, args);
>     va_end(args);
> }
> 
> /* Report warning including message from errno */
> void (err_sysremark)(int errnum, const char *format, ...)
> {
>     va_list         args;
>     int old_errno = errno;
> 
>     errno = errnum;
>     va_start(args, format);
>     err_vrn_print(ERR_SYSREM | err_getlogopts(), format, args);
>     va_end(args);
>     errno = old_errno;
> }
> 
> /* Report error including message from errno */
> void (err_syserr)(const char *format, ...)
> {
>     va_list         args;
> 
>     va_start(args, format);
>     err_vxn_print(ERR_SYSERR | err_getlogopts(), ERR_STAT, format, args);
>     va_end(args);
> }
> 
> /* Report error including message from errno */
> void (err_syserror)(int errnum, const char *format, ...)
> {
>     va_list         args;
>     int old_errno = errno;
> 
>     errno = errnum;
>     va_start(args, format);
>     err_vxn_print(ERR_SYSERR | err_getlogopts(), ERR_STAT, format, args);
>     va_end(args);
>     errno = old_errno;
> }
> 
> /* Report warning */
> void (err_remark)(const char *format, ...)
> {
>     va_list         args;
> 
>     va_start(args, format);
>     err_vrn_print(ERR_REM | err_getlogopts(), format, args);
>     va_end(args);
> }
> 
> /* Report error */
> void (err_error)(const char *format, ...)
> {
>     va_list         args;
> 
>     va_start(args, format);
>     err_vxn_print(ERR_ERR | err_getlogopts(), ERR_STAT, format, args);
>     va_end(args);
> }
> 
> /* Report message - sometimes exiting too */
> void (err_report)(int flags, int estat, const char *format, ...)
> {
>     va_list         args;
> 
>     va_start(args, format);
>     err_print(flags, estat, format, args);
>     va_end(args);
> }
> 
> /* Print usage message and exit with failure status */
> void (err_usage)(const char *s1)
> {
>     err_exn_print(ERR_NOARG0|ERR_EXIT, EXIT_FAILURE, "Usage: %s %s\n", 
> err_getarg0(), s1);
> }
> 
> /* Report failure and generate core dump */
> void (err_abort)(const char *format, ...)
> {
>     va_list         args;
> 
>     va_start(args, format);
>     err_vxn_print(ERR_ABORT | err_getlogopts(), EXIT_FAILURE, format, args);
>     va_end(args);
> }
> 
> /* Report version information (no exit), removing embedded RCS keyword 
> strings (but not values) */
> void (err_printversion)(const char *program, const char *verinfo)
> {
>     char buffer[64];
> 
>     if (strchr(verinfo, '$'))
>         verinfo = err_rcs_string(verinfo, buffer, sizeof(buffer));
>     err_erf_print(stdout, ERR_DEFAULT, "%s Version %s\n", program, verinfo);
> }
> 
> /* Report version information and exit, removing embedded RCS keyword strings 
> (but not values) */
> void (err_version)(const char *program, const char *verinfo)
> {
>     err_printversion(program, verinfo);
>     exit(EXIT_SUCCESS);
> }
> 
> /* Report an internal error and exit */
> /* Abort if JLSS_INTERNAL_ERROR_ABORT set in environment */
> void (err_internal)(const char *function, const char *format, ...)
> {
>     va_list args;
>     int flags = ERR_EXIT;
>     const char *ev = getenv("JLSS_INTERNAL_ERROR_ABORT");
> 
>     va_start(args, format);
>     if (ev != 0 && *ev != '\0')
>         flags = ERR_ABORT;  /* Generate core dump */
>     err_remark("unrecoverable internal error in function %s():\n", function);
>     err_vxn_print(flags | err_getlogopts(), EXIT_FAILURE, format, args);
>     va_end(args);
> }
> 
> #ifdef TEST
> 
> #error Use separate test program test.stderr.c
> 
> #endif /* TEST */

> /*
> @(#)File:           $RCSfile: stderr.h,v $
> @(#)Version:        $Revision: 10.10 $
> @(#)Last changed:   $Date: 2015/10/14 23:12:19 $
> @(#)Purpose:        Header file for standard error functions
> @(#)Author:         J Leffler
> @(#)Copyright:      (C) JLSS 1989-93,1996-99,2003,2005-11,2015
> @(#)Product:        :PRODUCT:
> */
> 
> #if !defined(STDERR_H)
> #define STDERR_H
> 
> #if defined(MAIN_PROGRAM)
> #if !defined(lint)
> /* Prevent over-aggressive optimizers from eliminating ID string */
> extern const char jlss_id_stderr_h[];
> const char jlss_id_stderr_h[] = "@(#)$Id: stderr.h,v 10.10 2015/10/14 
> 23:12:19 jleffler Exp $";
> #endif /* lint */
> #endif
> 
> #if defined(HAVE_CONFIG_H)
> #include "config.h"
> #endif /* HAVE_CONFIG_H */
> 
> #if defined(JLSS_STDERR)
> #undef  USE_STDERR_FILEDESC
> #define USE_STDERR_FILEDESC
> #if defined(HAVE_SYSLOG) && defined(HAVE_SYSLOG_H)
> #undef  USE_STDERR_SYSLOG
> #define USE_STDERR_SYSLOG
> #endif /* HAVE_SYSLOG && HAVE_SYSLOG_H */
> #endif /* JLSS_STDERR */
> 
> #include <stdio.h>
> #include <stdarg.h>
> 
> #if !defined(NORETURN)
> #if __STDC_VERSION__ >= 201112L
> #define NORETURN      _Noreturn
> #elif defined(__GNUC__)
> #define NORETURN      __attribute__((noreturn))
> #else
> #define NORETURN      /* If only */
> #endif /* __STDC_VERSION__ || __GNUC__ */
> #endif /* NORETURN */
> 
> #if !defined(PRINTFLIKE)
> #if defined(__GNUC__)
> #define PRINTFLIKE(n,m) __attribute__((format(printf,n,m)))
> #else
> #define PRINTFLIKE(n,m) /* If only */
> #endif /* __GNUC__ */
> #endif /* PRINTFLIKE */
> 
> /* -- Definitions for error handling */
> 
> enum { ERR_STAT    = 1 };           /* Default exit status     */
> 
> enum { ERR_DEFAULT = 0x0000 };      /* Default flag                      */
> enum { ERR_NOFLUSH = 0x0001 };      /* Do not flush open files           */
> enum { ERR_EXIT    = 0x0004 };      /* Exit  -- do not return            */
> enum { ERR_ABORT   = 0x0008 };      /* Abort -- do not return            */
> enum { ERR_STAMP   = 0x0020 };      /* Timestamp messages (whole second) */
> enum { ERR_NOARG0  = 0x0040 };      /* Do not print arg0 prefix          */
> enum { ERR_PID     = 0x0080 };      /* Include pid=nnnnn info            */
> enum { ERR_ERRNO   = 0x0100 };      /* Include system error              */
> enum { ERR_MILLI   = 0x0200 };      /* Timestamp messages (millisecond)  */
> enum { ERR_MICRO   = 0x0400 };      /* Timestamp messages (microsecond)  */
> enum { ERR_NANO    = 0x0800 };      /* Timestamp messages (nanosecond)   */
> 
> #if defined(USE_STDERR_SYSLOG)
> /* Definitions related to using syslog */
> enum { ERR_LOG_EMERG    = 0x01000 };    /* system is unusable */
> enum { ERR_LOG_ALERT    = 0x02000 };    /* action must be taken immediately */
> enum { ERR_LOG_CRIT     = 0x04000 };    /* critical conditions */
> enum { ERR_LOG_ERR      = 0x08000 };    /* error conditions */
> enum { ERR_LOG_WARNING  = 0x10000 };    /* warning conditions */
> enum { ERR_LOG_NOTICE   = 0x20000 };    /* normal but significant condition */
> enum { ERR_LOG_INFO     = 0x40000 };    /* informational */
> enum { ERR_LOG_DEBUG    = 0x80000 };    /* debug-level messages */
> enum { ERR_LOG_LEVEL_HI = 
> ERR_LOG_EMERG|ERR_LOG_ALERT|ERR_LOG_CRIT|ERR_LOG_ERR };
> enum { ERR_LOG_LEVEL_LO = 
> ERR_LOG_WARNING|ERR_LOG_NOTICE|ERR_LOG_INFO|ERR_LOG_DEBUG };
> enum { ERR_LOG_LEVEL    = ERR_LOG_LEVEL_HI|ERR_LOG_LEVEL_LO };
> #endif /* USE_STDERR_SYSLOG */
> 
> /* -- Standard combinations of flags */
> 
> enum { ERR_REM    = ERR_DEFAULT       };
> enum { ERR_ERR    = ERR_EXIT          };
> enum { ERR_ABT    = ERR_ABORT         };
> enum { ERR_LOG    = ERR_STAMP|ERR_PID };
> enum { ERR_SYSREM = ERR_REM|ERR_ERRNO };
> enum { ERR_SYSERR = ERR_ERR|ERR_ERRNO };
> 
> /* -- Maximum recorded length of argv[0]; extra is truncated */
> 
> enum { ERR_MAXLEN_ARGV0 = 63 };
> 
> /* -- Global definitions */
> 
> extern const char *err_getarg0(void);
> extern void        err_setarg0(const char *argv0);
> 
> extern FILE       *err_stderr(FILE *fp);
> extern const char *err_rcs_string(const char *s, char *buffer, size_t buflen);
> 
> extern NORETURN void err_abort(const char *format, ...) PRINTFLIKE(1,2);
> extern NORETURN void err_error(const char *format, ...) PRINTFLIKE(1,2);
> extern NORETURN void err_error1(const char *s1);
> extern NORETURN void err_error2(const char *s1, const char *s2);
> extern NORETURN void err_help(const char *use_str, const char *hlp_str);
> extern NORETURN void err_helplist(const char *use_str, const char * const 
> *help_list);
> extern NORETURN void err_internal(const char *function, const char *format, 
> ...) PRINTFLIKE(2,3);
> extern NORETURN void err_syserr(const char *format, ...) PRINTFLIKE(1,2);
> extern NORETURN void err_syserr1(const char *s1);
> extern NORETURN void err_syserr2(const char *s1, const char *s2);
> extern NORETURN void err_syserror(int errnum, const char *format, ...) 
> PRINTFLIKE(2,3);
> extern NORETURN void err_usage(const char *usestr);
> extern NORETURN void err_version(const char *program, const char *verinfo);
> 
> extern void err_logmsg(FILE *fp, int flags, int estat, const char *format, 
> ...) PRINTFLIKE(4,5);
> extern void err_print(int flags, int estat, const char *format, va_list args);
> extern void err_printversion(const char *program, const char *verinfo);
> extern void err_remark(const char *format, ...) PRINTFLIKE(1,2);
> extern void err_remark1(const char *s1);
> extern void err_remark2(const char *s1, const char *s2);
> extern void err_report(int flags, int estat, const char *format, ...) 
> PRINTFLIKE(3,4);
> extern void err_sysrem(const char *format, ...) PRINTFLIKE(1,2);
> extern void err_sysrem1(const char *s1);
> extern void err_sysrem2(const char *s1, const char *s2);
> extern void err_sysremark(int errnum, const char *format, ...) 
> PRINTFLIKE(2,3);
> 
> extern int  err_getlogopts(void);           /* Get default log options */
> extern int  err_setlogopts(int new_opts);   /* Set default log options */
> 
> #if defined(USE_STDERR_FILEDESC)
> extern int  err_use_fd(int fd);             /* Use file descriptor */
> #endif /* USE_STDERR_FILEDESC */
> #if defined(USE_STDERR_SYSLOG)
> /* In case of doubt, use zero for both logopts and facility */
> extern int  err_use_syslog(int logopts, int facility);  /* Configure/use 
> syslog() */
> #endif /* USE_STDERR_SYSLOG */
> 
> /*
> ** JL 2003-07-31: Security Note.
> ** Question: given that err_remark("abc\n") and err_remark1("abc")
> **           produce the same output, when should you use err_remark1()
> **           instead of err_remark()?
> ** Answer 1: trivia - when you can't put the newline in the string.
> ** Answer 2: security - when the argument contains user input and could,
> **           therefore, contain conversion specifiers, etc.  The code in
> **           err_remark() does not (and cannot) verify that you have
> **           passed correct arguments for the conversion specifiers in
> **           the format string.
> ** Answer 3: inertia - when migrating code that uses remark().
> **
> ** Generalizing: when you use a function that has 'const char *format'
> ** in the prototype above, make sure your code is fully in charge of the
> ** format string to avoid security lapses.  Do not allow the user to
> ** provide that string unless you stringently check it beforehand.
> */
> 
> #endif /* STDERR_H */





reply via email to

[Prev in Thread] Current Thread [Next in Thread]