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