Additions to testing to better check return values/etc for servers.
[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
246 return exit_code;
247 }
248
249 void Application::add_option(const std::string& arg)
250 {
251 _options.push_back(std::make_pair(arg, std::string()));
252 }
253
254 void Application::add_option(const std::string& name, const std::string& value)
255 {
256 _options.push_back(std::make_pair(name, value));
257 }
258
259 Application::Pipe::Pipe()
260 {
261 _fd[0]= -1;
262 _fd[1]= -1;
263 _open[0]= false;
264 _open[1]= false;
265 }
266
267 void Application::Pipe::reset()
268 {
269 close(READ);
270 close(WRITE);
271
272 int ret;
273 if ((ret= pipe(_fd)) < 0)
274 {
275 throw strerror(ret);
276 }
277 _open[0]= true;
278 _open[1]= true;
279
280 {
281 ret= fcntl(_fd[0], F_GETFL, 0);
282 if (ret == -1)
283 {
284 Error << "fcntl(F_GETFL) " << strerror(ret);
285 throw strerror(ret);
286 }
287
288 ret= fcntl(_fd[0], F_SETFL, ret | O_NONBLOCK);
289 if (ret == -1)
290 {
291 Error << "fcntl(F_SETFL) " << strerror(ret);
292 throw strerror(ret);
293 }
294 }
295 }
296
297 Application::Pipe::~Pipe()
298 {
299 close(READ);
300 close(WRITE);
301 }
302
303 void Application::Pipe::dup_for_spawn(const close_t& arg, posix_spawn_file_actions_t& file_actions, const int newfildes)
304 {
305 int type= int(arg);
306
307 int ret;
308 if ((ret= posix_spawn_file_actions_adddup2(&file_actions, _fd[type], newfildes )) < 0)
309 {
310 Error << "posix_spawn_file_actions_adddup2(" << strerror(ret) << ")";
311 throw strerror(ret);
312 }
313
314 if ((ret= posix_spawn_file_actions_addclose(&file_actions, _fd[type])) < 0)
315 {
316 Error << "posix_spawn_file_actions_adddup2(" << strerror(ret) << ")";
317 throw strerror(ret);
318 }
319 }
320
321 void Application::Pipe::close(const close_t& arg)
322 {
323 int type= int(arg);
324
325 if (_open[type])
326 {
327 int ret;
328 if ((ret= ::close(_fd[type])) < 0)
329 {
330 Error << "close(" << strerror(ret) << ")";
331 }
332 _open[type]= false;
333 }
334 }
335
336 void Application::create_argv(const char *args[])
337 {
338 _argc= 2 +_use_libtool ? 2 : 0; // +1 for the command, +2 for libtool/mode=execute, +1 for the NULL
339
340 if (_use_libtool)
341 {
342 _argc+= 2; // +2 for libtool --mode=execute
343 }
344
345 for (Options::const_iterator iter= _options.begin(); iter != _options.end(); iter++)
346 {
347 _argc++;
348 if ((*iter).second.empty() == false)
349 {
350 _argc++;
351 }
352 }
353
354 if (args)
355 {
356 for (const char **ptr= args; *ptr; ++ptr)
357 {
358 _argc++;
359 }
360 }
361
362 delete_argv();
363 built_argv= new char * [_argc];
364
365 size_t x= 0;
366 if (_use_libtool)
367 {
368 assert(libtool());
369 built_argv[x++]= strdup(libtool());
370 built_argv[x++]= strdup("--mode=execute");
371 }
372 built_argv[x++]= strdup(_exectuble_with_path.c_str());
373
374 for (Options::const_iterator iter= _options.begin(); iter != _options.end(); iter++)
375 {
376 built_argv[x++]= strdup((*iter).first.c_str());
377 if ((*iter).second.empty() == false)
378 {
379 built_argv[x++]= strdup((*iter).second.c_str());
380 }
381 }
382
383 if (args)
384 {
385 for (const char **ptr= args; *ptr; ++ptr)
386 {
387 built_argv[x++]= strdup(*ptr);
388 }
389 }
390 built_argv[_argc -1]= NULL;
391 }
392
393 std::string Application::print()
394 {
395 return print_argv(built_argv, _argc, _pid);
396 }
397
398 void Application::delete_argv()
399 {
400 if (built_argv == NULL)
401 {
402 return;
403 }
404
405 for (size_t x= 0; x < _argc; x++)
406 {
407 if (built_argv[x])
408 {
409 ::free(built_argv[x]);
410 }
411 }
412 delete[] built_argv;
413 built_argv= NULL;
414 _argc= 0;
415 }
416
417
418 int exec_cmdline(const std::string& command, const char *args[], bool use_libtool)
419 {
420 Application app(command, use_libtool);
421
422 Application::error_t ret= app.run(args);
423
424 if (ret != Application::SUCCESS)
425 {
426 return int(ret);
427 }
428 ret= app.wait();
429
430 return int(ret);
431 }
432
433 const char *gearmand_binary()
434 {
435 return GEARMAND_BINARY;
436 }
437
438 } // namespace exec_cmdline