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