+/**
+ * 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;
+}
+
+static void para_init_random_seed(void)
+{
+ struct timeval now;
+ unsigned int seed;
+
+ gettimeofday(&now, NULL);
+ seed = now.tv_usec;
+ srand(seed);
+}
+
+/**
+ * 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);
+}
+
+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);
+ }
+}
+
+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;
+}
+
+static int afs_init(void)
+{
+ int ret, shmid;
+ void *shm_area;
+ enum play_mode current_play_mode;
+
+ para_init_random_seed();
+
+ ret = attribute_init(&afs_tables[TBLNUM_ATTRIBUTES]);
+ PARA_DEBUG_LOG("ret %d\n", ret);
+ if (ret < 0)
+ return ret;
+ 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();
+ play_loop(current_play_mode);
+
+ 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);
+ return 1;
+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);
+ return ret;
+}
+
+static uint32_t afs_socket_cookie;
+static int para_random(unsigned max)
+{
+ return ((max + 0.0) * (rand() / (RAND_MAX + 1.0)));
+}
+
+int setup(void)
+{
+ int ret, afs_server_socket[2];
+
+ para_init_random_seed();
+ ret = socketpair(PF_UNIX, SOCK_DGRAM, 0, afs_server_socket);
+ if (ret < 0)
+ exit(EXIT_FAILURE);
+ afs_socket_cookie = para_random((uint32_t)-1);
+ ret = fork();
+ if (ret < 0)
+ exit(EXIT_FAILURE);
+ if (!ret) { /* child (afs) */
+ char *socket_name = "/tmp/afs_command_socket";
+ struct sockaddr_un unix_addr;
+ int fd;
+
+ 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);
+ fd = ret;
+ if (listen(fd , 5) < 0) {
+ PARA_EMERG_LOG("%s", "can not listen on socket\n");
+ exit(EXIT_FAILURE);
+ }
+ ret = afs_init();
+ if (ret < 0)
+ exit(EXIT_FAILURE);
+ PARA_NOTICE_LOG("accepting\n");
+ ret = para_accept(fd, &unix_addr, sizeof(struct sockaddr_un));
+ return ret;
+ }
+ ret = fork();
+ if (ret < 0)
+ exit(EXIT_FAILURE);
+ if (!ret) { /* child (handler) */
+ PARA_NOTICE_LOG("reading stdin\n");
+ for (;;) {
+ char buf[255];
+ read(0, buf, 255);
+ PARA_NOTICE_LOG("read: %s\n", buf);
+ }
+
+ }
+ for (;;) {
+ sleep(10);
+ PARA_NOTICE_LOG("sending next requerst\n");
+ }
+}
+
+
+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)