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