findmnt: add new command
Signed-off-by: Karel Zak <kzak@redhat.com>
This commit is contained in:
parent
b6bd3efde1
commit
04fd7a9feb
|
@ -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"
|
||||
|
||||
|
|
|
@ -17,3 +17,4 @@ uuidd
|
|||
findfs
|
||||
blkid
|
||||
wipefs
|
||||
findmnt
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue