Add a mechanism where we can register notifications to be sent out to a SRVID when...
authorRonnie Sahlberg <ronniesahlberg@gmail.com>
Fri, 23 Oct 2009 04:24:51 +0000 (15:24 +1100)
committerRonnie Sahlberg <ronniesahlberg@gmail.com>
Fri, 23 Oct 2009 04:24:51 +0000 (15:24 +1100)
The way to use this is from a client to :
1, first create a message handle and bind it to a SRVID
   A special prefix for the srvid space has been set aside for samba :
   Only samba is allowed to use srvid's with the top 32 bits set like this.
   The lower 32 bits are for samba to use internally.

2, register a "notification" using the new control :
                    CTDB_CONTROL_REGISTER_NOTIFY         = 114,
   This control takes as indata a structure like this :
struct ctdb_client_notify_register {
        uint64_t srvid;
        uint32_t len;
        uint8_t notify_data[1];
};

srvid is the srvid used in the space set aside above.
len and notify_data is an arbitrary blob.
When notifications are later sent out to all clients, this is the payload of that notification message.

If a client has registered with control 114 and then disconnects from ctdbd, ctdbd will broadcast a message to that srvid to all nodes/listeners in the cluster.

A client can resister itself with as many different srvid's it want, but this is handled through a linked list from the client structure so it mainly designed for "few notifications per client".

3, a client that no longer wants to have a notification set up can deregister using control
                    CTDB_CONTROL_DEREGISTER_NOTIFY       = 115,
which takes this as arguments :
struct ctdb_client_notify_deregister {
        uint64_t srvid;
};

When a client deregisters, there will no longer be sent a message to all other clients when this client disconnects from ctdbd.

include/ctdb.h
include/ctdb_private.h
server/ctdb_control.c
server/ctdb_daemon.c

index abe09a3f966dfd2464336764212fa3d1158a510f..1ede66205a249b518a2675107aa382d2e7f43177 100644 (file)
@@ -111,6 +111,10 @@ struct ctdb_call_info {
 */
 #define CTDB_SRVID_TAKEOVER_RUN_RESPONSE  0xFD00000000000000LL
 
+/* A port reserved for samba (top 32 bits)
+ */
+#define CTDB_SRVID_SAMBA_NOTIFY  0xFE00000000000000LL
+
 /* used on the domain socket, send a pdu to the local daemon */
 #define CTDB_CURRENT_NODE     0xF0000001
 /* send a broadcast to all nodes in the cluster, active or not */
@@ -144,6 +148,15 @@ struct ctdb_client_control_state {
        } async;        
 };
 
+struct ctdb_client_notify_register {
+       uint64_t srvid;
+       uint32_t len;
+       uint8_t notify_data[1];
+};
+
+struct ctdb_client_notify_deregister {
+       uint64_t srvid;
+};
 
 struct event_context;
 
index 2f4937e60f844a1f3a8c234d1efe7b6e10072a91..ad84628a99dfc9814a19e7cad428b57670d95477 100644 (file)
@@ -160,7 +160,6 @@ typedef void (*ctdb_control_callback_fn_t)(struct ctdb_context *,
                                           int32_t status, TDB_DATA data, 
                                           const char *errormsg,
                                           void *private_data);
-
 /*
   structure describing a connected client in the daemon
  */
@@ -173,6 +172,7 @@ struct ctdb_client {
        struct ctdb_tcp_list *tcp_list;
        uint32_t db_id;
        uint32_t num_persistent_updates;
+       struct ctdb_client_notify_list *notify;
 };
 
 
