]> git.ipfire.org Git - thirdparty/util-linux.git/blame - misc-utils/fincore.c
libsmartcols: (sample) cleanup line separator usage
[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 ). */
452089fa 43#define N_PAGES_IN_WINDOW ((size_t)(32 * 1024))
a921a7de 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[] = {
0508f347
YC
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")},
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 {
452089fa 71 const size_t pagesize;
3e37d7b7
KZ
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)
780ce22c 123 err(EXIT_FAILURE, _("failed to allocate output line"));
3e37d7b7
KZ
124
125 for (i = 0; i < ncolumns; i++) {
b7ebf49c
KZ
126 int rc = 0;
127
3e37d7b7
KZ
128 switch(get_column_id(i)) {
129 case COL_FILE:
b7ebf49c 130 rc = scols_line_set_data(ln, i, name);
3e37d7b7
KZ
131 break;
132 case COL_PAGES:
133 xasprintf(&tmp, "%jd", (intmax_t) count_incore);
b7ebf49c 134 rc = scols_line_refer_data(ln, i, tmp);
3e37d7b7 135 break;
e4e8b57b
KZ
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);
b7ebf49c 144 rc = scols_line_refer_data(ln, i, tmp);
e4e8b57b
KZ
145 break;
146 }
3e37d7b7
KZ
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);
b7ebf49c 152 rc = scols_line_refer_data(ln, i, tmp);
3e37d7b7
KZ
153 break;
154 default:
155 return -EINVAL;
156 }
b7ebf49c
KZ
157
158 if (rc)
159 err(EXIT_FAILURE, _("failed to add output data"));
3e37d7b7
KZ
160 }
161
162 return 0;
163}
164
165static int do_mincore(struct fincore_control *ctl,
166 void *window, const size_t len,
167 const char *name,
168 off_t *count_incore)
a921a7de
MY
169{
170 static unsigned char vec[N_PAGES_IN_WINDOW];
3e37d7b7 171 int n = (len / ctl->pagesize) + ((len % ctl->pagesize)? 1: 0);
a921a7de
MY
172
173 if (mincore (window, len, vec) < 0) {
174 warn(_("failed to do mincore: %s"), name);
b57fe43c 175 return -errno;
a921a7de
MY
176 }
177
178 while (n > 0)
179 {
180 if (vec[--n] & 0x1)
181 {
182 vec[n] = 0;
183 (*count_incore)++;
184 }
185 }
186
b57fe43c 187 return 0;
a921a7de
MY
188}
189
3e37d7b7
KZ
190static int fincore_fd (struct fincore_control *ctl,
191 int fd,
192 const char *name,
a921a7de
MY
193 off_t file_size,
194 off_t *count_incore)
195{
452089fa 196 size_t window_size = N_PAGES_IN_WINDOW * ctl->pagesize;
a921a7de
MY
197 off_t file_offset;
198 void *window = NULL;
b57fe43c 199 int rc = 0;
a921a7de
MY
200 int warned_once = 0;
201
202 for (file_offset = 0; file_offset < file_size; file_offset += window_size) {
203 size_t len;
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) {
b57fe43c 212 rc = -EINVAL;
a921a7de
MY
213 warn(_("failed to do mmap: %s"), name);
214 warned_once = 1;
215 }
216 break;
217 }
218
3e37d7b7 219 rc = do_mincore(ctl, window, len, name, count_incore);
b57fe43c
KZ
220 if (rc)
221 break;
a921a7de
MY
222
223 munmap (window, len);
224 }
225
b57fe43c 226 return rc;
a921a7de
MY
227}
228
3e37d7b7
KZ
229/*
230 * Returns: <0 on error, 0 success, 1 ignore.
231 */
232static int fincore_name(struct fincore_control *ctl,
233 const char *name,
234 struct stat *sb,
235 off_t *count_incore)
a921a7de
MY
236{
237 int fd;
b57fe43c 238 int rc = 0;
a921a7de
MY
239
240 if ((fd = open (name, O_RDONLY)) < 0) {
241 warn(_("failed to open: %s"), name);
cff1c113 242 return -errno;
a921a7de
MY
243 }
244
245 if (fstat (fd, sb) < 0) {
246 warn(_("failed to do fstat: %s"), name);
b9dc8d07 247 close (fd);
b57fe43c 248 return -errno;
a921a7de
MY
249 }
250
3e37d7b7
KZ
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);
a921a7de
MY
256
257 close (fd);
b57fe43c 258 return rc;
a921a7de
MY
259}
260
86be6a32 261static void __attribute__((__noreturn__)) usage(void)
3f91dd88 262{
86be6a32 263 FILE *out = stdout;
c5cb5412
KZ
264 size_t i;
265
3f91dd88
KZ
266 fputs(USAGE_HEADER, out);
267 fprintf(out, _(" %s [options] file...\n"), program_invocation_short_name);
268
269 fputs(USAGE_OPTIONS, out);
9b48766f 270 fputs(_(" -J, --json use JSON output format\n"), out);
c5cb5412
KZ
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);
9b48766f 274 fputs(_(" -r, --raw use raw output format\n"), out);
3f91dd88
KZ
275
276 fputs(USAGE_SEPARATOR, out);
f45f3ec3 277 printf(USAGE_HELP_OPTIONS(23));
3f91dd88 278
c3a4cfc5 279 fprintf(out, USAGE_COLUMNS);
c5cb5412
KZ
280
281 for (i = 0; i < ARRAY_SIZE(infos); i++)
282 fprintf(out, " %11s %s\n", infos[i].name, _(infos[i].help));
283
f45f3ec3 284 printf(USAGE_MAN_TAIL("fincore(1)"));
3f91dd88 285
86be6a32 286 exit(EXIT_SUCCESS);
3f91dd88
KZ
287}
288
a921a7de
MY
289int main(int argc, char ** argv)
290{
291 int c;
3e37d7b7 292 size_t i;
b57fe43c 293 int rc = EXIT_SUCCESS;
c5cb5412 294 char *outarg = NULL;
a921a7de 295
3e37d7b7 296 struct fincore_control ctl = {
c5cb5412 297 .pagesize = getpagesize()
3e37d7b7
KZ
298 };
299
a921a7de 300 static const struct option longopts[] = {
3f91dd88
KZ
301 { "bytes", no_argument, NULL, 'b' },
302 { "noheadings", no_argument, NULL, 'n' },
c5cb5412 303 { "output", required_argument, NULL, 'o' },
a921a7de
MY
304 { "version", no_argument, NULL, 'V' },
305 { "help", no_argument, NULL, 'h' },
9b48766f
KZ
306 { "json", no_argument, NULL, 'J' },
307 { "raw", no_argument, NULL, 'r' },
a921a7de
MY
308 { NULL, 0, NULL, 0 },
309 };
310
311 setlocale(LC_ALL, "");
312 bindtextdomain(PACKAGE, LOCALEDIR);
313 textdomain(PACKAGE);
314 atexit(close_stdout);
315
9b48766f 316 while ((c = getopt_long (argc, argv, "bno:JrVh", longopts, NULL)) != -1) {
a921a7de 317 switch (c) {
3f91dd88
KZ
318 case 'b':
319 ctl.bytes = 1;
320 break;
321 case 'n':
322 ctl.noheadings = 1;
323 break;
c5cb5412
KZ
324 case 'o':
325 outarg = optarg;
326 break;
9b48766f
KZ
327 case 'J':
328 ctl.json = 1;
329 break;
330 case 'r':
331 ctl.raw = 1;
332 break;
a921a7de
MY
333 case 'V':
334 printf(UTIL_LINUX_VERSION);
335 return EXIT_SUCCESS;
336 case 'h':
86be6a32 337 usage();
a921a7de
MY
338 default:
339 errtryhelp(EXIT_FAILURE);
340 }
341 }
342
343 if (optind == argc) {
b57fe43c
KZ
344 warnx(_("no file specified"));
345 errtryhelp(EXIT_FAILURE);
a921a7de
MY
346 }
347
3e37d7b7 348 if (!ncolumns) {
e4e8b57b 349 columns[ncolumns++] = COL_RES;
3e37d7b7
KZ
350 columns[ncolumns++] = COL_PAGES;
351 columns[ncolumns++] = COL_SIZE;
352 columns[ncolumns++] = COL_FILE;
353 }
354
c5cb5412
KZ
355 if (outarg && string_add_to_idarray(outarg, columns, ARRAY_SIZE(columns),
356 &ncolumns, column_name_to_id) < 0)
357 return EXIT_FAILURE;
358
3e37d7b7
KZ
359 scols_init_debug(0);
360 ctl.tb = scols_new_table();
361 if (!ctl.tb)
780ce22c 362 err(EXIT_FAILURE, _("failed to allocate output table"));
3e37d7b7 363
3f91dd88 364 scols_table_enable_noheadings(ctl.tb, ctl.noheadings);
9b48766f
KZ
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");
3f91dd88 369
3e37d7b7
KZ
370 for (i = 0; i < ncolumns; i++) {
371 const struct colinfo *col = get_column_info(i);
a921a7de 372
3e37d7b7 373 if (!scols_table_new_column(ctl.tb, col->name, col->whint, col->flags))
780ce22c 374 err(EXIT_FAILURE, _("failed to allocate output column"));
3e37d7b7 375 }
b57fe43c 376
a921a7de
MY
377 for(; optind < argc; optind++) {
378 char *name = argv[optind];
379 struct stat sb;
380 off_t count_incore = 0;
381
3e37d7b7
KZ
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:
b57fe43c 389 rc = EXIT_FAILURE;
3e37d7b7 390 break;
a921a7de
MY
391 }
392 }
393
3e37d7b7
KZ
394 scols_print_table(ctl.tb);
395 scols_unref_table(ctl.tb);
396
b57fe43c 397 return rc;
a921a7de 398}