]> git.tuebingen.mpg.de Git - adu.git/blob - select.c
Introduce select-mode and use pretty formating for the global lists.
[adu.git] / select.c
1 /*
2  * Copyright (C) 2008 Andre Noll <maan@systemlinux.org>
3  *
4  * Licensed under the GPL v2. For licencing details see COPYING.
5  */
6
7 /** \file select.c The select mode of adu. */
8
9 #include <dirent.h> /* readdir() */
10 #include "format.h"
11 #include "adu.h"
12 #include "gcc-compat.h"
13 #include "cmdline.h"
14 #include "fd.h"
15 #include "string.h"
16 #include "error.h"
17 #include "portable_io.h"
18
19 /** Global dir count. */
20 static uint64_t num_dirs;
21 /** Global files count. */
22 static uint64_t num_files;
23 /** Global bytes count. */
24 static uint64_t num_bytes;
25
26
27 /** The decimal representation of an uint64_t never exceeds that size. */
28 #define FORMATED_VALUE_SIZE 25
29
30 #define GLOBAL_LIST_ATOMS \
31         ATOM(size, SIZE) \
32         ATOM(files, COUNT) \
33         ATOM(dirname, STRING) \
34
35 #define ATOM(x, y) { .name = #x, .type = AT_ ## y},
36 struct atom global_list_atoms[] = {
37         GLOBAL_LIST_ATOMS
38         {.name = NULL}
39 };
40 #undef ATOM
41 #define ATOM(x, y) gla_ ## x,
42 enum global_list_atoms {GLOBAL_LIST_ATOMS};
43 #undef ATOM
44
45 #define GLOBAL_SUMMARY_ATOMS \
46         ATOM(dirs, COUNT) \
47         ATOM(files, COUNT) \
48         ATOM(size, SIZE)
49
50 #define ATOM(x, y) { .name = #x, .type = AT_ ## y},
51 struct atom global_summary_atoms[] = {
52         GLOBAL_SUMMARY_ATOMS
53         {.name = NULL}
54 };
55 #undef ATOM
56 #define ATOM(x, y) gsa_ ## x,
57 enum global_summary_atoms {GLOBAL_SUMMARY_ATOMS};
58 #undef ATOM
59
60 #define USER_SUMMARY_ATOMS \
61         ATOM(pw_name, STRING) \
62         ATOM(uid, ID) \
63         ATOM(dirs, COUNT) \
64         ATOM(files, COUNT) \
65         ATOM(size, SIZE)
66
67 #define ATOM(x, y) { .name = #x, .type = AT_ ## y},
68 struct atom user_summary_atoms[] = {
69         USER_SUMMARY_ATOMS
70         {.name = NULL}
71 };
72 #undef ATOM
73 #define ATOM(x, y) usa_ ## x,
74 enum user_summary_atoms {USER_SUMMARY_ATOMS};
75 #undef ATOM
76
77
78
79 /* these get filled in by the select command. */
80 static char count_unit_buf[4] = "( )", size_unit_buf[4] = "( )";
81
82 struct global_list_info {
83         uint32_t count;
84         int ret;
85         int osl_errno;
86         struct format_info *fi;
87 };
88
89 enum user_stats_flags {
90         USF_PRINT_DIRNAME = 1,
91         USF_PRINT_BYTES = 2,
92         USF_PRINT_FILES = 4,
93         USF_COMPUTE_SUMMARY = 8,
94 };
95
96 struct user_stats_info {
97         uint32_t count;
98         enum user_stats_flags flags;
99         int ret;
100         int osl_errno;
101         struct user_info *ui;
102 };
103
104 static const uint64_t size_unit_divisors[] = {
105         [size_unit_arg_b] = 1ULL,
106         [size_unit_arg_k] = 1024ULL,
107         [size_unit_arg_m] = 1024ULL * 1024ULL,
108         [size_unit_arg_g] = 1024ULL * 1024ULL * 1024ULL,
109         [size_unit_arg_t] = 1024ULL * 1024ULL * 1024ULL * 1024ULL,
110 };
111
112 static const uint64_t count_unit_divisors[] = {
113
114         [count_unit_arg_n] = 1ULL,
115         [count_unit_arg_k] = 1000ULL,
116         [count_unit_arg_m] = 1000ULL * 1000ULL,
117         [count_unit_arg_g] = 1000ULL * 1000ULL * 1000ULL,
118         [count_unit_arg_t] = 1000ULL * 1000ULL * 1000ULL * 1000ULL,
119 };
120
121 static const char size_unit_abbrevs[] = " BKMGT";
122 static const char count_unit_abbrevs[] = "  kmgt";
123 static enum enum_size_unit format_size_value(enum enum_size_unit unit,
124                 uint64_t value, int print_unit, char *result)
125 {
126         enum enum_size_unit u = unit;
127         char unit_buf[2] = "\0\0";
128
129         if (unit == size_unit_arg_h) /* human readable */
130                 for (u = size_unit_arg_b; u < size_unit_arg_t &&
131                                 value > size_unit_divisors[u + 1]; u++)
132                         ; /* nothing */
133         if (print_unit)
134                 unit_buf[0] = size_unit_abbrevs[u];
135         sprintf(result, "%llu%s",
136                 (long long unsigned)value / size_unit_divisors[u], unit_buf);
137         return u;
138 }
139
140 static enum enum_count_unit format_count_value(enum enum_count_unit unit,
141                 uint64_t value, int print_unit, char *result)
142 {
143         enum enum_count_unit u = unit;
144         char unit_buf[2] = "\0\0";
145
146         if (unit == count_unit_arg_h) /* human readable */
147                 for (u = count_unit_arg_n; u < count_unit_arg_t &&
148                                 value > count_unit_divisors[u + 1]; u++)
149                         ; /* nothing */
150         if (print_unit)
151                 unit_buf[0] = count_unit_abbrevs[u];
152         sprintf(result, "%llu%s",
153                 (long long unsigned)value / count_unit_divisors[u], unit_buf);
154         return u;
155 }
156
157 static FILE *output_file;
158
159 __printf_1_2 static int output(const char const *fmt, ...)
160 {
161         va_list argp;
162         int ret;
163
164         va_start(argp, fmt);
165         ret = vfprintf(output_file, fmt, argp);
166         va_end(argp);
167         return ret < 0? -E_OUTPUT : 1;
168 }
169
170 static int get_dir_name_by_number(uint64_t *dirnum, char **name)
171 {
172         char *result = NULL, *tmp;
173         struct osl_row *row;
174         uint64_t val = *dirnum;
175         struct osl_object obj;
176         int ret;
177         char *pfx;
178
179 again:
180         obj.data = &val;
181         obj.size = sizeof(val);
182         ret = osl(osl_get_row(dir_table, DT_NUM, &obj, &row));
183         if (ret < 0)
184                 goto out;
185         ret = osl(osl_get_object(dir_table, row, DT_PARENT_NUM, &obj));
186         if (ret < 0)
187                 goto out;
188         val = *(uint64_t *)obj.data;
189         ret = osl(osl_get_object(dir_table, row, DT_NAME, &obj));
190         if (ret < 0)
191                 goto out;
192         pfx = (select_conf.print_base_dir_given || val)? (char *)obj.data :  ".";
193         tmp = make_message("%s/%s", pfx, result? result : "");
194         free(result);
195         result = tmp;
196         if (val)
197                 goto again;
198 out:
199         if (ret < 0) {
200                 free(result);
201                 *name = NULL;
202         } else {
203                 assert(result);
204                 *name = result;
205         }
206         return ret;
207 }
208
209 static int get_dir_name_of_row(struct osl_row *dir_table_row, char **name)
210 {
211         struct osl_object obj;
212         int ret;
213
214         *name = NULL;
215         ret = osl(osl_get_object(dir_table, dir_table_row, DT_NUM, &obj));
216         if (ret < 0)
217                 return ret;
218         return get_dir_name_by_number((uint64_t *)obj.data, name);
219 }
220
221 static int user_stats_loop_function(struct osl_row *row, void *data)
222 {
223         struct user_stats_info *usi = data;
224         struct osl_object obj;
225         int ret, summary = usi->flags & GSF_COMPUTE_SUMMARY;
226         char formated_value[FORMATED_VALUE_SIZE];
227
228         check_signals();
229         if (!usi->count && !summary) {
230                 ret = -E_LOOP_COMPLETE;
231                 goto err;
232         }
233         if (summary || (usi->count && (usi->flags & USF_PRINT_FILES))) {
234                 uint64_t files;
235                 ret = osl(osl_get_object(usi->ui->table, row, UT_FILES, &obj));
236                 if (ret < 0)
237                         goto err;
238                 files = *(uint64_t *)obj.data;
239                 if (usi->count && (usi->flags & USF_PRINT_FILES)) {
240                         format_count_value(select_conf.count_unit_arg, files,
241                                 select_conf.count_unit_arg == count_unit_arg_h,
242                                 formated_value);
243                         ret = output("\t%s%s", formated_value,
244                                 (usi->flags & (USF_PRINT_BYTES | USF_PRINT_DIRNAME))?
245                                         "\t" : "\n");
246                         if (ret < 0)
247                                 goto err;
248                 }
249                 if (summary)
250                         usi->ui->files += files;
251         }
252         if (summary || (usi->count && (usi->flags & USF_PRINT_BYTES))) {
253                 uint64_t bytes;
254                 ret = osl(osl_get_object(usi->ui->table, row, UT_BYTES, &obj));
255                 if (ret < 0)
256                         goto err;
257                 bytes = *(uint64_t *)obj.data;
258                 if (usi->count && (usi->flags & USF_PRINT_BYTES)) {
259                         format_size_value(select_conf.size_unit_arg, bytes,
260                                 select_conf.size_unit_arg == size_unit_arg_h,
261                                 formated_value);
262                         ret = output("%s%s%s",
263                                 (usi->flags & USF_PRINT_FILES)? "" : "\t",
264                                 formated_value,
265                                 usi->flags & USF_PRINT_DIRNAME?  "\t" : "\n"
266                         );
267                         if (ret < 0)
268                                 goto err;
269                 }
270                 if (summary) {
271                         usi->ui->bytes += bytes;
272                         usi->ui->dirs++;
273                 }
274
275         }
276         if (usi->count && (usi->flags & USF_PRINT_DIRNAME)) {
277                 char *dirname;
278                 ret = osl(osl_get_object(usi->ui->table, row, UT_DIR_NUM, &obj));
279                 if (ret < 0)
280                         goto err;
281                 ret = get_dir_name_by_number((uint64_t *)obj.data, &dirname);
282                 if (ret < 0)
283                         goto err;
284                 ret = output("%s%s\n",
285                         (usi->flags & (USF_PRINT_BYTES | USF_PRINT_FILES))? "" : "\t",
286                         dirname);
287                 free(dirname);
288                 if (ret < 0)
289                         goto err;
290         }
291         if (usi->count > 0)
292                 usi->count--;
293         return 1;
294 err:
295         usi->ret = ret;
296         usi->osl_errno = (ret == -E_OSL)? osl_errno : 0;
297         return -1;
298 }
299
300 static int check_loop_return(int ret, int loop_ret, int loop_osl_errno)
301 {
302         if (ret >= 0)
303                 return ret;
304         assert(ret == -E_OSL);
305         if (osl_errno != E_OSL_LOOP)
306                 /* error not caused by loop function returning negative. */
307                 return ret;
308         assert(loop_ret < 0);
309         if (loop_ret == -E_LOOP_COMPLETE) /* no error */
310                 return 1;
311         if (loop_ret == -E_OSL) { /* osl error in loop function */
312                 assert(loop_osl_errno);
313                 osl_errno = loop_osl_errno;
314         }
315         return loop_ret;
316 }
317
318 static int adu_loop_reverse(struct osl_table *t, unsigned col_num, void *private_data,
319                 osl_rbtree_loop_func *func, int *loop_ret, int *loop_osl_errno)
320 {
321         int ret = osl(osl_rbtree_loop_reverse(t, col_num, private_data, func));
322         return check_loop_return(ret, *loop_ret, *loop_osl_errno);
323 }
324
325 static int print_global_summary(struct format_info *fi)
326 {
327         int ret;
328         char *buf;
329         union atom_value values[] = {
330                 [gsa_dirs] = {.num_value = (long long unsigned)num_dirs},
331                 [gsa_files] = {.num_value =  (long long unsigned)num_files},
332                 [gsa_size] = {.num_value =  (long long unsigned)num_bytes}
333         };
334
335         if (select_conf.no_global_summary_given)
336                 return 1;
337         if (!select_conf.no_headers_given) {
338                 ret = output("Global summary\n");
339                 if (ret < 0)
340                         return ret;
341         }
342         buf = format_items(fi, values);
343         ret = output("%s", buf);
344         free(buf);
345         return ret;
346 }
347
348 static int print_user_summary_line(struct user_info *ui, __a_unused void *data)
349 {
350         struct format_info *fi = data;
351         union atom_value values[] = {
352                 [usa_pw_name] = {.string_value = ui->pw_name?
353                         ui->pw_name : "?"},
354                 [usa_uid] = {.num_value = (long long unsigned)ui->uid},
355                 [usa_dirs] = {.num_value = (long long unsigned)ui->dirs},
356                 [usa_files] = {.num_value =  (long long unsigned)ui->files},
357                 [usa_size] = {.num_value =  (long long unsigned)ui->bytes}
358         };
359         char *buf = format_items(fi, values);
360         int ret = output("%s", buf);
361
362         free(buf);
363         return ret;
364 }
365
366 static int name_comp(const void *a, const void *b)
367 {
368         char *x = ((struct user_info *)a)->pw_name;
369         char *y = ((struct user_info *)b)->pw_name;
370
371         if (!x)
372                 return 1;
373         if (!y)
374                 return -1;
375         return strcmp(x, y);
376 }
377
378 static int uid_comp(const void *a, const void *b)
379 {
380         return -NUM_COMPARE(((struct user_info *)a)->uid,
381                 ((struct user_info *)b)->uid);
382 }
383
384 static int dir_count_comp(const void *a, const void *b)
385 {
386         return NUM_COMPARE(((struct user_info *)a)->dirs,
387                 ((struct user_info *)b)->dirs);
388 }
389
390 static int file_count_comp(const void *a, const void *b)
391 {
392         return NUM_COMPARE(((struct user_info *)a)->files,
393                 ((struct user_info *)b)->files);
394 }
395
396 static int size_comp(const void *a, const void *b)
397 {
398         return NUM_COMPARE(((struct user_info *)a)->bytes,
399                 ((struct user_info *)b)->bytes);
400 }
401
402 /*
403  * The comparators for sorting the user summary.
404  *
405  * This is an array of pointers to functions taking two constant void *
406  * pointers and returning an int.
407  */
408 static int (*summary_comparators[])(const void *, const void *) = {
409         [user_summary_sort_arg_name] = name_comp,
410         [user_summary_sort_arg_uid] = uid_comp,
411         [user_summary_sort_arg_dir_count] = dir_count_comp,
412         [user_summary_sort_arg_file_count] = file_count_comp,
413         [user_summary_sort_arg_size] = size_comp,
414 };
415
416 static int print_user_summary(struct format_info *fi)
417 {
418         if (select_conf.no_user_summary_given)
419                 return 1;
420         if (!select_conf.no_headers_given) {
421                 int ret = output("User summary\n");
422                 if (ret < 0)
423                         return ret;
424         }
425         sort_hash_table(summary_comparators[select_conf.user_summary_sort_arg]);
426         return for_each_admissible_user(print_user_summary_line, fi);
427 }
428
429 static int print_user_list(struct user_info *ui, __a_unused void *data)
430 {
431         int ret;
432         struct user_stats_info usi;
433         enum enum_user_list ula = select_conf.user_list_arg;
434         int print_size_list = (ula == user_list_arg_size
435                 || ula == user_list_arg_both);
436
437         if (print_size_list) {
438                 usi.count = select_conf.limit_arg;
439                 usi.ui = ui;
440                 usi.flags = USF_PRINT_DIRNAME | USF_PRINT_BYTES | USF_COMPUTE_SUMMARY;
441                 if (!select_conf.no_headers_given) {
442                         ret = output("%s (uid %u), by size%s:\n",
443                                 ui->pw_name? ui->pw_name : "?", (unsigned)ui->uid,
444                                 size_unit_buf);
445                         if (ret < 0)
446                                 return ret;
447                 }
448                 ret = adu_loop_reverse(ui->table, UT_BYTES, &usi, user_stats_loop_function,
449                         &usi.ret, &usi.osl_errno);
450                 if (ret < 0)
451                         return ret;
452                 ret = output("\n");
453                 if (ret < 0)
454                         return ret;
455         }
456         if (ula == user_list_arg_file_count || ula == user_list_arg_both) {
457                 if (!select_conf.no_headers_given) {
458                         ret = output("%s (uid %u), by file count%s:\n",
459                                 ui->pw_name? ui->pw_name : "?", (unsigned)ui->uid,
460                                 count_unit_buf);
461                         if (ret < 0)
462                                 return ret;
463                 }
464                 usi.count = select_conf.limit_arg,
465                 usi.ui = ui;
466                 usi.flags = USF_PRINT_DIRNAME | USF_PRINT_FILES;
467                 ret = adu_loop_reverse(ui->table, UT_FILES, &usi, user_stats_loop_function,
468                         &usi.ret, &usi.osl_errno);
469                 if (ret < 0)
470                         return ret;
471                 ret = output("\n");
472                 if (ret < 0)
473                         return ret;
474         }
475         if (ula == user_list_arg_none && !select_conf.no_user_summary_given) {
476                 usi.count = select_conf.limit_arg;
477                 usi.ui = ui;
478                 usi.flags = USF_COMPUTE_SUMMARY;
479                 ret = adu_loop_reverse(ui->table, UT_FILES, &usi, user_stats_loop_function,
480                         &usi.ret, &usi.osl_errno);
481                 if (ret < 0)
482                         return ret;
483         }
484         return 1;
485 }
486
487 static int print_user_lists(void)
488 {
489         return for_each_admissible_user(print_user_list, NULL);
490 }
491
492 static int get_num_files_of_row(struct osl_row *row, uint64_t *num_files)
493 {
494         struct osl_object obj;
495         int ret = osl(osl_get_object(dir_table, row, DT_FILES, &obj));
496         if (ret < 0)
497                 return ret;
498         *num_files = *(uint64_t *)obj.data;
499         return 1;
500 }
501
502 static int get_num_bytes_of_row(struct osl_row *row, uint64_t *num_bytes)
503 {
504         struct osl_object obj;
505         int ret = osl(osl_get_object(dir_table, row, DT_BYTES, &obj));
506         if (ret < 0)
507                 return ret;
508         *num_bytes = *(uint64_t *)obj.data;
509         return 1;
510 }
511
512 static int global_list_loop_function(struct osl_row *row, void *data)
513 {
514         struct global_list_info *gli = data;
515         union atom_value values[] = {
516                 [gla_size] = {.num_value = 0ULL},
517                 [gla_files] = {.num_value =  0ULL},
518                 [gla_dirname] = {.string_value = NULL}
519         };
520         uint64_t num_files, num_bytes;
521         char *dirname, *buf;
522         int ret;
523
524         check_signals();
525         ret = -E_LOOP_COMPLETE;
526         if (!gli->count)
527                 goto err;
528
529         ret = get_num_files_of_row(row, &num_files);
530         if (ret < 0)
531                 goto err;
532         values[gla_files].num_value = (long long unsigned)num_files;
533
534         ret = get_num_bytes_of_row(row, &num_bytes);
535         if (ret < 0)
536                 goto err;
537         values[gla_size].num_value = (long long unsigned)num_bytes;
538
539         ret = get_dir_name_of_row(row, &dirname);
540         if (ret < 0)
541                 goto err;
542         values[gla_dirname].string_value = dirname;
543
544         buf = format_items(gli->fi, values);
545         free(dirname);
546         ret = output("%s", buf);
547         free(buf);
548         if (gli->count > 0)
549                 gli->count--;
550         return ret;
551 err:
552         gli->ret = ret;
553         gli->osl_errno = (ret == -E_OSL)? osl_errno : 0;
554         return -1;
555 }
556
557 static int print_global_list(struct format_info *fi)
558 {
559         int ret;
560         enum dir_table_columns sort_column = DT_BYTES;
561         struct global_list_info gli = {
562                 .fi = fi,
563                 .count = select_conf.limit_arg
564         };
565
566         if (!select_conf.no_headers_given) {
567                 ret = output("Global list\n");
568                 if (ret < 0)
569                         return ret;
570         }
571         if (select_conf.sort_arg == sort_arg_files)
572                 sort_column = DT_FILES;
573         return adu_loop_reverse(dir_table, sort_column, &gli,
574                 global_list_loop_function, &gli.ret, &gli.osl_errno);
575 }
576
577 static int print_statistics(struct select_format_info *sli)
578 {
579         int ret;
580
581         switch (select_conf.select_mode_arg) {
582                 case select_mode_arg_global_list:
583                         ret = print_global_list(sli->global_list_fi);
584                         free_format_info(sli->global_list_fi);
585                         return ret;
586                 case select_mode_arg_global_summary:
587                         ret = print_global_summary(sli->global_summary_fi);
588                         free_format_info(sli->global_summary_fi);
589                         return ret;
590                 case select_mode_arg_user_list:
591                         ret = print_user_lists();
592                         return ret;
593                 case select_mode_arg_user_summary:
594                         ret = print_user_summary(sli->user_summary_fi);
595                         free_format_info(sli->user_summary_fi);
596                         return ret;
597         };
598         ERROR_LOG("bad select mode\n");
599         return ERRNO_TO_ERROR(-EINVAL);
600 }
601
602 static int read_uid_file(struct uid_range *admissible_uids)
603 {
604         size_t size;
605         uint32_t n;
606         char *filename = get_uid_list_name(), *map;
607         int ret = mmap_full_file(filename, O_RDONLY, (void **)&map, &size, NULL);
608         unsigned bits;
609
610         if (ret < 0) {
611                 INFO_LOG("failed to map %s\n", filename);
612                 free(filename);
613                 return ret;
614         }
615         num_uids = size / 4;
616         INFO_LOG("found %u uids in %s\n", (unsigned)num_uids, filename);
617         free(filename);
618         /*
619          * Compute number of hash table bits. The hash table size must be a
620          * power of two and larger than the number of uids.
621          */
622         bits = 2;
623         while (1 << bits < num_uids)
624                 bits++;
625         create_hash_table(bits);
626         for (n = 0; n < num_uids; n++) {
627                 uint32_t uid = read_u32(map + n * sizeof(uid));
628                 ret = search_uid(uid, admissible_uids, OPEN_USER_TABLE, NULL);
629                 if (ret < 0)
630                         goto out;
631         }
632 out:
633         adu_munmap(map, size);
634         return ret;
635 }
636
637 int run_select_query(struct uid_range *admissible_uids,
638                 struct select_format_info *sfi)
639 {
640         int ret;
641
642         if (select_conf.output_given && strcmp(select_conf.output_arg, "-")) {
643                 output_file = fopen(select_conf.output_arg, "w");
644                 if (!output_file)
645                         return -ERRNO_TO_ERROR(errno);
646         } else
647                 output_file = stdout;
648
649         if (select_conf.count_unit_arg != count_unit_arg_h)
650                 count_unit_buf[1] = count_unit_abbrevs[select_conf.count_unit_arg];
651         else
652                 count_unit_buf[0] = '\0';
653         if (select_conf.size_unit_arg != size_unit_arg_h)
654                 size_unit_buf[1] = size_unit_abbrevs[select_conf.size_unit_arg];
655         else
656                 size_unit_buf[0] = '\0';
657
658         ret = open_dir_table(0);
659         if (ret < 0)
660                 goto out;
661         check_signals();
662         ret = read_uid_file(admissible_uids);
663         if (ret < 0)
664                 goto out;
665         check_signals();
666         ret = print_statistics(sfi);
667 out:
668         close_all_tables();
669         if (output_file != stdout)
670                 fclose(output_file);
671         return ret;
672 }
673
674 /* return: < 0: error, >0: OK, == 0: help given */
675 int parse_select_options(char *string, struct select_cmdline_parser_params *params,
676                 struct uid_range **admissible_uids, struct select_format_info *sfi)
677 {
678         int ret;
679         const char **line;
680
681         if (conf.select_options_given) {
682                 int argc;
683                 char **argv;
684
685                 ret = create_argv(string, &argv);
686                 if (ret < 0)
687                         return ret;
688                 argc = ret;
689                 ret = select_cmdline_parser_ext(argc, argv, &select_conf, params);
690                 free_argv(argv);
691                 if (ret)
692                         return -E_SYNTAX;
693                 if (select_conf.help_given || select_conf.detailed_help_given)
694                         goto help;
695
696         }
697         ret = parse_uid_arg(select_conf.uid_arg, admissible_uids);
698         if (ret < 0)
699                 return ret;
700         ret = parse_format_string(select_conf.user_summary_format_arg,
701                 user_summary_atoms, &sfi->user_summary_fi);
702         if (ret < 0)
703                 return ret;
704         ret = parse_format_string(select_conf.global_summary_format_arg,
705                 global_summary_atoms, &sfi->global_summary_fi);
706         if (ret < 0)
707                 goto global_summary_err;
708         ret = parse_format_string(select_conf.global_list_format_arg,
709                 global_list_atoms, &sfi->global_list_fi);
710         if (ret < 0)
711                 goto global_list_err;
712         return 1;
713 global_list_err:
714         free_format_info(sfi->global_summary_fi);
715 global_summary_err:
716         free_format_info(sfi->user_summary_fi);
717         return ret;
718 help:
719         line = select_conf.detailed_help_given?
720                 select_args_info_detailed_help : select_args_info_help;
721         if (!output_file)
722                 output_file = stdout;
723         for (; *line; line++) {
724                 ret = output("%s\n", *line);
725                 if (ret < 0)
726                         return ret;
727         }
728         return 0;
729 }
730
731 int com_select(void)
732 {
733         struct uid_range *admissible_uids = NULL;
734         struct select_format_info sfi;
735         int ret;
736         struct select_cmdline_parser_params params = {
737                 .override = 1,
738                 .initialize = 1,
739                 .check_required = 1,
740                 .check_ambiguity = 1,
741                 .print_errors = 1
742         };
743
744         select_cmdline_parser_init(&select_conf);
745         ret = parse_select_options(conf.select_options_arg, &params,
746                 &admissible_uids, &sfi);
747         if (ret <= 0) /* do not run query if help was given */
748                 return ret;
749         return run_select_query(admissible_uids, &sfi);
750 }