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"
29 #include "lib/util/util.h"
30 #include "lib/util/smb_strtox.h"
33 enum ctdb_runstate runstate
;
36 { CTDB_RUNSTATE_UNKNOWN
, "UNKNOWN" },
37 { CTDB_RUNSTATE_INIT
, "INIT" },
38 { CTDB_RUNSTATE_SETUP
, "SETUP" },
39 { CTDB_RUNSTATE_FIRST_RECOVERY
, "FIRST_RECOVERY" },
40 { CTDB_RUNSTATE_STARTUP
, "STARTUP" },
41 { CTDB_RUNSTATE_RUNNING
, "RUNNING" },
42 { CTDB_RUNSTATE_SHUTDOWN
, "SHUTDOWN" },
46 const char *ctdb_runstate_to_string(enum ctdb_runstate runstate
)
50 for (i
=0; runstate_map
[i
].label
!= NULL
; i
++) {
51 if (runstate_map
[i
].runstate
== runstate
) {
52 return runstate_map
[i
].label
;
56 return runstate_map
[0].label
;
59 enum ctdb_runstate
ctdb_runstate_from_string(const char *runstate_str
)
63 for (i
=0; runstate_map
[i
].label
!= NULL
; i
++) {
64 if (strcasecmp(runstate_map
[i
].label
,
66 return runstate_map
[i
].runstate
;
70 return CTDB_RUNSTATE_UNKNOWN
;
74 enum ctdb_event event
;
77 { CTDB_EVENT_INIT
, "init" },
78 { CTDB_EVENT_SETUP
, "setup" },
79 { CTDB_EVENT_STARTUP
, "startup" },
80 { CTDB_EVENT_START_RECOVERY
, "startrecovery" },
81 { CTDB_EVENT_RECOVERED
, "recovered" },
82 { CTDB_EVENT_TAKE_IP
, "takeip" },
83 { CTDB_EVENT_RELEASE_IP
, "releaseip" },
84 { CTDB_EVENT_MONITOR
, "monitor" },
85 { CTDB_EVENT_SHUTDOWN
, "shutdown" },
86 { CTDB_EVENT_UPDATE_IP
, "updateip" },
87 { CTDB_EVENT_IPREALLOCATED
, "ipreallocated" },
88 { CTDB_EVENT_MAX
, "all" },
92 const char *ctdb_event_to_string(enum ctdb_event event
)
96 for (i
=0; event_map
[i
].label
!= NULL
; i
++) {
97 if (event_map
[i
].event
== event
) {
98 return event_map
[i
].label
;
105 enum ctdb_event
ctdb_event_from_string(const char *event_str
)
109 for (i
=0; event_map
[i
].label
!= NULL
; i
++) {
110 if (strcmp(event_map
[i
].label
, event_str
) == 0) {
111 return event_map
[i
].event
;
115 return CTDB_EVENT_MAX
;
118 int ctdb_sock_addr_to_buf(char *buf
, socklen_t buflen
,
119 ctdb_sock_addr
*addr
, bool with_port
)
124 switch (addr
->sa
.sa_family
) {
126 t
= inet_ntop(addr
->ip
.sin_family
, &addr
->ip
.sin_addr
,
137 char tmp
[INET6_ADDRSTRLEN
];
139 t
= inet_ntop(addr
->ip6
.sin6_family
,
140 &addr
->ip6
.sin6_addr
,
148 int ret
= snprintf(buf
, buflen
, "[%s]", tmp
);
154 len
= strlcpy(buf
, tmp
, buflen
);
170 ret
= snprintf(buf
+len
, buflen
-len
,
171 ":%u", ctdb_sock_addr_port(addr
));
172 if (ret
< 0 || (size_t)ret
>= buflen
-len
) {
180 char *ctdb_sock_addr_to_string(TALLOC_CTX
*mem_ctx
,
181 ctdb_sock_addr
*addr
,
188 cip
= talloc_size(mem_ctx
, len
);
194 ret
= ctdb_sock_addr_to_buf(cip
, len
, addr
, with_port
);
203 static int ipv4_from_string(const char *str
, struct sockaddr_in
*ip
)
207 *ip
= (struct sockaddr_in
) {
208 .sin_family
= AF_INET
,
211 ret
= inet_pton(AF_INET
, str
, &ip
->sin_addr
);
216 #ifdef HAVE_SOCK_SIN_LEN
217 ip
->sin_len
= sizeof(*ip
);
222 static int ipv6_from_string(const char *str
, struct sockaddr_in6
*ip6
)
226 *ip6
= (struct sockaddr_in6
) {
227 .sin6_family
= AF_INET6
,
230 ret
= inet_pton(AF_INET6
, str
, &ip6
->sin6_addr
);
235 #ifdef HAVE_SOCK_SIN6_LEN
236 ip6
->sin6_len
= sizeof(*ip6
);
241 static int ip_from_string(const char *str
, ctdb_sock_addr
*addr
)
250 ZERO_STRUCTP(addr
); /* valgrind :-) */
252 /* IPv4 or IPv6 address?
254 * Use strrchr() because we need the right-most ':' below for
255 * IPv4-mapped IPv6 addresses anyway...
257 p
= strrchr(str
, ':');
259 ret
= ipv4_from_string(str
, &addr
->ip
);
261 static const uint8_t ipv4_mapped_prefix
[12] = {
262 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff
264 size_t len
= strlen(str
);
267 len
= strlcpy(s
, str
, sizeof(s
));
268 if (len
>= sizeof(s
)) {
272 if ((len
>= 2) && (s
[0] == '[') && (s
[len
-1] == ']')) {
275 p
= strrchr(str
, ':');
278 ret
= ipv6_from_string(str
, &addr
->ip6
);
284 * Check for IPv4-mapped IPv6 address
285 * (e.g. ::ffff:192.0.2.128) - reparse as IPv4 if
288 if (memcmp(&addr
->ip6
.sin6_addr
.s6_addr
[0],
290 sizeof(ipv4_mapped_prefix
)) == 0) {
291 /* Initialize addr struct to zero before reparsing as IPV4 */
294 /* Reparse as IPv4 */
295 ret
= ipv4_from_string(p
+1, &addr
->ip
);
302 int ctdb_sock_addr_from_string(const char *str
,
303 ctdb_sock_addr
*addr
, bool with_port
)
306 char s
[64]; /* Much longer than INET6_ADDRSTRLEN */
312 ret
= ip_from_string(str
, addr
);
316 /* Parse out port number and then IP address */
318 len
= strlcpy(s
, str
, sizeof(s
));
319 if (len
>= sizeof(s
)) {
328 port
= smb_strtoul(p
+1, NULL
, 10, &ret
, SMB_STR_FULL_STR_CONV
);
330 /* Empty string or trailing garbage */
335 ret
= ip_from_string(s
, addr
);
337 ctdb_sock_addr_set_port(addr
, port
);
342 int ctdb_sock_addr_mask_from_string(const char *str
,
343 ctdb_sock_addr
*addr
,
347 char s
[64]; /* Much longer than INET6_ADDRSTRLEN */
352 if (addr
== NULL
|| mask
== NULL
) {
356 len
= strlcpy(s
, str
, sizeof(s
));
357 if (len
>= sizeof(s
)) {
366 m
= smb_strtoul(p
+1, NULL
, 10, &ret
, SMB_STR_FULL_STR_CONV
);
368 /* Empty string or trailing garbage */
373 ret
= ip_from_string(s
, addr
);
382 unsigned int ctdb_sock_addr_port(ctdb_sock_addr
*addr
)
384 switch (addr
->sa
.sa_family
) {
386 return ntohs(addr
->ip
.sin_port
);
389 return ntohs(addr
->ip6
.sin6_port
);
396 void ctdb_sock_addr_set_port(ctdb_sock_addr
*addr
, unsigned int port
)
398 switch (addr
->sa
.sa_family
) {
400 addr
->ip
.sin_port
= htons(port
);
403 addr
->ip6
.sin6_port
= htons(port
);
410 static int ctdb_sock_addr_cmp_family(const ctdb_sock_addr
*addr1
,
411 const ctdb_sock_addr
*addr2
)
413 /* This is somewhat arbitrary. However, when used for sorting
414 * it just needs to be consistent.
416 if (addr1
->sa
.sa_family
< addr2
->sa
.sa_family
) {
419 if (addr1
->sa
.sa_family
> addr2
->sa
.sa_family
) {
426 int ctdb_sock_addr_cmp_ip(const ctdb_sock_addr
*addr1
,
427 const ctdb_sock_addr
*addr2
)
431 ret
= ctdb_sock_addr_cmp_family(addr1
, addr2
);
436 switch (addr1
->sa
.sa_family
) {
438 ret
= memcmp(&addr1
->ip
.sin_addr
.s_addr
,
439 &addr2
->ip
.sin_addr
.s_addr
, 4);
443 ret
= memcmp(addr1
->ip6
.sin6_addr
.s6_addr
,
444 addr2
->ip6
.sin6_addr
.s6_addr
, 16);
454 int ctdb_sock_addr_cmp(const ctdb_sock_addr
*addr1
,
455 const ctdb_sock_addr
*addr2
)
459 ret
= ctdb_sock_addr_cmp_ip(addr1
, addr2
);
464 switch (addr1
->sa
.sa_family
) {
466 if (addr1
->ip
.sin_port
< addr2
->ip
.sin_port
) {
468 } else if (addr1
->ip
.sin_port
> addr2
->ip
.sin_port
) {
474 if (addr1
->ip6
.sin6_port
< addr2
->ip6
.sin6_port
) {
476 } else if (addr1
->ip6
.sin6_port
> addr2
->ip6
.sin6_port
) {
488 bool ctdb_sock_addr_same_ip(const ctdb_sock_addr
*addr1
,
489 const ctdb_sock_addr
*addr2
)
491 return (ctdb_sock_addr_cmp_ip(addr1
, addr2
) == 0);
494 bool ctdb_sock_addr_same(const ctdb_sock_addr
*addr1
,
495 const ctdb_sock_addr
*addr2
)
497 return (ctdb_sock_addr_cmp(addr1
, addr2
) == 0);
500 int ctdb_connection_to_buf(char *buf
,
502 struct ctdb_connection
*conn
,
506 char server
[64], client
[64];
509 ret
= ctdb_sock_addr_to_buf(server
, sizeof(server
),
510 &conn
->server
, true);
515 ret
= ctdb_sock_addr_to_buf(client
, sizeof(client
),
516 &conn
->client
, true);
521 if (! client_first
) {
522 ret
= snprintf(buf
, buflen
, "%s%s%s", server
, sep
, client
);
524 ret
= snprintf(buf
, buflen
, "%s%s%s", client
, sep
, server
);
526 if (ret
< 0 || (size_t)ret
>= buflen
) {
533 char *ctdb_connection_to_string(TALLOC_CTX
*mem_ctx
,
534 struct ctdb_connection
*conn
,
537 const size_t len
= 128;
541 out
= talloc_size(mem_ctx
, len
);
546 ret
= ctdb_connection_to_buf(out
, len
, conn
, client_first
, " ");
555 int ctdb_connection_from_string(const char *str
, bool client_first
,
556 struct ctdb_connection
*conn
)
559 char *t1
= NULL
, *t2
= NULL
;
561 ctdb_sock_addr
*first
= (client_first
? &conn
->client
: &conn
->server
);
562 ctdb_sock_addr
*second
= (client_first
? &conn
->server
: &conn
->client
);
565 len
= strlcpy(s
, str
, sizeof(s
));
566 if (len
>= sizeof(s
)) {
570 t1
= strtok(s
, " \t\n");
575 t2
= strtok(NULL
, " \t\n\0");
580 ret
= ctdb_sock_addr_from_string(t1
, first
, true);
585 ret
= ctdb_sock_addr_from_string(t2
, second
, true);
590 ret
= ctdb_sock_addr_cmp_family(first
, second
);
598 int ctdb_connection_list_add(struct ctdb_connection_list
*conn_list
,
599 struct ctdb_connection
*conn
)
603 if (conn_list
== NULL
) {
607 /* Ensure array is big enough */
608 len
= talloc_array_length(conn_list
->conn
);
609 if (conn_list
->num
== len
) {
610 conn_list
->conn
= talloc_realloc(conn_list
, conn_list
->conn
,
611 struct ctdb_connection
,
613 if (conn_list
->conn
== NULL
) {
618 conn_list
->conn
[conn_list
->num
] = *conn
;
624 static int connection_cmp(const void *a
, const void *b
)
626 const struct ctdb_connection
*conn_a
= a
;
627 const struct ctdb_connection
*conn_b
= b
;
630 ret
= ctdb_sock_addr_cmp(&conn_a
->server
, &conn_b
->server
);
632 ret
= ctdb_sock_addr_cmp(&conn_a
->client
, &conn_b
->client
);
638 int ctdb_connection_list_sort(struct ctdb_connection_list
*conn_list
)
640 if (conn_list
== NULL
) {
644 if (conn_list
->num
> 0) {
645 qsort(conn_list
->conn
, conn_list
->num
,
646 sizeof(struct ctdb_connection
), connection_cmp
);
652 char *ctdb_connection_list_to_string(
654 struct ctdb_connection_list
*conn_list
, bool client_first
)
659 out
= talloc_strdup(mem_ctx
, "");
664 if (conn_list
== NULL
|| conn_list
->num
== 0) {
668 for (i
= 0; i
< conn_list
->num
; i
++) {
672 ret
= ctdb_connection_to_buf(buf
,
682 out
= talloc_asprintf_append(out
, "%s\n", buf
);
691 struct ctdb_connection_list_read_state
{
692 struct ctdb_connection_list
*list
;
696 static int ctdb_connection_list_read_line(char *line
, void *private_data
)
698 struct ctdb_connection_list_read_state
*state
=
699 (struct ctdb_connection_list_read_state
*)private_data
;
700 struct ctdb_connection conn
;
703 /* Skip empty lines */
704 if (line
[0] == '\0') {
709 if (line
[0] == '#') {
713 ret
= ctdb_connection_from_string(line
, state
->client_first
, &conn
);
718 ret
= ctdb_connection_list_add(state
->list
, &conn
);
726 int ctdb_connection_list_read(TALLOC_CTX
*mem_ctx
,
729 struct ctdb_connection_list
**conn_list
)
731 struct ctdb_connection_list_read_state state
;
734 if (conn_list
== NULL
) {
738 state
.list
= talloc_zero(mem_ctx
, struct ctdb_connection_list
);
739 if (state
.list
== NULL
) {
743 state
.client_first
= client_first
;
748 ctdb_connection_list_read_line
,
752 *conn_list
= state
.list
;