/*
* 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;
}