From c1cb78d574c0429aa5e3ff3a2b3886e4bc153212 Mon Sep 17 00:00:00 2001 From: bbergeron Date: Wed, 3 Apr 2024 17:32:01 -0400 Subject: Reset Git repo and use a pseudonym to sign commits I used to sign my commits with my real name and my personal email address, which I wanted scrubbed off the "B." pseudosphere. Re-creating a new git repository was safer than simpler than re-writing the history (although the latter could've also worked but, oh well). --- daemon.c | 273 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 273 insertions(+) create mode 100644 daemon.c (limited to 'daemon.c') 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 +#include +#include +#include +#include +#include +#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; +} -- cgit v1.2.3