X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=blobdiff_plain;f=aft.c;h=c04d4f9c99e89afb451afe94be7fcc64461b15e1;hp=3190a9bafa5e4fdd0ef750f3fcf4160057ce7ddf;hb=ba83a291cd486e8ab53ac64d81b17d8f5705d715;hpb=27b5b500c5fac0660d0ef0eaf1bea81a091300cc diff --git a/aft.c b/aft.c index 3190a9ba..c04d4f9c 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,54 +22,6 @@ #include "sideband.h" #include "command.h" -static struct osl_table *audio_file_table; -static char *status_items; -static char *parser_friendly_status_items; - -/** The different sorting methods of the ls command. */ -enum ls_sorting_method { - /** -sp (default) */ - LS_SORT_BY_PATH, - /** -ss */ - LS_SORT_BY_SCORE, - /** -sl */ - LS_SORT_BY_LAST_PLAYED, - /** -sn */ - LS_SORT_BY_NUM_PLAYED, - /** -sf */ - LS_SORT_BY_FREQUENCY, - /** -sc */ - LS_SORT_BY_CHANNELS, - /** -si */ - LS_SORT_BY_IMAGE_ID, - /** -sy */ - LS_SORT_BY_LYRICS_ID, - /** -sb */ - LS_SORT_BY_BITRATE, - /** -sd */ - LS_SORT_BY_DURATION, - /** -sa */ - LS_SORT_BY_AUDIO_FORMAT, - /** -sh */ - LS_SORT_BY_HASH, -}; - -/** The different listing modes of the ls command. */ -enum ls_listing_mode { - /** Default listing mode. */ - LS_MODE_SHORT, - /** -l or -ll */ - LS_MODE_LONG, - /** -lv */ - LS_MODE_VERBOSE, - /** -lm */ - LS_MODE_MBOX, - /** -lc */ - LS_MODE_CHUNKS, - /** -lp */ - LS_MODE_PARSER, -}; - /* Data about one audio file. Needed for ls and stat output. */ struct ls_data { /* Usual audio format handler information. */ @@ -88,6 +36,44 @@ struct ls_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 { + LS_SORT_BY_PATH, /**< -s=p (default) */ + LS_SORT_BY_SCORE, /**< -s=s */ + LS_SORT_BY_LAST_PLAYED, /**< -s=l */ + LS_SORT_BY_NUM_PLAYED, /**< -s=n */ + LS_SORT_BY_FREQUENCY, /**< -s=f */ + LS_SORT_BY_CHANNELS, /**< -s=c */ + LS_SORT_BY_IMAGE_ID, /**< -s=i */ + LS_SORT_BY_LYRICS_ID, /**< -s=y */ + LS_SORT_BY_BITRATE, /**< -s=b */ + LS_SORT_BY_DURATION, /**< -s=d */ + LS_SORT_BY_AUDIO_FORMAT, /**< -s=a */ + LS_SORT_BY_HASH, /**< -s=h */ +}; + +/** The different listing modes of the ls command. */ +enum ls_listing_mode { + LS_MODE_SHORT, /**< Default listing mode. */ + LS_MODE_LONG, /**< -l or -l=l */ + LS_MODE_VERBOSE, /** -l=v */ + LS_MODE_MBOX, /** -l=m */ + LS_MODE_CHUNKS, /** -l=c */ + LS_MODE_PARSER, /** -l=p */ +}; + /** * The size of the individual output fields of the ls command. * @@ -138,7 +124,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 +153,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 +178,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) { @@ -320,8 +306,8 @@ enum afhi_offsets { CHUNKS_TOTAL_OFFSET = 20, /** The length of the audio file header (4 bytes). */ HEADER_LEN_OFFSET = 24, - /** Was: The start of the audio file header (4 bytes). */ - AFHI_UNUSED2_OFFSET = 28, + /** Size of the largest chunk in bytes. (4 bytes). */ + AFHI_MAX_CHUNK_SIZE_OFFSET = 28, /** The seconds part of the chunk time (4 bytes). */ CHUNK_TV_TV_SEC_OFFSET = 32, /** The microseconds part of the chunk time (4 bytes). */ @@ -361,11 +347,14 @@ static void save_afhi(struct afh_info *afhi, char *buf) write_u8(buf + AFHI_CHANNELS_OFFSET, afhi->channels); write_u32(buf + CHUNKS_TOTAL_OFFSET, afhi->chunks_total); write_u32(buf + HEADER_LEN_OFFSET, afhi->header_len); - write_u32(buf + AFHI_UNUSED2_OFFSET, 0); + write_u32(buf + AFHI_MAX_CHUNK_SIZE_OFFSET, afhi->max_chunk_size); 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 +363,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); @@ -383,6 +373,7 @@ static void load_afhi(const char *buf, struct afh_info *afhi) afhi->channels = read_u8(buf + AFHI_CHANNELS_OFFSET); afhi->chunks_total = read_u32(buf + CHUNKS_TOTAL_OFFSET); afhi->header_len = read_u32(buf + HEADER_LEN_OFFSET); + afhi->max_chunk_size = read_u32(buf + AFHI_MAX_CHUNK_SIZE_OFFSET); afhi->chunk_tv.tv_sec = read_u32(buf + CHUNK_TV_TV_SEC_OFFSET); afhi->chunk_tv.tv_usec = read_u32(buf + CHUNK_TV_TV_USEC_OFFSET); afhi->techinfo = (char *)buf + AFHI_INFO_STRING_OFFSET; @@ -393,42 +384,37 @@ static void load_afhi(const char *buf, struct afh_info *afhi) afhi->tags.comment = afhi->tags.album + strlen(afhi->tags.album) + 1; } +/* Only used for saving the chunk table, but not for loading. */ static unsigned sizeof_chunk_table(struct afh_info *afhi) { - if (!afhi) + if (!afhi || !afhi->chunk_table) return 0; return 4 * (afhi->chunks_total + 1); } -static uint32_t save_chunk_table(struct afh_info *afhi, char *buf) +static void save_chunk_table(struct afh_info *afhi, char *buf) { - int i; - uint32_t max = 0, old = 0; + uint32_t n; - for (i = 0; i <= afhi->chunks_total; i++) { - uint32_t val = afhi->chunk_table[i]; - write_u32(buf + 4 * i, val); - /* - * If the first chunk is the header, do not consider it for the - * calculation of the largest chunk size. - */ - if (i == 0 || (i == 1 && afhi->header_len > 0)) { - old = val; - continue; - } - max = PARA_MAX(max, val - old); - old = val; - } - return max; + if (!afhi->chunk_table || afhi->chunks_total == 0) + return; + for (n = 0; n <= afhi->chunks_total; n++) + write_u32(buf + 4 * n, afhi->chunk_table[n]); } -static void load_chunk_table(struct afh_info *afhi, char *buf) +static void load_chunk_table(struct afh_info *afhi, const struct osl_object *ct) { int i; + size_t sz; - afhi->chunk_table = para_malloc(sizeof_chunk_table(afhi)); - for (i = 0; i <= afhi->chunks_total; i++) - afhi->chunk_table[i] = read_u32(buf + 4 * i); + if (!ct->data || ct->size < 4) { + afhi->chunk_table = NULL; + return; + } + sz = PARA_MIN(((size_t)afhi->chunks_total + 1) * 4, ct->size) + 1; + afhi->chunk_table = para_malloc(sz); + for (i = 0; i <= afhi->chunks_total && i * 4 + 3 < ct->size; i++) + afhi->chunk_table[i] = read_u32(ct->data + 4 * i); } /** @@ -532,7 +518,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. @@ -542,10 +528,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; } /** @@ -556,7 +544,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) @@ -594,7 +582,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) { @@ -623,7 +613,13 @@ static int save_afd(struct audio_file_data *afd) goto err; buf = shm_afd; buf += sizeof(*afd); - afd->max_chunk_size = save_chunk_table(&afd->afhi, buf); + save_chunk_table(&afd->afhi, buf); + if (afd->afhi.max_chunk_size == 0) { /* v0.5.x on-disk afhi */ + set_max_chunk_size(&afd->afhi); + PARA_NOTICE_LOG("max chunk size unset, re-add required\n"); + } else + PARA_INFO_LOG("using max chunk size from afhi\n"); + afd->max_chunk_size = afd->afhi.max_chunk_size; *(struct audio_file_data *)shm_afd = *afd; shm_detach(shm_afd); return shmid; @@ -648,14 +644,22 @@ int load_afd(int shmid, struct audio_file_data *afd) { void *shm_afd; int ret; + struct osl_object obj; ret = shm_attach(shmid, ATTACH_RO, &shm_afd); if (ret < 0) return ret; + ret = shm_size(shmid, &obj.size); + if (ret < 0) + goto detach; *afd = *(struct audio_file_data *)shm_afd; - load_chunk_table(&afd->afhi, shm_afd + sizeof(*afd)); + obj.data = shm_afd + sizeof(*afd); + obj.size -= sizeof(*afd); + load_chunk_table(&afd->afhi, &obj); + ret = 1; +detach: shm_detach(shm_afd); - return 1; + return ret; } static int get_local_time(uint64_t *seconds, char *buf, size_t size, @@ -684,7 +688,7 @@ static int get_local_time(uint64_t *seconds, char *buf, size_t size, /* * If the given time is more than six month away from the current time, * we print only the year. The additional space character in the format - * string below makes the formated date align nicely with dates that + * string below makes the formatted date align nicely with dates that * contain the time (those written by the above strftime() statement). */ if (!strftime(buf, size, "%b %e %Y", tm)) @@ -738,11 +742,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; } @@ -751,9 +755,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)"); } @@ -761,9 +765,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)"); } @@ -773,14 +777,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); } @@ -805,7 +809,11 @@ static int print_chunk_table(struct ls_data *d, struct para_buffer *b) (long unsigned) d->afhi.chunk_tv.tv_usec ); buf = chunk_table_obj.data; - for (i = 0; i <= d->afhi.chunks_total; i++) + for ( + i = 0; + i <= d->afhi.chunks_total && 4 * i + 3 < chunk_table_obj.size; + i++ + ) para_printf(b, "%u ", (unsigned) read_u32(buf + 4 * i)); para_printf(b, "\n"); ret = 1; @@ -893,34 +901,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_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_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); if (opts->mode == LS_MODE_MBOX) { struct osl_object lyrics_def; lyr_get_def_by_id(afsi->lyrics_id, &lyrics_def); @@ -934,9 +944,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}; @@ -954,11 +961,22 @@ 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); } -static int make_status_items(void) +/** + * 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 void make_status_items(void) { const struct lls_command *cmd = SERVER_CMD_CMD_PTR(LS); char *argv[] = {"ls", "--admissible", "--listing-mode=verbose"}; @@ -967,6 +985,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; ret = lls_parse(ARRAY_SIZE(argv), argv, cmd, &opts.lpr, NULL); assert(ret >= 0); time(¤t_time); @@ -974,25 +995,24 @@ 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) { + PARA_WARNING_LOG("could not create status items: %s\n", + para_strerror(-ret)); + free_status_items(); + } lls_free_parse_result(opts.lpr, cmd); - return ret; } /** @@ -1039,8 +1059,15 @@ again: d->afhi.chunk_table = afd->afhi.chunk_table = NULL; ret = osl(osl_open_disk_object(audio_file_table, current_aft_row, AFTCOL_CHUNKS, &chunk_table_obj)); - if (ret < 0) - return ret; + if (ret < 0) { + if (!afh_supports_dynamic_chunks(d->afsi.audio_format_id)) + return ret; + PARA_INFO_LOG("no chunk table for %s\n", d->path); + chunk_table_obj.data = NULL; + chunk_table_obj.size = 0; + } else { + PARA_INFO_LOG("chunk table: %zu bytes\n", chunk_table_obj.size); + } ret = mmap_full_file(d->path, O_RDONLY, &map.data, &map.size, &afd->fd); if (ret < 0) goto out; @@ -1057,7 +1084,7 @@ again: save_afsi(&new_afsi, &afsi_obj); /* in-place update */ afd->audio_format_id = d->afsi.audio_format_id; - load_chunk_table(&afd->afhi, chunk_table_obj.data); + load_chunk_table(&afd->afhi, &chunk_table_obj); aced.aft_row = current_aft_row; aced.old_afsi = &d->afsi; /* @@ -1070,7 +1097,8 @@ again: ret = save_afd(afd); out: free(afd->afhi.chunk_table); - osl_close_disk_object(&chunk_table_obj); + if (chunk_table_obj.data) + osl_close_disk_object(&chunk_table_obj); if (ret < 0) { PARA_ERROR_LOG("%s: %s\n", d->path, para_strerror(-ret)); ret = score_delete(current_aft_row); @@ -1080,6 +1108,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; @@ -1197,6 +1231,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; } @@ -1345,7 +1381,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); @@ -1411,6 +1446,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; @@ -1551,7 +1588,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: @@ -1672,6 +1709,7 @@ static int com_add_callback(struct afs_callback_arg *aca) &objs[AFTCOL_AFHI])); if (ret < 0) goto out; + /* truncate the file to size zero if there is no chunk table */ ret = osl(osl_update_object(audio_file_table, row, AFTCOL_CHUNKS, &objs[AFTCOL_CHUNKS])); if (ret < 0) @@ -1882,11 +1920,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, @@ -2112,7 +2146,9 @@ static int copy_selector_info(__a_unused struct osl_table *table, ret = get_afsi_object_of_row(row, &target_afsi_obj); if (ret < 0) return ret; - load_afsi(&target_afsi, &target_afsi_obj); + ret = load_afsi(&target_afsi, &target_afsi_obj); + if (ret < 0) + return ret; old_afsi = target_afsi; if (cad->copy_all || y_given) target_afsi.lyrics_id = cad->source_afsi.lyrics_id; @@ -2370,8 +2406,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) { @@ -2435,28 +2469,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; @@ -2467,7 +2503,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; @@ -2523,6 +2572,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; @@ -2531,6 +2590,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; }