]> err.no Git - util-linux/commitdiff
findmnt: add new command
authorKarel Zak <kzak@redhat.com>
Mon, 1 Mar 2010 21:44:53 +0000 (22:44 +0100)
committerKarel Zak <kzak@redhat.com>
Thu, 3 Jun 2010 13:20:11 +0000 (15:20 +0200)
Signed-off-by: Karel Zak <kzak@redhat.com>
include/pathnames.h
misc-utils/.gitignore
misc-utils/Makefile.am
misc-utils/findmnt.c [new file with mode: 0644]

index 7d57c528f4d013ae532dffa9740f56cbc46cba02..c0572954e055ef847c7b7161ebbdf5ed3b1e63e8 100644 (file)
@@ -79,6 +79,7 @@
 #define _PATH_PROC_MOUNTS      "/proc/mounts"
 #define _PATH_PROC_PARTITIONS  "/proc/partitions"
 #define _PATH_PROC_DEVICES     "/proc/devices"
+#define _PATH_PROC_MOUNTINFO   "/proc/self/mountinfo"
 
 #define _PATH_SYS_BLOCK                "/sys/block"
 
index 5d34a50dbc811f972809895d01387ebf9a387f7e..a39caaf3f4c811c01bfd3faa36668effe49b749b 100644 (file)
@@ -17,3 +17,4 @@ uuidd
 findfs
 blkid
 wipefs
+findmnt
index c98107d68ce11534530532f230a8634842b50d0b..218a2103a514b0189d3b6ac19d21bce5a1e69f40 100644 (file)
@@ -34,7 +34,7 @@ endif
 endif
 
 if BUILD_LIBBLKID
-sbin_PROGRAMS += blkid findfs wipefs
+sbin_PROGRAMS += blkid findfs wipefs findmnt
 dist_man_MANS += blkid.8 findfs.8 wipefs.8
 blkid_SOURCES = blkid.c $(top_srcdir)/lib/ismounted.c \
                $(top_srcdir)/lib/strtosize.c
@@ -45,6 +45,8 @@ findfs_CFLAGS = $(AM_CFLAGS) -I$(ul_libblkid_incdir)
 wipefs_SOURCES = wipefs.c $(top_srcdir)/lib/strtosize.c
 wipefs_LDADD = $(ul_libblkid_la)
 wipefs_CFLAGS = $(AM_CFLAGS) -I$(ul_libblkid_incdir)
+findmnt_LDADD = $(ul_libmount_la)
+findmnt_CFLAGS = $(AM_CFLAGS) -I$(ul_libmount_incdir)
 if HAVE_STATIC_BLKID
 sbin_PROGRAMS += blkid.static
 blkid_static_SOURCES = $(blkid_SOURCES)
