]> git.tuebingen.mpg.de Git - paraslash.git/commitdiff
Complete the afs callback conversion.
authorAndre Noll <maan@systemlinux.org>
Mon, 24 Mar 2008 23:18:01 +0000 (00:18 +0100)
committerAndre Noll <maan@systemlinux.org>
Mon, 24 Mar 2008 23:18:01 +0000 (00:18 +0100)
This makes it possible for the callbacks to pass a partial
result buffer through the command handler <-> callback socket.
This removes a serious limitation of previous paraslash-0.3.x
versions: As callbacks could not send data directly to the
command handler they had to store the full command output in
a buffer. This buffer was sent to the command handler after the
callback returns.

The problem with this approach is that it caused the callbacks
to  allocate large amounts of memory. If that amount exceeded what
can be stored in a shared memory area (32MB on Linux), the command
even failed.

The fix consists of adding a max_size field and a max_size_handler
function pointer to the para_buffer structure. Whenever para_printf()
would need to allocate more memory than specified by max_size, it
calls the max_size_handler which is supposed to send out the data.
para_printf then prints the requested data into the old but now
empty buffer.

Almost all users set the max_size_handler to pass_buffer_as_shm(),
a helper function implemented in afs.c that copies the buffer to
a shared memory area and sends its identifier through the socket.

NEWS
afs.c
afs.h
aft.c
attribute.c
blob.c
ipc.h
mood.c
playlist.c
string.c
string.h

diff --git a/NEWS b/NEWS
index 4cc2d68d88a9d79de80d17408ab64061610c88cb..cca6150c49ceeb6676636d0fcc200b881ef0aef3 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -7,6 +7,9 @@ NEWS
 
        - new ls option: -lc (list chunk table)
        - new executable: para_afh, the stand-alone audio file handler tool
+       - afs commands can send output more than SHMMAX (32MB on Linux). This
+         also reduces the memory usage of commands that produce large amounts
+         of output.
 
 ---------------------------------------
 0.3.1 (2008-02-23) "liquid interaction"
diff --git a/afs.c b/afs.c
index d41a518d791f9908b0230beda99849f0a8f4c3e9..2260344e873ac67ef730d75b21173776b2ec70ee 100644 (file)
--- a/afs.c
+++ b/afs.c
@@ -573,36 +573,43 @@ static int activate_mood_or_playlist(char *arg, int *num_admissible)
        return 1;
 }
 
-static int com_select_callback(const struct osl_object *query,
-               struct osl_object *result)
+static void com_select_callback(int fd, 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
+       };
        char *arg = query->data;
-       int num_admissible, ret;
+       int num_admissible, ret, ret2;
 
        ret = clear_score_table();
-       if (ret < 0)
-               return ret;
+       if (ret < 0) {
+               ret2 = para_printf(&pb, "%s\n", para_strerror(-ret));
+               goto out;
+       }
        if (current_play_mode == PLAY_MODE_MOOD)
                close_current_mood();
        else
                playlist_close();
        ret = activate_mood_or_playlist(arg, &num_admissible);
        if (ret < 0) {
-               para_printf(&pb, "%s\n", para_strerror(-ret));
-               para_printf(&pb, "switching back to %s\n", current_mop?
+               ret2 = para_printf(&pb, "%s\nswitching back to %s\n",
+                       para_strerror(-ret), current_mop?
                        current_mop : "dummy");
                ret = activate_mood_or_playlist(current_mop, &num_admissible);
                if (ret < 0) {
-                       para_printf(&pb, "failed, switching to dummy\n");
+                       if (ret2 >= 0)
+                               ret2 = para_printf(&pb, "failed, switching to dummy\n");
                        activate_mood_or_playlist(NULL, &num_admissible);
                }
-       }
-       para_printf(&pb, "activated %s (%d admissible files)\n", current_mop?
-               current_mop : "dummy mood", num_admissible);
-       result->data = pb.buf;
-       result->size = pb.offset;
-       return 1;
+       } else
+               ret2 = para_printf(&pb, "activated %s (%d admissible files)\n", current_mop?
+                       current_mop : "dummy mood", num_admissible);
+out:
+       if (ret2 >= 0 && pb.offset)
+               pass_buffer_as_shm(pb.buf, pb.offset, &fd);
+       free(pb.buf);
 }
 
 int send_result(struct osl_object *result, void *private_result_data)
@@ -798,15 +805,15 @@ static void command_pre_select(struct sched *s, struct task *t)
        t->ret = 1;
 }
 
