]>
Commit | Line | Data |
---|---|---|
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 */ |
44 | enum { | |
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 |
56 | enum { |
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 */ |
74 | struct 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 */ |
82 | struct 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 | 98 | int flags; |
46236388 | 99 | int tt_flags = 0; |
9d67679b | 100 | |
fdedb45e | 101 | /* array with IDs of enabled columns */ |
9d67679b | 102 | int columns[__NCOLUMNS]; |
04fd7a9f | 103 | int ncolumns; |
9d67679b | 104 | |
9d67679b | 105 | /* libmount cache */ |
68164f6c | 106 | struct libmnt_cache *cache; |
04fd7a9f | 107 | |
00b4bcdf | 108 | static 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 | 115 | static struct colinfo *get_column_info(int num) |
9d67679b KZ |
116 | { |
117 | return &infos[ get_column_id(num) ]; | |
118 | } | |
119 | ||
00b4bcdf | 120 | static const char *column_id_to_name(int id) |
9d67679b KZ |
121 | { |
122 | assert(id < __NCOLUMNS); | |
123 | return infos[id].name; | |
124 | } | |
125 | ||
00b4bcdf | 126 | static const char *get_column_name(int num) |
9d67679b | 127 | { |
fdedb45e | 128 | return get_column_info(num)->name; |
9d67679b KZ |
129 | } |
130 | ||
00b4bcdf | 131 | static float get_column_whint(int num) |
9d67679b | 132 | { |
fdedb45e | 133 | return get_column_info(num)->whint; |
9d67679b KZ |
134 | } |
135 | ||
32e5466a | 136 | static int get_column_flags(int num) |
9d67679b | 137 | { |
32e5466a | 138 | return get_column_info(num)->flags; |
9d67679b KZ |
139 | } |
140 | ||
00b4bcdf | 141 | static const char *get_match(int id) |
9d67679b KZ |
142 | { |
143 | assert(id < __NCOLUMNS); | |
144 | return infos[id].match; | |
145 | } | |
146 | ||
00b4bcdf | 147 | static void set_match(int id, const char *match) |
9d67679b KZ |
148 | { |
149 | assert(id < __NCOLUMNS); | |
150 | infos[id].match = match; | |
151 | } | |
152 | ||
32e5466a KZ |
153 | static 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 | 171 | static 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 | 184 | static 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 | 196 | static 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 | 207 | static 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 | 222 | static 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 | 242 | static 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 |
312 | static 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 | 358 | static 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 |
375 | static 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 | 392 | static 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 |
405 | static 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; |
438 | leave: | |
439 | mnt_free_iter(itr); | |
440 | return rc; | |
04fd7a9f KZ |
441 | } |
442 | ||
20055151 | 443 | /* error callback */ |
68164f6c | 444 | static 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 | 451 | static 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()) */ |
479 | static 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 |
504 | static 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 | */ | |
535 | again: | |
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 |
554 | static 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; | |
582 | done: | |
583 | mnt_free_iter(itr); | |
584 | return rc; | |
585 | } | |
586 | ||
32e5466a KZ |
587 | static 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; | |
675 | done: | |
676 | mnt_free_table(tb_new); | |
677 | mnt_free_tabdiff(diff); | |
678 | mnt_free_iter(itr); | |
679 | return rc; | |
680 | } | |
681 | ||
abafd686 | 682 | static 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 | 739 | static void __attribute__((__noreturn__)) |
9d67679b KZ |
740 | errx_mutually_exclusive(const char *opts) |
741 | { | |
742 | errx(EXIT_FAILURE, "%s %s", opts, _("options are mutually exclusive")); | |
743 | } | |
744 | ||
04fd7a9f KZ |
745 | int 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 |
1009 | leave: |
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 | } |