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