blob_get_name_by_id(): Treat id of dummy row as invalid.
[paraslash.git] / afs.c
diff --git a/afs.c b/afs.c
index a5ec7b2f7cafc1ef37edfdc767e342d3d7769f4e..0b24a3b8cdb7c53f70b09bba317f9afe523d9320 100644 (file)
--- a/afs.c
+++ b/afs.c
@@ -97,10 +97,11 @@ static char *current_mop; /* mode or playlist specifier. NULL means dummy mood *
 /**
  * A random number used to "authenticate" the connection.
  *
- * para_server picks this number by random before forking the afs process.  The
- * command handlers write this number together with the id of the shared memory
- * area containing the query. This way, a malicious local user has to know this
- * number to be able to cause the afs process to crash by sending fake queries.
+ * para_server picks this number by random before it forks the afs process. The
+ * command handlers know this number as well and write it to the afs socket,
+ * together with the id of the shared memory area which contains the payload of
+ * the afs command. A local process has to know this number to abuse the afs
+ * service provided by the local socket.
  */
 extern uint32_t afs_socket_cookie;
 
@@ -130,7 +131,7 @@ extern uint32_t afs_socket_cookie;
  */
 struct callback_query {
        /** The function to be called. */
-       callback_function *handler;
+       afs_callback *handler;
        /** The number of bytes of the query */
        size_t query_size;
 };
