/* called when we get contacted by another node currently makes no attempt to check if the connection is really from a ctdb node in our cluster */ static void ctdb_listen_event(struct tevent_context *ev, struct tevent_fd *fde, uint16_t flags, void *private_data) { struct ctdb_context *ctdb = talloc_get_type(private_data, struct ctdb_context); struct ctdb_tcp *ctcp = talloc_get_type(ctdb->private_data, struct ctdb_tcp); ctdb_sock_addr addr; socklen_t len; int fd, nodeid; struct ctdb_incoming *in; int one = 1; memset(&addr, 0, sizeof(addr)); len = sizeof(addr); fd = accept(ctcp->listen_fd, (struct sockaddr *)&addr, &len); if (fd == -1) return; nodeid = ctdb_ip_to_nodeid(ctdb, &addr); if (nodeid == -1) { DEBUG(DEBUG_ERR, ("Refused connection from unknown node %s\n", ctdb_addr_to_str(&addr))); close(fd); return; } in = talloc_zero(ctcp, struct ctdb_incoming); in->fd = fd; in->ctdb = ctdb; set_nonblocking(in->fd); set_close_on_exec(in->fd); DEBUG(DEBUG_DEBUG, (__location__ " Created SOCKET FD:%d to incoming ctdb connection\n", fd)); if (setsockopt(in->fd,SOL_SOCKET,SO_KEEPALIVE,(char *)&one,sizeof(one)) == -1) { DEBUG(DEBUG_WARNING, ("Failed to set KEEPALIVE on fd - %s\n", strerror(errno))); } in->queue = ctdb_queue_setup(ctdb, in, in->fd, CTDB_TCP_ALIGNMENT, ctdb_tcp_read_cb, in, "ctdbd-%s", ctdb_addr_to_str(&addr)); }
/* setup the local node address */ int ctdb_set_address(struct ctdb_context *ctdb, const char *address) { ctdb->address = talloc(ctdb, ctdb_sock_addr); CTDB_NO_MEMORY(ctdb, ctdb->address); if (ctdb_parse_address(ctdb, address, ctdb->address) != 0) { return -1; } ctdb->name = talloc_asprintf(ctdb, "%s:%u", ctdb_addr_to_str(ctdb->address), ctdb_addr_to_port(ctdb->address)); return 0; }
/* Allocate any unassigned IPs just by looping through the IPs and * finding the best node for each. */ void basic_allocate_unassigned(struct ipalloc_state *ipalloc_state) { struct public_ip_list *t; /* loop over all ip's and find a physical node to cover for each unassigned ip. */ for (t = ipalloc_state->all_ips; t != NULL; t = t->next) { if (t->pnn == -1) { if (find_takeover_node(ipalloc_state, t)) { DEBUG(DEBUG_WARNING, ("Failed to find node to cover ip %s\n", ctdb_addr_to_str(&t->addr))); } } } }
void unassign_unsuitable_ips(struct ipalloc_state *ipalloc_state) { struct public_ip_list *t; /* verify that the assigned nodes can serve that public ip and set it to -1 if not */ for (t = ipalloc_state->all_ips; t != NULL; t = t->next) { if (t->pnn == -1) { continue; } if (!can_node_host_ip(ipalloc_state, t->pnn, t) != 0) { /* this node can not serve this ip. */ DEBUG(DEBUG_DEBUG,("Unassign IP: %s from %d\n", ctdb_addr_to_str(&(t->addr)), t->pnn)); t->pnn = -1; } } }
/* Load a nodes list file into a nodes array */ static int convert_node_map_to_list(struct ctdb_context *ctdb, TALLOC_CTX *mem_ctx, struct ctdb_node_map_old *node_map, struct ctdb_node ***nodes, uint32_t *num_nodes) { int i; *nodes = talloc_zero_array(mem_ctx, struct ctdb_node *, node_map->num); CTDB_NO_MEMORY(ctdb, *nodes); *num_nodes = node_map->num; for (i = 0; i < node_map->num; i++) { struct ctdb_node *node; node = talloc_zero(*nodes, struct ctdb_node); CTDB_NO_MEMORY(ctdb, node); (*nodes)[i] = node; node->address = node_map->nodes[i].addr; node->name = talloc_asprintf(node, "%s:%u", ctdb_addr_to_str(&node->address), ctdb_addr_to_port(&node->address)); node->flags = node_map->nodes[i].flags; if (!(node->flags & NODE_FLAGS_DELETED)) { node->flags = NODE_FLAGS_UNHEALTHY; } node->flags |= NODE_FLAGS_DISCONNECTED; node->pnn = i; node->ctdb = ctdb; node->dead_count = 0; } return 0; }
/* search the node lists list for a node to takeover this ip. pick the node that currently are serving the least number of ips so that the ips get spread out evenly. */ int find_takeover_node(struct ipalloc_state *ipalloc_state, struct public_ip_list *ip) { int pnn, min=0, num; int i, numnodes; numnodes = ipalloc_state->num; pnn = -1; for (i=0; i<numnodes; i++) { /* verify that this node can serve this ip */ if (!can_node_takeover_ip(ipalloc_state, i, ip)) { /* no it couldnt so skip to the next node */ continue; } num = node_ip_coverage(i, ipalloc_state->all_ips); /* was this the first node we checked ? */ if (pnn == -1) { pnn = i; min = num; } else { if (num < min) { pnn = i; min = num; } } } if (pnn == -1) { DEBUG(DEBUG_WARNING,(__location__ " Could not find node to take over public address '%s'\n", ctdb_addr_to_str(&ip->addr))); return -1; } ip->pnn = pnn; return 0; }
/* automatically find which address to listen on */ static int ctdb_tcp_listen_automatic(struct ctdb_context *ctdb) { struct ctdb_tcp *ctcp = talloc_get_type(ctdb->private_data, struct ctdb_tcp); ctdb_sock_addr sock; int lock_fd, i; const char *lock_path = CTDB_RUNDIR "/.socket_lock"; struct flock lock; int one = 1; int sock_size; struct tevent_fd *fde; /* If there are no nodes, then it won't be possible to find * the first one. Log a failure and short circuit the whole * process. */ if (ctdb->num_nodes == 0) { DEBUG(DEBUG_CRIT,("No nodes available to attempt bind to - is the nodes file empty?\n")); return -1; } /* in order to ensure that we don't get two nodes with the same adddress, we must make the bind() and listen() calls atomic. The SO_REUSEADDR setsockopt only prevents double binds if the first socket is in LISTEN state */ lock_fd = open(lock_path, O_RDWR|O_CREAT, 0666); if (lock_fd == -1) { DEBUG(DEBUG_CRIT,("Unable to open %s\n", lock_path)); return -1; } lock.l_type = F_WRLCK; lock.l_whence = SEEK_SET; lock.l_start = 0; lock.l_len = 1; lock.l_pid = 0; if (fcntl(lock_fd, F_SETLKW, &lock) != 0) { DEBUG(DEBUG_CRIT,("Unable to lock %s\n", lock_path)); close(lock_fd); return -1; } for (i=0; i < ctdb->num_nodes; i++) { if (ctdb->nodes[i]->flags & NODE_FLAGS_DELETED) { continue; } sock = ctdb->nodes[i]->address; switch (sock.sa.sa_family) { case AF_INET: sock_size = sizeof(sock.ip); break; case AF_INET6: sock_size = sizeof(sock.ip6); break; default: DEBUG(DEBUG_ERR, (__location__ " unknown family %u\n", sock.sa.sa_family)); continue; } ctcp->listen_fd = socket(sock.sa.sa_family, SOCK_STREAM, IPPROTO_TCP); if (ctcp->listen_fd == -1) { ctdb_set_error(ctdb, "socket failed\n"); continue; } set_close_on_exec(ctcp->listen_fd); if (setsockopt(ctcp->listen_fd,SOL_SOCKET,SO_REUSEADDR, (char *)&one,sizeof(one)) == -1) { DEBUG(DEBUG_WARNING, ("Failed to set REUSEADDR on fd - %s\n", strerror(errno))); } if (bind(ctcp->listen_fd, (struct sockaddr * )&sock, sock_size) == 0) { break; } if (errno == EADDRNOTAVAIL) { DEBUG(DEBUG_DEBUG,(__location__ " Failed to bind() to socket. %s(%d)\n", strerror(errno), errno)); } else { DEBUG(DEBUG_ERR,(__location__ " Failed to bind() to socket. %s(%d)\n", strerror(errno), errno)); } close(ctcp->listen_fd); ctcp->listen_fd = -1; } if (i == ctdb->num_nodes) { DEBUG(DEBUG_CRIT,("Unable to bind to any of the node addresses - giving up\n")); goto failed; } ctdb->address = talloc_memdup(ctdb, &ctdb->nodes[i]->address, sizeof(ctdb_sock_addr)); if (ctdb->address == NULL) { ctdb_set_error(ctdb, "Out of memory at %s:%d", __FILE__, __LINE__); goto failed; } ctdb->name = talloc_asprintf(ctdb, "%s:%u", ctdb_addr_to_str(ctdb->address), ctdb_addr_to_port(ctdb->address)); if (ctdb->name == NULL) { ctdb_set_error(ctdb, "Out of memory at %s:%d", __FILE__, __LINE__); goto failed; } DEBUG(DEBUG_INFO,("ctdb chose network address %s\n", ctdb->name)); if (listen(ctcp->listen_fd, 10) == -1) { goto failed; } fde = tevent_add_fd(ctdb->ev, ctcp, ctcp->listen_fd, TEVENT_FD_READ, ctdb_listen_event, ctdb); tevent_fd_set_auto_close(fde); close(lock_fd); return 0; failed: close(lock_fd); if (ctcp->listen_fd != -1) { close(ctcp->listen_fd); ctcp->listen_fd = -1; } return -1; }