]> git.tuebingen.mpg.de Git - adu.git/blob - select.c
7537f4cae3b638bccf6c51c23282ccb68b9b693c
[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 "user.h"
18 #include "select.cmdline.h"
19
20 /* global list */
21 #define GLOBAL_LIST_ATOMS \
22         ATOM(size, SIZE) \
23         ATOM(files, COUNT) \
24         ATOM(dirname, STRING) \
25
26 #define ATOM(x, y) { .name = #x, .type = AT_ ## y},
27 struct atom global_list_atoms[] = {
28         GLOBAL_LIST_ATOMS
29         {.name = NULL}
30 };
31 #undef ATOM
32 #define ATOM(x, y) gla_ ## x,
33 enum global_list_atoms {GLOBAL_LIST_ATOMS};
34 #undef ATOM
35
36 /* global summary */
37 #define GLOBAL_SUMMARY_ATOMS \
38         ATOM(dirs, COUNT) \
39         ATOM(files, COUNT) \
40         ATOM(size, SIZE)
41
42 #define ATOM(x, y) { .name = #x, .type = AT_ ## y},
43 struct atom global_summary_atoms[] = {
44         GLOBAL_SUMMARY_ATOMS
45         {.name = NULL}
46 };
47 #undef ATOM
48 #define ATOM(x, y) gsa_ ## x,
49 enum global_summary_atoms {GLOBAL_SUMMARY_ATOMS};
50 #undef ATOM
51
52 /* user list */
53 #define USER_LIST_ATOMS \
54         ATOM(pw_name, STRING) \
55         ATOM(uid, ID) \
56         ATOM(size, SIZE) \
57         ATOM(files, COUNT) \
58         ATOM(dirname, STRING) \
59
60 #define ATOM(x, y) { .name = #x, .type = AT_ ## y},
61 struct atom user_list_atoms[] = {
62         USER_LIST_ATOMS
63         {.name = NULL}
64 };
65 #undef ATOM
66 #define ATOM(x, y) ula_ ## x,
67 enum user_list_atoms {USER_LIST_ATOMS};
68 #undef ATOM
69
70 /* user summary */
71 #define USER_SUMMARY_ATOMS \
72         ATOM(pw_name, STRING) \
73         ATOM(uid, ID) \
74         ATOM(dirs, COUNT) \
75         ATOM(files, COUNT) \
76         ATOM(size, SIZE)
77
78 #define ATOM(x, y) { .name = #x, .type = AT_ ## y},
79 struct atom user_summary_atoms[] = {
80         USER_SUMMARY_ATOMS
81         {.name = NULL}
82 };
83 #undef ATOM
84 #define ATOM(x, y) usa_ ## x,
85 enum user_summary_atoms {USER_SUMMARY_ATOMS};
86 #undef ATOM
87
88 struct global_list_info {
89         uint32_t count;
90         int ret;
91         int osl_errno;
92         struct format_info *fi;
93 };
94
95 struct global_summary_info {
96         /** Global dir count. */
97         uint64_t num_dirs;
98         /** Global files count. */
99         uint64_t num_files;
100         /** Global bytes count. */
101         uint64_t num_bytes;
102         int ret;
103         int osl_errno;
104 };
105
106 struct user_list_info {
107         uint32_t count;
108         struct user_info *ui;
109         struct format_info *fi;
110         int ret;
111         int osl_errno;
112 };
113
114 struct user_summary_info {
115         struct user_info *ui;
116         int ret;
117         int osl_errno;
118 };
119
120 struct user_summary_line_info {
121         struct format_info *fi;
122         uint32_t count;
123 };
124
125 static FILE *output_file;
126
127 __printf_1_2 static int output(const char const *fmt, ...)
128 {
129         va_list argp;
130         int ret;
131
132         va_start(argp, fmt);
133         ret = vfprintf(output_file, fmt, argp);
134         va_end(argp);
135         return ret < 0? -E_OUTPUT : 1;
136 }
137
138 static int get_dir_name_by_number(uint64_t *dirnum, char **name)
139 {
140         char *result = NULL, *tmp;
141         struct osl_row *row;
142         uint64_t val = *dirnum;
143         struct osl_object obj;
144         int ret;
145         char *pfx;
146
147 again:
148         obj.data = &val;
149         obj.size = sizeof(val);
150         ret = osl(osl_get_row(dir_table, DT_NUM, &obj, &row));
151         if (ret < 0)
152                 goto out;
153         ret = osl(osl_get_object(dir_table, row, DT_PARENT_NUM, &obj));
154         if (ret < 0)
155                 goto out;
156         val = *(uint64_t *)obj.data;
157         ret = osl(osl_get_object(dir_table, row, DT_NAME, &obj));
158         if (ret < 0)
159                 goto out;
160         pfx = (select_conf.print_base_dir_given || val)? (char *)obj.data :  ".";
161         tmp = make_message("%s/%s", pfx, result? result : "");
162         free(result);
163         result = tmp;
164         if (val)
165                 goto again;
166 out:
167         if (ret < 0) {
168                 free(result);
169                 *name = NULL;
170         } else {
171                 assert(result);
172                 *name = result;
173         }
174         return ret;
175 }
176
177 static int get_dir_name_of_row(struct osl_row *dir_table_row, char **name)
178 {
179         struct osl_object obj;
180         int ret;
181
182         *name = NULL;
183         ret = osl(osl_get_object(dir_table, dir_table_row, DT_NUM, &obj));
184         if (ret < 0)
185                 return ret;
186         return get_dir_name_by_number((uint64_t *)obj.data, name);
187 }
188
189 static int get_dir_name_of_user_row(struct osl_row *user_table_row,
190                 struct user_info *ui, char **dirname)
191 {
192         struct osl_object obj;
193         int ret = osl(osl_get_object(ui->table, user_table_row,
194                 UT_DIR_NUM, &obj));
195
196         if (ret < 0)
197                 return ret;
198         return get_dir_name_by_number((uint64_t *)obj.data, dirname);
199 }
200
201 static int get_num_files_of_row(struct osl_row *row, uint64_t *num_files)
202 {
203         struct osl_object obj;
204         int ret = osl(osl_get_object(dir_table, row, DT_FILES, &obj));
205         if (ret < 0)
206                 return ret;
207         *num_files = *(uint64_t *)obj.data;
208         return 1;
209 }
210
211 static int get_num_user_files(struct osl_row *row, struct user_info *ui,
212                 uint64_t *num_files)
213 {
214         struct osl_object obj;
215         int ret = osl(osl_get_object(ui->table, row, UT_FILES, &obj));
216
217         if (ret < 0)
218                 return ret;
219         *num_files = *(uint64_t *)obj.data;
220         return 1;
221 }
222
223 static int get_num_bytes_of_row(struct osl_row *row, uint64_t *num_bytes)
224 {
225         struct osl_object obj;
226         int ret = osl(osl_get_object(dir_table, row, DT_BYTES, &obj));
227         if (ret < 0)
228                 return ret;
229         *num_bytes = *(uint64_t *)obj.data;
230         return 1;
231 }
232
233 static int get_num_user_bytes(struct osl_row *row, struct user_info *ui,
234                 uint64_t *num_bytes)
235 {
236         struct osl_object obj;
237         int ret = osl(osl_get_object(ui->table, row, UT_BYTES, &obj));
238
239         if (ret < 0)
240                 return ret;
241         *num_bytes = *(uint64_t *)obj.data;
242         return 1;
243 }
244
245 static int check_loop_return(int ret, int loop_ret, int loop_osl_errno)
246 {
247         if (ret >= 0)
248                 return ret;
249         assert(ret == -E_OSL);
250         if (osl_errno != E_OSL_LOOP)
251                 /* error not caused by loop function returning negative. */
252                 return ret;
253         assert(loop_ret < 0);
254         if (loop_ret == -E_LOOP_COMPLETE) /* no error */
255                 return 1;
256         if (loop_ret == -E_OSL) { /* osl error in loop function */
257                 assert(loop_osl_errno);
258                 osl_errno = loop_osl_errno;
259         }
260         return loop_ret;
261 }
262
263 static int adu_loop_reverse(struct osl_table *t, unsigned col_num, void *private_data,
264                 osl_rbtree_loop_func *func, int *loop_ret, int *loop_osl_errno)
265 {
266         int ret = osl(osl_rbtree_loop_reverse(t, col_num, private_data, func));
267         return check_loop_return(ret, *loop_ret, *loop_osl_errno);
268 }
269
270 static int global_summary_loop_function(struct osl_row *row, void *data)
271 {
272         struct global_summary_info *gsi = data;
273         int ret;
274         uint64_t num;
275
276         ret = get_num_files_of_row(row, &num);
277         if (ret < 0)
278                 goto err;
279         gsi->num_files += num;
280
281         ret = get_num_bytes_of_row(row, &num);
282         if (ret < 0)
283                 goto err;
284         gsi->num_bytes += num;
285         gsi->num_dirs++;
286         return 1;
287 err:
288         gsi->ret = ret;
289         gsi->osl_errno = (ret == -E_OSL)? osl_errno : 0;
290         return ret;
291 }
292
293 static int print_global_summary(struct format_info *fi)
294 {
295         int ret;
296         char *buf;
297         struct global_summary_info gsi = {.num_dirs = 0};
298
299         union atom_value values[] = {
300                 [gsa_dirs] = {.num_value = 0ULL},
301                 [gsa_files] = {.num_value =  0ULL},
302                 [gsa_size] = {.num_value =  0ULL}
303         };
304
305         ret = adu_loop_reverse(dir_table, DT_BYTES, &gsi,
306                 global_summary_loop_function, &gsi.ret, &gsi.osl_errno);
307         if (ret < 0)
308                 return ret;
309         values[gsa_dirs].num_value = (long long unsigned)gsi.num_dirs;
310         values[gsa_files].num_value = (long long unsigned)gsi.num_files;
311         values[gsa_size].num_value = (long long unsigned)gsi.num_bytes;
312         if (!select_conf.no_headers_given) {
313                 ret = output("Global summary\n");
314                 if (ret < 0)
315                         return ret;
316         }
317         buf = format_items(fi, values);
318         ret = output("%s", buf);
319         free(buf);
320         return ret;
321 }
322
323 static int user_summary_loop_function(struct osl_row *row, void *data)
324 {
325         struct user_summary_info *usi = data;
326         uint64_t num;
327         int ret;
328
329         ret = get_num_user_files(row, usi->ui, &num);
330         if (ret < 0)
331                 goto err;
332         usi->ui->files += num;
333         ret = get_num_user_bytes(row, usi->ui, &num);
334         if (ret < 0)
335                 goto err;
336         usi->ui->bytes += num;
337         usi->ui->dirs++;
338         return 1;
339 err:
340         usi->ret = ret;
341         usi->osl_errno = (ret == -E_OSL)? osl_errno : 0;
342         return ret;
343 }
344
345 static int compute_user_summary(struct user_info *ui, __a_unused void *data)
346 {
347         struct user_summary_info usi = {.ui = ui};
348
349         return adu_loop_reverse(ui->table, UT_BYTES, &usi, user_summary_loop_function,
350                 &usi.ret, &usi.osl_errno);
351 }
352
353 static int print_user_summary_line(struct user_info *ui, void *data)
354 {
355         struct user_summary_line_info *usli = data;
356         union atom_value values[] = {
357                 [usa_pw_name] = {.string_value = ui->pw_name?
358                         ui->pw_name : "?"},
359                 [usa_uid] = {.num_value = (long long unsigned)ui->uid},
360                 [usa_dirs] = {.num_value = (long long unsigned)ui->dirs},
361                 [usa_files] = {.num_value = (long long unsigned)ui->files},
362                 [usa_size] = {.num_value =  (long long unsigned)ui->bytes}
363         };
364         char *buf;
365         int ret = -E_LOOP_COMPLETE;
366
367         if (!usli->count)
368                 return ret;
369
370         buf = format_items(usli->fi, values);
371         ret = output("%s", buf);
372         free(buf);
373         usli->count--;
374         return ret;
375 }
376
377 static int name_comp(struct user_info *a, struct user_info *b)
378 {
379         char *x = a->pw_name;
380         char *y = b->pw_name;
381
382         if (!x)
383                 return 1;
384         if (!y)
385                 return -1;
386         return strcmp(x, y);
387 }
388
389 static int uid_comp(struct user_info *a, struct user_info *b)
390 {
391         return -NUM_COMPARE(a->uid, b->uid);
392 }
393
394 static int dir_count_comp(struct user_info *a, struct user_info *b)
395 {
396         return NUM_COMPARE(a->dirs, b->dirs);
397 }
398
399 static int file_count_comp(struct user_info *a, struct user_info *b)
400 {
401         return NUM_COMPARE(a->files, b->files);
402 }
403
404 static int size_comp(struct user_info *a, struct user_info *b)
405 {
406         return NUM_COMPARE(a->bytes, b->bytes);
407 }
408
409 static int print_user_summary(struct format_info *fi)
410 {
411         int ret;
412         int (*comp)(struct user_info *a, struct user_info *b);
413         struct user_summary_line_info usli = {
414                 .fi = fi,
415                 .count = select_conf.limit_arg
416         };
417
418         if (!select_conf.no_headers_given) {
419                 ret = output("User summary\n");
420                 if (ret < 0)
421                         return ret;
422         }
423         ret = for_each_admissible_user(compute_user_summary, NULL);
424         if (ret < 0)
425                 return ret;
426         switch (select_conf.user_summary_sort_arg) {
427         case user_summary_sort_arg_name:
428                 comp = name_comp;
429                 break;
430         case user_summary_sort_arg_uid:
431                 comp = uid_comp;
432                 break;
433         case user_summary_sort_arg_dir_count:
434                 comp = dir_count_comp;
435                 break;
436         case user_summary_sort_arg_file_count:
437                 comp = file_count_comp;
438                 break;
439         case user_summary_sort_arg_size:
440                 comp = size_comp;
441                 break;
442         }
443         sort_hash_table(comp);
444         ret = for_each_admissible_user(print_user_summary_line, &usli);
445         if (ret == -E_LOOP_COMPLETE)
446                 ret = 1;
447         return ret;
448 }
449
450 static int user_list_loop_function(struct osl_row *row, void *data)
451 {
452         struct user_list_info *uli = data;
453         union atom_value values[] = {
454                 [ula_pw_name] = {.string_value = uli->ui->pw_name?
455                         uli->ui->pw_name : "?"},
456                 [ula_uid] = {.num_value = (long long unsigned)uli->ui->uid},
457                 [ula_files] = {.num_value = 0ULL},
458                 [ula_size] = {.num_value =  0ULL},
459                 [ula_dirname] = {.string_value = NULL}
460         };
461         uint64_t num;
462         int ret;
463         char *dirname, *buf;
464
465         check_signals();
466         ret = -E_LOOP_COMPLETE;
467         if (!uli->count)
468                 goto err;
469
470         ret = get_num_user_files(row, uli->ui, &num);
471         if (ret < 0)
472                 goto err;
473         values[ula_files].num_value = num;
474
475         ret = get_num_user_bytes(row, uli->ui, &num);
476         if (ret < 0)
477                 goto err;
478         values[ula_size].num_value = num;
479
480         ret = get_dir_name_of_user_row(row, uli->ui, &dirname);
481         if (ret < 0)
482                 goto err;
483         values[ula_dirname].string_value = dirname;
484
485         buf = format_items(uli->fi, values);
486         free(dirname);
487         ret = output("%s", buf);
488         free(buf);
489         if (ret < 0)
490                 goto err;
491         uli->count--;
492         return ret;
493 err:
494         uli->ret = ret;
495         uli->osl_errno = (ret == -E_OSL)? osl_errno : 0;
496         return ret;
497 }
498
499 static int print_user_list(struct user_info *ui, void *data)
500 {
501         struct format_info *fi = data;
502         int ret;
503         enum user_table_columns sort_column = UT_BYTES;
504         struct user_list_info uli = {
505                 .ui = ui,
506                 .fi = fi,
507                 .count = select_conf.limit_arg
508         };
509
510         if (select_conf.list_sort_arg == list_sort_arg_file_count)
511                 sort_column = UT_FILES;
512
513         if (!select_conf.no_headers_given) {
514                 ret = output("%s (uid %u)\n",
515                         ui->pw_name? ui->pw_name : "?", (unsigned)ui->uid);
516                 if (ret < 0)
517                         return ret;
518         }
519         return adu_loop_reverse(ui->table, sort_column, &uli, user_list_loop_function,
520                 &uli.ret, &uli.osl_errno);
521 }
522
523 static int global_list_loop_function(struct osl_row *row, void *data)
524 {
525         struct global_list_info *gli = data;
526         union atom_value values[] = {
527                 [gla_size] = {.num_value = 0ULL},
528                 [gla_files] = {.num_value =  0ULL},
529                 [gla_dirname] = {.string_value = NULL}
530         };
531         uint64_t num_files, num_bytes;
532         char *dirname, *buf;
533         int ret;
534
535         check_signals();
536         ret = -E_LOOP_COMPLETE;
537         if (!gli->count)
538                 goto err;
539
540         ret = get_num_files_of_row(row, &num_files);
541         if (ret < 0)
542                 goto err;
543         values[gla_files].num_value = (long long unsigned)num_files;
544
545         ret = get_num_bytes_of_row(row, &num_bytes);
546         if (ret < 0)
547                 goto err;
548         values[gla_size].num_value = (long long unsigned)num_bytes;
549
550         ret = get_dir_name_of_row(row, &dirname);
551         if (ret < 0)
552                 goto err;
553         values[gla_dirname].string_value = dirname;
554
555         buf = format_items(gli->fi, values);
556         free(dirname);
557         ret = output("%s", buf);
558         free(buf);
559         if (ret < 0)
560                 goto err;
561         if (gli->count > 0)
562                 gli->count--;
563         return ret;
564 err:
565         gli->ret = ret;
566         gli->osl_errno = (ret == -E_OSL)? osl_errno : 0;
567         return -1;
568 }
569
570 static int print_global_list(struct format_info *fi)
571 {
572         int ret;
573         enum dir_table_columns sort_column = DT_BYTES;
574         struct global_list_info gli = {
575                 .fi = fi,
576                 .count = select_conf.limit_arg
577         };
578
579         if (!select_conf.no_headers_given) {
580                 ret = output("Global list\n");
581                 if (ret < 0)
582                         return ret;
583         }
584         if (select_conf.list_sort_arg == list_sort_arg_file_count)
585                 sort_column = DT_FILES;
586         return adu_loop_reverse(dir_table, sort_column, &gli,
587                 global_list_loop_function, &gli.ret, &gli.osl_errno);
588 }
589
590 static int print_statistics(struct format_info *fi)
591 {
592         switch (select_conf.select_mode_arg) {
593                 case select_mode_arg_global_list:
594                         return print_global_list(fi);
595                 case select_mode_arg_global_summary:
596                         return print_global_summary(fi);
597                 case select_mode_arg_user_list:
598                         return for_each_admissible_user(print_user_list, fi);
599                 case select_mode_arg_user_summary:
600                         return print_user_summary(fi);
601         };
602         ERROR_LOG("bad select mode\n");
603         return -ERRNO_TO_ERROR(EINVAL);
604 }
605
606 static int open_pipe(char *path)
607 {
608         int p[2], ret, argc;
609         char **argv;
610
611         ret = pipe(p);
612         if (ret < 0)
613                 return ERRNO_TO_ERROR(errno);
614         ret = fork();
615         if (ret < 0)
616                 return ERRNO_TO_ERROR(errno);
617         if (ret) { /* parent */
618                 DEBUG_LOG("created process %d\n", ret);
619                 close(p[0]);
620                 output_file = fdopen(p[1], "w");
621                 if (!output_file)
622                         return ERRNO_TO_ERROR(errno);
623                 return 1;
624         }
625         close(p[1]);
626         if (p[0] != STDIN_FILENO)
627                 dup2(p[0], STDIN_FILENO);
628         DEBUG_LOG("executing %s\n", path);
629         argc = split_args(path, &argv, " \t");
630         execvp(argv[0], argv);
631         ERROR_LOG("error executing %s: %s\n", path,
632                 adu_strerror(ERRNO_TO_ERROR(errno)));
633         _exit(EXIT_FAILURE);
634 }
635
636 static int open_output_stream(void)
637 {
638         char *p;
639         int ret, flags = O_WRONLY | O_CREAT;
640
641         if (!select_conf.output_given)
642                 goto use_stdout;
643         p = select_conf.output_arg;
644         switch (p[0]) {
645         case '\0': /* empty string */
646                 goto bad_output_arg;
647         case '-':
648                 if (!p[1]) /* "-" means stdout */
649                         goto use_stdout;
650                 /* string starting with a dash */
651                 flags |= O_EXCL;
652                 goto open_file;
653         case '>':
654                 if (!p[1]) /* ">" is invalid */
655                         goto bad_output_arg;
656                 if (p[1] != '>') {
657                         p++;
658                         flags |= O_TRUNC;
659                         goto open_file;
660                 }
661                 /* string starting with ">>" */
662                 if (!p[2]) /* ">>" is invalid */
663                         goto bad_output_arg;
664                 flags |= O_APPEND;
665                 p += 2;
666                 goto open_file;
667         case '|':
668                 if (!p[1]) /* "|" is invalid */
669                         goto bad_output_arg;
670                 p++;
671                 return open_pipe(p);
672         default: /* args starts with no magic character */
673                 flags |= O_EXCL;
674                 goto open_file;
675         }
676 use_stdout:
677         output_file = stdout;
678         return 1;
679 bad_output_arg:
680         output_file = NULL;
681         return -E_BAD_OUTPUT_ARG;
682 open_file:
683         /*
684          * glibc's 'x' mode to fopen is not portable, so use open() and
685          * fdopen().
686          */
687         ret = open(p, flags, 0644);
688         if (ret < 0)
689                 return -ERRNO_TO_ERROR(errno);
690         output_file = fdopen(ret, "w");
691         if (!output_file)
692                 return -ERRNO_TO_ERROR(errno);
693         return 1;
694 }
695
696 int run_select_query(struct uid_range *admissible_uids, struct format_info *fi)
697 {
698         int ret = open_output_stream();
699
700         if (ret < 0)
701                 goto out;
702         ret = open_dir_table(0);
703         if (ret < 0)
704                 goto out;
705         check_signals();
706         ret = open_admissible_user_tables(admissible_uids);
707         if (ret < 0)
708                 goto out;
709         check_signals();
710         ret = print_statistics(fi);
711 out:
712         if (output_file && output_file != stdout) {
713                 fclose(output_file);
714                 output_file = NULL;
715         }
716         return ret;
717 }
718
719 #define GLOBAL_LIST_DFLT_FMT "%(size:r:8) %(files:r:8) %(dirname)\n"
720 #define GLOBAL_SUMMARY_DFLT_FMT "#directories: %(dirs), #files: %(files), size: %(size)\n\n"
721 #define USER_LIST_DFLT_FMT "%(size:r:5) %(files:r:5) %(dirname)\n"
722 #define USER_SUMMARY_DFLT_FMT "%(pw_name:l:16) %(uid:r:5) %(dirs:r:5) %(files:r:5) %(size:r:5)\n"
723
724 static int setup_format_string(char *fmt, struct format_info **fi)
725 {
726         struct atom *atoms;
727
728         if (!fmt)
729                 INFO_LOG("using default format string\n");
730         switch (select_conf.select_mode_arg) {
731         case select_mode_arg_global_list:
732                 if (!fmt)
733                         fmt = GLOBAL_LIST_DFLT_FMT;
734                 atoms = global_list_atoms;
735                 break;
736         case select_mode_arg_global_summary:
737                 if (!fmt)
738                         fmt = GLOBAL_SUMMARY_DFLT_FMT;
739                 atoms = global_summary_atoms;
740                 break;
741         case select_mode_arg_user_list:
742                 if (!fmt)
743                         fmt = USER_LIST_DFLT_FMT;
744                 atoms = user_list_atoms;
745                 break;
746         case select_mode_arg_user_summary:
747                 if (!fmt)
748                         fmt = USER_SUMMARY_DFLT_FMT;
749                 atoms = user_summary_atoms;
750                 break;
751         default:
752                 ERROR_LOG("bad select mode\n");
753                 return -ERRNO_TO_ERROR(EINVAL);
754         };
755         INFO_LOG("format string: %s\n", fmt);
756         return parse_format_string(fmt, atoms, fi);
757 }
758
759 /* return: < 0: error, >0: OK, == 0: help given */
760 int parse_select_options(char *string, struct select_cmdline_parser_params *params,
761                 struct uid_range **admissible_uids, struct format_info **fi)
762 {
763         int ret, num_uid_ranges;
764         const char **line;
765         char *fmt = NULL;
766
767         if (string) {
768                 int argc;
769                 char **argv;
770
771                 ret = create_argv(string, &argv);
772                 if (ret < 0)
773                         return ret;
774                 argc = ret;
775                 ret = select_cmdline_parser_ext(argc, argv, &select_conf, params);
776                 free_argv(argv);
777                 if (ret)
778                         return -E_SYNTAX;
779                 if (select_conf.help_given || select_conf.detailed_help_given)
780                         goto help;
781                 fmt = select_conf.format_arg;
782         }
783         ret = parse_uid_arg(select_conf.uid_arg, admissible_uids);
784         if (ret < 0)
785                 return ret;
786         num_uid_ranges = ret;
787         ret = append_users(select_conf.user_arg, select_conf.user_given,
788                 admissible_uids, num_uid_ranges);
789         if (ret < 0)
790                 return ret;
791         return setup_format_string(fmt, fi);
792 help:
793         line = select_conf.detailed_help_given?
794                 select_args_info_detailed_help : select_args_info_help;
795         if (!output_file)
796                 output_file = stdout;
797         for (; *line; line++) {
798                 ret = output("%s\n", *line);
799                 if (ret < 0)
800                         return ret;
801         }
802         return 0;
803 }
804
805 int com_select(void)
806 {
807         struct uid_range *admissible_uids = NULL;
808         int ret;
809         struct format_info *fi;
810         struct select_cmdline_parser_params params = {
811                 .override = 1,
812                 .initialize = 1,
813                 .check_required = 1,
814                 .check_ambiguity = 1,
815                 .print_errors = 1
816         };
817
818         ret = parse_select_options(conf.select_options_arg, &params,
819                 &admissible_uids, &fi);
820         if (ret > 0) {
821                 ret = read_uid_file();
822                 if (ret < 0)
823                         goto out;
824                 ret = run_select_query(admissible_uids, fi);
825                 free_format_info(fi);
826         }
827 out:
828         select_cmdline_parser_free(&select_conf);
829         return ret;
830 }