SRS: Rework to handle multiple clients in a better way
authorPaul Kocialkowski <contact@paulk.fr>
Mon, 5 Nov 2012 21:06:31 +0000 (22:06 +0100)
committerPaul Kocialkowski <contact@paulk.fr>
Mon, 5 Nov 2012 21:06:31 +0000 (22:06 +0100)
Signed-off-by: Paul Kocialkowski <contact@paulk.fr>
samsung-ril.c
samsung-ril.h
srs.c
srs.h

index 94ea564..36cdbb5 100644 (file)
@@ -421,7 +421,7 @@ void ipc_rfs_dispatch(struct ipc_message_info *info)
        RIL_UNLOCK();
 }
 
-void srs_dispatch(int fd, struct srs_message *message)
+void srs_dispatch(struct srs_message *message)
 {
        if(message == NULL)
                return;
@@ -430,7 +430,7 @@ void srs_dispatch(int fd, struct srs_message *message)
 
        switch(message->command) {
                case SRS_CONTROL_PING:
-                       srs_control_ping(fd, message);
+                       srs_control_ping(message);
                        break;
                case SRS_SND_SET_CALL_CLOCK_SYNC:
                        srs_snd_set_call_clock_sync(message);
index 4415727..862391c 100644 (file)
@@ -199,7 +199,7 @@ extern struct ril_data ril_data;
 
 void ipc_fmt_dispatch(struct ipc_message_info *info);
 void ipc_rfs_dispatch(struct ipc_message_info *info);
-void srs_dispatch(int fd, struct srs_message *message);
+void srs_dispatch(struct srs_message *message);
 
 /* GEN */
 
diff --git a/srs.c b/srs.c
index 8683535..13f50a5 100644 (file)
--- a/srs.c
+++ b/srs.c
@@ -18,6 +18,7 @@
  *
  */
 
+#include <fcntl.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 #include "samsung-ril.h"
 #include "util.h"
 
-static int srs_server_send_message(int client_fd, struct srs_message *message)
+int srs_client_register(struct srs_client_data *client_data, int fd)
 {
-       fd_set fds;
+       struct srs_client_info *client;
+       struct list_head *list_end;
+       struct list_head *list;
+
+       if(client_data == NULL)
+               return -1;
+
+       client = calloc(1, sizeof(struct srs_client_info));
+       if(client == NULL)
+               return -1;
+
+       client->fd = fd;
+
+       list_end = client_data->clients;
+       while(list_end != NULL && list_end->next != NULL)
+               list_end = list_end->next;
+
+       list = list_head_alloc((void *) client, list_end, NULL);
+
+       if(client_data->clients == NULL)
+               client_data->clients = list;
+
+       return 0;
+}
+
+void srs_client_unregister(struct srs_client_data *client_data, struct srs_client_info *client)
+{
+       struct list_head *list;
+
+       if(client_data == NULL || client == NULL)
+               return;
+
+       list = client_data->clients;
+       while(list != NULL) {
+               if(list->data == (void *) client) {
+                       memset(client, 0, sizeof(struct srs_client_info));
+                       free(client);
+
+                       if(list == client_data->clients)
+                               client_data->clients = list->next;
+
+                       list_head_free(list);
+
+                       break;
+               }
+list_continue:
+               list = list->next;
+       }
+}
+
+struct srs_client_info *srs_client_info_find(struct srs_client_data *client_data)
+{
+       struct srs_client_info *client;
+       struct list_head *list;
+
+       list = client_data->clients;
+       while(list != NULL) {
+               client = (struct srs_client_info *) list->data;
+               if(client == NULL)
+                       goto list_continue;
+
+               return client;
+
+list_continue:
+               list = list->next;
+       }
+
+       return NULL;
+}
+
+struct srs_client_info *srs_client_info_find_fd(struct srs_client_data *client_data, int fd)
+{
+       struct srs_client_info *client;
+       struct list_head *list;
+
+       list = client_data->clients;
+       while(list != NULL) {
+               client = (struct srs_client_info *) list->data;
+               if(client == NULL)
+                       goto list_continue;
+
+               if(client->fd == fd)
+                       return client;
+
+list_continue:
+               list = list->next;
+       }
+
+       return NULL;
+}
 
+int srs_client_info_fill_fd_set(struct srs_client_data *client_data, fd_set *fds)
+{
+       struct srs_client_info *client;
+       struct list_head *list;
+       int fd_max;
+
+       if(client_data == NULL || fds == NULL)
+               return -1;
+
+       fd_max = -1;
+       list = client_data->clients;
+       while(list != NULL) {
+               client = (struct srs_client_info *) list->data;
+               if(client == NULL)
+                       goto list_continue;
+
+               FD_SET(client->fd, fds);
+               if(client->fd > fd_max)
+                       fd_max = client->fd;
+
+list_continue:
+               list = list->next;
+       }
+
+       return fd_max;
+}
+
+int srs_client_info_get_fd_set(struct srs_client_data *client_data, fd_set *fds)
+{
+       struct srs_client_info *client;
+       struct list_head *list;
+       int fd;
+
+       if(client_data == NULL || fds == NULL)
+               return -1;
+
+       list = client_data->clients;
+       while(list != NULL) {
+               client = (struct srs_client_info *) list->data;
+               if(client == NULL)
+                       goto list_continue;
+
+               if(FD_ISSET(client->fd, fds)) {
+                       FD_CLR(client->fd, fds);
+                       return client->fd;
+               }
+
+list_continue:
+               list = list->next;
+       }
+
+       return -1;
+}
+
+int srs_client_send_message(struct srs_client_data *client_data, struct srs_message *message)
+{
        struct srs_header header;
        void *data;
 
+       struct timeval timeout;
+       fd_set fds;
+       int rc;
+
+       if(client_data == NULL || message == NULL)
+               return -1;
+
+       memset(&header, 0, sizeof(header));
        header.length = message->data_len + sizeof(header);
        header.group = SRS_GROUP(message->command);
        header.index = SRS_INDEX(message->command);
 
-       data = malloc(header.length);
-       memset(data, 0, header.length);
+       data = calloc(1, header.length);
+       if(data == NULL)
+               return -1;
 
        memcpy(data, &header, sizeof(header));
-       memcpy((void *) ((char*)data + sizeof(header)),
-               message->data, message->data_len);
+       memcpy((void *) ((char *) data + sizeof(header)), message->data, message->data_len);
+
+       memset(&timeout, 0, sizeof(timeout));
+       timeout.tv_usec = 300;
+
+       if(client_data->client_fd < 0)
+               goto error;
 
        FD_ZERO(&fds);
-       FD_SET(client_fd, &fds);
+       FD_SET(client_data->client_fd, &fds);
 
-       select(FD_SETSIZE, NULL, &fds, NULL, NULL);
+       rc = select(client_data->client_fd + 1, NULL, &fds, NULL, &timeout);
 
-       write(client_fd, data, header.length);
+       if(!FD_ISSET(client_data->client_fd, &fds)) {
+               LOGE("SRS write select failed on fd %d", client_data->client_fd);
+               goto error;
+       }
+
+       rc = write(client_data->client_fd, data, header.length);
+       if(rc < (int) sizeof(struct srs_header)) {
+               LOGE("SRS write failed on fd %d with %d bytes", client_data->client_fd, rc);
+               goto error;
+       }
 
        free(data);
+       return rc;
 
+error:
+       free(data);
        return 0;
 }
 
-static int srs_server_send(int fd, unsigned short command, void *data,
-       int data_len)
+int srs_client_send(struct srs_client_data *client_data, unsigned short command, void *data, int length)
 {
+       struct srs_client_info *client;
        struct srs_message message;
        int rc;
 
+       if(client_data == NULL)
+               return -1;
+
+       memset(&message, 0, sizeof(message));
        message.command = command;
        message.data = data;
-       message.data_len = data_len;
+       message.data_len = length;
+
+       RIL_CLIENT_LOCK(client_data->client);
+       rc = srs_client_send_message(client_data, &message);
+       RIL_CLIENT_UNLOCK(client_data->client);
 
-       rc = srs_server_send_message(fd, &message);
+       if(rc <= 0) {
+               LOGD("SRS client with fd %d terminated", client_data->client_fd);
+
+               client = srs_client_info_find_fd(client_data, client_data->client_fd);
+               if(client != NULL)
+                       srs_client_unregister(client_data, client);
+               close(client_data->client_fd);
+               client_data->client_fd = -1;
+       }
 
        return rc;
 }
 
-static int srs_server_recv(int client_fd, struct srs_message *message)
+int srs_send(unsigned short command, void *data, int length)
+{
+       struct srs_client_data *client_data;
+       int rc;
+
+       if(ril_data.srs_client == NULL || ril_data.srs_client->data == NULL)
+               return -1;
+
+       client_data = (struct srs_client_data *) ril_data.srs_client->data;
+
+       LOGD("SEND SRS: fd=%d command=%d data_len=%d", client_data->client_fd, command, length);
+       if(data != NULL && length > 0) {
+               LOGD("==== SRS DATA DUMP ====");
+               hex_dump(data, length);
+               LOGD("=======================");
+       }
+
+       return srs_client_send(client_data, command, data, length);
+}
+
+int srs_client_recv(struct srs_client_data *client_data, struct srs_message *message)
 {
-       void *raw_data = malloc(SRS_DATA_MAX_SIZE);
        struct srs_header *header;
+       void *data;
+
+       struct timeval timeout;
+       fd_set fds;
        int rc;
 
-       rc = read(client_fd, raw_data, SRS_DATA_MAX_SIZE);
-       if(rc < (int)sizeof(struct srs_header)) {
+       if(client_data == NULL || message == NULL)
+               return -1;
+
+       data = calloc(1, SRS_DATA_MAX_SIZE);
+       if(data == NULL)
                return -1;
+
+       memset(&timeout, 0, sizeof(timeout));
+       timeout.tv_usec = 300;
+
+       if(client_data->client_fd < 0)
+               goto error;
+
+       FD_ZERO(&fds);
+       FD_SET(client_data->client_fd, &fds);
+
+       rc = select(client_data->client_fd + 1, &fds, NULL, NULL, &timeout);
+
+       if(!FD_ISSET(client_data->client_fd, &fds)) {
+               LOGE("SRS read select failed on fd %d", client_data->client_fd);
+               goto error;
+       }
+
+       rc = read(client_data->client_fd, data, SRS_DATA_MAX_SIZE);
+       if(rc < (int) sizeof(struct srs_header)) {
+               LOGE("SRS read failed on fd %d with %d bytes", client_data->client_fd, rc);
+               goto error;
        }
 
-       header = raw_data;
+       header = (struct srs_header *) data;
 
+       memset(message, 0, sizeof(struct srs_message));
        message->command = SRS_COMMAND(header);
        message->data_len = header->length - sizeof(struct srs_header);
-       message->data = malloc(message->data_len);
-
-       memcpy(message->data, (char*)raw_data + sizeof(struct srs_header),
-               message->data_len);
+       if(message->data_len > 0) {
+               message->data = calloc(1, message->data_len);
+               memcpy(message->data, (void *) ((char *) data + sizeof(struct srs_header)), message->data_len);
+       } else {
+               message->data = NULL;
+       }
 
-       free(raw_data);
+       free(data);
+       return rc;
 
+error:
+       free(data);
        return 0;
 }
 
-void srs_control_ping(int fd, struct srs_message *message)
+void srs_control_ping(struct srs_message *message)
 {
        int caffe;
 
-       if(message->data == NULL)
+       if(message == NULL || message->data == NULL || message->data_len < (int) sizeof(int))
                return;
 
        caffe=*((int *) message->data);
 
        if(caffe == SRS_CONTROL_CAFFE) {
-               srs_server_send(fd, SRS_CONTROL_PING, &caffe, sizeof(caffe));
+               srs_send(SRS_CONTROL_PING, &caffe, sizeof(caffe));
        }
 }
 
 static int srs_server_open(void)
 {
-       int server_fd = -1;
+       int server_fd;
+       int t;
 
-       int t = 0;
-
-       while(t < 5) {
+       for(t=0 ; t < 5 ; t++) {
                unlink(SRS_SOCKET_NAME);
 #if RIL_VERSION >= 6
                server_fd = socket_local_server(SRS_SOCKET_NAME, ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
@@ -132,154 +372,204 @@ static int srs_server_open(void)
                server_fd = socket_local_server(SRS_SOCKET_NAME, ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM);
 #endif
                if(server_fd >= 0)
-                       break;
-
-               t++;
+                       return server_fd;
        }
 
-       return server_fd;
+       return -1;
 }
 
-static void* srs_process_client(void *pfd)
+void *srs_client_read_loop(void *data)
 {
-       struct srs_message srs_message;
+       struct srs_client_info *client;
+       struct srs_client_data *client_data;
+       struct srs_message message;
+       struct timeval timeout;
        fd_set fds;
-       int client_fd = -1;
-       if (!pfd) {
-               LOGE("SRS client data is NULL");
-               goto fail;
-       }
+       int fd_max;
+       int fd;
+       int rc;
 
-       client_fd = ((int*)pfd)[0];
+       if(data == NULL)
+               pthread_exit(NULL);
 
-       while (1) {
-               if (client_fd < 0)
-                       break;
+       client_data = (struct srs_client_data *) data;
 
+       while(client_data->running) {
                FD_ZERO(&fds);
-               FD_SET(client_fd, &fds);
 
-               select(FD_SETSIZE, &fds, NULL, NULL, NULL);
+               SRS_CLIENT_LOCK();
+               fd_max = srs_client_info_fill_fd_set(client_data, &fds);
+               SRS_CLIENT_UNLOCK();
+
+               if(fd_max < 0) {
+                       usleep(3000);
+                       continue;
+               }
+
+               timeout.tv_sec = 0;
+               timeout.tv_usec = 3000;
+
+               select(fd_max + 1, &fds, NULL, NULL, &timeout);
+
+               SRS_CLIENT_LOCK();
+               while((fd = srs_client_info_get_fd_set(client_data, &fds)) >= 0) {
+                       client_data->client_fd = fd;
+
+                       RIL_CLIENT_LOCK(client_data->client);
+                       rc = srs_client_recv(client_data, &message);
+                       if(rc <= 0) {
+                               LOGD("SRS client with fd %d terminated", fd);
 
-               if (FD_ISSET(client_fd, &fds)) {
-                       if (srs_server_recv(client_fd, &srs_message) < 0) {
-                               LOGE("SRS recv failed, aborting!");
-                               break;
+                               client = srs_client_info_find_fd(client_data, fd);
+                               if(client != NULL)
+                                       srs_client_unregister(client_data, client);
+                               close(fd);
+
+                               RIL_CLIENT_UNLOCK(client_data->client);
+                               continue;
                        }
+                       RIL_CLIENT_UNLOCK(client_data->client);
 
-                       LOGD("SRS recv: command=%d data_len=%d",
-                            srs_message.command, srs_message.data_len);
-                       hex_dump(srs_message.data, srs_message.data_len);
+                       LOGD("RECV SRS: fd=%d command=%d data_len=%d", fd, message.command, message.data_len);
+                       if(message.data != NULL && message.data_len > 0) {
+                               LOGD("==== SRS DATA DUMP ====");
+                               hex_dump(message.data, message.data_len);
+                               LOGD("=======================");
+                       }
 
-                       srs_dispatch(client_fd, &srs_message);
+                       srs_dispatch(&message);
 
-                       if (srs_message.data != NULL)
-                               free(srs_message.data);
-               }
-       }
+                       if(message.data != NULL)
+                               free(message.data);
 
-fail:
-       if(client_fd >= 0) {
-               close(client_fd);
+                       client_data->client_fd = -1;
+               }
+               SRS_CLIENT_UNLOCK();
        }
 
-       LOGE("SRS server client ended!");
+       pthread_exit(NULL);
        return NULL;
 }
 
-static int srs_read_loop(struct ril_client *client)
+int srs_read_loop(struct ril_client *client)
 {
-       int rc;
-
+       struct srs_client_data *client_data;
        struct sockaddr_un client_addr;
        int client_addr_len;
+       pthread_attr_t attr;
+       int fd;
+       int rc;
 
-       if(client == NULL) {
-               LOGE("client is NULL, aborting!");
+       if(client == NULL || client->data == NULL)
                return -1;
-       }
 
-       if(client->data == NULL) {
-               LOGE("client data is NULL, aborting!");
-               return -1;
-       }
+       client_data = (struct srs_client_data *) client->data;
 
-       int server_fd = ((int*)client->data)[0];
+       pthread_attr_init(&attr);
+       pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
 
-       while(1) {
-               if(server_fd < 0) {
-                       LOGE("SRS client server_fd is negative, aborting!");
-                       return -1;
-               }
+       client_data->running = 1;
+
+       rc = pthread_create(&client_data->thread, &attr, srs_client_read_loop, (void *) client_data);
+       if(rc < 0) {
+               LOGE("Unable to create SRS client read loop thread");
+               return -1;
+       }
 
-               rc = accept(server_fd, (struct sockaddr*)&client_addr,
+       while(client_data->server_fd >= 0) {
+               fd = accept(client_data->server_fd, (struct sockaddr *) &client_addr,
                        &client_addr_len);
-               if (rc < 0) {
-                       LOGE("SRS Failed to accept errno %d error %s",
-                               errno, strerror(errno));
-                       return -1;
-               }
-               LOGI("SRS accepted fd %d", rc);
-               int *pfd = (int*)malloc(sizeof(int));
-               if (!pfd) {
-                       LOGE("out of memory for the client socket");
-                       close(rc);
-                       return -1;
+               if(fd < 0) {
+                       LOGE("Unable to accept new SRS client");
+                       break;
                }
-               *pfd = rc;
-
-               pthread_t t;
-               if (pthread_create(&t, NULL, srs_process_client, pfd)) {
-                       LOGE("SRS failed to start client thread errno %d error %s",
-                               errno, strerror(errno));
-                       close(rc);
-                       return -1;
+
+               fcntl(fd, F_SETFL, O_NONBLOCK);
+
+               LOGD("Accepted new SRS client from fd %d", fd);
+
+               SRS_CLIENT_LOCK();
+               rc = srs_client_register(client_data, fd);
+               SRS_CLIENT_UNLOCK();
+               if(rc < 0) {
+                       LOGE("Unable to register SRS client");
+                       break;
                }
        }
 
+       LOGE("SRS server failure");
+
+       client_data->running = 0;
+
+       // Wait for the thread to finish
+       pthread_join(client_data->thread, NULL);
+
        return 0;
 }
 
-static int srs_create(struct ril_client *client)
+int srs_create(struct ril_client *client)
 {
-       int *srs_server = NULL;
+       struct srs_client_data *client_data = NULL;
+
+       if(client == NULL)
+               return -1;
 
        LOGD("Creating new SRS client");
 
-       srs_server = malloc(sizeof(int));
-       if (!srs_server) {
-               LOGE("SRS out of memory for server fd");
-               goto fail;
+       signal(SIGPIPE, SIG_IGN);
+
+       client_data = (struct srs_client_data *) calloc(1, sizeof(struct srs_client_data));
+       if(client_data == NULL) {
+               LOGE("SRS client data creation failed");
+               return -1;
        }
 
-       client->data = (void *) srs_server;
-       if((*srs_server = srs_server_open()) < 0) {
-               LOGE("%s: samsung-ril-socket server open failed", __FUNCTION__);
+       client_data->server_fd = srs_server_open();
+       if(client_data->server_fd < 0) {
+               LOGE("SRS server creation failed");
                goto fail;
        }
 
+       pthread_mutex_init(&client_data->mutex, NULL);
+
+       client_data->client = client;
+       client->data = (void *) client_data;
+
        return 0;
 
 fail:
-       if (srs_server) {
-               free(srs_server);
-       }
+       if(client_data != NULL)
+               free(client_data);
+
        return -1;
 }
 
-static int srs_destroy(struct ril_client *client)
+int srs_destroy(struct ril_client *client)
 {
-       if (!client) {
-               return 0;
-       }
+       struct srs_client_data *client_data = NULL;
+       struct srs_client_info *client_info;
 
-       int *srs_server = (int*) client->data;
-       if (!srs_server) {
+       if(client == NULL)
                return 0;
+
+       if(client->data == NULL)
+               return -1;
+
+       client_data = (struct srs_client_data *) client->data;
+
+       pthread_mutex_destroy(&client_data->mutex);
+
+       while((client_info = srs_client_info_find(client_data)) != NULL) {
+               close(client_info->fd);
+               srs_client_unregister(client_data, client_info);
        }
 
-       close(*srs_server);
-       free(srs_server);
+       if(client_data->server_fd > 0)
+               close(client_data->server_fd);
+
+       memset(client_data, 0, sizeof(struct srs_client_data));
+       free(client_data);
+       client->data = NULL;
 
        return 0;
 }
diff --git a/srs.h b/srs.h
index c71dac3..b88731c 100644 (file)
--- a/srs.h
+++ b/srs.h
@@ -23,6 +23,7 @@
 
 #include <sys/types.h>
 #include <sys/socket.h>
+#include <sys/select.h>
 #include <sys/un.h>
 
 #include <arpa/inet.h>
 
 #include <samsung-ril-socket.h>
 
+#define SRS_CLIENT_LOCK() pthread_mutex_lock(&client_data->mutex)
+#define SRS_CLIENT_UNLOCK() pthread_mutex_unlock(&client_data->mutex)
+
+struct srs_client_info {
+       int fd;
+};
+
+struct srs_client_data {
+       struct ril_client *client;
+
+       int server_fd;
+       int client_fd;
+
+       struct list_head *clients;
+
+       pthread_t thread;
+       pthread_mutex_t mutex;
+       int running;
+};
+
 extern struct ril_client_funcs srs_client_funcs;
-extern void srs_control_ping(int fd, struct srs_message *message);
+
+int srs_send(unsigned short command, void *data, int length);
+void srs_control_ping(struct srs_message *message);
 
 #endif