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