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