1 /*
2 * $Header$
3 *
4 * Handles watchdog connection, and protocol communication with pgpool-II
5 *
6 * pgpool: a language independent connection pool server for PostgreSQL
7 * written by Tatsuo Ishii
8 *
9 * Copyright (c) 2003-2015 PgPool Global Development Group
10 *
11 * Permission to use, copy, modify, and distribute this software and
12 * its documentation for any purpose and without fee is hereby
13 * granted, provided that the above copyright notice appear in all
14 * copies and that both that copyright notice and this permission
15 * notice appear in supporting documentation, and that the name of the
16 * author not be used in advertising or publicity pertaining to
17 * distribution of the software without specific, written prior
18 * permission. The author makes no representations about the
19 * suitability of this software for any purpose. It is provided "as
20 * is" without express or implied warranty.
21 *
22 */
23
24 #include <stdio.h>
25 #include <errno.h>
26 #include <ctype.h>
27 #include <time.h>
28 #include <string.h>
29 #include <stdlib.h>
30 #include <sys/stat.h>
31 #include <sys/un.h>
32 #include <sys/types.h>
33 #include <sys/socket.h>
34 #include <netinet/in.h>
35 #include <netinet/tcp.h>
36 #include <netinet/ip.h>
37 #include <netdb.h>
38 #include <arpa/inet.h>
39 #include <unistd.h>
40 #include <fcntl.h>
41
42 #if defined(SO_BINDTODEVICE)
43 #include <net/if.h>
44 #endif
45
46 #include "pool.h"
47 #include "utils/palloc.h"
48 #include "utils/memutils.h"
49 #include "utils/elog.h"
50 #include "pool_config.h"
51 #include "auth/md5.h"
52 #include "watchdog/watchdog.h"
53 #include "watchdog/wd_lifecheck.h"
54 #include "watchdog/wd_utils.h"
55
56 #define MAX_BIND_TRIES 5
57 /*
58 * heartbeat packet
59 */
60 typedef struct {
61 char from[WD_MAX_HOST_NAMELEN];
62 int from_pgpool_port;
63 struct timeval send_time;
64 char hash[WD_AUTH_HASH_LEN + 1];
65 } WdHbPacket;
66
67
68 static RETSIGTYPE hb_sender_exit(int sig);
69 static RETSIGTYPE hb_receiver_exit(int sig);
70 static int hton_wd_hb_packet(WdHbPacket *to, WdHbPacket *from);
71 static int ntoh_wd_hb_packet(WdHbPacket *to, WdHbPacket *from);
72 static int packet_to_string_hb(WdHbPacket *pkt, char * str, int maxlen);
73 static void wd_set_reuseport(int sock);
74
75 static int wd_create_hb_send_socket(WdHbIf * hb_if);
76 static int wd_create_hb_recv_socket(WdHbIf * hb_if);
77
78 static void wd_hb_send(int sock, WdHbPacket * pkt, int len, const char * destination, const int dest_port);
79 static void wd_hb_recv(int sock, WdHbPacket * pkt, char *from_addr);
80
81 /* create socket for sending heartbeat */
82 static int
wd_create_hb_send_socket(WdHbIf * hb_if)83 wd_create_hb_send_socket(WdHbIf *hb_if)
84 {
85 int sock;
86 int tos;
87
88 /* create socket */
89 if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
90 {
91 /* socket create failed */
92 ereport(ERROR,
93 (errmsg("failed to create watchdog heartbeat sender socket"),
94 errdetail("create socket failed with reason: \"%s\"", strerror(errno))));
95 }
96
97 /* set socket option */
98 tos = IPTOS_LOWDELAY;
99 if (setsockopt(sock, IPPROTO_IP, IP_TOS, (char *) &tos, sizeof(tos)) == -1 )
100 {
101 close(sock);
102 ereport(ERROR,
103 (errmsg("failed to create watchdog heartbeat sender socket"),
104 errdetail("setsockopt(IP_TOS) failed with reason: \"%s\"", strerror(errno))));
105 }
106
107 if (hb_if->if_name[0] != '\0')
108 {
109 #if defined(SO_BINDTODEVICE)
110 {
111 if (geteuid() == 0) /* check root privileges */
112 {
113 struct ifreq i;
114 strlcpy(i.ifr_name, hb_if->if_name, sizeof(i.ifr_name));
115
116 if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, &i, sizeof(i)) == -1)
117 {
118 close(sock);
119 ereport(ERROR,
120 (errmsg("failed to create watchdog heartbeat sender socket"),
121 errdetail("setsockopt(SO_BINDTODEVICE) failed with reason: \"%s\"", strerror(errno))));
122
123 }
124 ereport(LOG,
125 (errmsg("creating socket for sending heartbeat"),
126 errdetail("bind send socket to device: %s", i.ifr_name)));
127 }
128 else
129 ereport(LOG,
130 (errmsg("creating socket for sending heartbeat"),
131 errdetail("setsockopt(SO_BINDTODEVICE) requires root privilege")));
132 }
133 #else
134 ereport(LOG,
135 (errmsg("creating socket for sending heartbeat"),
136 errdetail("setsockopt(SO_BINDTODEVICE) is not available on this platform")));
137 #endif
138 }
139
140 wd_set_reuseport(sock);
141 ereport(LOG,
142 (errmsg("creating socket for sending heartbeat"),
143 errdetail("set SO_REUSEPORT")));
144
145 if (fcntl(sock, F_SETFD, FD_CLOEXEC) < 0)
146 {
147 close(sock);
148 ereport(ERROR,
149 (errmsg("failed to create watchdog heartbeat sender socket"),
150 errdetail("setting close-on-exec flag failed with reason: \"%s\"", strerror(errno))));
151 }
152
153 return sock;
154 }
155
156 /* create socket for receiving heartbeat */
157 static int
wd_create_hb_recv_socket(WdHbIf * hb_if)158 wd_create_hb_recv_socket(WdHbIf *hb_if)
159 {
160 struct sockaddr_in addr;
161 int sock;
162 const int one = 1;
163 int bind_tries;
164 int bind_is_done;
165
166 memset(&(addr), 0, sizeof(addr));
167 addr.sin_family = AF_INET;
168 addr.sin_port = htons(pool_config->wd_heartbeat_port);
169 addr.sin_addr.s_addr = INADDR_ANY;
170
171 /* create socket */
172 if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
173 {
174 /* socket create failed */
175 ereport(ERROR,
176 (errmsg("failed to create watchdog heartbeat receive socket"),
177 errdetail("create socket failed with reason: \"%s\"", strerror(errno))));
178 }
179
180 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &one, sizeof(one)) == -1 )
181 {
182 close(sock);
183 ereport(ERROR,
184 (errmsg("failed to create watchdog heartbeat receive socket"),
185 errdetail("setsockopt(SO_REUSEADDR) failed with reason: \"%s\"", strerror(errno))));
186 }
187
188 if (hb_if->if_name[0] != '\0')
189 {
190 #if defined(SO_BINDTODEVICE)
191 {
192 if (geteuid() == 0) /* check root privileges */
193 {
194 struct ifreq i;
195 strlcpy(i.ifr_name, hb_if->if_name, sizeof(i.ifr_name));
196
197 if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, &i, sizeof(i)) == -1)
198 {
199 close(sock);
200 ereport(ERROR,
201 (errmsg("failed to create watchdog heartbeat receive socket"),
202 errdetail("setsockopt(SO_BINDTODEVICE) failed with reason: \"%s\"", strerror(errno))));
203 }
204 ereport(LOG,
205 (errmsg("createing watchdog heartbeat receive socket."),
206 errdetail("bind receive socket to device: \"%s\"", i.ifr_name)));
207
208 }
209 else
210 ereport(LOG,
211 (errmsg("failed to create watchdog heartbeat receive socket."),
212 errdetail("setsockopt(SO_BINDTODEVICE) requies root privilege")));
213 }
214 #else
215 ereport(LOG,
216 (errmsg("failed to create watchdog heartbeat receive socket"),
217 errdetail("setsockopt(SO_BINDTODEVICE) is not available on this platform")));
218 #endif
219 }
220
221 wd_set_reuseport(sock);
222
223 ereport(LOG,
224 (errmsg("creating watchdog heartbeat receive socket."),
225 errdetail("set SO_REUSEPORT")));
226
227 bind_is_done = 0;
228 for (bind_tries = 0; !bind_is_done && bind_tries < MAX_BIND_TRIES; bind_tries++)
229 {
230 if (bind(sock, (struct sockaddr *)&addr, sizeof(struct sockaddr)) < 0)
231 {
232 ereport(LOG,
233 (errmsg("failed to create watchdog heartbeat receive socket. retrying..."),
234 errdetail("bind failed with reason: \"%s\"",
235 strerror(errno))));
236
237 sleep(1);
238 }
239 else
240 {
241 bind_is_done = 1;
242 }
243 }
244
245 /* bind failed finally */
246 if (!bind_is_done)
247 {
248 close(sock);
249 ereport(ERROR,
250 (errmsg("failed to create watchdog heartbeat receive socket"),
251 errdetail("bind socket failed with reason: \"%s\"", strerror(errno))));
252 }
253
254 if (fcntl(sock, F_SETFD, FD_CLOEXEC) < 0)
255 {
256 close(sock);
257 ereport(ERROR,
258 (errmsg("failed to create watchdog heartbeat receive socket"),
259 errdetail("setting close-on-exec flag failed with reason: \"%s\"", strerror(errno))));
260 }
261
262 return sock;
263 }
264
265 /* send heartbeat signal */
266 static void
wd_hb_send(int sock,WdHbPacket * pkt,int len,const char * host,const int port)267 wd_hb_send(int sock, WdHbPacket * pkt, int len, const char * host, const int port)
268 {
269 int rtn;
270 struct sockaddr_in addr;
271 struct hostent *hp;
272 WdHbPacket buf;
273
274 if (!host || !strlen(host))
275 ereport(ERROR,
276 (errmsg("failed to send watchdog heartbeat. host name is empty")));
277
278 hp = gethostbyname(host);
279 if ((hp == NULL) || (hp->h_addrtype != AF_INET))
280 ereport(ERROR,
281 (errmsg("failed to send watchdog heartbeat, gethostbyname() failed"),
282 errdetail("gethostbyname on \"%s\" failed with reason: \"%s\"",host, hstrerror(h_errno))));
283
284 memmove((char *) &(addr.sin_addr), (char *) hp->h_addr, hp->h_length);
285
286 addr.sin_family = AF_INET;
287 addr.sin_port = htons(port);
288
289 hton_wd_hb_packet(&buf, pkt);
290
291 if ((rtn = sendto(sock, &buf, sizeof(WdHbPacket), 0,
292 (struct sockaddr *)&addr, sizeof(addr))) != len)
293 {
294 ereport(ERROR,
295 (errmsg("failed to send watchdog heartbeat, sendto failed"),
296 errdetail("sending packet to \"%s\" failed with reason: \"%s\"",host, strerror(errno))));
297
298 }
299 ereport(DEBUG2,
300 (errmsg("watchdog heartbeat: send %d byte packet", rtn)));
301
302 }
303
304 /*
305 * receive heartbeat signal
306 * the function expects the from_addr to be at least WD_MAX_HOST_NAMELEN bytes long
307 */
308 void
wd_hb_recv(int sock,WdHbPacket * pkt,char * from_addr)309 static wd_hb_recv(int sock, WdHbPacket * pkt, char *from_addr)
310 {
311 int rtn;
312 struct sockaddr_in senderinfo;
313 socklen_t addrlen;
314 WdHbPacket buf;
315
316 addrlen = sizeof(senderinfo);
317
318 rtn = recvfrom(sock, &buf, sizeof(WdHbPacket), 0,
319 (struct sockaddr *)&senderinfo, &addrlen);
320 if (rtn < 0)
321 ereport(ERROR,
322 (errmsg("failed to receive heartbeat packet")));
323 else if (rtn == 0)
324 ereport(ERROR,
325 (errmsg("failed to receive heartbeat received zero length packet")));
326 else
327 ereport(DEBUG2,
328 (errmsg("watchdog heartbeat: received %d byte packet", rtn)));
329
330 strncpy(from_addr,inet_ntoa(senderinfo.sin_addr),WD_MAX_HOST_NAMELEN-1);
331
332 ntoh_wd_hb_packet(pkt, &buf);
333 }
334
335 /* fork heartbeat receiver child */
336 pid_t
wd_hb_receiver(int fork_wait_time,WdHbIf * hb_if)337 wd_hb_receiver(int fork_wait_time, WdHbIf *hb_if)
338 {
339 int sock;
340 pid_t pid = 0;
341 WdHbPacket pkt;
342 struct timeval tv;
343 char from[WD_MAX_HOST_NAMELEN];
344 int from_pgpool_port;
345 char buf[WD_AUTH_HASH_LEN + 1];
346 char pack_str[WD_MAX_PACKET_STRING];
347 int pack_str_len;
348 sigjmp_buf local_sigjmp_buf;
349
350 pid = fork();
351 if (pid != 0)
352 {
353 if (pid == -1)
354 ereport(PANIC,
355 (errmsg("failed to fork a heartbeat receiver child")));
356 return pid;
357 }
358
359 on_exit_reset();
360 processType = PT_HB_RECEIVER;
361
362 if (fork_wait_time > 0)
363 {
364 sleep(fork_wait_time);
365 }
366
367 POOL_SETMASK(&UnBlockSig);
368
369 pool_signal(SIGTERM, hb_receiver_exit);
370 pool_signal(SIGINT, hb_receiver_exit);
371 pool_signal(SIGQUIT, hb_receiver_exit);
372 pool_signal(SIGCHLD, SIG_DFL);
373 pool_signal(SIGHUP, SIG_IGN);
374 pool_signal(SIGUSR1, SIG_IGN);
375 pool_signal(SIGUSR2, SIG_IGN);
376 pool_signal(SIGPIPE, SIG_IGN);
377 pool_signal(SIGALRM, SIG_IGN);
378
379 init_ps_display("", "", "", "");
380 /* Create per loop iteration memory context */
381 ProcessLoopContext = AllocSetContextCreate(TopMemoryContext,
382 "wdhb_hb_receiver",
383 ALLOCSET_DEFAULT_MINSIZE,
384 ALLOCSET_DEFAULT_INITSIZE,
385 ALLOCSET_DEFAULT_MAXSIZE);
386
387 MemoryContextSwitchTo(TopMemoryContext);
388
389 sock = wd_create_hb_recv_socket(hb_if);
390
391 set_ps_display("heartbeat receiver", false);
392
393 if (sigsetjmp(local_sigjmp_buf, 1) != 0)
394 {
395 /* Since not using PG_TRY, must reset error stack by hand */
396 error_context_stack = NULL;
397
398 EmitErrorReport();
399 MemoryContextSwitchTo(TopMemoryContext);
400 FlushErrorState();
401 }
402
403 /* We can now handle ereport(ERROR) */
404 PG_exception_stack = &local_sigjmp_buf;
405
406 for(;;)
407 {
408 int i;
409 MemoryContextSwitchTo(ProcessLoopContext);
410 MemoryContextResetAndDeleteChildren(ProcessLoopContext);
411
412 /* receive heartbeat signal */
413 wd_hb_recv(sock, &pkt, from);
414 /* authentication */
415 if (strlen(pool_config->wd_authkey))
416 {
417 /* calculate hash from packet */
418 pack_str_len = packet_to_string_hb(&pkt, pack_str, sizeof(pack_str));
419 wd_calc_hash(pack_str, pack_str_len, buf);
420
421 if (buf[0] == '\0')
422 ereport(WARNING,
423 (errmsg("failed to calculate wd_authkey hash from a received heartbeat packet")));
424
425 if (strcmp(pkt.hash, buf))
426 ereport(ERROR,
427 (errmsg("watchdog heartbeat receive"),
428 errdetail("authentication failed")));
429 }
430
431 /* get current time */
432 gettimeofday(&tv, NULL);
433
434 /* who send this packet? */
435 from_pgpool_port = pkt.from_pgpool_port;
436 for (i = 0; i< gslifeCheckCluster->nodeCount; i++)
437 {
438 LifeCheckNode* node = &gslifeCheckCluster->lifeCheckNodes[i];
439
440 ereport(DEBUG2,
441 (errmsg("received heartbeat signal from \"%s:%d\" hostname:%s",
442 from, from_pgpool_port, pkt.from)));
443
444 if ( (!strcmp(node->hostName, pkt.from) || !strcmp(node->hostName, from)) && node->pgpoolPort == from_pgpool_port)
445 {
446 /* this is the first packet or the latest packet */
447 if (!WD_TIME_ISSET(node->hb_send_time) ||
448 WD_TIME_BEFORE(node->hb_send_time, pkt.send_time))
449 {
450 ereport(DEBUG1,
451 (errmsg("received heartbeat signal from \"%s(%s):%d\" node:%s",
452 from,pkt.from, from_pgpool_port, node->nodeName)));
453
454 node->hb_send_time = pkt.send_time;
455 node->hb_last_recv_time = tv;
456 }
457 else
458 {
459 ereport(NOTICE,
460 (errmsg("received heartbeat signal is older than the latest, ignored")));
461 }
462 break;
463 }
464 }
465 }
466
467 return pid;
468 }
469
470 /* fork heartbeat sender child */
471 pid_t
wd_hb_sender(int fork_wait_time,WdHbIf * hb_if)472 wd_hb_sender(int fork_wait_time, WdHbIf *hb_if)
473 {
474 int sock;
475 pid_t pid = 0;
476 WdHbPacket pkt;
477
478 char pack_str[WD_MAX_PACKET_STRING];
479 int pack_str_len;
480 sigjmp_buf local_sigjmp_buf;
481
482 pid = fork();
483 if (pid != 0)
484 {
485 if (pid == -1)
486 ereport(PANIC,
487 (errmsg("failed to fork a heartbeat sender child")));
488 return pid;
489 }
490
491 on_exit_reset();
492 processType = PT_HB_SENDER;
493
494 if (fork_wait_time > 0)
495 {
496 sleep(fork_wait_time);
497 }
498
499 POOL_SETMASK(&UnBlockSig);
500
501 pool_signal(SIGTERM, hb_sender_exit);
502 pool_signal(SIGINT, hb_sender_exit);
503 pool_signal(SIGQUIT, hb_sender_exit);
504 pool_signal(SIGCHLD, SIG_DFL);
505 pool_signal(SIGHUP, SIG_IGN);
506 pool_signal(SIGUSR1, SIG_IGN);
507 pool_signal(SIGUSR2, SIG_IGN);
508 pool_signal(SIGPIPE, SIG_IGN);
509 pool_signal(SIGALRM, SIG_IGN);
510
511 init_ps_display("", "", "", "");
512 /* Create per loop iteration memory context */
513 ProcessLoopContext = AllocSetContextCreate(TopMemoryContext,
514 "wdhb_sender",
515 ALLOCSET_DEFAULT_MINSIZE,
516 ALLOCSET_DEFAULT_INITSIZE,
517 ALLOCSET_DEFAULT_MAXSIZE);
518
519 MemoryContextSwitchTo(TopMemoryContext);
520
521 sock = wd_create_hb_send_socket(hb_if);
522
523 set_ps_display("heartbeat sender", false);
524
525 if (sigsetjmp(local_sigjmp_buf, 1) != 0)
526 {
527 /* Since not using PG_TRY, must reset error stack by hand */
528 error_context_stack = NULL;
529
530 EmitErrorReport();
531 MemoryContextSwitchTo(TopMemoryContext);
532 FlushErrorState();
533 sleep(pool_config->wd_heartbeat_keepalive);
534 }
535
536 /* We can now handle ereport(ERROR) */
537 PG_exception_stack = &local_sigjmp_buf;
538
539 for(;;)
540 {
541 MemoryContextSwitchTo(ProcessLoopContext);
542 MemoryContextResetAndDeleteChildren(ProcessLoopContext);
543
544 /* contents of packet */
545 gettimeofday(&pkt.send_time, NULL);
546 strlcpy(pkt.from, pool_config->wd_hostname, sizeof(pkt.from));
547 pkt.from_pgpool_port = pool_config->port;
548
549 /* authentication key */
550 if (strlen(pool_config->wd_authkey))
551 {
552 /* calculate hash from packet */
553 pack_str_len = packet_to_string_hb(&pkt, pack_str, sizeof(pack_str));
554 wd_calc_hash(pack_str, pack_str_len, pkt.hash);
555
556 if (pkt.hash[0] == '\0')
557 ereport(WARNING,
558 (errmsg("failed to calculate wd_authkey hash from a heartbeat packet to be sent")));
559 }
560
561 /* send heartbeat signal */
562 wd_hb_send(sock, &pkt, sizeof(pkt), hb_if->addr, hb_if->dest_port);
563 ereport(DEBUG1,
564 (errmsg("watchdog heartbeat: send heartbeat signal to %s:%d", hb_if->addr, hb_if->dest_port)));
565 sleep(pool_config->wd_heartbeat_keepalive);
566 }
567
568 return pid;
569 }
570
571 static RETSIGTYPE
hb_sender_exit(int sig)572 hb_sender_exit(int sig)
573 {
574 switch (sig)
575 {
576 case SIGTERM: /* smart shutdown */
577 case SIGINT: /* fast shutdown */
578 case SIGQUIT: /* immediate shutdown */
579 ereport(DEBUG1,
580 (errmsg("watchdog heartbeat sender child receives shutdown request signal %d", sig )));
581 break;
582 default:
583 ereport(LOG,
584 (errmsg("hb_sender child receives unknown signal: %d", sig )));
585 }
586
587 exit(0);
588 }
589
590 static RETSIGTYPE
hb_receiver_exit(int sig)591 hb_receiver_exit(int sig)
592 {
593 switch (sig)
594 {
595 case SIGTERM: /* smart shutdown */
596 case SIGINT: /* fast shutdown */
597 case SIGQUIT: /* immediate shutdown */
598 ereport(DEBUG1,
599 (errmsg("watchdog heartbeat receiver child receives shutdown request signal %d", sig )));
600 break;
601 default:
602 ereport(LOG,
603 (errmsg("hb_receiver child receives unknown signal: %d", sig )));
604 }
605
606 exit(0);
607 }
608
609 static int
hton_wd_hb_packet(WdHbPacket * to,WdHbPacket * from)610 hton_wd_hb_packet(WdHbPacket * to, WdHbPacket * from)
611 {
612 if ((to == NULL) || (from == NULL))
613 {
614 return WD_NG;
615 }
616
617 to->send_time.tv_sec = htonl(from->send_time.tv_sec);
618 to->send_time.tv_usec = htonl(from->send_time.tv_usec);
619 memcpy(to->from, from->from, sizeof(to->from));
620 to->from_pgpool_port = htonl(from->from_pgpool_port);
621 memcpy(to->hash, from->hash, sizeof(to->hash));
622
623 return WD_OK;
624 }
625
626 static int
ntoh_wd_hb_packet(WdHbPacket * to,WdHbPacket * from)627 ntoh_wd_hb_packet(WdHbPacket * to, WdHbPacket * from)
628 {
629 if ((to == NULL) || (from == NULL))
630 {
631 return WD_NG;
632 }
633
634 to->send_time.tv_sec = ntohl(from->send_time.tv_sec);
635 to->send_time.tv_usec = ntohl(from->send_time.tv_usec);
636 memcpy(to->from, from->from, sizeof(to->from));
637 to->from_pgpool_port = ntohl(from->from_pgpool_port);
638 memcpy(to->hash, from->hash, sizeof(to->hash));
639
640 return WD_OK;
641 }
642
643 /* convert packet to string and return length of the string */
644 static int
packet_to_string_hb(WdHbPacket * pkt,char * str,int maxlen)645 packet_to_string_hb(WdHbPacket *pkt, char *str, int maxlen)
646 {
647 int len;
648 len = snprintf(str, maxlen, "tv_sec=%ld tv_usec=%ld from=%s",
649 pkt->send_time.tv_sec, pkt->send_time.tv_usec, pkt->from);
650
651 return len;
652 }
653
654 /*
655 * Set SO_REUSEPORT option to the socket. If the option is available
656 * in the compile time but not available in the run time, just emit a
657 * log and treat it as normal.
658 */
wd_set_reuseport(int sock)659 static void wd_set_reuseport(int sock)
660 {
661 #if defined(SO_REUSEPORT)
662 int one = 1;
663
664 if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)) == -1)
665 {
666 if (errno == EINVAL || errno == ENOPROTOOPT)
667 {
668 ereport(LOG,
669 (errmsg("error seting SO_REUSEPORT option to the socket"),
670 errdetail("setsockopt(SO_REUSEPORT) is not supported by the kernel. detail: %s",
671 strerror(errno))));
672 }
673 else
674 {
675 close(sock);
676 ereport(ERROR,
677 (errmsg("failed to create watchdog heartbeat socket"),
678 errdetail("setsockopt(SO_REUSEPORT) failed with reason: \"%s\"", strerror(errno))));
679 }
680 }
681 else
682 ereport(LOG,
683 (errmsg("set SO_REUSEPORT option to the socket")));
684
685 #endif
686 }
687