ctdb-daemon: Fix signed/unsigned comparisons by casting
[samba.git] / ctdb / server / ctdb_daemon.c
index 90e8b715bae8db051699b9b2004b7f0f198ebbee..6a4e42d8010eb05fbdd3c6922ee5f86af98cc8c1 100644 (file)
@@ -36,7 +36,7 @@
 #include "lib/util/blocking.h"
 #include "lib/util/become_daemon.h"
 
-#include "ctdb_version.h"
+#include "version.h"
 #include "ctdb_private.h"
 #include "ctdb_client.h"
 
@@ -72,7 +72,126 @@ static void print_exit_message(void)
        }
 }
 
+#ifdef HAVE_GETRUSAGE
 
+struct cpu_check_threshold_data {
+       unsigned short percent;
+       struct timeval timeofday;
+       struct timeval ru_time;
+};
+
+static void ctdb_cpu_check_threshold(struct tevent_context *ev,
+                                    struct tevent_timer *te,
+                                    struct timeval tv,
+                                    void *private_data)
+{
+       struct ctdb_context *ctdb = talloc_get_type_abort(
+               private_data, struct ctdb_context);
+       uint32_t interval = 60;
+
+       static unsigned short threshold = 0;
+       static struct cpu_check_threshold_data prev = {
+               .percent = 0,
+               .timeofday = { .tv_sec = 0 },
+               .ru_time = { .tv_sec = 0 },
+       };
+
+       struct rusage usage;
+       struct cpu_check_threshold_data curr = {
+               .percent = 0,
+       };
+       int64_t ru_time_diff, timeofday_diff;
+       bool first;
+       int ret;
+
+       /*
+        * Cache the threshold so that we don't waste time checking
+        * the environment variable every time
+        */
+       if (threshold == 0) {
+               const char *t;
+
+               threshold = 90;
+
+               t = getenv("CTDB_TEST_CPU_USAGE_THRESHOLD");
+               if (t != NULL) {
+                       int th;
+
+                       th = atoi(t);
+                       if (th <= 0 || th > 100) {
+                               DBG_WARNING("Failed to parse env var: %s\n", t);
+                       } else {
+                               threshold = th;
+                       }
+               }
+       }
+
+       ret = getrusage(RUSAGE_SELF, &usage);
+       if (ret != 0) {
+               DBG_WARNING("rusage() failed: %d\n", ret);
+               goto next;
+       }
+
+       /* Sum the system and user CPU usage */
+       curr.ru_time = timeval_sum(&usage.ru_utime, &usage.ru_stime);
+
+       curr.timeofday = tv;
+
+       first = timeval_is_zero(&prev.timeofday);
+       if (first) {
+               /* No previous values recorded so no calculation to do */
+               goto done;
+       }
+
+       timeofday_diff = usec_time_diff(&curr.timeofday, &prev.timeofday);
+       if (timeofday_diff <= 0) {
+               /*
+                * Time went backwards or didn't progress so no (sane)
+                * calculation can be done
+                */
+               goto done;
+       }
+
+       ru_time_diff = usec_time_diff(&curr.ru_time, &prev.ru_time);
+
+       curr.percent = ru_time_diff * 100 / timeofday_diff;
+
+       if (curr.percent >= threshold) {
+               /* Log only if the utilisation changes */
+               if (curr.percent != prev.percent) {
+                       D_WARNING("WARNING: CPU utilisation %hu%% >= "
+                                 "threshold (%hu%%)\n",
+                                 curr.percent,
+                                 threshold);
+               }
+       } else {
+               /* Log if the utilisation falls below the threshold */
+               if (prev.percent >= threshold) {
+                       D_WARNING("WARNING: CPU utilisation %hu%% < "
+                                 "threshold (%hu%%)\n",
+                                 curr.percent,
+                                 threshold);
+               }
+       }
+
+done:
+       prev = curr;
+
+next:
+       tevent_add_timer(ctdb->ev, ctdb,
+                        timeval_current_ofs(interval, 0),
+                        ctdb_cpu_check_threshold,
+                        ctdb);
+}
+
+static void ctdb_start_cpu_check_threshold(struct ctdb_context *ctdb)
+{
+       tevent_add_timer(ctdb->ev, ctdb,
+                        timeval_current(),
+                        ctdb_cpu_check_threshold,
+                        ctdb);
+}
+#endif /* HAVE_GETRUSAGE */
 
 static void ctdb_time_tick(struct tevent_context *ev, struct tevent_timer *te,
                                  struct timeval t, void *private_data)
