/* called when a ctdb_call times out */ void ctdb_call_timeout(struct event_context *ev, struct timed_event *te, struct timeval t, void *private_data) { struct ctdb_call_state *state = talloc_get_type(private_data, struct ctdb_call_state); state->state = CTDB_CALL_ERROR; ctdb_set_error(state->node->ctdb, "ctdb_call %u timed out", state->c->hdr.reqid); if (state->async.fn) { state->async.fn(state); } }
int ctdb_ibw_get_address(struct ctdb_context *ctdb, const char *address, struct in_addr *addr) { if (inet_pton(AF_INET, address, addr) <= 0) { struct hostent *he = gethostbyname(address); if (he == NULL || he->h_length > sizeof(*addr)) { ctdb_set_error(ctdb, "invalid nework address '%s'\n", address); return -1; } memcpy(addr, he->h_addr, he->h_length); } return 0; }
/* make a remote ctdb call - async recv - called in daemon context This is called when the program wants to wait for a ctdb_call to complete and get the results. This call will block unless the call has already completed. */ int ctdb_daemon_call_recv(struct ctdb_call_state *state, struct ctdb_call *call) { while (state->state < CTDB_CALL_DONE) { event_loop_once(state->node->ctdb->ev); } if (state->state != CTDB_CALL_DONE) { ctdb_set_error(state->node->ctdb, "%s", state->errmsg); talloc_free(state); return -1; } if (state->call.reply_data.dsize) { call->reply_data.dptr = talloc_memdup(state->node->ctdb, state->call.reply_data.dptr, state->call.reply_data.dsize); call->reply_data.dsize = state->call.reply_data.dsize; } else { call->reply_data.dptr = NULL; call->reply_data.dsize = 0; } call->status = state->call.status; talloc_free(state); return 0; }
/* local version of ctdb_call */ int ctdb_call_local(struct ctdb_db_context *ctdb_db, struct ctdb_call *call, struct ctdb_ltdb_header *header, TDB_DATA *data, uint32_t caller) { struct ctdb_call_info *c; struct ctdb_registered_call *fn; struct ctdb_context *ctdb = ctdb_db->ctdb; c = talloc(ctdb, struct ctdb_call_info); CTDB_NO_MEMORY(ctdb, c); c->key = call->key; c->call_data = &call->call_data; c->record_data.dptr = talloc_memdup(c, data->dptr, data->dsize); c->record_data.dsize = data->dsize; CTDB_NO_MEMORY(ctdb, c->record_data.dptr); c->new_data = NULL; c->reply_data = NULL; c->status = 0; for (fn=ctdb_db->calls;fn;fn=fn->next) { if (fn->id == call->call_id) break; } if (fn == NULL) { ctdb_set_error(ctdb, "Unknown call id %u\n", call->call_id); talloc_free(c); return -1; } if (fn->fn(c) != 0) { ctdb_set_error(ctdb, "ctdb_call %u failed\n", call->call_id); talloc_free(c); return -1; } if (header->laccessor != caller) { header->lacount = 0; } header->laccessor = caller; header->lacount++; /* we need to force the record to be written out if this was a remote access, so that the lacount is updated */ if (c->new_data == NULL && header->laccessor != ctdb->vnn) { c->new_data = &c->record_data; } if (c->new_data) { if (ctdb_ltdb_store(ctdb_db, call->key, header, *c->new_data) != 0) { ctdb_set_error(ctdb, "ctdb_call tdb_store failed\n"); talloc_free(c); return -1; } } if (c->reply_data) { call->reply_data = *c->reply_data; talloc_steal(ctdb, call->reply_data.dptr); talloc_set_name_const(call->reply_data.dptr, __location__); } else { call->reply_data.dptr = NULL; call->reply_data.dsize = 0; } call->status = c->status; talloc_free(c); return 0; }
/* listen on our own address */ int ctdb_tcp_listen(struct ctdb_context *ctdb) { struct ctdb_tcp *ctcp = talloc_get_type(ctdb->private_data, struct ctdb_tcp); ctdb_sock_addr sock; int sock_size; int one = 1; struct tevent_fd *fde; /* we can either auto-bind to the first available address, or we can use a specified address */ if (!ctdb->address) { return ctdb_tcp_listen_automatic(ctdb); } sock = *ctdb->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)); goto failed; } ctcp->listen_fd = socket(sock.sa.sa_family, SOCK_STREAM, IPPROTO_TCP); if (ctcp->listen_fd == -1) { ctdb_set_error(ctdb, "socket failed\n"); return -1; } 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) { DEBUG(DEBUG_ERR,(__location__ " Failed to bind() to socket. %s(%d)\n", strerror(errno), errno)); goto failed; } 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); return 0; failed: if (ctcp->listen_fd != -1) { close(ctcp->listen_fd); } ctcp->listen_fd = -1; return -1; }
/* 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; }