+int com_setatt(struct command_context *cc)
+{
+ if (cc->argc < 3)
+ return -E_ATTR_SYNTAX;
+ return send_standard_callback_request(cc->argc - 1, cc->argv + 1,
+ com_setatt_callback, afs_cb_result_handler, cc);
+}
+
+static int afs_stat_callback(struct afs_callback_arg *aca)
+{
+ int *parser_friendly = aca->query.data;
+ char *buf = *parser_friendly?
+ parser_friendly_status_items : status_items;
+
+ if (!buf)
+ return 0;
+ return pass_buffer_as_shm(aca->fd, SBD_OUTPUT, buf, strlen(buf));
+}
+
+/**
+ * Get the current afs status items from the afs process and send it.
+ *
+ * \param cc The command context, used e.g. for data encryption.
+ * \param parser_friendly Whether parser-friendly output format should be used.
+ *
+ * As the contents of the afs status items change in time and the command
+ * handler only has a COW version created at fork time, it can not send
+ * up-to-date afs status items directly. Therefore the usual callback mechanism
+ * is used to pass the status items from the afs process to the command handler
+ * via a shared memory area and a pipe.
+ *
+ * \return The return value of the underlying call to \ref send_callback_request().
+ */
+int send_afs_status(struct command_context *cc, int parser_friendly)
+{
+ struct osl_object query = {.data = &parser_friendly,
+ .size = sizeof(parser_friendly)};
+
+ return send_callback_request(afs_stat_callback, &query,
+ afs_cb_result_handler, cc);
+}
+
+/* returns success on non-fatal errors to keep the loop going */
+static int check_audio_file(struct osl_row *row, void *data)
+{
+ char *path;
+ struct para_buffer *pb = data;
+ struct stat statbuf;
+ int ret = get_audio_file_path_of_row(row, &path);
+ struct afs_info afsi;
+ char *blob_name;
+
+ if (ret < 0) {
+ para_printf(pb, "%s\n", para_strerror(-ret));
+ return ret;
+ }
+ if (stat(path, &statbuf) < 0)
+ para_printf(pb, "%s: stat error (%s)\n", path, strerror(errno));
+ else if (!S_ISREG(statbuf.st_mode))
+ para_printf(pb, "%s: not a regular file\n", path);
+ ret = get_afsi_of_row(row, &afsi);
+ if (ret < 0) {
+ para_printf(pb, "%s: %s\n", path, para_strerror(-ret));
+ return 1;
+ }
+ ret = lyr_get_name_by_id(afsi.lyrics_id, &blob_name);
+ if (ret < 0)
+ para_printf(pb, "%s lyrics id %u: %s\n", path, afsi.lyrics_id,
+ para_strerror(-ret));
+ ret = img_get_name_by_id(afsi.image_id, &blob_name);
+ if (ret < 0)
+ para_printf(pb, "%s image id %u: %s\n", path, afsi.image_id,
+ para_strerror(-ret));
+ return 0;
+}
+
+/**
+ * Check the audio file table for inconsistencies.
+ *
+ * \param aca Only ->pbout is used for diagnostics.
+ *
+ * \return Standard. Inconsistencies are reported but not regarded as an error.
+ *
+ * \sa com_check().
+ */
+int aft_check_callback(struct afs_callback_arg *aca)
+{
+ para_printf(&aca->pbout, "checking audio file table...\n");
+ return audio_file_loop(&aca->pbout, check_audio_file);
+}
+
+struct aft_check_atts_data {
+ uint64_t att_mask;
+ struct para_buffer *pb;
+};
+
+static int check_atts_of_audio_file(struct osl_row *row, void *data)
+{
+ struct aft_check_atts_data *acad = data;
+ int ret;
+ struct afs_info afsi;
+ char *path;
+ uint64_t bad_bits;
+
+ ret = get_afsi_of_row(row, &afsi);
+ if (ret < 0) {
+ para_printf(acad->pb, "cannot get afsi\n");
+ return ret;
+ }
+ bad_bits = afsi.attributes & ~acad->att_mask;
+ if (bad_bits == 0) /* OK */
+ return 0;
+ ret = get_audio_file_path_of_row(row, &path);
+ if (ret < 0) {
+ para_printf(acad->pb, "cannot get path\n");
+ return ret;
+ }
+ para_printf(acad->pb, "invalid attribute bits (%" PRIu64 "): %s\n",
+ bad_bits, path);
+ /* return success to keep looping */
+ return 1;
+}
+
+/**
+ * Iterate over all audio files and check the attribute bit mask.
+ *
+ * \param att_mask The mask of all valid attributes.
+ * \param pb Used for reporting inconsistencies.
+ *
+ * This reads the attribute bit mask of each audio file from the afs info
+ * structure stored in the audio file table and verifies that all set bits are
+ * also turned on in \a att_mask, i.e., correspond to an attribute of the
+ * attribute table. Audio files for which this is not the case are reported via
+ * \a pb.
+ *
+ * \return Standard. Inconsistencies are not regarded as errors.
+ *
+ * \sa \ref attribute_check_callback().
+ */
+int aft_check_attributes(uint64_t att_mask, struct para_buffer *pb)
+{
+ struct aft_check_atts_data acad = {.att_mask = att_mask, .pb = pb};
+
+ para_printf(pb, "checking attributes, mask: %" PRIx64 "\n", att_mask);
+ return audio_file_loop(&acad, check_atts_of_audio_file);
+}
+