@@ -111,6 +230,10 @@ static void ctdb_start_periodic_events(struct ctdb_context *ctdb)
 
        /* start listening to timer ticks */
        ctdb_start_time_tickd(ctdb);
+
+#ifdef HAVE_GETRUSAGE
+       ctdb_start_cpu_check_threshold(ctdb);
+#endif /* HAVE_GETRUSAGE */
 }
 
 static void ignore_signal(int signum)
@@ -209,6 +332,36 @@ int daemon_deregister_message_handler(struct ctdb_context *ctdb, uint32_t client
        return srvid_deregister(ctdb->srv, srvid, client);
 }
 
+void daemon_tunnel_handler(uint64_t tunnel_id, TDB_DATA data,
+                          void *private_data)
+{
+       struct ctdb_client *client =
+               talloc_get_type_abort(private_data, struct ctdb_client);
+       struct ctdb_req_tunnel_old *c, *pkt;
+       size_t len;
+
+       pkt = (struct ctdb_req_tunnel_old *)data.dptr;
+
+       len = offsetof(struct ctdb_req_tunnel_old, data) + pkt->datalen;
+       c = ctdbd_allocate_pkt(client->ctdb, client->ctdb, CTDB_REQ_TUNNEL,
+                              len, struct ctdb_req_tunnel_old);
+       if (c == NULL) {
+               DEBUG(DEBUG_ERR, ("Memory error in daemon_tunnel_handler\n"));
+               return;
+       }
+
+       talloc_set_name_const(c, "req_tunnel packet");
+
+       c->tunnel_id = tunnel_id;
+       c->flags = pkt->flags;
+       c->datalen = pkt->datalen;
+       memcpy(c->data, pkt->data, pkt->datalen);
+
+       daemon_queue_send(client, &c->hdr);
+
+       talloc_free(c);
+}
+
 /*
   destroy a ctdb_client
 */
@@ -787,6 +940,8 @@ static void daemon_request_call_from_client(struct ctdb_client *client,
 
 static void daemon_request_control_from_client(struct ctdb_client *client, 
                                               struct ctdb_req_control_old *c);
+static void daemon_request_tunnel_from_client(struct ctdb_client *client,
+                                             struct ctdb_req_tunnel_old *c);
 
 /* data contains a packet from the client */
 static void daemon_incoming_packet(void *p, struct ctdb_req_header *hdr)
@@ -828,6 +983,11 @@ static void daemon_incoming_packet(void *p, struct ctdb_req_header *hdr)
                daemon_request_control_from_client(client, (struct ctdb_req_control_old *)hdr);
                break;
 
+       case CTDB_REQ_TUNNEL:
+               CTDB_INCREMENT_STAT(ctdb, client.req_tunnel);
+               daemon_request_tunnel_from_client(client, (struct ctdb_req_tunnel_old *)hdr);
+               break;
+
        default:
                DEBUG(DEBUG_CRIT,(__location__ " daemon: unrecognized operation %u\n",
                         hdr->operation));
@@ -858,20 +1018,15 @@ static void ctdb_daemon_read_cb(uint8_t *data, size_t cnt, void *args)
                return;
        }
        hdr = (struct ctdb_req_header *)data;
-       if (cnt != hdr->length) {
-               ctdb_set_error(client->ctdb, "Bad header length %u expected %u\n in daemon", 
-                              (unsigned)hdr->length, (unsigned)cnt);
-               return;
-       }
 
        if (hdr->ctdb_magic != CTDB_MAGIC) {
                ctdb_set_error(client->ctdb, "Non CTDB packet rejected\n");
-               return;
+               goto err_out;
        }
 
        if (hdr->ctdb_version != CTDB_PROTOCOL) {
                ctdb_set_error(client->ctdb, "Bad CTDB version 0x%x rejected in daemon\n", hdr->ctdb_version);
-               return;
+               goto err_out;
        }
 
        DEBUG(DEBUG_DEBUG,(__location__ " client request %u of type %u length %u from "
@@ -880,6 +1035,10 @@ static void ctdb_daemon_read_cb(uint8_t *data, size_t cnt, void *args)
 
        /* it is the responsibility of the incoming packet function to free 'data' */
        daemon_incoming_packet(client, hdr);
+       return;
+
+err_out:
+       TALLOC_FREE(data);
 }
 
 
@@ -892,6 +1051,44 @@ static int ctdb_clientpid_destructor(struct ctdb_client_pid_list *client_pid)
        return 0;
 }
 
