Botan  1.10.9
unix_cmd.cpp
Go to the documentation of this file.
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 */
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 
56  pipe_wrapper(int f, pid_t p) : fd(f), pid(p) {}
57  ~pipe_wrapper() { ::close(fd); }
58  };
59 
60 /**
61 * Read from the pipe
62 */
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 */
95 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 
102 /**
103 * Check if we reached EOF
104 */
106  {
107  return (pipe) ? false : true;
108  }
109 
110 /**
111 * Return the Unix file descriptor of the pipe
112 */
114  {
115  if(!pipe)
116  return -1;
117  return pipe->fd;
118  }
119 
120 /**
121 * Return a human-readable ID for this stream
122 */
123 std::string DataSource_Command::id() const
124  {
125  return "Unix command: " + arg_list[0];
126  }
127 
128 /**
129 * Create the pipe
130 */
131 void DataSource_Command::create_pipe(const std::vector<std::string>& paths)
132  {
133  bool found_something = false;
134 
135  for(size_t j = 0; j != paths.size(); j++)
136  {
137  const std::string full_path = paths[j] + "/" + arg_list[0];
138  if(::access(full_path.c_str(), X_OK) == 0)
139  {
140  found_something = true;
141  break;
142  }
143  }
144 
145  if(!found_something)
146  return;
147 
148  int pipe_fd[2];
149  if(::pipe(pipe_fd) != 0)
150  return;
151 
152  pid_t pid = ::fork();
153 
154  if(pid == -1)
155  {
156  ::close(pipe_fd[0]);
157  ::close(pipe_fd[1]);
158  }
159  else if(pid > 0)
160  {
161  pipe = new pipe_wrapper(pipe_fd[0], pid);
162  ::close(pipe_fd[1]);
163  }
164  else
165  {
166  if(dup2(pipe_fd[1], STDOUT_FILENO) == -1)
167  ::exit(127);
168  if(close(pipe_fd[0]) != 0 || close(pipe_fd[1]) != 0)
169  ::exit(127);
170  if(close(STDERR_FILENO) != 0)
171  ::exit(127);
172 
173  do_exec(arg_list, paths);
174  ::exit(127);
175  }
176  }
177 
178 /**
179 * Shutdown the pipe
180 */
181 void DataSource_Command::shutdown_pipe()
182  {
183  if(pipe)
184  {
185  pid_t reaped = waitpid(pipe->pid, 0, WNOHANG);
186 
187  if(reaped == 0)
188  {
189  kill(pipe->pid, SIGTERM);
190 
191  struct ::timeval tv;
192  tv.tv_sec = 0;
193  tv.tv_usec = KILL_WAIT;
194  select(0, 0, 0, 0, &tv);
195 
196  reaped = ::waitpid(pipe->pid, 0, WNOHANG);
197 
198  if(reaped == 0)
199  {
200  ::kill(pipe->pid, SIGKILL);
201  do
202  reaped = ::waitpid(pipe->pid, 0, 0);
203  while(reaped == -1);
204  }
205  }
206 
207  delete pipe;
208  pipe = 0;
209  }
210  }
211 
212 /**
213 * DataSource_Command Constructor
214 */
215 DataSource_Command::DataSource_Command(const std::string& prog_and_args,
216  const std::vector<std::string>& paths) :
217  MAX_BLOCK_USECS(100000), KILL_WAIT(10000)
218  {
219  arg_list = split_on(prog_and_args, ' ');
220 
221  if(arg_list.size() == 0)
222  throw Invalid_Argument("DataSource_Command: No command given");
223  if(arg_list.size() > 5)
224  throw Invalid_Argument("DataSource_Command: Too many args");
225 
226  pipe = 0;
227  create_pipe(paths);
228  }
229 
230 /**
231 * DataSource_Command Destructor
232 */
234  {
235  if(!end_of_data())
236  shutdown_pipe();
237  }
238 
239 }
std::vector< std::string > split_on(const std::string &str, char delim)
Definition: parsing.cpp:152
std::invalid_argument Invalid_Argument
Definition: exceptn.h:20
size_t read(byte[], size_t)
Definition: unix_cmd.cpp:63
unsigned char byte
Definition: types.h:22
size_t peek(byte[], size_t, size_t) const
Definition: unix_cmd.cpp:95
bool end_of_data() const
Definition: unix_cmd.cpp:105
std::string id() const
Definition: unix_cmd.cpp:123
DataSource_Command(const std::string &, const std::vector< std::string > &paths)
Definition: unix_cmd.cpp:215