Update libtest.
[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 <config.h>
23 #include <libtest/common.h>
24
25 using namespace libtest;
26
27 #include <cstdlib>
28 #include <cstring>
29 #include <cerrno>
30 #include <fcntl.h>
31 #include <fstream>
32 #include <memory>
33 #include <poll.h>
34 #include <spawn.h>
35 #include <sstream>
36 #include <string>
37 #include <sys/stat.h>
38 #include <sys/types.h>
39
40 extern "C" {
41 static int exited_successfully(int status)
42 {
43 if (status == 0)
44 {
45 return EXIT_SUCCESS;
46 }
47
48 if (WIFEXITED(status) == true)
49 {
50 return WEXITSTATUS(status);
51 }
52 else if (WIFSIGNALED(status) == true)
53 {
54 return WTERMSIG(status);
55 }
56
57 return EXIT_FAILURE;
58 }
59 }
60
61 namespace {
62
63 std::string print_argv(char * * & built_argv, const size_t& argc)
64 {
65 std::stringstream arg_buffer;
66
67 for (size_t x= 0; x < argc; x++)
68 {
69 arg_buffer << built_argv[x] << " ";
70 }
71
72 return arg_buffer.str();
73 }
74
75 std::string print_argv(char** argv)
76 {
77 std::stringstream arg_buffer;
78
79 for (char** ptr= argv; *ptr; ptr++)
80 {
81 arg_buffer << *ptr << " ";
82 }
83
84 return arg_buffer.str();
85 }
86
87 static Application::error_t int_to_error_t(int arg)
88 {
89 switch (arg)
90 {
91 case 127:
92 return Application::INVALID;
93
94 case 0:
95 return Application::SUCCESS;
96
97 default:
98 case 1:
99 return Application::FAILURE;
100 }
101 }
102 }
103
104 namespace libtest {
105
106 Application::Application(const std::string& arg, const bool _use_libtool_arg) :
107 _use_libtool(_use_libtool_arg),
108 _use_valgrind(false),
109 _use_gdb(false),
110 _argc(0),
111 _exectuble(arg),
112 stdin_fd(STDIN_FILENO),
113 stdout_fd(STDOUT_FILENO),
114 stderr_fd(STDERR_FILENO),
115 built_argv(NULL),
116 _pid(-1)
117 {
118 if (_use_libtool)
119 {
120 if (libtool() == NULL)
121 {
122 fatal_message("libtool requested, but know libtool was found");
123 }
124 }
125
126 // Find just the name of the application with no path
127 {
128 size_t found= arg.find_last_of("/\\");
129 if (found)
130 {
131 _exectuble_name= arg.substr(found +1);
132 }
133 else
134 {
135 _exectuble_name= arg;
136 }
137 }
138
139 if (_use_libtool and getenv("PWD"))
140 {
141 _exectuble_with_path+= getenv("PWD");
142 _exectuble_with_path+= "/";
143 }
144 _exectuble_with_path+= _exectuble;
145 }
146
147 Application::~Application()
148 {
149 murder();
150 delete_argv();
151 }
152
153 Application::error_t Application::run(const char *args[])
154 {
155 stdin_fd.reset();
156 stdout_fd.reset();
157 stderr_fd.reset();
158 _stdout_buffer.clear();
159 _stderr_buffer.clear();
160
161 posix_spawn_file_actions_t file_actions;
162 posix_spawn_file_actions_init(&file_actions);
163
164 stdin_fd.dup_for_spawn(Application::Pipe::READ, file_actions);
165 stdout_fd.dup_for_spawn(Application::Pipe::WRITE, file_actions);
166 stderr_fd.dup_for_spawn(Application::Pipe::WRITE, file_actions);
167
168 posix_spawnattr_t spawnattr;
169 posix_spawnattr_init(&spawnattr);
170
171 sigset_t set;
172 sigemptyset(&set);
173 fatal_assert(posix_spawnattr_setsigmask(&spawnattr, &set) == 0);
174
175 create_argv(args);
176
177 int spawn_ret;
178 if (_use_gdb)
179 {
180 std::string gdb_run_file= create_tmpfile(_exectuble_name);
181 std::fstream file_stream;
182 file_stream.open(gdb_run_file.c_str(), std::fstream::out | std::fstream::trunc);
183
184 _gdb_filename= create_tmpfile(_exectuble_name);
185 file_stream
186 << "set logging redirect on" << std::endl
187 << "set logging file " << _gdb_filename << std::endl
188 << "set logging overwrite on" << std::endl
189 << "set logging on" << std::endl
190 << "set environment LIBTEST_IN_GDB=1" << std::endl
191 << "run " << arguments() << std::endl
192 << "thread apply all bt" << std::endl
193 << "quit" << std::endl;
194
195 fatal_assert(file_stream.good());
196 file_stream.close();
197
198 if (_use_libtool)
199 {
200 // libtool --mode=execute gdb -f -x binary
201 char *argv[]= {
202 const_cast<char *>(libtool()),
203 const_cast<char *>("--mode=execute"),
204 const_cast<char *>("gdb"),
205 const_cast<char *>("-batch"),
206 const_cast<char *>("-f"),
207 const_cast<char *>("-x"),
208 const_cast<char *>(gdb_run_file.c_str()),
209 const_cast<char *>(_exectuble_with_path.c_str()),
210 0};
211
212 spawn_ret= posix_spawnp(&_pid, libtool(), &file_actions, &spawnattr, argv, NULL);
213 }
214 else
215 {
216 // gdb binary
217 char *argv[]= {
218 const_cast<char *>("gdb"),
219 const_cast<char *>("-batch"),
220 const_cast<char *>("-f"),
221 const_cast<char *>("-x"),
222 const_cast<char *>(gdb_run_file.c_str()),
223 const_cast<char *>(_exectuble_with_path.c_str()),
224 0};
225 spawn_ret= posix_spawnp(&_pid, "gdb", &file_actions, &spawnattr, argv, NULL);
226 }
227 }
228 else
229 {
230 if (_use_libtool)
231 {
232 spawn_ret= posix_spawn(&_pid, built_argv[0], &file_actions, &spawnattr, built_argv, NULL);
233 }
234 else
235 {
236 spawn_ret= posix_spawnp(&_pid, built_argv[0], &file_actions, &spawnattr, built_argv, NULL);
237 }
238 }
239
240 posix_spawn_file_actions_destroy(&file_actions);
241 posix_spawnattr_destroy(&spawnattr);
242
243 stdin_fd.close(Application::Pipe::READ);
244 stdout_fd.close(Application::Pipe::WRITE);
245 stderr_fd.close(Application::Pipe::WRITE);
246
247 if (spawn_ret != 0)
248 {
249 Error << strerror(spawn_ret) << "(" << spawn_ret << ")";
250 _pid= -1;
251 return Application::INVALID;
252 }
253
254 return Application::SUCCESS;
255 }
256
257 bool Application::check() const
258 {
259 if (_pid > 1 and kill(_pid, 0) == 0)
260 {
261 return true;
262 }
263
264 return false;
265 }
266
267 void Application::murder()
268 {
269 if (check())
270 {
271 int count= 5;
272 while ((count--) > 0 and check())
273 {
274 int kill_ret= kill(_pid, SIGTERM);
275 if (kill_ret == 0)
276 {
277 int status= 0;
278 pid_t waitpid_ret;
279 if ((waitpid_ret= waitpid(_pid, &status, WNOHANG)) == -1)
280 {
281 switch (errno)
282 {
283 case ECHILD:
284 case EINTR:
285 break;
286
287 default:
288 Error << "waitpid() failed after kill with error of " << strerror(errno);
289 break;
290 }
291 }
292
293 if (waitpid_ret == 0)
294 {
295 libtest::dream(1, 0);
296 }
297 }
298 else
299 {
300 Error << "kill(pid, SIGTERM) failed after kill with error of " << strerror(errno);
301 continue;
302 }
303
304 break;
305 }
306
307 // If for whatever reason it lives, kill it hard
308 if (check())
309 {
310 (void)kill(_pid, SIGKILL);
311 }
312 }
313 slurp();
314 }
315
316 // false means that no data was returned
317 bool Application::slurp()
318 {
319 struct pollfd fds[2];
320 fds[0].fd= stdout_fd.fd();
321 fds[0].events= POLLRDNORM;
322 fds[0].revents= 0;
323 fds[1].fd= stderr_fd.fd();
324 fds[1].events= POLLRDNORM;
325 fds[1].revents= 0;
326
327 int active_fd;
328 if ((active_fd= poll(fds, 2, 0)) == -1)
329 {
330 int error;
331 switch ((error= errno))
332 {
333 #ifdef TARGET_OS_LINUX
334 case ERESTART:
335 #endif
336 case EINTR:
337 break;
338
339 case EFAULT:
340 case ENOMEM:
341 fatal_message(strerror(error));
342 break;
343
344 case EINVAL:
345 fatal_message("RLIMIT_NOFILE exceeded, or if OSX the timeout value was invalid");
346 break;
347
348 default:
349 fatal_message(strerror(error));
350 break;
351 }
352
353 return false;
354 }
355
356 if (active_fd == 0)
357 {
358 return false;
359 }
360
361 bool data_was_read= false;
362 if (fds[0].revents & POLLRDNORM)
363 {
364 if (stdout_fd.read(_stdout_buffer) == true)
365 {
366 data_was_read= true;
367 }
368 }
369
370 if (fds[1].revents & POLLRDNORM)
371 {
372 if (stderr_fd.read(_stderr_buffer) == true)
373 {
374 data_was_read= true;
375 }
376 }
377
378 return data_was_read;
379 }
380
381 Application::error_t Application::wait(bool nohang)
382 {
383 if (_pid == -1)
384 {
385 return Application::INVALID;
386 }
387
388 slurp();
389
390 error_t exit_code= FAILURE;
391 {
392 int status= 0;
393 pid_t waited_pid;
394 if ((waited_pid= waitpid(_pid, &status, nohang ? WNOHANG : 0)) == -1)
395 {
396 switch (errno)
397 {
398 case ECHILD:
399 exit_code= Application::SUCCESS;
400 break;
401
402 case EINTR:
403 break;
404
405 default:
406 Error << "Error occured while waitpid(" << strerror(errno) << ") on pid " << int(_pid);
407 break;
408 }
409 }
410 else if (waited_pid == 0)
411 {
412 exit_code= Application::SUCCESS;
413 }
414 else
415 {
416 if (waited_pid != _pid)
417 {
418 throw libtest::fatal(LIBYATL_DEFAULT_PARAM, "Pid mismatch, %d != %d", int(waited_pid), int(_pid));
419 }
420 exit_code= int_to_error_t(exited_successfully(status));
421 }
422 }
423
424 slurp();
425
426 #if 0
427 if (exit_code == Application::INVALID)
428 {
429 Error << print_argv(built_argv, _argc);
430 }
431 #endif
432
433 return exit_code;
434 }
435
436 void Application::add_long_option(const std::string& name, const std::string& option_value)
437 {
438 std::string arg(name);
439 arg+= option_value;
440 _options.push_back(std::make_pair(arg, std::string()));
441 }
442
443 void Application::add_option(const std::string& arg)
444 {
445 _options.push_back(std::make_pair(arg, std::string()));
446 }
447
448 void Application::add_option(const std::string& name, const std::string& value)
449 {
450 _options.push_back(std::make_pair(name, value));
451 }
452
453 Application::Pipe::Pipe(int arg) :
454 _std_fd(arg)
455 {
456 _pipe_fd[READ]= -1;
457 _pipe_fd[WRITE]= -1;
458 _open[READ]= false;
459 _open[WRITE]= false;
460 }
461
462 int Application::Pipe::Pipe::fd()
463 {
464 if (_std_fd == STDOUT_FILENO)
465 {
466 return _pipe_fd[READ];
467 }
468 else if (_std_fd == STDERR_FILENO)
469 {
470 return _pipe_fd[READ];
471 }
472
473 return _pipe_fd[WRITE]; // STDIN_FILENO
474 }
475
476
477 bool Application::Pipe::read(libtest::vchar_t& arg)
478 {
479 fatal_assert(_std_fd == STDOUT_FILENO or _std_fd == STDERR_FILENO);
480
481 bool data_was_read= false;
482
483 ssize_t read_length;
484 char buffer[1024]= { 0 };
485 while ((read_length= ::read(_pipe_fd[READ], buffer, sizeof(buffer))))
486 {
487 if (read_length == -1)
488 {
489 switch(errno)
490 {
491 case EAGAIN:
492 break;
493
494 default:
495 Error << strerror(errno);
496 break;
497 }
498
499 break;
500 }
501
502 data_was_read= true;
503 arg.reserve(read_length +1);
504 for (size_t x= 0; x < read_length; x++)
505 {
506 arg.push_back(buffer[x]);
507 }
508 // @todo Suck up all errput code here
509 }
510
511 return data_was_read;
512 }
513
514 void Application::Pipe::nonblock()
515 {
516 int ret;
517 if ((ret= fcntl(_pipe_fd[READ], F_GETFL, 0)) == -1)
518 {
519 Error << "fcntl(F_GETFL) " << strerror(errno);
520 throw strerror(errno);
521 }
522
523 if ((ret= fcntl(_pipe_fd[READ], F_SETFL, ret | O_NONBLOCK)) == -1)
524 {
525 Error << "fcntl(F_SETFL) " << strerror(errno);
526 throw strerror(errno);
527 }
528 }
529
530 void Application::Pipe::reset()
531 {
532 close(READ);
533 close(WRITE);
534
535 #if _GNU_SOURCE
536 if (pipe2(_pipe_fd, O_NONBLOCK) == -1)
537 #else
538 if (pipe(_pipe_fd) == -1)
539 #endif
540 {
541 fatal_message(strerror(errno));
542 }
543 _open[0]= true;
544 _open[1]= true;
545
546 if (true)
547 {
548 nonblock();
549 cloexec();
550 }
551 }
552
553 void Application::Pipe::cloexec()
554 {
555 int ret;
556 if ((ret= fcntl(_pipe_fd[WRITE], F_GETFD, 0)) == -1)
557 {
558 Error << "fcntl(F_GETFD) " << strerror(errno);
559 throw strerror(errno);
560 }
561
562 if ((ret= fcntl(_pipe_fd[WRITE], F_SETFD, ret | FD_CLOEXEC)) == -1)
563 {
564 Error << "fcntl(F_SETFD) " << strerror(errno);
565 throw strerror(errno);
566 }
567 }
568
569 Application::Pipe::~Pipe()
570 {
571 close(READ);
572 close(WRITE);
573 }
574
575 void Application::Pipe::dup_for_spawn(const close_t& arg, posix_spawn_file_actions_t& file_actions)
576 {
577 int type= int(arg);
578
579 int ret;
580 if ((ret= posix_spawn_file_actions_adddup2(&file_actions, _pipe_fd[type], _std_fd )) < 0)
581 {
582 Error << "posix_spawn_file_actions_adddup2(" << strerror(ret) << ")";
583 fatal_message(strerror(ret));
584 }
585
586 if ((ret= posix_spawn_file_actions_addclose(&file_actions, _pipe_fd[type])) < 0)
587 {
588 Error << "posix_spawn_file_actions_adddup2(" << strerror(ret) << ")";
589 fatal_message(strerror(ret));
590 }
591 }
592
593 void Application::Pipe::close(const close_t& arg)
594 {
595 int type= int(arg);
596
597 if (_open[type])
598 {
599 int ret;
600 if (::close(_pipe_fd[type]) == -1)
601 {
602 Error << "close(" << strerror(errno) << ")";
603 }
604 _open[type]= false;
605 _pipe_fd[type]= -1;
606 }
607 }
608
609 void Application::create_argv(const char *args[])
610 {
611 delete_argv();
612 fatal_assert(_argc == 0);
613
614 if (_use_libtool)
615 {
616 _argc+= 2; // +2 for libtool --mode=execute
617 }
618
619 _argc+= 1; // For the command
620
621 /*
622 valgrind --error-exitcode=1 --leak-check=yes --show-reachable=yes --track-fds=yes --malloc-fill=A5 --free-fill=DE
623 */
624 if (_use_valgrind)
625 {
626 _argc+= 7;
627 }
628 else if (_use_gdb) // gdb
629 {
630 _argc+= 1;
631 }
632
633 for (Options::const_iterator iter= _options.begin(); iter != _options.end(); iter++)
634 {
635 _argc++;
636 if ((*iter).second.empty() == false)
637 {
638 _argc++;
639 }
640 }
641
642 if (args)
643 {
644 for (const char **ptr= args; *ptr; ++ptr)
645 {
646 _argc++;
647 }
648 }
649
650 _argc+= 1; // for the NULL
651
652 built_argv= new char * [_argc];
653
654 size_t x= 0;
655 if (_use_libtool)
656 {
657 assert(libtool());
658 built_argv[x++]= strdup(libtool());
659 built_argv[x++]= strdup("--mode=execute");
660 }
661
662 if (_use_valgrind)
663 {
664 /*
665 valgrind --error-exitcode=1 --leak-check=yes --show-reachable=yes --track-fds=yes --malloc-fill=A5 --free-fill=DE
666 */
667 built_argv[x++]= strdup("valgrind");
668 built_argv[x++]= strdup("--error-exitcode=1");
669 built_argv[x++]= strdup("--leak-check=yes");
670 built_argv[x++]= strdup("--show-reachable=yes");
671 built_argv[x++]= strdup("--track-fds=yes");
672 built_argv[x++]= strdup("--malloc-fill=A5");
673 built_argv[x++]= strdup("--free-fill=DE");
674 }
675 else if (_use_gdb)
676 {
677 built_argv[x++]= strdup("gdb");
678 }
679
680 built_argv[x++]= strdup(_exectuble_with_path.c_str());
681
682 for (Options::const_iterator iter= _options.begin(); iter != _options.end(); iter++)
683 {
684 built_argv[x++]= strdup((*iter).first.c_str());
685 if ((*iter).second.empty() == false)
686 {
687 built_argv[x++]= strdup((*iter).second.c_str());
688 }
689 }
690
691 if (args)
692 {
693 for (const char **ptr= args; *ptr; ++ptr)
694 {
695 built_argv[x++]= strdup(*ptr);
696 }
697 }
698 built_argv[x++]= NULL;
699 fatal_assert(x == _argc);
700 }
701
702 std::string Application::print()
703 {
704 return print_argv(built_argv, _argc);
705 }
706
707 std::string Application::arguments()
708 {
709 std::stringstream arg_buffer;
710
711 for (size_t x= 1 + _use_libtool ? 2 : 0;
712 x < _argc and built_argv[x];
713 x++)
714 {
715 arg_buffer << built_argv[x] << " ";
716 }
717
718 return arg_buffer.str();
719 }
720
721 void Application::delete_argv()
722 {
723 if (built_argv)
724 {
725 for (size_t x= 0; x < _argc; x++)
726 {
727 if (built_argv[x])
728 {
729 ::free(built_argv[x]);
730 }
731 }
732 delete[] built_argv;
733 built_argv= NULL;
734 _argc= 0;
735 }
736 }
737
738
739 int exec_cmdline(const std::string& command, const char *args[], bool use_libtool)
740 {
741 Application app(command, use_libtool);
742
743 Application::error_t ret= app.run(args);
744
745 if (ret != Application::SUCCESS)
746 {
747 return int(ret);
748 }
749
750 return int(app.wait(false));
751 }
752
753 const char *gearmand_binary()
754 {
755 return GEARMAND_BINARY;
756 }
757
758 } // namespace exec_cmdline