]> git.ipfire.org Git - thirdparty/util-linux.git/blame - misc-utils/fincore.c
misc: cosmetics, remove argument from usage(FILE*)
[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;
a921a7de
MY
197 off_t file_offset;
198 void *window = NULL;
b57fe43c 199 int rc = 0;
a921a7de
MY
200 int warned_once = 0;
201
202 for (file_offset = 0; file_offset < file_size; file_offset += window_size) {
203 size_t len;
204
205 len = file_size - file_offset;
206 if (len >= window_size)
207 len = window_size;
208
209 window = mmap(window, len, PROT_NONE, MAP_PRIVATE, fd, file_offset);
210 if (window == MAP_FAILED) {
211 if (!warned_once) {
b57fe43c 212 rc = -EINVAL;
a921a7de
MY
213 warn(_("failed to do mmap: %s"), name);
214 warned_once = 1;
215 }
216 break;
217 }
218
3e37d7b7 219 rc = do_mincore(ctl, window, len, name, count_incore);
b57fe43c
KZ
220 if (rc)
221 break;
a921a7de
MY
222
223 munmap (window, len);
224 }
225
b57fe43c 226 return rc;
a921a7de
MY
227}
228
3e37d7b7
KZ
229/*
230 * Returns: <0 on error, 0 success, 1 ignore.
231 */
232static int fincore_name(struct fincore_control *ctl,
233 const char *name,
234 struct stat *sb,
235 off_t *count_incore)
a921a7de
MY
236{
237 int fd;
b57fe43c 238 int rc = 0;
a921a7de
MY
239
240 if ((fd = open (name, O_RDONLY)) < 0) {
241 warn(_("failed to open: %s"), name);
cff1c113 242 return -errno;
a921a7de
MY
243 }
244
245 if (fstat (fd, sb) < 0) {
246 warn(_("failed to do fstat: %s"), name);
b9dc8d07 247 close (fd);
b57fe43c 248 return -errno;
a921a7de
MY
249 }
250
3e37d7b7
KZ
251 if (S_ISDIR(sb->st_mode))
252 rc = 1; /* ignore */
253
254 else if (sb->st_size)
255 rc = fincore_fd(ctl, fd, name, sb->st_size, count_incore);
a921a7de
MY
256
257 close (fd);
b57fe43c 258 return rc;
a921a7de
MY
259}
260
86be6a32 261static void __attribute__((__noreturn__)) usage(void)
3f91dd88 262{
86be6a32 263 FILE *out = stdout;
c5cb5412
KZ
264 size_t i;
265
3f91dd88
KZ
266 fputs(USAGE_HEADER, out);
267 fprintf(out, _(" %s [options] file...\n"), program_invocation_short_name);
268
269 fputs(USAGE_OPTIONS, out);
9b48766f 270 fputs(_(" -J, --json use JSON output format\n"), out);
c5cb5412
KZ
271 fputs(_(" -b, --bytes print sizes in bytes rather than in human readable format\n"), out);
272 fputs(_(" -n, --noheadings don't print headings\n"), out);
273 fputs(_(" -o, --output <list> output columns\n"), out);
9b48766f 274 fputs(_(" -r, --raw use raw output format\n"), out);
3f91dd88
KZ
275
276 fputs(USAGE_SEPARATOR, out);
277 fputs(USAGE_HELP, out);
278 fputs(USAGE_VERSION, out);
279
c3a4cfc5 280 fprintf(out, USAGE_COLUMNS);
c5cb5412
KZ
281
282 for (i = 0; i < ARRAY_SIZE(infos); i++)
283 fprintf(out, " %11s %s\n", infos[i].name, _(infos[i].help));
284
3f91dd88
KZ
285 fprintf(out, USAGE_MAN_TAIL("fincore(1)"));
286
86be6a32 287 exit(EXIT_SUCCESS);
3f91dd88
KZ
288}
289
a921a7de
MY
290int main(int argc, char ** argv)
291{
292 int c;
3e37d7b7 293 size_t i;
b57fe43c 294 int rc = EXIT_SUCCESS;
c5cb5412 295 char *outarg = NULL;
a921a7de 296
3e37d7b7 297 struct fincore_control ctl = {
c5cb5412 298 .pagesize = getpagesize()
3e37d7b7
KZ
299 };
300
a921a7de 301 static const struct option longopts[] = {
3f91dd88
KZ
302 { "bytes", no_argument, NULL, 'b' },
303 { "noheadings", no_argument, NULL, 'n' },
c5cb5412 304 { "output", required_argument, NULL, 'o' },
a921a7de
MY
305 { "version", no_argument, NULL, 'V' },
306 { "help", no_argument, NULL, 'h' },
9b48766f
KZ
307 { "json", no_argument, NULL, 'J' },
308 { "raw", no_argument, NULL, 'r' },
a921a7de
MY
309 { NULL, 0, NULL, 0 },
310 };
311
312 setlocale(LC_ALL, "");
313 bindtextdomain(PACKAGE, LOCALEDIR);
314 textdomain(PACKAGE);
315 atexit(close_stdout);
316
9b48766f 317 while ((c = getopt_long (argc, argv, "bno:JrVh", longopts, NULL)) != -1) {
a921a7de 318 switch (c) {
3f91dd88
KZ
319 case 'b':
320 ctl.bytes = 1;
321 break;
322 case 'n':
323 ctl.noheadings = 1;
324 break;
c5cb5412
KZ
325 case 'o':
326 outarg = optarg;
327 break;
9b48766f
KZ
328 case 'J':
329 ctl.json = 1;
330 break;
331 case 'r':
332 ctl.raw = 1;
333 break;
a921a7de
MY
334 case 'V':
335 printf(UTIL_LINUX_VERSION);
336 return EXIT_SUCCESS;
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);
a921a7de 373
3e37d7b7 374 if (!scols_table_new_column(ctl.tb, col->name, col->whint, col->flags))
780ce22c 375 err(EXIT_FAILURE, _("failed to allocate output column"));
3e37d7b7 376 }
b57fe43c 377
a921a7de
MY
378 for(; optind < argc; optind++) {
379 char *name = argv[optind];
380 struct stat sb;
381 off_t count_incore = 0;
382
3e37d7b7
KZ
383 switch (fincore_name(&ctl, name, &sb, &count_incore)) {
384 case 0:
385 add_output_data(&ctl, name, sb.st_size, count_incore);
386 break;
387 case 1:
388 break; /* ignore */
389 default:
b57fe43c 390 rc = EXIT_FAILURE;
3e37d7b7 391 break;
a921a7de
MY
392 }
393 }
394
3e37d7b7
KZ
395 scols_print_table(ctl.tb);
396 scols_unref_table(ctl.tb);
397
b57fe43c 398 return rc;
a921a7de 399}