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