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