Really fix the dup warning.
[userinfo.git] / src / ui.c
blobb09a250a937fd439663c35cf0bdc2ea1193b3307
1 /* $Id: ui.c,v 2.4 2005-07-30 15:21:21 bjk Exp $ */
2 /*
3 Copyright (C) 2001-2005 Ben Kibbey <bjk@arbornet.org>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #include <stdio.h>
20 #include <unistd.h>
21 #include <stdlib.h>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <errno.h>
25 #include <ctype.h>
26 #include <pwd.h>
27 #include <time.h>
29 #ifdef HAVE_CONFIG_H
30 #include <config.h>
31 #endif
33 #ifndef HAVE_ERR_H
34 #include "err.c"
35 #endif
37 #include "ui.h"
39 static void *Realloc(void *p, size_t size)
41 void *p2;
43 if ((p2 = realloc(p, size)) == NULL)
44 err(EXIT_FAILURE, "%s", "realloc()");
46 return p2;
49 /* This may be used in modules to keep a consistant time format with other
50 * modules. */
51 char *stamp(time_t epoch, const char *format)
53 static char buf[TIMEBUFSIZE];
54 struct tm *t;
56 t = localtime(&epoch);
57 strftime(buf, sizeof(buf), format, t);
59 return buf;
62 /*
63 * This may be used in modules to add a string to the buffer (ui_module_exec()).
65 void add_string(char ***buf, const char *str)
67 char **s;
68 int i = 0;
70 if (*buf) {
71 for (s = *buf; *s; *s++)
72 i++;
75 s = *buf;
76 s = Realloc(s, (i + 2) * sizeof(char *));
77 s[i++] = strdup(str);
78 s[i] = NULL;
79 *buf = s;
80 return;
83 /* This is for the field separators (-F and -m). */
84 static int escapes(const char *str)
86 int c = 0;
88 if (str[0] != '\\')
89 return str[0];
91 switch (*++str) {
92 case 't':
93 c = '\t';
94 break;
95 case 'n':
96 c = '\n';
97 break;
98 case '\\':
99 c = '\\';
100 break;
101 case 'v':
102 c = '\v';
103 break;
104 case 'b':
105 c = '\b';
106 break;
107 case 'f':
108 c = '\f';
109 break;
110 case 'r':
111 c = '\r';
112 break;
113 case '\'':
114 c = '\'';
115 break;
116 default:
117 c = 0;
118 break;
121 return c;
124 /* Help text. Module help text is displayed after this. */
125 static void usage_header()
127 printf("Usage: %s [-vhVlf] [-c <filename>] [-t fmt] [-m c] [-F c] [-d]\n"
128 "\t[[-xX] -O <module1> [options] [-- [-xX] -O <module2> [...]]]\n"
129 "\t[- | username | filename] [...]\n\n", __progname);
130 return;
133 /* Help text. Module help text is displayed before this. */
134 static void usage()
136 printf(" -d\tLoad the default modules (passwd.so, mail.so, and login.so).\n");
137 printf(" -c\tRead a configuration file. Can be used more than once.\n");
138 printf(" -O\tLoad a module. Can be used more than once.\n");
139 printf(" -x\tChain module1's output to module2's input.\n");
140 printf(" -X\tDon't output module1's info, only chain it.\n");
141 printf(" -F c\tSeparate output with the specified character "
142 "('%c').\n", delimchar);
143 printf(" -m c\tSeparate multi-string values with the specified "
144 "character ('%c').\n", multichar);
145 printf(" -t tf\tstrftime(3) time format ('%s').\n", DEFAULT_TIMEFORMAT);
146 printf(" -f\tUsers are the owners of the specified files.\n");
147 printf(" -l\tFollow symbolic links.\n");
148 printf(" -v\tVerbose output when possible.\n");
149 printf(" -h\tThis help text.\n");
150 printf(" -V\tVersion information.\n\n");
151 printf("Output key: %s=unknown/error, %s=none, %s=yes/on, "
152 "%s=no/off\n", UNKNOWN, NONE, ON, OFF);
153 return;
157 * Add a module to the array of loaded modules. The index argument is the
158 * current item number being added stored in an integer. The module is also
159 * initialized here with ui_module_init().
161 static int open_module(char *filename, int *index)
163 void *m;
164 module_init *init;
165 char *p, s[FILENAME_MAX];
166 int i;
167 int chainable = 0;
169 strncpy(s, filename, sizeof(s));
171 if ((p = strrchr(s, '/')) != NULL)
172 p++;
173 else {
174 strncpy(s, filename, sizeof(s));
175 p = s;
178 for (i = 0; i < module_index; i++) {
179 if (strcmp(p, modules[i].name) == 0) {
180 if (TEST_FLAG(modules[i].flags, MODULE_DUP))
181 break;
183 SET_FLAG(modules[i].flags, MODULE_DUP);
184 warnx("warning: a module by this name is already loaded (%s)", p);
188 if ((m = dlopen(filename, RTLD_LAZY)) == NULL) {
189 warnx("%s", dlerror());
190 chaining = 0;
191 chain_output = 1;
192 return 1;
195 modules = Realloc(modules, (module_index + 2) * sizeof(struct module));
196 modules[module_index].m = m;
198 strncpy(modules[module_index].name, p, sizeof(modules[module_index].name));
199 *index = module_index++;
201 if ((init = dlsym(modules[*index].m, "ui_module_init")) == NULL)
202 warnx("%s", dlerror());
203 else
204 (*init) (&chainable);
206 if (chainable)
207 SET_FLAG(modules[*index].flags, MODULE_CHAINABLE);
209 if (*index - 1 >= 0 && TEST_FLAG(modules[*index - 1].flags, MODULE_CHAINED) &&
210 !TEST_FLAG(modules[*index].flags, MODULE_CHAINABLE)) {
211 warnx("%s: this module is not chainable", modules[*index].name);
212 return 1;
215 /* Module chaining. See junction() for more info. */
216 if (chaining)
217 SET_FLAG(modules[*index].flags, MODULE_CHAINED);
219 if (chain_output)
220 SET_FLAG(modules[*index].flags, MODULE_OUTPUT);
222 chaining = 0;
223 chain_output = 1;
225 return 0;
228 /* This just free's up the array of modules. The modules should clean up after
229 * themselves via the ui_module_exit() function. */
230 static void cleanup_modules()
232 int i;
234 for (i = 0; i < module_index; i++) {
235 module_exit *e;
237 if ((e = dlsym(modules[i].m, "ui_module_exit")) == NULL)
238 warnx("%s", dlerror());
239 else
240 (*e) ();
242 dlclose(modules[i].m);
245 free(modules);
246 return;
249 static void output(char **s, const int sep, int which)
251 int i;
253 if (s) {
254 for (i = 0; s[i]; i++) {
255 printf("%s", s[i]);
257 if (s[i + 1])
258 printf("%c", sep);
262 if (which == OUTPUT_DONE)
263 printf("\n");
264 else if (which == OUTPUT_APPEND)
265 printf("%c", sep);
267 return;
270 /* Pass the argument to each loaded module. */
271 static int junction(const char *arg)
273 int i;
274 struct passwd *pw;
275 struct stat st;
276 int ret = EXIT_SUCCESS;
277 char **s = NULL;
279 if (usefile) {
280 if ((STAT(arg, &st)) == -1) {
281 warn("%s", arg);
282 return EXIT_FAILURE;
285 errno = 0;
287 if ((pw = getpwuid(st.st_uid)) == NULL) {
288 #ifdef __NetBSD__
289 warnx("%s: no such user", arg);
290 #else
291 if (errno == 0 || errno == ENOENT || errno == EPERM
292 || errno == EBADF || errno == ESRCH)
293 warnx("%s: no such uid %u", arg, st.st_uid);
294 else
295 warn("%s", "getpwuid()");
296 #endif
298 return EXIT_FAILURE;
301 else {
302 errno = 0;
304 if ((pw = getpwnam(arg)) == NULL) {
305 #ifdef __NetBSD__
306 warnx("%s: no such user", arg);
307 #else
308 if (errno == 0 || errno == ENOENT || errno == EPERM
309 || errno == EBADF || errno == ESRCH)
310 warnx("%s: no such user", arg);
311 else
312 warn("%s", "getpwnam()");
313 #endif
315 return EXIT_FAILURE;
319 for (i = 0; i < module_index; i++) {
320 module_exec *m_exec;
321 char **p;
323 if ((m_exec = dlsym(modules[i].m, "ui_module_exec")) == NULL) {
324 warnx("%s", dlerror());
325 continue;
328 ret |= (*m_exec) (&s, pw, multichar, verbose, tf);
330 if (!TEST_FLAG(modules[i].flags, MODULE_CHAINED) || (TEST_FLAG(modules[i].flags, MODULE_CHAINED) && TEST_FLAG(modules[i].flags, MODULE_OUTPUT))) {
331 output(s, delimchar,
332 ((i + 1) < module_index) ? OUTPUT_APPEND : OUTPUT_DONE);
334 if (!TEST_FLAG(modules[i].flags, MODULE_CHAINED)) {
335 for (p = s; *p; *p++) {
336 free(*p);
339 free(s);
340 s = NULL;
345 return ret;
348 /* Copy options for each module into it's own argc and argv variables stopping
349 * at -- (getopt(3)). */
350 static int init_module_options(int the_argc, char **the_argv, int index)
352 char tmp[255];
353 module_options *m;
354 module_options_init *o;
355 int old_optind = optind;
356 int argc = 0;
357 char **argv = NULL;
358 int opt;
359 int ret = EXIT_SUCCESS;
360 char *optstring = NULL;
361 char *defaults = NULL;
362 int have_an_argument = 0;
364 if ((o = dlsym(modules[index].m, "ui_module_options_init")) == NULL) {
365 warnx("%s", dlerror());
366 return EXIT_FAILURE;
369 if ((optstring = (*o) (&defaults))) {
370 argv = Realloc(argv, (argc + 2) * sizeof(char *));
371 argv[argc++] = strdup(__progname);
372 argv[argc] = NULL;
374 /* Probably a default module. */
375 if (the_argv == NULL)
376 goto blah;
378 while ((opt = getopt(the_argc, the_argv, optstring)) != -1) {
379 switch (opt) {
380 case '?':
381 warnx("%s: invalid option -- %c\n", modules[index].name,
382 optopt);
383 return EXIT_FAILURE;
384 default:
385 break;
388 argv = Realloc(argv, (argc + 2) * sizeof(char *));
389 snprintf(tmp, sizeof(tmp), "-%c%s", opt, (optarg) ? optarg : "");
390 argv[argc++] = strdup(tmp);
391 argv[argc] = NULL;
392 have_an_argument = 1;
395 else
396 goto skip_option_stuff;
398 blah:
400 * No options were specified for this module. Set the modules default
401 * options (ui_module_options_init()) if any.
403 if (!have_an_argument && defaults) {
404 argv = Realloc(argv, (argc + 2) * sizeof(char *));
405 snprintf(tmp, sizeof(tmp), "-%s", defaults);
406 argv[argc++] = strdup(tmp);
407 argv[argc] = NULL;
410 old_optind = optind;
411 opterr = optind = optopt = 1;
413 if ((m = dlsym(modules[index].m, "ui_module_options")) == NULL) {
414 warnx("%s", dlerror());
415 return EXIT_FAILURE;
418 ret |= (*m) (argc, argv);
419 optind = old_optind;
421 skip_option_stuff:
422 return ret;
426 * parseargs.c
428 * This will parse a line used as an argument list for the exec() line of
429 * functions returning a dynamically allocated array of character pointers so
430 * you should free() it afterwards. Both ' and " quoting is supported (with
431 * escapes) for multi-word arguments.
433 * This is my second attempt at it. Works alot better than the first. :)
435 * 2002/10/05
436 * Ben Kibbey <bjk@arbornet.org>
438 * 2004/11/07
439 * Modified to handle argv[0] and argc. (Ben Kibbey <bjk@arbornet.org>)
441 static char **parseargv(char *str, const char *progname, int *me_argc)
443 char **pptr, *s;
444 char arg[255];
445 int index = 0;
446 int quote = 0;
447 int lastchar = 0;
448 int i;
449 int my_argc = 0;
451 if (!str)
452 return NULL;
454 if (!(pptr = malloc(sizeof(char *))))
455 return NULL;
457 pptr = Realloc(pptr, (index + 2) * sizeof(char *));
458 pptr[index++] = strdup(progname);
459 my_argc++;
461 for (i = 0, s = str; *s; lastchar = *s++) {
462 if ((*s == '\"' || *s == '\'') && lastchar != '\\') {
463 quote = (quote) ? 0 : 1;
464 continue;
467 if (*s == ' ' && !quote) {
468 arg[i] = 0;
469 pptr = Realloc(pptr, (index + 2) * sizeof(char *));
470 pptr[index++] = strdup(arg);
471 my_argc++;
472 arg[0] = i = 0;
473 continue;
476 if ((i + 1) == sizeof(arg))
477 continue;
479 arg[i++] = *s;
482 arg[i] = 0;
484 if (arg[0]) {
485 pptr = Realloc(pptr, (index + 2) * sizeof(char *));
486 pptr[index++] = strdup(arg);
487 my_argc++;
490 pptr[index] = NULL;
491 *me_argc = my_argc;
492 return pptr;
495 static char *get_home_directory()
497 struct passwd *pw;
498 static char dir[FILENAME_MAX];
500 errno = 0;
502 if ((pw = getpwuid(getuid())) == NULL) {
503 if (errno)
504 warn("getpwuid()");
505 else
506 warnx("getpwuid(): no such uid");
508 return NULL;
511 strncpy(dir, pw->pw_dir, sizeof(dir));
512 return dir;
515 /* Read in a configuration file adding modules to the module array and
516 * checking any module options. */
517 static int parse_rc_file(const char *filename)
519 char line[LINE_MAX], *p;
520 FILE *fp;
521 int index;
522 int old_optind = optind;
524 if ((fp = fopen(filename, "r")) == NULL) {
525 warn("%s", filename);
526 return 1;
529 while ((p = fgets(line, sizeof(line), fp)) != NULL) {
530 char name[FILENAME_MAX], options[LINE_MAX], tmp[LINE_MAX], *s;
531 int my_argc;
532 char **my_argv;
533 int lastchar = '\0';
535 while (*p && isspace((unsigned char) *p))
536 p++;
538 if (*p == '#')
539 continue;
541 s = name;
543 if (*p == '>' || *p == '-') {
544 chaining = 1;
546 if (*p == '-')
547 chain_output = 0;
549 p++;
552 while (*p && *p != ' ' && *p != '\t') {
553 if (*p == '\n') {
554 p++;
555 break;
558 *s++ = *p++;
561 *s = '\0';
563 if (!name[0])
564 continue;
566 s = options;
568 while (*p && isspace((unsigned char) *p))
569 p++;
571 lastchar = *p;
573 while (*p) {
574 if (*p == '\n' || (*p == '#' && lastchar != '\\'))
575 break;
577 if (*p == '#' && lastchar == '\\') {
578 lastchar = *--s = *p++;
579 s++;
580 continue;
583 lastchar = *s++ = *p++;
586 *s = '\0';
587 p = name;
589 if (*p == '~') {
590 s = get_home_directory();
591 strncpy(tmp, s, sizeof(tmp));
592 p++;
593 strncat(tmp, p, sizeof(tmp));
594 strncpy(name, tmp, sizeof(name));
597 if (open_module(name, &index))
598 continue;
600 if ((my_argv = parseargv(options, __progname, &my_argc)) == NULL)
601 continue;
603 optind = 0;
605 if (init_module_options(my_argc, my_argv, index)) {
606 fclose(fp);
607 return 2;
610 optind = old_optind;
613 fclose(fp);
614 return 0;
617 int main(int argc, char *argv[])
619 int i = 0;
620 int ret = EXIT_SUCCESS;
621 int opt;
622 char line[LINE_MAX], *s = NULL;
623 int want_help = 0;
625 #ifndef HAVE___PROGNAME
626 __progname = argv[0];
627 #endif
628 delimchar = DEFAULT_DELIMINATING_CHAR;
629 multichar = DEFAULT_MULTI_CHAR;
630 strncpy(tf, DEFAULT_TIMEFORMAT, sizeof(tf));
631 chain_output = 1;
633 while ((opt = getopt(argc, argv, "+x:X:dm:c:hO:F:t:vVlf")) != -1) {
635 * See getopt(3).
637 opterr = 0;
639 switch (opt) {
640 case 'd':
641 i = module_index;
643 if (open_module("passwd.so", &i) == 0) {
644 if (init_module_options(1, NULL, i))
645 want_help = 1;
647 else {
648 ret = EXIT_FAILURE;
649 goto cleanup;
652 if (open_module("mail.so", &i) == 0) {
653 if (init_module_options(1, NULL, i))
654 want_help = 1;
656 else {
657 ret = EXIT_FAILURE;
658 goto cleanup;
661 if (open_module("login.so", &i) == 0) {
662 if (init_module_options(1, NULL, i))
663 want_help = 1;
665 else {
666 ret = EXIT_FAILURE;
667 goto cleanup;
670 break;
671 case 'm':
672 if ((optarg[0] != '\\' && strlen(optarg) > 1) ||
673 (optarg[0] == '\\' && strlen(optarg) != 2)) {
674 want_help = 1;
675 break;
678 if ((multichar = escapes(optarg)) == 0)
679 want_help = 1;
681 break;
682 case 'c':
683 if ((ret = parse_rc_file(optarg)) != 0) {
684 if (ret == 2)
685 want_help = 1;
686 else
687 exit(EXIT_FAILURE);
689 break;
690 case 'F':
691 if ((optarg[0] != '\\' && strlen(optarg) > 1) ||
692 (optarg[0] == '\\' && strlen(optarg) != 2)) {
693 want_help = 1;
694 break;
697 if ((delimchar = escapes(optarg)) == 0)
698 want_help = 1;
700 break;
701 case 't':
702 strncpy(tf, optarg, sizeof(tf));
703 break;
704 case 'V':
705 printf("%s\n%s\n", PACKAGE_STRING, COPYRIGHT);
706 exit(EXIT_SUCCESS);
707 break;
708 case 'l':
709 followsymlinks = 1;
710 break;
711 case 'f':
712 usefile = 1;
713 break;
714 case 'v':
715 verbose = 1;
716 break;
717 case 'X':
718 chain_output = 0;
719 case 'x':
720 chaining = 1;
721 case 'O':
722 if (open_module(optarg, &i)) {
723 ret = EXIT_FAILURE;
724 goto cleanup;
727 if (init_module_options(argc, argv, i))
728 want_help = 1;
730 break;
731 case 'h':
732 default:
733 want_help = 1;
734 break;
738 /* The last module cannot be chained (syntax). */
739 if (!module_index || TEST_FLAG(modules[module_index - 1].flags, MODULE_CHAINED))
740 want_help = 1;
742 /* Cycle through the modules and output their help text. */
743 if (want_help) {
744 usage_header();
746 for (i = 0; i < module_index; i++) {
747 module_help *m_help;
749 if (TEST_FLAG(modules[i].flags, MODULE_DUP))
750 continue;
752 if ((m_help = dlsym(modules[i].m, "ui_module_help")) == NULL) {
753 warnx("%s", dlerror());
754 continue;
757 fprintf(stderr, "%s\n", modules[i].name);
758 (*m_help) ();
761 usage();
762 cleanup_modules();
763 exit(EXIT_FAILURE);
766 if (argc == optind || strcmp(argv[optind], "-") == 0) {
767 while ((s = fgets(line, sizeof(line), stdin)) != NULL) {
768 if (s[strlen(s) - 1] == '\n')
769 s[strlen(s) - 1] = '\0';
771 ret |= junction(s);
774 else {
775 for (; optind < argc; optind++)
776 ret |= junction(argv[optind]);
779 cleanup:
780 cleanup_modules();
781 exit(ret);