Disable gtk-doc check
[platform/upstream/util-linux.git] / misc-utils / fincore.c
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"
32 #include "xalloc.h"
33 #include "strutils.h"
34
35 #include "libsmartcols.h"
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 ((size_t)(32 * 1024))
44
45
46 struct colinfo {
47         const char *name;
48         double whint;
49         int flags;
50         const char *help;
51 };
52
53 enum {
54         COL_PAGES,
55         COL_SIZE,
56         COL_FILE,
57         COL_RES
58 };
59
60 static struct colinfo infos[] = {
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")}, 
63         [COL_SIZE]   = { "SIZE",     5, SCOLS_FL_RIGHT, N_("size of the file")},
64         [COL_FILE]   = { "FILE",     4, 0, N_("file name")},
65 };
66
67 static int columns[ARRAY_SIZE(infos) * 2] = {-1};
68 static size_t ncolumns;
69
70 struct fincore_control {
71         const size_t pagesize;
72
73         struct libscols_table *tb;              /* output */
74
75         unsigned int bytes : 1,
76                      noheadings : 1,
77                      raw : 1,
78                      json : 1;
79 };
80
81
82 static 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
96 static int get_column_id(int num)
97 {
98         assert(num >= 0);
99         assert((size_t) num < ncolumns);
100         assert(columns[num] < (int) ARRAY_SIZE(infos));
101         return columns[num];
102 }
103
104 static const struct colinfo *get_column_info(int num)
105 {
106         return &infos[ get_column_id(num) ];
107 }
108
109 static 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 allocate output line"));
124
125         for (i = 0; i < ncolumns; i++) {
126                 int rc = 0;
127
128                 switch(get_column_id(i)) {
129                 case COL_FILE:
130                         rc = scols_line_set_data(ln, i, name);
131                         break;
132                 case COL_PAGES:
133                         xasprintf(&tmp, "%jd",  (intmax_t) count_incore);
134                         rc = scols_line_refer_data(ln, i, tmp);
135                         break;
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);
144                         rc = scols_line_refer_data(ln, i, tmp);
145                         break;
146                 }
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);
152                         rc = scols_line_refer_data(ln, i, tmp);
153                         break;
154                 default:
155                         return -EINVAL;
156                 }
157
158                 if (rc)
159                         err(EXIT_FAILURE, _("failed to add output data"));
160         }
161
162         return 0;
163 }
164
165 static int do_mincore(struct fincore_control *ctl,
166                       void *window, const size_t len,
167                       const char *name,
168                       off_t *count_incore)
169 {
170         static unsigned char vec[N_PAGES_IN_WINDOW];
171         int n = (len / ctl->pagesize) + ((len % ctl->pagesize)? 1: 0);
172
173         if (mincore (window, len, vec) < 0) {
174                 warn(_("failed to do mincore: %s"), name);
175                 return -errno;
176         }
177
178         while (n > 0)
179         {
180                 if (vec[--n] & 0x1)
181                 {
182                         vec[n] = 0;
183                         (*count_incore)++;
184                 }
185         }
186
187         return 0;
188 }
189
190 static int fincore_fd (struct fincore_control *ctl,
191                        int fd,
192                        const char *name,
193                        off_t file_size,
194                        off_t *count_incore)
195 {
196         size_t window_size = N_PAGES_IN_WINDOW * ctl->pagesize;
197         off_t  file_offset;
198         void  *window = NULL;
199         int rc = 0;
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) {
212                                 rc = -EINVAL;
213                                 warn(_("failed to do mmap: %s"), name);
214                                 warned_once = 1;
215                         }
216                         break;
217                 }
218
219                 rc = do_mincore(ctl, window, len, name, count_incore);
220                 if (rc)
221                         break;
222
223                 munmap (window, len);
224         }
225
226         return rc;
227 }
228
229 /*
230  * Returns: <0 on error, 0 success, 1 ignore.
231  */
232 static int fincore_name(struct fincore_control *ctl,
233                         const char *name,
234                         struct stat *sb,
235                         off_t *count_incore)
236 {
237         int fd;
238         int rc = 0;
239
240         if ((fd = open (name, O_RDONLY)) < 0) {
241                 warn(_("failed to open: %s"), name);
242                 return -errno;
243         }
244
245         if (fstat (fd, sb) < 0) {
246                 warn(_("failed to do fstat: %s"), name);
247                 close (fd);
248                 return -errno;
249         }
250
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);
256
257         close (fd);
258         return rc;
259 }
260
261 static void __attribute__((__noreturn__)) usage(FILE *out)
262 {
263         size_t i;
264
265         fputs(USAGE_HEADER, out);
266         fprintf(out, _(" %s [options] file...\n"), program_invocation_short_name);
267
268         fputs(USAGE_OPTIONS, out);
269         fputs(_(" -J, --json            use JSON output format\n"), out);
270         fputs(_(" -b, --bytes           print sizes in bytes rather than in human readable format\n"), out);
271         fputs(_(" -n, --noheadings      don't print headings\n"), out);
272         fputs(_(" -o, --output <list>   output columns\n"), out);
273         fputs(_(" -r, --raw             use raw output format\n"), out);
274
275         fputs(USAGE_SEPARATOR, out);
276         fputs(USAGE_HELP, out);
277         fputs(USAGE_VERSION, out);
278
279         fprintf(out, _("\nAvailable columns (for --output):\n"));
280
281         for (i = 0; i < ARRAY_SIZE(infos); i++)
282                 fprintf(out, " %11s  %s\n", infos[i].name, _(infos[i].help));
283
284         fprintf(out, USAGE_MAN_TAIL("fincore(1)"));
285
286         exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
287 }
288
289 int main(int argc, char ** argv)
290 {
291         int c;
292         size_t i;
293         int rc = EXIT_SUCCESS;
294         char *outarg = NULL;
295
296         struct fincore_control ctl = {
297                 .pagesize = getpagesize()
298         };
299
300         static const struct option longopts[] = {
301                 { "bytes",      no_argument, NULL, 'b' },
302                 { "noheadings", no_argument, NULL, 'n' },
303                 { "output",     required_argument, NULL, 'o' },
304                 { "version",    no_argument, NULL, 'V' },
305                 { "help",       no_argument, NULL, 'h' },
306                 { "json",       no_argument, NULL, 'J' },
307                 { "raw",        no_argument, NULL, 'r' },
308                 { NULL, 0, NULL, 0 },
309         };
310
311         setlocale(LC_ALL, "");
312         bindtextdomain(PACKAGE, LOCALEDIR);
313         textdomain(PACKAGE);
314         atexit(close_stdout);
315
316         while ((c = getopt_long (argc, argv, "bno:JrVh", longopts, NULL)) != -1) {
317                 switch (c) {
318                 case 'b':
319                         ctl.bytes = 1;
320                         break;
321                 case 'n':
322                         ctl.noheadings = 1;
323                         break;
324                 case 'o':
325                         outarg = optarg;
326                         break;
327                 case 'J':
328                         ctl.json = 1;
329                         break;
330                 case 'r':
331                         ctl.raw = 1;
332                         break;
333                 case 'V':
334                         printf(UTIL_LINUX_VERSION);
335                         return EXIT_SUCCESS;
336                 case 'h':
337                         usage(stdout);
338                 default:
339                         errtryhelp(EXIT_FAILURE);
340                 }
341         }
342
343         if (optind == argc) {
344                 warnx(_("no file specified"));
345                 errtryhelp(EXIT_FAILURE);
346         }
347
348         if (!ncolumns) {
349                 columns[ncolumns++] = COL_RES;
350                 columns[ncolumns++] = COL_PAGES;
351                 columns[ncolumns++] = COL_SIZE;
352                 columns[ncolumns++] = COL_FILE;
353         }
354
355         if (outarg && string_add_to_idarray(outarg, columns, ARRAY_SIZE(columns),
356                                          &ncolumns, column_name_to_id) < 0)
357                 return EXIT_FAILURE;
358
359         scols_init_debug(0);
360         ctl.tb = scols_new_table();
361         if (!ctl.tb)
362                 err(EXIT_FAILURE, _("failed to allocate output table"));
363
364         scols_table_enable_noheadings(ctl.tb, ctl.noheadings);
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");
369
370         for (i = 0; i < ncolumns; i++) {
371                 const struct colinfo *col = get_column_info(i);
372
373                 if (!scols_table_new_column(ctl.tb, col->name, col->whint, col->flags))
374                         err(EXIT_FAILURE, _("failed to allocate output column"));
375         }
376
377         for(; optind < argc; optind++) {
378                 char *name = argv[optind];
379                 struct stat sb;
380                 off_t count_incore = 0;
381
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:
389                         rc = EXIT_FAILURE;
390                         break;
391                 }
392         }
393
394         scols_print_table(ctl.tb);
395         scols_unref_table(ctl.tb);
396
397         return rc;
398 }