#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; }