+/**
+ * Read from stdin, and send the result to the parent process.
+ *
+ * \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.
+ * \param result The result of the query is stored here.
+ *
+ * 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
+ * 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,
+ 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);
+
+ if (ret < 0)
+ return ret;
+ stdin_len = ret;
+ query.size = arg_obj->size + stdin_len;
+ 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);
+ ret = send_callback_request(f, &query, result);
+ free(query.data);
+ return ret;
+}
+
+/**
+ * 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.
+ *
+ * \return Positive on success, negative on errors.
+ *
+ * \sa close_audio_file(), open_and_update_audio_file().
+ */
+int open_next_audio_file(struct audio_file_data *afd)
+{
+ struct osl_row *aft_row;
+ int ret;
+ for (;;) {
+ ret = score_get_best(&aft_row, &afd->score);
+ if (ret < 0)
+ return ret;
+ ret = open_and_update_audio_file(aft_row, afd);
+ if (ret >= 0)
+ return ret;
+ }
+}
+
+/**
+ * Free all resources which were allocated by open_next_audio_file().
+ *
+ * \param afd The structure previously filled in by open_next_audio_file().
+ *
+ * \return The return value of the underlying call to para_munmap().
+ *
+ * \sa open_next_audio_file().
+ */
+int close_audio_file(struct audio_file_data *afd)
+{
+ free(afd->afhi.chunk_table);
+ return para_munmap(afd->map.data, afd->map.size);
+}
+
+#if 0
+static void play_loop(enum play_mode current_play_mode)
+{
+ int i, ret;
+ struct audio_file_data afd;
+
+ afd.current_play_mode = current_play_mode;
+ for (i = 0; i < 0; i++) {
+ ret = open_next_audio_file(&afd);
+ if (ret < 0) {
+ PARA_ERROR_LOG("failed to open next audio file: %d\n", ret);
+ return;
+ }
+ PARA_NOTICE_LOG("next audio file: %s, score: %li\n", afd.path, afd.score);
+ sleep(1);
+ close_audio_file(&afd);
+ }
+}
+#endif
+
+
+static enum play_mode init_admissible_files(void)
+{
+ int ret;
+ char *given_mood, *given_playlist;
+
+ given_mood = "mood_that_was_given_at_the_command_line";
+ given_playlist = "given_playlist";
+
+ if (given_mood) {
+ ret = mood_open(given_mood);
+ if (ret >= 0) {
+ if (given_playlist)
+ PARA_WARNING_LOG("ignoring playlist %s\n",
+ given_playlist);
+ return PLAY_MODE_MOOD;
+ }
+ }
+ if (given_playlist) {
+ ret = playlist_open(given_playlist);
+ if (ret >= 0)
+ return PLAY_MODE_PLAYLIST;
+ }
+ ret = mood_open(NULL); /* open first available mood */
+ if (ret >= 0)
+ return PLAY_MODE_MOOD;
+ mood_open(""); /* open dummy mood, always successful */
+ return PLAY_MODE_MOOD;
+}
+
+int command_socket;
+
+static void setup_command_socket(void)
+{
+ int ret;
+ char *socket_name = "/tmp/afs_command_socket";
+ struct sockaddr_un unix_addr;
+
+ unlink(socket_name);
+ ret = create_local_socket(socket_name, &unix_addr,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IWOTH);
+ if (ret < 0)
+ exit(EXIT_FAILURE);
+ command_socket = ret;
+ if (listen(command_socket , 5) < 0) {
+ PARA_EMERG_LOG("%s", "can not listen on socket\n");
+ exit(EXIT_FAILURE);
+ }
+ PARA_INFO_LOG("listening on command socket %s (fd %d)\n", socket_name,
+ command_socket);
+}
+
+static int server_socket;
+
+void loop(void)
+{
+ for (;;)
+ sleep(1);
+}
+
+static void afs_shutdown(enum osl_close_flags flags)
+{
+ PARA_NOTICE_LOG("cleaning up\n");
+ score_shutdown(flags);
+ attribute_shutdown(flags);
+ mood_close();
+ playlist_close();
+ moods_shutdown(flags);
+ playlists_shutdown(flags);
+ lyrics_shutdown(flags);
+ images_shutdown(flags);
+ aft_shutdown(flags);
+}
+
+static void signal_pre_select(struct sched *s, struct task *t)
+{
+ struct signal_task *st = t->private_data;
+ t->ret = 1;
+ para_fd_set(st->fd, &s->rfds, &s->max_fileno);
+}
+
+static void signal_post_select(struct sched *s, struct task *t)
+{
+ struct signal_task *st = t->private_data;
+ t->ret = 1;
+ 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 */
+ afs_shutdown(OSL_MARK_CLEAN);
+ t->ret = -E_SIGNAL_CAUGHT;
+}
+
+static void register_signal_task(void)
+{
+ static struct signal_task signal_task_struct;
+ struct signal_task *st = &signal_task_struct;
+ st->fd = para_signal_init();
+ PARA_INFO_LOG("signal pipe: fd %d\n", st->fd);
+ para_install_sighandler(SIGINT);
+ para_install_sighandler(SIGTERM);
+ para_install_sighandler(SIGPIPE);
+
+ st->task.pre_select = signal_pre_select;
+ st->task.post_select = signal_post_select;
+ st->task.private_data = st;
+ sprintf(st->task.status, "signal task");
+ register_task(&st->task);
+}
+
+void register_tasks(void)
+{
+ register_signal_task();
+}
+
+__noreturn int afs_init(uint32_t cookie, int socket_fd)
+{
+ int ret;
+// void *shm_area;
+ enum play_mode current_play_mode;
+ struct sched s;
+
+ server_socket = socket_fd;
+ socket_cookie = cookie;
+ PARA_INFO_LOG("server_socket: %d, afs_socket_cookie: %u\n",
+ server_socket, (unsigned) cookie);
+ setup_command_socket();
+
+ ret = attribute_init(&afs_tables[TBLNUM_ATTRIBUTES]);
+ if (ret < 0)
+ goto attribute_init_error;
+ ret = moods_init(&afs_tables[TBLNUM_MOODS]);
+ if (ret < 0)
+ goto moods_init_error;
+ ret = playlists_init(&afs_tables[TBLNUM_PLAYLIST]);
+ if (ret < 0)
+ goto playlists_init_error;
+ ret = lyrics_init(&afs_tables[TBLNUM_LYRICS]);
+ if (ret < 0)
+ goto lyrics_init_error;
+ ret = images_init(&afs_tables[TBLNUM_IMAGES]);
+ if (ret < 0)
+ goto images_init_error;
+ ret = score_init(&afs_tables[TBLNUM_SCORES]);
+ if (ret < 0)
+ goto score_init_error;
+ ret = aft_init(&afs_tables[TBLNUM_AUDIO_FILES]);
+ if (ret < 0)
+ goto aft_init_error;
+
+ current_play_mode = init_admissible_files();
+ register_tasks();
+ s.default_timeout.tv_sec = 0;
+ s.default_timeout.tv_usec = 99 * 1000;
+ sched(&s);
+
+#if 0
+ ret = shm_new(sizeof(struct callback_data));
+ if (ret < 0)
+ return ret;
+ shmid = ret;
+ ret = shm_attach(shmid, ATTACH_RW, &shm_area);
+ if (ret < 0)
+ return ret;
+ shm_callback_data = shm_area;
+ ret = mutex_new();
+ if (ret < 0)
+ return ret;
+ callback_mutex = ret;
+ ret = mutex_new();
+ if (ret < 0)
+ return ret;
+ child_mutex = ret;
+ ret = mutex_new();
+ if (ret < 0)
+ return ret;
+ result_mutex = ret;
+ mutex_lock(result_mutex);
+#endif
+aft_init_error:
+ score_shutdown(OSL_MARK_CLEAN);
+score_init_error:
+ images_shutdown(OSL_MARK_CLEAN);
+images_init_error:
+ lyrics_shutdown(OSL_MARK_CLEAN);
+lyrics_init_error:
+ playlists_shutdown(OSL_MARK_CLEAN);
+playlists_init_error:
+ moods_shutdown(OSL_MARK_CLEAN);
+moods_init_error:
+ attribute_shutdown(OSL_MARK_CLEAN);
+attribute_init_error:
+ exit(EXIT_FAILURE);
+}
+
+static int create_all_tables(void)
+{
+ int i, ret;
+
+ for (i = 0; i < NUM_AFS_TABLES; i++) {
+ struct table_info *ti = afs_tables + i;
+
+ if (ti->flags & TBLFLAG_SKIP_CREATE)