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