]> git.ipfire.org Git - thirdparty/util-linux.git/blame - misc-utils/fincore.c
Support device files.
[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
ab0a7517
JU
249 if (S_ISBLK(sb.st_mode) || S_ISREG(sb.st_mode)) {
250 if (blkdev_get_size(fd, size) == 0)
251 rc = fincore_fd(ctl, fd, name, *size, count_incore);
252 else
253 warn(_("failed ioctl to get size: %s"), name);
254 } else {
255 rc = 1; /* ignore things like symlinks
256 * and directories*/
257 }
a921a7de
MY
258
259 close (fd);
b57fe43c 260 return rc;
a921a7de
MY
261}
262
86be6a32 263static void __attribute__((__noreturn__)) usage(void)
3f91dd88 264{
86be6a32 265 FILE *out = stdout;
c5cb5412
KZ
266 size_t i;
267
3f91dd88
KZ
268 fputs(USAGE_HEADER, out);
269 fprintf(out, _(" %s [options] file...\n"), program_invocation_short_name);
270
271 fputs(USAGE_OPTIONS, out);
9b48766f 272 fputs(_(" -J, --json use JSON output format\n"), out);
c5cb5412
KZ
273 fputs(_(" -b, --bytes print sizes in bytes rather than in human readable format\n"), out);
274 fputs(_(" -n, --noheadings don't print headings\n"), out);
275 fputs(_(" -o, --output <list> output columns\n"), out);
9b48766f 276 fputs(_(" -r, --raw use raw output format\n"), out);
3f91dd88
KZ
277
278 fputs(USAGE_SEPARATOR, out);
f45f3ec3 279 printf(USAGE_HELP_OPTIONS(23));
3f91dd88 280
c3a4cfc5 281 fprintf(out, USAGE_COLUMNS);
c5cb5412
KZ
282
283 for (i = 0; i < ARRAY_SIZE(infos); i++)
284 fprintf(out, " %11s %s\n", infos[i].name, _(infos[i].help));
285
f45f3ec3 286 printf(USAGE_MAN_TAIL("fincore(1)"));
3f91dd88 287
86be6a32 288 exit(EXIT_SUCCESS);
3f91dd88
KZ
289}
290
a921a7de
MY
291int main(int argc, char ** argv)
292{
293 int c;
3e37d7b7 294 size_t i;
b57fe43c 295 int rc = EXIT_SUCCESS;
c5cb5412 296 char *outarg = NULL;
a921a7de 297
3e37d7b7 298 struct fincore_control ctl = {
c5cb5412 299 .pagesize = getpagesize()
3e37d7b7
KZ
300 };
301
a921a7de 302 static const struct option longopts[] = {
3f91dd88
KZ
303 { "bytes", no_argument, NULL, 'b' },
304 { "noheadings", no_argument, NULL, 'n' },
c5cb5412 305 { "output", required_argument, NULL, 'o' },
a921a7de
MY
306 { "version", no_argument, NULL, 'V' },
307 { "help", no_argument, NULL, 'h' },
9b48766f
KZ
308 { "json", no_argument, NULL, 'J' },
309 { "raw", no_argument, NULL, 'r' },
a921a7de
MY
310 { NULL, 0, NULL, 0 },
311 };
312
313 setlocale(LC_ALL, "");
314 bindtextdomain(PACKAGE, LOCALEDIR);
315 textdomain(PACKAGE);
2c308875 316 close_stdout_atexit();
a921a7de 317
9b48766f 318 while ((c = getopt_long (argc, argv, "bno:JrVh", longopts, NULL)) != -1) {
a921a7de 319 switch (c) {
3f91dd88
KZ
320 case 'b':
321 ctl.bytes = 1;
322 break;
323 case 'n':
324 ctl.noheadings = 1;
325 break;
c5cb5412
KZ
326 case 'o':
327 outarg = optarg;
328 break;
9b48766f
KZ
329 case 'J':
330 ctl.json = 1;
331 break;
332 case 'r':
333 ctl.raw = 1;
334 break;
a921a7de 335 case 'V':
2c308875 336 print_version(EXIT_SUCCESS);
a921a7de 337 case 'h':
86be6a32 338 usage();
a921a7de
MY
339 default:
340 errtryhelp(EXIT_FAILURE);
341 }
342 }
343
344 if (optind == argc) {
b57fe43c
KZ
345 warnx(_("no file specified"));
346 errtryhelp(EXIT_FAILURE);
a921a7de
MY
347 }
348
3e37d7b7 349 if (!ncolumns) {
e4e8b57b 350 columns[ncolumns++] = COL_RES;
3e37d7b7
KZ
351 columns[ncolumns++] = COL_PAGES;
352 columns[ncolumns++] = COL_SIZE;
353 columns[ncolumns++] = COL_FILE;
354 }
355
c5cb5412
KZ
356 if (outarg && string_add_to_idarray(outarg, columns, ARRAY_SIZE(columns),
357 &ncolumns, column_name_to_id) < 0)
358 return EXIT_FAILURE;
359
3e37d7b7
KZ
360 scols_init_debug(0);
361 ctl.tb = scols_new_table();
362 if (!ctl.tb)
780ce22c 363 err(EXIT_FAILURE, _("failed to allocate output table"));
3e37d7b7 364
3f91dd88 365 scols_table_enable_noheadings(ctl.tb, ctl.noheadings);
9b48766f
KZ
366 scols_table_enable_raw(ctl.tb, ctl.raw);
367 scols_table_enable_json(ctl.tb, ctl.json);
368 if (ctl.json)
369 scols_table_set_name(ctl.tb, "fincore");
3f91dd88 370
3e37d7b7
KZ
371 for (i = 0; i < ncolumns; i++) {
372 const struct colinfo *col = get_column_info(i);
8945d9dc 373 struct libscols_column *cl;
a921a7de 374
8945d9dc
KZ
375 cl = scols_table_new_column(ctl.tb, col->name, col->whint, col->flags);
376 if (!cl)
780ce22c 377 err(EXIT_FAILURE, _("failed to allocate output column"));
8945d9dc
KZ
378
379 if (ctl.json) {
380 int id = get_column_id(i);
381
382 switch (id) {
383 case COL_FILE:
384 scols_column_set_json_type(cl, SCOLS_JSON_STRING);
385 break;
386 case COL_SIZE:
387 case COL_RES:
388 if (!ctl.bytes)
389 break;
390 /* fallthrough */
391 default:
392 scols_column_set_json_type(cl, SCOLS_JSON_NUMBER);
393 break;
394 }
395 }
3e37d7b7 396 }
b57fe43c 397
a921a7de
MY
398 for(; optind < argc; optind++) {
399 char *name = argv[optind];
a921a7de 400 off_t count_incore = 0;
ab0a7517 401 unsigned long long size = 0;
a921a7de 402
ab0a7517 403 switch (fincore_name(&ctl, name, &size, &count_incore)) {
3e37d7b7 404 case 0:
ab0a7517 405 add_output_data(&ctl, name, size, count_incore);
3e37d7b7
KZ
406 break;
407 case 1:
408 break; /* ignore */
409 default:
b57fe43c 410 rc = EXIT_FAILURE;
3e37d7b7 411 break;
a921a7de
MY
412 }
413 }
414
3e37d7b7
KZ
415 scols_print_table(ctl.tb);
416 scols_unref_table(ctl.tb);
417
b57fe43c 418 return rc;
a921a7de 419}