+static int get_new_client_id(struct reqid_context *idr,
+                            struct ctdb_client *client,
+                            uint32_t *out)
+{
+       uint32_t client_id;
+
+       client_id = reqid_new(idr, client);
+       /*
+        * Some places in the code (e.g. ctdb_control_db_attach(),
+        * ctdb_control_db_detach()) assign a special meaning to
+        * client_id 0.  The assumption is that if client_id is 0 then
+        * the control has come from another daemon.  Therefore, we
+        * should never return client_id == 0.
+        */
+       if (client_id == 0) {
+               /*
+                * Don't leak ID 0.  This is safe because the ID keeps
+                * increasing.  A test will be added to ensure that
+                * this doesn't change.
+                */
+               reqid_remove(idr, 0);
+
+               client_id = reqid_new(idr, client);
+       }
+
+       if (client_id == REQID_INVALID) {
+               return EINVAL;
+       }
+
+       if (client_id == 0) {
+               /* Every other ID must have been used and we can't use 0 */
+               reqid_remove(idr, 0);
+               return EINVAL;
+       }
+
+       *out = client_id;
+       return 0;
+}
 
 static void ctdb_accept_client(struct tevent_context *ev,
                               struct tevent_fd *fde, uint16_t flags,
@@ -912,6 +1109,7 @@ static void ctdb_accept_client(struct tevent_context *ev,
        if (fd == -1) {
                return;
        }
+       smb_set_close_on_exec(fd);
 
        ret = set_blocking(fd, false);
        if (ret != 0) {
@@ -934,7 +1132,15 @@ static void ctdb_accept_client(struct tevent_context *ev,
 
        client->ctdb = ctdb;
        client->fd = fd;
-       client->client_id = reqid_new(ctdb->idr, client);
+
+       ret = get_new_client_id(ctdb->idr, client, &client->client_id);
+       if (ret != 0) {
+               DBG_ERR("Unable to get client ID (%d)\n", ret);
+               close(fd);
+               talloc_free(client);
+               return;
+       }
+
        client->pid = peer_pid;
 
        client_pid = talloc(client, struct ctdb_client_pid_list);
@@ -1051,12 +1257,6 @@ static void ctdb_setup_event_callback(struct ctdb_context *ctdb, int status,
        }
        ctdb_run_notification_script(ctdb, "setup");
 
-       /* tell all other nodes we've just started up */
-       ctdb_daemon_send_control(ctdb, CTDB_BROADCAST_ALL,
-                                0, CTDB_CONTROL_STARTUP, 0,
-                                CTDB_CTRL_FLAG_NOREPLY,
-                                tdb_null, NULL, NULL);
-
        /* Start the recovery daemon */
        if (ctdb_start_recoverd(ctdb) != 0) {
                DEBUG(DEBUG_ALERT,("Failed to start recovery daemon\n"));
@@ -1145,7 +1345,7 @@ static void ctdb_create_pidfile(TALLOC_CTX *mem_ctx)
 
 static void ctdb_initialise_vnn_map(struct ctdb_context *ctdb)
 {
-       int i, j, count;
+       unsigned int i, j, count;
 
        /* initialize the vnn mapping table, skipping any deleted nodes */
        ctdb->vnn_map = talloc(ctdb, struct ctdb_vnn_map);
@@ -1199,14 +1399,14 @@ int ctdb_start_daemon(struct ctdb_context *ctdb, bool do_fork)
        int res, ret = -1;
        struct tevent_fd *fde;
 
-       become_daemon(do_fork, false, false);
+       become_daemon(do_fork, !do_fork, false);
 
        ignore_signal(SIGPIPE);
        ignore_signal(SIGUSR1);
 
        ctdb->ctdbd_pid = getpid();
        DEBUG(DEBUG_ERR, ("Starting CTDBD (Version %s) as PID: %u\n",
-                         CTDB_VERSION_STRING, ctdb->ctdbd_pid));
+                         SAMBA_VERSION_STRING, ctdb->ctdbd_pid));
        ctdb_create_pidfile(ctdb);
 
        /* create a unix domain stream socket to listen to */
@@ -1256,6 +1456,12 @@ int ctdb_start_daemon(struct ctdb_context *ctdb, bool do_fork)
                exit(1);
        }
 
+       TALLOC_FREE(ctdb->tunnels);
+       if (srvid_init(ctdb, &ctdb->tunnels) != 0) {
+               DEBUG(DEBUG_ERR, ("Failed to setup tunnels context\n"));
+               exit(1);
+       }
+
        /* initialize statistics collection */
        ctdb_statistics_init(ctdb);
 
@@ -1302,12 +1508,10 @@ int ctdb_start_daemon(struct ctdb_context *ctdb, bool do_fork)
 
        initialise_node_flags(ctdb);
 
-       if (ctdb->public_addresses_file) {
-               ret = ctdb_set_public_addresses(ctdb, true);
-               if (ret == -1) {
-                       DEBUG(DEBUG_ALERT,("Unable to setup public address list\n"));
-                       exit(1);
-               }
+       ret = ctdb_set_public_addresses(ctdb, true);
+       if (ret == -1) {
+               D_ERR("Unable to setup public IP addresses\n");
+               exit(1);
        }
 
        ctdb_initialise_vnn_map(ctdb);
@@ -1523,6 +1727,39 @@ static void daemon_request_control_from_client(struct ctdb_client *client,
        talloc_free(tmp_ctx);
 }
 
+static void daemon_request_tunnel_from_client(struct ctdb_client *client,
+                                             struct ctdb_req_tunnel_old *c)
+{
+       TDB_DATA data;
+       int ret;
+
+       if (! ctdb_validate_pnn(client->ctdb, c->hdr.destnode)) {
+               DEBUG(DEBUG_ERR, ("Invalid destination 0x%x\n",
+                                 c->hdr.destnode));
+               return;
+       }
+
+       ret = srvid_exists(client->ctdb->tunnels, c->tunnel_id, NULL);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("tunnel id 0x%"PRIx64" not registered, dropping pkt\n",
+                      c->tunnel_id));
+               return;
+       }
+
+       data = (TDB_DATA) {
+               .dsize = c->datalen,
+               .dptr = &c->data[0],
+       };
+
+       ret = ctdb_daemon_send_tunnel(client->ctdb, c->hdr.destnode,
+                                     c->tunnel_id, c->flags, data);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR, ("Failed to set tunnel to remote note %u\n",
+                                 c->hdr.destnode));
+       }
+}
+
 /*
   register a call function
 */