@@ -612,6 +612,8 @@ enum ctdb_controls {CTDB_CONTROL_PROCESS_EXISTS          = 0,
                    CTDB_CONTROL_SET_DB_PRIORITY         = 111,
                    CTDB_CONTROL_GET_DB_PRIORITY         = 112,
                    CTDB_CONTROL_TRANSACTION_CANCEL      = 113,
+                   CTDB_CONTROL_REGISTER_NOTIFY         = 114,
+                   CTDB_CONTROL_DEREGISTER_NOTIFY       = 115,
 };     
 
 /*
@@ -1501,4 +1503,8 @@ int32_t ctdb_control_set_ban_state(struct ctdb_context *ctdb, TDB_DATA indata);
 int32_t ctdb_control_get_ban_state(struct ctdb_context *ctdb, TDB_DATA *outdata);
 int32_t ctdb_control_set_db_priority(struct ctdb_context *ctdb, TDB_DATA indata);
 
+int32_t ctdb_control_register_notify(struct ctdb_context *ctdb, uint32_t client_id, TDB_DATA indata);
+
+int32_t ctdb_control_deregister_notify(struct ctdb_context *ctdb, uint32_t client_id, TDB_DATA indata);
+
 #endif
index 24d22d0df79796b00d41201717d7beba4db990c1..904bebe8ff9516382d6793b4e4cf332b4a8b52eb 100644 (file)
@@ -552,6 +552,13 @@ static int32_t ctdb_control_dispatch(struct ctdb_context *ctdb,
                CHECK_CONTROL_DATA_SIZE(0);
                return ctdb_control_transaction_cancel(ctdb);
 
+       case CTDB_CONTROL_REGISTER_NOTIFY:
+               return ctdb_control_register_notify(ctdb, client_id, indata);
+
+       case CTDB_CONTROL_DEREGISTER_NOTIFY:
+               CHECK_CONTROL_DATA_SIZE(sizeof(struct ctdb_client_notify_deregister));
+               return ctdb_control_deregister_notify(ctdb, client_id, indata);
+
        default:
                DEBUG(DEBUG_CRIT,(__location__ " Unknown CTDB control opcode %u\n", opcode));
                return -1;
index 532887c7842f788f5c598721b5010fa7aa212043..a8dc651749973a9603bf55f881561424d5cd50e9 100644 (file)
@@ -31,7 +31,6 @@
 
 static void daemon_incoming_packet(void *, struct ctdb_req_header *);
 
-
 static void print_exit_message(void)
 {
        DEBUG(DEBUG_NOTICE,("CTDB daemon shutting down\n"));
@@ -1043,3 +1042,105 @@ int ctdb_daemon_send_message(struct ctdb_context *ctdb, uint32_t pnn,
        return 0;
 }
 
+
+
+struct ctdb_client_notify_list {
+       struct ctdb_client_notify_list *next, *prev;
+       struct ctdb_context *ctdb;
+       uint64_t srvid;
+       TDB_DATA data;
+};
+
+
+static int ctdb_client_notify_destructor(struct ctdb_client_notify_list *nl)
+{
+       int ret;
+
+       DEBUG(DEBUG_ERR,("Sending client notify message for srvid:%llu\n", (unsigned long long)nl->srvid));
+
+       ret = ctdb_daemon_send_message(nl->ctdb, CTDB_BROADCAST_CONNECTED, (unsigned long long)nl->srvid, nl->data);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,("Failed to send client notify message\n"));
+       }
+
+       return 0;
+}
+
+int32_t ctdb_control_register_notify(struct ctdb_context *ctdb, uint32_t client_id, TDB_DATA indata)
+{
+       struct ctdb_client_notify_register *notify = (struct ctdb_client_notify_register *)indata.dptr;
+        struct ctdb_client *client = ctdb_reqid_find(ctdb, client_id, struct ctdb_client); 
+       struct ctdb_client_notify_list *nl;
+
+       DEBUG(DEBUG_ERR,("Register srvid %llu for client %d\n", (unsigned long long)notify->srvid, client_id));
+
+       if (indata.dsize < offsetof(struct ctdb_client_notify_register, notify_data)) {
+               DEBUG(DEBUG_ERR,(__location__ " Too little data in control : %d\n", (int)indata.dsize));
+               return -1;
+       }
+
+       if (indata.dsize != (notify->len + offsetof(struct ctdb_client_notify_register, notify_data))) {
+               DEBUG(DEBUG_ERR,(__location__ " Wrong amount of data in control. Got %d, expected %d\n", (int)indata.dsize, (int)(notify->len + offsetof(struct ctdb_client_notify_register, notify_data))));
+               return -1;
+       }
+
+
+        if (client == NULL) {
+                DEBUG(DEBUG_ERR,(__location__ " Could not find client parent structure. You can not send this control to a remote node\n"));
+                return -1;
+        }
+
+       for(nl=client->notify; nl; nl=nl->next) {
+               if (nl->srvid == notify->srvid) {
+                       break;
+               }
+       }
+       if (nl != NULL) {
+                DEBUG(DEBUG_ERR,(__location__ " Notification for srvid:%llu already exists for this client\n", (unsigned long long)notify->srvid));
+                return -1;
+        }
+
+       nl = talloc(client, struct ctdb_client_notify_list);
+       CTDB_NO_MEMORY(ctdb, nl);
+       nl->ctdb       = ctdb;
+       nl->srvid      = notify->srvid;
+       nl->data.dsize = notify->len;
+       nl->data.dptr  = talloc_size(nl, nl->data.dsize);
+       CTDB_NO_MEMORY(ctdb, nl->data.dptr);
+       memcpy(nl->data.dptr, notify->notify_data, nl->data.dsize);
+       
+       DLIST_ADD(client->notify, nl);
+       talloc_set_destructor(nl, ctdb_client_notify_destructor);
+
+       return 0;
+}
+
+int32_t ctdb_control_deregister_notify(struct ctdb_context *ctdb, uint32_t client_id, TDB_DATA indata)
+{
+       struct ctdb_client_notify_deregister *notify = (struct ctdb_client_notify_deregister *)indata.dptr;
+        struct ctdb_client *client = ctdb_reqid_find(ctdb, client_id, struct ctdb_client); 
+       struct ctdb_client_notify_list *nl;
+
+       DEBUG(DEBUG_ERR,("Deregister srvid %llu for client %d\n", (unsigned long long)notify->srvid, client_id));
+
+        if (client == NULL) {
+                DEBUG(DEBUG_ERR,(__location__ " Could not find client parent structure. You can not send this control to a remote node\n"));
+                return -1;
+        }
+
+       for(nl=client->notify; nl; nl=nl->next) {
+               if (nl->srvid == notify->srvid) {
+                       break;
+               }
+       }
+       if (nl == NULL) {
+                DEBUG(DEBUG_ERR,(__location__ " No notification for srvid:%llu found for this client\n", (unsigned long long)notify->srvid));
+                return -1;
+        }
+
+       DLIST_REMOVE(client->notify, nl);
+       talloc_set_destructor(nl, NULL);
+       talloc_free(nl);
+
+       return 0;
+}