-int pass_object_as_shm(int fd, struct osl_object *obj)
+int pass_buffer_as_shm(char *buf, size_t size, void *private_data)
 {
-       int ret, shmid;
+       int ret, shmid, fd = *(int *)private_data;
        void *shm;
        struct callback_result *cr;
 
-       if (!obj->data || !obj->size)
+       if (!buf || !size)
                return 0;
-       ret = shm_new(obj->size + sizeof(struct callback_result));
+       ret = shm_new(size + sizeof(struct callback_result));
        if (ret < 0)
                return ret;
        shmid = ret;
@@ -814,8 +821,8 @@ int pass_object_as_shm(int fd, struct osl_object *obj)
        if (ret < 0)
                goto err;
        cr = shm;
-       cr->result_size = obj->size;
-       memcpy(shm + sizeof(*cr), obj->data, obj->size);
+       cr->result_size = size;
+       memcpy(shm + sizeof(*cr), buf, size);
        ret = shm_detach(shm);
        if (ret < 0)
                goto err;
@@ -833,27 +840,21 @@ err:
  * On success: If query produced a result, the result_shmid is written to fd.
  * Otherwise, zero is written.
  */
-static void call_callback(int fd, int query_shmid)
+static int call_callback(int fd, int query_shmid)
 {
        void *query_shm;
        struct callback_query *cq;
-       struct osl_object query, result = {.data = NULL};
+       struct osl_object query;
        int ret;
 
        ret = shm_attach(query_shmid, ATTACH_RW, &query_shm);
        if (ret < 0)
-               goto out;
+               return ret;
        cq = query_shm;
        query.data = (char *)query_shm + sizeof(*cq);
        query.size = cq->query_size;
-       ret = cq->handler(&query, &result);
-       if (ret < 0)
-               goto out;
-       ret = pass_object_as_shm(fd, &result);
-out:
-       free(result.data);
-       if (ret < 0)
-               PARA_ERROR_LOG("%s\n", para_strerror(-ret));
+       cq->handler(fd, &query);
+       return 1;
 }
 
 static void execute_server_command(void)
@@ -887,10 +888,8 @@ static void execute_afs_command(int fd, uint32_t expected_cookie)
        char buf[sizeof(cookie) + sizeof(query_shmid)];
        int ret = recv_bin_buffer(fd, buf, sizeof(buf));
 
-       if (ret < 0) {
-               PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
-               return;
-       }
+       if (ret < 0)
+               goto err;
        if (ret != sizeof(buf)) {
                PARA_NOTICE_LOG("short read (%d bytes, expected %lu)\n",
                        ret, (long unsigned) sizeof(buf));
@@ -908,7 +907,11 @@ static void execute_afs_command(int fd, uint32_t expected_cookie)
                        query_shmid);
                return;
        }
-       call_callback(fd, query_shmid);
+       ret = call_callback(fd, query_shmid);
+       if (ret >= 0)
+               return;
+err:
+       PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
 }
 
 /** Shutdown connection if query has not arrived until this many seconds. */
@@ -1017,11 +1020,11 @@ __noreturn void afs_init(uint32_t cookie, int socket_fd)
        exit(EXIT_FAILURE);
 }
 
-static int create_tables_callback(const struct osl_object *query,
-               __a_unused struct osl_object *result)
+static void create_tables_callback(int fd, const struct osl_object *query)
 {
        uint32_t table_mask = *(uint32_t *)query->data;
        int i, ret;
+       char *buf;
 
        close_afs_tables();
        for (i = 0; i < NUM_AFS_TABLES; i++) {
@@ -1033,10 +1036,16 @@ static int create_tables_callback(const struct osl_object *query,
                        continue;
                ret = t->create(database_dir);
                if (ret < 0)
-                       return ret;
+                       goto out;
        }
        ret = open_afs_tables();
-       return ret < 0? ret: 0;
+out:
+       if (ret >= 0)
+               buf = make_message("successfully created afs table(s)\n");
+       else
+               buf = make_message("%s\n", para_strerror(-ret));
+       pass_buffer_as_shm(buf, strlen(buf), &fd);
+       free(buf);
 }
 
 int com_init(int fd, int argc, char * const * const argv)
@@ -1066,8 +1075,8 @@ int com_init(int fd, int argc, char * const * const argv)
        }
        ret = send_callback_request(create_tables_callback, &query, NULL, NULL);
        if (ret < 0)
-               return ret;
-       return send_va_buffer(fd, "successfully created afs table(s)\n");
+               return send_va_buffer(fd, "%s\n", para_strerror(-ret));
+       return ret;
 }
 
 /**
diff --git a/afs.h b/afs.h
index 569798457b1338bd3261883557549a0b0129de02..ad9bfc86fb46edb3c37f0ac9906b9db5b7559d5a 100644 (file)
--- a/afs.h
+++ b/afs.h
@@ -168,9 +168,10 @@ struct pattern_match_data {
  *
  * \sa send_callback_request().
  */
-typedef int callback_function(const struct osl_object *, struct osl_object *);
+typedef void callback_function(int fd, const struct osl_object *);
 typedef int callback_result_handler(struct osl_object *result, void *private);
 int send_result(struct osl_object *result, void *private_result_data);
