Botan 1.10.17
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
20namespace Botan {
21
22namespace {
23
24/**
25* Attempt to execute the command
26*/
27void 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*/
51struct 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*/
63size_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*/
95size_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
103 {
104 throw Stream_IO_Error("Cannot check available bytes on a pipe");
105 }
106
107/**
108* Check if we reached EOF
109*/
111 {
112 return (pipe) ? false : true;
113 }
114
115/**
116* Return the Unix file descriptor of the pipe
117*/
119 {
120 if(!pipe)
121 return -1;
122 return pipe->fd;
123 }
124
125/**
126* Return a human-readable ID for this stream
127*/
128std::string DataSource_Command::id() const
129 {
130 return "Unix command: " + arg_list[0];
131 }
132
133/**
134* Create the pipe
135*/
136void 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*/
186void 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*/
220DataSource_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*/
239 {
240 if(!end_of_data())
241 shutdown_pipe();
242 }
243
244}
size_t read(byte[], size_t)
Definition unix_cmd.cpp:63
bool check_available(size_t n)
Definition unix_cmd.cpp:102
DataSource_Command(const std::string &, const std::vector< std::string > &paths)
Definition unix_cmd.cpp:220
std::string id() const
Definition unix_cmd.cpp:128
size_t peek(byte[], size_t, size_t) const
Definition unix_cmd.cpp:95
T select(T mask, T from0, T from1)
Definition ct_utils.h:45
std::invalid_argument Invalid_Argument
Definition exceptn.h:20
std::vector< std::string > split_on(const std::string &str, char delim)
Definition parsing.cpp:152
Invalid_State(const std::string &err)
Definition exceptn.h:27
Stream_IO_Error(const std::string &err)
Definition exceptn.h:167