Complete the afs callback conversion.
[paraslash.git] / aft.c
diff --git a/aft.c b/aft.c
index d6abc13d9c22854f7a2f12092f5a79e70998231c..0ffacbe4f4b0a52ae6cfc73625d3cc5f29de8100 100644 (file)
--- a/aft.c
+++ b/aft.c
@@ -12,6 +12,8 @@
 #include "string.h"
 #include <sys/mman.h>
 #include <fnmatch.h>
+#include <sys/shm.h>
+
 #include "afh.h"
 #include "afs.h"
 #include "net.h"
@@ -688,7 +690,7 @@ int open_and_update_audio_file(struct osl_row *aft_row, long score,
                        .score = score,
                        .hash = file_hash
                };
-               struct para_buffer pb = {.buf = NULL};
+               struct para_buffer pb = {.max_size = VERBOSE_LS_OUTPUT_SIZE - 1};
                ret = make_status_items(&d, &pb);
                if (ret < 0)
                        goto err;
@@ -835,19 +837,24 @@ static int print_chunk_table(struct ls_data *d, struct para_buffer *b)
                AFTCOL_CHUNKS, &chunk_table_obj);
        if (ret < 0)
                return ret;
-       para_printf(b, "%s\n"
+       ret = para_printf(b, "%s\n"
                "chunk_time: %lu:%lu\nchunk_offsets: ",
                d->path,
                (long unsigned) d->afhi.chunk_tv.tv_sec,
                (long unsigned) d->afhi.chunk_tv.tv_usec
        );
+       if (ret < 0)
+               goto out;
        buf = chunk_table_obj.data;
-       for (i = 0; i <= d->afhi.chunks_total; i++)
-               para_printf(b, "%u ",
-                       (unsigned) read_u32(buf + 4 * i));
+       for (i = 0; i <= d->afhi.chunks_total; i++) {
+               ret = para_printf(b, "%u ", (unsigned) read_u32(buf + 4 * i));
+               if (ret < 0)
+                       goto out;
+       }
+       ret = para_printf(b, "\n");
+out:
        osl_close_disk_object(&chunk_table_obj);
-       para_printf(b, "\n");
-       return 1;
+       return ret;
 }
 
 static int print_list_item(struct ls_data *d, struct ls_options *opts,
@@ -865,10 +872,8 @@ static int print_list_item(struct ls_data *d, struct ls_options *opts,
        char asc_hash[2 * HASH_SIZE + 1];
        char *att_lines, *lyrics_lines, *image_lines, *filename_lines;
 
-       if (opts->mode == LS_MODE_SHORT) {
-               para_printf(b, "%s\n", d->path);
-               return 1;
-       }
+       if (opts->mode == LS_MODE_SHORT)
+               return para_printf(b, "%s\n", d->path);
        if (opts->mode == LS_MODE_CHUNKS)
                return print_chunk_table(d, b);
        get_attribute_bitmap(&afsi->attributes, att_buf);
@@ -885,7 +890,7 @@ static int print_list_item(struct ls_data *d, struct ls_options *opts,
        }
 
        if (opts->mode == LS_MODE_LONG) {
-               para_printf(b,
+               return para_printf(b,
                        "%s"    /* score */
                        "%s "   /* attributes */
                        "%*d "  /* image_id  */
@@ -911,7 +916,6 @@ static int print_list_item(struct ls_data *d, struct ls_options *opts,
                        last_played_time,
                        d->path
                );
-               return 1;
        }
        hash_to_asc(d->hash, asc_hash);
        att_lines = make_attribute_lines(att_buf, afsi);
@@ -920,14 +924,16 @@ static int print_list_item(struct ls_data *d, struct ls_options *opts,
        filename_lines = make_filename_lines(d->path, opts->flags);
        if (opts->mode == LS_MODE_MBOX) {
                const char *bn = para_basename(d->path);
-               para_printf(b,
+               ret = para_printf(b,
                        "From foo@localhost %s\n"
                        "Received: from\nTo: bar\nFrom: a\n"
                        "Subject: %s\n\n",
                        last_played_time,
                        bn? bn : "?");
+               if (ret < 0)
+                       return ret;
        }
-       para_printf(b,
+       ret = para_printf(b,
                "%s" /* filename stuff */
                "%s%s%s%s" /* score */
                "%s\n" /* attributes */
@@ -966,11 +972,13 @@ static int print_list_item(struct ls_data *d, struct ls_options *opts,
                status_item_list[SI_CHUNK_TIME], tv2ms(&afhi->chunk_tv),
                status_item_list[SI_NUM_CHUNKS], afhi->chunks_total
        );
+       if (ret < 0)
+               return ret;
        if (opts->mode == LS_MODE_MBOX) {
                struct osl_object lyrics_def;
                lyr_get_def_by_id(afsi->lyrics_id, &lyrics_def);
                if (lyrics_def.data) {
-                       para_printf(b, "Lyrics:\n~~~~~~~\n%s",
+                       ret = para_printf(b, "Lyrics:\n~~~~~~~\n%s",
                                (char *)lyrics_def.data);
                        osl_close_disk_object(&lyrics_def);
                }
@@ -979,7 +987,7 @@ static int print_list_item(struct ls_data *d, struct ls_options *opts,
        free(lyrics_lines);
        free(image_lines);
        free(filename_lines);
-       return 1;
+       return ret;
 }
 
 void make_empty_status_items(char *buf)
@@ -1237,12 +1245,12 @@ static int prepare_ls_row(struct osl_row *row, void *ls_opts)
        return 1;
 }
 
-static int com_ls_callback(const struct osl_object *query,
-               struct osl_object *ls_output)
+static void com_ls_callback(int fd, const struct osl_object *query)
 {
        struct ls_options *opts = query->data;
        char *p, *pattern_start = (char *)query->data + sizeof(*opts);
-       struct para_buffer b = {.buf = NULL, .size = 0};
+       struct para_buffer b = {.max_size = SHMMAX,
+               .max_size_handler = pass_buffer_as_shm, .private_data = &fd};
        int i = 0, ret;
        time_t current_time;
 
@@ -1273,22 +1281,21 @@ static int com_ls_callback(const struct osl_object *query,
                for (i = opts->num_matching_paths - 1; i >= 0; i--) {
                        ret = print_list_item(opts->data_ptr[i], opts, &b, current_time);
                        if (ret < 0)
-                               break;
+                               goto out;
                }
        else
                for (i = 0; i < opts->num_matching_paths; i++) {
                        ret = print_list_item(opts->data_ptr[i], opts, &b, current_time);
                        if (ret < 0)
-                               break;
+                               goto out;
                }
-       ret = 1;
 out:
-       ls_output->data = b.buf;
-       ls_output->size = b.offset;
+       if (b.offset)
+               pass_buffer_as_shm(b.buf, b.offset, &fd);
+       free(b.buf);
        free(opts->data);
        free(opts->data_ptr);
        free(opts->patterns);
-       return ret;
 }
 
 /*
@@ -1546,8 +1553,7 @@ enum com_add_flags {
        ADD_FLAG_ALL = 8,
 };
 
-static int com_add_callback(const struct osl_object *query,
-               struct osl_object *result)
+static void com_add_callback(int fd, const struct osl_object *query)
 {
        char *buf = query->data, *path;
        struct osl_row *pb, *aft_row;
@@ -1559,7 +1565,8 @@ static int com_add_callback(const struct osl_object *query,
        char afsi_buf[AFSI_SIZE];
        uint32_t flags = read_u32(buf + CAB_FLAGS_OFFSET);
        struct afs_info default_afsi = {.last_played = 0};
-       struct para_buffer msg = {.buf = NULL};
+       struct para_buffer msg = {.max_size = SHMMAX,
+               .max_size_handler = pass_buffer_as_shm, .private_data = &fd};
 
        hash = (HASH_TYPE *)buf + CAB_HASH_OFFSET;
        hash_to_asc(hash, asc);;
@@ -1574,18 +1581,22 @@ static int com_add_callback(const struct osl_object *query,
        hs = find_hash_sister(hash);
        ret = aft_get_row_of_path(path, &pb);
        if (ret < 0 && ret != -E_RB_KEY_NOT_FOUND)
-               return ret;
+               goto out;
        if (hs && pb && hs == pb && !(flags & ADD_FLAG_FORCE)) {
                if (flags & ADD_FLAG_VERBOSE)
-                       para_printf(&msg, "ignoring duplicate\n");
-               ret = 1;
+                       ret = para_printf(&msg, "ignoring duplicate\n");
+               else
+                       ret = 1;
                goto out;
        }
        if (hs && hs != pb) {
                struct osl_object obj;
                if (pb) { /* hs trumps pb, remove pb */
-                       if (flags & ADD_FLAG_VERBOSE)
-                               para_printf(&msg, "removing path brother\n");
+                       if (flags & ADD_FLAG_VERBOSE) {
+                               ret = para_printf(&msg, "removing path brother\n");
+                               if (ret < 0)
+                                       goto out;
+                       }
                        ret = osl_del_row(audio_file_table, pb);
                        if (ret < 0)
                                goto out;
@@ -1597,7 +1608,9 @@ static int com_add_callback(const struct osl_object *query,
                                AFTCOL_PATH, &obj);
                        if (ret < 0)
                                goto out;
-                       para_printf(&msg, "renamed from %s\n", (char *)obj.data);
+                       ret = para_printf(&msg, "renamed from %s\n", (char *)obj.data);
+                       if (ret < 0)
+                               goto out;
                }
                ret = osl_update_object(audio_file_table, hs, AFTCOL_PATH,
                        &objs[AFTCOL_PATH]);
@@ -1625,9 +1638,12 @@ static int com_add_callback(const struct osl_object *query,
                if (ret < 0)
                        goto out;
                hash_to_asc(old_hash, old_asc);
-               if (flags & ADD_FLAG_VERBOSE)
-                       para_printf(&msg, "file change: %s -> %s\n",
+               if (flags & ADD_FLAG_VERBOSE) {
+                       ret = para_printf(&msg, "file change: %s -> %s\n",
                                old_asc, asc);
+                       if (ret < 0)
+                               goto out;
+               }
                ret = osl_update_object(audio_file_table, pb, AFTCOL_HASH,
                        &objs[AFTCOL_HASH]);
                if (ret < 0)
@@ -1636,8 +1652,11 @@ static int com_add_callback(const struct osl_object *query,
        if (hs || pb) { /* (hs != NULL and pb != NULL) implies hs == pb */
                struct osl_row *row = pb? pb : hs;
                /* update afhi and chunk_table */
-               if (flags & ADD_FLAG_VERBOSE)
-                       para_printf(&msg, "updating afhi and chunk table\n");
+               if (flags & ADD_FLAG_VERBOSE) {
+                       ret = para_printf(&msg, "updating afhi and chunk table\n");
+                       if (ret < 0)
+                               goto out;
+               }
                ret = osl_update_object(audio_file_table, row, AFTCOL_AFHI,
                        &objs[AFTCOL_AFHI]);
                if (ret < 0)
@@ -1650,8 +1669,11 @@ static int com_add_callback(const struct osl_object *query,
                goto out;
        }
        /* new entry, use default afsi */
-       if (flags & ADD_FLAG_VERBOSE)
-               para_printf(&msg, "new file\n");
+       if (flags & ADD_FLAG_VERBOSE) {
+               ret = para_printf(&msg, "new file\n");
+               if (ret < 0)
+                       goto out;
+       }
        default_afsi.last_played = time(NULL) - 365 * 24 * 60 * 60;
        default_afsi.audio_format_id = read_u8(buf + CAB_AUDIO_FORMAT_OFFSET);
 
@@ -1662,12 +1684,10 @@ static int com_add_callback(const struct osl_object *query,
        afs_event(AUDIO_FILE_ADD, &msg, aft_row);
 out:
        if (ret < 0)
-               para_printf(&msg, "%s\n", para_strerror(-ret));
-       if (!msg.buf)
-               return 0;
-       result->data = msg.buf;
-       result->size = msg.offset;
-       return 1;
+               ret = para_printf(&msg, "%s\n", para_strerror(-ret));
+       if (msg.offset)
+               pass_buffer_as_shm(msg.buf, msg.offset, &fd);
+       free(msg.buf);
 }
 
 /** Used by com_add(). */
@@ -1678,36 +1698,28 @@ struct private_add_data {
        uint32_t flags;
 };
 
-static int path_brother_callback(const struct osl_object *query,
-               struct osl_object *result)
+static void path_brother_callback(int fd, const struct osl_object *query)
 {
        char *path = query->data;
        struct osl_row *path_brother;
        int ret = aft_get_row_of_path(path, &path_brother);
        if (ret < 0)
-               return ret;
-       result->data = para_malloc(sizeof(path_brother));
-       result->size = sizeof(path_brother);
-       *(struct osl_row **)(result->data) = path_brother;
-       return 1;
+               return;
+       pass_buffer_as_shm((char *)&path_brother, sizeof(path_brother), &fd);
 }
 
-static int hash_sister_callback(const struct osl_object *query,
-               struct osl_object *result)
+static void hash_sister_callback(int fd, const struct osl_object *query)
 {
        HASH_TYPE *hash = query->data;
        struct osl_row *hash_sister;
 
        hash_sister = find_hash_sister(hash);
        if (!hash_sister)
-               return -E_RB_KEY_NOT_FOUND;
-       result->data = para_malloc(sizeof(hash_sister));
-       result->size = sizeof(hash_sister);
-       *(struct osl_row **)(result->data) = hash_sister;
-       return 1;
+               return;
+       pass_buffer_as_shm((char *)&hash_sister, sizeof(hash_sister), &fd);
 }
 
-int get_row_pointer_from_result(struct osl_object *result, void *private)
+static int get_row_pointer_from_result(struct osl_object *result, void *private)
 {
        struct osl_row **row = private;
        *row = result->data;
@@ -1750,7 +1762,7 @@ static int add_one_audio_file(const char *path, void *private_data)
        query.size = HASH_SIZE;
        ret = send_callback_request(hash_sister_callback, &query,
                get_row_pointer_from_result, &hs);
-       if (ret < 0 && ret != -E_RB_KEY_NOT_FOUND)
+       if (ret < 0)
                goto out_unmap;
        /* Return success if we already know this file. */
        ret = 1;
@@ -1910,26 +1922,28 @@ static int touch_audio_file(__a_unused struct osl_table *table,
        struct afsi_change_event_data aced;
 
        ret = get_afsi_object_of_row(row, &obj);
-       if (ret < 0) {
-               para_printf(&tad->pb, "%s: %s\n", name, para_strerror(-ret));
-               return 1;
-       }
+       if (ret < 0)
+               return para_printf(&tad->pb, "%s: %s\n", name, para_strerror(-ret));
        ret = load_afsi(&old_afsi, &obj);
-       if (ret < 0) {
-               para_printf(&tad->pb, "%s: %s\n", name, para_strerror(-ret));
-               return 1;
-       }
+       if (ret < 0)
+               return para_printf(&tad->pb, "%s: %s\n", name, para_strerror(-ret));
        new_afsi = old_afsi;
        if (no_options) {
                new_afsi.num_played++;
                new_afsi.last_played = time(NULL);
-               if (tad->cto->flags & TOUCH_FLAG_VERBOSE)
-                       para_printf(&tad->pb, "%s: num_played = %u, "
+               if (tad->cto->flags & TOUCH_FLAG_VERBOSE) {
+                       ret = para_printf(&tad->pb, "%s: num_played = %u, "
                                "last_played = now()\n", name,
                                new_afsi.num_played);
+                       if (ret < 0)
+                               return ret;
+               }
        } else {
-               if (tad->cto->flags & TOUCH_FLAG_VERBOSE)
-                       para_printf(&tad->pb, "touching %s\n", name);
+               if (tad->cto->flags & TOUCH_FLAG_VERBOSE) {
+                       ret = para_printf(&tad->pb, "touching %s\n", name);
+                       if (ret < 0)
+                               return ret;
+               }
                if (tad->cto->lyrics_id >= 0)
                        new_afsi.lyrics_id = tad->cto->lyrics_id;
                if (tad->cto->image_id >= 0)
@@ -1947,11 +1961,16 @@ static int touch_audio_file(__a_unused struct osl_table *table,
        return 1;
 }
 
-static int com_touch_callback(const struct osl_object *query,
-               struct osl_object *result)
+static void com_touch_callback(int fd, const struct osl_object *query)
 {
-       struct touch_action_data tad = {.cto = query->data};
-       int ret;
+       struct touch_action_data tad = {.cto = query->data,
+               .pb = {
+                       .max_size = SHMMAX,
+                       .private_data = &fd,
+                       .max_size_handler = pass_buffer_as_shm
+               }
+       };
+       int ret, ret2 = 0;
        struct pattern_match_data pmd = {
                .table = audio_file_table,
                .loop_col_num = AFTCOL_HASH,
@@ -1965,16 +1984,13 @@ static int com_touch_callback(const struct osl_object *query,
                pmd.fnmatch_flags |= FNM_PATHNAME;
        ret = for_each_matching_row(&pmd);
        if (ret < 0)
-               para_printf(&tad.pb, "%s\n", para_strerror(-ret));
+               ret2 = para_printf(&tad.pb, "%s\n", para_strerror(-ret));
        else
                if (!tad.num_matches)
-                       para_printf(&tad.pb, "no matches\n");
-       if (tad.pb.buf) {
-               result->data = tad.pb.buf;
-               result->size = tad.pb.offset;
-               return 1;
-       }
-       return ret < 0? ret : 0;
+                       ret2 = para_printf(&tad.pb, "no matches\n");
+       if (ret2 >= 0 && tad.pb.offset)
+               pass_buffer_as_shm(tad.pb.buf, tad.pb.offset, &fd);
+       free(tad.pb.buf);
 }
 
 int com_touch(int fd, int argc, char * const * const argv)
@@ -2064,23 +2080,32 @@ static int remove_audio_file(__a_unused struct osl_table *table,
                struct osl_row *row, const char *name, void *data)
 {
        struct com_rm_action_data *crd = data;
-       int ret;
+       int ret, ret2;
 
-       if (crd->flags & RM_FLAG_VERBOSE)
-               para_printf(&crd->pb, "removing %s\n", name);
+       if (crd->flags & RM_FLAG_VERBOSE) {
+               ret = para_printf(&crd->pb, "removing %s\n", name);
+               if (ret < 0)
+                       return ret;
+       }
        afs_event(AUDIO_FILE_REMOVE, &crd->pb, row);
        ret = osl_del_row(audio_file_table, row);
        if (ret < 0)
-               para_printf(&crd->pb, "%s: %s\n", name, para_strerror(-ret));
+               ret2 = para_printf(&crd->pb, "%s: %s\n", name,
+                       para_strerror(-ret));
        else
                crd->num_removed++;
-       return 1;
+       return ret;
 }
 
-static int com_rm_callback(const struct osl_object *query,
-               struct osl_object *result)
+static void com_rm_callback(int fd, const struct osl_object *query)
 {
-       struct com_rm_action_data crd = {.flags = *(uint32_t *)query->data};
+       struct com_rm_action_data crd = {.flags = *(uint32_t *)query->data,
+               .pb = {
+                       .max_size = SHMMAX,
+                       .private_data = &fd,
+                       .max_size_handler = pass_buffer_as_shm
+               }
+       };
        int ret;
        struct pattern_match_data pmd = {
                .table = audio_file_table,
@@ -2094,20 +2119,19 @@ static int com_rm_callback(const struct osl_object *query,
        if (crd.flags & RM_FLAG_FNM_PATHNAME)
                pmd.fnmatch_flags |= FNM_PATHNAME;
        ret = for_each_matching_row(&pmd);
-       if (ret < 0)
-               para_printf(&crd.pb, "%s\n", para_strerror(-ret));
+       if (ret < 0) {
+               ret = para_printf(&crd.pb, "%s\n", para_strerror(-ret));
+               return;
+       }
        if (!crd.num_removed && !(crd.flags & RM_FLAG_FORCE))
-               para_printf(&crd.pb, "no matches -- nothing removed\n");
+               ret = para_printf(&crd.pb, "no matches -- nothing removed\n");
        else {
                if (crd.flags & RM_FLAG_VERBOSE)
-                       para_printf(&crd.pb, "removed %u files\n", crd.num_removed);
-       }
-       if (crd.pb.buf) {
-               result->data = crd.pb.buf;
-               result->size = crd.pb.offset;
-               return 1;
+                       ret = para_printf(&crd.pb, "removed %u files\n", crd.num_removed);
        }
-       return ret < 0? ret : 0;
+       if (ret >= 0 && crd.pb.offset)
+               pass_buffer_as_shm(crd.pb.buf, crd.pb.offset, &fd);
+       free(crd.pb.buf);
 }
 
 /* TODO options: -r (recursive) */
@@ -2206,18 +2230,27 @@ static int copy_selector_info(__a_unused struct osl_table *table,
                target_afsi.attributes = cad->source_afsi.attributes;
        save_afsi(&target_afsi, &target_afsi_obj); /* in-place update */
        cad->num_copied++;
-       if (cad->flags & CPSI_FLAG_VERBOSE)
-               para_printf(&cad->pb, "copied afsi to %s\n", name);
+       if (cad->flags & CPSI_FLAG_VERBOSE) {
+               ret = para_printf(&cad->pb, "copied afsi to %s\n", name);
+               if (ret < 0)
+                       return ret;
+       }
        aced.aft_row = row;
        aced.old_afsi = &old_afsi;
        afs_event(AFSI_CHANGE, &cad->pb, &aced);
        return 1;
 }
 
-static int com_cpsi_callback(const struct osl_object *query,
-               struct osl_object *result)
+static void com_cpsi_callback(int fd, const struct osl_object *query)
 {
-       struct cpsi_action_data cad = {.flags = *(unsigned *)query->data};
+       struct cpsi_action_data cad = {
+               .flags = *(unsigned *)query->data,
+               .pb = {
+                       .max_size = SHMMAX,
+                       .private_data = &fd,
+                       .max_size_handler = pass_buffer_as_shm
+               }
+       };
        int ret;
        char *source_path = (char *)query->data + sizeof(cad.flags);
 
@@ -2237,21 +2270,18 @@ static int com_cpsi_callback(const struct osl_object *query,
        ret = for_each_matching_row(&pmd);
 out:
        if (ret < 0)
-               para_printf(&cad.pb, "%s\n", para_strerror(-ret));
-       if (cad.flags & CPSI_FLAG_VERBOSE) {
+               ret = para_printf(&cad.pb, "%s\n", para_strerror(-ret));
+       else if (cad.flags & CPSI_FLAG_VERBOSE) {
                if (cad.num_copied)
-                       para_printf(&cad.pb, "copied requested afsi from %s "
+                       ret = para_printf(&cad.pb, "copied requested afsi from %s "
                                "to %u files\n",
                                source_path, cad.num_copied);
                else
-                       para_printf(&cad.pb, "nothing copied\n");
-       }
-       if (cad.pb.buf) {
-               result->data = cad.pb.buf;
-               result->size = cad.pb.offset;
-               return 1;
+                       ret = para_printf(&cad.pb, "nothing copied\n");
        }
-       return ret < 0? ret : 0;
+       if (cad.pb.offset)
+               pass_buffer_as_shm(cad.pb.buf, cad.pb.offset, &fd);
+       free(cad.pb.buf);
 }
 
 int com_cpsi(int fd, int argc,  char * const * const argv)
@@ -2315,30 +2345,34 @@ static int check_audio_file(struct osl_row *row, void *data)
        struct afs_info afsi;
        char *blob_name;
 
-       if (ret < 0) {
-               para_printf(pb, "%s\n", para_strerror(-ret));
-               return 1;
-       }
-       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);
+       if (ret < 0)
+               return para_printf(pb, "%s\n", para_strerror(-ret));
+       if (stat(path, &statbuf) < 0) {
+               ret = para_printf(pb, "%s: stat error (%s)\n", path, strerror(errno));
+               if (ret < 0)
+                       return ret;
+       } else {
+               if (!S_ISREG(statbuf.st_mode)) {
+                       ret = para_printf(pb, "%s: not a regular file\n", path);
+                       if (ret < 0)
+                               return ret;
+               }
        }
        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,
+               return para_printf(pb, "%s: %s\n", path, para_strerror(-ret));
+       ret = lyr_get_name_by_id(afsi.lyrics_id, &blob_name);
+       if (ret < 0) {
+               ret = para_printf(pb, "%s lyrics id %u: %s\n", path, afsi.lyrics_id,
                        para_strerror(-ret));
+               if (ret < 0)
+                       return 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,
+               ret = para_printf(pb, "%s image id %u: %s\n", path, afsi.image_id,
                        para_strerror(-ret));
-       return 1;
+       return ret;
 }
 
 /**
@@ -2351,16 +2385,21 @@ static int check_audio_file(struct osl_row *row, void *data)
  *
  * \sa com_check().
  */
-int aft_check_callback(__a_unused const struct osl_object *query, struct osl_object *result)
+void aft_check_callback(int fd, __a_unused const struct osl_object *query)
 {
-       struct para_buffer pb = {.buf = NULL};
+       struct para_buffer pb = {
+               .max_size = SHMMAX,
+               .private_data = &fd,
+               .max_size_handler = pass_buffer_as_shm
+       };
+       int ret = para_printf(&pb, "checking audio file table...\n");
 
-       para_printf(&pb, "checking audio file table...\n");
+       if (ret < 0)
+               return;
        audio_file_loop(&pb, check_audio_file);
-       result->data = pb.buf;
-       result->size = pb.offset;
-       return 1;
-
+       if (pb.offset)
+               pass_buffer_as_shm(pb.buf, pb.offset, &fd);
+       free(pb.buf);
 }
 
 /**
@@ -2431,12 +2470,16 @@ static int clear_attribute(struct osl_row *row, void *data)
 static int aft_event_handler(enum afs_events event, struct para_buffer *pb,
                void *data)
 {
+       int ret;
+
        switch(event) {
        case ATTRIBUTE_REMOVE: {
                const struct rmatt_event_data *red = data;
-               para_printf(pb, "clearing attribute %s (bit %u) from all "
+               ret = para_printf(pb, "clearing attribute %s (bit %u) from all "
                        "entries in the audio file table\n", red->name,
                        red->bitnum);
+               if (ret < 0)
+                       return ret;
                return audio_file_loop(data, clear_attribute);
                }
        default: