From 49bd626084bf5f48e4d80075258b6da4703752cf Mon Sep 17 00:00:00 2001 From: Andre Noll Date: Tue, 25 Mar 2008 00:18:01 +0100 Subject: [PATCH] Complete the afs callback conversion. 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 | 3 + afs.c | 95 ++++++++------- afs.h | 11 +- aft.c | 333 +++++++++++++++++++++++++++++----------------------- attribute.c | 154 +++++++++++++----------- blob.c | 150 +++++++++++------------ ipc.h | 4 + mood.c | 33 ++++-- playlist.c | 40 ++++--- string.c | 45 ++++--- string.h | 10 ++ 11 files changed, 498 insertions(+), 380 deletions(-) diff --git a/NEWS b/NEWS index 4cc2d68d..cca6150c 100644 --- 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 d41a518d..2260344e 100644 --- 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 56979845..ad9bfc86 100644 --- 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 d6abc13d..0ffacbe4 100644 --- a/aft.c +++ b/aft.c @@ -12,6 +12,8 @@ #include "string.h" #include #include +#include + #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: diff --git a/attribute.c b/attribute.c index acbb8f4d..bfb6d3f2 100644 --- a/attribute.c +++ b/attribute.c @@ -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 899bd5a3..cdad8d7f 100644 --- 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 c4dd1d7d..71e09ec1 100644 --- 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 2e562f4f..590a68fb 100644 --- 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 diff --git a/playlist.c b/playlist.c index 19a91314..7a912bad 100644 --- a/playlist.c +++ b/playlist.c @@ -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); } /** diff --git a/string.c b/string.c index bd53e8d2..752fb8f9 100644 --- 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. */ diff --git a/string.h b/string.h index 84101a87..c01d6196 100644 --- 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); -- 2.39.2