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