@@ -1744,7 +1981,7 @@ struct ctdb_client *ctdb_find_client_by_pid(struct ctdb_context *ctdb, pid_t pid
 /* This control is used by samba when probing if a process (of a samba daemon)
    exists on the node.
    Samba does this when it needs/wants to check if a subrecord in one of the
-   databases is still valied, or if it is stale and can be removed.
+   databases is still valid, or if it is stale and can be removed.
    If the node is in unhealthy or stopped state we just kill of the samba
    process holding this sub-record and return to the calling samba that
    the process does not exist.
@@ -1774,7 +2011,7 @@ int32_t ctdb_control_process_exists(struct ctdb_context *ctdb, pid_t pid)
 int32_t ctdb_control_check_pid_srvid(struct ctdb_context *ctdb,
                                     TDB_DATA indata)
 {
-        struct ctdb_client *client;
+       struct ctdb_client_pid_list *client_pid;
        pid_t pid;
        uint64_t srvid;
        int ret;
@@ -1782,17 +2019,19 @@ int32_t ctdb_control_check_pid_srvid(struct ctdb_context *ctdb,
        pid = *(pid_t *)indata.dptr;
        srvid = *(uint64_t *)(indata.dptr + sizeof(pid_t));
 
-       client = ctdb_find_client_by_pid(ctdb, pid);
-       if (client == NULL) {
-               return -1;
-       }
-
-       ret = srvid_exists(ctdb->srv, srvid, client);
-       if (ret != 0) {
-               return -1;
+       for (client_pid = ctdb->client_pids;
+            client_pid != NULL;
+            client_pid = client_pid->next) {
+               if (client_pid->pid == pid) {
+                       ret = srvid_exists(ctdb->srv, srvid,
+                                          client_pid->client);
+                       if (ret == 0) {
+                               return 0;
+                       }
+               }
        }
 
-       return 0;
+       return -1;
 }
 
 int ctdb_control_getnodesfile(struct ctdb_context *ctdb, uint32_t opcode, TDB_DATA indata, TDB_DATA *outdata)