]> git.ipfire.org Git - thirdparty/util-linux.git/blob - misc-utils/fincore.c
fincore: add --raw and --json
[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 (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 };
58
59 static struct colinfo infos[] = {
60 [COL_PAGES] = { "PAGES", 1, SCOLS_FL_RIGHT, N_("number of memory page")},
61 [COL_SIZE] = { "SIZE", 5, SCOLS_FL_RIGHT, N_("size of the file")},
62 [COL_FILE] = { "FILE", 4, 0, N_("file name")},
63 };
64
65 static int columns[ARRAY_SIZE(infos) * 2] = {-1};
66 static size_t ncolumns;
67
68 struct fincore_control {
69 const int pagesize;
70
71 struct libscols_table *tb; /* output */
72
73 unsigned int bytes : 1,
74 noheadings : 1,
75 raw : 1,
76 json : 1;
77 };
78
79
80 static int column_name_to_id(const char *name, size_t namesz)
81 {
82 size_t i;
83
84 for (i = 0; i < ARRAY_SIZE(infos); i++) {
85 const char *cn = infos[i].name;
86
87 if (!strncasecmp(name, cn, namesz) && !*(cn + namesz))
88 return i;
89 }
90 warnx(_("unknown column: %s"), name);
91 return -1;
92 }
93
94 static int get_column_id(int num)
95 {
96 assert(num >= 0);
97 assert((size_t) num < ncolumns);
98 assert(columns[num] < (int) ARRAY_SIZE(infos));
99 return columns[num];
100 }
101
102 static const struct colinfo *get_column_info(int num)
103 {
104 return &infos[ get_column_id(num) ];
105 }
106
107 static int add_output_data(struct fincore_control *ctl,
108 const char *name,
109 off_t file_size,
110 off_t count_incore)
111 {
112 size_t i;
113 char *tmp;
114 struct libscols_line *ln;
115
116 assert(ctl);
117 assert(ctl->tb);
118
119 ln = scols_table_new_line(ctl->tb, NULL);
120 if (!ln)
121 err(EXIT_FAILURE, _("failed to initialize output line"));
122
123 for (i = 0; i < ncolumns; i++) {
124 switch(get_column_id(i)) {
125 case COL_FILE:
126 scols_line_set_data(ln, i, name);
127 break;
128 case COL_PAGES:
129 xasprintf(&tmp, "%jd", (intmax_t) count_incore);
130 scols_line_refer_data(ln, i, tmp);
131 break;
132 case COL_SIZE:
133 if (ctl->bytes)
134 xasprintf(&tmp, "%jd", (intmax_t) file_size);
135 else
136 tmp = size_to_human_string(SIZE_SUFFIX_1LETTER, file_size);
137 scols_line_refer_data(ln, i, tmp);
138 break;
139 default:
140 return -EINVAL;
141 }
142 }
143
144 return 0;
145 }
146
147 static int do_mincore(struct fincore_control *ctl,
148 void *window, const size_t len,
149 const char *name,
150 off_t *count_incore)
151 {
152 static unsigned char vec[N_PAGES_IN_WINDOW];
153 int n = (len / ctl->pagesize) + ((len % ctl->pagesize)? 1: 0);
154
155 if (mincore (window, len, vec) < 0) {
156 warn(_("failed to do mincore: %s"), name);
157 return -errno;
158 }
159
160 while (n > 0)
161 {
162 if (vec[--n] & 0x1)
163 {
164 vec[n] = 0;
165 (*count_incore)++;
166 }
167 }
168
169 return 0;
170 }
171
172 static int fincore_fd (struct fincore_control *ctl,
173 int fd,
174 const char *name,
175 off_t file_size,
176 off_t *count_incore)
177 {
178 size_t window_size = N_PAGES_IN_WINDOW * ctl->pagesize;
179 off_t file_offset;
180 void *window = NULL;
181 int rc = 0;
182 int warned_once = 0;
183
184 for (file_offset = 0; file_offset < file_size; file_offset += window_size) {
185 size_t len;
186
187 len = file_size - file_offset;
188 if (len >= window_size)
189 len = window_size;
190
191 window = mmap(window, len, PROT_NONE, MAP_PRIVATE, fd, file_offset);
192 if (window == MAP_FAILED) {
193 if (!warned_once) {
194 rc = -EINVAL;
195 warn(_("failed to do mmap: %s"), name);
196 warned_once = 1;
197 }
198 break;
199 }
200
201 rc = do_mincore(ctl, window, len, name, count_incore);
202 if (rc)
203 break;
204
205 munmap (window, len);
206 }
207
208 return rc;
209 }
210
211 /*
212 * Returns: <0 on error, 0 success, 1 ignore.
213 */
214 static int fincore_name(struct fincore_control *ctl,
215 const char *name,
216 struct stat *sb,
217 off_t *count_incore)
218 {
219 int fd;
220 int rc = 0;
221
222 if ((fd = open (name, O_RDONLY)) < 0) {
223 warn(_("failed to open: %s"), name);
224 return 0;
225 }
226
227 if (fstat (fd, sb) < 0) {
228 warn(_("failed to do fstat: %s"), name);
229 return -errno;
230 }
231
232 if (S_ISDIR(sb->st_mode))
233 rc = 1; /* ignore */
234
235 else if (sb->st_size)
236 rc = fincore_fd(ctl, fd, name, sb->st_size, count_incore);
237
238 close (fd);
239 return rc;
240 }
241
242 static void __attribute__((__noreturn__)) usage(FILE *out)
243 {
244 size_t i;
245
246 fputs(USAGE_HEADER, out);
247 fprintf(out, _(" %s [options] file...\n"), program_invocation_short_name);
248
249 fputs(USAGE_OPTIONS, out);
250 fputs(_(" -J, --json use JSON output format\n"), out);
251 fputs(_(" -b, --bytes print sizes in bytes rather than in human readable format\n"), out);
252 fputs(_(" -n, --noheadings don't print headings\n"), out);
253 fputs(_(" -o, --output <list> output columns\n"), out);
254 fputs(_(" -r, --raw use raw output format\n"), out);
255
256 fputs(USAGE_SEPARATOR, out);
257 fputs(USAGE_HELP, out);
258 fputs(USAGE_VERSION, out);
259
260 fprintf(out, _("\nAvailable columns (for --output):\n"));
261
262 for (i = 0; i < ARRAY_SIZE(infos); i++)
263 fprintf(out, " %11s %s\n", infos[i].name, _(infos[i].help));
264
265 fprintf(out, USAGE_MAN_TAIL("fincore(1)"));
266
267 exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
268 }
269
270 int main(int argc, char ** argv)
271 {
272 int c;
273 size_t i;
274 int rc = EXIT_SUCCESS;
275 char *outarg = NULL;
276
277 struct fincore_control ctl = {
278 .pagesize = getpagesize()
279 };
280
281 static const struct option longopts[] = {
282 { "bytes", no_argument, NULL, 'b' },
283 { "noheadings", no_argument, NULL, 'n' },
284 { "output", required_argument, NULL, 'o' },
285 { "version", no_argument, NULL, 'V' },
286 { "help", no_argument, NULL, 'h' },
287 { "json", no_argument, NULL, 'J' },
288 { "raw", no_argument, NULL, 'r' },
289 { NULL, 0, NULL, 0 },
290 };
291
292 setlocale(LC_ALL, "");
293 bindtextdomain(PACKAGE, LOCALEDIR);
294 textdomain(PACKAGE);
295 atexit(close_stdout);
296
297 while ((c = getopt_long (argc, argv, "bno:JrVh", longopts, NULL)) != -1) {
298 switch (c) {
299 case 'b':
300 ctl.bytes = 1;
301 break;
302 case 'n':
303 ctl.noheadings = 1;
304 break;
305 case 'o':
306 outarg = optarg;
307 break;
308 case 'J':
309 ctl.json = 1;
310 break;
311 case 'r':
312 ctl.raw = 1;
313 break;
314 case 'V':
315 printf(UTIL_LINUX_VERSION);
316 return EXIT_SUCCESS;
317 case 'h':
318 usage(stdout);
319 default:
320 errtryhelp(EXIT_FAILURE);
321 }
322 }
323
324 if (optind == argc) {
325 warnx(_("no file specified"));
326 errtryhelp(EXIT_FAILURE);
327 }
328
329 if (!ncolumns) {
330 columns[ncolumns++] = COL_PAGES;
331 columns[ncolumns++] = COL_SIZE;
332 columns[ncolumns++] = COL_FILE;
333 }
334
335 if (outarg && string_add_to_idarray(outarg, columns, ARRAY_SIZE(columns),
336 &ncolumns, column_name_to_id) < 0)
337 return EXIT_FAILURE;
338
339 scols_init_debug(0);
340 ctl.tb = scols_new_table();
341 if (!ctl.tb)
342 err(EXIT_FAILURE, _("failed to create output table"));
343
344 scols_table_enable_noheadings(ctl.tb, ctl.noheadings);
345 scols_table_enable_raw(ctl.tb, ctl.raw);
346 scols_table_enable_json(ctl.tb, ctl.json);
347 if (ctl.json)
348 scols_table_set_name(ctl.tb, "fincore");
349
350 for (i = 0; i < ncolumns; i++) {
351 const struct colinfo *col = get_column_info(i);
352
353 if (!scols_table_new_column(ctl.tb, col->name, col->whint, col->flags))
354 err(EXIT_FAILURE, _("failed to initialize output column"));
355 }
356
357 for(; optind < argc; optind++) {
358 char *name = argv[optind];
359 struct stat sb;
360 off_t count_incore = 0;
361
362 switch (fincore_name(&ctl, name, &sb, &count_incore)) {
363 case 0:
364 add_output_data(&ctl, name, sb.st_size, count_incore);
365 break;
366 case 1:
367 break; /* ignore */
368 default:
369 rc = EXIT_FAILURE;
370 break;
371 }
372 }
373
374 scols_print_table(ctl.tb);
375 scols_unref_table(ctl.tb);
376
377 return rc;
378 }