6807c338af6fe20e69efffdff9be152e05ae9b09
[m6w6/libmemcached] / libtest / cmdline.cc
1 /* vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
2 *
3 * libtest
4 *
5 * Copyright (C) 2011 Data Differential, http://datadifferential.com/
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 3 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22 #include <libtest/common.h>
23
24 using namespace libtest;
25
26 #include <cstdlib>
27 #include <cstring>
28 #include <fcntl.h>
29 #include <memory>
30 #include <spawn.h>
31 #include <sstream>
32 #include <string>
33 #include <sys/stat.h>
34 #include <sys/types.h>
35
36 extern "C" {
37 static int exited_successfully(int status)
38 {
39 if (status == 0)
40 {
41 return EXIT_SUCCESS;
42 }
43
44 if (WIFEXITED(status) == true)
45 {
46 return WEXITSTATUS(status);
47 }
48 else if (WIFSIGNALED(status) == true)
49 {
50 return WTERMSIG(status);
51 }
52
53 return EXIT_FAILURE;
54 }
55 }
56
57 namespace {
58
59 std::string print_argv(char * * & built_argv, const size_t& argc, const pid_t& pid)
60 {
61 std::stringstream arg_buffer;
62
63 for (size_t x= 0; x < argc; x++)
64 {
65 arg_buffer << built_argv[x] << " ";
66 }
67
68 return arg_buffer.str();
69 }
70
71 static Application::error_t int_to_error_t(int arg)
72 {
73 switch (arg)
74 {
75 case 127:
76 return Application::INVALID;
77
78 case 0:
79 return Application::SUCCESS;
80
81 default:
82 case 1:
83 return Application::FAILURE;
84 }
85 }
86 }
87
88 namespace libtest {
89
90 Application::Application(const std::string& arg, const bool _use_libtool_arg) :
91 _use_libtool(_use_libtool_arg),
92 _argc(0),
93 _exectuble(arg),
94 built_argv(NULL),
95 _pid(-1)
96 {
97 if (_use_libtool)
98 {
99 if (libtool() == NULL)
100 {
101 throw "libtool requested, but know libtool was found";
102 }
103 }
104
105 if (_use_libtool and getenv("PWD"))
106 {
107 _exectuble_with_path+= getenv("PWD");
108 _exectuble_with_path+= "/";
109 }
110 _exectuble_with_path+= _exectuble;
111 }
112
113 Application::~Application()
114 {
115 delete_argv();
116 }
117
118 Application::error_t Application::run(const char *args[])
119 {
120 stdin_fd.reset();
121 stdout_fd.reset();
122 stderr_fd.reset();
123 _stdout_buffer.clear();
124 _stderr_buffer.clear();
125
126 posix_spawn_file_actions_t file_actions;
127 posix_spawn_file_actions_init(&file_actions);
128
129 stdin_fd.dup_for_spawn(Application::Pipe::READ, file_actions, STDIN_FILENO);
130 stdout_fd.dup_for_spawn(Application::Pipe::WRITE, file_actions, STDOUT_FILENO);
131 stderr_fd.dup_for_spawn(Application::Pipe::WRITE, file_actions, STDERR_FILENO);
132
133 create_argv(args);
134
135 int spawn_ret;
136 if (_use_libtool)
137 {
138 spawn_ret= posix_spawn(&_pid, built_argv[0], &file_actions, NULL, built_argv, NULL);
139 }
140 else
141 {
142 spawn_ret= posix_spawnp(&_pid, built_argv[0], &file_actions, NULL, built_argv, NULL);
143 }
144
145 posix_spawn_file_actions_destroy(&file_actions);
146
147 stdin_fd.close(Application::Pipe::READ);
148 stdout_fd.close(Application::Pipe::WRITE);
149 stderr_fd.close(Application::Pipe::WRITE);
150
151 if (spawn_ret)
152 {
153 Error << print();
154 return Application::INVALID;
155 }
156
157 return Application::SUCCESS;
158 }
159
160 Application::error_t Application::wait()
161 {
162 if (_pid == -1)
163 {
164 Error << "wait() got an invalid pid_t";
165 return Application::INVALID;
166 }
167
168 {
169 ssize_t read_length;
170 char buffer[1024]= { 0 };
171 bool bail= false;
172 while (((read_length= ::read(stdout_fd.fd()[0], buffer, sizeof(buffer))) != 0) or bail)
173 {
174 if (read_length == -1)
175 {
176 switch(errno)
177 {
178 case EAGAIN:
179 continue;
180
181 default:
182 Error << strerror(errno);
183 bail= true;
184 }
185 }
186 _stdout_buffer.reserve(read_length +1);
187 for (size_t x= 0; x < read_length; x++)
188 {
189 _stdout_buffer.push_back(buffer[x]);
190 }
191 // @todo Suck up all output code here
192 }
193 }
194
195 {
196 ssize_t read_length;
197 char buffer[1024]= { 0 };
198 bool bail= false;
199 while (((read_length= ::read(stderr_fd.fd()[0], buffer, sizeof(buffer))) != 0) or bail)
200 {
201 if (read_length == -1)
202 {
203 switch(errno)
204 {
205 case EAGAIN:
206 continue;
207
208 default:
209 Error << strerror(errno);
210 bail= true;
211 }
212 }
213 _stderr_buffer.reserve(read_length +1);
214 for (size_t x= 0; x < read_length; x++)
215 {
216 _stderr_buffer.push_back(buffer[x]);
217 }
218 // @todo Suck up all errput code here
219 }
220 }
221
222 error_t exit_code= FAILURE;
223 {
224 int status= 0;
225 pid_t waited_pid;
226 if ((waited_pid= waitpid(_pid, &status, 0)) == -1)
227 {
228 Error << "Error occured while waitpid(" << strerror(errno) << ") on pid " << int(_pid);
229 }
230 else
231 {
232 if (waited_pid != _pid)
233 {
234 throw libtest::fatal(LIBYATL_DEFAULT_PARAM, "Pid mismatch, %d != %d", int(waited_pid), int(_pid));
235 }
236 exit_code= int_to_error_t(exited_successfully(status));
237 }
238 }
239
240 if (exit_code == Application::INVALID)
241 {
242 Error << print_argv(built_argv, _argc, _pid);
243 }
244
245 return exit_code;
246 }
247
248 void Application::add_option(const std::string& arg)
249 {
250 _options.push_back(std::make_pair(arg, std::string()));
251 }
252
253 void Application::add_option(const std::string& name, const std::string& value)
254 {
255 _options.push_back(std::make_pair(name, value));
256 }
257
258 Application::Pipe::Pipe()
259 {
260 _fd[0]= -1;
261 _fd[1]= -1;
262 _open[0]= false;
263 _open[1]= false;
264 }
265
266 void Application::Pipe::reset()
267 {
268 close(READ);
269 close(WRITE);
270
271 int ret;
272 if ((ret= pipe(_fd)) < 0)
273 {
274 throw strerror(ret);
275 }
276 _open[0]= true;
277 _open[1]= true;
278
279 {
280 ret= fcntl(_fd[0], F_GETFL, 0);
281 if (ret == -1)
282 {
283 Error << "fcntl(F_GETFL) " << strerror(ret);
284 throw strerror(ret);
285 }
286
287 ret= fcntl(_fd[0], F_SETFL, ret | O_NONBLOCK);
288 if (ret == -1)
289 {
290 Error << "fcntl(F_SETFL) " << strerror(ret);
291 throw strerror(ret);
292 }
293 }
294 }
295
296 Application::Pipe::~Pipe()
297 {
298 close(READ);
299 close(WRITE);
300 }
301
302 void Application::Pipe::dup_for_spawn(const close_t& arg, posix_spawn_file_actions_t& file_actions, const int newfildes)
303 {
304 int type= int(arg);
305
306 int ret;
307 if ((ret= posix_spawn_file_actions_adddup2(&file_actions, _fd[type], newfildes )) < 0)
308 {
309 Error << "posix_spawn_file_actions_adddup2(" << strerror(ret) << ")";
310 throw strerror(ret);
311 }
312
313 if ((ret= posix_spawn_file_actions_addclose(&file_actions, _fd[type])) < 0)
314 {
315 Error << "posix_spawn_file_actions_adddup2(" << strerror(ret) << ")";
316 throw strerror(ret);
317 }
318 }
319
320 void Application::Pipe::close(const close_t& arg)
321 {
322 int type= int(arg);
323
324 if (_open[type])
325 {
326 int ret;
327 if ((ret= ::close(_fd[type])) < 0)
328 {
329 Error << "close(" << strerror(ret) << ")";
330 }
331 _open[type]= false;
332 }
333 }
334
335 void Application::create_argv(const char *args[])
336 {
337 _argc= 2 +_use_libtool ? 2 : 0; // +1 for the command, +2 for libtool/mode=execute, +1 for the NULL
338
339 if (_use_libtool)
340 {
341 _argc+= 2; // +2 for libtool --mode=execute
342 }
343
344 for (Options::const_iterator iter= _options.begin(); iter != _options.end(); iter++)
345 {
346 _argc++;
347 if ((*iter).second.empty() == false)
348 {
349 _argc++;
350 }
351 }
352
353 if (args)
354 {
355 for (const char **ptr= args; *ptr; ++ptr)
356 {
357 _argc++;
358 }
359 }
360
361 delete_argv();
362 built_argv= new char * [_argc];
363
364 size_t x= 0;
365 if (_use_libtool)
366 {
367 assert(libtool());
368 built_argv[x++]= strdup(libtool());
369 built_argv[x++]= strdup("--mode=execute");
370 }
371 built_argv[x++]= strdup(_exectuble_with_path.c_str());
372
373 for (Options::const_iterator iter= _options.begin(); iter != _options.end(); iter++)
374 {
375 built_argv[x++]= strdup((*iter).first.c_str());
376 if ((*iter).second.empty() == false)
377 {
378 built_argv[x++]= strdup((*iter).second.c_str());
379 }
380 }
381
382 if (args)
383 {
384 for (const char **ptr= args; *ptr; ++ptr)
385 {
386 built_argv[x++]= strdup(*ptr);
387 }
388 }
389 built_argv[_argc -1]= NULL;
390 }
391
392 std::string Application::print()
393 {
394 return print_argv(built_argv, _argc, _pid);
395 }
396
397 void Application::delete_argv()
398 {
399 if (built_argv == NULL)
400 {
401 return;
402 }
403
404 for (size_t x= 0; x < _argc; x++)
405 {
406 if (built_argv[x])
407 {
408 ::free(built_argv[x]);
409 }
410 }
411 delete[] built_argv;
412 built_argv= NULL;
413 _argc= 0;
414 }
415
416
417 int exec_cmdline(const std::string& command, const char *args[], bool use_libtool)
418 {
419 Application app(command, use_libtool);
420
421 Application::error_t ret= app.run(args);
422
423 if (ret != Application::SUCCESS)
424 {
425 return int(ret);
426 }
427
428 return int(app.wait());
429 }
430
431 const char *gearmand_binary()
432 {
433 return GEARMAND_BINARY;
434 }
435
436 } // namespace exec_cmdline