summaryrefslogtreecommitdiff
path: root/daemon.c
diff options
context:
space:
mode:
Diffstat (limited to 'daemon.c')
-rw-r--r--daemon.c273
1 files changed, 273 insertions, 0 deletions
diff --git a/daemon.c b/daemon.c
new file mode 100644
index 0000000..bcb5049
--- /dev/null
+++ b/daemon.c
@@ -0,0 +1,273 @@
+#define _DEFAULT_SOURCE
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include "common.h"
+#include "daemon.h"
+#include "streamer.h"
+
+int daemon_run = 0;
+
+// TODO: Too pedantic and verbose, replace with read_val, write_val, read_str, write_str
+#define strict_read(fd, ptr, count) (read(fd, ptr, sizeof(*ptr) * count) != sizeof(*ptr) * count)
+#define strict_write(fd, ptr, count) (write(fd, ptr, sizeof(*ptr) * count) != sizeof(*ptr) * count)
+
+enum Opcode {
+ OP_DAEMON,
+ OP_PUSH,
+ OP_POP,
+ OP_TERM,
+ OP_SKIP,
+ OP_LIST,
+ OP_PAUSE,
+ OP_RESUME,
+};
+
+struct Command {
+ enum Opcode opcode;
+ int opindex;
+ int urllen;
+};
+
+#define write_val(fd, ptr) (write(fd, ptr, sizeof(*ptr)))
+#define read_val(fd, ptr) (read(fd, ptr, sizeof(*ptr)))
+static void write_str (int fd, const char *str);
+static char *read_str (int fd);
+
+// ===================================================================
+// Server code
+// ===================================================================
+
+static inline int send_reply (int fd, struct StateInfo *state_info)
+{
+ int i;
+ struct Entry *entry;
+
+ errno = 0;
+ write_val(fd, &state_info->state);
+ write_str(fd, state_info->current_song);
+ write_val(fd, &state_info->song_count);
+ for (i = 0; i < state_info->song_count; i ++) {
+ entry = &state_info->next_songs[i];
+ write_val(fd, &entry->id);
+ write_str(fd, entry->file);
+ }
+
+ return errno ? -1 : 0;
+}
+
+int daemon_start(int sockfd, const struct StreamerOpt *opts)
+{
+ Streamer *streamer;
+ int connfd, new_id;
+ char *url;
+ struct Command msg;
+ struct StateInfo state_info;
+ enum State state;
+
+ if ((streamer = streamer_init(opts)) == NULL) {
+ fprintf(stderr, "Failed to allocate streamer\n");
+ return EXIT_FAILURE;
+ }
+
+ url = NULL;
+ daemon_run = 1;
+ while (daemon_run) {
+ puts("Awaiting connection");
+
+ /* Block and wait for another connection. accept(2) shall returns a
+ * nonnegative file descriptor on sucess, or -1 if an error occured. All
+ * error are considered fatal, except for EINTR, which is seen as an
+ * occasion to re-check whether the daemon should keep running.
+ */
+ if ((connfd = accept(sockfd, NULL, NULL)) < 0) {
+ if (errno == EINTR) continue;
+ perror("Failed to accept connection");
+ break;
+ }
+
+ if (read(connfd, &msg, sizeof(msg)) != sizeof(msg)) {
+ fputs("Unexpected end of package, discarding.\n", stderr);
+ close(connfd);
+ continue;
+ }
+
+ switch (msg.opcode) {
+ case OP_PUSH:
+ if ((url = realloc(url, msg.urllen + 1)) == NULL) {
+ perror("Failed to allocate URL buffer");
+ break;
+ }
+ read(connfd, url, msg.urllen);
+ url[msg.urllen] = '\0';
+
+ new_id = streamer_push(streamer, url, msg.opindex);
+ if (new_id < 0)
+ fprintf(stderr, "Failed to add '%s' to the playlist: %s\n", url, strerror(errno));
+ else
+ printf("Pushed '%s' with id %i\n", url, new_id);
+ break;
+
+ case OP_POP:
+ if (streamer_pop(streamer, msg.opindex))
+ fprintf(stderr, "Failed remove entry #%i\n", msg.opindex);
+ else
+ printf("Succesfullt remove '%i' from the playlist\n", msg.opindex);
+ break;
+
+ case OP_TERM:
+ daemon_run = 0;
+ break;
+ case OP_SKIP:
+ streamer_skip(streamer);
+ break;
+ case OP_PAUSE:
+ streamer_pause(streamer);
+ break;
+ case OP_RESUME:
+ streamer_resume(streamer);
+ break;
+ case OP_LIST:
+ state = streamer_info(streamer, &state_info);
+ if (state == -1) {
+ perror("Failed to reatrieve streamer info");
+ break;
+ }
+
+ if (send_reply(sockfd, &state_info)) {
+ perror("Failed to send back state info");
+ break;
+ }
+ break;
+ default:
+ fprintf(stderr, "Ignoring unknown command code: %i\n", msg.opcode);
+ break;
+ }
+
+ close(connfd);
+ errno = 0;
+ }
+ puts("done\n");
+
+ daemon_run = 0;
+ streamer_free(streamer);
+ return errno ? -1 : -0;
+}
+
+
+// ===================================================================
+// Client code
+// ===================================================================
+
+static inline int send_command(int fd, int code, unsigned int index, int len)
+{
+ struct Command m;
+ m.opcode = code;
+ m.opindex = index;
+ m.urllen = len;
+ return strict_write(fd, &m, 1) ? -1 : 0;
+}
+
+int daemon_push(int fd, const char *track, unsigned int index)
+{
+ int len;
+ char *realtrack;
+
+ if ((realtrack = realpath(track, NULL)) == NULL)
+ return -1;
+
+ len = strlen(realtrack);
+ if (send_command(fd, OP_PUSH, index, len))
+ goto error;
+ if (strict_write(fd, realtrack, len))
+ goto error;
+ free(realtrack);
+ return 0;
+
+error:
+ free(realtrack);
+ return -1;
+}
+
+
+int daemon_pop(int fd, unsigned int index)
+{
+ return send_command(fd, OP_POP, index, 0);
+}
+
+int daemon_skip(int fd)
+{
+ return send_command(fd, OP_SKIP, 0, 0);
+}
+
+int daemon_pause(int fd)
+{
+ return send_command(fd, OP_PAUSE, 0, 0);
+}
+
+int daemon_resume(int fd)
+{
+ return send_command(fd, OP_RESUME, 0, 0);
+}
+
+int daemon_kill(int fd)
+{
+ return send_command(fd, OP_TERM, 0, 0);
+}
+
+int daemon_query(int fd, struct StateInfo *state_info)
+{
+ int i, len;
+ enum State state;
+ struct Entry *entry;
+
+ if (send_command(fd, OP_LIST, 0, 0))
+ return -1;
+
+ errno = 0;
+ read_val(fd, &state_info->state);
+ state_info->current_song = read_str(fd);
+ read_val(fd, &state_info->song_count);
+
+ state_info->next_songs = calloc(state_info->song_count, sizeof(struct Entry));
+ if (state_info->next_songs == NULL) {
+ free(state_info->current_song);
+ return -1;
+ }
+
+ for (i = 0; i < state_info->song_count; i ++) {
+ entry = &state_info->next_songs[i];
+ read_val(fd, &entry->id);
+ entry->file = read_str(fd);
+ }
+
+ if (errno) {
+ stateinfo_free(state_info);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void write_str (int fd, const char *str)
+{
+ size_t len;
+ len = strlen(str) + 1;
+ write(fd, &len, sizeof(size_t));
+ write(fd, str, len);
+}
+
+static char *read_str(int fd)
+{
+ size_t len;
+ char *str;
+
+ read(fd, &len, sizeof(size_t));
+ if ((str = malloc(len)) == NULL)
+ return NULL;
+ read(fd, str, len);
+ return str;
+}