Imported Upstream version 3.3.3
[debian/amanda] / client-src / getfsent.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-1998, 2001 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  * Authors: the Amanda Development Team.  Its members are listed in a
25  * file named AUTHORS, in the root directory of this distribution.
26  */
27 /*
28  * $Id: getfsent.c,v 1.38 2006/07/19 17:41:14 martinea Exp $
29  *
30  * generic version of code to read fstab
31  */
32
33 #include "amanda.h"
34 #include "util.h"
35
36 #ifdef TEST
37 #  include <stdio.h>
38 #  include <sys/types.h>
39 #endif
40
41 #include "getfsent.h"
42
43 static char *dev2rdev(char *);
44
45 /*
46  * You are in a twisty maze of passages, all alike.
47  * Geesh.
48  */
49
50 #if defined(HAVE_FSTAB_H) && !defined(HAVE_MNTENT_H) /* { */
51 /*
52 ** BSD (GETFSENT_BSD)
53 */
54 #define GETFSENT_TYPE "BSD (Ultrix, AIX)"
55
56 #include <fstab.h>
57
58 int
59 open_fstab(void)
60 {
61     return setfsent();
62 }
63
64 void
65 close_fstab(void)
66 {
67     endfsent();
68 }
69
70
71 int
72 get_fstab_nextentry(
73     generic_fsent_t *   fsent)
74 {
75     struct fstab *sys_fsent = getfsent();
76     static char *xfsname = NULL, *xmntdir = NULL;
77     static char *xfstype = NULL, *xmntopts = NULL;
78
79     if(!sys_fsent)
80         return 0;
81     fsent->fsname  = xfsname  = newstralloc(xfsname,  sys_fsent->fs_spec);
82     fsent->mntdir  = xmntdir  = newstralloc(xmntdir,  sys_fsent->fs_file);
83     fsent->freq    = sys_fsent->fs_freq;
84     fsent->passno  = sys_fsent->fs_passno;
85 #ifdef STATFS_ULTRIX
86     fsent->fstype  = xfstype  = newstralloc(xfstype,  sys_fsent->fs_name);
87     fsent->mntopts = xmntopts = newstralloc(xmntopts, sys_fsent->fs_opts);
88 #else
89 #if defined(_AIX)
90     fsent->fstype  = xfstype  = newstralloc(xfstype,  _("unknown"));
91     fsent->mntopts = xmntopts = newstralloc(xmntopts, sys_fsent->fs_type);
92 #else
93     fsent->fstype  = xfstype  = newstralloc(xfstype,  sys_fsent->fs_vfstype);
94     fsent->mntopts = xmntopts = newstralloc(xmntopts, sys_fsent->fs_mntops);
95 #endif
96 #endif
97     return 1;
98 }
99
100 #else
101 #if defined(HAVE_SYS_VFSTAB_H) /* } { */
102 /*
103 ** SVR4 (GETFSENT_SOLARIS)
104 */
105 #define GETFSENT_TYPE "SVR4 (Solaris)"
106
107 #include <sys/vfstab.h>
108
109 static FILE *fstabf = NULL;
110
111 int
112 open_fstab(void)
113 {
114     close_fstab();
115     return (fstabf = fopen(VFSTAB, "r")) != NULL;
116 }
117
118 void
119 close_fstab(void)
120 {
121     if(fstabf)
122         afclose(fstabf);
123     fstabf = NULL;
124 }
125
126 int
127 get_fstab_nextentry(
128     generic_fsent_t *   fsent)
129 {
130     struct vfstab sys_fsent;
131
132     memset(&sys_fsent, 0, SIZEOF(sys_fsent));
133     if(getvfsent(fstabf, &sys_fsent) != 0)
134         return 0;
135
136     fsent->fsname  = sys_fsent.vfs_special;
137     fsent->fstype  = sys_fsent.vfs_fstype;
138     fsent->mntdir  = sys_fsent.vfs_mountp;
139     fsent->mntopts = sys_fsent.vfs_mntopts;
140     fsent->freq    = 1; /* N/A */
141     fsent->passno  = sys_fsent.vfs_fsckpass? atoi(sys_fsent.vfs_fsckpass) : 0;
142     return 1;
143 }
144
145 #else
146 #  if defined(HAVE_MNTENT_H) /* } { */
147
148 /*
149 ** System V.3 (GETFSENT_SVR3, GETFSENT_LINUX)
150 */
151 #define GETFSENT_TYPE "SVR3 (NeXTstep, Irix, Linux, HP-UX)"
152
153 #include <mntent.h>
154
155 #if defined(HAVE_ENDMNTENT)
156 #define AMCLOSE_MNTENT(x)       endmntent(x)
157 #else
158 #define AMCLOSE_MNTENT(x)       fclose(x)
159 #endif
160
161 static FILE *fstabf1 = NULL;            /* /proc/mounts */
162 static FILE *fstabf2 = NULL;            /* MOUNTED */
163 static FILE *fstabf3 = NULL;            /* MNTTAB */
164
165 int
166 open_fstab(void)
167 {
168     close_fstab();
169 #if defined(HAVE_SETMNTENT)
170     fstabf1 = setmntent("/proc/mounts", "r");
171 # if defined(MOUNTED)
172     fstabf2 = setmntent(MOUNTED, "r");
173 # endif
174 # if defined(MNTTAB)
175     fstabf3 = setmntent(MNTTAB, "r");
176 # endif
177 #else
178 # if defined(MNTTAB)
179     fstabf3 = fopen(MNTTAB, "r");
180 # endif
181 #endif
182     return (fstabf1 != NULL || fstabf2 != NULL || fstabf3 != NULL);
183 }
184
185 void
186 close_fstab(void)
187 {
188     if (fstabf1) {
189         AMCLOSE_MNTENT(fstabf1);
190         fstabf1 = NULL;
191     }
192     if (fstabf2) {
193         AMCLOSE_MNTENT(fstabf2);
194         fstabf2 = NULL;
195     }
196     if (fstabf3) {
197         AMCLOSE_MNTENT(fstabf3);
198         fstabf3 = NULL;
199     }
200 }
201
202 int
203 get_fstab_nextentry(
204     generic_fsent_t *   fsent)
205 {
206     struct mntent *sys_fsent = NULL;
207
208     if(fstabf1) {
209         sys_fsent = getmntent(fstabf1);
210         if(!sys_fsent) {
211             AMCLOSE_MNTENT(fstabf1);
212             fstabf1 = NULL;
213         }
214     }
215     if(!sys_fsent && fstabf2) {
216         sys_fsent = getmntent(fstabf2);
217         if(!sys_fsent) {
218             AMCLOSE_MNTENT(fstabf2);
219             fstabf2 = NULL;
220         }
221     }
222     if(!sys_fsent && fstabf3) {
223         sys_fsent = getmntent(fstabf3);
224         if(!sys_fsent) {
225             AMCLOSE_MNTENT(fstabf3);
226             fstabf3 = NULL;
227         }
228     }
229     if(!sys_fsent) {
230         return 0;
231     }
232
233     fsent->fsname  = sys_fsent->mnt_fsname;
234     fsent->fstype  = sys_fsent->mnt_type;
235     fsent->mntdir  = sys_fsent->mnt_dir;
236     fsent->mntopts = sys_fsent->mnt_opts;
237     fsent->freq    = sys_fsent->mnt_freq;
238     fsent->passno  = sys_fsent->mnt_passno;
239     return 1;
240 }
241
242 #  else
243 #    if defined(HAVE_SYS_MNTTAB_H) || defined(STATFS_SCO_OS5) /* } { */
244
245 /* we won't actually include mnttab.h, since it contains nothing useful.. */
246
247 #define GETFSENT_TYPE "SVR3 (Interactive UNIX)"
248
249 #include <stdio.h>
250 #include <string.h>
251 #include <ctype.h>
252
253 #define FSTAB "/etc/fstab"
254
255 static FILE *fstabf = NULL;
256
257 int
258 open_fstab(void)
259 {
260     close_fstab();
261     return (fstabf = fopen(FSTAB, "r")) != NULL;
262 }
263
264 void
265 close_fstab(void)
266 {
267     if(fstabf)
268         afclose(fstabf);
269     fstabf = NULL;
270 }
271
272 static generic_fsent_t _fsent;
273
274 int
275 get_fstab_nextentry(
276     generic_fsent_t *   fsent)
277 {
278     static char *lfsnam = NULL;
279     static char *opts = NULL;
280     static char *cp = NULL;
281     char *s;
282     int ch;
283
284     amfree(cp);
285     for (; (cp = agets(fstabf)) != NULL; free(cp)) {
286         if (cp[0] == '\0')
287             continue;
288         fsent->fsname = strtok(cp, " \t");
289         if ( fsent->fsname && *fsent->fsname != '#' )
290             break;
291     }
292     if (cp == NULL) return 0;
293
294     fsent->mntdir = strtok((char *)NULL, " \t");
295     fsent->mntopts = strtok((char *)NULL, " \t");
296     if ( *fsent->mntopts != '-' )  {
297         fsent->fstype = fsent->mntopts;
298         fsent->mntopts = "rw";
299     } else {
300         fsent->fstype = "";
301         if (strcmp(fsent->mntopts, "-r") == 0) {
302             fsent->mntopts = "ro";
303         }
304     }
305     if ((s = strchr(fsent->fstype, ',')) != NULL) {
306         *s++ = '\0';
307         strappend(fsent->mntopts, ",");
308         strappend(fsent->mntopts, s);
309     }
310
311     lfsnam = newstralloc(lfsnam, fsent->fstype);
312     s = lfsnam;
313     while((ch = *s++) != '\0') {
314         if(isupper(ch)) ch = tolower(ch);
315         s[-1] = ch;
316     }
317     fsent->fstype = lfsnam;
318
319     if (strncmp_const(fsent->fstype, "hs") == 0)
320         fsent->fstype = "iso9660";
321
322     fsent->freq = 0;
323     fsent->passno = 0;
324
325     return 1;
326 }
327
328 #    else
329 #      if defined(HAVE_MNTTAB_H) /* } { */
330
331 #define GETFSENT_TYPE "SVR3 (SCO UNIX)"
332
333 #include <mnttab.h>
334 #include <sys/fstyp.h>
335 #include <sys/statfs.h>
336
337 #define MNTTAB "/etc/mnttab"
338
339 /*
340  * If these are defined somewhere please let me know.
341  */
342
343 #define MNT_READONLY 0101
344 #define MNT_READWRITE 0100
345
346 static FILE *fstabf = NULL;
347
348 int
349 open_fstab(void)
350 {
351     close_fstab();
352     return (fstabf = fopen(MNTTAB, "r")) != NULL;
353 }
354
355 void
356 close_fstab(void)
357 {
358     if(fstabf)
359         afclose(fstabf);
360     fstabf = NULL;
361 }
362
363 static generic_fsent_t _fsent;
364
365 int
366 get_fstab_nextentry(
367     generic_fsent_t *fsent)
368 {
369     struct statfs fsd;
370     char typebuf[FSTYPSZ];
371     static struct mnttab mnt;
372     char *dp, *ep;
373
374     if(!fread (&mnt, SIZEOF(mnt), 1, fstabf))
375       return 0;
376
377     fsent->fsname  = mnt.mt_dev;
378     fsent->mntdir  = mnt.mt_filsys;
379     fsent->fstype = "";
380
381     if (statfs (fsent->mntdir, &fsd, SIZEOF(fsd), 0) != -1
382         && sysfs (GETFSTYP, fsd.f_fstyp, typebuf) != -1) {
383        dp = typebuf;
384        ep = fsent->fstype = malloc(strlen(typebuf)+2);
385        while (*dp)
386             *ep++ = tolower(*dp++);
387        *ep=0;
388     }
389
390     if ( mnt.mt_ro_flg == MNT_READONLY ) {
391         fsent->mntopts = "ro";
392     } else {
393         fsent->mntopts = "rw";
394     }
395
396     fsent->freq = 0;
397     fsent->passno = 0;
398     return 1;
399 }
400
401 #      else /* } { */
402
403 #define GETFSENT_TYPE "undefined"
404
405 #      endif
406 #    endif
407 #  endif
408 #endif
409 #endif /* } */
410
411 /*
412  *=====================================================================
413  * Convert either a block or character device name to a character (raw)
414  * device name.
415  *
416  * static char *dev2rdev(const char *name);
417  *
418  * entry:       name - device name to convert
419  * exit:        matching character device name if found,
420  *              otherwise returns the input
421  *
422  * The input must be an absolute path.
423  *
424  * The exit string area is always an alloc-d area that the caller is
425  * responsible for releasing.
426  *=====================================================================
427  */
428
429 static char *
430 dev2rdev(
431     char *      name)
432 {
433   char *fname = NULL;
434   struct stat st;
435   char *s;
436   int ch;
437
438   if(stat(name, &st) == 0 && !S_ISBLK(st.st_mode)) {
439     /*
440      * If the input is already a character device, just return it.
441      */
442     return stralloc(name);
443   }
444
445   s = name;
446   ch = *s++;
447
448   if(ch == '\0' || ch != '/') return stralloc(name);
449
450   ch = *s++;                                    /* start after first '/' */
451   /*
452    * Break the input path at each '/' and create a new name with an
453    * 'r' before the right part.  For instance:
454    *
455    *   /dev/sd0a -> /dev/rsd0a
456    *   /dev/dsk/c0t0d0s0 -> /dev/rdsk/c0t0d0s0 -> /dev/dsk/rc0t0d0s0
457    */
458   while(ch) {
459     if (ch == '/') {
460       s[-1] = '\0';
461       fname = newvstralloc(fname, name, "/r", s, NULL);
462       s[-1] = (char)ch;
463       if(stat(fname, &st) == 0 && S_ISCHR(st.st_mode)) return fname;
464     }
465     ch = *s++;
466   }
467   amfree(fname);
468   return stralloc(name);                        /* no match */
469 }
470
471 #ifndef IGNORE_FSTAB
472 static int samefile(struct stat[3], struct stat *);
473
474 static int
475 samefile(
476     struct stat stats[3],
477     struct stat *estat)
478 {
479   int i;
480   for(i = 0; i < 3; ++i) {
481     if (stats[i].st_dev == estat->st_dev &&
482         stats[i].st_ino == estat->st_ino)
483       return 1;
484   }
485   return 0;
486 }
487 #endif /* !IGNORE_FSTAB */
488
489 int
490 search_fstab(
491      char *             name,
492      generic_fsent_t *  fsent,
493      int                check_dev)
494 {
495 #ifdef IGNORE_FSTAB
496   /* There is no real mount table so this will always fail and
497    * we are using GNU tar so we can just return here.
498    */
499   (void)name;           /* Quiet unused parameter warning */
500   (void)fsent;          /* Quiet unused parameter warning */
501   (void)check_dev;      /* Quiet unused parameter warning */
502   return 0;
503 #else
504   struct stat stats[3];
505   char *fullname = NULL;
506   char *rdev = NULL;
507   int rc;
508
509   if (!name)
510     return 0;
511
512   memset(stats, 0, SIZEOF(stats));
513   stats[0].st_dev = stats[1].st_dev = stats[2].st_dev = (dev_t)-1;
514
515   if (stat(name, &stats[0]) == -1)
516     stats[0].st_dev = (dev_t)-1;
517   if (name[0] != '/') {
518     fullname = stralloc2(DEV_PREFIX, name);
519     if (stat(fullname, &stats[1]) == -1)
520       stats[1].st_dev = (dev_t)-1;
521     fullname = newstralloc2(fullname, RDEV_PREFIX, name);
522     if (stat(fullname, &stats[2]) == -1)
523       stats[2].st_dev = (dev_t)-1;
524     amfree(fullname);
525   }
526   else if (stat((rdev = dev2rdev(name)), &stats[1]) == -1)
527     stats[1].st_dev = (dev_t)-1;
528
529   amfree(rdev);
530
531   if (!open_fstab())
532     return 0;
533
534   rc = 0;
535   while(get_fstab_nextentry(fsent)) {
536     struct stat mntstat;
537     struct stat fsstat;
538     struct stat fsrstat;
539     int smnt = -1, sfs = -1, sfsr = -1;
540
541     amfree(rdev);
542
543     if(fsent->mntdir != NULL)
544        smnt = stat(fsent->mntdir, &mntstat);
545
546     if(fsent->fsname != NULL) {
547       sfs = stat(fsent->fsname, &fsstat);
548       sfsr = stat((rdev = dev2rdev(fsent->fsname)), &fsrstat);
549       if(check_dev == 1 && sfs == -1 && sfsr == -1)
550         continue;
551     }
552
553     if((fsent->mntdir != NULL &&
554         smnt != -1 &&
555         samefile(stats, &mntstat)) || 
556        (fsent->fsname != NULL &&
557         sfs != -1 &&
558         samefile(stats, &fsstat)) ||
559        (fsent->fsname != NULL &&
560         sfsr != -1 &&
561         samefile(stats, &fsrstat))) {
562       rc = 1;
563       break;
564     }
565   }
566   amfree(rdev);
567   close_fstab();
568   return rc;
569 #endif /* !IGNORE_FSTAB */
570 }
571
572 int
573 is_local_fstype(
574     generic_fsent_t *   fsent)
575 {
576     if(fsent->fstype == NULL)   /* unknown, assume local */
577         return 1;
578
579     /* just eliminate fstypes known to be remote or unsavable */
580
581     return strcmp(fsent->fstype, "nfs") != 0 && /* NFS */
582            strcmp(fsent->fstype, "afs") != 0 && /* Andrew Filesystem */
583            strcmp(fsent->fstype, "swap") != 0 && /* Swap */
584            strcmp(fsent->fstype, "iso9660") != 0 && /* CDROM */
585            strcmp(fsent->fstype, "hs") != 0 && /* CDROM */
586            strcmp(fsent->fstype, "piofs") != 0; /* an AIX printer thing? */
587 }
588
589
590 char *
591 amname_to_devname(
592     char *      str)
593 {
594     generic_fsent_t fsent;
595
596     if(search_fstab(str, &fsent, 1) && fsent.fsname != NULL)
597         str = fsent.fsname;
598     else if(search_fstab(str, &fsent, 0) && fsent.fsname != NULL)
599         str = fsent.fsname;
600
601     return dev2rdev(str);
602 }
603
604 char *
605 amname_to_dirname(
606     char *      str)
607 {
608     generic_fsent_t fsent;
609
610     if(search_fstab(str, &fsent, 1) && fsent.mntdir != NULL)
611         str = fsent.mntdir;
612     else if(search_fstab(str, &fsent, 0) && fsent.mntdir != NULL)
613         str = fsent.mntdir;
614
615     return stralloc(str);
616 }
617
618 char *amname_to_fstype(
619     char *      str)
620 {
621     generic_fsent_t fsent;
622
623     if (!search_fstab(str, &fsent, 1) && !search_fstab(str, &fsent, 0))
624       return stralloc("");
625
626     return stralloc(fsent.fstype);
627 }
628
629 #ifdef TEST
630
631 void print_entry(generic_fsent_t *fsent);
632
633 void
634 print_entry(
635     generic_fsent_t *   fsent)
636 {
637 #define nchk(s) ((s)? (s) : "<NULL>")
638     g_printf("%-20.20s %-14.14s %-7.7s %4d %5d %s\n",
639            nchk(fsent->fsname), nchk(fsent->mntdir), nchk(fsent->fstype),
640            fsent->freq, fsent->passno, nchk(fsent->mntopts));
641 }
642
643 int
644 main(
645     int         argc,
646     char **     argv)
647 {
648     generic_fsent_t fsent;
649     char *s;
650     char *name = NULL;
651
652     /*
653      * Configure program for internationalization:
654      *   1) Only set the message locale for now.
655      *   2) Set textdomain for all amanda related programs to "amanda"
656      *      We don't want to be forced to support dozens of message catalogs.
657      */  
658     setlocale(LC_MESSAGES, "C");
659     textdomain("amanda"); 
660
661     safe_fd(-1, 0);
662
663     set_pname("getfsent");
664
665     dbopen(NULL);
666
667     /* Don't die when child closes pipe */
668     signal(SIGPIPE, SIG_IGN);
669
670     if(!open_fstab()) {
671         g_fprintf(stderr, _("getfsent_test: could not open fstab\n"));
672         return 1;
673     }
674
675     g_printf("getfsent (%s)\n",GETFSENT_TYPE);
676     g_printf("l/r fsname               mntdir         fstype  freq pass# mntopts\n");
677     while(get_fstab_nextentry(&fsent)) {
678         g_printf("%c  ",is_local_fstype(&fsent)? 'l' : 'r');
679         print_entry(&fsent);
680     }
681     g_printf("--------\n");
682
683     close_fstab();
684
685     name = newstralloc(name, "/usr");
686     if(search_fstab(name, &fsent, 1) || search_fstab(name, &fsent, 0)) {
687         g_printf(_("Found %s mount for %s:\n"),
688                is_local_fstype(&fsent)? _("local") : _("remote"), name);
689         print_entry(&fsent);
690     }
691     else 
692         g_printf(_("Mount for %s not found\n"), name);
693
694     name = newstralloc(name, "/");
695     if(search_fstab(name, &fsent, 1) || search_fstab(name, &fsent, 0)) {
696         g_printf(_("Found %s mount for %s:\n"),
697                is_local_fstype(&fsent)? _("local") : _("remote"), name);
698         print_entry(&fsent);
699     }
700     else 
701         g_printf(_("Mount for %s not found\n"), name);
702
703     name = newstralloc(name, "/");
704     s = amname_to_fstype(name);
705     g_printf(_("fstype of `%s': %s\n"), name, s);
706     amfree(s);
707     name = newstralloc(name, "/dev/root");
708     s = amname_to_fstype(name);
709     g_printf(_("fstype of `%s': %s\n"), name, s);
710     amfree(s);
711     name = newstralloc(name, "/usr");
712     s = amname_to_fstype(name);
713     g_printf(_("fstype of `%s': %s\n"), name, s);
714     amfree(s);
715     name = newstralloc(name, "c0t3d0s0");
716     s = amname_to_fstype(name);
717     g_printf(_("fstype of `%s': %s\n"), name, s);
718     amfree(s);
719
720     name = newstralloc(name, "/tmp/foo");
721     s = amname_to_devname(name);
722     g_printf(_("device of `%s': %s\n"), name, s);
723     amfree(s);
724     s = amname_to_dirname(name);
725     g_printf(_("dirname of `%s': %s\n"), name, s);
726     amfree(s);
727     s = amname_to_fstype(name);
728     g_printf(_("fstype of `%s': %s\n"), name, s);
729     amfree(s);
730
731     name = newstralloc(name, "./foo");
732     s = amname_to_devname(name);
733     g_printf(_("device of `%s': %s\n"), name, s);
734     amfree(s);
735     s = amname_to_dirname(name);
736     g_printf(_("dirname of `%s': %s\n"), name, s);
737     amfree(s);
738     s = amname_to_fstype(name);
739     g_printf(_("fstype of `%s': %s\n"), name, s);
740     amfree(s);
741
742     while (--argc > 0) {
743         name = newstralloc(name, *++argv);
744         s = amname_to_devname(name);
745         g_printf(_("device of `%s': %s\n"), name, s);
746         amfree(s);
747         s = amname_to_dirname(name);
748         g_printf(_("dirname of `%s': %s\n"), name, s);
749         amfree(s);
750         s = amname_to_fstype(name);
751         g_printf(_("fstype of `%s': %s\n"), name, s);
752         amfree(s);
753     }
754
755     amfree(name);
756
757     dbclose();
758     return 0;
759 }
760
761 #endif