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