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