X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=blobdiff_plain;f=aft.c;h=0544091bdba4e5054b120e24322504f56e38df4f;hp=063e50681575573c0e6a45f1cdb4acb04c406fc5;hb=9c9d3e49b2f17e5a83b44b009785b9b6dd5b95aa;hpb=74767b3c0c1eb2056ea5577011bc3f37af917496 diff --git a/aft.c b/aft.c index 063e5068..0544091b 100644 --- a/aft.c +++ b/aft.c @@ -1,8 +1,4 @@ -/* - * Copyright (C) 2007 Andre Noll - * - * Licensed under the GPL v2. For licencing details see COPYING. - */ +/* Copyright (C) 2007 Andre Noll , see file COPYING. */ /** \file aft.c Audio file table functions. */ @@ -26,9 +22,31 @@ #include "sideband.h" #include "command.h" -static struct osl_table *audio_file_table; +/* Data about one audio file. Needed for ls and stat output. */ +struct ls_data { + /* Usual audio format handler information. */ + struct afh_info afhi; + /* Audio file selector information. */ + struct afs_info afsi; + /* The full path of the audio file. */ + char *path; + /* The score value (if -a was given). */ + long score; + /* The hash value of the audio file data. */ + unsigned char *hash; +}; + +/* + * The internal state of the audio file table is described by the following + * variables which are private to aft.c. + */ +static struct osl_table *audio_file_table; /* NULL if table not open */ +static struct osl_row *current_aft_row; /* NULL if no audio file open */ +static unsigned char current_hash[HASH_SIZE]; /* only used on sighup */ + static char *status_items; static char *parser_friendly_status_items; +static struct ls_data status_item_ls_data; /** The different sorting methods of the ls command. */ enum ls_sorting_method { @@ -74,20 +92,6 @@ enum ls_listing_mode { LS_MODE_PARSER, }; -/* Data about one audio file. Needed for ls and stat output. */ -struct ls_data { - /* Usual audio format handler information. */ - struct afh_info afhi; - /* Audio file selector information. */ - struct afs_info afsi; - /* The full path of the audio file. */ - char *path; - /* The score value (if -a was given). */ - long score; - /* The hash value of the audio file data. */ - unsigned char *hash; -}; - /** * The size of the individual output fields of the ls command. * @@ -138,7 +142,7 @@ struct ls_options { /** * Describes the layout of the mmapped-afs info struct. * - * \sa struct afs_info. + * \sa struct \ref afs_info. */ enum afsi_offsets { /** Where .last_played is stored. */ @@ -167,7 +171,7 @@ enum afsi_offsets { * \param afsi Pointer to the audio file info to be converted. * \param obj Result pointer. * - * \sa load_afsi(). + * \sa \ref load_afsi(). */ static void save_afsi(struct afs_info *afsi, struct osl_object *obj) { @@ -192,7 +196,7 @@ static void save_afsi(struct afs_info *afsi, struct osl_object *obj) * * \return Standard. * - * \sa save_afsi(). + * \sa \ref save_afsi(). */ static int load_afsi(struct afs_info *afsi, struct osl_object *obj) { @@ -365,7 +369,10 @@ static void save_afhi(struct afh_info *afhi, char *buf) write_u32(buf + CHUNK_TV_TV_SEC_OFFSET, afhi->chunk_tv.tv_sec); write_u32(buf + CHUNK_TV_TV_USEC_OFFSET, afhi->chunk_tv.tv_usec); p = buf + AFHI_INFO_STRING_OFFSET; - /* The sprintf's below are OK as our caller made sure that buf is large enough */ + /* + * The below sprintf(3) calls are OK because our caller already made + * sure that buf is large enough. + */ p += sprintf(p, "%s", afhi->techinfo) + 1; p += sprintf(p, "%s", afhi->tags.artist) + 1; p += sprintf(p, "%s", afhi->tags.title) + 1; @@ -374,6 +381,7 @@ static void save_afhi(struct afh_info *afhi, char *buf) sprintf(p, "%s", afhi->tags.comment); } +/* does not load the chunk table */ static void load_afhi(const char *buf, struct afh_info *afhi) { afhi->seconds_total = read_u32(buf + AFHI_SECONDS_TOTAL_OFFSET); @@ -528,7 +536,7 @@ static int get_afsi_of_path(const char *path, struct afs_info *afsi) * \param row Pointer to a row in the audio file table. * \param path Result pointer. * - * The result is a pointer to mmapped data. The caller must not attempt + * The result is a pointer to memory-mapped data. The caller must not attempt * to free it. * * \return Standard. @@ -538,10 +546,12 @@ int get_audio_file_path_of_row(const struct osl_row *row, char **path) struct osl_object path_obj; int ret = osl(osl_get_object(audio_file_table, row, AFTCOL_PATH, &path_obj)); + if (ret < 0) - return ret; - *path = path_obj.data; - return 1; + *path = NULL; + else + *path = path_obj.data; + return ret; } /** @@ -552,7 +562,7 @@ int get_audio_file_path_of_row(const struct osl_row *row, char **path) * * \return The return value of the underlying call to osl_get_object(). * - * \sa get_hash_of_row(). + * \sa \ref get_hash_of_row(). */ static int get_hash_object_of_aft_row(const struct osl_row *row, struct osl_object *obj) @@ -590,7 +600,9 @@ static int get_hash_of_row(const struct osl_row *row, unsigned char **hash) * * \return The return value of the underlying call to osl_get_object(). * - * \sa get_chunk_table_of_row(). + * After the call the members of the afhi structure point to mapped memory + * which is owned by the osl table, Hence the caller must not attempt to free + * this memory by calling \ref clear_afhi(). */ int get_afhi_of_row(const struct osl_row *row, struct afh_info *afhi) { @@ -748,11 +760,11 @@ static int write_attribute_items(struct para_buffer *b, char *att_text; int ret; - WRITE_STATUS_ITEM(b, SI_ATTRIBUTES_BITMAP, "%s\n", att_bitmap); + WRITE_STATUS_ITEM(b, SI_attributes_bitmap, "%s\n", att_bitmap); ret = get_attribute_text(&afsi->attributes, " ", &att_text); if (ret < 0) return ret; - WRITE_STATUS_ITEM(b, SI_ATTRIBUTES_TXT, "%s\n", att_text); + WRITE_STATUS_ITEM(b, SI_attributes_txt, "%s\n", att_text); free(att_text); return ret; } @@ -761,9 +773,9 @@ static void write_lyrics_items(struct para_buffer *b, struct afs_info *afsi) { char *lyrics_name; - WRITE_STATUS_ITEM(b, SI_LYRICS_ID, "%u\n", afsi->lyrics_id); + WRITE_STATUS_ITEM(b, SI_lyrics_id, "%u\n", afsi->lyrics_id); lyr_get_name_by_id(afsi->lyrics_id, &lyrics_name); - WRITE_STATUS_ITEM(b, SI_LYRICS_NAME, "%s\n", lyrics_name? + WRITE_STATUS_ITEM(b, SI_lyrics_name, "%s\n", lyrics_name? lyrics_name : "(none)"); } @@ -771,9 +783,9 @@ static void write_image_items(struct para_buffer *b, struct afs_info *afsi) { char *image_name; - WRITE_STATUS_ITEM(b, SI_IMAGE_ID, "%u\n", afsi->image_id); + WRITE_STATUS_ITEM(b, SI_image_id, "%u\n", afsi->image_id); img_get_name_by_id(afsi->image_id, &image_name); - WRITE_STATUS_ITEM(b, SI_IMAGE_NAME, "%s\n", image_name? + WRITE_STATUS_ITEM(b, SI_image_name, "%s\n", image_name? image_name : "(none)"); } @@ -783,14 +795,14 @@ static void write_filename_items(struct para_buffer *b, const char *path, char *val; if (basename) { - WRITE_STATUS_ITEM(b, SI_BASENAME, "%s\n", path); + WRITE_STATUS_ITEM(b, SI_basename, "%s\n", path); return; } - WRITE_STATUS_ITEM(b, SI_PATH, "%s\n", path); + WRITE_STATUS_ITEM(b, SI_path, "%s\n", path); val = para_basename(path); - WRITE_STATUS_ITEM(b, SI_BASENAME, "%s\n", val? val : ""); + WRITE_STATUS_ITEM(b, SI_basename, "%s\n", val? val : ""); val = para_dirname(path); - WRITE_STATUS_ITEM(b, SI_DIRECTORY, "%s\n", val? val : ""); + WRITE_STATUS_ITEM(b, SI_directory, "%s\n", val? val : ""); free(val); } @@ -907,36 +919,36 @@ static int print_list_item(struct ls_data *d, struct ls_options *opts, } write_filename_items(b, d->path, lls_opt_given(r_b)); if (lls_opt_given(r_a)) - WRITE_STATUS_ITEM(b, SI_SCORE, "%li\n", d->score); + WRITE_STATUS_ITEM(b, SI_score, "%li\n", d->score); ret = write_attribute_items(b, att_buf, afsi); if (ret < 0) goto out; write_image_items(b, afsi); write_lyrics_items(b, afsi); hash_to_asc(d->hash, asc_hash); - WRITE_STATUS_ITEM(b, SI_HASH, "%s\n", asc_hash); - WRITE_STATUS_ITEM(b, SI_BITRATE, "%dkbit/s\n", afhi->bitrate); - WRITE_STATUS_ITEM(b, SI_FORMAT, "%s\n", + WRITE_STATUS_ITEM(b, SI_hash, "%s\n", asc_hash); + WRITE_STATUS_ITEM(b, SI_bitrate, "%dkbit/s\n", afhi->bitrate); + WRITE_STATUS_ITEM(b, SI_format, "%s\n", audio_format_name(afsi->audio_format_id)); - WRITE_STATUS_ITEM(b, SI_FREQUENCY, "%dHz\n", afhi->frequency); - WRITE_STATUS_ITEM(b, SI_CHANNELS, "%d\n", afhi->channels); - WRITE_STATUS_ITEM(b, SI_DURATION, "%s\n", duration_buf); - WRITE_STATUS_ITEM(b, SI_SECONDS_TOTAL, "%" PRIu32 "\n", + WRITE_STATUS_ITEM(b, SI_frequency, "%dHz\n", afhi->frequency); + WRITE_STATUS_ITEM(b, SI_channels, "%d\n", afhi->channels); + WRITE_STATUS_ITEM(b, SI_duration, "%s\n", duration_buf); + WRITE_STATUS_ITEM(b, SI_seconds_total, "%" PRIu32 "\n", afhi->seconds_total); - WRITE_STATUS_ITEM(b, SI_LAST_PLAYED, "%s\n", last_played_time); - WRITE_STATUS_ITEM(b, SI_NUM_PLAYED, "%u\n", afsi->num_played); - WRITE_STATUS_ITEM(b, SI_AMPLIFICATION, "%u\n", afsi->amp); - WRITE_STATUS_ITEM(b, SI_CHUNK_TIME, "%lu\n", tv2ms(&afhi->chunk_tv)); - WRITE_STATUS_ITEM(b, SI_NUM_CHUNKS, "%" PRIu32 "\n", + WRITE_STATUS_ITEM(b, SI_last_played, "%s\n", last_played_time); + WRITE_STATUS_ITEM(b, SI_num_played, "%u\n", afsi->num_played); + WRITE_STATUS_ITEM(b, SI_amplification, "%u\n", afsi->amp); + WRITE_STATUS_ITEM(b, SI_chunk_time, "%lu\n", tv2ms(&afhi->chunk_tv)); + WRITE_STATUS_ITEM(b, SI_num_chunks, "%" PRIu32 "\n", afhi->chunks_total); - WRITE_STATUS_ITEM(b, SI_MAX_CHUNK_SIZE, "%" PRIu32 "\n", + WRITE_STATUS_ITEM(b, SI_max_chunk_size, "%" PRIu32 "\n", afhi->max_chunk_size); - WRITE_STATUS_ITEM(b, SI_TECHINFO, "%s\n", afhi->techinfo); - WRITE_STATUS_ITEM(b, SI_ARTIST, "%s\n", afhi->tags.artist); - WRITE_STATUS_ITEM(b, SI_TITLE, "%s\n", afhi->tags.title); - WRITE_STATUS_ITEM(b, SI_YEAR, "%s\n", afhi->tags.year); - WRITE_STATUS_ITEM(b, SI_ALBUM, "%s\n", afhi->tags.album); - WRITE_STATUS_ITEM(b, SI_COMMENT, "%s\n", afhi->tags.comment); + WRITE_STATUS_ITEM(b, SI_techinfo, "%s\n", afhi->techinfo); + WRITE_STATUS_ITEM(b, SI_artist, "%s\n", afhi->tags.artist); + WRITE_STATUS_ITEM(b, SI_title, "%s\n", afhi->tags.title); + WRITE_STATUS_ITEM(b, SI_year, "%s\n", afhi->tags.year); + WRITE_STATUS_ITEM(b, SI_album, "%s\n", afhi->tags.album); + WRITE_STATUS_ITEM(b, SI_comment, "%s\n", afhi->tags.comment); if (opts->mode == LS_MODE_MBOX) { struct osl_object lyrics_def; lyr_get_def_by_id(afsi->lyrics_id, &lyrics_def); @@ -950,9 +962,6 @@ out: return ret; } -static struct ls_data status_item_ls_data; -static struct osl_row *current_aft_row; - static void make_inode_status_items(struct para_buffer *pb) { struct stat statbuf = {.st_size = 0}; @@ -970,8 +979,19 @@ static void make_inode_status_items(struct para_buffer *pb) ret = strftime(mtime_str, 29, "%b %d %Y", &mtime_tm); assert(ret > 0); /* number of bytes placed in mtime_str */ out: - WRITE_STATUS_ITEM(pb, SI_MTIME, "%s\n", mtime_str); - WRITE_STATUS_ITEM(pb, SI_FILE_SIZE, "%ld\n", statbuf.st_size / 1024); + WRITE_STATUS_ITEM(pb, SI_mtime, "%s\n", mtime_str); + WRITE_STATUS_ITEM(pb, SI_file_size, "%ld\n", statbuf.st_size / 1024); +} + +/** + * Deallocate and invalidate the status item strings. + * + * This needs to be a public function so that afs.c can call it on shutdown. + */ +void free_status_items(void) +{ + freep(&status_items); + freep(&parser_friendly_status_items); } static int make_status_items(void) @@ -983,6 +1003,9 @@ static int make_status_items(void) time_t current_time; int ret; + free_status_items(); + if (!status_item_ls_data.path) /* no audio file open */ + return 0; ret = lls_parse(ARRAY_SIZE(argv), argv, cmd, &opts.lpr, NULL); assert(ret >= 0); time(¤t_time); @@ -990,23 +1013,20 @@ static int make_status_items(void) if (ret < 0) goto out; make_inode_status_items(&pb); - free(status_items); status_items = pb.buf; memset(&pb, 0, sizeof(pb)); pb.max_size = shm_get_shmmax() - 1; pb.flags = PBF_SIZE_PREFIX; ret = print_list_item(&status_item_ls_data, &opts, &pb, current_time); - if (ret < 0) { - free(status_items); - status_items = NULL; - return ret; - } + if (ret < 0) + goto out; make_inode_status_items(&pb); - free(parser_friendly_status_items); parser_friendly_status_items = pb.buf; ret = 1; out: + if (ret < 0) + free_status_items(); lls_free_parse_result(opts.lpr, cmd); return ret; } @@ -1104,6 +1124,12 @@ out: return ret; } +static int ls_hash_compare(const void *a, const void *b) +{ + struct ls_data *d1 = *(struct ls_data **)a, *d2 = *(struct ls_data **)b; + return memcmp(d1->hash, d2->hash, HASH_SIZE); +} + static int ls_audio_format_compare(const void *a, const void *b) { struct ls_data *d1 = *(struct ls_data **)a, *d2 = *(struct ls_data **)b; @@ -1221,6 +1247,8 @@ static int sort_matching_paths(struct ls_options *options) compar = ls_duration_compare; break; case LS_SORT_BY_AUDIO_FORMAT: compar = ls_audio_format_compare; break; + case LS_SORT_BY_HASH: + compar = ls_hash_compare; break; default: return -E_BAD_SORT; } @@ -1369,7 +1397,6 @@ out: return ret; } -/* TODO: flags -h (sort by hash) */ static int com_ls(struct command_context *cc, struct lls_parse_result *lpr) { const struct lls_command *cmd = SERVER_CMD_CMD_PTR(LS); @@ -1435,6 +1462,8 @@ static int com_ls(struct command_context *cc, struct lls_parse_result *lpr) opts->sorting = LS_SORT_BY_DURATION; else if (!strcmp(val, "a") || !strcmp(val, "audio-format")) opts->sorting = LS_SORT_BY_AUDIO_FORMAT; + else if (!strcmp(val, "h") || !strcmp(val, "hash")) + opts->sorting = LS_SORT_BY_HASH; else { ret = -E_AFT_SYNTAX; goto out; @@ -1575,7 +1604,7 @@ ACTION: Table modifications to be done by the callback. +----+----+---+------+---------------------------------------------------+ | N | N | Y | Y | (new file) create new entry (force has no effect) +----+----+---+------+---------------------------------------------------+ -| N | N | N | Y | (new file) create new entry +| N | N | N | Y | (new file) create new entry +----+----+---+------+---------------------------------------------------+ Notes: @@ -1907,11 +1936,7 @@ out: } EXPORT_SERVER_CMD_HANDLER(add); -/** - * Flags used by the touch command. - * - * \sa com_touch(). - */ +/** Flags used by the touch command. */ enum touch_flags { /** Whether the \p FNM_PATHNAME flag should be passed to fnmatch(). */ TOUCH_FLAG_FNM_PATHNAME = 1, @@ -2397,8 +2422,6 @@ static int check_audio_file(struct osl_row *row, void *data) * \param aca Only ->pbout is used for diagnostics. * * \return Standard. Inconsistencies are reported but not regarded as an error. - * - * \sa com_check(). */ int aft_check_callback(struct afs_callback_arg *aca) { @@ -2462,28 +2485,30 @@ int aft_check_attributes(uint64_t att_mask, struct para_buffer *pb) return audio_file_loop(&acad, check_atts_of_audio_file); } -/** - * Close the audio file table. - * - * \param flags Usual flags that are passed to osl_close_table(). - * - * \sa osl_close_table(). +/* + * This sets audio_file_table to NULL, but leaves current_aft_row unmodified, + * though stale (pointing to unmapped memory). If the table is being closed + * because we received SIGHUP, the table will be reopened after the config file + * has been reloaded. We remember the hash of the current audio file here so + * that aft_open() can initialize current_aft_row by looking up the saved hash. */ static void aft_close(void) { + int ret; + unsigned char *p; + + if (current_aft_row) { + ret = get_hash_of_row(current_aft_row, &p); + if (ret < 0) { + PARA_WARNING_LOG("hash lookup failure\n"); + current_aft_row = NULL; + } else + memcpy(current_hash, p, HASH_SIZE); + } osl_close_table(audio_file_table, OSL_MARK_CLEAN); audio_file_table = NULL; } -/** - * Open the audio file table. - * - * \param dir The database directory. - * - * \return Standard. - * - * \sa osl_open_table(). - */ static int aft_open(const char *dir) { int ret; @@ -2494,7 +2519,20 @@ static int aft_open(const char *dir) unsigned num; osl_get_num_rows(audio_file_table, &num); PARA_INFO_LOG("audio file table contains %u files\n", num); - return ret; + if (!current_aft_row) { + PARA_DEBUG_LOG("no current aft row\n"); + return 1; + } + /* SIGHUP case, update current_aft_row */ + ret = aft_get_row_of_hash(current_hash, ¤t_aft_row); + if (ret < 0) { /* not fatal */ + PARA_WARNING_LOG("current hash lookup failure: %s\n", + para_strerror(-ret)); + current_aft_row = NULL; + return 1; + } + PARA_NOTICE_LOG("current audio file hash lookup: success\n"); + return 1; } PARA_NOTICE_LOG("failed to open audio file table\n"); audio_file_table = NULL; @@ -2550,6 +2588,16 @@ static int aft_event_handler(enum afs_events event, struct para_buffer *pb, status_item_ls_data.afsi.last_played = old_last_played; make_status_items(); return 1; + } case AUDIO_FILE_RENAME: { + char *path; + if (data != current_aft_row) + return 0; + ret = get_audio_file_path_of_row(current_aft_row, &path); + if (ret < 0) + return ret; + status_item_ls_data.path = path; + make_status_items(); + return 1; } case AFHI_CHANGE: { if (data != current_aft_row) return 0; @@ -2558,6 +2606,21 @@ static int aft_event_handler(enum afs_events event, struct para_buffer *pb, return ret; make_status_items(); return 1; + } case AUDIO_FILE_REMOVE: { + if (data == current_aft_row) + current_aft_row = NULL; + return 0; + } + case BLOB_RENAME: + case BLOB_REMOVE: + case BLOB_ADD: { + /* + * These events are rare. We don't bother to check whether the + * current status items are affected and simply recreate them + * every time. + */ + make_status_items(); + return 0; } default: return 0; }