]> git.ipfire.org Git - thirdparty/util-linux.git/blame - misc-utils/fincore.c
fincore: add --output-all
[thirdparty/util-linux.git] / misc-utils / fincore.c
CommitLineData
a921a7de
MY
1/*
2 * fincore - count pages of file contents in core
3 *
4 * Copyright (C) 2017 Red Hat, Inc. All rights reserved.
5 * Written by Masatake YAMATO <yamato@redhat.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it would be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License 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.
20 */
21
22#include <sys/mman.h>
23#include <sys/stat.h>
24#include <unistd.h>
25#include <getopt.h>
26#include <stdio.h>
27#include <string.h>
28
29#include "c.h"
30#include "nls.h"
31#include "closestream.h"
3e37d7b7
KZ
32#include "xalloc.h"
33#include "strutils.h"
ab0a7517 34#include "blkdev.h"
3e37d7b7
KZ
35
36#include "libsmartcols.h"
a921a7de
MY
37
38/* For large files, mmap is called in iterative way.
39 Window is the unit of vma prepared in each mmap
40 calling.
41
42 Window size depends on page size.
43 e.g. 128MB on x86_64. ( = N_PAGES_IN_WINDOW * 4096 ). */
452089fa 44#define N_PAGES_IN_WINDOW ((size_t)(32 * 1024))
a921a7de 45
3e37d7b7
KZ
46
47struct colinfo {
37b2b3fa 48 const char * const name;
3e37d7b7
KZ
49 double whint;
50 int flags;
51 const char *help;
52};
53
54enum {
55 COL_PAGES,
56 COL_SIZE,
e4e8b57b
KZ
57 COL_FILE,
58 COL_RES
3e37d7b7
KZ
59};
60
37b2b3fa 61static const struct colinfo infos[] = {
0508f347 62 [COL_PAGES] = { "PAGES", 1, SCOLS_FL_RIGHT, N_("file data resident in memory in pages")},
2bb3aa36 63 [COL_RES] = { "RES", 5, SCOLS_FL_RIGHT, N_("file data resident in memory in bytes")},
3e37d7b7
KZ
64 [COL_SIZE] = { "SIZE", 5, SCOLS_FL_RIGHT, N_("size of the file")},
65 [COL_FILE] = { "FILE", 4, 0, N_("file name")},
66};
67
68static int columns[ARRAY_SIZE(infos) * 2] = {-1};
69static size_t ncolumns;
70
71struct fincore_control {
452089fa 72 const size_t pagesize;
3e37d7b7
KZ
73
74 struct libscols_table *tb; /* output */
75
3f91dd88 76 unsigned int bytes : 1,
9b48766f
KZ
77 noheadings : 1,
78 raw : 1,
79 json : 1;
3e37d7b7
KZ
80};
81
a921a7de 82
3e37d7b7
KZ
83static int column_name_to_id(const char *name, size_t namesz)
84{
85 size_t i;
86
87 for (i = 0; i < ARRAY_SIZE(infos); i++) {
88 const char *cn = infos[i].name;
89
90 if (!strncasecmp(name, cn, namesz) && !*(cn + namesz))
91 return i;
92 }
93 warnx(_("unknown column: %s"), name);
94 return -1;
95}
96
97static int get_column_id(int num)
a921a7de 98{
3e37d7b7
KZ
99 assert(num >= 0);
100 assert((size_t) num < ncolumns);
101 assert(columns[num] < (int) ARRAY_SIZE(infos));
102 return columns[num];
a921a7de
MY
103}
104
3e37d7b7 105static const struct colinfo *get_column_info(int num)
a921a7de 106{
3e37d7b7 107 return &infos[ get_column_id(num) ];
a921a7de
MY
108}
109
3e37d7b7
KZ
110static int add_output_data(struct fincore_control *ctl,
111 const char *name,
112 off_t file_size,
113 off_t count_incore)
114{
115 size_t i;
116 char *tmp;
117 struct libscols_line *ln;
118
119 assert(ctl);
120 assert(ctl->tb);
121
122 ln = scols_table_new_line(ctl->tb, NULL);
123 if (!ln)
780ce22c 124 err(EXIT_FAILURE, _("failed to allocate output line"));
3e37d7b7
KZ
125
126 for (i = 0; i < ncolumns; i++) {
b7ebf49c
KZ
127 int rc = 0;
128
3e37d7b7
KZ
129 switch(get_column_id(i)) {
130 case COL_FILE:
b7ebf49c 131 rc = scols_line_set_data(ln, i, name);
3e37d7b7
KZ
132 break;
133 case COL_PAGES:
134 xasprintf(&tmp, "%jd", (intmax_t) count_incore);
b7ebf49c 135 rc = scols_line_refer_data(ln, i, tmp);
3e37d7b7 136 break;
e4e8b57b
KZ
137 case COL_RES:
138 {
139 uintmax_t res = (uintmax_t) count_incore * ctl->pagesize;
140
141 if (ctl->bytes)
142 xasprintf(&tmp, "%ju", res);
143 else
144 tmp = size_to_human_string(SIZE_SUFFIX_1LETTER, res);
b7ebf49c 145 rc = scols_line_refer_data(ln, i, tmp);
e4e8b57b
KZ
146 break;
147 }
3e37d7b7
KZ
148 case COL_SIZE:
149 if (ctl->bytes)
150 xasprintf(&tmp, "%jd", (intmax_t) file_size);
151 else
152 tmp = size_to_human_string(SIZE_SUFFIX_1LETTER, file_size);
b7ebf49c 153 rc = scols_line_refer_data(ln, i, tmp);
3e37d7b7
KZ
154 break;
155 default:
156 return -EINVAL;
157 }
b7ebf49c
KZ
158
159 if (rc)
160 err(EXIT_FAILURE, _("failed to add output data"));
3e37d7b7
KZ
161 }
162
163 return 0;
164}
165
166static int do_mincore(struct fincore_control *ctl,
167 void *window, const size_t len,
168 const char *name,
169 off_t *count_incore)
a921a7de
MY
170{
171 static unsigned char vec[N_PAGES_IN_WINDOW];
3e37d7b7 172 int n = (len / ctl->pagesize) + ((len % ctl->pagesize)? 1: 0);
a921a7de
MY
173
174 if (mincore (window, len, vec) < 0) {
175 warn(_("failed to do mincore: %s"), name);
b57fe43c 176 return -errno;
a921a7de
MY
177 }
178
179 while (n > 0)
180 {
181 if (vec[--n] & 0x1)
182 {
183 vec[n] = 0;
184 (*count_incore)++;
185 }
186 }
187
b57fe43c 188 return 0;
a921a7de
MY
189}
190
3e37d7b7
KZ
191static int fincore_fd (struct fincore_control *ctl,
192 int fd,
193 const char *name,
a921a7de
MY
194 off_t file_size,
195 off_t *count_incore)
196{
452089fa 197 size_t window_size = N_PAGES_IN_WINDOW * ctl->pagesize;
fcfe1a5c 198 off_t file_offset, len;
b57fe43c 199 int rc = 0;
a921a7de 200
fcfe1a5c 201 for (file_offset = 0; file_offset < file_size; file_offset += len) {
57940872 202 void *window = NULL;
a921a7de
MY
203
204 len = file_size - file_offset;
ea62c152 205 if (len >= (off_t) window_size)
a921a7de
MY
206 len = window_size;
207
cf397444
TW
208 /* PROT_NONE is enough for Linux, but qemu-user wants PROT_READ */
209 window = mmap(window, len, PROT_READ, MAP_PRIVATE, fd, file_offset);
a921a7de 210 if (window == MAP_FAILED) {
374cb543
KZ
211 rc = -EINVAL;
212 warn(_("failed to do mmap: %s"), name);
a921a7de
MY
213 break;
214 }
215
3e37d7b7 216 rc = do_mincore(ctl, window, len, name, count_incore);
b57fe43c
KZ
217 if (rc)
218 break;
a921a7de
MY
219
220 munmap (window, len);
221 }
222
b57fe43c 223 return rc;
a921a7de
MY
224}
225
3e37d7b7
KZ
226/*
227 * Returns: <0 on error, 0 success, 1 ignore.
228 */
229static int fincore_name(struct fincore_control *ctl,
230 const char *name,
ab0a7517 231 unsigned long long *size,
3e37d7b7 232 off_t *count_incore)
a921a7de
MY
233{
234 int fd;
b57fe43c 235 int rc = 0;
ab0a7517 236 struct stat sb;
a921a7de
MY
237
238 if ((fd = open (name, O_RDONLY)) < 0) {
239 warn(_("failed to open: %s"), name);
cff1c113 240 return -errno;
a921a7de
MY
241 }
242
ab0a7517 243 if (fstat (fd, &sb) < 0) {
a921a7de 244 warn(_("failed to do fstat: %s"), name);
b9dc8d07 245 close (fd);
b57fe43c 246 return -errno;
a921a7de
MY
247 }
248
5a02eb13
JU
249 if (S_ISBLK(sb.st_mode)) {
250 rc = blkdev_get_size(fd, size);
251 if (rc)
ab0a7517 252 warn(_("failed ioctl to get size: %s"), name);
5a02eb13
JU
253 } else if (S_ISREG(sb.st_mode)) {
254 *size = sb.st_size;
ab0a7517
JU
255 } else {
256 rc = 1; /* ignore things like symlinks
257 * and directories*/
258 }
a921a7de 259
5a02eb13
JU
260 if (!rc) {
261 rc = fincore_fd(ctl, fd, name, *size, count_incore);
262 }
263
a921a7de 264 close (fd);
b57fe43c 265 return rc;
a921a7de
MY
266}
267
86be6a32 268static void __attribute__((__noreturn__)) usage(void)
3f91dd88 269{
86be6a32 270 FILE *out = stdout;
c5cb5412
KZ
271 size_t i;
272
3f91dd88
KZ
273 fputs(USAGE_HEADER, out);
274 fprintf(out, _(" %s [options] file...\n"), program_invocation_short_name);
275
276 fputs(USAGE_OPTIONS, out);
9b48766f 277 fputs(_(" -J, --json use JSON output format\n"), out);
c5cb5412
KZ
278 fputs(_(" -b, --bytes print sizes in bytes rather than in human readable format\n"), out);
279 fputs(_(" -n, --noheadings don't print headings\n"), out);
280 fputs(_(" -o, --output <list> output columns\n"), out);
1f583cb9 281 fputs(_(" --output-all output all columns\n"), out);
9b48766f 282 fputs(_(" -r, --raw use raw output format\n"), out);
3f91dd88
KZ
283
284 fputs(USAGE_SEPARATOR, out);
f45f3ec3 285 printf(USAGE_HELP_OPTIONS(23));
3f91dd88 286
c3a4cfc5 287 fprintf(out, USAGE_COLUMNS);
c5cb5412
KZ
288
289 for (i = 0; i < ARRAY_SIZE(infos); i++)
290 fprintf(out, " %11s %s\n", infos[i].name, _(infos[i].help));
291
f45f3ec3 292 printf(USAGE_MAN_TAIL("fincore(1)"));
3f91dd88 293
86be6a32 294 exit(EXIT_SUCCESS);
3f91dd88
KZ
295}
296
a921a7de
MY
297int main(int argc, char ** argv)
298{
299 int c;
3e37d7b7 300 size_t i;
b57fe43c 301 int rc = EXIT_SUCCESS;
c5cb5412 302 char *outarg = NULL;
a921a7de 303
3e37d7b7 304 struct fincore_control ctl = {
c5cb5412 305 .pagesize = getpagesize()
3e37d7b7
KZ
306 };
307
1f583cb9
TW
308 enum {
309 OPT_OUTPUT_ALL = CHAR_MAX + 1
310 };
a921a7de 311 static const struct option longopts[] = {
3f91dd88
KZ
312 { "bytes", no_argument, NULL, 'b' },
313 { "noheadings", no_argument, NULL, 'n' },
c5cb5412 314 { "output", required_argument, NULL, 'o' },
1f583cb9 315 { "output-all", no_argument, NULL, OPT_OUTPUT_ALL },
a921a7de
MY
316 { "version", no_argument, NULL, 'V' },
317 { "help", no_argument, NULL, 'h' },
9b48766f
KZ
318 { "json", no_argument, NULL, 'J' },
319 { "raw", no_argument, NULL, 'r' },
a921a7de
MY
320 { NULL, 0, NULL, 0 },
321 };
322
323 setlocale(LC_ALL, "");
324 bindtextdomain(PACKAGE, LOCALEDIR);
325 textdomain(PACKAGE);
2c308875 326 close_stdout_atexit();
a921a7de 327
9b48766f 328 while ((c = getopt_long (argc, argv, "bno:JrVh", longopts, NULL)) != -1) {
a921a7de 329 switch (c) {
3f91dd88
KZ
330 case 'b':
331 ctl.bytes = 1;
332 break;
333 case 'n':
334 ctl.noheadings = 1;
335 break;
c5cb5412
KZ
336 case 'o':
337 outarg = optarg;
338 break;
1f583cb9
TW
339 case OPT_OUTPUT_ALL:
340 for (ncolumns = 0; ncolumns < ARRAY_SIZE(infos); ncolumns++)
341 columns[ncolumns] = ncolumns;
342 break;
9b48766f
KZ
343 case 'J':
344 ctl.json = 1;
345 break;
346 case 'r':
347 ctl.raw = 1;
348 break;
a921a7de 349 case 'V':
2c308875 350 print_version(EXIT_SUCCESS);
a921a7de 351 case 'h':
86be6a32 352 usage();
a921a7de
MY
353 default:
354 errtryhelp(EXIT_FAILURE);
355 }
356 }
357
358 if (optind == argc) {
b57fe43c
KZ
359 warnx(_("no file specified"));
360 errtryhelp(EXIT_FAILURE);
a921a7de
MY
361 }
362
3e37d7b7 363 if (!ncolumns) {
e4e8b57b 364 columns[ncolumns++] = COL_RES;
3e37d7b7
KZ
365 columns[ncolumns++] = COL_PAGES;
366 columns[ncolumns++] = COL_SIZE;
367 columns[ncolumns++] = COL_FILE;
368 }
369
c5cb5412
KZ
370 if (outarg && string_add_to_idarray(outarg, columns, ARRAY_SIZE(columns),
371 &ncolumns, column_name_to_id) < 0)
372 return EXIT_FAILURE;
373
3e37d7b7
KZ
374 scols_init_debug(0);
375 ctl.tb = scols_new_table();
376 if (!ctl.tb)
780ce22c 377 err(EXIT_FAILURE, _("failed to allocate output table"));
3e37d7b7 378
3f91dd88 379 scols_table_enable_noheadings(ctl.tb, ctl.noheadings);
9b48766f
KZ
380 scols_table_enable_raw(ctl.tb, ctl.raw);
381 scols_table_enable_json(ctl.tb, ctl.json);
382 if (ctl.json)
383 scols_table_set_name(ctl.tb, "fincore");
3f91dd88 384
3e37d7b7
KZ
385 for (i = 0; i < ncolumns; i++) {
386 const struct colinfo *col = get_column_info(i);
8945d9dc 387 struct libscols_column *cl;
a921a7de 388
8945d9dc
KZ
389 cl = scols_table_new_column(ctl.tb, col->name, col->whint, col->flags);
390 if (!cl)
780ce22c 391 err(EXIT_FAILURE, _("failed to allocate output column"));
8945d9dc
KZ
392
393 if (ctl.json) {
394 int id = get_column_id(i);
395
396 switch (id) {
397 case COL_FILE:
398 scols_column_set_json_type(cl, SCOLS_JSON_STRING);
399 break;
400 case COL_SIZE:
401 case COL_RES:
402 if (!ctl.bytes)
403 break;
404 /* fallthrough */
405 default:
406 scols_column_set_json_type(cl, SCOLS_JSON_NUMBER);
407 break;
408 }
409 }
3e37d7b7 410 }
b57fe43c 411
a921a7de
MY
412 for(; optind < argc; optind++) {
413 char *name = argv[optind];
a921a7de 414 off_t count_incore = 0;
ab0a7517 415 unsigned long long size = 0;
a921a7de 416
ab0a7517 417 switch (fincore_name(&ctl, name, &size, &count_incore)) {
3e37d7b7 418 case 0:
ab0a7517 419 add_output_data(&ctl, name, size, count_incore);
3e37d7b7
KZ
420 break;
421 case 1:
422 break; /* ignore */
423 default:
b57fe43c 424 rc = EXIT_FAILURE;
3e37d7b7 425 break;
a921a7de
MY
426 }
427 }
428
3e37d7b7
KZ
429 scols_print_table(ctl.tb);
430 scols_unref_table(ctl.tb);
431
b57fe43c 432 return rc;
a921a7de 433}