struct task task;
};
+extern struct misc_meta_data *mmd;
+
static int server_socket;
static struct command_task command_task_struct;
static struct signal_task signal_task_struct;
+static enum play_mode current_play_mode;
+static char *current_mop; /* mode or playlist specifier. NULL means dummy mooe */
+
/**
* A random number used to "authenticate" the connection.
* \param result Callback result will be stored here.
*
* This function creates a shared memory area, copies the buffer pointed to by
- * \a buf to that area and notifies the afs process that \a f should be
+ * query to that area and notifies the afs process that \a f should be
* called ASAP.
*
* \return Negative, on errors, the return value of the callback function
return ret;
}
-int pass_afd(int fd, char *buf, size_t size)
+static int pass_afd(int fd, char *buf, size_t size)
{
struct msghdr msg = {.msg_iov = NULL};
struct cmsghdr *cmsg;
/* Sum of the length of all control messages in the buffer */
msg.msg_controllen = cmsg->cmsg_len;
- PARA_NOTICE_LOG("passing %zu bytes and fd %d\n", size, fd);
+ PARA_DEBUG_LOG("passing %zu bytes and fd %d\n", size, fd);
ret = sendmsg(server_socket, &msg, 0);
if (ret < 0) {
ret = -ERRNO_TO_PARA_ERROR(errno);
/**
* Open the audio file with highest score.
*
- * \param afd Audio file data is returned here.
- *
- * This stores all information for streaming the "best" audio file
- * in the \a afd structure.
+ * This stores all information for streaming the "best" audio file in a shared
+ * memory area. The id of that area and an open file descriptor for the next
+ * audio file are passed to the server process.
*
- * \return Positive on success, negative on errors.
+ * \return Standard.
*
- * \sa close_audio_file(), open_and_update_audio_file().
+ * \sa open_and_update_audio_file().
*/
int open_next_audio_file(void)
{
struct audio_file_data afd;
int ret, shmid;
char buf[8];
+ long score;
- PARA_NOTICE_LOG("getting next af\n");
- ret = score_get_best(&aft_row, &afd.score);
+ PARA_NOTICE_LOG("getting next audio file\n");
+ ret = score_get_best(&aft_row, &score);
if (ret < 0)
return ret;
- ret = open_and_update_audio_file(aft_row, &afd);
+ ret = open_and_update_audio_file(aft_row, &afd, score);
if (ret < 0)
return ret;
shmid = ret;
- PARA_NOTICE_LOG("shmid: %u\n", shmid);
if (!write_ok(server_socket)) {
PARA_EMERG_LOG("afs_socket not writable\n");
goto destroy;
*(uint32_t *)buf = NEXT_AUDIO_FILE;
*(uint32_t *)(buf + 4) = (uint32_t)shmid;
ret = pass_afd(afd.fd, buf, 8);
+ close(afd.fd);
if (ret >= 0)
return ret;
PARA_ERROR_LOG("%s\n", PARA_STRERROR(-ret));
return ret;
}
-static enum play_mode init_admissible_files(void)
+/* Never fails if arg == NULL */
+static int activate_mood_or_playlist(char *arg, int *num_admissible)
{
- int ret = 0;
- char *arg = conf.afs_initial_mode_arg;
+ enum play_mode mode;
+ int ret;
- if (conf.afs_initial_mode_given) {
+ if (!arg) {
+ ret = change_current_mood(NULL); /* always successful */
+ mode = PLAY_MODE_MOOD;
+ } else {
if (!strncmp(arg, "p:", 2)) {
ret = playlist_open(arg + 2);
- if (ret >= 0)
- return PLAY_MODE_PLAYLIST;
- goto dummy;
- }
- if (!strncmp(arg, "m:", 2)) {
+ mode = PLAY_MODE_PLAYLIST;
+ } else if (!strncmp(arg, "m:", 2)) {
ret = change_current_mood(arg + 2);
- if (ret >= 0)
- return PLAY_MODE_MOOD;
- goto dummy;
+ mode = PLAY_MODE_MOOD;
+ } else
+ ret = -E_AFS_SYNTAX;
+ if (ret < 0)
+ return ret;
+ }
+ if (num_admissible)
+ *num_admissible = ret;
+ current_play_mode = mode;
+ if (arg != current_mop) {
+ free(current_mop);
+ if (arg) {
+ current_mop = para_strdup(arg);
+ mmd_lock();
+ strncpy(mmd->afs_mode_string, arg,
+ sizeof(mmd->afs_mode_string));
+ mmd->afs_mode_string[sizeof(mmd->afs_mode_string) - 1] = '\0';
+ mmd_unlock();
+ } else {
+ mmd_lock();
+ strcpy(mmd->afs_mode_string, "dummy");
+ mmd_unlock();
+ current_mop = NULL;
}
- PARA_ERROR_LOG("bad afs initial mode arg: %s\n", arg);
}
-dummy:
+ return 1;
+}
+
+static int com_select_callback(const struct osl_object *query,
+ struct osl_object *result)
+{
+ struct para_buffer pb = {.buf = NULL};
+ char *arg = query->data;
+ int num_admissible, ret;
+
+ ret = clear_score_table();
if (ret < 0)
- PARA_ERROR_LOG("%s\n", PARA_STRERROR(-ret));
- PARA_NOTICE_LOG("defaulting to dummy mood\n");
- change_current_mood(""); /* always successful */
- return PLAY_MODE_MOOD;
+ return ret;
+ if (current_play_mode == PLAY_MODE_MOOD)
+ close_current_mood();
+ else
+ playlist_close();
+ ret = activate_mood_or_playlist(arg, &num_admissible);
+ if (ret < 0) {
+ para_printf(&pb, "%s\n", PARA_STRERROR(-ret));
+ para_printf(&pb, "switching back to %s\n", current_mop?
+ current_mop : "dummy");
+ ret = activate_mood_or_playlist(current_mop, &num_admissible);
+ if (ret < 0) {
+ para_printf(&pb, "failed, switching to dummy\n");
+ activate_mood_or_playlist(NULL, &num_admissible);
+ }
+ }
+ para_printf(&pb, "activated %s (%d admissible files)\n", current_mop?
+ current_mop : "dummy mood", num_admissible);
+ result->data = pb.buf;
+ result->size = pb.size;
+ return 1;
}
-static int setup_command_socket_or_die(void)
+int com_select(int fd, int argc, char * const * const argv)
{
int ret;
+ struct osl_object query, result;
+
+ if (argc != 2)
+ return -E_AFS_SYNTAX;
+ query.data = argv[1];
+ query.size = strlen(argv[1]) + 1;
+ ret = send_callback_request(com_select_callback, &query,
+ &result);
+ if (ret > 0 && result.data && result.size) {
+ ret = send_va_buffer(fd, "%s", (char *)result.data);
+ free(result.data);
+ }
+ return ret;
+}
+
+static void init_admissible_files(char *arg)
+{
+ if (activate_mood_or_playlist(arg, NULL) < 0)
+ activate_mood_or_playlist(NULL, NULL); /* always successful */
+}
+
+static int setup_command_socket_or_die(void)
+{
+ int ret, socket_fd;
char *socket_name = conf.afs_socket_arg;
struct sockaddr_un unix_addr;
PARA_EMERG_LOG("%s: %s\n", PARA_STRERROR(-ret), socket_name);
exit(EXIT_FAILURE);
}
- if (listen(ret , 5) < 0) {
- PARA_EMERG_LOG("%s", "can not listen on socket\n");
+ socket_fd = ret;
+ if (listen(socket_fd , 5) < 0) {
+ PARA_EMERG_LOG("can not listen on socket\n");
exit(EXIT_FAILURE);
}
- PARA_INFO_LOG("listening on command socket %s (fd %d)\n", socket_name,
- ret);
- return ret;
+ ret = mark_fd_nonblock(socket_fd);
+ if (ret < 0) {
+ close(socket_fd);
+ return ret;
+ }
+ PARA_INFO_LOG("listening on socket %s (fd %d)\n", socket_name, ret);
+ return socket_fd;
}
static void close_afs_tables(void)
int i, ret;
get_database_dir();
+ PARA_NOTICE_LOG("opening %u osl tables in %s\n", NUM_AFS_TABLES,
+ database_dir);
for (i = 0; i < NUM_AFS_TABLES; i++) {
ret = afs_tables[i].open(database_dir);
if (ret >= 0)
continue;
PARA_ERROR_LOG("%s init: %s\n", afs_tables[i].name,
PARA_STRERROR(-ret));
+ break;
}
if (ret >= 0)
return ret;
- do
- afs_tables[i].close();
- while (i--);
+ while (i)
+ afs_tables[--i].close();
return ret;
}
static void signal_post_select(struct sched *s, struct task *t)
{
struct signal_task *st = t->private_data;
+ t->ret = -E_AFS_PARENT_DIED;
+ if (getppid() == 1)
+ goto err;
t->ret = 1;
if (!FD_ISSET(st->fd, &s->rfds))
return;
if (st->signum == SIGHUP) {
close_afs_tables();
t->ret = open_afs_tables();
+ if (t->ret < 0)
+ goto err;
+ init_admissible_files(current_mop);
return;
}
- PARA_NOTICE_LOG("caught signal %d\n", st->signum);
t->ret = -E_AFS_SIGNAL;
+err:
+ PARA_NOTICE_LOG("%s\n", PARA_STRERROR(-t->ret));
unregister_tasks();
}
static struct list_head afs_client_list;
+/** Describes on connected afs client. */
struct afs_client {
+ /** Position in the afs client list. */
struct list_head node;
+ /** The socket file descriptor for this client. */
int fd;
+ /** The time the client connected. */
struct timeval connect_time;
};
return;
}
buf[ret] = '\0';
- PARA_NOTICE_LOG("received: %s\n", buf);
+ PARA_DEBUG_LOG("received: %s\n", buf);
if (!strcmp(buf, "new")) {
ret = open_next_audio_file();
- PARA_NOTICE_LOG("ret: %d\n", ret);
+ if (ret < 0)
+ PARA_ERROR_LOG("%s\n", PARA_STRERROR(-ret));
return;
}
PARA_ERROR_LOG("unknown command\n");
struct command_task *ct = t->private_data;
struct sockaddr_un unix_addr;
struct afs_client *client, *tmp;
-
+ int fd;
if (FD_ISSET(server_socket, &s->rfds))
execute_server_command();
PARA_NOTICE_LOG("%s\n", PARA_STRERROR(-t->ret));
goto out;
}
+ fd = t->ret;
+ t->ret = mark_fd_nonblock(fd);
+ if (t->ret < 0) {
+ PARA_NOTICE_LOG("%s\n", PARA_STRERROR(-t->ret));
+ close(fd);
+ goto out;
+ }
client = para_malloc(sizeof(*client));
- client->fd = t->ret;
+ client->fd = fd;
client->connect_time = *now;
para_list_add(&client->node, &afs_client_list);
out:
*/
__noreturn void afs_init(uint32_t cookie, int socket_fd)
{
- enum play_mode current_play_mode;
struct sched s;
int i, ret;
exit(EXIT_FAILURE);
PARA_INFO_LOG("server_socket: %d, afs_socket_cookie: %u\n",
server_socket, (unsigned) cookie);
- current_play_mode = init_admissible_files();
+ init_admissible_files(conf.afs_initial_mode_arg);
register_tasks(cookie);
s.default_timeout.tv_sec = 0;
- s.default_timeout.tv_usec = 99 * 1000;
+ s.default_timeout.tv_usec = 999 * 1000;
ret = sched(&s);
if (ret < 0)
PARA_EMERG_LOG("%s\n", PARA_STRERROR(-ret));