Update libtest/associated tests.
[awesomized/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 }
72
73 namespace libtest {
74
75 Application::Application(const std::string& arg, const bool _use_libtool_arg) :
76 _use_libtool(_use_libtool_arg),
77 _argc(0),
78 _exectuble(arg),
79 built_argv(NULL),
80 _pid(-1)
81 {
82 if (_use_libtool)
83 {
84 if (libtool() == NULL)
85 {
86 throw "libtool requested, but know libtool was found";
87 }
88 }
89
90 if (_use_libtool and getenv("PWD"))
91 {
92 _exectuble_with_path+= getenv("PWD");
93 _exectuble_with_path+= "/";
94 }
95 _exectuble_with_path+= _exectuble;
96 }
97
98 Application::~Application()
99 {
100 delete_argv();
101 }
102
103 Application::error_t Application::run(const char *args[])
104 {
105 stdin_fd.reset();
106 stdout_fd.reset();
107 stderr_fd.reset();
108 _stdout_buffer.clear();
109 _stderr_buffer.clear();
110
111 posix_spawn_file_actions_t file_actions;
112 posix_spawn_file_actions_init(&file_actions);
113
114 stdin_fd.dup_for_spawn(Application::Pipe::READ, file_actions, STDIN_FILENO);
115 stdout_fd.dup_for_spawn(Application::Pipe::WRITE, file_actions, STDOUT_FILENO);
116 stderr_fd.dup_for_spawn(Application::Pipe::WRITE, file_actions, STDERR_FILENO);
117
118 create_argv(args);
119
120 int spawn_ret;
121 if (_use_libtool)
122 {
123 spawn_ret= posix_spawn(&_pid, built_argv[0], &file_actions, NULL, built_argv, NULL);
124 }
125 else
126 {
127 spawn_ret= posix_spawnp(&_pid, built_argv[0], &file_actions, NULL, built_argv, NULL);
128 }
129
130 posix_spawn_file_actions_destroy(&file_actions);
131
132 stdin_fd.close(Application::Pipe::READ);
133 stdout_fd.close(Application::Pipe::WRITE);
134 stderr_fd.close(Application::Pipe::WRITE);
135
136 if (spawn_ret)
137 {
138 Error << print();
139 return Application::INVALID;
140 }
141
142 return Application::SUCCESS;
143 }
144
145 Application::error_t Application::wait()
146 {
147 if (_pid == -1)
148 {
149 Error << "wait() got an invalid pid_t";
150 return Application::INVALID;
151 }
152
153 {
154 ssize_t read_length;
155 char buffer[1024]= { 0 };
156 bool bail= false;
157 while (((read_length= ::read(stdout_fd.fd()[0], buffer, sizeof(buffer))) != 0) or bail)
158 {
159 if (read_length == -1)
160 {
161 switch(errno)
162 {
163 case EAGAIN:
164 continue;
165
166 default:
167 Error << strerror(errno);
168 bail= true;
169 }
170 }
171 _stdout_buffer.reserve(read_length +1);
172 for (size_t x= 0; x < read_length; x++)
173 {
174 _stdout_buffer.push_back(buffer[x]);
175 }
176 // @todo Suck up all output code here
177 }
178 }
179
180 {
181 ssize_t read_length;
182 char buffer[1024]= { 0 };
183 bool bail= false;
184 while (((read_length= ::read(stderr_fd.fd()[0], buffer, sizeof(buffer))) != 0) or bail)
185 {
186 if (read_length == -1)
187 {
188 switch(errno)
189 {
190 case EAGAIN:
191 continue;
192
193 default:
194 Error << strerror(errno);
195 bail= true;
196 }
197 }
198 _stderr_buffer.reserve(read_length +1);
199 for (size_t x= 0; x < read_length; x++)
200 {
201 _stderr_buffer.push_back(buffer[x]);
202 }
203 // @todo Suck up all errput code here
204 }
205 }
206
207 error_t exit_code= FAILURE;
208 {
209 int status= 0;
210 pid_t waited_pid;
211 if ((waited_pid= waitpid(_pid, &status, 0)) == -1)
212 {
213 Error << "Error occured while waitpid(" << strerror(errno) << ") on pid " << int(_pid);
214 }
215 else
216 {
217 assert(waited_pid == _pid);
218 exit_code= error_t(exited_successfully(status));
219 }
220 }
221
222 if (exit_code == Application::INVALID)
223 {
224 #if 0
225 Error << print_argv(built_argv, _argc, _pid);
226 #endif
227 }
228
229 return exit_code;
230 }
231
232 void Application::add_option(const std::string& arg)
233 {
234 _options.push_back(std::make_pair(arg, std::string()));
235 }
236
237 void Application::add_option(const std::string& name, const std::string& value)
238 {
239 _options.push_back(std::make_pair(name, value));
240 }
241
242 Application::Pipe::Pipe()
243 {
244 _fd[0]= -1;
245 _fd[1]= -1;
246 _open[0]= false;
247 _open[1]= false;
248 }
249
250 void Application::Pipe::reset()
251 {
252 close(READ);
253 close(WRITE);
254
255 int ret;
256 if ((ret= pipe(_fd)) < 0)
257 {
258 throw strerror(ret);
259 }
260 _open[0]= true;
261 _open[1]= true;
262
263 {
264 ret= fcntl(_fd[0], F_GETFL, 0);
265 if (ret == -1)
266 {
267 Error << "fcntl(F_GETFL) " << strerror(ret);
268 throw strerror(ret);
269 }
270
271 ret= fcntl(_fd[0], F_SETFL, ret | O_NONBLOCK);
272 if (ret == -1)
273 {
274 Error << "fcntl(F_SETFL) " << strerror(ret);
275 throw strerror(ret);
276 }
277 }
278 }
279
280 Application::Pipe::~Pipe()
281 {
282 close(READ);
283 close(WRITE);
284 }
285
286 void Application::Pipe::dup_for_spawn(const close_t& arg, posix_spawn_file_actions_t& file_actions, const int newfildes)
287 {
288 int type= int(arg);
289
290 int ret;
291 if ((ret= posix_spawn_file_actions_adddup2(&file_actions, _fd[type], newfildes )) < 0)
292 {
293 Error << "posix_spawn_file_actions_adddup2(" << strerror(ret) << ")";
294 throw strerror(ret);
295 }
296
297 if ((ret= posix_spawn_file_actions_addclose(&file_actions, _fd[type])) < 0)
298 {
299 Error << "posix_spawn_file_actions_adddup2(" << strerror(ret) << ")";
300 throw strerror(ret);
301 }
302 }
303
304 void Application::Pipe::close(const close_t& arg)
305 {
306 int type= int(arg);
307
308 if (_open[type])
309 {
310 int ret;
311 if ((ret= ::close(_fd[type])) < 0)
312 {
313 Error << "close(" << strerror(ret) << ")";
314 }
315 _open[type]= false;
316 }
317 }
318
319 void Application::create_argv(const char *args[])
320 {
321 _argc= 2 +_use_libtool ? 2 : 0; // +1 for the command, +2 for libtool/mode=execute, +1 for the NULL
322
323 if (_use_libtool)
324 {
325 _argc+= 2; // +2 for libtool --mode=execute
326 }
327
328 for (Options::const_iterator iter= _options.begin(); iter != _options.end(); iter++)
329 {
330 _argc++;
331 if ((*iter).second.empty() == false)
332 {
333 _argc++;
334 }
335 }
336
337 if (args)
338 {
339 for (const char **ptr= args; *ptr; ++ptr)
340 {
341 _argc++;
342 }
343 }
344
345 delete_argv();
346 built_argv= new char * [_argc];
347
348 size_t x= 0;
349 if (_use_libtool)
350 {
351 assert(libtool());
352 built_argv[x++]= strdup(libtool());
353 built_argv[x++]= strdup("--mode=execute");
354 }
355 built_argv[x++]= strdup(_exectuble_with_path.c_str());
356
357 for (Options::const_iterator iter= _options.begin(); iter != _options.end(); iter++)
358 {
359 built_argv[x++]= strdup((*iter).first.c_str());
360 if ((*iter).second.empty() == false)
361 {
362 built_argv[x++]= strdup((*iter).second.c_str());
363 }
364 }
365
366 if (args)
367 {
368 for (const char **ptr= args; *ptr; ++ptr)
369 {
370 built_argv[x++]= strdup(*ptr);
371 }
372 }
373 built_argv[_argc -1]= NULL;
374 }
375
376 std::string Application::print()
377 {
378 return print_argv(built_argv, _argc, _pid);
379 }
380
381 void Application::delete_argv()
382 {
383 if (built_argv == NULL)
384 {
385 return;
386 }
387
388 for (size_t x= 0; x < _argc; x++)
389 {
390 if (built_argv[x])
391 {
392 ::free(built_argv[x]);
393 }
394 }
395 delete[] built_argv;
396 built_argv= NULL;
397 _argc= 0;
398 }
399
400
401 int exec_cmdline(const std::string& command, const char *args[], bool use_libtool)
402 {
403 Application app(command, use_libtool);
404
405 Application::error_t ret= app.run(args);
406
407 if (ret != Application::SUCCESS)
408 {
409 return int(ret);
410 }
411 ret= app.wait();
412
413 return int(ret);
414 }
415
416 const char *gearmand_binary()
417 {
418 return GEARMAND_BINARY;
419 }
420
421 } // namespace exec_cmdline