source: trunk/src/sh_mounts.c@ 148

Last change on this file since 148 was 140, checked in by rainer, 17 years ago

Utility function for threaded modules.

File size: 19.1 KB
Line 
1/*
2 * File: sh_mounts.c
3 * Desc: A module for Samhain; checks for mounts present and options on them.
4 * Auth: Cian Synnott <cian.synnott@eircom.net>
5 *
6 */
7/* This program is free software; you can redistribute it */
8/* and/or modify */
9/* it under the terms of the GNU General Public License as */
10/* published by */
11/* the Free Software Foundation; either version 2 of the License, or */
12/* (at your option) any later version. */
13/* */
14/* This program is distributed in the hope that it will be useful, */
15/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
16/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
17/* GNU General Public License for more details. */
18/* */
19/* You should have received a copy of the GNU General Public License */
20/* along with this program; if not, write to the Free Software */
21/* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
22
23#include "config_xor.h"
24
25
26/* Used in the call tracing macros to keep track of where we are in the code */
27#undef FIL__
28#define FIL__ _("sh_mounts.c")
29
30
31#include "samhain.h"
32#include "sh_utils.h"
33#include "sh_error.h"
34#include "sh_modules.h"
35#include "sh_mounts.h"
36
37#ifdef SH_USE_MOUNTS
38#if defined (SH_WITH_CLIENT) || defined (SH_STANDALONE)
39
40/*
41 * #ifdef HAVE_STRING_H
42 * #include <string.h>
43 * #endif
44 */
45
46#ifdef TM_IN_SYS_TIME
47#include <sys/time.h>
48#else
49#include <time.h>
50#endif
51
52/* Prototypes for configuration functions */
53int sh_mounts_config_activate (const char * opt);
54int sh_mounts_config_timer (const char * opt);
55int sh_mounts_config_mount (const char * opt);
56int sh_mounts_config_sevmnt (const char * opt);
57int sh_mounts_config_sevopt (const char * opt);
58
59/* Prototype for the function to read info on mounted filesystems */
60static struct sh_mounts_mnt *readmounts(void);
61
62/* Table for configuration options, and pointers to the functions that will
63 * configure them. Each function is passed the string resulting from stripping
64 * the option and the "equals" from the config file; e.g. MountCheckActive=1 in
65 * the configuration file will result in the string "1" being passed to
66 * sh_mounts_config_activate() */
67sh_rconf sh_mounts_table[] = {
68 {
69 N_("mountcheckactive"),
70 sh_mounts_config_activate
71 },
72 {
73 N_("mountcheckinterval"),
74 sh_mounts_config_timer
75 },
76 {
77 N_("checkmount"),
78 sh_mounts_config_mount
79 },
80 {
81 N_("severitymountmissing"),
82 sh_mounts_config_sevmnt
83 },
84 {
85 N_("severityoptionmissing"),
86 sh_mounts_config_sevopt
87 },
88 {
89 NULL,
90 NULL
91 },
92};
93
94/* Structures for storing my configuration information, and functions for
95 * manipulating them */
96struct sh_mounts_mnt {
97 char * path;
98 struct sh_mounts_opt * opts;
99 struct sh_mounts_mnt * next;
100};
101
102struct sh_mounts_opt {
103 char * opt;
104 struct sh_mounts_opt * next;
105};
106
107/* Return the mount structure whose path matches 'mnt' or NULL if not found */
108static
109struct sh_mounts_mnt *sh_mounts_mnt_member(struct sh_mounts_mnt *m, char *mnt)
110{
111 struct sh_mounts_mnt *it;
112
113 for (it = m; it != NULL; it = it->next) {
114 if (0 == sl_strcmp(it->path, mnt)) {
115 return it;
116 }
117 }
118 return NULL;
119}
120
121/* Return the opt structure whose option matches 'opt' or NULL if not found */
122static
123struct sh_mounts_opt *sh_mounts_opt_member(struct sh_mounts_opt *o, char *opt)
124{
125 struct sh_mounts_opt *it;
126
127 for (it = o; it != NULL; it = it->next) {
128 /* if (!strcmp(it->opt, opt)) { */
129 if (0 == sl_strcmp(it->opt, opt)) {
130 return it;
131 }
132 }
133 return NULL;
134}
135
136static
137void sh_mounts_opt_free(struct sh_mounts_opt *o) {
138 if (o != NULL) {
139 sh_mounts_opt_free(o->next);
140 SH_FREE(o->opt);
141 SH_FREE(o);
142 }
143}
144
145static
146void sh_mounts_mnt_free(struct sh_mounts_mnt *m) {
147 if (m != NULL) {
148 sh_mounts_mnt_free(m->next);
149 sh_mounts_opt_free(m->opts);
150 SH_FREE(m->path);
151 SH_FREE(m);
152 }
153}
154
155/* Some configuration variables I'll be using */
156static time_t lastcheck = (time_t) 0;
157static int ShMountsActive = S_FALSE;
158static time_t ShMountsInterval = 86400;
159static int ShMountsSevMnt = 7;
160static int ShMountsSevOpt = 7;
161
162static struct sh_mounts_mnt *mountlist = NULL;
163
164/* Module initialisation
165 * This is called once at the start of each samhain run.
166 * Non-configuration setup code should be placed here. */
167int sh_mounts_init (struct mod_type * arg)
168{
169 (void) arg;
170 SL_ENTER(_("sh_mounts_init"));
171
172 /* This is a little odd. Because we've built the configured mount list at
173 * this point, if we've set the module inactive, we need to free the list -
174 * otherwise when we reconf() with it set active, we'll end up with a
175 * duplicated list. Interesting. */
176 if (ShMountsActive == S_FALSE) {
177 sh_mounts_mnt_free(mountlist);
178 mountlist = NULL;
179 SL_RETURN(-1, _("sh_mounts_init"));
180 }
181
182 lastcheck = time(NULL);
183
184 SL_RETURN(0, _("sh_mounts_init"));
185}
186
187/* Module timer
188 * This timer function is called periodically with the current time to see if
189 * it is time to run the module's "check" function. On nonzero return, the
190 * check is run. */
191int sh_mounts_timer (time_t tcurrent)
192{
193 SL_ENTER(_("sh_mounts_timer"));
194
195 if ((time_t) (tcurrent - lastcheck) >= ShMountsInterval) {
196 lastcheck = tcurrent;
197 SL_RETURN(-1, _("sh_mounts_timer"));
198 }
199
200 SL_RETURN(0, _("sh_mounts_timer"));
201}
202
203/* Module check
204 * The business end of things. This is the actual check code for this module.
205 * Everything you want to do periodically should go here. */
206int sh_mounts_check ()
207{
208 struct sh_mounts_mnt *memlist;
209 struct sh_mounts_mnt *cfgmnt, *mnt;
210 struct sh_mounts_opt *cfgopt, *opt;
211
212 SL_ENTER(_("sh_mounts_check"));
213
214 /* Log the check run. For each message type you want, you need to define it
215 * as an enum in sh_cat.h, and then set it up in terms of priority and format
216 * string in sh_cat.c */
217 sh_error_handle(-1, FIL__, __LINE__, 0, MSG_MNT_CHECK);
218
219 /* Read the list of mounts from memory */
220 memlist = readmounts();
221
222 if (memlist == NULL) {
223 sh_error_handle(-1, FIL__, __LINE__, 0, MSG_MNT_MEMLIST);
224 }
225
226 /* For each mount we are configured to check, run through the list of mounted
227 * filesystems and compare the pathnames */
228 for (cfgmnt = mountlist; cfgmnt != NULL; cfgmnt = cfgmnt->next) {
229 mnt = sh_mounts_mnt_member(memlist, cfgmnt->path);
230
231 if (mnt) {
232 for (cfgopt = cfgmnt->opts; cfgopt != NULL; cfgopt = cfgopt->next) {
233 opt = sh_mounts_opt_member(mnt->opts, cfgopt->opt);
234
235 if (!opt) {
236 sh_error_handle(ShMountsSevOpt, FIL__, __LINE__, 0, MSG_MNT_OPTMISS,
237 cfgmnt->path, cfgopt->opt);
238 }
239 }
240 }
241
242 else {
243 sh_error_handle(ShMountsSevMnt, FIL__, __LINE__, 0, MSG_MNT_MNTMISS,
244 cfgmnt->path);
245 }
246 }
247
248 /* Make sure to clean up after ourselves */
249 sh_mounts_mnt_free(memlist);
250
251 SL_RETURN(0, _("sh_mounts_check"));
252}
253
254/* Module cleanup
255 * The end of the tour - when samhain is shutting down, this is run. */
256int sh_mounts_cleanup ()
257{
258 SL_ENTER(_("sh_mounts_cleanup"));
259 sh_mounts_mnt_free(mountlist);
260 mountlist = NULL;
261 SL_RETURN( (0), _("sh_mounts_cleanup"));
262}
263
264/* Module reconfiguration
265 * Run on receipt of a HUP. Right now this is identical to _end(), but it may
266 * not always be. */
267int sh_mounts_reconf()
268{
269 SL_ENTER(_("sh_mounts_null"));
270 sh_mounts_mnt_free(mountlist);
271 mountlist = NULL;
272 SL_RETURN( (0), _("sh_mounts_null"));
273}
274
275/* Module configuration
276 * These functions are called when the configuration file is being parsed. */
277
278/* Configure to check a particular mount */
279int sh_mounts_config_mount (const char * opt_in)
280{
281 struct sh_mounts_mnt *m;
282 struct sh_mounts_opt *o;
283 char *sp, *temp, *opt;
284
285 SL_ENTER(_("sh_mounts_config_mount"));
286
287 /* It's probably best to make a copy of opt before messing about with it
288 * via string functions. Good practice and all that. */
289 temp = sh_util_strdup(opt_in);
290
291 /* Since we're going to "consume" this new buffer, it'll be good to have a
292 * reference to it's allocated memory so we can free it later. Let's use
293 * temp for that, and "opt" for consumption */
294 opt = temp;
295
296 m = (struct sh_mounts_mnt *) SH_ALLOC(sizeof(struct sh_mounts_mnt));
297
298 /* First, strip out the mount path. */
299 m->path = sh_util_strdup(sh_util_strsep(&opt, " \t"));
300 m->opts = NULL;
301
302 /* Now get all of the mount options - they can be delimited by comma or
303 * whitespace */
304 while (opt != NULL) {
305 sp = sh_util_strsep(&opt, ", \t");
306
307 /* This just catches multiple separators appearing together */
308 if (*sp == '\0') {
309 continue;
310 }
311
312 o = (struct sh_mounts_opt *) SH_ALLOC(sizeof(struct sh_mounts_opt));
313 o->next = m->opts;
314 m->opts = o;
315
316 o->opt = sh_util_strdup(sp);
317 }
318
319 /* Add to the list of configured mounts */
320 m->next = mountlist;
321 mountlist = m;
322
323 /* Free the string buffer we allocated earlier */
324 SH_FREE(temp);
325
326 SL_RETURN(0, _("sh_mounts_config_mount"));
327}
328
329/* Simply sets our boolean as to whether this module is active */
330int sh_mounts_config_activate (const char * opt)
331{
332 int i;
333 SL_ENTER(_("sh_mounts_config_activate"));
334 i = sh_util_flagval(opt, &ShMountsActive);
335 SL_RETURN(i, _("sh_mounts_config_activate"));
336}
337
338/* Sets up our timer */
339int sh_mounts_config_timer (const char * opt)
340{
341 long val;
342 int retval = 0;
343
344 SL_ENTER(_("sh_mounts_config_timer"));
345 val = strtol (opt, (char **)NULL, 10);
346 if (val <= 0)
347 {
348 sh_error_handle (-1, FIL__, __LINE__, EINVAL, MSG_EINVALS,
349 _("mounts timer"), opt);
350 retval = -1;
351 }
352 val = (val <= 0 ? 86400 : val);
353
354 ShMountsInterval = (time_t) val;
355
356 SL_RETURN(retval, _("sh_mounts_config_timer"));
357}
358
359/* Configure severity for "mount missing" messages */
360int sh_mounts_config_sevmnt (const char * opt)
361{
362 int retval = 0;
363 char tmp[32];
364
365
366 SL_ENTER(_("sh_mounts_config_sevmnt"));
367 tmp[0] = '='; tmp[1] = '\0';
368 (void) sl_strlcat (tmp, opt, 32);
369 retval = sh_error_set_level (tmp, &ShMountsSevMnt);
370 SL_RETURN(retval, _("sh_mounts_config_sevmnt"));
371}
372
373int sh_mounts_config_sevopt (const char * opt)
374{
375 int retval = 0;
376 char tmp[32];
377
378 SL_ENTER(_("sh_mounts_config_sevopt"));
379 tmp[0] = '='; tmp[1] = '\0';
380 (void) sl_strlcat (tmp, opt, 32);
381 retval = sh_error_set_level (tmp, &ShMountsSevOpt);
382 SL_RETURN(retval, _("sh_mounts_config_sevopt"));
383}
384
385
386/*
387 * Below here we have the code for actually reading options on mounted fs's
388 * I've just got code here to work on FreeBSD, Linux and Solaris. I'm sure
389 * others could be added. Note that some small bits of the OS-specific code
390 * are from mountlist.c in GNU fileutils.
391 */
392
393/* FreeBSD includes */
394#ifdef HOST_IS_FREEBSD
395#include <sys/param.h>
396#include <sys/ucred.h>
397#include <sys/mount.h>
398#endif
399
400/* Linux includes */
401#ifdef HOST_IS_LINUX
402#include <stdio.h>
403#include <mntent.h>
404#endif
405
406/* Solaris includes */
407#ifdef HOST_IS_SOLARIS
408#include <stdio.h>
409#include <sys/mnttab.h>
410#endif
411
412/* HP_UX includes */
413#ifdef HOST_IS_HPUX
414#include <stdio.h>
415#include <mntent.h>
416#endif
417
418/* AIX includes and helper routines (from gnome-vfs-unix-mounts.c */
419#if 0
420#ifdef HOST_IS_AIX
421#include <stdio.h>
422#include <string.h>
423#include <ctype.h>
424
425/* gnome-vfs-unix-mounts.c - read and monitor fstab/mtab
426
427 Copyright (C) 2003 Red Hat, Inc
428
429 The Gnome Library is free software; you can redistribute it and/or
430 modify it under the terms of the GNU Library General Public License as
431 published by the Free Software Foundation; either version 2 of the
432 License, or (at your option) any later version.
433
434 The Gnome Library is distributed in the hope that it will be useful,
435 but WITHOUT ANY WARRANTY; without even the implied warranty of
436 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
437 Library General Public License for more details.
438
439 You should have received a copy of the GNU Library General Public
440 License along with the Gnome Library; see the file COPYING.LIB. If not,
441 write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
442 Boston, MA 02111-1307, USA.
443
444 Author: Alexander Larsson <alexl@redhat.com>
445*/
446
447/* read character, ignoring comments (begin with '*', end with '\n' */
448static int aix_fs_getc (FILE *fd)
449{
450 int c;
451
452 while ((c = getc (fd)) == '*') {
453 while (((c = getc (fd)) != '\n') && (c != EOF)) {} /* do nothing */
454 }
455}
456
457/* eat all continuous spaces in a file */
458static int aix_fs_ignorespace (FILE *fd)
459{
460 int c;
461
462 while ((c = aix_fs_getc (fd)) != EOF) {
463 if (! (isascii(c) && isspace (c)) ) {
464 ungetc (c,fd);
465 return c;
466 }
467 }
468
469 return EOF;
470}
471
472/* read one word from file */
473static int aix_fs_getword (FILE *fd, char *word, int len)
474{
475 int c;
476 int i = 0;
477
478 --len;
479
480 aix_fs_ignorespace (fd);
481
482 while (((c = aix_fs_getc (fd)) != EOF) && !( isascii(c) && isspace(c) ))
483 {
484 if (c == '"')
485 {
486 while (((c = aix_fs_getc (fd)) != EOF) && (c != '"'))
487 {
488 *word++ = c; ++i;
489 if (i == len)
490 break;
491 }
492 }
493 else
494 {
495 *word++ = c; ++i;
496 }
497 if (i == len)
498 break;
499 }
500 *word = 0;
501
502 return c;
503}
504
505/* PATH_MAX is in sys/limits.h, included via stdio.h
506 */
507typedef struct {
508 char mnt_mount[PATH_MAX];
509 char mnt_special[PATH_MAX];
510 char mnt_fstype[16];
511 char mnt_options[128];
512} AixMountTableEntry;
513
514/* read mount points properties */
515static int aix_fs_get (FILE *fd, AixMountTableEntry *prop)
516{
517 /* Need space for PATH_MAX + ':' (terminating '\0' is in PATH_MAX; SUSv3)
518 */
519 static char word[PATH_MAX+1] = { 0 };
520 char value[PATH_MAX];
521
522 /* reset */
523
524 if (fd == NULL)
525 {
526 word[0] = '\0';
527 return 0;
528 }
529
530 /* read stanza */
531
532 if (word[0] == 0) {
533 if (aix_fs_getword (fd, word, (PATH_MAX+1)) == EOF)
534 return EOF;
535 }
536
537 word[strlen(word) - 1] = 0;
538 sl_strlcpy (prop->mnt_mount, word, PATH_MAX);
539
540 /* read attributes and value */
541
542 while (aix_fs_getword (fd, word, (PATH_MAX+1)) != EOF) {
543 /* test if is attribute or new stanza */
544
545 if (word[strlen(word) - 1] == ':') {
546 return 0;
547 }
548
549 /* read "=" */
550 aix_fs_getword (fd, value, PATH_MAX);
551
552 /* read value */
553 aix_fs_getword (fd, value, PATH_MAX);
554
555 if (strcmp (word, "dev") == 0) {
556 sl_strlcpy (prop->mnt_special, value, PATH_MAX);
557 } else if (strcmp (word, "vfs") == 0) {
558 sl_strlcpy (prop->mnt_fstype, value, 16);
559 } else if (strcmp (word, "options") == 0) {
560 sl_strlcpy(prop->mnt_options, value, 128);
561 }
562 }
563
564 return 0;
565}
566
567/* end AIX helper routines */
568#endif
569#endif
570
571#if defined(HOST_IS_FREEBSD)
572
573/* FreeBSD returns flags instead of strings as mount options, so we'll convert
574 * them here. */
575static
576struct sh_mounts_opt * getoptlist(int flags) {
577 struct sh_mounts_opt *list, *o;
578 int i;
579
580 struct {char *opt; int flag;} table[] = {
581 {"ro", MNT_RDONLY},
582 {"noexec", MNT_NOEXEC},
583 {"nosuid", MNT_NOSUID},
584 {"nodev", MNT_NODEV},
585 {"sync", MNT_SYNCHRONOUS},
586 {"async", MNT_ASYNC},
587 {"local", MNT_LOCAL},
588 {"quota", MNT_QUOTA},
589 {"bound", -1}
590 };
591
592 SL_ENTER(_("getoptlist"));
593
594 list = NULL;
595
596 /* Add any flags found to the list */
597 for (i = 0; table[i].flag != -1; i++) {
598 if (flags & table[i].flag) {
599 o = (struct sh_mounts_opt *) SH_ALLOC(sizeof(struct sh_mounts_opt));
600 o->opt = sh_util_strdup(table[i].opt);
601 o->next = list;
602 list = o;
603 }
604 }
605
606 SL_RETURN(list, _("getoptlist"));
607}
608
609/* Solaris & Linux return identical option string formats */
610#else
611
612/* We just separate the options out by parsing for commas */
613static
614struct sh_mounts_opt * getoptlist(char *opt)
615{
616 struct sh_mounts_opt *list, *o;
617 char *sp, *temp;
618
619 SL_ENTER(_("getoptlist"));
620
621 /* See the comments in sh_mounts_config_mount() above for the reasons for
622 * this arcane little zig-zag */
623 temp = sh_util_strdup(opt);
624 opt = temp;
625
626 list = NULL;
627
628 /* For each option, add to the list */
629 while (opt != NULL) {
630 sp = sh_util_strsep(&opt, ", \t");
631
632 if (*sp == '\0') {
633 continue;
634 }
635
636 o = (struct sh_mounts_opt *) SH_ALLOC(sizeof(struct sh_mounts_opt));
637 o->next = list;
638 list = o;
639
640 o->opt = sh_util_strdup(sp);
641 }
642
643 SH_FREE(temp);
644
645 SL_RETURN(list, _("getoptlist"));
646}
647
648#endif
649
650/* Read the list of mounts from whereever is appropriate to the OS and return
651 * it. Return NULL on error. */
652static struct sh_mounts_mnt * readmounts(void) {
653 struct sh_mounts_mnt *list, *m;
654
655 SL_ENTER(_("readmounts"));
656 m = NULL; /* use it to avoid compiler warning */
657 list = m;
658
659/* The FreeBSD way */
660#ifdef HOST_IS_FREEBSD
661{
662 struct statfs *fsp;
663 int entries;
664
665 entries = getmntinfo(&fsp, MNT_NOWAIT);
666 if (entries < 0) {
667 SL_RETURN((NULL), _("readmounts"));
668 }
669
670 for (; entries-- > 0; fsp++) {
671 m = (struct sh_mounts_mnt *) SH_ALLOC(sizeof (struct sh_mounts_mnt));
672 m->path = sh_util_strdup(fsp->f_mntonname);
673 m->opts = getoptlist(fsp->f_flags);
674
675 m->next = list;
676 list = m;
677 }
678}
679#endif
680
681/* The Linux way */
682#ifdef HOST_IS_LINUX
683{
684 struct mntent *mp;
685 FILE *tab = setmntent(_PATH_MOUNTED, "r");
686
687 if (tab == NULL) {
688 SL_RETURN((NULL), _("readmounts"));
689 }
690
691 mp = getmntent(tab);
692 while (mp != NULL) {
693 m = (struct sh_mounts_mnt *) SH_ALLOC(sizeof (struct sh_mounts_mnt));
694 m->path = sh_util_strdup(mp->mnt_dir);
695 m->opts = getoptlist(mp->mnt_opts);
696
697 m->next = list;
698 list = m;
699
700 mp = getmntent(tab);
701 }
702
703 (void) endmntent(tab);
704}
705#endif
706
707/* The Solaris way */
708#ifdef HOST_IS_SOLARIS
709{
710 struct mnttab mp;
711 FILE *tab = fopen(MNTTAB, "r");
712
713 if (tab == NULL) {
714 SL_RETURN((NULL), _("readmounts"));
715 }
716
717 while (!getmntent(tab, &mp)) {
718 m = (struct sh_mounts_mnt *) SH_ALLOC(sizeof (struct sh_mounts_mnt));
719 m->path = sh_util_strdup(mp.mnt_mountp);
720 m->opts = getoptlist(mp.mnt_mntopts);
721
722 m->next = list;
723 list = m;
724 }
725
726 fclose(tab);
727}
728#endif
729
730
731/* The HP-UX way */
732#ifdef HOST_IS_HPUX
733{
734 struct mntent *mp;
735 FILE *tab = setmntent(MNT_MNTTAB, "r");
736
737 if (tab == NULL) {
738 SL_RETURN((NULL), _("readmounts"));
739 }
740
741 mp = getmntent(tab);
742 while (mp != NULL) {
743 m = (struct sh_mounts_mnt *) SH_ALLOC(sizeof (struct sh_mounts_mnt));
744 m->path = sh_util_strdup(mp->mnt_dir);
745 m->opts = getoptlist(mp->mnt_opts);
746
747 m->next = list;
748 list = m;
749
750 mp = getmntent(tab);
751 }
752
753 (void) endmntent(tab);
754}
755#endif
756
757/* The AIX way */
758#if 0
759#ifdef HOST_IS_AIX
760{
761 AixMountTableEntry mntent;
762 FILE *tab = fopen("/etc/filesystems", "r");
763
764 if (tab == NULL) {
765 SL_RETURN((NULL), _("readmounts"));
766 }
767
768 while (!aix_fs_get (tab, &mntent))
769 {
770 m = (struct sh_mounts_mnt *) SH_ALLOC(sizeof (struct sh_mounts_mnt));
771 m->path = sh_util_strdup(mntent.mnt_mount);
772 m->opts = getoptlist(mntent.mnt_options);
773
774 m->next = list;
775 list = m;
776
777 mntent.mnt_mount[0] = '\0';
778 mntent.mnt_special[0] = '\0';
779 mntent.mnt_fstype[0] = '\0';
780 mntent.mnt_options[0] = '\0';
781 }
782
783 (void) fclose(tab);
784 aix_fs_get (NULL, NULL); /* reset */
785}
786#endif
787#endif
788
789 SL_RETURN((list), _("readmounts"));
790
791}
792
793
794/* #if defined (SH_WITH_CLIENT) || defined (SH_STANDALONE) */
795#endif
796
797/* #ifdef SH_USE_MOUNTS */
798#endif
799
Note: See TracBrowser for help on using the repository browser.