README,INSTALL: Fix some spelling errors.
[paraslash.git] / afs.c
diff --git a/afs.c b/afs.c
index 00aab24b745e16eb95cfeead62d4a4a2cdd7e867..f40e02244313054ca08f2066e8bb12b488de2579 100644 (file)
--- a/afs.c
+++ b/afs.c
@@ -6,6 +6,7 @@
 
 /** \file afs.c Paraslash's audio file selector. */
 
+#include <fnmatch.h>
 #include "server.cmdline.h"
 #include "para.h"
 #include "afh.h"
@@ -144,11 +145,12 @@ int send_callback_request(callback_function *f, struct osl_object *query,
        int ret, fd = -1, query_shmid, result_shmid;
        void *query_shm, *result_shm;
        char buf[sizeof(afs_socket_cookie) + sizeof(int)];
-//     char *tmpsocket_name;
        struct sockaddr_un unix_addr;
+       size_t query_shm_size = sizeof(*cq);
 
-       assert(query->data && query->size);
-       ret = shm_new(query->size + sizeof(*cq));
+       if (query)
+               query_shm_size += query->size;
+       ret = shm_new(query_shm_size);
        if (ret < 0)
                return ret;
        query_shmid = ret;
@@ -157,9 +159,10 @@ int send_callback_request(callback_function *f, struct osl_object *query,
                goto out;
        cq = query_shm;
        cq->handler = f;
-       cq->query_size = query->size;
+       cq->query_size = query_shm_size - sizeof(*cq);
 
-       memcpy(query_shm + sizeof(*cq), query->data, query->size);
+       if (query)
+               memcpy(query_shm + sizeof(*cq), query->data, query->size);
        ret = shm_detach(query_shm);
        if (ret < 0)
                goto out;
@@ -174,8 +177,8 @@ int send_callback_request(callback_function *f, struct osl_object *query,
        ret = init_unix_addr(&unix_addr, conf.afs_socket_arg);
        if (ret < 0)
                goto out;
-       ret = -E_CONNECT;
-       if (connect(fd, (struct sockaddr *)&unix_addr, sizeof(unix_addr)) < 0) /* FIXME: Use para_connect() */
+       ret = PARA_CONNECT(fd, &unix_addr);
+       if (ret < 0)
                goto out;
        ret = send_bin_buffer(fd, buf, sizeof(buf));
        if (ret < 0)
@@ -276,6 +279,42 @@ int send_standard_callback_request(int argc,  char * const * const argv,
        return send_option_arg_callback_request(NULL, argc, argv, f, result);
 }
 
+static int action_if_pattern_matches(struct osl_row *row, void *data)
+{
+       struct pattern_match_data *pmd = data;
+       struct osl_object name_obj;
+       const char *p, *name;
+       int ret = osl_get_object(pmd->table, row, pmd->match_col_num, &name_obj);
+       const char *pattern_txt = (const char *)pmd->patterns.data;
+
+       if (ret < 0)
+               return ret;
+       name = (char *)name_obj.data;
+       if ((!name || !*name) && (pmd->pm_flags & PM_SKIP_EMPTY_NAME))
+               return 1;
+       if (!pmd->patterns.size && (pmd->pm_flags & PM_NO_PATTERN_MATCHES_EVERYTHING))
+               return pmd->action(pmd->table, row, name, pmd->data);
+       for (p = pattern_txt; p < pattern_txt + pmd->patterns.size;
+                       p += strlen(p) + 1) {
+               ret = fnmatch(p, name, pmd->fnmatch_flags);
+               if (ret == FNM_NOMATCH)
+                       continue;
+               if (ret)
+                       return -E_FNMATCH;
+               return pmd->action(pmd->table, row, name, pmd->data);
+       }
+       return 1;
+}
+
+int for_each_matching_row(struct pattern_match_data *pmd)
+{
+       if (pmd->pm_flags & PM_REVERSE_LOOP)
+               return osl_rbtree_loop_reverse(pmd->table, pmd->loop_col_num, pmd,
+                       action_if_pattern_matches);
+       return osl_rbtree_loop(pmd->table, pmd->loop_col_num, pmd,
+                       action_if_pattern_matches);
+}
+
 /**
  * Compare two osl objects of string type.
  *
@@ -298,82 +337,41 @@ int string_compare(const struct osl_object *obj1, const struct osl_object *obj2)
        return strncmp(str1, str2, PARA_MIN(obj1->size, obj2->size));
 }
 
-/**
- * A wrapper for strtol(3).
- *
- * \param str The string to be converted to a long integer.
- * \param result The converted value is stored here.
- *
- * \return Positive on success, -E_ATOL on errors.
- *
- * \sa strtol(3), atoi(3).
- */
-int para_atol(const char *str, long *result)
-{
-       char *endptr;
-       long val;
-       int ret, base = 10;
-
-       errno = 0; /* To distinguish success/failure after call */
-       val = strtol(str, &endptr, base);
-       ret = -E_ATOL;
-       if (errno == ERANGE && (val == LONG_MAX || val == LONG_MIN))
-               goto out; /* overflow */
-       if (errno != 0 && val == 0)
-               goto out; /* other error */
-       if (endptr == str)
-               goto out; /* No digits were found */
-       if (*endptr != '\0')
-               goto out; /* Further characters after number */
-       *result = val;
-       ret = 1;
-out:
-       return ret;
-}
-
-
 /*
- * write input from fd to dynamically allocated char array,
- * but maximal max_size byte. Return size.
+ * write input from fd to dynamically allocated buffer,
+ * but maximal max_size byte.
  */
-static int fd2buf(int fd, char **buf, int max_size)
+static int fd2buf(int fd, unsigned max_size, struct osl_object *obj)
 {
        const size_t chunk_size = 1024;
-       size_t size = 2048;
-       char *p;
+       size_t size = 2048, received = 0;
        int ret;
+       char *buf = para_malloc(size);
 
-       *buf = para_malloc(size * sizeof(char));
-       p = *buf;
-       while ((ret = read(fd, p, chunk_size)) > 0) {
-               p += ret;
-               if ((p - *buf) + chunk_size >= size) {
-                       char *tmp;
-
+       for (;;) {
+               ret = recv_bin_buffer(fd, buf + received, chunk_size);
+               if (ret <= 0)
+                       break;
+               received += ret;
+               if (received + chunk_size >= size) {
                        size *= 2;
-                       if (size > max_size) {
-                               ret = -E_INPUT_TOO_LARGE;
-                               goto out;
-                       }
-                       tmp = para_realloc(*buf, size);
-                       p = (p - *buf) + tmp;
-                       *buf = tmp;
+                       ret = -E_INPUT_TOO_LARGE;
+                       if (size > max_size)
+                               break;
+                       buf = para_realloc(buf, size);
                }
        }
-       if (ret < 0) {
-               ret = -E_READ;
-               goto out;
-       }
-       ret = p - *buf;
-out:
-       if (ret < 0 && *buf)
-               free(*buf);
+       obj->data = buf;
+       obj->size = received;
+       if (ret < 0)
+               free(buf);
        return ret;
 }
 
 /**
- * Read from stdin, and send the result to the parent process.
+ * Read data from a file descriptor, and send it to the afs process.
  *
+ * \param fd File descriptor to read data from.
  * \param arg_obj Pointer to the arguments to \a f.
  * \param f The callback function.
  * \param max_len Don't read more than that many bytes from stdin.
@@ -381,29 +379,30 @@ out:
  *
  * This function is used by commands that wish to let para_server store
  * arbitrary data specified by the user (for instance the add_blob family of
- * commands). First, at most \a max_len bytes are read from stdin, the result
+ * commands). First, at most \a max_len bytes are read from \a fd, the result
  * is concatenated with the buffer given by \a arg_obj, and the combined buffer
  * is made available to the parent process via shared memory.
  *
  * \return Negative on errors, the return value of the underlying call to
  * send_callback_request() otherwise.
  */
-int stdin_command(struct osl_object *arg_obj, callback_function *f,
+int stdin_command(int fd, struct osl_object *arg_obj, callback_function *f,
                unsigned max_len, struct osl_object *result)
 {
-       char *stdin_buf;
-       size_t stdin_len;
-       struct osl_object query;
-       int ret = fd2buf(STDIN_FILENO, &stdin_buf, max_len);
+       struct osl_object query, stdin_obj;
+       int ret;
 
+       ret = send_buffer(fd, AWAITING_DATA_MSG);
        if (ret < 0)
                return ret;
-       stdin_len = ret;
-       query.size = arg_obj->size + stdin_len;
+       ret = fd2buf(fd, max_len, &stdin_obj);
+       if (ret < 0)
+               return ret;
+       query.size = arg_obj->size + stdin_obj.size;
        query.data = para_malloc(query.size);
        memcpy(query.data, arg_obj->data, arg_obj->size);
-       memcpy((char *)query.data + arg_obj->size, stdin_buf, stdin_len);
-       free(stdin_buf);
+       memcpy((char *)query.data + arg_obj->size, stdin_obj.data, stdin_obj.size);
+       free(stdin_obj.data);
        ret = send_callback_request(f, &query, result);
        free(query.data);
        return ret;
@@ -480,7 +479,7 @@ static enum play_mode init_admissible_files(void)
        given_playlist = "given_playlist";
 
        if (given_mood) {
-               ret = mood_open(given_mood);
+               ret = change_current_mood(given_mood);
                if (ret >= 0) {
                        if (given_playlist)
                                PARA_WARNING_LOG("ignoring playlist %s\n",
@@ -493,10 +492,10 @@ static enum play_mode init_admissible_files(void)
                if (ret >= 0)
                        return PLAY_MODE_PLAYLIST;
        }
-       ret = mood_open(NULL); /* open first available mood */
+       ret = change_current_mood(NULL); /* open first available mood */
        if (ret >= 0)
                return PLAY_MODE_MOOD;
-       mood_open(""); /* open dummy mood, always successful */
+       change_current_mood(""); /* open dummy mood, always successful */
        return PLAY_MODE_MOOD;
 }
 
@@ -537,7 +536,7 @@ static void close_afs_tables(enum osl_close_flags flags)
        PARA_NOTICE_LOG("closing afs_tables\n");
        score_shutdown(flags);
        attribute_shutdown(flags);
-       mood_close();
+       close_current_mood();
        playlist_close();
        moods_shutdown(flags);
        playlists_shutdown(flags);
@@ -560,10 +559,10 @@ static void signal_post_select(struct sched *s, struct task *t)
        if (!FD_ISSET(st->fd, &s->rfds))
                return;
        st->signum = para_next_signal();
-       PARA_NOTICE_LOG("caught signal %d\n", st->signum);
        t->ret = 1;
        if (st->signum == SIGUSR1)
                return; /* ignore SIGUSR1 */
+       PARA_NOTICE_LOG("caught signal %d\n", st->signum);
        t->ret = -E_SIGNAL_CAUGHT;
        unregister_tasks();
 }
@@ -795,6 +794,9 @@ __noreturn int afs_init(uint32_t cookie, int socket_fd)
                exit(EXIT_FAILURE);
        }
        server_socket = socket_fd;
+       ret = mark_fd_nonblock(server_socket);
+       if (ret < 0)
+               exit(EXIT_FAILURE);
        PARA_INFO_LOG("server_socket: %d, afs_socket_cookie: %u\n",
                server_socket, (unsigned) cookie);
        current_play_mode = init_admissible_files();
@@ -859,3 +861,77 @@ int com_init(int fd, int argc, char * const * const argv)
                return ret;
        return send_va_buffer(fd, "successfully created afs table(s)\n");
 }
+
+enum com_check_flags {
+       CHECK_AFT = 1,
+       CHECK_MOODS = 2,
+       CHECK_PLAYLISTS = 4
+};
+
+int com_check(int fd, int argc, char * const * const argv)
+{
+       unsigned flags = 0;
+       int i, ret;
+       struct osl_object result;
+
+       for (i = 1; i < argc; i++) {
+               const char *arg = argv[i];
+               if (arg[0] != '-')
+                       break;
+               if (!strcmp(arg, "--")) {
+                       i++;
+                       break;
+               }
+               if (!strcmp(arg, "-a")) {
+                       flags |= CHECK_AFT;
+                       continue;
+               }
+               if (!strcmp(arg, "-p")) {
+                       flags |= CHECK_PLAYLISTS;
+                       continue;
+               }
+               if (!strcmp(arg, "-m")) {
+                       flags |= CHECK_MOODS;
+                       continue;
+               }
+               return -E_AFS_SYNTAX;
+       }
+       if (i < argc)
+               return -E_AFS_SYNTAX;
+       if (!flags)
+               flags = ~0U;
+       if (flags & CHECK_AFT) {
+               ret = send_callback_request(aft_check_callback, NULL, &result);
+               if (ret < 0)
+                       return ret;
+               if (ret > 0) {
+                       ret = send_buffer(fd, (char *) result.data);
+                       free(result.data);
+                       if (ret < 0)
+                               return ret;
+               }
+       }
+       if (flags & CHECK_PLAYLISTS) {
+               ret = send_callback_request(playlist_check_callback, NULL, &result);
+               if (ret < 0)
+                       return ret;
+               if (ret > 0) {
+                       ret = send_buffer(fd, (char *) result.data);
+                       free(result.data);
+                       if (ret < 0)
+                               return ret;
+               }
+       }
+       if (flags & CHECK_MOODS) {
+               ret = send_callback_request(mood_check_callback, NULL, &result);
+               if (ret < 0)
+                       return ret;
+               if (ret > 0) {
+                       ret = send_buffer(fd, (char *) result.data);
+                       free(result.data);
+                       if (ret < 0)
+                               return ret;
+               }
+       }
+       return 1;
+}