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