]> git.ipfire.org Git - thirdparty/util-linux.git/blame - misc-utils/fincore.c
fincore: add --raw and --json
[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 ). */
43#define N_PAGES_IN_WINDOW (32 * 1024)
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,
56 COL_FILE
57};
58
59static 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
65static int columns[ARRAY_SIZE(infos) * 2] = {-1};
66static size_t ncolumns;
67
68struct fincore_control {
69 const int pagesize;
70
71 struct libscols_table *tb; /* output */
72
3f91dd88 73 unsigned int bytes : 1,
9b48766f
KZ
74 noheadings : 1,
75 raw : 1,
76 json : 1;
3e37d7b7
KZ
77};
78
a921a7de 79
3e37d7b7
KZ
80static 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
94static int get_column_id(int num)
a921a7de 95{
3e37d7b7
KZ
96 assert(num >= 0);
97 assert((size_t) num < ncolumns);
98 assert(columns[num] < (int) ARRAY_SIZE(infos));
99 return columns[num];
a921a7de
MY
100}
101
3e37d7b7 102static const struct colinfo *get_column_info(int num)
a921a7de 103{
3e37d7b7 104 return &infos[ get_column_id(num) ];
a921a7de
MY
105}
106
3e37d7b7
KZ
107static 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
147static int do_mincore(struct fincore_control *ctl,
148 void *window, const size_t len,
149 const char *name,
150 off_t *count_incore)
a921a7de
MY
151{
152 static unsigned char vec[N_PAGES_IN_WINDOW];
3e37d7b7 153 int n = (len / ctl->pagesize) + ((len % ctl->pagesize)? 1: 0);
a921a7de
MY
154
155 if (mincore (window, len, vec) < 0) {
156 warn(_("failed to do mincore: %s"), name);
b57fe43c 157 return -errno;
a921a7de
MY
158 }
159
160 while (n > 0)
161 {
162 if (vec[--n] & 0x1)
163 {
164 vec[n] = 0;
165 (*count_incore)++;
166 }
167 }
168
b57fe43c 169 return 0;
a921a7de
MY
170}
171
3e37d7b7
KZ
172static int fincore_fd (struct fincore_control *ctl,
173 int fd,
174 const char *name,
a921a7de
MY
175 off_t file_size,
176 off_t *count_incore)
177{
3e37d7b7 178 size_t window_size = N_PAGES_IN_WINDOW * ctl->pagesize;
a921a7de
MY
179 off_t file_offset;
180 void *window = NULL;
b57fe43c 181 int rc = 0;
a921a7de
MY
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) {
b57fe43c 194 rc = -EINVAL;
a921a7de
MY
195 warn(_("failed to do mmap: %s"), name);
196 warned_once = 1;
197 }
198 break;
199 }
200
3e37d7b7 201 rc = do_mincore(ctl, window, len, name, count_incore);
b57fe43c
KZ
202 if (rc)
203 break;
a921a7de
MY
204
205 munmap (window, len);
206 }
207
b57fe43c 208 return rc;
a921a7de
MY
209}
210
3e37d7b7
KZ
211/*
212 * Returns: <0 on error, 0 success, 1 ignore.
213 */
214static int fincore_name(struct fincore_control *ctl,
215 const char *name,
216 struct stat *sb,
217 off_t *count_incore)
a921a7de
MY
218{
219 int fd;
b57fe43c 220 int rc = 0;
a921a7de
MY
221
222 if ((fd = open (name, O_RDONLY)) < 0) {
223 warn(_("failed to open: %s"), name);
b57fe43c 224 return 0;
a921a7de
MY
225 }
226
227 if (fstat (fd, sb) < 0) {
228 warn(_("failed to do fstat: %s"), name);
b57fe43c 229 return -errno;
a921a7de
MY
230 }
231
3e37d7b7
KZ
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);
a921a7de
MY
237
238 close (fd);
b57fe43c 239 return rc;
a921a7de
MY
240}
241
3f91dd88
KZ
242static void __attribute__((__noreturn__)) usage(FILE *out)
243{
c5cb5412
KZ
244 size_t i;
245
3f91dd88
KZ
246 fputs(USAGE_HEADER, out);
247 fprintf(out, _(" %s [options] file...\n"), program_invocation_short_name);
248
249 fputs(USAGE_OPTIONS, out);
9b48766f 250 fputs(_(" -J, --json use JSON output format\n"), out);
c5cb5412
KZ
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);
9b48766f 254 fputs(_(" -r, --raw use raw output format\n"), out);
3f91dd88
KZ
255
256 fputs(USAGE_SEPARATOR, out);
257 fputs(USAGE_HELP, out);
258 fputs(USAGE_VERSION, out);
259
c5cb5412
KZ
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
3f91dd88
KZ
265 fprintf(out, USAGE_MAN_TAIL("fincore(1)"));
266
267 exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
268}
269
a921a7de
MY
270int main(int argc, char ** argv)
271{
272 int c;
3e37d7b7 273 size_t i;
b57fe43c 274 int rc = EXIT_SUCCESS;
c5cb5412 275 char *outarg = NULL;
a921a7de 276
3e37d7b7 277 struct fincore_control ctl = {
c5cb5412 278 .pagesize = getpagesize()
3e37d7b7
KZ
279 };
280
a921a7de 281 static const struct option longopts[] = {
3f91dd88
KZ
282 { "bytes", no_argument, NULL, 'b' },
283 { "noheadings", no_argument, NULL, 'n' },
c5cb5412 284 { "output", required_argument, NULL, 'o' },
a921a7de
MY
285 { "version", no_argument, NULL, 'V' },
286 { "help", no_argument, NULL, 'h' },
9b48766f
KZ
287 { "json", no_argument, NULL, 'J' },
288 { "raw", no_argument, NULL, 'r' },
a921a7de
MY
289 { NULL, 0, NULL, 0 },
290 };
291
292 setlocale(LC_ALL, "");
293 bindtextdomain(PACKAGE, LOCALEDIR);
294 textdomain(PACKAGE);
295 atexit(close_stdout);
296
9b48766f 297 while ((c = getopt_long (argc, argv, "bno:JrVh", longopts, NULL)) != -1) {
a921a7de 298 switch (c) {
3f91dd88
KZ
299 case 'b':
300 ctl.bytes = 1;
301 break;
302 case 'n':
303 ctl.noheadings = 1;
304 break;
c5cb5412
KZ
305 case 'o':
306 outarg = optarg;
307 break;
9b48766f
KZ
308 case 'J':
309 ctl.json = 1;
310 break;
311 case 'r':
312 ctl.raw = 1;
313 break;
a921a7de
MY
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) {
b57fe43c
KZ
325 warnx(_("no file specified"));
326 errtryhelp(EXIT_FAILURE);
a921a7de
MY
327 }
328
3e37d7b7
KZ
329 if (!ncolumns) {
330 columns[ncolumns++] = COL_PAGES;
331 columns[ncolumns++] = COL_SIZE;
332 columns[ncolumns++] = COL_FILE;
333 }
334
c5cb5412
KZ
335 if (outarg && string_add_to_idarray(outarg, columns, ARRAY_SIZE(columns),
336 &ncolumns, column_name_to_id) < 0)
337 return EXIT_FAILURE;
338
3e37d7b7
KZ
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
3f91dd88 344 scols_table_enable_noheadings(ctl.tb, ctl.noheadings);
9b48766f
KZ
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");
3f91dd88 349
3e37d7b7
KZ
350 for (i = 0; i < ncolumns; i++) {
351 const struct colinfo *col = get_column_info(i);
a921a7de 352
3e37d7b7
KZ
353 if (!scols_table_new_column(ctl.tb, col->name, col->whint, col->flags))
354 err(EXIT_FAILURE, _("failed to initialize output column"));
355 }
b57fe43c 356
a921a7de
MY
357 for(; optind < argc; optind++) {
358 char *name = argv[optind];
359 struct stat sb;
360 off_t count_incore = 0;
361
3e37d7b7
KZ
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:
b57fe43c 369 rc = EXIT_FAILURE;
3e37d7b7 370 break;
a921a7de
MY
371 }
372 }
373
3e37d7b7
KZ
374 scols_print_table(ctl.tb);
375 scols_unref_table(ctl.tb);
376
b57fe43c 377 return rc;
a921a7de 378}