2 CTDB protocol marshalling
4 Copyright (C) Amitay Isaacs 2015
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, see <http://www.gnu.org/licenses/>.
21 #include "system/network.h"
25 #include "common/line.h"
28 #include "protocol_util.h"
31 enum ctdb_runstate runstate
;
34 { CTDB_RUNSTATE_UNKNOWN
, "UNKNOWN" },
35 { CTDB_RUNSTATE_INIT
, "INIT" },
36 { CTDB_RUNSTATE_SETUP
, "SETUP" },
37 { CTDB_RUNSTATE_FIRST_RECOVERY
, "FIRST_RECOVERY" },
38 { CTDB_RUNSTATE_STARTUP
, "STARTUP" },
39 { CTDB_RUNSTATE_RUNNING
, "RUNNING" },
40 { CTDB_RUNSTATE_SHUTDOWN
, "SHUTDOWN" },
44 const char *ctdb_runstate_to_string(enum ctdb_runstate runstate
)
48 for (i
=0; runstate_map
[i
].label
!= NULL
; i
++) {
49 if (runstate_map
[i
].runstate
== runstate
) {
50 return runstate_map
[i
].label
;
54 return runstate_map
[0].label
;
57 enum ctdb_runstate
ctdb_runstate_from_string(const char *runstate_str
)
61 for (i
=0; runstate_map
[i
].label
!= NULL
; i
++) {
62 if (strcasecmp(runstate_map
[i
].label
,
64 return runstate_map
[i
].runstate
;
68 return CTDB_RUNSTATE_UNKNOWN
;
72 enum ctdb_event event
;
75 { CTDB_EVENT_INIT
, "init" },
76 { CTDB_EVENT_SETUP
, "setup" },
77 { CTDB_EVENT_STARTUP
, "startup" },
78 { CTDB_EVENT_START_RECOVERY
, "startrecovery" },
79 { CTDB_EVENT_RECOVERED
, "recovered" },
80 { CTDB_EVENT_TAKE_IP
, "takeip" },
81 { CTDB_EVENT_RELEASE_IP
, "releaseip" },
82 { CTDB_EVENT_MONITOR
, "monitor" },
83 { CTDB_EVENT_SHUTDOWN
, "shutdown" },
84 { CTDB_EVENT_UPDATE_IP
, "updateip" },
85 { CTDB_EVENT_IPREALLOCATED
, "ipreallocated" },
86 { CTDB_EVENT_MAX
, "all" },
90 const char *ctdb_event_to_string(enum ctdb_event event
)
94 for (i
=0; event_map
[i
].label
!= NULL
; i
++) {
95 if (event_map
[i
].event
== event
) {
96 return event_map
[i
].label
;
103 enum ctdb_event
ctdb_event_from_string(const char *event_str
)
107 for (i
=0; event_map
[i
].label
!= NULL
; i
++) {
108 if (strcmp(event_map
[i
].label
, event_str
) == 0) {
109 return event_map
[i
].event
;
113 return CTDB_EVENT_MAX
;
116 int ctdb_sock_addr_to_buf(char *buf
, socklen_t buflen
,
117 ctdb_sock_addr
*addr
, bool with_port
)
121 switch (addr
->sa
.sa_family
) {
123 t
= inet_ntop(addr
->ip
.sin_family
, &addr
->ip
.sin_addr
,
131 t
= inet_ntop(addr
->ip6
.sin6_family
, &addr
->ip6
.sin6_addr
,
144 size_t len
= strlen(buf
);
147 ret
= snprintf(buf
+len
, buflen
-len
,
148 ":%u", ctdb_sock_addr_port(addr
));
149 if (ret
>= buflen
-len
) {
157 const char *ctdb_sock_addr_to_string(TALLOC_CTX
*mem_ctx
,
158 ctdb_sock_addr
*addr
, bool with_port
)
164 cip
= talloc_size(mem_ctx
, len
);
170 ret
= ctdb_sock_addr_to_buf(cip
, len
, addr
, with_port
);
179 static int ipv4_from_string(const char *str
, struct sockaddr_in
*ip
)
183 *ip
= (struct sockaddr_in
) {
184 .sin_family
= AF_INET
,
187 ret
= inet_pton(AF_INET
, str
, &ip
->sin_addr
);
192 #ifdef HAVE_SOCK_SIN_LEN
193 ip
->sin_len
= sizeof(*ip
);
198 static int ipv6_from_string(const char *str
, struct sockaddr_in6
*ip6
)
202 *ip6
= (struct sockaddr_in6
) {
203 .sin6_family
= AF_INET6
,
206 ret
= inet_pton(AF_INET6
, str
, &ip6
->sin6_addr
);
211 #ifdef HAVE_SOCK_SIN6_LEN
212 ip6
->sin6_len
= sizeof(*ip6
);
217 static int ip_from_string(const char *str
, ctdb_sock_addr
*addr
)
226 ZERO_STRUCTP(addr
); /* valgrind :-) */
228 /* IPv4 or IPv6 address?
230 * Use rindex() because we need the right-most ':' below for
231 * IPv4-mapped IPv6 addresses anyway...
233 p
= rindex(str
, ':');
235 ret
= ipv4_from_string(str
, &addr
->ip
);
237 uint8_t ipv4_mapped_prefix
[12] = {
238 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff
241 ret
= ipv6_from_string(str
, &addr
->ip6
);
247 * Check for IPv4-mapped IPv6 address
248 * (e.g. ::ffff:192.0.2.128) - reparse as IPv4 if
251 if (memcmp(&addr
->ip6
.sin6_addr
.s6_addr
[0],
253 sizeof(ipv4_mapped_prefix
)) == 0) {
254 /* Initialize addr struct to zero before reparsing as IPV4 */
257 /* Reparse as IPv4 */
258 ret
= ipv4_from_string(p
+1, &addr
->ip
);
265 int ctdb_sock_addr_from_string(const char *str
,
266 ctdb_sock_addr
*addr
, bool with_port
)
269 char s
[64]; /* Much longer than INET6_ADDRSTRLEN */
276 ret
= ip_from_string(str
, addr
);
280 /* Parse out port number and then IP address */
282 len
= strlcpy(s
, str
, sizeof(s
));
283 if (len
>= sizeof(s
)) {
292 port
= strtoul(p
+1, &endp
, 10);
293 if (endp
== p
+1 || *endp
!= '\0') {
294 /* Empty string or trailing garbage */
299 ret
= ip_from_string(s
, addr
);
301 ctdb_sock_addr_set_port(addr
, port
);
306 int ctdb_sock_addr_mask_from_string(const char *str
,
307 ctdb_sock_addr
*addr
,
311 char s
[64]; /* Much longer than INET6_ADDRSTRLEN */
317 if (addr
== NULL
|| mask
== NULL
) {
321 len
= strlcpy(s
, str
, sizeof(s
));
322 if (len
>= sizeof(s
)) {
331 m
= strtoul(p
+1, &endp
, 10);
332 if (endp
== p
+1 || *endp
!= '\0') {
333 /* Empty string or trailing garbage */
338 ret
= ip_from_string(s
, addr
);
347 unsigned int ctdb_sock_addr_port(ctdb_sock_addr
*addr
)
349 switch (addr
->sa
.sa_family
) {
351 return ntohs(addr
->ip
.sin_port
);
354 return ntohs(addr
->ip6
.sin6_port
);
361 void ctdb_sock_addr_set_port(ctdb_sock_addr
*addr
, unsigned int port
)
363 switch (addr
->sa
.sa_family
) {
365 addr
->ip
.sin_port
= htons(port
);
368 addr
->ip6
.sin6_port
= htons(port
);
375 static int ctdb_sock_addr_cmp_family(const ctdb_sock_addr
*addr1
,
376 const ctdb_sock_addr
*addr2
)
378 /* This is somewhat arbitrary. However, when used for sorting
379 * it just needs to be consistent.
381 if (addr1
->sa
.sa_family
< addr2
->sa
.sa_family
) {
384 if (addr1
->sa
.sa_family
> addr2
->sa
.sa_family
) {
391 int ctdb_sock_addr_cmp_ip(const ctdb_sock_addr
*addr1
,
392 const ctdb_sock_addr
*addr2
)
396 ret
= ctdb_sock_addr_cmp_family(addr1
, addr2
);
401 switch (addr1
->sa
.sa_family
) {
403 ret
= memcmp(&addr1
->ip
.sin_addr
.s_addr
,
404 &addr2
->ip
.sin_addr
.s_addr
, 4);
408 ret
= memcmp(addr1
->ip6
.sin6_addr
.s6_addr
,
409 addr2
->ip6
.sin6_addr
.s6_addr
, 16);
419 int ctdb_sock_addr_cmp(const ctdb_sock_addr
*addr1
,
420 const ctdb_sock_addr
*addr2
)
424 ret
= ctdb_sock_addr_cmp_ip(addr1
, addr2
);
429 switch (addr1
->sa
.sa_family
) {
431 if (addr1
->ip
.sin_port
< addr2
->ip
.sin_port
) {
433 } else if (addr1
->ip
.sin_port
> addr2
->ip
.sin_port
) {
439 if (addr1
->ip6
.sin6_port
< addr2
->ip6
.sin6_port
) {
441 } else if (addr1
->ip6
.sin6_port
> addr2
->ip6
.sin6_port
) {
453 bool ctdb_sock_addr_same_ip(const ctdb_sock_addr
*addr1
,
454 const ctdb_sock_addr
*addr2
)
456 return (ctdb_sock_addr_cmp_ip(addr1
, addr2
) == 0);
459 bool ctdb_sock_addr_same(const ctdb_sock_addr
*addr1
,
460 const ctdb_sock_addr
*addr2
)
462 return (ctdb_sock_addr_cmp(addr1
, addr2
) == 0);
465 int ctdb_connection_to_buf(char *buf
, size_t buflen
,
466 struct ctdb_connection
*conn
, bool client_first
)
468 char server
[64], client
[64];
471 ret
= ctdb_sock_addr_to_buf(server
, sizeof(server
),
472 &conn
->server
, true);
477 ret
= ctdb_sock_addr_to_buf(client
, sizeof(client
),
478 &conn
->client
, true);
483 if (! client_first
) {
484 ret
= snprintf(buf
, buflen
, "%s %s", server
, client
);
486 ret
= snprintf(buf
, buflen
, "%s %s", client
, server
);
495 const char *ctdb_connection_to_string(TALLOC_CTX
*mem_ctx
,
496 struct ctdb_connection
*conn
,
499 const size_t len
= 128;
503 out
= talloc_size(mem_ctx
, len
);
508 ret
= ctdb_connection_to_buf(out
, len
, conn
, client_first
);
517 int ctdb_connection_from_string(const char *str
, bool client_first
,
518 struct ctdb_connection
*conn
)
521 char *t1
= NULL
, *t2
= NULL
;
523 ctdb_sock_addr
*first
= (client_first
? &conn
->client
: &conn
->server
);
524 ctdb_sock_addr
*second
= (client_first
? &conn
->server
: &conn
->client
);
527 len
= strlcpy(s
, str
, sizeof(s
));
528 if (len
>= sizeof(s
)) {
532 t1
= strtok(s
, " \t\n");
537 t2
= strtok(NULL
, " \t\n\0");
542 ret
= ctdb_sock_addr_from_string(t1
, first
, true);
547 ret
= ctdb_sock_addr_from_string(t2
, second
, true);
552 ret
= ctdb_sock_addr_cmp_family(first
, second
);
560 int ctdb_connection_list_add(struct ctdb_connection_list
*conn_list
,
561 struct ctdb_connection
*conn
)
565 if (conn_list
== NULL
) {
569 /* Ensure array is big enough */
570 len
= talloc_array_length(conn_list
->conn
);
571 if (conn_list
->num
== len
) {
572 conn_list
->conn
= talloc_realloc(conn_list
, conn_list
->conn
,
573 struct ctdb_connection
,
575 if (conn_list
->conn
== NULL
) {
580 conn_list
->conn
[conn_list
->num
] = *conn
;
586 static int connection_cmp(const void *a
, const void *b
)
588 const struct ctdb_connection
*conn_a
= a
;
589 const struct ctdb_connection
*conn_b
= b
;
592 ret
= ctdb_sock_addr_cmp(&conn_a
->server
, &conn_b
->server
);
594 ret
= ctdb_sock_addr_cmp(&conn_a
->client
, &conn_b
->client
);
600 int ctdb_connection_list_sort(struct ctdb_connection_list
*conn_list
)
602 if (conn_list
== NULL
) {
606 if (conn_list
->num
> 0) {
607 qsort(conn_list
->conn
, conn_list
->num
,
608 sizeof(struct ctdb_connection
), connection_cmp
);
614 const char *ctdb_connection_list_to_string(
616 struct ctdb_connection_list
*conn_list
, bool client_first
)
621 out
= talloc_strdup(mem_ctx
, "");
626 if (conn_list
== NULL
|| conn_list
->num
== 0) {
630 for (i
= 0; i
< conn_list
->num
; i
++) {
634 ret
= ctdb_connection_to_buf(buf
, sizeof(buf
),
635 &conn_list
->conn
[i
], client_first
);
641 out
= talloc_asprintf_append(out
, "%s\n", buf
);
650 struct ctdb_connection_list_read_state
{
651 struct ctdb_connection_list
*list
;
655 static int ctdb_connection_list_read_line(char *line
, void *private_data
)
657 struct ctdb_connection_list_read_state
*state
=
658 (struct ctdb_connection_list_read_state
*)private_data
;
659 struct ctdb_connection conn
;
662 /* Skip empty lines */
663 if (line
[0] == '\0') {
668 if (line
[0] == '#') {
672 ret
= ctdb_connection_from_string(line
, state
->client_first
, &conn
);
677 ret
= ctdb_connection_list_add(state
->list
, &conn
);
685 int ctdb_connection_list_read(TALLOC_CTX
*mem_ctx
,
688 struct ctdb_connection_list
**conn_list
)
690 struct ctdb_connection_list_read_state state
;
693 if (conn_list
== NULL
) {
697 state
.list
= talloc_zero(mem_ctx
, struct ctdb_connection_list
);
698 if (state
.list
== NULL
) {
702 state
.client_first
= client_first
;
707 ctdb_connection_list_read_line
,
711 *conn_list
= state
.list
;