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