1 /* Copyright 2010-2015 Codership Oy <http://www.codership.com>
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; version 2 of the License.
6
7 This program is distributed in the hope that it will be useful,
8 but WITHOUT ANY WARRANTY; without even the implied warranty of
9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 GNU General Public License for more details.
11
12 You should have received a copy of the GNU General Public License
13 along with this program; if not, write to the Free Software
14 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
15 */
16
17 //! @file some utility functions and classes not directly related to replication
18
19 #ifndef _GNU_SOURCE
20 #define _GNU_SOURCE // POSIX_SPAWN_USEVFORK flag
21 #endif
22
23 #include "wsrep_utils.h"
24 #include "wsrep_mysqld.h"
25
26 #include <sql_class.h>
27 #include <socket_connection.h>
28
29 #include <signal.h>
30 #include <spawn.h> // posix_spawn()
31 #include <unistd.h> // pipe()
32 #include <errno.h> // errno
33 #include <signal.h> // sigemptyset(), sigaddset()
34 #include <string.h> // strerror()
35 #include <sys/wait.h> // waitpid()
36 #include <sys/types.h>
37 #include <sys/socket.h>
38 #include <netdb.h> // getaddrinfo()
39
40 #ifdef HAVE_GETIFADDRS
41 #include <net/if.h>
42 #include <ifaddrs.h>
43 #endif // HAVE_GETIFADDRS
44
45 extern char** environ; // environment variables
46
47 static wsp::string wsrep_PATH;
48
49 void
wsrep_prepend_PATH(const char * path)50 wsrep_prepend_PATH (const char* path)
51 {
52 int count = 0;
53
54 while (environ[count])
55 {
56 if (strncmp (environ[count], "PATH=", 5))
57 {
58 count++;
59 continue;
60 }
61
62 char* const old_path (environ[count]);
63
64 if (strstr (old_path, path)) return; // path already there
65
66 size_t const new_path_len(strlen(old_path) + strlen(":") +
67 strlen(path) + 1);
68
69 char* const new_path (static_cast<char*>(malloc(new_path_len)));
70
71 if (new_path)
72 {
73 snprintf (new_path, new_path_len, "PATH=%s:%s", path,
74 old_path + strlen("PATH="));
75
76 wsrep_PATH.set (new_path);
77 environ[count] = new_path;
78 }
79 else
80 {
81 WSREP_ERROR ("Failed to allocate 'PATH' environment variable "
82 "buffer of size %zu.", new_path_len);
83 }
84
85 return;
86 }
87
88 WSREP_ERROR ("Failed to find 'PATH' environment variable. "
89 "State snapshot transfer may not be working.");
90 }
91
92 namespace wsp
93 {
94
95 bool
ctor_common(char ** e)96 env::ctor_common(char** e)
97 {
98 env_ = static_cast<char**>(malloc((len_ + 1) * sizeof(char*)));
99
100 if (env_)
101 {
102 for (size_t i(0); i < len_; ++i)
103 {
104 assert(e[i]); // caller should make sure about len_
105 env_[i] = strdup(e[i]);
106 if (!env_[i])
107 {
108 errno_ = errno;
109 WSREP_ERROR("Failed to allocate env. var: %s", e[i]);
110 return true;
111 }
112 }
113
114 env_[len_] = NULL;
115 return false;
116 }
117 else
118 {
119 errno_ = errno;
120 WSREP_ERROR("Failed to allocate env. var vector of length: %zu", len_);
121 return true;
122 }
123 }
124
125 void
dtor()126 env::dtor()
127 {
128 if (env_)
129 {
130 /* don't need to go beyond the first NULL */
131 for (size_t i(0); env_[i] != NULL; ++i) { free(env_[i]); }
132 free(env_);
133 env_ = NULL;
134 }
135 len_ = 0;
136 }
137
env(char ** e)138 env::env(char** e)
139 : len_(0), env_(NULL), errno_(0)
140 {
141 if (!e) { e = environ; }
142 /* count the size of the vector */
143 while (e[len_]) { ++len_; }
144
145 if (ctor_common(e)) dtor();
146 }
147
env(const env & e)148 env::env(const env& e)
149 : len_(e.len_), env_(0), errno_(0)
150 {
151 if (ctor_common(e.env_)) dtor();
152 }
153
~env()154 env::~env() { dtor(); }
155
156 int
append(const char * val)157 env::append(const char* val)
158 {
159 char** tmp = static_cast<char**>(realloc(env_, (len_ + 2)*sizeof(char*)));
160
161 if (tmp)
162 {
163 env_ = tmp;
164 env_[len_] = strdup(val);
165
166 if (env_[len_])
167 {
168 ++len_;
169 env_[len_] = NULL;
170 }
171 else errno_ = errno;
172 }
173 else errno_ = errno;
174
175 return errno_;
176 }
177
178
179 #define PIPE_READ 0
180 #define PIPE_WRITE 1
181 #define STDIN_FD 0
182 #define STDOUT_FD 1
183
184 #ifndef POSIX_SPAWN_USEVFORK
185 # define POSIX_SPAWN_USEVFORK 0
186 #endif
187
process(const char * cmd,const char * type,char ** env)188 process::process (const char* cmd, const char* type, char** env)
189 : str_(cmd ? strdup(cmd) : strdup("")), io_(NULL), err_(EINVAL), pid_(0)
190 {
191 if (0 == str_)
192 {
193 WSREP_ERROR ("Can't allocate command line of size: %zu", strlen(cmd));
194 err_ = ENOMEM;
195 return;
196 }
197
198 if (0 == strlen(str_))
199 {
200 WSREP_ERROR ("Can't start a process: null or empty command line.");
201 return;
202 }
203
204 if (NULL == type || (strcmp (type, "w") && strcmp(type, "r")))
205 {
206 WSREP_ERROR ("type argument should be either \"r\" or \"w\".");
207 return;
208 }
209
210 if (NULL == env) { env = environ; } // default to global environment
211
212 int pipe_fds[2] = { -1, };
213 if (::pipe(pipe_fds))
214 {
215 err_ = errno;
216 WSREP_ERROR ("pipe() failed: %d (%s)", err_, strerror(err_));
217 return;
218 }
219
220 // which end of pipe will be returned to parent
221 int const parent_end (strcmp(type,"w") ? PIPE_READ : PIPE_WRITE);
222 int const child_end (parent_end == PIPE_READ ? PIPE_WRITE : PIPE_READ);
223 int const close_fd (parent_end == PIPE_READ ? STDOUT_FD : STDIN_FD);
224
225 char* const pargv[4] = { strdup("sh"), strdup("-c"), strdup(str_), NULL };
226 if (!(pargv[0] && pargv[1] && pargv[2]))
227 {
228 err_ = ENOMEM;
229 WSREP_ERROR ("Failed to allocate pargv[] array.");
230 goto cleanup_pipe;
231 }
232
233 posix_spawnattr_t attr;
234 err_ = posix_spawnattr_init (&attr);
235 if (err_)
236 {
237 WSREP_ERROR ("posix_spawnattr_init() failed: %d (%s)",
238 err_, strerror(err_));
239 goto cleanup_pipe;
240 }
241
242 /* make sure that no signlas are masked in child process */
243 sigset_t sigmask_empty;
244 sigemptyset(&sigmask_empty);
245 err_ = posix_spawnattr_setsigmask(&attr, &sigmask_empty);
246 if (err_)
247 {
248 WSREP_ERROR ("posix_spawnattr_setsigmask() failed: %d (%s)",
249 err_, strerror(err_));
250 goto cleanup_attr;
251 }
252
253 /* make sure the following signals are not ignored in child process */
254 sigset_t default_signals; sigemptyset(&default_signals);
255 sigaddset(&default_signals, SIGHUP);
256 sigaddset(&default_signals, SIGINT);
257 sigaddset(&default_signals, SIGQUIT);
258 sigaddset(&default_signals, SIGPIPE);
259 sigaddset(&default_signals, SIGTERM);
260 sigaddset(&default_signals, SIGCHLD);
261 err_ = posix_spawnattr_setsigdefault(&attr, &default_signals);
262 if (err_)
263 {
264 WSREP_ERROR ("posix_spawnattr_setsigdefault() failed: %d (%s)",
265 err_, strerror(err_));
266 goto cleanup_attr;
267 }
268
269 err_ = posix_spawnattr_setflags (&attr, POSIX_SPAWN_SETSIGDEF |
270 POSIX_SPAWN_SETSIGMASK |
271 /* start a new process group */ POSIX_SPAWN_SETPGROUP |
272 POSIX_SPAWN_USEVFORK);
273 if (err_)
274 {
275 WSREP_ERROR ("posix_spawnattr_setflags() failed: %d (%s)",
276 err_, strerror(err_));
277 goto cleanup_attr;
278 }
279
280 posix_spawn_file_actions_t fact;
281 err_ = posix_spawn_file_actions_init (&fact);
282 if (err_)
283 {
284 WSREP_ERROR ("posix_spawn_file_actions_init() failed: %d (%s)",
285 err_, strerror(err_));
286 goto cleanup_attr;
287 }
288
289 // close child's stdout|stdin depending on what we returning
290 err_ = posix_spawn_file_actions_addclose (&fact, close_fd);
291 if (err_)
292 {
293 WSREP_ERROR ("posix_spawn_file_actions_addclose() failed: %d (%s)",
294 err_, strerror(err_));
295 goto cleanup_fact;
296 }
297
298 // substitute our pipe descriptor in place of the closed one
299 err_ = posix_spawn_file_actions_adddup2 (&fact,
300 pipe_fds[child_end], close_fd);
301 if (err_)
302 {
303 WSREP_ERROR ("posix_spawn_file_actions_addup2() failed: %d (%s)",
304 err_, strerror(err_));
305 goto cleanup_fact;
306 }
307
308 err_ = posix_spawnp (&pid_, pargv[0], &fact, &attr, pargv, env);
309 if (err_)
310 {
311 WSREP_ERROR ("posix_spawnp(%s) failed: %d (%s)",
312 pargv[2], err_, strerror(err_));
313 pid_ = 0; // just to make sure it was not messed up in the call
314 goto cleanup_fact;
315 }
316
317 io_ = fdopen (pipe_fds[parent_end], type);
318
319 if (io_)
320 {
321 pipe_fds[parent_end] = -1; // skip close on cleanup
322 }
323 else
324 {
325 err_ = errno;
326 WSREP_ERROR ("fdopen() failed: %d (%s)", err_, strerror(err_));
327 }
328
329 cleanup_fact:
330 int err; // to preserve err_ code
331 err = posix_spawn_file_actions_destroy (&fact);
332 if (err)
333 {
334 WSREP_ERROR ("posix_spawn_file_actions_destroy() failed: %d (%s)\n",
335 err, strerror(err));
336 }
337
338 cleanup_attr:
339 err = posix_spawnattr_destroy (&attr);
340 if (err)
341 {
342 WSREP_ERROR ("posix_spawnattr_destroy() failed: %d (%s)",
343 err, strerror(err));
344 }
345
346 cleanup_pipe:
347 if (pipe_fds[0] >= 0) close (pipe_fds[0]);
348 if (pipe_fds[1] >= 0) close (pipe_fds[1]);
349
350 free (pargv[0]);
351 free (pargv[1]);
352 free (pargv[2]);
353 }
354
~process()355 process::~process ()
356 {
357 if (io_)
358 {
359 assert (pid_);
360 assert (str_);
361
362 WSREP_WARN("Closing pipe to child process: %s, PID(%ld) "
363 "which might still be running.", str_, (long)pid_);
364
365 if (fclose (io_) == -1)
366 {
367 err_ = errno;
368 WSREP_ERROR("fclose() failed: %d (%s)", err_, strerror(err_));
369 }
370 }
371
372 if (str_) free (const_cast<char*>(str_));
373 }
374
375 int
wait()376 process::wait ()
377 {
378 if (pid_)
379 {
380 int status;
381 if (-1 == waitpid(pid_, &status, 0))
382 {
383 err_ = errno; assert (err_);
384 WSREP_ERROR("Waiting for process failed: %s, PID(%ld): %d (%s)",
385 str_, (long)pid_, err_, strerror (err_));
386 }
387 else
388 { // command completed, check exit status
389 if (WIFEXITED (status)) {
390 err_ = WEXITSTATUS (status);
391 }
392 else { // command didn't complete with exit()
393 WSREP_ERROR("Process was aborted.");
394 err_ = errno ? errno : ECHILD;
395 }
396
397 if (err_) {
398 switch (err_) /* Translate error codes to more meaningful */
399 {
400 case 126: err_ = EACCES; break; /* Permission denied */
401 case 127: err_ = ENOENT; break; /* No such file or directory */
402 case 143: err_ = EINTR; break; /* Subprocess killed */
403 }
404 WSREP_ERROR("Process completed with error: %s: %d (%s)",
405 str_, err_, strerror(err_));
406 }
407
408 pid_ = 0;
409 if (io_) fclose (io_);
410 io_ = NULL;
411 }
412 }
413 else {
414 assert (NULL == io_);
415 WSREP_ERROR("Command did not run: %s", str_);
416 }
417
418 return err_;
419 }
420
thd(my_bool won)421 thd::thd (my_bool won) : init(), ptr(new THD)
422 {
423 if (ptr)
424 {
425 ptr->thread_stack= (char*) &ptr;
426 ptr->store_globals();
427 ptr->variables.option_bits&= ~OPTION_BIN_LOG; // disable binlog
428 ptr->variables.wsrep_on = won;
429 ptr->security_context()->set_master_access(~(ulong)0);
430 lex_start(ptr);
431 }
432 }
433
~thd()434 thd::~thd ()
435 {
436 if (ptr)
437 {
438 delete ptr;
439 pthread_setspecific(THR_THD, 0);
440 }
441 }
442
443 } // namespace wsp
444
445 /* Returns INADDR_NONE, INADDR_ANY, INADDR_LOOPBACK or something else */
wsrep_check_ip(const char * const addr)446 unsigned int wsrep_check_ip (const char* const addr)
447 {
448 if (addr && 0 == strcasecmp(addr, MY_BIND_ALL_ADDRESSES)) return INADDR_ANY;
449
450 unsigned int ret = INADDR_NONE;
451 struct addrinfo *res, hints;
452
453 memset (&hints, 0, sizeof(hints));
454 hints.ai_flags= AI_PASSIVE/*|AI_ADDRCONFIG*/;
455 hints.ai_socktype= SOCK_STREAM;
456 hints.ai_family= AF_UNSPEC;
457
458 int gai_ret = getaddrinfo(addr, NULL, &hints, &res);
459 if (0 == gai_ret)
460 {
461 if (AF_INET == res->ai_family) /* IPv4 */
462 {
463 struct sockaddr_in* a= (struct sockaddr_in*)res->ai_addr;
464 ret= htonl(a->sin_addr.s_addr);
465 }
466 else /* IPv6 */
467 {
468 struct sockaddr_in6* a= (struct sockaddr_in6*)res->ai_addr;
469 if (IN6_IS_ADDR_UNSPECIFIED(&a->sin6_addr))
470 ret= INADDR_ANY;
471 else if (IN6_IS_ADDR_LOOPBACK(&a->sin6_addr))
472 ret= INADDR_LOOPBACK;
473 else
474 ret= 0xdeadbeef;
475 }
476 freeaddrinfo (res);
477 }
478 else {
479 WSREP_ERROR ("getaddrinfo() failed on '%s': %d (%s)",
480 addr, gai_ret, gai_strerror(gai_ret));
481 }
482
483 // uint8_t* b= (uint8_t*)&ret;
484 // fprintf (stderr, "########## wsrep_check_ip returning: %hhu.%hhu.%hhu.%hhu\n",
485 // b[0], b[1], b[2], b[3]);
486
487 return ret;
488 }
489
wsrep_guess_ip(char * buf,size_t buf_len)490 size_t wsrep_guess_ip (char* buf, size_t buf_len)
491 {
492 size_t ip_len = 0;
493
494 if (my_bind_addr_str && my_bind_addr_str[0] != '\0')
495 {
496 unsigned int const ip_type= wsrep_check_ip(my_bind_addr_str);
497
498 if (INADDR_NONE == ip_type) {
499 WSREP_ERROR("Networking not configured, cannot receive state transfer.");
500 return 0;
501 }
502
503 if (INADDR_ANY != ip_type) {
504 strncpy (buf, my_bind_addr_str, buf_len);
505 return strlen(buf);
506 }
507 }
508
509 // mysqld binds to all interfaces - try IP from wsrep_node_address
510 if (wsrep_node_address && wsrep_node_address[0] != '\0') {
511 const char* const colon_ptr = strchr(wsrep_node_address, ':');
512
513 if (colon_ptr)
514 ip_len = colon_ptr - wsrep_node_address;
515 else
516 ip_len = strlen(wsrep_node_address);
517
518 if (ip_len >= buf_len) {
519 WSREP_WARN("default_ip(): buffer too short: %zu <= %zd", buf_len, ip_len);
520 return 0;
521 }
522
523 memcpy (buf, wsrep_node_address, ip_len);
524 buf[ip_len] = '\0';
525 return ip_len;
526 }
527
528
529 //
530 // getifaddrs() is avaiable at least on Linux since glib 2.3, FreeBSD
531 // MAC OS X, opensolaris, Solaris.
532 //
533 // On platforms which do not support getifaddrs() this function returns
534 // a failure and user is prompted to do manual configuration.
535 //
536 #if HAVE_GETIFADDRS
537 struct ifaddrs *ifaddr, *ifa;
538 if (getifaddrs(&ifaddr) == 0)
539 {
540 for (ifa= ifaddr; ifa != NULL; ifa = ifa->ifa_next)
541 {
542 if (!ifa->ifa_addr || ifa->ifa_addr->sa_family != AF_INET) // TODO AF_INET6
543 continue;
544
545 // Skip loopback interfaces (like lo:127.0.0.1)
546 if (ifa->ifa_flags & IFF_LOOPBACK)
547 continue;
548
549 if (vio_getnameinfo(ifa->ifa_addr, buf, buf_len, NULL, 0, NI_NUMERICHOST))
550 continue;
551
552 freeifaddrs(ifaddr);
553 return strlen(buf);
554 }
555 freeifaddrs(ifaddr);
556 }
557 #endif // HAVE_GETIFADDRS
558
559 return 0;
560 }
561
562 /* returns the length of the host part of the address string */
wsrep_host_len(const char * const addr,size_t const addr_len)563 size_t wsrep_host_len(const char* const addr, size_t const addr_len)
564 {
565 // check for IPv6 notation first
566 const char* const bracket= ('[' == addr[0] ? strchr(addr, ']') : NULL);
567
568 if (bracket) { // IPv6
569 return (bracket - addr + 1);
570 }
571 else { // host part ends at ':' or end of string
572 const char* const colon= strchr(addr, ':');
573 return (colon ? colon - addr : addr_len);
574 }
575 }
576