]> git.tuebingen.mpg.de Git - paraslash.git/commitdiff
Merge topic branch t/openssl-3 into master
authorAndre Noll <maan@tuebingen.mpg.de>
Thu, 1 Dec 2022 17:08:44 +0000 (18:08 +0100)
committerAndre Noll <maan@tuebingen.mpg.de>
Thu, 1 Dec 2022 17:09:39 +0000 (18:09 +0100)
Two patches. The first suppresses warnings when compiling against
openssl-3, the second switches the two hash functions over to the
EVP API. More work is needed but it does not hurt to merge this first
step now.

* refs/heads/t/openssl-3:
  openssl: Switch to evp API for sha1 and sha256.
  openssl: Deactivate openssl-3 warnings for now.

27 files changed:
Doxyfile
NEWS.md
afs.c
afs.h
aft.c
attribute.c
blob.c
client_common.c
command.c
dccp_recv.c
dccp_send.c
error.h
http_recv.c
m4/lls/mixer.suite.m4
mixer.c
mood.c
mood.h [deleted file]
mp.c
net.c
net.h
para.h
playlist.c
score.c
signal.c
udp_recv.c
udp_send.c
vss.c

index b11683e424443baef8754b34097ad3cb8dbed4fe..e147548f31f090b1fc351a537caa894d3fc87f1f 100644 (file)
--- a/Doxyfile
+++ b/Doxyfile
@@ -2019,8 +2019,7 @@ INCLUDE_FILE_PATTERNS  =
 # recursively expanded use the := operator instead of the = operator.
 # This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
 
-PREDEFINED             = __GNUC__=4 \
-                         __GNUC_MINOR__=4
+PREDEFINED             =
 
 # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
 # tag can be used to specify a list of macro names that should be expanded. The
diff --git a/NEWS.md b/NEWS.md
index 5a00175c3255702b03ec86e0af1938863f53ba4d..fff7a242bba99d3a322b47c0d101d36b93e8d8d5 100644 (file)
--- a/NEWS.md
+++ b/NEWS.md
@@ -1,6 +1,23 @@
 NEWS
 ====
 
+------------------------------------------
+0.7.2 (to be announced) "optical friction"
+------------------------------------------
+
+- A major cleanup of the audio file selector.
+- The client no longer prints error messages from afs commands to
+  stdout but to stderr.
+- The sleep subcommand of para_mixer gained two options to control
+  the startup mood and the time period before fade-out starts. A bunch
+  of further improvements for this subcommand went in as well.
+- Minor cleanup of the net subsystem.
+- The openssl specific code now employs the EVP API to compute hashes.
+  It should compile without warnings against openssl-3.
+
+Downloads:
+[tarball](./releases/paraslash-git.tar.xz)
+
 --------------------------------------
 0.7.1 (2022-10-03) "digital spindrift"
 --------------------------------------
@@ -27,6 +44,7 @@ usual mix of bug fixes and minor improvements not mentioned here.
   requires support from the compiler, the oldest supported gcc version
   has been bumped to gcc-5.4 (released in 2015).
 
+Downloads:
 [tarball](./releases/paraslash-0.7.1.tar.xz),
 [signature](./releases/paraslash-0.7.1.tar.xz.asc)
 
@@ -219,6 +237,23 @@ Downloads:
 [tarball](./releases/paraslash-0.6.1.tar.xz),
 [signature](./releases/paraslash-0.6.1.tar.xz.asc)
 
+---------------------------------------
+0.5.9 (2021-11-04) "reversed dimension"
+---------------------------------------
+This release contains a few important fixes which have accumulated in
+the maint branch. The paraslash-0.5.x series has now reached its end
+of life and will no longer be supported. All users should upgrade to
+a more recent version at this point.
+
+- Fix an issue with the bash completion script.
+- Initialize the random seed also when using libgrypt.
+- Fix some compiler warnings in the resample filter
+- Don't return spurious errors from the ff server command.
+
+Downloads:
+[tarball](./releases/paraslash-0.5.9.tar.bz2),
+[signature](./releases/paraslash-0.5.9.tar.bz2.asc)
+
 ---------------------------------------
 0.5.8 (2017-09-23) "branching parabola"
 ---------------------------------------
diff --git a/afs.c b/afs.c
index 3da39f324b83b2a6d83d1f468732f74cc4313ae2..b15f83852e6919c4f0867065d13776c211416087 100644 (file)
--- a/afs.c
+++ b/afs.c
 #include "sched.h"
 #include "fd.h"
 #include "signal.h"
-#include "mood.h"
 #include "sideband.h"
 #include "command.h"
 
-/** The osl tables used by afs. \sa \ref blob.c. */
-enum afs_table_num {
-       /** Contains audio file information. See \ref aft.c. */
-       TBLNUM_AUDIO_FILES,
-       /** The table for the paraslash attributes. See \ref attribute.c. */
-       TBLNUM_ATTRIBUTES,
-       /*
-        * Moods and playlists organize the current set of admissible files in
-        * an osl table which contains only volatile columns. Each row consists
-        * of a pointer to an audio file and the score value of this file.
-        */
-       TBLNUM_SCORES,
-       /**
-        * A standard blob table containing the mood definitions. For details
-        * see \ref mood.c.
-        */
-       TBLNUM_MOODS,
-       /** A blob table containing lyrics on a per-song basis. */
-       TBLNUM_LYRICS,
-       /** Another blob table for images (for example album cover art). */
-       TBLNUM_IMAGES,
-       /** Yet another blob table for storing standard playlists. */
-       TBLNUM_PLAYLIST,
-       /** How many tables are in use? */
-       NUM_AFS_TABLES
-};
-
-static struct afs_table afs_tables[NUM_AFS_TABLES] = {
-       [TBLNUM_AUDIO_FILES] = {.init = aft_init, .name = "audio_files"},
-       [TBLNUM_ATTRIBUTES] = {.init = attribute_init, .name = "attributes"},
-       [TBLNUM_SCORES] = {.init = score_init, .name = "scores"},
-       [TBLNUM_MOODS] = {.init = moods_init, .name = "moods"},
-       [TBLNUM_LYRICS] = {.init = lyrics_init, .name = "lyrics"},
-       [TBLNUM_IMAGES] = {.init = images_init, .name = "images"},
-       [TBLNUM_PLAYLIST] = {.init = playlists_init, .name = "playlists"},
+/**
+ * The array of tables of the audio file selector.
+ *
+ * We organize them in an array to be able to loop over all tables.
+ */
+static const struct afs_table {
+       /** The name is no table operation, so define it here. */
+       const char * const name;
+       /** The only way to invoke the ops is via this pointer. */
+       const struct afs_table_operations *ops;
+} afs_tables[] = {
+       {.name = "audio_files", .ops = &aft_ops},
+       {.name = "attributes", .ops = &attr_ops},
+       {.name = "scores", .ops = &score_ops},
+       {.name = "moods", .ops = &moods_ops},
+       {.name = "lyrics", .ops = &lyrics_ops},
+       {.name = "images", .ops = &images_ops},
+       {.name = "playlists", .ops = &playlists_ops},
 };
+/** Used to loop over the afs tables. */
+#define NUM_AFS_TABLES ARRAY_SIZE(afs_tables)
 
 struct command_task {
        /** The file descriptor for the local socket. */
@@ -116,7 +100,7 @@ extern uint32_t afs_socket_cookie;
  */
 struct callback_query {
        /** The function to be called. */
-       afs_callback *handler;
+       afs_callback *cb;
        /** The number of bytes of the query */
        size_t query_size;
 };
@@ -207,7 +191,7 @@ int send_callback_request(afs_callback *f, struct osl_object *query,
        if (ret < 0)
                goto out;
        cq = query_shm;
-       cq->handler = f;
+       cq->cb = f;
        cq->query_size = query_shm_size - sizeof(*cq);
 
        if (query)
@@ -447,40 +431,30 @@ no_admissible_files:
        return write_all(server_socket, buf, 8);
 }
 
-static int activate_mood_or_playlist(const char *arg, int *num_admissible,
-               char **errmsg)
+static int activate_mood_or_playlist(const char *arg, struct para_buffer *pb)
 {
        enum play_mode mode;
        int ret;
+       char *msg;
 
        if (!arg) {
+               ret = mood_load(NULL, &msg);
+               mode = PLAY_MODE_MOOD;
+       } else if (!strncmp(arg, "p/", 2)) {
+               ret = playlist_load(arg + 2, &msg);
+               mode = PLAY_MODE_PLAYLIST;
+       } else if (!strncmp(arg, "m/", 2)) {
+               ret = mood_load(arg + 2, &msg);
                mode = PLAY_MODE_MOOD;
-               ret = change_current_mood(NULL, errmsg);
-               if (ret < 0) {
-                       if (num_admissible)
-                               *num_admissible = 0;
-                       return ret;
-               }
        } else {
-               if (!strncmp(arg, "p/", 2)) {
-                       ret = playlist_open(arg + 2);
-                       if (ret < 0 && errmsg)
-                               *errmsg = make_message( "could not open %s",
-                                       arg);
-                       mode = PLAY_MODE_PLAYLIST;
-               } else if (!strncmp(arg, "m/", 2)) {
-                       ret = change_current_mood(arg + 2, errmsg);
-                       mode = PLAY_MODE_MOOD;
-               } else {
-                       if (errmsg)
-                               *errmsg = make_message("%s: parse error", arg);
-                       return -ERRNO_TO_PARA_ERROR(EINVAL);
-               }
-               if (ret < 0)
-                       return ret;
+               ret = -ERRNO_TO_PARA_ERROR(EINVAL);
+               msg = make_message("%s: parse error", arg);
        }
-       if (num_admissible)
-               *num_admissible = ret;
+       if (pb)
+               para_printf(pb, "%s", msg);
+       free(msg);
+       if (ret < 0)
+               return ret;
        current_play_mode = mode;
        /*
         * We get called with arg == current_mop from the signal dispatcher
@@ -490,22 +464,15 @@ static int activate_mood_or_playlist(const char *arg, int *num_admissible,
         */
        if (arg != current_mop) {
                free(current_mop);
-               if (arg) {
-                       current_mop = para_strdup(arg);
-                       mutex_lock(mmd_mutex);
-                       strncpy(mmd->afs_mode_string, arg,
-                               sizeof(mmd->afs_mode_string));
-                       mmd->afs_mode_string[sizeof(mmd->afs_mode_string) - 1] = '\0';
-                       mmd->events++;
-                       mutex_unlock(mmd_mutex);
-               } else {
-                       mutex_lock(mmd_mutex);
-                       strcpy(mmd->afs_mode_string, "dummy");
-                       mmd->events++;
-                       mutex_unlock(mmd_mutex);
-                       current_mop = NULL;
-               }
+               current_mop = arg? para_strdup(arg) : NULL;
        }
+       /* Notify the server about the mood/playlist change. */
+       mutex_lock(mmd_mutex);
+       strncpy(mmd->afs_mode_string, arg? arg: "dummy",
+               sizeof(mmd->afs_mode_string));
+       mmd->afs_mode_string[sizeof(mmd->afs_mode_string) - 1] = '\0';
+       mmd->events++;
+       mutex_unlock(mmd_mutex);
        return 1;
 }
 
@@ -558,77 +525,14 @@ static void flush_and_free_pb(struct para_buffer *pb)
        free(pb->buf);
 }
 
-static int com_select_callback(struct afs_callback_arg *aca)
-{
-       const struct lls_command *cmd = SERVER_CMD_CMD_PTR(SELECT);
-       const char *arg;
-       int num_admissible, ret;
-       char *errmsg;
-
-       ret = lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr);
-       assert(ret >= 0);
-       arg = lls_input(0, aca->lpr);
-       ret = clear_score_table();
-       if (ret < 0) {
-               para_printf(&aca->pbout, "could not clear score table\n");
-               goto free_lpr;
-       }
-       if (current_play_mode == PLAY_MODE_MOOD)
-               close_current_mood();
-       else
-               playlist_close();
-       ret = activate_mood_or_playlist(arg, &num_admissible, &errmsg);
-       if (ret >= 0)
-               goto out;
-       /* ignore subsequent errors (but log them) */
-       para_printf(&aca->pbout, "%s\n", errmsg);
-       free(errmsg);
-       para_printf(&aca->pbout, "could not activate %s\n", arg);
-       if (current_mop && strcmp(current_mop, arg) != 0) {
-               int ret2;
-               para_printf(&aca->pbout, "switching back to %s\n", current_mop);
-               ret2 = activate_mood_or_playlist(current_mop, &num_admissible,
-                       &errmsg);
-               if (ret2 >= 0)
-                       goto out;
-               para_printf(&aca->pbout, "%s\n", errmsg);
-               free(errmsg);
-               para_printf(&aca->pbout, "could not reactivate %s: %s\n",
-                       current_mop, para_strerror(-ret2));
-       }
-       para_printf(&aca->pbout, "activating dummy mood\n");
-       activate_mood_or_playlist(NULL, &num_admissible, NULL);
-out:
-       para_printf(&aca->pbout, "activated %s (%d admissible file%s)\n",
-               current_mop? current_mop : "dummy mood", num_admissible,
-                       num_admissible == 1? "" : "s");
-free_lpr:
-       lls_free_parse_result(aca->lpr, cmd);
-       return ret;
-}
-
-static int com_select(struct command_context *cc, struct lls_parse_result *lpr)
-{
-       const struct lls_command *cmd = SERVER_CMD_CMD_PTR(SELECT);
-       char *errctx;
-       int ret = lls(lls_check_arg_count(lpr, 1, 1, &errctx));
-
-       if (ret < 0) {
-               send_errctx(cc, errctx);
-               return ret;
-       }
-       return send_lls_callback_request(com_select_callback, cmd, lpr, cc);
-}
-EXPORT_SERVER_CMD_HANDLER(select);
-
 static void init_admissible_files(const char *arg)
 {
-       int ret = activate_mood_or_playlist(arg, NULL, NULL);
+       int ret = activate_mood_or_playlist(arg, NULL);
        if (ret < 0) {
                PARA_WARNING_LOG("could not activate %s: %s\n", arg,
                        para_strerror(-ret));
                if (arg)
-                       activate_mood_or_playlist(NULL, NULL, NULL);
+                       activate_mood_or_playlist(NULL, NULL);
        }
 }
 
@@ -656,7 +560,7 @@ static void close_afs_tables(void)
        int i;
        PARA_NOTICE_LOG("closing afs tables\n");
        for (i = 0; i < NUM_AFS_TABLES; i++)
