Imported Upstream version 3.1.0
[debian/amanda] / common-src / file.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1997-1998 University of Maryland at College Park
4  * All Rights Reserved.
5  *
6  * Permission to use, copy, modify, distribute, and sell this software and its
7  * documentation for any purpose is hereby granted without fee, provided that
8  * the above copyright notice appear in all copies and that both that
9  * copyright notice and this permission notice appear in supporting
10  * documentation, and that the name of U.M. not be used in advertising or
11  * publicity pertaining to distribution of the software without specific,
12  * written prior permission.  U.M. makes no representations about the
13  * suitability of this software for any purpose.  It is provided "as is"
14  * without express or implied warranty.
15  *
16  * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
18  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22  *
23  * Author: AMANDA core development group.
24  */
25 /*
26  * $Id: file.c,v 1.40 2006/07/19 17:41:15 martinea Exp $
27  *
28  * file and directory bashing routines
29  */
30
31 #include "amanda.h"
32 #include "util.h"
33 #include "timestamp.h"
34 #include "arglist.h"
35 #include "file.h"
36
37 static void areads_getbuf(const char *s, int l, int fd);
38 static char *original_cwd = NULL;
39
40 /*
41  * Make a directory hierarchy given an entry to be created (by the caller)
42  * in the new target.  In other words, create all the directories down to
43  * the last element, but not the last element.  So a (potential) file name
44  * may be passed to mkpdir and all the parents of that file will be created.
45  */
46 int
47 mkpdir(
48     char *      file,   /* file to create parent directories for */
49     mode_t      mode,   /* mode for new directories */
50     uid_t       uid,    /* uid for new directories */
51     gid_t       gid)    /* gid for new directories */
52 {
53     char *dir;
54     char *p;
55     int rc;     /* return code */
56
57     rc = 0;
58
59     /* Remove last member of file, put the result in dir */
60     dir = stralloc(file); /* make a copy we can play with */
61     p = strrchr(dir, '/');
62     *p = '\0';
63
64     rc = mkdir(dir, mode);
65     if (rc != 0) {
66         if (errno == ENOENT) { /* create parent directory */
67             rc = mkpdir(dir, mode, uid, gid);
68             if (rc != 0)
69                 return rc;
70             rc = mkdir(dir, mode);
71         }
72         if (rc != 0 && errno == EEXIST) {
73             amfree(dir);
74             return 0;
75         }
76     }
77
78     /* mkdir succeded, set permission and ownership */
79     if (rc == 0) {
80         /* mkdir is affected by umask, so set the mode bits manually */
81         rc = chmod(dir, mode);
82
83         if (rc == 0 && geteuid() == 0) {
84             rc = chown(dir, uid, gid);
85         }
86     }
87
88     amfree(dir);
89     return rc;
90 }
91
92
93 /* Remove as much of a directory hierarchy as possible.
94 ** Notes:
95 **  - assumes that rmdir() on a non-empty directory will fail!
96 **  - stops deleting before topdir, ie: topdir will not be removed
97 **  - if file is not under topdir this routine will not notice
98 */
99 int
100 rmpdir(
101     char *      file,   /* directory hierarchy to remove */
102     char *      topdir) /* where to stop removing */
103 {
104     int rc;
105     char *p, *dir;
106
107     if(strcmp(file, topdir) == 0) return 0; /* all done */
108
109     rc = rmdir(file);
110     if (rc != 0) switch(errno) {
111 #ifdef ENOTEMPTY
112 #if ENOTEMPTY != EEXIST                 /* AIX makes these the same */
113         case ENOTEMPTY:
114 #endif
115 #endif
116         case EEXIST:    /* directory not empty */
117             return 0; /* cant do much more */
118         case ENOENT:    /* it has already gone */
119             rc = 0; /* ignore */
120             break;
121         case ENOTDIR:   /* it was a file */
122             rc = unlink(file);
123             break;
124         }
125
126     if(rc != 0) return -1; /* unexpected error */
127
128     dir = stralloc(file);
129
130     p = strrchr(dir, '/');
131     if (p == NULL || p == dir) {
132         rc = 0;
133     } else {
134         *p = '\0';
135         rc = rmpdir(dir, topdir);
136     }
137
138     amfree(dir);
139
140     return rc;
141 }
142
143
144 /*
145  *=====================================================================
146  * Change directory to a "safe" location and set some base environment.
147  *
148  * void safe_cd (void)
149  *
150  * Set a default umask of 0077.
151  *
152  * Create the Amada debug directory (if defined) and the Amanda temp
153  * directory.
154  *
155  * Try to chdir to the Amanda debug directory first, but it must be owned
156  * by the Amanda user and not allow rwx to group or other.  Otherwise,
157  * try the same thing to the Amanda temp directory.
158  *
159  * If that is all OK, call save_core().
160  *
161  * Otherwise, cd to "/" so if we take a signal we cannot drop core
162  * unless the system administrator has made special arrangements (e.g.
163  * pre-created a core file with the right ownership and permissions).
164  *=====================================================================
165  */
166
167 void
168 safe_cd(void)
169 {
170     int                 cd_ok = 0;
171     struct stat         sbuf;
172     char                *d;
173     uid_t               client_uid = get_client_uid();
174     gid_t               client_gid = get_client_gid();
175
176     (void) umask(0077);
177
178     /* stash away the current directory for later reference */
179     if (original_cwd == NULL) {
180         original_cwd = g_get_current_dir();
181     }
182
183     if (client_uid != (uid_t) -1) {
184 #if defined(AMANDA_DBGDIR)
185         d = stralloc2(AMANDA_DBGDIR, "/.");
186         (void) mkpdir(d, (mode_t)0700, client_uid, client_gid);
187         amfree(d);
188 #endif
189         d = stralloc2(AMANDA_TMPDIR, "/.");
190         (void) mkpdir(d, (mode_t)0700, client_uid, client_gid);
191         amfree(d);
192     }
193
194 #if defined(AMANDA_DBGDIR)
195     if (chdir(AMANDA_DBGDIR) != -1
196         && stat(".", &sbuf) != -1
197         && (sbuf.st_mode & 0777) == 0700        /* drwx------ */
198         && sbuf.st_uid == client_uid) {         /* owned by Amanda user */
199         cd_ok = 1;                              /* this is a good place to be */
200     }
201 #endif
202     if (! cd_ok
203         && chdir(AMANDA_TMPDIR) != -1
204         && stat(".", &sbuf) != -1
205         && (sbuf.st_mode & 0777) == 0700        /* drwx------ */
206         && sbuf.st_uid == client_uid) {         /* owned by Amanda user */
207         cd_ok = 1;                              /* this is a good place to be */
208     }
209     if(cd_ok) {
210         save_core();                            /* save any old core file */
211     } else {
212         if ((cd_ok = chdir("/")) == -1) {
213             (void)cd_ok;        /* Quiet compiler warning if DEBUG disabled */
214         }
215     }
216 }
217
218 /*
219  *=====================================================================
220  * Close all file descriptors except stdin, stdout and stderr.  Make
221  * sure they are open.
222  *
223  * void safe_fd (fd_start, fd_count)
224  *
225  * entry:       fd_start - start of fd-s to leave alone (or -1)
226  *              fd_count - count of fd-s to leave alone
227  * exit:        none
228  *
229  * On exit, all three standard file descriptors will be open and pointing
230  * someplace (either what we were handed or /dev/null) and all other
231  * file descriptors (up to FD_SETSIZE) will be closed.
232  *=====================================================================
233  */
234
235 void
236 safe_fd(
237     int         fd_start,
238     int         fd_count)
239 {
240     int                 fd;
241
242     for(fd = 0; fd < (int)FD_SETSIZE; fd++) {
243         if (fd < 3) {
244             /*
245              * Open three file descriptors.  If one of the standard
246              * descriptors is not open it will be pointed to /dev/null...
247              *
248              * This avoids, for instance, someone running us with stderr
249              * closed so that when we open some other file, messages
250              * sent to stderr do not accidentally get written to the
251              * wrong file.
252              */
253             if (fcntl(fd, F_GETFD) == -1) {
254                 if (open("/dev/null", O_RDWR) == -1) {
255                    g_fprintf(stderr, _("/dev/null is inaccessable: %s\n"),
256                            strerror(errno));
257                    exit(1);
258                 }
259             }
260         } else {
261             /*
262              * Make sure nobody spoofs us with a lot of extra open files
263              * that would cause an open we do to get a very high file
264              * descriptor, which in turn might be used as an index into
265              * an array (e.g. an fd_set).
266              */
267             if (fd < fd_start || fd >= fd_start + fd_count) {
268                 close(fd);
269             }
270         }
271     }
272 }
273
274 /*
275  *=====================================================================
276  * Save an existing core file.
277  *
278  * void save_core (void)
279  *
280  * entry:       none
281  * exit:        none
282  *
283  * Renames:
284  *
285  *      "core"          to "coreYYYYMMDD",
286  *      "coreYYYYMMDD"  to "coreYYYYMMDDa",
287  *      "coreYYYYMMDDa" to "coreYYYYMMDDb",
288  *      ...
289  *
290  * ... where YYYYMMDD is the modification time of the original file.
291  * If it gets that far, an old "coreYYYYMMDDz" is thrown away.
292  *=====================================================================
293  */
294
295 void
296 save_core(void)
297 {
298     struct stat sbuf;
299
300     if(stat("core", &sbuf) != -1) {
301         char *ts;
302         char suffix[2];
303         char *old, *new;
304
305         ts = get_datestamp_from_time(sbuf.st_mtime);
306         suffix[0] = 'z';
307         suffix[1] = '\0';
308         old = vstralloc("core", ts, suffix, NULL);
309         new = NULL;
310         while(ts[0] != '\0') {
311             amfree(new);
312             new = old;
313             if(suffix[0] == 'a') {
314                 suffix[0] = '\0';
315             } else if(suffix[0] == '\0') {
316                 ts[0] = '\0';
317             } else {
318                 suffix[0]--;
319             }
320             old = vstralloc("core", ts, suffix, NULL);
321             (void)rename(old, new);         /* it either works ... */
322         }
323         amfree(ts);
324         amfree(old);
325         amfree(new);
326     }
327 }
328
329 /*
330 ** Sanitise a file name.
331 ** 
332 ** Convert all '/', ':', and '\' characters to '_' so that we can use,
333 ** for example, disk names as part of file names.
334 ** Notes: 
335 **  - there is a many-to-one mapping between input and output
336 **  - Only / and '\0' are disallowed in filenames by POSIX, but Windows
337 **    disallows ':' and '\' as well.  Furthermore, we use ':' as a 
338 **    delimiter at other points in Amanda.
339 */
340 char *
341 sanitise_filename(
342     char *      inp)
343 {
344     char *buf;
345     size_t buf_size;
346     char *s, *d;
347     int ch;
348
349     buf_size = strlen(inp) + 1;         /* worst case */
350     buf = alloc(buf_size);
351     d = buf;
352     s = inp;
353     while((ch = *s++) != '\0') {
354         if((ch == '/') || (ch == ':') || (ch == '\\')) {
355             ch = '_';   /* convert "bad" to "_" */
356         }
357         *d++ = (char)ch;
358     }
359     assert(d < buf + buf_size);
360     *d = '\0';
361
362     return buf;
363 }
364
365 /* duplicate '_' */
366 char *
367 old_sanitise_filename(
368     char *      inp)
369 {
370     char *buf;
371     size_t buf_size;
372     char *s, *d;
373     int ch;
374
375     buf_size = 2*strlen(inp) + 1;               /* worst case */
376     buf = alloc(buf_size);
377     d = buf;
378     s = inp;
379     while((ch = *s++) != '\0') {
380         if(ch == '_') {
381             *d++ = (char)ch;
382         }
383         if(ch == '/') {
384             ch = '_';   /* convert "bad" to "_" */
385         }
386         *d++ = (char)ch;
387     }
388     assert(d < buf + buf_size);
389     *d = '\0';
390
391     return buf;
392 }
393
394 void
395 canonicalize_pathname(char *pathname, char *result_buf)
396 {
397 #ifdef __CYGWIN__
398     cygwin_conv_to_full_posix_path(pathname, result_buf);
399 #else
400     strncpy(result_buf, pathname, PATH_MAX-1);
401     result_buf[PATH_MAX-1] = '\0';
402 #endif
403 }
404
405 /*
406  *=====================================================================
407  * Get the next line of input from a stdio file.
408  *
409  * char *agets (FILE *stream)
410  *
411  * entry:       stream  -  stream to read
412  * exit:        returns a pointer to an alloc'd string or NULL
413  *              at EOF or error.  The functions ferror(stream) and
414  *              feof(stream) should be checked by caller to determine
415  *              stream status.
416  *
417  * Notes:       the newline at the end of a line, if read, is removed from
418  *              the string. Quoted newlines are left intact.
419  *              the caller is responsible for free'ing the string
420  *
421  *=====================================================================
422  */
423
424 #define AGETS_LINE_INCR 128
425
426 char *
427 debug_agets(
428     const char *sourcefile,
429     int         lineno,
430     FILE *      stream)
431 {
432     int ch;
433     char *line = alloc(AGETS_LINE_INCR);
434     size_t line_size = 0;
435     size_t loffset = 0;
436     int inquote = 0;
437     int escape = 0;
438
439     (void)sourcefile;   /* Quiet unused parameter warning if not debugging */
440     (void)lineno;       /* Quiet unused parameter warning if not debugging */
441
442     while ((ch = fgetc(stream)) != EOF) {
443         if (ch == '\n') {
444             if (!inquote) {
445                 if (escape) {
446                     escape = 0;
447                     loffset--;  /* Consume escape in buffer */
448                     continue;
449                 }
450                 /* Reached end of line so exit without passing on LF */
451                 break;
452             }
453         }
454
455         if (ch == '\\') {
456             escape = !escape;
457         } else {
458             if (ch == '"') {
459                 if (!escape) 
460                     inquote = !inquote;
461             }
462             escape = 0;
463         }
464
465         if ((loffset + 1) >= line_size) {
466             char *tmpline;
467
468             /*
469              * Reallocate input line.
470              * alloc() never return NULL pointer.
471              */
472             tmpline = alloc(line_size + AGETS_LINE_INCR);
473             memcpy(tmpline, line, line_size);
474             amfree(line);
475             line = tmpline;
476             line_size = line_size + AGETS_LINE_INCR;
477         }
478         line[loffset++] = (char)ch;
479     }
480
481     if ((ch == EOF) && (loffset == 0)) {
482         amfree(line); /* amfree zeros line... */
483     } else {
484         line[loffset] = '\0';
485     }
486
487     /*
488      * Return what we got even if there was not a newline.
489      * Only report done (NULL) when no data was processed.
490      */
491     return line;
492 }
493
494
495 /*
496  *=====================================================================
497  * Find/create a buffer for a particular file descriptor for use with
498  * areads().
499  *
500  * void areads_getbuf (const char *file, size_t line, int fd)
501  *
502  * entry:       file, line = caller source location
503  *              fd = file descriptor to look up
504  * exit:        returns a pointer to the buffer, possibly new
505  *=====================================================================
506  */
507
508 static struct areads_buffer {
509     char *buffer;
510     char *endptr;
511     size_t bufsize;
512 } *areads_buffer = NULL;
513 static int areads_bufcount = 0;
514 static size_t areads_bufsize = BUFSIZ;          /* for the test program */
515
516 static void
517 areads_getbuf(
518     const char *s,
519     int         l,
520     int         fd)
521 {
522     struct areads_buffer *new;
523     size_t size;
524
525     assert(fd >= 0);
526     if(fd >= areads_bufcount) {
527         size = (size_t)(fd + 1) * SIZEOF(*areads_buffer);
528         new = (struct areads_buffer *) debug_alloc(s, l, size);
529         memset((char *)new, 0, size);
530         if(areads_buffer) {
531             size = areads_bufcount * SIZEOF(*areads_buffer);
532             memcpy(new, areads_buffer, size);
533         }
534         amfree(areads_buffer);
535         areads_buffer = new;
536         areads_bufcount = fd + 1;
537     }
538     if(areads_buffer[fd].buffer == NULL) {
539         areads_buffer[fd].bufsize = areads_bufsize;
540         areads_buffer[fd].buffer = debug_alloc(s, l,
541                                                areads_buffer[fd].bufsize + 1);
542         areads_buffer[fd].buffer[0] = '\0';
543         areads_buffer[fd].endptr = areads_buffer[fd].buffer;
544     }
545 }
546
547 /*
548  *=====================================================================
549  * Return the amount of data still in an areads buffer.
550  *
551  * ssize_t areads_dataready (int fd)
552  *
553  * entry:       fd = file descriptor to release buffer for
554  * exit:        returns number of bytes of data ready to process
555  *=====================================================================
556  */
557
558 ssize_t
559 areads_dataready(
560     int fd)
561 {
562     ssize_t r = 0;
563
564     if(fd >= 0 && fd < areads_bufcount && areads_buffer[fd].buffer != NULL) {
565         r = (ssize_t) (areads_buffer[fd].endptr - areads_buffer[fd].buffer);
566     }
567     return r;
568 }
569
570 /*
571  *=====================================================================
572  * Release a buffer for a particular file descriptor used by areads().
573  *
574  * void areads_relbuf (int fd)
575  *
576  * entry:       fd = file descriptor to release buffer for
577  * exit:        none
578  *=====================================================================
579  */
580
581 void
582 areads_relbuf(
583     int fd)
584 {
585     if(fd >= 0 && fd < areads_bufcount) {
586         amfree(areads_buffer[fd].buffer);
587         areads_buffer[fd].endptr = NULL;
588         areads_buffer[fd].bufsize = 0;
589     }
590 }
591
592 /*
593  *=====================================================================
594  * Get the next line of input from a file descriptor.
595  *
596  * char *areads (int fd)
597  *
598  * entry:       fd = file descriptor to read
599  * exit:        returns a pointer to an alloc'd string or NULL at EOF
600  *              or error (errno will be zero on EOF).
601  *
602  * Notes:       the newline, if read, is removed from the string
603  *              the caller is responsible for free'ing the string
604  *=====================================================================
605  */
606
607 char *
608 debug_areads (
609     const char *s,
610     int         l,
611     int         fd)
612 {
613     char *nl;
614     char *line;
615     char *buffer;
616     char *endptr;
617     char *newbuf;
618     size_t buflen;
619     size_t size;
620     ssize_t r;
621
622     if(fd < 0) {
623         errno = EBADF;
624         return NULL;
625     }
626     areads_getbuf(s, l, fd);
627     buffer = areads_buffer[fd].buffer;
628     endptr = areads_buffer[fd].endptr;
629     buflen = areads_buffer[fd].bufsize - (size_t)(endptr - buffer);
630     while((nl = strchr(buffer, '\n')) == NULL) {
631         /*
632          * No newline yet, so get more data.
633          */
634         if (buflen == 0) {
635             if ((size = areads_buffer[fd].bufsize) < 256 * areads_bufsize) {
636                 size *= 2;
637             } else {
638                 size += 256 * areads_bufsize;
639             }
640             newbuf = debug_alloc(s, l, size + 1);
641             memcpy (newbuf, buffer, areads_buffer[fd].bufsize + 1);
642             amfree(areads_buffer[fd].buffer);
643             areads_buffer[fd].buffer = newbuf;
644             areads_buffer[fd].endptr = newbuf + areads_buffer[fd].bufsize;
645             areads_buffer[fd].bufsize = size;
646             buffer = areads_buffer[fd].buffer;
647             endptr = areads_buffer[fd].endptr;
648             buflen = areads_buffer[fd].bufsize - (size_t)(endptr - buffer);
649         }
650         if ((r = read(fd, endptr, buflen)) <= 0) {
651             if(r == 0) {
652                 errno = 0;              /* flag EOF instead of error */
653             }
654             return NULL;
655         }
656         endptr[r] = '\0';               /* we always leave room for this */
657         endptr += r;
658         buflen -= r;
659     }
660     *nl++ = '\0';
661     line = stralloc(buffer);
662     size = (size_t)(endptr - nl);       /* data still left in buffer */
663     memmove(buffer, nl, size);
664     areads_buffer[fd].endptr = buffer + size;
665     areads_buffer[fd].endptr[0] = '\0';
666     return line;
667 }
668
669 int robust_open(const char * pathname, int flags, mode_t mode) {
670     int result = -1;
671     int e_busy_count = 0;
672
673     for (;;) {
674         if (flags & O_CREAT) {
675             result = open(pathname, flags, mode);
676         } else {
677             result = open(pathname, flags);
678         }
679
680         if (result < 0) {
681 #ifdef EBUSY
682             /* EBUSY is a tricky one; sometimes it is synonymous with
683                EINTR, but sometimes it means the device is open
684                elsewhere (e.g., with a tape drive on Linux). We take
685                the middle path and retry, but with limited
686                patience. */
687             if (errno == EBUSY && e_busy_count < 10) {
688                 e_busy_count ++;
689                 continue;
690             } else
691 #endif
692             if (0
693                 /* Always retry on EINTR; if the caller did
694                    not specify non-blocking mode, then also retry on
695                    EAGAIN or EWOULDBLOCK. */
696 #ifdef EINTR
697                 || errno == EINTR
698 #endif
699                 || ( 1
700 #ifdef O_NONBLOCK
701                   && !(flags & O_NONBLOCK)
702 #endif
703                   && ( 0
704 #ifdef EAGAIN
705                        || errno == EAGAIN
706 #endif
707 #ifdef EWOULDBLOCK
708                        || errno == EWOULDBLOCK
709 #endif
710                        ) ) ) {
711                 /* Try again */
712                 continue;
713             } else {
714                 /* Failure. */
715                 return result;
716             }
717         } else {
718             break;
719         }
720     }
721
722 #ifdef F_SETFD
723     if (result >= 0) {
724         fcntl(result, F_SETFD, 1); /* Throw away result. */
725     }
726 #endif
727
728     return result;
729 }
730
731 int robust_close(int fd) {
732     for (;;) {
733         int result;
734
735         result = close(fd);
736         if (result != 0 && (0
737 #ifdef EINTR
738                             || errno == EINTR
739 #endif
740 #ifdef EBUSY
741                             || errno == EBUSY
742 #endif
743 #ifdef EAGAIN
744                             || errno == EAGAIN
745 #endif
746 #ifdef EWOULDBLOCK
747                             || errno == EWOULDBLOCK
748 #endif
749                             )) {
750             continue;
751         } else {
752             return result;
753         }
754     }
755 }
756
757 uid_t
758 get_client_uid(void)
759 {
760     static uid_t client_uid = (uid_t) -1;
761     struct passwd      *pwent;
762
763     if(client_uid == (uid_t) -1 && (pwent = getpwnam(CLIENT_LOGIN)) != NULL) {
764         client_uid = pwent->pw_uid;
765         endpwent();
766     }
767
768     return client_uid;
769 }
770
771 gid_t
772 get_client_gid(void)
773 {
774     static gid_t client_gid = (gid_t) -1;
775     struct passwd      *pwent;
776
777     if(client_gid == (gid_t) -1 && (pwent = getpwnam(CLIENT_LOGIN)) != NULL) {
778         client_gid = pwent->pw_gid;
779         endpwent();
780     }
781
782     return client_gid;
783 }
784
785 char *
786 get_original_cwd(void)
787 {
788     if (original_cwd == NULL) {
789         original_cwd = g_get_current_dir();
790     }
791
792     return original_cwd;
793 }
794
795 #ifdef TEST
796
797 int
798 main(
799     int         argc,
800     char **     argv)
801 {
802         int rc;
803         int fd;
804         char *name;
805         char *top;
806         char *file;
807         char *line;
808
809         /*
810          * Configure program for internationalization:
811          *   1) Only set the message locale for now.
812          *   2) Set textdomain for all amanda related programs to "amanda"
813          *      We don't want to be forced to support dozens of message catalogs
814          */  
815         setlocale(LC_MESSAGES, "C");
816         textdomain("amanda"); 
817
818         safe_fd(-1, 0);
819
820         set_pname("file test");
821
822         dbopen(NULL);
823
824         /* Don't die when child closes pipe */
825         signal(SIGPIPE, SIG_IGN);
826
827         name = "/tmp/a/b/c/d/e";
828         if (argc > 2 && argv[1][0] != '\0') {
829                 name = argv[1];
830         }
831         top = "/tmp";
832         if (argc > 3 && argv[2][0] != '\0') {
833                 name = argv[2];
834         }
835         file = "/etc/hosts";
836         if (argc > 4 && argv[3][0] != '\0') {
837                 name = argv[3];
838         }
839
840         g_fprintf(stderr, _("Create parent directories of %s ..."), name);
841         rc = mkpdir(name, (mode_t)02777, (uid_t)-1, (gid_t)-1);
842         if (rc == 0)
843                 g_fprintf(stderr, " done\n");
844         else {
845                 perror(_("failed"));
846                 return rc;
847         }
848
849         g_fprintf(stderr, _("Delete %s back to %s ..."), name, top);
850         rc = rmpdir(name, top);
851         if (rc == 0)
852                 g_fprintf(stderr, _(" done\n"));
853         else {
854                 perror(_("failed"));
855                 return rc;
856         }
857
858         g_fprintf(stderr, _("areads dump of %s ..."), file);
859         if ((fd = open (file, 0)) < 0) {
860                 perror(file);
861                 return 1;
862         }
863         areads_bufsize = 1;                     /* force buffer overflow */
864         while ((line = areads(fd)) != NULL) {
865                 puts(line);
866                 amfree(line);
867         }
868         aclose(fd);
869         g_fprintf(stderr, _(" done.\n"));
870
871         g_fprintf(stderr, _("Finished.\n"));
872
873         dbclose();
874         return 0;
875 }
876
877 #endif
878