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 }