diff --git a/misc-utils/findmnt.c b/misc-utils/findmnt.c
new file mode 100644 (file)
index 0000000..04aadb7
--- /dev/null
@@ -0,0 +1,431 @@
+/*
+ * findmnt(8)
+ *
+ * Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+ * Written by Karel Zak <kzak@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <err.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <string.h>
+
+#include <mount.h>
+
+#include "pathnames.h"
+#include "nls.h"
+
+/*
+ * Column IDs
+ */
+enum {
+       COL_SOURCE,
+       COL_TARGET,
+       COL_FSTYPE,
+       COL_OPTIONS,
+       COL_ISMOUNTED,
+       COL_LABEL,
+       COL_UUID,
+
+       __NCOLUMNS
+};
+
+/*
+ * Column names
+ */
+const char *colnames[] = {
+       [COL_SOURCE] = "source",
+       [COL_TARGET] = "target",
+       [COL_FSTYPE] = "fstype",
+       [COL_OPTIONS] = "options",
+       [COL_ISMOUNTED] = "mounted",
+       [COL_LABEL] = "label",
+       [COL_UUID] = "uuid"
+};
+
+enum {
+       FL_EVALUATE     = (1 << 1),
+       FL_CANONICALIZE = (1 << 2),
+       FL_FIRSTONLY    = (1 << 3),
+       FL_INVERT       = (1 << 4)
+};
+
+struct match_data {
+       const char *source;
+       const char *target;
+       const char *fstypes;
+       const char *options;
+};
+
+int flags;
+int columns[ __NCOLUMNS ];
+int ncolumns;
+mnt_cache *cache;
+
+/*
+ * converts @name to column ID
+ */
+static int get_column_id(const char *name, size_t namesz)
+{
+       int i;
+
+       for (i = 0; i < __NCOLUMNS; i++) {
+               const char *cn = colnames[i];
+
+               if (!strncmp(name, cn, namesz) && !*(cn + namesz))
+                       return i;
+       }
+
+       errx(EXIT_FAILURE, _("unknown column: %*s"), (int) namesz, name);
+       return -1;
+}
+
+/*
+ * parses list of columns from @str and set column IDs to columns[]
+ */
+static int ctl_set_columns(const char *str)
+{
+       const char *begin = NULL, *end = NULL, *p;
+
+       ncolumns = 0;
+
+       if (!str || !*str)
+               return -1;
+
+       p = str;
+       for (; p && *p; p++) {
+               if (!begin)
+                       begin = p;              /* begin of the column name */
+               if (*p == ',')
+                       end = p;                /* terminate the name */
+               if (*(p + 1) == '\0')
+                       end = p + 1;            /* end of string */
+               if (!begin || !end)
+                       continue;
+               if (end <= begin)
+                       return -1;
+
+               columns[ ncolumns++ ] = get_column_id(begin, end - begin);
+       }
+       return 0;
+}
+
+static int print_column(mnt_fs *fs, int id)
+{
+       const char *str = NULL;
+
+       if (!fs)
+               return -1;
+
+       switch(id) {
+       case COL_SOURCE:
+               /* dir or dev */
+               str = mnt_fs_get_source(fs);
+
+               if (str && ((flags & FL_EVALUATE) || (flags & FL_CANONICALIZE)))
+                       str = mnt_resolve_spec(str, cache);
+               break;
+       case COL_TARGET:
+               str = mnt_fs_get_target(fs);
+               break;
+       case COL_FSTYPE:
+               str = mnt_fs_get_fstype(fs);
+               break;
+       case COL_OPTIONS:
+               str = mnt_fs_get_optstr(fs);
+               break;
+       default:
+               return -1;
+       }
+
+       if (str)
+               printf("%s", str);
+       return 0;
+}
+
+static int print_fs(mnt_fs *fs)
+{
+       int i;
+
+       for (i = 0; i < ncolumns; i++) {
+               print_column(fs, i);
+               printf("\t");
+       }
+       printf("\n");
+       return 0;
+}
+
+static mnt_tab *parse_tabfile(const char *path)
+{
+       mnt_tab *tb = mnt_new_tab(path);
+       if (!tb)
+               return NULL;
+
+       if (mnt_tab_parse_file(tb) != 0)
+               goto err;
+
+       if (mnt_tab_get_nerrs(tb)) {
+               char buf[BUFSIZ];
+               mnt_tab_strerror(tb, buf, sizeof(buf));
+               warnx(_("%s: parse error: %s"), path, buf);
+       }
+       return tb;
+err:
+       mnt_free_tab(tb);
+       err(EXIT_FAILURE, _("can't read: %s"), path);
+
+       return NULL;
+}
+
+static int match_func(mnt_fs *fs, void *data)
+{
+       struct match_data *m = (struct match_data *) data;
+       int rc = flags & FL_INVERT ? 1 : 0;
+
+       /*fprintf(stderr, "source: %s : %s\n", m->source, mnt_fs_get_source(fs));*/
+
+       if (m->target && !mnt_fs_match_target(fs, m->target, cache))
+               return rc;
+       if (m->source && !mnt_fs_match_source(fs, m->source, cache))
+               return rc;
+       if (m->fstypes && !mnt_fs_match_fstype(fs, m->fstypes))
+               return rc;
+       if (m->options && !mnt_fs_match_options(fs, m->options))
+               return rc;
+
+       return !rc;
+}
+
+static inline int is_list_mode(struct match_data *m)
+{
+       return (!m->source && !m->target && !m->fstypes && !m->options);
+}
+
+static inline int is_mount_mode(struct match_data *m, int fl)
+{
+       if (!m->source)
+              return 0;                /* <devname|TAG=|mountpoint> is required */
+       if (m->fstypes || m->options)
+               return 0;               /* cannot be restricted by -t or -O */
+       if (!(fl & FL_FIRSTONLY))
+               return 0;               /* we have to return the first entry only */
+
+       return 1;                       /* ok */
+}
+
+static void __attribute__((__noreturn__)) usage(FILE *out)
+{
+       fprintf(out, _("Usage: %s [options] <spec> [<target>]\n\nOptions:\n"),
+                       program_invocation_short_name);
+
+       fprintf(out, _(
+       " -s, --fstab            search in static table of filesystems\n"
+       " -m, --mtab             search in table of mounted filesystems (default)\n"
+       " -k, --kernel           search in kernel (mountinfo) file\n\n"
+
+       " -c, --canonicalize     canonicalize printed paths\n"
+       " -d, --direction <word> search direction - 'forward' or 'backward'\n"
+       " -e, --evaluate         print all TAGs (LABEL/UUID) evaluated\n"
+       " -h, --help             print this help\n"
+       " -i, --invert           invert sense of matching\n"
+        " -l, --first-only       print the first found filesystem only\n"
+       " -o, --output <list>    output columns\n"
+       " -O, --options <list>   limit the set of filesystems by mount options\n"
+       " -t, --types <list>     limit the set of filesystem by FS types\n"));
+
+       fprintf(out, _("\nFor more information see findmnt(1).\n"));
+
+       exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+int main(int argc, char *argv[])
+{
+       char *tabfile = NULL;
+       int direction = MNT_ITER_FORWARD, ct = 0;
+       mnt_tab *tb;
+       mnt_iter *itr;
+       mnt_fs *fs = NULL;
+       int c;
+       struct match_data mdata_buf, *mdata = &mdata_buf;
+
+       struct option longopts[] = {
+           { "fstab",        0, 0, 's' },
+           { "mtab",         0, 0, 'm' },
+           { "kernel",       0, 0, 'k' },
+           { "canonicalize", 0, 0, 'c' },
+           { "direction",    1, 0, 'd' },
+           { "evaluate",     0, 0, 'e' },
+           { "help",         0, 0, 'h' },
+           { "invert",       0, 0, 'i' },
+           { "first-only",   0, 0, 'l' },
+           { "output",       0, 0, 'o' },
+           { "options",      1, 0, 'O' },
+           { "types",        1, 0, 't' },
+           { NULL,           0, 0, 0 }
+       };
+
+       setlocale(LC_ALL, "");
+       bindtextdomain(PACKAGE, LOCALEDIR);
+       textdomain(PACKAGE);
+
+       /* default */
+       columns[ncolumns++] = COL_SOURCE;
+       columns[ncolumns++] = COL_TARGET;
+       columns[ncolumns++] = COL_FSTYPE;
+       columns[ncolumns++] = COL_OPTIONS;
+
+       memset(mdata, 0, sizeof(*mdata));
+
+       while ((c = getopt_long(argc, argv, "cd:ehiloO:kmst:", longopts, NULL)) != -1) {
+               switch(c) {
+               case 'c':
+                       flags |= FL_CANONICALIZE;
+                       break;
+               case 'd':
+                       if (!strcmp(optarg, _("forward")))
+                               direction = MNT_ITER_FORWARD;
+                       else if (!strcmp(optarg, _("backward")))
+                               direction = MNT_ITER_BACKWARD;
+                       else
+                               errx(EXIT_FAILURE,
+                                       _("uknown direction '%s')"), optarg);
+                       break;
+               case 'e':
+                       flags |= FL_EVALUATE;
+                       break;
+               case 'h':
+                       usage(stdout);
+                       break;
+               case 'i':
+                       flags |= FL_INVERT;
+                       break;
+               case 'l':
+                       flags |= FL_FIRSTONLY;
+                       break;
+               case 'o':
+                       ctl_set_columns(optarg);
+                       break;
+               case 'O':
+                       mdata->options = optarg;
+                       break;
+               case 'm':
+                       if (tabfile)
+                               errx(EXIT_FAILURE, _("--{fstab,mtab,kernel} "
+                                       "options are mutually exclusive"));
+                       tabfile = _PATH_MOUNTED;
+                       break;
+               case 's':
+                       if (tabfile)
+                               errx(EXIT_FAILURE, _("--{fstab,mtab,kernel} "
+                                       "options are mutually exclusive"));
+                       tabfile = _PATH_MNTTAB;
+                       break;
+               case 'k':
+                       if (tabfile)
+                               errx(EXIT_FAILURE, _("--{fstab,mtab,kernel} "
+                                       "options are mutually exclusive"));
+                       tabfile = _PATH_PROC_MOUNTINFO;
+                       break;
+               case 't':
+                       mdata->fstypes = optarg;
+                       break;
+               default:
+                       usage(stderr);
+                       break;
+               }
+       }
+
+       if (!tabfile)
+               tabfile = _PATH_MOUNTED;
+       if (optind < argc)
+               /* dev, tag or mountpoint */
+               mdata->source = argv[optind++];
+       if (optind < argc)
+               /* mountpoint */
+               mdata->target = argv[optind++];
+
+       tb = parse_tabfile(tabfile);
+       if (!tb)
+               return EXIT_FAILURE;
+
+       itr = mnt_new_iter(direction);
+       if (!itr)
+               err(EXIT_FAILURE, _("failed to initialize libmount iterator"));
+
+       cache = mnt_new_cache();
+       if (!cache)
+               err(EXIT_FAILURE, _("failed to initialize libmount cache"));
+
+       mnt_tab_set_cache(tb, cache);
+
+       if (is_list_mode(mdata)) {
+               /*
+                * Print whole file
+                */
+               while(mnt_tab_next_fs(tb, itr, &fs) == 0) {
+                       print_fs(fs);
+                       ct++;
+                       if (flags & FL_FIRSTONLY)
+                               break;
+               }
+       } else if (is_mount_mode(mdata, flags)) {
+
+               /*
+                * Look up for FS in the same way how mount(8) searchs in fstab
+                *
+                *   findmnt -l <spec>
+                */
+               fs = mnt_tab_find_source(tb, mdata->source, direction);
+               if (!fs)
+                       fs = mnt_tab_find_target(tb, mdata->source, direction);
+               if (fs) {
+                       print_fs(fs);
+                       ct++;
+               }
+       } else {
+               /*
+                * Look up for all matching entries
+                *
+                *    findmnt [-l] <source> <target> [-O <options>] [-t <types>]
+                *    findmnt [-l] <spec> [-O <options>] [-t <types>]
+                */
+again:
+               while (!mnt_tab_find_next_fs(tb, itr,
+                                       match_func, (void *) mdata, &fs)) {
+                       print_fs(fs);
+                       ct++;
+                       if (flags & FL_FIRSTONLY)
+                               break;
+               }
+               if (!ct && !mdata->target && mdata->source) {
+                       /* swap 'spec' and target. */
+                       mdata->target = mdata->source;
+                       mdata->source = NULL;
+                       mnt_reset_iter(itr, direction);
+                       goto again;
+               }
+       }
+
+       mnt_free_tab(tb);
+       mnt_free_cache(cache);
+       mnt_free_iter(itr);
+
+       return ct ? EXIT_SUCCESS : 2;
+}