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 02110-1335 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
43 extern char** environ; // environment variables
44
45 static wsp::string wsrep_PATH;
46
47 void
wsrep_prepend_PATH(const char * path)48 wsrep_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
90 namespace wsp
91 {
92
93 bool
ctor_common(char ** e)94 env::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
123 void
dtor()124 env::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
env(char ** e)136 env::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
env(const env & e)146 env::env(const env& e)
147 : len_(e.len_), env_(0), errno_(0)
148 {
149 if (ctor_common(e.env_)) dtor();
150 }
151
~env()152 env::~env() { dtor(); }
153
154 int
append(const char * val)155 env::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
process(const char * cmd,const char * type,char ** env)186 process::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
325 cleanup_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
334 cleanup_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
342 cleanup_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
~process()351 process::~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
371 int
wait()372 process::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
thd(my_bool won,bool system_thread)417 thd::thd (my_bool won, bool system_thread) : 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 if (system_thread)
426 ptr->system_thread= SYSTEM_THREAD_GENERIC;
427 ptr->security_ctx->master_access= ~(ulong)0;
428 lex_start(ptr);
429 }
430 }
431
~thd()432 thd::~thd ()
433 {
434 if (ptr)
435 {
436 delete ptr;
437 my_pthread_setspecific_ptr (THR_THD, 0);
438 }
439 }
440
441 } // namespace wsp
442
443 /* Returns INADDR_NONE, INADDR_ANY, INADDR_LOOPBACK or something else */
wsrep_check_ip(const char * const addr,bool * is_ipv6)444 unsigned int wsrep_check_ip (const char* const addr, bool *is_ipv6)
445 {
446 unsigned int ret = INADDR_NONE;
447 struct addrinfo *res, hints;
448
449 memset (&hints, 0, sizeof(hints));
450 hints.ai_flags= AI_PASSIVE/*|AI_ADDRCONFIG*/;
451 hints.ai_socktype= SOCK_STREAM;
452 hints.ai_family= AF_UNSPEC;
453
454 *is_ipv6= false;
455
456 int gai_ret = getaddrinfo(addr, NULL, &hints, &res);
457 if (0 == gai_ret)
458 {
459 if (AF_INET == res->ai_family) /* IPv4 */
460 {
461 struct sockaddr_in* a= (struct sockaddr_in*)res->ai_addr;
462 ret= htonl(a->sin_addr.s_addr);
463 }
464 else /* IPv6 */
465 {
466 struct sockaddr_in6* a= (struct sockaddr_in6*)res->ai_addr;
467 if (IN6_IS_ADDR_UNSPECIFIED(&a->sin6_addr))
468 ret= INADDR_ANY;
469 else if (IN6_IS_ADDR_LOOPBACK(&a->sin6_addr))
470 ret= INADDR_LOOPBACK;
471 else
472 ret= 0xdeadbeef;
473
474 *is_ipv6= true;
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 return ret;
484 }
485
486 extern char* my_bind_addr_str;
487
wsrep_guess_ip(char * buf,size_t buf_len)488 size_t wsrep_guess_ip (char* buf, size_t buf_len)
489 {
490 size_t ret= 0;
491
492 // Attempt 1: Try to get the IP from bind-address.
493 if (my_bind_addr_str && my_bind_addr_str[0] != '\0')
494 {
495 bool unused;
496 unsigned int const ip_type= wsrep_check_ip(my_bind_addr_str, &unused);
497
498 if (INADDR_NONE == ip_type) {
499 WSREP_ERROR("Networking not configured, cannot receive state "
500 "transfer.");
501 ret= 0;
502 goto done;
503 } else if (INADDR_ANY != ip_type) {
504 strncpy (buf, my_bind_addr_str, buf_len);
505 ret= strlen(buf);
506 goto done;
507 }
508 }
509
510 // Attempt 2: mysqld binds to all interfaces, try IP from wsrep_node_address.
511 if (wsrep_node_address && wsrep_node_address[0] != '\0') {
512 wsp::Address addr(wsrep_node_address);
513 if (!addr.is_valid())
514 {
515 WSREP_WARN("Could not parse wsrep_node_address : %s",
516 wsrep_node_address);
517 ret= 0;
518 goto done;
519 }
520
521 /* Safety check: Buffer length should be sufficiently large. */
522 DBUG_ASSERT(buf_len >= addr.get_address_len());
523
524 memcpy(buf, addr.get_address(), addr.get_address_len());
525 ret= addr.get_address_len();
526 goto done;
527 }
528
529 /*
530 Attempt 3: Try to get the IP from the list of available interfaces.
531
532 getifaddrs() is avaiable at least on Linux since glib 2.3, FreeBSD,
533 MAC OSX, OpenSolaris, Solaris.
534
535 On platforms which do not support getifaddrs() this function returns
536 a failure and user is prompted to do manual configuration.
537 */
538 #if HAVE_GETIFADDRS
539 struct ifaddrs *ifaddr, *ifa;
540 int family;
541
542 if (getifaddrs(&ifaddr) == 0)
543 {
544 for (ifa= ifaddr; ifa != NULL; ifa = ifa->ifa_next)
545 {
546 if (!ifa->ifa_addr)
547 continue;
548
549 family= ifa->ifa_addr->sa_family;
550
551 if ((family != AF_INET) && (family != AF_INET6))
552 continue;
553
554 // Skip loopback interfaces (like lo:127.0.0.1)
555 if (ifa->ifa_flags & IFF_LOOPBACK)
556 continue;
557
558 /*
559 Get IP address from the socket address. The resulting address may have
560 zone ID appended for IPv6 addresses (<address>%<zone-id>).
561 */
562 if (vio_getnameinfo(ifa->ifa_addr, buf, buf_len, NULL, 0, NI_NUMERICHOST))
563 continue;
564
565 freeifaddrs(ifaddr);
566
567 ret= strlen(buf);
568 goto done;
569 }
570 freeifaddrs(ifaddr);
571 }
572 #endif /* HAVE_GETIFADDRS */
573
574 done:
575 WSREP_DEBUG("wsrep_guess_ip() : %s", (ret > 0) ? buf : "????");
576 return ret;
577 }
578
579 /* returns the length of the host part of the address string */
wsrep_host_len(const char * const addr,size_t const addr_len)580 size_t wsrep_host_len(const char* const addr, size_t const addr_len)
581 {
582 // check for IPv6 notation first
583 const char* const bracket= ('[' == addr[0] ? strchr(addr, ']') : NULL);
584
585 if (bracket) { // IPv6
586 return (bracket - addr + 1);
587 }
588 else { // host part ends at ':' or end of string
589 const char* const colon= strchr(addr, ':');
590 return (colon ? colon - addr : addr_len);
591 }
592 }
593