From 04fd7a9febad79917fbf98a618ef23dcb5a14fb1 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Mon, 1 Mar 2010 22:44:53 +0100 Subject: [PATCH] findmnt: add new command Signed-off-by: Karel Zak --- include/pathnames.h | 1 + misc-utils/.gitignore | 1 + misc-utils/Makefile.am | 4 +- misc-utils/findmnt.c | 431 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 436 insertions(+), 1 deletion(-) create mode 100644 misc-utils/findmnt.c diff --git a/include/pathnames.h b/include/pathnames.h index 7d57c528f..c0572954e 100644 --- a/include/pathnames.h +++ b/include/pathnames.h @@ -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" diff --git a/misc-utils/.gitignore b/misc-utils/.gitignore index 5d34a50db..a39caaf3f 100644 --- a/misc-utils/.gitignore +++ b/misc-utils/.gitignore @@ -17,3 +17,4 @@ uuidd findfs blkid wipefs +findmnt diff --git a/misc-utils/Makefile.am b/misc-utils/Makefile.am index c98107d68..218a2103a 100644 --- a/misc-utils/Makefile.am +++ b/misc-utils/Makefile.am @@ -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 index 000000000..04aadb778 --- /dev/null +++ b/misc-utils/findmnt.c @@ -0,0 +1,431 @@ +/* + * findmnt(8) + * + * Copyright (C) 2010 Red Hat, Inc. All rights reserved. + * Written by Karel Zak + * + * 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 +#include +#include +#include +#include +#include +#include + +#include + +#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; /* 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] []\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 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 output columns\n" + " -O, --options limit the set of filesystems by mount options\n" + " -t, --types 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 + */ + 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] [-O ] [-t ] + * findmnt [-l] [-O ] [-t ] + */ +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; +}