diff options
Diffstat (limited to 'misc-utils/findmnt.c')
-rw-r--r-- | misc-utils/findmnt.c | 230 |
1 files changed, 170 insertions, 60 deletions
diff --git a/misc-utils/findmnt.c b/misc-utils/findmnt.c index 733bbc1..cc397da 100644 --- a/misc-utils/findmnt.c +++ b/misc-utils/findmnt.c @@ -20,6 +20,7 @@ */ #include <stdio.h> #include <stdlib.h> +#include <stdbool.h> #include <errno.h> #include <unistd.h> #include <getopt.h> @@ -48,6 +49,7 @@ #include "optutils.h" #include "mangle.h" #include "buffer.h" +#include "column-list-table.h" #include "findmnt.h" @@ -60,6 +62,10 @@ enum { COL_FSTYPE, COL_FS_OPTIONS, COL_ID, + COL_INO_AVAIL, + COL_INO_TOTAL, + COL_INO_USED, + COL_INO_USEPERC, COL_LABEL, COL_MAJMIN, COL_OLD_OPTIONS, @@ -90,7 +96,7 @@ enum { /* column names */ struct colinfo { - const char *name; /* header */ + const char * const name; /* header */ double whint; /* width hint (N < 1 is in percent of termwidth) */ int flags; /* libsmartcols flags */ const char *help; /* column description */ @@ -101,12 +107,16 @@ struct colinfo { /* columns descriptions (don't use const, this is writable) */ static struct colinfo infos[] = { [COL_ACTION] = { "ACTION", 10, SCOLS_FL_STRICTWIDTH, N_("action detected by --poll") }, - [COL_AVAIL] = { "AVAIL", 5, SCOLS_FL_RIGHT, N_("filesystem size available") }, + [COL_AVAIL] = { "AVAIL", 5, SCOLS_FL_RIGHT, N_("filesystem size available, use <number> if --bytes is given") }, [COL_FREQ] = { "FREQ", 1, SCOLS_FL_RIGHT, N_("dump(8) period in days [fstab only]") }, [COL_FSROOT] = { "FSROOT", 0.25, SCOLS_FL_NOEXTREMES, N_("filesystem root") }, [COL_FSTYPE] = { "FSTYPE", 0.10, SCOLS_FL_TRUNC, N_("filesystem type") }, [COL_FS_OPTIONS] = { "FS-OPTIONS", 0.10, SCOLS_FL_TRUNC, N_("FS specific mount options") }, [COL_ID] = { "ID", 2, SCOLS_FL_RIGHT, N_("mount ID") }, + [COL_INO_AVAIL] = { "INO.AVAIL", 5, SCOLS_FL_RIGHT, N_("number of available inodes") }, + [COL_INO_TOTAL] = { "INO.TOTAL", 5, SCOLS_FL_RIGHT, N_("total number of inodes") }, + [COL_INO_USED] = { "INO.USED", 5, SCOLS_FL_RIGHT, N_("number of used inodes") }, + [COL_INO_USEPERC] = { "INO.USE%", 3, SCOLS_FL_RIGHT, N_("percentage of INO.USED divided by INO.TOTAL") }, [COL_LABEL] = { "LABEL", 0.10, 0, N_("filesystem label") }, [COL_MAJMIN] = { "MAJ:MIN", 6, 0, N_("major:minor device number") }, [COL_OLD_OPTIONS] = { "OLD-OPTIONS", 0.10, SCOLS_FL_TRUNC, N_("old mount options saved by --poll") }, @@ -118,12 +128,12 @@ static struct colinfo infos[] = { [COL_PARTUUID] = { "PARTUUID", 36, 0, N_("partition UUID") }, [COL_PASSNO] = { "PASSNO", 1, SCOLS_FL_RIGHT, N_("pass number on parallel fsck(8) [fstab only]") }, [COL_PROPAGATION] = { "PROPAGATION", 0.10, 0, N_("VFS propagation flags") }, - [COL_SIZE] = { "SIZE", 5, SCOLS_FL_RIGHT, N_("filesystem size") }, + [COL_SIZE] = { "SIZE", 5, SCOLS_FL_RIGHT, N_("filesystem size, use <number> if --bytes is given") }, [COL_SOURCES] = { "SOURCES", 0.25, SCOLS_FL_WRAP, N_("all possible source devices") }, [COL_SOURCE] = { "SOURCE", 0.25, SCOLS_FL_NOEXTREMES, N_("source device") }, [COL_TARGET] = { "TARGET", 0.30, SCOLS_FL_TREE| SCOLS_FL_NOEXTREMES, N_("mountpoint") }, [COL_TID] = { "TID", 4, SCOLS_FL_RIGHT, N_("task ID") }, - [COL_USED] = { "USED", 5, SCOLS_FL_RIGHT, N_("filesystem size used") }, + [COL_USED] = { "USED", 5, SCOLS_FL_RIGHT, N_("filesystem size used, use <number> if --bytes is given") }, [COL_USEPERC] = { "USE%", 3, SCOLS_FL_RIGHT, N_("filesystem use percentage") }, [COL_UUID] = { "UUID", 36, 0, N_("filesystem UUID") }, [COL_VFS_OPTIONS] = { "VFS-OPTIONS", 0.20, SCOLS_FL_TRUNC, N_("VFS specific mount options") } @@ -475,6 +485,7 @@ static char *get_vfs_attr(struct libmnt_fs *fs, int sizetype) struct statvfs buf; uint64_t vfs_attr = 0; char *sizestr; + bool no_bytes = false; if (statvfs(mnt_fs_get_target(fs), &buf) != 0) return NULL; @@ -497,11 +508,38 @@ static char *get_vfs_attr(struct libmnt_fs *fs, int sizetype) (double)(buf.f_blocks - buf.f_bfree) / buf.f_blocks * 100); return sizestr; + case COL_INO_AVAIL: + /* Quoted from startvfs(3): + * + * Under Linux, f_favail is always the same as + * f_ffree, and there's no way for a filesystem to + * report otherwise. This is not an issue, since no + * filesystems with an inode root reservation exist. + */ + no_bytes = true; + vfs_attr = buf.f_ffree; + break; + case COL_INO_TOTAL: + no_bytes = true; + vfs_attr = buf.f_files; + break; + case COL_INO_USED: + no_bytes = true; + vfs_attr = buf.f_files - buf.f_ffree; + break; + case COL_INO_USEPERC: + if (buf.f_files == 0) + return xstrdup("-"); + + xasprintf(&sizestr, "%.0f%%", + (double)(buf.f_files - buf.f_ffree) / + buf.f_files * 100); + return sizestr; } if (!vfs_attr) sizestr = xstrdup("0"); - else if (flags & FL_BYTES) + else if ((flags & FL_BYTES) || no_bytes) xasprintf(&sizestr, "%ju", vfs_attr); else sizestr = size_to_human_string(SIZE_SUFFIX_1LETTER, vfs_attr); @@ -511,7 +549,7 @@ static char *get_vfs_attr(struct libmnt_fs *fs, int sizetype) /* reads sources from libmount/libblkid */ -static char *get_data_col_sources(struct libmnt_fs *fs, int evaluate) +static char *get_data_col_sources(struct libmnt_fs *fs, int evaluate, size_t *datasiz) { const char *tag = NULL, *p = NULL; int i = 0; @@ -565,14 +603,14 @@ static char *get_data_col_sources(struct libmnt_fs *fs, int evaluate) if (!dev) continue; if (i != 0) - ul_buffer_append_data(&buf, "\n", 1); + ul_buffer_append_data(&buf, "\0", 1); ul_buffer_append_string(&buf, blkid_dev_devname(dev)); i++; } blkid_dev_iterate_end(iter); free(val); - return ul_buffer_get_data(&buf, NULL, NULL); + return ul_buffer_get_data(&buf, datasiz, NULL); nothing: free(val); @@ -581,7 +619,7 @@ nothing: /* reads FS data from libmount */ -static char *get_data(struct libmnt_fs *fs, int num) +static char *get_data(struct libmnt_fs *fs, int num, size_t *datasiz) { char *str = NULL; int col_id = get_column_id(num); @@ -589,7 +627,7 @@ static char *get_data(struct libmnt_fs *fs, int num) switch (col_id) { case COL_SOURCES: /* print all devices with the same tag (LABEL, UUID) */ - str = get_data_col_sources(fs, flags & FL_EVALUATE); + str = get_data_col_sources(fs, flags & FL_EVALUATE, datasiz); if (str) break; @@ -672,6 +710,10 @@ static char *get_data(struct libmnt_fs *fs, int num) case COL_AVAIL: case COL_USED: case COL_USEPERC: + case COL_INO_TOTAL: + case COL_INO_AVAIL: + case COL_INO_USED: + case COL_INO_USEPERC: str = get_vfs_attr(fs, col_id); break; case COL_FSROOT: @@ -730,7 +772,8 @@ static char *get_data(struct libmnt_fs *fs, int num) static char *get_tabdiff_data(struct libmnt_fs *old_fs, struct libmnt_fs *new_fs, int change, - int num) + int num, + size_t *datasiz) { char *str = NULL; @@ -769,14 +812,30 @@ static char *get_tabdiff_data(struct libmnt_fs *old_fs, break; default: if (new_fs) - str = get_data(new_fs, num); + str = get_data(new_fs, num, datasiz); else - str = get_data(old_fs, num); + str = get_data(old_fs, num, datasiz); break; } return str; } +static void set_line_data(struct libscols_line *ln, size_t i, char *data, size_t datasiz) +{ + int rc; + struct libscols_cell *ce; + + ce = scols_line_get_cell(ln, i); + if (!ce) + return; + if (datasiz) + rc = scols_cell_refer_memory(ce, data, datasiz); + else + rc = scols_cell_refer_data(ce, data); + if (rc) + err(EXIT_FAILURE, _("failed to add output data")); +} + /* adds one line to the output @tab */ static struct libscols_line *add_line(struct libscols_table *table, struct libmnt_fs *fs, struct libscols_line *parent) @@ -788,8 +847,11 @@ static struct libscols_line *add_line(struct libscols_table *table, struct libmn err(EXIT_FAILURE, _("failed to allocate output line")); for (i = 0; i < ncolumns; i++) { - if (scols_line_refer_data(line, i, get_data(fs, i))) - err(EXIT_FAILURE, _("failed to add output data")); + size_t datasiz = 0; + char *data = get_data(fs, i, &datasiz); + + if (data) + set_line_data(line, i, data, datasiz); } scols_line_set_userdata(line, fs); @@ -806,9 +868,11 @@ static struct libscols_line *add_tabdiff_line(struct libscols_table *table, stru err(EXIT_FAILURE, _("failed to allocate output line")); for (i = 0; i < ncolumns; i++) { - if (scols_line_refer_data(line, i, - get_tabdiff_data(old_fs, new_fs, change, i))) - err(EXIT_FAILURE, _("failed to add output data")); + size_t datasiz = 0; + char *data = get_tabdiff_data(old_fs, new_fs, change, i, &datasiz); + + if (data) + set_line_data(line, i, data, datasiz); } return line; @@ -901,7 +965,7 @@ static int parser_errcb(struct libmnt_table *tb __attribute__ ((__unused__)), static char **append_tabfile(char **files, int *nfiles, char *filename) { - files = xrealloc(files, sizeof(char *) * (*nfiles + 1)); + files = xreallocarray(files, *nfiles + 1, sizeof(char *)); files[(*nfiles)++] = filename; return files; } @@ -1272,9 +1336,7 @@ static int poll_table(struct libmnt_table *tb, const char *tabfile, if (count) { rc = scols_table_print_range(table, NULL, NULL); - if (rc == 0) - fputc('\n', scols_table_get_stream(table)); - fflush(stdout); + fflush(scols_table_get_stream(table)); if (rc) goto done; } @@ -1310,10 +1372,36 @@ static int uniq_fs_target_cmp( return !mnt_fs_match_target(a, mnt_fs_get_target(b), cache); } +static int get_column_json_type(int id, int scols_flags, int *multi) +{ + switch (id) { + case COL_SIZE: + case COL_AVAIL: + case COL_USED: + if (multi) + *multi = 1; + if (!(flags & FL_BYTES)) + break; + /* fallthrough */ + case COL_ID: + case COL_PARENT: + case COL_FREQ: + case COL_PASSNO: + case COL_TID: + return SCOLS_JSON_NUMBER; + + default: + if (scols_flags & SCOLS_FL_WRAP) + return SCOLS_JSON_ARRAY_STRING; + break; + } + + return SCOLS_JSON_STRING; /* default */ +} + static void __attribute__((__noreturn__)) usage(void) { FILE *out = stdout; - size_t i; fputs(USAGE_HEADER, out); fprintf(out, _( @@ -1348,13 +1436,14 @@ static void __attribute__((__noreturn__)) usage(void) " to device names\n"), out); fputs(_(" -F, --tab-file <path> alternative file for -s, -m or -k options\n"), out); fputs(_(" -f, --first-only print the first found filesystem only\n"), out); + fputs(_(" -I, --dfi imitate the output of df(1) with -i option\n"), out); fputs(_(" -i, --invert invert the sense of matching\n"), out); fputs(_(" -J, --json use JSON output format\n"), out); fputs(_(" -l, --list use list format output\n"), out); fputs(_(" -N, --task <tid> use alternative namespace (/proc/<tid>/mountinfo file)\n"), out); fputs(_(" -n, --noheadings don't print column headings\n"), out); fputs(_(" -O, --options <list> limit the set of filesystems by mount options\n"), out); - fputs(_(" -o, --output <list> the output columns to be shown\n"), out); + fputs(_(" -o, --output <list> output columns (see --list-columns)\n"), out); fputs(_(" --output-all output all available columns\n"), out); fputs(_(" -P, --pairs use key=\"value\" output format\n"), out); fputs(_(" --pseudo print only pseudo-filesystems\n"), out); @@ -1379,23 +1468,45 @@ static void __attribute__((__noreturn__)) usage(void) fputs(_(" --vfs-all print all VFS options\n"), out); fputs(USAGE_SEPARATOR, out); - printf(USAGE_HELP_OPTIONS(24)); + fputs(_(" -H, --list-columns list the available columns\n"), out); + fprintf(out, USAGE_HELP_OPTIONS(24)); - fputs(USAGE_COLUMNS, out); - for (i = 0; i < ARRAY_SIZE(infos); i++) - fprintf(out, " %11s %s\n", infos[i].name, _(infos[i].help)); + fprintf(out, USAGE_MAN_TAIL("findmnt(8)")); + + exit(EXIT_SUCCESS); +} + +static void __attribute__((__noreturn__)) list_colunms(void) +{ + size_t i; + struct libscols_table *tb = xcolumn_list_table_new("findmnt-columns", stdout, + flags & FL_RAW, + flags & FL_JSON); + + for (i = 0; i < ARRAY_SIZE(infos); i++) { + const struct colinfo *ci = &infos[i]; + int multi = 0; + int json = get_column_json_type(i, ci->flags, &multi); + + xcolumn_list_table_append_line(tb, ci->name, + multi ? -1 : json, + multi ? "<string|number>" : NULL, + _(ci->help)); + } - printf(USAGE_MAN_TAIL("findmnt(8)")); + scols_print_table(tb); + scols_unref_table(tb); exit(EXIT_SUCCESS); } + int main(int argc, char *argv[]) { struct libmnt_table *tb = NULL; char **tabfiles = NULL; int direction = MNT_ITER_FORWARD; - int verify = 0; + int verify = 0, collist = 0; int c, rc = -1, timeout = -1; int ntabfiles = 0, tabtype = 0; char *outarg = NULL; @@ -1421,6 +1532,7 @@ int main(int argc, char *argv[]) { "canonicalize", no_argument, NULL, 'c' }, { "direction", required_argument, NULL, 'd' }, { "df", no_argument, NULL, 'D' }, + { "dfi", no_argument, NULL, 'I' }, { "evaluate", no_argument, NULL, 'e' }, { "first-only", no_argument, NULL, 'f' }, { "fstab", no_argument, NULL, 's' }, @@ -1458,6 +1570,7 @@ int main(int argc, char *argv[]) { "pseudo", no_argument, NULL, FINDMNT_OPT_PSEUDO }, { "vfs-all", no_argument, NULL, FINDMNT_OPT_VFS_ALL }, { "shadowed", no_argument, NULL, FINDMNT_OPT_SHADOWED }, + { "list-columns", no_argument, NULL, 'H' }, { NULL, 0, NULL, 0 } }; @@ -1484,7 +1597,7 @@ int main(int argc, char *argv[]) flags |= FL_TREE; while ((c = getopt_long(argc, argv, - "AabCcDd:ehiJfF:o:O:p::PklmM:nN:rst:uvRS:T:Uw:Vxy", + "AabCcDd:ehIiJfF:o:O:p::PklmM:nN:rst:uvRS:T:Uw:VxyH", longopts, NULL)) != -1) { err_exclusive_options(c, longopts, excl, excl_st); @@ -1521,6 +1634,10 @@ int main(int argc, char *argv[]) case 'e': flags |= FL_EVALUATE; break; + case 'I': + flags &= ~FL_TREE; + flags |= FL_DF_INODES; + break; case 'i': flags |= FL_INVERT; break; @@ -1642,6 +1759,10 @@ int main(int argc, char *argv[]) case FINDMNT_OPT_SHADOWED: flags |= FL_SHADOWED; break; + + case 'H': + collist = 1; + break; case 'h': usage(); case 'V': @@ -1651,7 +1772,19 @@ int main(int argc, char *argv[]) } } - if (!ncolumns && (flags & FL_DF)) { + if (collist) + list_colunms(); /* print end exit */ + + if (!ncolumns && (flags & FL_DF_INODES)) { + add_column(columns, ncolumns++, COL_SOURCE); + add_column(columns, ncolumns++, COL_FSTYPE); + add_column(columns, ncolumns++, COL_INO_TOTAL); + add_column(columns, ncolumns++, COL_INO_USED); + add_column(columns, ncolumns++, COL_INO_AVAIL); + add_column(columns, ncolumns++, COL_INO_USEPERC); + add_column(columns, ncolumns++, COL_TARGET); + } + else if (!ncolumns && (flags & FL_DF)) { add_column(columns, ncolumns++, COL_SOURCE); add_column(columns, ncolumns++, COL_FSTYPE); add_column(columns, ncolumns++, COL_SIZE); @@ -1793,36 +1926,13 @@ int main(int argc, char *argv[]) goto leave; } /* multi-line cells (now used for SOURCES) */ - if (fl & SCOLS_FL_WRAP) { + if (fl & SCOLS_FL_WRAP) scols_column_set_wrapfunc(cl, - scols_wrapnl_chunksize, - scols_wrapnl_nextchunk, + NULL, + scols_wrapzero_nextchunk, NULL); - scols_column_set_safechars(cl, "\n"); - } - if (flags & FL_JSON) { - switch (id) { - case COL_SIZE: - case COL_AVAIL: - case COL_USED: - if (!(flags & FL_BYTES)) - break; - /* fallthrough */ - case COL_ID: - case COL_PARENT: - case COL_FREQ: - case COL_PASSNO: - case COL_TID: - scols_column_set_json_type(cl, SCOLS_JSON_NUMBER); - break; - default: - if (fl & SCOLS_FL_WRAP) - scols_column_set_json_type(cl, SCOLS_JSON_ARRAY_STRING); - else - scols_column_set_json_type(cl, SCOLS_JSON_STRING); - break; - } - } + if (flags & FL_JSON) + scols_column_set_json_type(cl, get_column_json_type(id, fl, NULL)); } /* |