2 Copyright (C) 2001-2015 Ben Kibbey <bjk@luxsci.net>
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02110-1301 USA
25 #include <sys/types.h>
35 #ifdef _POSIX2_LINE_MAX
36 #define LINE_MAX _POSIX2_LINE_MAX
51 #include "safe_strncat.c"
54 static void *Realloc(void *p
, size_t size
)
58 if ((p2
= realloc(p
, size
)) == NULL
)
59 err(EXIT_FAILURE
, "%s", "realloc()");
64 /* This may be used in modules to keep a consistant time format with other
66 char *stamp(time_t epoch
, const char *format
)
68 static char buf
[TIMEBUFSIZE
];
71 t
= localtime(&epoch
);
72 strftime(buf
, sizeof(buf
), format
, t
);
77 * This may be used in modules to add a string to the buffer (ui_module_exec()).
79 void add_string(char ***buf
, const char *str
)
85 for (s
= *buf
; *s
; s
++)
90 s
= Realloc(s
, (i
+ 2) * sizeof(char *));
96 /* This is for the field separators (-F and -m). */
97 static int escapes(const char *str
)
137 /* Help text. Module help text is displayed after this. */
138 static void usage_header()
140 printf("Usage: %s [-vhVL] [-c <filename>] [-t fmt] [-m c] [-F c] [-d]\n"
141 "\t[[-xX] -O <module1> [options] [-- [-xX] -O <module2> [...]]]\n"
142 "\t[- | username | -f filename] [...]\n\n", __progname
);
145 /* Help text. Module help text is displayed before this. */
148 printf(" -d\tLoad the default modules (passwd.so, mail.so, and login.so).\n");
149 printf(" -c\tRead a configuration file. Can be used more than once.\n");
150 printf(" -O\tLoad a module. Can be used more than once.\n");
151 printf(" -x\tChain module1's output to module2's input.\n");
152 printf(" -X\tDon't output module1's info, only chain it.\n");
153 printf(" -F c\tSeparate output with the specified character "
154 "('%c').\n", delimchar
);
155 printf(" -m c\tSeparate multi-string values with the specified "
156 "character ('%c').\n", multichar
);
157 printf(" -t tf\tstrftime(3) time format ('%s').\n", DEFAULT_TIMEFORMAT
);
158 printf(" -f\tUsers are the owners of the specified files.\n");
159 printf(" -L\tFollow symbolic links.\n");
160 printf(" -v\tVerbose output when possible (twice for all modules).\n");
161 printf(" -h\tThis help text.\n");
162 printf(" -V\tVersion information.\n\n");
163 printf("Output key: %s=unknown/error, %s=none, %s=yes/on, "
164 "%s=no/off\n", UNKNOWN
, NONE
, ON
, OFF
);
168 * Add a module to the array of loaded modules. The index argument is the
169 * current item number being added stored in an integer. The module is also
170 * initialized here with ui_module_init().
172 static int open_module(char *filename
, struct module_s
**ptr
)
176 char *p
, s
[PATH_MAX
];
178 struct module_s
*mod
, **mods
, *last
;
181 strncpy(s
, filename
, sizeof(s
));
184 if ((p
= strrchr(s
, '/')) != NULL
)
187 strncpy(s
, filename
, sizeof(s
));
192 for (mods
= modules
; mods
&& *mods
; mods
++) {
193 if (strcmp(p
, (*mods
)->name
) == 0) {
199 if ((m
= dlopen(filename
, RTLD_NOW
)) == NULL
) {
200 warnx("%s", dlerror());
206 last
= modules
? modules
[module_total
-1] : NULL
;
207 modules
= Realloc(modules
, (module_total
+2) * sizeof(struct module_s
*));
208 mod
= calloc(1, sizeof(struct module_s
));
209 modules
[module_total
++] = mod
;
210 modules
[module_total
] = NULL
;
212 strncpy(mod
->name
, p
, sizeof(mod
->name
));
213 mod
->name
[sizeof(mod
->name
)-1] = 0;
215 if ((init
= dlsym(mod
->m
, "ui_module_init")) == NULL
)
216 warnx("%s", dlerror());
218 (*init
) (&chainable
);
221 SET_FLAG(mod
->flags
, MODULE_CHAINABLE
);
223 if (last
&& TEST_FLAG(last
->flags
, MODULE_CHAINED
) &&
224 !TEST_FLAG(mod
->flags
, MODULE_CHAINABLE
)) {
225 warnx("%s: this module is not chainable", mod
->name
);
227 modules
[module_total
] = NULL
;
232 /* Module chaining. See junction() for more info. */
234 SET_FLAG(mod
->flags
, MODULE_CHAINED
);
237 SET_FLAG(mod
->flags
, MODULE_OUTPUT
);
240 SET_FLAG(mod
->flags
, MODULE_VERBOSE
);
243 SET_FLAG(mod
->flags
, MODULE_DUP
);
244 warnx("%s: a module by this name is already loaded", p
);
249 verbose
= (verbose
< 2) ? 0 : 2;
254 /* This just free's up the array of modules. The modules should clean up after
255 * themselves via the ui_module_exit() function. */
256 static void cleanup_modules()
258 struct module_s
**mod
;
263 for (mod
= modules
; *mod
; mod
++) {
267 if ((e
= dlsym((*mod
)->m
, "ui_module_exit")) == NULL
)
268 warnx("%s", dlerror());
275 for (p
= (*mod
)->argv
; *p
; p
++)
287 static void output(char **s
, const int sep
, int which
)
300 printf("%c", which
== OUTPUT_DONE
? '\n' : sep
);
303 /* Pass the argument to each loaded module. */
304 static int junction(const char *arg
)
308 int ret
= EXIT_SUCCESS
;
310 struct module_s
**mod
;
313 if ((STAT(arg
, &st
)) == -1) {
320 if ((pw
= getpwuid(st
.st_uid
)) == NULL
) {
322 warnx("%s: no such user", arg
);
324 if (errno
== 0 || errno
== ENOENT
|| errno
== EPERM
325 || errno
== EBADF
|| errno
== ESRCH
)
326 warnx("%s: no such uid %u", arg
, st
.st_uid
);
328 warn("%s", "getpwuid()");
337 if ((pw
= getpwnam(arg
)) == NULL
) {
339 warnx("%s: no such user", arg
);
341 if (errno
== 0 || errno
== ENOENT
|| errno
== EPERM
342 || errno
== EBADF
|| errno
== ESRCH
)
343 warnx("%s: no such user", arg
);
345 warn("%s", "getpwnam()");
352 for (mod
= modules
; *mod
; mod
++) {
355 if ((m_exec
= dlsym((*mod
)->m
, "ui_module_exec")) == NULL
) {
356 warnx("%s", dlerror());
360 ret
|= (*m_exec
) (&s
, pw
, multichar
,
361 TEST_FLAG((*mod
)->flags
, MODULE_VERBOSE
), tf
);
363 if (!TEST_FLAG((*mod
)->flags
, MODULE_CHAINED
) ||
364 (TEST_FLAG((*mod
)->flags
, MODULE_CHAINED
) &&
365 TEST_FLAG((*mod
)->flags
, MODULE_OUTPUT
))) {
367 (*(mod
+1)) ? OUTPUT_APPEND
: OUTPUT_DONE
);
369 if (!TEST_FLAG((*mod
)->flags
, MODULE_CHAINED
)) {
372 for (p
= s
; *p
; p
++) {
385 /* Copy options for each module into it's own argc and argv variables stopping
386 * at -- (getopt(3)). */
387 static int init_module_options(int the_argc
, char **the_argv
,
388 struct module_s
*mod
)
392 module_options_init
*o
;
393 int old_optind
= optind
;
395 int ret
= EXIT_SUCCESS
;
396 char *optstring
= NULL
;
397 char *defaults
= NULL
;
398 int have_an_argument
= 0;
400 if ((o
= dlsym(mod
->m
, "ui_module_options_init")) == NULL
) {
401 warnx("%s", dlerror());
405 if ((optstring
= (*o
) (&defaults
))) {
406 mod
->argv
= Realloc(mod
->argv
, (mod
->argc
+ 2) * sizeof(char *));
407 mod
->argv
[mod
->argc
++] = strdup(__progname
);
408 mod
->argv
[mod
->argc
] = NULL
;
410 /* Probably a default module. */
411 if (the_argv
== NULL
)
414 while ((opt
= getopt(the_argc
, the_argv
, optstring
)) != -1) {
417 warnx("%s: invalid option -- %c\n", mod
->name
,
424 mod
->argv
= Realloc(mod
->argv
, (mod
->argc
+ 2) * sizeof(char *));
425 snprintf(tmp
, sizeof(tmp
), "-%c%s", opt
, (optarg
) ? optarg
: "");
426 mod
->argv
[mod
->argc
++] = strdup(tmp
);
427 mod
->argv
[mod
->argc
] = NULL
;
428 have_an_argument
= 1;
432 goto skip_option_stuff
;
436 * No options were specified for this module. Set the modules default
437 * options (ui_module_options_init()) if any.
439 if (!have_an_argument
&& defaults
) {
440 mod
->argv
= Realloc(mod
->argv
, (mod
->argc
+ 2) * sizeof(char *));
441 snprintf(tmp
, sizeof(tmp
), "-%s", defaults
);
442 mod
->argv
[mod
->argc
++] = strdup(tmp
);
443 mod
->argv
[mod
->argc
] = NULL
;
447 opterr
= optind
= optopt
= 1;
449 if ((m
= dlsym(mod
->m
, "ui_module_options")) == NULL
) {
450 warnx("%s", dlerror());
454 ret
|= (*m
) (mod
->argc
, mod
->argv
);
464 * This will parse a line used as an argument list for the exec() line of
465 * functions returning a dynamically allocated array of character pointers so
466 * you should free() it afterwards. Both ' and " quoting is supported (with
467 * escapes) for multi-word arguments.
469 * This is my second attempt at it. Works alot better than the first. :)
472 * Ben Kibbey <bjk@luxsci.net>
475 * Modified to handle argv[0] and argc. (Ben Kibbey <bjk@luxsci.net>)
477 static char **parseargv(char *str
, const char *progname
, int *me_argc
)
490 if (!(pptr
= malloc(sizeof(char *))))
493 pptr
= Realloc(pptr
, (idx
+ 2) * sizeof(char *));
494 pptr
[idx
++] = strdup(progname
);
497 for (i
= 0, s
= str
; *s
; lastchar
= *s
++) {
498 if ((*s
== '\"' || *s
== '\'') && lastchar
!= '\\') {
499 quote
= (quote
) ? 0 : 1;
503 if (*s
== ' ' && !quote
) {
505 pptr
= Realloc(pptr
, (idx
+ 2) * sizeof(char *));
506 pptr
[idx
++] = strdup(arg
);
512 if ((i
+ 1) == sizeof(arg
))
521 pptr
= Realloc(pptr
, (idx
+ 2) * sizeof(char *));
522 pptr
[idx
++] = strdup(arg
);
531 static char *get_home_directory()
534 static char dir
[PATH_MAX
];
538 if ((pw
= getpwuid(getuid())) == NULL
) {
542 warnx("getpwuid(): no such uid");
547 strncpy(dir
, pw
->pw_dir
, sizeof(dir
));
548 dir
[sizeof(dir
)-1] = 0;
552 /* Read in a configuration file adding modules to the module array and
553 * checking any module options. */
554 static int parse_rc_file(const char *filename
)
556 char line
[LINE_MAX
], *p
;
558 int old_optind
= optind
;
559 struct module_s
*mod
;
561 if ((fp
= fopen(filename
, "r")) == NULL
) {
562 warn("%s", filename
);
566 while ((p
= fgets(line
, sizeof(line
), fp
)) != NULL
) {
567 char name
[PATH_MAX
], options
[LINE_MAX
], tmp
[PATH_MAX
], *s
;
569 char **my_argv
, **ap
;
573 while (*p
&& isspace((unsigned char) *p
))
581 if (*p
== '>' || *p
== '-') {
590 while (*p
&& *p
!= ' ' && *p
!= '\t') {
606 while (*p
&& isspace((unsigned char) *p
))
612 if (*p
== '\n' || (*p
== '#' && lastchar
!= '\\'))
615 if (*p
== '#' && lastchar
== '\\') {
616 lastchar
= *--s
= *p
++;
621 lastchar
= *s
++ = *p
++;
628 s
= get_home_directory();
629 strncpy(tmp
, s
, sizeof(tmp
));
630 tmp
[sizeof(tmp
)-1] = 0;
632 safe_strncat(tmp
, p
, sizeof(tmp
));
633 strncpy(name
, tmp
, sizeof(name
));
634 name
[sizeof(name
)-1] = 0;
637 if (open_module(name
, &mod
))
640 if ((my_argv
= parseargv(options
, __progname
, &my_argc
)) == NULL
)
644 n
= init_module_options(my_argc
, my_argv
, mod
);
646 for (ap
= my_argv
; *ap
; ap
++)
663 int main(int argc
, char *argv
[])
665 int ret
= EXIT_SUCCESS
;
667 char line
[LINE_MAX
], *s
= NULL
;
670 #ifndef HAVE___PROGNAME
671 __progname
= argv
[0];
673 delimchar
= DEFAULT_DELIMINATING_CHAR
;
674 multichar
= DEFAULT_MULTI_CHAR
;
675 strncpy(tf
, DEFAULT_TIMEFORMAT
, sizeof(tf
));
678 while ((opt
= getopt(argc
, argv
, "+x:X:dm:c:hO:F:t:vVLf")) != -1) {
679 struct module_s
*mod
;
688 if (open_module("passwd.so", &mod
) == 0) {
689 if (init_module_options(1, NULL
, mod
))
697 if (open_module("mail.so", &mod
) == 0) {
698 if (init_module_options(1, NULL
, mod
))
706 if (open_module("login.so", &mod
) == 0) {
707 if (init_module_options(1, NULL
, mod
))
717 if ((optarg
[0] != '\\' && strlen(optarg
) > 1) ||
718 (optarg
[0] == '\\' && strlen(optarg
) != 2)) {
723 if ((multichar
= escapes(optarg
)) == 0)
728 if ((ret
= parse_rc_file(optarg
)) != 0) {
736 if ((optarg
[0] != '\\' && strlen(optarg
) > 1) ||
737 (optarg
[0] == '\\' && strlen(optarg
) != 2)) {
742 if ((delimchar
= escapes(optarg
)) == 0)
747 strncpy(tf
, optarg
, sizeof(tf
));
750 printf("%s\n%s\n", PACKAGE_STRING
, COPYRIGHT
);
767 if (open_module(optarg
, &mod
)) {
772 if (init_module_options(argc
, argv
, mod
))
776 * For modules which have no options at all (to keep getopt
777 * from interpreting the rest as arguments.
780 if (strcmp(argv
[optind
], "--") == 0)
792 /* The last module cannot be chained (syntax). */
793 if (!module_total
|| TEST_FLAG(modules
[module_total
-1]->flags
, MODULE_CHAINED
))
796 /* Cycle through the modules and output their help text. */
799 struct module_s
**mod
;
801 for (mod
= modules
; mod
&& *mod
; mod
++) {
804 if (TEST_FLAG((*mod
)->flags
, MODULE_DUP
))
807 if ((m_help
= dlsym((*mod
)->m
, "ui_module_help")) == NULL
) {
808 warnx("%s", dlerror());
812 printf("%s\n", (*mod
)->name
);
821 if (argc
== optind
|| strcmp(argv
[optind
], "-") == 0) {
822 while ((s
= fgets(line
, sizeof(line
), stdin
)) != NULL
) {
823 if (s
[strlen(s
) - 1] == '\n')
824 s
[strlen(s
) - 1] = '\0';
830 for (; optind
< argc
; optind
++)
831 ret
|= junction(argv
[optind
]);