/* SPDX-License-Identifier: GPL-2.0 */ /** \file afs.h The audio file selector API. * * The code of the audio file selector (afs) is distributed across several * source files, which results in a rather large set of non-static functions * and variables with intricate inter-dependencies. * * The central file is \ref afs.c, which contains the afs initialization * and shutdown functions and the table-independent code. Other source files * which include this header and whose object files become part of afs are * \ref attribute.c, \ref aft.c, \ref score.c, \ref mood.c, \ref playlist.c * and \ref blob.c. * * There is only this single header file for the afs API. All non-static * symbols of the source files listed above are declared here. */ /** Audio file selector data stored in the audio file table. */ struct afs_info { /** Seconds since the epoch. */ uint64_t last_played; /** Bit field of set attributes. */ uint64_t attributes; /** Counts how many times the file was selected. */ uint32_t num_played; /** Image blob associated with this file (foreign key). */ uint32_t image_id; /** Lyrics blob associated with this file (foreign key). */ uint32_t lyrics_id; /** Mp3, ogg, ... */ uint8_t audio_format_id; /** Amplification value. */ uint8_t amp; }; /** * Events caused by changes to an afs table. * * Whenever an afs table changes, an event is generated by calling \ref * afs_event(), which calls the event handlers of all other tables. For * example, if an audio file is added, the event handler of the mood table * checks the new file for admissibility. */ enum afs_events { /** An attribute was added. */ ATTRIBUTE_ADD, /** An attribute was renamed. */ ATTRIBUTE_RENAME, /** An attribute was removed. */ ATTRIBUTE_REMOVE, /** The afs info struct of an audio file changed. */ AFSI_CHANGE, /** The afh info struct of an audio file changed. */ AFHI_CHANGE, /** The vss streams a new file, implies afsi and afhi change */ VSS_NEW_AUDIO_FILE, /** An audio file was renamed. */ AUDIO_FILE_RENAME, /** An audio file was added. */ AUDIO_FILE_ADD, /** An audio file is about to be removed. */ AUDIO_FILE_REMOVE, /** A new blob was added. */ BLOB_ADD, /** A blob was renamed. */ BLOB_RENAME, /** A blob is about to be removed. */ BLOB_REMOVE, }; /** Methods for table startup/shutdown and event handling. */ struct afs_table_operations { /** Gets called on startup and on SIGHUP. */ int (*open)(const char *base_dir); /** Gets called on shutdown and on SIGHUP. */ void (*close)(void); /** Called from the init command. */ int (*create)(const char *); /** Handle events generated by other tables. See enum \ref afs_events. */ int (*event_handler)(enum afs_events event, void *data); }; /** * Codes used for communication between the server and the afs process. * * Before forking the afs child, para_server creates a bidirectional pipe * through which both processes communicate. Usually para_server requests a * new audio file in order to start streaming, for example when the end of * the current audio file has been reached. The afs process responds to such * a request by sending back an eight byte buffer. The first four bytes is * the uint32_t representation of the code, usually NEXT_AUDIO_FILE if an * admissible audio file was found, successfully opened and verified. The * other four bytes encode the ID of the shared memory area that contains * details about the audio file to be streamed. The open file descriptor of * this file is also passed to the server process through the same pipe. */ enum afs_server_code { /** An audio file was successfully opened. */ NEXT_AUDIO_FILE, /** No admissible audio file was found. */ NO_ADMISSIBLE_FILES, }; /** Flags passed to for_each_matching_row(). */ enum pattern_match_flags { /** Loop in reverse order. */ PM_REVERSE_LOOP = 1, /** If no pattern is given, loop over all rows. */ PM_NO_PATTERN_MATCHES_EVERYTHING = 2, /** If the data in match_column is the empty string, skip this row. */ PM_SKIP_EMPTY_NAME = 4, }; /** Structure passed to for_each_matching_row(). */ struct pattern_match_data { /** Loop over all rows in this table. */ struct osl_table *table; /** Determines the loop order. Must be an rbtree column. */ unsigned loop_col_num; /** Data from this column is matched against the given patterns. */ unsigned match_col_num; /** \see \ref pattern_match_flags. */ unsigned pm_flags; /** This value is passed verbatim to fnmatch(). */ int fnmatch_flags; /** Obtained by de-serializing the query buffer in the callback. */ struct lls_parse_result *lpr; /** Do not try to match the first inputs of the lopsub parse result. */ unsigned input_skip; /** Data pointer passed to the action function. */ void *data; /** Gets increased by one for each match. */ unsigned num_matches; /** For each matching row, this function will be called. */ int (*action)(struct osl_table *table, struct osl_row *row, const char *name, void *data); }; /** Arguments passed to each afs callback. */ struct afs_callback_arg { /** The local socket connecting afs and the command handler. */ int fd; /** Callback-specific data. */ struct osl_object query; /** Will be written on band SBD_OUTPUT, fully buffered. */ struct para_buffer pbout; /** * Convenience pointer for the de-serialized parse result. * * Most afs command handlers call \ref send_lls_callback_request() * to serialize the parse result of the subcommand and pass it to * the callback. In afs context a pointer to the de-serialized parse * result is stored here. */ struct lls_parse_result *lpr; }; /** * Metadata for the result of a callback. * * A serialized version of an instance of this structure is combined with the * result buffer produced by an afs callback. A reference to this combined * buffer is then passed to the command handler via shared memory. * * \sa struct \ref afs_callback, \ref pass_buffer_as_shm(). */ struct callback_result { /** The number of bytes of the result. */ size_t result_size; /** The band designator (loglevel for the result). */ uint8_t band; }; /** * The "top half" of an afs command. * * The afs command handler functions are called from sibling process of the * afs process, so they can not change the address space of the afs process. * Therefore, an afs subcommand typically consists of two functions: The * command handler function and the corresponding callback function that * runs in afs context. * * \sa \ref send_callback_request(). */ typedef int afs_callback(struct afs_callback_arg *aca); /** * Dispatch the output of an afs callback. * * Most afs callbacks need to send data back to the command handler * process. Pointers to this type of function are passed to \ref * send_callback_request() and related functions to dispatch the data in the * command handler process. Most callbacks pass \ref afs_cb_result_handler(), * which sends the output of the callback to the connected client. */ typedef int callback_result_handler(struct osl_object *result, uint8_t band, void *private); int afs_cb_result_handler(struct osl_object *result, uint8_t band, void *private); int pass_buffer_as_shm(int fd, uint8_t band, const char *buf, size_t size); /** Structure passed to the afs max_size handler. */ struct afs_max_size_handler_data { /** Local socket connecting the command handler and the afs process. */ int fd; /** The sideband designator for this data packet. */ uint8_t band; }; __noreturn void afs_init(int socket_fd); __must_check int afs_event(enum afs_events event, void *data); int send_callback_request(afs_callback *f, int afs_fd, struct osl_object *query, callback_result_handler *result_handler, void *private_result_data); struct command_context; int send_lls_callback_request(afs_callback *f, int afs_fd, const struct lls_command * const cmd, struct lls_parse_result *lpr, struct command_context *cc); __printf_2_3 void afs_error(const struct afs_callback_arg *aca, const char *fmt,...); int string_compare(const struct osl_object *obj1, const struct osl_object *obj2); int for_each_matching_row(struct pattern_match_data *pmd); /* score */ void score_open(struct osl_table **result); void score_close(struct osl_table *t); int score_loop(int (*cb)(struct osl_row *, long, void *), struct osl_table *t, void *data); int score_get_best(struct osl_row **aft_row, long *score, struct osl_table *t); int score_add(const struct osl_row *aft_row, long score, struct osl_table *t); int score_move_to_end(const struct osl_row *aft_row, struct osl_table *t); int score_delete(const struct osl_row *aft_row, struct osl_table *t); /* attribute */ extern const struct afs_table_operations attr_ops; int attr_get_defined_mask(uint64_t *result); int attr_name_to_bitnum(const char *att_name); int attr_bitmap_to_text(uint64_t *atts, char **text); int attr_get_max_bitnum(void); /* aft */ extern const struct afs_table_operations aft_ops; int aft_get_row_of_path(const char *path, struct osl_row **row); int open_and_update_audio_file(int *fd); int load_afd(int shmid, struct audio_file_data *afd); int get_afsi_of_row(const struct osl_row *row, struct afs_info *afsi); int get_afhi_of_row(const struct osl_row *row, struct afh_info *afhi); int get_audio_file_path_of_row(const struct osl_row *row, char **path); int audio_file_loop(void *private_data, osl_rbtree_loop_func *func); int aft_check_callback(struct afs_callback_arg *aca); void free_status_items(void); /** How audio files are selected. */ enum selector_id { /** Admissible files are determined by a mood definition. */ SEL_MOOD, /** All listed files are admissible. */ SEL_PLAYLIST, /** Only two so far. */ NUM_SELECTORS }; /* Opaque, describes an active mood or playlist. */ struct selector_instance; /** * API implemented by the mood and playlist selectors. * * A selector implements a set of operations, including a method to map a * name to a list of audio files called the admissible files. * * The mood and playlist selector determine the admissible files by readuing * a blob of the corresponding blob table, then consulting the audio file * table. Both selectors maintain a score table which contains one row for each * admissible file. This implementation detail is hidden in the API, though. */ struct selector_operations { /** * A reference to the newly created instance is returned via the * result pointer, which must not be NULL. The caller can pass this * reference to various methods, for example to ->loop() to iterate * the admissible files. It should call ->unload() to free the mood * instance when it is no longer needed. * * If the message pointer is not NULL, a suitable message is returned * there in all cases. The caller must free this string. */ int (*load)(const char *mood_name, struct para_buffer *pbout, struct afs_callback_arg *aca, struct selector_instance **result); /** Iterate over all admissible files. */ int (*loop)(int (*func)(struct osl_row *aft_row, long score, void *data), struct selector_instance *si, void *data); /** Free all memory allocated at load time. */ void (*unload)(struct selector_instance *si); /** Perform integrity checks, if any. */ int (*check)(struct afs_callback_arg *aca); /** Get a reference to the highest scoring audio file. */ int (*get_best)(struct osl_row **aft_row, long *score, struct selector_instance *si); /** Mark an audio file as non-admissible. */ int (*invalidate)(struct osl_row *aft_row, struct selector_instance *si); }; /** Needed by com_select() and com_ls(). */ extern enum selector_id current_selector_id, previous_selector_id; /** Used to turn a selector ID into an ops struct pointer. */ extern const struct selector_operations *selector_ops[NUM_SELECTORS]; /** The mood or playlist currently active. */ extern struct selector_instance *current_selector_instance; /** Name of the current mood or playlist, including the {m,p}/ prefix. */ extern char *current_mop; /** Name of the previous mood or playlist. */ extern char *previous_mop; /** * Get the selector operations of the current mood or playlist. * * \return Pointer to either the mood ops or the playlist ops. */ _static_inline_ const struct selector_operations *current_selector(void) { return selector_ops[current_selector_id]; }; /** * Compare two numbers of arbitrary type. * * This evaluates to 1 if x < y, to -1 if x > y and to 0 if x == y. Beware: The macro * expands both arguments twice. */ #define NUM_COMPARE(x, y) ((int)((x) < (y)) - (int)((x) > (y))) /** * Declare public functions and global variables of an osl blob table. * * This macro gets expanded four times to create symbols for the lyrics, * images, moods and playlists tables. */ #define DECLARE_BLOB_SYMBOLS(table_name, cmd_prefix) \ int cmd_prefix ## _get_name_by_id(uint32_t id, char **name); \ int cmd_prefix ## _get_def_by_name(const char *name, struct osl_object *def); \ int cmd_prefix ## _get_name_and_def_by_row(const struct osl_row *row, \ char **name, struct osl_object *def); \ int table_name ##_event_handler(enum afs_events event, void *data); \ extern struct osl_table *table_name ## _table; \ extern const struct afs_table_operations table_name ## _ops; /** \cond doxygen_ignore */ DECLARE_BLOB_SYMBOLS(lyrics, lyr) DECLARE_BLOB_SYMBOLS(images, img) DECLARE_BLOB_SYMBOLS(moods, mood) DECLARE_BLOB_SYMBOLS(playlists, pl) /** \endcond */ /** The columns of a blob table. */ enum blob_table_columns { /** The identifier, a positive integer that never repeats. */ BLOBCOL_ID, /** The unique name of the blob. */ BLOBCOL_NAME, /** The actual blob contents. */ BLOBCOL_DEF, /** Each blob table has this many columns. */ NUM_BLOB_COLUMNS };