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