]>
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 | * | |
7cebf0bb SK |
17 | * You should have received a copy of the GNU General Public License along |
18 | * with this program; if not, write to the Free Software Foundation, Inc., | |
19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
04fd7a9f | 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 | 28 | #ifdef HAVE_SYS_IOCTL_H |
e346233e | 29 | # include <sys/ioctl.h> |
9d67679b | 30 | #endif |
9d67679b | 31 | #include <assert.h> |
32e5466a | 32 | #include <poll.h> |
a85ef33b DR |
33 | #include <sys/statvfs.h> |
34 | #include <sys/types.h> | |
e346233e KZ |
35 | #ifdef HAVE_LIBUDEV |
36 | # include <libudev.h> | |
37 | #endif | |
2a1f429a | 38 | #include <libmount.h> |
fdedb45e | 39 | |
04fd7a9f KZ |
40 | #include "pathnames.h" |
41 | #include "nls.h" | |
c05a80ca | 42 | #include "closestream.h" |
9d67679b | 43 | #include "c.h" |
fdedb45e | 44 | #include "tt.h" |
ad38fb9f | 45 | #include "strutils.h" |
6f312c89 | 46 | #include "xalloc.h" |
fb9a0042 | 47 | #include "optutils.h" |
e346233e | 48 | #include "mangle.h" |
04fd7a9f | 49 | |
9d67679b KZ |
50 | /* flags */ |
51 | enum { | |
52 | FL_EVALUATE = (1 << 1), | |
53 | FL_CANONICALIZE = (1 << 2), | |
54 | FL_FIRSTONLY = (1 << 3), | |
55 | FL_INVERT = (1 << 4), | |
9d67679b | 56 | FL_NOSWAPMATCH = (1 << 6), |
b2214e1f | 57 | FL_NOFSROOT = (1 << 7), |
049caefd | 58 | FL_SUBMOUNTS = (1 << 8), |
cd4ed46f KZ |
59 | FL_POLL = (1 << 9), |
60 | FL_DF = (1 << 10), | |
61 | FL_ALL = (1 << 11) | |
9d67679b KZ |
62 | }; |
63 | ||
64 | /* column IDs */ | |
04fd7a9f KZ |
65 | enum { |
66 | COL_SOURCE, | |
67 | COL_TARGET, | |
68 | COL_FSTYPE, | |
69 | COL_OPTIONS, | |
631280e0 KZ |
70 | COL_VFS_OPTIONS, |
71 | COL_FS_OPTIONS, | |
04fd7a9f KZ |
72 | COL_LABEL, |
73 | COL_UUID, | |
090b5e84 KZ |
74 | COL_PARTLABEL, |
75 | COL_PARTUUID, | |
46236388 | 76 | COL_MAJMIN, |
32e5466a KZ |
77 | COL_ACTION, |
78 | COL_OLD_TARGET, | |
79 | COL_OLD_OPTIONS, | |
a85ef33b DR |
80 | COL_SIZE, |
81 | COL_AVAIL, | |
82 | COL_USED, | |
83 | COL_USEPERC, | |
8b8cd87b | 84 | COL_FSROOT, |
7d8f4e0c | 85 | COL_TID, |
9238e0fa | 86 | COL_ID, |
327ea85a | 87 | COL_OPT_FIELDS, |
624f2b47 | 88 | COL_PROPAGATION, |
426f0cea KZ |
89 | COL_FREQ, |
90 | COL_PASSNO, | |
04fd7a9f | 91 | |
af0dc7d3 | 92 | FINDMNT_NCOLUMNS |
04fd7a9f KZ |
93 | }; |
94 | ||
8c5eba3e KZ |
95 | enum { |
96 | TABTYPE_FSTAB = 1, | |
97 | TABTYPE_MTAB, | |
98 | TABTYPE_KERNEL | |
99 | }; | |
100 | ||
9d67679b KZ |
101 | /* column names */ |
102 | struct colinfo { | |
103 | const char *name; /* header */ | |
104 | double whint; /* width hint (N < 1 is in percent of termwidth) */ | |
32e5466a | 105 | int flags; /* tt flags */ |
b152e359 | 106 | const char *help; /* column description */ |
9d67679b | 107 | const char *match; /* pattern for match_func() */ |
cd492186 | 108 | void *match_data; /* match specific data */ |
04fd7a9f KZ |
109 | }; |
110 | ||
af0dc7d3 KZ |
111 | /* columns descriptions (don't use const, this is writable) */ |
112 | static struct colinfo infos[FINDMNT_NCOLUMNS] = { | |
7aa69cf2 KZ |
113 | [COL_SOURCE] = { "SOURCE", 0.25, TT_FL_NOEXTREMES, N_("source device") }, |
114 | [COL_TARGET] = { "TARGET", 0.30, TT_FL_TREE | TT_FL_NOEXTREMES, N_("mountpoint") }, | |
b152e359 KZ |
115 | [COL_FSTYPE] = { "FSTYPE", 0.10, TT_FL_TRUNC, N_("filesystem type") }, |
116 | [COL_OPTIONS] = { "OPTIONS", 0.10, TT_FL_TRUNC, N_("all mount options") }, | |
117 | [COL_VFS_OPTIONS] = { "VFS-OPTIONS", 0.20, TT_FL_TRUNC, N_("VFS specific mount options") }, | |
118 | [COL_FS_OPTIONS] = { "FS-OPTIONS", 0.10, TT_FL_TRUNC, N_("FS specific mount options") }, | |
119 | [COL_LABEL] = { "LABEL", 0.10, 0, N_("filesystem label") }, | |
120 | [COL_UUID] = { "UUID", 36, 0, N_("filesystem UUID") }, | |
090b5e84 KZ |
121 | [COL_PARTLABEL] = { "PARTLABEL", 0.10, 0, N_("partition label") }, |
122 | [COL_PARTUUID] = { "PARTUUID", 36, 0, N_("partition UUID") }, | |
b152e359 KZ |
123 | [COL_MAJMIN] = { "MAJ:MIN", 6, 0, N_("major:minor device number") }, |
124 | [COL_ACTION] = { "ACTION", 10, TT_FL_STRICTWIDTH, N_("action detected by --poll") }, | |
125 | [COL_OLD_OPTIONS] = { "OLD-OPTIONS", 0.10, TT_FL_TRUNC, N_("old mount options saved by --poll") }, | |
126 | [COL_OLD_TARGET] = { "OLD-TARGET", 0.30, 0, N_("old mountpoint saved by --poll") }, | |
451d7646 KZ |
127 | [COL_SIZE] = { "SIZE", 5, TT_FL_RIGHT, N_("filesystem size") }, |
128 | [COL_AVAIL] = { "AVAIL", 5, TT_FL_RIGHT, N_("filesystem size available") }, | |
129 | [COL_USED] = { "USED", 5, TT_FL_RIGHT, N_("filesystem size used") }, | |
130 | [COL_USEPERC] = { "USE%", 3, TT_FL_RIGHT, N_("filesystem use percentage") }, | |
8b8cd87b | 131 | [COL_FSROOT] = { "FSROOT", 0.25, TT_FL_NOEXTREMES, N_("filesystem root") }, |
7d8f4e0c | 132 | [COL_TID] = { "TID", 4, TT_FL_RIGHT, N_("task ID") }, |
9238e0fa | 133 | [COL_ID] = { "ID", 2, TT_FL_RIGHT, N_("mount ID") }, |
327ea85a | 134 | [COL_OPT_FIELDS] = { "OPT-FIELDS", 0.10, TT_FL_TRUNC, N_("optional mount fields") }, |
426f0cea | 135 | [COL_PROPAGATION] = { "PROPAGATION", 0.10, 0, N_("VFS propagation flags") }, |
97b820bf | 136 | [COL_FREQ] = { "FREQ", 1, TT_FL_RIGHT, N_("dump(8) period in days [fstab only]") }, |
426f0cea | 137 | [COL_PASSNO] = { "PASSNO", 1, TT_FL_RIGHT, N_("pass number on parallel fsck(8) [fstab only]") } |
04fd7a9f KZ |
138 | }; |
139 | ||
9d67679b | 140 | /* global flags */ |
af0dc7d3 KZ |
141 | static int flags; |
142 | static int tt_flags; | |
9d67679b | 143 | |
fdedb45e | 144 | /* array with IDs of enabled columns */ |
af0dc7d3 KZ |
145 | static int columns[FINDMNT_NCOLUMNS]; |
146 | static int ncolumns; | |
9d67679b | 147 | |
582a5006 | 148 | /* poll actions (parsed --poll=<list> */ |
af0dc7d3 KZ |
149 | #define FINDMNT_NACTIONS 4 /* mount, umount, move, remount */ |
150 | static int actions[FINDMNT_NACTIONS]; | |
151 | static int nactions; | |
582a5006 | 152 | |
9d67679b | 153 | /* libmount cache */ |
af0dc7d3 | 154 | static struct libmnt_cache *cache; |
04fd7a9f | 155 | |
e346233e KZ |
156 | #ifdef HAVE_LIBUDEV |
157 | struct udev *udev; | |
158 | #endif | |
159 | ||
31f67453 KZ |
160 | static int match_func(struct libmnt_fs *fs, void *data __attribute__ ((__unused__))); |
161 | ||
162 | ||
00b4bcdf | 163 | static int get_column_id(int num) |
9d67679b | 164 | { |
9d67679b | 165 | assert(num < ncolumns); |
af0dc7d3 | 166 | assert(columns[num] < FINDMNT_NCOLUMNS); |
fdedb45e | 167 | return columns[num]; |
9d67679b KZ |
168 | } |
169 | ||
00b4bcdf | 170 | static struct colinfo *get_column_info(int num) |
9d67679b KZ |
171 | { |
172 | return &infos[ get_column_id(num) ]; | |
173 | } | |
174 | ||
00b4bcdf | 175 | static const char *column_id_to_name(int id) |
9d67679b | 176 | { |
af0dc7d3 | 177 | assert(id < FINDMNT_NCOLUMNS); |
9d67679b KZ |
178 | return infos[id].name; |
179 | } | |
180 | ||
00b4bcdf | 181 | static const char *get_column_name(int num) |
9d67679b | 182 | { |
fdedb45e | 183 | return get_column_info(num)->name; |
9d67679b KZ |
184 | } |
185 | ||
00b4bcdf | 186 | static float get_column_whint(int num) |
9d67679b | 187 | { |
fdedb45e | 188 | return get_column_info(num)->whint; |
9d67679b KZ |
189 | } |
190 | ||
32e5466a | 191 | static int get_column_flags(int num) |
9d67679b | 192 | { |
32e5466a | 193 | return get_column_info(num)->flags; |
9d67679b KZ |
194 | } |
195 | ||
00b4bcdf | 196 | static const char *get_match(int id) |
9d67679b | 197 | { |
af0dc7d3 | 198 | assert(id < FINDMNT_NCOLUMNS); |
9d67679b KZ |
199 | return infos[id].match; |
200 | } | |
201 | ||
cd492186 KZ |
202 | static void *get_match_data(int id) |
203 | { | |
204 | assert(id < FINDMNT_NCOLUMNS); | |
205 | return infos[id].match_data; | |
206 | } | |
207 | ||
00b4bcdf | 208 | static void set_match(int id, const char *match) |
9d67679b | 209 | { |
af0dc7d3 | 210 | assert(id < FINDMNT_NCOLUMNS); |
9d67679b KZ |
211 | infos[id].match = match; |
212 | } | |
213 | ||
cd492186 KZ |
214 | static void set_match_data(int id, void *data) |
215 | { | |
216 | assert(id < FINDMNT_NCOLUMNS); | |
217 | infos[id].match_data = data; | |
218 | } | |
219 | ||
4bfd4bff KZ |
220 | /* |
221 | * source match means COL_SOURCE *or* COL_MAJMIN, depends on | |
222 | * data format. | |
223 | */ | |
cd492186 KZ |
224 | static void set_source_match(const char *data) |
225 | { | |
226 | int maj, min; | |
227 | ||
228 | if (sscanf(data, "%d:%d", &maj, &min) == 2) { | |
229 | dev_t *devno = xmalloc(sizeof(dev_t)); | |
230 | ||
231 | *devno = makedev(maj, min); | |
232 | set_match(COL_MAJMIN, data); | |
233 | set_match_data(COL_MAJMIN, (void *) devno); | |
4bfd4bff | 234 | flags |= FL_NOSWAPMATCH; |
cd492186 KZ |
235 | } else |
236 | set_match(COL_SOURCE, data); | |
237 | } | |
238 | ||
4bfd4bff KZ |
239 | static void enable_extra_target_match(void) |
240 | { | |
241 | char *cn = NULL, *mnt = NULL; | |
242 | ||
243 | /* | |
244 | * Check if match pattern is mountpoint, if not use the | |
245 | * real mountpoint. | |
246 | */ | |
247 | cn = mnt_resolve_path(get_match(COL_TARGET), cache); | |
248 | if (!cn) | |
249 | return; | |
250 | ||
251 | mnt = mnt_get_mountpoint(cn); | |
252 | if (!mnt || strcmp(mnt, cn) == 0) | |
253 | return; | |
254 | ||
255 | /* replace the current setting with the real mountpoint */ | |
256 | set_match(COL_TARGET, mnt); | |
257 | } | |
258 | ||
cd492186 | 259 | |
32e5466a KZ |
260 | static int is_tabdiff_column(int id) |
261 | { | |
af0dc7d3 | 262 | assert(id < FINDMNT_NCOLUMNS); |
32e5466a KZ |
263 | |
264 | switch(id) { | |
265 | case COL_ACTION: | |
266 | case COL_OLD_TARGET: | |
267 | case COL_OLD_OPTIONS: | |
268 | return 1; | |
269 | default: | |
270 | break; | |
271 | } | |
272 | return 0; | |
273 | } | |
274 | ||
9d67679b KZ |
275 | /* |
276 | * "findmnt" without any filter | |
277 | */ | |
00b4bcdf | 278 | static int is_listall_mode(void) |
9d67679b | 279 | { |
cd4ed46f KZ |
280 | if ((flags & FL_DF) && !(flags & FL_ALL)) |
281 | return 0; | |
282 | ||
9d67679b KZ |
283 | return (!get_match(COL_SOURCE) && |
284 | !get_match(COL_TARGET) && | |
285 | !get_match(COL_FSTYPE) && | |
cd492186 KZ |
286 | !get_match(COL_OPTIONS) && |
287 | !get_match(COL_MAJMIN)); | |
9d67679b KZ |
288 | } |
289 | ||
582a5006 KZ |
290 | /* |
291 | * Returns 1 if the @act is in the --poll=<list> | |
292 | */ | |
293 | static int has_poll_action(int act) | |
294 | { | |
295 | int i; | |
296 | ||
297 | if (!nactions) | |
298 | return 1; /* all actions enabled */ | |
299 | for (i = 0; i < nactions; i++) | |
300 | if (actions[i] == act) | |
301 | return 1; | |
302 | return 0; | |
303 | } | |
304 | ||
305 | static int poll_action_name_to_id(const char *name, size_t namesz) | |
306 | { | |
307 | int id = -1; | |
308 | ||
309 | if (strncasecmp(name, "move", namesz) == 0 && namesz == 4) | |
310 | id = MNT_TABDIFF_MOVE; | |
311 | else if (strncasecmp(name, "mount", namesz) == 0 && namesz == 5) | |
312 | id = MNT_TABDIFF_MOUNT; | |
313 | else if (strncasecmp(name, "umount", namesz) == 0 && namesz == 6) | |
314 | id = MNT_TABDIFF_UMOUNT; | |
315 | else if (strncasecmp(name, "remount", namesz) == 0 && namesz == 7) | |
316 | id = MNT_TABDIFF_REMOUNT; | |
317 | else | |
318 | warnx(_("unknown action: %s"), name); | |
319 | ||
320 | return id; | |
321 | } | |
322 | ||
9d67679b KZ |
323 | /* |
324 | * findmnt --first-only <devname|TAG=|mountpoint> | |
325 | * | |
326 | * ... it works like "mount <devname|TAG=|mountpoint>" | |
327 | */ | |
00b4bcdf | 328 | static int is_mount_compatible_mode(void) |
9d67679b KZ |
329 | { |
330 | if (!get_match(COL_SOURCE)) | |
331 | return 0; /* <devname|TAG=|mountpoint> is required */ | |
332 | if (get_match(COL_FSTYPE) || get_match(COL_OPTIONS)) | |
333 | return 0; /* cannot be restricted by -t or -O */ | |
334 | if (!(flags & FL_FIRSTONLY)) | |
335 | return 0; /* we have to return the first entry only */ | |
336 | ||
337 | return 1; /* ok */ | |
338 | } | |
339 | ||
32e5466a | 340 | static void disable_columns_truncate(void) |
9d67679b KZ |
341 | { |
342 | int i; | |
343 | ||
af0dc7d3 | 344 | for (i = 0; i < FINDMNT_NCOLUMNS; i++) |
32e5466a | 345 | infos[i].flags &= ~TT_FL_TRUNC; |
9d67679b KZ |
346 | } |
347 | ||
04fd7a9f KZ |
348 | /* |
349 | * converts @name to column ID | |
350 | */ | |
9d67679b | 351 | static int column_name_to_id(const char *name, size_t namesz) |
04fd7a9f KZ |
352 | { |
353 | int i; | |
354 | ||
af0dc7d3 | 355 | for (i = 0; i < FINDMNT_NCOLUMNS; i++) { |
9d67679b | 356 | const char *cn = column_id_to_name(i); |
04fd7a9f | 357 | |
9d67679b | 358 | if (!strncasecmp(name, cn, namesz) && !*(cn + namesz)) |
04fd7a9f KZ |
359 | return i; |
360 | } | |
fdedb45e | 361 | warnx(_("unknown column: %s"), name); |
04fd7a9f KZ |
362 | return -1; |
363 | } | |
364 | ||
e346233e KZ |
365 | |
366 | #ifdef HAVE_LIBUDEV | |
367 | static char *get_tag_from_udev(const char *devname, int col) | |
368 | { | |
369 | struct udev_device *dev; | |
370 | const char *data = NULL; | |
4ba4893b | 371 | char *res = NULL, *path; |
e346233e KZ |
372 | |
373 | if (!udev) | |
374 | udev = udev_new(); | |
375 | if (!udev) | |
376 | return NULL; | |
377 | ||
4ba4893b KZ |
378 | /* libudev don't like /dev/mapper/ symlinks */ |
379 | path = realpath(devname, NULL); | |
380 | if (path) | |
381 | devname = path; | |
382 | ||
e346233e KZ |
383 | if (strncmp(devname, "/dev/", 5) == 0) |
384 | devname += 5; | |
385 | ||
386 | dev = udev_device_new_from_subsystem_sysname(udev, "block", devname); | |
4ba4893b KZ |
387 | free(path); |
388 | ||
e346233e KZ |
389 | if (!dev) |
390 | return NULL; | |
391 | ||
392 | switch(col) { | |
393 | case COL_LABEL: | |
394 | data = udev_device_get_property_value(dev, "ID_FS_LABEL_ENC"); | |
395 | break; | |
396 | case COL_UUID: | |
397 | data = udev_device_get_property_value(dev, "ID_FS_UUID_ENC"); | |
398 | break; | |
399 | case COL_PARTUUID: | |
400 | data = udev_device_get_property_value(dev, "ID_PART_ENTRY_UUID"); | |
401 | break; | |
402 | case COL_PARTLABEL: | |
403 | data = udev_device_get_property_value(dev, "ID_PART_ENTRY_NAME"); | |
404 | break; | |
405 | default: | |
406 | break; | |
407 | } | |
408 | ||
409 | if (data) { | |
410 | res = xstrdup(data); | |
411 | unhexmangle_string(res); | |
412 | } | |
413 | ||
414 | udev_device_unref(dev); | |
415 | return res; | |
416 | } | |
417 | #endif /* HAVE_LIBUDEV */ | |
418 | ||
fdedb45e | 419 | /* Returns LABEL or UUID */ |
81499ab2 KZ |
420 | static const char *get_tag(struct libmnt_fs *fs, const char *tagname, int col |
421 | #ifndef HAVE_LIBUDEV | |
422 | __attribute__((__unused__)) | |
423 | #endif | |
424 | ) | |
9d67679b | 425 | { |
e346233e | 426 | const char *t, *v, *res = NULL; |
9d67679b KZ |
427 | |
428 | if (!mnt_fs_get_tag(fs, &t, &v) && !strcmp(t, tagname)) | |
429 | res = v; | |
430 | else { | |
e346233e KZ |
431 | const char *dev = mnt_fs_get_source(fs); |
432 | ||
433 | if (dev) | |
434 | dev = mnt_resolve_spec(dev, cache); | |
435 | #ifdef HAVE_LIBUDEV | |
436 | if (dev) | |
437 | res = get_tag_from_udev(dev, col); | |
438 | #endif | |
439 | if (!res) | |
440 | res = mnt_cache_find_tag_value(cache, dev, tagname); | |
9d67679b KZ |
441 | } |
442 | ||
443 | return res; | |
444 | } | |
445 | ||
a85ef33b DR |
446 | static const char *get_vfs_attr(struct libmnt_fs *fs, int sizetype) |
447 | { | |
448 | struct statvfs buf; | |
6901f22c | 449 | uint64_t vfs_attr = 0; |
a85ef33b DR |
450 | char *sizestr; |
451 | ||
452 | if (statvfs(mnt_fs_get_target(fs), &buf) != 0) | |
453 | return NULL; | |
454 | ||
455 | switch(sizetype) { | |
456 | case COL_SIZE: | |
457 | vfs_attr = buf.f_frsize * buf.f_blocks; | |
458 | break; | |
459 | case COL_AVAIL: | |
ed6c8c1b | 460 | vfs_attr = buf.f_frsize * buf.f_bavail; |
a85ef33b DR |
461 | break; |
462 | case COL_USED: | |
463 | vfs_attr = buf.f_frsize * (buf.f_blocks - buf.f_bfree); | |
464 | break; | |
465 | case COL_USEPERC: | |
466 | if (buf.f_blocks == 0) | |
467 | return "-"; | |
468 | ||
d7bcb205 KZ |
469 | xasprintf(&sizestr, "%.0f%%", |
470 | (double)(buf.f_blocks - buf.f_bfree) / | |
471 | buf.f_blocks * 100); | |
a85ef33b DR |
472 | return sizestr; |
473 | } | |
474 | ||
475 | return vfs_attr == 0 ? "0" : | |
476 | size_to_human_string(SIZE_SUFFIX_1LETTER, vfs_attr); | |
477 | } | |
478 | ||
46236388 | 479 | /* reads FS data from libmount |
46236388 | 480 | */ |
68164f6c | 481 | static const char *get_data(struct libmnt_fs *fs, int num) |
9d67679b KZ |
482 | { |
483 | const char *str = NULL; | |
60b17c6f | 484 | char *tmp; |
a85ef33b | 485 | int col_id = get_column_id(num); |
9d67679b | 486 | |
a85ef33b | 487 | switch (col_id) { |
04fd7a9f | 488 | case COL_SOURCE: |
b2214e1f KZ |
489 | { |
490 | const char *root = mnt_fs_get_root(fs); | |
491 | ||
9d67679b | 492 | str = mnt_fs_get_srcpath(fs); |
04fd7a9f | 493 | |
9d67679b KZ |
494 | if (str && (flags & FL_CANONICALIZE)) |
495 | str = mnt_resolve_path(str, cache); | |
496 | if (!str) { | |
497 | str = mnt_fs_get_source(fs); | |
498 | ||
499 | if (str && (flags & FL_EVALUATE)) | |
500 | str = mnt_resolve_spec(str, cache); | |
501 | } | |
b2214e1f | 502 | if (root && str && !(flags & FL_NOFSROOT) && strcmp(root, "/")) { |
60b17c6f KZ |
503 | xasprintf(&tmp, "%s[%s]", str, root); |
504 | str = tmp; | |
b2214e1f | 505 | } |
04fd7a9f | 506 | break; |
b2214e1f | 507 | } |
04fd7a9f | 508 | case COL_TARGET: |
fdedb45e | 509 | str = mnt_fs_get_target(fs); |
04fd7a9f KZ |
510 | break; |
511 | case COL_FSTYPE: | |
512 | str = mnt_fs_get_fstype(fs); | |
513 | break; | |
514 | case COL_OPTIONS: | |
d783ee0b | 515 | str = mnt_fs_get_options(fs); |
04fd7a9f | 516 | break; |
631280e0 | 517 | case COL_VFS_OPTIONS: |
411fe06e | 518 | str = mnt_fs_get_vfs_options(fs); |
631280e0 KZ |
519 | break; |
520 | case COL_FS_OPTIONS: | |
411fe06e | 521 | str = mnt_fs_get_fs_options(fs); |
631280e0 | 522 | break; |
327ea85a KZ |
523 | case COL_OPT_FIELDS: |
524 | str = mnt_fs_get_optional_fields(fs); | |
525 | break; | |
9d67679b | 526 | case COL_UUID: |
e346233e | 527 | str = get_tag(fs, "UUID", col_id); |
9d67679b | 528 | break; |
090b5e84 | 529 | case COL_PARTUUID: |
e346233e | 530 | str = get_tag(fs, "PARTUUID", col_id); |
090b5e84 | 531 | break; |
9d67679b | 532 | case COL_LABEL: |
e346233e | 533 | str = get_tag(fs, "LABEL", col_id); |
9d67679b | 534 | break; |
090b5e84 | 535 | case COL_PARTLABEL: |
e346233e | 536 | str = get_tag(fs, "PARTLABEL", col_id); |
090b5e84 KZ |
537 | break; |
538 | ||
46236388 KZ |
539 | case COL_MAJMIN: |
540 | { | |
541 | dev_t devno = mnt_fs_get_devno(fs); | |
60b17c6f KZ |
542 | if (!devno) |
543 | break; | |
544 | ||
545 | if ((tt_flags & TT_FL_RAW) || (tt_flags & TT_FL_EXPORT)) | |
546 | xasprintf(&tmp, "%u:%u", major(devno), minor(devno)); | |
547 | else | |
548 | xasprintf(&tmp, "%3u:%-3u", major(devno), minor(devno)); | |
549 | str = tmp; | |
a85ef33b | 550 | break; |
46236388 | 551 | } |
a85ef33b DR |
552 | case COL_SIZE: |
553 | case COL_AVAIL: | |
554 | case COL_USED: | |
555 | case COL_USEPERC: | |
556 | str = get_vfs_attr(fs, col_id); | |
557 | break; | |
8b8cd87b DR |
558 | case COL_FSROOT: |
559 | str = mnt_fs_get_root(fs); | |
560 | break; | |
7d8f4e0c KZ |
561 | case COL_TID: |
562 | if (mnt_fs_get_tid(fs)) { | |
60b17c6f KZ |
563 | xasprintf(&tmp, "%d", mnt_fs_get_tid(fs)); |
564 | str = tmp; | |
7d8f4e0c KZ |
565 | } |
566 | break; | |
9238e0fa KZ |
567 | case COL_ID: |
568 | if (mnt_fs_get_id(fs)) { | |
569 | xasprintf(&tmp, "%d", mnt_fs_get_id(fs)); | |
570 | str = tmp; | |
571 | } | |
572 | break; | |
624f2b47 KZ |
573 | case COL_PROPAGATION: |
574 | if (mnt_fs_is_kernel(fs)) { | |
575 | unsigned long fl = 0; | |
576 | char *n = NULL; | |
577 | ||
578 | if (mnt_fs_get_propagation(fs, &fl) != 0) | |
579 | break; | |
580 | ||
581 | n = xstrdup((fl & MS_SHARED) ? "shared" : "private"); | |
582 | ||
583 | if (fl & MS_SLAVE) { | |
584 | xasprintf(&tmp, "%s,slave", n); | |
585 | free(n); | |
586 | n = tmp; | |
587 | } | |
588 | if (fl & MS_UNBINDABLE) { | |
589 | xasprintf(&tmp, "%s,unbindable", n); | |
590 | free(n); | |
591 | n = tmp; | |
592 | } | |
593 | str = n; | |
594 | } | |
595 | break; | |
426f0cea KZ |
596 | case COL_FREQ: |
597 | if (!mnt_fs_is_kernel(fs)) { | |
598 | xasprintf(&tmp, "%d", mnt_fs_get_freq(fs)); | |
599 | str = tmp; | |
600 | } | |
601 | break; | |
602 | case COL_PASSNO: | |
603 | if (!mnt_fs_is_kernel(fs)) { | |
604 | xasprintf(&tmp, "%d", mnt_fs_get_passno(fs)); | |
605 | str = tmp; | |
606 | } | |
607 | break; | |
04fd7a9f | 608 | default: |
9d67679b | 609 | break; |
04fd7a9f | 610 | } |
fdedb45e | 611 | return str; |
9d67679b KZ |
612 | } |
613 | ||
32e5466a KZ |
614 | static const char *get_tabdiff_data(struct libmnt_fs *old_fs, |
615 | struct libmnt_fs *new_fs, | |
616 | int change, | |
617 | int num) | |
618 | { | |
619 | const char *str = NULL; | |
620 | ||
621 | switch (get_column_id(num)) { | |
622 | case COL_ACTION: | |
623 | switch (change) { | |
624 | case MNT_TABDIFF_MOUNT: | |
625 | str = _("mount"); | |
626 | break; | |
627 | case MNT_TABDIFF_UMOUNT: | |
628 | str = _("umount"); | |
629 | break; | |
630 | case MNT_TABDIFF_REMOUNT: | |
631 | str = _("remount"); | |
632 | break; | |
633 | case MNT_TABDIFF_MOVE: | |
634 | str = _("move"); | |
635 | break; | |
636 | default: | |
637 | str = _("unknown"); | |
638 | break; | |
639 | } | |
640 | break; | |
641 | case COL_OLD_OPTIONS: | |
77a1c5f7 KZ |
642 | if (old_fs && (change == MNT_TABDIFF_REMOUNT || |
643 | change == MNT_TABDIFF_UMOUNT)) | |
32e5466a KZ |
644 | str = mnt_fs_get_options(old_fs); |
645 | break; | |
646 | case COL_OLD_TARGET: | |
77a1c5f7 KZ |
647 | if (old_fs && (change == MNT_TABDIFF_MOVE || |
648 | change == MNT_TABDIFF_UMOUNT)) | |
32e5466a KZ |
649 | str = mnt_fs_get_target(old_fs); |
650 | break; | |
651 | default: | |
652 | if (new_fs) | |
653 | str = get_data(new_fs, num); | |
654 | else | |
655 | str = get_data(old_fs, num); | |
656 | break; | |
657 | } | |
658 | return str; | |
659 | } | |
660 | ||
fdedb45e | 661 | /* adds one line to the output @tab */ |
68164f6c | 662 | static struct tt_line *add_line(struct tt *tt, struct libmnt_fs *fs, |
fdedb45e | 663 | struct tt_line *parent) |
9d67679b | 664 | { |
fdedb45e KZ |
665 | int i; |
666 | struct tt_line *line = tt_add_line(tt, parent); | |
9d67679b | 667 | |
fdedb45e KZ |
668 | if (!line) { |
669 | warn(_("failed to add line to output")); | |
670 | return NULL; | |
9d67679b | 671 | } |
fdedb45e KZ |
672 | for (i = 0; i < ncolumns; i++) |
673 | tt_line_set_data(line, i, get_data(fs, i)); | |
9d67679b | 674 | |
049caefd | 675 | tt_line_set_userdata(line, fs); |
fdedb45e | 676 | return line; |
9d67679b KZ |
677 | } |
678 | ||
32e5466a KZ |
679 | static struct tt_line *add_tabdiff_line(struct tt *tt, struct libmnt_fs *new_fs, |
680 | struct libmnt_fs *old_fs, int change) | |
681 | { | |
682 | int i; | |
683 | struct tt_line *line = tt_add_line(tt, NULL); | |
684 | ||
685 | if (!line) { | |
686 | warn(_("failed to add line to output")); | |
687 | return NULL; | |
688 | } | |
689 | for (i = 0; i < ncolumns; i++) | |
690 | tt_line_set_data(line, i, | |
691 | get_tabdiff_data(old_fs, new_fs, change, i)); | |
692 | ||
693 | return line; | |
694 | } | |
695 | ||
68164f6c | 696 | static int has_line(struct tt *tt, struct libmnt_fs *fs) |
049caefd KZ |
697 | { |
698 | struct list_head *p; | |
699 | ||
700 | list_for_each(p, &tt->tb_lines) { | |
701 | struct tt_line *ln = list_entry(p, struct tt_line, ln_lines); | |
68164f6c | 702 | if ((struct libmnt_fs *) ln->userdata == fs) |
049caefd KZ |
703 | return 1; |
704 | } | |
705 | return 0; | |
706 | } | |
707 | ||
708 | /* reads filesystems from @tb (libmount) and fillin @tt (output table) */ | |
68164f6c KZ |
709 | static int create_treenode(struct tt *tt, struct libmnt_table *tb, |
710 | struct libmnt_fs *fs, struct tt_line *parent_line) | |
04fd7a9f | 711 | { |
68164f6c KZ |
712 | struct libmnt_fs *chld = NULL; |
713 | struct libmnt_iter *itr = NULL; | |
fdedb45e KZ |
714 | struct tt_line *line; |
715 | int rc = -1; | |
9d67679b | 716 | |
fdedb45e KZ |
717 | if (!fs) { |
718 | /* first call, get root FS */ | |
68164f6c | 719 | if (mnt_table_get_root_fs(tb, &fs)) |
fdedb45e KZ |
720 | goto leave; |
721 | parent_line = NULL; | |
049caefd KZ |
722 | |
723 | } else if ((flags & FL_SUBMOUNTS) && has_line(tt, fs)) | |
724 | return 0; | |
9d67679b | 725 | |
fdedb45e KZ |
726 | itr = mnt_new_iter(MNT_ITER_FORWARD); |
727 | if (!itr) | |
728 | goto leave; | |
9d67679b | 729 | |
31f67453 KZ |
730 | if ((flags & FL_SUBMOUNTS) || match_func(fs, NULL)) { |
731 | line = add_line(tt, fs, parent_line); | |
732 | if (!line) | |
733 | goto leave; | |
734 | } else | |
735 | line = parent_line; | |
9d67679b | 736 | |
fdedb45e KZ |
737 | /* |
738 | * add all children to the output table | |
739 | */ | |
68164f6c | 740 | while(mnt_table_next_child_fs(tb, itr, fs, &chld) == 0) { |
fdedb45e KZ |
741 | if (create_treenode(tt, tb, chld, line)) |
742 | goto leave; | |
9d67679b | 743 | } |
fdedb45e KZ |
744 | rc = 0; |
745 | leave: | |
746 | mnt_free_iter(itr); | |
747 | return rc; | |
04fd7a9f KZ |
748 | } |
749 | ||
20055151 | 750 | /* error callback */ |
6e5c0fc2 KZ |
751 | static int parser_errcb(struct libmnt_table *tb __attribute__ ((__unused__)), |
752 | const char *filename, int line) | |
20055151 | 753 | { |
b1b9f1c1 | 754 | warnx(_("%s: parse error at line %d"), filename, line); |
20055151 KZ |
755 | return 0; |
756 | } | |
757 | ||
8c5eba3e KZ |
758 | static char **append_tabfile(char **files, int *nfiles, char *filename) |
759 | { | |
99d618c0 | 760 | files = xrealloc(files, sizeof(char *) * (*nfiles + 1)); |
8c5eba3e KZ |
761 | files[(*nfiles)++] = filename; |
762 | return files; | |
763 | } | |
764 | ||
c7fcc830 KZ |
765 | static char **append_pid_tabfile(char **files, int *nfiles, pid_t pid) |
766 | { | |
767 | char *path = NULL; | |
768 | ||
769 | xasprintf(&path, "/proc/%d/mountinfo", (int) pid); | |
770 | return append_tabfile(files, nfiles, path); | |
771 | } | |
772 | ||
fdedb45e | 773 | /* calls libmount fstab/mtab/mountinfo parser */ |
8c5eba3e KZ |
774 | static struct libmnt_table *parse_tabfiles(char **files, |
775 | int nfiles, | |
776 | int tabtype) | |
04fd7a9f | 777 | { |
8c5eba3e | 778 | struct libmnt_table *tb; |
762ef0e1 | 779 | int rc = 0; |
20055151 | 780 | |
8c5eba3e | 781 | tb = mnt_new_table(); |
fdedb45e | 782 | if (!tb) { |
32e5466a | 783 | warn(_("failed to initialize libmount table")); |
04fd7a9f | 784 | return NULL; |
fdedb45e | 785 | } |
68164f6c | 786 | mnt_table_set_parser_errcb(tb, parser_errcb); |
20055151 | 787 | |
8c5eba3e KZ |
788 | do { |
789 | /* NULL means that libmount will use default paths */ | |
790 | const char *path = nfiles ? *files++ : NULL; | |
791 | ||
792 | switch (tabtype) { | |
793 | case TABTYPE_FSTAB: | |
794 | rc = mnt_table_parse_fstab(tb, path); | |
795 | break; | |
796 | case TABTYPE_MTAB: | |
797 | rc = mnt_table_parse_mtab(tb, path); | |
798 | break; | |
799 | case TABTYPE_KERNEL: | |
800 | if (!path) | |
801 | path = access(_PATH_PROC_MOUNTINFO, R_OK) == 0 ? | |
802 | _PATH_PROC_MOUNTINFO : | |
803 | _PATH_PROC_MOUNTS; | |
804 | ||
805 | rc = mnt_table_parse_file(tb, path); | |
806 | break; | |
807 | } | |
808 | if (rc) { | |
809 | mnt_free_table(tb); | |
810 | warn(_("can't read %s"), path); | |
811 | return NULL; | |
812 | } | |
813 | } while (--nfiles > 0); | |
20055151 | 814 | |
04fd7a9f | 815 | return tb; |
04fd7a9f KZ |
816 | } |
817 | ||
2f1ac44b KZ |
818 | /* checks if @tb contains parent->child relations */ |
819 | static int tab_is_tree(struct libmnt_table *tb) | |
820 | { | |
821 | struct libmnt_fs *fs = NULL; | |
822 | struct libmnt_iter *itr = NULL; | |
823 | int rc = 0; | |
824 | ||
825 | itr = mnt_new_iter(MNT_ITER_BACKWARD); | |
826 | if (!itr) | |
827 | return 0; | |
828 | ||
829 | if (mnt_table_next_fs(tb, itr, &fs) == 0) | |
830 | rc = mnt_fs_get_id(fs) > 0 && mnt_fs_get_parent_id(fs) > 0; | |
831 | ||
832 | mnt_free_iter(itr); | |
833 | return rc; | |
834 | } | |
835 | ||
b215d8e9 | 836 | |
68164f6c | 837 | /* filter function for libmount (mnt_table_find_next_fs()) */ |
6e5c0fc2 KZ |
838 | static int match_func(struct libmnt_fs *fs, |
839 | void *data __attribute__ ((__unused__))) | |
04fd7a9f | 840 | { |
04fd7a9f | 841 | int rc = flags & FL_INVERT ? 1 : 0; |
9d67679b | 842 | const char *m; |
cd492186 | 843 | void *md; |
04fd7a9f | 844 | |
9d67679b KZ |
845 | m = get_match(COL_FSTYPE); |
846 | if (m && !mnt_fs_match_fstype(fs, m)) | |
04fd7a9f | 847 | return rc; |
9d67679b KZ |
848 | |
849 | m = get_match(COL_OPTIONS); | |
850 | if (m && !mnt_fs_match_options(fs, m)) | |
04fd7a9f KZ |
851 | return rc; |
852 | ||
cd492186 KZ |
853 | md = get_match_data(COL_MAJMIN); |
854 | if (md && mnt_fs_get_devno(fs) != *((dev_t *) md)) | |
855 | return rc; | |
856 | ||
4096946f KZ |
857 | m = get_match(COL_TARGET); |
858 | if (m && !mnt_fs_match_target(fs, m, cache)) | |
859 | return rc; | |
860 | ||
861 | m = get_match(COL_SOURCE); | |
862 | if (m && !mnt_fs_match_source(fs, m, cache)) | |
863 | return rc; | |
864 | ||
cd4ed46f KZ |
865 | if ((flags & FL_DF) && !(flags & FL_ALL)) { |
866 | const char *type = mnt_fs_get_fstype(fs); | |
867 | ||
868 | if (type && strstr(type, "tmpfs")) /* tmpfs is wanted */ | |
869 | return !rc; | |
870 | ||
871 | if (mnt_fs_is_pseudofs(fs)) | |
872 | return rc; | |
873 | } | |
874 | ||
04fd7a9f KZ |
875 | return !rc; |
876 | } | |
877 | ||
fdedb45e | 878 | /* iterate over filesystems in @tb */ |
68164f6c KZ |
879 | static struct libmnt_fs *get_next_fs(struct libmnt_table *tb, |
880 | struct libmnt_iter *itr) | |
04fd7a9f | 881 | { |
68164f6c | 882 | struct libmnt_fs *fs = NULL; |
9d67679b KZ |
883 | |
884 | if (is_listall_mode()) { | |
885 | /* | |
886 | * Print whole file | |
887 | */ | |
e3963f60 KZ |
888 | if (mnt_table_next_fs(tb, itr, &fs) != 0) |
889 | return NULL; | |
9d67679b KZ |
890 | |
891 | } else if (is_mount_compatible_mode()) { | |
892 | /* | |
893 | * Look up for FS in the same way how mount(8) searchs in fstab | |
894 | * | |
895 | * findmnt -f <spec> | |
896 | */ | |
68164f6c | 897 | fs = mnt_table_find_source(tb, get_match(COL_SOURCE), |
9d67679b | 898 | mnt_iter_get_direction(itr)); |
9a30c6ef KZ |
899 | |
900 | if (!fs && !(flags & FL_NOSWAPMATCH)) | |
68164f6c | 901 | fs = mnt_table_find_target(tb, get_match(COL_SOURCE), |
9d67679b KZ |
902 | mnt_iter_get_direction(itr)); |
903 | } else { | |
904 | /* | |
905 | * Look up for all matching entries | |
906 | * | |
907 | * findmnt [-l] <source> <target> [-O <options>] [-t <types>] | |
908 | * findmnt [-l] <spec> [-O <options>] [-t <types>] | |
909 | */ | |
910 | again: | |
68164f6c | 911 | mnt_table_find_next_fs(tb, itr, match_func, NULL, &fs); |
9d67679b KZ |
912 | |
913 | if (!fs && | |
914 | !(flags & FL_NOSWAPMATCH) && | |
915 | !get_match(COL_TARGET) && get_match(COL_SOURCE)) { | |
916 | ||
917 | /* swap 'spec' and target. */ | |
918 | set_match(COL_TARGET, get_match(COL_SOURCE)); | |
919 | set_match(COL_SOURCE, NULL); | |
920 | mnt_reset_iter(itr, -1); | |
921 | ||
922 | goto again; | |
923 | } | |
924 | } | |
9d67679b | 925 | |
fdedb45e | 926 | return fs; |
04fd7a9f KZ |
927 | } |
928 | ||
31f67453 KZ |
929 | /* |
930 | * Filter out unwanted lines for --list output or top level lines for | |
931 | * --submounts tree output. | |
932 | */ | |
68164f6c KZ |
933 | static int add_matching_lines(struct libmnt_table *tb, |
934 | struct tt *tt, int direction) | |
049caefd | 935 | { |
68164f6c KZ |
936 | struct libmnt_iter *itr = NULL; |
937 | struct libmnt_fs *fs; | |
049caefd KZ |
938 | int nlines = 0, rc = -1; |
939 | ||
940 | itr = mnt_new_iter(direction); | |
941 | if (!itr) { | |
942 | warn(_("failed to initialize libmount iterator")); | |
943 | goto done; | |
944 | } | |
945 | ||
946 | while((fs = get_next_fs(tb, itr))) { | |
947 | if ((tt_flags & TT_FL_TREE) || (flags & FL_SUBMOUNTS)) | |
948 | rc = create_treenode(tt, tb, fs, NULL); | |
949 | else | |
950 | rc = !add_line(tt, fs, NULL); | |
951 | if (rc) | |
952 | goto done; | |
953 | nlines++; | |
954 | if (flags & FL_FIRSTONLY) | |
955 | break; | |
956 | flags |= FL_NOSWAPMATCH; | |
957 | } | |
958 | ||
959 | if (nlines) | |
960 | rc = 0; | |
961 | done: | |
962 | mnt_free_iter(itr); | |
963 | return rc; | |
964 | } | |
965 | ||
582a5006 KZ |
966 | static int poll_match(struct libmnt_fs *fs) |
967 | { | |
968 | int rc = match_func(fs, NULL); | |
969 | ||
970 | if (rc == 0 && !(flags & FL_NOSWAPMATCH) && | |
971 | get_match(COL_SOURCE) && !get_match(COL_TARGET)) { | |
972 | /* | |
973 | * findmnt --poll /foo | |
cd492186 | 974 | * The '/foo' maybe source as well as target. |
582a5006 KZ |
975 | */ |
976 | const char *str = get_match(COL_SOURCE); | |
977 | ||
978 | set_match(COL_TARGET, str); /* swap */ | |
979 | set_match(COL_SOURCE, NULL); | |
980 | ||
981 | rc = match_func(fs, NULL); | |
982 | ||
983 | set_match(COL_TARGET, NULL); /* restore */ | |
984 | set_match(COL_SOURCE, str); | |
985 | ||
986 | } | |
987 | return rc; | |
988 | } | |
989 | ||
32e5466a KZ |
990 | static int poll_table(struct libmnt_table *tb, const char *tabfile, |
991 | int timeout, struct tt *tt, int direction) | |
992 | { | |
d1cabd5c | 993 | FILE *f = NULL; |
32e5466a KZ |
994 | int rc = -1; |
995 | struct libmnt_iter *itr = NULL; | |
996 | struct libmnt_table *tb_new = NULL; | |
997 | struct libmnt_tabdiff *diff = NULL; | |
998 | struct pollfd fds[1]; | |
999 | ||
1000 | tb_new = mnt_new_table(); | |
1001 | if (!tb_new) { | |
1002 | warn(_("failed to initialize libmount table")); | |
1003 | goto done; | |
1004 | } | |
1005 | ||
1006 | itr = mnt_new_iter(direction); | |
1007 | if (!itr) { | |
1008 | warn(_("failed to initialize libmount iterator")); | |
1009 | goto done; | |
1010 | } | |
1011 | ||
1012 | diff = mnt_new_tabdiff(); | |
1013 | if (!diff) { | |
1014 | warn(_("failed to initialize libmount tabdiff")); | |
1015 | goto done; | |
1016 | } | |
1017 | ||
1018 | /* cache is unnecessary to detect changes */ | |
1019 | mnt_table_set_cache(tb, NULL); | |
1020 | mnt_table_set_cache(tb_new, NULL); | |
1021 | ||
1022 | f = fopen(tabfile, "r"); | |
1023 | if (!f) { | |
289dcc90 | 1024 | warn(_("cannot open %s"), tabfile); |
32e5466a KZ |
1025 | goto done; |
1026 | } | |
1027 | ||
1028 | mnt_table_set_parser_errcb(tb_new, parser_errcb); | |
1029 | ||
1030 | fds[0].fd = fileno(f); | |
1031 | fds[0].events = POLLPRI; | |
1032 | ||
1033 | while (1) { | |
1034 | struct libmnt_table *tmp; | |
1035 | struct libmnt_fs *old, *new; | |
582a5006 | 1036 | int change, count; |
32e5466a | 1037 | |
582a5006 KZ |
1038 | count = poll(fds, 1, timeout); |
1039 | if (count == 0) | |
ad38fb9f | 1040 | break; /* timeout */ |
582a5006 | 1041 | if (count < 0) { |
32e5466a KZ |
1042 | warn(_("poll() failed")); |
1043 | goto done; | |
1044 | } | |
1045 | ||
1046 | rewind(f); | |
1047 | rc = mnt_table_parse_stream(tb_new, f, tabfile); | |
1048 | if (!rc) | |
1049 | rc = mnt_diff_tables(diff, tb, tb_new); | |
1050 | if (rc < 0) | |
1051 | goto done; | |
1052 | ||
582a5006 | 1053 | count = 0; |
32e5466a KZ |
1054 | mnt_reset_iter(itr, direction); |
1055 | while(mnt_tabdiff_next_change( | |
1056 | diff, itr, &old, &new, &change) == 0) { | |
1057 | ||
582a5006 KZ |
1058 | if (!has_poll_action(change)) |
1059 | continue; | |
1060 | if (!poll_match(new ? new : old)) | |
1061 | continue; | |
1062 | count++; | |
32e5466a KZ |
1063 | rc = !add_tabdiff_line(tt, new, old, change); |
1064 | if (rc) | |
1065 | goto done; | |
1066 | if (flags & FL_FIRSTONLY) | |
1067 | break; | |
1068 | } | |
1069 | ||
582a5006 KZ |
1070 | if (count) { |
1071 | rc = tt_print_table(tt); | |
1072 | if (rc) | |
1073 | goto done; | |
1074 | } | |
32e5466a KZ |
1075 | |
1076 | /* swap tables */ | |
1077 | tmp = tb; | |
1078 | tb = tb_new; | |
1079 | tb_new = tmp; | |
1080 | ||
1081 | tt_remove_lines(tt); | |
1082 | mnt_reset_table(tb_new); | |
582a5006 KZ |
1083 | |
1084 | if (count && (flags & FL_FIRSTONLY)) | |
1085 | break; | |
32e5466a KZ |
1086 | } |
1087 | ||
1088 | rc = 0; | |
1089 | done: | |
1090 | mnt_free_table(tb_new); | |
1091 | mnt_free_tabdiff(diff); | |
1092 | mnt_free_iter(itr); | |
d1cabd5c KZ |
1093 | if (f) |
1094 | fclose(f); | |
32e5466a KZ |
1095 | return rc; |
1096 | } | |
1097 | ||
abafd686 | 1098 | static void __attribute__((__noreturn__)) usage(FILE *out) |
04fd7a9f | 1099 | { |
9ead0006 KZ |
1100 | int i; |
1101 | ||
5cc12c40 | 1102 | fputs(USAGE_HEADER, out); |
9d67679b | 1103 | fprintf(out, _( |
9d67679b KZ |
1104 | " %1$s [options]\n" |
1105 | " %1$s [options] <device> | <mountpoint>\n" | |
1106 | " %1$s [options] <device> <mountpoint>\n" | |
1107 | " %1$s [options] [--source <device>] [--target <mountpoint>]\n"), | |
1108 | program_invocation_short_name); | |
04fd7a9f KZ |
1109 | |
1110 | fprintf(out, _( | |
9d67679b | 1111 | "\nOptions:\n" |
2f1ac44b | 1112 | " -s, --fstab search in static table of filesystems\n" |
cbec3cbf | 1113 | " -m, --mtab search in table of mounted filesystems\n" |
c3214059 | 1114 | " -k, --kernel search in kernel table of mounted\n" |
415b61fc | 1115 | " filesystems (default)\n\n")); |
04fd7a9f | 1116 | |
415b61fc | 1117 | fprintf(out, _( |
582a5006 | 1118 | " -p, --poll[=<list>] monitor changes in table of mounted filesystems\n" |
415b61fc | 1119 | " -w, --timeout <num> upper limit in milliseconds that --poll will block\n\n")); |
32e5466a | 1120 | |
415b61fc | 1121 | fprintf(out, _( |
cd4ed46f | 1122 | " -A, --all disable all built-in filters, print all filesystems\n" |
c3214059 | 1123 | " -a, --ascii use ASCII chars for tree formatting\n" |
04fd7a9f | 1124 | " -c, --canonicalize canonicalize printed paths\n" |
eda399b9 | 1125 | " -D, --df imitate the output of df(1)\n" |
c3214059 | 1126 | " -d, --direction <word> direction of search, 'forward' or 'backward'\n" |
cd492186 KZ |
1127 | " -e, --evaluate convert tags (LABEL,UUID,PARTUUID,PARTLABEL) \n" |
1128 | " to device names\n" | |
2f1ac44b | 1129 | " -F, --tab-file <path> alternative file for --fstab, --mtab or --kernel options\n" |
415b61fc BS |
1130 | " -f, --first-only print the first found filesystem only\n")); |
1131 | ||
1132 | fprintf(out, _( | |
c3214059 | 1133 | " -i, --invert invert the sense of matching\n" |
46236388 | 1134 | " -l, --list use list format output\n" |
c7fcc830 | 1135 | " -N, --task <tid> use alternative namespace (/proc/<tid>/mountinfo file)\n" |
c3214059 | 1136 | " -n, --noheadings don't print column headings\n" |
415b61fc | 1137 | " -u, --notruncate don't truncate text in columns\n")); |
ff2bd506 SK |
1138 | fputs (_(" -O, --options <list> limit the set of filesystems by mount options\n"), out); |
1139 | fputs (_(" -o, --output <list> the output columns to be shown\n"), out); | |
1140 | fputs (_(" -P, --pairs use key=\"value\" output format\n"), out); | |
1141 | fputs (_(" -r, --raw use raw output format\n"), out); | |
1142 | fputs (_(" -t, --types <list> limit the set of filesystems by FS types\n"), out); | |
415b61fc | 1143 | fprintf(out, _( |
b2214e1f | 1144 | " -v, --nofsroot don't print [/dir] for bind or btrfs mounts\n" |
c3214059 | 1145 | " -R, --submounts print all submounts for the matching filesystems\n" |
cd492186 KZ |
1146 | " -S, --source <string> the device to mount (by name, maj:min, \n" |
1147 | " LABEL=, UUID=, PARTUUID=, PARTLABEL=)\n" | |
5cc12c40 | 1148 | " -T, --target <string> the mountpoint to use\n")); |
04fd7a9f | 1149 | |
5cc12c40 SK |
1150 | fputs(USAGE_SEPARATOR, out); |
1151 | fputs(USAGE_HELP, out); | |
1152 | fputs(USAGE_VERSION, out); | |
9ead0006 KZ |
1153 | |
1154 | fprintf(out, _("\nAvailable columns:\n")); | |
1155 | ||
b152e359 KZ |
1156 | for (i = 0; i < FINDMNT_NCOLUMNS; i++) |
1157 | fprintf(out, " %11s %s\n", infos[i].name, _(infos[i].help)); | |
9ead0006 | 1158 | |
beda9dd3 | 1159 | fprintf(out, USAGE_MAN_TAIL("findmnt(8)")); |
04fd7a9f KZ |
1160 | |
1161 | exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS); | |
1162 | } | |
1163 | ||
1164 | int main(int argc, char *argv[]) | |
1165 | { | |
68164f6c | 1166 | struct libmnt_table *tb = NULL; |
8c5eba3e | 1167 | char **tabfiles = NULL; |
9d67679b | 1168 | int direction = MNT_ITER_FORWARD; |
cd4ed46f | 1169 | int i, c, rc = -1, timeout = -1; |
8c5eba3e | 1170 | int ntabfiles = 0, tabtype = 0; |
c57dca68 | 1171 | char *outarg = NULL; |
fdedb45e | 1172 | |
fdedb45e | 1173 | struct tt *tt = NULL; |
fdedb45e | 1174 | |
6c7d5ae9 | 1175 | static const struct option longopts[] = { |
cd4ed46f | 1176 | { "all", 0, 0, 'A' }, |
9d67679b | 1177 | { "ascii", 0, 0, 'a' }, |
04fd7a9f KZ |
1178 | { "canonicalize", 0, 0, 'c' }, |
1179 | { "direction", 1, 0, 'd' }, | |
eda399b9 | 1180 | { "df", 0, 0, 'D' }, |
04fd7a9f | 1181 | { "evaluate", 0, 0, 'e' }, |
9d67679b | 1182 | { "first-only", 0, 0, 'f' }, |
2f1ac44b | 1183 | { "fstab", 0, 0, 's' }, |
04fd7a9f KZ |
1184 | { "help", 0, 0, 'h' }, |
1185 | { "invert", 0, 0, 'i' }, | |
9d67679b KZ |
1186 | { "kernel", 0, 0, 'k' }, |
1187 | { "list", 0, 0, 'l' }, | |
1188 | { "mtab", 0, 0, 'm' }, | |
1189 | { "noheadings", 0, 0, 'n' }, | |
1190 | { "notruncate", 0, 0, 'u' }, | |
04fd7a9f | 1191 | { "options", 1, 0, 'O' }, |
9d67679b | 1192 | { "output", 1, 0, 'o' }, |
582a5006 | 1193 | { "poll", 2, 0, 'p' }, |
49e9fd3a | 1194 | { "pairs", 0, 0, 'P' }, |
9d67679b | 1195 | { "raw", 0, 0, 'r' }, |
04fd7a9f | 1196 | { "types", 1, 0, 't' }, |
395eb05d | 1197 | { "nofsroot", 0, 0, 'v' }, |
049caefd | 1198 | { "submounts", 0, 0, 'R' }, |
9d67679b | 1199 | { "source", 1, 0, 'S' }, |
2f1ac44b | 1200 | { "tab-file", 1, 0, 'F' }, |
c7fcc830 | 1201 | { "task", 1, 0, 'N' }, |
9d67679b | 1202 | { "target", 1, 0, 'T' }, |
ad38fb9f | 1203 | { "timeout", 1, 0, 'w' }, |
5cc12c40 | 1204 | { "version", 0, 0, 'V' }, |
9d67679b | 1205 | |
04fd7a9f KZ |
1206 | { NULL, 0, 0, 0 } |
1207 | }; | |
1208 | ||
f1622b57 KZ |
1209 | static const ul_excl_t excl[] = { /* rows and cols in in ASCII order */ |
1210 | { 'N','k','m','s' }, /* task,kernel,mtab,fstab */ | |
1211 | { 'P','l','r' }, /* pairs,list,raw */ | |
1212 | { 'm','p','s' }, /* mtab,poll,fstab */ | |
1213 | { 0 } | |
1214 | }; | |
1215 | int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT; | |
1216 | ||
af0dc7d3 | 1217 | assert(ARRAY_SIZE(columns) == FINDMNT_NCOLUMNS); |
9d67679b | 1218 | |
04fd7a9f KZ |
1219 | setlocale(LC_ALL, ""); |
1220 | bindtextdomain(PACKAGE, LOCALEDIR); | |
1221 | textdomain(PACKAGE); | |
c05a80ca | 1222 | atexit(close_stdout); |
04fd7a9f | 1223 | |
9d67679b | 1224 | /* default output format */ |
fdedb45e | 1225 | tt_flags |= TT_FL_TREE; |
04fd7a9f | 1226 | |
9d67679b | 1227 | while ((c = getopt_long(argc, argv, |
c7fcc830 | 1228 | "AacDd:ehifF:o:O:p::PklmnN:rst:uvRS:T:w:V", |
d466c6a1 | 1229 | longopts, NULL)) != -1) { |
f1622b57 KZ |
1230 | |
1231 | err_exclusive_options(c, longopts, excl, excl_st); | |
1232 | ||
04fd7a9f | 1233 | switch(c) { |
cd4ed46f KZ |
1234 | case 'A': |
1235 | flags |= FL_ALL; | |
1236 | break; | |
9d67679b | 1237 | case 'a': |
fdedb45e | 1238 | tt_flags |= TT_FL_ASCII; |
9d67679b | 1239 | break; |
04fd7a9f KZ |
1240 | case 'c': |
1241 | flags |= FL_CANONICALIZE; | |
1242 | break; | |
eda399b9 DR |
1243 | case 'D': |
1244 | tt_flags &= ~TT_FL_TREE; | |
cd4ed46f | 1245 | flags |= FL_DF; |
eda399b9 | 1246 | break; |
04fd7a9f | 1247 | case 'd': |
4e6bd74c | 1248 | if (!strcmp(optarg, "forward")) |
04fd7a9f | 1249 | direction = MNT_ITER_FORWARD; |
4e6bd74c | 1250 | else if (!strcmp(optarg, "backward")) |
04fd7a9f KZ |
1251 | direction = MNT_ITER_BACKWARD; |
1252 | else | |
1253 | errx(EXIT_FAILURE, | |
8e350e48 | 1254 | _("unknown direction '%s'"), optarg); |
04fd7a9f KZ |
1255 | break; |
1256 | case 'e': | |
1257 | flags |= FL_EVALUATE; | |
1258 | break; | |
1259 | case 'h': | |
1260 | usage(stdout); | |
1261 | break; | |
1262 | case 'i': | |
1263 | flags |= FL_INVERT; | |
1264 | break; | |
9d67679b | 1265 | case 'f': |
04fd7a9f KZ |
1266 | flags |= FL_FIRSTONLY; |
1267 | break; | |
2f1ac44b | 1268 | case 'F': |
8c5eba3e | 1269 | tabfiles = append_tabfile(tabfiles, &ntabfiles, optarg); |
2f1ac44b | 1270 | break; |
9d67679b | 1271 | case 'u': |
32e5466a | 1272 | disable_columns_truncate(); |
9d67679b | 1273 | break; |
04fd7a9f | 1274 | case 'o': |
c57dca68 | 1275 | outarg = optarg; |
04fd7a9f KZ |
1276 | break; |
1277 | case 'O': | |
9d67679b | 1278 | set_match(COL_OPTIONS, optarg); |
04fd7a9f | 1279 | break; |
32e5466a | 1280 | case 'p': |
bdc3ed66 | 1281 | if (optarg) { |
c87638ad | 1282 | nactions = string_to_idarray(optarg, |
bdc3ed66 KZ |
1283 | actions, ARRAY_SIZE(actions), |
1284 | poll_action_name_to_id); | |
1285 | if (nactions < 0) | |
1286 | exit(EXIT_FAILURE); | |
1287 | } | |
32e5466a KZ |
1288 | flags |= FL_POLL; |
1289 | tt_flags &= ~TT_FL_TREE; | |
1290 | break; | |
49e9fd3a KZ |
1291 | case 'P': |
1292 | tt_flags |= TT_FL_EXPORT; | |
1293 | tt_flags &= ~TT_FL_TREE; | |
1294 | break; | |
fdedb45e | 1295 | case 'm': /* mtab */ |
8c5eba3e | 1296 | tabtype = TABTYPE_MTAB; |
fdedb45e | 1297 | tt_flags &= ~TT_FL_TREE; |
04fd7a9f | 1298 | break; |
fdedb45e | 1299 | case 's': /* fstab */ |
8c5eba3e | 1300 | tabtype = TABTYPE_FSTAB; |
ac808156 | 1301 | tt_flags &= ~TT_FL_TREE; |
04fd7a9f | 1302 | break; |
fdedb45e | 1303 | case 'k': /* kernel (mountinfo) */ |
8c5eba3e | 1304 | tabtype = TABTYPE_KERNEL; |
04fd7a9f KZ |
1305 | break; |
1306 | case 't': | |
9d67679b KZ |
1307 | set_match(COL_FSTYPE, optarg); |
1308 | break; | |
1309 | case 'r': | |
fdedb45e KZ |
1310 | tt_flags &= ~TT_FL_TREE; /* disable the default */ |
1311 | tt_flags |= TT_FL_RAW; /* enable raw */ | |
9d67679b KZ |
1312 | break; |
1313 | case 'l': | |
fdedb45e | 1314 | tt_flags &= ~TT_FL_TREE; /* disable the default */ |
9d67679b KZ |
1315 | break; |
1316 | case 'n': | |
fdedb45e | 1317 | tt_flags |= TT_FL_NOHEADINGS; |
9d67679b | 1318 | break; |
c7fcc830 | 1319 | case 'N': |
c7fcc830 KZ |
1320 | tabtype = TABTYPE_KERNEL; |
1321 | tabfiles = append_pid_tabfile(tabfiles, &ntabfiles, | |
1322 | strtou32_or_err(optarg, | |
1323 | _("invalid TID argument"))); | |
1324 | break; | |
b2214e1f KZ |
1325 | case 'v': |
1326 | flags |= FL_NOFSROOT; | |
1327 | break; | |
049caefd KZ |
1328 | case 'R': |
1329 | flags |= FL_SUBMOUNTS; | |
1330 | break; | |
9d67679b | 1331 | case 'S': |
cd492186 | 1332 | set_source_match(optarg); |
9d67679b KZ |
1333 | flags |= FL_NOSWAPMATCH; |
1334 | break; | |
1335 | case 'T': | |
1336 | set_match(COL_TARGET, optarg); | |
1337 | flags |= FL_NOSWAPMATCH; | |
04fd7a9f | 1338 | break; |
ad38fb9f | 1339 | case 'w': |
db41a429 | 1340 | timeout = strtos32_or_err(optarg, _("invalid timeout argument")); |
ad38fb9f | 1341 | break; |
5cc12c40 SK |
1342 | case 'V': |
1343 | printf(UTIL_LINUX_VERSION); | |
1344 | return EXIT_SUCCESS; | |
04fd7a9f KZ |
1345 | default: |
1346 | usage(stderr); | |
1347 | break; | |
1348 | } | |
1349 | } | |
1350 | ||
cd4ed46f | 1351 | if (!ncolumns && (flags & FL_DF)) { |
eda399b9 DR |
1352 | columns[ncolumns++] = COL_SOURCE; |
1353 | columns[ncolumns++] = COL_FSTYPE; | |
1354 | columns[ncolumns++] = COL_SIZE; | |
1355 | columns[ncolumns++] = COL_USED; | |
1356 | columns[ncolumns++] = COL_AVAIL; | |
1357 | columns[ncolumns++] = COL_USEPERC; | |
1358 | columns[ncolumns++] = COL_TARGET; | |
1359 | } | |
1360 | ||
32e5466a KZ |
1361 | /* default columns */ |
1362 | if (!ncolumns) { | |
1363 | if (flags & FL_POLL) | |
1364 | columns[ncolumns++] = COL_ACTION; | |
1365 | ||
1366 | columns[ncolumns++] = COL_TARGET; | |
1367 | columns[ncolumns++] = COL_SOURCE; | |
1368 | columns[ncolumns++] = COL_FSTYPE; | |
1369 | columns[ncolumns++] = COL_OPTIONS; | |
1370 | } | |
1371 | ||
c57dca68 MB |
1372 | if (outarg && string_add_to_idarray(outarg, columns, ARRAY_SIZE(columns), |
1373 | &ncolumns, column_name_to_id) < 0) | |
1374 | return EXIT_FAILURE; | |
1375 | ||
8c5eba3e KZ |
1376 | if (!tabtype) |
1377 | tabtype = TABTYPE_KERNEL; | |
2f1ac44b | 1378 | |
f1622b57 KZ |
1379 | if ((flags & FL_POLL) && ntabfiles > 1) |
1380 | errx(EXIT_FAILURE, _("--poll accepts only one file, but more specified by --tab-file")); | |
b2214e1f | 1381 | |
9d67679b KZ |
1382 | if (optind < argc && (get_match(COL_SOURCE) || get_match(COL_TARGET))) |
1383 | errx(EXIT_FAILURE, _( | |
1384 | "options --target and --source can't be used together " | |
1385 | "with command line element that is not an option")); | |
1386 | ||
04fd7a9f | 1387 | if (optind < argc) |
cd492186 | 1388 | set_source_match(argv[optind++]); /* dev/tag/mountpoint/maj:min */ |
04fd7a9f | 1389 | if (optind < argc) |
9d67679b | 1390 | set_match(COL_TARGET, argv[optind++]); /* mountpoint */ |
04fd7a9f | 1391 | |
049caefd KZ |
1392 | if ((flags & FL_SUBMOUNTS) && is_listall_mode()) |
1393 | /* don't care about submounts if list all mounts */ | |
1394 | flags &= ~FL_SUBMOUNTS; | |
1395 | ||
31f67453 KZ |
1396 | if (!(flags & FL_SUBMOUNTS) && ((flags & FL_FIRSTONLY) |
1397 | || get_match(COL_TARGET) | |
1398 | || get_match(COL_SOURCE) | |
1399 | || get_match(COL_MAJMIN))) | |
fdedb45e KZ |
1400 | tt_flags &= ~TT_FL_TREE; |
1401 | ||
ac808156 KZ |
1402 | if (!(flags & FL_NOSWAPMATCH) && |
1403 | !get_match(COL_TARGET) && get_match(COL_SOURCE)) { | |
1404 | /* | |
1405 | * Check if we can swap source and target, it's | |
1406 | * not possible if the source is LABEL=/UUID= | |
1407 | */ | |
1408 | const char *x = get_match(COL_SOURCE); | |
1409 | ||
090b5e84 KZ |
1410 | if (!strncmp(x, "LABEL=", 6) || !strncmp(x, "UUID=", 5) || |
1411 | !strncmp(x, "PARTLABEL=", 10) || !strncmp(x, "PARTUUID=", 9)) | |
ac808156 KZ |
1412 | flags |= FL_NOSWAPMATCH; |
1413 | } | |
1414 | ||
fdedb45e KZ |
1415 | /* |
1416 | * initialize libmount | |
1417 | */ | |
ac808156 KZ |
1418 | mnt_init_debug(0); |
1419 | ||
8c5eba3e | 1420 | tb = parse_tabfiles(tabfiles, ntabfiles, tabtype); |
04fd7a9f | 1421 | if (!tb) |
fdedb45e | 1422 | goto leave; |
04fd7a9f | 1423 | |
aca72ac2 | 1424 | if ((tt_flags & TT_FL_TREE) && (ntabfiles > 1 || !tab_is_tree(tb))) |
2f1ac44b KZ |
1425 | tt_flags &= ~TT_FL_TREE; |
1426 | ||
04fd7a9f | 1427 | cache = mnt_new_cache(); |
fdedb45e KZ |
1428 | if (!cache) { |
1429 | warn(_("failed to initialize libmount cache")); | |
1430 | goto leave; | |
1431 | } | |
68164f6c | 1432 | mnt_table_set_cache(tb, cache); |
04fd7a9f | 1433 | |
4bfd4bff | 1434 | |
fdedb45e | 1435 | /* |
00b4bcdf | 1436 | * initialize output formatting (tt.h) |
fdedb45e KZ |
1437 | */ |
1438 | tt = tt_new_table(tt_flags); | |
1439 | if (!tt) { | |
1440 | warn(_("failed to initialize output table")); | |
1441 | goto leave; | |
1442 | } | |
ac808156 | 1443 | |
fdedb45e | 1444 | for (i = 0; i < ncolumns; i++) { |
32e5466a KZ |
1445 | int fl = get_column_flags(i); |
1446 | int id = get_column_id(i); | |
1447 | ||
1448 | if (!(tt_flags & TT_FL_TREE)) | |
1449 | fl &= ~TT_FL_TREE; | |
04fd7a9f | 1450 | |
32e5466a | 1451 | if (!(flags & FL_POLL) && is_tabdiff_column(id)) { |
77a1c5f7 | 1452 | warnx(_("%s column is requested, but --poll " |
32e5466a KZ |
1453 | "is not enabled"), get_column_name(i)); |
1454 | goto leave; | |
1455 | } | |
fdedb45e KZ |
1456 | if (!tt_define_column(tt, get_column_name(i), |
1457 | get_column_whint(i), fl)) { | |
1458 | warn(_("failed to initialize output column")); | |
1459 | goto leave; | |
9d67679b | 1460 | } |
fdedb45e | 1461 | } |
9d67679b | 1462 | |
fdedb45e KZ |
1463 | /* |
1464 | * Fill in data to the output table | |
1465 | */ | |
275c9a48 | 1466 | if (flags & FL_POLL) { |
8c5eba3e | 1467 | /* poll mode (accept the first tabfile only) */ |
b8a670ae | 1468 | rc = poll_table(tb, tabfiles ? *tabfiles : _PATH_PROC_MOUNTINFO, timeout, tt, direction); |
32e5466a | 1469 | |
31f67453 | 1470 | } else if ((tt_flags & TT_FL_TREE) && !(flags & FL_SUBMOUNTS)) { |
049caefd KZ |
1471 | /* whole tree */ |
1472 | rc = create_treenode(tt, tb, NULL, NULL); | |
31f67453 | 1473 | } else { |
049caefd KZ |
1474 | /* whole lits of sub-tree */ |
1475 | rc = add_matching_lines(tb, tt, direction); | |
fdedb45e | 1476 | |
3e373967 KZ |
1477 | if (rc != 0 |
1478 | && tabtype == TABTYPE_KERNEL | |
1479 | && (flags & FL_NOSWAPMATCH) | |
1480 | && get_match(COL_TARGET)) { | |
1481 | /* | |
1482 | * Found nothing, maybe the --target is regular file, | |
1483 | * try it again with extra functionality for target | |
1484 | * match | |
1485 | */ | |
1486 | enable_extra_target_match(); | |
1487 | rc = add_matching_lines(tb, tt, direction); | |
1488 | } | |
1489 | } | |
1490 | ||
fdedb45e | 1491 | /* |
32e5466a | 1492 | * Print the output table for non-poll modes |
fdedb45e | 1493 | */ |
32e5466a | 1494 | if (!rc && !(flags & FL_POLL)) |
049caefd | 1495 | tt_print_table(tt); |
fdedb45e KZ |
1496 | leave: |
1497 | tt_free_table(tt); | |
1498 | ||
68164f6c | 1499 | mnt_free_table(tb); |
04fd7a9f | 1500 | mnt_free_cache(cache); |
8c5eba3e | 1501 | free(tabfiles); |
e346233e KZ |
1502 | #ifdef HAVE_LIBUDEV |
1503 | udev_unref(udev); | |
1504 | #endif | |
049caefd | 1505 | return rc ? EXIT_FAILURE : EXIT_SUCCESS; |
04fd7a9f | 1506 | } |