-               afs_tables[i].close();
+               afs_tables[i].ops->close();
        free(database_dir);
        database_dir = NULL;
 }
@@ -692,10 +596,10 @@ static int open_afs_tables(void)
        int i, ret;
 
        get_database_dir();
-       PARA_NOTICE_LOG("opening %d osl tables in %s\n", NUM_AFS_TABLES,
+       PARA_NOTICE_LOG("opening %zu 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);
+               ret = afs_tables[i].ops->open(database_dir);
                if (ret >= 0)
                        continue;
                PARA_ERROR_LOG("could not open %s\n", afs_tables[i].name);
@@ -704,7 +608,7 @@ static int open_afs_tables(void)
        if (ret >= 0)
                return ret;
        while (i)
-               afs_tables[--i].close();
+               afs_tables[--i].ops->close();
        return ret;
 }
 
@@ -824,6 +728,43 @@ err:
        return ret;
 }
 
+/**
+ * Format and send an error message to the command handler.
+ *
+ * To pass an error message from the callback of an afs command to the client,
+ * this function should be called. It formats the message into a buffer which
+ * is passed as a shared memory area to the command handler from where it
+ * propagates to the client.
+ *
+ * The message will be tagged with the ERROR_LOG sideband designator so that
+ * the client writes it to its stderr stream rather than to stdout as with
+ * aca->pbout. In analogy to the default Unix semantics of stderr, the message
+ * is sent without buffering.
+ *
+ * If sending the error message fails, an error is logged on the server side,
+ * but no other action is taken.
+ *
+ * \param aca Used to obtain the fd to send the shmid to.
+ * \param fmt Usual format string.
+ */
+__printf_2_3 void afs_error(const struct afs_callback_arg *aca,
+               const char *fmt,...)
+{
+       va_list argp;
+       char *msg;
+       unsigned n;
+       int ret;
+
+       va_start(argp, fmt);
+       n = xvasprintf(&msg, fmt, argp);
+       va_end(argp);
+       ret = pass_buffer_as_shm(aca->fd, SBD_ERROR_LOG, msg, n + 1);
+       if (ret < 0)
+               PARA_ERROR_LOG("Could not send %s: %s\n", msg,
+                       para_strerror(-ret));
+       free(msg);
+}
+
 static int call_callback(int fd, int query_shmid)
 {
        void *query_shm;
@@ -843,7 +784,7 @@ static int call_callback(int fd, int query_shmid)
                .fd = fd,
                .band = SBD_OUTPUT
        };
-       ret = cq->handler(&aca);
+       ret = cq->cb(&aca);
        ret2 = shm_detach(query_shm);
        if (ret2 < 0) {
                if (ret < 0) /* ignore (but log) detach error */
@@ -996,12 +937,10 @@ static int afs_poll(struct pollfd *fds, nfds_t nfds, int timeout)
 __noreturn void afs_init(int socket_fd)
 {
        static struct sched s;
-       int i, ret;
+       int ret;
 
        register_signal_task(&s);
        init_list_head(&afs_client_list);
-       for (i = 0; i < NUM_AFS_TABLES; i++)
-               afs_tables[i].init(&afs_tables[i]);
        ret = open_afs_tables();
        if (ret < 0)
                goto out;
@@ -1023,7 +962,7 @@ __noreturn void afs_init(int socket_fd)
        }
        ret = schedule(&s);
        sched_shutdown(&s);
-       close_current_mood();
+       mood_unload();
 out_close:
        close_afs_tables();
 out:
@@ -1036,6 +975,53 @@ out:
        exit(EXIT_FAILURE);
 }
 
