From: Andre Noll Date: Thu, 22 Jan 2015 17:45:38 +0000 (+0100) Subject: Merge branch 'maint' X-Git-Tag: v0.5.4~3 X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=commitdiff_plain;h=f167629b3191c57a6b691cd2a6af04a45a74ccb0;hp=-c Merge branch 'maint' Was cooking for a week and seems to be regression-free. aft: Generate a remove event when adding duplicate files. command.c: Add missing items to EMPTY_STATUS_ITEMS. aft.c: Don't call osl_close_disk_object() on failure. Add missing osl() wrapper calls. fd.c: Add missing va_end(). The conflict in aft.c was trivial to fix up. --- f167629b3191c57a6b691cd2a6af04a45a74ccb0 diff --combined aft.c index c61d3820,301da7c7..727292c7 --- a/aft.c +++ b/aft.c @@@ -1,5 -1,5 +1,5 @@@ /* - * Copyright (C) 2007-2013 Andre Noll + * Copyright (C) 2007 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@@ -87,9 -87,10 +87,9 @@@ enum ls_flags /** * The size of the individual output fields of the ls command. * - * These depend on the actual content being listed. If, for instance only files - * with duration less than an hour are being listed, then the duration with is - * made smaller because then the duration is listed as mm:ss rather than - * hh:mm:ss. + * These depend on the content being listed. For example, if each listed file + * is shorter than an hour, the duration format is set to mm:ss. Otherwise it + * is set to hh:mm:ss. */ struct ls_widths { /** size of the score field. */ @@@ -162,7 -163,7 +162,7 @@@ enum afsi_offsets AFSI_SIZE = 32 }; -/** +/* * Convert a struct afs_info to an osl object. * * \param afsi Pointer to the audio file info to be converted. @@@ -170,7 -171,7 +170,7 @@@ * * \sa load_afsi(). */ -void save_afsi(struct afs_info *afsi, struct osl_object *obj) +static void save_afsi(struct afs_info *afsi, struct osl_object *obj) { char *buf = obj->data; @@@ -185,17 -186,17 +185,17 @@@ memset(buf + AFSI_AUDIO_FORMAT_UNUSED_OFFSET, 0, 2); } -/** - * Get the audio file selector info struct stored in an osl object. +/* + * Get the audio file selector info struct stored in an osl object. * * \param afsi Points to the audio_file info structure to be filled in. * \param obj The osl object holding the data. * - * \return Positive on success, negative on errors. Possible errors: \p E_BAD_AFS. + * \return Standard. * * \sa save_afsi(). */ -int load_afsi(struct afs_info *afsi, struct osl_object *obj) +static int load_afsi(struct afs_info *afsi, struct osl_object *obj) { char *buf = obj->data; if (obj->size < AFSI_SIZE) @@@ -492,16 -493,15 +492,16 @@@ static int aft_get_row_of_hash(unsigne return osl(osl_get_row(audio_file_table, AFTCOL_HASH, &obj, row)); } -/** - * Get the osl object holding the audio file selector info of a row. +/* + * Get the audio file selector info object of a row. * * \param row Pointer to a row in the audio file table. * \param obj Result pointer. * * \return Standard. */ -int get_afsi_object_of_row(const struct osl_row *row, struct osl_object *obj) +static int get_afsi_object_of_row(const struct osl_row *row, + struct osl_object *obj) { return osl(osl_get_object(audio_file_table, row, AFTCOL_AFSI, obj)); } @@@ -541,15 -541,15 +541,15 @@@ int get_afsi_of_row(const struct osl_ro return load_afsi(afsi, &obj); } -/** +/* * Get the audio file selector info, given the path of an audio table. * * \param path The full path of the audio file. * \param afsi Result pointer. * - * \return Positive on success, negative on errors. + * \return Standard. */ -int get_afsi_of_path(const char *path, struct afs_info *afsi) +static int get_afsi_of_path(const char *path, struct afs_info *afsi) { struct osl_object obj; int ret = get_afsi_object_of_path(path, &obj); @@@ -755,6 -755,7 +755,6 @@@ static void get_duration_buf(int second } } - static int write_attribute_items(struct para_buffer *b, const char *att_bitmap, struct afs_info *afsi) { @@@ -829,8 -830,8 +829,8 @@@ static int print_chunk_table(struct ls_ ret = aft_get_row_of_hash(d->hash, &aft_row); if (ret < 0) return ret; - ret = osl_open_disk_object(audio_file_table, aft_row, - AFTCOL_CHUNKS, &chunk_table_obj); + ret = osl(osl_open_disk_object(audio_file_table, aft_row, + AFTCOL_CHUNKS, &chunk_table_obj)); if (ret < 0) return ret; ret = para_printf(b, "%s\n" @@@ -1105,11 -1106,12 +1105,11 @@@ int open_and_update_audio_file(struct o if (ret < 0) return ret; afd->afhi.chunk_table = NULL; - ret = osl_open_disk_object(audio_file_table, aft_row, - AFTCOL_CHUNKS, &chunk_table_obj); + ret = osl(osl_open_disk_object(audio_file_table, aft_row, + AFTCOL_CHUNKS, &chunk_table_obj)); if (ret < 0) - goto err; + return ret; - ret = mmap_full_file(path, O_RDONLY, &map.data, - &map.size, &afd->fd); + ret = mmap_full_file(path, O_RDONLY, &map.data, &map.size, &afd->fd); if (ret < 0) goto err; hash_function(map.data, map.size, file_hash); @@@ -1716,10 -1718,11 +1716,11 @@@ static void com_add_callback(int fd, co struct osl_object obj; if (pb) { /* hs trumps pb, remove pb */ if (flags & ADD_FLAG_VERBOSE) { - ret = para_printf(&msg, "removing path brother\n"); + ret = para_printf(&msg, "removing %s\n", path); if (ret < 0) goto out; } + afs_event(AUDIO_FILE_REMOVE, &msg, pb); ret = osl(osl_del_row(audio_file_table, pb)); if (ret < 0) goto out; @@@ -1874,8 -1877,12 +1875,8 @@@ static int add_one_audio_file(const cha ret = 1; if (pb && (pad->flags & ADD_FLAG_LAZY)) { /* lazy is really cheap */ if (pad->flags & ADD_FLAG_VERBOSE) - send_ret = pad->cc->use_sideband? - send_sb_va(&pad->cc->scc, SBD_OUTPUT, - "lazy-ignore: %s\n", path) - : - sc_send_va_buffer(&pad->cc->scc, - "lazy-ignore: %s\n", path); + send_ret = send_sb_va(&pad->cc->scc, SBD_OUTPUT, + "lazy-ignore: %s\n", path); goto out_free; } /* We still want to add this file. Compute its hash. */ @@@ -1895,8 -1902,12 +1896,8 @@@ ret = 1; if (pb && hs && hs == pb && !(pad->flags & ADD_FLAG_FORCE)) { if (pad->flags & ADD_FLAG_VERBOSE) - send_ret = pad->cc->use_sideband? - send_sb_va(&pad->cc->scc, SBD_OUTPUT, - "%s exists, not forcing update\n", path) - : - sc_send_va_buffer(&pad->cc->scc, - "%s exists, not forcing update\n", path); + send_ret = send_sb_va(&pad->cc->scc, SBD_OUTPUT, + "%s exists, not forcing update\n", path); goto out_unmap; } /* @@@ -1913,8 -1924,12 +1914,8 @@@ munmap(map.data, map.size); close(fd); if (pad->flags & ADD_FLAG_VERBOSE) { - send_ret = pad->cc->use_sideband? - send_sb_va(&pad->cc->scc, SBD_OUTPUT, - "adding %s\n", path) - : - sc_send_va_buffer(&pad->cc->scc, - "adding %s\n", path); + send_ret = send_sb_va(&pad->cc->scc, SBD_OUTPUT, + "adding %s\n", path); if (send_ret < 0) goto out_free; } @@@ -1929,8 -1944,14 +1930,8 @@@ out_unmap munmap(map.data, map.size); out_free: if (ret < 0 && send_ret >= 0) - send_ret = pad->cc->use_sideband? - send_sb_va(&pad->cc->scc, SBD_ERROR_LOG, - "failed to add %s (%s)\n", path, - para_strerror(-ret)) - : - sc_send_va_buffer(&pad->cc->scc, - "failed to add %s (%s)\n", path, - para_strerror(-ret)); + send_ret = send_sb_va(&pad->cc->scc, SBD_ERROR_LOG, + "failed to add %s (%s)\n", path, para_strerror(-ret)); free(obj.data); clear_afhi(afhi_ptr); /* Stop adding files only on send errors. */ @@@ -1974,7 -1995,11 +1975,7 @@@ int com_add(struct command_context *cc char *path; ret = verify_path(cc->argv[i], &path); if (ret < 0) { - ret = cc->use_sideband? - send_sb_va(&cc->scc, SBD_ERROR_LOG, "%s: %s\n", - cc->argv[i], para_strerror(-ret)) - : - sc_send_va_buffer(&cc->scc, "%s: %s\n", + ret = send_sb_va(&cc->scc, SBD_ERROR_LOG, "%s: %s\n", cc->argv[i], para_strerror(-ret)); if (ret < 0) return ret; @@@ -1982,9 -2007,14 +1983,9 @@@ } ret = stat(path, &statbuf); if (ret < 0) { - ret = cc->use_sideband? - send_sb_va(&cc->scc, SBD_ERROR_LOG, - "failed to stat %s (%s)\n", path, - strerror(errno)) - : - sc_send_va_buffer(&cc->scc, - "failed to stat %s (%s)\n", path, - strerror(errno)); + ret = send_sb_va(&cc->scc, SBD_ERROR_LOG, + "failed to stat %s (%s)\n", path, + strerror(errno)); free(path); if (ret < 0) return ret; @@@ -1996,14 -2026,19 +1997,14 @@@ else ret = add_one_audio_file(path, &pad); if (ret < 0) { - if (cc->use_sideband) - send_sb_va(&cc->scc, SBD_OUTPUT, "%s: %s\n", path, - para_strerror(-ret)); - else - sc_send_va_buffer(&cc->scc, "%s: %s\n", path, - para_strerror(-ret)); + send_sb_va(&cc->scc, SBD_OUTPUT, "%s: %s\n", path, + para_strerror(-ret)); free(path); return ret; } free(path); } return 1; - } /** @@@ -2405,12 -2440,13 +2406,12 @@@ static void com_cpsi_callback(int fd, c out: if (ret < 0) para_printf(&cad.pb, "%s\n", para_strerror(-ret)); - else if (cad.flags & CPSI_FLAG_VERBOSE) { - if (pmd.num_matches > 0) + else if (pmd.num_matches > 0) { + if (cad.flags & CPSI_FLAG_VERBOSE) para_printf(&cad.pb, "copied requested afsi from %s " "to %u files\n", source_path, pmd.num_matches); - else - para_printf(&cad.pb, "nothing copied\n"); - } + } else + para_printf(&cad.pb, "no matches - nothing copied\n"); if (cad.pb.offset) pass_buffer_as_shm(fd, SBD_OUTPUT, cad.pb.buf, cad.pb.offset); free(cad.pb.buf); @@@ -2467,108 -2503,6 +2468,108 @@@ int com_cpsi(struct command_context *cc return ret; } +struct change_atts_data { + uint64_t add_mask, del_mask; + struct para_buffer pb; +}; + +static int change_atts(__a_unused struct osl_table *table, + struct osl_row *row, __a_unused const char *name, void *data) +{ + int ret; + struct osl_object obj; + struct afs_info old_afsi, new_afsi; + struct afsi_change_event_data aced = { + .aft_row = row, + .old_afsi = &old_afsi + }; + struct change_atts_data *cad = data; + + ret = get_afsi_object_of_row(row, &obj); + if (ret < 0) + return ret; + ret = load_afsi(&old_afsi, &obj); + if (ret < 0) + return ret; + new_afsi = old_afsi; + new_afsi.attributes |= cad->add_mask; + new_afsi.attributes &= ~cad->del_mask; + save_afsi(&new_afsi, &obj); /* in-place update */ + afs_event(AFSI_CHANGE, &cad->pb, &aced); + return 1; +} + +static void com_setatt_callback(int fd, const struct osl_object *query) +{ + char *p; + int ret; + size_t len; + struct change_atts_data cad = { + .pb = { + .max_size = shm_get_shmmax(), + .max_size_handler = afs_max_size_handler, + .private_data = &(struct afs_max_size_handler_data) { + .fd = fd, + .band = SBD_OUTPUT + } + } + }; + struct pattern_match_data pmd = { + .table = audio_file_table, + .loop_col_num = AFTCOL_HASH, + .match_col_num = AFTCOL_PATH, + .pm_flags = PM_SKIP_EMPTY_NAME, + .data = &cad, + .action = change_atts + }; + + for (p = query->data; p < (char *)query->data + query->size; p += len + 1) { + char c; + unsigned char bitnum; + + len = strlen(p); + ret = -E_ATTR_SYNTAX; + if (len == 0) + goto out; + c = p[len - 1]; + if (c != '+' && c != '-') + break; + p[len - 1] = '\0'; + ret = get_attribute_bitnum_by_name(p, &bitnum); + if (ret < 0) + goto out; + if (c == '+') + cad.add_mask |= (1UL << bitnum); + else + cad.del_mask |= (1UL << bitnum); + } + ret = -E_ATTR_SYNTAX; + if (!cad.add_mask && !cad.del_mask) + goto out; + pmd.patterns.data = p; + assert(p < (char *)query->data + query->size); + pmd.patterns.size = (char *)query->data + query->size - p; + ret = for_each_matching_row(&pmd); + if (ret < 0) + goto out; + if (pmd.num_matches == 0) + para_printf(&cad.pb, "no matches\n"); +out: + if (ret < 0) + para_printf(&cad.pb, "%s\n", para_strerror(-ret)); + if (cad.pb.offset) + pass_buffer_as_shm(fd, SBD_OUTPUT, cad.pb.buf, cad.pb.offset); + free(cad.pb.buf); +} + +int com_setatt(struct command_context *cc) +{ + if (cc->argc < 3) + return -E_ATTR_SYNTAX; + return send_standard_callback_request(cc->argc - 1, cc->argv + 1, + com_setatt_callback, afs_cb_result_handler, cc); +} + static void afs_stat_callback(int fd, const struct osl_object *query) { int *parser_friendly = query->data; @@@ -2592,7 -2526,7 +2593,7 @@@ * is used to pass the status items from the afs process to the command handler * via a shared memory area and a pipe. * - * \return The return value of the underyling call to \ref send_callback_request(). + * \return The return value of the underlying call to \ref send_callback_request(). */ int send_afs_status(struct command_context *cc, int parser_friendly) { @@@ -2743,7 -2677,7 +2744,7 @@@ static int aft_event_handler(enum afs_e { int ret; - switch(event) { + switch (event) { case ATTRIBUTE_REMOVE: { const struct rmatt_event_data *red = data; ret = para_printf(pb, "clearing attribute %s (bit %u) from all " diff --combined blob.c index 3f7c48da,2f175021..7aedf59b --- a/blob.c +++ b/blob.c @@@ -1,5 -1,5 +1,5 @@@ /* - * Copyright (C) 2007-2013 Andre Noll + * Copyright (C) 2007 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@@ -311,12 -311,12 +311,12 @@@ static int com_rmblob(callback_functio afs_cb_result_handler, cc); } -static void com_addblob_callback(struct osl_table *table, __a_unused int fd, +static void com_addblob_callback(struct osl_table *table, int fd, const struct osl_object *query) { struct osl_object objs[NUM_BLOB_COLUMNS]; - char *name = query->data; - size_t name_len = strlen(name) + 1; + char *name = query->data, *msg; + size_t name_len = strlen(name) + 1, msg_len; uint32_t id; unsigned num_rows; int ret; @@@ -344,10 -344,6 +344,10 @@@ if (ret < 0 && ret != -OSL_ERRNO_TO_PARA_ERROR(E_OSL_RB_KEY_NOT_FOUND)) goto out; if (ret >= 0) { /* we already have a blob with this name */ + ret = osl(osl_get_object(table, row, BLOBCOL_ID, &obj)); + if (ret < 0) + goto out; + id = *(uint32_t *)obj.data; obj.data = name + name_len; obj.size = query->size - name_len; ret = osl(osl_update_object(table, row, BLOBCOL_DEF, &obj)); @@@ -381,88 -377,81 +381,88 @@@ afs_event(BLOB_ADD, NULL, table); out: if (ret < 0) - PARA_NOTICE_LOG("%s\n", para_strerror(-ret)); + msg_len = xasprintf(&msg, "could not add %s: %s\n", name, + para_strerror(-ret)); + else + msg_len = xasprintf(&msg, "added %s as id %u\n", name, id); + pass_buffer_as_shm(fd, SBD_OUTPUT, msg, msg_len); + free(msg); } -/* - * write input from fd to dynamically allocated buffer, - * but maximal max_size byte. - */ -static int fd2buf(struct stream_cipher_context *scc, unsigned max_size, struct osl_object *obj) +/* Write input from fd to dynamically allocated buffer, but maximal 10M. */ +static int fd2buf(struct stream_cipher_context *scc, struct osl_object *obj) { - const size_t chunk_size = 1024; - size_t size = 2048, received = 0; + size_t max_size = 10 * 1024 * 1024; int ret; - char *buf = para_malloc(size); + struct iovec iov; - for (;;) { - ret = sc_recv_bin_buffer(scc, buf + received, chunk_size); - if (ret <= 0) - break; - received += ret; - if (received + chunk_size >= size) { - size *= 2; - ret = -E_INPUT_TOO_LARGE; - if (size > max_size) - break; - buf = para_realloc(buf, size); - } + obj->data = NULL; + obj->size = 0; +again: + do { + ret = recv_sb(scc, SBD_BLOB_DATA, max_size, &iov); + } while (ret == 0); + + if (ret < 0) { + free(obj->data); + obj->data = NULL; + obj->size = 0; + return ret; } - obj->data = buf; - obj->size = received; - if (ret < 0) - free(buf); - return ret; + if (iov.iov_len == 0) /* end of blob */ + return 1; + if (!obj->data) { + obj->data = iov.iov_base; + obj->size = iov.iov_len; + } else { + obj->data = para_realloc(obj->data, obj->size + iov.iov_len); + memcpy(obj->data + obj->size, iov.iov_base, iov.iov_len); + obj->size += iov.iov_len; + free(iov.iov_base); + max_size -= iov.iov_len; + } + goto again; + return 1; } /* * Read data from a file descriptor, and send it to the afs process. * - * \param scc crypt context containing the file descriptor to read data from. + * \param cc Contains the file descriptor to read data from. * \param arg_obj Pointer to the arguments to \a f. * \param f The callback function. - * \param max_len Don't read more than that many bytes from stdin. * \param result_handler See \ref send_callback_request. * \param private_result_data See \ref send_callback_request. * - * This function is used by commands that wish to let para_server store - * arbitrary data specified by the user (for instance the add_blob family of - * commands). First, at most \a max_len bytes are read and decrypted from the - * file descriptor given by \a scc. The result is concatenated with the buffer - * given by \a arg_obj, and the combined buffer is made available to the afs - * process via the callback method. See \ref send_callback_request for details. + * This function is used by the addblob commands that instruct para_server to + * store arbitrary data in a blob table. Input data is read and decrypted from + * the file descriptor given by \a cc. This data is concatenated with the + * buffer given by \a arg_obj, and the combined buffer is made available to the + * afs process via the callback method. See \ref send_callback_request for + * details. * * \return Negative on errors, the return value of the underlying call to * send_callback_request() otherwise. */ static int stdin_command(struct command_context *cc, struct osl_object *arg_obj, - callback_function *f, unsigned max_len, - callback_result_handler *result_handler, + callback_function *f, callback_result_handler *result_handler, void *private_result_data) { struct osl_object query, stdin_obj; int ret; - if (cc->use_sideband) - ret = send_sb(&cc->scc, NULL, 0, SBD_AWAITING_DATA, false); - else - ret = sc_send_buffer(&cc->scc, AWAITING_DATA_MSG); + ret = send_sb(&cc->scc, NULL, 0, SBD_AWAITING_DATA, false); if (ret < 0) return ret; - ret = fd2buf(&cc->scc, max_len, &stdin_obj); + ret = fd2buf(&cc->scc, &stdin_obj); if (ret < 0) return ret; query.size = arg_obj->size + stdin_obj.size; query.data = para_malloc(query.size); memcpy(query.data, arg_obj->data, arg_obj->size); - memcpy((char *)query.data + arg_obj->size, stdin_obj.data, stdin_obj.size); + if (stdin_obj.size > 0) + memcpy((char *)query.data + arg_obj->size, stdin_obj.data, + stdin_obj.size); free(stdin_obj.data); ret = send_callback_request(f, &query, result_handler, private_result_data); free(query.data); @@@ -479,54 -468,38 +479,54 @@@ static int com_addblob(callback_functio return -E_BLOB_SYNTAX; arg_obj.size = strlen(cc->argv[1]) + 1; arg_obj.data = (char *)cc->argv[1]; - return stdin_command(cc, &arg_obj, f, 10 * 1024 * 1024, NULL, NULL); + return stdin_command(cc, &arg_obj, f, afs_cb_result_handler, cc); } -/* FIXME: Print output to client, not to log file */ -static void com_mvblob_callback(struct osl_table *table, __a_unused int fd, +static void com_mvblob_callback(struct osl_table *table, int fd, const struct osl_object *query) { char *src = (char *) query->data; struct osl_object obj = {.data = src, .size = strlen(src) + 1}; char *dest = src + obj.size; struct osl_row *row; + struct para_buffer pb = { + .max_size = shm_get_shmmax(), + .private_data = &fd, + .max_size_handler = afs_max_size_handler + }; int ret = osl(osl_get_row(table, BLOBCOL_NAME, &obj, &row)); - if (ret < 0) + if (ret < 0) { + para_printf(&pb, "could not locate %s: %s\n", src, + para_strerror(-ret)); goto out; + } obj.data = dest; obj.size = strlen(dest) + 1; ret = osl(osl_update_object(table, row, BLOBCOL_NAME, &obj)); - if (ret < 0) + if (ret < 0) { + para_printf(&pb, "failed to update object %s: %s\n", dest, + para_strerror(-ret)); goto out; + } afs_event(BLOB_RENAME, NULL, table); out: - if (ret < 0) - PARA_NOTICE_LOG("%s\n", para_strerror(-ret)); + if (pb.offset) + pass_buffer_as_shm(fd, SBD_OUTPUT, pb.buf, pb.offset); + free(pb.buf); } static int com_mvblob(callback_function *f, struct command_context *cc) { + int ret; + if (cc->argc != 3) return -E_MOOD_SYNTAX; - return send_option_arg_callback_request(NULL, cc->argc - 1, - cc->argv + 1, f, NULL, NULL); + ret = send_option_arg_callback_request(NULL, cc->argc - 1, + cc->argv + 1, f, afs_cb_result_handler, cc); + if (ret < 0) + send_strerror(cc, -ret); + return ret; } #define DEFINE_BLOB_COMMAND(cmd_name, table_name, cmd_prefix) \ @@@ -645,7 -618,7 +645,7 @@@ static int blob_get_name_and_def_by_row static int table_name ## _create(const char *dir) \ { \ table_name ## _table_desc.dir = dir; \ - return osl_create_table(&table_name ## _table_desc); \ + return osl(osl_create_table(&table_name ## _table_desc)); \ } static int blob_open(struct osl_table **table, diff --combined command.c index 43f85ca6,2e733c5c..2ef9c5a8 --- a/command.c +++ b/command.c @@@ -1,20 -1,15 +1,20 @@@ /* - * Copyright (C) 1997-2013 Andre Noll + * Copyright (C) 1997 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ /** \file command.c Client authentication and server commands. */ +#include +#include #include #include #include #include +#include +#include +#include #include "para.h" #include "error.h" @@@ -35,13 -30,13 +35,13 @@@ #include "fd.h" #include "ipc.h" #include "user_list.h" -#include "server_command_list.h" -#include "afs_command_list.h" +#include "server.command_list.h" +#include "afs.command_list.h" #include "signal.h" #include "version.h" -struct server_command afs_cmds[] = {DEFINE_AFS_CMD_ARRAY}; -struct server_command server_cmds[] = {DEFINE_SERVER_CMD_ARRAY}; +static struct server_command afs_cmds[] = {DEFINE_AFS_CMD_ARRAY}; +static struct server_command server_cmds[] = {DEFINE_SERVER_CMD_ARRAY}; /** Commands including options must be shorter than this. */ #define MAX_COMMAND_LEN 32768 @@@ -115,6 -110,7 +115,6 @@@ static unsigned get_status(struct misc_ char mtime[30] = ""; char *status, *flags; /* vss status info */ /* nobody updates our version of "now" */ - char *ut = get_server_uptime_str(NULL); long offset = (nmmd->offset + 500) / 1000; struct timeval current_time; struct tm mtime_tm; @@@ -148,6 -144,7 +148,6 @@@ (long unsigned)current_time.tv_usec); free(flags); free(status); - free(ut); *result = b.buf; return b.offset; } @@@ -210,7 -207,7 +210,7 @@@ static int check_sender_args(int argc, * * The nonblock flag must be disabled for the file descriptor given by \a scc. * - * Stream cipher encryption is automatically activated if neccessary via the + * Stream cipher encryption is automatically activated if necessary via the * sideband transformation, depending on the value of \a band. * * \return Standard. @@@ -223,8 -220,8 +223,8 @@@ int send_sb(struct stream_cipher_contex int ret; struct sb_context *sbc; struct iovec iov[2]; - struct sb_buffer sbb = SBB_INIT(band, buf, numbytes); sb_transformation trafo = band < SBD_PROCEED? NULL : sc_trafo; + struct sb_buffer sbb = SBB_INIT(band, buf, numbytes); sbc = sb_new_send(&sbb, dont_free, trafo, scc->send); do { @@@ -271,7 -268,10 +271,7 @@@ __printf_3_4 int send_sb_va(struct stre */ int send_strerror(struct command_context *cc, int err) { - return cc->use_sideband? - send_sb_va(&cc->scc, SBD_ERROR_LOG, "%s\n", para_strerror(err)) - : - sc_send_va_buffer(&cc->scc, "%s\n", para_strerror(err)); + return send_sb_va(&cc->scc, SBD_ERROR_LOG, "%s\n", para_strerror(err)); } /** @@@ -338,17 -338,23 +338,17 @@@ static int com_sender(struct command_co free(msg); msg = tmp; } - if (cc->use_sideband) - return send_sb(&cc->scc, msg, ret, SBD_OUTPUT, false); - ret = sc_send_buffer(&cc->scc, msg); - free(msg); - return ret; + return send_sb(&cc->scc, msg, ret, SBD_OUTPUT, false); } ret = check_sender_args(cc->argc, cc->argv, &scd); if (ret < 0) { if (scd.sender_num < 0) return ret; - msg = senders[scd.sender_num].help(); - if (cc->use_sideband) - return send_sb(&cc->scc, msg, strlen(msg), SBD_OUTPUT, - false); - ret = sc_send_buffer(&cc->scc, msg); - free(msg); - return ret; + if (strcmp(cc->argv[2], "status") == 0) + msg = senders[scd.sender_num].status(); + else + msg = senders[scd.sender_num].help(); + return send_sb(&cc->scc, msg, strlen(msg), SBD_OUTPUT, false); } switch (scd.cmd_num) { @@@ -363,13 -369,11 +363,13 @@@ for (i = 0; i < 10; i++) { mutex_lock(mmd_mutex); if (mmd->sender_cmd_data.cmd_num >= 0) { + /* another sender command is active, retry in 100ms */ + struct timespec ts = {.tv_nsec = 100 * 1000 * 1000}; mutex_unlock(mmd_mutex); - usleep(100 * 1000); + nanosleep(&ts, NULL); continue; } - memcpy(&mmd->sender_cmd_data, &scd, sizeof(scd)); + mmd->sender_cmd_data = scd; mutex_unlock(mmd_mutex); break; } @@@ -379,20 -383,28 +379,20 @@@ /* server info */ static int com_si(struct command_context *cc) { - int i, ret; - char *msg, *ut, *sender_info = NULL; + int ret; + char *msg, *ut; if (cc->argc != 1) return -E_COMMAND_SYNTAX; mutex_lock(mmd_mutex); - for (i = 0; senders[i].name; i++) { - char *info = senders[i].info(); - sender_info = para_strcat(sender_info, info); - free(info); - } - ut = get_server_uptime_str(now); + ut = daemon_get_uptime_str(now); ret = xasprintf(&msg, - "version: %s\n" "up: %s\nplayed: %u\n" "server_pid: %d\n" "afs_pid: %d\n" "connections (active/accepted/total): %u/%u/%u\n" "current loglevel: %s\n" - "supported audio formats: %s\n" - "%s", - version_git(), + "supported audio formats: %s\n", ut, mmd->num_played, (int)getppid(), (int)mmd->afs_pid, @@@ -400,11 -412,17 +400,11 @@@ mmd->num_commands, mmd->num_connects, conf.loglevel_arg, - AUDIO_FORMAT_HANDLERS, - sender_info + AUDIO_FORMAT_HANDLERS ); mutex_unlock(mmd_mutex); free(ut); - free(sender_info); - if (cc->use_sideband) - return send_sb(&cc->scc, msg, ret, SBD_OUTPUT, false); - ret = sc_send_bin_buffer(&cc->scc, msg, ret); - free(msg); - return ret; + return send_sb(&cc->scc, msg, ret, SBD_OUTPUT, false); } /* version */ @@@ -416,9 -434,12 +416,10 @@@ static int com_version(struct command_c if (cc->argc != 1) return -E_COMMAND_SYNTAX; len = xasprintf(&msg, "%s", version_text("server")); - if (cc->use_sideband) - return send_sb(&cc->scc, msg, len, SBD_OUTPUT, false); - return sc_send_bin_buffer(&cc->scc, msg, len); + return send_sb(&cc->scc, msg, len, SBD_OUTPUT, false); } + /** These status items are cleared if no audio file is currently open. */ #define EMPTY_STATUS_ITEMS \ ITEM(PATH) \ ITEM(DIRECTORY) \ @@@ -445,7 -466,11 +446,11 @@@ ITEM(YEAR) \ ITEM(ALBUM) \ ITEM(COMMENT) \ - ITEM(AMPLIFICATION) + ITEM(MTIME) \ + ITEM(FILE_SIZE) \ + ITEM(CHUNK_TIME) \ + ITEM(NUM_CHUNKS) \ + ITEM(AMPLIFICATION) \ /** * Write a list of audio-file related status items with empty values. @@@ -516,13 -541,24 +521,13 @@@ static int com_stat(struct command_cont for (;;) { mmd_dup(nmmd); ret = get_status(nmmd, parser_friendly, &s); - if (cc->use_sideband) - ret = send_sb(&cc->scc, s, ret, SBD_OUTPUT, false); - else { - ret = sc_send_bin_buffer(&cc->scc, s, ret); - free(s); - } + ret = send_sb(&cc->scc, s, ret, SBD_OUTPUT, false); if (ret < 0) goto out; if (nmmd->vss_status_flags & VSS_NEXT) { char *esi; ret = empty_status_items(parser_friendly, &esi); - if (cc->use_sideband) - ret = send_sb(&cc->scc, esi, ret, SBD_OUTPUT, - false); - else { - ret = sc_send_bin_buffer(&cc->scc, esi, ret); - free(esi); - } + ret = send_sb(&cc->scc, esi, ret, SBD_OUTPUT, false); if (ret < 0) goto out; } else @@@ -542,6 -578,7 +547,6 @@@ out static int send_list_of_commands(struct command_context *cc, struct server_command *cmd, const char *handler) { - int ret; char *msg = NULL; for (; cmd->name; cmd++) { @@@ -552,8 -589,11 +557,8 @@@ msg = para_strcat(msg, tmp); free(tmp); } - if (cc->use_sideband) - return send_sb(&cc->scc, msg, strlen(msg), SBD_OUTPUT, false); - ret = sc_send_buffer(&cc->scc, msg); - free(msg); - return ret; + assert(msg); + return send_sb(&cc->scc, msg, strlen(msg), SBD_OUTPUT, false); } /* returns string that must be freed by the caller */ @@@ -609,7 -649,11 +614,7 @@@ static int com_help(struct command_cont ); free(perms); free(handler); - if (cc->use_sideband) - return send_sb(&cc->scc, buf, ret, SBD_OUTPUT, false); - ret = sc_send_buffer(&cc->scc, buf); - free(buf); - return ret; + return send_sb(&cc->scc, buf, ret, SBD_OUTPUT, false); } /* hup */ @@@ -718,7 -762,7 +723,7 @@@ static int com_ff(struct command_contex promille += 1000 * i / mmd->afd.afhi.seconds_total; if (promille < 0) promille = 0; - if (promille > 1000) { + if (promille > 1000) { mmd->new_vss_status_flags |= VSS_NEXT; goto out; } @@@ -749,8 -793,8 +754,8 @@@ static int com_jmp(struct command_conte if (i > 100) i = 100; PARA_INFO_LOG("jumping to %lu%%\n", i); - mmd->repos_request = (mmd->afd.afhi.chunks_total * i + 50)/ 100; - PARA_INFO_LOG("sent: %lu, offset before jmp: %lu\n", + mmd->repos_request = (mmd->afd.afhi.chunks_total * i + 50) / 100; + PARA_INFO_LOG("sent: %lu, offset before jmp: %lu\n", mmd->chunks_sent, mmd->offset); mmd->new_vss_status_flags |= VSS_REPOS; mmd->new_vss_status_flags &= ~VSS_NEXT; @@@ -761,16 -805,6 +766,16 @@@ out return ret; } +static int com_tasks(struct command_context *cc) +{ + char *tl = server_get_tasks(); + int ret = 1; + + if (tl) + ret = send_sb(&cc->scc, tl, strlen(tl), SBD_OUTPUT, false); + return ret; +} + /* * check if perms are sufficient to exec a command having perms cmd_perms. * Returns 0 if perms are sufficient, -E_PERM otherwise. @@@ -781,6 -815,58 +786,6 @@@ static int check_perms(unsigned int per return (cmd_ptr->perms & perms) < cmd_ptr->perms ? -E_PERM : 0; } -/* - * Parse first string from *cmd and lookup in table of valid commands. - * On error, NULL is returned. - */ -static struct server_command *parse_cmd(const char *cmdstr) -{ - char buf[255]; - int n = 0; - - sscanf(cmdstr, "%200s%n", buf, &n); - if (!n) - return NULL; - buf[n] = '\0'; - return get_cmd_ptr(buf, NULL); -} - -static int read_command(struct stream_cipher_context *scc, char **result) -{ - int ret; - char buf[4096]; - char *command = NULL; - - for (;;) { - size_t numbytes; - char *p; - - ret = sc_recv_buffer(scc, buf, sizeof(buf)); - if (ret < 0) - goto out; - if (!ret) - break; - numbytes = ret; - ret = -E_COMMAND_SYNTAX; - if (command && numbytes + strlen(command) > MAX_COMMAND_LEN) /* DOS */ - goto out; - command = para_strcat(command, buf); - p = strstr(command, EOC_MSG); - if (p) { - *p = '\0'; - break; - } - } - ret = command? 1 : -E_COMMAND_SYNTAX; -out: - if (ret < 0) - free(command); - else - *result = command; - return ret; - -} - static void reset_signals(void) { para_sigaction(SIGCHLD, SIG_IGN); @@@ -789,20 -875,15 +794,20 @@@ para_sigaction(SIGHUP, SIG_DFL); } +struct connection_features { + bool sideband_requested; + bool aes_ctr128_requested; +}; + static int parse_auth_request(char *buf, int len, struct user **u, - bool *use_sideband) + struct connection_features *cf) { int ret; char *p, *username, **features = NULL; size_t auth_rq_len = strlen(AUTH_REQUEST_MSG); *u = NULL; - *use_sideband = false; + memset(cf, 0, sizeof(*cf)); if (len < auth_rq_len + 2) return -E_AUTH_REQUEST; if (strncmp(buf, AUTH_REQUEST_MSG, auth_rq_len) != 0) @@@ -818,16 -899,15 +823,16 @@@ create_argv(p, ",", &features); for (i = 0; features[i]; i++) { if (strcmp(features[i], "sideband") == 0) - *use_sideband = true; + cf->sideband_requested = true; + else if (strcmp(features[i], "aes_ctr128") == 0) + cf->aes_ctr128_requested = true; else { ret = -E_BAD_FEATURE; goto out; } } } - PARA_DEBUG_LOG("received auth request for user %s (sideband = %s)\n", - username, *use_sideband? "true" : "false"); + PARA_DEBUG_LOG("received auth request for user %s\n", username); *u = lookup_user(username); ret = 1; out: @@@ -854,7 -934,7 +859,7 @@@ static int parse_sb_command(struct comm if (ret < 0) goto out; end = iov->iov_base + iov->iov_len; - for (i = 0, p = iov->iov_base; p < end; i++) + for (i = 0; p < end; i++) p += strlen(p) + 1; cc->argc = i; cc->argv = para_malloc((cc->argc + 1) * sizeof(char *)); @@@ -875,22 -955,24 +880,22 @@@ out * \param fd The file descriptor to send output to. * \param peername Identifies the connecting peer. * - * Whenever para_server accepts an incoming tcp connection on - * the port it listens on, it forks and the resulting child - * calls this function. + * Whenever para_server accepts an incoming tcp connection on the port it + * listens on, it forks and the resulting child calls this function. * - * An RSA-based challenge/response is used to authenticate - * the peer. It that authentication succeeds, a random - * session key is generated and sent back to the peer, - * encrypted with its RSA public key. From this point on, - * all transfers are crypted with this session key. + * An RSA-based challenge/response is used to authenticate the peer. It that + * authentication succeeds, a random session key is generated and sent back to + * the peer, encrypted with its RSA public key. From this point on, all + * transfers are crypted with this session key. * - * Next it is checked if the peer supplied a valid server command or a command - * for the audio file selector. If yes, and if the user has sufficient + * Next it is checked if the peer supplied a valid server command or a command + * for the audio file selector. If yes, and if the user has sufficient * permissions to execute that command, the function calls the corresponding * command handler which does argument checking and further processing. * - * In order to cope with a DOS attacks, a timeout is set up - * which terminates the function if the connection was not - * authenticated when the timeout expires. + * In order to cope with a DOS attacks, a timeout is set up which terminates + * the function if the connection was not authenticated when the timeout + * expires. * * \sa alarm(2), crypt.c, crypt.h */ @@@ -899,11 -981,9 +904,11 @@@ __noreturn void handle_connect(int fd, int ret; unsigned char rand_buf[CHALLENGE_SIZE + 2 * SESSION_KEY_LEN]; unsigned char challenge_hash[HASH_SIZE]; - char *p, *command = NULL, *buf = para_malloc(HANDSHAKE_BUFSIZE) /* must be on the heap */; + char *command = NULL, *buf = para_malloc(HANDSHAKE_BUFSIZE) /* must be on the heap */; size_t numbytes; struct command_context cc_struct = {.peer = peername}, *cc = &cc_struct; + struct iovec iov; + struct connection_features cf; cc->scc.fd = fd; reset_signals(); @@@ -913,8 -993,8 +918,8 @@@ goto net_err; /* send Welcome message */ ret = write_va_buffer(fd, "This is para_server, version " - PACKAGE_VERSION ".\n" - "Features: sideband\n" + PACKAGE_VERSION ".\n" + "Features: sideband,aes_ctr128\n" ); if (ret < 0) goto net_err; @@@ -922,14 -1002,12 +927,14 @@@ ret = recv_buffer(fd, buf, HANDSHAKE_BUFSIZE); if (ret < 0) goto net_err; - ret = parse_auth_request(buf, ret, &cc->u, &cc->use_sideband); + ret = parse_auth_request(buf, ret, &cc->u, &cf); if (ret < 0) goto net_err; - p = buf + strlen(AUTH_REQUEST_MSG); - PARA_DEBUG_LOG("received auth request for user %s\n", p); - cc->u = lookup_user(p); + if (!cf.sideband_requested) { /* sideband is mandatory */ + PARA_ERROR_LOG("client did not request sideband\n"); + ret = -E_BAD_FEATURE; + goto net_err; + } if (cc->u) { get_random_bytes_or_die(rand_buf, sizeof(rand_buf)); ret = pub_encrypt(cc->u->pubkey, rand_buf, sizeof(rand_buf), @@@ -946,18 -1024,30 +951,18 @@@ numbytes = 256; get_random_bytes_or_die((unsigned char *)buf, numbytes); } - PARA_DEBUG_LOG("sending %u byte challenge + rc4 keys (%zu bytes)\n", + PARA_DEBUG_LOG("sending %u byte challenge + session key (%zu bytes)\n", CHALLENGE_SIZE, numbytes); - if (cc->use_sideband) { - struct iovec iov; - ret = send_sb(&cc->scc, buf, numbytes, SBD_CHALLENGE, false); - buf = NULL; - if (ret < 0) - goto net_err; - ret = recv_sb(&cc->scc, SBD_CHALLENGE_RESPONSE, - HANDSHAKE_BUFSIZE, &iov); - if (ret < 0) - goto net_err; - buf = iov.iov_base; - numbytes = iov.iov_len; - } else { - ret = write_all(fd, buf, numbytes); - if (ret < 0) - goto net_err; - /* recv challenge response */ - ret = recv_bin_buffer(fd, buf, HASH_SIZE); - if (ret < 0) - goto net_err; - numbytes = ret; - } + ret = send_sb(&cc->scc, buf, numbytes, SBD_CHALLENGE, false); + buf = NULL; + if (ret < 0) + goto net_err; + ret = recv_sb(&cc->scc, SBD_CHALLENGE_RESPONSE, + HANDSHAKE_BUFSIZE, &iov); + if (ret < 0) + goto net_err; + buf = iov.iov_base; + numbytes = iov.iov_len; PARA_DEBUG_LOG("received %zu bytes challenge response\n", numbytes); ret = -E_BAD_USER; if (!cc->u) @@@ -976,20 -1066,43 +981,20 @@@ alarm(0); PARA_INFO_LOG("good auth for %s\n", cc->u->name); /* init stream cipher keys with the second part of the random buffer */ - cc->scc.recv = sc_new(rand_buf + CHALLENGE_SIZE, SESSION_KEY_LEN); - cc->scc.send = sc_new(rand_buf + CHALLENGE_SIZE + SESSION_KEY_LEN, SESSION_KEY_LEN); - if (cc->use_sideband) - ret = send_sb(&cc->scc, NULL, 0, SBD_PROCEED, false); - else - ret = sc_send_buffer(&cc->scc, PROCEED_MSG); + cc->scc.recv = sc_new(rand_buf + CHALLENGE_SIZE, SESSION_KEY_LEN, + cf.aes_ctr128_requested); + cc->scc.send = sc_new(rand_buf + CHALLENGE_SIZE + SESSION_KEY_LEN, + SESSION_KEY_LEN, cf.aes_ctr128_requested); + ret = send_sb(&cc->scc, NULL, 0, SBD_PROCEED, false); if (ret < 0) goto net_err; - if (cc->use_sideband) { - struct iovec iov; - ret = recv_sb(&cc->scc, SBD_COMMAND, MAX_COMMAND_LEN, &iov); - if (ret < 0) - goto net_err; - ret = parse_sb_command(cc, &iov); - if (ret < 0) - goto err_out; - cc->argc = ret; - } else { - ret = read_command(&cc->scc, &command); - if (ret == -E_COMMAND_SYNTAX) - goto err_out; - if (ret < 0) - goto net_err; - ret = -E_BAD_CMD; - cc->cmd = parse_cmd(command); - if (!cc->cmd) - goto err_out; - /* valid command, check permissions */ - ret = check_perms(cc->u->perms, cc->cmd); - if (ret < 0) - goto err_out; - /* valid command and sufficient perms */ - ret = create_argv(command, "\n", &cc->argv); - if (ret < 0) - goto err_out; - cc->argc = ret; - } + ret = recv_sb(&cc->scc, SBD_COMMAND, MAX_COMMAND_LEN, &iov); + if (ret < 0) + goto net_err; + ret = parse_sb_command(cc, &iov); + if (ret < 0) + goto err_out; + cc->argc = ret; PARA_NOTICE_LOG("calling com_%s() for %s@%s\n", cc->cmd->name, cc->u->name, peername); ret = cc->cmd->handler(cc); @@@ -1000,7 -1113,7 +1005,7 @@@ if (ret >= 0) goto out; err_out: - if (send_strerror(cc, -ret) >= 0 && cc->use_sideband) + if (send_strerror(cc, -ret) >= 0) send_sb(&cc->scc, NULL, 0, SBD_EXIT__FAILURE, true); net_err: PARA_NOTICE_LOG("%s\n", para_strerror(-ret)); @@@ -1012,7 -1125,7 +1017,7 @@@ out mmd->events++; mmd->active_connections--; mutex_unlock(mmd_mutex); - if (ret >= 0 && cc->use_sideband) { + if (ret >= 0) { ret = send_sb(&cc->scc, NULL, 0, SBD_EXIT__SUCCESS, true); if (ret < 0) PARA_NOTICE_LOG("%s\n", para_strerror(-ret)); diff --combined fd.c index 456476e5,20dc07da..ceff71f5 --- a/fd.c +++ b/fd.c @@@ -1,5 -1,5 +1,5 @@@ /* - * Copyright (C) 2006-2013 Andre Noll + * Copyright (C) 2006 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@@ -10,6 -10,8 +10,6 @@@ #include #include #include -#include -#include #include "para.h" #include "error.h" @@@ -144,6 -146,7 +144,7 @@@ __printf_2_3 int write_va_buffer(int fd va_start(ap, fmt); ret = xvasprintf(&msg, fmt, ap); + va_end(ap); ret = write_all(fd, msg, ret); free(msg); return ret; @@@ -166,7 -169,7 +167,7 @@@ * * \return Zero or a negative error code. If the underlying call to readv(2) * returned zero (indicating an end of file condition) or failed for some - * reason other than \p EAGAIN, a negative return value is returned. + * reason other than \p EAGAIN, a negative error code is returned. * * In any case, \a num_bytes contains the number of bytes that have been * successfully read from \a fd (zero if the first readv() call failed with @@@ -524,7 -527,6 +525,7 @@@ static int para_opendir(const char *dir { int ret; + *dir = NULL; if (cwd) { ret = para_open(".", O_RDONLY, 0); if (ret < 0)