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