]> git.ipfire.org Git - thirdparty/util-linux.git/blame - misc-utils/fincore.c
wipefs: add --lock and LOCK_BLOCK_DEVICE
[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 61 [COL_PAGES] = { "PAGES", 1, SCOLS_FL_RIGHT, N_("file data resident in memory in pages")},
2bb3aa36 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;
fcfe1a5c 197 off_t file_offset, len;
b57fe43c 198 int rc = 0;
a921a7de 199
fcfe1a5c 200 for (file_offset = 0; file_offset < file_size; file_offset += len) {
57940872 201 void *window = NULL;
a921a7de
MY
202
203 len = file_size - file_offset;
ea62c152 204 if (len >= (off_t) window_size)
a921a7de
MY
205 len = window_size;
206
207 window = mmap(window, len, PROT_NONE, MAP_PRIVATE, fd, file_offset);
208 if (window == MAP_FAILED) {
374cb543
KZ
209 rc = -EINVAL;
210 warn(_("failed to do mmap: %s"), name);
a921a7de
MY
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);
b9dc8d07 242 close (fd);
b57fe43c 243 return -errno;
a921a7de
MY
244 }
245
3e37d7b7
KZ
246 if (S_ISDIR(sb->st_mode))
247 rc = 1; /* ignore */
248
249 else if (sb->st_size)
250 rc = fincore_fd(ctl, fd, name, sb->st_size, count_incore);
a921a7de
MY
251
252 close (fd);
b57fe43c 253 return rc;
a921a7de
MY
254}
255
86be6a32 256static void __attribute__((__noreturn__)) usage(void)
3f91dd88 257{
86be6a32 258 FILE *out = stdout;
c5cb5412
KZ
259 size_t i;
260
3f91dd88
KZ
261 fputs(USAGE_HEADER, out);
262 fprintf(out, _(" %s [options] file...\n"), program_invocation_short_name);
263
264 fputs(USAGE_OPTIONS, out);
9b48766f 265 fputs(_(" -J, --json use JSON output format\n"), out);
c5cb5412
KZ
266 fputs(_(" -b, --bytes print sizes in bytes rather than in human readable format\n"), out);
267 fputs(_(" -n, --noheadings don't print headings\n"), out);
268 fputs(_(" -o, --output <list> output columns\n"), out);
9b48766f 269 fputs(_(" -r, --raw use raw output format\n"), out);
3f91dd88
KZ
270
271 fputs(USAGE_SEPARATOR, out);
f45f3ec3 272 printf(USAGE_HELP_OPTIONS(23));
3f91dd88 273
c3a4cfc5 274 fprintf(out, USAGE_COLUMNS);
c5cb5412
KZ
275
276 for (i = 0; i < ARRAY_SIZE(infos); i++)
277 fprintf(out, " %11s %s\n", infos[i].name, _(infos[i].help));
278
f45f3ec3 279 printf(USAGE_MAN_TAIL("fincore(1)"));
3f91dd88 280
86be6a32 281 exit(EXIT_SUCCESS);
3f91dd88
KZ
282}
283
a921a7de
MY
284int main(int argc, char ** argv)
285{
286 int c;
3e37d7b7 287 size_t i;
b57fe43c 288 int rc = EXIT_SUCCESS;
c5cb5412 289 char *outarg = NULL;
a921a7de 290
3e37d7b7 291 struct fincore_control ctl = {
c5cb5412 292 .pagesize = getpagesize()
3e37d7b7
KZ
293 };
294
a921a7de 295 static const struct option longopts[] = {
3f91dd88
KZ
296 { "bytes", no_argument, NULL, 'b' },
297 { "noheadings", no_argument, NULL, 'n' },
c5cb5412 298 { "output", required_argument, NULL, 'o' },
a921a7de
MY
299 { "version", no_argument, NULL, 'V' },
300 { "help", no_argument, NULL, 'h' },
9b48766f
KZ
301 { "json", no_argument, NULL, 'J' },
302 { "raw", no_argument, NULL, 'r' },
a921a7de
MY
303 { NULL, 0, NULL, 0 },
304 };
305
306 setlocale(LC_ALL, "");
307 bindtextdomain(PACKAGE, LOCALEDIR);
308 textdomain(PACKAGE);
2c308875 309 close_stdout_atexit();
a921a7de 310
9b48766f 311 while ((c = getopt_long (argc, argv, "bno:JrVh", longopts, NULL)) != -1) {
a921a7de 312 switch (c) {
3f91dd88
KZ
313 case 'b':
314 ctl.bytes = 1;
315 break;
316 case 'n':
317 ctl.noheadings = 1;
318 break;
c5cb5412
KZ
319 case 'o':
320 outarg = optarg;
321 break;
9b48766f
KZ
322 case 'J':
323 ctl.json = 1;
324 break;
325 case 'r':
326 ctl.raw = 1;
327 break;
a921a7de 328 case 'V':
2c308875 329 print_version(EXIT_SUCCESS);
a921a7de 330 case 'h':
86be6a32 331 usage();
a921a7de
MY
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)
780ce22c 356 err(EXIT_FAILURE, _("failed to allocate output table"));
3e37d7b7 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);
8945d9dc 366 struct libscols_column *cl;
a921a7de 367
8945d9dc
KZ
368 cl = scols_table_new_column(ctl.tb, col->name, col->whint, col->flags);
369 if (!cl)
780ce22c 370 err(EXIT_FAILURE, _("failed to allocate output column"));
8945d9dc
KZ
371
372 if (ctl.json) {
373 int id = get_column_id(i);
374
375 switch (id) {
376 case COL_FILE:
377 scols_column_set_json_type(cl, SCOLS_JSON_STRING);
378 break;
379 case COL_SIZE:
380 case COL_RES:
381 if (!ctl.bytes)
382 break;
383 /* fallthrough */
384 default:
385 scols_column_set_json_type(cl, SCOLS_JSON_NUMBER);
386 break;
387 }
388 }
3e37d7b7 389 }
b57fe43c 390
a921a7de
MY
391 for(; optind < argc; optind++) {
392 char *name = argv[optind];
393 struct stat sb;
394 off_t count_incore = 0;
395
3e37d7b7
KZ
396 switch (fincore_name(&ctl, name, &sb, &count_incore)) {
397 case 0:
398 add_output_data(&ctl, name, sb.st_size, count_incore);
399 break;
400 case 1:
401 break; /* ignore */
402 default:
b57fe43c 403 rc = EXIT_FAILURE;
3e37d7b7 404 break;
a921a7de
MY
405 }
406 }
407
3e37d7b7
KZ
408 scols_print_table(ctl.tb);
409 scols_unref_table(ctl.tb);
410
b57fe43c 411 return rc;
a921a7de 412}