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