]> git.ipfire.org Git - thirdparty/util-linux.git/blob - misc-utils/fincore.c
6bad2c04012128fcd2a96920d50b49cffc7a91d7
[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 int warned_once = 0;
200
201 for (file_offset = 0; file_offset < file_size; file_offset += len) {
202 void *window = NULL;
203
204 len = file_size - file_offset;
205 if (len >= (off_t) window_size)
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) {
211 rc = -EINVAL;
212 warn(_("failed to do mmap: %s"), name);
213 warned_once = 1;
214 }
215 break;
216 }
217
218 rc = do_mincore(ctl, window, len, name, count_incore);
219 if (rc)
220 break;
221
222 munmap (window, len);
223 }
224
225 return rc;
226 }
227
228 /*
229 * Returns: <0 on error, 0 success, 1 ignore.
230 */
231 static int fincore_name(struct fincore_control *ctl,
232 const char *name,
233 struct stat *sb,
234 off_t *count_incore)
235 {
236 int fd;
237 int rc = 0;
238
239 if ((fd = open (name, O_RDONLY)) < 0) {
240 warn(_("failed to open: %s"), name);
241 return -errno;
242 }
243
244 if (fstat (fd, sb) < 0) {
245 warn(_("failed to do fstat: %s"), name);
246 close (fd);
247 return -errno;
248 }
249
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);
255
256 close (fd);
257 return rc;
258 }
259
260 static void __attribute__((__noreturn__)) usage(void)
261 {
262 FILE *out = stdout;
263 size_t i;
264
265 fputs(USAGE_HEADER, out);
266 fprintf(out, _(" %s [options] file...\n"), program_invocation_short_name);
267
268 fputs(USAGE_OPTIONS, out);
269 fputs(_(" -J, --json use JSON output format\n"), out);
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);
273 fputs(_(" -r, --raw use raw output format\n"), out);
274
275 fputs(USAGE_SEPARATOR, out);
276 printf(USAGE_HELP_OPTIONS(23));
277
278 fprintf(out, USAGE_COLUMNS);
279
280 for (i = 0; i < ARRAY_SIZE(infos); i++)
281 fprintf(out, " %11s %s\n", infos[i].name, _(infos[i].help));
282
283 printf(USAGE_MAN_TAIL("fincore(1)"));
284
285 exit(EXIT_SUCCESS);
286 }
287
288 int main(int argc, char ** argv)
289 {
290 int c;
291 size_t i;
292 int rc = EXIT_SUCCESS;
293 char *outarg = NULL;
294
295 struct fincore_control ctl = {
296 .pagesize = getpagesize()
297 };
298
299 static const struct option longopts[] = {
300 { "bytes", no_argument, NULL, 'b' },
301 { "noheadings", no_argument, NULL, 'n' },
302 { "output", required_argument, NULL, 'o' },
303 { "version", no_argument, NULL, 'V' },
304 { "help", no_argument, NULL, 'h' },
305 { "json", no_argument, NULL, 'J' },
306 { "raw", no_argument, NULL, 'r' },
307 { NULL, 0, NULL, 0 },
308 };
309
310 setlocale(LC_ALL, "");
311 bindtextdomain(PACKAGE, LOCALEDIR);
312 textdomain(PACKAGE);
313 atexit(close_stdout);
314
315 while ((c = getopt_long (argc, argv, "bno:JrVh", longopts, NULL)) != -1) {
316 switch (c) {
317 case 'b':
318 ctl.bytes = 1;
319 break;
320 case 'n':
321 ctl.noheadings = 1;
322 break;
323 case 'o':
324 outarg = optarg;
325 break;
326 case 'J':
327 ctl.json = 1;
328 break;
329 case 'r':
330 ctl.raw = 1;
331 break;
332 case 'V':
333 printf(UTIL_LINUX_VERSION);
334 return EXIT_SUCCESS;
335 case 'h':
336 usage();
337 default:
338 errtryhelp(EXIT_FAILURE);
339 }
340 }
341
342 if (optind == argc) {
343 warnx(_("no file specified"));
344 errtryhelp(EXIT_FAILURE);
345 }
346
347 if (!ncolumns) {
348 columns[ncolumns++] = COL_RES;
349 columns[ncolumns++] = COL_PAGES;
350 columns[ncolumns++] = COL_SIZE;
351 columns[ncolumns++] = COL_FILE;
352 }
353
354 if (outarg && string_add_to_idarray(outarg, columns, ARRAY_SIZE(columns),
355 &ncolumns, column_name_to_id) < 0)
356 return EXIT_FAILURE;
357
358 scols_init_debug(0);
359 ctl.tb = scols_new_table();
360 if (!ctl.tb)
361 err(EXIT_FAILURE, _("failed to allocate output table"));
362
363 scols_table_enable_noheadings(ctl.tb, ctl.noheadings);
364 scols_table_enable_raw(ctl.tb, ctl.raw);
365 scols_table_enable_json(ctl.tb, ctl.json);
366 if (ctl.json)
367 scols_table_set_name(ctl.tb, "fincore");
368
369 for (i = 0; i < ncolumns; i++) {
370 const struct colinfo *col = get_column_info(i);
371 struct libscols_column *cl;
372
373 cl = scols_table_new_column(ctl.tb, col->name, col->whint, col->flags);
374 if (!cl)
375 err(EXIT_FAILURE, _("failed to allocate output column"));
376
377 if (ctl.json) {
378 int id = get_column_id(i);
379
380 switch (id) {
381 case COL_FILE:
382 scols_column_set_json_type(cl, SCOLS_JSON_STRING);
383 break;
384 case COL_SIZE:
385 case COL_RES:
386 if (!ctl.bytes)
387 break;
388 /* fallthrough */
389 default:
390 scols_column_set_json_type(cl, SCOLS_JSON_NUMBER);
391 break;
392 }
393 }
394 }
395
396 for(; optind < argc; optind++) {
397 char *name = argv[optind];
398 struct stat sb;
399 off_t count_incore = 0;
400
401 switch (fincore_name(&ctl, name, &sb, &count_incore)) {
402 case 0:
403 add_output_data(&ctl, name, sb.st_size, count_incore);
404 break;
405 case 1:
406 break; /* ignore */
407 default:
408 rc = EXIT_FAILURE;
409 break;
410 }
411 }
412
413 scols_print_table(ctl.tb);
414 scols_unref_table(ctl.tb);
415
416 return rc;
417 }