+static int com_select_callback(struct afs_callback_arg *aca)
+{
+       const struct lls_command *cmd = SERVER_CMD_CMD_PTR(SELECT);
+       const char *arg;
+       int ret;
+
+       ret = lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr);
+       assert(ret >= 0);
+       arg = lls_input(0, aca->lpr);
+       score_clear();
+       if (current_play_mode == PLAY_MODE_MOOD)
+               mood_unload();
+       else
+               playlist_unload();
+       ret = activate_mood_or_playlist(arg, &aca->pbout);
+       if (ret >= 0)
+               goto free_lpr;
+       /* ignore subsequent errors (but log them) */
+       if (current_mop && strcmp(current_mop, arg) != 0) {
+               int ret2;
+               afs_error(aca, "switching back to %s\n", current_mop);
+               ret2 = activate_mood_or_playlist(current_mop, &aca->pbout);
+               if (ret2 >= 0)
+                       goto free_lpr;
+               afs_error(aca, "could not reactivate %s: %s\n", current_mop,
+                       para_strerror(-ret2));
+       }
+       activate_mood_or_playlist(NULL, &aca->pbout);
+free_lpr:
+       lls_free_parse_result(aca->lpr, cmd);
+       return ret;
+}
+
+static int com_select(struct command_context *cc, struct lls_parse_result *lpr)
+{
+       const struct lls_command *cmd = SERVER_CMD_CMD_PTR(SELECT);
+       char *errctx;
+       int ret = lls(lls_check_arg_count(lpr, 1, 1, &errctx));
+
+       if (ret < 0) {
+               send_errctx(cc, errctx);
+               return ret;
+       }
+       return send_lls_callback_request(com_select_callback, cmd, lpr, cc);
+}
+EXPORT_SERVER_CMD_HANDLER(select);
+
 static int com_init_callback(struct afs_callback_arg *aca)
 {
        uint32_t table_mask = *(uint32_t *)aca->query.data;
@@ -1044,16 +1030,15 @@ static int com_init_callback(struct afs_callback_arg *aca)
        close_afs_tables();
        get_database_dir();
        for (i = 0; i < NUM_AFS_TABLES; i++) {
-               struct afs_table *t = &afs_tables[i];
+               const struct afs_table *t = afs_tables + i;
 
                if (!(table_mask & (1 << i)))
                        continue;
-               if (!t->create)
+               if (!t->ops->create)
                        continue;
-               ret = t->create(database_dir);
+               ret = t->ops->create(database_dir);
                if (ret < 0) {
-                       para_printf(&aca->pbout, "cannot create table %s\n",
-                               t->name);
+                       afs_error(aca, "cannot create table %s\n", t->name);
                        goto out;
                }
                para_printf(&aca->pbout, "successfully created %s table\n",
@@ -1061,7 +1046,7 @@ static int com_init_callback(struct afs_callback_arg *aca)
        }
        ret = open_afs_tables();
        if (ret < 0)
-               para_printf(&aca->pbout, "cannot open afs tables: %s\n",
+               afs_error(aca, "cannot open afs tables: %s\n",
                        para_strerror(-ret));
 out:
        return ret;
@@ -1082,7 +1067,7 @@ static int com_init(struct command_context *cc, struct lls_parse_result *lpr)
                table_mask = 0;
                for (i = 0; i < num_inputs; i++) {
                        for (j = 0; j < NUM_AFS_TABLES; j++) {
-                               struct afs_table *t = &afs_tables[j];
+                               const struct afs_table *t = afs_tables + j;
 
                                if (strcmp(lls_input(i, lpr), t->name))
                                        continue;
@@ -1156,10 +1141,10 @@ __must_check int afs_event(enum afs_events event, struct para_buffer *pb,
        int i, ret;
 
        for (i = 0; i < NUM_AFS_TABLES; i++) {
-               struct afs_table *t = &afs_tables[i];
-               if (!t->event_handler)
+               const struct afs_table *t = afs_tables + i;
+               if (!t->ops->event_handler)
                        continue;
-               ret = t->event_handler(event, pb, data);
+               ret = t->ops->event_handler(event, pb, data);
                if (ret < 0) {
                        PARA_CRIT_LOG("table %s, event %u: %s\n", t->name,
                                event, para_strerror(-ret));
diff --git a/afs.h b/afs.h
index b1606493a05f047afb1e8ecb0fdd0a5bbcbafcfe..9a1d7d9ca0f130c4590f55c23abc181b632c6483 100644 (file)
--- a/afs.h
+++ b/afs.h
@@ -73,19 +73,15 @@ struct afsi_change_event_data {
        struct afs_info *old_afsi;
 };
 
-/** Function pointers for table handling.  */
-struct afs_table {
-       /** Initializes the other pointers in this struct. */
-       void (*init)(struct afs_table *t);
-       /** The name of this table. */
-       const char *name;
-       /** Gets called on startup and on \p SIGHUP. */
+/** Methods for table startup/shutdown and event handling. */
+struct afs_table_operations {
+       /** Gets called on startup and on SIGHUP. */
        int (*open)(const char *base_dir);
-       /** Gets called on shutdown and on \p SIGHUP. */
+       /** Gets called on shutdown and on SIGHUP. */
        void (*close)(void);
-       /** Called by the \a init afs command. */
+       /** Called from the init command. */
        int (*create)(const char *);
-       /** Handles afs events. */
+       /** Handle events generated by other tables. See enum \ref afs_events. */
        int (*event_handler)(enum afs_events event, struct para_buffer *pb,
                void *data);
 };
@@ -173,6 +169,8 @@ struct afs_callback_arg {
 };
 
 /**
+ * The "top half" of an afs command.
+ *
  * Afs command handlers run as a process which is not related to the afs
  * process, i.e. they can not change the address space of afs directly.
  * Therefore afs commands typically consist of two functions: The command
@@ -183,9 +181,13 @@ struct afs_callback_arg {
 typedef int afs_callback(struct afs_callback_arg *aca);
 
 /**
+ * Dispatch the output of an afs callback.
+ *
  * Some AFS callbacks need to send data back to the command handler. Pointers
  * to this type of function are passed to \ref send_callback_request() and
- * related functions to dispatch the data in the command handler process.
+ * related functions to dispatch the data in the command handler process. Most
+ * (but not all) afs commands pass \ref afs_cb_result_handler(), which sends
+ * the output of the callback to the connected client.
  */
 typedef int callback_result_handler(struct osl_object *result, uint8_t band, void *private);
 int afs_cb_result_handler(struct osl_object *result, uint8_t band, void *private);
@@ -229,30 +231,31 @@ int send_callback_request(afs_callback *f, struct osl_object *query,
 int send_lls_callback_request(afs_callback *f,
                const struct lls_command * const cmd,
                struct lls_parse_result *lpr, void *private_result_data);
+__printf_2_3 void afs_error(const struct afs_callback_arg *aca,
+               const char *fmt,...);
 int string_compare(const struct osl_object *obj1, const struct osl_object *obj2);
 int for_each_matching_row(struct pattern_match_data *pmd);
 
 /* score */
-void score_init(struct afs_table *t);
-int admissible_file_loop(void *data, osl_rbtree_loop_func *func);
+extern const struct afs_table_operations score_ops;
+int score_loop(osl_rbtree_loop_func *func, void *data);
 int score_get_best(struct osl_row **aft_row, long *score);
 int get_score_and_aft_row(struct osl_row *score_row, long *score, struct osl_row **aft_row);
 int score_add(const struct osl_row *row, long score);
 int score_update(const struct osl_row *aft_row, long new_score);
-int get_num_admissible_files(unsigned *num);
 int score_delete(const struct osl_row *aft_row);
-int clear_score_table(void);
-int row_belongs_to_score_table(const struct osl_row *aft_row, unsigned *rank);
+void score_clear(void);
+bool row_belongs_to_score_table(const struct osl_row *aft_row);
 
 /* attribute */
-void attribute_init(struct afs_table *t);
+extern const struct afs_table_operations attr_ops;
 void get_attribute_bitmap(const uint64_t *atts, char *buf); /* needed by com_ls() */
 int get_attribute_bitnum_by_name(const char *att_name, unsigned char *bitnum);
 int get_attribute_text(uint64_t *atts, const char *delim, char **text);
 int attribute_check_callback(struct afs_callback_arg *aca);
 
 /* aft */
-void aft_init(struct afs_table *t);
+extern const struct afs_table_operations aft_ops;
 int aft_get_row_of_path(const char *path, struct osl_row **row);
 int aft_check_attributes(uint64_t att_mask, struct para_buffer *pb);
 int open_and_update_audio_file(int *fd);
@@ -264,9 +267,14 @@ int audio_file_loop(void *private_data, osl_rbtree_loop_func *func);
 int aft_check_callback(struct afs_callback_arg *aca);
 void free_status_items(void);
 
+/* mood */
+int mood_load(const char *mood_name, char **msg);
+void mood_unload(void);
+int mood_check_callback(struct afs_callback_arg *aca);
+
 /* playlist */
-int playlist_open(const char *name);
-void playlist_close(void);
+int playlist_load(const char *name, char **msg);
+void playlist_unload(void);
 int playlist_check_callback(struct afs_callback_arg *aca);
 
 /** evaluates to 1 if x < y, to -1 if x > y and to 0 if x == y */
@@ -275,15 +283,15 @@ int playlist_check_callback(struct afs_callback_arg *aca);
 
 /** Define exported functions and a table pointer for an osl blob table. */
 #define DECLARE_BLOB_SYMBOLS(table_name, cmd_prefix) \
-       void table_name ## _init(struct afs_table *t); \
        int cmd_prefix ## _get_name_by_id(uint32_t id, char **name); \
        int cmd_prefix ## _get_def_by_id(uint32_t id, struct osl_object *def); \
-       int cmd_prefix ## _get_def_by_name(char *name, struct osl_object *def); \
+       int cmd_prefix ## _get_def_by_name(const char *name, struct osl_object *def); \
        int cmd_prefix ## _get_name_and_def_by_row(const struct osl_row *row, \
                char **name, struct osl_object *def); \
        int table_name ##_event_handler(enum afs_events event, \
                struct para_buffer *pb, void *data); \
-       extern struct osl_table *table_name ## _table;
+       extern struct osl_table *table_name ## _table; \
+       extern const struct afs_table_operations table_name ## _ops;
 
 /** \cond blob_symbols */
 DECLARE_BLOB_SYMBOLS(lyrics, lyr);
diff --git a/aft.c b/aft.c
index f690e81951b4c7911c2f726b36fee9a673badce7..0009a54f3c5b1f169381ff631f20b141da71f75b 100644 (file)
--- a/aft.c
+++ b/aft.c
@@ -1375,7 +1375,7 @@ static int com_ls_callback(struct afs_callback_arg *aca)
 
        aca->pbout.flags = (opts->mode == LS_MODE_PARSER)? PBF_SIZE_PREFIX : 0;
        if (admissible_only(opts))
-               ret = admissible_file_loop(opts, prepare_ls_row);
+               ret = score_loop(prepare_ls_row, opts);
        else
                ret = osl(osl_rbtree_loop(audio_file_table, AFTCOL_PATH, opts,
                        prepare_ls_row));
@@ -1761,7 +1761,7 @@ static int com_add_callback(struct afs_callback_arg *aca)
        ret = afs_event(AUDIO_FILE_ADD, &aca->pbout, aft_row);
 out:
        if (ret < 0)
-               para_printf(&aca->pbout, "could not add %s\n", path);
+               afs_error(aca, "could not add %s\n", path);
        lls_free_parse_result(aca->lpr, cmd);
        return ret;
 }
@@ -1979,12 +1979,12 @@ static int touch_audio_file(__a_unused struct osl_table *table,
 
        ret = get_afsi_object_of_row(row, &obj);
        if (ret < 0) {
-               para_printf(&aca->pbout, "cannot touch %s\n", name);
+               afs_error(aca, "cannot touch %s\n", name);
                return ret;
        }
        ret = load_afsi(&old_afsi, &obj);
        if (ret < 0) {
-               para_printf(&aca->pbout, "cannot touch %s\n", name);
+               afs_error(aca, "cannot touch %s\n", name);
                return ret;
        }
        new_afsi = old_afsi;
@@ -2038,7 +2038,7 @@ static int com_touch_callback(struct afs_callback_arg *aca)
                uint32_t id = lls_uint32_val(0, r_i);
                ret = img_get_name_by_id(id, NULL);
                if (ret < 0) {
-                       para_printf(&aca->pbout, "invalid image ID: %u\n", id);
+                       afs_error(aca, "invalid image ID: %u\n", id);
                        return ret;
                }
        }
@@ -2047,7 +2047,7 @@ static int com_touch_callback(struct afs_callback_arg *aca)
                uint32_t id = lls_uint32_val(0, r_y);
                ret = lyr_get_name_by_id(id, NULL);
                if (ret < 0) {
-                       para_printf(&aca->pbout, "invalid lyrics ID: %u\n", id);
+                       afs_error(aca, "invalid lyrics ID: %u\n", id);
                        return ret;
                }
        }
@@ -2090,7 +2090,7 @@ static int remove_audio_file(__a_unused struct osl_table *table,
                return ret;
        ret = osl(osl_del_row(audio_file_table, row));
        if (ret < 0)
-               para_printf(&aca->pbout, "cannot remove %s\n", name);
+               afs_error(aca, "cannot remove %s\n", name);
        return ret;
 }
 
@@ -2326,7 +2326,7 @@ static int com_setatt_callback(struct afs_callback_arg *aca)
                ret = get_attribute_bitnum_by_name(p, &bitnum);
                free(p);
                if (ret < 0) {
-                       para_printf(&aca->pbout, "invalid argument: %s\n", arg);
+                       afs_error(aca, "invalid argument: %s\n", arg);
                        goto out;
                }
                if (c == '+')
@@ -2648,15 +2648,10 @@ static int aft_event_handler(enum afs_events event, struct para_buffer *pb,
        }
 }
 
-/**
- * Initialize the audio file table.
- *
- * \param t Pointer to the structure to be initialized.
- */
-void aft_init(struct afs_table *t)
-{
-       t->open = aft_open;
-       t->close = aft_close;
-       t->create = aft_create;
-       t->event_handler = aft_event_handler;
-}
+/** The audio file table contains information about known audio files. */
+const struct afs_table_operations aft_ops = {
+       .open = aft_open,
+       .close = aft_close,
+       .create = aft_create,
+       .event_handler = aft_event_handler,
+};
index fb1b3eac3482f1c0725e0e79a4b80166be511350..51630b257f7d88829c657361b06c1fd09d008326 100644 (file)
@@ -119,7 +119,7 @@ static int print_attribute(struct osl_table *table, struct osl_row *row,
        }
        ret = osl(osl_get_object(table, row, ATTCOL_BITNUM, &bitnum_obj));
        if (ret < 0) {
-               para_printf(&aca->pbout, "%s: %s\n", name, para_strerror(-ret));
+               afs_error(aca, "%s: %s\n", name, para_strerror(-ret));
                return ret;
        }
        para_printf(&aca->pbout, "%u\t%s\n", *(unsigned char*)bitnum_obj.data,
@@ -168,11 +168,6 @@ static int com_lsatt(struct command_context *cc, struct lls_parse_result *lpr)
 }
 EXPORT_SERVER_CMD_HANDLER(lsatt);
 
-struct addatt_event_data {
-       const char *name;
-       unsigned char bitnum;
-};
-
 static int com_addatt_callback(struct afs_callback_arg *aca)
 {
        const struct lls_command *cmd = SERVER_CMD_CMD_PTR(ADDATT);
@@ -188,12 +183,10 @@ static int com_addatt_callback(struct afs_callback_arg *aca)
                struct osl_object objs[NUM_ATT_COLUMNS];
                struct osl_row *row;
                unsigned char bitnum;
-               struct addatt_event_data aed;
 
                len = strlen(name);
                if (len == 0 || name[len - 1] == '-' || name[len - 1] == '+') {
-                       para_printf(&aca->pbout,
-                               "invalid attribute name: %s\n", name);
+                       afs_error(aca, "invalid attribute name: %s\n", name);
                        continue;
                }
                ret = get_attribute_bitnum_by_name(name, &bitnum);
@@ -225,16 +218,14 @@ static int com_addatt_callback(struct afs_callback_arg *aca)
                ret = osl(osl_add_row(attribute_table, objs));
                if (ret < 0)
                        goto out;
-               aed.name = name;
-               aed.bitnum = bitnum;
-               ret = afs_event(ATTRIBUTE_ADD, &aca->pbout, &aed);
+               ret = afs_event(ATTRIBUTE_ADD, &aca->pbout, NULL);
                if (ret < 0)
                        goto out;
                greatest_att_bitnum = PARA_MAX(greatest_att_bitnum, (int)bitnum);
        }
 out:
        if (ret < 0)
-               para_printf(&aca->pbout, "error while adding %s\n",
+               afs_error(aca, "error while adding %s\n",
                        lls_input(i, aca->lpr));
        lls_free_parse_result(aca->lpr, cmd);
        return ret;
@@ -277,7 +268,7 @@ static int com_mvatt_callback(struct afs_callback_arg *aca)
        ret = osl(osl_update_object(attribute_table, row, ATTCOL_NAME, &obj));
 out:
        if (ret < 0)
-               para_printf(&aca->pbout, "cannot rename %s to %s\n", old, new);
+               afs_error(aca, "cannot rename %s to %s\n", old, new);
        else
                ret = afs_event(ATTRIBUTE_RENAME, &aca->pbout, NULL);
        lls_free_parse_result(aca->lpr, cmd);
@@ -306,13 +297,13 @@ static int remove_attribute(struct osl_table *table, struct osl_row *row,
 
        ret = get_attribute_bitnum_by_name(name, &red.bitnum);
        if (ret < 0) {
-               para_printf(&aca->pbout, "cannot remove %s\n", name);
+               afs_error(aca, "cannot remove %s\n", name);
                return ret;
        }
        para_printf(&aca->pbout, "removing attribute %s\n", name);
        ret = osl(osl_del_row(table, row));
        if (ret < 0) {
-               para_printf(&aca->pbout, "cannot remove %s\n", name);
+               afs_error(aca, "cannot remove %s\n", name);
                return ret;
        }
        return afs_event(ATTRIBUTE_REMOVE, &aca->pbout, &red);
@@ -499,14 +490,9 @@ static int attribute_create(const char *dir)
        return osl(osl_create_table(&attribute_table_desc));
 }
 
-/**
- * Initialize the attribute table structure.
- *
- * \param t The table structure to initialize.
- */
-void attribute_init(struct afs_table *t)
-{
-       t->open = attribute_open;
-       t->close = attribute_close;
-       t->create = attribute_create;
-}
+/** The attribute table stores name/bitnum pairs. */
+const struct afs_table_operations attr_ops = { /* no event handler */
+       .open = attribute_open,
+       .close = attribute_close,
+       .create = attribute_create,
+};
diff --git a/blob.c b/blob.c
index b63724014138513dfb89d4300cb38056bfaa601a..1802de5d31f89bf6fe951c5977cbef4967f9c11e 100644 (file)
--- a/blob.c
+++ b/blob.c
@@ -100,7 +100,7 @@ static int print_blob(struct osl_table *table, struct osl_row *row,
        }
        ret = osl(osl_get_object(table, row, BLOBCOL_ID, &obj));
        if (ret < 0) {
-               para_printf(&aca->pbout, "cannot list %s\n", name);
+               afs_error(aca, "cannot list %s\n", name);
                return ret;
        }
        id = read_u32(obj.data);
@@ -210,7 +210,7 @@ static int remove_blob(struct osl_table *table, struct osl_row *row,
        int ret = osl(osl_del_row(table, row));
 
        if (ret < 0) {
-               para_printf(&aca->pbout, "cannot remove %s\n", name);
+               afs_error(aca, "cannot remove %s\n", name);
                return ret;
        }
        return 1;
@@ -338,7 +338,7 @@ static int com_addblob_callback(__a_unused const struct lls_command * const cmd,
        ret = afs_event(BLOB_ADD, NULL, table);
 out:
        if (ret < 0)
-               para_printf(&aca->pbout, "cannot add %s\n", name);
+               afs_error(aca, "cannot add %s\n", name);
        else
                para_printf(&aca->pbout, "added %s as id %u\n", name, id);
        return ret;
@@ -446,15 +446,14 @@ static int com_mvblob_callback(const struct lls_command * const cmd,
        ret = osl(osl_get_row(table, BLOBCOL_NAME, &obj, &row));
 
        if (ret < 0) {
-               para_printf(&aca->pbout, "cannot find source blob %s\n", src);
+               afs_error(aca, "cannot find source blob %s\n", src);
                goto out;
        }
        obj.data = (char *)dest;
        obj.size = strlen(dest) + 1;
        ret = osl(osl_update_object(table, row, BLOBCOL_NAME, &obj));
        if (ret < 0) {
-               para_printf(&aca->pbout, "cannot rename blob %s to %s\n",
-                       src, dest);
+               afs_error(aca, "cannot rename blob %s to %s\n", src, dest);
                goto out;
        }
        ret = afs_event(BLOB_RENAME, NULL, table);
@@ -520,11 +519,11 @@ static int blob_get_name_by_id(struct osl_table *table, uint32_t id,
                return blob_get_name_by_id(table_name ## _table, id, name); \
        }
 
-static int blob_get_def_by_name(struct osl_table *table, char *name,
+static int blob_get_def_by_name(struct osl_table *table, const char *name,
                struct osl_object *def)
 {
        struct osl_row *row;
-       struct osl_object obj = {.data = name, .size = strlen(name) + 1};
+       struct osl_object obj = {.data = (void *)name, .size = strlen(name) + 1};
        int ret;
 
        def->data = NULL;
@@ -538,7 +537,7 @@ static int blob_get_def_by_name(struct osl_table *table, char *name,
 
 /** Define the \p get_def_by_id function for this blob type. */
 #define DEFINE_GET_DEF_BY_NAME(table_name, cmd_prefix) \
-       int cmd_prefix ## _get_def_by_name(char *name, struct osl_object *def) \
+       int cmd_prefix ## _get_def_by_name(const char *name, struct osl_object *def) \
        { \
                return blob_get_def_by_name(table_name ## _table, name, def); \
        }
@@ -625,25 +624,21 @@ static int blob_open(struct osl_table **table,
                        &table_name ## _table_desc, dir); \
        }
 
-
-/** Define the \p init function for this blob type. */
-#define DEFINE_BLOB_INIT(table_name) \
-       void table_name ## _init(struct afs_table *t) \
-       { \
-               t->open = table_name ## _open; \
-               t->close = table_name ## _close; \
-               t->create = table_name ## _create;\
-               t->event_handler = table_name ##_event_handler; \
-               table_name ## _table = NULL; \
-       }
-
+/** Blob tables map integers to blobs. */
+#define DEFINE_BLOB_AFS_TABLE_OPS(table_name) \
+       const struct afs_table_operations table_name ## _ops = { \
+               .open = table_name ## _open, \
+               .close = table_name ## _close, \
+               .create = table_name ## _create, \
+               .event_handler = table_name ##_event_handler, \
+       };
 
 /** Define all functions for this blob type. */
 #define DEFINE_BLOB_FUNCTIONS(table_name, short_name, c_short_name) \
        DEFINE_BLOB_OPEN(table_name) \
        DEFINE_BLOB_CLOSE(table_name) \
        DEFINE_BLOB_CREATE(table_name) \
-       DEFINE_BLOB_INIT(table_name) \
+       DEFINE_BLOB_AFS_TABLE_OPS(table_name) \
        DEFINE_BLOB_COMMAND(ls, LS, table_name, short_name, c_short_name) \
        DEFINE_BLOB_COMMAND(cat, CAT, table_name, short_name, c_short_name) \
        DEFINE_BLOB_COMMAND(add, ADD, table_name, short_name, c_short_name) \
index 64f6c67612a5c665b87dcdb8d3e051bc7500b455..95e59fd29de2015fcc65443a4ffc6442387ec0f8 100644 (file)
@@ -490,7 +490,7 @@ int client_connect(struct client_task *ct, struct sched *s,
 
        PARA_NOTICE_LOG("connecting %s:%u\n", host, port);
        ct->scc.fd = -1;
-       ret = para_connect_simple(IPPROTO_TCP, host, port);
+       ret = para_connect(IPPROTO_TCP, host, port);
        if (ret < 0)
                return ret;
        ct->scc.fd = ret;
index 00d2c5a61ada8fdcedfad2989e43844265ca46d5..c56a15822dfaf8a7b165b44480a6adcf90d5e1a3 100644 (file)
--- a/command.c
+++ b/command.c
@@ -47,12 +47,14 @@ extern struct misc_meta_data *mmd;
 int send_afs_status(struct command_context *cc, int parser_friendly);
 static bool subcmd_should_die;
 
+/*
+ * Don't call PARA_XXX_LOG() here as we might already hold the log mutex. See
+ * generic_signal_handler() for details.
+ */
 static void command_handler_sighandler(int s)
 {
-       if (s != SIGTERM)
-               return;
-       PARA_EMERG_LOG("terminating on signal %d\n", SIGTERM);
-       subcmd_should_die = true;
+       if (s == SIGTERM)
+               subcmd_should_die = true;
 }
 
 /*
@@ -505,6 +507,7 @@ static int com_stat(struct command_context *cc, struct lls_parse_result *lpr)
         * while we sleep.
         */
        para_block_signal(SIGTERM);
+       para_block_signal(SIGUSR1);
        for (;;) {
                sigset_t set;
                /*
@@ -536,8 +539,10 @@ static int com_stat(struct command_context *cc, struct lls_parse_result *lpr)
                 * open a race window similar to the one described above.
                 */
                pselect(1, NULL, NULL, NULL, &ts, &set);
-               if (subcmd_should_die)
+               if (subcmd_should_die) {
+                       PARA_EMERG_LOG("terminating on SIGTERM\n");
                        goto out;
+               }
                ret = -E_SERVER_CRASH;
                if (getppid() == 1)
                        goto out;
index fe2f7abf2af5fdb9abb27dcfb41c9da705ebf91a..0b20bcc8b1149132c4a1fa81a238c4479cb60df6 100644 (file)
 #include "net.h"
 #include "fd.h"
 
+#ifndef DCCP_SOCKOPT_CCID
+#define DCCP_SOCKOPT_CCID 13 /**< Sets both TX/RX CCID. */
+#endif
+
 static void dccp_recv_close(struct receiver_node *rn)
 {
        if (rn->fd > 0)
@@ -59,6 +63,9 @@ static int dccp_recv_ccid_support_check(const struct lls_parse_result *lpr)
        return 1;
 }
 
+/** Flowopt shortcut */
+#define OPT_ADD(fo, lev, opt, val, len) flowopt_add(fo, lev, opt, #opt, val, len)
+
 static int dccp_recv_open(struct receiver_node *rn)
 {
        struct lls_parse_result *lpr = rn->lpr;
@@ -83,7 +90,7 @@ static int dccp_recv_open(struct receiver_node *rn)
                OPT_ADD(fo, SOL_DCCP, DCCP_SOCKOPT_CCID, ccids, i);
        }
 
-       fd = makesock(IPPROTO_DCCP, 0, host, port, fo);
+       fd = makesock(IPPROTO_DCCP, false, host, port, fo);
        flowopt_cleanup(fo);
        free(ccids);
        if (fd < 0)
index 15a361babfa8570f61d2b51c15e60c83a8bab009..6182c964fde04719ee4736dc43482caf434ee395 100644 (file)
@@ -45,6 +45,10 @@ static void dccp_pre_monitor(struct sched *s)
                        sched_monitor_readfd(dss->listen_fds[n], s);
 }
 
+#ifndef DCCP_SOCKOPT_TX_CCID
+#define DCCP_SOCKOPT_TX_CCID 14 /**< Set/get the TX CCID. */
+#endif
+
 /**
  * Query the TX CCID used on the sender-client half connection.
  * \param sockfd Server socket descriptor to query (after accept(2)).
@@ -89,6 +93,13 @@ static void dccp_shutdown(void)
        free_sender_status(dss);
 }
 
+#ifndef DCCP_SOCKOPT_GET_CUR_MPS
+#define DCCP_SOCKOPT_GET_CUR_MPS 5 /**< Max packet size, RFC 4340, 14. */
+#endif
+
+/** Estimated worst-case length of a DCCP header including options. */
+#define DCCP_MAX_HEADER 128
+
 /** * Obtain current MPS according to RFC 4340, sec. 14. */
 static int dccp_init_fec(struct sender_client *sc)
 {
diff --git a/error.h b/error.h
index 82920ea8007e0d17d9de4b02ead6d402c9a334b4..7c146da2076ae74d896bb82a0e931d8b5b6f49f5 100644 (file)
--- a/error.h
+++ b/error.h
@@ -2,6 +2,7 @@
 
 /** \file error.h List of error codes and messages. */
 
+/** \cond para_error */
 /** Codes and messages. */
 #define PARA_ERRORS \
        PARA_ERROR(SUCCESS, "success"), \
        PARA_ERROR(NO_AUDIO_FILE, "no audio file"), \
        PARA_ERROR(NOFD, "did not receive open fd from afs"), \
        PARA_ERROR(NO_MATCH, "no matches"), \
-       PARA_ERROR(NO_MOOD, "no mood available"), \
        PARA_ERROR(NO_MORE_SLOTS, "no more empty slots"), \
        PARA_ERROR(NOT_PLAYING, "not playing"), \
        PARA_ERROR(NO_VALID_FILES, "no valid file found in playlist"), \
        PARA_ERROR(OPUS_SET_GAIN, "opus: could not set gain"), \
        PARA_ERROR(PATH_FOUND, ""), /* not really an error */ \
        PARA_ERROR(PLAYLIST_EMPTY, "attempted to load empty playlist"), \
-       PARA_ERROR(PLAYLIST_LOADED, ""), /* not really an error */ \
        PARA_ERROR(PREBUFFER_SUCCESS, "prebuffering complete"), \
        PARA_ERROR(PRIVATE_KEY, "can not read private key"), \
        PARA_ERROR(QUEUE, "packet queue overrun"), \
@@ -261,6 +260,7 @@ enum para_error_codes {PARA_ERRORS};
 extern const char * const para_errlist[];
 /** Exactly one .c file per executable must define the array. */
 #define DEFINE_PARA_ERRLIST const char * const para_errlist[] = {PARA_ERRORS}
+/** \endcond para_error */
 
 /**
  * This bit indicates whether a number is considered a system error number
index 5aafacb840df1f863536fac1911c8d1caf8eb2b0..32c9e7b9af6b24ca0fc93f3d7d65fce15c32e03a 100644 (file)
@@ -150,7 +150,7 @@ static int http_recv_open(struct receiver_node *rn)
        struct lls_parse_result *lpr = rn->lpr;
        const char *r_i = RECV_CMD_OPT_STRING_VAL(HTTP, HOST, lpr);
        uint32_t r_p = RECV_CMD_OPT_UINT32_VAL(HTTP, PORT, lpr);
-       int fd, ret = para_connect_simple(IPPROTO_TCP, r_i, r_p);
+       int fd, ret = para_connect(IPPROTO_TCP, r_i, r_p);
 
        if (ret < 0)
                return ret;
index 3019f7693a1d986f53cfcea45d13e572a4295a5f..e366a201569e538bc374f5356bf3d1c957164955 100644 (file)
@@ -147,11 +147,10 @@ caption = List of subcommands
 [subcommand sleep]
        purpose = stream, fade out, sleep, fade in
        [description]
-               Change to the initial volume and select the initial mood/playlist.
-               Fade out to the given fade-out volume in the specified time. Switch
-               to the sleep mood/playlist and wait until wake time minus fade-in
-               time. Finally, switch to the wake mood/playlist and fade in to the
-               fade-in volume.
+               Set the initial volume and mood, start playing and sleep. Then switch
+               to the fade-out mood and fade to the fade-out volume. Next, switch to
+               the sleep mood and wait until wake time minus fade-in time. Finally,
+               switch to the wake mood and fade in to the fade-in volume.
        [/description]
        [option ivol]
                summary = set initial volume
@@ -165,6 +164,26 @@ caption = List of subcommands
                        channel part may be omitted, in which case the default channel is
                        used. This option may be given multiple times.
                [/help]
+       [option initial-mood]
+               summary = mood or playlist to start with
+               arg_info = required_arg
+               arg_type = string
+               typestr = mood_spec
+               [help]
+                       This mood or playlist is selected right after setting the initial
+                       volume and before fade-out starts. If unset, fade-out starts
+                       immediately.
+               [/help]
+       [option initial-delay]
+               summary = time before fade-out starts.
+               arg_info = required_arg
+               arg_type = uint32
+               typestr = seconds
+               default_val = 0
+               [help]
+                       If left at the default, no initial delay occurs even if an initial
+                       mood is given.
+               [/help]
        [option fo-mood]
                summary = mood or playlist for fade-out
                arg_info = required_arg
diff --git a/mixer.c b/mixer.c
index eae8929164770053c5c18843f4cf4e4e8bc5e616..dda7fc1df729722266db7136733a28ac2391e977 100644 (file)
--- a/mixer.c
+++ b/mixer.c
@@ -187,11 +187,11 @@ static int com_fade(const struct mixer *m)
 }
 EXPORT_CMD(fade);
 
-static void client_cmd(const char *cmd)
+static void run(const char *exe, const char *cmd)
 {
        int ret, status, fds[3] = {0, 0, 0};
        pid_t pid;
-       char *cmdline = make_message(BINDIR "/para_client %s", cmd);
+       char *cmdline = make_message("%s %s", exe, cmd);
 
        PARA_NOTICE_LOG("%s\n", cmdline);
        ret = para_exec_cmdline_pid(&pid, cmdline, fds);
@@ -215,6 +215,16 @@ fail:
        exit(EXIT_FAILURE);
 }
 
+static void client_cmd(const char *cmd)
+{
+       run(BINDIR "/para_client", cmd);
+}
+
+static void audioc_cmd(const char *cmd)
+{
+       run(BINDIR "/para_audioc", cmd);
+}
+
 static void change_afs_mode(const char *afs_mode)
 {
        char *cmd;
@@ -263,6 +273,20 @@ static int set_initial_volume(const struct mixer *m, struct mixer_handle *h)
        return 1;
 }
 
+static void stop(const struct mixer *m, struct mixer_handle *h)
+{
+       int ret, old_vol = 0;
+
+       ret = m->get(h);
+       if (ret > 0)
+               old_vol = ret;
+       fade(m, h, 0, 3);
+       audioc_cmd("off");
+       client_cmd("stop");
+       audioc_cmd("on");
+       m->set(h, old_vol);
+}
+
 static int com_sleep(const struct mixer *m)
 {
        time_t t1, wake_time_epoch;
@@ -270,6 +294,7 @@ static int com_sleep(const struct mixer *m)
        struct tm *tm;
        int ret;
        const char *wake_time = OPT_STRING_VAL(SLEEP, WAKE_TIME);
+       const char *initial_mood = OPT_STRING_VAL(SLEEP, INITIAL_MOOD);
        const char *fo_mood = OPT_STRING_VAL(SLEEP, FO_MOOD);
        const char *fi_mood = OPT_STRING_VAL(SLEEP, FI_MOOD);
        const char *sleep_mood = OPT_STRING_VAL(SLEEP, SLEEP_MOOD);
@@ -316,17 +341,27 @@ static int com_sleep(const struct mixer *m)
        }
        wake_time_epoch = mktime(tm);
        PARA_INFO_LOG("waketime: %d:%02d\n", tm->tm_hour, tm->tm_min);
-       client_cmd("stop");
-       sleep(1);
+       stop(m, h);
+       ret = set_initial_volume(m, h);
+       if (ret < 0)
+               goto close_mixer;
+       /*
+        * Setting the volume invalidates the current channel setting, so we
+        * have to set it again.
+        */
+       ret = set_channel(m, h, OPT_STRING_VAL(PARA_MIXER, MIXER_CHANNEL));
+       if (ret < 0)
+               goto close_mixer;
+       delay = OPT_UINT32_VAL(SLEEP, INITIAL_DELAY);
+       if (delay > 0 && initial_mood && *initial_mood) {
+               change_afs_mode(initial_mood);
+               client_cmd("play");
+               sleep(delay);
+               stop(m, h);
+       }
        if (fot && fo_mood && *fo_mood) {
-               ret = set_initial_volume(m, h);
-               if (ret < 0)
-                       goto close_mixer;
                change_afs_mode(fo_mood);
                client_cmd("play");
-               ret = set_channel(m, h, OPT_STRING_VAL(PARA_MIXER, MIXER_CHANNEL));
-               if (ret < 0)
-                       goto close_mixer;
                ret = fade(m, h, fov, fot);
                if (ret < 0)
                        goto close_mixer;
@@ -340,7 +375,7 @@ static int com_sleep(const struct mixer *m)
                if (!fot || !fo_mood) /* currently stopped */
                        client_cmd("play");
        } else if (fot && fo_mood && *fo_mood) /* currently playing */
-               client_cmd("stop");
+               stop(m, h);
        m->close(&h);
        if (!fit || !fi_mood || !*fi_mood) /* nothing to do */
                return 1;
@@ -355,13 +390,12 @@ static int com_sleep(const struct mixer *m)
                sleep(delay);
        }
        change_afs_mode(fi_mood);
-       if (sleep_mood && *sleep_mood) /* currently playing */
-               client_cmd("next");
-       else /* currently stopped */
-               client_cmd("play");
        ret = open_mixer_and_set_channel(m, &h);
        if (ret < 0)
                return ret;
+       if (sleep_mood && *sleep_mood) /* currently playing */
+               stop(m, h);
+       client_cmd("play");
        ret = fade(m, h, fiv, fit);
 close_mixer:
        m->close(&h);
diff --git a/mood.c b/mood.c
index d47c54efa360b856c63b660fd4cdb5b81d442b68..9cdfd011a6ef4f2ab9fb180892f6412247eb68fe 100644 (file)
--- a/mood.c
+++ b/mood.c
@@ -12,7 +12,6 @@
 #include "afh.h"
 #include "afs.h"
 #include "list.h"
-#include "mood.h"
 
 /*
  * Mood parser API. It's overkill to have an own header file for
@@ -47,20 +46,29 @@ struct afs_statistics {
        /** Number of admissible files */
        unsigned num;
 };
-static struct afs_statistics statistics = {.normalization_divisor = 1};
 
-struct mood {
-       /** The name of this mood. */
+/**
+ * Stores an instance of a loaded mood (parser and statistics).
+ *
+ * A structure of this type is allocated and initialized when a mood is loaded.
+ */
+struct mood_instance {
+       /** NULL means that this is the "dummy" mood. */
        char *name;
-       /** Info for the bison parser. */
+       /** Bison's abstract syntax tree, used to determine admissibility. */
        struct mp_context *parser_context;
+       /** To compute the score. */
+       struct afs_statistics stats;
 };
 
 /*
- * If current_mood is NULL then no mood is currently open. If
- * current_mood->name is NULL, the dummy mood is currently open.
+ * If current_mood is NULL then no mood is currently loaded. If
+ * current_mood->name is NULL, the current mood is the dummy mood.
+ *
+ * The statistics are adjusted dynamically through this pointer as files are
+ * added, removed or played.
  */
-static struct mood *current_mood;
+static struct mood_instance *current_mood;
 
 /*
  * Find the position of the most-significant set bit.
@@ -119,15 +127,7 @@ __a_const static uint64_t int_sqrt(uint64_t x)
        return res;
 }
 
-/* returns 1 if row admissible, 0 if not, negative on errors */
-static int row_is_admissible(const struct osl_row *aft_row, struct mood *m)
-{
-       if (!m)
-               return -E_NO_MOOD;
-       return mp_eval_row(aft_row, m->parser_context);
-}
-
-static void destroy_mood(struct mood *m)
+static void destroy_mood(struct mood_instance *m)
 {
        if (!m)
                return;
@@ -136,32 +136,36 @@ static void destroy_mood(struct mood *m)
        free(m);
 }
 
-static struct mood *alloc_new_mood(const char *name)
+static struct mood_instance *alloc_new_mood(const char *name)
 {
-       struct mood *m = zalloc(sizeof(struct mood));
+       struct mood_instance *m = zalloc(sizeof(*m));
+
        if (name)
                m->name = para_strdup(name);
+       m->stats.normalization_divisor = 1;
        return m;
 }
 
-static int load_mood(const struct osl_row *mood_row, struct mood **m,
-               char **errmsg)
+static int init_mood_parser(const char *mood_name, struct mood_instance **m,
+               char **err)
 {
-       char *mood_name;
        struct osl_object mood_def;
        int ret;
 
-       ret = mood_get_name_and_def_by_row(mood_row, &mood_name, &mood_def);
+       if (!*mood_name) {
+               if (err)
+                       *err = make_message("empty mood name\n");
+               return -ERRNO_TO_PARA_ERROR(EINVAL);
+       }
+       ret = mood_get_def_by_name(mood_name, &mood_def);
        if (ret < 0) {
-               if (errmsg)
-                       *errmsg = make_message(
-                               "could not read mood definition");
+               if (err)
+                       *err = make_message("could not read mood definition\n");
                return ret;
        }
-       assert(*mood_name);
        *m = alloc_new_mood(mood_name);
-       PARA_INFO_LOG("opening mood %s\n", mood_name);
-       ret = mp_init(mood_def.data, mood_def.size, &(*m)->parser_context, errmsg);
+       PARA_INFO_LOG("loading mood %s\n", mood_name);
+       ret = mp_init(mood_def.data, mood_def.size, &(*m)->parser_context, err);
        osl_close_disk_object(&mood_def);
        if (ret < 0)
                destroy_mood(*m);
@@ -170,14 +174,14 @@ static int load_mood(const struct osl_row *mood_row, struct mood **m,
 
 static int check_mood(struct osl_row *mood_row, void *data)
 {
-       struct para_buffer *pb = data;
+       struct afs_callback_arg *aca = data;
        char *mood_name, *errmsg;
        struct osl_object mood_def;
-       struct mood *m;
+       struct mood_instance *m;
        int ret = mood_get_name_and_def_by_row(mood_row, &mood_name, &mood_def);
 
        if (ret < 0) {
-               para_printf(pb, "cannot read mood\n");
+               afs_error(aca, "cannot read mood\n");
                return ret;
        }
        if (!*mood_name) /* ignore dummy row */
@@ -186,9 +190,9 @@ static int check_mood(struct osl_row *mood_row, void *data)
        ret = mp_init(mood_def.data, mood_def.size, &m->parser_context,
                &errmsg);
        if (ret < 0) {
-               para_printf(pb, "%s: %s\n", mood_name, errmsg);
+               afs_error(aca, "%s: %s\n%s\n", mood_name, errmsg,
+                       para_strerror(-ret));
                free(errmsg);
-               para_printf(pb, "%s\n", para_strerror(-ret));
        } else
                destroy_mood(m);
        ret = 1; /* don't fail the loop on invalid mood definitions */
@@ -200,7 +204,7 @@ out:
 /**
  * Check all moods for syntax errors.
  *
- * \param aca Only ->pbout is used for diagnostics.
+ * \param aca Output goes to ->pbout, errors to ->fd on the error band.
  *
  * \return Negative on fatal errors. Inconsistent mood definitions are not
  * considered an error.
@@ -208,8 +212,7 @@ out:
 int mood_check_callback(struct afs_callback_arg *aca)
 {
        para_printf(&aca->pbout, "checking moods...\n");
-       return osl(osl_rbtree_loop(moods_table, BLOBCOL_ID, &aca->pbout,
-               check_mood));
+       return osl(osl_rbtree_loop(moods_table, BLOBCOL_ID, aca, check_mood));
 }
 
 /*
@@ -249,25 +252,27 @@ int mood_check_callback(struct afs_callback_arg *aca)
  * overflows and rounding errors we store the common divisor of the
  * correction factors separately.
  */
-static long compute_score(struct afs_info *afsi)
+static long compute_score(struct afs_info *afsi,
+               const struct afs_statistics *stats)
 {
        int64_t mean_n, mean_l,score_n, score_l;
 
-       assert(statistics.normalization_divisor > 0);
-       assert(statistics.num > 0);
-       mean_n = statistics.num_played_sum / statistics.num;
-       mean_l = statistics.last_played_sum / statistics.num;
+       assert(stats->normalization_divisor > 0);
+       assert(stats->num > 0);
+       mean_n = stats->num_played_sum / stats->num;
+       mean_l = stats->last_played_sum / stats->num;
 
        score_n = -((int64_t)afsi->num_played - mean_n)
-               * statistics.num_played_correction
-               / statistics.normalization_divisor;
+               * stats->num_played_correction
+               / stats->normalization_divisor;
        score_l = -((int64_t)afsi->last_played - mean_l)
-               * statistics.last_played_correction
-               / statistics.normalization_divisor;
+               * stats->last_played_correction
+               / stats->normalization_divisor;
        return (score_n + score_l) / 2;
 }
 
-static int add_afs_statistics(const struct osl_row *row)
+static int add_afs_statistics(const struct osl_row *row,
+               struct afs_statistics *stats)
 {
        uint64_t n, x, s, q;
        struct afs_info afsi;
@@ -276,64 +281,65 @@ static int add_afs_statistics(const struct osl_row *row)
        ret = get_afsi_of_row(row, &afsi);
        if (ret < 0)
                return ret;
-       n = statistics.num;
+       n = stats->num;
        x = afsi.last_played;
-       s = statistics.last_played_sum;
+       s = stats->last_played_sum;
        if (n > 0) {
                q = (x > s / n)? x - s / n : s / n - x;
-               statistics.last_played_qd += q * q * n / (n + 1);
+               stats->last_played_qd += q * q * n / (n + 1);
        }
-       statistics.last_played_sum += x;
+       stats->last_played_sum += x;
 
        x = afsi.num_played;
-       s = statistics.num_played_sum;
+       s = stats->num_played_sum;
        if (n > 0) {
                q = (x > s / n)? x - s / n : s / n - x;
-               statistics.num_played_qd += q * q * n / (n + 1);
+               stats->num_played_qd += q * q * n / (n + 1);
        }
-       statistics.num_played_sum += x;
-       statistics.num++;
+       stats->num_played_sum += x;
+       stats->num++;
        return 1;
 }
 
 static int del_afs_statistics(const struct osl_row *row)
 {
+       struct afs_statistics *stats = &current_mood->stats;
        uint64_t n, s, q, a, new_s;
        struct afs_info afsi;
        int ret;
        ret = get_afsi_of_row(row, &afsi);
        if (ret < 0)
                return ret;
-       n = statistics.num;
+       n = stats->num;
        assert(n);
        if (n == 1) {
-               memset(&statistics, 0, sizeof(statistics));
-               statistics.normalization_divisor = 1;
+               memset(stats, 0, sizeof(*stats));
+               stats->normalization_divisor = 1;
                return 1;
        }
 
-       s = statistics.last_played_sum;
-       q = statistics.last_played_qd;
+       s = stats->last_played_sum;
+       q = stats->last_played_qd;
        a = afsi.last_played;
        new_s = s - a;
-       statistics.last_played_sum = new_s;
-       statistics.last_played_qd = q + s * s / n - a * a
+       stats->last_played_sum = new_s;
+       stats->last_played_qd = q + s * s / n - a * a
                - new_s * new_s / (n - 1);
 
-       s = statistics.num_played_sum;
-       q = statistics.num_played_qd;
+       s = stats->num_played_sum;
+       q = stats->num_played_qd;
        a = afsi.num_played;
        new_s = s - a;
-       statistics.num_played_sum = new_s;
-       statistics.num_played_qd = q + s * s / n - a * a
+       stats->num_played_sum = new_s;
+       stats->num_played_qd = q + s * s / n - a * a
                - new_s * new_s / (n - 1);
 
-       statistics.num--;
+       stats->num--;
        return 1;
 }
 
 /*
- * At mood open time we determine the set of admissible files for the given
+ * At mood load time we determine the set of admissible files for the given
  * mood where each file is identified by a pointer to a row of the audio file
  * table. In the first pass the pointers are added to a temporary array and
  * statistics are computed. When all admissible files have been processed in
@@ -343,7 +349,7 @@ static int del_afs_statistics(const struct osl_row *row)
  */
 struct admissible_array {
        /** Files are admissible wrt. this mood. */
-       struct mood *m;
+       struct mood_instance *m;
        /** The size of the array */
        unsigned size;
        /** Pointer to the array of admissible files. */
@@ -357,19 +363,18 @@ struct admissible_array {
 static int add_if_admissible(struct osl_row *aft_row, void *data)
 {
        struct admissible_array *aa = data;
-       int ret;
+       struct afs_statistics *stats = &aa->m->stats;
 
-       ret = row_is_admissible(aft_row, aa->m);
-       if (ret <= 0)
-               return ret;
-       if (statistics.num >= aa->size) {
+       if (!mp_eval_row(aft_row, aa->m->parser_context))
+               return 0;
+       if (stats->num >= aa->size) {
                aa->size *= 2;
                aa->size += 100;
                aa->array = arr_realloc(aa->array, aa->size,
                        sizeof(struct osl_row *));
        }
-       aa->array[statistics.num] = aft_row;
-       return add_afs_statistics(aft_row);
+       aa->array[stats->num] = aft_row;
+       return add_afs_statistics(aft_row, stats);
 }
 
 /**
@@ -414,29 +419,25 @@ _static_inline_ int64_t update_quadratic_deviation(int64_t n, int64_t old_qd,
        return old_qd + delta * (sigma - 2 * old_sum / n - delta / n);
 }
 
-static int update_afs_statistics(struct afs_info *old_afsi,
+static void update_afs_statistics(struct afs_info *old_afsi,
                struct afs_info *new_afsi)
 {
-       unsigned n;
-       int ret = get_num_admissible_files(&n);
-
-       if (ret < 0)
-               return ret;
-       assert(n);
-
-       statistics.last_played_qd = update_quadratic_deviation(n,
-               statistics.last_played_qd, old_afsi->last_played,
-               new_afsi->last_played, statistics.last_played_sum);
-       statistics.last_played_sum += new_afsi->last_played - old_afsi->last_played;
-
-       statistics.num_played_qd = update_quadratic_deviation(n,
-               statistics.num_played_qd, old_afsi->num_played,
-               new_afsi->num_played, statistics.num_played_sum);
-       statistics.num_played_sum += new_afsi->num_played - old_afsi->num_played;
-       return 1;
+       struct afs_statistics *stats = &current_mood->stats;
+
+       assert(stats->num > 0);
+       stats->last_played_qd = update_quadratic_deviation(stats->num,
+               stats->last_played_qd, old_afsi->last_played,
+               new_afsi->last_played, stats->last_played_sum);
+       stats->last_played_sum += new_afsi->last_played - old_afsi->last_played;
+
+       stats->num_played_qd = update_quadratic_deviation(stats->num,
+               stats->num_played_qd, old_afsi->num_played,
+               new_afsi->num_played, stats->num_played_sum);
+       stats->num_played_sum += new_afsi->num_played - old_afsi->num_played;
 }
 
-static int add_to_score_table(const struct osl_row *aft_row)
+static int add_to_score_table(const struct osl_row *aft_row,
+               const struct afs_statistics *stats)
 {
        long score;
        struct afs_info afsi;
@@ -444,7 +445,7 @@ static int add_to_score_table(const struct osl_row *aft_row)
 
        if (ret < 0)
                return ret;
-       score = compute_score(&afsi);
+       score = compute_score(&afsi, stats);
        return score_add(aft_row, score);
 }
 
@@ -457,23 +458,18 @@ static int delete_from_statistics_and_score_table(const struct osl_row *aft_row)
 }
 
 /**
- * Delete one entry from the statistics and from the score table.
+ * Delete an audio file from the score table and update mood statistics.
  *
- * \param aft_row The audio file which is no longer admissible.
+ * \param aft_row Identifies the row to delete.
  *
- * \return Positive on success, negative on errors.
+ * \return Standard.
  *
  * \sa \ref score_delete().
  */
 static int mood_delete_audio_file(const struct osl_row *aft_row)
 {
-       int ret;
-
-       ret = row_belongs_to_score_table(aft_row, NULL);
-       if (ret < 0)
-               return ret;
-       if (!ret) /* not admissible, nothing to do */
-               return 1;
+       if (!row_belongs_to_score_table(aft_row))
+               return 0;
        return delete_from_statistics_and_score_table(aft_row);
 }
 
@@ -492,95 +488,71 @@ static int mood_update_audio_file(const struct osl_row *aft_row,
                struct afs_info *old_afsi)
 {
        long score, percent;
-       int ret, is_admissible, was_admissible = 0;
+       int ret;
+       bool is_admissible, was_admissible;
        struct afs_info afsi;
-       unsigned rank;
 
        if (!current_mood)
                return 1; /* nothing to do */
-       ret = row_belongs_to_score_table(aft_row, &rank);
-       if (ret < 0)
-               return ret;
-       was_admissible = ret;
-       ret = row_is_admissible(aft_row, current_mood);
-       if (ret < 0)
-               return ret;
-       is_admissible = (ret > 0);
+       was_admissible = row_belongs_to_score_table(aft_row);
+       is_admissible = mp_eval_row(aft_row, current_mood->parser_context);
        if (!was_admissible && !is_admissible)
                return 1;
        if (was_admissible && !is_admissible)
                return delete_from_statistics_and_score_table(aft_row);
        if (!was_admissible && is_admissible) {
-               ret = add_afs_statistics(aft_row);
+               ret = add_afs_statistics(aft_row, &current_mood->stats);
                if (ret < 0)
                        return ret;
-               return add_to_score_table(aft_row);
+               return add_to_score_table(aft_row, &current_mood->stats);
        }
        /* update score */
        ret = get_afsi_of_row(aft_row, &afsi);
        if (ret < 0)
                return ret;
-       if (old_afsi) {
-               ret = update_afs_statistics(old_afsi, &afsi);
-               if (ret < 0)
-                       return ret;
-       }
-       score = compute_score(&afsi);
+       if (old_afsi)
+               update_afs_statistics(old_afsi, &afsi);
+       score = compute_score(&afsi, &current_mood->stats);
        PARA_DEBUG_LOG("score: %li\n", score);
        percent = (score + 100) / 3;
        if (percent > 100)
                percent = 100;
        else if (percent < 0)
                percent = 0;
-       PARA_DEBUG_LOG("moving from rank %u to %li%%\n", rank, percent);
+       PARA_DEBUG_LOG("moving to %li%%\n", percent);
        return score_update(aft_row, percent);
 }
 
 /* sse: seconds since epoch. */
-static void log_statistics(int64_t sse)
+static char *get_statistics(struct mood_instance *m, int64_t sse)
 {
-       unsigned n = statistics.num;
+       unsigned n = m->stats.num;
        int mean_days, sigma_days;
 
-       assert(current_mood);
-       PARA_NOTICE_LOG("loaded mood %s\n", current_mood->name?
-               current_mood->name : "(dummy)");
-       if (!n) {
-               PARA_WARNING_LOG("no admissible files\n");
-               return;
-       }
-       PARA_NOTICE_LOG("%u admissible files\n", statistics.num);
-       mean_days = (sse - statistics.last_played_sum / n) / 3600 / 24;
-       sigma_days = int_sqrt(statistics.last_played_qd / n) / 3600 / 24;
-       PARA_NOTICE_LOG("last_played mean/sigma: %d/%d days\n", mean_days, sigma_days);
-       PARA_NOTICE_LOG("num_played mean/sigma: %" PRId64 "/%" PRIu64 "\n",
-               statistics.num_played_sum / n,
-               int_sqrt(statistics.num_played_qd / n));
-       PARA_NOTICE_LOG("num_played correction factor: %" PRId64 "\n",
-               statistics.num_played_correction);
-       PARA_NOTICE_LOG("last_played correction factor: %" PRId64 "\n",
-               statistics.last_played_correction);
-       PARA_NOTICE_LOG("normalization divisor: %" PRId64 "\n",
-               statistics.normalization_divisor);
+       mean_days = (sse - m->stats.last_played_sum / n) / 3600 / 24;
+       sigma_days = int_sqrt(m->stats.last_played_qd / n) / 3600 / 24;
+       return make_message(
+               "loaded mood %s (%u files)\n"
+               "last_played mean/sigma: %d/%d days\n"
+               "num_played mean/sigma: %" PRId64 "/%" PRIu64 "\n"
+       ,
+               m->name? m->name : "(dummy)",
+               n,
+               mean_days, sigma_days,
+               m->stats.num_played_sum / n,
+                       int_sqrt(m->stats.num_played_qd / n)
+       );
 }
 
-/**
- * Close the current mood.
- *
- * Frees all resources of the current mood.
- */
-void close_current_mood(void)
+/** Free all resources of the current mood, if any. */
+void mood_unload(void)
 {
        destroy_mood(current_mood);
        current_mood = NULL;
-       memset(&statistics, 0, sizeof(statistics));
-       statistics.normalization_divisor = 1;
 }
 
-static void compute_correction_factors(int64_t sse)
+static void compute_correction_factors(int64_t sse, struct afs_statistics *s)
 {
-       struct afs_statistics *s = &statistics;
-
        if (s->num > 0) {
                s->normalization_divisor = int_sqrt(s->last_played_qd)
                        * int_sqrt(s->num_played_qd) / s->num / 100;
@@ -598,28 +570,24 @@ static void compute_correction_factors(int64_t sse)
 /**
  * Change the current mood.
  *
- * \param mood_name The name of the mood to open.
- * \param errmsg Error description is returned here.
+ * \param mood_name The name of the mood to load.
+ * \param msg Error message or mood info is returned here.
  *
  * If \a mood_name is \a NULL, load the dummy mood that accepts every audio file
  * and uses a scoring method based only on the \a last_played information.
  *
- * The errmsg pointer may be NULL, in which case no error message will be
- * returned. If a non-NULL pointer is given, the caller must free *errmsg.
- *
- * If there is already an open mood, it will be closed first.
+ * If the message pointer is not NULL, a suitable message is returned there in
+ * all cases. The caller must free this string.
  *
- * \return Positive on success, negative on errors.
+ * \return The number of admissible files on success, negative on errors. It is
+ * not considered an error if no files are admissible.
  *
  * \sa struct \ref afs_info::last_played, \ref mp_eval_row().
  */
-int change_current_mood(const char *mood_name, char **errmsg)
+int mood_load(const char *mood_name, char **msg)
 {
        int i, ret;
-       struct admissible_array aa = {
-               .size = 0,
-               .array = NULL
-       };
+       struct admissible_array aa = {.size = 0};
        /*
         * We can not use the "now" pointer from sched.c here because we are
         * called before schedule(), which initializes "now".
@@ -627,85 +595,68 @@ int change_current_mood(const char *mood_name, char **errmsg)
        struct timeval rnow;
 
        if (mood_name) {
-               struct mood *m;
-               struct osl_row *row;
-               struct osl_object obj;
-
-               if (!*mood_name) {
-                       *errmsg = make_message("empty mood name");
-                       return -ERRNO_TO_PARA_ERROR(EINVAL);
-               }
-               obj.data = (char *)mood_name;
-               obj.size = strlen(mood_name) + 1;
-               ret = osl(osl_get_row(moods_table, BLOBCOL_NAME, &obj, &row));
-               if (ret < 0) {
-                       if (errmsg)
-                               *errmsg = make_message("no such mood: %s",
-                                       mood_name);
-                       return ret;
-               }
-               ret = load_mood(row, &m, errmsg);
+               ret = init_mood_parser(mood_name, &aa.m, msg);
                if (ret < 0)
                        return ret;
-               close_current_mood();
-               current_mood = m;
-       } else { /* load dummy mood */
-               close_current_mood();
-               current_mood = alloc_new_mood(NULL);
-       }
-       aa.m = current_mood;
+       } else /* load dummy mood */
+               aa.m = alloc_new_mood(NULL);
        PARA_NOTICE_LOG("computing statistics of admissible files\n");
        ret = audio_file_loop(&aa, add_if_admissible);
        if (ret < 0) {
-               if (errmsg)
-                       *errmsg = make_message("audio file loop failed");
+               if (msg) /* false if we are called via the event handler */
+                       *msg = make_message("audio file loop failed\n");
                goto out;
        }
        clock_get_realtime(&rnow);
-       compute_correction_factors(rnow.tv_sec);
-       log_statistics(rnow.tv_sec);
-       for (i = 0; i < statistics.num; i++) {
-               ret = add_to_score_table(aa.array[i]);
+       compute_correction_factors(rnow.tv_sec, &aa.m->stats);
+       if (aa.m->stats.num == 0) {
+               if (msg)
+                       *msg = make_message("no admissible files\n");
+               ret = 0;
+               goto out;
+       }
+       for (i = 0; i < aa.m->stats.num; i++) {
+               ret = add_to_score_table(aa.array[i], &aa.m->stats);
                if (ret < 0) {
-                       if (errmsg)
-                               *errmsg = make_message(
-                                       "could not add row to score table");
+                       if (msg)
+                               *msg = make_message(
+                                       "could not add row to score table\n");
                        goto out;
                }
        }
-       ret = statistics.num;
+       /* success */
+       if (msg)
+               *msg = get_statistics(aa.m, rnow.tv_sec);
+       ret = aa.m->stats.num;
+       mood_unload();
+       current_mood = aa.m;
 out:
        free(aa.array);
        if (ret < 0)
-               close_current_mood();
+               destroy_mood(aa.m);
        return ret;
 }
 
 /*
- * Close and re-open the current mood.
+ * Empty the score table and start over.
  *
  * This function is called on events which render the current list of
  * admissible files useless, for example if an attribute is removed from the
  * attribute table.
- *
- * If no mood is currently open, the function returns success.
  */
 static int reload_current_mood(void)
 {
        int ret;
        char *mood_name = NULL;
 
-       ret = clear_score_table();
-       if (ret < 0)
-               return ret;
-       if (!current_mood)
-               return 1;
+       assert(current_mood);
+       score_clear();
        PARA_NOTICE_LOG("reloading %s\n", current_mood->name?
                current_mood->name : "(dummy)");
        if (current_mood->name)
                mood_name = para_strdup(current_mood->name);
-       close_current_mood();
-       ret = change_current_mood(mood_name, NULL);
+       mood_unload();
+       ret = mood_load(mood_name, NULL);
        free(mood_name);
        return ret;
 }
diff --git a/mood.h b/mood.h
deleted file mode 100644 (file)
index fcfe1ef..0000000
--- a/mood.h
+++ /dev/null
@@ -1,7 +0,0 @@
-/* Copyright (C) 2007 Andre Noll <maan@tuebingen.mpg.de>, see file COPYING. */
-
-/** \file mood.h Public functions of mood.c. */
-
-int change_current_mood(const char *mood_name, char **errmsg);
-void close_current_mood(void);
-int mood_check_callback(struct afs_callback_arg *aca);
diff --git a/mp.c b/mp.c
index fb5a0c07472c46bb7d5ca42fcb4061d54755f96b..a068b043faf3c09b72befde39771058108f1aec9 100644 (file)
--- a/mp.c
+++ b/mp.c
@@ -557,7 +557,7 @@ int mp_init(const char *definition, int nbytes, struct mp_context **result,
  * function returns true (without looking at the audio file metadata) to
  * indicate that the given audio file should be considered admissible.
  *
- * \sa \ref change_current_mood(), \ref mp_eval_ast().
+ * \sa \ref mood_load(), \ref mp_eval_ast().
  */
 bool mp_eval_row(const struct osl_row *aft_row, struct mp_context *ctx)
 {
diff --git a/net.c b/net.c
index e01af24be27c49a00a60a61a98448bc3d82046f0..a24081f5123bd5a62236ff6d8202afc4e16c009b 100644 (file)
--- a/net.c
+++ b/net.c
 #include "list.h"
 #include "fd.h"
 
+/* Whether the given address conforms to the IPv4 address format. */
+static inline bool is_valid_ipv4_address(const char *address)
+{
+       struct in_addr test_it;
+       return inet_pton(AF_INET, address, &test_it) != 0;
+}
+
 /**
  * Parse and validate IPv4 address/netmask string.
  *
@@ -58,13 +65,6 @@ failed:
        return NULL;
 }
 
-
-/**
- * Match string as a candidate IPv4 address.
- *
- * \param address The string to match.
- * \return True if \a address has "dot-quad" format.
- */
 static bool is_v4_dot_quad(const char *address)
 {
        bool result;
@@ -77,6 +77,13 @@ static bool is_v4_dot_quad(const char *address)
        return result;
 }
 
+/* Whether a string conforms to IPv6 address format (RFC 4291). */
+static inline bool is_valid_ipv6_address(const char *address)
+{
+       struct in6_addr test_it;
+       return inet_pton(AF_INET6, address, &test_it) != 0;
+}
+
 /**
  * Perform basic syntax checking on the host-part of an URL:
  *
@@ -205,7 +212,7 @@ char *format_url(const char *url, int default_port)
  * \param transport Transport protocol name (e.g. "udp", "tcp"), or NULL.
  * \return Pointer to static result buffer.
  *
- * \sa getservent(3), services(5), nsswitch.conf(5).
+ * \sa getservbyport(3), services(5), nsswitch.conf(5).
  */
 const char *stringify_port(int port, const char *transport)
 {
@@ -224,12 +231,13 @@ const char *stringify_port(int port, const char *transport)
        return service;
 }
 
-/**
- * Determine the socket type for a given layer-4 protocol.
- *
- * \param l4type The symbolic name of the transport-layer protocol.
- *
- * \sa ip(7), socket(2).
+#ifndef SOCK_DCCP
+#define SOCK_DCCP 6 /**< Linux socket type. */
+#endif
+
+/*
+ * Determine the socket type, given the symbolic name of the transport-layer
+ * protocol. See ip(7), socket(2).
  */
 static inline int sock_type(const unsigned l4type)
 {
@@ -241,9 +249,7 @@ static inline int sock_type(const unsigned l4type)
        return -1;              /* not supported here */
 }
 
-/**
- * Pretty-print transport-layer name.
- */
+/* Pretty-print transport-layer name. */
 static const char *layer4_name(const unsigned l4type)
 {
        switch (l4type) {
@@ -273,7 +279,12 @@ struct pre_conn_opt {
        struct list_head node;          /**< FIFO, as sockopt order matters. */
 };
 
-/** FIFO list of pre-connection socket options to be set */
+/**
+ * List of pre-connection socket options to be set.
+ *
+ * This list contains transport-layer independent encapsulation of socket
+ * options that need to be registered prior to setting up a connection.
+ */
 struct flowopts {
        struct list_head sockopts;
 };
@@ -325,7 +336,7 @@ void flowopt_add(struct flowopts *fo, int lev, int opt,
        list_add_tail(&new->node, &fo->sockopts);
 }
 
-/** Set the entire bunch of pre-connection options at once. */
+/* Set the entire bunch of pre-connection options at once. */
 static void flowopt_setopts(int sockfd, struct flowopts *fo)
 {
        struct pre_conn_opt *pc;
@@ -509,7 +520,7 @@ int makesock(unsigned l4type, bool passive, const char *host, uint16_t port_numb
        if (ai)
                freeaddrinfo(ai);
        if (ret < 0) {
-               PARA_ERROR_LOG("can not create %s socket %s#%d.\n",
+               PARA_NOTICE_LOG("can not create %s socket %s#%d.\n",
                layer4_name(l4type), host? host : (passive?
                "[loopback]" : "[localhost]"), port_number);
        }
@@ -571,11 +582,7 @@ int para_listen_simple(unsigned l4type, uint16_t port)
        return para_listen(l4type, NULL, port);
 }
 
-/**
- * Determine IPv4/v6 socket address length.
- * \param sa Container of IPv4 or IPv6 address.
- * \return Address-family dependent address length.
- */
+/* Compute the address-family dependent address length of an IPv4/v6 socket. */
 static socklen_t salen(const struct sockaddr *sa)
 {
        assert(sa->sa_family == AF_INET || sa->sa_family == AF_INET6);
@@ -585,7 +592,7 @@ static socklen_t salen(const struct sockaddr *sa)
                : sizeof(struct sockaddr_in);
 }
 
-/** True if @ss holds a v6-mapped-v4 address (RFC 4291, 2.5.5.2) */
+/* True if ss holds a v6-mapped-v4 address (RFC 4291, 2.5.5.2) */
 static bool SS_IS_ADDR_V4MAPPED(const struct sockaddr_storage *ss)
 {
        const struct sockaddr_in6 *ia6 = (const struct sockaddr_in6 *)ss;
@@ -593,10 +600,10 @@ static bool SS_IS_ADDR_V4MAPPED(const struct sockaddr_storage *ss)
        return ss->ss_family == AF_INET6 && IN6_IS_ADDR_V4MAPPED(&ia6->sin6_addr);
 }
 
-/**
+/*
  * Process IPv4/v6 address, turn v6-mapped-v4 address into normal IPv4 address.
- * \param ss Container of IPv4/6 address.
- * \return Pointer to normalized address (may be static storage).
+ * ss: Container of IPv4/6 address.
+ * Returns: Pointer to normalized address (may be static storage).
  *
  * \sa RFC 3493.
  */
@@ -617,7 +624,7 @@ normalize_ip_address(const struct sockaddr_storage *ss)
        return (const struct sockaddr *)ss;
 }
 
-/**
+/*
  * Generic/fallback MTU values
  *
  * These are taken from RFC 1122, RFC 2460, and RFC 5405.
@@ -632,7 +639,7 @@ static inline int generic_mtu(const int af_type)
        return af_type == AF_INET6 ? 1280 : 576;
 }
 
-/** Crude approximation of IP header overhead - neglecting options. */
+/* Crude approximation of IP header overhead - neglecting options. */
 static inline int estimated_header_overhead(const int af_type)
 {
        return af_type == AF_INET6 ? 40 : 20;
@@ -829,6 +836,10 @@ int para_accept(int fd, void *addr, socklen_t size, int *new_fd)
        return -ERRNO_TO_PARA_ERROR(errno);
 }
 
+#ifndef DCCP_SOCKOPT_AVAILABLE_CCIDS
+#define DCCP_SOCKOPT_AVAILABLE_CCIDS 12 /**< List of supported CCIDs. */
+#endif
+
 /**
  * Probe the list of DCCP CCIDs configured on this host.
  * \param ccid_array Pointer to return statically allocated array in.
@@ -843,7 +854,7 @@ int dccp_available_ccids(uint8_t **ccid_array)
        socklen_t nccids = sizeof(ccids);
        int ret, fd;
 
-       ret = fd = makesock(IPPROTO_DCCP, 1, NULL, 0, NULL);
+       ret = fd = makesock(IPPROTO_DCCP, true /* passive */, NULL, 0, NULL);
        if (ret < 0)
                return ret;
 
@@ -861,6 +872,18 @@ int dccp_available_ccids(uint8_t **ccid_array)
        return nccids;
 }
 
+/**
+ * The buffer size of the sun_path component of struct sockaddr_un.
+ *
+ * While glibc doesn't define UNIX_PATH_MAX, it documents it has being limited
+ * to 108 bytes. On NetBSD it is only 104 bytes though. We trust UNIX_PATH_MAX
+ * if it is defined and use the size of the ->sun_path member otherwise. This
+ * should be safe everywhere.
+ */
+#ifndef UNIX_PATH_MAX
+#define UNIX_PATH_MAX (sizeof(((struct sockaddr_un *)0)->sun_path))
+#endif
+
 /*
  * Prepare a structure for AF_UNIX socket addresses.
  *
diff --git a/net.h b/net.h
index fd89dc5db0695ae6285484e0755ae0d28b286693..d206881c587b8ec185856e23e84bb26ecf032f45 100644 (file)
--- a/net.h
+++ b/net.h
 /* Copyright (C) 2006 Andre Noll <maan@tuebingen.mpg.de>, see file COPYING. */
 /** \file net.h exported symbols from net.c */
 
-/**
- * The buffer size of the sun_path component of struct sockaddr_un.
- *
- * While glibc doesn't define \p UNIX_PATH_MAX, it documents it has being
- * limited to 108 bytes. On NetBSD it is only 104 bytes though. We trust \p
- * UNIX_PATH_MAX if it is defined and use the size of the ->sun_path member
- * otherwise. This should be safe everywhere.
- */
-#ifndef UNIX_PATH_MAX
-#define UNIX_PATH_MAX (sizeof(((struct sockaddr_un *)0)->sun_path))
-#endif
-
 /* Userland defines for Linux DCCP support. */
 
-#ifndef IPPROTO_DCCP
-#define IPPROTO_DCCP 33 /**< IANA assigned value. */
-#endif
-
-#ifndef SOCK_DCCP
-#define SOCK_DCCP 6 /**< Linux socket type. */
-#endif
-
-#ifndef DCCP_SOCKOPT_RX_CCID
-/** Per-connection CCID support (set/get the RX CCID, since v2.6.30-rc1). */
-#define DCCP_SOCKOPT_RX_CCID 15
-#endif
-
 #ifndef SOL_DCCP
 #define SOL_DCCP 269 /**< Linux socket level. */
 #endif
 
-#ifndef DCCP_SOCKOPT_GET_CUR_MPS
-#define DCCP_SOCKOPT_GET_CUR_MPS  5 /**< Max packet size, RFC 4340, 14. */
-#endif
-
-#ifndef DCCP_SOCKOPT_AVAILABLE_CCIDS
-#define DCCP_SOCKOPT_AVAILABLE_CCIDS 12 /**< List of supported CCIDs. */
-#endif
-
-#ifndef DCCP_SOCKOPT_CCID
-#define DCCP_SOCKOPT_CCID 13 /**< Sets both TX/RX CCID. */
-#endif
-
-#ifndef DCCP_SOCKOPT_TX_CCID
-#define DCCP_SOCKOPT_TX_CCID 14 /**< Set/get the TX CCID. */
-#endif
-
 /** The maximum length of the host component in an URL. */
 #define MAX_HOSTLEN 256
 
-/**
- * Flowopts: Transport-layer independent encapsulation of socket options
- *           that need to be registered prior to setting up a connection.
- */
+/* Opaque, only known to net.c. */
 struct flowopts;
 
-extern struct flowopts *flowopt_new(void);
-extern void flowopt_add(struct flowopts *fo, int level, int opt,
+struct flowopts *flowopt_new(void);
+void flowopt_add(struct flowopts *fo, int level, int opt,
                const char *name, const void *val, int len);
 void flowopt_cleanup(struct flowopts *fo);
-/** Flowopt shortcut macros */
-#define OPT_ADD(fo, lev, opt, val, len)        flowopt_add(fo, lev, opt, #opt, val, len)
 
 /**
  * Functions to parse and validate (parts of) URLs.
  */
-extern char *parse_cidr(const char *cidr,
-                       char *addr, ssize_t addrlen, int32_t *netmask);
-extern char *parse_url(const char *url,
-                      char *host, ssize_t hostlen, int32_t *port);
+char *parse_cidr(const char *cidr,
+               char *addr, ssize_t addrlen, int32_t *netmask);
+char *parse_url(const char *url,
+               char *host, ssize_t hostlen, int32_t *port);
 char *format_url(const char *url, int default_port);
-extern const char *stringify_port(int port, const char *transport);
-/**
- * Ensure that string conforms to the IPv4 address format.
- *
- * \param address The address string to check.
- *
- * \return 1 if \a address conforms to the IPv4 address format, else 0.
- */
-_static_inline_ bool is_valid_ipv4_address(const char *address)
-{
-       struct in_addr test_it;
-
-       return inet_pton(AF_INET, address, &test_it) != 0;
-}
-
-/**
- * Ensure that string conforms to IPv6 address format.
- *
- * \param address The address string to check.
- *
- * \return 1 if string has a valid IPv6 address syntax, 0 if not.
- * \sa RFC 4291.
- */
-_static_inline_ bool is_valid_ipv6_address(const char *address)
-{
-       struct in6_addr test_it;
-
-       return inet_pton(AF_INET6, address, &test_it) != 0;
-}
+const char *stringify_port(int port, const char *transport);
 
 int lookup_address(unsigned l4type, bool passive, const char *host,
                int port_number, struct addrinfo **result);
@@ -114,10 +40,9 @@ int makesock(unsigned l4type, bool passive, const char *host,
 int makesock_addrinfo(unsigned l4type, bool passive, struct addrinfo *ai,
                struct flowopts *fo);
 
-static inline int para_connect_simple(unsigned l4type,
-                                     const char *host, uint16_t port)
+static inline int para_connect(unsigned l4type, const char *host, uint16_t port)
 {
-       return makesock(l4type, 0, host, port, NULL);
+       return makesock(l4type, false, host, port, NULL);
 }
 
 void extract_v4_addr(const struct sockaddr_storage *ss, struct in_addr *ia);
@@ -133,12 +58,12 @@ int para_listen(unsigned l4type, const char *addr, uint16_t port);
 int para_listen_simple(unsigned l4type, uint16_t port);
 
 /** Pretty-printing of IPv4/6 socket addresses */
-extern char *remote_name(int sockfd);
+char *remote_name(int sockfd);
 
 /**
  * Determining maximum payload (packet) size
  */
-extern int generic_max_transport_msg_size(int sockfd);
+int generic_max_transport_msg_size(int sockfd);
 
 int recv_bin_buffer(int fd, char *buf, size_t size);
 int recv_buffer(int fd, char *buf, size_t size);
@@ -152,8 +77,6 @@ ssize_t send_cred_buffer(int, char*);
 /**
  * Functions and definitions to support \p IPPROTO_DCCP
  */
-/** Estimated worst-case length of a DCCP header including options. */
-#define DCCP_MAX_HEADER                128
 /** Hardcoded maximum number of separate CCID modules compiled into a host. */
 #define DCCP_MAX_HOST_CCIDS    20
-extern int dccp_available_ccids(uint8_t **ccid_array);
+int dccp_available_ccids(uint8_t **ccid_array);
diff --git a/para.h b/para.h
index bbf91330855610ce0316986f2195d5161e3bc2bb..280c282323db369e7d0e84173324016aa425a92d 100644 (file)
--- a/para.h
+++ b/para.h
@@ -222,6 +222,7 @@ enum loglevels {LOGLEVELS, NUM_LOGLEVELS};
 #define PARA_CRIT_LOG(f,...) para_log(LL_CRIT, "%s: " f, __FUNCTION__, ## __VA_ARGS__)
 #define PARA_EMERG_LOG(f,...) para_log(LL_EMERG, "%s: " f, __FUNCTION__, ## __VA_ARGS__)
 
+/** \cond status_items */
 #define STATUS_ITEMS \
        STATUS_ITEM(basename) \
        STATUS_ITEM(status) \
@@ -269,6 +270,7 @@ enum loglevels {LOGLEVELS, NUM_LOGLEVELS};
 enum status_items {STATUS_ITEMS NUM_STAT_ITEMS};
 #undef STATUS_ITEM
 #define STATUS_ITEM(_name) #_name,
+/** \endcond status items */
 
 extern const char *status_item_list[];
 /** Loop over each status item. */
index 5f83b0fe0a35c9e5c31c97828bfa7af2bebc6208..d02ade3ba911a186f0f7e5a57f419ce9074092d0 100644 (file)
 /** \file playlist.c Functions for loading and saving playlists. */
 
 /** Structure used for adding entries to a playlist. */
-struct playlist_info {
+struct playlist_instance {
        /** The name of the playlist. */
        char *name;
        /** The number of entries currently in the playlist. */
        unsigned length;
 };
-static struct playlist_info current_playlist;
+static struct playlist_instance current_playlist;
 
 /**
  * Re-insert an audio file into the tree of admissible files.
@@ -38,7 +38,7 @@ static int playlist_update_audio_file(const struct osl_row *aft_row)
 
 static int add_playlist_entry(char *path, void *data)
 {
-       struct playlist_info *playlist = data;
+       struct playlist_instance *playlist = data;
        struct osl_row *aft_row;
        int ret = aft_get_row_of_path(path, &aft_row);
 
@@ -55,64 +55,34 @@ static int add_playlist_entry(char *path, void *data)
        return 1;
 }
 
-/* returns -E_PLAYLIST_LOADED on _success_ to terminate the loop */
-static int load_playlist(struct osl_row *row, void *data)
-{
-       struct playlist_info *playlist = data;
-       struct osl_object playlist_def;
-       char *playlist_name;
-       int ret;
-
-       ret = pl_get_name_and_def_by_row(row, &playlist_name, &playlist_def);
-       if (ret < 0)
-               goto err;
-       playlist->length = 0;
-       ret = for_each_line(FELF_READ_ONLY, playlist_def.data,
-               playlist_def.size, add_playlist_entry, playlist);
-       osl_close_disk_object(&playlist_def);
-       if (ret < 0)
-               goto err;
-       ret = -E_PLAYLIST_EMPTY;
-       if (!playlist->length)
-               goto err;
-       playlist->name = para_strdup(playlist_name);
-       PARA_NOTICE_LOG("loaded playlist %s (%u files)\n", playlist->name,
-               playlist->length);
-       return -E_PLAYLIST_LOADED;
-err:
-       if (ret != -E_DUMMY_ROW)
-               PARA_NOTICE_LOG("unable to load playlist (%s)\n",
-                       para_strerror(-ret));
-       return 1;
-}
-
 static int check_playlist_path(char *path, void *data)
 {
-       struct para_buffer *pb = data;
+       struct afs_callback_arg *aca = data;
        struct osl_row *aft_row;
        int ret = aft_get_row_of_path(path, &aft_row);
 
        if (ret < 0)
-               para_printf(pb, "%s: %s\n", path, para_strerror(-ret));
+               afs_error(aca, "%s: %s\n", path, para_strerror(-ret));
        return 1; /* do not fail the loop on bad paths */
 }
 
 static int check_playlist(struct osl_row *row, void *data)
 {
-       struct para_buffer *pb = data;
+       struct afs_callback_arg *aca = data;
+       struct para_buffer *pb = &aca->pbout;
        struct osl_object playlist_def;
        char *playlist_name;
        int ret = pl_get_name_and_def_by_row(row, &playlist_name, &playlist_def);
 
        if (ret < 0) { /* log error, but continue */
-               para_printf(pb, "failed to get playlist data: %s\n",
+               afs_error(aca, "failed to get playlist data: %s\n",
                        para_strerror(-ret));
                return 1;
        }
        if (*playlist_name) { /* skip dummy row */
                para_printf(pb, "checking playlist %s...\n", playlist_name);
                for_each_line(FELF_READ_ONLY, playlist_def.data,
-                       playlist_def.size, check_playlist_path, pb);
+                       playlist_def.size, check_playlist_path, aca);
        }
        osl_close_disk_object(&playlist_def);
        return 1;
@@ -129,49 +99,64 @@ static int check_playlist(struct osl_row *row, void *data)
 int playlist_check_callback(struct afs_callback_arg *aca)
 {
        para_printf(&aca->pbout, "checking playlists...\n");
-       return osl(osl_rbtree_loop(playlists_table, BLOBCOL_ID, &aca->pbout,
+       return osl(osl_rbtree_loop(playlists_table, BLOBCOL_ID, aca,
                check_playlist));
 }
 
-/**
- * Close the current playlist.
- *
- * \sa \ref playlist_open().
- */
-void playlist_close(void)
+/** Free all resources of the current playlist, if any. */
+void playlist_unload(void)
 {
        if (!current_playlist.name)
                return;
        free(current_playlist.name);
        current_playlist.name = NULL;
+       current_playlist.length = 0;
 }
 
 /**
- * Open the given playlist.
+ * Populate the score table from the paths of a playlist database object.
  *
- * \param name The name of the playlist to open.
+ * This loads the blob object which corresponds to the given name from the
+ * playlist table. Each line of the blob is regarded as a path which is looked
+ * up in the audio file table. If the path lookup succeeds, a reference to the
+ * corresponding row of the audio file table is added to the score table.
  *
- * Files which are listed in the playlist, but not contained in the database
- * are ignored.  This is not considered an error.
+ * \param name The name of the playlist to load.
+ * \param msg Error message or playlist info is returned here.
  *
- * \return Standard.
+ * \return The length of the loaded playlist on success, negative error code
+ * else. Files which are listed in the playlist, but are not contained in the
+ * database are ignored. This is not considered an error.
  */
-int playlist_open(const char *name)
+int playlist_load(const char *name, char **msg)
 {
-       struct osl_object obj;
        int ret;
-       struct osl_row *row;
+       struct playlist_instance *playlist = &current_playlist;
+       struct osl_object playlist_def;
 
-       obj.data = (char *)name;
-       obj.size = strlen(obj.data);
-       ret = osl(osl_get_row(playlists_table, BLOBCOL_NAME, &obj, &row));
+       ret = pl_get_def_by_name(name, &playlist_def);
        if (ret < 0) {
-               PARA_NOTICE_LOG("failed to load playlist %s\n", name);
+               *msg = make_message("could not read playlist %s\n", name);
                return ret;
        }
-       playlist_close();
-       ret = load_playlist(row, &current_playlist);
-       return (ret == -E_PLAYLIST_LOADED)? current_playlist.length : ret;
+       playlist_unload();
+       ret = for_each_line(FELF_READ_ONLY, playlist_def.data,
+               playlist_def.size, add_playlist_entry, playlist);
+       osl_close_disk_object(&playlist_def);
+       if (ret < 0)
+               goto err;
+       ret = -E_PLAYLIST_EMPTY;
+       if (!playlist->length)
+               goto err;
+       playlist->name = para_strdup(name);
+       *msg = make_message("loaded playlist %s (%u files)\n", playlist->name,
+               playlist->length);
+       /* success */
+       return current_playlist.length;
+err:
+       PARA_NOTICE_LOG("unable to load playlist %s\n", name);
+       *msg = make_message("unable to load playlist %s\n", name);
+       return ret;
 }
 
 static int search_path(char *path, void *data)
@@ -183,17 +168,14 @@ static int search_path(char *path, void *data)
 
 static int handle_audio_file_event(enum afs_events event, void *data)
 {
-       int ret, was_admissible = 0, is_admissible;
+       int ret;
+       bool was_admissible = false, is_admissible;
        struct osl_object playlist_def;
        char *new_path;
        const struct osl_row *row = data;
 
-       if (event == AUDIO_FILE_RENAME) {
-               ret = row_belongs_to_score_table(row, NULL);
-               if (ret < 0)
-                       return ret;
-               was_admissible = ret;
-       }
+       if (event == AUDIO_FILE_RENAME)
+               was_admissible = row_belongs_to_score_table(row);
        ret = get_audio_file_path_of_row(row, &new_path);
        if (ret < 0)
                return ret;
@@ -229,7 +211,6 @@ static int handle_audio_file_event(enum afs_events event, void *data)
 int playlists_event_handler(enum afs_events event,
        __a_unused struct para_buffer *pb, void *data)
 {
-       int ret;
        struct afsi_change_event_data *aced = data;
 
        if (!current_playlist.name)
@@ -241,10 +222,7 @@ int playlists_event_handler(enum afs_events event,
        case AUDIO_FILE_ADD:
                return handle_audio_file_event(event, data);
        case AUDIO_FILE_REMOVE:
-               ret = row_belongs_to_score_table(data, NULL);
-               if (ret < 0)
-                       return ret;
-               if (!ret)
+               if (!row_belongs_to_score_table(data))
                        return 1;
                current_playlist.length--;
                return score_delete(data);
diff --git a/score.c b/score.c
index 54af56f7c60100ad65ac09cafb52e7769514c958..10cd254a8dba2926614d0a76ffa29cad90e74792 100644 (file)
--- a/score.c
+++ b/score.c
@@ -76,18 +76,6 @@ static struct osl_table_description score_table_desc = {
        .column_descriptions = score_cols
 };
 
-/**
- * Compute the number of files in score table.
- *
- * \param num Result is returned here.
- *
- * \return Positive on success, negative on errors.
- */
-int get_num_admissible_files(unsigned *num)
-{
-       return osl(osl_get_num_rows(score_table, num));
-}
-
 /* On errors (negative return value) the content of score is undefined. */
 static int get_score_of_row(void *score_row, long *score)
 {
@@ -132,16 +120,6 @@ int score_add(const struct osl_row *aft_row, long score)
        return ret;
 }
 
-static int get_nth_score(unsigned n, long *score)
-{
-       struct osl_row *row;
-       int ret = osl(osl_get_nth_row(score_table, SCORECOL_SCORE, n, &row));
-
-       if (ret < 0)
-               return ret;
-       return get_score_of_row(row, score);
-}
-
 /**
  * Replace a row of the score table.
  *
@@ -156,7 +134,7 @@ static int get_nth_score(unsigned n, long *score)
  */
 int score_update(const struct osl_row *aft_row, long percent)
 {
-       struct osl_row *row;
+       struct osl_row *row, *rrow; /* score row, reference row */
        long new_score;
        unsigned n, new_pos;
        struct osl_object obj = {.data = (struct osl_row *)aft_row,
@@ -167,11 +145,14 @@ int score_update(const struct osl_row *aft_row, long percent)
                return 1;
        if (ret < 0)
                return ret;
-       ret = get_num_admissible_files(&n);
+       ret = osl(osl_get_num_rows(score_table, &n));
        if (ret < 0)
                return ret;
        new_pos = 1 + (n - 1) * percent / 100;
-       ret = get_nth_score(new_pos, &new_score);
+       ret = osl(osl_get_nth_row(score_table, SCORECOL_SCORE, new_pos, &rrow));
+       if (ret < 0)
+               return ret;
+       ret = get_score_of_row(rrow, &new_score);
        if (ret < 0)
                return ret;
        new_score--;
@@ -215,17 +196,15 @@ static int get_score_row_from_aft_row(const struct osl_row *aft_row,
 }
 
 /**
- * Loop over all files in the score table.
- *
- * \param data A pointer to arbitrary data.
- * \param func Function to be called for each admissible file.
+ * Call the given function for each row of the score table.
  *
- * \return The return value of the underlying call to osl_rbtree_loop().
+ * \param func Callback, called once per row.
+ * \param data Passed verbatim to the callback.
  *
- * This is used for the ls command. The \a data parameter is passed as the
- * second argument to \a func.
+ * \return The return value of the underlying call to osl_rbtree_loop(). The
+ * loop terminates early if the callback returns negative.
  */
-int admissible_file_loop(void *data, osl_rbtree_loop_func *func)
+int score_loop(osl_rbtree_loop_func *func, void *data)
 {
        return osl(osl_rbtree_loop(score_table, SCORECOL_SCORE, data, func));
 }
@@ -276,27 +255,20 @@ int score_delete(const struct osl_row *aft_row)
  * Find out whether an audio file is contained in the score table.
  *
  * \param aft_row The row of the audio file table.
- * \param rank Result pointer
  *
- * \return Positive, if \a aft_row belongs to the audio file table,
- * zero if not, negative on errors. If \a aft_row was found, and \a rank
- * is not \p NULL, the rank of \a aft_row is returned in \a rank.
+ * \return If the lookup operation fails for any other reason than "not found",
+ * the function aborts the current process (afs), since this is considered a
+ * fatal error that should never happen.
  */
-int row_belongs_to_score_table(const struct osl_row *aft_row, unsigned *rank)
+bool row_belongs_to_score_table(const struct osl_row *aft_row)
 {
        struct osl_row *score_row;
        int ret = get_score_row_from_aft_row(aft_row, &score_row);
 
        if (ret == -OSL_ERRNO_TO_PARA_ERROR(E_OSL_RB_KEY_NOT_FOUND))
-               return 0;
-       if (ret < 0)
-               return ret;
-       if (!rank)
-               return 1;
-       ret = osl(osl_get_rank(score_table, score_row, SCORECOL_SCORE, rank));
-       if (ret < 0)
-               return ret;
-       return 1;
+               return false;
+       assert(ret >= 0);
+       return true;
 }
 
 static void score_close(void)
@@ -307,36 +279,21 @@ static void score_close(void)
 
 static int score_open(__a_unused const char *dir)
 {
-       score_table_desc.dir = NULL; /* this table has only volatile columns */
-       return osl(osl_open_table(&score_table_desc, &score_table));
+       assert(osl_open_table(&score_table_desc, &score_table) >= 0);
+       return 1;
 }
 
 /**
  * Remove all entries from the score table, but keep the table open.
- *
- * \return Standard.
  */
-int clear_score_table(void)
+void score_clear(void)
 {
        score_close();
-       return score_open(NULL);
+       score_open(NULL);
 }
 
-static int score_event_handler(__a_unused enum afs_events event,
-               __a_unused struct para_buffer *pb, __a_unused void *data)
-{
-       return 1;
-}
-
-/**
- * Initialize the scoring subsystem.
- *
- * \param t The members of \a t are filled in by the function.
- */
-void score_init(struct afs_table *t)
-{
-       t->name = score_table_desc.name;
-       t->open = score_open;
-       t->close = score_close;
-       t->event_handler = score_event_handler;
-}
+/** The score table stores (aft row, score) pairs in memory. */
+const struct afs_table_operations score_ops = {
+       .open = score_open,
+       .close = score_close,
+};
index bdafeaf296e026508698c306e4b498e0710a9cee..d9a6aa37057415117c32a6a18c33d81977499392 100644 (file)
--- a/signal.c
+++ b/signal.c
@@ -72,10 +72,13 @@ static void generic_signal_handler(int s)
                errno = save_errno;
                return;
        }
-       if (ret < 0)
-               PARA_EMERG_LOG("%s\n", strerror(errno));
-       else
-               PARA_EMERG_LOG("short write to signal pipe\n");
+       /*
+        * This is a fatal error which should never happen. We must not call
+        * PARA_XXX_LOG() here because this might acquire the log mutex which
+        * is already taken by the main program if the interrupt occurs while a
+        * log message is being printed. The mutex will not be released as long
+        * as this signal handler is running, so a deadlock ensues.
+        */
        exit(EXIT_FAILURE);
 }
 
index 8d1274bc1e919e703f3856cc8159269d33756f2c..365b6863087ca78c1bdc446099ff7f17c8b15c6c 100644 (file)
@@ -168,7 +168,7 @@ static int udp_recv_open(struct receiver_node *rn)
        uint32_t port = RECV_CMD_OPT_UINT32_VAL(UDP, PORT, lpr);
        int ret;
 
-       ret = makesock(IPPROTO_UDP, 1, host, port, NULL);
+       ret = makesock(IPPROTO_UDP, true /* passive */, host, port, NULL);
        if (ret < 0)
                return ret;
        rn->fd = ret;
index 289479878592f6b50e5e31bb30a1098e71588865..fe001025bc1ad6c73c0e64c8589977db1e3203af 100644 (file)
@@ -187,7 +187,7 @@ static int udp_resolve_target(const char *url, struct sender_command_data *scd)
                return ret;
        port = scd->port > 0 ? scd->port : OPT_UINT32_VAL(UDP_DEFAULT_PORT);
 
-       ret = para_connect_simple(IPPROTO_UDP, scd->host, port);
+       ret = para_connect(IPPROTO_UDP, scd->host, port);
        if (ret < 0)
                return ret;
 
@@ -336,7 +336,7 @@ static int udp_com_add(struct sender_command_data *scd)
 
        sc->private_data = ut;
        sc->fd = -1;
-       ret = para_connect_simple(IPPROTO_UDP, scd->host, scd->port);
+       ret = para_connect(IPPROTO_UDP, scd->host, scd->port);
        if (ret < 0)
                goto err;
        sc->fd = ret;
diff --git a/vss.c b/vss.c
index dd4ac2fbc96f54064f0dacd38ef2c9c39fa4f065..cd55851c366cec61c0bf7bc9501609fc2fa8a6c5 100644 (file)
--- a/vss.c
+++ b/vss.c
@@ -1014,13 +1014,8 @@ err:
 }
 
 /**
- * Main sending function.
- *
- * This function gets called from vss_post_monitor(). It checks whether the next
- * chunk of data should be pushed out. It obtains a pointer to the data to be
- * sent out as well as its length from mmd->afd.afhi. This information is then
- * passed to each supported sender's send() function as well as to the send()
- * functions of each registered fec client.
+ * If the next chunk needs to be sent, pass a pointer to the chunk data to all
+ * registered fec clients and to each sender's ->send() method.
  */
 static void vss_send(struct vss_task *vsst)
 {