diff options
Diffstat (limited to 'src/rc/mountinfo.c')
-rw-r--r-- | src/rc/mountinfo.c | 464 |
1 files changed, 464 insertions, 0 deletions
diff --git a/src/rc/mountinfo.c b/src/rc/mountinfo.c new file mode 100644 index 00000000..05ce8dd7 --- /dev/null +++ b/src/rc/mountinfo.c @@ -0,0 +1,464 @@ +/* + mountinfo.c + Obtains information about mounted filesystems. + */ + +/* + * Copyright 2007 Roy Marples + * All rights reserved + + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/types.h> + +#if defined(__DragonFly__) || defined(__FreeBSD__) || \ + defined(__NetBSD__) || defined(__OpenBSD__) +#define BSD +#include <sys/param.h> +#include <sys/ucred.h> +#include <sys/mount.h> +#elif defined (__linux__) +#include <mntent.h> +#endif + +#include <errno.h> +#include <getopt.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <regex.h> + +#include "builtins.h" +#include "einfo.h" +#include "rc.h" +#include "rc-misc.h" +#include "strlist.h" + +static const char *applet; + +typedef enum { + mount_from, + mount_to, + mount_fstype, + mount_options +} mount_type; + +typedef enum { + net_ignore, + net_yes, + net_no +} net_opts; + +struct args { + regex_t *node_regex; + regex_t *skip_node_regex; + regex_t *fstype_regex; + regex_t *skip_fstype_regex; + regex_t *options_regex; + regex_t *skip_options_regex; + char **mounts; + mount_type mount_type; + net_opts netdev; +}; + +static int process_mount (char ***list, struct args *args, + char *from, char *to, char *fstype, char *options, + int netdev) +{ + char *p; + + errno = ENOENT; + +#ifdef __linux__ + /* Skip the really silly rootfs */ + if (strcmp (fstype, "rootfs") == 0) + return (-1); +#endif + + if (args->netdev == net_yes && (netdev != -1 || args->mounts)) { + if (netdev != 0) + return (1); + } else if (args->netdev == net_no && (netdev != -1 || args->mounts)) { + if (netdev != 1) + return (1); + } else { + if (args->node_regex && + regexec (args->node_regex, from, 0, NULL, 0) != 0) + return (1); + if (args->skip_node_regex && + regexec (args->skip_node_regex, from, 0, NULL, 0) == 0) + return (1); + + if (args->fstype_regex && + regexec (args->fstype_regex, fstype, 0, NULL, 0) != 0) + return (-1); + if (args->skip_fstype_regex && + regexec (args->skip_fstype_regex, fstype, 0, NULL, 0) == 0) + return (-1); + + if (args->options_regex && + regexec (args->options_regex, options, 0, NULL, 0) != 0) + return (-1); + if (args->skip_options_regex && + regexec (args->skip_options_regex, options, 0, NULL, 0) == 0) + return (-1); + } + + if (args->mounts) { + bool found = false; + int j; + char *mnt; + STRLIST_FOREACH (args->mounts, mnt, j) + if (strcmp (mnt, to) == 0) { + found = true; + break; + } + if (! found) + return (-1); + } + + switch (args->mount_type) { + case mount_from: + p = from; + break; + case mount_to: + p = to; + break; + case mount_fstype: + p = fstype; + break; + case mount_options: + p = options; + break; + default: + p = NULL; + errno = EINVAL; + break; + } + + if (p) { + errno = 0; + rc_strlist_addsortc (list, p); + return (0); + } + + return (-1); +} + +#ifdef BSD + +/* Translate the mounted options to english + * This is taken directly from FreeBSD mount.c */ +static struct opt { + int o_opt; + const char *o_name; +} optnames[] = { + { MNT_ASYNC, "asynchronous" }, + { MNT_EXPORTED, "NFS exported" }, + { MNT_LOCAL, "local" }, + { MNT_NOATIME, "noatime" }, + { MNT_NOEXEC, "noexec" }, + { MNT_NOSUID, "nosuid" }, + { MNT_NOSYMFOLLOW, "nosymfollow" }, + { MNT_QUOTA, "with quotas" }, + { MNT_RDONLY, "read-only" }, + { MNT_SYNCHRONOUS, "synchronous" }, + { MNT_UNION, "union" }, + { MNT_NOCLUSTERR, "noclusterr" }, + { MNT_NOCLUSTERW, "noclusterw" }, + { MNT_SUIDDIR, "suiddir" }, + { MNT_SOFTDEP, "soft-updates" }, + { MNT_MULTILABEL, "multilabel" }, + { MNT_ACLS, "acls" }, +#ifdef MNT_GJOURNAL + { MNT_GJOURNAL, "gjournal" }, +#endif + { 0, NULL } +}; + +static char **find_mounts (struct args *args) +{ + struct statfs *mnts; + int nmnts; + int i; + char **list = NULL; + char *options = NULL; + int flags; + struct opt *o; + + if ((nmnts = getmntinfo (&mnts, MNT_NOWAIT)) == 0) + eerrorx ("getmntinfo: %s", strerror (errno)); + + for (i = 0; i < nmnts; i++) { + int netdev = 0; + flags = mnts[i].f_flags & MNT_VISFLAGMASK; + for (o = optnames; flags && o->o_opt; o++) { + if (flags & o->o_opt) { + if (o->o_opt == MNT_LOCAL) + netdev = 1; + if (! options) + options = xstrdup (o->o_name); + else { + char *tmp = NULL; + int l = strlen (options) + strlen (o->o_name) + 2; + tmp = xmalloc (sizeof (char) * l); + snprintf (tmp, l, "%s,%s", options, o->o_name); + free (options); + options = tmp; + } + } + flags &= ~o->o_opt; + } + + process_mount (&list, args, + mnts[i].f_mntfromname, + mnts[i].f_mntonname, + mnts[i].f_fstypename, + options, + netdev); + + free (options); + options = NULL; + } + + return (list); +} + +#elif defined (__linux__) +static struct mntent *getmntfile (const char *file) +{ + struct mntent *ent = NULL; + FILE *fp; + + fp = setmntent ("/etc/fstab", "r"); + while ((ent = getmntent (fp))) + if (strcmp (file, ent->mnt_dir) == 0) + break; + endmntent (fp); + + return (ent); +} + +static char **find_mounts (struct args *args) +{ + FILE *fp; + char *buffer; + char *p; + char *from; + char *to; + char *fst; + char *opts; + char **list = NULL; + struct mntent *ent; + int netdev; + + if ((fp = fopen ("/proc/mounts", "r")) == NULL) + eerrorx ("getmntinfo: %s", strerror (errno)); + + buffer = xmalloc (sizeof (char) * PATH_MAX * 3); + while (fgets (buffer, PATH_MAX * 3, fp)) { + netdev = -1; + p = buffer; + from = strsep (&p, " "); + to = strsep (&p, " "); + fst = strsep (&p, " "); + opts = strsep (&p, " "); + + if ((ent = getmntfile (to))) { + if (strstr (ent->mnt_opts, "_netdev")) + netdev = 0; + } + + process_mount (&list, args, from, to, fst, opts, netdev); + } + free (buffer); + fclose (fp); + + return (list); +} + +#else +# error "Operating system not supported!" +#endif + +static regex_t *get_regex (const char *string) +{ + regex_t *reg = xmalloc (sizeof (regex_t)); + int result; + char buffer[256]; + + if ((result = regcomp (reg, string, REG_EXTENDED | REG_NOSUB)) != 0) + { + regerror (result, reg, buffer, sizeof (buffer)); + eerrorx ("%s: invalid regex `%s'", applet, buffer); + } + + return (reg); +} + +#include "_usage.h" +#define extraopts "[mount1] [mount2] ..." +#define getoptstring "f:F:n:N:o:O:p:P:ist" getoptstring_COMMON +static struct option longopts[] = { + { "fstype-regex", 1, NULL, 'f'}, + { "skip-fstype-regex", 1, NULL, 'F'}, + { "node-regex", 1, NULL, 'n'}, + { "skip-node-regex", 1, NULL, 'N'}, + { "options-regex", 1, NULL, 'o'}, + { "skip-options-regex", 1, NULL, 'O'}, + { "point-regex", 1, NULL, 'p'}, + { "skip-point-regex", 1, NULL, 'P'}, + { "options", 0, NULL, 'i'}, + { "fstype", 0, NULL, 's'}, + { "node", 0, NULL, 't'}, + { "netdev", 0, NULL, 'e'}, + { "nonetdev", 0, NULL, 'E'}, + longopts_COMMON +}; +static const char * const longopts_help[] = { + "fstype regex to find", + "fstype regex to skip", + "node regex to find", + "node regex to skip", + "options regex to find", + "options regex to skip", + "point regex to find", + "point regex to skip", + "print options", + "print fstype", + "print node", + "is it a network device", + "is it not a network device", + longopts_help_COMMON +}; +#include "_usage.c" + +int mountinfo (int argc, char **argv) +{ + int i; + struct args args; + regex_t *point_regex = NULL; + regex_t *skip_point_regex = NULL; + char **nodes = NULL; + char *n; + int opt; + int result; + bool quiet; + + /* Ensure that we are only quiet when explicitly told to be */ + unsetenv ("EINFO_QUIET"); + +#define DO_REG(_var) \ + if (_var) free (_var); \ + _var = get_regex (optarg); +#define REG_FREE(_var) \ + if (_var) { regfree (_var); free (_var); } + + memset (&args, 0, sizeof (struct args)); + args.mount_type = mount_to; + args.netdev = net_ignore; + + while ((opt = getopt_long (argc, argv, getoptstring, + longopts, (int *) 0)) != -1) + { + switch (opt) { + case 'e': + args.netdev = net_yes; + break; + case 'E': + args.netdev = net_no; + break; + case 'f': + DO_REG (args.fstype_regex); + break; + case 'F': + DO_REG (args.skip_fstype_regex); + break; + case 'n': + DO_REG (args.node_regex); + break; + case 'N': + DO_REG (args.skip_node_regex); + break; + case 'o': + DO_REG (args.options_regex); + break; + case 'O': + DO_REG (args.skip_options_regex); + break; + case 'p': + DO_REG (point_regex); + break; + case 'P': + DO_REG (skip_point_regex); + break; + case 'i': + args.mount_type = mount_options; + break; + case 's': + args.mount_type = mount_fstype; + break; + case 't': + args.mount_type = mount_from; + break; + + case_RC_COMMON_GETOPT + } + } + + while (optind < argc) { + if (argv[optind][0] != '/') + eerrorx ("%s: `%s' is not a mount point", argv[0], argv[optind]); + rc_strlist_add (&args.mounts, argv[optind++]); + } + + nodes = find_mounts (&args); + + REG_FREE (args.fstype_regex); + REG_FREE (args.skip_fstype_regex); + REG_FREE (args.node_regex); + REG_FREE (args.skip_node_regex); + REG_FREE (args.options_regex); + REG_FREE (args.skip_options_regex); + + rc_strlist_reverse (nodes); + + result = EXIT_FAILURE; + quiet = rc_yesno (getenv ("EINFO_QUIET")); + STRLIST_FOREACH (nodes, n, i) { + if (point_regex && regexec (point_regex, n, 0, NULL, 0) != 0) + continue; + if (skip_point_regex && regexec (skip_point_regex, n, 0, NULL, 0) == 0) + continue; + if (! quiet) + printf ("%s\n", n); + result = EXIT_SUCCESS; + } + rc_strlist_free (nodes); + + REG_FREE (point_regex); + REG_FREE (skip_point_regex); + + exit (result); +} |