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