socket-handler: Use array of pointers for clients array
When a client registers a poller, it uses a pointer to the struct client
in the poller's data field.
Since the clients are allocated in the socket handler's ->clients array
directly, and this array may be realloc()ed, a client's pointer may
change when other clients connect or disconnect.
This change uses separate allocations for the clients and the
sh->clients array, so the client pointers remain valid for the life of
the client.
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
diff --git a/socket-handler.c b/socket-handler.c
index 7be9c71..2e4f5c2 100644
--- a/socket-handler.c
+++ b/socket-handler.c
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+
+#include <assert.h>
#include <err.h>
#include <stdbool.h>
#include <stdio.h>
@@ -39,7 +41,7 @@
struct poller *poller;
int sd;
- struct client *clients;
+ struct client **clients;
int n_clients;
};
@@ -56,7 +58,14 @@
if (client->poller)
console_unregister_poller(sh->console, client->poller);
- idx = client - sh->clients;
+ for (idx = 0; idx < sh->n_clients; idx++)
+ if (sh->clients[idx] == client)
+ break;
+
+ assert(idx < sh->n_clients);
+
+ free(client);
+ client = NULL;
sh->n_clients--;
memmove(&sh->clients[idx], &sh->clients[idx+1],
@@ -112,14 +121,18 @@
if (fd < 0)
return POLLER_OK;
- n = sh->n_clients++;
- sh->clients = realloc(sh->clients, sizeof(*client) * sh->n_clients);
- client = &sh->clients[n];
+ client = malloc(sizeof(*client));
+ memset(client, 0, sizeof(*client));
client->fd = fd;
client->poller = console_register_poller(sh->console, handler,
client_poll, client->fd, POLLIN, client);
+ n = sh->n_clients++;
+ sh->clients = realloc(sh->clients,
+ sizeof(*sh->clients) * sh->n_clients);
+ sh->clients[n] = client;
+
return POLLER_OK;
}
@@ -170,7 +183,7 @@
int i;
for (i = 0; i < sh->n_clients; i++) {
- struct client *client = &sh->clients[i];
+ struct client *client = sh->clients[i];
client_send_data(sh, client, buf, len);
}
return 0;
@@ -182,7 +195,7 @@
int i;
for (i = 0; i < sh->n_clients; i++)
- client_close(sh, &sh->clients[i]);
+ client_close(sh, sh->clients[i]);
if (sh->poller)
console_unregister_poller(sh->console, sh->poller);