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). --- main.c | 353 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 353 insertions(+) create mode 100644 main.c (limited to 'main.c') diff --git a/main.c b/main.c new file mode 100644 index 0000000..7899b7b --- /dev/null +++ b/main.c @@ -0,0 +1,353 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "common.h" +#include "daemon.h" + +#define SOCKET_NAME "/cybd.socket" + +#define SUN_LEN(ptr) (offsetof (struct sockaddr_un, sun_path) + strlen ((ptr)->sun_path)) + +static int fd; +static char *socketname; + +static int main_daemon (int fd, int argc, char **argv); +static int main_push (int fd, int argc, char **argv); +static int main_pop (int fd, int argc, char **argv); +static int main_skip (int fd, int argc, char **argv); +static int main_pause (int fd, int argc, char **argv); +static int main_resume (int fd, int argc, char **argv); +static int main_query (int fd, int argc, char **argv); +static int main_kill (int fd, int argc, char **argv); + +static int get_socket_fd (const char *socketname, int do_bind); +static char *get_default_socketname (void); + + +int main(int argc, char **argv) +{ + int opt, err, is_daemon; + char *command; + + socketname = NULL; + while ((opt = getopt(argc, argv, "s:")) != -1) switch (opt) { + case 's': + socketname = strdup(optarg); + break; + default: + goto usage; + } + + if (socketname == NULL) + socketname = get_default_socketname(); + + if ((command = argv[optind ++]) == NULL) { + fprintf(stderr, "%s: missing command\n", argv[0]); + free(socketname); + goto usage; + } + + is_daemon = strcmp(command, "daemon") == 0; + if (is_daemon) unlink(socketname); + if ((fd = get_socket_fd(socketname, is_daemon)) == -1) { + fprintf(stderr, "%s: Failed to bind socket at '%s': %s\n", argv[0], socketname, strerror(errno)); + free(socketname); + return EXIT_FAILURE; + } + + if (is_daemon) + err = main_daemon (fd, argc, argv); + else { + free(socketname); + if (strcmp(command, "push" ) == 0) err = main_push (fd, argc, argv); + else if (strcmp(command, "pop" ) == 0) err = main_pop (fd, argc, argv); + else if (strcmp(command, "skip" ) == 0) err = main_skip (fd, argc, argv); + else if (strcmp(command, "pause" ) == 0) err = main_pause (fd, argc, argv); + else if (strcmp(command, "resume") == 0) err = main_resume (fd, argc, argv); + else if (strcmp(command, "kill" ) == 0) err = main_kill (fd, argc, argv); + else if (strcmp(command, "query" ) == 0) err = main_query (fd, argc, argv); + else { + fprintf(stderr, "%s: %s: Unknown command\n", argv[0], command); + close(fd); + goto usage; + } + } + + close(fd); + return err; + +usage: + fprintf(stderr, "Usage: %s [-s socket] [args...]\n", argv[0]); + return EXIT_FAILURE; +} + +char *get_default_socketname(void) +{ + /* Suggest a default socket name for cybd. This function returns either one + * of the two following suggestions; + * + * 1. If the XDG_RUNTIME_DIR environment is set, we assume the user adhere + * to the XDG directory specification and behave accordingly. + * 2. If not, we use the FHS-specified '/run' directory. + * + * In either case, the socket names is set to ends with SOCKET_NAME and a + * dynamically allocated string is returned. This function may also returns + * NULL if cybd has run out of memory, in which case errno will be set to + * ENOMEM. + */ + char *name, *xdg; + + if ((xdg = getenv("XDG_RUNTIME_DIR"))) { + name = malloc(strlen(xdg) + sizeof(SOCKET_NAME)); + if (name == NULL) return NULL; + + sprintf(name, "%s" SOCKET_NAME, xdg); + return name; + } + + return strdup("/run" SOCKET_NAME); +} + +int get_socket_fd (const char *socketname, int as_daemon) +{ + int fd; + struct sockaddr_un addr; + + // TODO: Re-check the documentation apropos socket name length + if (strlen(socketname) > sizeof(addr.sun_path) - 1) { + fputs("Failed to create socket: filename too long\n", stderr); + return -1; + } + + if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { + perror("Failed to create socket"); + return -1; + } + + addr.sun_family = AF_UNIX; + strcpy(addr.sun_path, socketname); + + if ((as_daemon ? bind : connect)(fd, (struct sockaddr *)&addr, SUN_LEN(&addr))) { + close(fd); + fprintf(stderr, "Failed to open socket at '%s': %s\n", socketname, strerror(errno)); + return -1; + } + + if (as_daemon) + listen(fd, SOMAXCONN); + + return fd; +} + +static int main_push(int fd, int argc, char **argv) +{ + int read; + unsigned int id; + char *file; + + if (optind == argc) { + fprintf(stderr, "%s: Missing input argument\n", argv[0]); + goto usage; + } + file = argv[optind ++]; + + if (argv[optind] != NULL) { + sscanf(argv[optind], "%u%n", &id, &read); + if (read != strlen(argv[optind])) { + fprintf(stderr, "%s: bad index argument: '%u'\n", argv[0], id); + goto usage; + } + optind ++; + } + else id = PLAYLIST_END; + + if (argv[optind] != NULL) { + fprintf(stderr, "%s: Too many arguments\n", argv[0]); + goto usage; + } + + if (daemon_push(fd, file, id)) { + perror("Failed to push entry"); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; + +usage: + fprintf(stderr, "Usage: %s [-s socket] push [index]\n", argv[0]); + return EXIT_FAILURE; +} + +static int main_pop(int fd, int argc, char **argv) +{ + int read; + unsigned int id; + + if (argv[optind] != NULL) { + sscanf(argv[optind], "%u%n", &id, &read); + if (read != strlen(argv[optind])) { + fprintf(stderr, "%s: bad index argument: '%u'\n", argv[0], id); + goto usage; + } + optind ++; + } + else id = PLAYLIST_END; + + if (argv[optind] != NULL) { + fprintf(stderr, "%s: Too many arguments\n", argv[0]); + goto usage; + } + + if (daemon_pop(fd, id)) { + perror("Failed to pop entry"); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; + +usage: + fprintf(stderr, "Usage: %s [-s socket] pop \n", argv[0]); + return EXIT_FAILURE; +} + +static int main_skip(int fd, int argc, char **argv) +{ + if (argv[optind] != NULL) { + fprintf(stderr, "%s: Too many arguments\n", argv[0]); + fprintf(stderr, "Usage: %s [-s socket] skip\n", argv[0]); + return EXIT_FAILURE; + } + if (daemon_skip(fd)) { + perror("Failed to send skip request"); + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} + +static int main_pause(int fd, int argc, char **argv) +{ + if (argv[optind] != NULL) { + fprintf(stderr, "%s: Too many arguments\n", argv[0]); + fprintf(stderr, "Usage: %s [-s socket] pause\n", argv[0]); + return EXIT_FAILURE; + } + if (daemon_pause(fd)) { + perror("Failed to send pause request"); + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} + +static int main_resume(int fd, int argc, char **argv) +{ + if (argv[optind] != NULL) { + fprintf(stderr, "%s: Too many arguments\n", argv[0]); + fprintf(stderr, "Usage: %s [-s socket] resume\n", argv[0]); + return EXIT_FAILURE; + } + if (daemon_resume(fd)) { + perror("Failed to send resume request"); + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} + +static int main_kill(int fd, int argc, char **argv) +{ + if (argv[optind] != NULL) { + fprintf(stderr, "%s: Too many arguments\n", argv[0]); + fprintf(stderr, "Usage: %s [-s socket] kill\n", argv[0]); + return EXIT_FAILURE; + } + if (daemon_kill(fd)) { + perror("Failed to send kill request"); + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} + +static int main_query (int fd, int argc, char **argv) +{ + int i; + struct Entry *entry; + struct StateInfo state_info; + + if (optind != argc) { + fprintf(stderr, "%s: Too many arguments\n", argv[0]); + fprintf(stderr, "Usage: %s [-s socket] query\n", argv[0]); + return -1; + } + + if (daemon_query(fd, &state_info)) { + perror("Failed to query daemon"); + return -1; + } + + switch (state_info.state) { + case STATE_PAUSED: + printf("%s (paused)\n", state_info.current_song); + break; + case STATE_PLAYING: + printf("%s (playing)\n", state_info.current_song); + break; + case STATE_STANDBY: + puts("standby"); + break; + } + + for (i = 0; i < state_info.song_count; i ++) { + entry = &state_info.next_songs[i]; + printf("%u\t%s", entry->id, entry->file); + free(entry->file); + } + + stateinfo_free(&state_info); + return 0; +} + +static void close_socket(int sig) +{ + puts("Signal received, terminating daemon"); + shutdown(fd, SHUT_RDWR); +} + +static int main_daemon (int fd, int argc, char **argv) +{ + time_t t; + int err; + struct tm *tm_info; + struct StreamerOpt opts; + struct sigaction sigact; + + sigact.sa_handler = close_socket; + sigemptyset(&sigact.sa_mask); + sigact.sa_flags = 0; + sigaction(SIGTERM, &sigact, NULL); + sigaction(SIGINT, &sigact, NULL); + + // TODO: Read from args + opts.filename = "./test/stream.m3u8"; + + + time(&t); + tm_info = localtime(&t); + + char buffer[50]; + strftime(buffer, 50, "Daemon started at %Y-%m-%d %H:%M:%S", tm_info); + puts(buffer); + + err = daemon_start(fd, &opts); + unlink(socketname); + free(socketname); + + puts("Goodbye."); + return err ? EXIT_FAILURE : EXIT_SUCCESS; +} -- cgit v1.2.3