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