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