1 /*
2 * Unix Command Execution
3 * (C) 1999-2007 Jack Lloyd
4 *
5 * Distributed under the terms of the Botan license
6 */
7 
8 #include <botan/internal/unix_cmd.h>
9 #include <botan/parsing.h>
10 #include <botan/exceptn.h>
11 
12 #include <sys/time.h>
13 #include <sys/types.h>
14 #include <sys/wait.h>
15 #include <string.h>
16 #include <stdlib.h>
17 #include <unistd.h>
18 #include <signal.h>
19 
20 namespace Botan {
21 
22 namespace {
23 
24 /**
25 * Attempt to execute the command
26 */
do_exec(const std::vector<std::string> & arg_list,const std::vector<std::string> & paths)27 void do_exec(const std::vector<std::string>& arg_list,
28              const std::vector<std::string>& paths)
29    {
30    const size_t args = arg_list.size() - 1;
31 
32    const char* arg1 = (args >= 1) ? arg_list[1].c_str() : 0;
33    const char* arg2 = (args >= 2) ? arg_list[2].c_str() : 0;
34    const char* arg3 = (args >= 3) ? arg_list[3].c_str() : 0;
35    const char* arg4 = (args >= 4) ? arg_list[4].c_str() : 0;
36 
37    for(size_t j = 0; j != paths.size(); j++)
38       {
39       const std::string full_path = paths[j] + "/" + arg_list[0];
40       const char* fsname = full_path.c_str();
41 
42       ::execl(fsname, fsname, arg1, arg2, arg3, arg4, NULL);
43       }
44    }
45 
46 }
47 
48 /**
49 * Local information about the pipe
50 */
51 struct pipe_wrapper
52    {
53    int fd;
54    pid_t pid;
55 
pipe_wrapperBotan::pipe_wrapper56    pipe_wrapper(int f, pid_t p) : fd(f), pid(p) {}
~pipe_wrapperBotan::pipe_wrapper57    ~pipe_wrapper() { ::close(fd); }
58    };
59 
60 /**
61 * Read from the pipe
62 */
read(byte buf[],size_t length)63 size_t DataSource_Command::read(byte buf[], size_t length)
64    {
65    if(end_of_data())
66       return 0;
67 
68    fd_set set;
69    FD_ZERO(&set);
70    FD_SET(pipe->fd, &set);
71 
72    struct ::timeval tv;
73    tv.tv_sec = 0;
74    tv.tv_usec = MAX_BLOCK_USECS;
75 
76    ssize_t got = 0;
77    if(::select(pipe->fd + 1, &set, 0, 0, &tv) == 1)
78       {
79       if(FD_ISSET(pipe->fd, &set))
80          got = ::read(pipe->fd, buf, length);
81       }
82 
83    if(got <= 0)
84       {
85       shutdown_pipe();
86       return 0;
87       }
88 
89    return static_cast<size_t>(got);
90    }
91 
92 /**
93 * Peek at the pipe contents
94 */
peek(byte[],size_t,size_t) const95 size_t DataSource_Command::peek(byte[], size_t, size_t) const
96    {
97    if(end_of_data())
98       throw Invalid_State("DataSource_Command: Cannot peek when out of data");
99    throw Stream_IO_Error("Cannot peek/seek on a command pipe");
100    }
101 
check_available(size_t)102 bool DataSource_Command::check_available(size_t)
103    {
104    throw Stream_IO_Error("Cannot check available bytes on a pipe");
105    }
106 
107 /**
108 * Check if we reached EOF
109 */
end_of_data() const110 bool DataSource_Command::end_of_data() const
111    {
112    return (pipe) ? false : true;
113    }
114 
115 /**
116 * Return the Unix file descriptor of the pipe
117 */
fd() const118 int DataSource_Command::fd() const
119    {
120    if(!pipe)
121       return -1;
122    return pipe->fd;
123    }
124 
125 /**
126 * Return a human-readable ID for this stream
127 */
id() const128 std::string DataSource_Command::id() const
129    {
130    return "Unix command: " + arg_list[0];
131    }
132 
133 /**
134 * Create the pipe
135 */
create_pipe(const std::vector<std::string> & paths)136 void DataSource_Command::create_pipe(const std::vector<std::string>& paths)
137    {
138    bool found_something = false;
139 
140    for(size_t j = 0; j != paths.size(); j++)
141       {
142       const std::string full_path = paths[j] + "/" + arg_list[0];
143       if(::access(full_path.c_str(), X_OK) == 0)
144          {
145          found_something = true;
146          break;
147          }
148       }
149 
150    if(!found_something)
151       return;
152 
153    int pipe_fd[2];
154    if(::pipe(pipe_fd) != 0)
155       return;
156 
157    pid_t pid = ::fork();
158 
159    if(pid == -1)
160       {
161       ::close(pipe_fd[0]);
162       ::close(pipe_fd[1]);
163       }
164    else if(pid > 0)
165       {
166       pipe = new pipe_wrapper(pipe_fd[0], pid);
167       ::close(pipe_fd[1]);
168       }
169    else
170       {
171       if(dup2(pipe_fd[1], STDOUT_FILENO) == -1)
172          ::exit(127);
173       if(close(pipe_fd[0]) != 0 || close(pipe_fd[1]) != 0)
174          ::exit(127);
175       if(close(STDERR_FILENO) != 0)
176          ::exit(127);
177 
178       do_exec(arg_list, paths);
179       ::exit(127);
180       }
181    }
182 
183 /**
184 * Shutdown the pipe
185 */
shutdown_pipe()186 void DataSource_Command::shutdown_pipe()
187    {
188    if(pipe)
189       {
190       pid_t reaped = waitpid(pipe->pid, 0, WNOHANG);
191 
192       if(reaped == 0)
193          {
194          kill(pipe->pid, SIGTERM);
195 
196          struct ::timeval tv;
197          tv.tv_sec = 0;
198          tv.tv_usec = KILL_WAIT;
199          select(0, 0, 0, 0, &tv);
200 
201          reaped = ::waitpid(pipe->pid, 0, WNOHANG);
202 
203          if(reaped == 0)
204             {
205             ::kill(pipe->pid, SIGKILL);
206             do
207                reaped = ::waitpid(pipe->pid, 0, 0);
208             while(reaped == -1);
209             }
210          }
211 
212       delete pipe;
213       pipe = 0;
214       }
215    }
216 
217 /**
218 * DataSource_Command Constructor
219 */
DataSource_Command(const std::string & prog_and_args,const std::vector<std::string> & paths)220 DataSource_Command::DataSource_Command(const std::string& prog_and_args,
221                                        const std::vector<std::string>& paths) :
222    MAX_BLOCK_USECS(100000), KILL_WAIT(10000)
223    {
224    arg_list = split_on(prog_and_args, ' ');
225 
226    if(arg_list.size() == 0)
227       throw Invalid_Argument("DataSource_Command: No command given");
228    if(arg_list.size() > 5)
229       throw Invalid_Argument("DataSource_Command: Too many args");
230 
231    pipe = 0;
232    create_pipe(paths);
233    }
234 
235 /**
236 * DataSource_Command Destructor
237 */
~DataSource_Command()238 DataSource_Command::~DataSource_Command()
239    {
240    if(!end_of_data())
241       shutdown_pipe();
242    }
243 
244 }
245