add concept of names and ability for clients to own them
This commit is contained in:
parent
fbaffe515e
commit
36162d106e
5 changed files with 273 additions and 30 deletions
13
main.c
13
main.c
|
@ -3,19 +3,6 @@
|
|||
#include <stdio.h>
|
||||
#include "server.h"
|
||||
|
||||
// https://en.wikipedia.org/wiki/Fowler-Noll-Vo_hash_function#FNV-1a_hash
|
||||
uint64_t hashmap_hash(const char *bytes, size_t bytes_n, size_t map_len)
|
||||
{
|
||||
uint64_t hash = 0xcbf29ce484222325;
|
||||
|
||||
for (size_t i = 0; i < bytes_n; i++) {
|
||||
hash *= 0x100000001b3;
|
||||
hash ^= bytes[i];
|
||||
}
|
||||
|
||||
return (hash % map_len);
|
||||
}
|
||||
|
||||
const char *arg_shift(int *argc, char ***argv) {
|
||||
if (*argc < 1)
|
||||
return NULL;
|
||||
|
|
200
server.c
200
server.c
|
@ -5,15 +5,39 @@
|
|||
#include <sys/un.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include "try.h"
|
||||
#include "server.h"
|
||||
#include "wire.h"
|
||||
|
||||
char *string_dup(const char *s)
|
||||
{
|
||||
size_t len = strlen(s) + 1;
|
||||
char *p = malloc(len);
|
||||
return p ? memcpy(p, s, len) : NULL;
|
||||
}
|
||||
|
||||
|
||||
// https://en.wikipedia.org/wiki/Fowler-Noll-Vo_hash_function#FNV-1a_hash
|
||||
uint64_t hashmap_hash(const char *bytes, size_t bytes_n, size_t map_len)
|
||||
{
|
||||
uint64_t hash = 0xcbf29ce484222325;
|
||||
|
||||
for (size_t i = 0; i < bytes_n; i++) {
|
||||
hash *= 0x100000001b3;
|
||||
hash ^= bytes[i];
|
||||
}
|
||||
|
||||
return (hash % map_len);
|
||||
}
|
||||
|
||||
int jb_server_client_add(struct jb_server *s, int fd)
|
||||
{
|
||||
for (int i = 0; i < JB_MAX_CLIENTS; i++) {
|
||||
if (s->clients[i].fd < 0) {
|
||||
s->clients[i].fd = fd;
|
||||
s->clients[i].owned_name_index = -1;
|
||||
s->clients[i].unique_name_index = -1;
|
||||
s->clients[i].state = JB_CLIENT_STATE_WAIT_AUTH;
|
||||
s->fds[i].fd = fd;
|
||||
s->fds[i].events = POLLIN;
|
||||
|
@ -23,11 +47,107 @@ int jb_server_client_add(struct jb_server *s, int fd)
|
|||
return -1;
|
||||
}
|
||||
|
||||
int jb_server_name_find(struct jb_server *s, char *name)
|
||||
{
|
||||
int bucket = hashmap_hash(name, strlen(name), JB_MAX_NAMES);
|
||||
|
||||
for (int i = bucket; i < bucket + 12 && i < JB_MAX_NAMES; i++) {
|
||||
if (s->names[i].name && strcmp(s->names[i].name, name) == 0) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int jb_server_name_add(struct jb_server *s, char *name, int client_index)
|
||||
{
|
||||
if (jb_server_name_find(s, name) >= 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int bucket = hashmap_hash(name, strlen(name), JB_MAX_NAMES);
|
||||
|
||||
for (int i = bucket; i < bucket + 12 && i < JB_MAX_NAMES; i++) {
|
||||
if (s->names[i].client_index == -1) {
|
||||
s->names[i].client_index = client_index;
|
||||
s->names[i].name = name;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int jb_server_client_assign_unique_name(struct jb_server *s, int i)
|
||||
{
|
||||
struct jb_client *c = &s->clients[i];
|
||||
if (c->unique_name_index != -1) {
|
||||
return -1;
|
||||
}
|
||||
uint32_t id = 0;
|
||||
FILE *urandom_file = fopen("/dev/urandom", "rb");
|
||||
if (!urandom_file) {
|
||||
return -1;
|
||||
}
|
||||
if (fread(&id, 1, sizeof(uint32_t), urandom_file) != sizeof(uint32_t)) {
|
||||
return -1;
|
||||
}
|
||||
char *name = malloc(sizeof(char) * 16);
|
||||
if (!name) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (snprintf(name, 16, ":%"PRIu32, id) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int name_index = jb_server_name_add(s, name, i);
|
||||
if (name_index < 0) {
|
||||
free(name);
|
||||
return -1;
|
||||
}
|
||||
c->unique_name_index = name_index;
|
||||
|
||||
return name_index;
|
||||
}
|
||||
|
||||
int jb_server_client_assign_own_name(struct jb_server *s, int i, char *name)
|
||||
{
|
||||
struct jb_client *c = &s->clients[i];
|
||||
if (c->owned_name_index != -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int name_index = jb_server_name_add(s, name, i);
|
||||
if (name_index < 0) {
|
||||
return -1;
|
||||
}
|
||||
c->owned_name_index = name_index;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void jb_server_name_remove(struct jb_server *s, int i)
|
||||
{
|
||||
if (i >= 0) {
|
||||
if (s->names[i].name) {
|
||||
free(s->names[i].name);
|
||||
s->names[i].name = NULL;
|
||||
}
|
||||
s->names[i].client_index = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void jb_server_client_remove(struct jb_server *s, int i)
|
||||
{
|
||||
if (s->clients[i].fd >= 0) {
|
||||
close(s->clients[i].fd);
|
||||
}
|
||||
jb_server_name_remove(s, s->clients[i].unique_name_index);
|
||||
jb_server_name_remove(s, s->clients[i].owned_name_index);
|
||||
s->clients[i].unique_name_index = -1;
|
||||
s->clients[i].owned_name_index = -1;
|
||||
s->clients[i].fd = -1;
|
||||
s->clients[i].state = JB_CLIENT_STATE_NONE;
|
||||
s->fds[i].fd = -1;
|
||||
|
@ -53,6 +173,8 @@ ssize_t jb_server_client_recv(struct jb_server *s, int i, void *buf, size_t n)
|
|||
|
||||
int jb_server_client_process_message(struct jb_server *s, int i, uint8_t *data, size_t data_len)
|
||||
{
|
||||
struct jb_client *client = &s->clients[i];
|
||||
|
||||
wire_context_t ctx = {
|
||||
.byte_cursor = 0,
|
||||
.data = data,
|
||||
|
@ -63,7 +185,13 @@ int jb_server_client_process_message(struct jb_server *s, int i, uint8_t *data,
|
|||
|
||||
TRY_NONNEGATIVE(int, wire_parse_message(&ctx, &msg), -1);
|
||||
|
||||
printf("length: %d, serial: %d, header fields length in bytes: %d\n", msg.body_length, msg.serial, msg.header_fields_length);
|
||||
uint8_t reply_data[512];
|
||||
memset(reply_data, 0, 512);
|
||||
wire_context_t reply_ctx = {
|
||||
.byte_cursor = 0,
|
||||
.data = reply_data,
|
||||
.data_len = 511,
|
||||
};
|
||||
|
||||
switch (msg.type) {
|
||||
case DBUS_MESSAGE_METHOD_CALL: {
|
||||
|
@ -83,26 +211,57 @@ int jb_server_client_process_message(struct jb_server *s, int i, uint8_t *data,
|
|||
|
||||
if (for_dbus) {
|
||||
if (strcmp(member, "Hello") == 0) {
|
||||
printf("jb_server_client_process_message: message was for dbus, got hello\n");
|
||||
uint8_t reply_data[512];
|
||||
memset(reply_data, 0, 512);
|
||||
wire_context_t reply_ctx = {
|
||||
.byte_cursor = 0,
|
||||
.data = reply_data,
|
||||
.data_len = 511,
|
||||
};
|
||||
int unique_name_index = jb_server_client_assign_unique_name(s, i);
|
||||
if (unique_name_index < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint32_t *body_length;
|
||||
TRY_NONNEGATIVE(int, wire_compose_reply(&reply_ctx, &msg, "s", &body_length), -1);
|
||||
uint32_t body_start = reply_ctx.byte_cursor;
|
||||
TRY_NONNULL(char*, wire_set_string(&reply_ctx, ":1.0"), -1);
|
||||
TRY_NONNULL(char*, wire_set_string(&reply_ctx, s->names[unique_name_index].name), -1);
|
||||
*body_length = reply_ctx.byte_cursor - body_start;
|
||||
if (send(s->fds[i].fd, reply_data, reply_ctx.byte_cursor, 0) != reply_ctx.byte_cursor) {
|
||||
return -1;
|
||||
}
|
||||
printf("jb_server_client_process_message: processed hello\n");
|
||||
printf("assigned unique name '%s' to connection %d\n", s->names[unique_name_index].name, i);
|
||||
} else if (strcmp(member, "RequestName") == 0) {
|
||||
char *name = TRY_NONNULL(char*, wire_get_string(&ctx, NULL), -1);
|
||||
int name_len = strlen(name);
|
||||
if (name_len < 1 || name_len > 256) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
char *name_str = string_dup(name);
|
||||
if (!name_str) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int status_code = DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER;
|
||||
|
||||
if (jb_server_client_assign_own_name(s, i, name_str) < 0) {
|
||||
free(name_str);
|
||||
// TODO: report the actual error
|
||||
status_code = DBUS_REQUEST_NAME_REPLY_EXISTS;
|
||||
}
|
||||
|
||||
uint32_t *body_length;
|
||||
TRY_NONNEGATIVE(int, wire_compose_reply(&reply_ctx, &msg, "u", &body_length), -1);
|
||||
uint32_t body_start = reply_ctx.byte_cursor;
|
||||
TRY_NONNULL(uint32_t*, wire_set_u32(&reply_ctx, status_code), -1);
|
||||
*body_length = reply_ctx.byte_cursor - body_start;
|
||||
if (send(s->fds[i].fd, reply_data, reply_ctx.byte_cursor, 0) != reply_ctx.byte_cursor) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (status_code == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
|
||||
printf("client '%s' (index=%d) now owns name '%s'\n", s->names[client->unique_name_index].name, i, name_str);
|
||||
}
|
||||
} else {
|
||||
printf("jb_server_client_process_message: message was for dbus, however got unimplemented member '%s'\n", member);
|
||||
TRY_NONNEGATIVE(int, wire_compose_error(&reply_ctx, &msg, "org.freedesktop.DBus.Error.UnknownMethod"), -1);
|
||||
if (send(s->fds[i].fd, reply_data, reply_ctx.byte_cursor, 0) != reply_ctx.byte_cursor) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
printf("jb_server_client_process_message: skipping message not for dbus for now\n");
|
||||
|
@ -110,7 +269,11 @@ int jb_server_client_process_message(struct jb_server *s, int i, uint8_t *data,
|
|||
} break;
|
||||
|
||||
default: {
|
||||
printf("jb_server_client_process_message: unimplemented message type: %d\n", msg.type);
|
||||
TRY_NONNEGATIVE(int, wire_compose_error(&reply_ctx, &msg, "xyz.hippoz.jitterbug.NotImplemented"), -1);
|
||||
if (send(s->fds[i].fd, reply_data, reply_ctx.byte_cursor, 0) != reply_ctx.byte_cursor) {
|
||||
return -1;
|
||||
}
|
||||
return -1;
|
||||
} break;
|
||||
}
|
||||
|
||||
|
@ -157,12 +320,19 @@ struct jb_server *jb_server_create(const char *socket_path)
|
|||
|
||||
for (int i = 0; i < JB_MAX_CLIENTS; i++) {
|
||||
s->clients[i].fd = -1;
|
||||
s->clients[i].owned_name_index = -1;
|
||||
s->clients[i].unique_name_index = -1;
|
||||
s->clients[i].state = JB_CLIENT_STATE_NONE;
|
||||
s->fds[i].fd = -1;
|
||||
s->fds[i].events = 0;
|
||||
s->fds[i].revents = 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < JB_MAX_NAMES; i++) {
|
||||
s->names[i].client_index = -1;
|
||||
s->names[i].name = NULL;
|
||||
}
|
||||
|
||||
s->fds[JB_MAX_CLIENTS].fd = s->sock_fd;
|
||||
s->fds[JB_MAX_CLIENTS].events = POLLIN;
|
||||
s->fd_num = JB_MAX_CLIENTS + 1;
|
||||
|
@ -254,7 +424,9 @@ int jb_server_turn(struct jb_server *s)
|
|||
} break;
|
||||
|
||||
case JB_CLIENT_STATE_READY: {
|
||||
jb_server_client_process_message(s, i, (uint8_t*)data, data_buffer_len);
|
||||
if (jb_server_client_process_message(s, i, (uint8_t*)data, data_buffer_len) < 0) {
|
||||
_client_die("failed to process message");
|
||||
}
|
||||
} break;
|
||||
|
||||
case JB_CLIENT_STATE_NONE: {} /* through */
|
||||
|
|
17
server.h
17
server.h
|
@ -2,9 +2,12 @@
|
|||
#define _JITTERBUG__SERVER_H
|
||||
|
||||
#include <poll.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define JB_MAX_CLIENTS 128
|
||||
#define JB_BACKLOG 10
|
||||
// TODO: dynamically size the arrays
|
||||
#define JB_MAX_CLIENTS 256
|
||||
#define JB_MAX_NAMES 512
|
||||
#define JB_BACKLOG 12
|
||||
|
||||
enum {
|
||||
JB_CLIENT_STATE_NONE,
|
||||
|
@ -15,13 +18,21 @@ enum {
|
|||
|
||||
struct jb_client {
|
||||
int fd;
|
||||
int state;
|
||||
uint8_t state;
|
||||
int16_t unique_name_index;
|
||||
int16_t owned_name_index;
|
||||
};
|
||||
|
||||
struct jb_name {
|
||||
char *name;
|
||||
int16_t client_index;
|
||||
};
|
||||
|
||||
struct jb_server {
|
||||
int sock_fd;
|
||||
int fd_num;
|
||||
struct jb_client clients[JB_MAX_CLIENTS];
|
||||
struct jb_name names[JB_MAX_NAMES];
|
||||
struct pollfd fds[JB_MAX_CLIENTS + 1];
|
||||
};
|
||||
|
||||
|
|
57
wire.c
57
wire.c
|
@ -157,6 +157,11 @@ int wire_parse_message(wire_context_t *c, wire_message_t *msg)
|
|||
msg->fields[msg->header_fields_count].t.str = str;
|
||||
printf("DBUS_HEADER_FIELD_DESTINATION: %s\n", str);
|
||||
} break;
|
||||
case DBUS_HEADER_FIELD_SIGNATURE: {
|
||||
char *str = TRY_NONNULL(char*, wire_get_signature(c, NULL), -1);
|
||||
msg->fields[msg->header_fields_count].t.str = str;
|
||||
printf("DBUS_HEADER_FIELD_SIGNATURE: %s\n", str);
|
||||
} break;
|
||||
|
||||
default: {
|
||||
printf("header: unknown field: %d\n", field_code);
|
||||
|
@ -173,6 +178,9 @@ int wire_parse_message(wire_context_t *c, wire_message_t *msg)
|
|||
}
|
||||
}
|
||||
|
||||
/* header ends at 8 byte boundry */
|
||||
TRY_NONNULL(bool, wire_align(c, 8), -1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -228,3 +236,52 @@ int wire_compose_reply(wire_context_t *c, wire_message_t *msg, const char *signa
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wire_compose_error(wire_context_t *c, wire_message_t *msg, const char *error_name)
|
||||
{
|
||||
|
||||
TRY_NONNULL(uint8_t*, wire_set_u8(c, msg->endianness), -1); /* endianness */
|
||||
TRY_NONNULL(uint8_t*, wire_set_u8(c, DBUS_MESSAGE_ERROR), -1); /* type */
|
||||
TRY_NONNULL(uint8_t*, wire_set_u8(c, 0), -1); /* flags */
|
||||
TRY_NONNULL(uint8_t*, wire_set_u8(c, DBUS_PROTOCOL_VERSION), -1); /* protocol_version */
|
||||
TRY_NONNULL(uint32_t*, wire_set_u32(c, 0), -1); /* body length */
|
||||
TRY_NONNULL(uint32_t*, wire_set_u32(c, msg->serial+1), -1); /* serial */
|
||||
uint32_t *header_fields_length = TRY_NONNULL(uint32_t*, wire_set_u32(c, 0), -1); /* header_fields_length */
|
||||
|
||||
uint32_t header_fields_start = c->byte_cursor;
|
||||
|
||||
/* header fields */
|
||||
{
|
||||
/* ERROR_NAME */
|
||||
{
|
||||
/* byte (field code) */
|
||||
TRY_NONNULL(uint8_t*, wire_set_u8(c, DBUS_HEADER_FIELD_ERROR_NAME), -1);
|
||||
/* variant */
|
||||
/* signature */
|
||||
TRY_NONNULL(char*, wire_set_signature(c, "s"), -1);
|
||||
/* string */
|
||||
TRY_NONNULL(char*, wire_set_string(c, error_name), -1);
|
||||
|
||||
// need to align to 8 byte boundry after each array element?
|
||||
TRY_NONNULL(bool, wire_write_align(c, 8), -1);
|
||||
}
|
||||
|
||||
/* REPLY_SERIAL */
|
||||
{
|
||||
/* byte (field code) */
|
||||
TRY_NONNULL(uint8_t*, wire_set_u8(c, DBUS_HEADER_FIELD_REPLY_SERIAL), -1);
|
||||
/* variant */
|
||||
/* signature */
|
||||
TRY_NONNULL(char*, wire_set_signature(c, "u"), -1);
|
||||
/* reply serial */
|
||||
TRY_NONNULL(uint32_t*, wire_set_u32(c, msg->serial), -1);
|
||||
}
|
||||
}
|
||||
|
||||
*header_fields_length = c->byte_cursor - header_fields_start;
|
||||
|
||||
// header ends on an 8 byte alignment
|
||||
TRY_NONNULL(bool, wire_write_align(c, 8), -1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
16
wire.h
16
wire.h
|
@ -8,6 +8,21 @@
|
|||
|
||||
#define DBUS_PROTOCOL_VERSION 1
|
||||
|
||||
/* The caller is now the primary owner of the name, replacing any previous owner.
|
||||
Either the name had no owner before, or the caller specified DBUS_NAME_FLAG_REPLACE_EXISTING
|
||||
and the current owner specified DBUS_NAME_FLAG_ALLOW_REPLACEMENT. */
|
||||
#define DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER 1
|
||||
/* The name already had an owner, DBUS_NAME_FLAG_DO_NOT_QUEUE was not specified,
|
||||
and either the current owner did not specify DBUS_NAME_FLAG_ALLOW_REPLACEMENT or
|
||||
the requesting application did not specify DBUS_NAME_FLAG_REPLACE_EXISTING. */
|
||||
#define DBUS_REQUEST_NAME_REPLY_IN_QUEUE 2
|
||||
/* The name already has an owner, DBUS_NAME_FLAG_DO_NOT_QUEUE was specified,
|
||||
and either DBUS_NAME_FLAG_ALLOW_REPLACEMENT was not specified by the current owner,
|
||||
or DBUS_NAME_FLAG_REPLACE_EXISTING was not specified by the requesting application. */
|
||||
#define DBUS_REQUEST_NAME_REPLY_EXISTS 3
|
||||
/* The application trying to request ownership of a name is already the owner of it. */
|
||||
#define DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER 4
|
||||
|
||||
enum {
|
||||
DBUS_HEADER_FIELD_INVALID,
|
||||
DBUS_HEADER_FIELD_PATH,
|
||||
|
@ -112,5 +127,6 @@ char *wire_get_signature(wire_context_t *c, uint8_t *out_len);
|
|||
char *wire_set_signature(wire_context_t *c, const char *str);
|
||||
int wire_parse_message(wire_context_t *c, wire_message_t *msg);
|
||||
int wire_compose_reply(wire_context_t *c, wire_message_t *msg, const char *signature, uint32_t **out_body_length);
|
||||
int wire_compose_error(wire_context_t *c, wire_message_t *msg, const char *error_name);
|
||||
|
||||
#endif // _JITTERBUG__WIRE_H
|
||||
|
|
Loading…
Reference in a new issue