1/*++
2/* NAME
3/*	exec_command 3
4/* SUMMARY
5/*	execute command
6/* SYNOPSIS
7/*	#include <exec_command.h>
8/*
9/*	NORETURN exec_command(command)
10/*	const char *command;
11/* DESCRIPTION
12/*	\fIexec_command\fR() replaces the current process by an instance
13/*	of \fIcommand\fR. This routine uses a simple heuristic to avoid
14/*	the overhead of running a command shell interpreter.
15/* DIAGNOSTICS
16/*	This routine never returns. All errors are fatal.
17/* LICENSE
18/* .ad
19/* .fi
20/*	The Secure Mailer license must be distributed with this software.
21/* AUTHOR(S)
22/*	Wietse Venema
23/*	IBM T.J. Watson Research
24/*	P.O. Box 704
25/*	Yorktown Heights, NY 10598, USA
26/*--*/
27
28/* System library. */
29
30#include <sys_defs.h>
31#include <unistd.h>
32#include <string.h>
33#ifdef USE_PATHS_H
34#include <paths.h>
35#endif
36#include <errno.h>
37#include <string.h>
38
39/* Utility library. */
40
41#include <msg.h>
42#include <argv.h>
43#include <exec_command.h>
44
45/* Application-specific. */
46
47#define SPACE_TAB	" \t"
48
49/* exec_command - exec command */
50
51NORETURN exec_command(const char *command)
52{
53    ARGV   *argv;
54
55    /*
56     * Character filter. In this particular case, we allow space and tab in
57     * addition to the regular character set.
58     */
59    static char ok_chars[] = "1234567890!@%-_=+:,./\
60abcdefghijklmnopqrstuvwxyz\
61ABCDEFGHIJKLMNOPQRSTUVWXYZ" SPACE_TAB;
62
63    /*
64     * See if this command contains any shell magic characters.
65     */
66    if (command[strspn(command, ok_chars)] == 0
67	&& command[strspn(command, SPACE_TAB)] != 0) {
68
69	/*
70	 * No shell meta characters found, so we can try to avoid the overhead
71	 * of running a shell. Just split the command on whitespace and exec
72	 * the result directly.
73	 */
74	argv = argv_split(command, SPACE_TAB);
75	(void) execvp(argv->argv[0], argv->argv);
76
77	/*
78	 * Auch. Perhaps they're using some shell built-in command.
79	 */
80	if (errno != ENOENT || strchr(argv->argv[0], '/') != 0)
81	    msg_fatal("execvp %s: %m", argv->argv[0]);
82
83	/*
84	 * Not really necessary, but...
85	 */
86	argv_free(argv);
87    }
88
89    /*
90     * Pass the command to a shell.
91     */
92    (void) execl(_PATH_BSHELL, "sh", "-c", command, (char *) 0);
93    msg_fatal("execl %s: %m", _PATH_BSHELL);
94}
95
96#ifdef TEST
97
98 /*
99  * Yet another proof-of-concept test program.
100  */
101#include <vstream.h>
102#include <msg_vstream.h>
103
104int     main(int argc, char **argv)
105{
106    msg_vstream_init(argv[0], VSTREAM_ERR);
107    if (argc != 2)
108	msg_fatal("usage: %s 'command'", argv[0]);
109    exec_command(argv[1]);
110}
111
112#endif
113