]> git.ipfire.org Git - thirdparty/util-linux.git/blame - misc-utils/findmnt.c
findmnt: improve spelling, wording and order of help text
[thirdparty/util-linux.git] / misc-utils / findmnt.c
CommitLineData
04fd7a9f
KZ
1/*
2 * findmnt(8)
3 *
68164f6c 4 * Copyright (C) 2010,2011 Red Hat, Inc. All rights reserved.
04fd7a9f
KZ
5 * Written by Karel Zak <kzak@redhat.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it would be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software Foundation,
19 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
04fd7a9f
KZ
21#include <stdio.h>
22#include <stdlib.h>
23#include <errno.h>
04fd7a9f
KZ
24#include <unistd.h>
25#include <getopt.h>
26#include <string.h>
9d67679b 27#include <termios.h>
9d67679b
KZ
28#ifdef HAVE_SYS_IOCTL_H
29#include <sys/ioctl.h>
30#endif
9d67679b 31#include <assert.h>
32e5466a 32#include <poll.h>
9d67679b 33
2a1f429a 34#include <libmount.h>
fdedb45e 35
04fd7a9f
KZ
36#include "pathnames.h"
37#include "nls.h"
9d67679b 38#include "c.h"
fdedb45e 39#include "tt.h"
ad38fb9f 40#include "strutils.h"
04fd7a9f 41
9d67679b
KZ
42/* flags */
43enum {
44 FL_EVALUATE = (1 << 1),
45 FL_CANONICALIZE = (1 << 2),
46 FL_FIRSTONLY = (1 << 3),
47 FL_INVERT = (1 << 4),
9d67679b 48 FL_NOSWAPMATCH = (1 << 6),
b2214e1f 49 FL_NOFSROOT = (1 << 7),
049caefd 50 FL_SUBMOUNTS = (1 << 8),
32e5466a 51 FL_POLL = (1 << 9)
9d67679b
KZ
52};
53
54/* column IDs */
04fd7a9f
KZ
55enum {
56 COL_SOURCE,
57 COL_TARGET,
58 COL_FSTYPE,
59 COL_OPTIONS,
631280e0
KZ
60 COL_VFS_OPTIONS,
61 COL_FS_OPTIONS,
04fd7a9f
KZ
62 COL_LABEL,
63 COL_UUID,
46236388 64 COL_MAJMIN,
32e5466a
KZ
65 COL_ACTION,
66 COL_OLD_TARGET,
67 COL_OLD_OPTIONS,
04fd7a9f 68
af0dc7d3 69 FINDMNT_NCOLUMNS
04fd7a9f
KZ
70};
71
9d67679b
KZ
72/* column names */
73struct colinfo {
74 const char *name; /* header */
75 double whint; /* width hint (N < 1 is in percent of termwidth) */
32e5466a 76 int flags; /* tt flags */
b152e359 77 const char *help; /* column description */
9d67679b 78 const char *match; /* pattern for match_func() */
04fd7a9f
KZ
79};
80
af0dc7d3
KZ
81/* columns descriptions (don't use const, this is writable) */
82static struct colinfo infos[FINDMNT_NCOLUMNS] = {
b152e359
KZ
83 [COL_SOURCE] = { "SOURCE", 0.25, 0, N_("source device") },
84 [COL_TARGET] = { "TARGET", 0.30, TT_FL_TREE, N_("mountpoint") },
85 [COL_FSTYPE] = { "FSTYPE", 0.10, TT_FL_TRUNC, N_("filesystem type") },
86 [COL_OPTIONS] = { "OPTIONS", 0.10, TT_FL_TRUNC, N_("all mount options") },
87 [COL_VFS_OPTIONS] = { "VFS-OPTIONS", 0.20, TT_FL_TRUNC, N_("VFS specific mount options") },
88 [COL_FS_OPTIONS] = { "FS-OPTIONS", 0.10, TT_FL_TRUNC, N_("FS specific mount options") },
89 [COL_LABEL] = { "LABEL", 0.10, 0, N_("filesystem label") },
90 [COL_UUID] = { "UUID", 36, 0, N_("filesystem UUID") },
91 [COL_MAJMIN] = { "MAJ:MIN", 6, 0, N_("major:minor device number") },
92 [COL_ACTION] = { "ACTION", 10, TT_FL_STRICTWIDTH, N_("action detected by --poll") },
93 [COL_OLD_OPTIONS] = { "OLD-OPTIONS", 0.10, TT_FL_TRUNC, N_("old mount options saved by --poll") },
94 [COL_OLD_TARGET] = { "OLD-TARGET", 0.30, 0, N_("old mountpoint saved by --poll") },
04fd7a9f
KZ
95};
96
9d67679b 97/* global flags */
af0dc7d3
KZ
98static int flags;
99static int tt_flags;
9d67679b 100
fdedb45e 101/* array with IDs of enabled columns */
af0dc7d3
KZ
102static int columns[FINDMNT_NCOLUMNS];
103static int ncolumns;
9d67679b 104
582a5006 105/* poll actions (parsed --poll=<list> */
af0dc7d3
KZ
106#define FINDMNT_NACTIONS 4 /* mount, umount, move, remount */
107static int actions[FINDMNT_NACTIONS];
108static int nactions;
582a5006 109
9d67679b 110/* libmount cache */
af0dc7d3 111static struct libmnt_cache *cache;
04fd7a9f 112
00b4bcdf 113static int get_column_id(int num)
9d67679b 114{
9d67679b 115 assert(num < ncolumns);
af0dc7d3 116 assert(columns[num] < FINDMNT_NCOLUMNS);
fdedb45e 117 return columns[num];
9d67679b
KZ
118}
119
00b4bcdf 120static struct colinfo *get_column_info(int num)
9d67679b
KZ
121{
122 return &infos[ get_column_id(num) ];
123}
124
00b4bcdf 125static const char *column_id_to_name(int id)
9d67679b 126{
af0dc7d3 127 assert(id < FINDMNT_NCOLUMNS);
9d67679b
KZ
128 return infos[id].name;
129}
130
00b4bcdf 131static const char *get_column_name(int num)
9d67679b 132{
fdedb45e 133 return get_column_info(num)->name;
9d67679b
KZ
134}
135
00b4bcdf 136static float get_column_whint(int num)
9d67679b 137{
fdedb45e 138 return get_column_info(num)->whint;
9d67679b
KZ
139}
140
32e5466a 141static int get_column_flags(int num)
9d67679b 142{
32e5466a 143 return get_column_info(num)->flags;
9d67679b
KZ
144}
145
00b4bcdf 146static const char *get_match(int id)
9d67679b 147{
af0dc7d3 148 assert(id < FINDMNT_NCOLUMNS);
9d67679b
KZ
149 return infos[id].match;
150}
151
00b4bcdf 152static void set_match(int id, const char *match)
9d67679b 153{
af0dc7d3 154 assert(id < FINDMNT_NCOLUMNS);
9d67679b
KZ
155 infos[id].match = match;
156}
157
32e5466a
KZ
158static int is_tabdiff_column(int id)
159{
af0dc7d3 160 assert(id < FINDMNT_NCOLUMNS);
32e5466a
KZ
161
162 switch(id) {
163 case COL_ACTION:
164 case COL_OLD_TARGET:
165 case COL_OLD_OPTIONS:
166 return 1;
167 default:
168 break;
169 }
170 return 0;
171}
172
9d67679b
KZ
173/*
174 * "findmnt" without any filter
175 */
00b4bcdf 176static int is_listall_mode(void)
9d67679b
KZ
177{
178 return (!get_match(COL_SOURCE) &&
179 !get_match(COL_TARGET) &&
180 !get_match(COL_FSTYPE) &&
181 !get_match(COL_OPTIONS));
182}
183
582a5006
KZ
184/*
185 * Returns 1 if the @act is in the --poll=<list>
186 */
187static int has_poll_action(int act)
188{
189 int i;
190
191 if (!nactions)
192 return 1; /* all actions enabled */
193 for (i = 0; i < nactions; i++)
194 if (actions[i] == act)
195 return 1;
196 return 0;
197}
198
199static int poll_action_name_to_id(const char *name, size_t namesz)
200{
201 int id = -1;
202
203 if (strncasecmp(name, "move", namesz) == 0 && namesz == 4)
204 id = MNT_TABDIFF_MOVE;
205 else if (strncasecmp(name, "mount", namesz) == 0 && namesz == 5)
206 id = MNT_TABDIFF_MOUNT;
207 else if (strncasecmp(name, "umount", namesz) == 0 && namesz == 6)
208 id = MNT_TABDIFF_UMOUNT;
209 else if (strncasecmp(name, "remount", namesz) == 0 && namesz == 7)
210 id = MNT_TABDIFF_REMOUNT;
211 else
212 warnx(_("unknown action: %s"), name);
213
214 return id;
215}
216
9d67679b
KZ
217/*
218 * findmnt --first-only <devname|TAG=|mountpoint>
219 *
220 * ... it works like "mount <devname|TAG=|mountpoint>"
221 */
00b4bcdf 222static int is_mount_compatible_mode(void)
9d67679b
KZ
223{
224 if (!get_match(COL_SOURCE))
225 return 0; /* <devname|TAG=|mountpoint> is required */
226 if (get_match(COL_FSTYPE) || get_match(COL_OPTIONS))
227 return 0; /* cannot be restricted by -t or -O */
228 if (!(flags & FL_FIRSTONLY))
229 return 0; /* we have to return the first entry only */
230
231 return 1; /* ok */
232}
233
32e5466a 234static void disable_columns_truncate(void)
9d67679b
KZ
235{
236 int i;
237
af0dc7d3 238 for (i = 0; i < FINDMNT_NCOLUMNS; i++)
32e5466a 239 infos[i].flags &= ~TT_FL_TRUNC;
9d67679b
KZ
240}
241
04fd7a9f
KZ
242/*
243 * converts @name to column ID
244 */
9d67679b 245static int column_name_to_id(const char *name, size_t namesz)
04fd7a9f
KZ
246{
247 int i;
248
af0dc7d3 249 for (i = 0; i < FINDMNT_NCOLUMNS; i++) {
9d67679b 250 const char *cn = column_id_to_name(i);
04fd7a9f 251
9d67679b 252 if (!strncasecmp(name, cn, namesz) && !*(cn + namesz))
04fd7a9f
KZ
253 return i;
254 }
fdedb45e 255 warnx(_("unknown column: %s"), name);
04fd7a9f
KZ
256 return -1;
257}
258
fdedb45e 259/* Returns LABEL or UUID */
68164f6c 260static const char *get_tag(struct libmnt_fs *fs, const char *tagname)
9d67679b
KZ
261{
262 const char *t, *v, *res;
263
264 if (!mnt_fs_get_tag(fs, &t, &v) && !strcmp(t, tagname))
265 res = v;
266 else {
267 res = mnt_fs_get_source(fs);
268 if (res)
269 res = mnt_resolve_spec(res, cache);
270 if (res)
271 res = mnt_cache_find_tag_value(cache, res, tagname);
272 }
273
274 return res;
275}
276
46236388
KZ
277/* reads FS data from libmount
278 * TODO: add function that will deallocate data allocated by get_data()
279 */
68164f6c 280static const char *get_data(struct libmnt_fs *fs, int num)
9d67679b
KZ
281{
282 const char *str = NULL;
283
284 switch(get_column_id(num)) {
04fd7a9f 285 case COL_SOURCE:
b2214e1f
KZ
286 {
287 const char *root = mnt_fs_get_root(fs);
288
9d67679b 289 str = mnt_fs_get_srcpath(fs);
04fd7a9f 290
9d67679b
KZ
291 if (str && (flags & FL_CANONICALIZE))
292 str = mnt_resolve_path(str, cache);
293 if (!str) {
294 str = mnt_fs_get_source(fs);
295
296 if (str && (flags & FL_EVALUATE))
297 str = mnt_resolve_spec(str, cache);
298 }
b2214e1f
KZ
299 if (root && str && !(flags & FL_NOFSROOT) && strcmp(root, "/")) {
300 char *tmp;
301
302 if (asprintf(&tmp, "%s[%s]", str, root) > 0)
303 str = tmp;
304 }
04fd7a9f 305 break;
b2214e1f 306 }
04fd7a9f 307 case COL_TARGET:
fdedb45e 308 str = mnt_fs_get_target(fs);
04fd7a9f
KZ
309 break;
310 case COL_FSTYPE:
311 str = mnt_fs_get_fstype(fs);
312 break;
313 case COL_OPTIONS:
d783ee0b 314 str = mnt_fs_get_options(fs);
04fd7a9f 315 break;
631280e0 316 case COL_VFS_OPTIONS:
411fe06e 317 str = mnt_fs_get_vfs_options(fs);
631280e0
KZ
318 break;
319 case COL_FS_OPTIONS:
411fe06e 320 str = mnt_fs_get_fs_options(fs);
631280e0 321 break;
9d67679b
KZ
322 case COL_UUID:
323 str = get_tag(fs, "UUID");
324 break;
325 case COL_LABEL:
326 str = get_tag(fs, "LABEL");
327 break;
46236388
KZ
328 case COL_MAJMIN:
329 {
330 dev_t devno = mnt_fs_get_devno(fs);
331 if (devno) {
332 char *tmp;
333 int rc = 0;
49e9fd3a 334 if ((tt_flags & TT_FL_RAW) || (tt_flags & TT_FL_EXPORT))
68164f6c
KZ
335 rc = asprintf(&tmp, "%u:%u",
336 major(devno), minor(devno));
46236388 337 else
68164f6c
KZ
338 rc = asprintf(&tmp, "%3u:%-3u",
339 major(devno), minor(devno));
46236388
KZ
340 if (rc)
341 str = tmp;
342 }
343 }
04fd7a9f 344 default:
9d67679b 345 break;
04fd7a9f 346 }
fdedb45e 347 return str;
9d67679b
KZ
348}
349
32e5466a
KZ
350static const char *get_tabdiff_data(struct libmnt_fs *old_fs,
351 struct libmnt_fs *new_fs,
352 int change,
353 int num)
354{
355 const char *str = NULL;
356
357 switch (get_column_id(num)) {
358 case COL_ACTION:
359 switch (change) {
360 case MNT_TABDIFF_MOUNT:
361 str = _("mount");
362 break;
363 case MNT_TABDIFF_UMOUNT:
364 str = _("umount");
365 break;
366 case MNT_TABDIFF_REMOUNT:
367 str = _("remount");
368 break;
369 case MNT_TABDIFF_MOVE:
370 str = _("move");
371 break;
372 default:
373 str = _("unknown");
374 break;
375 }
376 break;
377 case COL_OLD_OPTIONS:
77a1c5f7
KZ
378 if (old_fs && (change == MNT_TABDIFF_REMOUNT ||
379 change == MNT_TABDIFF_UMOUNT))
32e5466a
KZ
380 str = mnt_fs_get_options(old_fs);
381 break;
382 case COL_OLD_TARGET:
77a1c5f7
KZ
383 if (old_fs && (change == MNT_TABDIFF_MOVE ||
384 change == MNT_TABDIFF_UMOUNT))
32e5466a
KZ
385 str = mnt_fs_get_target(old_fs);
386 break;
387 default:
388 if (new_fs)
389 str = get_data(new_fs, num);
390 else
391 str = get_data(old_fs, num);
392 break;
393 }
394 return str;
395}
396
fdedb45e 397/* adds one line to the output @tab */
68164f6c 398static struct tt_line *add_line(struct tt *tt, struct libmnt_fs *fs,
fdedb45e 399 struct tt_line *parent)
9d67679b 400{
fdedb45e
KZ
401 int i;
402 struct tt_line *line = tt_add_line(tt, parent);
9d67679b 403
fdedb45e
KZ
404 if (!line) {
405 warn(_("failed to add line to output"));
406 return NULL;
9d67679b 407 }
fdedb45e
KZ
408 for (i = 0; i < ncolumns; i++)
409 tt_line_set_data(line, i, get_data(fs, i));
9d67679b 410
049caefd 411 tt_line_set_userdata(line, fs);
fdedb45e 412 return line;
9d67679b
KZ
413}
414
32e5466a
KZ
415static struct tt_line *add_tabdiff_line(struct tt *tt, struct libmnt_fs *new_fs,
416 struct libmnt_fs *old_fs, int change)
417{
418 int i;
419 struct tt_line *line = tt_add_line(tt, NULL);
420
421 if (!line) {
422 warn(_("failed to add line to output"));
423 return NULL;
424 }
425 for (i = 0; i < ncolumns; i++)
426 tt_line_set_data(line, i,
427 get_tabdiff_data(old_fs, new_fs, change, i));
428
429 return line;
430}
431
68164f6c 432static int has_line(struct tt *tt, struct libmnt_fs *fs)
049caefd
KZ
433{
434 struct list_head *p;
435
436 list_for_each(p, &tt->tb_lines) {
437 struct tt_line *ln = list_entry(p, struct tt_line, ln_lines);
68164f6c 438 if ((struct libmnt_fs *) ln->userdata == fs)
049caefd
KZ
439 return 1;
440 }
441 return 0;
442}
443
444/* reads filesystems from @tb (libmount) and fillin @tt (output table) */
68164f6c
KZ
445static int create_treenode(struct tt *tt, struct libmnt_table *tb,
446 struct libmnt_fs *fs, struct tt_line *parent_line)
04fd7a9f 447{
68164f6c
KZ
448 struct libmnt_fs *chld = NULL;
449 struct libmnt_iter *itr = NULL;
fdedb45e
KZ
450 struct tt_line *line;
451 int rc = -1;
9d67679b 452
fdedb45e
KZ
453 if (!fs) {
454 /* first call, get root FS */
68164f6c 455 if (mnt_table_get_root_fs(tb, &fs))
fdedb45e
KZ
456 goto leave;
457 parent_line = NULL;
049caefd
KZ
458
459 } else if ((flags & FL_SUBMOUNTS) && has_line(tt, fs))
460 return 0;
9d67679b 461
fdedb45e
KZ
462 itr = mnt_new_iter(MNT_ITER_FORWARD);
463 if (!itr)
464 goto leave;
9d67679b 465
fdedb45e
KZ
466 line = add_line(tt, fs, parent_line);
467 if (!line)
468 goto leave;
9d67679b 469
fdedb45e
KZ
470 /*
471 * add all children to the output table
472 */
68164f6c 473 while(mnt_table_next_child_fs(tb, itr, fs, &chld) == 0) {
fdedb45e
KZ
474 if (create_treenode(tt, tb, chld, line))
475 goto leave;
9d67679b 476 }
fdedb45e
KZ
477 rc = 0;
478leave:
479 mnt_free_iter(itr);
480 return rc;
04fd7a9f
KZ
481}
482
20055151 483/* error callback */
6e5c0fc2
KZ
484static int parser_errcb(struct libmnt_table *tb __attribute__ ((__unused__)),
485 const char *filename, int line)
20055151
KZ
486{
487 warn(_("%s: parse error at line %d"), filename, line);
488 return 0;
489}
490
fdedb45e 491/* calls libmount fstab/mtab/mountinfo parser */
68164f6c 492static struct libmnt_table *parse_tabfile(const char *path)
04fd7a9f 493{
20055151 494 int rc;
68164f6c 495 struct libmnt_table *tb = mnt_new_table();
20055151 496
fdedb45e 497 if (!tb) {
32e5466a 498 warn(_("failed to initialize libmount table"));
04fd7a9f 499 return NULL;
fdedb45e 500 }
20055151 501
68164f6c 502 mnt_table_set_parser_errcb(tb, parser_errcb);
20055151
KZ
503
504 if (!strcmp(path, _PATH_MNTTAB))
68164f6c 505 rc = mnt_table_parse_fstab(tb, NULL);
0532ba1d 506 else if (!strcmp(path, _PATH_MOUNTED))
68164f6c 507 rc = mnt_table_parse_mtab(tb, NULL);
20055151 508 else
68164f6c 509 rc = mnt_table_parse_file(tb, path);
20055151
KZ
510
511 if (rc) {
68164f6c 512 mnt_free_table(tb);
e8ab5ce3 513 warn(_("can't read %s"), path);
fdedb45e
KZ
514 return NULL;
515 }
04fd7a9f 516 return tb;
04fd7a9f
KZ
517}
518
68164f6c 519/* filter function for libmount (mnt_table_find_next_fs()) */
6e5c0fc2
KZ
520static int match_func(struct libmnt_fs *fs,
521 void *data __attribute__ ((__unused__)))
04fd7a9f 522{
04fd7a9f 523 int rc = flags & FL_INVERT ? 1 : 0;
9d67679b 524 const char *m;
04fd7a9f 525
9d67679b
KZ
526 m = get_match(COL_TARGET);
527 if (m && !mnt_fs_match_target(fs, m, cache))
04fd7a9f 528 return rc;
9d67679b
KZ
529
530 m = get_match(COL_SOURCE);
531 if (m && !mnt_fs_match_source(fs, m, cache))
04fd7a9f 532 return rc;
9d67679b
KZ
533
534 m = get_match(COL_FSTYPE);
535 if (m && !mnt_fs_match_fstype(fs, m))
04fd7a9f 536 return rc;
9d67679b
KZ
537
538 m = get_match(COL_OPTIONS);
539 if (m && !mnt_fs_match_options(fs, m))
04fd7a9f
KZ
540 return rc;
541
542 return !rc;
543}
544
fdedb45e 545/* iterate over filesystems in @tb */
68164f6c
KZ
546static struct libmnt_fs *get_next_fs(struct libmnt_table *tb,
547 struct libmnt_iter *itr)
04fd7a9f 548{
68164f6c 549 struct libmnt_fs *fs = NULL;
9d67679b
KZ
550
551 if (is_listall_mode()) {
552 /*
553 * Print whole file
554 */
e3963f60
KZ
555 if (mnt_table_next_fs(tb, itr, &fs) != 0)
556 return NULL;
9d67679b
KZ
557
558 } else if (is_mount_compatible_mode()) {
559 /*
560 * Look up for FS in the same way how mount(8) searchs in fstab
561 *
562 * findmnt -f <spec>
563 */
68164f6c 564 fs = mnt_table_find_source(tb, get_match(COL_SOURCE),
9d67679b 565 mnt_iter_get_direction(itr));
9a30c6ef
KZ
566
567 if (!fs && !(flags & FL_NOSWAPMATCH))
68164f6c 568 fs = mnt_table_find_target(tb, get_match(COL_SOURCE),
9d67679b
KZ
569 mnt_iter_get_direction(itr));
570 } else {
571 /*
572 * Look up for all matching entries
573 *
574 * findmnt [-l] <source> <target> [-O <options>] [-t <types>]
575 * findmnt [-l] <spec> [-O <options>] [-t <types>]
576 */
577again:
68164f6c 578 mnt_table_find_next_fs(tb, itr, match_func, NULL, &fs);
9d67679b
KZ
579
580 if (!fs &&
581 !(flags & FL_NOSWAPMATCH) &&
582 !get_match(COL_TARGET) && get_match(COL_SOURCE)) {
583
584 /* swap 'spec' and target. */
585 set_match(COL_TARGET, get_match(COL_SOURCE));
586 set_match(COL_SOURCE, NULL);
587 mnt_reset_iter(itr, -1);
588
589 goto again;
590 }
591 }
9d67679b 592
fdedb45e 593 return fs;
04fd7a9f
KZ
594}
595
68164f6c
KZ
596static int add_matching_lines(struct libmnt_table *tb,
597 struct tt *tt, int direction)
049caefd 598{
68164f6c
KZ
599 struct libmnt_iter *itr = NULL;
600 struct libmnt_fs *fs;
049caefd
KZ
601 int nlines = 0, rc = -1;
602
603 itr = mnt_new_iter(direction);
604 if (!itr) {
605 warn(_("failed to initialize libmount iterator"));
606 goto done;
607 }
608
609 while((fs = get_next_fs(tb, itr))) {
610 if ((tt_flags & TT_FL_TREE) || (flags & FL_SUBMOUNTS))
611 rc = create_treenode(tt, tb, fs, NULL);
612 else
613 rc = !add_line(tt, fs, NULL);
614 if (rc)
615 goto done;
616 nlines++;
617 if (flags & FL_FIRSTONLY)
618 break;
619 flags |= FL_NOSWAPMATCH;
620 }
621
622 if (nlines)
623 rc = 0;
624done:
625 mnt_free_iter(itr);
626 return rc;
627}
628
582a5006
KZ
629static int poll_match(struct libmnt_fs *fs)
630{
631 int rc = match_func(fs, NULL);
632
633 if (rc == 0 && !(flags & FL_NOSWAPMATCH) &&
634 get_match(COL_SOURCE) && !get_match(COL_TARGET)) {
635 /*
636 * findmnt --poll /foo
637 * The '/foo' source as well as target.
638 */
639 const char *str = get_match(COL_SOURCE);
640
641 set_match(COL_TARGET, str); /* swap */
642 set_match(COL_SOURCE, NULL);
643
644 rc = match_func(fs, NULL);
645
646 set_match(COL_TARGET, NULL); /* restore */
647 set_match(COL_SOURCE, str);
648
649 }
650 return rc;
651}
652
32e5466a
KZ
653static int poll_table(struct libmnt_table *tb, const char *tabfile,
654 int timeout, struct tt *tt, int direction)
655{
656 FILE *f;
657 int rc = -1;
658 struct libmnt_iter *itr = NULL;
659 struct libmnt_table *tb_new = NULL;
660 struct libmnt_tabdiff *diff = NULL;
661 struct pollfd fds[1];
662
663 tb_new = mnt_new_table();
664 if (!tb_new) {
665 warn(_("failed to initialize libmount table"));
666 goto done;
667 }
668
669 itr = mnt_new_iter(direction);
670 if (!itr) {
671 warn(_("failed to initialize libmount iterator"));
672 goto done;
673 }
674
675 diff = mnt_new_tabdiff();
676 if (!diff) {
677 warn(_("failed to initialize libmount tabdiff"));
678 goto done;
679 }
680
681 /* cache is unnecessary to detect changes */
682 mnt_table_set_cache(tb, NULL);
683 mnt_table_set_cache(tb_new, NULL);
684
685 f = fopen(tabfile, "r");
686 if (!f) {
687 warn(_("%s: open failed"), tabfile);
688 goto done;
689 }
690
691 mnt_table_set_parser_errcb(tb_new, parser_errcb);
692
693 fds[0].fd = fileno(f);
694 fds[0].events = POLLPRI;
695
696 while (1) {
697 struct libmnt_table *tmp;
698 struct libmnt_fs *old, *new;
582a5006 699 int change, count;
32e5466a 700
582a5006
KZ
701 count = poll(fds, 1, timeout);
702 if (count == 0)
ad38fb9f 703 break; /* timeout */
582a5006 704 if (count < 0) {
32e5466a
KZ
705 warn(_("poll() failed"));
706 goto done;
707 }
708
709 rewind(f);
710 rc = mnt_table_parse_stream(tb_new, f, tabfile);
711 if (!rc)
712 rc = mnt_diff_tables(diff, tb, tb_new);
713 if (rc < 0)
714 goto done;
715
582a5006 716 count = 0;
32e5466a
KZ
717 mnt_reset_iter(itr, direction);
718 while(mnt_tabdiff_next_change(
719 diff, itr, &old, &new, &change) == 0) {
720
582a5006
KZ
721 if (!has_poll_action(change))
722 continue;
723 if (!poll_match(new ? new : old))
724 continue;
725 count++;
32e5466a
KZ
726 rc = !add_tabdiff_line(tt, new, old, change);
727 if (rc)
728 goto done;
729 if (flags & FL_FIRSTONLY)
730 break;
731 }
732
582a5006
KZ
733 if (count) {
734 rc = tt_print_table(tt);
735 if (rc)
736 goto done;
737 }
32e5466a
KZ
738
739 /* swap tables */
740 tmp = tb;
741 tb = tb_new;
742 tb_new = tmp;
743
744 tt_remove_lines(tt);
745 mnt_reset_table(tb_new);
582a5006
KZ
746
747 if (count && (flags & FL_FIRSTONLY))
748 break;
32e5466a
KZ
749 }
750
751 rc = 0;
752done:
753 mnt_free_table(tb_new);
754 mnt_free_tabdiff(diff);
755 mnt_free_iter(itr);
756 return rc;
757}
758
abafd686 759static void __attribute__((__noreturn__)) usage(FILE *out)
04fd7a9f 760{
9ead0006
KZ
761 int i;
762
9d67679b
KZ
763 fprintf(out, _(
764 "\nUsage:\n"
765 " %1$s [options]\n"
766 " %1$s [options] <device> | <mountpoint>\n"
767 " %1$s [options] <device> <mountpoint>\n"
768 " %1$s [options] [--source <device>] [--target <mountpoint>]\n"),
769 program_invocation_short_name);
04fd7a9f
KZ
770
771 fprintf(out, _(
9d67679b 772 "\nOptions:\n"
04fd7a9f 773 " -s, --fstab search in static table of filesystems\n"
cbec3cbf 774 " -m, --mtab search in table of mounted filesystems\n"
c3214059
BS
775 " -k, --kernel search in kernel table of mounted\n"
776 " filesystems (default)\n\n"
04fd7a9f 777
582a5006 778 " -p, --poll[=<list>] monitor changes in table of mounted filesystems\n"
c3214059 779 " -w, --timeout <num> upper limit in milliseconds that --poll will block\n\n"
32e5466a 780
c3214059 781 " -a, --ascii use ASCII chars for tree formatting\n"
04fd7a9f 782 " -c, --canonicalize canonicalize printed paths\n"
c3214059
BS
783 " -d, --direction <word> direction of search, 'forward' or 'backward'\n"
784 " -e, --evaluate convert tags (LABEL/UUID) to device names\n"
9d67679b 785 " -f, --first-only print the first found filesystem only\n"
c3214059
BS
786 " -h, --help display this help text and exit\n"
787 " -i, --invert invert the sense of matching\n"
46236388 788 " -l, --list use list format output\n"
c3214059 789 " -n, --noheadings don't print column headings\n"
9d67679b 790 " -u, --notruncate don't truncate text in columns\n"
04fd7a9f 791 " -O, --options <list> limit the set of filesystems by mount options\n"
c3214059 792 " -o, --output <list> the output columns to be shown\n"
49e9fd3a
KZ
793 " -P, --pairs use key=\"value\" output format\n"
794 " -r, --raw use raw output format\n"
c3214059 795 " -t, --types <list> limit the set of filesystems by FS types\n"
b2214e1f 796 " -v, --nofsroot don't print [/dir] for bind or btrfs mounts\n"
c3214059
BS
797 " -R, --submounts print all submounts for the matching filesystems\n"
798 " -S, --source <string> the device to mount (by name, LABEL= or UUID=)\n"
799 " -T, --target <string> the mountpoint to use\n\n"));
04fd7a9f 800
9ead0006
KZ
801
802 fprintf(out, _("\nAvailable columns:\n"));
803
b152e359
KZ
804 for (i = 0; i < FINDMNT_NCOLUMNS; i++)
805 fprintf(out, " %11s %s\n", infos[i].name, _(infos[i].help));
9ead0006 806
9ead0006 807
04fd7a9f
KZ
808 fprintf(out, _("\nFor more information see findmnt(1).\n"));
809
810 exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
811}
812
abafd686 813static void __attribute__((__noreturn__))
9d67679b
KZ
814errx_mutually_exclusive(const char *opts)
815{
816 errx(EXIT_FAILURE, "%s %s", opts, _("options are mutually exclusive"));
817}
818
04fd7a9f
KZ
819int main(int argc, char *argv[])
820{
fdedb45e 821 /* libmount */
68164f6c 822 struct libmnt_table *tb = NULL;
04fd7a9f 823 char *tabfile = NULL;
9d67679b 824 int direction = MNT_ITER_FORWARD;
ad38fb9f 825 int i, c, rc = -1, timeout = -1;
fdedb45e
KZ
826
827 /* table.h */
828 struct tt *tt = NULL;
fdedb45e 829
6c7d5ae9 830 static const struct option longopts[] = {
9d67679b 831 { "ascii", 0, 0, 'a' },
04fd7a9f
KZ
832 { "canonicalize", 0, 0, 'c' },
833 { "direction", 1, 0, 'd' },
834 { "evaluate", 0, 0, 'e' },
9d67679b
KZ
835 { "first-only", 0, 0, 'f' },
836 { "fstab", 0, 0, 's' },
04fd7a9f
KZ
837 { "help", 0, 0, 'h' },
838 { "invert", 0, 0, 'i' },
9d67679b
KZ
839 { "kernel", 0, 0, 'k' },
840 { "list", 0, 0, 'l' },
841 { "mtab", 0, 0, 'm' },
842 { "noheadings", 0, 0, 'n' },
843 { "notruncate", 0, 0, 'u' },
04fd7a9f 844 { "options", 1, 0, 'O' },
9d67679b 845 { "output", 1, 0, 'o' },
582a5006 846 { "poll", 2, 0, 'p' },
49e9fd3a 847 { "pairs", 0, 0, 'P' },
9d67679b 848 { "raw", 0, 0, 'r' },
04fd7a9f 849 { "types", 1, 0, 't' },
b2214e1f 850 { "fsroot", 0, 0, 'v' },
049caefd 851 { "submounts", 0, 0, 'R' },
9d67679b
KZ
852 { "source", 1, 0, 'S' },
853 { "target", 1, 0, 'T' },
ad38fb9f 854 { "timeout", 1, 0, 'w' },
9d67679b 855
04fd7a9f
KZ
856 { NULL, 0, 0, 0 }
857 };
858
af0dc7d3 859 assert(ARRAY_SIZE(columns) == FINDMNT_NCOLUMNS);
9d67679b 860
04fd7a9f
KZ
861 setlocale(LC_ALL, "");
862 bindtextdomain(PACKAGE, LOCALEDIR);
863 textdomain(PACKAGE);
864
9d67679b 865 /* default output format */
fdedb45e 866 tt_flags |= TT_FL_TREE;
04fd7a9f 867
9d67679b 868 while ((c = getopt_long(argc, argv,
49e9fd3a 869 "acd:ehifo:O:p::Pklmnrst:uvRS:T:w:", longopts, NULL)) != -1) {
04fd7a9f 870 switch(c) {
9d67679b 871 case 'a':
fdedb45e 872 tt_flags |= TT_FL_ASCII;
9d67679b 873 break;
04fd7a9f
KZ
874 case 'c':
875 flags |= FL_CANONICALIZE;
876 break;
877 case 'd':
4e6bd74c 878 if (!strcmp(optarg, "forward"))
04fd7a9f 879 direction = MNT_ITER_FORWARD;
4e6bd74c 880 else if (!strcmp(optarg, "backward"))
04fd7a9f
KZ
881 direction = MNT_ITER_BACKWARD;
882 else
883 errx(EXIT_FAILURE,
8e350e48 884 _("unknown direction '%s'"), optarg);
04fd7a9f
KZ
885 break;
886 case 'e':
887 flags |= FL_EVALUATE;
888 break;
889 case 'h':
890 usage(stdout);
891 break;
892 case 'i':
893 flags |= FL_INVERT;
894 break;
9d67679b 895 case 'f':
04fd7a9f
KZ
896 flags |= FL_FIRSTONLY;
897 break;
9d67679b 898 case 'u':
32e5466a 899 disable_columns_truncate();
9d67679b 900 break;
04fd7a9f 901 case 'o':
c87638ad 902 ncolumns = string_to_idarray(optarg,
bdc3ed66
KZ
903 columns, ARRAY_SIZE(columns),
904 column_name_to_id);
905 if (ncolumns < 0)
fdedb45e 906 exit(EXIT_FAILURE);
04fd7a9f
KZ
907 break;
908 case 'O':
9d67679b 909 set_match(COL_OPTIONS, optarg);
04fd7a9f 910 break;
32e5466a 911 case 'p':
bdc3ed66 912 if (optarg) {
c87638ad 913 nactions = string_to_idarray(optarg,
bdc3ed66
KZ
914 actions, ARRAY_SIZE(actions),
915 poll_action_name_to_id);
916 if (nactions < 0)
917 exit(EXIT_FAILURE);
918 }
32e5466a
KZ
919 flags |= FL_POLL;
920 tt_flags &= ~TT_FL_TREE;
921 break;
49e9fd3a
KZ
922 case 'P':
923 tt_flags |= TT_FL_EXPORT;
924 tt_flags &= ~TT_FL_TREE;
925 break;
fdedb45e 926 case 'm': /* mtab */
04fd7a9f 927 if (tabfile)
9d67679b 928 errx_mutually_exclusive("--{fstab,mtab,kernel}");
04fd7a9f 929 tabfile = _PATH_MOUNTED;
fdedb45e 930 tt_flags &= ~TT_FL_TREE;
04fd7a9f 931 break;
fdedb45e 932 case 's': /* fstab */
04fd7a9f 933 if (tabfile)
9d67679b 934 errx_mutually_exclusive("--{fstab,mtab,kernel}");
04fd7a9f 935 tabfile = _PATH_MNTTAB;
ac808156 936 tt_flags &= ~TT_FL_TREE;
04fd7a9f 937 break;
fdedb45e 938 case 'k': /* kernel (mountinfo) */
04fd7a9f 939 if (tabfile)
9d67679b 940 errx_mutually_exclusive("--{fstab,mtab,kernel}");
04fd7a9f
KZ
941 tabfile = _PATH_PROC_MOUNTINFO;
942 break;
943 case 't':
9d67679b
KZ
944 set_match(COL_FSTYPE, optarg);
945 break;
946 case 'r':
fdedb45e
KZ
947 tt_flags &= ~TT_FL_TREE; /* disable the default */
948 tt_flags |= TT_FL_RAW; /* enable raw */
9d67679b
KZ
949 break;
950 case 'l':
49e9fd3a
KZ
951 if ((tt_flags & TT_FL_RAW) && (tt_flags & TT_FL_EXPORT))
952 errx_mutually_exclusive("--{raw,list,pairs}");
9d67679b 953
fdedb45e 954 tt_flags &= ~TT_FL_TREE; /* disable the default */
9d67679b
KZ
955 break;
956 case 'n':
fdedb45e 957 tt_flags |= TT_FL_NOHEADINGS;
9d67679b 958 break;
b2214e1f
KZ
959 case 'v':
960 flags |= FL_NOFSROOT;
961 break;
049caefd
KZ
962 case 'R':
963 flags |= FL_SUBMOUNTS;
964 break;
9d67679b
KZ
965 case 'S':
966 set_match(COL_SOURCE, optarg);
967 flags |= FL_NOSWAPMATCH;
968 break;
969 case 'T':
970 set_match(COL_TARGET, optarg);
971 flags |= FL_NOSWAPMATCH;
04fd7a9f 972 break;
ad38fb9f
KZ
973 case 'w':
974 timeout = strtol_or_err(optarg,
975 _("failed to parse timeout"));
976 break;
04fd7a9f
KZ
977 default:
978 usage(stderr);
979 break;
980 }
981 }
982
32e5466a
KZ
983 /* default columns */
984 if (!ncolumns) {
985 if (flags & FL_POLL)
986 columns[ncolumns++] = COL_ACTION;
987
988 columns[ncolumns++] = COL_TARGET;
989 columns[ncolumns++] = COL_SOURCE;
990 columns[ncolumns++] = COL_FSTYPE;
991 columns[ncolumns++] = COL_OPTIONS;
992 }
993
fdedb45e 994 if (!tabfile) {
9d67679b
KZ
995 tabfile = _PATH_PROC_MOUNTINFO;
996
fdedb45e
KZ
997 if (access(tabfile, R_OK)) { /* old kernel? */
998 tabfile = _PATH_PROC_MOUNTS;
999 tt_flags &= ~TT_FL_TREE;
1000 }
1001 }
b2214e1f 1002
9d67679b
KZ
1003 if (optind < argc && (get_match(COL_SOURCE) || get_match(COL_TARGET)))
1004 errx(EXIT_FAILURE, _(
1005 "options --target and --source can't be used together "
1006 "with command line element that is not an option"));
1007
04fd7a9f 1008 if (optind < argc)
9d67679b 1009 set_match(COL_SOURCE, argv[optind++]); /* dev/tag/mountpoint */
04fd7a9f 1010 if (optind < argc)
9d67679b 1011 set_match(COL_TARGET, argv[optind++]); /* mountpoint */
04fd7a9f 1012
049caefd
KZ
1013 if ((flags & FL_SUBMOUNTS) && is_listall_mode())
1014 /* don't care about submounts if list all mounts */
1015 flags &= ~FL_SUBMOUNTS;
1016
1017 if (!(flags & FL_SUBMOUNTS) &&
1018 (!is_listall_mode() || (flags & FL_FIRSTONLY)))
fdedb45e
KZ
1019 tt_flags &= ~TT_FL_TREE;
1020
ac808156
KZ
1021 if (!(flags & FL_NOSWAPMATCH) &&
1022 !get_match(COL_TARGET) && get_match(COL_SOURCE)) {
1023 /*
1024 * Check if we can swap source and target, it's
1025 * not possible if the source is LABEL=/UUID=
1026 */
1027 const char *x = get_match(COL_SOURCE);
1028
1029 if (!strncmp(x, "LABEL=", 6) || !strncmp(x, "UUID=", 5))
1030 flags |= FL_NOSWAPMATCH;
1031 }
1032
fdedb45e
KZ
1033 /*
1034 * initialize libmount
1035 */
ac808156
KZ
1036 mnt_init_debug(0);
1037
04fd7a9f
KZ
1038 tb = parse_tabfile(tabfile);
1039 if (!tb)
fdedb45e 1040 goto leave;
04fd7a9f 1041
04fd7a9f 1042 cache = mnt_new_cache();
fdedb45e
KZ
1043 if (!cache) {
1044 warn(_("failed to initialize libmount cache"));
1045 goto leave;
1046 }
68164f6c 1047 mnt_table_set_cache(tb, cache);
04fd7a9f 1048
fdedb45e 1049 /*
00b4bcdf 1050 * initialize output formatting (tt.h)
fdedb45e
KZ
1051 */
1052 tt = tt_new_table(tt_flags);
1053 if (!tt) {
1054 warn(_("failed to initialize output table"));
1055 goto leave;
1056 }
ac808156 1057
fdedb45e 1058 for (i = 0; i < ncolumns; i++) {
32e5466a
KZ
1059 int fl = get_column_flags(i);
1060 int id = get_column_id(i);
1061
1062 if (!(tt_flags & TT_FL_TREE))
1063 fl &= ~TT_FL_TREE;
04fd7a9f 1064
32e5466a 1065 if (!(flags & FL_POLL) && is_tabdiff_column(id)) {
77a1c5f7 1066 warnx(_("%s column is requested, but --poll "
32e5466a
KZ
1067 "is not enabled"), get_column_name(i));
1068 goto leave;
1069 }
fdedb45e
KZ
1070 if (!tt_define_column(tt, get_column_name(i),
1071 get_column_whint(i), fl)) {
1072 warn(_("failed to initialize output column"));
1073 goto leave;
9d67679b 1074 }
fdedb45e 1075 }
9d67679b 1076
fdedb45e
KZ
1077 /*
1078 * Fill in data to the output table
1079 */
32e5466a
KZ
1080 if (flags & FL_POLL)
1081 /* poll mode */
ad38fb9f 1082 rc = poll_table(tb, tabfile, timeout, tt, direction);
32e5466a
KZ
1083
1084 else if ((tt_flags & TT_FL_TREE) && is_listall_mode())
049caefd
KZ
1085 /* whole tree */
1086 rc = create_treenode(tt, tb, NULL, NULL);
1087 else
1088 /* whole lits of sub-tree */
1089 rc = add_matching_lines(tb, tt, direction);
fdedb45e
KZ
1090
1091 /*
32e5466a 1092 * Print the output table for non-poll modes
fdedb45e 1093 */
32e5466a 1094 if (!rc && !(flags & FL_POLL))
049caefd 1095 tt_print_table(tt);
fdedb45e
KZ
1096leave:
1097 tt_free_table(tt);
1098
68164f6c 1099 mnt_free_table(tb);
04fd7a9f 1100 mnt_free_cache(cache);
04fd7a9f 1101
049caefd 1102 return rc ? EXIT_FAILURE : EXIT_SUCCESS;
04fd7a9f 1103}