@@ -201,7 +202,7 @@ static int dispatch_result(int result_shmid, callback_result_handler *handler,
  *
  * \sa send_option_arg_callback_request(), send_standard_callback_request().
  */
-int send_callback_request(callback_function *f, struct osl_object *query,
+int send_callback_request(afs_callback *f, struct osl_object *query,
                callback_result_handler *result_handler,
                void *private_result_data)
 {
@@ -299,7 +300,7 @@ out:
  * \sa send_standard_callback_request(), send_callback_request().
  */
 int send_option_arg_callback_request(struct osl_object *options,
-               int argc,  char * const * const argv, callback_function *f,
+               int argc,  char * const * const argv, afs_callback *f,
                callback_result_handler *result_handler,
                void *private_result_data)
 {
@@ -341,7 +342,7 @@ int send_option_arg_callback_request(struct osl_object *options,
  * send_option_arg_callback_request().
  */
 int send_standard_callback_request(int argc,  char * const * const argv,
-               callback_function *f, callback_result_handler *result_handler,
+               afs_callback *f, callback_result_handler *result_handler,
                void *private_result_data)
 {
        return send_option_arg_callback_request(NULL, argc, argv, f, result_handler,
@@ -423,7 +424,7 @@ static int pass_afd(int fd, char *buf, size_t size)
 {
        struct msghdr msg = {.msg_iov = NULL};
        struct cmsghdr *cmsg;
-       char control[255];
+       char control[255] __a_aligned(8);
        int ret;
        struct iovec iov;
 
@@ -501,7 +502,6 @@ static int activate_mood_or_playlist(char *arg, int *num_admissible)
        enum play_mode mode;
        int ret;
 
-       PARA_INFO_LOG("new playlist: %s\n", arg);
        if (!arg) {
                ret = change_current_mood(NULL); /* always successful */
                mode = PLAY_MODE_MOOD;
@@ -574,7 +574,7 @@ int afs_cb_result_handler(struct osl_object *result, uint8_t band,
        }
 }
 
-void flush_and_free_pb(struct para_buffer *pb)
+static void flush_and_free_pb(struct para_buffer *pb)
 {
        int ret;
        struct afs_max_size_handler_data *amshd = pb->private_data;
@@ -588,22 +588,14 @@ void flush_and_free_pb(struct para_buffer *pb)
        free(pb->buf);
 }
 
-static int com_select_callback(int fd, const struct osl_object *query)
+static int com_select_callback(struct afs_callback_arg *aca)
 {
-       struct para_buffer pb = {
-               .max_size = shm_get_shmmax(),
-               .private_data = &(struct afs_max_size_handler_data) {
-                       .fd = fd,
-                       .band = SBD_OUTPUT
-               },
-               .max_size_handler = afs_max_size_handler,
-       };
-       char *arg = query->data;
+       char *arg = aca->query.data;
        int num_admissible, ret;
 
        ret = clear_score_table();
        if (ret < 0) {
-               para_printf(&pb, "could not clear score table: %s\n",
+               para_printf(&aca->pbout, "could not clear score table: %s\n",
                        para_strerror(-ret));
                return ret;
        }
@@ -614,20 +606,19 @@ static int com_select_callback(int fd, const struct osl_object *query)
        ret = activate_mood_or_playlist(arg, &num_admissible);
        if (ret >= 0)
                goto out;
-       para_printf(&pb, "could not activate %s: %s\n"
+       /* ignore subsequent errors (but log them) */
+       para_printf(&aca->pbout, "could not activate %s: %s\n"
                "switching back to %s\n",
                arg, para_strerror(-ret), current_mop? current_mop : "dummy");
-       /* ignore subsequent errors (but log them) */
        ret = activate_mood_or_playlist(current_mop, &num_admissible);
        if (ret >= 0)
                goto out;
-       para_printf(&pb, "could not activate %s: %s\nswitching to dummy\n",
+       para_printf(&aca->pbout, "could not activate %s: %s\nswitching to dummy\n",
                current_mop, para_strerror(-ret));
        activate_mood_or_playlist(NULL, &num_admissible);
 out:
-       para_printf(&pb, "activated %s (%d admissible files)\n",
+       para_printf(&aca->pbout, "activated %s (%d admissible files)\n",
                current_mop? current_mop : "dummy mood", num_admissible);
-       flush_and_free_pb(&pb);
        return ret;
 }
 
@@ -702,7 +693,7 @@ static int make_database_dir(void)
 
        get_database_dir();
        ret = para_mkdir(database_dir, 0777);
-       if (ret >= 0 || is_errno(-ret, EEXIST))
+       if (ret >= 0 || ret == -ERRNO_TO_PARA_ERROR(EEXIST))
                return 1;
        return ret;
 }
@@ -849,16 +840,22 @@ static int call_callback(int fd, int query_shmid)
 {
        void *query_shm;
        struct callback_query *cq;
-       struct osl_object query;
        int ret, ret2;
+       struct afs_callback_arg aca = {.fd = fd};
 
        ret = shm_attach(query_shmid, ATTACH_RW, &query_shm);
        if (ret < 0)
                return ret;
        cq = query_shm;
-       query.data = (char *)query_shm + sizeof(*cq);
-       query.size = cq->query_size;
-       ret = cq->handler(fd, &query);
+       aca.query.data = (char *)query_shm + sizeof(*cq);
+       aca.query.size = cq->query_size;
+       aca.pbout.max_size = shm_get_shmmax();
+       aca.pbout.max_size_handler = afs_max_size_handler;
+       aca.pbout.private_data = &(struct afs_max_size_handler_data) {
+               .fd = fd,
+               .band = SBD_OUTPUT
+       };
+       ret = cq->handler(&aca);
        ret2 = shm_detach(query_shm);
        if (ret2 < 0) {
                if (ret < 0) /* ignore (but log) detach error */
@@ -867,6 +864,7 @@ static int call_callback(int fd, int query_shmid)
                else
                        ret = ret2;
        }
+       flush_and_free_pb(&aca.pbout);
        if (ret < 0) {
                ret2 = pass_buffer_as_shm(fd, SBD_AFS_CB_FAILURE,
                        (const char *)&ret, sizeof(ret));
@@ -1033,17 +1031,10 @@ out:
        exit(EXIT_FAILURE);
 }
 
-static int create_tables_callback(int fd, const struct osl_object *query)
+static int com_init_callback(struct afs_callback_arg *aca)
 {
-       uint32_t table_mask = *(uint32_t *)query->data;
+       uint32_t table_mask = *(uint32_t *)aca->query.data;
        int i, ret;
-       struct para_buffer pb = {
-               .max_size = shm_get_shmmax(),
-               .private_data = &(struct afs_max_size_handler_data) {
-                       .fd = fd,
-                       .band = SBD_OUTPUT
-               }
-       };
 
        close_afs_tables();
        for (i = 0; i < NUM_AFS_TABLES; i++) {
@@ -1054,16 +1045,19 @@ static int create_tables_callback(int fd, const struct osl_object *query)
                if (!t->create)
                        continue;
                ret = t->create(database_dir);
-               if (ret < 0)
+               if (ret < 0) {
+                       para_printf(&aca->pbout, "cannot create table %s\n",
+                               t->name);
                        goto out;
-               para_printf(&pb, "successfully created %s table\n", t->name);
+               }
+               para_printf(&aca->pbout, "successfully created %s table\n",
+                       t->name);
        }
        ret = open_afs_tables();
-out:
        if (ret < 0)
-               para_printf(&pb, "%s\n", para_strerror(-ret));
-       flush_and_free_pb(&pb);
-       return 0;
+               para_printf(&aca->pbout, "cannot open afs tables\n");
+out:
+       return ret;
 }
 
 int com_init(struct command_context *cc)
@@ -1091,7 +1085,7 @@ int com_init(struct command_context *cc)
                                return -E_BAD_TABLE_NAME;
                }
        }
-       return send_callback_request(create_tables_callback, &query,
+       return send_callback_request(com_init_callback, &query,
                afs_cb_result_handler, cc);
 }
 
@@ -1106,7 +1100,9 @@ enum com_check_flags {
        /** Check the mood table. */
        CHECK_MOODS = 2,
        /** Check the playlist table. */
-       CHECK_PLAYLISTS = 4
+       CHECK_PLAYLISTS = 4,
+       /** Check the attribute table against the audio file table. */
+       CHECK_ATTS = 8
 };
 
 int com_check(struct command_context *cc)
@@ -1126,6 +1122,10 @@ int com_check(struct command_context *cc)
                        flags |= CHECK_AFT;
                        continue;
                }
+               if (!strcmp(arg, "-A")) {
+                       flags |= CHECK_ATTS;
+                       continue;
+               }
                if (!strcmp(arg, "-p")) {
                        flags |= CHECK_PLAYLISTS;
                        continue;
@@ -1146,6 +1146,12 @@ int com_check(struct command_context *cc)
                if (ret < 0)
                        return ret;
        }
+       if (flags & CHECK_ATTS) {
+               ret = send_callback_request(attribute_check_callback, NULL,
+                       afs_cb_result_handler, cc);
+               if (ret < 0)
+                       return ret;
+       }
        if (flags & CHECK_PLAYLISTS) {
                ret = send_callback_request(playlist_check_callback,
                        NULL, afs_cb_result_handler, cc);
@@ -1168,10 +1174,14 @@ int com_check(struct command_context *cc)
  * \param pb May be \p NULL.
  * \param data Type depends on \a event.
  *
- * This function calls the table handlers of all tables and passes \a pb and \a
- * data verbatim. It's up to the handlers to interpret the \a data pointer.
+ * This function calls each table event handler, passing \a pb and \a data
+ * verbatim. It's up to the handlers to interpret the \a data pointer. If a
+ * handler returns negative, the loop is aborted.
+ *
+ * \return The (negative) error code of the first handler that failed, or non-negative
+ * if all handlers succeeded.
  */
-void afs_event(enum afs_events event, struct para_buffer *pb,
+__must_check int afs_event(enum afs_events event, struct para_buffer *pb,
                void *data)
 {
        int i, ret;
@@ -1181,10 +1191,13 @@ void afs_event(enum afs_events event, struct para_buffer *pb,
                if (!t->event_handler)
                        continue;
                ret = t->event_handler(event, pb, data);
-               if (ret < 0)
+               if (ret < 0) {
                        PARA_CRIT_LOG("table %s, event %d: %s\n", t->name,
                                event, para_strerror(-ret));
+                       return ret;
+               }
        }
+       return 1;
 }
 
 /**