+int pass_buffer_as_shm(char *buf, size_t size, void *private_data);
 
 __noreturn void afs_init(uint32_t cookie, int socket_fd);
 void afs_event(enum afs_events event, struct para_buffer *pb,
@@ -224,21 +225,19 @@ int get_afsi_of_path(const char *path, struct afs_info *afsi);
 int get_audio_file_path_of_row(const struct osl_row *row, char **path);
 int get_afsi_object_of_row(const struct osl_row *row, struct osl_object *obj);
 int audio_file_loop(void *private_data, osl_rbtree_loop_func *func);
-int aft_check_callback(const struct osl_object *query, struct osl_object *result);
+void aft_check_callback(int fd, __a_unused const struct osl_object *query);
 
 /* mood */
 int change_current_mood(char *mood_name);
 void close_current_mood(void);
 int reload_current_mood(void);
-int mood_check_callback(__a_unused const struct osl_object *query,
-       struct osl_object *result);
+void mood_check_callback(int fd, __a_unused const struct osl_object *query);
 
 
 /* playlist */
 int playlist_open(char *name);
 void playlist_close(void);
-int playlist_check_callback(__a_unused const struct osl_object *query,
-       struct osl_object *result);
+void playlist_check_callback(int fd, __a_unused const struct osl_object *query);
 
 /** evaluates to 1 if x < y, to -1 if x > y and to 0 if x == y */
 #define NUM_COMPARE(x, y) ((int)((x) < (y)) - (int)((x) > (y)))
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:
index acbb8f4d2873ae405a6f091709fdf544709516ee..bfb6d3f2765749be1147b430439bb78dfe3d8d8a 100644 (file)
@@ -11,6 +11,7 @@
 #include "afh.h"
 #include "afs.h"
 #include "net.h"
+#include "ipc.h"
 
 static struct osl_table *attribute_table;
 static int greatest_att_bitnum;
@@ -126,26 +127,30 @@ static int print_attribute(struct osl_table *table, struct osl_row *row,
 {
        struct lsatt_action_data *laad = data;
        struct osl_object bitnum_obj;
-       int ret;
+       int ret, ret2;
 
-       if (!(laad->flags & LSATT_FLAG_LONG)) {
-               para_printf(&laad->pb, "%s\n", name);
-               return 1;
-       }
+       if (!(laad->flags & LSATT_FLAG_LONG))
+               return para_printf(&laad->pb, "%s\n", name);
        ret = osl_get_object(table, row, ATTCOL_BITNUM, &bitnum_obj);
        if (ret < 0) {
-               para_printf(&laad->pb, "%s: %s\n", name, para_strerror(-ret));
+               ret2 = para_printf(&laad->pb, "%s: %s\n", name, para_strerror(-ret));
                return ret;
        }
-       para_printf(&laad->pb, "%u\t%s\n", *(unsigned char*)bitnum_obj.data,
+       return para_printf(&laad->pb, "%u\t%s\n", *(unsigned char*)bitnum_obj.data,
                name);
-       return 1;
 }
 
-static int com_lsatt_callback(const struct osl_object *query,
-               struct osl_object *result)
+static void com_lsatt_callback(int fd, const struct osl_object *query)
 {
-       struct lsatt_action_data laad = {.flags = *(unsigned *) query->data};
+       struct lsatt_action_data laad = {
+               .flags = *(unsigned *) query->data,
+               .pb = {
+                       .max_size = SHMMAX,
+                       .private_data = &fd,
+                       .max_size_handler = pass_buffer_as_shm
+               }
+
+       };
        struct pattern_match_data pmd = {
                .table = attribute_table,
                .loop_col_num = ATTCOL_BITNUM,
@@ -156,20 +161,14 @@ static int com_lsatt_callback(const struct osl_object *query,
                .data = &laad,
                .action = print_attribute
        };
-       int ret;
-
        if (laad.flags & LSATT_FLAG_SORT_BY_ID)
                pmd.loop_col_num = ATTCOL_NAME;
        if (laad.flags & LSATT_FLAG_REVERSE)
                pmd.pm_flags |= PM_REVERSE_LOOP;
-       ret = for_each_matching_row(&pmd);
-       if (ret < 0)
-               para_printf(&laad.pb, "%s\n", para_strerror(-ret));
-       if (!laad.pb.buf)
-               return 0;
-       result->data = laad.pb.buf;
-       result->size = laad.pb.offset;
-       return 1;
+       for_each_matching_row(&pmd);
+       if (!laad.pb.offset)
+               pass_buffer_as_shm(laad.pb.buf, laad.pb.offset, &fd);
+       free(laad.pb.buf);
 }
 
 int com_lsatt(int fd, int argc, char * const * const argv)
@@ -209,8 +208,7 @@ int com_lsatt(int fd, int argc, char * const * const argv)
        return ret;
 }
 
-static int com_setatt_callback(const struct osl_object *query,
-               __a_unused struct osl_object *result)
+static void com_setatt_callback(__a_unused int fd, const struct osl_object *query)
 {
        char *p;
        uint64_t add_mask = 0, del_mask = 0;
@@ -223,8 +221,9 @@ static int com_setatt_callback(const struct osl_object *query,
                char c;
 
                len = strlen(p);
+               ret = -E_ATTR_SYNTAX;
                if (!*p)
-                       return -E_ATTR_SYNTAX;
+                       goto out;
                c = p[len - 1];
                if (c != '+' && c != '-')
                        break;
@@ -233,18 +232,19 @@ static int com_setatt_callback(const struct osl_object *query,
                obj.size = len + 1;
                ret = osl_get_row(attribute_table, ATTCOL_NAME, &obj, &row);
                if (ret < 0)
-                       return ret;
+                       goto out;
                ret = osl_get_object(attribute_table, row, ATTCOL_BITNUM,
                        &obj);
                if (ret < 0)
-                       return ret;
+                       goto out;
                if (c == '+')
                        add_mask |= (1UL << *(unsigned char *)obj.data);
                else
                        del_mask |= (1UL << *(unsigned char *)obj.data);
        }
+       ret = -E_ATTR_SYNTAX;
        if (!add_mask && !del_mask)
-               return -E_ATTR_SYNTAX;
+               goto out;
        PARA_DEBUG_LOG("masks: %llx:%llx\n",(long long unsigned)add_mask,
                (long long unsigned)del_mask);
        for (; p < (char *)query->data + query->size; p += len + 1) { /* TODO: fnmatch */
@@ -254,20 +254,22 @@ static int com_setatt_callback(const struct osl_object *query,
                len = strlen(p);
                ret = aft_get_row_of_path(p, &aced.aft_row);
                if (ret < 0)
-                       return ret;
+                       goto out;
                ret = get_afsi_object_of_row(aced.aft_row, &obj);
                if (ret < 0)
-                       return ret;
+                       goto out;
                ret = load_afsi(&old_afsi, &obj);
                if (ret < 0)
-                       return ret;
+                       goto out;
                new_afsi = old_afsi;
                new_afsi.attributes |= add_mask;
                new_afsi.attributes &= ~del_mask;
                save_afsi(&new_afsi, &obj); /* in-place update */
                afs_event(AFSI_CHANGE, NULL, &aced);
        }
-       return 1;
+out:
+       if (ret < 0)
+               PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
 }
 
 int com_setatt(__a_unused int fd, int argc, char * const * const argv)
@@ -284,12 +286,15 @@ struct addatt_event_data {
 };
 
 
-static int com_addatt_callback(const struct osl_object *query,
-               struct osl_object *result)
+static void com_addatt_callback(int fd, const struct osl_object *query)
 {
        char *p = query->data;
-       int ret = 1;
-       struct para_buffer pb = {.size = 0};
+       int ret = 1, ret2 = 0;
+       struct para_buffer pb = {
+               .max_size = SHMMAX,
+               .private_data = &fd,
+               .max_size_handler = pass_buffer_as_shm
+       };
        size_t len;
 
        for (p = query->data; p < (char *)query->data + query->size; p += len + 1) {
@@ -300,12 +305,16 @@ static int com_addatt_callback(const struct osl_object *query,
                struct addatt_event_data aed;
 
                if (!len || p[len - 1] == '-' || p[len - 1] == '+') {
-                       para_printf(&pb, "invalid attribute name: %s\n", p);
+                       ret2 = para_printf(&pb, "invalid attribute name: %s\n", p);
+                       if (ret2 < 0)
+                               goto out;
                        continue;
                }
                ret = get_attribute_bitnum_by_name(p, &bitnum);
                if (ret >= 0) {
-                       para_printf(&pb, "attribute \"%s\" already exists\n", p);
+                       ret2 = para_printf(&pb, "attribute \"%s\" already exists\n", p);
+                       if (ret2 < 0)
+                               goto out;
                        continue;
                }
                if (ret != -E_RB_KEY_NOT_FOUND) /* error */
@@ -337,11 +346,11 @@ static int com_addatt_callback(const struct osl_object *query,
                greatest_att_bitnum = PARA_MAX(greatest_att_bitnum, bitnum);
        }
 out:
-       if (ret < 0)
-               para_printf(&pb, "%s: %s\n", p, para_strerror(-ret));
-       result->data = pb.buf;
-       result->size = pb.offset;
-       return result->data? 0 : 1;
+       if (ret < 0 && ret2 >= 0)
+               ret = para_printf(&pb, "%s: %s\n", p, para_strerror(-ret));
+       if (pb.offset)
+               pass_buffer_as_shm(pb.buf, pb.offset, &fd);
+       free(pb.buf);
 }
 
 int com_addatt(int fd, int argc, char * const * const argv)
@@ -357,15 +366,18 @@ int com_addatt(int fd, int argc, char * const * const argv)
        return ret;
 }
 
-static int com_mvatt_callback(const struct osl_object *query,
-               struct osl_object *result)
+static void com_mvatt_callback(int fd, const struct osl_object *query)
 {
        char *old = query->data;
        size_t size = strlen(old) + 1;
        char *new = old + size;
        struct osl_object obj = {.data = old, .size = size};
        struct osl_row *row;
-       struct para_buffer pb = {.size = 0};
+       struct para_buffer pb = {
+               .max_size = SHMMAX,
+               .private_data = &fd,
+               .max_size_handler = pass_buffer_as_shm
+       };
        int ret;
 
        ret = osl_get_row(attribute_table, ATTCOL_NAME, &obj, &row);
@@ -376,14 +388,12 @@ static int com_mvatt_callback(const struct osl_object *query,
        ret = osl_update_object(attribute_table, row, ATTCOL_NAME, &obj);
 out:
        if (ret < 0)
-               para_printf(&pb, "%s\n", para_strerror(-ret));
+               ret = para_printf(&pb, "%s\n", para_strerror(-ret));
        else
                afs_event(ATTRIBUTE_RENAME, &pb, NULL);
-       if (!pb.buf)
-               return 0;
-       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);
 }
 
 int com_mvatt(int fd, int argc, char * const * const argv)
@@ -417,27 +427,29 @@ static int remove_attribute(struct osl_table *table, struct osl_row *row,
        struct rmatt_event_data red = {.name = name};
 
        ret = get_attribute_bitnum_by_name(name, &red.bitnum);
-       if (ret < 0) {
-               para_printf(&raad->pb, "%s: %s\n", name, para_strerror(-ret));
-               return 1;
-       }
+       if (ret < 0)
+               return para_printf(&raad->pb, "%s: %s\n", name, para_strerror(-ret));
        ret = osl_del_row(table, row);
-       if (ret < 0) {
-               para_printf(&raad->pb, "%s: %s\n", name, para_strerror(-ret));
-               return 1;
-       }
-       para_printf(&raad->pb, "removed attribute %s\n", name);
+       if (ret < 0)
+               return para_printf(&raad->pb, "%s: %s\n", name, para_strerror(-ret));
+       ret = para_printf(&raad->pb, "removed attribute %s\n", name);
        raad->num_removed++;
        raad->mask_of_removed_atts |= (1 << red.bitnum);
        afs_event(ATTRIBUTE_REMOVE, &raad->pb, &red);
-       return 1;
+       return ret;
 }
 
-static int com_rmatt_callback(const struct osl_object *query,
-               struct osl_object *result)
+static void com_rmatt_callback(int fd, const struct osl_object *query)
 {
-       struct remove_attribute_action_data raad = {.num_removed = 0};
-       int ret;
+       struct remove_attribute_action_data raad = {
+               .num_removed = 0,
+               .pb = {
+                       .max_size = SHMMAX,
+                       .private_data = &fd,
+                       .max_size_handler = pass_buffer_as_shm
+               }
+       };
+       int ret, ret2 = 0;
        struct pattern_match_data pmd = {
                .table = attribute_table,
                .patterns = *query,
@@ -448,12 +460,12 @@ static int com_rmatt_callback(const struct osl_object *query,
        };
        ret = for_each_matching_row(&pmd);
        if (ret < 0)
-               para_printf(&raad.pb, "%s\n", para_strerror(-ret));
-       if (!raad.num_removed)
-               para_printf(&raad.pb, "no match -- nothing removed\n");
-       result->data = raad.pb.buf;
-       result->size = raad.pb.offset;
-       return 1;
+               ret2 = para_printf(&raad.pb, "%s\n", para_strerror(-ret));
+       else if (!raad.num_removed)
+               ret2 = para_printf(&raad.pb, "no match -- nothing removed\n");
+       if (ret2 >= 0 && raad.pb.offset)
+               pass_buffer_as_shm(raad.pb.buf, raad.pb.offset, &fd);
+       free(raad.pb.buf);
 }
 
 int com_rmatt(int fd, int argc, char * const * const argv)
diff --git a/blob.c b/blob.c
index 899bd5a3d4f6fa1cb81c242ee7547e22c35bc726..cdad8d7f268330384a897f19d024fe8a6831d7ef 100644 (file)
--- a/blob.c
+++ b/blob.c
@@ -13,6 +13,7 @@
 #include "afh.h"
 #include "afs.h"
 #include "net.h"
+#include "ipc.h"
 
 static struct osl_column_description blob_cols[] = {
        [BLOBCOL_ID] = {
@@ -66,26 +67,30 @@ static int print_blob(struct osl_table *table, struct osl_row *row,
        struct lsblob_action_data *lbad = data;
        struct osl_object obj;
        uint32_t id;
-       int ret;
+       int ret, ret2;
 
-       if (!(lbad->flags & BLOB_LS_FLAG_LONG)) {
-               para_printf(&lbad->pb, "%s\n", name);
-               return 1;
-       }
+       if (!(lbad->flags & BLOB_LS_FLAG_LONG))
+               return para_printf(&lbad->pb, "%s\n", name);
        ret = osl_get_object(table, row, BLOBCOL_ID, &obj);
        if (ret < 0) {
-               para_printf(&lbad->pb, "%s: %s\n", name, para_strerror(-ret));
+               ret2 = para_printf(&lbad->pb, "%s: %s\n", name, para_strerror(-ret));
                return ret;
        }
        id = *(uint32_t *)obj.data;
-       para_printf(&lbad->pb, "%u\t%s\n", id, name);
-       return 1;
+       return para_printf(&lbad->pb, "%u\t%s\n", id, name);
 }
 
-static int com_lsblob_callback(struct osl_table *table,
-               const struct osl_object *query, struct osl_object *result)
+static void com_lsblob_callback(struct osl_table *table,
+               int fd, const struct osl_object *query)
 {
-       struct lsblob_action_data lbad = {.flags = *(uint32_t *)query->data};
+       struct lsblob_action_data lbad = {
+               .flags = *(uint32_t *)query->data,
+               .pb = {
+                       .max_size = SHMMAX,
+                       .private_data = &fd,
+                       .max_size_handler = pass_buffer_as_shm
+               }
+       };
        struct pattern_match_data pmd = {
                .table = table,
                .patterns = {.data = (char *)query->data + sizeof(uint32_t),
@@ -105,12 +110,10 @@ static int com_lsblob_callback(struct osl_table *table,
                pmd.loop_col_num = BLOBCOL_ID;
        ret = for_each_matching_row(&pmd);
        if (ret < 0)
-               para_printf(&lbad.pb, "%s\n", para_strerror(-ret));
-       if (!lbad.pb.buf)
-               return 0;
-       result->data = lbad.pb.buf;
-       result->size = lbad.pb.offset;
-       return 1;
+               ret = para_printf(&lbad.pb, "%s\n", para_strerror(-ret));
+       if (lbad.pb.offset)
+               pass_buffer_as_shm(lbad.pb.buf, lbad.pb.offset, &fd);
+       free(lbad.pb.buf);
 }
 
 static int com_lsblob(callback_function *f, int fd, int argc, char * const * const argv)
@@ -150,39 +153,31 @@ static int com_lsblob(callback_function *f, int fd, int argc, char * const * con
 static int cat_blob(struct osl_table *table, struct osl_row *row,
                __a_unused const char *name, void *data)
 {
-       int ret;
-       struct osl_object *blobs = data;
+       int ret = 0, ret2;
        struct osl_object obj;
 
        ret = osl_open_disk_object(table, row, BLOBCOL_DEF, &obj);
        if (ret < 0)
                return ret;
-       if (obj.size) {
-               blobs->data = para_realloc(blobs->data, blobs->size + obj.size);
-               memcpy(blobs->data + blobs->size, obj.data, obj.size);
-               blobs->size += obj.size;
-       }
-       return osl_close_disk_object(&obj);
+       if (obj.size)
+               ret = pass_buffer_as_shm(obj.data, obj.size, data);
+       ret2 = osl_close_disk_object(&obj);
+       return (ret < 0)? ret : ret2;
 }
 
-static int com_catblob_callback(struct osl_table *table,
-               const struct osl_object *query, struct osl_object *result)
+static void com_catblob_callback(struct osl_table *table, int fd,
+               const struct osl_object *query)
 {
-       int ret;
        struct pattern_match_data pmd = {
                .table = table,
                .patterns = *query,
                .loop_col_num = BLOBCOL_NAME,
                .match_col_num = BLOBCOL_NAME,
                .pm_flags = PM_SKIP_EMPTY_NAME,
-               .data = result,
+               .data = &fd,
                .action = cat_blob
        };
-       result->data = NULL;
-       ret = for_each_matching_row(&pmd);
-       if (result->data)
-               return 1;
-       return (ret > 0)? 0 : ret;
+       for_each_matching_row(&pmd);
 }
 
 static int com_catblob(callback_function *f, int fd, int argc,
@@ -205,21 +200,27 @@ static int remove_blob(struct osl_table *table, struct osl_row *row,
                const char *name, void *data)
 {
        struct rmblob_data *rmbd = data;
-       int ret = osl_del_row(table, row);
+       int ret = osl_del_row(table, row), ret2;
        if (ret < 0) {
-               para_printf(&rmbd->pb, "%s: %s\n", name, para_strerror(-ret));
+               ret2 = para_printf(&rmbd->pb, "%s: %s\n", name, para_strerror(-ret));
                return ret;
        }
        rmbd->num_removed++;
-       return 1; /* return success to remove other matching blobs. */
+       return 1;
 }
 
-static int com_rmblob_callback(struct osl_table *table,
-               const struct osl_object *query,
-               struct osl_object *result)
+static void com_rmblob_callback(struct osl_table *table, int fd,
+               const struct osl_object *query)
 {
-       int ret;
-       struct rmblob_data rmbd = {.num_removed = 0};
+       int ret, ret2 = 0;
+       struct rmblob_data rmbd = {
+               .num_removed = 0,
+               .pb = {
+                       .max_size = SHMMAX,
+                       .private_data = &fd,
+                       .max_size_handler = pass_buffer_as_shm
+               }
+       };
        struct pattern_match_data pmd = {
                .table = table,
                .patterns = *query,
@@ -229,19 +230,22 @@ static int com_rmblob_callback(struct osl_table *table,
                .data = &rmbd,
                .action = remove_blob
        };
-       result->data = NULL;
        ret = for_each_matching_row(&pmd);
-       if (ret < 0)
-               para_printf(&rmbd.pb, "%s\n", para_strerror(-ret));
+       if (ret < 0) {
+               ret2 = para_printf(&rmbd.pb, "%s\n", para_strerror(-ret));
+               if (ret2 < 0)
+                       goto out;
+       }
        if (!rmbd.num_removed)
-               para_printf(&rmbd.pb, "no matches, nothing removed\n");
+               ret2 = para_printf(&rmbd.pb, "no matches, nothing removed\n");
        else {
-               para_printf(&rmbd.pb, "removed %d blobs\n", rmbd.num_removed);
+               ret2 = para_printf(&rmbd.pb, "removed %d blobs\n", rmbd.num_removed);
                afs_event(BLOB_RENAME, NULL, table);
        }
-       result->data = rmbd.pb.buf;
-       result->size = rmbd.pb.offset;
-       return 1;
+out:
+       if (ret2 >= 0 && rmbd.pb.offset)
+               pass_buffer_as_shm(rmbd.pb.buf, rmbd.pb.offset, &fd);
+       free(rmbd.pb.buf);
 }
 
 static int com_rmblob(callback_function *f, int fd, int argc,
@@ -253,9 +257,8 @@ static int com_rmblob(callback_function *f, int fd, int argc,
                send_result, &fd);
 }
 
-static int com_addblob_callback(struct osl_table *table,
-               const struct osl_object *query,
-               __a_unused struct osl_object *result)
+static void com_addblob_callback(struct osl_table *table, __a_unused int fd,
+               const struct osl_object *query)
 {
        struct osl_object objs[NUM_BLOB_COLUMNS];
        char *name = query->data;
@@ -266,7 +269,7 @@ static int com_addblob_callback(struct osl_table *table,
 
        ret = osl_get_num_rows(table, &num_rows);
        if (ret < 0)
-               return ret;
+               goto out;
        if (!num_rows) { /* this is the first entry ever added */
                /* insert dummy row containing the id */
                id = 2; /* this entry will be entry #1, so 2 is the next */
@@ -278,33 +281,34 @@ static int com_addblob_callback(struct osl_table *table,
                objs[BLOBCOL_DEF].size = 1;
                ret = osl_add_row(table, objs);
                if (ret < 0)
-                       return ret;
+                       goto out;
        } else {
                /* check if name already exists */
                struct osl_row *row;
                struct osl_object obj = {.data = name, .size = name_len};
                ret = osl_get_row(table, BLOBCOL_NAME, &obj, &row);
                if (ret < 0 && ret != -E_RB_KEY_NOT_FOUND)
-                       return ret;
+                       goto out;
                if (ret >= 0) { /* we already have a blob with this name */
                        obj.data = name + name_len;
                        obj.size = query->size - name_len;
-                       return osl_update_object(table, row, BLOBCOL_DEF, &obj);
+                       ret = osl_update_object(table, row, BLOBCOL_DEF, &obj);
+                       goto out;
                }
                /* new blob, get id of the dummy row and increment it */
                obj.data = "";
                obj.size = 1;
                ret = osl_get_row(table, BLOBCOL_NAME, &obj, &row);
                if (ret < 0)
-                       return ret;
+                       goto out;
                ret = osl_get_object(table, row, BLOBCOL_ID, &obj);
                if (ret < 0)
-                       return ret;
+                       goto out;
                id = *(uint32_t *)obj.data + 1;
                obj.data = &id;
                ret = osl_update_object(table, row, BLOBCOL_ID, &obj);
                if (ret < 0)
-                       return ret;
+                       goto out;
        }
        id--;
        objs[BLOBCOL_ID].data = &id;
@@ -315,9 +319,11 @@ static int com_addblob_callback(struct osl_table *table,
        objs[BLOBCOL_DEF].size = query->size - name_len;
        ret = osl_add_row(table, objs);
        if (ret < 0)
-               return ret;
+               goto out;
        afs_event(BLOB_ADD, NULL, table);
-       return 1;
+out:
+       if (ret < 0)
+               PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
 }
 
 static int com_addblob(callback_function *f, int fd, int argc,
@@ -329,15 +335,14 @@ static int com_addblob(callback_function *f, int fd, int argc,
                return -E_BLOB_SYNTAX;
        if (!*argv[1]) /* empty name is reserved for the dummy row */
                return -E_BLOB_SYNTAX;
-       PARA_NOTICE_LOG("argv[1]: %s\n", argv[1]);
        arg_obj.size = strlen(argv[1]) + 1;
        arg_obj.data = (char *)argv[1];
        return stdin_command(fd, &arg_obj, f, 10 * 1024 * 1024, NULL, NULL);
 }
 
-static int com_mvblob_callback(struct osl_table *table,
-               const struct osl_object *query,
-               __a_unused struct osl_object *result)
+/* FIXME: Print output to client, not to log file */
+static void com_mvblob_callback(struct osl_table *table, __a_unused int fd,
+               const struct osl_object *query)
 {
        char *src = (char *) query->data;
        struct osl_object obj = {.data = src, .size = strlen(src) + 1};
@@ -346,14 +351,16 @@ static int com_mvblob_callback(struct osl_table *table,
        int ret = osl_get_row(table, BLOBCOL_NAME, &obj, &row);
 
        if (ret < 0)
-               return ret;
+               goto out;
        obj.data = dest;
        obj.size = strlen(dest) + 1;
        ret = osl_update_object(table, row, BLOBCOL_NAME, &obj);
        if (ret < 0)
-               return ret;
+               goto out;
        afs_event(BLOB_RENAME, NULL, table);
-       return 1;
+out:
+       if (ret < 0)
+               PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
 }
 
 static int com_mvblob(callback_function *f, __a_unused int fd,
@@ -366,10 +373,9 @@ static int com_mvblob(callback_function *f, __a_unused int fd,
 }
 
 #define DEFINE_BLOB_COMMAND(cmd_name, table_name, cmd_prefix) \
-       static int com_ ## cmd_name ## cmd_prefix ## _callback(const struct osl_object *query, \
-                       struct osl_object *output) \
+       static void com_ ## cmd_name ## cmd_prefix ## _callback(int fd, const struct osl_object *query) \
        { \
-               return com_ ## cmd_name ## blob_callback(table_name ## _table, query, output); \
+               return com_ ## cmd_name ## blob_callback(table_name ## _table, fd, query); \
        } \
        int com_ ## cmd_name ## cmd_prefix(int fd, int argc, char * const * const argv) \
        { \
diff --git a/ipc.h b/ipc.h
index c4dd1d7d620ca17d40371d712aa91ab764176c8c..71e09ec1d1a7a21ea5a7fcab5bfb47835c70e464 100644 (file)
--- a/ipc.h
+++ b/ipc.h
@@ -11,3 +11,7 @@ int shm_new(size_t size);
 int shm_attach(int id, enum shm_attach_mode mode, void **result);
 int shm_detach(void *addr);
 int shm_destroy(int id);
+
+#ifndef SHMMAX
+#define SHMMAX 65535
+#endif
diff --git a/mood.c b/mood.c
index 2e562f4fefdd0a559aa37329ddafddfd2d861316..590a68fb4d6e0ad3dedfd9efd6683bf69a7d9ec1 100644 (file)
--- a/mood.c
+++ b/mood.c
@@ -13,6 +13,7 @@
 #include "afh.h"
 #include "afs.h"
 #include "list.h"
+#include "ipc.h"
 
 /**
  * Contains statistical data of the currently admissible audio files.
@@ -535,23 +536,26 @@ static int check_mood(struct osl_row *mood_row, void *data)
        struct osl_object mood_def;
        struct mood_line_parser_data mlpd = {.line_num = 0};
 
-       int ret = mood_get_name_and_def_by_row(mood_row, &mood_name, &mood_def);
+       int ret2, ret = mood_get_name_and_def_by_row(mood_row, &mood_name, &mood_def);
 
        if (ret < 0) {
-               para_printf(pb, "failed to get mood definition\n");
+               ret2 = para_printf(pb, "failed to get mood definition: %s\n",
+                       para_strerror(-ret));
                return ret;
        }
        if (!*mood_name) /* ignore dummy row */
                goto out;
-       para_printf(pb, "checking mood %s...\n", mood_name);
+       ret = para_printf(pb, "checking mood %s...\n", mood_name);
+       if (ret < 0)
+               goto out;
        ret = for_each_line_ro(mood_def.data, mood_def.size,
                parse_mood_line, &mlpd);
        if (ret < 0)
-               para_printf(pb, "%s line %u: %s\n", mood_name, mlpd.line_num,
+               ret2 = para_printf(pb, "%s line %u: %s\n", mood_name, mlpd.line_num,
                        para_strerror(-ret));
 out:
        osl_close_disk_object(&mood_def);
-       return 1;
+       return ret;
 }
 
 /**
@@ -560,17 +564,22 @@ out:
  * \param query Unused.
  * \param result: Contains check messages.
  */
-int mood_check_callback(__a_unused const struct osl_object *query,
-       struct osl_object *result)
+void mood_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
+       };
 
-       para_printf(&pb, "checking moods...\n");
+       int ret = para_printf(&pb, "checking moods...\n");
+       if (ret < 0)
+               return;
        osl_rbtree_loop(moods_table, BLOBCOL_ID, &pb,
                check_mood);
-       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);
 }
 
 #if 0
index 19a913144586a3b574dccef2b874964dbc7c2610..7a912bade5f3890796d2c1a7a672757bee12fcb8 100644 (file)
@@ -9,6 +9,7 @@
 #include "string.h"
 #include "afh.h"
 #include "afs.h"
+#include "ipc.h"
 
 /** \file playlist.c Functions for loading and saving playlists. */
 
@@ -90,9 +91,9 @@ static int check_playlist_path(char *path, void *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));
-       return 1;
+       if (ret >= 0)
+               return 1;
+       return para_printf(pb, "%s: %s\n", path, para_strerror(-ret));
 }
 
 static int check_playlist(struct osl_row *row, void *data)
@@ -102,18 +103,18 @@ static int check_playlist(struct osl_row *row, void *data)
        char *playlist_name;
        int ret = pl_get_name_and_def_by_row(row, &playlist_name, &playlist_def);
 
-       if (ret < 0) {
-               para_printf(pb, "failed to get playlist data: %s\n",
+       if (ret < 0)
+               return para_printf(pb, "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_ro(playlist_def.data, playlist_def.size,
+               ret = para_printf(pb, "checking playlist %s...\n", playlist_name);
+               if (ret < 0)
+                       return ret;
+               ret = for_each_line_ro(playlist_def.data, playlist_def.size,
                        check_playlist_path, pb);
        }
        osl_close_disk_object(&playlist_def);
-       return 1;
+       return ret;
 }
 
 /**
@@ -124,17 +125,22 @@ static int check_playlist(struct osl_row *row, void *data)
  *
  * \return The return value of the underlying call to osl_rbtree_loop().
  */
-int playlist_check_callback(__a_unused const struct osl_object *query,
-               struct osl_object *result)
+void playlist_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 playlists...\n");
 
-       para_printf(&pb, "checking playlists...\n");
+       if (ret < 0)
+               return;
        osl_rbtree_loop(playlists_table, BLOBCOL_ID, &pb,
                check_playlist);
-       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);
 }
 
 /**
index bd53e8d24462d1bbde5045f7b0b80de939460588..752fb8f945d1a381b7b7af4e65ed56b646df8d47 100644 (file)
--- a/string.c
+++ b/string.c
@@ -484,7 +484,15 @@ int for_each_line_ro(char *buf, size_t size, line_handler_t *line_handler,
  *
  * This function prints into the buffer given by \a b at the offset which is
  * also given by \a b. If there is not enough space to hold the result, the
- * buffer size is doubled until the underlying call to vsnprintf() succeeds.
+ * buffer size is doubled until the underlying call to vsnprintf() succeeds
+ * or the size of the buffer exceeds the maximal size specified in \a pb.
+ *
+ * In the latter case the unmodified \a buf and \a offset values as well as the
+ * private_data pointer of \a b are passed to the \a max_size_handler of \a b.
+ * If this function succeeds, i.e. returns a non-negative value, the offset of
+ * \a b is reset to zero and the given data is written to the beginning of the
+ * buffer.
+ *
  * Upon return, the offset of \a b is adjusted accordingly so that subsequent
  * calls to this function append data to what is already contained in the
  * buffer.
@@ -505,26 +513,35 @@ __printf_2_3 int para_printf(struct para_buffer *b, const char *fmt, ...)
                b->buf = para_malloc(128);
                b->size = 128;
                b->offset = 0;
-       } else if (b->size <= b->offset + 1) {
-               b->size *= 2;
-               b->buf = para_realloc(b->buf, b->size);
        }
        while (1) {
                char *p = b->buf + b->offset;
                size_t size = b->size - b->offset;
                va_list ap;
-               va_start(ap, fmt);
-               ret = vsnprintf(p, size, fmt, ap);
-               va_end(ap);
-               if (ret > -1 && ret < size) { /* success */
-                       b->offset += ret;
-                       break;
+               if (size) {
+                       va_start(ap, fmt);
+                       ret = vsnprintf(p, size, fmt, ap);
+                       va_end(ap);
+                       if (ret > -1 && ret < size) { /* success */
+                               b->offset += ret;
+                               return ret;
+                       }
+               }
+               /* check if we may grow the buffer */
+               if (!b->max_size || 2 * b->size < b->max_size) { /* yes */
+                       /* try again with more space */
+                       b->size *= 2;
+                       b->buf = para_realloc(b->buf, b->size);
+                       continue;
                }
-               /* try again with more space */
-               b->size *= 2;
-               b->buf = para_realloc(b->buf, b->size);
+               /* can't grow buffer */
+               if (!b->offset || !b->max_size_handler) /* message too large */
+                       return -ERRNO_TO_PARA_ERROR(ENOSPC);
+               ret = b->max_size_handler(b->buf, b->offset, b->private_data);
+               if (ret < 0)
+                       return ret;
+               b->offset = 0;
        }
-       return ret;
 }
 
 /** \cond LLONG_MAX and LLONG_LIN might not be defined. */
index 84101a873f66437c2304fa1af8f2b4ecadf385cc..c01d61964c71041edb079cea67cc0ffbcbc63c7c 100644 (file)
--- a/string.h
+++ b/string.h
@@ -12,8 +12,18 @@ struct para_buffer {
        char *buf;
        /** The size of \a buf. */
        size_t size;
+       /** The maximal size this buffer may grow. Zero means unlimited. */
+       size_t max_size;
        /** The next para_printf() will write at this offset. */
        size_t offset;
+       /**
+        * If an attempt to print into this buffer is made that would need to
+        * grow \buf to a size larger than \a max_size, then this function is
+        * called.
+        */
+       int (*max_size_handler)(char *buf, size_t size, void *private_data);
+       /** Passed to max_size_handler(). */
+       void *private_data;
 };
 
 __must_check __malloc void *para_realloc(void *p, size_t size);