X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=blobdiff_plain;f=afs.c;h=f6d6b8d78145829993fd6e817555b56b669fc460;hp=a95ca415de3bb9255305657f6518e171d1724cc8;hb=769888ee3175ed60df3c23caf51cdad5db33d141;hpb=15151d60026dfe17cfaad02284d0abbb8b9389b2 diff --git a/afs.c b/afs.c index a95ca415..f6d6b8d7 100644 --- a/afs.c +++ b/afs.c @@ -6,6 +6,8 @@ /** \file afs.c Paraslash's audio file selector. */ +#include +#include #include "server.cmdline.h" #include "para.h" #include "afh.h" @@ -278,6 +280,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. * @@ -300,40 +338,6 @@ 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 buffer, * but maximal max_size byte. @@ -366,8 +370,9 @@ static int fd2buf(int fd, unsigned max_size, struct osl_object *obj) } /** - * 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. @@ -375,7 +380,7 @@ static int fd2buf(int fd, unsigned max_size, struct osl_object *obj) * * 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. * @@ -475,7 +480,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", @@ -488,10 +493,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; } @@ -532,7 +537,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); @@ -555,10 +560,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(); } @@ -579,11 +584,23 @@ static void register_signal_task(void) register_task(&st->task); } +static struct list_head afs_client_list; + +struct afs_client { + struct list_head node; + int fd; + struct timeval connect_time; +}; + static void command_pre_select(struct sched *s, struct task *t) { struct command_task *ct = t->private_data; - t->ret = 1; + struct afs_client *client; + para_fd_set(ct->fd, &s->rfds, &s->max_fileno); + list_for_each_entry(client, &afs_client_list, node) + para_fd_set(client->fd, &s->rfds, &s->max_fileno); + t->ret = 1; } /* @@ -641,54 +658,75 @@ out: return ret; } -static void command_post_select(struct sched *s, struct task *t) +static void execute_afs_command(int fd, uint32_t expected_cookie) { - struct command_task *ct = t->private_data; - struct sockaddr_un unix_addr; - char buf[sizeof(uint32_t) + sizeof(int)]; uint32_t cookie; - int query_shmid, fd; + int query_shmid; + char buf[sizeof(cookie) + sizeof(query_shmid)]; + int ret = recv_bin_buffer(fd, buf, sizeof(buf)); - t->ret = 1; - if (!FD_ISSET(ct->fd, &s->rfds)) - return; - t->ret = para_accept(ct->fd, &unix_addr, sizeof(unix_addr)); - if (t->ret < 0) + if (ret < 0) { + PARA_NOTICE_LOG("%s\n", PARA_STRERROR(-ret)); return; - /* - * The following errors may be caused by a malicious local user. So do - * not return an error in this case as this would terminate para_afs - * and para_server. - */ - fd = t->ret; - /* FIXME: This is easily dosable (peer doesn't send data) */ - t->ret = recv_bin_buffer(fd, buf, sizeof(buf)); - if (t->ret < 0) { - PARA_NOTICE_LOG("%s (%d)\n", PARA_STRERROR(-t->ret), t->ret); - goto out; } - if (t->ret != sizeof(buf)) { + if (ret != sizeof(buf)) { PARA_NOTICE_LOG("short read (%d bytes, expected %lu)\n", - t->ret, (long unsigned) sizeof(buf)); - goto out; + ret, (long unsigned) sizeof(buf)); + return; } cookie = *(uint32_t *)buf; - if (cookie != ct->cookie) { + if (cookie != expected_cookie) { PARA_NOTICE_LOG("received invalid cookie(got %u, expected %u)\n", - (unsigned)cookie, (unsigned)ct->cookie); - goto out; + (unsigned)cookie, (unsigned)expected_cookie); + return; } query_shmid = *(int *)(buf + sizeof(cookie)); if (query_shmid < 0) { PARA_WARNING_LOG("received invalid query shmid %d)\n", query_shmid); - goto out; + return; } /* Ignore return value: Errors might be ok here. */ call_callback(fd, query_shmid); +} + +#define AFS_CLIENT_TIMEOUT 3 + +static void command_post_select(struct sched *s, struct task *t) +{ + struct command_task *ct = t->private_data; + struct sockaddr_un unix_addr; + struct afs_client *client, *tmp; + + /* First, check the list of connected clients. */ + list_for_each_entry_safe(client, tmp, &afs_client_list, node) { + if (FD_ISSET(client->fd, &s->rfds)) + execute_afs_command(client->fd, ct->cookie); + else { /* prevent bogus connection flodding */ + struct timeval diff; + tv_diff(now, &client->connect_time, &diff); + if (diff.tv_sec < AFS_CLIENT_TIMEOUT) + continue; + PARA_WARNING_LOG("connection timeout\n"); + } + close(client->fd); + list_del(&client->node); + free(client); + } + /* Next, accept connections on the local socket. */ + if (!FD_ISSET(ct->fd, &s->rfds)) + goto out; + t->ret = para_accept(ct->fd, &unix_addr, sizeof(unix_addr)); + if (t->ret < 0) { + PARA_NOTICE_LOG("%s\n", PARA_STRERROR(-t->ret)); + goto out; + } + client = para_malloc(sizeof(*client)); + client->fd = t->ret; + client->connect_time = *now; + para_list_add(&client->node, &afs_client_list); out: t->ret = 1; - close(fd); } static void register_command_task(uint32_t cookie) @@ -783,7 +821,10 @@ __noreturn int afs_init(uint32_t cookie, int socket_fd) { enum play_mode current_play_mode; struct sched s; - int ret = open_afs_tables(); + int ret; + + INIT_LIST_HEAD(&afs_client_list); + ret = open_afs_tables(); if (ret < 0) { PARA_EMERG_LOG("%s\n", PARA_STRERROR(-ret)); @@ -918,5 +959,16 @@ int com_check(int fd, int argc, char * const * const argv) 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; }