From: Andre Noll Date: Sat, 11 Jul 2009 14:58:40 +0000 (+0200) Subject: Merge branch 'master' into next X-Git-Tag: v0.4.0~51^2~4 X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=commitdiff_plain;h=aef566e9c8680629bac1ea84893b8b3ccd13da77;hp=2a4d7bb5f5cecdbcbda6b9f9b531c919ecc6b161 Merge branch 'master' into next Conflicts: mood.c --- diff --git a/INSTALL b/INSTALL index 09a2616f..092bf873 100644 --- a/INSTALL +++ b/INSTALL @@ -17,16 +17,17 @@ optional. The configure script will detect what is installed on your system and will only try to build those executables that can be built with your setup. -Note that no special library (not even the mp3 decoding library libmad) -is needed for para_server if you only want to stream mp3 files. -Also, it's fine to use para_server on a box without sound card as -para_server only sends the audio stream to connected clients. +Note that no mp3 library (not even the mp3 decoding library libmad) +is needed for para_server if you only want to stream mp3 files. Also, +it's fine to use para_server on a box without sound card as para_server +only sends the audio stream to connected clients. ------------------------- Install server and client ------------------------- -Install the package on all machines, you'd like this software to run on: +Install the paraslash package on all machines, you'd like this software +to run on: (./configure && make) > /dev/null @@ -43,12 +44,14 @@ successfully, execute as root, make install ----------------------------------- -Setup user list and create rsa keys +Setup user list and create RSA keys ----------------------------------- -If you already have your rsa keys, skip this step. If you are new -to paraslash, you have to generate an rsa key pair for each user you -want to allow to connect. You need at least one user. +Note that the RSA keys for paraslash 0.3.x will not work for version +0.4.x as the new version requires stronger (2048 bit) keys. If you +already have your 2048 bit keys, skip this step. If you are new to +paraslash, you have to generate a key pair for each user you want to +allow to connect. You need at least one user. Let's assume that you'd like to run the server on host server_host as user foo, and that you want to connect from client_host as user bar. @@ -70,7 +73,7 @@ with the commands key=~/.paraslash/key.$LOGNAME mkdir -p ~/.paraslash - (umask 077 && openssl genrsa -out $key) + (umask 077 && openssl genrsa -out $key 2048) Next, extract its public part: @@ -121,11 +124,11 @@ Create the database para_client init -This creates some empty tables under ~/.paraslash/afs_database. -You normally don't need to look at these tables, but it's good -to know that you can start from scratch with +This creates some empty tables under ~/.paraslash/afs_database-0.4. +You normally don't need to look at these tables, but it's good to +know that you can start from scratch with - rm -rf ~/.paraslash/afs_database + rm -rf ~/.paraslash/afs_database-0.4 in case something went wrong. @@ -149,7 +152,7 @@ Start streaming manually ------------------------ para_client play - para_client stat 2 + para_client -- stat -n=2 This starts streaming and dumps some information about the current audio file to stdout. @@ -165,7 +168,7 @@ Paraslash comes with its own receiving and playing software, which will be described next. Try the following on client_host (assuming Linux/ALSA and an mp3 stream): - para_recv -l info -r 'http -i server_host' > file.mp3 + para_recv -r 'http -i server_host' > file.mp3 # (interrupt with CTRL+C after a few seconds) ls -l file.mp3 # should not be empty para_filter -f mp3dec -f wav < file.mp3 > file.wav diff --git a/Makefile.in b/Makefile.in index b4392a3c..05d7b3e0 100644 --- a/Makefile.in +++ b/Makefile.in @@ -13,7 +13,7 @@ build_date := $(shell date) uname_s := $(shell uname -s 2>/dev/null || echo "UNKNOWN_OS") uname_rs := $(shell uname -rs) cc_version := $(shell $(CC) --version | head -n 1) -codename := symplectic separability +codename := simultaneous independence DEBUG_CPPFLAGS += -Wno-sign-compare -g -Wunused -Wundef -W DEBUG_CPPFLAGS += -Wredundant-decls @@ -55,19 +55,20 @@ CPPFLAGS += -DMAIN_INPUT_FILE_IS_$(*F) CPPFLAGS += @SSL_CPPFLAGS@ CPPFLAGS += @ncurses_cppflags@ CPPFLAGS += @arch_cppflags@ +CPPFLAGS += -I/usr/local/include BINARIES = para_server para_client para_audioc para_recv \ - para_filter para_write para_fsck para_afh @extra_binaries@ + para_filter para_write para_afh @extra_binaries@ man_binaries := $(BINARIES) man_pages := $(patsubst %, man/man1/%.1, $(man_binaries)) man_pages_in := $(patsubst %, web/%.man.in.html, $(man_binaries)) ggo_dir := ggo -m4_ggos := afh audioc audiod client filter fsck gui recv server write +m4_ggos := afh audioc audiod client filter gui recv server write all_ggos := $(m4_ggos) dccp_recv oggdec_filter alsa_write oss_write fade http_recv \ osx_write udp_recv amp_filter compress_filter file_write \ - grab_client mp3dec_filter + mp3dec_filter ggo_generated := $(addsuffix .cmdline.c, $(all_ggos)) $(addsuffix .cmdline.h, $(all_ggos)) \ $(addsuffix .ggo, $(addprefix $(ggo_dir)/,$(m4_ggos))) @@ -164,9 +165,6 @@ para_fade: @fade_objs@ para_server: @server_objs@ $(CC) $(LDFLAGS) -o $@ @server_objs@ @server_ldflags@ -para_fsck: @fsck_objs@ - $(CC) $(LDFLAGS) -o $@ @fsck_objs@ @fsck_ldflags@ - para_write: @write_objs@ $(CC) $(LDFLAGS) -o $@ @write_objs@ @write_ldflags@ @@ -175,6 +173,7 @@ para_afh: @afh_objs@ clean: rm -f *.o $(BINARIES) + rm -f *_command_list.* rm -rf man distclean: clean rm -f Makefile autoscan.log config.status config.log && \ @@ -185,7 +184,6 @@ maintainer-clean: distclean rm -f $(ggo_generated) *.tar.bz2 \ config.h configure \ config.h.in skencil/*.pdf skencil/*.ps - rm -f *_command_list.* *.man man/man1/* rm -rf web_sync install: all man diff --git a/NEWS b/NEWS index dff82086..793e3234 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,24 @@ NEWS ==== +--------------------------------------------------- +0.4.0 (to be announced) "simultaneous independence" +--------------------------------------------------- + +Two significant changes which require the new version number: The +improved authentication dialog and the fact that the database code +has been moved to a library, libosl. To use the new version, you have +to generate new RSA keys, see INSTALL for details. A shell script is +provided for conversion of the 0.3 database to the new 0.4 format. + + - stronger crypto for client authentication + - the database code has been moved to a library + - improved status item handling + - the new parser-friendly listing mode for the ls and stat commands + - mandatory rc4 encryption + - major audio format handler cleanups + - improved tag handling + ------------------------------------------------- 0.3.5 (to be announced) "symplectic separability" ------------------------------------------------- diff --git a/README.afs b/README.afs index ca01df6a..ef51c294 100644 --- a/README.afs +++ b/README.afs @@ -78,10 +78,10 @@ A mood consists of a unique name and its *mood definition*, which is a set of *mood lines* containing expressions in terms of attributes and other data contained in the database. -A mood defines a subset of audio files called the *admissible audio -files* for that mood. At any time, at most one mood can be *active* -which means that para_server is going to select only files from that -subset of admissible files. +A mood defines a subset of audio files called the *admissible audio files* +for that mood. At any time, at most one mood can be *active* which +means that para_server is going to select only files from that subset +of admissible files. So in order to create a mood definition one has to write a set of mood lines. Mood lines come in three flavours: Accept lines, deny diff --git a/REQUIREMENTS b/REQUIREMENTS index c1743fd2..1991737b 100644 --- a/REQUIREMENTS +++ b/REQUIREMENTS @@ -3,6 +3,10 @@ Requirements In any case you need + - libosl: If you are compiling from git the osl sources should + have been cloned automatically. It is also available for + download from http://git.tuebingen.mpg.de/osl. Or run the + command "git clone git://git.tuebingen.mpg.de/osl" - gcc, the gnu compiler collection (shipped with distro): gcc-3.3 or newer is required. - gnu make (shipped with disto, might be called gmake) diff --git a/aac_afh.c b/aac_afh.c index 80b50a4d..7de73706 100644 --- a/aac_afh.c +++ b/aac_afh.c @@ -10,12 +10,13 @@ /** \file aac_afh.c para_server's aac audio format handler */ +#include +#include + #include "para.h" #include "error.h" -#include "string.h" #include "afh.h" -#include "afs.h" -#include "server.h" +#include "string.h" #include "aac.h" static int aac_find_stsz(unsigned char *buf, size_t buflen, off_t *skip) @@ -91,11 +92,9 @@ static char *get_tag(unsigned char *p, int size) return buf; } -static char *read_tags(unsigned char *buf, size_t buflen) +static void read_tags(unsigned char *buf, size_t buflen, struct afh_info *afhi) { unsigned char *p = buf; - char *title = NULL, *artist = NULL, *album = NULL, *year = NULL, - *comment = NULL, *result; while (p + 32 < buf + buflen) { unsigned char *q, type1[5], type2[5]; @@ -114,27 +113,20 @@ static char *read_tags(unsigned char *buf, size_t buflen) if (q + size2 > buf + buflen) break; if (!atom_cmp(type1, "©ART")) - artist = get_tag(q, size2); + afhi->tags.artist = get_tag(q, size2); else if (!atom_cmp(type1, "©alb")) - album = get_tag(q, size2); + afhi->tags.album = get_tag(q, size2); else if (!atom_cmp(type1, "©nam")) - title = get_tag(q, size2); + afhi->tags.title = get_tag(q, size2); else if (!atom_cmp(type1, "©cmt")) - comment = get_tag(q, size2); + afhi->tags.comment = get_tag(q, size2); else if (!atom_cmp(type1, "©day")) - year = get_tag(q, size2); + afhi->tags.year = get_tag(q, size2); p += size1; } - result = make_taginfo(title, artist, album, year, comment); - free(title); - free(artist); - free(album); - free(year); - free(comment); - return result; } -static char *read_meta(unsigned char *buf, size_t buflen) +static void read_meta(unsigned char *buf, size_t buflen, struct afh_info *afhi) { unsigned char *p = buf; @@ -145,12 +137,12 @@ static char *read_meta(unsigned char *buf, size_t buflen) continue; } p += 4; - return read_tags(p, buflen - (p - buf)); + return read_tags(p, buflen - (p - buf), afhi); } - return make_taginfo(NULL, NULL, NULL, NULL, NULL); } -static char *aac_get_taginfo(unsigned char *buf, size_t buflen) +static void aac_get_taginfo(unsigned char *buf, size_t buflen, + struct afh_info *afhi) { int i; uint64_t subsize; @@ -165,10 +157,9 @@ static char *aac_get_taginfo(unsigned char *buf, size_t buflen) p = buf + i; i += read_atom_header(p, &subsize, type); p = buf + i; - return read_meta(p, buflen - i); + return read_meta(p, buflen - i, afhi); } PARA_INFO_LOG("no meta data\n"); - return make_taginfo(NULL, NULL, NULL, NULL, NULL); } static ssize_t aac_compute_chunk_table(struct afh_info *afhi, @@ -232,12 +223,11 @@ static int aac_get_file_info(char *map, size_t numbytes, __a_unused int fd, mp4AudioSpecificConfig mp4ASC; NeAACDecHandle handle = NULL; unsigned char *umap = (unsigned char *) map; - char *taginfo; ret = aac_find_esds(umap, numbytes, &skip, &decoder_len); if (ret < 0) goto out; - taginfo = aac_get_taginfo(umap, numbytes); + aac_get_taginfo(umap, numbytes, afhi); handle = aac_open(); ret = -E_AAC_AFH_INIT; if (NeAACDecInit(handle, umap + skip, decoder_len, &rate, &channels)) @@ -268,11 +258,6 @@ static int aac_get_file_info(char *map, size_t numbytes, __a_unused int fd, ret = (afhi->chunk_table[afhi->chunks_total] - afhi->chunk_table[0]) * 8; /* bits */ ret += (channels * afhi->seconds_total * 500); /* avoid rounding error */ afhi->bitrate = ret / (channels * afhi->seconds_total * 1000); - afhi->info_string = make_message("%s:\n%s", - status_item_list[SI_AUDIO_FILE_INFO], - taginfo); - free(taginfo); - tv_scale(20, &afhi->chunk_tv, &afhi->eof_tv); ret = 1; out: if (handle) diff --git a/aacdec_filter.c b/aacdec_filter.c index 1ceca1fc..59c49893 100644 --- a/aacdec_filter.c +++ b/aacdec_filter.c @@ -10,8 +10,9 @@ /** \file aacdec_filter.c paraslash's aac (m4a) decoder. */ -#include "para.h" +#include +#include "para.h" #include "list.h" #include "sched.h" #include "ggo.h" diff --git a/acl.c b/acl.c index ffcd1685..087ea0c8 100644 --- a/acl.c +++ b/acl.c @@ -6,6 +6,8 @@ /** \file acl.c Access control lists for paraslash senders. */ +#include + #include "para.h" #include "error.h" #include "string.h" diff --git a/afh.c b/afh.c index fad93c49..c0064493 100644 --- a/afh.c +++ b/afh.c @@ -6,6 +6,7 @@ /** \file afh.c Paraslash's standalone audio format handler tool. */ +#include #include #include @@ -32,18 +33,28 @@ static void print_info(int audio_format_num, struct afh_info *afhi) "%s: %dHz\n" /* frequency */ "%s: %d\n" /* channels */ "%s: %lu\n" /* seconds total */ - "%s" /* tag info */ "%s: %lu: %lu\n" /* chunk time */ - "%s: %lu\n", /* num chunks */ + "%s: %lu\n" /* num chunks */ + "%s: %s\n" /* techinfo */ + "%s: %s\n" /* artist */ + "%s: %s\n" /* title */ + "%s: %s\n" /* year */ + "%s: %s\n" /* album */ + "%s: %s\n", /* comment */ status_item_list[SI_BITRATE], afhi->bitrate, status_item_list[SI_FORMAT], audio_format_name(audio_format_num), status_item_list[SI_FREQUENCY], afhi->frequency, status_item_list[SI_CHANNELS], afhi->channels, status_item_list[SI_SECONDS_TOTAL], afhi->seconds_total, - afhi->info_string, status_item_list[SI_CHUNK_TIME], (long unsigned)afhi->chunk_tv.tv_sec, (long unsigned)afhi->chunk_tv.tv_usec, - status_item_list[SI_NUM_CHUNKS], afhi->chunks_total + status_item_list[SI_NUM_CHUNKS], afhi->chunks_total, + status_item_list[SI_TECHINFO], afhi->techinfo? afhi->techinfo : "", + status_item_list[SI_ARTIST], afhi->tags.artist? afhi->tags.artist : "", + status_item_list[SI_TITLE], afhi->tags.title? afhi->tags.title : "", + status_item_list[SI_YEAR], afhi->tags.year? afhi->tags.year : "", + status_item_list[SI_ALBUM], afhi->tags.album? afhi->tags.album : "", + status_item_list[SI_COMMENT], afhi->tags.comment? afhi->tags.comment : "" ); } @@ -166,6 +177,13 @@ int main(int argc, char **argv) print_info(audio_format_num, &afhi); if (conf.chunk_table_given) print_chunk_table(&afhi); + free(afhi.techinfo); + free(afhi.tags.artist); + free(afhi.tags.title); + free(afhi.tags.year); + free(afhi.tags.album); + free(afhi.tags.comment); + free(afhi.chunk_table); printf("\n"); } ret2 = para_munmap(audio_file_data, audio_file_size); diff --git a/afh.h b/afh.h index 35cc7ef1..72987b31 100644 --- a/afh.h +++ b/afh.h @@ -23,14 +23,35 @@ /** \endcond */ +/** + * The tags used by all audio format handlers. + * + * Paraslash only uses the more common tags. These are recognized + * for all supported audio formats. + */ +struct taginfo { + /** TPE1 (id3v2) / ARTIST (vorbis) / ©ART (aac) */ + char *artist; + /** TIT2/TITLE/©nam */ + char *title; + /** TDRC/YEAR/©day */ + char *year; + /** TALB/ALBUM/©alb */ + char *album; + /** COMM/COMMENT/©cmt */ + char *comment; +}; + /** Audio format dependent information. */ struct afh_info { /** The number of chunks this audio file contains. */ long unsigned chunks_total; /** The length of the audio file in seconds. */ long unsigned seconds_total; - /** A string that gets filled in by the audio format handler. */ - char *info_string; + /** Audio handler specific info about the file. */ + char *techinfo; + /** Id3 tags, vorbis comments, aac tags. */ + struct taginfo tags; /** * The table that specifies the offset of the individual pieces in * the current audio file. @@ -38,8 +59,6 @@ struct afh_info { uint32_t *chunk_table; /** Period of time between sending data chunks. */ struct timeval chunk_tv; - /** End of file timeout - Do not load new audio file until this time. */ - struct timeval eof_tv; /** * The position of the header within the audio file. Ignored if \a * header_len equals zero. @@ -104,5 +123,3 @@ void afh_get_chunk(long unsigned chunk_num, struct afh_info *afhi, void *map, const char **buf, size_t *len); uint32_t afh_get_largest_chunk_size(struct afh_info *afhi); void afh_get_header(struct afh_info *afhi, void *map, const char **buf, size_t *len); -char *make_taginfo(char *title, char *artist, char *album, char *year, - char *comment); diff --git a/afh_common.c b/afh_common.c index 8c0eed27..bb04453f 100644 --- a/afh_common.c +++ b/afh_common.c @@ -10,6 +10,7 @@ #include /* gettimeofday */ #include #include +#include #include "para.h" #include "error.h" @@ -121,39 +122,6 @@ int guess_audio_format(const char *name) return -E_AUDIO_FORMAT; } -/** - * Pretty-print the given meta-info. - * - * \param title The title of the audio file. - * \param artist The artist. - * \param album The name of the album. - * \param year Year of release. - * \param comment Further comments. - * - * This function is called by each audio format handler to produce the tag info - * status items. Usually, the audio format handlers read this info from the - * audio file (id3 tags, vorbis comments, ...). - * - * It is OK to pass \p NULL pointers for any argument in which case a suitable - * string is inserted which indicates that this information is not available. - * - * \return The status item string. It must be freed by the caller. - */ -char *make_taginfo(char *title, char *artist, char *album, char *year, - char *comment) -{ - return make_message("%s: %s, by %s\n" /* taginfo1 */ - "%s: A: %s, Y: %s, C: %s\n", /* taginfo2 */ - status_item_list[SI_TAGINFO1], - (title && *title)? title : "(title tag not set)", - (artist && *artist)? artist : "(artist tag not set)", - status_item_list[SI_TAGINFO2], - (album && *album)? album : "(album tag not set)", - (year && *year)? year : "????", - (comment && *comment)? comment : "(comment tag not set)" - ); -} - /** * Call get_file_info() to obtain an afhi structure. * @@ -180,22 +148,52 @@ int compute_afhi(const char *path, char *data, size_t size, int fd, afhi->header_offset = 0; afhi->header_len = 0; + afhi->techinfo = NULL; + afhi->tags.artist = NULL; + afhi->tags.title = NULL; + afhi->tags.year = NULL; + afhi->tags.album = NULL; + afhi->tags.comment = NULL; format = guess_audio_format(path); if (format >= 0) { ret = afl[format].get_file_info(data, size, fd, afhi); - if (ret >= 0) - return format; + if (ret >= 0) { + ret = format; + goto success; + } } FOR_EACH_AUDIO_FORMAT(i) { if (i == format) /* we already tried this one to no avail */ continue; ret = afl[i].get_file_info(data, size, fd, afhi); - if (ret >= 0) - return i; + if (ret >= 0) { + ret = i; + goto success; + } PARA_WARNING_LOG("%s\n", para_strerror(-ret)); } return -E_AUDIO_FORMAT; +success: + if (!afhi->techinfo) + afhi->techinfo = para_strdup(NULL); + if (!afhi->tags.artist) + afhi->tags.artist = para_strdup(NULL); + if (!afhi->tags.title) + afhi->tags.title = para_strdup(NULL); + if (!afhi->tags.year) + afhi->tags.year = para_strdup(NULL); + if (!afhi->tags.album) + afhi->tags.album = para_strdup(NULL); + if (!afhi->tags.comment) + afhi->tags.comment = para_strdup(NULL); + PARA_DEBUG_LOG("techinfo: %s\n", afhi->techinfo); + PARA_DEBUG_LOG("artist: %s\n", afhi->tags.artist); + PARA_DEBUG_LOG("title: %s\n", afhi->tags.title); + PARA_DEBUG_LOG("year: %s\n", afhi->tags.year); + PARA_DEBUG_LOG("album: %s\n", afhi->tags.album); + PARA_DEBUG_LOG("comment: %s\n", afhi->tags.comment); + return ret; } /** @@ -252,7 +250,7 @@ uint32_t afh_get_largest_chunk_size(struct afh_info *afhi) * * This function sets \a buf to \p NULL and \a len to zero if \a map or \a * afhi is \p NULL, or if the current audio format does not need special - * header treamtment. + * header treatment. */ void afh_get_header(struct afh_info *afhi, void *map, const char **buf, size_t *len) { diff --git a/afs.c b/afs.c index 72e2490e..531cada8 100644 --- a/afs.c +++ b/afs.c @@ -6,11 +6,16 @@ /** \file afs.c Paraslash's audio file selector. */ +#include #include #include +#include +#include + #include "server.cmdline.h" #include "para.h" #include "error.h" +#include "crypt.h" #include "string.h" #include "afh.h" #include "afs.h" @@ -24,6 +29,7 @@ #include "sched.h" #include "signal.h" #include "fd.h" +#include "mood.h" /** The osl tables used by afs. \sa blob.c. */ enum afs_table_num { @@ -85,7 +91,6 @@ static struct signal_task signal_task_struct; static enum play_mode current_play_mode; static char *current_mop; /* mode or playlist specifier. NULL means dummy mooe */ - /** * A random number used to "authenticate" the connection. * @@ -342,7 +347,7 @@ static int action_if_pattern_matches(struct osl_row *row, void *data) struct pattern_match_data *pmd = data; struct osl_object name_obj; const char *p, *name; - int ret = osl_get_object(pmd->table, row, pmd->match_col_num, &name_obj); + int ret = osl(osl_get_object(pmd->table, row, pmd->match_col_num, &name_obj)); const char *pattern_txt = (const char *)pmd->patterns.data; if (ret < 0) @@ -369,16 +374,15 @@ static int action_if_pattern_matches(struct osl_row *row, void *data) * * \param pmd Describes what to match and how. * - * \return The return value of the underlying call to osl_rbtree_loop() - * or osl_rbtree_loop_reverse(). + * \return Standard. */ int for_each_matching_row(struct pattern_match_data *pmd) { if (pmd->pm_flags & PM_REVERSE_LOOP) - return osl_rbtree_loop_reverse(pmd->table, pmd->loop_col_num, pmd, - action_if_pattern_matches); - return osl_rbtree_loop(pmd->table, pmd->loop_col_num, pmd, - action_if_pattern_matches); + return osl(osl_rbtree_loop_reverse(pmd->table, pmd->loop_col_num, pmd, + action_if_pattern_matches)); + return osl(osl_rbtree_loop(pmd->table, pmd->loop_col_num, pmd, + action_if_pattern_matches)); } /** @@ -403,80 +407,6 @@ int string_compare(const struct osl_object *obj1, const struct osl_object *obj2) return strncmp(str1, str2, PARA_MIN(obj1->size, obj2->size)); } -/* - * write input from fd to dynamically allocated buffer, - * but maximal max_size byte. - */ -static int fd2buf(int fd, unsigned max_size, struct osl_object *obj) -{ - const size_t chunk_size = 1024; - size_t size = 2048, received = 0; - int ret; - char *buf = para_malloc(size); - - for (;;) { - ret = recv_bin_buffer(fd, 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 = buf; - obj->size = received; - if (ret < 0) - free(buf); - return ret; -} - -/** - * Read data from a file descriptor, and send it to the afs process. - * - * \param fd 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 from \a fd, 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. - * - * \return Negative on errors, the return value of the underlying call to - * send_callback_request() otherwise. - */ -int stdin_command(int fd, struct osl_object *arg_obj, callback_function *f, - unsigned max_len, callback_result_handler *result_handler, - void *private_result_data) -{ - struct osl_object query, stdin_obj; - int ret; - - ret = send_buffer(fd, AWAITING_DATA_MSG); - if (ret < 0) - return ret; - ret = fd2buf(fd, max_len, &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); - free(stdin_obj.data); - ret = send_callback_request(f, &query, result_handler, private_result_data); - free(query.data); - return ret; -} - static int pass_afd(int fd, char *buf, size_t size) { struct msghdr msg = {.msg_iov = NULL}; @@ -652,21 +582,22 @@ out: * Result handler for sending data to the para_client process. * * \param result The data to be sent. - * \param fd_ptr Pointer to the file descriptor. + * \param private Pointer to rc4 context. * - * \return The return value of the underlying call to send_bin_buffer(). + * \return The return value of the underlying call to rc4_send_bin_buffer(). * - * \sa \ref callback_result_handler. + * \sa \ref callback_result_handler, \ref rc4_send_bin_buffer(). */ -int send_result(struct osl_object *result, void *fd_ptr) +int rc4_send_result(struct osl_object *result, void *private) { - int fd = *(int *)fd_ptr; + struct rc4_context *rc4c = private; + if (!result->size) return 1; - return send_bin_buffer(fd, result->data, result->size); + return rc4_send_bin_buffer(rc4c, result->data, result->size); } -int com_select(int fd, int argc, char * const * const argv) +int com_select(struct rc4_context *rc4c, int argc, char * const * const argv) { struct osl_object query; @@ -675,7 +606,7 @@ int com_select(int fd, int argc, char * const * const argv) query.data = argv[1]; query.size = strlen(argv[1]) + 1; return send_callback_request(com_select_callback, &query, - &send_result, &fd); + &rc4_send_result, rc4c); } static void init_admissible_files(char *arg) @@ -730,7 +661,7 @@ static void get_database_dir(void) else { char *home = para_homedir(); database_dir = make_message( - "%s/.paraslash/afs_database", home); + "%s/.paraslash/afs_database-0.4", home); free(home); } } @@ -1096,7 +1027,7 @@ out: free(buf); } -int com_init(int fd, int argc, char * const * const argv) +int com_init(struct rc4_context *rc4c, int argc, char * const * const argv) { int i, j, ret; uint32_t table_mask = (1 << (NUM_AFS_TABLES + 1)) - 1; @@ -1121,9 +1052,10 @@ int com_init(int fd, int argc, char * const * const argv) return -E_BAD_TABLE_NAME; } } - ret = send_callback_request(create_tables_callback, &query, &send_result, &fd); + ret = send_callback_request(create_tables_callback, &query, + rc4_send_result, rc4c); if (ret < 0) - return send_va_buffer(fd, "%s\n", para_strerror(-ret)); + return rc4_send_va_buffer(rc4c, "%s\n", para_strerror(-ret)); return ret; } @@ -1141,7 +1073,7 @@ enum com_check_flags { CHECK_PLAYLISTS = 4 }; -int com_check(int fd, int argc, char * const * const argv) +int com_check(struct rc4_context *rc4c, int argc, char * const * const argv) { unsigned flags = 0; int i, ret; @@ -1173,17 +1105,20 @@ int com_check(int fd, int argc, char * const * const argv) if (!flags) flags = ~0U; if (flags & CHECK_AFT) { - ret = send_callback_request(aft_check_callback, NULL, send_result, &fd); + ret = send_callback_request(aft_check_callback, NULL, + rc4_send_result, rc4c); if (ret < 0) return ret; } if (flags & CHECK_PLAYLISTS) { - ret = send_callback_request(playlist_check_callback, NULL, send_result, &fd); + ret = send_callback_request(playlist_check_callback, + NULL, rc4_send_result, rc4c); if (ret < 0) return ret; } if (flags & CHECK_MOODS) { - ret = send_callback_request(mood_check_callback, NULL, send_result, &fd); + ret = send_callback_request(mood_check_callback, NULL, + rc4_send_result, rc4c); if (ret < 0) return ret; } diff --git a/afs.cmd b/afs.cmd index 0af45113..65d815c7 100644 --- a/afs.cmd +++ b/afs.cmd @@ -3,7 +3,8 @@ SF: afs.c aft.c attribute.c HC: Prototypes for the commands of the audio file selector. CC: Array of commands for the audio file selector. AT: server_command -IN: para error string afh afs server list user_list +SI: openssl/rc4 osl regex +IN: para error crypt command string afh afs server list user_list SN: list of afs commands TM: mood lyr img pl --- @@ -54,6 +55,8 @@ H: -ll: long listing mode (equivalent to -l) H: H: -lv: verbose listing mode H: +H: -lp: parser-friendly mode +H: H: -lm: mbox listing mode H: H: -lc: chunk-table listing mode @@ -94,7 +97,7 @@ H: -sa: sort by audio format. --- N: lsatt P: AFS_READ -D: List attributes +D: List attributes. U: lsatt [-i] [-l] [-r] [pattern] H: Print the list of all defined attributes which match the H: given pattern. If no pattern is given, the full list is @@ -184,7 +187,7 @@ H: a slash (see fnmatch(3)). N: touch P: AFS_READ | AFS_WRITE D: Manipulate the afs data for all audio files matching a pattern. -U: touch [-n numplayed] [-l lastplayed] [-y lyrics_id] [-i image_id] [-a amp] [-v] [-p] pattern +U: touch [-n=numplayed] [-l=lastplayed] [-y=lyrics_id] [-i=image_id] [-a=amp] [-v] [-p] pattern H: If no option is given, lastplayed is set to the current time H: and numplayed is increased by one. Otherwise, only the given H: options are taken into account. @@ -257,7 +260,7 @@ H: loads the mood named 'foo'. --- T: add N: add@member@ -O: int com_add@member@(int fd, int argc, char * const * const argv); +O: int com_add@member@(struct rc4_context *rc4c, int argc, char * const * const argv); P: AFS_READ | AFS_WRITE D: Read data from stdin and add it as a blob to the @member@ table. U: add@member@ @member@_name @@ -270,7 +273,7 @@ H: given name already exists, its contents are replaced by the new data. --- T: cat N: cat@member@ -O: int com_cat@member@(int fd, int argc, char * const * const argv); +O: int com_cat@member@(struct rc4_context *rc4c, int argc, char * const * const argv); P: AFS_READ D: Dump the contents of a blob of type @member@ to stdout. U: cat@member@ @member@_name @@ -280,7 +283,7 @@ H: they were previously added. --- T: ls N: ls@member@ -O: int com_ls@member@(int fd, int argc, char * const * const argv); +O: int com_ls@member@(struct rc4_context *rc4c, int argc, char * const * const argv); P: AFS_READ D: List blobs of type @member@ matching a pattern. U: ls@member@ [-i] [-l] [-r] [pattern] @@ -300,7 +303,7 @@ H: -r Reverse sort order. --- T: rm N: rm@member@ -O: int com_rm@member@(int fd, int argc, char * const * const argv); +O: int com_rm@member@(struct rc4_context *rc4c, int argc, char * const * const argv); P: AFS_READ | AFS_WRITE D: Remove blob(s) of type @member@ from the @member@ table. U: rm@member@ pattern... @@ -309,7 +312,7 @@ H: any given pattern. --- T: mv N: mv@member@ -O: int com_mv@member@(int fd, int argc, char * const * const argv); +O: int com_mv@member@(struct rc4_context *rc4c, int argc, char * const * const argv); P: AFS_READ | AFS_WRITE D: Rename a blob of type @member@. U: mv@member@ old_@member@_name new_@member@_name diff --git a/afs.h b/afs.h index bcc36309..ccfb7b8d 100644 --- a/afs.h +++ b/afs.h @@ -7,7 +7,6 @@ /** \file afs.h Exported symbols of the audio file selector. */ #include -#include "osl.h" #include "hash.h" /** Audio file selector data stored in the audio file table. */ @@ -118,15 +117,8 @@ struct ls_data { HASH_TYPE *hash; }; -void make_empty_status_items(char *buf); - -/** At most that many bytes will be passed from afs to para_server. */ -#define VERBOSE_LS_OUTPUT_SIZE 4096 - /** Data about the current audio file, passed from afs to server. */ struct audio_file_data { - /** Same info as ls -lv -p current audio_file. */ - char verbose_ls_output[VERBOSE_LS_OUTPUT_SIZE]; /** The open file descriptor to the current audio file. */ int fd; /** Vss needs this for streaming. */ @@ -188,7 +180,7 @@ typedef void callback_function(int fd, const struct osl_object *); * \sa \ref send_callback_request(). */ typedef int callback_result_handler(struct osl_object *result, void *private); -int send_result(struct osl_object *result, void *fd_ptr); +int rc4_send_result(struct osl_object *result, void *private); int pass_buffer_as_shm(char *buf, size_t size, void *fd_ptr); __noreturn void afs_init(uint32_t cookie, int socket_fd); @@ -204,9 +196,6 @@ int send_option_arg_callback_request(struct osl_object *options, int send_standard_callback_request(int argc, char * const * const argv, callback_function *f, callback_result_handler *result_handler, void *private_result_data); -int stdin_command(int fd, struct osl_object *arg_obj, callback_function *f, - unsigned max_len, callback_result_handler *result_handler, - void *private_result_data); int string_compare(const struct osl_object *obj1, const struct osl_object *obj2); int for_each_matching_row(struct pattern_match_data *pmd); @@ -245,13 +234,6 @@ int get_afsi_object_of_row(const struct osl_row *row, struct osl_object *obj); int audio_file_loop(void *private_data, osl_rbtree_loop_func *func); void aft_check_callback(int fd, __a_unused const struct osl_object *query); -/* mood */ -int change_current_mood(char *mood_name); -void close_current_mood(void); -int reload_current_mood(void); -void mood_check_callback(int fd, __a_unused const struct osl_object *query); - - /* playlist */ int playlist_open(char *name); void playlist_close(void); diff --git a/aft.c b/aft.c index 20fa3a96..220e019e 100644 --- a/aft.c +++ b/aft.c @@ -6,14 +6,18 @@ /** \file aft.c Audio file table functions. */ +#include #include /* readdir() */ -#include "para.h" -#include "error.h" -#include "string.h" +#include #include #include #include +#include +#include "para.h" +#include "error.h" +#include "crypt.h" +#include "string.h" #include "afh.h" #include "afs.h" #include "net.h" @@ -23,6 +27,8 @@ #include "portable_io.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 { @@ -63,7 +69,9 @@ enum ls_listing_mode { /** -lm */ LS_MODE_MBOX, /** -lc */ - LS_MODE_CHUNKS + LS_MODE_CHUNKS, + /** -lp */ + LS_MODE_PARSER, }; /** The flags accepted by the ls command. */ @@ -220,12 +228,27 @@ enum audio_file_table_columns { NUM_AFT_COLUMNS }; +/** + * Compare two osl objects pointing to hash values. + * + * \param obj1 Pointer to the first hash object. + * \param obj2 Pointer to the second hash object. + * + * \return The values required for an osl compare function. + * + * \sa osl_compare_func, uint32_compare(). + */ +int aft_hash_compare(const struct osl_object *obj1, const struct osl_object *obj2) +{ + return hash_compare((HASH_TYPE *)obj1->data, (HASH_TYPE *)obj2->data); +} + static struct osl_column_description aft_cols[] = { [AFTCOL_HASH] = { .storage_type = OSL_MAPPED_STORAGE, .storage_flags = OSL_RBTREE | OSL_FIXED_SIZE | OSL_UNIQUE, .name = "hash", - .compare_function = osl_hash_compare, + .compare_function = aft_hash_compare, .data_size = HASH_SIZE }, [AFTCOL_PATH] = { @@ -338,23 +361,29 @@ enum afhi_offsets { CHUNK_TV_TV_USEC_OFFSET = 36, /** Number of channels is stored here. (1 byte) */ AFHI_CHANNELS_OFFSET = 40, - /** EOF timeout in ms. (2 byte) */ - AFHI_EOF_OFFSET = 41, /** The tag info position. */ - AFHI_INFO_STRING_OFFSET = 43, + AFHI_INFO_STRING_OFFSET = 41, /** Minimal on-disk size of a valid afhi struct. */ - MIN_AFHI_SIZE = 44 + MIN_AFHI_SIZE = 47, /* at least 6 null bytes for techinfo/tags */ }; static unsigned sizeof_afhi_buf(const struct afh_info *afhi) { if (!afhi) return 0; - return strlen(afhi->info_string) + MIN_AFHI_SIZE; + return MIN_AFHI_SIZE + + strlen(afhi->techinfo) + + strlen(afhi->tags.artist) + + strlen(afhi->tags.title) + + strlen(afhi->tags.year) + + strlen(afhi->tags.album) + + strlen(afhi->tags.comment); } static void save_afhi(struct afh_info *afhi, char *buf) { + char *p; + if (!afhi) return; write_u32(buf + AFHI_SECONDS_TOTAL_OFFSET, afhi->seconds_total); @@ -368,8 +397,14 @@ static void save_afhi(struct afh_info *afhi, char *buf) write_u32(buf + HEADER_OFFSET_OFFSET, afhi->header_offset); 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); - write_u16(buf + AFHI_EOF_OFFSET, tv2ms(&afhi->eof_tv)); - strcpy(buf + AFHI_INFO_STRING_OFFSET, afhi->info_string); /* OK */ + p = buf + AFHI_INFO_STRING_OFFSET; + /* The sprintf's below are OK as our caller 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; + p += sprintf(p, "%s", afhi->tags.year) + 1; + p += sprintf(p, "%s", afhi->tags.album) + 1; + sprintf(p, "%s", afhi->tags.comment); } static void load_afhi(const char *buf, struct afh_info *afhi) @@ -385,8 +420,12 @@ static void load_afhi(const char *buf, struct afh_info *afhi) afhi->header_offset = read_u32(buf + HEADER_OFFSET_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); - ms2tv(read_u16(buf + AFHI_EOF_OFFSET), &afhi->eof_tv); - afhi->info_string = para_strdup(buf + AFHI_INFO_STRING_OFFSET); + afhi->techinfo = (char *)buf + AFHI_INFO_STRING_OFFSET; + afhi->tags.artist = afhi->techinfo + strlen(afhi->techinfo) + 1; + afhi->tags.title = afhi->tags.artist + strlen(afhi->tags.artist) + 1; + afhi->tags.year = afhi->tags.title + strlen(afhi->tags.title) + 1; + afhi->tags.album = afhi->tags.year + strlen(afhi->tags.year) + 1; + afhi->tags.comment = afhi->tags.album + strlen(afhi->tags.album) + 1; } static unsigned sizeof_chunk_table(struct afh_info *afhi) @@ -419,27 +458,27 @@ static void load_chunk_table(struct afh_info *afhi, char *buf) * \param path The full path of the audio file. * \param row Result pointer. * - * \return The return value of the underlying call to osl_get_row(). + * \return Standard. */ int aft_get_row_of_path(const char *path, struct osl_row **row) { struct osl_object obj = {.data = (char *)path, .size = strlen(path) + 1}; - return osl_get_row(audio_file_table, AFTCOL_PATH, &obj, row); + return osl(osl_get_row(audio_file_table, AFTCOL_PATH, &obj, row)); } /** * Get the row of the audio file table corresponding to the given hash value. * * \param hash The hash value of the desired audio file. - * \param row resul pointer. + * \param row Result pointer. * - * \return The return value of the underlying call to osl_get_row(). + * \return Standard. */ int aft_get_row_of_hash(HASH_TYPE *hash, struct osl_row **row) { const struct osl_object obj = {.data = hash, .size = HASH_SIZE}; - return osl_get_row(audio_file_table, AFTCOL_HASH, &obj, row); + return osl(osl_get_row(audio_file_table, AFTCOL_HASH, &obj, row)); } /** @@ -448,11 +487,11 @@ int aft_get_row_of_hash(HASH_TYPE *hash, struct osl_row **row) * \param row Pointer to a row in the audio file table. * \param obj Result pointer. * - * \return The return value of the underlying call to osl_get_object(). + * \return Standard. */ int get_afsi_object_of_row(const struct osl_row *row, struct osl_object *obj) { - return osl_get_object(audio_file_table, row, AFTCOL_AFSI, obj); + return osl(osl_get_object(audio_file_table, row, AFTCOL_AFSI, obj)); } /** @@ -521,8 +560,8 @@ int get_afsi_of_path(const char *path, struct afs_info *afsi) int get_audio_file_path_of_row(const struct osl_row *row, char **path) { struct osl_object path_obj; - int ret = osl_get_object(audio_file_table, row, AFTCOL_PATH, - &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; @@ -539,9 +578,10 @@ int get_audio_file_path_of_row(const struct osl_row *row, char **path) * * \sa get_hash_of_row(). */ -static int get_hash_object_of_aft_row(const struct osl_row *row, struct osl_object *obj) +static int get_hash_object_of_aft_row(const struct osl_row *row, + struct osl_object *obj) { - return osl_get_object(audio_file_table, row, AFTCOL_HASH, obj); + return osl(osl_get_object(audio_file_table, row, AFTCOL_HASH, obj)); } /** @@ -579,8 +619,8 @@ static int get_hash_of_row(const struct osl_row *row, HASH_TYPE **hash) int get_afhi_of_row(const struct osl_row *row, struct afh_info *afhi) { struct osl_object obj; - int ret = osl_get_object(audio_file_table, row, AFTCOL_AFHI, - &obj); + int ret = osl(osl_get_object(audio_file_table, row, AFTCOL_AFHI, + &obj)); if (ret < 0) return ret; load_afhi(obj.data, afhi); @@ -691,56 +731,68 @@ static void get_duration_buf(int seconds, char *buf, struct ls_options *opts) } } -static char *make_attribute_lines(const char *att_bitmap, struct afs_info *afsi) + +static int write_attribute_items(struct para_buffer *b, + const char *att_bitmap, struct afs_info *afsi) { - char *att_text, *att_lines; + char *att_text; + int ret; - get_attribute_text(&afsi->attributes, " ", &att_text); - if (!att_text) - return para_strdup(att_bitmap); - att_lines = make_message("%s: %s\n%s: %s", - status_item_list[SI_ATTRIBUTES_BITMAP], att_bitmap, - status_item_list[SI_ATTRIBUTES_TXT], att_text); + ret = WRITE_STATUS_ITEM(b, SI_ATTRIBUTES_BITMAP, "%s\n", att_bitmap); + if (ret < 0) + return ret; + ret = get_attribute_text(&afsi->attributes, " ", &att_text); + if (ret < 0) + return ret; + ret = WRITE_STATUS_ITEM(b, SI_ATTRIBUTES_TXT, "%s\n", att_text); free(att_text); - return att_lines; + return ret; } -static char *make_lyrics_lines(struct afs_info *afsi) +static int write_lyrics_items(struct para_buffer *b, struct afs_info *afsi) { char *lyrics_name; + int ret; + ret = WRITE_STATUS_ITEM(b, SI_LYRICS_ID, "%u\n", afsi->lyrics_id); + if (ret < 0) + return ret; lyr_get_name_by_id(afsi->lyrics_id, &lyrics_name); - return make_message("%s: %u\n%s: %s\n", - status_item_list[SI_LYRICS_ID], afsi->lyrics_id, - status_item_list[SI_LYRICS_NAME], lyrics_name? - lyrics_name : "(none)"); + return WRITE_STATUS_ITEM(b, SI_LYRICS_NAME, "%s\n", lyrics_name? + lyrics_name : "(none)"); } -static char *make_image_lines(struct afs_info *afsi) +static int write_image_items(struct para_buffer *b, struct afs_info *afsi) { char *image_name; + int ret; + + ret = WRITE_STATUS_ITEM(b, SI_IMAGE_ID, "%u\n", afsi->image_id); + if (ret < 0) + return ret; img_get_name_by_id(afsi->image_id, &image_name); - return make_message("%s: %u\n%s: %s\n", - status_item_list[SI_IMAGE_ID], afsi->image_id, - status_item_list[SI_IMAGE_NAME], image_name? - image_name : "(none)"); + return WRITE_STATUS_ITEM(b, SI_IMAGE_NAME, "%s\n", image_name? + image_name : "(none)"); } -static char *make_filename_lines(const char *path, unsigned flags) +static int write_filename_items(struct para_buffer *b, const char *path, + unsigned flags) { - char *dirname, *ret; - const char *basename; + char *val; + int ret; if (!(flags & LS_FLAG_FULL_PATH)) - return make_message("%s: %s\n", - status_item_list[SI_BASENAME], path); - basename = para_basename(path), - dirname = para_dirname(path); - ret = make_message("%s: %s\n%s: %s\n%s: %s\n", - status_item_list[SI_PATH], path, - status_item_list[SI_DIRECTORY], dirname? dirname : "?", - status_item_list[SI_BASENAME], basename? basename : "?"); - free(dirname); + return WRITE_STATUS_ITEM(b, SI_BASENAME, "%s\n", path); + ret = WRITE_STATUS_ITEM(b, SI_PATH, "%s\n", path); + if (ret < 0) + return ret; + val = para_basename(path); + ret = WRITE_STATUS_ITEM(b, SI_BASENAME, "%s\n", val? val : ""); + if (ret < 0) + return ret; + val = para_dirname(path); + ret = WRITE_STATUS_ITEM(b, SI_DIRECTORY, "%s\n", val? val : ""); + free(val); return ret; } @@ -778,6 +830,14 @@ out: return ret; } +static int write_score(struct para_buffer *b, struct ls_data *d, + struct ls_options *opts) +{ + if (!(opts->flags & LS_FLAG_ADMISSIBLE_ONLY)) /* no score*/ + return 0; + return WRITE_STATUS_ITEM(b, SI_SCORE, "%li\n", d->score); +} + static int print_list_item(struct ls_data *d, struct ls_options *opts, struct para_buffer *b, time_t current_time) { @@ -785,13 +845,9 @@ static int print_list_item(struct ls_data *d, struct ls_options *opts, char att_buf[65]; char last_played_time[30]; char duration_buf[30]; /* nobody has an audio file long enough to overflow this */ - char score_buf[30] = ""; struct afs_info *afsi = &d->afsi; struct afh_info *afhi = &d->afhi; - struct ls_widths *w = &opts->widths; - int have_score = opts->flags & LS_FLAG_ADMISSIBLE_ONLY; char asc_hash[2 * HASH_SIZE + 1]; - char *att_lines, *lyrics_lines, *image_lines, *filename_lines; if (opts->mode == LS_MODE_SHORT) { ret = para_printf(b, "%s\n", d->path); @@ -812,16 +868,15 @@ static int print_list_item(struct ls_data *d, struct ls_options *opts, goto out; } get_duration_buf(afhi->seconds_total, duration_buf, opts); - if (have_score) { - if (opts->mode == LS_MODE_LONG) - sprintf(score_buf, "%*li ", w->score_width, d->score); - else - sprintf(score_buf, "%li ", d->score); - } - if (opts->mode == LS_MODE_LONG) { + struct ls_widths *w = &opts->widths; + if (opts->flags & LS_FLAG_ADMISSIBLE_ONLY) { + ret = para_printf(b, "%*li ", + opts->widths.score_width, d->score); + if (ret < 0) + goto out; + } ret = para_printf(b, - "%s" /* score */ "%s " /* attributes */ "%*u " /* amp */ "%*d " /* image_id */ @@ -834,7 +889,6 @@ static int print_list_item(struct ls_data *d, struct ls_options *opts, "%*d " /* num_played */ "%s " /* last_played */ "%s\n", /* path */ - score_buf, att_buf, w->amp_width, afsi->amp, w->image_id_width, afsi->image_id, @@ -850,11 +904,6 @@ static int print_list_item(struct ls_data *d, struct ls_options *opts, ); goto out; } - hash_to_asc(d->hash, asc_hash); - att_lines = make_attribute_lines(att_buf, afsi); - lyrics_lines = make_lyrics_lines(afsi); - image_lines = make_image_lines(afsi); - filename_lines = make_filename_lines(d->path, opts->flags); if (opts->mode == LS_MODE_MBOX) { const char *bn = para_basename(d->path); ret = para_printf(b, @@ -866,47 +915,78 @@ static int print_list_item(struct ls_data *d, struct ls_options *opts, if (ret < 0) goto out; } - ret = para_printf(b, - "%s" /* filename stuff */ - "%s%s%s%s" /* score */ - "%s\n" /* attributes */ - "%s: %s\n" /* hash */ - "%s" /* image id, image name */ - "%s" /* lyrics */ - "%s: %dkbit/s\n" /* bitrate */ - "%s: %s\n" /* format */ - "%s: %dHz\n" /* frequency */ - "%s: %d\n" /* channels */ - "%s: %s\n" /* duration */ - "%s: %lu\n" /* seconds total */ - "%s: %s\n" /* last played time */ - "%s: %d\n" /* num_played */ - "%s: %u\n" /* ampplification */ - "%s" /* tag info */ - "%s: %lu\n" /* chunk time */ - "%s: %lu\n", /* num chunks */ - filename_lines, - have_score? status_item_list[SI_SCORE] : "", - have_score? ": " : "", - score_buf, - have_score? "\n" : "", - att_lines, - status_item_list[SI_HASH], asc_hash, - image_lines, - lyrics_lines, - status_item_list[SI_BITRATE], afhi->bitrate, - status_item_list[SI_FORMAT], audio_format_name(afsi->audio_format_id), - status_item_list[SI_FREQUENCY], afhi->frequency, - status_item_list[SI_CHANNELS], afhi->channels, - status_item_list[SI_DURATION], duration_buf, - status_item_list[SI_SECONDS_TOTAL], afhi->seconds_total, - status_item_list[SI_LAST_PLAYED], last_played_time, - status_item_list[SI_NUM_PLAYED], afsi->num_played, - status_item_list[SI_AMPLIFICATION], afsi->amp, - afhi->info_string, - status_item_list[SI_CHUNK_TIME], tv2ms(&afhi->chunk_tv), - status_item_list[SI_NUM_CHUNKS], afhi->chunks_total - ); + ret = write_filename_items(b, d->path, opts->flags); + if (ret < 0) + goto out; + ret = write_score(b, d, opts); + if (ret < 0) + goto out; + ret = write_attribute_items(b, att_buf, afsi); + if (ret < 0) + goto out; + ret = write_image_items(b, afsi); + if (ret < 0) + goto out; + ret = write_lyrics_items(b, afsi); + if (ret < 0) + goto out; + hash_to_asc(d->hash, asc_hash); + ret = WRITE_STATUS_ITEM(b, SI_HASH, "%s\n", asc_hash); + if (ret < 0) + goto out; + ret = WRITE_STATUS_ITEM(b, SI_BITRATE, "%dkbit/s\n", afhi->bitrate); + if (ret < 0) + goto out; + ret = WRITE_STATUS_ITEM(b, SI_FORMAT, "%s\n", + audio_format_name(afsi->audio_format_id)); + if (ret < 0) + goto out; + ret = WRITE_STATUS_ITEM(b, SI_FREQUENCY, "%dHz\n", afhi->frequency); + if (ret < 0) + goto out; + ret = WRITE_STATUS_ITEM(b, SI_CHANNELS, "%d\n", afhi->channels); + if (ret < 0) + goto out; + ret = WRITE_STATUS_ITEM(b, SI_DURATION, "%s\n", duration_buf); + if (ret < 0) + goto out; + ret = WRITE_STATUS_ITEM(b, SI_SECONDS_TOTAL, "%lu\n", + afhi->seconds_total); + if (ret < 0) + goto out; + ret = WRITE_STATUS_ITEM(b, SI_LAST_PLAYED, "%s\n", last_played_time); + if (ret < 0) + goto out; + ret = WRITE_STATUS_ITEM(b, SI_NUM_PLAYED, "%d\n", afsi->num_played); + if (ret < 0) + goto out; + ret = WRITE_STATUS_ITEM(b, SI_AMPLIFICATION, "%u\n", afsi->amp); + if (ret < 0) + goto out; + ret = WRITE_STATUS_ITEM(b, SI_CHUNK_TIME, "%lu\n", + tv2ms(&afhi->chunk_tv)); + if (ret < 0) + goto out; + ret = WRITE_STATUS_ITEM(b, SI_NUM_CHUNKS, "%lu\n", + afhi->chunks_total); + if (ret < 0) + goto out; + ret = WRITE_STATUS_ITEM(b, SI_TECHINFO, "%s\n", afhi->techinfo); + if (ret < 0) + goto out; + ret = WRITE_STATUS_ITEM(b, SI_ARTIST, "%s\n", afhi->tags.artist); + if (ret < 0) + goto out; + ret = WRITE_STATUS_ITEM(b, SI_TITLE, "%s\n", afhi->tags.title); + if (ret < 0) + goto out; + ret = WRITE_STATUS_ITEM(b, SI_YEAR, "%s\n", afhi->tags.year); + if (ret < 0) + goto out; + ret = WRITE_STATUS_ITEM(b, SI_ALBUM, "%s\n", afhi->tags.album); + if (ret < 0) + goto out; + ret = WRITE_STATUS_ITEM(b, SI_COMMENT, "%s\n", afhi->tags.comment); if (ret < 0) goto out; if (opts->mode == LS_MODE_MBOX) { @@ -918,129 +998,10 @@ static int print_list_item(struct ls_data *d, struct ls_options *opts, osl_close_disk_object(&lyrics_def); } } - free(att_lines); - free(lyrics_lines); - free(image_lines); - free(filename_lines); out: - free(afhi->info_string); return ret; } -/** - * Write a list of audio-file related status items with empty values. - * - * \param buf Result pointer. - * - * This is used by vss when currently no audio file is open. - */ -void make_empty_status_items(char *buf) -{ - sprintf(buf, - "%s: \n" /* path */ - "%s: \n" /* dirname */ - "%s: \n" /* basename */ - "%s: \n" /* score */ - "%s: \n" /* attributes bitmap */ - "%s: \n" /* attributes txt */ - "%s: \n" /* hash */ - "%s: \n" /* image id */ - "%s: \n" /* image name */ - "%s: \n" /* lyrics id */ - "%s: \n" /* lyrics name */ - "%s: \n" /* bitrate */ - "%s: \n" /* format */ - "%s: \n" /* frequency */ - "%s: \n" /* channels */ - "%s: \n" /* duration */ - "%s: \n" /* seconds total */ - "%s: \n" /* num played */ - "%s: \n" /* last played */ - "%s: \n" /* audio file info */ - "%s: \n" /* taginfo1 */ - "%s: \n" /* taginfo2 */ - "%s: \n" /* amplification */ - , - status_item_list[SI_PATH], - status_item_list[SI_DIRECTORY], - status_item_list[SI_BASENAME], - status_item_list[SI_SCORE], - status_item_list[SI_ATTRIBUTES_BITMAP], - status_item_list[SI_ATTRIBUTES_TXT], - status_item_list[SI_HASH], - status_item_list[SI_IMAGE_ID], - status_item_list[SI_IMAGE_NAME], - status_item_list[SI_LYRICS_ID], - status_item_list[SI_LYRICS_NAME], - status_item_list[SI_BITRATE], - status_item_list[SI_FORMAT], - status_item_list[SI_FREQUENCY], - status_item_list[SI_CHANNELS], - status_item_list[SI_DURATION], - status_item_list[SI_SECONDS_TOTAL], - status_item_list[SI_NUM_PLAYED], - status_item_list[SI_LAST_PLAYED], - status_item_list[SI_AUDIO_FILE_INFO], - status_item_list[SI_TAGINFO1], - status_item_list[SI_TAGINFO2], - status_item_list[SI_AMPLIFICATION] - ); -} - -static void fixup_taginfo(char *begin, char *end) -{ - char *p = begin; - - for (;;) { - p = strchr(p, '\n'); - if (!p) - break; - if (p >= end - 1) - break; - *p = ' '; - p++; - } -} - -/* crap, remove this ASAP. */ -static int fixup_info_string(char *info_string) -{ - char *t1, *t2, *end; - - if (strncmp(info_string, "audio_file_info:", 16)) - return -ERRNO_TO_PARA_ERROR(EINVAL); - t1 = strstr(info_string, "\ntaginfo1:"); - if (!t1) - return -ERRNO_TO_PARA_ERROR(EINVAL); - t2 = strstr(info_string, "\ntaginfo2: "); - if (!t2) - return -ERRNO_TO_PARA_ERROR(EINVAL); - - end = t2 + strlen(t2) + 1; - fixup_taginfo(info_string + 16, t1); - fixup_taginfo(t1 + 10, t2); - fixup_taginfo(t2 + 10, end); - - if (t1 - info_string < 80 && t2 - t1 < 80 && end - t2 < 80) - return 0; - if (t1 - info_string >= 80) { - memmove(info_string + 80, t1, end - t1); - t1 = info_string + 80; - t2 -= t1 - info_string - 80; - end -= t1 - info_string - 80; - } - if (t2 - t1 >= 80) { - memmove(t1 + 80, t2, end - t2); - end -= t2 - t1 - 80; - t2 = t1 + 80; - } - if (end - t2 >= 80) { - t2[78] = '\n'; - t2[79] = '\0'; - } - return 1; -} - static int make_status_items(struct audio_file_data *afd, struct afs_info *afsi, char *path, long score, HASH_TYPE *hash) @@ -1056,26 +1017,28 @@ static int make_status_items(struct audio_file_data *afd, .flags = LS_FLAG_FULL_PATH | LS_FLAG_ADMISSIBLE_ONLY, .mode = LS_MODE_VERBOSE, }; - struct para_buffer pb = {.max_size = VERBOSE_LS_OUTPUT_SIZE - 1}; + struct para_buffer pb = {.max_size = SHMMAX - 1}; time_t current_time; int ret; - ret = fixup_info_string(afd->afhi.info_string); - if (ret < 0) { - PARA_WARNING_LOG("ignoring invalid tag info\n"); - afd->afhi.info_string[0] = '\0'; - } else if (ret) - PARA_NOTICE_LOG("truncated overlong tag info\n"); time(¤t_time); - ret = print_list_item(&d, &opts, &pb, current_time); /* frees info string */ - afd->afhi.info_string = NULL; + ret = print_list_item(&d, &opts, &pb, current_time); if (ret < 0) - goto out; - strncpy(afd->verbose_ls_output, pb.buf, VERBOSE_LS_OUTPUT_SIZE); - afd->verbose_ls_output[VERBOSE_LS_OUTPUT_SIZE - 1] = '\0'; -out: - free(pb.buf); - return ret; + return ret; + free(status_items); + status_items = pb.buf; + memset(&pb, 0, sizeof(pb)); + pb.max_size = SHMMAX - 1; + pb.flags = PBF_SIZE_PREFIX; + ret = print_list_item(&d, &opts, &pb, current_time); + if (ret < 0) { + free(status_items); + status_items = NULL; + return ret; + } + free(parser_friendly_status_items); + parser_friendly_status_items = pb.buf; + return 1; } /** @@ -1147,7 +1110,6 @@ int open_and_update_audio_file(struct osl_row *aft_row, long score, ret = save_afd(afd); err: free(afd->afhi.chunk_table); - free(afd->afhi.info_string); osl_close_disk_object(&chunk_table_obj); return ret; } @@ -1350,7 +1312,6 @@ static int prepare_ls_row(struct osl_row *row, void *ls_opts) } return 1; err: - free(d->afhi.info_string); return ret; } @@ -1359,11 +1320,11 @@ static void com_ls_callback(int fd, const struct osl_object *query) struct ls_options *opts = query->data; char *p, *pattern_start = (char *)query->data + sizeof(*opts); struct para_buffer b = {.max_size = SHMMAX, + .flags = (opts->mode == LS_MODE_PARSER)? PBF_SIZE_PREFIX : 0, .max_size_handler = pass_buffer_as_shm, .private_data = &fd}; int i = 0, ret; time_t current_time; - if (opts->num_patterns) { opts->patterns = para_malloc(opts->num_patterns * sizeof(char *)); for (i = 0, p = pattern_start; i < opts->num_patterns; i++) { @@ -1375,8 +1336,8 @@ static void com_ls_callback(int fd, const struct osl_object *query) if (opts->flags & LS_FLAG_ADMISSIBLE_ONLY) ret = admissible_file_loop(opts, prepare_ls_row); else - ret = osl_rbtree_loop(audio_file_table, AFTCOL_PATH, opts, - prepare_ls_row); + ret = osl(osl_rbtree_loop(audio_file_table, AFTCOL_PATH, opts, + prepare_ls_row)); if (ret < 0) goto out; if (!opts->num_matching_paths) @@ -1409,7 +1370,7 @@ out: /* * TODO: flags -h (sort by hash) */ -int com_ls(int fd, int argc, char * const * const argv) +int com_ls(struct rc4_context *rc4c, int argc, char * const * const argv) { int i, ret; unsigned flags = 0; @@ -1449,6 +1410,9 @@ int com_ls(int fd, int argc, char * const * const argv) case 'c': mode = LS_MODE_CHUNKS; continue; + case 'p': + mode = LS_MODE_PARSER; + continue; default: return -E_AFT_SYNTAX; } @@ -1518,7 +1482,7 @@ int com_ls(int fd, int argc, char * const * const argv) opts.mode = mode; opts.num_patterns = argc - i; ret = send_option_arg_callback_request(&query, opts.num_patterns, - argv + i, com_ls_callback, send_result, &fd); + argv + i, com_ls_callback, rc4_send_result, rc4c); return ret; } @@ -1528,12 +1492,12 @@ int com_ls(int fd, int argc, char * const * const argv) * \param private_data An arbitrary data pointer, passed to \a func. * \param func The custom function to be called. * - * \return The return value of the underlying call to osl_rbtree_loop(). + * \return Standard. */ int audio_file_loop(void *private_data, osl_rbtree_loop_func *func) { - return osl_rbtree_loop(audio_file_table, AFTCOL_HASH, private_data, - func); + return osl(osl_rbtree_loop(audio_file_table, AFTCOL_HASH, private_data, + func)); } static struct osl_row *find_hash_sister(HASH_TYPE *hash) @@ -1693,7 +1657,7 @@ static void com_add_callback(int fd, const struct osl_object *query) PARA_INFO_LOG("request to add %s\n", path); hs = find_hash_sister(hash); ret = aft_get_row_of_path(path, &pb); - if (ret < 0 && ret != -E_RB_KEY_NOT_FOUND) + if (ret < 0 && ret != -OSL_ERRNO_TO_PARA_ERROR(E_OSL_RB_KEY_NOT_FOUND)) goto out; if (hs && pb && hs == pb && !(flags & ADD_FLAG_FORCE)) { if (flags & ADD_FLAG_VERBOSE) @@ -1710,23 +1674,23 @@ static void com_add_callback(int fd, const struct osl_object *query) if (ret < 0) goto out; } - ret = osl_del_row(audio_file_table, pb); + ret = osl(osl_del_row(audio_file_table, pb)); if (ret < 0) goto out; pb = NULL; } /* file rename, update hs' path */ if (flags & ADD_FLAG_VERBOSE) { - ret = osl_get_object(audio_file_table, hs, - AFTCOL_PATH, &obj); + ret = osl(osl_get_object(audio_file_table, hs, + AFTCOL_PATH, &obj)); if (ret < 0) goto out; ret = para_printf(&msg, "renamed from %s\n", (char *)obj.data); if (ret < 0) goto out; } - ret = osl_update_object(audio_file_table, hs, AFTCOL_PATH, - &objs[AFTCOL_PATH]); + ret = osl(osl_update_object(audio_file_table, hs, AFTCOL_PATH, + &objs[AFTCOL_PATH])); if (ret < 0) goto out; afs_event(AUDIO_FILE_RENAME, &msg, hs); @@ -1770,12 +1734,12 @@ static void com_add_callback(int fd, const struct osl_object *query) if (ret < 0) goto out; } - ret = osl_update_object(audio_file_table, row, AFTCOL_AFHI, - &objs[AFTCOL_AFHI]); + ret = osl(osl_update_object(audio_file_table, row, AFTCOL_AFHI, + &objs[AFTCOL_AFHI])); if (ret < 0) goto out; - ret = osl_update_object(audio_file_table, row, AFTCOL_CHUNKS, - &objs[AFTCOL_CHUNKS]); + ret = osl(osl_update_object(audio_file_table, row, AFTCOL_CHUNKS, + &objs[AFTCOL_CHUNKS])); if (ret < 0) goto out; afs_event(AFHI_CHANGE, &msg, row); @@ -1793,7 +1757,7 @@ static void com_add_callback(int fd, const struct osl_object *query) objs[AFTCOL_AFSI].data = &afsi_buf; objs[AFTCOL_AFSI].size = AFSI_SIZE; save_afsi(&default_afsi, &objs[AFTCOL_AFSI]); - ret = osl_add_and_get_row(audio_file_table, objs, &aft_row); + ret = osl(osl_add_and_get_row(audio_file_table, objs, &aft_row)); afs_event(AUDIO_FILE_ADD, &msg, aft_row); out: if (ret < 0) @@ -1805,8 +1769,8 @@ out: /** Used by com_add(). */ struct private_add_data { - /** The socket file descriptor. */ - int fd; + /** The socket file descriptor, including rc4 keys. */ + struct rc4_context *rc4c; /** The given add flags. */ uint32_t flags; }; @@ -1856,12 +1820,13 @@ static int add_one_audio_file(const char *path, void *private_data) query.size = strlen(path) + 1; ret = send_callback_request(path_brother_callback, &query, get_row_pointer_from_result, &pb); - if (ret < 0 && ret != -E_RB_KEY_NOT_FOUND) + if (ret < 0 && ret != -OSL_ERRNO_TO_PARA_ERROR(E_OSL_RB_KEY_NOT_FOUND)) goto out_free; ret = 1; if (pb && (pad->flags & ADD_FLAG_LAZY)) { /* lazy is really cheap */ if (pad->flags & ADD_FLAG_VERBOSE) - send_ret = send_va_buffer(pad->fd, "lazy-ignore: %s\n", path); + send_ret = rc4_send_va_buffer(pad->rc4c, + "lazy-ignore: %s\n", path); goto out_free; } /* We still want to add this file. Compute its hash. */ @@ -1881,7 +1846,7 @@ static int add_one_audio_file(const char *path, void *private_data) ret = 1; if (pb && hs && hs == pb && !(pad->flags & ADD_FLAG_FORCE)) { if (pad->flags & ADD_FLAG_VERBOSE) - send_ret = send_va_buffer(pad->fd, + send_ret = rc4_send_va_buffer(pad->rc4c, "%s exists, not forcing update\n", path); goto out_unmap; } @@ -1899,13 +1864,13 @@ static int add_one_audio_file(const char *path, void *private_data) munmap(map.data, map.size); close(fd); if (pad->flags & ADD_FLAG_VERBOSE) { - send_ret = send_va_buffer(pad->fd, "adding %s\n", path); + send_ret = rc4_send_va_buffer(pad->rc4c, "adding %s\n", path); if (send_ret < 0) goto out_free; } save_add_callback_buffer(hash, path, afhi_ptr, pad->flags, format_num, &obj); /* Ask afs to consider this entry for adding. */ - ret = send_callback_request(com_add_callback, &obj, send_result, &pad->fd); + ret = send_callback_request(com_add_callback, &obj, rc4_send_result, pad->rc4c); goto out_free; out_unmap: @@ -1913,21 +1878,26 @@ out_unmap: munmap(map.data, map.size); out_free: if (ret < 0 && send_ret >= 0) - send_ret = send_va_buffer(pad->fd, "failed to add %s (%s)\n", path, - para_strerror(-ret)); + send_ret = rc4_send_va_buffer(pad->rc4c, + "failed to add %s (%s)\n", path, para_strerror(-ret)); free(obj.data); if (afhi_ptr) { free(afhi_ptr->chunk_table); - free(afhi_ptr->info_string); + free(afhi_ptr->techinfo); + free(afhi_ptr->tags.artist); + free(afhi_ptr->tags.title); + free(afhi_ptr->tags.year); + free(afhi_ptr->tags.album); + free(afhi_ptr->tags.comment); } /* Stop adding files only on send errors. */ return send_ret; } -int com_add(int fd, int argc, char * const * const argv) +int com_add(struct rc4_context *rc4c, int argc, char * const * const argv) { int i, ret; - struct private_add_data pad = {.fd = fd, .flags = 0}; + struct private_add_data pad = {.rc4c = rc4c, .flags = 0}; struct stat statbuf; for (i = 1; i < argc; i++) { @@ -1961,7 +1931,7 @@ int com_add(int fd, int argc, char * const * const argv) char *path; ret = verify_path(argv[i], &path); if (ret < 0) { - ret = send_va_buffer(fd, "%s: %s\n", argv[i], + ret = rc4_send_va_buffer(rc4c, "%s: %s\n", argv[i], para_strerror(-ret)); if (ret < 0) return ret; @@ -1969,7 +1939,7 @@ int com_add(int fd, int argc, char * const * const argv) } ret = stat(path, &statbuf); if (ret < 0) { - ret = send_va_buffer(fd, "failed to stat %s (%s)\n", path, + ret = rc4_send_va_buffer(rc4c, "failed to stat %s (%s)\n", path, strerror(errno)); free(path); if (ret < 0) @@ -1982,7 +1952,7 @@ int com_add(int fd, int argc, char * const * const argv) else ret = add_one_audio_file(path, &pad); if (ret < 0) { - send_va_buffer(fd, "%s: %s\n", path, para_strerror(-ret)); + rc4_send_va_buffer(rc4c, "%s: %s\n", path, para_strerror(-ret)); free(path); return ret; } @@ -2114,7 +2084,7 @@ static void com_touch_callback(int fd, const struct osl_object *query) free(tad.pb.buf); } -int com_touch(int fd, int argc, char * const * const argv) +int com_touch(struct rc4_context *rc4c, int argc, char * const * const argv) { struct com_touch_options cto = { .num_played = -1, @@ -2135,33 +2105,33 @@ int com_touch(int fd, int argc, char * const * const argv) i++; break; } - if (!strncmp(arg, "-n", 2)) { - ret = para_atoi32(arg + 2, &cto.num_played); + if (!strncmp(arg, "-n=", 3)) { + ret = para_atoi32(arg + 3, &cto.num_played); if (ret < 0) return ret; continue; } - if (!strncmp(arg, "-l", 2)) { - ret = para_atoi64(arg + 2, &cto.last_played); + if (!strncmp(arg, "-l=", 3)) { + ret = para_atoi64(arg + 3, &cto.last_played); if (ret < 0) return ret; continue; } - if (!strncmp(arg, "-y", 2)) { - ret = para_atoi32(arg + 2, &cto.lyrics_id); + if (!strncmp(arg, "-y=", 3)) { + ret = para_atoi32(arg + 3, &cto.lyrics_id); if (ret < 0) return ret; continue; } - if (!strncmp(arg, "-i", 2)) { - ret = para_atoi32(arg + 2, &cto.image_id); + if (!strncmp(arg, "-i=", 3)) { + ret = para_atoi32(arg + 3, &cto.image_id); if (ret < 0) return ret; continue; } - if (!strncmp(arg, "-a", 2)) { + if (!strncmp(arg, "-a=", 3)) { int32_t val; - ret = para_atoi32(arg + 2, &val); + ret = para_atoi32(arg + 3, &val); if (ret < 0) return ret; if (val < 0 || val > 255) @@ -2182,9 +2152,9 @@ int com_touch(int fd, int argc, char * const * const argv) if (i >= argc) return -E_AFT_SYNTAX; ret = send_option_arg_callback_request(&query, argc - i, - argv + i, com_touch_callback, send_result, &fd); + argv + i, com_touch_callback, rc4_send_result, rc4c); if (ret < 0) - send_va_buffer(fd, "%s\n", para_strerror(-ret)); + rc4_send_va_buffer(rc4c, "%s\n", para_strerror(-ret)); return ret; } @@ -2220,7 +2190,7 @@ static int remove_audio_file(__a_unused struct osl_table *table, return ret; } afs_event(AUDIO_FILE_REMOVE, &crd->pb, row); - ret = osl_del_row(audio_file_table, row); + ret = osl(osl_del_row(audio_file_table, row)); if (ret < 0) para_printf(&crd->pb, "%s: %s\n", name, para_strerror(-ret)); else @@ -2266,7 +2236,7 @@ static void com_rm_callback(int fd, const struct osl_object *query) } /* TODO options: -r (recursive) */ -int com_rm(int fd, int argc, char * const * const argv) +int com_rm(struct rc4_context *rc4c, int argc, char * const * const argv) { uint32_t flags = 0; struct osl_object query = {.data = &flags, .size = sizeof(flags)}; @@ -2297,9 +2267,9 @@ int com_rm(int fd, int argc, char * const * const argv) if (i >= argc) return -E_AFT_SYNTAX; ret = send_option_arg_callback_request(&query, argc - i, argv + i, - com_rm_callback, send_result, &fd); + com_rm_callback, rc4_send_result, rc4c); if (ret < 0) - send_va_buffer(fd, "%s\n", para_strerror(-ret)); + rc4_send_va_buffer(rc4c, "%s\n", para_strerror(-ret)); return ret; } @@ -2414,7 +2384,7 @@ out: free(cad.pb.buf); } -int com_cpsi(int fd, int argc, char * const * const argv) +int com_cpsi(struct rc4_context *rc4c, int argc, char * const * const argv) { unsigned flags = 0; int i, ret; @@ -2454,17 +2424,36 @@ int com_cpsi(int fd, int argc, char * const * const argv) } break; } - if (i + 1 >= argc) /* need at least souce file and pattern */ + if (i + 1 >= argc) /* need at least source file and pattern */ return -E_AFT_SYNTAX; if (!(flags & ~CPSI_FLAG_VERBOSE)) /* no copy flags given */ flags = ~(unsigned)CPSI_FLAG_VERBOSE | flags; ret = send_option_arg_callback_request(&options, argc - i, argv + i, - com_cpsi_callback, send_result, &fd); + com_cpsi_callback, rc4_send_result, rc4c); if (ret < 0) - send_va_buffer(fd, "%s\n", para_strerror(-ret)); + rc4_send_va_buffer(rc4c, "%s\n", para_strerror(-ret)); return ret; } +void afs_stat_callback(int fd, const struct osl_object *query) +{ + int *parser_friendly = query->data; + char *buf = *parser_friendly? + parser_friendly_status_items : status_items; + + if (!buf) + return; + pass_buffer_as_shm(buf, strlen(buf), &fd); +} + +int send_afs_status(struct rc4_context *rc4c, int parser_friendly) +{ + struct osl_object query = {.data = &parser_friendly, + .size = sizeof(parser_friendly)}; + + return send_callback_request(afs_stat_callback, &query, rc4_send_result, rc4c); +} + /* TODO: optionally fix problems by removing offending rows */ static int check_audio_file(struct osl_row *row, void *data) { @@ -2543,6 +2532,10 @@ static void aft_close(void) { osl_close_table(audio_file_table, OSL_MARK_CLEAN); audio_file_table = NULL; + free(status_items); + status_items = NULL; + free(parser_friendly_status_items); + parser_friendly_status_items = NULL; } /** @@ -2559,7 +2552,7 @@ static int aft_open(const char *dir) int ret; audio_file_table_desc.dir = dir; - ret = osl_open_table(&audio_file_table_desc, &audio_file_table); + ret = osl(osl_open_table(&audio_file_table_desc, &audio_file_table)); if (ret >= 0) { unsigned num; osl_get_num_rows(audio_file_table, &num); @@ -2568,7 +2561,7 @@ static int aft_open(const char *dir) } PARA_INFO_LOG("failed to open audio file table\n"); audio_file_table = NULL; - if (ret >= 0 || is_errno(-ret, ENOENT)) + if (ret >= 0 || ret == -OSL_ERRNO_TO_PARA_ERROR(E_OSL_NOENT)) return 1; return ret; } @@ -2576,7 +2569,7 @@ static int aft_open(const char *dir) static int aft_create(const char *dir) { audio_file_table_desc.dir = dir; - return osl_create_table(&audio_file_table_desc); + return osl(osl_create_table(&audio_file_table_desc)); } static int clear_attribute(struct osl_row *row, void *data) diff --git a/alsa_write.c b/alsa_write.c index cf0f335b..3c7c03b4 100644 --- a/alsa_write.c +++ b/alsa_write.c @@ -12,6 +12,7 @@ * based on the vplay program by Michael Beck. */ +#include #include #include #include diff --git a/amp_filter.c b/amp_filter.c index a011c411..63b1b9a6 100644 --- a/amp_filter.c +++ b/amp_filter.c @@ -6,6 +6,8 @@ /** \file amp_filter.c Paraslash's amplify filter. */ +#include + #include "para.h" #include "amp_filter.cmdline.h" #include "list.h" @@ -76,11 +78,9 @@ static void amp_open(struct filter_node *fn) pad->conf = fn->conf; fn->private_data = pad; - if (!pad->conf->amp_given && stat_item_values[SI_AMPLIFICATION]) { - int i = SI_AMPLIFICATION; - char *s = stat_item_values[i] + strlen(status_item_list[i]) + 1; - sscanf(s, "%u", &pad->amp); - } else + if (!pad->conf->amp_given && stat_item_values[SI_AMPLIFICATION]) + sscanf(stat_item_values[SI_AMPLIFICATION], "%u", &pad->amp); + else pad->amp = pad->conf->amp_arg; fn->bufsize = AMP_CHUNK_SIZE; fn->buf = para_malloc(fn->bufsize); diff --git a/attribute.c b/attribute.c index 30c12f1c..b962661a 100644 --- a/attribute.c +++ b/attribute.c @@ -5,8 +5,14 @@ */ /** \file attribute.c Attribute handling functions. */ + +#include +#include +#include + #include "para.h" #include "error.h" +#include "crypt.h" #include "string.h" #include "afh.h" #include "afs.h" @@ -89,11 +95,11 @@ int get_attribute_bitnum_by_name(const char *att_name, unsigned char *bitnum) struct osl_object obj = {.data = (char *)att_name, .size = strlen(att_name) + 1}; struct osl_row *row; - int ret = osl_get_row(attribute_table, ATTCOL_NAME, &obj, &row); + int ret = osl(osl_get_row(attribute_table, ATTCOL_NAME, &obj, &row)); if (ret < 0) return ret; - ret = osl_get_object(attribute_table, row, ATTCOL_BITNUM, &obj); + ret = osl(osl_get_object(attribute_table, row, ATTCOL_BITNUM, &obj)); if (ret < 0) return ret; *bitnum = *(unsigned char *)obj.data; @@ -131,7 +137,7 @@ static int print_attribute(struct osl_table *table, struct osl_row *row, if (!(laad->flags & LSATT_FLAG_LONG)) return para_printf(&laad->pb, "%s\n", name); - ret = osl_get_object(table, row, ATTCOL_BITNUM, &bitnum_obj); + ret = osl(osl_get_object(table, row, ATTCOL_BITNUM, &bitnum_obj)); if (ret < 0) { para_printf(&laad->pb, "%s: %s\n", name, para_strerror(-ret)); return ret; @@ -171,7 +177,7 @@ static void com_lsatt_callback(int fd, const struct osl_object *query) free(laad.pb.buf); } -int com_lsatt(int fd, int argc, char * const * const argv) +int com_lsatt(struct rc4_context *rc4c, int argc, char * const * const argv) { unsigned flags = 0; struct osl_object options = {.data = &flags, .size = sizeof(flags)}; @@ -199,12 +205,12 @@ int com_lsatt(int fd, int argc, char * const * const argv) } } ret = send_option_arg_callback_request(&options, argc - i, argv + i, - com_lsatt_callback, send_result, &fd); + com_lsatt_callback, rc4_send_result, rc4c); if (!ret) { if (argc > 1) - ret = send_va_buffer(fd, "no matches\n"); + ret = rc4_send_va_buffer(rc4c, "no matches\n"); } else if (ret < 0) - send_va_buffer(fd, "%s\n", para_strerror(-ret)); + rc4_send_va_buffer(rc4c, "%s\n", para_strerror(-ret)); return ret; } @@ -230,11 +236,11 @@ static void com_setatt_callback(__a_unused int fd, const struct osl_object *quer p[len - 1] = '\0'; obj.data = p; obj.size = len + 1; - ret = osl_get_row(attribute_table, ATTCOL_NAME, &obj, &row); + ret = osl(osl_get_row(attribute_table, ATTCOL_NAME, &obj, &row)); if (ret < 0) goto out; - ret = osl_get_object(attribute_table, row, ATTCOL_BITNUM, - &obj); + ret = osl(osl_get_object(attribute_table, row, ATTCOL_BITNUM, + &obj)); if (ret < 0) goto out; if (c == '+') @@ -272,7 +278,7 @@ out: PARA_NOTICE_LOG("%s\n", para_strerror(-ret)); } -int com_setatt(__a_unused int fd, int argc, char * const * const argv) +int com_setatt(__a_unused struct rc4_context *rc4c, int argc, char * const * const argv) { if (argc < 3) return -E_ATTR_SYNTAX; @@ -317,15 +323,15 @@ static void com_addatt_callback(int fd, const struct osl_object *query) goto out; continue; } - if (ret != -E_RB_KEY_NOT_FOUND) /* error */ + if (ret != -OSL_ERRNO_TO_PARA_ERROR(E_OSL_RB_KEY_NOT_FOUND)) /* error */ goto out; objs[ATTCOL_BITNUM].size = 1; /* find smallest unused attribute */ for (bitnum = 0; bitnum < 64; bitnum++) { objs[ATTCOL_BITNUM].data = &bitnum; - ret = osl_get_row(attribute_table, ATTCOL_BITNUM, - &objs[ATTCOL_BITNUM], &row); - if (ret == -E_RB_KEY_NOT_FOUND) + ret = osl(osl_get_row(attribute_table, ATTCOL_BITNUM, + &objs[ATTCOL_BITNUM], &row)); + if (ret == -OSL_ERRNO_TO_PARA_ERROR(E_OSL_RB_KEY_NOT_FOUND)) break; /* this bitnum is unused, use it */ if (ret < 0) /* error */ goto out; @@ -337,7 +343,7 @@ static void com_addatt_callback(int fd, const struct osl_object *query) } objs[ATTCOL_NAME].data = p; objs[ATTCOL_NAME].size = len + 1; - ret = osl_add_row(attribute_table, objs); + ret = osl(osl_add_row(attribute_table, objs)); if (ret < 0) goto out; aed.name = p; @@ -353,16 +359,16 @@ out: free(pb.buf); } -int com_addatt(int fd, int argc, char * const * const argv) +int com_addatt(struct rc4_context *rc4c, int argc, char * const * const argv) { int ret; if (argc < 2) return -E_ATTR_SYNTAX; ret = send_standard_callback_request(argc - 1, argv + 1, com_addatt_callback, - send_result, &fd); + rc4_send_result, rc4c); if (ret < 0) - send_va_buffer(fd, "%s\n", para_strerror(-ret)); + rc4_send_va_buffer(rc4c, "%s\n", para_strerror(-ret)); return ret; } @@ -380,12 +386,12 @@ static void com_mvatt_callback(int fd, const struct osl_object *query) }; int ret; - ret = osl_get_row(attribute_table, ATTCOL_NAME, &obj, &row); + ret = osl(osl_get_row(attribute_table, ATTCOL_NAME, &obj, &row)); if (ret < 0) goto out; obj.data = new; obj.size = strlen(new) + 1; - ret = osl_update_object(attribute_table, row, ATTCOL_NAME, &obj); + ret = osl(osl_update_object(attribute_table, row, ATTCOL_NAME, &obj)); out: if (ret < 0) para_printf(&pb, "%s\n", para_strerror(-ret)); @@ -396,16 +402,16 @@ out: free(pb.buf); } -int com_mvatt(int fd, int argc, char * const * const argv) +int com_mvatt(struct rc4_context *rc4c, int argc, char * const * const argv) { int ret; if (argc != 3) return -E_ATTR_SYNTAX; ret = send_standard_callback_request(argc - 1, argv + 1, com_mvatt_callback, - send_result, &fd); + rc4_send_result, rc4c); if (ret < 0) - send_va_buffer(fd, "%s\n", para_strerror(-ret)); + rc4_send_va_buffer(rc4c, "%s\n", para_strerror(-ret)); return ret; } @@ -429,7 +435,7 @@ static int remove_attribute(struct osl_table *table, struct osl_row *row, ret = get_attribute_bitnum_by_name(name, &red.bitnum); if (ret < 0) return para_printf(&raad->pb, "%s: %s\n", name, para_strerror(-ret)); - ret = osl_del_row(table, row); + ret = osl(osl_del_row(table, row)); if (ret < 0) return para_printf(&raad->pb, "%s: %s\n", name, para_strerror(-ret)); ret = para_printf(&raad->pb, "removed attribute %s\n", name); @@ -468,16 +474,16 @@ static void com_rmatt_callback(int fd, const struct osl_object *query) free(raad.pb.buf); } -int com_rmatt(int fd, int argc, char * const * const argv) +int com_rmatt(struct rc4_context *rc4c, int argc, char * const * const argv) { int ret; if (argc < 2) return -E_ATTR_SYNTAX; ret = send_standard_callback_request(argc - 1, argv + 1, com_rmatt_callback, - send_result, &fd); + rc4_send_result, rc4c); if (ret < 0) - send_va_buffer(fd, "%s\n", para_strerror(-ret)); + rc4_send_va_buffer(rc4c, "%s\n", para_strerror(-ret)); return ret; } @@ -533,10 +539,10 @@ int get_attribute_text(uint64_t *atts, const char *delim, char **text) if (!(*atts & (one << i))) continue; - ret = osl_get_row(attribute_table, ATTCOL_BITNUM, &obj, &row); + ret = osl(osl_get_row(attribute_table, ATTCOL_BITNUM, &obj, &row)); if (ret < 0) goto err; - ret = osl_get_object(attribute_table, row, ATTCOL_NAME, &obj); + ret = osl(osl_get_object(attribute_table, row, ATTCOL_NAME, &obj)); if (ret < 0) goto err; if (*text) { @@ -579,14 +585,14 @@ static int attribute_open(const char *dir) int ret; attribute_table_desc.dir = dir; - ret = osl_open_table(&attribute_table_desc, &attribute_table); + ret = osl(osl_open_table(&attribute_table_desc, &attribute_table)); greatest_att_bitnum = -1; /* no atts available */ if (ret >= 0) { find_greatest_att_bitnum(); return ret; } attribute_table = NULL; - if (ret >= 0 || is_errno(-ret, ENOENT)) + if (ret >= 0 || ret == -OSL_ERRNO_TO_PARA_ERROR(E_OSL_NOENT)) return 1; return ret; } @@ -594,7 +600,7 @@ static int attribute_open(const char *dir) static int attribute_create(const char *dir) { attribute_table_desc.dir = dir; - return osl_create_table(&attribute_table_desc); + return osl(osl_create_table(&attribute_table_desc)); } /** diff --git a/audioc.c b/audioc.c index 5c081da5..81e5d1c5 100644 --- a/audioc.c +++ b/audioc.c @@ -6,6 +6,7 @@ /** \file audioc.c the client program used to connect to para_audiod */ +#include #include #include diff --git a/audiod.c b/audiod.c index eb3b200a..32c83941 100644 --- a/audiod.c +++ b/audiod.c @@ -5,19 +5,21 @@ */ /** \file audiod.c the paraslash's audio daemon */ +#include #include #include #include +#include #include "para.h" #include "error.h" +#include "crypt.h" #include "audiod.cmdline.h" #include "list.h" #include "sched.h" #include "ggo.h" #include "recv.h" #include "filter.h" -#include "grab_client.cmdline.h" #include "grab_client.h" #include "client.cmdline.h" #include "client.h" @@ -160,7 +162,7 @@ struct command_task { * \return The audio format number on success, -E_UNSUPPORTED_AUDIO_FORMAT if * \a name is not a supported audio format. */ -int get_audio_format_num(char *name) +int get_audio_format_num(const char *name) { int i; @@ -219,8 +221,7 @@ out: seconds = PARA_MIN(seconds, length); seconds = PARA_MAX(seconds, 0); return make_message( - "%s: %s%d:%02d [%d:%02d] (%d%%/%d:%02d)\n", - status_item_list[SI_PLAY_TIME], + "%s%d:%02d [%d:%02d] (%d%%/%d:%02d)", s? "" : "~", seconds / 60, seconds % 60, @@ -231,7 +232,7 @@ out: length % 60 ); empty: - return make_message("%s:\n", status_item_list[SI_PLAY_TIME]); + return para_strdup(NULL); } static int want_colors(void) @@ -444,7 +445,7 @@ static void open_writers(int slot_num) stat_task->server_stream_start : *now; s->offset_seconds = stat_task->offset_seconds; s->seconds_total = stat_task->length_seconds; - activate_inactive_grab_clients(slot_num, s->format, s->fc); + activate_inactive_grab_clients(s->format, s->fc); } static int open_receiver(int format) @@ -578,45 +579,31 @@ out: return count; } -static int check_stat_line(char *line, __a_unused void *data) +static int update_item(int itemnum, char *buf) { - int itemnum; - size_t ilen = 0; long unsigned sec, usec; - char *tmp; - //PARA_INFO_LOG("line: %s\n", line); - if (!line) - return 1; - itemnum = stat_line_valid(line); - if (itemnum < 0) { - PARA_WARNING_LOG("invalid status line: %s\n", line); - return 1; - } if (stat_task->clock_diff_count && itemnum != SI_CURRENT_TIME) return 1; - tmp = make_message("%s\n", line); - stat_client_write(tmp, itemnum); - free(tmp); free(stat_item_values[itemnum]); - stat_item_values[itemnum] = para_strdup(line); - ilen = strlen(status_item_list[itemnum]); + stat_item_values[itemnum] = para_strdup(buf); + stat_client_write_item(itemnum); switch (itemnum) { case SI_STATUS_FLAGS: stat_task->vss_status = 0; - if (strchr(line, 'N')) + if (strchr(buf, 'N')) stat_task->vss_status |= VSS_STATUS_FLAG_NEXT; - if (strchr(line, 'P')) + if (strchr(buf, 'P')) stat_task->vss_status |= VSS_STATUS_FLAG_PLAYING; break; case SI_OFFSET: - stat_task->offset_seconds = atoi(line + ilen + 1); + stat_task->offset_seconds = atoi(buf); break; case SI_SECONDS_TOTAL: - stat_task->length_seconds = atoi(line + ilen + 1); + stat_task->length_seconds = atoi(buf); break; case SI_STREAM_START: - if (sscanf(line + ilen + 1, "%lu.%lu", &sec, &usec) == 2) { + if (sscanf(buf, "%lu.%lu", &sec, &usec) == 2) { struct timeval a_start, delay; delay.tv_sec = conf.stream_delay_arg / 1000; delay.tv_usec = (conf.stream_delay_arg % 1000) * 1000; @@ -634,14 +621,14 @@ static int check_stat_line(char *line, __a_unused void *data) } break; case SI_CURRENT_TIME: - if (sscanf(line + ilen + 1, "%lu.%lu", &sec, &usec) == 2) { + if (sscanf(buf, "%lu.%lu", &sec, &usec) == 2) { struct timeval tv = {sec, usec}; compute_time_diff(&tv); } break; case SI_FORMAT: - stat_task->current_audio_format_num = get_audio_format_num( - line + ilen + 1); + stat_task->current_audio_format_num + = get_audio_format_num(buf); } return 1; } @@ -935,17 +922,11 @@ static void init_command_task(struct command_task *ct) static void close_stat_pipe(void) { - int i; - if (!stat_task->ct) return; client_close(stat_task->ct); stat_task->ct = NULL; - FOR_EACH_STATUS_ITEM(i) { - free(stat_item_values[i]); - stat_item_values[i] = NULL; - } - dump_empty_status(); + clear_and_dump_items(); stat_task->length_seconds = 0; stat_task->offset_seconds = 0; stat_task->vss_status = 0; @@ -1069,7 +1050,7 @@ static void status_pre_select(struct sched *s, struct task *t) goto out; } if (st->ct) { - unsigned bytes_left; + int ret; if (st->ct->task.error < 0) { if (st->ct->task.error != -E_TASK_UNREGISTERED) goto out; @@ -1078,11 +1059,15 @@ static void status_pre_select(struct sched *s, struct task *t) } if (st->ct->status != CL_RECEIVING) goto out; - bytes_left = for_each_line(st->ct->buf, st->ct->loaded, - &check_stat_line, NULL); - if (st->ct->loaded != bytes_left) { + ret = for_each_stat_item(st->ct->buf, st->ct->loaded, + update_item); + if (ret < 0) { + st->ct->task.error = ret; + goto out; + } + if (st->ct->loaded != ret) { st->last_status_read = *now; - st->ct->loaded = bytes_left; + st->ct->loaded = ret; } else { struct timeval diff; tv_diff(now, &st->last_status_read, &diff); @@ -1094,25 +1079,23 @@ static void status_pre_select(struct sched *s, struct task *t) if (tv_diff(now, &st->restart_barrier, NULL) < 0) goto out; if (st->clock_diff_count) { /* get status only one time */ - char *argv[] = {"audiod", "stat", "1", NULL}; - int argc = 3; + char *argv[] = {"audiod", "--", "stat", "-p", "1", NULL}; + int argc = 5; PARA_INFO_LOG("clock diff count: %d\n", st->clock_diff_count); st->clock_diff_count--; client_open(argc, argv, &st->ct, NULL); set_stat_task_restart_barrier(2); } else { - char *argv[] = {"audiod", "stat", NULL}; - int argc = 2; + char *argv[] = {"audiod", "--", "stat", "-p", NULL}; + int argc = 4; client_open(argc, argv, &st->ct, NULL); set_stat_task_restart_barrier(5); } free(stat_item_values[SI_BASENAME]); - stat_item_values[SI_BASENAME] = make_message( - "%s: no connection to para_server\n", - status_item_list[SI_BASENAME]); - stat_client_write(stat_item_values[SI_BASENAME], - SI_BASENAME); + stat_item_values[SI_BASENAME] = para_strdup( + "no connection to para_server"); + stat_client_write_item(SI_BASENAME); st->last_status_read = *now; out: start_stop_decoders(s); diff --git a/audiod.cmd b/audiod.cmd index 5469476c..94f3ca04 100644 --- a/audiod.cmd +++ b/audiod.cmd @@ -14,11 +14,36 @@ H: on -> standby -> off -> on N: grab D: grab the audio stream L: -U: -- grab [grab_options] +U: -- grab -[n=] [-m[{s|p|a}]] [-i] [-o] [-f=] +H: H: grab ('splice') the audio stream at any position in the filter -H: chain and send that data back to the client. Try -H: para_audioc -- grab -h -H: for the list of available options. +H: chain and send that data back to the client. +H: +H: Options: +H: +H: -n Point of the filter chain to grab. Filters count from zero. +H: +H: -m Change grab mode. Defaults to sloppy grab if not given. +H: +H: -ms: sloppy grab +H: +H: -mp: pedantic grab +H: +H: -ma: aggressive grab +H: +H: The various grab modes only differ in what happens if the +H: file descriptor to write the grabbed audio data to is not +H: ready for writing (i.e. would block). Sloppy mode ignores +H: the write, pedantic mode aborts and aggressive mode tries +H: to write anyway. +H: +H: -i Grab the filter input instead of its output. +H: +H: -o One-shot mode: Stop grabbing if audio file changes. +H: +H: -f Only grab streams of this format (mp3, ogg, aac). The default is to +H: grab any stream. +H: --- N: help D: display command list or help for given command @@ -51,8 +76,9 @@ H: Stop all decoders but leave connection to para_server open. --- N: stat D: print status information -U: stat [item1 ...] -H: Dump given status items (all if none given) to stdout. +U: stat [-p] [item1 ...] +H: Dump given status items (all if none given) to stdout. If -p is given, use +H: parser-friendly mode. --- N: tasks D: list current tasks diff --git a/audiod.h b/audiod.h index 8b416ce5..18bf48ed 100644 --- a/audiod.h +++ b/audiod.h @@ -8,7 +8,7 @@ int num_filters(int audio_format_num); -int get_audio_format_num(char *name); +int get_audio_format_num(const char *name); /** enum of audio formats supported by para_audiod */ enum {AUDIOD_AUDIO_FORMATS_ENUM}; @@ -34,12 +34,6 @@ struct audiod_command { const char *name; /** pointer to the function that handles the command */ int (*handler)(int, int, char**); - /** - * if the command prefers to handle the full line (rather than the usual - * argv[] array), it stores a pointer to the corresponding line handling - * function here. In this case, the above \a handler pointer must be NULL. - */ - int (*line_handler)(int, char*); /** one-line description of the command */ const char *description; /** summary of the command line options */ @@ -82,7 +76,6 @@ extern int audiod_status; void __noreturn clean_exit(int status, const char *msg); int handle_connect(int accept_fd); void audiod_status_dump(void); -void dump_empty_status(void); char *get_time_string(int slot_num); /** iterate over all slots */ diff --git a/audiod_command.c b/audiod_command.c index ca1bff5b..b3312940 100644 --- a/audiod_command.c +++ b/audiod_command.c @@ -6,6 +6,7 @@ /** \file audiod_command.c commands for para_audiod */ +#include #include #include @@ -16,9 +17,7 @@ #include "sched.h" #include "ggo.h" #include "filter.h" -#include "grab_client.cmdline.h" #include "grab_client.h" - #include "error.h" #include "audiod.h" #include "net.h" @@ -35,7 +34,11 @@ extern char *stat_item_values[NUM_STAT_ITEMS]; static int client_write(int fd, const char *buf) { - size_t len = strlen(buf); + size_t len; + + if (!buf) + return 0; + len = strlen(buf); return write(fd, buf, len) != len? -E_CLIENT_WRITE: 1; } @@ -43,7 +46,7 @@ __malloc static char *audiod_status_string(void) { const char *status = (audiod_status == AUDIOD_ON)? "on" : (audiod_status == AUDIOD_OFF)? "off": "sb"; - return make_message("%s: %s\n", status_item_list[SI_AUDIOD_STATUS], status); + return para_strdup(status); } static int get_play_time_slot_num(void) @@ -72,13 +75,14 @@ __malloc static char *decoder_flags(void) char flag = '0'; if (s->receiver_node) flag += 1; - if (s->wng) + if (s->fc) flag += 2; + if (s->wng) + flag += 4; flags[i] = flag; } flags[MAX_STREAM_SLOTS] = '\0'; - return make_message("%s: %s\n", status_item_list[SI_DECODER_FLAGS], - flags); + return para_strdup(flags); } static int dump_commands(int fd) @@ -168,128 +172,50 @@ int com_kill(int fd, int argc, char **argv) int com_stat(int fd, int argc, char **argv) { - int i, ret; - char *buf = NULL; + int i, ret, parser_friendly = 0; uint64_t mask = 0; const uint64_t one = 1; + struct para_buffer b = {.flags = 0}; - if (argc > 1) { - for (i = 1; i < argc; i++) { - ret = stat_item_valid(argv[i]); - if (ret < 0) - return ret; - mask |= (one << ret); + for (i = 1; i < argc; i++) { + const char *arg = argv[i]; + if (arg[0] != '-') + break; + if (!strcmp(arg, "--")) { + i++; + break; } - } else - mask--; /* set all bits */ - PARA_INFO_LOG("mask: 0x%llx\n", (long long unsigned)mask); - if (mask & (one << SI_PLAY_TIME)) { - int slot_num = get_play_time_slot_num(); - char *ts = get_time_string(slot_num); - if (ts) { - ret = client_write(fd, ts); - if (ret < 0) - goto out; - free(ts); + if (!strncmp(arg, "-p", 2)) { + parser_friendly = 1; + b.flags = PBF_SIZE_PREFIX; + continue; } } - if (mask & (one << SI_AUDIOD_UPTIME)) { - char *tmp, *us = uptime_str(); - tmp = make_message("%s: %s\n", - status_item_list[SI_AUDIOD_UPTIME], us); - free(us); - ret = client_write(fd, tmp); - if (ret < 0) - goto out; - free(tmp); - } - if (mask & (one << SI_AUDIOD_STATUS)) { - char *s = audiod_status_string(); - ret = client_write(fd, s); - if (ret < 0) - goto out; - free(s); - } - if (mask & (one << SI_DECODER_FLAGS)) { - char *df = decoder_flags(); - ret = client_write(fd, df); + if (i >= argc) + mask--; /* set all bits */ + for (; i < argc; i++) { + ret = stat_item_valid(argv[i]); if (ret < 0) - goto out; - free(df); + return ret; + mask |= (one << ret); } + PARA_INFO_LOG("mask: 0x%llx\n", (long long unsigned)mask); FOR_EACH_STATUS_ITEM(i) { - char *tmp, *v; + char *item = stat_item_values[i]; if (!((one << i) & mask)) continue; - v = stat_item_values[i]; - tmp = make_message("%s%s%s", buf? buf: "", - v? v : "", v? "\n" : ""); - free(buf); - buf = tmp; + WRITE_STATUS_ITEM(&b, i, "%s\n", item? item : ""); } - ret = client_write(fd, buf); -out: - if (ret > 0) - ret = stat_client_add(fd, mask); - free(buf); + ret = client_write(fd, b.buf); + if (ret >= 0) + ret = stat_client_add(fd, mask, parser_friendly); + free(b.buf); return ret; } -static struct filter_node *find_filter_node(int slot_num, int format, int filternum) +int com_grab(int fd, int argc, char **argv) { - int i; - - FOR_EACH_SLOT(i) { - struct slot_info *s = &slot[i]; - if (s->format < 0 || !s->fc) - continue; - if (slot_num >= 0 && slot_num != i) - continue; - if (format >= 0 && s->format != format) - continue; - if (num_filters(i) <= filternum) - continue; - /* success */ - return s->fc->filter_nodes + filternum; - } - return NULL; -} - -int com_grab(int fd, char *cmdline) -{ - struct grab_client *gc; - struct filter_node *fn; - int i, err; - char *msg; - - gc = grab_client_new(fd, cmdline, &err); - if (!gc) - goto err_out; - fn = find_filter_node(gc->conf->slot_arg, gc->audio_format_num, gc->conf->filter_num_arg); - if (fn) - activate_grab_client(gc, fn); - return 1; -err_out: - if (err != -E_GC_HELP_GIVEN && err != -E_GC_VERSION_GIVEN) - return err; - if (err == -E_GC_HELP_GIVEN) { - msg = make_message("%s\n\n", grab_client_args_info_usage); - for (i = 0; grab_client_args_info_help[i]; i++) { - char *tmp = make_message("%s%s\n", msg, - grab_client_args_info_help[i]); - free(msg); - msg = tmp; - } - } else - msg = make_message("%s %s\n", - GRAB_CLIENT_CMDLINE_PARSER_PACKAGE, - GRAB_CLIENT_CMDLINE_PARSER_VERSION); - err = client_write(fd, msg); - free(msg); - if (err < 0) - return err; - close(fd); - return 1; + return grab_client_new(fd, argc, argv); } __noreturn int com_term(int fd, __a_unused int argc, __a_unused char **argv) @@ -366,7 +292,7 @@ static int check_perms(uid_t uid) int handle_connect(int accept_fd) { int i, argc, ret, clifd = -1; - char *cmd = NULL, *p, *buf = para_calloc(MAXLINE), **argv = NULL; + char buf[MAXLINE], **argv = NULL; struct sockaddr_un unix_addr; uid_t uid; @@ -374,7 +300,7 @@ int handle_connect(int accept_fd) if (ret < 0) goto out; clifd = ret; - ret = recv_cred_buffer(clifd, buf, MAXLINE - 1); + ret = recv_cred_buffer(clifd, buf, sizeof(buf) - 1); if (ret < 0) goto out; uid = ret; @@ -382,36 +308,20 @@ int handle_connect(int accept_fd) ret = check_perms(uid); if (ret < 0) goto out; - cmd = para_strdup(buf); - p = strchr(cmd, '\n'); - if (!p) - p = ""; - else { - *p = '\0'; - p++; - } - for (i = 0; audiod_cmds[i].name; i++) { - int j; - if (strcmp(audiod_cmds[i].name, cmd)) + ret = create_argv(buf, "\n", &argv); + if (ret < 0) + goto out; + argc = ret; + //PARA_INFO_LOG("argv[0]: %s, argc = %d\n", argv[0], argc); + FOR_EACH_COMMAND(i) { + if (strcmp(audiod_cmds[i].name, argv[0])) continue; - if (audiod_cmds[i].handler) { - argc = split_args(buf, &argv, "\n"); - PARA_INFO_LOG("argv[0]: %s, argc= %d\n", argv[0], argc); - ret = audiod_cmds[i].handler(clifd, argc, argv); - goto out; - } - for (j = 0; p[j]; j++) - if (p[j] == '\n') - p[j] = ' '; - PARA_INFO_LOG("cmd: %s, options: %s\n", cmd, p); - ret = audiod_cmds[i].line_handler(clifd, p); + ret = audiod_cmds[i].handler(clifd, argc, argv); goto out; } ret = -E_INVALID_AUDIOD_CMD; out: - free(cmd); - free(buf); - free(argv); + free_argv(argv); if (clifd > 0 && ret < 0 && ret != -E_CLIENT_WRITE) { char *tmp = make_message("%s\n", para_strerror(-ret)); client_write(clifd, tmp); @@ -420,21 +330,22 @@ out: } return ret; } + /** - * send the current audiod status to all connected stat clients + * Send the current audiod status to all connected stat clients. */ void audiod_status_dump(void) { int slot_num = get_play_time_slot_num(); - char *old, *new, *tmp; + char *old, *new; old = stat_item_values[SI_PLAY_TIME]; new = get_time_string(slot_num); if (new) { if (!old || strcmp(old, new)) { free(old); - stat_client_write(new, SI_PLAY_TIME); stat_item_values[SI_PLAY_TIME] = new; + stat_client_write_item(SI_PLAY_TIME); } else free(new); } @@ -443,11 +354,8 @@ void audiod_status_dump(void) old = stat_item_values[SI_AUDIOD_UPTIME]; if (!old || strcmp(old, new)) { free(old); - tmp = make_message("%s: %s\n", - status_item_list[SI_AUDIOD_UPTIME], new); - stat_client_write(tmp, SI_AUDIOD_UPTIME); - free(tmp); stat_item_values[SI_AUDIOD_UPTIME] = new; + stat_client_write_item(SI_AUDIOD_UPTIME); } else free(new); @@ -455,8 +363,8 @@ void audiod_status_dump(void) new = audiod_status_string(); if (!old || strcmp(old, new)) { free(old); - stat_client_write(new, SI_AUDIOD_STATUS); stat_item_values[SI_AUDIOD_STATUS] = new; + stat_client_write_item(SI_AUDIOD_STATUS); } else free(new); @@ -464,27 +372,25 @@ void audiod_status_dump(void) new = decoder_flags(); if (!old || strcmp(old, new)) { free(old); - stat_client_write(new, SI_DECODER_FLAGS); stat_item_values[SI_DECODER_FLAGS] = new; + stat_client_write_item(SI_DECODER_FLAGS); } else free(new); } /** - * send empty status list + * Flush and send all status items. * * Send to each connected client the full status item list * with empty values. */ -void dump_empty_status(void) +void clear_and_dump_items(void) { int i; FOR_EACH_STATUS_ITEM(i) { - char *tmp = make_message("%s:\n", status_item_list[i]); - stat_client_write(tmp, i); - free(tmp); free(stat_item_values[i]); stat_item_values[i] = NULL; + stat_client_write_item(i); } } diff --git a/blob.c b/blob.c index dbabba22..6f10f0a2 100644 --- a/blob.c +++ b/blob.c @@ -6,14 +6,42 @@ /** \file blob.c Macros and functions for blob handling. */ +#include #include +#include +#include + #include "para.h" #include "error.h" +#include "crypt.h" #include "string.h" #include "afh.h" #include "afs.h" #include "net.h" #include "ipc.h" +#include "portable_io.h" + +/** + * Compare two osl objects pointing to unsigned integers of 32 bit size. + * + * \param obj1 Pointer to the first integer. + * \param obj2 Pointer to the second integer. + * + * \return The values required for an osl compare function. + * + * \sa osl_compare_func, osl_hash_compare(). + */ +static int uint32_compare(const struct osl_object *obj1, const struct osl_object *obj2) +{ + uint32_t d1 = read_u32((const char *)obj1->data); + uint32_t d2 = read_u32((const char *)obj2->data); + + if (d1 < d2) + return 1; + if (d1 > d2) + return -1; + return 0; +} static struct osl_column_description blob_cols[] = { [BLOBCOL_ID] = { @@ -71,7 +99,7 @@ static int print_blob(struct osl_table *table, struct osl_row *row, if (!(lbad->flags & BLOB_LS_FLAG_LONG)) return para_printf(&lbad->pb, "%s\n", name); - ret = osl_get_object(table, row, BLOBCOL_ID, &obj); + ret = osl(osl_get_object(table, row, BLOBCOL_ID, &obj)); if (ret < 0) { para_printf(&lbad->pb, "%s: %s\n", name, para_strerror(-ret)); return ret; @@ -116,7 +144,7 @@ static void com_lsblob_callback(struct osl_table *table, free(lbad.pb.buf); } -static int com_lsblob(callback_function *f, int fd, int argc, char * const * const argv) +static int com_lsblob(callback_function *f, struct rc4_context *rc4c, int argc, char * const * const argv) { uint32_t flags = 0; struct osl_object options = {.data = &flags, .size = sizeof(flags)}; @@ -147,7 +175,7 @@ static int com_lsblob(callback_function *f, int fd, int argc, char * const * con // if (argc > i) // return -E_BLOB_SYNTAX; return send_option_arg_callback_request(&options, argc - i, - argv + i, f, send_result, &fd); + argv + i, f, rc4_send_result, rc4c); } static int cat_blob(struct osl_table *table, struct osl_row *row, @@ -156,12 +184,12 @@ static int cat_blob(struct osl_table *table, struct osl_row *row, int ret = 0, ret2; struct osl_object obj; - ret = osl_open_disk_object(table, row, BLOBCOL_DEF, &obj); + ret = osl(osl_open_disk_object(table, row, BLOBCOL_DEF, &obj)); if (ret < 0) return ret; if (obj.size) ret = pass_buffer_as_shm(obj.data, obj.size, data); - ret2 = osl_close_disk_object(&obj); + ret2 = osl(osl_close_disk_object(&obj)); return (ret < 0)? ret : ret2; } @@ -180,12 +208,13 @@ static void com_catblob_callback(struct osl_table *table, int fd, for_each_matching_row(&pmd); } -static int com_catblob(callback_function *f, int fd, int argc, +static int com_catblob(callback_function *f, struct rc4_context *rc4c, int argc, char * const * const argv) { if (argc < 2) return -E_BLOB_SYNTAX; - return send_standard_callback_request(argc - 1, argv + 1, f, send_result, &fd); + return send_standard_callback_request(argc - 1, argv + 1, f, + rc4_send_result, rc4c); } /** Used for removing rows from a blob table. */ @@ -200,7 +229,7 @@ static int remove_blob(struct osl_table *table, struct osl_row *row, const char *name, void *data) { struct rmblob_data *rmbd = data; - int ret = osl_del_row(table, row); + int ret = osl(osl_del_row(table, row)); if (ret < 0) { para_printf(&rmbd->pb, "%s: %s\n", name, para_strerror(-ret)); return ret; @@ -248,13 +277,13 @@ out: free(rmbd.pb.buf); } -static int com_rmblob(callback_function *f, int fd, int argc, +static int com_rmblob(callback_function *f, struct rc4_context *rc4c, int argc, char * const * const argv) { if (argc < 2) return -E_MOOD_SYNTAX; return send_option_arg_callback_request(NULL, argc - 1, argv + 1, f, - send_result, &fd); + rc4_send_result, rc4c); } static void com_addblob_callback(struct osl_table *table, __a_unused int fd, @@ -267,7 +296,7 @@ static void com_addblob_callback(struct osl_table *table, __a_unused int fd, unsigned num_rows; int ret; - ret = osl_get_num_rows(table, &num_rows); + ret = osl(osl_get_num_rows(table, &num_rows)); if (ret < 0) goto out; if (!num_rows) { /* this is the first entry ever added */ @@ -279,34 +308,34 @@ static void com_addblob_callback(struct osl_table *table, __a_unused int fd, objs[BLOBCOL_NAME].size = 1; objs[BLOBCOL_DEF].data = ""; objs[BLOBCOL_DEF].size = 1; - ret = osl_add_row(table, objs); + ret = osl(osl_add_row(table, objs)); if (ret < 0) goto out; } else { /* check if name already exists */ struct osl_row *row; struct osl_object obj = {.data = name, .size = name_len}; - ret = osl_get_row(table, BLOBCOL_NAME, &obj, &row); - if (ret < 0 && ret != -E_RB_KEY_NOT_FOUND) + ret = osl(osl_get_row(table, BLOBCOL_NAME, &obj, &row)); + 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 */ obj.data = name + name_len; obj.size = query->size - name_len; - ret = osl_update_object(table, row, BLOBCOL_DEF, &obj); + ret = osl(osl_update_object(table, row, BLOBCOL_DEF, &obj)); goto out; } /* new blob, get id of the dummy row and increment it */ obj.data = ""; obj.size = 1; - ret = osl_get_row(table, BLOBCOL_NAME, &obj, &row); + ret = osl(osl_get_row(table, BLOBCOL_NAME, &obj, &row)); if (ret < 0) goto out; - ret = osl_get_object(table, row, BLOBCOL_ID, &obj); + ret = osl(osl_get_object(table, row, BLOBCOL_ID, &obj)); if (ret < 0) goto out; id = *(uint32_t *)obj.data + 1; obj.data = &id; - ret = osl_update_object(table, row, BLOBCOL_ID, &obj); + ret = osl(osl_update_object(table, row, BLOBCOL_ID, &obj)); if (ret < 0) goto out; } @@ -317,7 +346,7 @@ static void com_addblob_callback(struct osl_table *table, __a_unused int fd, objs[BLOBCOL_NAME].size = name_len; objs[BLOBCOL_DEF].data = name + name_len; objs[BLOBCOL_DEF].size = query->size - name_len; - ret = osl_add_row(table, objs); + ret = osl(osl_add_row(table, objs)); if (ret < 0) goto out; afs_event(BLOB_ADD, NULL, table); @@ -326,7 +355,82 @@ out: PARA_NOTICE_LOG("%s\n", para_strerror(-ret)); } -static int com_addblob(callback_function *f, int fd, int argc, +/* + * write input from fd to dynamically allocated buffer, + * but maximal max_size byte. + */ +static int fd2buf(struct rc4_context *rc4c, unsigned max_size, struct osl_object *obj) +{ + const size_t chunk_size = 1024; + size_t size = 2048, received = 0; + int ret; + char *buf = para_malloc(size); + + for (;;) { + ret = rc4_recv_bin_buffer(rc4c, 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 = buf; + obj->size = received; + if (ret < 0) + free(buf); + return ret; +} + +/* + * Read data from a file descriptor, and send it to the afs process. + * + * \param rc4c crypt context containing 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 rc4c. 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. + * + * \return Negative on errors, the return value of the underlying call to + * send_callback_request() otherwise. + */ +static int stdin_command(struct rc4_context *rc4c, struct osl_object *arg_obj, + callback_function *f, unsigned max_len, + callback_result_handler *result_handler, + void *private_result_data) +{ + struct osl_object query, stdin_obj; + int ret; + + ret = rc4_send_buffer(rc4c, AWAITING_DATA_MSG); + if (ret < 0) + return ret; + ret = fd2buf(rc4c, max_len, &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); + free(stdin_obj.data); + ret = send_callback_request(f, &query, result_handler, private_result_data); + free(query.data); + return ret; +} + +static int com_addblob(callback_function *f, struct rc4_context *rc4c, int argc, char * const * const argv) { struct osl_object arg_obj; @@ -337,7 +441,7 @@ static int com_addblob(callback_function *f, int fd, int argc, return -E_BLOB_SYNTAX; arg_obj.size = strlen(argv[1]) + 1; arg_obj.data = (char *)argv[1]; - return stdin_command(fd, &arg_obj, f, 10 * 1024 * 1024, NULL, NULL); + return stdin_command(rc4c, &arg_obj, f, 10 * 1024 * 1024, NULL, NULL); } /* FIXME: Print output to client, not to log file */ @@ -348,13 +452,13 @@ static void com_mvblob_callback(struct osl_table *table, __a_unused int fd, struct osl_object obj = {.data = src, .size = strlen(src) + 1}; char *dest = src + obj.size; struct osl_row *row; - int ret = osl_get_row(table, BLOBCOL_NAME, &obj, &row); + int ret = osl(osl_get_row(table, BLOBCOL_NAME, &obj, &row)); if (ret < 0) goto out; obj.data = dest; obj.size = strlen(dest) + 1; - ret = osl_update_object(table, row, BLOBCOL_NAME, &obj); + ret = osl(osl_update_object(table, row, BLOBCOL_NAME, &obj)); if (ret < 0) goto out; afs_event(BLOB_RENAME, NULL, table); @@ -363,7 +467,7 @@ out: PARA_NOTICE_LOG("%s\n", para_strerror(-ret)); } -static int com_mvblob(callback_function *f, __a_unused int fd, +static int com_mvblob(callback_function *f, __a_unused struct rc4_context *rc4c, int argc, char * const * const argv) { if (argc != 3) @@ -377,9 +481,9 @@ static int com_mvblob(callback_function *f, __a_unused int fd, { \ return com_ ## cmd_name ## blob_callback(table_name ## _table, fd, query); \ } \ - int com_ ## cmd_name ## cmd_prefix(int fd, int argc, char * const * const argv) \ + int com_ ## cmd_name ## cmd_prefix(struct rc4_context *rc4c, int argc, char * const * const argv) \ { \ - return com_ ## cmd_name ## blob(com_ ## cmd_name ## cmd_prefix ## _callback, fd, argc, argv); \ + return com_ ## cmd_name ## blob(com_ ## cmd_name ## cmd_prefix ## _callback, rc4c, argc, argv); \ } static int blob_get_name_by_id(struct osl_table *table, uint32_t id, @@ -392,10 +496,10 @@ static int blob_get_name_by_id(struct osl_table *table, uint32_t id, *name = NULL; if (!id) return 1; - ret = osl_get_row(table, BLOBCOL_ID, &obj, &row); + ret = osl(osl_get_row(table, BLOBCOL_ID, &obj, &row)); if (ret < 0) return ret; - ret = osl_get_object(table, row, BLOBCOL_NAME, &obj); + ret = osl(osl_get_object(table, row, BLOBCOL_NAME, &obj)); if (ret < 0) return ret; *name = (char *)obj.data; @@ -420,10 +524,10 @@ static int blob_get_def_by_name(struct osl_table *table, char *name, def->data = NULL; if (!*name) return 1; - ret = osl_get_row(table, BLOBCOL_NAME, &obj, &row); + ret = osl(osl_get_row(table, BLOBCOL_NAME, &obj, &row)); if (ret < 0) return ret; - return osl_open_disk_object(table, row, BLOBCOL_DEF, def); + return osl(osl_open_disk_object(table, row, BLOBCOL_DEF, def)); } /** Define the \p get_def_by_id function for this blob type. */ @@ -443,10 +547,10 @@ static int blob_get_def_by_id(struct osl_table *table, uint32_t id, def->data = NULL; if (!id) return 1; - ret = osl_get_row(table, BLOBCOL_ID, &obj, &row); + ret = osl(osl_get_row(table, BLOBCOL_ID, &obj, &row)); if (ret < 0) return ret; - return osl_open_disk_object(table, row, BLOBCOL_DEF, def); + return osl(osl_open_disk_object(table, row, BLOBCOL_DEF, def)); } /** Define the \p get_def_by_id function for this blob type. */ @@ -460,11 +564,11 @@ static int blob_get_name_and_def_by_row(struct osl_table *table, const struct osl_row *row, char **name, struct osl_object *def) { struct osl_object obj; - int ret = osl_get_object(table, row, BLOBCOL_NAME, &obj); + int ret = osl(osl_get_object(table, row, BLOBCOL_NAME, &obj)); if (ret < 0) return ret; *name = obj.data; - return osl_open_disk_object(table, row, BLOBCOL_DEF, def); + return osl(osl_open_disk_object(table, row, BLOBCOL_DEF, def)); } /** Define the \p get_name_and_def_by_row function for this blob type. */ #define DEFINE_GET_NAME_AND_DEF_BY_ROW(table_name, cmd_prefix) \ @@ -497,11 +601,11 @@ static int blob_open(struct osl_table **table, { int ret; desc->dir = dir; - ret = osl_open_table(desc, table); + ret = osl(osl_open_table(desc, table)); if (ret >= 0) return ret; *table = NULL; - if (ret >= 0 || is_errno(-ret, ENOENT)) + if (ret >= 0 || ret == -OSL_ERRNO_TO_PARA_ERROR(E_OSL_NOENT)) return 1; return ret; } diff --git a/chunk_queue.c b/chunk_queue.c index fa317185..bb97c690 100644 --- a/chunk_queue.c +++ b/chunk_queue.c @@ -6,6 +6,8 @@ /** \file chunk_queue.c Queuing functions for paraslash senders. */ +#include + #include "para.h" #include "list.h" #include "afh.h" diff --git a/client.c b/client.c index 003c1e60..b7c1644c 100644 --- a/client.c +++ b/client.c @@ -6,9 +6,13 @@ /** \file client.c the client program used to connect to para_server */ +#include +#include + #include "para.h" #include "list.h" #include "sched.h" +#include "crypt.h" #include "client.cmdline.h" #include "string.h" #include "stdin.h" diff --git a/client.h b/client.h index c6d5c75d..fa965c35 100644 --- a/client.h +++ b/client.h @@ -41,8 +41,8 @@ enum { struct client_task { /** the state of the connection */ int status; - /** the file descriptor */ - int fd; + /** The file descriptor and the rc4 keys. */ + struct rc4_context rc4c; /** the configuration (including the command) */ struct client_args_info conf; /** the config file for client options */ @@ -51,10 +51,6 @@ struct client_task { char *key_file; /** paraslash user name */ char *user; - /** session key for receiving data */ - RC4_KEY rc4_recv_key; - /** session key for sending data */ - RC4_KEY rc4_send_key; /** the client task structure */ struct task task; /** the buffer used for handshake and receiving */ diff --git a/client_common.c b/client_common.c index 5bce7fb4..eb053581 100644 --- a/client_common.c +++ b/client_common.c @@ -6,8 +6,10 @@ /** \file client_common.c Common functions of para_client and para_audiod. */ +#include #include #include +#include #include "para.h" #include "error.h" @@ -21,33 +23,7 @@ #include "string.h" #include "client.cmdline.h" #include "client.h" - -/* - * Rc4-encrypt data before sending. - * - * \param len The number of bytes to encrypt. - * \param indata Pointer to the input data of length \a len to be encrypted. - * \param outdata Result-pointer that holds the encrypted data. - * \param private_data Contains the rc4 key. - */ -static void rc4_send(unsigned long len, const unsigned char *indata, - unsigned char *outdata, void *private_data) -{ - struct client_task *ct = private_data; - RC4(&ct->rc4_send_key, len, indata, outdata); -} - -/* - * Rc4-decrypt received data. - * - * Parameters are identical to those of rc4_send. - */ -static void rc4_recv(unsigned long len, const unsigned char *indata, - unsigned char *outdata, void *private_data) -{ - struct client_task *ct = private_data; - RC4(&ct->rc4_recv_key, len, indata, outdata); -} +#include "hash.h" /** * Close the connection to para_server and free all resources. @@ -60,10 +36,8 @@ void client_close(struct client_task *ct) { if (!ct) return; - if (ct->fd >= 0) { - disable_crypt(ct->fd); - close(ct->fd); - } + if (ct->rc4c.fd >= 0) + close(ct->rc4c.fd); free(ct->buf); free(ct->user); free(ct->config_file); @@ -92,27 +66,27 @@ static void client_pre_select(struct sched *s, struct task *t) ct->check_r = 0; ct->check_w = 0; - if (ct->fd < 0) + if (ct->rc4c.fd < 0) return; switch (ct->status) { case CL_CONNECTED: case CL_SENT_AUTH: case CL_SENT_CH_RESPONSE: case CL_SENT_COMMAND: - para_fd_set(ct->fd, &s->rfds, &s->max_fileno); + para_fd_set(ct->rc4c.fd, &s->rfds, &s->max_fileno); ct->check_r = 1; return; case CL_RECEIVED_WELCOME: case CL_RECEIVED_CHALLENGE: case CL_RECEIVED_PROCEED: - para_fd_set(ct->fd, &s->wfds, &s->max_fileno); + para_fd_set(ct->rc4c.fd, &s->wfds, &s->max_fileno); ct->check_w = 1; return; case CL_RECEIVING: if (ct->loaded < CLIENT_BUFSIZE - 1) { - para_fd_set(ct->fd, &s->rfds, &s->max_fileno); + para_fd_set(ct->rc4c.fd, &s->rfds, &s->max_fileno); ct->check_r = 1; } return; @@ -121,7 +95,7 @@ static void client_pre_select(struct sched *s, struct task *t) return; if (*ct->in_loaded) { PARA_INFO_LOG("loaded: %zd\n", *ct->in_loaded); - para_fd_set(ct->fd, &s->wfds, &s->max_fileno); + para_fd_set(ct->rc4c.fd, &s->wfds, &s->max_fileno); ct->check_w = 1; } else { if (*ct->in_error) { @@ -136,14 +110,19 @@ static void client_pre_select(struct sched *s, struct task *t) static ssize_t client_recv_buffer(struct client_task *ct) { - ssize_t ret = recv_buffer(ct->fd, ct->buf + ct->loaded, - CLIENT_BUFSIZE - ct->loaded); + ssize_t ret; + + if (ct->status < CL_SENT_CH_RESPONSE) + ret = recv_buffer(ct->rc4c.fd, ct->buf + ct->loaded, + CLIENT_BUFSIZE - ct->loaded); + else + ret = rc4_recv_buffer(&ct->rc4c, ct->buf + ct->loaded, + CLIENT_BUFSIZE - ct->loaded); if (!ret) return -E_SERVER_EOF; if (ret > 0) ct->loaded += ret; return ret; - } /** @@ -162,82 +141,80 @@ static ssize_t client_recv_buffer(struct client_task *ct) static void client_post_select(struct sched *s, struct task *t) { struct client_task *ct = container_of(t, struct client_task, task); + unsigned char crypt_buf[1024]; t->error = 0; - if (ct->fd < 0) + if (ct->rc4c.fd < 0) return; if (!ct->check_r && !ct->check_w) return; - if (ct->check_r && !FD_ISSET(ct->fd, &s->rfds)) + if (ct->check_r && !FD_ISSET(ct->rc4c.fd, &s->rfds)) return; - if (ct->check_w && !FD_ISSET(ct->fd, &s->wfds)) + if (ct->check_w && !FD_ISSET(ct->rc4c.fd, &s->wfds)) return; switch (ct->status) { case CL_CONNECTED: /* receive welcome message */ t->error = client_recv_buffer(ct); - if (t->error > 0) - ct->status = CL_RECEIVED_WELCOME; + if (t->error < 0) + goto err; + ct->status = CL_RECEIVED_WELCOME; return; case CL_RECEIVED_WELCOME: /* send auth command */ - sprintf(ct->buf, "auth %s%s", ct->conf.plain_given? - "" : "rc4 ", ct->user); + sprintf(ct->buf, AUTH_REQUEST_MSG "%s", ct->user); PARA_INFO_LOG("--> %s\n", ct->buf); - t->error = send_buffer(ct->fd, ct->buf); - if (t->error >= 0) - ct->status = CL_SENT_AUTH; + t->error = send_buffer(ct->rc4c.fd, ct->buf); + if (t->error < 0) + goto err; + ct->status = CL_SENT_AUTH; return; - case CL_SENT_AUTH: /* receive challenge number */ + case CL_SENT_AUTH: /* receive challenge and rc4 keys */ ct->loaded = 0; t->error = client_recv_buffer(ct); if (t->error < 0) - return; - if (t->error != 64) { - t->error = -E_INVALID_CHALLENGE; - PARA_ERROR_LOG("received the following: %s\n", ct->buf); - return; - } - PARA_INFO_LOG("<-- [challenge]\n"); - /* decrypt challenge number */ - t->error = para_decrypt_challenge(ct->key_file, &ct->challenge_nr, - (unsigned char *) ct->buf, 64); - if (t->error > 0) - ct->status = CL_RECEIVED_CHALLENGE; + goto err; + PARA_INFO_LOG("<-- [challenge] (%d bytes)\n", t->error); + /* decrypt challenge/rc4 buffer */ + t->error = para_decrypt_buffer(ct->key_file, crypt_buf, + (unsigned char *)ct->buf, t->error); + if (t->error < 0) + goto err; + ct->status = CL_RECEIVED_CHALLENGE; + RC4_set_key(&ct->rc4c.send_key, RC4_KEY_LEN, + crypt_buf + CHALLENGE_SIZE); + RC4_set_key(&ct->rc4c.recv_key, RC4_KEY_LEN, + crypt_buf + CHALLENGE_SIZE + RC4_KEY_LEN); return; - case CL_RECEIVED_CHALLENGE: /* send decrypted challenge */ - PARA_INFO_LOG("--> %lu\n", ct->challenge_nr); - t->error = send_va_buffer(ct->fd, "%s%lu", CHALLENGE_RESPONSE_MSG, - ct->challenge_nr); - if (t->error > 0) - ct->status = CL_SENT_CH_RESPONSE; + case CL_RECEIVED_CHALLENGE: + { + unsigned char challenge_sha1[HASH_SIZE]; + /* send sha1 of decrypted challenge */ + sha1_hash((char *)crypt_buf, CHALLENGE_SIZE, challenge_sha1); + hash_to_asc(challenge_sha1, ct->buf); + PARA_INFO_LOG("--> %s\n", ct->buf); + t->error = send_bin_buffer(ct->rc4c.fd, (char *)challenge_sha1, + HASH_SIZE); + if (t->error < 0) + goto err; + ct->status = CL_SENT_CH_RESPONSE; return; + } case CL_SENT_CH_RESPONSE: /* read server response */ { size_t bytes_received; - unsigned char rc4_buf[2 * RC4_KEY_LEN] = ""; ct->loaded = 0; t->error = client_recv_buffer(ct); if (t->error < 0) - return; + goto err; bytes_received = t->error; - PARA_DEBUG_LOG("++++ server info ++++\n%s\n++++ end of server " - "info ++++\n", ct->buf); /* check if server has sent "Proceed" message */ t->error = -E_CLIENT_AUTH; + if (bytes_received < PROCEED_MSG_LEN) + goto err; if (!strstr(ct->buf, PROCEED_MSG)) - return; - t->error = 0; + goto err; ct->status = CL_RECEIVED_PROCEED; - if (bytes_received < PROCEED_MSG_LEN + 32) - return; - PARA_INFO_LOG("decrypting session key\n"); - t->error = para_decrypt_buffer(ct->key_file, rc4_buf, - (unsigned char *)ct->buf + PROCEED_MSG_LEN + 1, - bytes_received - PROCEED_MSG_LEN - 1); - if (t->error < 0) - return; - RC4_set_key(&ct->rc4_send_key, RC4_KEY_LEN, rc4_buf); - RC4_set_key(&ct->rc4_recv_key, RC4_KEY_LEN, rc4_buf + RC4_KEY_LEN); - enable_crypt(ct->fd, rc4_recv, rc4_send, ct); + t->error = 0; + return; } case CL_RECEIVED_PROCEED: /* concat args and send command */ { @@ -251,33 +228,40 @@ static void client_post_select(struct sched *s, struct task *t) } command = para_strcat(command, EOC_MSG "\n"); PARA_DEBUG_LOG("--> %s\n", command); - t->error = send_buffer(ct->fd, command); + t->error = rc4_send_buffer(&ct->rc4c, command); free(command); - if (t->error > 0) - ct->status = CL_SENT_COMMAND; + if (t->error < 0) + goto err; + ct->status = CL_SENT_COMMAND; return; } case CL_SENT_COMMAND: ct->loaded = 0; t->error = client_recv_buffer(ct); if (t->error < 0) - return; + goto err; if (strstr(ct->buf, AWAITING_DATA_MSG)) ct->status = CL_SENDING; else ct->status = CL_RECEIVING; return; - case CL_SENDING: /* FIXME: might block */ + case CL_SENDING: PARA_INFO_LOG("loaded: %zd\n", *ct->in_loaded); - t->error = send_bin_buffer(ct->fd, ct->inbuf, *ct->in_loaded); + t->error = rc4_send_bin_buffer(&ct->rc4c, ct->inbuf, + *ct->in_loaded); if (t->error < 0) - return; + goto err; *ct->in_loaded = 0; return; case CL_RECEIVING: t->error = client_recv_buffer(ct); + if (t->error < 0) + goto err; return; } +err: + if (t->error != -E_SERVER_EOF) + PARA_ERROR_LOG("%s\n", para_strerror(-t->error)); } /* connect to para_server and register the client task */ @@ -285,14 +269,14 @@ static int client_connect(struct client_task *ct) { int ret; - ct->fd = -1; + ct->rc4c.fd = -1; ret = makesock(AF_UNSPEC, IPPROTO_TCP, 0, ct->conf.hostname_arg, ct->conf.server_port_arg); if (ret < 0) return ret; - ct->fd = ret; + ct->rc4c.fd = ret; ct->status = CL_CONNECTED; - ret = mark_fd_nonblocking(ct->fd); + ret = mark_fd_nonblocking(ct->rc4c.fd); if (ret < 0) goto err_out; ct->task.pre_select = client_pre_select; @@ -301,8 +285,8 @@ static int client_connect(struct client_task *ct) register_task(&ct->task); return 1; err_out: - close(ct->fd); - ct->fd = -1; + close(ct->rc4c.fd); + ct->rc4c.fd = -1; return ret; } @@ -330,7 +314,7 @@ int client_open(int argc, char *argv[], struct client_task **ct_ptr, ct->buf = para_malloc(CLIENT_BUFSIZE); *ct_ptr = ct; - ct->fd = -1; + ct->rc4c.fd = -1; ret = -E_CLIENT_SYNTAX; if (client_cmdline_parser(argc, argv, &ct->conf)) goto out; diff --git a/close_on_fork.c b/close_on_fork.c index 378799c7..fb190646 100644 --- a/close_on_fork.c +++ b/close_on_fork.c @@ -5,6 +5,9 @@ */ /** \file close_on_fork.c Manage a list of fds that should be closed on fork. */ + +#include + #include "para.h" #include "list.h" #include "string.h" diff --git a/command.c b/command.c index e3b15a5a..afa1e667 100644 --- a/command.c +++ b/command.c @@ -6,14 +6,18 @@ /** \file command.c Client authentication and server commands. */ +#include #include #include #include #include #include +#include #include "para.h" #include "error.h" +#include "crypt.h" +#include "command.h" #include "server.cmdline.h" #include "string.h" #include "afh.h" @@ -36,13 +40,12 @@ /** Commands including options must be shorter than this. */ #define MAX_COMMAND_LEN 32768 -static RC4_KEY rc4_recv_key; -static RC4_KEY rc4_send_key; -static unsigned char rc4_buf[2 * RC4_KEY_LEN]; - extern int mmd_mutex; extern struct misc_meta_data *mmd; extern struct sender senders[]; +int send_afs_status(struct rc4_context *rc4c, int parser_friendly); + +const char *status_item_list[] = {STATUS_ITEM_ARRAY}; static void dummy(__a_unused int s) { @@ -102,14 +105,15 @@ static char *vss_get_status_flags(unsigned int flags) return msg; } -static char *get_status(struct misc_meta_data *nmmd) +static char *get_status(struct misc_meta_data *nmmd, int parser_friendly) { - char *ret, mtime[30] = ""; + char mtime[30] = ""; char *status, *flags; /* vss status info */ char *ut = uptime_str(); long offset = (nmmd->offset + 500) / 1000; struct timeval current_time; struct tm mtime_tm; + struct para_buffer b = {.flags = parser_friendly? PBF_SIZE_PREFIX : 0}; /* report real status */ status = vss_status_tohuman(nmmd->vss_status_flags); @@ -119,38 +123,22 @@ static char *get_status(struct misc_meta_data *nmmd) strftime(mtime, 29, "%b %d %Y", &mtime_tm); } gettimeofday(¤t_time, NULL); - ret = make_message( - "%s: %zu\n" /* file size */ - "%s: %s\n" /* mtime */ - "%s: %s\n" /* status */ - "%s: %s\n" /* status flags */ - "%s: %li\n" /* offset */ - "%s: %s\n" /* afs mode */ - "%s: %lu.%lu\n" /* stream start */ - "%s: %lu.%lu\n" /* current server time */ - "%s", /* afs status info */ - status_item_list[SI_FILE_SIZE], nmmd->size / 1024, - status_item_list[SI_MTIME], mtime, - status_item_list[SI_STATUS], status, - status_item_list[SI_STATUS_FLAGS], flags, - - status_item_list[SI_OFFSET], offset, - status_item_list[SI_AFS_MODE], mmd->afs_mode_string, - - status_item_list[SI_STREAM_START], - (long unsigned)nmmd->stream_start.tv_sec, - (long unsigned)nmmd->stream_start.tv_usec, - status_item_list[SI_CURRENT_TIME], - (long unsigned)current_time.tv_sec, - (long unsigned)current_time.tv_usec, - - nmmd->afd.verbose_ls_output - - ); + WRITE_STATUS_ITEM(&b, SI_FILE_SIZE, "%zu\n", nmmd->size / 1024); + WRITE_STATUS_ITEM(&b, SI_MTIME, "%s\n", mtime); + WRITE_STATUS_ITEM(&b, SI_STATUS, "%s\n", status); + WRITE_STATUS_ITEM(&b, SI_STATUS_FLAGS, "%s\n", flags); + WRITE_STATUS_ITEM(&b, SI_OFFSET, "%li\n", offset); + WRITE_STATUS_ITEM(&b, SI_AFS_MODE, "%s\n", mmd->afs_mode_string); + WRITE_STATUS_ITEM(&b, SI_STREAM_START, "%lu.%lu\n", + (long unsigned)nmmd->stream_start.tv_sec, + (long unsigned)nmmd->stream_start.tv_usec); + WRITE_STATUS_ITEM(&b, SI_CURRENT_TIME, "%lu.%lu\n", + (long unsigned)current_time.tv_sec, + (long unsigned)current_time.tv_usec); free(flags); free(status); free(ut); - return ret; + return b.buf; } static int check_sender_args(int argc, char * const * argv, struct sender_command_data *scd) @@ -200,7 +188,7 @@ static int check_sender_args(int argc, char * const * argv, struct sender_comman return 1; } -int com_sender(int fd, int argc, char * const * argv) +int com_sender(struct rc4_context *rc4c, int argc, char * const * argv) { int i, ret; struct sender_command_data scd; @@ -213,7 +201,7 @@ int com_sender(int fd, int argc, char * const * argv) free(msg); msg = tmp; } - ret = send_buffer(fd, msg); + ret = rc4_send_buffer(rc4c, msg); free(msg); return ret; } @@ -223,7 +211,7 @@ int com_sender(int fd, int argc, char * const * argv) if (scd.sender_num < 0) return ret; msg = senders[scd.sender_num].help(); - ret = send_buffer(fd, msg); + ret = rc4_send_buffer(rc4c, msg); free(msg); return ret; } @@ -242,7 +230,7 @@ int com_sender(int fd, int argc, char * const * argv) } /* server info */ -int com_si(int fd, int argc, __a_unused char * const * argv) +int com_si(struct rc4_context *rc4c, int argc, __a_unused char * const * argv) { int i, ret; char *ut; @@ -259,7 +247,7 @@ int com_si(int fd, int argc, __a_unused char * const * argv) sender_list = para_strcat(sender_list, " "); } ut = uptime_str(); - ret = send_va_buffer(fd, "up: %s\nplayed: %u\n" + ret = rc4_send_va_buffer(rc4c, "up: %s\nplayed: %u\n" "server_pid: %d\n" "afs_pid: %d\n" "connections (active/accepted/total): %u/%u/%u\n" @@ -286,37 +274,120 @@ int com_si(int fd, int argc, __a_unused char * const * argv) } /* version */ -int com_version(int fd, int argc, __a_unused char * const * argv) +int com_version(struct rc4_context *rc4c, int argc, __a_unused char * const * argv) { if (argc != 1) return -E_COMMAND_SYNTAX; - return send_buffer(fd, VERSION_TEXT("server") + return rc4_send_buffer(rc4c, VERSION_TEXT("server") "built: " BUILD_DATE "\n" UNAME_RS ", " CC_VERSION "\n" ); } +#define EMPTY_STATUS_ITEMS \ + ITEM(PATH) \ + ITEM(DIRECTORY) \ + ITEM(BASENAME) \ + ITEM(SCORE) \ + ITEM(ATTRIBUTES_BITMAP) \ + ITEM(ATTRIBUTES_TXT) \ + ITEM(HASH) \ + ITEM(IMAGE_ID) \ + ITEM(IMAGE_NAME) \ + ITEM(LYRICS_ID) \ + ITEM(LYRICS_NAME) \ + ITEM(BITRATE) \ + ITEM(FORMAT) \ + ITEM(FREQUENCY) \ + ITEM(CHANNELS) \ + ITEM(DURATION) \ + ITEM(SECONDS_TOTAL) \ + ITEM(NUM_PLAYED) \ + ITEM(LAST_PLAYED) \ + ITEM(TECHINFO) \ + ITEM(ARTIST) \ + ITEM(TITLE) \ + ITEM(YEAR) \ + ITEM(ALBUM) \ + ITEM(COMMENT) \ + ITEM(AMPLIFICATION) + +/** + * Write a list of audio-file related status items with empty values. + * + * This is used by vss when currently no audio file is open. + */ +static char *empty_status_items(int parser_friendly) +{ + if (parser_friendly) + return make_message( + #define ITEM(x) "0004 %02x:\n" + EMPTY_STATUS_ITEMS + #undef ITEM + #define ITEM(x) , SI_ ## x + EMPTY_STATUS_ITEMS + #undef ITEM + ); + return make_message( + #define ITEM(x) "%s:\n" + EMPTY_STATUS_ITEMS + #undef ITEM + #define ITEM(x) ,status_item_list[SI_ ## x] + EMPTY_STATUS_ITEMS + #undef ITEM + ); +} +#undef EMPTY_STATUS_ITEMS + /* stat */ -int com_stat(int fd, int argc, char * const * argv) +int com_stat(struct rc4_context *rc4c, int argc, char * const * argv) { - int ret, num = 0;/* status will be printed that many - * times. num <= 0 means: print forever - */ + int i, ret; struct misc_meta_data tmp, *nmmd = &tmp; char *s; + int32_t num = 0; + int parser_friendly = 0; para_sigaction(SIGUSR1, dummy); - if (argc > 1) - num = atoi(argv[1]); + for (i = 1; i < argc; i++) { + const char *arg = argv[i]; + if (arg[0] != '-') + break; + if (!strcmp(arg, "--")) { + i++; + break; + } + if (!strncmp(arg, "-n=", 3)) { + ret = para_atoi32(arg + 3, &num); + if (ret < 0) + return ret; + continue; + } + if (!strcmp(arg, "-p")) { + parser_friendly = 1; + continue; + } + return -E_COMMAND_SYNTAX; + } + if (i != argc) + return -E_COMMAND_SYNTAX; for (;;) { - mmd_dup(nmmd); - s = get_status(nmmd); - ret = send_buffer(fd, s); + s = get_status(nmmd, parser_friendly); + ret = rc4_send_buffer(rc4c, s); free(s); if (ret < 0) goto out; + if (nmmd->vss_status_flags & VSS_NEXT) { + static char *esi; + if (!esi) + esi = empty_status_items(parser_friendly); + ret = rc4_send_buffer(rc4c, esi); + if (ret < 0) + goto out; + } else + send_afs_status(rc4c, parser_friendly); ret = 1; if (num > 0 && !--num) goto out; @@ -328,14 +399,14 @@ out: return ret; } -static int send_list_of_commands(int fd, struct server_command *cmd, +static int send_list_of_commands(struct rc4_context *rc4c, struct server_command *cmd, const char *handler) { int ret, i; for (i = 1; cmd->name; cmd++, i++) { char *perms = cmd_perms_itohuman(cmd->perms); - ret = send_va_buffer(fd, "%s\t%s\t%s\t%s\n", cmd->name, + ret = rc4_send_va_buffer(rc4c, "%s\t%s\t%s\t%s\n", cmd->name, handler, perms, cmd->description); @@ -368,7 +439,7 @@ static struct server_command *get_cmd_ptr(const char *name, char **handler) } /* help */ -int com_help(int fd, int argc, char * const * argv) +int com_help(struct rc4_context *rc4c, int argc, char * const * argv) { struct server_command *cmd; char *perms, *handler; @@ -376,9 +447,9 @@ int com_help(int fd, int argc, char * const * argv) if (argc < 2) { /* no argument given, print list of commands */ - if ((ret = send_list_of_commands(fd, server_cmds, "server")) < 0) + if ((ret = send_list_of_commands(rc4c, server_cmds, "server")) < 0) return ret; - return send_list_of_commands(fd, afs_cmds, "afs"); + return send_list_of_commands(rc4c, afs_cmds, "afs"); } /* argument given for help */ cmd = get_cmd_ptr(argv[1], &handler); @@ -387,7 +458,7 @@ int com_help(int fd, int argc, char * const * argv) return -E_BAD_CMD; } perms = cmd_perms_itohuman(cmd->perms); - ret = send_va_buffer(fd, + ret = rc4_send_va_buffer(rc4c, "%s - %s\n\n" "handler: %s\n" "permissions: %s\n" @@ -406,7 +477,7 @@ int com_help(int fd, int argc, char * const * argv) } /* hup */ -int com_hup(__a_unused int fd, int argc, __a_unused char * const * argv) +int com_hup(__a_unused struct rc4_context *rc4c, int argc, __a_unused char * const * argv) { if (argc != 1) return -E_COMMAND_SYNTAX; @@ -415,7 +486,7 @@ int com_hup(__a_unused int fd, int argc, __a_unused char * const * argv) } /* term */ -int com_term(__a_unused int fd, int argc, __a_unused char * const * argv) +int com_term(__a_unused struct rc4_context *rc4c, int argc, __a_unused char * const * argv) { if (argc != 1) return -E_COMMAND_SYNTAX; @@ -423,7 +494,7 @@ int com_term(__a_unused int fd, int argc, __a_unused char * const * argv) return 1; } -int com_play(__a_unused int fd, int argc, __a_unused char * const * argv) +int com_play(__a_unused struct rc4_context *rc4c, int argc, __a_unused char * const * argv) { if (argc != 1) return -E_COMMAND_SYNTAX; @@ -436,7 +507,7 @@ int com_play(__a_unused int fd, int argc, __a_unused char * const * argv) } /* stop */ -int com_stop(__a_unused int fd, int argc, __a_unused char * const * argv) +int com_stop(__a_unused struct rc4_context *rc4c, int argc, __a_unused char * const * argv) { if (argc != 1) return -E_COMMAND_SYNTAX; @@ -449,7 +520,7 @@ int com_stop(__a_unused int fd, int argc, __a_unused char * const * argv) } /* pause */ -int com_pause(__a_unused int fd, int argc, __a_unused char * const * argv) +int com_pause(__a_unused struct rc4_context *rc4c, int argc, __a_unused char * const * argv) { if (argc != 1) return -E_COMMAND_SYNTAX; @@ -464,7 +535,7 @@ int com_pause(__a_unused int fd, int argc, __a_unused char * const * argv) } /* next */ -int com_next(__a_unused int fd, int argc, __a_unused char * const * argv) +int com_next(__a_unused struct rc4_context *rc4c, int argc, __a_unused char * const * argv) { if (argc != 1) return -E_COMMAND_SYNTAX; @@ -476,7 +547,7 @@ int com_next(__a_unused int fd, int argc, __a_unused char * const * argv) } /* nomore */ -int com_nomore(__a_unused int fd, int argc, __a_unused char * const * argv) +int com_nomore(__a_unused struct rc4_context *rc4c, int argc, __a_unused char * const * argv) { if (argc != 1) return -E_COMMAND_SYNTAX; @@ -488,7 +559,7 @@ int com_nomore(__a_unused int fd, int argc, __a_unused char * const * argv) } /* ff */ -int com_ff(__a_unused int fd, int argc, char * const * argv) +int com_ff(__a_unused struct rc4_context *rc4c, int argc, char * const * argv) { long promille; int ret, backwards = 0; @@ -527,7 +598,7 @@ out: } /* jmp */ -int com_jmp(__a_unused int fd, int argc, char * const * argv) +int com_jmp(__a_unused struct rc4_context *rc4c, int argc, char * const * argv) { long unsigned int i; int ret; @@ -581,32 +652,7 @@ static struct server_command *parse_cmd(const char *cmdstr) return get_cmd_ptr(buf, NULL); } -static void init_rc4_keys(void) -{ - int i; - - for (i = 0; i < 2 * RC4_KEY_LEN; i++) - rc4_buf[i] = para_random(256); - PARA_DEBUG_LOG("rc4 keys initialized (%u:%u)\n", - (unsigned char) rc4_buf[0], - (unsigned char) rc4_buf[RC4_KEY_LEN]); - RC4_set_key(&rc4_recv_key, RC4_KEY_LEN, rc4_buf); - RC4_set_key(&rc4_send_key, RC4_KEY_LEN, rc4_buf + RC4_KEY_LEN); -} - -static void rc4_recv(unsigned long len, const unsigned char *indata, - unsigned char *outdata, __a_unused void *private_data) -{ - RC4(&rc4_recv_key, len, indata, outdata); -} - -static void rc4_send(unsigned long len, const unsigned char *indata, - unsigned char *outdata, __a_unused void *private_data) -{ - RC4(&rc4_send_key, len, indata, outdata); -} - -static int read_command(int fd, char **result) +static int read_command(struct rc4_context *rc4c, char **result) { int ret; char buf[4096]; @@ -616,7 +662,7 @@ static int read_command(int fd, char **result) size_t numbytes; char *p; - ret = recv_buffer(fd, buf, sizeof(buf)); + ret = rc4_recv_buffer(rc4c, buf, sizeof(buf)); if (ret < 0) goto out; if (!ret) @@ -679,22 +725,22 @@ static void reset_signals(void) */ __noreturn void handle_connect(int fd, const char *peername) { - int ret, argc, use_rc4 = 0; + int ret, argc; char buf[4096]; - unsigned char crypt_buf[MAXLINE]; + unsigned char rand_buf[CHALLENGE_SIZE + 2 * RC4_KEY_LEN]; + unsigned char challenge_sha1[HASH_SIZE]; struct user *u; struct server_command *cmd = NULL; - long unsigned challenge_nr, chall_response; char **argv = NULL; char *p, *command = NULL; size_t numbytes; + struct rc4_context rc4c = {.fd = fd}; reset_signals(); /* we need a blocking fd here as recv() might return EAGAIN otherwise. */ ret = mark_fd_blocking(fd); if (ret < 0) goto err_out; - challenge_nr = random(); /* send Welcome message */ ret = send_va_buffer(fd, "This is para_server, version " PACKAGE_VERSION ".\n" ); @@ -704,65 +750,69 @@ __noreturn void handle_connect(int fd, const char *peername) ret = recv_buffer(fd, buf, sizeof(buf)); if (ret < 0) goto err_out; - if (ret <= 6) { - ret = -E_AUTH; + if (ret < 10) { + ret = -E_AUTH_REQUEST; goto err_out; } numbytes = ret; - ret = -E_AUTH; - if (strncmp(buf, "auth ", 5)) + ret = -E_AUTH_REQUEST; + if (strncmp(buf, AUTH_REQUEST_MSG, strlen(AUTH_REQUEST_MSG))) goto err_out; - - if (numbytes < 9 || strncmp(buf, "auth rc4 ", 9)) - p = buf + 5; /* client version < 0.2.6 */ - else { - p = buf + 9; /* client version >= 0.2.6 */ - use_rc4 = 1; - } - PARA_DEBUG_LOG("received %s request for user %s\n", - use_rc4? "rc4" : "auth", p); + p = buf + strlen(AUTH_REQUEST_MSG); + PARA_DEBUG_LOG("received auth request for user %s\n", p); ret = -E_BAD_USER; u = lookup_user(p); - if (!u) - goto err_out; - ret = para_encrypt_challenge(u->rsa, challenge_nr, crypt_buf); - if (ret <= 0) - goto err_out; - numbytes = ret; - PARA_DEBUG_LOG("sending %zu byte challenge\n", numbytes); - /* We can't use send_buffer here since buf may contain null bytes */ - ret = send_bin_buffer(fd,(char *) crypt_buf, numbytes); + if (u) { + get_random_bytes_or_die(rand_buf, sizeof(rand_buf)); + ret = para_encrypt_buffer(u->rsa, rand_buf, sizeof(rand_buf), + (unsigned char *)buf); + if (ret < 0) + goto err_out; + numbytes = ret; + } else { + /* + * We don't want to reveal our user names, so we send a + * challenge to the client even if the user does not exist, and + * fail the authentication later. + */ + numbytes = 256; + get_random_bytes_or_die((unsigned char *)buf, numbytes); + } + PARA_DEBUG_LOG("sending %zu byte challenge + rc4 keys (%u bytes)\n", + CHALLENGE_SIZE, numbytes); + ret = send_bin_buffer(fd, buf, numbytes); if (ret < 0) goto net_err; - /* recv decrypted number */ - ret = recv_buffer(fd, buf, sizeof(buf)); + /* recv challenge response */ + ret = recv_bin_buffer(fd, buf, HASH_SIZE); if (ret < 0) goto net_err; numbytes = ret; - ret = -E_AUTH; - if (!numbytes) + PARA_DEBUG_LOG("received %zu bytes challenge response\n", ret); + ret = -E_BAD_USER; + if (!u) goto net_err; - if (sscanf(buf, CHALLENGE_RESPONSE_MSG "%lu", &chall_response) < 1 - || chall_response != challenge_nr) - goto err_out; - /* auth successful, send 'Proceed' message */ - PARA_INFO_LOG("good auth for %s (%lu)\n", u->name, challenge_nr); - sprintf(buf, "%s", PROCEED_MSG); - if (use_rc4) { - init_rc4_keys(); - ret = para_encrypt_buffer(u->rsa, rc4_buf, 2 * RC4_KEY_LEN, - (unsigned char *)buf + PROCEED_MSG_LEN + 1); - if (ret <= 0) - goto err_out; - numbytes = ret + strlen(PROCEED_MSG) + 1; - } else - numbytes = strlen(buf); - ret = send_bin_buffer(fd, buf, numbytes); + /* + * The correct response is the sha1 of the first CHALLENGE_SIZE bytes + * of the random data. + */ + ret = -E_BAD_AUTH; + if (numbytes != HASH_SIZE) + goto net_err; + sha1_hash((char *)rand_buf, CHALLENGE_SIZE, challenge_sha1); + if (memcmp(challenge_sha1, buf, HASH_SIZE)) + goto net_err; + /* auth successful */ + alarm(0); + PARA_INFO_LOG("good auth for %s\n", u->name); + /* init rc4 keys with the second part of the random buffer */ + RC4_set_key(&rc4c.recv_key, RC4_KEY_LEN, rand_buf + CHALLENGE_SIZE); + RC4_set_key(&rc4c.send_key, RC4_KEY_LEN, rand_buf + CHALLENGE_SIZE + + RC4_KEY_LEN); + ret = rc4_send_buffer(&rc4c, PROCEED_MSG); if (ret < 0) goto net_err; - if (use_rc4) - enable_crypt(fd, rc4_recv, rc4_send, NULL); - ret = read_command(fd, &command); + ret = read_command(&rc4c, &command); if (ret == -E_COMMAND_SYNTAX) goto err_out; if (ret < 0) @@ -776,23 +826,25 @@ __noreturn void handle_connect(int fd, const char *peername) if (ret < 0) goto err_out; /* valid command and sufficient perms */ - alarm(0); - argc = split_args(command, &argv, "\n"); + ret = create_argv(command, "\n", &argv); + if (ret < 0) + goto err_out; + argc = ret; PARA_NOTICE_LOG("calling com_%s() for %s@%s\n", cmd->name, u->name, peername); - ret = cmd->handler(fd, argc, argv); + ret = cmd->handler(&rc4c, argc, argv); + free_argv(argv); mutex_lock(mmd_mutex); mmd->num_commands++; mutex_unlock(mmd_mutex); if (ret >= 0) goto out; err_out: - send_va_buffer(fd, "%s\n", para_strerror(-ret)); + rc4_send_va_buffer(&rc4c, "%s\n", para_strerror(-ret)); net_err: PARA_NOTICE_LOG("%s\n", para_strerror(-ret)); out: free(command); - free(argv); mutex_lock(mmd_mutex); if (cmd && (cmd->perms & AFS_WRITE) && ret >= 0) mmd->events++; diff --git a/command.h b/command.h new file mode 100644 index 00000000..347dd81c --- /dev/null +++ b/command.h @@ -0,0 +1,19 @@ +/** \file command.h The structure of server and afs commands. */ + +/** + * Defines one command of para_server. + */ +struct server_command { + /** The name of the command. */ + const char *name; + /** Pointer to the function that handles the command. */ + int (*handler)(struct rc4_context *, int, char * const * const); + /** The privileges a user must have to execute this command. */ + unsigned int perms; + /** One-line description of the command. */ + const char *description; + /** Summary of the command line options. */ + const char *usage; + /** The long help text. */ + const char *help; +}; diff --git a/command_util.sh b/command_util.sh index 62ad9663..f388b2cf 100755 --- a/command_util.sh +++ b/command_util.sh @@ -26,6 +26,12 @@ read_header() AT:) array_type="$value" ;; + SI:) + for i in $value; do + system_includes="$system_includes +#include <$i.h>" + done + ;; IN:) for i in $value; do includes="$includes @@ -57,7 +63,6 @@ read_one_command() usage_txt="" help_txt="" perms_txt="" - line_handler=0 template=0 template_name="" template_prototype="" @@ -84,9 +89,6 @@ read_one_command() D:) desc_txt="$value" ;; - L:) - line_handler=1 - ;; U:) usage_txt="$value" ;; @@ -198,13 +200,13 @@ dump_proto() echo '/**' echo " * $desc_txt" echo ' *' - echo ' * \param fd The file descriptor to send output to.' - if test $line_handler -eq 0; then - echo ' * \param argc The number of arguments.' - echo ' * \param argv The argument vector.' + if [[ "$system_includes" =~ openssl/rc4.h ]]; then + echo ' * \param rc4c The rc4 crypt context.' else - echo ' * \param cmdline The full command line.' + echo ' * \param fd The file descriptor to send output to.' fi + echo ' * \param argc The number of arguments.' + echo ' * \param argv The argument vector.' echo ' * ' echo " * Usage: $usage_txt" echo ' * ' @@ -229,12 +231,7 @@ dump_array_member() { echo '{' echo ".name = \"$name_txt\"," - if test $line_handler -eq 0; then - echo ".handler = com_$name_txt," - else - echo ".handler = NULL," - echo ".line_handler = com_$name_txt," - fi + echo ".handler = com_$name_txt," if test -n "$perms_txt"; then echo ".perms = $perms_txt," fi @@ -273,6 +270,7 @@ template_loop() com_c_file() { echo "/** \file $output_file.c $c_file_comment */" + echo "$system_includes" echo "$includes" echo "struct $array_type $array_name[] = {" while : ; do diff --git a/compress_filter.c b/compress_filter.c index f853b633..2e263e87 100644 --- a/compress_filter.c +++ b/compress_filter.c @@ -10,6 +10,8 @@ * Uses ideas of AudioCompress, (C) 2002-2004 M. Hari Nezumi */ +#include + #include "para.h" #include "compress_filter.cmdline.h" #include "list.h" @@ -22,8 +24,6 @@ /** The size of the output data buffer. */ #define COMPRESS_CHUNK_SIZE 40960 -extern char *stat_item_values[NUM_STAT_ITEMS]; - /** Data specific to the compress filter. */ struct private_compress_data { /** The current multiplier. */ diff --git a/configure.ac b/configure.ac index 85a32729..5f39c245 100644 --- a/configure.ac +++ b/configure.ac @@ -80,13 +80,13 @@ AC_CHECK_FUNCS([atexit dup2 memchr memmove memset \ all_errlist_objs="server mp3_afh afh_common vss command net string signal time daemon stat crypt http_send close_on_fork ipc acl afh fade amp_filter -dccp_send fd user_list chunk_queue afs osl aft mood score attribute blob ringbuffer -playlist sha1 rbtree sched audiod grab_client filter_common wav_filter compress_filter +dccp_send fd user_list chunk_queue afs aft mood score attribute blob ringbuffer +playlist sha1 sched audiod grab_client filter_common wav_filter compress_filter http_recv dccp_recv recv_common write_common file_write audiod_command -client_common recv stdout filter stdin audioc write client fsck exec send_common ggo -udp_recv udp_send color fec fecdec_filter prebuffer_filter" +client_common recv stdout filter stdin audioc write client exec send_common ggo +udp_recv udp_send color fec fecdec_filter prebuffer_filter mm" -all_executables="server recv filter audioc write client fsck afh" +all_executables="server recv filter audioc write client afh" recv_cmdline_objs="recv.cmdline http_recv.cmdline dccp_recv.cmdline udp_recv.cmdline" recv_errlist_objs="http_recv recv_common recv time string net dccp_recv @@ -108,10 +108,10 @@ audioc_cmdline_objs="audioc.cmdline" audioc_errlist_objs="audioc string net fd" audioc_ldflags="" -audiod_cmdline_objs="audiod.cmdline grab_client.cmdline compress_filter.cmdline +audiod_cmdline_objs="audiod.cmdline compress_filter.cmdline http_recv.cmdline dccp_recv.cmdline file_write.cmdline client.cmdline audiod_command_list amp_filter.cmdline udp_recv.cmdline - prebuffer_filter.cmdline" + prebuffer_filter.cmdline sha1" audiod_errlist_objs="audiod signal string daemon stat net time grab_client filter_common wav_filter compress_filter amp_filter http_recv dccp_recv recv_common fd sched write_common file_write audiod_command crypt fecdec_filter @@ -125,10 +125,10 @@ afh_ldflags="" server_cmdline_objs="server.cmdline server_command_list afs_command_list" server_errlist_objs="server afh_common mp3_afh vss command net string signal - time daemon stat crypt http_send close_on_fork - ipc dccp_send fd user_list chunk_queue afs osl aft mood score attribute - blob playlist sha1 rbtree sched acl send_common udp_send color fec" -server_ldflags="" + time daemon crypt http_send close_on_fork mm + ipc dccp_send fd user_list chunk_queue afs aft mood score attribute + blob playlist sha1 sched acl send_common udp_send color fec" +server_ldflags="-losl" server_audio_formats=" mp3" write_cmdline_objs="write.cmdline file_write.cmdline" @@ -138,12 +138,10 @@ writers=" file" default_writer="FILE_WRITE" client_cmdline_objs="client.cmdline" -client_errlist_objs="client net string crypt fd sched stdin stdout client_common" +client_errlist_objs="client net string crypt fd sched stdin stdout + client_common sha1" client_ldflags="" -fsck_cmdline_objs="fsck.cmdline" -fsck_errlist_objs="osl rbtree fsck string sha1 fd" - gui_cmdline_objs="gui.cmdline" gui_errlist_objs="exec signal string stat ringbuffer fd" gui_other_objs="gui gui_theme" @@ -153,7 +151,17 @@ fade_cmdline_objs="fade.cmdline" fade_errlist_objs="fade exec string fd" - +########################################################################### osl +have_osl=yes +AC_CHECK_HEADER(osl.h, [], have_osl=no) +AC_CHECK_LIB([osl], [osl_open_table], [], have_osl=no) +if test "$have_osl" = "no"; then + AC_MSG_ERROR([libosl not found, type the following to download: + git clone git://git.tuebingen.mpg.de/osl +Install the library with + (cd osl && make && sudo make install) + ]) +fi ########################################################################### ssl dnl @synopsis CHECK_SSL dnl @@ -193,10 +201,9 @@ AC_ARG_ENABLE(ssldir, [AS_HELP_STRING(--enable-ssldir=path, [Search for openssl also in path.])]) if test "$enable_ssldir" = "yes"; then enable_ssldir=""; fi CHECK_SSL($enable_ssldir) -server_ldflags="$srver_ldflags $SSL_LDFLAGS $SSL_LIBS" +server_ldflags="$server_ldflags $SSL_LDFLAGS $SSL_LIBS" client_ldflags="$client_ldflags $SSL_LDFLAGS $SSL_LIBS" audiod_ldflags="$audiod_ldflags $SSL_LDFLAGS $SSL_LIBS" -fsck_ldflags="$fsck_ldflags $SSL_LDFLAGS $SSL_LIBS" ########################################################################### libsocket AC_CHECK_LIB([c], [socket], @@ -613,11 +620,12 @@ AC_DEFINE_UNQUOTED(DEFINE_ERRLIST_OBJECT_ENUM, ################################################################## status items status_items="basename status num_played mtime bitrate frequency file_size -status_flags format score audio_file_info taginfo1 taginfo2 afs_mode +status_flags format score techinfo afs_mode attributes_txt decoder_flags audiod_status play_time attributes_bitmap offset seconds_total stream_start current_time audiod_uptime image_id lyrics_id duration directory lyrics_name image_name path hash channels -last_played num_chunks chunk_time amplification" +last_played num_chunks chunk_time amplification artist title year album +comment" # $1: prefix, $2: items AC_DEFUN([make_enum_items], [$( @@ -649,7 +657,6 @@ audiod_objs="$audiod_cmdline_objs $audiod_errlist_objs" server_objs="$server_cmdline_objs $server_errlist_objs" write_objs="$write_cmdline_objs $write_errlist_objs" client_objs="$client_cmdline_objs $client_errlist_objs" -fsck_objs="$fsck_cmdline_objs $fsck_errlist_objs" audioc_objs="$audioc_cmdline_objs $audioc_errlist_objs" afh_objs="$afh_cmdline_objs $afh_errlist_objs" fade_objs="$fade_cmdline_objs $fade_errlist_objs" @@ -689,11 +696,6 @@ AC_SUBST(client_ldflags, $client_ldflags) AC_DEFINE_UNQUOTED(INIT_CLIENT_ERRLISTS, objlist_to_errlist($client_errlist_objs), errors used by para_client) -AC_SUBST(fsck_objs, add_dot_o($fsck_objs)) -AC_SUBST(fsck_ldflags, $fsck_ldflags) -AC_DEFINE_UNQUOTED(INIT_FSCK_ERRLISTS, - objlist_to_errlist($fsck_errlist_objs), errors used by para_fsck) - AC_SUBST(audioc_objs, add_dot_o($audioc_objs)) AC_SUBST(audioc_ldflags, $audioc_ldflags) AC_DEFINE_UNQUOTED(INIT_AUDIOC_ERRLISTS, diff --git a/convert_0.2-0.3.sh b/convert_0.2-0.3.sh deleted file mode 100755 index 24c937b9..00000000 --- a/convert_0.2-0.3.sh +++ /dev/null @@ -1,186 +0,0 @@ -#!/usr/bin/env bash - -#------------------------------------------------------------------------------- -## Script to convert the database of paraslash 0.2.x to version 0.3.x. -## -## Assumptions: -## - para_server 0.2.x is running and the mysql selector is active -## - para_server 0.3.x is running on another port -## - The database of paraslash 0.3.x has been initialized (i.e. -## para_client init has successfully been executed) -## - All audio files in the mysql database of paraslash 0.2.x. have -## already been added to the 0.3.x database (execute para_client add -## /my/audio/file/dir to do that) -## -## The script converts the attribute table, the set attributes for each audio -## file, the image table and all image ids, and finally the lastplayed and the -## numplayed data. -## -## However, it does not convert the paraslash stream definitions from 0.2.x to -## the moods of 0.3.x. You'll have to do this by hand. -#------------------------------------------------------------------------------- - -# Call this script without arguments to see usage info - -# How to connect to para_server 0.2.x. -client02=/usr/local/bin/para_client -port02=2991 -host02=localhost - -# How to connect to para_server 0.3.x. -client03=./para_client -port03=2990 -host03=localhost - -# Unset this to deactivate messages -debug=1 - - -client02_cmd="$client02 -p $port02 -i $host02" -client03_cmd="$client03 -p $port03 -i $host03" - -info_log() -{ - if test $debug -eq 1; then - echo "$@" - fi -} - -exec_client02_cmd() -{ - info_log "$client02_cmd -- $@" - result="$($client02_cmd -- "$@")" -} - -exec_client03_cmd() -{ - info_log "$client03_cmd -- $@" - result="$($client03_cmd -- "$@")" -} - -convert_attribute_table() -{ - local atts - exec_client02_cmd laa - atts="$result" - info_log "creating attributes: $atts" - exec_client03_cmd addatt $atts -} - -convert_attributes() -{ - local att atts current_atts cmd query="select dir.dir, dir.name" - exec_client02_cmd laa - atts="$result" - for att in $atts; do - query="$query, data.$att" - done - query="$query from dir,data where dir.name=data.name" - exec_client02_cmd verb "$query" - echo "$result" | while read dir name current_atts; do - cmd="setatt " - for att in $atts; do - if test "${current_atts#0}" = "$current_atts"; then - cmd="$cmd $att+" - current_atts=${current_atts#1 } - else - current_atts=${current_atts#0 } - fi - done - if test "$cmd" = "setatt "; then - continue - fi - exec_client03_cmd $cmd "$dir/$name" - done -} - -convert_lastplayed_numplayed() -{ - local query="select dir.dir, dir.name, unix_timestamp(data.lastplayed), data.numplayed from dir,data where data.name=dir.name" - local lp np data dir name - exec_client02_cmd verb "$query" - data="$result" - echo "$result" | while read dir name lp np; do - cmd="touch -n$np -l$lp $dir/$name" - exec_client03_cmd $cmd - done -} - -convert_image_table() -{ - local num size name - exec_client02_cmd piclist; - echo "$result" | while read num size name; do - info_log "converting $name" - $client02_cmd -- pic "#$num" | $client03_cmd -- addimg "$name" - done -} - -convert_image_ids() -{ - local query="select dir.dir, dir.name, pics.name from dir,data,pics where data.name=dir.name and pics.id=data.pic_id" - local img_ids_03 dir name img id - exec_client03_cmd lsimg -l - img_ids_03="$result" - exec_client02_cmd verb "$query" - echo "$result" | while read dir name img; do - id="$(echo "$img_ids_03" | grep " $img\$" | cut -f 1)" - exec_client03_cmd touch "-i$id" "$dir/$name" - done -} - - -usage() -{ - grep '^##' $0 | sed -e 's/^## *//' - echo ' -Usage: $0 command - -command is one of the following: - - attribute_table: create attributes - attributes: convert attributes for each audio file - lastplayed_numplayed: convert numplayed and lastplayed - data of each audio file - image_table: retrieve images from mysql and add them to the database - of paraslash-0.3.x - image_ids: convert image id of each audio file. - all: Do all of the above. - -Edit the top of the script to customize some options. -' -} - -if test $# -ne 1; then - usage - exit 1 -fi - -case "$1" in -attribute_table) - convert_attribute_table - ;; -attributes) - convert_attributes - ;; -lastplayed_numplayed) - convert_lastplayed_numplayed - ;; -image_table) - convert_image_table - ;; -image_ids) - convert_image_ids - ;; -all) - convert_attribute_table - convert_attributes - convert_lastplayed_numplayed - convert_image_table - convert_image_ids - ;; -*) - usage - exit 1 - ;; -esac diff --git a/convert_0.3-0.4.sh b/convert_0.3-0.4.sh new file mode 100755 index 00000000..aa0b239f --- /dev/null +++ b/convert_0.3-0.4.sh @@ -0,0 +1,221 @@ +#!/usr/bin/env bash + +#------------------------------------------------------------------------------- +## Script to convert the database of paraslash 0.3.5 to version 0.4.x. +## +## Assumptions: +## - para_server 0.3.5 is running +## - "para_client check" reports no errors +## - para_server 0.4.x is running, listens on another port and uses a +## different afs database and a different afs socket +## - The database of paraslash 0.4.x has been initialized (i.e. +## para_client init has successfully been executed) +## - All audio files in the 0.3.x database have already been added to +## the 0.4.x database (execute para_client add /my/audio/file/dir to +## do that) +## +#------------------------------------------------------------------------------- + +# Call this script without arguments to see usage info + +# How to connect to para_server 0.3.x. +client03=/usr/local/bin/para_client +port03=2991 +host03=localhost +database03=$HOME/.paraslash/afs_database + +# How to connect to para_server 0.4.x. +client04=$(pwd)/para_client +port04=2990 +host04=localhost +database04=$HOME/.paraslash/afs_database-0.4 + +# Any character that does not occur in any filename of an audio file +sep='|' + + +client03_cmd="$client03 -p $port03 -i $host03" +client04_cmd="$client04 -p $port04 -i $host04" + +exec_client03_cmd() +{ + result="$($client03_cmd -- "$@")" +} + +exec_client04_cmd() +{ + result="$($client04_cmd -- "$@")" +} + +convert_attribute_table() +{ + local atts + + echo "converting attribute table" + exec_client03_cmd lsatt + atts="$result" + exec_client04_cmd addatt $atts +} + +convert_attributes() +{ + local OIFS="$IFS" a p att atts + + printf "converting attributes: " + $client03_cmd -- ls -p -lv \ + | grep '^path:\|^attributes_txt:' \ + | sed -e "/^path:/N;s/\n/$sep/1" \ + | { + IFS="$sep" + while read p a; do + p=${p#path: } + a=${a#attributes_txt:} + IFS=" " + atts= + for att in $a; do + atts="$atts $att+" + done + IFS="$OIFS" + [[ -n "$atts" ]] && $client04_cmd -- setatt $atts "$p" + IFS="$sep" + printf "." + done + echo done + } + IFS="$OIFS" +} + +convert_lna() +{ + local OIFS="$IFS" p l n a + + printf "converting last_played, num_played, amplification values: " + $client03_cmd -- ls -p -d -lv \ + | grep '^path:\|^last_played: \|^num_played: \|^amplification: ' \ + | sed -e "/^path:/N;N;N;s/\n/$sep/g" \ + | { + IFS="$sep" + while read p l n a; do + #echo "p: $p, l:$l, n:$n a:$a" + p=${p#path: } + l=${l#last_played: } + n=${n#num_played: } + a=${a#amplification: } + IFS="$OIFS" + $client04_cmd -- touch "-l$l" "-n$n" "-a$a" "$p" + IFS="$sep" + printf "." + done + echo done + } + IFS="$OIFS" +} + +convert_blobs() +{ + local blob name + + for blob in img lyr mood pl; do + printf "converting $blob table: " + exec_client03_cmd ls$blob + echo "$result" | while read name; do + $client03_cmd -- cat$blob "$name" | $client04_cmd -- add$blob "$name" + printf "." + done + echo done + done +} + +convert_ids() +{ + local OIFS="$IFS" p i y iopts yopts + + printf "converting image and lyrics ids: " + $client03_cmd -- ls -p -lv \ + | grep '^path:\|^image_name: \|^lyrics_name: ' \ + | sed -e "/^path:/N;N;s/\n/$sep/g" \ + | { + IFS="$sep" + while read p i l; do + IFS="$OIFS" + iopts= + yopts= + p=${p#path: } + i=${i#image_name: } + l=${l#lyrics_name: } + if [[ "$i" != '(none)' ]]; then + exec_client04_cmd lsimg -l "$i" + iopts="-i${result%% *}" + fi + if [[ "$l" != '(none)' ]]; then + exec_client04_cmd lslyr -l "$l" + yopts="-y${result%% *}" + fi + if [[ -n "$iopts" && -n "$yopts" ]]; then + $client04_cmd -- touch "$iopts" "$yopts" "$p" + elif [[ -n "$iopts" ]]; then + $client04_cmd -- touch "$iopts" "$p" + elif [[ -n "$yopts" ]]; then + $client04_cmd -- touch "$yopts" "$p" + fi + printf "." + IFS="$sep" + done + echo done + } + IFS="$OIFS" +} + + +usage() +{ + grep '^##' $0 | sed -e 's/^## *//' + echo ' +Usage: $0 command + +command is one of the following: + + attribute_table: create attributes + attributes: convert attributes for each audio file + lna: convert the last_played/num_played/amplification values + blobs: convert the image/lyrics/moods/playlists tables + ids: convert image and the lyrics id of each audio file. + all: Do all of the above. + +Edit the top of the script to customize some options. +' +} + +if test $# -ne 1; then + usage + exit 1 +fi + +case "$1" in +attribute_table) + convert_attribute_table + ;; +attributes) + convert_attributes + ;; +lna) + convert_lna + ;; +blobs) + convert_blobs + ;; +ids) + convert_ids + ;; +all) + convert_attribute_table + convert_attributes + convert_lna + convert_blobs + convert_ids + ;; +*) + usage + exit 1 + ;; +esac diff --git a/crypt.c b/crypt.c index 73eebe18..352c5b8d 100644 --- a/crypt.c +++ b/crypt.c @@ -6,10 +6,64 @@ /** \file crypt.c openssl-based RSA encryption/decryption routines */ +#include +#include +#include +#include +#include +#include +#include + #include "para.h" #include "error.h" #include "string.h" #include "crypt.h" +#include "fd.h" +/** + * Fill a buffer with random content. + * + * \param buf The buffer to fill. + * \param num The size of \a buf in bytes. + * + * This function puts \a num cryptographically strong pseudo-random bytes into + * buf. If libssl can not guarantee an unpredictable byte sequence (for example + * because the PRNG has not been seeded with enough randomness) the function + * logs an error message and calls exit(). + */ +void get_random_bytes_or_die(unsigned char *buf, int num) +{ + unsigned long err; + + /* RAND_bytes() returns 1 on success, 0 otherwise. */ + if (RAND_bytes(buf, num) == 1) + return; + err = ERR_get_error(); + PARA_EMERG_LOG("%s\n", ERR_reason_error_string(err)); + exit(EXIT_FAILURE); +} + +/** + * Seed pseudo random number generators. + * + * This function reads 64 bytes from /dev/urandom and adds them to the SSL + * PRNG. It also seeds the PRNG used by random() with a random seed obtained + * from SSL. If /dev/random could not be read, an error message is logged and + * the function calls exit(). + * + * \sa RAND_load_file(3), \ref get_random_bytes_or_die(), srandom(3), + * random(3), \ref para_random(). + */ +void init_random_seed_or_die(void) +{ + int seed, ret = RAND_load_file("/dev/urandom", 64); + + if (ret != 64) { + PARA_EMERG_LOG("could not seed PRNG (ret = %d)\n", ret); + exit(EXIT_FAILURE); + } + get_random_bytes_or_die((unsigned char *)&seed, sizeof(seed)); + srandom(seed); +} static EVP_PKEY *load_key(const char *file, int private) { @@ -92,38 +146,11 @@ int para_decrypt_buffer(char *key_file, unsigned char *outbuf, unsigned char *in ret = get_rsa_key(key_file, &rsa, LOAD_PRIVATE_KEY); if (ret < 0) return ret; - ret = RSA_private_decrypt(inlen, inbuf, outbuf, rsa, RSA_PKCS1_PADDING); + ret = RSA_private_decrypt(inlen, inbuf, outbuf, rsa, RSA_PKCS1_OAEP_PADDING); rsa_free(rsa); return (ret > 0)? ret : -E_DECRYPT; } -/** - * decrypt the challenge number sent by para_server - * - * \param key_file full path of the rsa key - * \param challenge_nr result is stored here - * \param inbuf the input buffer - * \param rsa_inlen the length of \a inbuf - * - * \return positive on success, negative on errors - * - * \sa para_decrypt_buffer() - */ -int para_decrypt_challenge(char *key_file, long unsigned *challenge_nr, - unsigned char *inbuf, unsigned rsa_inlen) -{ - unsigned char *rsa_out = OPENSSL_malloc(rsa_inlen + 1); - int ret = para_decrypt_buffer(key_file, rsa_out, inbuf, rsa_inlen); - - if (ret >= 0) { - rsa_out[ret] = '\0'; - ret = sscanf((char *)rsa_out, "%lu", challenge_nr) == 1? - 1 : -E_CHALLENGE; - } - OPENSSL_free(rsa_out); - return ret; -} - /** * encrypt a buffer using an RSA key * @@ -143,30 +170,114 @@ int para_encrypt_buffer(RSA *rsa, unsigned char *inbuf, if (flen < 0) return -E_ENCRYPT; - ret = RSA_public_encrypt(flen, inbuf, outbuf, rsa, RSA_PKCS1_PADDING); - return ret < 0? -E_ENCRYPT : ret; + ret = RSA_public_encrypt(flen, inbuf, outbuf, rsa, RSA_PKCS1_OAEP_PADDING); + return ret < 0? -E_ENCRYPT : ret; } /** - * encrypt the given challenge number + * Encrypt and send a buffer. * - * \param rsa: public rsa key - * \param challenge_nr the number to be encrypted - * \param outbuf the output buffer + * \param rc4c The rc4 crypt context. + * \param buf The buffer to send. + * \param len The size of \a buf in bytes. * - * \a outbuf must be at least 64 bytes long + * \return The return value of the underyling call to write_all(). * - * \return The size of the encrypted data on success, negative on errors + * \sa \ref write_all(), RC4(3). + */ +int rc4_send_bin_buffer(struct rc4_context *rc4c, const char *buf, size_t len) +{ + int ret; + unsigned char *tmp; + + assert(len); + tmp = para_malloc(len); + RC4(&rc4c->send_key, len, (const unsigned char *)buf, tmp); + ret = write_all(rc4c->fd, (char *)tmp, &len); + free(tmp); + return ret; +} + +/** + * Encrypt and send a \p NULL-terminated buffer. * - * \sa para_encrypt_buffer() + * \param rc4c The rc4 crypt context. + * \param buf The buffer to send. * + * \return The return value of the underyling call to rc4_send_bin_buffer(). */ -int para_encrypt_challenge(RSA* rsa, long unsigned challenge_nr, - unsigned char *outbuf) +int rc4_send_buffer(struct rc4_context *rc4c, const char *buf) { - unsigned char *inbuf = (unsigned char*) make_message("%lu", challenge_nr); - int ret = para_encrypt_buffer(rsa, inbuf, strlen((char *)inbuf), outbuf); - free(inbuf); + return rc4_send_bin_buffer(rc4c, buf, strlen(buf)); +} + +/** + * Format, encrypt and send a buffer. + * + * \param rc4c The rc4 crypt context. + * \param fmt A format string. + * + * \return The return value of the underyling call to rc4_send_buffer(). + */ +__printf_2_3 int rc4_send_va_buffer(struct rc4_context *rc4c, const char *fmt, ...) +{ + char *msg; + int ret; + + PARA_VSPRINTF(fmt, msg); + ret = rc4_send_buffer(rc4c, msg); + free(msg); return ret; } +/** + * Receive a buffer and decrypt it. + * + * \param rc4c The rc4 crypt context. + * \param buf The buffer to write the decrypted data to. + * \param size The size of \a buf. + * + * \return The number of bytes received on success, negative on errors, zero if + * the peer has performed an orderly shutdown. + * + * \sa recv(2), RC4(3). + */ +int rc4_recv_bin_buffer(struct rc4_context *rc4c, char *buf, size_t size) +{ + unsigned char *tmp = para_malloc(size); + ssize_t ret = recv(rc4c->fd, tmp, size, 0); + + if (ret > 0) + RC4(&rc4c->recv_key, ret, tmp, (unsigned char *)buf); + else if (ret < 0) + ret = -ERRNO_TO_PARA_ERROR(errno); + free(tmp); + return ret; +} + +/** + * Receive a buffer, decrypt it and write terminating NULL byte. + * + * \param rc4c The rc4 crypt context. + * \param buf The buffer to write the decrypted data to. + * \param size The size of \a buf. + * + * Read at most \a size - 1 bytes from file descriptor given by \a rc4c, + * decrypt the received data and write a NULL byte at the end of the decrypted + * data. + * + * \return The return value of the underlying call to \ref + * rc4_recv_bin_buffer(). + */ +int rc4_recv_buffer(struct rc4_context *rc4c, char *buf, size_t size) +{ + int n; + + assert(size); + n = rc4_recv_bin_buffer(rc4c, buf, size - 1); + if (n >= 0) + buf[n] = '\0'; + else + *buf = '\0'; + return n; +} diff --git a/crypt.h b/crypt.h index 88af9e89..8edff413 100644 --- a/crypt.h +++ b/crypt.h @@ -7,10 +7,6 @@ /** \file crypt.h prototypes for the RSA crypt functions */ #include -int para_decrypt_challenge(char *key_file, long unsigned *challenge_nr, - unsigned char *buf, unsigned rsa_inlen); -int para_encrypt_challenge(RSA* rsa, long unsigned challenge_nr, - unsigned char *outbuf); int para_encrypt_buffer(RSA* rsa, unsigned char *inbuf, unsigned len, unsigned char *outbuf); int para_decrypt_buffer(char *key_file, unsigned char *outbuf, unsigned char *inbuf, @@ -18,8 +14,32 @@ int para_decrypt_buffer(char *key_file, unsigned char *outbuf, unsigned char *in int get_rsa_key(char *key_file, RSA **rsa, int private); void rsa_free(RSA *rsa); +void get_random_bytes_or_die(unsigned char *buf, int num); +void init_random_seed_or_die(void); + +/** + * Used on the server-side for client-server communication encryption. + * + * The traffic between (the forked child of) para_server and the remote + * client process is crypted by a RC4 session key. This structure contains + * the RC4 keys and the file descriptor for which these keys should be used. + */ +struct rc4_context { + /** The socket file descriptor. */ + int fd; + /** Key used for sending data. */ + RC4_KEY recv_key; + /** Key used for receiving data. */ + RC4_KEY send_key; +}; +int rc4_send_bin_buffer(struct rc4_context *rc4c, const char *buf, size_t len); +int rc4_send_buffer(struct rc4_context *rc4c, const char *buf); +__printf_2_3 int rc4_send_va_buffer(struct rc4_context *rc4c, const char *fmt, ...); +int rc4_recv_bin_buffer(struct rc4_context *rcc, char *buf, size_t size); +int rc4_recv_buffer(struct rc4_context *rcc, char *buf, size_t size); /** \cond used to distinguish between loading of private/public key */ #define LOAD_PUBLIC_KEY 0 #define LOAD_PRIVATE_KEY 1 +#define CHALLENGE_SIZE 64 /** \endcond **/ diff --git a/daemon.c b/daemon.c index cae63ea7..7f4aa6f5 100644 --- a/daemon.c +++ b/daemon.c @@ -5,12 +5,14 @@ */ /** \file daemon.c Some helpers for programs that detach from the console. */ -#include "para.h" -#include "daemon.h" + +#include #include #include /* getgrnam() */ #include +#include "para.h" +#include "daemon.h" #include "string.h" #include "color.h" diff --git a/dccp_recv.c b/dccp_recv.c index 93dea809..4d4ac86d 100644 --- a/dccp_recv.c +++ b/dccp_recv.c @@ -11,6 +11,7 @@ * (C) 2005 Ian McDonald */ +#include #include #include diff --git a/dccp_send.c b/dccp_send.c index 9b72b7c9..d2f81dd9 100644 --- a/dccp_send.c +++ b/dccp_send.c @@ -11,8 +11,10 @@ * (C) 2005 Ian McDonald */ +#include #include #include +#include #include "para.h" #include "error.h" diff --git a/error.h b/error.h index 95640da9..c9031f82 100644 --- a/error.h +++ b/error.h @@ -100,46 +100,6 @@ extern const char **para_errlist[]; PARA_ERROR(RANGE_VIOLATION, "range violation detected, very bad"), \ PARA_ERROR(NOT_A_REGULAR_FILE, "not a regular file"), \ - -#define OSL_ERRORS \ - PARA_ERROR(BAD_DB_DIR, "invalid database directory"), \ - PARA_ERROR(NO_COLUMN_DESC, "missing column description"), \ - PARA_ERROR(BAD_NAME, "invalid name for a column/table"), \ - PARA_ERROR(BAD_STORAGE_TYPE, "invalid storage type"), \ - PARA_ERROR(BAD_STORAGE_FLAGS, "invalid storage flags"), \ - PARA_ERROR(NO_COLUMN_NAME, "missing column name"), \ - PARA_ERROR(NO_COLUMNS, "at least one column required"), \ - PARA_ERROR(BAD_COLUMN_NAME, "invalid name for a table column"), \ - PARA_ERROR(NO_UNIQUE_RBTREE_COLUMN, "need at least one mapped column with OSL_UNIQE and OSL_RBTREE OSL"), \ - PARA_ERROR(NO_RBTREE_COL, "at least one column needs an rbtree"), \ - PARA_ERROR(DUPLICATE_COL_NAME, "column name given twice"), \ - PARA_ERROR(BAD_STORAGE_SIZE, "invalid storage size"), \ - PARA_ERROR(NO_COMPARE_FUNC, "missing compare function"), \ - PARA_ERROR(BAD_DATA_SIZE, "wrong data size for fixed-size column"), \ - PARA_ERROR(NOT_MAPPED, "file not mapped"), \ - PARA_ERROR(ALREADY_MAPPED, "file already mapped"), \ - PARA_ERROR(BAD_SIZE, "invalid size specified"), \ - PARA_ERROR(TRUNC, "failed to truncate file"), \ - PARA_ERROR(BAD_TABLE, "table not open"), \ - PARA_ERROR(BAD_TABLE_DESC, "invalid table description"), \ - PARA_ERROR(RB_KEY_EXISTS, "key already exists in rbtree"), \ - PARA_ERROR(RB_KEY_NOT_FOUND, "key not found in rbtree"), \ - PARA_ERROR(BAD_ROW_NUM, "invalid row number"), \ - PARA_ERROR(INDEX_CORRUPTION, "index corruption detected"), \ - PARA_ERROR(INVALID_OBJECT, "reference to invalid object"), \ - PARA_ERROR(STAT, "can not stat file"), \ - PARA_ERROR(WRITE, "write error"), \ - PARA_ERROR(LSEEK, "lseek error"), \ - PARA_ERROR(BUSY, "table is busy"), \ - PARA_ERROR(SHORT_TABLE, "table too short"), \ - PARA_ERROR(NO_MAGIC, "missing table header magic"), \ - PARA_ERROR(VERSION_MISMATCH, "table version not supported"), \ - PARA_ERROR(BAD_COLUMN_NUM, "invalid column number"), \ - PARA_ERROR(BAD_TABLE_FLAGS, "invalid flags in table description"), \ - PARA_ERROR(BAD_ROW, "invalid row"), \ - - - #define AFS_ERRORS \ PARA_ERROR(BAD_TABLE_NAME, "invalid table"), \ PARA_ERROR(INPUT_TOO_LARGE, "input too large for stdin command"), \ @@ -147,14 +107,18 @@ extern const char **para_errlist[]; PARA_ERROR(AFS_SIGNAL, "afs caught deadly signal"), \ PARA_ERROR(AFS_SOCKET, "afs socket not writable"), \ PARA_ERROR(AFS_SHORT_READ, "short read from afs socket"), \ + PARA_ERROR(OSL, "osl error"), \ #define MOOD_ERRORS \ - PARA_ERROR(MOOD_SYNTAX, "mood syntax error"), \ PARA_ERROR(NO_MOOD, "no mood available"), \ PARA_ERROR(DUMMY_ROW, "attempted to access blob dummy object"), \ +#define MM_ERRORS \ + PARA_ERROR(MOOD_SYNTAX, "mood syntax error"), \ + + #define ATTRIBUTE_ERRORS \ PARA_ERROR(ATTR_SYNTAX, "attribute syntax error"), \ PARA_ERROR(NO_ATTRIBUTES, "no attributes defined yet"), \ @@ -276,6 +240,7 @@ extern const char **para_errlist[]; #define STAT_ERRORS \ PARA_ERROR(TOO_MANY_CLIENTS, "maximal number of stat clients exceeded"), \ PARA_ERROR(UNKNOWN_STAT_ITEM, "status item not recognized"), \ + PARA_ERROR(STAT_ITEM_PARSE, "failed to parse status item"), \ #define OGGDEC_FILTER_ERRORS \ @@ -289,13 +254,10 @@ extern const char **para_errlist[]; #define GRAB_CLIENT_ERRORS \ - PARA_ERROR(PEDANTIC_GRAB, "fd not ready and pedantic grab requested"), \ PARA_ERROR(GC_WRITE, "grab client write error"), \ PARA_ERROR(BAD_GC_SLOT, "invalid slot requested by grab client"), \ PARA_ERROR(BAD_GC_FILTER_NUM, "invalid filter number given"), \ PARA_ERROR(GC_SYNTAX, "grab client syntax error"), \ - PARA_ERROR(GC_HELP_GIVEN, ""), /* not really an error */ \ - PARA_ERROR(GC_VERSION_GIVEN, ""), /* not really an error */ \ #define MP3DEC_FILTER_ERRORS \ @@ -314,6 +276,8 @@ extern const char **para_errlist[]; PARA_ERROR(STRTOLL, "unknown strtoll error"), \ PARA_ERROR(ATOI_NO_DIGITS, "no digits found in string"), \ PARA_ERROR(ATOI_JUNK_AT_END, "further characters after number"), \ + PARA_ERROR(SIZE_PREFIX, "bad size prefix"), \ + PARA_ERROR(REGEX, "regular expression error") \ #define EXEC_ERRORS \ @@ -367,14 +331,15 @@ extern const char **para_errlist[]; #define COMMAND_ERRORS \ PARA_ERROR(COMMAND_SYNTAX, "syntax error in command"), \ - PARA_ERROR(AUTH, "did not receive auth request"), \ + PARA_ERROR(AUTH_REQUEST, "did not receive auth request"), \ PARA_ERROR(NO_AUDIO_FILE, "no audio file"), \ PARA_ERROR(BAD_CMD, "invalid command"), \ PARA_ERROR(PERM, "permission denied"), \ PARA_ERROR(LOCK, "lock error"), \ PARA_ERROR(SENDER_CMD, "command not supported by this sender"), \ PARA_ERROR(SERVER_CRASH, "para_server crashed -- can not live without it"), \ - PARA_ERROR(BAD_USER, "you don't exist. Go away."), \ + PARA_ERROR(BAD_USER, "auth request for invalid user"), \ + PARA_ERROR(BAD_AUTH, "authentication failure"), \ #define DCCP_RECV_ERRORS \ @@ -478,12 +443,23 @@ extern const char **para_errlist[]; */ #define SYSTEM_ERROR_BIT 30 +/** + * Like the SYSTEM_ERROR_BIT, but indicates an error from the osl library. + */ +#define OSL_ERROR_BIT 29 + /** Check whether the system error bit is set. */ #define IS_SYSTEM_ERROR(num) (!!((num) & (1 << SYSTEM_ERROR_BIT))) +/** Check whether the osl error bit is set. */ +#define IS_OSL_ERROR(num) (!!((num) & (1 << OSL_ERROR_BIT))) + /** Set the system error bit for the given number. */ #define ERRNO_TO_PARA_ERROR(num) ((num) | (1 << SYSTEM_ERROR_BIT)) +/** Set the osl error bit for the given number. */ +#define OSL_ERRNO_TO_PARA_ERROR(num) ((num) | (1 << OSL_ERROR_BIT)) + /** Check whether a given number is a system error number. * * \param num The value to be checked. @@ -508,11 +484,33 @@ _static_inline_ int is_errno(int num, int _errno) _static_inline_ const char *para_strerror(int num) { assert(num > 0); +#ifdef _OSL_H + if (IS_OSL_ERROR(num)) + return osl_strerror(num & ((1 << OSL_ERROR_BIT) - 1)); +#endif if (IS_SYSTEM_ERROR(num)) - return strerror((num) & ((1 << SYSTEM_ERROR_BIT) - 1)); - else - return para_errlist[ERRNUM_TO_SS(num)][ERRNUM_TO_INDEX(num)]; + return strerror(num & ((1 << SYSTEM_ERROR_BIT) - 1)); + return para_errlist[ERRNUM_TO_SS(num)][ERRNUM_TO_INDEX(num)]; } + +/** + * Wrapper for osl library calls. + * + * \param ret The return value of an osl library function. + * + * This should be used for all calls to osl functions that return an osl error + * code. It changes the return value appropriately so that it can be used for + * printing the correct error message vi para_strerror(). + * + * \return \a ret if \a ret >= 0, a paraslash error code otherwise. + */ +_static_inline_ int osl(int ret) +{ + if (ret >= 0) + return ret; + return -OSL_ERRNO_TO_PARA_ERROR(-ret); +} + /** * Define the error list for one subsystem. # diff --git a/exec.c b/exec.c index 86a483cf..b603809d 100644 --- a/exec.c +++ b/exec.c @@ -5,12 +5,15 @@ */ /** \file exec.c Helper functions for spawning new processes. */ + +#include #include + #include "para.h" #include "close_on_fork.h" #include "error.h" -#include "string.h" #include "fd.h" +#include "string.h" /** * Spawn a new process and redirect fd 0, 1, and 2. @@ -137,11 +140,11 @@ int para_exec_cmdline_pid(pid_t *pid, const char *cmdline, int *fds) { int ret; char **argv; - char *tmp = para_strdup(cmdline); - split_args(tmp, &argv, " \t"); + ret = create_argv(cmdline, " \t", &argv); + if (ret < 0) + return ret; ret = para_exec(pid, argv[0], argv, fds); - free(argv); - free(tmp); + free_argv(argv); return ret; } diff --git a/fade.c b/fade.c index 8fb02d40..aa7997eb 100644 --- a/fade.c +++ b/fade.c @@ -6,13 +6,9 @@ /** \file fade.c A volume fader and alarm clock for linux. */ +#include #include #include - -#include "fade.cmdline.h" -#include "para.h" -#include "fd.h" - #include #include #include /* EXIT_SUCCESS */ @@ -21,10 +17,13 @@ #include #include #include + +#include "fade.cmdline.h" +#include "para.h" +#include "fd.h" #include "string.h" #include "error.h" - INIT_FADE_ERRLISTS; struct fade_args_info conf; diff --git a/fd.c b/fd.c index 8759d509..3dc490cd 100644 --- a/fd.c +++ b/fd.c @@ -6,6 +6,7 @@ /** \file fd.c Helper functions for file descriptor handling. */ +#include #include #include #include @@ -14,6 +15,7 @@ #include "para.h" #include "error.h" +#include "string.h" /** * Write a buffer to a file descriptor, re-write on short writes. @@ -114,11 +116,10 @@ int file_exists(const char *fn) int para_select(int n, fd_set *readfds, fd_set *writefds, struct timeval *timeout_tv) { - int ret, err; - do { + int ret; + do ret = select(n, readfds, writefds, NULL, timeout_tv); - err = errno; - } while (ret < 0 && err == EINTR); + while (ret < 0 && errno == EINTR); if (ret < 0) return -ERRNO_TO_PARA_ERROR(errno); return ret; @@ -446,18 +447,14 @@ int para_munmap(void *start, size_t length) int write_ok(int fd) { - struct timeval tv = {0, 0}; + struct timeval tv; fd_set wfds; - int ret; -again: + FD_ZERO(&wfds); FD_SET(fd, &wfds); tv.tv_sec = 0; tv.tv_usec = 0; - ret = select(fd + 1, NULL, &wfds, NULL, &tv); - if (ret < 0 && errno == EINTR) - goto again; - return ret; + return para_select(fd + 1, NULL, &wfds, &tv); } /** @@ -480,3 +477,65 @@ void valid_fd_012(void) } } } + +/** + * Traverse the given directory recursively. + * + * \param dirname The directory to traverse. + * \param func The function to call for each entry. + * \param private_data Pointer to an arbitrary data structure. + * + * For each regular file under \a dirname, the supplied function \a func is + * called. The full path of the regular file and the \a private_data pointer + * are passed to \a func. Directories for which the calling process has no + * permissions to change to are silently ignored. + * + * \return Standard. + */ +int for_each_file_in_dir(const char *dirname, + int (*func)(const char *, void *), void *private_data) +{ + DIR *dir; + struct dirent *entry; + int cwd_fd, ret2, ret = para_opendir(dirname, &dir, &cwd_fd); + + if (ret < 0) + return ret == -ERRNO_TO_PARA_ERROR(EACCES)? 1 : ret; + /* scan cwd recursively */ + while ((entry = readdir(dir))) { + mode_t m; + char *tmp; + struct stat s; + + if (!strcmp(entry->d_name, ".")) + continue; + if (!strcmp(entry->d_name, "..")) + continue; + if (lstat(entry->d_name, &s) == -1) + continue; + m = s.st_mode; + if (!S_ISREG(m) && !S_ISDIR(m)) + continue; + tmp = make_message("%s/%s", dirname, entry->d_name); + if (!S_ISDIR(m)) { + ret = func(tmp, private_data); + free(tmp); + if (ret < 0) + goto out; + continue; + } + /* directory */ + ret = for_each_file_in_dir(tmp, func, private_data); + free(tmp); + if (ret < 0) + goto out; + } + ret = 1; +out: + closedir(dir); + ret2 = para_fchdir(cwd_fd); + if (ret2 < 0 && ret >= 0) + ret = ret2; + close(cwd_fd); + return ret; +} diff --git a/fd.h b/fd.h index 7c364120..a21cd9f2 100644 --- a/fd.h +++ b/fd.h @@ -28,3 +28,5 @@ int write_ok(int fd); void valid_fd_012(void); int write_nonblock(int fd, const char *buf, size_t len, size_t max_bytes_per_write); +int for_each_file_in_dir(const char *dirname, + int (*func)(const char *, void *), void *private_data); diff --git a/fec.c b/fec.c index e4cdbaf3..d097b77d 100644 --- a/fec.c +++ b/fec.c @@ -33,6 +33,7 @@ * OF SUCH DAMAGE. */ +#include #include "para.h" #include "error.h" diff --git a/fecdec_filter.c b/fecdec_filter.c index a3cba9bb..7fe87437 100644 --- a/fecdec_filter.c +++ b/fecdec_filter.c @@ -6,6 +6,8 @@ /** \file fecdec_filter.c A filter fec-decodes an audio stream. */ +#include + #include #include "para.h" #include "error.h" diff --git a/file_write.c b/file_write.c index 92777d7d..3286ea87 100644 --- a/file_write.c +++ b/file_write.c @@ -6,8 +6,10 @@ /** \file file_write.c simple output plugin for testing purposes */ +#include #include #include +#include #include "para.h" #include "list.h" @@ -27,20 +29,37 @@ struct private_file_write_data { int check_fd; }; +/* + * Get a random filename. + * + * This is by no means a secure way to create temporary files in a hostile + * directory like \p /tmp. However, we use it only for creating temp files in + * ~/.paraslash, for which it is OK. Result must be freed by the caller. + */ +__must_check __malloc static char *random_filename(void) +{ + char *result, *home = para_homedir(); + struct timeval tv; + + gettimeofday(&tv, NULL); + srandom(tv.tv_usec); + result = make_message("%s/.paraslash/%08lu", home, + para_random(99999999)); + free(home); + return result; +} + static int file_write_open(struct writer_node *wn) { struct private_file_write_data *pfwd = para_calloc( sizeof(struct private_file_write_data)); struct file_write_args_info *conf = wn->conf; char *filename; + if (conf->filename_given) filename = conf->filename_arg; - else { - char *tmp = para_tmpname(), *home = para_homedir(); - filename = make_message("%s/.paraslash/%s", home, tmp); - free(home); - free(tmp); - } + else + filename = random_filename(); wn->private_data = pfwd; pfwd->fd = open(filename, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); if (!conf->filename_given) diff --git a/filter.c b/filter.c index 17b7f2fd..99b12217 100644 --- a/filter.c +++ b/filter.c @@ -6,8 +6,9 @@ /** \file filter.c The stand-alone filter program. */ -#include "para.h" +#include +#include "para.h" #include "filter.cmdline.h" #include "list.h" #include "sched.h" diff --git a/filter.h b/filter.h index 0bb51f5a..23f47f44 100644 --- a/filter.h +++ b/filter.h @@ -95,18 +95,10 @@ struct filter_chain { struct filter_callback { /** All callbacks are organized in a doubly linked list. */ struct list_head node; - /** - * Private data. - * - * May be initialized by the application before registering the callback. This - * pointer is not used by the filter subsystem. It is provided for use within - * the input/output/close callback functions. - */ - void *data; /** * The input callback. * - * In not \p NULL, the filter subsystem calls this function whenever the filter + * If not \p NULL, the filter subsystem calls this function whenever the filter * consumed some or all of its input buffer. A pointer to the buffer of consumed * data, its length and a pointer to the own \a filter_callback structure are passed * to \a input_cb. The input callback is expected to return a negative value on errors. diff --git a/filter_common.c b/filter_common.c index 1b02e1cd..cfe08dd1 100644 --- a/filter_common.c +++ b/filter_common.c @@ -6,6 +6,7 @@ /** \file filter_common.c Common helper functions for filter input/output. */ +#include #include #include @@ -43,7 +44,7 @@ void filter_init(void) */ static void close_filter_callback(struct filter_callback *fcb) { - PARA_NOTICE_LOG("closing filter_callback %p, data: %p\n", fcb, fcb->data); + PARA_NOTICE_LOG("closing filter_callback %p\n", fcb); list_del(&fcb->node); fcb->close(fcb); } @@ -208,15 +209,18 @@ static int parse_filter_args(int filter_num, char *options, void **conf) if (!f->parse_config) return strlen(options)? -E_BAD_FILTER_OPTIONS : filter_num; // PARA_DEBUG_LOG("options: %s\n", options); - argc = split_args(options, &argv, " \t"); -// PARA_DEBUG_LOG("argc = %d, argv[0]: %s\n", argc, argv[0]); + argc = create_argv(options, " \t", &argv); + if (argc < 0) + return -E_BAD_FILTER_OPTIONS; + PARA_DEBUG_LOG("argc = %d, argv[0]: %s\n", argc, argv[0]); for (i = argc - 1; i >= 0; i--) argv[i + 1] = argv[i]; argv[0] = para_strdup(f->name); - argc += 1; + argc++; ret = f->parse_config(argc, argv, conf); - free(argv[0]); - free(argv); + free(argv[argc - 1]); + argv[argc - 1] = NULL; + free_argv(argv); return ret < 0? ret : filter_num; } diff --git a/fsck.c b/fsck.c deleted file mode 100644 index 3b605c2d..00000000 --- a/fsck.c +++ /dev/null @@ -1,989 +0,0 @@ -/* - * Copyright (C) 1997-2009 Andre Noll - * - * Licensed under the GPL v2. For licencing details see COPYING. - */ - -/** \file fsck.c The program used to check an osl table. */ - - -#include -#include - -#include "para.h" -#include "fd.h" -#include "error.h" -#include "osl_core.h" -#include "fsck.cmdline.h" - -static struct fsck_args_info conf; - -INIT_FSCK_ERRLISTS; - -static int loglevel; -INIT_STDERR_LOGGING(loglevel); - -/* taken from git */ -signed char hexval_table[256] = { - -1, -1, -1, -1, -1, -1, -1, -1, /* 00-07 */ - -1, -1, -1, -1, -1, -1, -1, -1, /* 08-0f */ - -1, -1, -1, -1, -1, -1, -1, -1, /* 10-17 */ - -1, -1, -1, -1, -1, -1, -1, -1, /* 18-1f */ - -1, -1, -1, -1, -1, -1, -1, -1, /* 20-27 */ - -1, -1, -1, -1, -1, -1, -1, -1, /* 28-2f */ - 0, 1, 2, 3, 4, 5, 6, 7, /* 30-37 */ - 8, 9, -1, -1, -1, -1, -1, -1, /* 38-3f */ - -1, 10, 11, 12, 13, 14, 15, -1, /* 40-47 */ - -1, -1, -1, -1, -1, -1, -1, -1, /* 48-4f */ - -1, -1, -1, -1, -1, -1, -1, -1, /* 50-57 */ - -1, -1, -1, -1, -1, -1, -1, -1, /* 58-5f */ - -1, 10, 11, 12, 13, 14, 15, -1, /* 60-67 */ - -1, -1, -1, -1, -1, -1, -1, -1, /* 68-67 */ - -1, -1, -1, -1, -1, -1, -1, -1, /* 70-77 */ - -1, -1, -1, -1, -1, -1, -1, -1, /* 78-7f */ - -1, -1, -1, -1, -1, -1, -1, -1, /* 80-87 */ - -1, -1, -1, -1, -1, -1, -1, -1, /* 88-8f */ - -1, -1, -1, -1, -1, -1, -1, -1, /* 90-97 */ - -1, -1, -1, -1, -1, -1, -1, -1, /* 98-9f */ - -1, -1, -1, -1, -1, -1, -1, -1, /* a0-a7 */ - -1, -1, -1, -1, -1, -1, -1, -1, /* a8-af */ - -1, -1, -1, -1, -1, -1, -1, -1, /* b0-b7 */ - -1, -1, -1, -1, -1, -1, -1, -1, /* b8-bf */ - -1, -1, -1, -1, -1, -1, -1, -1, /* c0-c7 */ - -1, -1, -1, -1, -1, -1, -1, -1, /* c8-cf */ - -1, -1, -1, -1, -1, -1, -1, -1, /* d0-d7 */ - -1, -1, -1, -1, -1, -1, -1, -1, /* d8-df */ - -1, -1, -1, -1, -1, -1, -1, -1, /* e0-e7 */ - -1, -1, -1, -1, -1, -1, -1, -1, /* e8-ef */ - -1, -1, -1, -1, -1, -1, -1, -1, /* f0-f7 */ - -1, -1, -1, -1, -1, -1, -1, -1, /* f8-ff */ -}; - -int asc_to_hash(const char *asc_hash, int len, HASH_TYPE *hash) -{ - int i = 0; - const unsigned char *asc = (const unsigned char *) asc_hash; - - while (*asc && i++ < len) { - unsigned int val = (hexval_table[asc[0]] << 4) | hexval_table[asc[1]]; - if (val & ~0xff) - return -1; - *hash++ = val; - asc += 2; - - } - return 1; -} - -/* - * check for object boundary violations - * - * test whether the range pointed to by the index entry for a given cell is - * contained in mapped data file. This should always be the case. Otherwise - * we are in real trouble. - */ -static int check_range(struct osl_table *t, uint32_t row_num, uint32_t col_num) -{ - char *index_entry; - struct osl_object obj; - struct osl_column *col; - int ret; - char *map_start, *obj_start; - - ret = get_cell_index(t, row_num, col_num, &index_entry); - if (ret < 0) - return ret; - ret = get_mapped_object(t, col_num, row_num, &obj); - if (ret < 0) - return ret; - col = t->columns + col_num; - obj_start = obj.data; - map_start = col->data_map.data; -// PARA_INFO_LOG("obj: %p..%p\n", obj_start, obj_start + obj.size); -// PARA_INFO_LOG("map: %p..%p\n", map_start, map_start + col->data_map.size); - if (obj_start < map_start || obj_start + obj.size > map_start + col->data_map.size) { - PARA_CRIT_LOG("range violation in row %u, col %u\n", row_num, - col_num); - return -E_RANGE_VIOLATION; - } - PARA_DEBUG_LOG("col %u: ok\n", col_num); - return 1; -} - -/* - * check all cells of the given table for boundary violations - */ -static int check_index_ranges(struct osl_table *t) -{ - int i, j, ret; - - PARA_INFO_LOG("checking for range violations in index\n"); - //PARA_DEBUG_LOG("%d rows. %d columns\n", t->num_rows, t->desc->num_columns); - t->num_invalid_rows = 0; - for (i = 0; i < t->num_rows; i++) { - if (row_is_invalid(t, i)) { - t->num_invalid_rows++; - continue; - } - for (j = 0; j < t->desc->num_columns; j++) { /* FXIME */ - const struct osl_column_description *cd = - get_column_description(t->desc, j); - if (cd->storage_type != OSL_MAPPED_STORAGE) - continue; - ret = check_range(t, i, j); - if (ret < 0) { - if (ret != -E_INVALID_OBJECT && - ret != -E_RANGE_VIOLATION) - goto err; - if (ret == -E_INVALID_OBJECT) { - PARA_CRIT_LOG("row %d, col %d maps to an " - "invalid object\n", i, j); - } - ret = mark_row_invalid(t, i); - if (ret < 0) - goto err; - t->num_invalid_rows++; - break; - } - } - - } - if (t->num_invalid_rows) - PARA_NOTICE_LOG("ranges OK. %d invalid row(s) detected\n", - t->num_invalid_rows); - else - PARA_INFO_LOG("no invalid rows, no range violations, good\n"); - return 1; -err: - return ret; -} - -static int move_index_entry(struct osl_table *t, uint32_t dest, uint32_t src) -{ - char *dest_ie, *src_ie; - int ret = get_row_index(t, dest, &dest_ie); - - if (ret < 0) - return ret; - ret = get_row_index(t, src, &src_ie); - if (ret < 0) - return ret; - PARA_INFO_LOG("moving entry #%u to position %u\n", src, dest); - memcpy(dest_ie, src_ie, t->row_index_size); - return 1; -} - -static int map_index(const struct osl_table_description *desc, struct osl_object *map) -{ - char *filename = index_filename(desc); - int ret; - - ret = mmap_full_file(filename, O_RDWR, &map->data, &map->size, NULL); - PARA_DEBUG_LOG("mapping index %s: ret: %d, size: %zu\n", filename, ret, map->size); - free(filename); - return ret; -} - -static int prune_invalid_rows_from_index(struct osl_table *t) -{ - uint32_t top = 0, bottom; - char *filename; - int ret; - - if (!t->num_invalid_rows) { - PARA_INFO_LOG("all rows are valid, good\n"); - return 1; - } - PARA_NOTICE_LOG("deleting %u invalid row(s) (%d bytes) from index\n", - t->num_invalid_rows, t->row_index_size * t->num_invalid_rows); - bottom = t->num_rows - 1; - while (top < bottom) { - if (!row_is_invalid(t, top)) { - top++; - continue; - } - while (bottom > top) { - if (row_is_invalid(t, bottom)) { - bottom--; - continue; - } - /* move bottom index entry to top */ - move_index_entry(t, top, bottom); - bottom--; - top++; - break; - } - } - PARA_DEBUG_LOG("unmapping index\n"); - para_munmap(t->index_map.data, t->index_map.size); - filename = index_filename(t->desc); - ret = para_truncate(filename, t->row_index_size - * t->num_invalid_rows); - free(filename); - if (ret < 0) - return ret; - ret = map_index(t->desc, &t->index_map); - if (ret < 0) - return ret; - t->num_rows = table_num_rows(t); - return 1; -} - -static int check_for_invalid_objects(struct osl_table *t, uint32_t **lost_bytes) -{ - int i, j, ret; - const struct osl_column_description *cd; - uint32_t *loss = para_malloc(sizeof(uint32_t) * t->desc->num_columns); - - PARA_INFO_LOG("looking for mapped objects not contained in index\n"); - /* first count used bytes */ - FOR_EACH_MAPPED_COLUMN(i, t, cd) { - loss[i] = t->columns[i].data_map.size; - for (j = 0; j < t->num_rows; j++) { - struct osl_object obj; - ret = get_mapped_object(t, i, j, &obj); - if (ret >= 0) { - loss[i] -= obj.size + 1; /* add one for header byte */ - continue; - } - if (ret != -E_INVALID_OBJECT) - goto err; - PARA_CRIT_LOG("row %d, col %d points to an invalid " - "mapped object, bad\n", j, i); - } - } - ret = 0; - FOR_EACH_MAPPED_COLUMN(i, t, cd) { - if (loss[i]) { - PARA_NOTICE_LOG("column %u contains %u lost bytes\n", - i, loss[i]); - ret = 1; - } - } - if (!ret) - PARA_INFO_LOG("all mapped objects are valid, good\n"); - *lost_bytes = loss; - return ret; -err: - free(loss); - return ret; -} - -/* prune_invalid_rows() must be run on the table before calling this */ -static int prune_mapped_column(struct osl_table *t, uint32_t col_num, int fd) -{ - int i, ret; - uint32_t written = 0; - struct osl_column *col = t->columns + col_num; - - PARA_INFO_LOG("pruning col %u\n", col_num); - for (i = 0; i < t->num_rows; i++) { - struct osl_object obj; - char *index_entry; - - PARA_DEBUG_LOG("checking row %u/%u\n", i, t->num_rows); - ret = get_mapped_object(t, col_num, i, &obj); - if (ret < 0) - return ret; - ret = para_write_all(fd, (char *)(obj.data) - 1, obj.size + 1); - if (ret < 0) - return ret; - written += obj.size + 1; - ret = get_row_index(t, i, &index_entry); - if (ret < 0) - return ret; - update_cell_index(index_entry, col, written, obj.size); - } - return 1; -} - -static int prune_objects(struct osl_table *t, uint32_t *lost_bytes) -{ - int i, ret; - const struct osl_column_description *cd; - char **col_filenames = para_calloc(t->desc->num_columns * sizeof(char *)); - char **new_col_filenames = para_calloc(t->desc->num_columns * sizeof(char *)); - char *idx_filename = index_filename(t->desc); - char *old_idx_filename = make_message("%s.bak", idx_filename); - int fd; - - PARA_NOTICE_LOG("removing unreferenced objects from data files\n"); - /* first make a copy of the index */ - ret = para_open(old_idx_filename, O_WRONLY | O_CREAT | O_EXCL, 0644); - if (ret < 0) - goto out_free; - fd = ret; - ret = para_write_all(fd, t->index_map.data, t->index_map.size); - close(fd); - if (ret < 0) - goto out_free; - FOR_EACH_MAPPED_COLUMN(i, t, cd) { - if (!lost_bytes[i]) - continue; - col_filenames[i] = column_filename(t, i); - new_col_filenames[i] = make_message("%s.fsck", col_filenames[i]); - ret = para_open(new_col_filenames[i], O_WRONLY | O_CREAT | O_EXCL, 0644); - if (ret < 0) - goto out_unlink_data; - fd = ret; - ret = prune_mapped_column(t, i, fd); - close(fd); - if (ret < 0) - goto out_unlink_data; - } - ret = unmap_table(t, OSL_MARK_CLEAN); - if (ret < 0) - goto out_unlink_data; - FOR_EACH_MAPPED_COLUMN(i, t, cd) { - if (!lost_bytes[i]) - continue; - ret = para_rename(new_col_filenames[i], col_filenames[i]); - if (ret < 0) { /* we're kinda screwed here */ - PARA_CRIT_LOG("rename of col %i failed: %s\n", i, - strerror(errno)); - goto out_free; - } - } - unlink(old_idx_filename); - ret = map_table(t, 0); - goto out_free; -out_unlink_data: - FOR_EACH_MAPPED_COLUMN(i, t, cd) - unlink(new_col_filenames[i]); -out_free: - free(old_idx_filename); - free(idx_filename); - FOR_EACH_MAPPED_COLUMN(i, t, cd) { - free(col_filenames[i]); - free(new_col_filenames[i]); - } - free(col_filenames); - free(new_col_filenames); - return ret; -} - -static struct osl_column_description hash_tree_table_cols[] = { - { - .storage_type = OSL_NO_STORAGE, - .storage_flags = OSL_RBTREE | OSL_FIXED_SIZE | OSL_UNIQUE, - .name = "hash", - .compare_function = uint32_compare, - .data_size = HASH_SIZE - }, -}; - -static const struct osl_table_description hash_tree_table_desc = { - .dir = "/", /* irrelevant */ - .name = "hash_tree", - .num_columns = 1, - .flags = 0, - .column_descriptions = hash_tree_table_cols -}; - -/** - * The hash_tree table contains all hashes of the disk storage name column. - * of each row. It is used for checking if a disk storage file has a reference - * in the table. - */ -static struct osl_table *hash_tree_table; -static HASH_TYPE *hashes; - -static int check_disk_storage_column(struct osl_table *t, int row_num, - int col_num, char *ds_name, unsigned *num_missing_objects) -{ - int ret; - struct stat statbuf; - char *path = disk_storage_path(t, col_num, ds_name); - unsigned dsnc = t->disk_storage_name_column; - struct osl_object obj; - - PARA_DEBUG_LOG("checking if %s is a regular file\n", path); - ret = stat(path, &statbuf); - if (ret < 0 && errno == ENOENT) { - struct osl_row *row; - (*num_missing_objects)++; - PARA_ERROR_LOG("row %d: object %s is missing\n", row_num, path); - PARA_NOTICE_LOG("trying to delete row %d\n", row_num); - ret = osl_get_row(t, dsnc, &obj, &row); - if (ret < 0) { - PARA_CRIT_LOG("unable to get row %d\n", row_num); - mark_row_invalid(t, row_num); - PARA_CRIT_LOG("Please re-run fsck\n"); - goto out; - } - ret = osl_del_row(t, row); - if (ret < 0) - goto out; - } -out: - free(path); - if (ret < 0) - return ret; - ret = -E_NOT_A_REGULAR_FILE; - if (!(S_IFREG & statbuf.st_mode)) - return ret; - return 1; -} - -static int check_disk_storage_presence(struct osl_table *t) -{ - int ret, i, j; - struct osl_object obj, hash_obj = {.size = HASH_SIZE}; - char *ds_name; - const struct osl_column_description *cd; - unsigned dsnc = t->disk_storage_name_column, missing_objects = 0; - - if (!t->num_rows) - return 1; - hashes = para_malloc(t->num_rows * HASH_SIZE); - PARA_INFO_LOG("looking for missing disk storage objects\n"); - for (i = 0; i < t->num_rows; i++) { - if (row_is_invalid(t, i)) - continue; - ret = get_mapped_object(t, dsnc, i, &obj); - if (ret < 0) - return ret; - hash_object(&obj, hashes + i * HASH_SIZE); - hash_obj.data = hashes + i * HASH_SIZE; - osl_add_row(hash_tree_table, &hash_obj); - ds_name = disk_storage_name_of_hash(t, hashes + i * HASH_SIZE); - FOR_EACH_DISK_STORAGE_COLUMN(j, t, cd) { - ret = check_disk_storage_column(t, i, j, ds_name, - &missing_objects); - if (ret < 0) - goto err; - } - free(ds_name); - } - if (!missing_objects) - PARA_INFO_LOG("all referenced disk storage objects exist, good\n"); - else - PARA_NOTICE_LOG("%d missing object(s)\n", missing_objects); - return missing_objects; -err: - free(ds_name); - return ret; -} - -static int dummy_compare(const struct osl_object *obj1, const struct osl_object *obj2) -{ - if (obj1 < obj2) - return -1; - if (obj1 > obj2) - return 1; - return 0; -} - -static unsigned files_pruned; - -int prune_disk_storage_file(const char *path, void *private_data) -{ - HASH_TYPE hash[HASH_SIZE]; - unsigned flags = *(unsigned *)private_data; - struct osl_object obj = {.data = hash, .size = HASH_SIZE}; - struct osl_row *row; - int ret = -1; - size_t len = strlen(path); - - - PARA_DEBUG_LOG("path: %s\n", path); - if (flags & OSL_LARGE_TABLE) { - if (len < HASH_SIZE * 2 + 2) - goto invalid; -// PARA_NOTICE_LOG("p: %s\n", path + len - 2 * HASH_SIZE - 1); - ret = asc_to_hash(path + len - 2 * HASH_SIZE - 1, 1, hash); - if (ret < 0) - goto invalid; - ret = asc_to_hash(path + len - 2 * HASH_SIZE + 2, HASH_SIZE - 1, - hash + 1); - if (ret < 0) - goto invalid; -// PARA_INFO_LOG("high: %x, low: %x, hash: %x\n", high, low, hash); - } else { - if (len < 2 * HASH_SIZE + 1) - goto invalid; - ret = asc_to_hash(path + len - 2 * HASH_SIZE, 2 * HASH_SIZE, hash); - if (ret < 0) - goto invalid; -// PARA_INFO_LOG("hash: %x\n", hash); - } -#if 0 -{ - char asc[2 * HASH_SIZE + 1]; - hash_to_asc(hash, asc); - PARA_NOTICE_LOG("before: %s\nafter: %s\n", path, asc); -} -#endif - ret = osl_get_row(hash_tree_table, 0, &obj, &row); - if (ret >= 0) - return 1; - PARA_NOTICE_LOG("unreferenced file in hash dir: %s\n", path); - goto remove; -invalid: - PARA_ERROR_LOG("could not read hash value of %s\n", path); -remove: - PARA_NOTICE_LOG("removing %s\n", path); - unlink(path); - files_pruned++; - return 1; -} - -static int prune_disk_storage_files(struct osl_table *t) -{ - int i, ret = 1; - const struct osl_column_description *cd; - - PARA_INFO_LOG("looking for unreferenced disk storage files\n"); - FOR_EACH_DISK_STORAGE_COLUMN(i, t, cd) { - char *dirname = column_filename(t, i); - ret = for_each_file_in_dir(dirname, prune_disk_storage_file, - (unsigned *)&t->desc->flags); - free(dirname); - } - if (files_pruned) - PARA_NOTICE_LOG("%u disk storage files deleted\n", - files_pruned); - else - PARA_INFO_LOG("all files are are referenced, good\n"); - return ret; -} - -static int check_disk_storage_columns(struct osl_table *t) -{ - int ret, i; - const struct osl_column_description *cd; - - if (!t->num_disk_storage_columns) { - PARA_INFO_LOG("no disk storage columns in table '%s', " - "skipping checks\n", t->desc->name); - return 1; - } - FOR_EACH_COLUMN(i, t->desc, cd) - t->desc->column_descriptions[i].compare_function = dummy_compare; - ret = init_rbtrees(t); - if (ret < 0) - return ret; - PARA_INFO_LOG("creating rbtree for disk storage hash values\n"); - ret = osl_open_table(&hash_tree_table_desc, &hash_tree_table); - if (ret < 0) - goto out; - ret = check_disk_storage_presence(t); - if (ret < 0) - goto out_close_hash_tree; - ret = prune_disk_storage_files(t); -out_close_hash_tree: - osl_close_table(hash_tree_table, 0); - free(hashes); - hashes = NULL; -out: - clear_rbtrees(t); /* TODO why are we doing that here? Seems odd */ - return ret; -} - -static void set_dummy_contents(struct osl_table_description *desc) -{ - int i; - struct osl_column_description *cd; - - for (i = 0; i < desc->num_columns; i++) { - cd = get_column_description(desc, i); - cd->compare_function = dummy_compare; - } -} - -static int fsck_init(struct osl_table_description *desc, struct osl_table **t) -{ - struct osl_object map; - int ret = map_index(desc, &map); - - if (ret < 0) - goto out; - ret = read_table_desc(&map, desc); - if (ret < 0) { - para_munmap(map.data, map.size); - goto out; - } - set_dummy_contents(desc); - ret = init_table_structure(desc, t); - if (ret < 0) { - para_munmap(map.data, map.size); - goto out; - } - PARA_DEBUG_LOG("unmapping index\n"); - para_munmap(map.data, map.size); - if (conf.force_given) - ret = map_table(*t, (MAP_TBL_FL_IGNORE_DIRTY)); - else - ret = map_table(*t, 0); - if (ret >= 0) - (*t)->num_rows = table_num_rows(*t); -out: - return ret; -} - -static void fsck_cleanup(struct osl_table *t) -{ - int i; - - if (!t) - return; - if (t->desc->column_descriptions) { - struct osl_column_description *cd; - for (i = 0; i < t->desc->num_columns; i++) { - cd = get_column_description(t->desc, i); - free((char*)cd->name); - } - free(t->desc->column_descriptions); - } - free(t->columns); - free(t); - -} - -#define ST_CASE(st) case st: return #st - -const char *get_asc_storage_type(enum osl_storage_type st) -{ - switch (st) { - ST_CASE(OSL_MAPPED_STORAGE); - ST_CASE(OSL_DISK_STORAGE); - ST_CASE(OSL_NO_STORAGE); - } - return NULL; -} - -#define APPEND_ASC_SF(sf, flag, str) do { if (sf & flag) { \ - if (str) str = para_strcat(str, " | " # flag); \ - else str = para_strdup(#flag); }} while (0) - - -char *get_asc_storage_flags(enum osl_storage_type sf) -{ - char *asc_sf = NULL; - - APPEND_ASC_SF(sf, OSL_RBTREE, asc_sf); - APPEND_ASC_SF(sf, OSL_FIXED_SIZE, asc_sf); - APPEND_ASC_SF(sf, OSL_UNIQUE, asc_sf); - return asc_sf; -} - -static int dump_table_desc(struct osl_table *t, int fd) -{ - const struct osl_table_description *desc = t->desc; - int ret, i; - struct osl_column_description *cd; - char *msg = make_message("static struct osl_column_description cols[] = {\n"); - ret = para_write_all(fd, msg, strlen(msg)); - if (ret < 0) - return ret; - free(msg); - FOR_EACH_COLUMN(i, desc, cd) { - const char *asc_st; - msg = make_message("\t[%d] = {\n", i); - ret = para_write_all(fd, msg, strlen(msg)); - if (ret < 0) - return ret; - free(msg); - asc_st = get_asc_storage_type(cd->storage_type); - msg = make_message("\t\t.storage_type = %s,\n", asc_st); - ret = para_write_all(fd, msg, strlen(msg)); - if (ret < 0) - return ret; - free(msg); - if (cd->storage_flags) { - char *asc_sf = get_asc_storage_flags(cd->storage_flags); - msg = make_message("\t\t,storage_flags = %s,\n", asc_sf); - free(asc_sf); - ret = para_write_all(fd, msg, strlen(msg)); - if (ret < 0) - return ret; - free(msg); - } - if (cd->storage_flags & OSL_FIXED_SIZE) { - msg = make_message("\t\t.data_size = %u,\n", cd->data_size); - ret = para_write_all(fd, msg, strlen(msg)); - if (ret < 0) - return ret; - free(msg); - } - msg = make_message("\t\t.name = \"%s\",\n", cd->name); - ret = para_write_all(fd, msg, strlen(msg)); - if (ret < 0) - return ret; - free(msg); - if (cd->storage_flags & OSL_RBTREE) { - msg = make_message("\t\t.compare_function = compare_func,\n"); - ret = para_write_all(fd, msg, strlen(msg)); - if (ret < 0) - return ret; - free(msg); - } - msg = make_message("\t},\n"); - ret = para_write_all(fd, msg, strlen(msg)); - if (ret < 0) - return ret; - free(msg); - } - msg = make_message("};\n"); - ret = para_write_all(fd, msg, strlen(msg)); - if (ret < 0) - return ret; - free(msg); - return 1; -} - -static int dump_row(struct osl_table *t, unsigned row_num, const char *row_dir) -{ - int ret, i; - const struct osl_column_description *cd; - unsigned dsnc; - struct osl_object obj; - char *ds_name; - HASH_TYPE hash[HASH_SIZE]; - char *filename; - - FOR_EACH_MAPPED_COLUMN(i, t, cd) { - ret = get_mapped_object(t, i, row_num, &obj); - if (ret < 0) - return ret; - filename = make_message("%s/col_%03u", row_dir, i); - ret = para_write_file(filename, obj.data, obj.size); - free(filename); - if (ret < 0) - return ret; - } - if (!t->num_disk_storage_columns) - return 1; - dsnc = t->disk_storage_name_column; - ret = get_mapped_object(t, dsnc, row_num, &obj); - if (ret < 0) - return ret; - hash_object(&obj, hash); - ds_name = disk_storage_name_of_hash(t, hash); - FOR_EACH_DISK_STORAGE_COLUMN(i, t, cd) { - filename = disk_storage_path(t, i, ds_name); - ret = mmap_full_file(filename, O_RDONLY, &obj.data, &obj.size, NULL); - free(filename); - if (ret < 0) - goto out; - filename = make_message("%s/col_%03u", row_dir, i); - ret = para_write_file(filename, obj.data, obj.size); - free(filename); - if (ret < 0) - goto out; - } - ret = 1; -out: - free(ds_name); - return ret; -} - -static int dump_rows(char *dump_dir, struct osl_table *t) -{ - unsigned i; - char *current_dir = NULL; - int ret = 0; - - for (i = 0; i < t->num_rows; i++) { - char *row_dir; - if (row_is_invalid(t, i)) - continue; - if (!(i % 1000)) { - free(current_dir); - current_dir = make_message("%s/rows_%u-%u", dump_dir, i, i + 999); - PARA_NOTICE_LOG("dumping rows %u - %u\n", i, i + 999); - ret = para_mkdir(current_dir, 0777); - if (ret < 0 && !is_errno(-ret, EEXIST)) - goto out; - } - row_dir = make_message("%s/row_%03u", current_dir, i); - ret = para_mkdir(row_dir, 0777); - if (ret < 0 && !is_errno(-ret, EEXIST)) { - free(row_dir); - goto out; - } - ret = dump_row(t, i, row_dir); - free(row_dir); - if (ret < 0) - goto out; - } -out: - free(current_dir); - return ret; -} - -static int dump_table(char *dump_dir, struct osl_table_description *desc) -{ - struct osl_table *t = NULL; - int fd, ret = fsck_init(desc, &t); - char *desc_file; - char *table_dump_dir = NULL; - - if (ret < 0) - goto out; - ret = para_mkdir(dump_dir, 0777); - if (ret < 0 && !is_errno(-ret, EEXIST)) - goto out; - table_dump_dir = make_message("%s/%s", dump_dir, desc->name); - ret = para_mkdir(table_dump_dir, 0777); - if (ret < 0 && !is_errno(-ret, EEXIST)) - goto out; - desc_file = make_message("%s/table_description.c", table_dump_dir); - ret = para_open(desc_file, O_WRONLY | O_CREAT | O_EXCL, 0644); - free(desc_file); - if (ret < 0) - goto out; - fd = ret; - ret = dump_table_desc(t, fd); - close(fd); - if (ret < 0) - goto out; - ret = dump_rows(table_dump_dir, t); -out: - free(table_dump_dir); - fsck_cleanup(t); - return ret; -} - -static int fsck(struct osl_table_description *desc) -{ - int ret; - struct osl_table *t = NULL; - uint32_t *lost_bytes = NULL; - - ret = fsck_init(desc, &t); - if (ret < 0) - goto out; - ret = check_index_ranges(t); - if (ret < 0) - goto out_unmap; - ret = check_disk_storage_columns(t); - if (ret < 0) - goto out_unmap; - ret = prune_invalid_rows_from_index(t); - if (ret < 0) - goto out_unmap; - ret = check_for_invalid_objects(t, &lost_bytes); - if (ret < 0) - goto out_unmap; - if (ret > 0) { /* at least one mapped data file needs pruning */ - ret = prune_objects(t, lost_bytes); - if (ret < 0) - goto out_unmap; - } - free(lost_bytes); -out_unmap: - unmap_table(t, OSL_MARK_CLEAN); -out: - fsck_cleanup(t); - return ret; -} - -static int check_table(char *base_dir, char *table_name) -{ - struct osl_table_description desc = { - .column_descriptions = NULL, - .dir = base_dir, - .name = table_name - }; - int ret; - - PARA_INFO_LOG("checking table %s\n", table_name); - if (!conf.no_fsck_given) { - ret = fsck(&desc); - if (ret < 0) - goto out; - } - ret = 1; - if (!conf.dump_dir_given || !*conf.dump_dir_arg) - goto out; - ret = dump_table(conf.dump_dir_arg, &desc); -out: - if (ret < 0) - PARA_ERROR_LOG("failed to check table %s\n", table_name); - else - PARA_NOTICE_LOG("successfully checked table %s\n", table_name); - return ret; -} - -static int check_all_tables(char *base_dir) -{ - DIR *dir; - struct dirent *entry; - int cwd_fd, ret2, ret = para_opendir(base_dir, &dir, &cwd_fd); - - if (ret < 0) - return ret; - while ((entry = readdir(dir))) { - mode_t m; - struct stat s; - if (!strcmp(entry->d_name, ".")) - continue; - if (!strcmp(entry->d_name, "..")) - continue; - if (lstat(entry->d_name, &s) == -1) - continue; - m = s.st_mode; - if (!S_ISDIR(m)) - continue; - ret = check_table(base_dir, entry->d_name); - if (ret < 0) - break; - } - closedir(dir); - ret2 = para_fchdir(cwd_fd); - if (ret2 < 0 && ret >= 0) - ret = ret2; - close(cwd_fd); - return ret; -} - -/** - * The praslash database check program. - * - * \param argc Usual arg count. - * \param argv Usual arg vector. - * - * \return \p EXIT_SUCCESS or \p EXIT_FAILURE. - */ -int main(int argc, char **argv) -{ - int i, ret; - char *base_dir = NULL; - - ret = fsck_cmdline_parser(argc, argv, &conf); - if (ret < 0) { - ret = -E_FSCK_SYNTAX; - goto out; - } - HANDLE_VERSION_FLAG("fsck", conf); - loglevel = get_loglevel_by_name(conf.loglevel_arg); - if (conf.base_dir_given) - base_dir = para_strdup(conf.base_dir_arg); - else { - char *home = para_homedir(); - base_dir = make_message("%s/.paraslash/afs_database", home); - free(home); - } - if (!conf.inputs_num) { - ret = check_all_tables(base_dir); - goto out; - } - for (i = 0; i < conf.inputs_num; i++) { - ret = check_table(base_dir, conf.inputs[i]); - if (ret < 0) - break; - } -out: - if (ret < 0) { - PARA_ERROR_LOG("%s%s: %s\n", - base_dir? "base_dir: " : "", - base_dir? base_dir : "", - para_strerror(-ret) - ); - } else - PARA_NOTICE_LOG("success\n"); - if (base_dir) - free(base_dir); - return ret < 0? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/ggo/client.m4 b/ggo/client.m4 index 1ae5a38a..5770ff0a 100644 --- a/ggo/client.m4 +++ b/ggo/client.m4 @@ -11,7 +11,3 @@ option "key_file" k "(default='~/.paraslash/key.')" string typestr="filena include(loglevel.m4) include(config_file.m4) - - -option "plain" - "request an uncrypted session" flag off - diff --git a/ggo/grab_client.ggo b/ggo/grab_client.ggo deleted file mode 100644 index 5527143b..00000000 --- a/ggo/grab_client.ggo +++ /dev/null @@ -1,33 +0,0 @@ -option "filter_num" f - "point of filter chain to grab" - int typestr="num" - default="0" - optional - -option "slot" s - "only grab this slot; grab any slot if negative" - int typestr="num" - default="-1" - optional - -option "audio_format" a - "only grab this type of input stream; - grab any if empty" - string typestr="name" - default="" - optional - -option "input_grab" i - "grab the filter input instead of its output" - flag off - -option "one_shot" o - "stop grabbing if audio file changes" - flag off - -option "mode" m - "select grab mode" - typestr="grab_mode" - values="sloppy","aggressive","pedantic" - default="sloppy" - optional diff --git a/ggo/makefile b/ggo/makefile index a7715208..936b23eb 100644 --- a/ggo/makefile +++ b/ggo/makefile @@ -1,16 +1,5 @@ module_ggo_opts := --set-version="($(PACKAGE_STRING), $(codename))" -grab_client.cmdline.h grab_client.cmdline.c: $(ggo_dir)/grab_client.ggo - gengetopt $(module_ggo_opts) \ - -S \ - --set-package=grab \ - --no-handle-help \ - --no-handle-error \ - --no-handle-version \ - --arg-struct-name=grab_client_args_info \ - --file-name=$(subst .ggo,,$(data; - struct timeval tv = {0, 100}; - int ret; + int i; -// PARA_INFO_LOG("writing %d bytes to fd %d\n", len, gc->fd); - fd_set wfds; - FD_ZERO(&wfds); - FD_SET(gc->fd, &wfds); - ret = para_select(gc->fd + 1, NULL, &wfds, &tv); - if (ret <= 0) { - if (gc->mode == GRAB_PEDANTIC) - return -E_PEDANTIC_GRAB; - if (gc->mode == GRAB_SLOPPY) - return 1; - } -rewrite: - ret = write(gc->fd, buf, len); - if (ret < 0) { - ret = -E_GC_WRITE; - gc->error = E_GC_WRITE; - } else { - if (ret != len) { - if (gc->mode == GRAB_PEDANTIC) - return -E_PEDANTIC_GRAB; - if (gc->mode == GRAB_AGGRESSIVE) { - len -= ret; - memmove(buf, buf + ret, len); - goto rewrite; - } - } + FOR_EACH_SLOT(i) { + struct slot_info *s = &slot[i]; + if (s->format < 0 || !s->fc) + continue; + if (format >= 0 && s->format != format) + continue; + if (num_filters(i) <= filternum) + continue; + /* success */ + return s->fc->filter_nodes + filternum; } - return ret; + return NULL; } -/* TODO: gengetopt can handle the grab client modes */ -static int check_gc_args(struct grab_client *gc) +static int gc_write(char *buf, size_t len, struct filter_callback *fcb) { - int i; - struct grab_client_args_info *c = gc->conf; - const char **mv = grab_client_cmdline_parser_mode_values; + struct grab_client *gc = container_of(fcb, struct grab_client, fcb); + size_t written = 0; - PARA_INFO_LOG("filter_num: %d\n", c->filter_num_arg); - for (i = 0; mv[i]; i++) - if (!strcmp(c->mode_arg, mv[i])) - break; - if (!mv[i]) - return -E_GC_SYNTAX; - gc->mode = i; - gc->audio_format_num = -1; - if (c->audio_format_given) { - gc->audio_format_num = get_audio_format_num(c->audio_format_arg); - if (gc->audio_format_num < 0) - return gc->audio_format_num; + while (written < len) { + int ret = write_ok(gc->fd); + if (ret < 0) + goto err; + if (ret == 0) { /* fd not ready */ + if (gc->mode == GM_PEDANTIC) + goto err; + if (gc->mode == GM_SLOPPY) + return 1; + } + ret = write(gc->fd, buf + written, len - written); + if (ret < 0) { + if (errno != EAGAIN && errno != EINTR) + goto err; + if (gc->mode == GM_PEDANTIC) + goto err; + if (gc->mode == GM_SLOPPY) + return 1; + } else + written += ret; } - if (c->slot_arg >= MAX_STREAM_SLOTS) - return -E_BAD_GC_SLOT; - if (c->filter_num_arg < 0) - return -E_BAD_GC_FILTER_NUM; - if (c->audio_format_given) { - if (num_filters(gc->audio_format_num) <= c->filter_num_arg) - return -E_BAD_GC_FILTER_NUM; - } else - if (c->filter_num_arg >= max_num_filters()) - return -E_BAD_GC_FILTER_NUM; - return 1; +err: + gc->error = -E_GC_WRITE; + return -E_GC_WRITE; } static void add_inactive_gc(struct grab_client *gc) @@ -119,27 +99,14 @@ static void add_inactive_gc(struct grab_client *gc) para_list_add(&gc->node, &inactive_grab_client_list); } -static void gc_free(struct grab_client *gc) -{ - int i; - - for (i = 0; i < gc->argc; i++) - free(gc->argv[i]); - free(gc->argv); - free(gc->conf); - free(gc); - -} - static void gc_close(struct filter_callback *fcb) { - struct grab_client *gc = fcb->data; + struct grab_client *gc = container_of(fcb, struct grab_client, fcb); - if (gc->conf->one_shot_given || gc->error) { + if ((gc->flags & GF_ONE_SHOT) || gc->error < 0) { PARA_INFO_LOG("closing fd %d (grab client %p)\n", gc->fd, gc); close(gc->fd); - gc_free(gc); - /* close on fork ?*/ + free(gc); return; } add_inactive_gc(gc); @@ -163,7 +130,6 @@ void activate_grab_client(struct grab_client *gc, struct filter_node *fn) /** * Activate inactive grab clients if possible. * - * \param slot_num Audiod's slot for the new audio file. * \param audio_format_num The number of the audio format of the new audio file. * \param fc The filter chain containing the activated filters. * @@ -176,85 +142,125 @@ void activate_grab_client(struct grab_client *gc, struct filter_node *fn) * \sa filter_chain_info::filters, inactive_grab_client_list, * activate_grab_client. */ -void activate_inactive_grab_clients(int slot_num, int audio_format_num, +void activate_inactive_grab_clients(int audio_format_num, struct filter_chain *fc) { struct grab_client *gc, *tmp; - int filter_num; struct filter_node *fn; list_for_each_entry_safe(gc, tmp, &inactive_grab_client_list, node) { -// PARA_INFO_LOG("checking inactive grab client %p\n", gc); - if (gc->conf->slot_arg >= 0 && gc->conf->slot_arg != slot_num) - continue; if (gc->audio_format_num >= 0 && gc->audio_format_num != audio_format_num) continue; - filter_num = gc->conf->filter_num_arg; - if (filter_num >= num_filters(gc->audio_format_num)) + if (gc->filter_num >= num_filters(audio_format_num)) continue; - fn = fc->filter_nodes + filter_num; + fn = fc->filter_nodes + gc->filter_num; activate_grab_client(gc, fn); } } +static int check_gc_args(int argc, char **argv, struct grab_client *gc) +{ + int i, ret; + + gc->audio_format_num = -1; /* default: grab any audio format */ + for (i = 1; i < argc; i++) { + const char *arg = argv[i]; + if (arg[0] != '-') + break; + if (!strcmp(arg, "--")) { + i++; + break; + } + if (!strncmp(arg, "-n=", 3)) { + ret = para_atoi32(arg + 3, &gc->filter_num); + if (ret < 0) + return ret; + if (gc->filter_num < 0) + return -E_BAD_GC_FILTER_NUM; + if (gc->filter_num >= max_num_filters()) + return -E_BAD_GC_FILTER_NUM; + continue; + } + if (!strncmp(arg, "-m", 2)) { + if (*(arg + 3)) + return -E_GC_SYNTAX; + switch(*(arg + 2)) { + case 's': + gc->mode = GM_SLOPPY; + continue; + case 'a': + gc->mode = GM_AGGRESSIVE; + continue; + case 'p': + gc->mode = GM_PEDANTIC; + continue; + default: + return -E_GC_SYNTAX; + } + } + if (!strcmp(arg, "-i")) { + gc->flags |= GF_INPUT_GRAB; + continue; + } + if (!strcmp(arg, "-o")) { + gc->flags |= GF_ONE_SHOT; + continue; + } + if (!strncmp(arg, "-f=", 3)) { + ret = get_audio_format_num(arg + 3); + if (ret < 0) + return ret; + gc->audio_format_num = ret; + continue; + } + return -E_GC_SYNTAX; + } + if (i != argc) + return -E_GC_SYNTAX; + return 1; +} + /** * Check the command line options and allocate a grab_client structure. * * \param fd The file descriptor of the client. - * \param line The command line. - * \param err Non-zero if an error occurred. + * \param argc Argument count. + * \param argv Argument vector. * * If the command line options given by \a argc and \a argv are valid. * allocate a struct grab_client and initialize it with this valid * configuration. Moreover, add the new grab client to the inactive list. * - * \return On success, this function returns a pointer to the newly created - * struct. On errors, it returns NULL and sets \a err appropriately. + * \return Standard. * * \sa grab_client, inactive_grab_client_list, activate_grab_client, * filter_node::callbacks. */ -/* - * argc, argv get freed when com_grab() returns, so we have to make a - * copy. - */ -struct grab_client *grab_client_new(int fd, char *line, int *err) +int grab_client_new(int fd, int argc, char **argv) { int ret; struct grab_client *gc = para_calloc(sizeof(struct grab_client)); + struct filter_node *fn; - gc->conf = para_calloc(sizeof(struct grab_client_args_info)); - - ret = grab_client_cmdline_parser_string(line, gc->conf, "grab"); - *err = -E_GC_SYNTAX; - if (ret) - goto err_out; - *err = -E_GC_HELP_GIVEN; - if (gc->conf->help_given) + ret = check_gc_args(argc, argv, gc); + if (ret < 0) goto err_out; - *err = -E_GC_VERSION_GIVEN; - if (gc->conf->version_given) - goto err_out; - *err = check_gc_args(gc); - if (*err < 0) - goto err_out; - if (gc->conf->input_grab_given) { + if (gc->flags & GF_INPUT_GRAB) gc->fcb.input_cb = gc_write; - gc->fcb.output_cb = NULL; - } else { + else gc->fcb.output_cb = gc_write; - gc->fcb.input_cb = NULL; - } gc->fd = fd; gc->fcb.close = gc_close; - gc->fcb.data = gc; - add_inactive_gc(gc); - return gc; + fn = find_filter_node(gc->audio_format_num, gc->filter_num); + if (fn) + para_list_add(&gc->fcb.node, &fn->callbacks); + else + add_inactive_gc(gc); + return 1; err_out: - free(gc->conf); free(gc); - return NULL; + return ret; } /** diff --git a/grab_client.h b/grab_client.h index 2832f093..04e0a75b 100644 --- a/grab_client.h +++ b/grab_client.h @@ -7,43 +7,53 @@ /** \file grab_client.h exported symbols from grab_client.c */ #include "config.h" + /** - * handle blocking writes for the grab client fds - * - * - pedantic: close fd if write would block - * - sloppy: ignore the data and do not write - * - aggressive: write anyway (default) - * + * How to handle blocking writes for the grab client fds. */ -enum grab_mode {GRAB_SLOPPY, GRAB_AGGRESSIVE, GRAB_PEDANTIC}; +enum grab_mode { + /** Ignore the data and do not write. */ + GM_SLOPPY, + /** Write anyway (default). */ + GM_AGGRESSIVE, + /** Close fd if write would block. */ + GM_PEDANTIC, +}; -/** describes one active grab client +/** Flags specified as arguments to the grab command. */ +enum grab_flags { + /** Grab the filter input instead of its output. */ + GF_INPUT_GRAB = 1, + /** Stop grabbing if audio file changes. */ + GF_ONE_SHOT = 2, +}; + +/** + * Describes one active grab client. * - * \sa filter_callback, filter_node::callbacks + * \sa filter_callback, filter_node::callbacks. */ struct grab_client { -/** the file descriptor to send the grabbed stream to */ + /** The file descriptor to send the grabbed stream to. */ int fd; -/** the command line options for this grab client */ - struct grab_client_args_info *conf; -/** pedantic, sloppy, or aggressive, computed from command line */ - enum grab_mode mode; -/** non-zero if the write() to \a fd failed */ + /** Non-zero if the write() to \a fd failed. */ int error; -/** the number of the desired audio format, computed from command line */ + /** See \ref grab_mode. */ + enum grab_mode mode; + /** Point of filter chain to grab. */ + int32_t filter_num; + /** The number of the desired audio format. */ int audio_format_num; -/** the callback data which gets attached to a suitable filter_node */ + /** Flags given at the command line. */ + enum grab_flags flags; + /** The callback data which gets attached to a suitable filter_node. */ struct filter_callback fcb; -/** all grab clients belong either to a filter node or to the inactive list */ + /** All grab clients belong either to a filter node or to the inactive list. */ struct list_head node; -/** the number of command line options */ - int argc; -/** pointers to the command line options */ - char **argv; }; -__malloc struct grab_client *grab_client_new(int fd, char *line, int *err); -void activate_inactive_grab_clients(int slot_num, int audio_format_num, +int grab_client_new(int fd, int argc, char **argv); +void activate_inactive_grab_clients(int audio_format_num, struct filter_chain *fc); void activate_grab_client(struct grab_client *gc, struct filter_node *fn); void init_grabbing(void); diff --git a/gui.c b/gui.c index fc2cd9a1..394b69fe 100644 --- a/gui.c +++ b/gui.c @@ -6,14 +6,16 @@ /** \file gui.c Curses-based interface for paraslash. */ +#include #include #include #include +#include + #include "gui.cmdline.h" #include "para.h" #include "gui.h" #include "string.h" -#include #include "ringbuffer.h" #include "fd.h" #include "error.h" @@ -203,24 +205,6 @@ static int para_open_audiod_pipe(char *cmd) return ret; } -static int read_audiod_pipe(int fd, line_handler_t *line_handler) -{ - static char buf[4096]; - const ssize_t bufsize = sizeof(buf) - 1; - static ssize_t loaded; - ssize_t ret; - - if (loaded >= bufsize) - loaded = 0; - ret = read(fd, buf + loaded, bufsize - loaded); - if (ret > 0) { - loaded += ret; - buf[loaded] = '\0'; - loaded = for_each_line(buf, loaded, line_handler, NULL); - } - return ret; -} - static int find_cmd_byname(char *name) { int i; @@ -312,20 +296,23 @@ static void add_spaces(WINDOW* win, unsigned int num) * print aligned string to curses window. This function always prints * exactly len chars. */ -static int align_str(WINDOW* win, const char *string, unsigned int len, +static int align_str(WINDOW* win, char *str, unsigned int len, unsigned int align) { - int num; /* of spaces */ - char *str; + int i, num; /* of spaces */ - if (!win || !string) + if (!win || !str) return -1; - num = len - strlen(string); - str = para_strdup(string); + num = len - strlen(str); if (num < 0) { str[len] = '\0'; num = 0; } + /* replace newlines by spaces */ + for (i = 0; i < len; i++) { + if (str[i] == '\n') + str[i] = ' '; + } if (align == LEFT) { waddstr(win, str); add_spaces(win, num); @@ -337,7 +324,6 @@ static int align_str(WINDOW* win, const char *string, unsigned int len, waddstr(win, str[0]? str: ""); add_spaces(win, num - num / 2); } - free(str); return 1; } @@ -360,10 +346,14 @@ __printf_2_3 static void print_in_bar(int color, const char *fmt,...) */ static void print_status_bar(void) { + char *tmp; + if (!curses_active) return; + tmp = para_strdup(STANDARD_STATUS_BAR); wmove(sb.win, 0, 0); - align_str(sb.win, STANDARD_STATUS_BAR, sb.cols, CENTER); + align_str(sb.win, tmp, sb.cols, CENTER); + free(tmp); wrefresh(sb.win); } @@ -687,6 +677,63 @@ static void print_stat_item(int i) wrefresh(top.win); } +static int update_item(int item_num, char *buf) +{ + char **c = stat_content + item_num; + + free(*c); + if (buf && buf[0]) + goto dup; + switch (item_num) { + case SI_ARTIST: + *c = para_strdup("(artist tag not set)"); + goto print; + case SI_TITLE: + *c = para_strdup("(title tag not set)"); + goto print; + case SI_YEAR: + *c = para_strdup("????"); + goto print; + case SI_ALBUM: + *c = para_strdup("(album tag not set)"); + goto print; + case SI_COMMENT: + *c = para_strdup("(comment tag not set)"); + goto print; + } +dup: + *c = para_strdup(buf); +print: + print_stat_item(item_num); + return 1; +} + +static int read_audiod_pipe(int fd) +{ + static char *buf; + static int bufsize, loaded; + int ret; + + if (loaded >= bufsize) { + if (bufsize > 1000 * 1000) { + loaded = 0; + return 0; + } + bufsize += bufsize + 1000; + buf = para_realloc(buf, bufsize); + } + assert(loaded < bufsize); + ret = read(fd, buf + loaded, bufsize - loaded); + if (ret <= 0) + return ret; + loaded += ret; + ret = for_each_stat_item(buf, loaded, update_item); + if (ret < 0) + return ret; + loaded = ret; + return 1; +} + static void print_all_items(void) { int i; @@ -769,26 +816,6 @@ reap_next_child: goto reap_next_child; } -/* - * print status line if line starts with known command. - */ -static int check_stat_line(char *line, __a_unused void *data) -{ - int i; - -// PARA_INFO_LOG("%s: checking: %s\n", __func__, line); - i = stat_line_valid(line); - if (i >= 0) { - line += strlen(status_item_list[i]) + 1; - if (*line == ' ') - line++; - free(stat_content[i]); - stat_content[i] = para_strdup(line); - print_stat_item(i); - } - return 1; -} - /* * This sucker modifies its first argument. *handler and *arg are * pointers to 0-terminated strings (inside line). Crap. @@ -859,8 +886,8 @@ static void handle_signal(int sig) return; case SIGINT: PARA_WARNING_LOG("caught SIGINT, reset"); - /* Nothing to do. SIGINT killed our child, para_client stat. - * This get noticed by do_select which resets everything + /* Nothing to do. SIGINT killed our child which gets noticed + * by do_select and resets everything. */ return; case SIGUSR1: @@ -946,7 +973,7 @@ repeat: } } if (audiod_pipe >= 0 && FD_ISSET(audiod_pipe, &rfds)) - if (read_audiod_pipe(audiod_pipe, check_stat_line) <= 0) { + if (read_audiod_pipe(audiod_pipe) <= 0) { close(audiod_pipe); audiod_pipe = -1; clear_all_items(); diff --git a/gui_theme.c b/gui_theme.c index 9345f0ed..a33df3d2 100644 --- a/gui_theme.c +++ b/gui_theme.c @@ -281,32 +281,59 @@ static void init_theme_colorful_blackness(struct gui_theme *t) d[SI_AMPLIFICATION].y = 27; d[SI_AMPLIFICATION].len = 8; - d[SI_AUDIO_FILE_INFO].prefix = ""; - d[SI_AUDIO_FILE_INFO].postfix = ""; - d[SI_AUDIO_FILE_INFO].fg = COLOR_GREEN; - d[SI_AUDIO_FILE_INFO].bg = COLOR_BLACK; - d[SI_AUDIO_FILE_INFO].align = CENTER; - d[SI_AUDIO_FILE_INFO].x = 0; - d[SI_AUDIO_FILE_INFO].y = 43; - d[SI_AUDIO_FILE_INFO].len = 100; - - d[SI_TAGINFO1].prefix = ""; - d[SI_TAGINFO1].postfix = ""; - d[SI_TAGINFO1].fg = COLOR_GREEN; - d[SI_TAGINFO1].bg = COLOR_BLACK; - d[SI_TAGINFO1].align = CENTER; - d[SI_TAGINFO1].x = 0; - d[SI_TAGINFO1].y = 53; - d[SI_TAGINFO1].len = 100; - - d[SI_TAGINFO2].prefix = ""; - d[SI_TAGINFO2].postfix = ""; - d[SI_TAGINFO2].fg = COLOR_GREEN; - d[SI_TAGINFO2].bg = COLOR_BLACK; - d[SI_TAGINFO2].align = CENTER; - d[SI_TAGINFO2].x = 0; - d[SI_TAGINFO2].y = 63; - d[SI_TAGINFO2].len = 100; + d[SI_TECHINFO].prefix = ""; + d[SI_TECHINFO].postfix = ""; + d[SI_TECHINFO].fg = COLOR_GREEN; + d[SI_TECHINFO].bg = COLOR_BLACK; + d[SI_TECHINFO].align = CENTER; + d[SI_TECHINFO].x = 0; + d[SI_TECHINFO].y = 43; + d[SI_TECHINFO].len = 100; + + d[SI_TITLE].prefix = ""; + d[SI_TITLE].postfix = ","; + d[SI_TITLE].fg = COLOR_GREEN; + d[SI_TITLE].bg = COLOR_BLACK; + d[SI_TITLE].align = RIGHT; + d[SI_TITLE].x = 0; + d[SI_TITLE].y = 53; + d[SI_TITLE].len = 45; + + d[SI_ARTIST].prefix = " by "; + d[SI_ARTIST].postfix = ""; + d[SI_ARTIST].fg = COLOR_GREEN; + d[SI_ARTIST].bg = COLOR_BLACK; + d[SI_ARTIST].align = LEFT; + d[SI_ARTIST].x = 45; + d[SI_ARTIST].y = 53; + d[SI_ARTIST].len = 45; + + d[SI_YEAR].prefix = "("; + d[SI_YEAR].postfix = ")"; + d[SI_YEAR].fg = COLOR_GREEN; + d[SI_YEAR].bg = COLOR_BLACK; + d[SI_YEAR].align = RIGHT; + d[SI_YEAR].x = 90; + d[SI_YEAR].y = 53; + d[SI_YEAR].len = 10; + + d[SI_ALBUM].prefix = "A: "; + d[SI_ALBUM].postfix = ""; + d[SI_ALBUM].fg = COLOR_GREEN; + d[SI_ALBUM].bg = COLOR_BLACK; + d[SI_ALBUM].align = CENTER; + d[SI_ALBUM].x = 0; + d[SI_ALBUM].y = 63; + d[SI_ALBUM].len = 50; + + d[SI_COMMENT].prefix = "C: "; + d[SI_COMMENT].postfix = ""; + d[SI_COMMENT].fg = COLOR_GREEN; + d[SI_COMMENT].bg = COLOR_BLACK; + d[SI_COMMENT].align = CENTER; + d[SI_COMMENT].x = 50; + d[SI_COMMENT].y = 63; + d[SI_COMMENT].len = 50; d[SI_AFS_MODE].prefix = ""; d[SI_AFS_MODE].postfix = ""; diff --git a/http_recv.c b/http_recv.c index 7f130341..f644233b 100644 --- a/http_recv.c +++ b/http_recv.c @@ -6,6 +6,7 @@ /** \file http_recv.c paraslash's http receiver */ +#include #include #include diff --git a/http_send.c b/http_send.c index 5b98bbf4..3959cad4 100644 --- a/http_send.c +++ b/http_send.c @@ -6,8 +6,10 @@ /** \file http_send.c paraslash's http sender */ +#include #include #include +#include #include "para.h" #include "error.h" diff --git a/mm.c b/mm.c new file mode 100644 index 00000000..0130cbcb --- /dev/null +++ b/mm.c @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2007-2009 Andre Noll + * + * Licensed under the GPL v2. For licencing details see COPYING. + */ + +/** \file mm.c Paraslash's mood methods. */ + +#include +#include +#include +#include + +#include "para.h" +#include "error.h" +#include "string.h" +#include "afh.h" +#include "afs.h" +#include "mm.h" + +#define MOOD_COMPARATORS \ + MC(LESS, <) \ + MC(LESS_OR_EQUAL, <=) \ + MC(EQUAL, =) \ + MC(EQUAL2, ==) \ + MC(NOT_EQUAL, !=) \ + MC(NOT_EQUAL2, <>) \ + MC(GREATER, >) \ + MC(GREATER_OR_EQUAL, >=) \ + +#define MC(a, b) MC_ ## a, +enum mood_comparator_id {MOOD_COMPARATORS NUM_MOOD_COMPARATORS}; +#undef MC +#define MC(a, b) # b, +const char const *mood_comparators[] = {MOOD_COMPARATORS}; +#undef MC + +static int parse_mood_comparator(const char *word) +{ + int i; + + for (i = 0; i < NUM_MOOD_COMPARATORS; i++) + if (!strcmp(word, mood_comparators[i])) + return i; + return -E_MOOD_SYNTAX; +} + +static int compare_int32(int32_t a, int32_t b, enum mood_comparator_id id) +{ + int res; + + switch (id) { + case MC_LESS: + res = a < b; break; + case MC_LESS_OR_EQUAL: + res = a <= b; break; + case MC_EQUAL: + case MC_EQUAL2: + res = a == b; break; + case MC_NOT_EQUAL: + case MC_NOT_EQUAL2: + res = a != b; break; + case MC_GREATER: + res = a > b; break; + case MC_GREATER_OR_EQUAL: + res = a >= b; break; + default: + PARA_EMERG_LOG("BUG: invalid mood comparator\n"); + exit(EXIT_FAILURE); + } + return res? 100 : -100; +} + +struct mm_year_data { + /** The year given at the mood line. */ + int32_t year; + /** Used to detect Y2K issues. */ + int32_t current_year; + /** <, <=, =, !=, >=, or >. */ + enum mood_comparator_id id; +}; + +static int mm_year_parser(int argc, char **argv, void **private) +{ + int ret = -E_MOOD_SYNTAX; + struct mm_year_data *mmyd = para_malloc(sizeof(*mmyd)); + time_t current_time; + struct tm *gmt; + + if (argc != 2) + goto err; + ret = parse_mood_comparator(argv[1]); + mmyd->id = ret; + if (ret < 0) + goto err; + ret = para_atoi32(argv[2], &mmyd->year); + if (ret < 0) + goto err; + current_time = time(NULL); + gmt = gmtime(¤t_time); + /* tm_year is the number of years since 1900 */ + mmyd->current_year = gmt->tm_year + 1900; + *private = mmyd; + return 1; +err: + free(mmyd); + return ret; +} + +static int mm_year_score_function(__a_unused const char *path, + __a_unused const struct afs_info *afsi, + const struct afh_info *afhi, + const void *private) +{ + const struct mm_year_data *mmyd = private; + int32_t tag_year; + int ret = para_atoi32(afhi->tags.year, &tag_year); + + if (ret < 0) /* year tag not present or not a number */ + return -100; + if (tag_year < 0) + return -100; + /* try to work around Y2K issues */ + if (tag_year < 100) { + tag_year += 1900; + if (tag_year + 100 <= mmyd->current_year) + tag_year += 100; /* assume tag_year >= 2000 */ + } + return compare_int32(tag_year, mmyd->year, mmyd->id); +} + +static void mm_year_cleanup(void *private) +{ + free(private); +} + +static int mm_no_attributes_set_parser(int argc, __a_unused char **argv, + __a_unused void **ignored) +{ + return argc? -E_MOOD_SYNTAX : 1; +} + +static int mm_no_attributes_set_score_function(__a_unused const char *path, + const struct afs_info *afsi, + __a_unused const struct afh_info *afhi, + __a_unused const void *data) +{ + if (!afsi->attributes) + return 100; + return -100; +} + +static int mm_path_matches_score_function(const char *path, + __a_unused const struct afs_info *afsi, + __a_unused const struct afh_info *afhi, + const void *data) +{ + if (fnmatch(data, path, 0)) + return -100; + return 100; +} + +static int mm_path_matches_parser(int argc, char **argv, void **data) +{ + if (argc != 1) + return -E_MOOD_SYNTAX; + *data = para_strdup(argv[1]); + return 1; +} + +static void mm_path_matches_cleanup(void *data) +{ + free(data); +} + +static int mm_is_set_parser(int argc, char **argv, void **bitnum) +{ + int ret; + unsigned char c, *res; + + if (argc != 1) + return -E_MOOD_SYNTAX; + ret = get_attribute_bitnum_by_name(argv[1], &c); + if (ret < 0) + return ret; + res = para_malloc(1); + *res = c; + *bitnum = res; + return 1; +} + +static int mm_is_set_score_function(__a_unused const char *path, + __a_unused const struct afs_info *afsi, + __a_unused const struct afh_info *afhi, + const void *data) +{ + const unsigned char *bn = data; + if (afsi->attributes & (1ULL << *bn)) + return 100; + return -100; +} + +#define DEFINE_MOOD_METHOD(_name) \ +.parser = mm_ ## _name ## _parser, \ +.score_function = mm_ ## _name ## _score_function, \ +.name = #_name + +#define DEFINE_MOOD_METHOD_WITH_CLEANUP(_name) \ + DEFINE_MOOD_METHOD(_name), \ + .cleanup = mm_ ## _name ## _cleanup + +const struct mood_method mood_methods[] = { + {DEFINE_MOOD_METHOD(no_attributes_set)}, + {DEFINE_MOOD_METHOD(is_set)}, + {DEFINE_MOOD_METHOD_WITH_CLEANUP(path_matches)}, + {DEFINE_MOOD_METHOD_WITH_CLEANUP(year)}, + {.parser = NULL} +}; + diff --git a/mm.h b/mm.h new file mode 100644 index 00000000..c1744dcb --- /dev/null +++ b/mm.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2007-2009 Andre Noll + * + * Licensed under the GPL v2. For licencing details see COPYING. + */ + +/** \file mm.h Symbols and declarations for mood methods. */ + +/** + * Assign scores according to a mood_method. + * + * Each mood_method has its own mood_score_function. The first three parameters + * passed to that function are informations about the audio file whose score is + * to be computed. The data argument depends on the mood method this function + * is used for. It usually is the argument given at the end of a mood line. + * + * Mood score functions must return values between -100 and +100 inclusively. + * Boolean score functions should always return either -100 or +100. + * + * \sa struct mood_method, mood_parser. + */ +typedef int mood_score_function(const char *path, const struct afs_info *afsi, + const struct afh_info *afhi, const void *data); + +/** + * Pre-process a mood line. + * + * The mood_parser of a mood_method is called once at mood open time for each + * line of the current mood definition that contains the mood_method's name as + * a keyword. The line is passed to the mood_parser as the first argument. The + * mood_parser must determine whether the line is syntactically correct and + * return a positive value if so and a negative value otherwise. + * + * Some mood parsers pre-process the data given in the mood line to compute a + * structure which depends of the particular mood_method and which is used + * later in the mood_score_function of the mood_method. The mood_parser may + * store a pointer to its structure via the void** pointer. + * + * \sa mood_open(), mood_cleanup_function, mood_score_function. + */ +typedef int mood_parser(int, char **, void **); + +/** + * Deallocate resources which were allocated by the mood_parser. + * + * This optional function of a mood_method is used to free any resources + * allocated in mood_open() by the mood_parser. The argument passed is a + * pointer to the mood_method specific data structure that was returned by the + * mood_parser. + * + * \sa mood_parser. + */ +typedef void mood_cleanup_function(void *); + +/** + * Used for scoring and to determine whether a file is admissible. + */ +struct mood_method { + /** The name of the method. */ + const char *name; + /** Pointer to the mood parser. */ + mood_parser *parser; + /** Pointer to the score function */ + mood_score_function *score_function; + /** Optional cleanup function. */ + mood_cleanup_function *cleanup; +}; + +/** The array of available mood methods. */ +extern const struct mood_method mood_methods[]; diff --git a/mood.c b/mood.c index 24d1de29..26d68dc5 100644 --- a/mood.c +++ b/mood.c @@ -6,7 +6,9 @@ /** \file mood.c Paraslash's mood handling functions. */ -#include +#include +#include + #include "para.h" #include "error.h" #include "string.h" @@ -14,6 +16,7 @@ #include "afs.h" #include "list.h" #include "ipc.h" +#include "mm.h" /** * Contains statistical data of the currently admissible audio files. @@ -34,66 +37,6 @@ struct afs_statistics { }; struct afs_statistics statistics; -/** - * Assign scores according to a mood_method. - * - * Each mood_method has its own mood_score_function. The first three parameters - * passed to that function are informations about the audio file whose score is - * to be computed. The data argument depends on the mood method this function - * is used for. It usually is the argument given at the end of a mood line. - * - * Mood score functions must return values between -100 and +100 inclusively. - * Boolean score functions should always return either -100 or +100. - * - * \sa struct mood_method, mood_parser. - */ -typedef int mood_score_function(const char *path, const struct afs_info *afsi, - const struct afh_info *afhi, const void *data); - -/** - * Pre-process a mood line. - * - * The mood_parser of a mood_method is called once at mood open time for each - * line of the current mood definition that contains the mood_method's name as - * a keyword. The line is passed to the mood_parser as the first argument. The - * mood_parser must determine whether the line is syntactically correct and - * return a positive value if so and a negative value otherwise. - * - * Some mood parsers pre-process the data given in the mood line to compute a - * structure which depends of the particular mood_method and which is used - * later in the mood_score_function of the mood_method. The mood_parser may - * store a pointer to its structure via the second argument. - * - * \sa mood_open(), mood_cleanup_function, mood_score_function. - */ -typedef int mood_parser(const char *, void **); - -/** - * Deallocate resources which were allocated by the mood_parser. - * - * This optional function of a mood_method is used to free any resources - * allocated in mood_open() by the mood_parser. The argument passed is a - * pointer to the mood_method specific data structure that was returned by the - * mood_parser. - * - * \sa mood_parser. - */ -typedef void mood_cleanup_function(void *); - -/** - * Used for scoring and to determine whether a file is admissible. - */ -struct mood_method { - /** The name of the method. */ - const char *name; - /** Pointer to the mood parser. */ - mood_parser *parser; - /** Pointer to the score function */ - mood_score_function *score_function; - /** Optional cleanup function. */ - mood_cleanup_function *cleanup; -}; - /** * Each line of the current mood corresponds to a mood_item. */ @@ -164,90 +107,6 @@ static uint64_t int_sqrt(uint64_t x) return res; } -static int mm_no_attributes_set_parser(const char *arg, __a_unused void **ignored) -{ - if (arg && *arg) - PARA_WARNING_LOG("ignored junk at eol: %s\n", arg); - return 1; -} - -static int mm_no_attributes_set_score_function(__a_unused const char *path, - const struct afs_info *afsi, - __a_unused const struct afh_info *afhi, - __a_unused const void *data) -{ - if (!afsi->attributes) - return 100; - return -100; -} - -static int mm_played_rarely_score_function(__a_unused const char *path, - const struct afs_info *afsi, - __a_unused const struct afh_info *afhi, - __a_unused const void *data) -{ - unsigned num; - int ret = get_num_admissible_files(&num); - - if (ret < 0) - return 0; - if (statistics.num_played_sum - num * afsi->num_played - > int_sqrt(statistics.num_played_qd * num)) - return 100; - return -100; -} - -static int mm_played_rarely_parser(const char *arg, __a_unused void **ignored) -{ - if (arg && *arg) - PARA_WARNING_LOG("ignored junk at eol: %s\n", arg); - return 1; -} - -static int mm_path_matches_score_function(const char *path, - __a_unused const struct afs_info *afsi, - __a_unused const struct afh_info *afhi, - const void *data) -{ - if (fnmatch(data, path, 0)) - return -100; - return 100; -} - -static int mm_path_matches_parser(const char *arg, void **data) -{ - *data = para_strdup(arg); - return 1; -} - -static void mm_path_matches_cleanup(void *data) -{ - free(data); -} - -static int mm_is_set_parser(const char *arg, void **bitnum) -{ - unsigned char *c = para_malloc(1); - int ret = get_attribute_bitnum_by_name(arg, c); - - if (ret >= 0) - *bitnum = c; - else - free(c); - return ret; -} - -static int mm_is_set_score_function(__a_unused const char *path, - __a_unused const struct afs_info *afsi, - __a_unused const struct afh_info *afhi, - const void *data) -{ - const unsigned char *bn = data; - if (afsi->attributes & (1ULL << *bn)) - return 100; - return -100; -} - /* returns 1 if row matches score item, 0 if not, negative on errors */ static int get_item_score(const struct osl_row *row, struct mood_item *item, long *score, long *score_arg_sum) @@ -266,7 +125,6 @@ static int get_item_score(const struct osl_row *row, struct mood_item *item, ret = get_afhi_of_row(row, &afhi); if (ret< 0) return ret; - free(afhi.info_string); /* don't need the tag info */ ret = get_audio_file_path_of_row(row, &path); if (ret< 0) return ret; @@ -328,23 +186,6 @@ static int compute_mood_score(const struct osl_row *aft_row, struct mood *m, return 1; } -#define DEFINE_MOOD_METHOD(_name) \ -.parser = mm_ ## _name ## _parser, \ -.score_function = mm_ ## _name ## _score_function, \ -.name = #_name - -#define DEFINE_MOOD_METHOD_WITH_CLEANUP(_name) \ - DEFINE_MOOD_METHOD(_name), \ - .cleanup = mm_ ## _name ## _cleanup - -static const struct mood_method mood_methods[] = { - {DEFINE_MOOD_METHOD(no_attributes_set)}, - {DEFINE_MOOD_METHOD(played_rarely)}, - {DEFINE_MOOD_METHOD(is_set)}, - {DEFINE_MOOD_METHOD_WITH_CLEANUP(path_matches)}, - {.parser = NULL} -}; - static void cleanup_list_entry(struct mood_item *item) { if (item->method && item->method->cleanup) @@ -412,17 +253,17 @@ static int parse_mood_line(char *mood_line, void *data) { struct mood_line_parser_data *mlpd = data; char **argv; - char *delim = " \t"; unsigned num_words; char **w; int i, ret; enum mood_line_type mlt = ML_INVALID; struct mood_item *mi = NULL; - char *buf = para_strdup(mood_line); mlpd->line_num++; - num_words = split_args(buf, &argv, delim); - ret = 1; + ret = create_argv(mood_line, " \t", &argv); + if (ret < 0) + return ret; + num_words = ret; if (!num_words) /* empty line */ goto out; w = argv; @@ -495,8 +336,8 @@ check_for_if: ret = -E_MOOD_SYNTAX; if (!mood_methods[i].parser) goto out; - w++; - ret = mood_methods[i].parser(*w, &mi->parser_data); + ret = mood_methods[i].parser(num_words - 1 - (w - argv), w, + &mi->parser_data); if (ret < 0) goto out; mi->method = &mood_methods[i]; @@ -513,8 +354,7 @@ success: (mlt == ML_DENY? "deny" : "score"), mi->method); ret = 1; out: - free(argv); - free(buf); + free_argv(argv); if (ret >= 0) return ret; if (mi) { @@ -961,7 +801,6 @@ void close_current_mood(void) memset(&statistics, 0, sizeof(statistics)); } - /** * Change the current mood. * @@ -993,7 +832,7 @@ int change_current_mood(char *mood_name) .data = mood_name, .size = strlen(mood_name) + 1 }; - ret = osl_get_row(moods_table, BLOBCOL_NAME, &obj, &row); + ret = osl(osl_get_row(moods_table, BLOBCOL_NAME, &obj, &row)); if (ret < 0) { PARA_NOTICE_LOG("no such mood: %s\n", mood_name); return ret; diff --git a/mood.h b/mood.h new file mode 100644 index 00000000..51c27a36 --- /dev/null +++ b/mood.h @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2007-2009 Andre Noll + * + * Licensed under the GPL v2. For licencing details see COPYING. + */ + +/** \file mood.h Functions exported by mood.h. */ + +int change_current_mood(char *mood_name); +void close_current_mood(void); +int reload_current_mood(void); +void mood_check_callback(int fd, __a_unused const struct osl_object *query); diff --git a/mp3_afh.c b/mp3_afh.c index 2d18d5e5..5eacf63b 100644 --- a/mp3_afh.c +++ b/mp3_afh.c @@ -16,12 +16,13 @@ * Johannes Overmann */ +#include +#include + #include "para.h" #include "error.h" #include "afh.h" #include "string.h" -#include "afs.h" -#include "server.h" /** \cond some defines and structs which are only used in this file */ @@ -126,62 +127,49 @@ static char *get_strings(struct id3_frame *fr) return NULL; } -static char *mp3_get_id3(__a_unused unsigned char *map, - __a_unused size_t numbytes, int fd) +static void mp3_get_id3(__a_unused unsigned char *map, + __a_unused size_t numbytes, int fd, struct taginfo *tags) { int i; struct id3_tag *id3_t; - char *title = NULL, *artist = NULL, *album = NULL, *year = NULL, - *comment = NULL, *result; struct id3_file *id3_f = id3_file_fdopen(fd, ID3_FILE_MODE_READONLY); if (!id3_f) - goto no_tag; + return; id3_t = id3_file_tag(id3_f); - if (!id3_t) - goto no_tag; + if (!id3_t) { + id3_file_close(id3_f); + return; + } for (i = 0; i < id3_t->nframes; i++) { struct id3_frame *fr = id3_t->frames[i]; if (!strcmp(fr->id, "TIT2")) { - if (!title) - title = get_strings(fr); + if (!tags->title) + tags->title = get_strings(fr); continue; } if (!strcmp(fr->id, "TPE1")) { - if (!artist) - artist = get_strings(fr); + if (!tags->artist) + tags->artist = get_strings(fr); continue; } if (!strcmp(fr->id, "TALB")) { - if (!album) - album = get_strings(fr); + if (!tags->album) + tags->album = get_strings(fr); continue; } if (!strcmp(fr->id, "TDRC")) { - if (!year) - year = get_strings(fr); + if (!tags->year) + tags->year = get_strings(fr); continue; } if (!strcmp(fr->id, "COMM")) { - if (!comment) - comment = get_strings(fr); + if (!tags->comment) + tags->comment = get_strings(fr); continue; } } id3_file_close(id3_f); - result = make_taginfo(title, artist, album, year, comment); - free(title); - free(artist); - free(album); - free(year); - free(comment); - return result; -no_tag: - if (id3_f) - id3_file_close(id3_f); - return make_message("%s: (no id3 v1/v2 tag)\n%s:\n", - status_item_list[SI_TAGINFO1], - status_item_list[SI_TAGINFO2]); } #else /* HAVE_LIBID3TAG */ @@ -197,16 +185,15 @@ static char *unpad(char *string) return string; } -static char *mp3_get_id3(unsigned char *map, size_t numbytes, __a_unused int fd) +static void mp3_get_id3(unsigned char *map, size_t numbytes, __a_unused int fd, + struct taginfo *tags) { char title[31], artist[31], album[31], year[5], comment[31]; off_t fpos; if (numbytes < 128 || strncmp("TAG", (char *)map + numbytes - 128, 3)) { PARA_DEBUG_LOG("no id3 v1 tag\n"); - return make_message("%s: (no id3 v1 tag)\n%s:\n", - status_item_list[SI_TAGINFO1], - status_item_list[SI_TAGINFO2]); + return; } fpos = numbytes - 125; memcpy(title, map + fpos, 30); @@ -228,7 +215,11 @@ static char *mp3_get_id3(unsigned char *map, size_t numbytes, __a_unused int fd) unpad(album); unpad(year); unpad(comment); - return make_taginfo(title, artist, album, year, comment); + tags->artist = para_strdup(artist); + tags->title = para_strdup(title); + tags->year = para_strdup(year); + tags->album = para_strdup(album); + tags->comment = para_strdup(comment); } #endif /* HAVE_LIBID3TAG */ @@ -400,11 +391,9 @@ static int mp3_read_info(unsigned char *map, size_t numbytes, int fd, unsigned chunk_table_size = 1000; /* gets increased on demand */ off_t fpos = 0; struct mp3header header; - char *taginfo; afhi->chunks_total = 0; afhi->chunk_table = para_malloc(chunk_table_size * sizeof(uint32_t)); - taginfo = mp3_get_id3(map, numbytes, fd); while (1) { int freq, br; struct timeval tmp, cct; /* current chunk time */ @@ -460,15 +449,11 @@ static int mp3_read_info(unsigned char *map, size_t numbytes, int fd, tv_divide(afhi->chunks_total, &total_time, &afhi->chunk_tv); PARA_DEBUG_LOG("%lu chunks, each %lums\n", afhi->chunks_total, tv2ms(&afhi->chunk_tv)); - tv_scale(3, &afhi->chunk_tv, &afhi->eof_tv); - PARA_DEBUG_LOG("eof timeout: %lu\n", tv2ms(&afhi->eof_tv)); - afhi->info_string = make_message("%s: %cbr, %s\n%s", - status_item_list[SI_AUDIO_FILE_INFO], vbr? 'v' : 'c', - header_mode(&header), taginfo); - free(taginfo); + afhi->techinfo = make_message("%cbr, %s", vbr? 'v' : 'c', + header_mode(&header)); + mp3_get_id3(map, numbytes, fd, &afhi->tags); return 1; err_out: - free(taginfo); PARA_ERROR_LOG("%s\n", para_strerror(-ret)); free(afhi->chunk_table); return ret; diff --git a/mp3dec_filter.c b/mp3dec_filter.c index 92da4a5c..aceee9ae 100644 --- a/mp3dec_filter.c +++ b/mp3dec_filter.c @@ -6,6 +6,9 @@ /** \file mp3dec_filter.c Paraslash's mp3 decoder. */ +#include +#include + #include "para.h" #include "mp3dec_filter.cmdline.h" #include "list.h" @@ -13,7 +16,6 @@ #include "ggo.h" #include "filter.h" #include "error.h" -#include #include "string.h" /** Convert a sample value from libmad to a signed short. */ diff --git a/net.c b/net.c index ab6a9894..61f50dd7 100644 --- a/net.c +++ b/net.c @@ -27,71 +27,15 @@ #include #include +#include #include "para.h" #include "error.h" +#include "crypt.h" #include "net.h" #include "string.h" #include "fd.h" - -/** Information about one encrypted connection. */ -struct crypt_data { - /** Function used to decrypt received data. */ - crypt_function *recv; - /** Function used to encrypt data to be sent. */ - crypt_function *send; - /** - * Context-dependent data (crypt keys), passed verbatim to the above - * crypt functions. - */ - void *private_data; -}; -/** Array holding per fd crypt data. */ -static struct crypt_data *crypt_data_array; -/** Current size of the crypt data array. */ -static unsigned cda_size = 0; - -/** - * Activate encryption for one file descriptor. - * - * \param fd The file descriptor. - * \param recv_f The function used for decrypting received data. - * \param send_f The function used for encrypting before sending. - * \param private_data User data supplied by the caller. - */ -void enable_crypt(int fd, crypt_function *recv_f, crypt_function *send_f, - void *private_data) -{ - if (fd + 1 > cda_size) { - crypt_data_array = para_realloc(crypt_data_array, - (fd + 1) * sizeof(struct crypt_data)); - memset(crypt_data_array + cda_size, 0, - (fd + 1 - cda_size) * sizeof(struct crypt_data)); - cda_size = fd + 1; - } - crypt_data_array[fd].recv = recv_f; - crypt_data_array[fd].send = send_f; - crypt_data_array[fd].private_data = private_data; - PARA_INFO_LOG("rc4 encryption activated for fd %d\n", fd); -} - -/** - * Deactivate encryption for a given fd. - * - * \param fd The file descriptor. - * - * This must be called if and only if \p fd was activated via enable_crypt(). - */ -void disable_crypt(int fd) -{ - if (cda_size < fd + 1) - return; - crypt_data_array[fd].recv = NULL; - crypt_data_array[fd].send = NULL; - crypt_data_array[fd].private_data = NULL; -} - /** * Parse and validate IPv4 address/netmask string. * @@ -144,7 +88,8 @@ static bool is_v4_dot_quad(const char *address) bool result; regex_t r; - assert(!regcomp(&r, "^([0-9]+\\.){3}[0-9]+$", REG_EXTENDED|REG_NOSUB)); + assert(para_regcomp(&r, "^([0-9]+\\.){3}[0-9]+$", + REG_EXTENDED | REG_NOSUB) >= 0); result = regexec(&r, address, 0, NULL, 0) == 0; regfree(&r); return result; @@ -528,41 +473,26 @@ struct in_addr extract_v4_addr(const struct sockaddr_storage *ss) } /** - * Encrypt and send a binary buffer. + * Send a binary buffer. * * \param fd The file descriptor. - * \param buf The buffer to be encrypted and sent. + * \param buf The buffer to be sent. * \param len The length of \a buf. * - * Check if encryption is available. If yes, encrypt the given buffer. Send - * out the buffer, encrypted or not, and try to resend the remaining part in - * case of short writes. + * Send out the buffer and try to resend the remaining part in case of short + * writes. * * \return Standard. */ int send_bin_buffer(int fd, const char *buf, size_t len) { - int ret; - crypt_function *cf = NULL; - if (!len) PARA_CRIT_LOG("len == 0\n"); - if (fd + 1 <= cda_size) - cf = crypt_data_array[fd].send; - if (cf) { - void *private = crypt_data_array[fd].private_data; - /* RC4 may write more than len to the output buffer */ - unsigned char *outbuf = para_malloc(ROUND_UP(len, 8)); - (*cf)(len, (unsigned char *)buf, outbuf, private); - ret = write_all(fd, (char *)outbuf, &len); - free(outbuf); - } else - ret = write_all(fd, buf, &len); - return ret; + return write_all(fd, buf, &len); } /** - * Encrypt and send null terminated buffer. + * Send a \p NULL-terminated buffer. * * \param fd The file descriptor. * \param buf The null-terminated buffer to be send. @@ -576,9 +506,8 @@ int send_buffer(int fd, const char *buf) return send_bin_buffer(fd, buf, strlen(buf)); } - /** - * Send and encrypt a buffer given by a format string. + * Send a buffer given by a format string. * * \param fd The file descriptor. * \param fmt A format string. @@ -597,51 +526,37 @@ __printf_2_3 int send_va_buffer(int fd, const char *fmt, ...) } /** - * Receive and decrypt. + * Receive data from a file descriptor. * * \param fd The file descriptor. - * \param buf The buffer to write the decrypted data to. + * \param buf The buffer to write the data to. * \param size The size of \a buf. * - * Receive at most \a size bytes from file descriptor \a fd. If encryption is - * available, decrypt the received buffer. + * Receive at most \a size bytes from file descriptor \a fd. * - * \return The number of bytes received on success, negative on errors. + * \return The number of bytes received on success, negative on errors, zero if + * the peer has performed an orderly shutdown. * - * \sa recv(2) + * \sa recv(2). */ __must_check int recv_bin_buffer(int fd, char *buf, size_t size) { ssize_t n; - crypt_function *cf = NULL; - - if (fd + 1 <= cda_size) - cf = crypt_data_array[fd].recv; - if (cf) { - unsigned char *tmp = para_malloc(size); - void *private = crypt_data_array[fd].private_data; - n = recv(fd, tmp, size, 0); - if (n > 0) { - size_t numbytes = n; - unsigned char *b = (unsigned char *)buf; - (*cf)(numbytes, tmp, b, private); - } - free(tmp); - } else - n = recv(fd, buf, size, 0); + + n = recv(fd, buf, size, 0); if (n == -1) return -ERRNO_TO_PARA_ERROR(errno); return n; } /** - * Receive, decrypt and write terminating NULL byte. + * Receive and write terminating NULL byte. * * \param fd The file descriptor. - * \param buf The buffer to write the decrypted data to. + * \param buf The buffer to write the data to. * \param size The size of \a buf. * - * Read and decrypt at most \a size - 1 bytes from file descriptor \a fd and + * Read at most \a size - 1 bytes from file descriptor \a fd and * write a NULL byte at the end of the received data. * * \return The return value of the underlying call to \a recv_bin_buffer(). diff --git a/net.h b/net.h index 8b706178..15412586 100644 --- a/net.h +++ b/net.h @@ -80,15 +80,13 @@ extern int para_listen(unsigned l3type, unsigned l4type, unsigned short port); extern char *local_name(int sockfd); extern char *remote_name(int sockfd); -/** used to crypt the communication between para_server and para_client */ -typedef void crypt_function(unsigned long len, - const unsigned char *indata, unsigned char *outdata, void *private_data); - -int send_buffer(int, const char *); int send_bin_buffer(int, const char *, size_t); +int send_buffer(int, const char *); __printf_2_3 int send_va_buffer(int fd, const char *fmt, ...); -int recv_buffer(int fd, char *buf, size_t size); + int recv_bin_buffer(int fd, char *buf, size_t size); +int recv_buffer(int fd, char *buf, size_t size); + int para_accept(int, void *addr, socklen_t size); int create_local_socket(const char *name, struct sockaddr_un *unix_addr, mode_t mode); @@ -96,6 +94,3 @@ int create_remote_socket(const char *name); int recv_cred_buffer(int, char *, size_t); ssize_t send_cred_buffer(int, char*); int recv_pattern(int fd, const char *pattern, size_t bufsize); -void enable_crypt(int fd, crypt_function *recv_f, crypt_function *send_f, - void *private_data); -void disable_crypt(int fd); diff --git a/ogg_afh.c b/ogg_afh.c index 55cf4ced..65573cfc 100644 --- a/ogg_afh.c +++ b/ogg_afh.c @@ -5,17 +5,17 @@ */ /** \file ogg_afh.c para_server's ogg vorbis audio format handler */ +#include #include #include #include #include +#include #include "para.h" -#include "afh.h" #include "error.h" +#include "afh.h" #include "string.h" -#include "afs.h" -#include "server.h" /** must be big enough to hold header */ #define CHUNK_SIZE 32768 @@ -233,26 +233,17 @@ static long unsigned ogg_compute_chunk_table(OggVorbis_File *of, return num_chunks; } -static void ogg_write_info_string(OggVorbis_File *vf, struct afh_info *afhi) +static void ogg_get_vorbis_comments(OggVorbis_File *vf, struct afh_info *afhi) { - char *taginfo; vorbis_comment *vc = ov_comment(vf,-1); - if (vc) { - char *artist, *title, *album, *year, *comment; - artist = vorbis_comment_query(vc, "artist", 0); - title = vorbis_comment_query(vc, "title", 0); - album = vorbis_comment_query(vc, "album", 0); - year = vorbis_comment_query(vc, "year", 0); - comment = vorbis_comment_query(vc, "comment", 0); - taginfo = make_taginfo(title, artist, album, year, comment); - } else - taginfo = make_message("%s: (no vorbis comments found)\n%s:\n", - status_item_list[SI_TAGINFO1], - status_item_list[SI_TAGINFO2]); - afhi->info_string = make_message("%s:\n%s", - status_item_list[SI_AUDIO_FILE_INFO], taginfo); - free(taginfo); + if (!vc) + return; + afhi->tags.artist = para_strdup(vorbis_comment_query(vc, "artist", 0)); + afhi->tags.title = para_strdup(vorbis_comment_query(vc, "title", 0)); + afhi->tags.album = para_strdup(vorbis_comment_query(vc, "album", 0)); + afhi->tags.year = para_strdup(vorbis_comment_query(vc, "year", 0)); + afhi->tags.comment = para_strdup(vorbis_comment_query(vc, "comment", 0)); } /* @@ -289,8 +280,7 @@ static int ogg_get_file_info(char *map, size_t numbytes, __a_unused int fd, afhi->chunks_total = ogg_compute_chunk_table(&of, afhi, afhi->seconds_total); afhi->chunk_tv.tv_sec = 0; afhi->chunk_tv.tv_usec = 250 * 1000; - tv_scale(10 / afhi->channels, &afhi->chunk_tv, &afhi->eof_tv); - ogg_write_info_string(&of, afhi); + ogg_get_vorbis_comments(&of, afhi); ret = 1; err: ov_clear(&of); /* keeps the file open */ diff --git a/oggdec_filter.c b/oggdec_filter.c index b158a5e1..02f04b48 100644 --- a/oggdec_filter.c +++ b/oggdec_filter.c @@ -6,8 +6,10 @@ /** \file oggdec_filter.c Paraslash's ogg vorbis decoder. */ -#include "para.h" +#include +#include +#include "para.h" #include "oggdec_filter.cmdline.h" #include "list.h" #include "sched.h" @@ -16,8 +18,6 @@ #include "error.h" #include "string.h" -#include - /** Determine byte sex. */ #ifdef WORDS_BIGENDIAN #define ENDIAN 1 diff --git a/osl.c b/osl.c deleted file mode 100644 index e2c1ef46..00000000 --- a/osl.c +++ /dev/null @@ -1,2082 +0,0 @@ -/* - * Copyright (C) 2007-2009 Andre Noll - * - * Licensed under the GPL v2. For licencing details see COPYING. - */ - -/** \file osl.c Object storage layer functions. */ -#include /* readdir() */ -#include - - -#include "para.h" -#include "error.h" -#include "fd.h" -#include "osl_core.h" -/** - * A wrapper for lseek(2). - * - * \param fd The file descriptor whose offset is to be to repositioned. - * \param offset A value-result parameter. - * \param whence Usual repositioning directive. - * - * Reposition the offset of the file descriptor \a fd to the argument \a offset - * according to the directive \a whence. Upon successful return, \a offset - * contains the resulting offset location as measured in bytes from the - * beginning of the file. - * - * \return Positive on success. Otherwise, the function returns \p -E_LSEEK. - * - * \sa lseek(2). - */ -int para_lseek(int fd, off_t *offset, int whence) -{ - int ret = -E_LSEEK; - - *offset = lseek(fd, *offset, whence); - if (*offset == -1) - return ret; - return 1; -} - -/** - * Wrapper for the write system call. - * - * \param fd The file descriptor to write to. - * \param buf The buffer to write. - * \param size The length of \a buf in bytes. - * - * This function writes out the given buffer and retries if an interrupt - * occurred during the write. - * - * \return On success, the number of bytes written is returned, otherwise, the - * function returns \p -E_WRITE. - * - * \sa write(2). - */ -ssize_t para_write(int fd, const void *buf, size_t size) -{ - ssize_t ret; - - for (;;) { - ret = write(fd, buf, size); - if ((ret < 0) && (errno == EAGAIN || errno == EINTR)) - continue; - return ret >= 0? ret : -E_WRITE; - } -} - -/** - * Write the whole buffer to a file descriptor. - * - * \param fd The file descriptor to write to. - * \param buf The buffer to write. - * \param size The length of \a buf in bytes. - * - * This function writes the given buffer and continues on short writes and - * when interrupted by a signal. - * - * \return Positive on success, negative on errors. Possible errors: any - * errors returned by para_write(). - * - * \sa para_write(). - */ -ssize_t para_write_all(int fd, const void *buf, size_t size) -{ -// PARA_DEBUG_LOG("writing %zu bytes\n", size); - const char *b = buf; - while (size) { - ssize_t ret = para_write(fd, b, size); -// PARA_DEBUG_LOG("ret: %zd\n", ret); - if (ret < 0) - return ret; - b += ret; - size -= ret; - } - return 1; -} -/** - * Open a file, write the given buffer and close the file. - * - * \param filename Full path to the file to open. - * \param buf The buffer to write to the file. - * \param size The size of \a buf. - * - * \return Positive on success, negative on errors. Possible errors include: - * any errors from para_open() or para_write(). - * - * \sa para_open(), para_write(). - */ -int para_write_file(const char *filename, const void *buf, size_t size) -{ - int ret, fd; - - ret = para_open(filename, O_WRONLY | O_CREAT | O_EXCL, 0644); - if (ret < 0) - return ret; - fd = ret; - ret = para_write_all(fd, buf, size); - if (ret < 0) - goto out; - ret = 1; -out: - close(fd); - return ret; -} - -static int append_file(const char *filename, char *header, size_t header_size, - char *data, size_t data_size, uint32_t *new_pos) -{ - int ret, fd; - -// PARA_DEBUG_LOG("appending %zu + %zu bytes\n", header_size, data_size); - ret = para_open(filename, O_WRONLY | O_CREAT | O_APPEND, 0644); - if (ret < 0) - return ret; - fd = ret; - if (header && header_size) { - ret = para_write_all(fd, header, header_size); - if (ret < 0) - goto out; - } - ret = para_write_all(fd, data, data_size); - if (ret < 0) - goto out; - if (new_pos) { - off_t offset = 0; - ret = para_lseek(fd, &offset, SEEK_END); - if (ret < 0) - goto out; -// PARA_DEBUG_LOG("new file size: " FMT_OFF_T "\n", offset); - *new_pos = offset; - } - ret = 1; -out: - close(fd); - return ret; -} - -/** - * Traverse the given directory recursively. - * - * \param dirname The directory to traverse. - * \param func The function to call for each entry. - * \param private_data Pointer to an arbitrary data structure. - * - * For each regular file under \a dirname, the supplied function \a func is - * called. The full path of the regular file and the \a private_data pointer - * are passed to \a func. Directories for which the calling process has no - * permissions to change to are silently ignored. - * - * \return Standard. - */ -int for_each_file_in_dir(const char *dirname, - int (*func)(const char *, void *), void *private_data) -{ - DIR *dir; - struct dirent *entry; - int cwd_fd, ret2, ret = para_opendir(dirname, &dir, &cwd_fd); - - if (ret < 0) - return ret == -ERRNO_TO_PARA_ERROR(EACCES)? 1 : ret; - /* scan cwd recursively */ - while ((entry = readdir(dir))) { - mode_t m; - char *tmp; - struct stat s; - - if (!strcmp(entry->d_name, ".")) - continue; - if (!strcmp(entry->d_name, "..")) - continue; - if (lstat(entry->d_name, &s) == -1) - continue; - m = s.st_mode; - if (!S_ISREG(m) && !S_ISDIR(m)) - continue; - tmp = make_message("%s/%s", dirname, entry->d_name); - if (!S_ISDIR(m)) { - ret = func(tmp, private_data); - free(tmp); - if (ret < 0) - goto out; - continue; - } - /* directory */ - ret = for_each_file_in_dir(tmp, func, private_data); - free(tmp); - if (ret < 0) - goto out; - } - ret = 1; -out: - closedir(dir); - ret2 = para_fchdir(cwd_fd); - if (ret2 < 0 && ret >= 0) - ret = ret2; - close(cwd_fd); - return ret; -} - -static int verify_name(const char *name) -{ - if (!name) - return -E_BAD_NAME; - if (!*name) - return -E_BAD_NAME; - if (strchr(name, '/')) - return -E_BAD_NAME; - if (!strcmp(name, "..")) - return -E_BAD_NAME; - if (!strcmp(name, ".")) - return -E_BAD_NAME; - return 1; -} - -/** - * Compare two osl objects pointing to unsigned integers of 32 bit size. - * - * \param obj1 Pointer to the first integer. - * \param obj2 Pointer to the second integer. - * - * \return The values required for an osl compare function. - * - * \sa osl_compare_func, osl_hash_compare(). - */ -int uint32_compare(const struct osl_object *obj1, const struct osl_object *obj2) -{ - uint32_t d1 = read_u32((const char *)obj1->data); - uint32_t d2 = read_u32((const char *)obj2->data); - - if (d1 < d2) - return 1; - if (d1 > d2) - return -1; - return 0; -} - -/** - * Compare two osl objects pointing to hash values. - * - * \param obj1 Pointer to the first hash object. - * \param obj2 Pointer to the second hash object. - * - * \return The values required for an osl compare function. - * - * \sa osl_compare_func, uint32_compare(). - */ -int osl_hash_compare(const struct osl_object *obj1, const struct osl_object *obj2) -{ - return hash_compare((HASH_TYPE *)obj1->data, (HASH_TYPE *)obj2->data); -} - -static char *disk_storage_dirname(const struct osl_table *t, unsigned col_num, - const char *ds_name) -{ - char *dirname, *column_name = column_filename(t, col_num); - - if (!(t->desc->flags & OSL_LARGE_TABLE)) - return column_name; - dirname = make_message("%s/%.2s", column_name, ds_name); - free(column_name); - return dirname; -} - -static char *disk_storage_name_of_object(const struct osl_table *t, - const struct osl_object *obj) -{ - HASH_TYPE hash[HASH_SIZE]; - hash_object(obj, hash); - return disk_storage_name_of_hash(t, hash); -} - -static int disk_storage_name_of_row(const struct osl_table *t, - const struct osl_row *row, char **name) -{ - struct osl_object obj; - int ret = osl_get_object(t, row, t->disk_storage_name_column, &obj); - - if (ret < 0) - return ret; - *name = disk_storage_name_of_object(t, &obj); - return 1; -} - -static void column_name_hash(const char *col_name, HASH_TYPE *hash) -{ - hash_function(col_name, strlen(col_name), hash); -} - -static int init_column_descriptions(struct osl_table *t) -{ - int i, j, ret; - const struct osl_column_description *cd; - - ret = verify_name(t->desc->name); - if (ret < 0) - goto err; - ret = -E_BAD_DB_DIR; - if (!t->desc->dir && (t->num_disk_storage_columns || t->num_mapped_columns)) - goto err; - /* the size of the index header without column descriptions */ - t->index_header_size = IDX_COLUMN_DESCRIPTIONS; - FOR_EACH_COLUMN(i, t->desc, cd) { - struct osl_column *col = t->columns + i; - if (cd->storage_flags & OSL_RBTREE) { - if (!cd->compare_function) - return -E_NO_COMPARE_FUNC; - } - if (cd->storage_type == OSL_NO_STORAGE) - continue; - ret = -E_NO_COLUMN_NAME; - if (!cd->name || !cd->name[0]) - goto err; - ret = verify_name(cd->name); - if (ret < 0) - goto err; - t->index_header_size += index_column_description_size(cd->name); - column_name_hash(cd->name, col->name_hash); - ret = -E_DUPLICATE_COL_NAME; - for (j = i + 1; j < t->desc->num_columns; j++) { - const char *name2 = get_column_description(t->desc, - j)->name; - if (cd->name && name2 && !strcmp(cd->name, name2)) - goto err; - } - } - return 1; -err: - return ret; -} - -/** - * Initialize a struct table from given table description. - * - * \param desc The description of the osl table. - * \param table_ptr Result is returned here. - * - * This function performs several sanity checks on \p desc and returns if any - * of these tests fail. On success, a struct \p osl_table is allocated and - * initialized with data derived from \p desc. - * - * \return Positive on success, negative on errors. Possible errors include: \p - * E_BAD_TABLE_DESC, \p E_NO_COLUMN_DESC, \p E_NO_COLUMNS, \p - * E_BAD_STORAGE_TYPE, \p E_BAD_STORAGE_FLAGS, \p E_BAD_STORAGE_SIZE, \p - * E_NO_UNIQUE_RBTREE_COLUMN, \p E_NO_RBTREE_COL. - * - * \sa struct osl_table. - */ -int init_table_structure(const struct osl_table_description *desc, - struct osl_table **table_ptr) -{ - const struct osl_column_description *cd; - struct osl_table *t = para_calloc(sizeof(*t)); - int i, ret = -E_BAD_TABLE_DESC, have_disk_storage_name_column = 0; - - if (!desc) - goto err; - PARA_DEBUG_LOG("creating table structure for '%s' from table " - "description\n", desc->name); - ret = -E_NO_COLUMN_DESC; - if (!desc->column_descriptions) - goto err; - ret = -E_NO_COLUMNS; - if (!desc->num_columns) - goto err; - t->columns = para_calloc(desc->num_columns * sizeof(struct osl_column)); - t->desc = desc; - FOR_EACH_COLUMN(i, t->desc, cd) { - enum osl_storage_type st = cd->storage_type; - enum osl_storage_flags sf = cd->storage_flags; - struct osl_column *col = &t->columns[i]; - - ret = -E_BAD_STORAGE_TYPE; - if (st != OSL_MAPPED_STORAGE && st != OSL_DISK_STORAGE - && st != OSL_NO_STORAGE) - goto err; - ret = -E_BAD_STORAGE_FLAGS; - if (st == OSL_DISK_STORAGE && sf & OSL_RBTREE) - goto err; - ret = -E_BAD_STORAGE_SIZE; - if (sf & OSL_FIXED_SIZE && !cd->data_size) - goto err; - switch (st) { - case OSL_DISK_STORAGE: - t->num_disk_storage_columns++; - break; - case OSL_MAPPED_STORAGE: - t->num_mapped_columns++; - col->index_offset = t->row_index_size; - t->row_index_size += 8; - break; - case OSL_NO_STORAGE: - col->volatile_num = t->num_volatile_columns; - t->num_volatile_columns++; - break; - } - if (sf & OSL_RBTREE) { - col->rbtree_num = t->num_rbtrees; - t->num_rbtrees++; - if ((sf & OSL_UNIQUE) && (st == OSL_MAPPED_STORAGE)) { - if (!have_disk_storage_name_column) - t->disk_storage_name_column = i; - have_disk_storage_name_column = 1; - } - } - } - ret = -E_NO_UNIQUE_RBTREE_COLUMN; - if (t->num_disk_storage_columns && !have_disk_storage_name_column) - goto err; - ret = -E_NO_RBTREE_COL; - if (!t->num_rbtrees) - goto err; - /* success */ - PARA_DEBUG_LOG("OK. Index entry size: %u\n", t->row_index_size); - ret = init_column_descriptions(t); - if (ret < 0) - goto err; - *table_ptr = t; - return 1; -err: - free(t->columns); - free(t); - return ret; -} - -/** - * Read the table description from index header. - * - * \param map The memory mapping of the index file. - * \param desc The values found in the index header are returned here. - * - * Read the index header, check for the paraslash magic string and the table version number. - * Read all information stored in the index header into \a desc. - * - * \return Positive on success, negative on errors. - * - * \sa struct osl_table_description, osl_create_table. - */ -int read_table_desc(struct osl_object *map, struct osl_table_description *desc) -{ - char *buf = map->data; - uint8_t version; - uint16_t header_size; - int ret, i; - unsigned offset; - struct osl_column_description *cd; - - if (map->size < MIN_INDEX_HEADER_SIZE(1)) - return -E_SHORT_TABLE; - if (strncmp(buf + IDX_PARA_MAGIC, PARA_MAGIC, strlen(PARA_MAGIC))) - return -E_NO_MAGIC; - version = read_u8(buf + IDX_VERSION); - if (version < MIN_TABLE_VERSION || version > MAX_TABLE_VERSION) - return -E_VERSION_MISMATCH; - desc->num_columns = read_u8(buf + IDX_TABLE_FLAGS); - desc->flags = read_u8(buf + IDX_TABLE_FLAGS); - desc->num_columns = read_u16(buf + IDX_NUM_COLUMNS); - PARA_DEBUG_LOG("%u columns\n", desc->num_columns); - if (!desc->num_columns) - return -E_NO_COLUMNS; - header_size = read_u16(buf + IDX_HEADER_SIZE); - if (map->size < header_size) - return -E_BAD_SIZE; - desc->column_descriptions = para_calloc(desc->num_columns - * sizeof(struct osl_column_description)); - offset = IDX_COLUMN_DESCRIPTIONS; - FOR_EACH_COLUMN(i, desc, cd) { - char *null_byte; - - ret = -E_SHORT_TABLE; - if (map->size < offset + MIN_IDX_COLUMN_DESCRIPTION_SIZE) { - PARA_ERROR_LOG("map size = %zu < %u = offset + min desc size\n", - map->size, offset + MIN_IDX_COLUMN_DESCRIPTION_SIZE); - goto err; - } - cd->storage_type = read_u16(buf + offset + IDX_CD_STORAGE_TYPE); - cd->storage_flags = read_u16(buf + offset + - IDX_CD_STORAGE_FLAGS); - cd->data_size = read_u32(buf + offset + IDX_CD_DATA_SIZE); - null_byte = memchr(buf + offset + IDX_CD_NAME, '\0', - map->size - offset - IDX_CD_NAME); - ret = -E_INDEX_CORRUPTION; - if (!null_byte) - goto err; - cd->name = para_strdup(buf + offset + IDX_CD_NAME); - offset += index_column_description_size(cd->name); - } - if (offset != header_size) { - ret = -E_INDEX_CORRUPTION; - PARA_ERROR_LOG("real header size = %u != %u = stored header size\n", - offset, header_size); - goto err; - } - return 1; -err: - FOR_EACH_COLUMN(i, desc, cd) - free(cd->name); - return ret; -} - -/* - * check whether the table description given by \p t->desc matches the on-disk - * table structure stored in the index of \a t. - */ -static int compare_table_descriptions(struct osl_table *t) -{ - int i, ret; - struct osl_table_description desc; - const struct osl_column_description *cd1, *cd2; - - /* read the on-disk structure into desc */ - ret = read_table_desc(&t->index_map, &desc); - if (ret < 0) - return ret; - ret = -E_BAD_TABLE_FLAGS; - if (desc.flags != t->desc->flags) - goto out; - ret = -E_BAD_COLUMN_NUM; - if (desc.num_columns != t->desc->num_columns) - goto out; - FOR_EACH_COLUMN(i, t->desc, cd1) { - cd2 = get_column_description(&desc, i); - ret = -E_BAD_STORAGE_TYPE; - if (cd1->storage_type != cd2->storage_type) - goto out; - ret = -E_BAD_STORAGE_FLAGS; - if (cd1->storage_flags != cd2->storage_flags) { - PARA_ERROR_LOG("sf1 = %u != %u = sf2\n", - cd1->storage_flags, cd2->storage_flags); - goto out; - } - ret = -E_BAD_DATA_SIZE; - if (cd1->storage_flags & OSL_FIXED_SIZE) - if (cd1->data_size != cd2->data_size) - goto out; - ret = -E_BAD_COLUMN_NAME; - if (strcmp(cd1->name, cd2->name)) - goto out; - } - PARA_DEBUG_LOG("table description of '%s' matches on-disk data, good\n", - t->desc->name); - ret = 1; -out: - FOR_EACH_COLUMN(i, &desc, cd1) - free(cd1->name); - free(desc.column_descriptions); - return ret; -} - -static int create_table_index(struct osl_table *t) -{ - char *buf, *filename; - int i, ret; - size_t size = t->index_header_size; - const struct osl_column_description *cd; - unsigned offset; - - PARA_INFO_LOG("creating %zu byte index for table %s\n", size, - t->desc->name); - buf = para_calloc(size); - sprintf(buf + IDX_PARA_MAGIC, "%s", PARA_MAGIC); - write_u8(buf + IDX_TABLE_FLAGS, t->desc->flags); - write_u8(buf + IDX_DIRTY_FLAG, 0); - write_u8(buf + IDX_VERSION, CURRENT_TABLE_VERSION); - write_u16(buf + IDX_NUM_COLUMNS, t->desc->num_columns); - write_u16(buf + IDX_HEADER_SIZE, t->index_header_size); - offset = IDX_COLUMN_DESCRIPTIONS; - FOR_EACH_COLUMN(i, t->desc, cd) { - write_u16(buf + offset + IDX_CD_STORAGE_TYPE, - cd->storage_type); - write_u16(buf + offset + IDX_CD_STORAGE_FLAGS, - cd->storage_flags); - if (cd->storage_flags & OSL_FIXED_SIZE) - write_u32(buf + offset + IDX_CD_DATA_SIZE, - cd->data_size); - strcpy(buf + offset + IDX_CD_NAME, cd->name); - offset += index_column_description_size(cd->name); - } - assert(offset = size); - filename = index_filename(t->desc); - ret = para_write_file(filename, buf, size); - free(buf); - free(filename); - return ret; -} - -/** - * Create a new osl table. - * - * \param desc Pointer to the table description. - * - * \return Standard. - */ -int osl_create_table(const struct osl_table_description *desc) -{ - const struct osl_column_description *cd; - char *table_dir = NULL, *filename; - struct osl_table *t; - int i, ret = init_table_structure(desc, &t); - - if (ret < 0) - return ret; - PARA_INFO_LOG("creating %s\n", desc->name); - FOR_EACH_COLUMN(i, t->desc, cd) { - if (cd->storage_type == OSL_NO_STORAGE) - continue; - if (!table_dir) { - ret = para_mkdir(desc->dir, 0777); - if (ret < 0 && !is_errno(-ret, EEXIST)) - goto out; - table_dir = make_message("%s/%s", desc->dir, - desc->name); - ret = para_mkdir(table_dir, 0777); - if (ret < 0) - goto out; - } - filename = column_filename(t, i); - PARA_INFO_LOG("filename: %s\n", filename); - if (cd->storage_type == OSL_MAPPED_STORAGE) { - ret = para_open(filename, O_RDWR | O_CREAT | O_EXCL, - 0644); - free(filename); - if (ret < 0) - goto out; - close(ret); - continue; - } - /* DISK STORAGE */ - ret = para_mkdir(filename, 0777); - free(filename); - if (ret < 0) - goto out; - } - if (t->num_mapped_columns) { - ret = create_table_index(t); - if (ret < 0) - goto out; - } - ret = 1; -out: - free(table_dir); - free(t->columns); - free(t); - return ret; -} - -static int table_is_dirty(struct osl_table *t) -{ - char *buf = (char *)t->index_map.data + IDX_DIRTY_FLAG; - uint8_t dirty = read_u8(buf) & 0x1; - return !!dirty; -} - -static void mark_table_dirty(struct osl_table *t) -{ - char *buf = (char *)t->index_map.data + IDX_DIRTY_FLAG; - write_u8(buf, read_u8(buf) | 1); -} - -static void mark_table_clean(struct osl_table *t) -{ - char *buf = (char *)t->index_map.data + IDX_DIRTY_FLAG; - write_u8(buf, read_u8(buf) & 0xfe); -} - -static void unmap_column(struct osl_table *t, unsigned col_num) -{ - struct osl_object map = t->columns[col_num].data_map; - int ret; - if (!map.data) - return; - ret = para_munmap(map.data, map.size); - assert(ret > 0); - map.data = NULL; -} - -/** - * Unmap all mapped files of an osl table. - * - * \param t Pointer to a mapped table. - * \param flags Options for unmapping. - * - * \return Positive on success, negative on errors. - * - * \sa map_table(), enum osl_close_flags, para_munmap(). - */ -int unmap_table(struct osl_table *t, enum osl_close_flags flags) -{ - unsigned i; - const struct osl_column_description *cd; - int ret; - - if (!t->num_mapped_columns) /* can this ever happen? */ - return 1; - PARA_DEBUG_LOG("unmapping table '%s'\n", t->desc->name); - if (!t->index_map.data) - return -E_NOT_MAPPED; - if (flags & OSL_MARK_CLEAN) - mark_table_clean(t); - ret = para_munmap(t->index_map.data, t->index_map.size); - if (ret < 0) - return ret; - t->index_map.data = NULL; - if (!t->num_rows) - return 1; - FOR_EACH_MAPPED_COLUMN(i, t, cd) - unmap_column(t, i); - return 1; -} - -static int map_column(struct osl_table *t, unsigned col_num) -{ - struct stat statbuf; - char *filename = column_filename(t, col_num); - int ret = -E_STAT; - if (stat(filename, &statbuf) < 0) { - free(filename); - return ret; - } - if (!(S_IFREG & statbuf.st_mode)) { - free(filename); - return ret; - } - ret = mmap_full_file(filename, O_RDWR, - &t->columns[col_num].data_map.data, - &t->columns[col_num].data_map.size, - NULL); - free(filename); - return ret; -} - -/** - * Map the index file and all columns of type \p OSL_MAPPED_STORAGE into memory. - * - * \param t Pointer to an initialized table structure. - * \param flags Mapping options. - * - * \return Negative return value on errors; on success the number of rows - * (including invalid rows) is returned. - * - * \sa unmap_table(), enum map_table_flags, osl_open_table(), mmap(2). - */ -int map_table(struct osl_table *t, enum map_table_flags flags) -{ - char *filename; - const struct osl_column_description *cd; - int i = 0, ret, num_rows = 0; - - if (!t->num_mapped_columns) - return 0; - if (t->index_map.data) - return -E_ALREADY_MAPPED; - filename = index_filename(t->desc); - PARA_DEBUG_LOG("mapping table '%s' (index: %s)\n", t->desc->name, filename); - ret = mmap_full_file(filename, flags & MAP_TBL_FL_MAP_RDONLY? - O_RDONLY : O_RDWR, &t->index_map.data, &t->index_map.size, NULL); - free(filename); - if (ret < 0) - return ret; - if (flags & MAP_TBL_FL_VERIFY_INDEX) { - ret = compare_table_descriptions(t); - if (ret < 0) - goto err; - } - ret = -E_BUSY; - if (!(flags & MAP_TBL_FL_IGNORE_DIRTY)) { - if (table_is_dirty(t)) { - PARA_ERROR_LOG("%s is dirty\n", t->desc->name); - goto err; - } - } - mark_table_dirty(t); - num_rows = table_num_rows(t); - if (!num_rows) - return num_rows; - /* map data files */ - FOR_EACH_MAPPED_COLUMN(i, t, cd) { - ret = map_column(t, i); - if (ret < 0) - goto err; - } - return num_rows; -err: /* unmap what is already mapped */ - for (i--; i >= 0; i--) { - struct osl_object map = t->columns[i].data_map; - para_munmap(map.data, map.size); - map.data = NULL; - } - para_munmap(t->index_map.data, t->index_map.size); - t->index_map.data = NULL; - return ret; -} - -/** - * Retrieve a mapped object by row and column number. - * - * \param t Pointer to an open osl table. - * \param col_num Number of the mapped column containing the object to retrieve. - * \param row_num Number of the row containing the object to retrieve. - * \param obj The result is returned here. - * - * It is considered an error if \a col_num does not refer to a column - * of storage type \p OSL_MAPPED_STORAGE. - * - * \return Positive on success, negative on errors. Possible errors include: - * \p E_BAD_ROW_NUM, \p E_INVALID_OBJECT. - * - * \sa osl_storage_type. - */ -int get_mapped_object(const struct osl_table *t, unsigned col_num, - uint32_t row_num, struct osl_object *obj) -{ - struct osl_column *col = &t->columns[col_num]; - uint32_t offset; - char *header; - char *cell_index; - int ret; - - if (t->num_rows <= row_num) - return -E_BAD_ROW_NUM; - ret = get_cell_index(t, row_num, col_num, &cell_index); - if (ret < 0) - return ret; - offset = read_u32(cell_index); - obj->size = read_u32(cell_index + 4) - 1; - header = col->data_map.data + offset; - obj->data = header + 1; - if (read_u8(header) == 0xff) { - PARA_ERROR_LOG("col %u, size %zu, offset %u\n", col_num, - obj->size, offset); - return -E_INVALID_OBJECT; - } - return 1; -} - -static int search_rbtree(const struct osl_object *obj, - const struct osl_table *t, unsigned col_num, - struct rb_node **result, struct rb_node ***rb_link) -{ - struct osl_column *col = &t->columns[col_num]; - struct rb_node **new = &col->rbtree.rb_node, *parent = NULL; - const struct osl_column_description *cd = - get_column_description(t->desc, col_num); - enum osl_storage_type st = cd->storage_type; - while (*new) { - struct osl_row *this_row = get_row_pointer(*new, - col->rbtree_num); - int ret; - struct osl_object this_obj; - parent = *new; - if (st == OSL_MAPPED_STORAGE) { - ret = get_mapped_object(t, col_num, this_row->num, - &this_obj); - if (ret < 0) - return ret; - } else - this_obj = this_row->volatile_objects[col->volatile_num]; - ret = cd->compare_function(obj, &this_obj); - if (!ret) { - if (result) - *result = get_rb_node_pointer(this_row, - col->rbtree_num); - return 1; - } - if (ret < 0) - new = &((*new)->rb_left); - else - new = &((*new)->rb_right); - } - if (result) - *result = parent; - if (rb_link) - *rb_link = new; - return -E_RB_KEY_NOT_FOUND; -} - -static int insert_rbtree(struct osl_table *t, unsigned col_num, - const struct osl_row *row, const struct osl_object *obj) -{ - struct rb_node *parent, **rb_link; - unsigned rbtree_num; - struct rb_node *n; - int ret = search_rbtree(obj, t, col_num, &parent, &rb_link); - - if (ret > 0) - return -E_RB_KEY_EXISTS; - rbtree_num = t->columns[col_num].rbtree_num; - n = get_rb_node_pointer(row, rbtree_num); - rb_link_node(n, parent, rb_link); - rb_insert_color(n, &t->columns[col_num].rbtree); - return 1; -} - -static void remove_rb_node(struct osl_table *t, unsigned col_num, - const struct osl_row *row) -{ - struct osl_column *col = &t->columns[col_num]; - const struct osl_column_description *cd = - get_column_description(t->desc, col_num); - enum osl_storage_flags sf = cd->storage_flags; - struct rb_node *victim, *splice_out_node, *tmp; - if (!(sf & OSL_RBTREE)) - return; - /* - * Which node is removed/spliced out actually depends on how many - * children the victim node has: If it has no children, it gets - * deleted. If it has one child, it gets spliced out. If it has two - * children, its successor (which has at most a right child) gets - * spliced out. - */ - victim = get_rb_node_pointer(row, col->rbtree_num); - if (victim->rb_left && victim->rb_right) - splice_out_node = rb_next(victim); - else - splice_out_node = victim; - /* Go up to the root and decrement the size of each node in the path. */ - for (tmp = splice_out_node; tmp; tmp = rb_parent(tmp)) - tmp->size--; - rb_erase(victim, &col->rbtree); -} - -static int add_row_to_rbtrees(struct osl_table *t, uint32_t row_num, - struct osl_object *volatile_objs, struct osl_row **row_ptr) -{ - unsigned i; - int ret; - struct osl_row *row = allocate_row(t->num_rbtrees); - const struct osl_column_description *cd; - - row->num = row_num; - row->volatile_objects = volatile_objs; - FOR_EACH_RBTREE_COLUMN(i, t, cd) { - if (cd->storage_type == OSL_MAPPED_STORAGE) { - struct osl_object obj; - ret = get_mapped_object(t, i, row_num, &obj); - if (ret < 0) - goto err; - ret = insert_rbtree(t, i, row, &obj); - } else { /* volatile */ - const struct osl_object *obj - = volatile_objs + t->columns[i].volatile_num; - ret = insert_rbtree(t, i, row, obj); - } - if (ret < 0) - goto err; - } - if (row_ptr) - *row_ptr = row; - return 1; -err: /* rollback changes, i.e. remove added entries from rbtrees */ - while (i) - remove_rb_node(t, i--, row); - free(row); - return ret; -} - -static void free_volatile_objects(const struct osl_table *t, - enum osl_close_flags flags) -{ - int i, j; - struct rb_node *n; - struct osl_column *rb_col; - const struct osl_column_description *cd; - - if (!t->num_volatile_columns) - return; - /* find the first rbtree column (any will do) */ - FOR_EACH_RBTREE_COLUMN(i, t, cd) - break; - rb_col = t->columns + i; - /* walk that rbtree and free all volatile objects */ - for (n = rb_first(&rb_col->rbtree); n; n = rb_next(n)) { - struct osl_row *r = get_row_pointer(n, rb_col->rbtree_num); - if (flags & OSL_FREE_VOLATILE) - FOR_EACH_VOLATILE_COLUMN(j, t, cd) { - if (cd->storage_flags & OSL_DONT_FREE) - continue; - free(r->volatile_objects[ - t->columns[j].volatile_num].data); - } -// for (j = 0; j < t->num_volatile_columns; j++) -// free(r->volatile_objects[j].data); - free(r->volatile_objects); - } -} - -/** - * Erase all rbtree nodes and free resources. - * - * \param t Pointer to an open osl table. - * - * This function is called by osl_close_table(). - */ -void clear_rbtrees(struct osl_table *t) -{ - const struct osl_column_description *cd; - unsigned i, rbtrees_cleared = 0; - - FOR_EACH_RBTREE_COLUMN(i, t, cd) { - struct osl_column *col = &t->columns[i]; - struct rb_node *n; - rbtrees_cleared++; - for (n = rb_first(&col->rbtree); n;) { - struct osl_row *r; - rb_erase(n, &col->rbtree); - if (rbtrees_cleared == t->num_rbtrees) { - r = get_row_pointer(n, col->rbtree_num); - n = rb_next(n); - free(r); - } else - n = rb_next(n); - } - } - -} - -/** - * Close an osl table. - * - * \param t Pointer to the table to be closed. - * \param flags Options for what should be cleaned up. - * - * If osl_open_table() succeeds, the resulting table pointer must later be - * passed to this function in order to flush all changes to the file system and - * to free the resources that were allocated by osl_open_table(). - * - * \return Positive on success, negative on errors. Possible errors: \p E_BAD_TABLE, - * errors returned by unmap_table(). - * - * \sa osl_open_table(), unmap_table(). - */ -int osl_close_table(struct osl_table *t, enum osl_close_flags flags) -{ - int ret; - - if (!t) - return -E_BAD_TABLE; - free_volatile_objects(t, flags); - clear_rbtrees(t); - ret = unmap_table(t, flags); - if (ret < 0) - PARA_ERROR_LOG("unmap_table failed: %d\n", ret); - free(t->columns); - free(t); - return ret; -} - -/** - * Find out whether the given row number corresponds to an invalid row. - * - * \param t Pointer to the osl table. - * \param row_num The number of the row in question. - * - * By definition, a row is considered invalid if all its index entries - * are invalid. - * - * \return Positive if \a row_num corresponds to an invalid row, - * zero if it corresponds to a valid row, negative on errors. - */ -int row_is_invalid(struct osl_table *t, uint32_t row_num) -{ - char *row_index; - int i, ret = get_row_index(t, row_num, &row_index); - - if (ret < 0) - return ret; - for (i = 0; i < t->row_index_size; i++) { - if ((unsigned char)row_index[i] != 0xff) - return 0; - } - PARA_INFO_LOG("row %d is invalid\n", row_num); - return 1; -} - -/** - * Invalidate a row of an osl table. - * - * \param t Pointer to an open osl table. - * \param row_num Number of the row to mark as invalid. - * - * This function marks each mapped object in the index entry of \a row as - * invalid. - * - * \return Positive on success, negative on errors. - */ -int mark_row_invalid(struct osl_table *t, uint32_t row_num) -{ - char *row_index; - int ret = get_row_index(t, row_num, &row_index); - - if (ret < 0) - return ret; - PARA_INFO_LOG("marking row %d as invalid\n", row_num); - memset(row_index, 0xff, t->row_index_size); - return 1; -} - -/** - * Initialize all rbtrees and compute number of invalid rows. - * - * \param t The table containing the rbtrees to be initialized. - * - * \return Positive on success, negative on errors. - */ -int init_rbtrees(struct osl_table *t) -{ - int i, ret; - const struct osl_column_description *cd; - - /* create rbtrees */ - FOR_EACH_RBTREE_COLUMN(i, t, cd) - t->columns[i].rbtree = RB_ROOT; - /* add valid rows to rbtrees */ - t->num_invalid_rows = 0; - for (i = 0; i < t->num_rows; i++) { - ret = row_is_invalid(t, i); - if (ret < 0) - return ret; - if (ret) { - t->num_invalid_rows++; - continue; - } - ret = add_row_to_rbtrees(t, i, NULL, NULL); - if (ret < 0) - return ret; - } - return 1; -} - -/** - * Open an osl table. - * - * Each osl table must be opened before its data can be accessed. - * - * \param table_desc Describes the table to be opened. - * \param result Contains a pointer to the open table on success. - * - * The table description given by \a desc should coincide with the - * description used at creation time. - * - * \return Standard. - */ -int osl_open_table(const struct osl_table_description *table_desc, - struct osl_table **result) -{ - int i, ret; - struct osl_table *t; - const struct osl_column_description *cd; - - PARA_INFO_LOG("opening table %s\n", table_desc->name); - ret = init_table_structure(table_desc, &t); - if (ret < 0) - return ret; - FOR_EACH_DISK_STORAGE_COLUMN(i, t, cd) { - /* check if directory exists */ - char *dirname = column_filename(t, i); - struct stat statbuf; - ret = stat(dirname, &statbuf); - free(dirname); - if (ret < 0) { - ret = -ERRNO_TO_PARA_ERROR(errno); - goto err; - } - ret = -ERRNO_TO_PARA_ERROR(ENOTDIR); - if (!S_ISDIR(statbuf.st_mode)) - goto err; - } - ret = map_table(t, MAP_TBL_FL_VERIFY_INDEX); - if (ret < 0) - goto err; - t->num_rows = ret; - PARA_DEBUG_LOG("num rows: %d\n", t->num_rows); - ret = init_rbtrees(t); - if (ret < 0) { - osl_close_table(t, OSL_MARK_CLEAN); /* ignore further errors */ - return ret; - } - *result = t; - return 1; -err: - free(t->columns); - free(t); - return ret; -} - -static int create_disk_storage_object_dir(const struct osl_table *t, - unsigned col_num, const char *ds_name) -{ - char *dirname; - int ret; - - if (!(t->desc->flags & OSL_LARGE_TABLE)) - return 1; - dirname = disk_storage_dirname(t, col_num, ds_name); - ret = para_mkdir(dirname, 0777); - free(dirname); - if (ret < 0 && !is_errno(-ret, EEXIST)) - return ret; - return 1; -} - -static int write_disk_storage_file(const struct osl_table *t, unsigned col_num, - const struct osl_object *obj, const char *ds_name) -{ - int ret; - char *filename; - - ret = create_disk_storage_object_dir(t, col_num, ds_name); - if (ret < 0) - return ret; - filename = disk_storage_path(t, col_num, ds_name); - ret = para_write_file(filename, obj->data, obj->size); - free(filename); - return ret; -} - -static int append_map_file(const struct osl_table *t, unsigned col_num, - const struct osl_object *obj, uint32_t *new_size) -{ - char *filename = column_filename(t, col_num); - int ret; - char header = 0; /* zero means valid object */ - -// PARA_DEBUG_LOG("appending %zu + 1 byte\n", obj->size); - ret = append_file(filename, &header, 1, obj->data, obj->size, - new_size); - free(filename); - return ret; -} - -static int append_row_index(const struct osl_table *t, char *row_index) -{ - char *filename; - int ret; - - if (!t->num_mapped_columns) - return 1; - filename = index_filename(t->desc); - ret = append_file(filename, NULL, 0, row_index, - t->row_index_size, NULL); - free(filename); - return ret; -} - -/** - * A wrapper for truncate(2) - * - * \param path Name of the regular file to truncate - * \param size Number of bytes to \b shave \b off - * - * Truncate the regular file named by \a path by \a size bytes. - * - * \return Positive on success, negative on errors. Possible errors include: \p - * E_STAT, \p E_BAD_SIZE, \p E_TRUNC. - * - * \sa truncate(2) - */ -int para_truncate(const char *path, off_t size) -{ - int ret; - struct stat statbuf; - - ret = -E_STAT; - if (stat(path, &statbuf) < 0) - goto out; - ret = -E_BAD_SIZE; - if (statbuf.st_size < size) - goto out; - ret = -E_TRUNC; - if (truncate(path, statbuf.st_size - size) < 0) - goto out; - ret = 1; -out: - return ret; -} - -static int truncate_mapped_file(const struct osl_table *t, unsigned col_num, - off_t size) -{ - char *filename = column_filename(t, col_num); - int ret = para_truncate(filename, size); - free(filename); - return ret; -} - -static int delete_disk_storage_file(const struct osl_table *t, unsigned col_num, - const char *ds_name) -{ - char *dirname, *filename = disk_storage_path(t, col_num, ds_name); - int ret = unlink(filename), err = errno; - - free(filename); - if (ret < 0) - return -ERRNO_TO_PARA_ERROR(err); - if (!(t->desc->flags & OSL_LARGE_TABLE)) - return 1; - dirname = disk_storage_dirname(t, col_num, ds_name); - rmdir(dirname); - free(dirname); - return 1; -} - -/** - * Add a new row to an osl table and retrieve this row. - * - * \param t Pointer to an open osl table. - * \param objects Array of objects to be added. - * \param row Result pointer. - * - * The \a objects parameter must point to an array containing one object per - * column. The order of the objects in the array is given by the table - * description of \a table. Several sanity checks are performed during object - * insertion and the function returns without modifying the table if any of - * these tests fail. In fact, it is atomic in the sense that it either - * succeeds or leaves the table unchanged (i.e. either all or none of the - * objects are added to the table). - * - * It is considered an error if an object is added to a column with associated - * rbtree if this object is equal to an object already contained in that column - * (i.e. the compare function for the column's rbtree returns zero). - * - * Possible errors include: \p E_RB_KEY_EXISTS, \p E_BAD_DATA_SIZE. - * - * \return Positive on success, negative on errors. - * - * \sa struct osl_table_description, osl_compare_func, osl_add_row(). - */ -int osl_add_and_get_row(struct osl_table *t, struct osl_object *objects, - struct osl_row **row) -{ - int i, ret; - char *ds_name = NULL; - struct rb_node **rb_parents = NULL, ***rb_links = NULL; - char *new_row_index = NULL; - struct osl_object *volatile_objs = NULL; - const struct osl_column_description *cd; - - if (!t) - return -E_BAD_TABLE; - rb_parents = para_malloc(t->num_rbtrees * sizeof(struct rn_node*)); - rb_links = para_malloc(t->num_rbtrees * sizeof(struct rn_node**)); - if (t->num_mapped_columns) - new_row_index = para_malloc(t->row_index_size); - /* pass 1: sanity checks */ -// PARA_DEBUG_LOG("sanity tests: %p:%p\n", objects[0].data, -// objects[1].data); - FOR_EACH_COLUMN(i, t->desc, cd) { - enum osl_storage_type st = cd->storage_type; - enum osl_storage_flags sf = cd->storage_flags; - -// ret = -E_NULL_OBJECT; -// if (!objects[i]) -// goto out; - if (st == OSL_DISK_STORAGE) - continue; - if (sf & OSL_RBTREE) { - unsigned rbtree_num = t->columns[i].rbtree_num; - ret = -E_RB_KEY_EXISTS; -// PARA_DEBUG_LOG("checking whether %p exists\n", -// objects[i].data); - if (search_rbtree(objects + i, t, i, - &rb_parents[rbtree_num], - &rb_links[rbtree_num]) > 0) - goto out; - } - if (sf & OSL_FIXED_SIZE) { -// PARA_DEBUG_LOG("fixed size. need: %zu, have: %d\n", -// objects[i].size, cd->data_size); - ret = -E_BAD_DATA_SIZE; - if (objects[i].size != cd->data_size) - goto out; - } - } - if (t->num_disk_storage_columns) - ds_name = disk_storage_name_of_object(t, - &objects[t->disk_storage_name_column]); - ret = unmap_table(t, OSL_MARK_CLEAN); - if (ret < 0) - goto out; -// PARA_DEBUG_LOG("sanity tests passed%s\n", ""); - /* pass 2: create data files, append map data */ - FOR_EACH_COLUMN(i, t->desc, cd) { - enum osl_storage_type st = cd->storage_type; - if (st == OSL_NO_STORAGE) - continue; - if (st == OSL_MAPPED_STORAGE) { - uint32_t new_size; - struct osl_column *col = &t->columns[i]; -// PARA_DEBUG_LOG("appending object of size %zu\n", -// objects[i].size); - ret = append_map_file(t, i, objects + i, &new_size); - if (ret < 0) - goto rollback; - update_cell_index(new_row_index, col, new_size, - objects[i].size); - continue; - } - /* DISK_STORAGE */ - ret = write_disk_storage_file(t, i, objects + i, ds_name); - if (ret < 0) - goto rollback; - } - ret = append_row_index(t, new_row_index); - if (ret < 0) - goto rollback; - ret = map_table(t, MAP_TBL_FL_VERIFY_INDEX); - if (ret < 0) { /* truncate index and rollback changes */ - char *filename = index_filename(t->desc); - para_truncate(filename, t->row_index_size); - free(filename); - goto rollback; - } - /* pass 3: add entry to rbtrees */ - if (t->num_volatile_columns) { - volatile_objs = para_calloc(t->num_volatile_columns - * sizeof(struct osl_object)); - FOR_EACH_VOLATILE_COLUMN(i, t, cd) - volatile_objs[t->columns[i].volatile_num] = objects[i]; - } - t->num_rows++; -// PARA_DEBUG_LOG("adding new entry as row #%d\n", t->num_rows - 1); - ret = add_row_to_rbtrees(t, t->num_rows - 1, volatile_objs, row); - if (ret < 0) - goto out; -// PARA_DEBUG_LOG("added new entry as row #%d\n", t->num_rows - 1); - ret = 1; - goto out; -rollback: /* rollback all changes made, ignore further errors */ - for (i--; i >= 0; i--) { - enum osl_storage_type st; - - cd = get_column_description(t->desc, i); - st = cd->storage_type; - if (st == OSL_NO_STORAGE) - continue; - - if (st == OSL_MAPPED_STORAGE) - truncate_mapped_file(t, i, objects[i].size); - else /* disk storage */ - delete_disk_storage_file(t, i, ds_name); - } - /* ignore error and return previous error value */ - map_table(t, MAP_TBL_FL_VERIFY_INDEX); -out: - free(new_row_index); - free(ds_name); - free(rb_parents); - free(rb_links); - return ret; -} - -/** - * Add a new row to an osl table. - * - * \param t Same meaning as osl_add_and_get_row(). - * \param objects Same meaning as osl_add_and_get_row(). - * - * \return The return value of the underlying call to osl_add_and_get_row(). - * - * This is equivalent to osl_add_and_get_row(t, objects, NULL). - */ -int osl_add_row(struct osl_table *t, struct osl_object *objects) -{ - return osl_add_and_get_row(t, objects, NULL); -} - -/** - * Retrieve an object identified by row and column - * - * \param t Pointer to an open osl table. - * \param r Pointer to the row. - * \param col_num The column number. - * \param object The result pointer. - * - * The column determined by \a col_num must be of type \p OSL_MAPPED_STORAGE - * or \p OSL_NO_STORAGE, i.e. no disk storage objects may be retrieved by this - * function. - * - * \return Positive if object was found, negative on errors. Possible errors - * include: \p E_BAD_TABLE, \p E_BAD_STORAGE_TYPE. - * - * \sa osl_storage_type, osl_open_disk_object(). - */ -int osl_get_object(const struct osl_table *t, const struct osl_row *r, - unsigned col_num, struct osl_object *object) -{ - const struct osl_column_description *cd; - - if (!t) - return -E_BAD_TABLE; - cd = get_column_description(t->desc, col_num); - /* col must not be disk storage */ - if (cd->storage_type == OSL_DISK_STORAGE) - return -E_BAD_STORAGE_TYPE; - if (cd->storage_type == OSL_MAPPED_STORAGE) - return get_mapped_object(t, col_num, r->num, object); - /* volatile */ - *object = r->volatile_objects[t->columns[col_num].volatile_num]; - return 1; -} - -static int mark_mapped_object_invalid(const struct osl_table *t, - uint32_t row_num, unsigned col_num) -{ - struct osl_object obj; - char *p; - int ret = get_mapped_object(t, col_num, row_num, &obj); - - if (ret < 0) - return ret; - p = obj.data; - p--; - *p = 0xff; - return 1; -} - -/** - * Delete a row from an osl table. - * - * \param t Pointer to an open osl table. - * \param row Pointer to the row to delete. - * - * This removes all disk storage objects, removes all rbtree nodes, and frees - * all volatile objects belonging to the given row. For mapped columns, the - * data is merely marked invalid and may be pruned from time to time by - * para_fsck. - * - * \return Positive on success, negative on errors. Possible errors include: - * \p E_BAD_TABLE, errors returned by osl_get_object(). - */ -int osl_del_row(struct osl_table *t, struct osl_row *row) -{ - struct osl_row *r = row; - int i, ret; - const struct osl_column_description *cd; - - if (!t) - return -E_BAD_TABLE; - PARA_INFO_LOG("deleting row %p\n", row); - - if (t->num_disk_storage_columns) { - char *ds_name; - ret = disk_storage_name_of_row(t, r, &ds_name); - if (ret < 0) - goto out; - FOR_EACH_DISK_STORAGE_COLUMN(i, t, cd) - delete_disk_storage_file(t, i, ds_name); - free(ds_name); - } - FOR_EACH_COLUMN(i, t->desc, cd) { - struct osl_column *col = t->columns + i; - enum osl_storage_type st = cd->storage_type; - remove_rb_node(t, i, r); - if (st == OSL_MAPPED_STORAGE) { - mark_mapped_object_invalid(t, r->num, i); - continue; - } - if (st == OSL_NO_STORAGE && !(cd->storage_flags & OSL_DONT_FREE)) - free(r->volatile_objects[col->volatile_num].data); - } - if (t->num_mapped_columns) { - ret = mark_row_invalid(t, r->num); - if (ret < 0) - goto out; - t->num_invalid_rows++; - } else - t->num_rows--; - ret = 1; -out: - free(r->volatile_objects); - free(r); - return ret; -} - -/* test if column has an rbtree */ -static int check_rbtree_col(const struct osl_table *t, unsigned col_num, - struct osl_column **col) -{ - if (!t) - return -E_BAD_TABLE; - if (!(get_column_description(t->desc, col_num)->storage_flags & OSL_RBTREE)) - return -E_BAD_STORAGE_FLAGS; - *col = t->columns + col_num; - return 1; -} - -/** - * Get the row that contains the given object. - * - * \param t Pointer to an open osl table. - * \param col_num The number of the column to be searched. - * \param obj The object to be looked up. - * \param result Points to the row containing \a obj. - * - * Lookup \a obj in \a t and return the row containing \a obj. The column - * specified by \a col_num must have an associated rbtree. - * - * \return Positive on success, negative on errors. If an error occurred, \a - * result is set to \p NULL. Possible errors include: \p E_BAD_TABLE, \p - * E_BAD_STORAGE_FLAGS, errors returned by get_mapped_object(), \p - * E_RB_KEY_NOT_FOUND. - * - * \sa osl_storage_flags - */ -int osl_get_row(const struct osl_table *t, unsigned col_num, - const struct osl_object *obj, struct osl_row **result) -{ - int ret; - struct rb_node *node; - struct osl_row *row; - struct osl_column *col; - - *result = NULL; - ret = check_rbtree_col(t, col_num, &col); - if (ret < 0) - return ret; - ret = search_rbtree(obj, t, col_num, &node, NULL); - if (ret < 0) - return ret; - row = get_row_pointer(node, t->columns[col_num].rbtree_num); - *result = row; - return 1; -} - -static int rbtree_loop(struct osl_column *col, void *private_data, - osl_rbtree_loop_func *func) -{ - struct rb_node *n, *tmp; - - /* this for-loop is safe against removal of an entry */ - for (n = rb_first(&col->rbtree), tmp = n? rb_next(n) : NULL; - n; - n = tmp, tmp = tmp? rb_next(tmp) : NULL) { - struct osl_row *r = get_row_pointer(n, col->rbtree_num); - int ret = func(r, private_data); - if (ret < 0) - return ret; - } - return 1; -} - -static int rbtree_loop_reverse(struct osl_column *col, void *private_data, - osl_rbtree_loop_func *func) -{ - struct rb_node *n, *tmp; - - /* safe against removal of an entry */ - for (n = rb_last(&col->rbtree), tmp = n? rb_prev(n) : NULL; - n; - n = tmp, tmp = tmp? rb_prev(tmp) : NULL) { - struct osl_row *r = get_row_pointer(n, col->rbtree_num); - int ret = func(r, private_data); - if (ret < 0) - return ret; - } - return 1; -} - -/** - * Loop over all nodes in an rbtree. - * - * \param t Pointer to an open osl table. - * \param col_num The column to use for iterating over the elements. - * \param private_data Pointer that gets passed to \a func. - * \param func The function to be called for each node in the rbtree. - * - * This function does an in-order walk of the rbtree associated with \a - * col_num. It is an error if the \p OSL_RBTREE flag is not set for this - * column. For each node in the rbtree, the given function \a func is called - * with two pointers as arguments: The first osl_row* argument points to the - * row that contains the object corresponding to the rbtree node currently - * traversed, and the \a private_data pointer is passed verbatim to \a func as the - * second argument. The loop terminates either if \a func returns a negative - * value, or if all nodes of the tree have been visited. - * - * - * \return Positive on success, negative on errors. If the termination of the - * loop was caused by \a func returning a negative value, this value is - * returned. - * - * \sa osl_storage_flags, osl_rbtree_loop_reverse(), osl_compare_func. - */ -int osl_rbtree_loop(const struct osl_table *t, unsigned col_num, - void *private_data, osl_rbtree_loop_func *func) -{ - struct osl_column *col; - - int ret = check_rbtree_col(t, col_num, &col); - if (ret < 0) - return ret; - return rbtree_loop(col, private_data, func); -} - -/** - * Loop over all nodes in an rbtree in reverse order. - * - * \param t Identical meaning as in \p osl_rbtree_loop(). - * \param col_num Identical meaning as in \p osl_rbtree_loop(). - * \param private_data Identical meaning as in \p osl_rbtree_loop(). - * \param func Identical meaning as in \p osl_rbtree_loop(). - * - * This function is identical to \p osl_rbtree_loop(), the only difference - * is that the tree is walked in reverse order. - * - * \return The same return value as \p osl_rbtree_loop(). - * - * \sa osl_rbtree_loop(). - */ -int osl_rbtree_loop_reverse(const struct osl_table *t, unsigned col_num, - void *private_data, osl_rbtree_loop_func *func) -{ - struct osl_column *col; - - int ret = check_rbtree_col(t, col_num, &col); - if (ret < 0) - return ret; - return rbtree_loop_reverse(col, private_data, func); -} - -/* TODO: Rollback changes on errors */ -static int rename_disk_storage_objects(struct osl_table *t, - struct osl_object *old_obj, struct osl_object *new_obj) -{ - int i, ret; - const struct osl_column_description *cd; - char *old_ds_name, *new_ds_name; - - if (!t->num_disk_storage_columns) - return 1; /* nothing to do */ - if (old_obj->size == new_obj->size && !memcmp(new_obj->data, - old_obj->data, new_obj->size)) - return 1; /* object did not change */ - old_ds_name = disk_storage_name_of_object(t, old_obj); - new_ds_name = disk_storage_name_of_object(t, new_obj); - FOR_EACH_DISK_STORAGE_COLUMN(i, t, cd) { - char *old_filename, *new_filename; - ret = create_disk_storage_object_dir(t, i, new_ds_name); - if (ret < 0) - goto out; - old_filename = disk_storage_path(t, i, old_ds_name); - new_filename = disk_storage_path(t, i, new_ds_name); - ret = para_rename(old_filename, new_filename); - free(old_filename); - free(new_filename); - if (ret < 0) - goto out; - } - ret = 1; -out: - free(old_ds_name); - free(new_ds_name); - return ret; - -} - -/** - * Change an object in an osl table. - * - * \param t Pointer to an open osl table. - * \param r Pointer to the row containing the object to be updated. - * \param col_num Number of the column containing the object to be updated. - * \param obj Pointer to the replacement object. - * - * This function gets rid of all references to the old object. This includes - * removal of the rbtree node in case there is an rbtree associated with \a - * col_num. It then inserts \a obj into the table and the rbtree if necessary. - * - * If the \p OSL_RBTREE flag is set for \a col_num, you \b MUST call this - * function in order to change the contents of an object, even for volatile or - * mapped columns of constant size (which may be updated directly if \p - * OSL_RBTREE is not set). Otherwise the rbtree might become corrupted. - * - * \return Standard - */ -int osl_update_object(struct osl_table *t, const struct osl_row *r, - unsigned col_num, struct osl_object *obj) -{ - struct osl_column *col; - const struct osl_column_description *cd; - int ret; - - if (!t) - return -E_BAD_TABLE; - col = &t->columns[col_num]; - cd = get_column_description(t->desc, col_num); - PARA_DEBUG_LOG("updating column %u of %s\n", col_num, t->desc->name); - if (cd->storage_flags & OSL_RBTREE) { - if (search_rbtree(obj, t, col_num, NULL, NULL) > 0) - return -E_RB_KEY_EXISTS; - } - if (cd->storage_flags & OSL_FIXED_SIZE) { - if (obj->size != cd->data_size) - return -E_BAD_DATA_SIZE; - } - remove_rb_node(t, col_num, r); - if (cd->storage_type == OSL_NO_STORAGE) { /* TODO: If fixed size, reuse object? */ - free(r->volatile_objects[col->volatile_num].data); - r->volatile_objects[col->volatile_num] = *obj; - } else if (cd->storage_type == OSL_DISK_STORAGE) { - char *ds_name; - ret = disk_storage_name_of_row(t, r, &ds_name); - if (ret < 0) - return ret; - ret = delete_disk_storage_file(t, col_num, ds_name); - if (ret < 0 && !is_errno(-ret, ENOENT)) { - free(ds_name); - return ret; - } - ret = write_disk_storage_file(t, col_num, obj, ds_name); - free(ds_name); - if (ret < 0) - return ret; - } else { /* mapped storage */ - struct osl_object old_obj; - ret = get_mapped_object(t, col_num, r->num, &old_obj); - if (ret < 0) - return ret; - /* - * If the updated column is the disk storage name column, the - * disk storage name changes, so we have to rename all disk - * storage objects accordingly. - */ - if (col_num == t->disk_storage_name_column) { - ret = rename_disk_storage_objects(t, &old_obj, obj); - if (ret < 0) - return ret; - } - if (cd->storage_flags & OSL_FIXED_SIZE) - memcpy(old_obj.data, obj->data, cd->data_size); - else { /* TODO: if the size doesn't change, use old space */ - uint32_t new_data_map_size; - char *row_index; - ret = get_row_index(t, r->num, &row_index); - if (ret < 0) - return ret; - ret = mark_mapped_object_invalid(t, r->num, col_num); - if (ret < 0) - return ret; - unmap_column(t, col_num); - ret = append_map_file(t, col_num, obj, - &new_data_map_size); - if (ret < 0) - return ret; - ret = map_column(t, col_num); - if (ret < 0) - return ret; - update_cell_index(row_index, col, new_data_map_size, - obj->size); - } - } - if (cd->storage_flags & OSL_RBTREE) { - ret = insert_rbtree(t, col_num, r, obj); - if (ret < 0) - return ret; - } - return 1; -} - -/** - * Retrieve an object of type \p OSL_DISK_STORAGE by row and column. - * - * \param t Pointer to an open osl table. - * \param r Pointer to the row containing the object. - * \param col_num The column number. - * \param obj Points to the result upon successful return. - * - * For columns of type \p OSL_DISK_STORAGE, this function must be used to - * retrieve one of its containing objects. Afterwards, osl_close_disk_object() - * must be called in order to deallocate the resources. - * - * \return Positive on success, negative on errors. Possible errors include: - * \p E_BAD_TABLE, \p E_BAD_STORAGE_TYPE, errors returned by osl_get_object(). - * - * \sa osl_get_object(), osl_storage_type, osl_close_disk_object(). - */ -int osl_open_disk_object(const struct osl_table *t, const struct osl_row *r, - unsigned col_num, struct osl_object *obj) -{ - const struct osl_column_description *cd; - char *ds_name, *filename; - int ret; - - if (!t) - return -E_BAD_TABLE; - cd = get_column_description(t->desc, col_num); - if (cd->storage_type != OSL_DISK_STORAGE) - return -E_BAD_STORAGE_TYPE; - - ret = disk_storage_name_of_row(t, r, &ds_name); - if (ret < 0) - return ret; - filename = disk_storage_path(t, col_num, ds_name); - free(ds_name); - PARA_DEBUG_LOG("filename: %s\n", filename); - ret = mmap_full_file(filename, O_RDONLY, &obj->data, &obj->size, NULL); - free(filename); - return ret; -} - -/** - * Free resources that were allocated during osl_open_disk_object(). - * - * \param obj Pointer to the object previously returned by open_disk_object(). - * - * \return The return value of the underlying call to para_munmap(). - * - * \sa para_munmap(). - */ -int osl_close_disk_object(struct osl_object *obj) -{ - return para_munmap(obj->data, obj->size); -} - -/** - * Get the number of rows of the given table. - * - * \param t Pointer to an open osl table. - * \param num_rows Result is returned here. - * - * The number of rows returned via \a num_rows excluding any invalid rows. - * - * \return Positive on success, \p -E_BAD_TABLE if \a t is \p NULL. - */ -int osl_get_num_rows(const struct osl_table *t, unsigned *num_rows) -{ - if (!t) - return -E_BAD_TABLE; - assert(t->num_rows >= t->num_invalid_rows); - *num_rows = t->num_rows - t->num_invalid_rows; - return 1; -} - -/** - * Get the rank of a row. - * - * \param t An open osl table. - * \param r The row to get the rank of. - * \param col_num The number of an rbtree column. - * \param rank Result pointer. - * - * The rank is, by definition, the position of the row in the linear order - * determined by an in-order tree walk of the rbtree associated with column - * number \a col_num of \a table. - * - * \return Positive on success, negative on errors. - * - * \sa osl_get_nth_row(). - */ -int osl_get_rank(const struct osl_table *t, struct osl_row *r, - unsigned col_num, unsigned *rank) -{ - struct osl_object obj; - struct osl_column *col; - struct rb_node *node; - int ret = check_rbtree_col(t, col_num, &col); - - if (ret < 0) - return ret; - ret = osl_get_object(t, r, col_num, &obj); - if (ret < 0) - return ret; - ret = search_rbtree(&obj, t, col_num, &node, NULL); - if (ret < 0) - return ret; - ret = rb_rank(node, rank); - if (ret < 0) - return -E_BAD_ROW; - return 1; -} - -/** - * Get the row with n-th greatest value. - * - * \param t Pointer to an open osl table. - * \param col_num The column number. - * \param n The rank of the desired row. - * \param result Row is returned here. - * - * Retrieve the n-th order statistic with respect to the compare function - * of the rbtree column \a col_num. In other words, get that row with - * \a n th greatest value in column \a col_num. It's an error if - * \a col_num is not a rbtree column, or if \a n is larger than the - * number of rows in the table. - * - * \return Positive on success, negative on errors. Possible errors: - * \p E_BAD_TABLE, \p E_BAD_STORAGE_FLAGS, \p E_RB_KEY_NOT_FOUND. - * - * \sa osl_storage_flags, osl_compare_func, osl_get_row(), - * osl_rbtree_last_row(), osl_rbtree_first_row(), osl_get_rank(). - */ -int osl_get_nth_row(const struct osl_table *t, unsigned col_num, - unsigned n, struct osl_row **result) -{ - struct osl_column *col; - struct rb_node *node; - unsigned num_rows; - int ret; - - if (n == 0) - return -E_RB_KEY_NOT_FOUND; - ret = osl_get_num_rows(t, &num_rows); - if (ret < 0) - return ret; - if (n > num_rows) - return -E_RB_KEY_NOT_FOUND; - ret = check_rbtree_col(t, col_num, &col); - if (ret < 0) - return ret; - node = rb_nth(col->rbtree.rb_node, n); - if (!node) - return -E_RB_KEY_NOT_FOUND; - *result = get_row_pointer(node, col->rbtree_num); - return 1; -} - -/** - * Get the row corresponding to the smallest rbtree node of a column. - * - * \param t An open rbtree table. - * \param col_num The number of the rbtree column. - * \param result A pointer to the first row is returned here. - * - * The rbtree node of the smallest object (with respect to the corresponding - * compare function) is selected and the row containing this object is - * returned. It is an error if \a col_num refers to a column without an - * associated rbtree. - * - * \return Positive on success, negative on errors. - * - * \sa osl_get_nth_row(), osl_rbtree_last_row(). - */ -int osl_rbtree_first_row(const struct osl_table *t, unsigned col_num, - struct osl_row **result) -{ - return osl_get_nth_row(t, col_num, 1, result); -} - -/** - * Get the row corresponding to the greatest rbtree node of a column. - * - * \param t The same meaning as in \p osl_rbtree_first_row(). - * \param col_num The same meaning as in \p osl_rbtree_first_row(). - * \param result The same meaning as in \p osl_rbtree_first_row(). - * - * This function works just like osl_rbtree_first_row(), the only difference - * is that the row containing the greatest rather than the smallest object is - * returned. - * - * \return Positive on success, negative on errors. - * - * \sa osl_get_nth_row(), osl_rbtree_first_row(). - */ -int osl_rbtree_last_row(const struct osl_table *t, unsigned col_num, - struct osl_row **result) -{ - unsigned num_rows; - int ret = osl_get_num_rows(t, &num_rows); - - if (ret < 0) - return ret; - return osl_get_nth_row(t, col_num, num_rows, result); -} diff --git a/osl.h b/osl.h deleted file mode 100644 index 3c35fd4e..00000000 --- a/osl.h +++ /dev/null @@ -1,187 +0,0 @@ -#include -/* - * Copyright (C) 2007-2009 Andre Noll - * - * Licensed under the GPL v2. For licencing details see COPYING. - */ - -/** \file osl.h User interface for the object storage layer. */ - -/** describes an object of the object storage layer (osl) */ -struct osl_object { - /** Pointer to the data of the object. */ - void *data; - /** The object's size. */ - size_t size; -}; - -/** Flags that change the internal handling of osl tables. */ -enum osl_table_flags { - /** This table will have many rows. */ - OSL_LARGE_TABLE = 1 -}; - -/** The three different types of storage for an osl column */ -enum osl_storage_type { - /** - * All data for this column is stored in one file which gets mmapped by - * osl_open_table(). This is suitable for columns that do not hold much - * data. - */ - OSL_MAPPED_STORAGE, - /** - * Each entry is stored on disk and is loaded on demand by - * open_disk_object(). This is the preferable storage type for large - * objects that need not be in memory all the time. - */ - OSL_DISK_STORAGE, - /** - * Objects for columns of this type are volatile: They are only stored - * in memory and are discarded once the table gets closed. - */ - OSL_NO_STORAGE -}; - -/** - * Additional per-column flags - */ -enum osl_storage_flags { - /** - * Build an rbtree for this column. This is only possible if the - * storage type of the column is either \a OSL_MAPPED_STORAGE or \a - * OSL_NO_STORAGE. In order to lookup objects in the table by using \a - * osl_get_row(), the lookup column must have an associated rbtree. - * - * \sa osl_storage_type, osl_get_row() - */ - OSL_RBTREE = 1, - /** The data for this column will have constant size. */ - OSL_FIXED_SIZE = 2, - /** All values of this column will be different. */ - OSL_UNIQUE = 4, - /** Do not free the data for this column (\p OSL_NO_STORAGE). */ - OSL_DONT_FREE = 8 -}; - -struct osl_table; -struct osl_row; - -/** - * In order to build up an rbtree a compare function for the objects must be - * specified. Such a function always takes pointers to the two objects to be - * compared. It must return -1, zero, or 1, if the first argument is considered - * to be respectively less than, equal to, or greater than the second. If two - * members compare as equal, their order in the sorted array is undefined. - */ -typedef int osl_compare_func(const struct osl_object *obj1, - const struct osl_object *obj2); -typedef int osl_rbtree_loop_func(struct osl_row *row, void *data); - -osl_compare_func osl_hash_compare, uint32_compare; - -/** - * Describes one column of a osl table. - */ -struct osl_column_description { - /** One of zje tree possible types of storage */ - enum osl_storage_type storage_type; - /** Specifies further properties of the column */ - enum osl_storage_flags storage_flags; - /** - * The column name determines the name of the directory where all data - * for this column will be stored. Its hash is stored in the table - * header. This field is ignored if the storage type is \a NO_STORAGE - */ - char *name; - /** - * For columns with an associated rbtree, this must point to a function - * that compares the values of two objects, either a built-in function - * or a function defined by the application may be supplied. This - * field is ignored if the column does not have an associated rbtree. - * - * \sa osl_storage_flags, osl_compare_func - */ - osl_compare_func *compare_function; - /** - * If the \a OSL_FIXED_SIZE flag is set for this column, this value - * determines the fixed size of all objects of this column. It is - * ignored, if \a OSL_FIXED_SIZE is not set. - */ - uint32_t data_size; -}; - -/** - * Describes one osl table. - */ -struct osl_table_description { - /** The directory which contains all files of this table. */ - const char *dir; - /** - * The table name. A subdirectory of \a dir called \a name is created - * at table creation time. It must be a valid name for a subdirectory. - * In particular, no slashes are allowed for \a name. - */ - const char *name; - /** The number of columns of this table. */ - uint16_t num_columns; - /** Further table-wide information. */ - enum osl_table_flags flags; - /** The array describing the individual columns of the table. */ - struct osl_column_description *column_descriptions; -}; - -/** Flags to be passed to \a osl_close_table(). */ -enum osl_close_flags { - /** - * The table header contains a "dirty" flag which specifies whether - * the table is currently open by another process. This flag specifies - * that the dirty flag should be cleared before closing the table. - */ - OSL_MARK_CLEAN = 1, - /** - * If the table contains columns of type \a OSL_NO_STORAGE and this - * flag is passed to osl_close_table(), free(3) is called for each - * object of each column of type \a OSL_NO_STORAGE. - */ - OSL_FREE_VOLATILE = 2 -}; - - - -int osl_create_table(const struct osl_table_description *desc); -int osl_open_table(const struct osl_table_description *desc, - struct osl_table **result); -int osl_close_table(struct osl_table *t, enum osl_close_flags flags); -int osl_get_row(const struct osl_table *t, unsigned col_num, - const struct osl_object *obj, struct osl_row **result); -int osl_get_object(const struct osl_table *t, const struct osl_row *row, - unsigned col_num, struct osl_object *object); -int osl_open_disk_object(const struct osl_table *t, - const struct osl_row *r, unsigned col_num, struct osl_object *obj); -int osl_close_disk_object(struct osl_object *obj); -int osl_add_and_get_row(struct osl_table *t, struct osl_object *objects, - struct osl_row **row); -int osl_add_row(struct osl_table *t, struct osl_object *objects); -int osl_del_row(struct osl_table *t, struct osl_row *row); -int osl_rbtree_loop(const struct osl_table *t, unsigned col_num, - void *private_data, osl_rbtree_loop_func *func); -int osl_rbtree_loop_reverse(const struct osl_table *t, unsigned col_num, - void *private_data, osl_rbtree_loop_func *func); -int osl_update_object(struct osl_table *t, const struct osl_row *r, - unsigned col_num, struct osl_object *obj); -int osl_get_num_rows(const struct osl_table *t, unsigned *num_rows); -int osl_rbtree_first_row(const struct osl_table *t, unsigned col_num, - struct osl_row **result); -int osl_rbtree_last_row(const struct osl_table *t, unsigned col_num, - struct osl_row **result); -int osl_get_nth_row(const struct osl_table *t, unsigned col_num, - unsigned n, struct osl_row **result); -int osl_get_rank(const struct osl_table *t, struct osl_row *r, - unsigned col_num, unsigned *rank); - -int for_each_file_in_dir(const char *dirname, - int (*func)(const char *, void *), void *private_data); -ssize_t para_write_all(int fd, const void *buf, size_t size); -int para_lseek(int fd, off_t *offset, int whence); -int para_write_file(const char *filename, const void *buf, size_t size); - diff --git a/osl_core.h b/osl_core.h deleted file mode 100644 index de53cdd1..00000000 --- a/osl_core.h +++ /dev/null @@ -1,591 +0,0 @@ -/* - * Copyright (C) 2007-2009 Andre Noll - * - * Licensed under the GPL v2. For licencing details see COPYING. - */ - -/** \file osl_core.h Object storage layer details, not visible to users. */ - -#include "rbtree.h" -#include "osl.h" -#include "string.h" -#include "portable_io.h" -#include "hash.h" - -/** Internal representation of a column of an osl table. */ -struct osl_column { - /** The memory mapping of this comumn (only used for mapped columns). */ - struct osl_object data_map; - /** The root of the rbtree (only used for columns with rbtrees). */ - struct rb_root rbtree; - /** The index in the array of rb nodes (only used for columns with rbtrees). */ - unsigned rbtree_num; - /** Index for volatile_objects of struct osl_row. */ - unsigned volatile_num; - /** - * Starting point of the data for this column within the index - * (only used for mapped columns). - */ - uint16_t index_offset; - /** - * The hash value of the name of this column. - * - * This is only used for mapped and disk storage columns). - */ - HASH_TYPE name_hash[HASH_SIZE]; -}; - -/** Internal representation of an osl table */ -struct osl_table { - /** Pointer to the table description */ - const struct osl_table_description *desc; - /** The size of the index header of this table. */ - uint16_t index_header_size; - /** Contains the mapping of the table's index file */ - struct osl_object index_map; - /** Total number of rows, including invalid rows. */ - unsigned num_rows; - /** Keeps track of the number of invalid rows in the table. */ - uint32_t num_invalid_rows; - /** Number of columns of type \p OSL_MAPPED_STORAGE. */ - unsigned num_mapped_columns; - /** Number of columns of type \p OSL_NO_STORAGE. */ - unsigned num_volatile_columns; - /** Number of columns of type \p OSL_DISK_STORAGE. */ - unsigned num_disk_storage_columns; - /** Number of columns with associated rbtree. */ - unsigned num_rbtrees; - /** - * The number of the column that determines the name of the disk - * storage objects. - */ - unsigned disk_storage_name_column; - /** The number of bytes of an index entry of a row. */ - unsigned row_index_size; - /** Pointer to the internal representation of the columns. */ - struct osl_column *columns; -}; - -/** Internal representation of a row of an osl table */ -struct osl_row { - /** - * The row number. - * - * It is only used if there is at least one mapped column. - */ - off_t num; - /** Array of size \a num_volatile_columns. */ - struct osl_object *volatile_objects; -}; - -int read_table_desc(struct osl_object *map, struct osl_table_description *desc); -int init_table_structure(const struct osl_table_description *desc, - struct osl_table **table_ptr); -int row_is_invalid(struct osl_table *t, uint32_t row_num); -int get_mapped_object(const struct osl_table *t, unsigned col_num, - uint32_t row_num, struct osl_object *obj); -int para_truncate(const char *filename, off_t size); -int unmap_table(struct osl_table *t, enum osl_close_flags flags); -int init_rbtrees(struct osl_table *t); - -/** - * Flags to be specified for map_table(). - * - * \sa map_table(). - */ -enum map_table_flags { - /** - * Check whether the entries in the table index match the entries in - * the table description. - */ - MAP_TBL_FL_VERIFY_INDEX = 1, - /** Do not complain even if the dirty flag is set. */ - MAP_TBL_FL_IGNORE_DIRTY = 2, - /** Use read-only mappings. */ - MAP_TBL_FL_MAP_RDONLY = 4 -}; - -int map_table(struct osl_table *t, enum map_table_flags flags); -void clear_rbtrees(struct osl_table *t); -int mark_row_invalid(struct osl_table *t, uint32_t row_num); - - -/** - * Get the description of a column by column number - * - * \param d Pointer to the table description. - * \param col_num The number of the column to get the description for. - * - * \return The table description. - * - * \sa struct osl_column_description. - */ -_static_inline_ struct osl_column_description *get_column_description( - const struct osl_table_description *d, unsigned col_num) -{ - return &d->column_descriptions[col_num]; -} - -/** - * Format of the header of the index file of an osl table. - * - * Bytes 16-31 are currently unused. - * - * \sa enum index_column_desc_offsets, HASH_SIZE, osl_table_flags. - */ -enum index_header_offsets { - /** Bytes 0-8: PARASLASH. */ - IDX_PARA_MAGIC = 0, - /** Byte 9: Dirty flag (nonzero if table is mapped). */ - IDX_DIRTY_FLAG = 9, - /** Byte 10: osl table version number. */ - IDX_VERSION = 10, - /** Byte 11: Table flags.*/ - IDX_TABLE_FLAGS = 11, - /** Byte 12,13: Number of columns. */ - IDX_NUM_COLUMNS, - /** Byte 14,15 Size of the index header. */ - IDX_HEADER_SIZE = 14, - /** Column descriptions start here. */ - IDX_COLUMN_DESCRIPTIONS = 32 -}; - -/** - * Format of the column description in the index header. - * - * \sa index_header_offsets. - */ -enum index_column_desc_offsets { - /** Bytes 0,1: Storage_type. */ - IDX_CD_STORAGE_TYPE = 0, - /** Bytes 2,3: Storage_flags. */ - IDX_CD_STORAGE_FLAGS = 2, - /** Bytes 4 - 7: Data_size (only used for fixed size columns). */ - IDX_CD_DATA_SIZE = 4, - /** Bytes 8 - ... Name of the column. */ - IDX_CD_NAME = 8, -}; - -/** Magic string contained in the header of the index file of each osl table. */ -#define PARA_MAGIC "PARASLASH" - -/** - * The minimal number of bytes for a column in the index header. - * - * The column name starts at byte IDX_CD_NAME and must at least contain one - * character, plus the terminating NULL byte. - */ -#define MIN_IDX_COLUMN_DESCRIPTION_SIZE (IDX_CD_NAME + 2) - -/** - * Get the number of bytes used for a column in the index header. - * - * \param name The name of the column. - * - * The amount of space used for a column in the index header of a table depends - * on the (length of the) name of the column. - * - * \return The total number of bytes needed to store one column of this name. - */ -_static_inline_ size_t index_column_description_size(const char *name) -{ - return MIN_IDX_COLUMN_DESCRIPTION_SIZE + strlen(name) - 1; -} - -#define CURRENT_TABLE_VERSION 1 -#define MIN_TABLE_VERSION 1 -#define MAX_TABLE_VERSION 1 -/** An index header must be at least that many bytes long. */ -#define MIN_INDEX_HEADER_SIZE(num_cols) (MIN_IDX_COLUMN_DESCRIPTION_SIZE \ - * num_cols + IDX_COLUMN_DESCRIPTIONS) - -/** - * Get the number of rows from the size of the memory mapping. - * - * \param t Pointer to an open table. - * - * \return The number of rows, including invalid rows. - */ -_static_inline_ unsigned table_num_rows(const struct osl_table *t) -{ - return (t->index_map.size - t->index_header_size) - / t->row_index_size; -} - -/** - * Get the path of the index file from a table description. - * - * \param d The table description. - * - * \return The full path of the index file. Result must be freed by - * the caller. - */ -_static_inline_ char *index_filename(const struct osl_table_description *d) -{ - return make_message("%s/%s/index", d->dir, d->name); -} - -/** - * Get the path of storage of a column. - * - * \param t Pointer to an initialized table. - * \param col_num The number of the column to get the path for. - * - * \return The full path of the mapped data file (mapped storage) or the - * data directory (disk storage). Result must be freed by the caller. - * - * \sa osl_storage_type. - */ -_static_inline_ char *column_filename(const struct osl_table *t, unsigned col_num) -{ - char asc[2 * HASH_SIZE + 1]; - hash_to_asc(t->columns[col_num].name_hash, asc); - return make_message("%s/%s/%s", t->desc->dir, t->desc->name, asc); -} - -/** - * Get the start of an index entry - * - * \param t Pointer to a table which has been mapped. - * \param row_num The number of the row whose index entry should be retrieved. - * \param row_index Result pointer. - * - * \return Positive on success, \p -E_INDEX_CORRUPTION otherwise. - * - * \sa get_cell_index(). - */ -_static_inline_ int get_row_index(const struct osl_table *t, uint32_t row_num, - char **row_index) -{ - uint32_t index_offset; - index_offset = t->index_header_size + t->row_index_size * row_num; - if (index_offset + 8 > t->index_map.size) { - *row_index = NULL; - return -E_INDEX_CORRUPTION; - } - *row_index = (char *)(t->index_map.data) + index_offset; - return 1; -} - -/** - * Get the index entry of a row/column pair. - * - * \param t Pointer to a table which has been mapped. - * \param row_num The number of the row whose index entry should be retrieved. - * \param col_num The number of the column whose index entry should be retrieved. - * \param cell_index Result pointer. - * - * \return Positive on success, \p -E_INDEX_CORRUPTION otherwise. - * - * \sa get_row_index(). - */ -_static_inline_ int get_cell_index(const struct osl_table *t, uint32_t row_num, - uint32_t col_num, char **cell_index) -{ - int ret = get_row_index(t, row_num, cell_index); - if (ret < 0) - return ret; - *cell_index += t->columns[col_num].index_offset; - return ret; -} - -/** - * Change an index entry of a column after object was added. - * - * \param row_index Pointer to the index of the row to update. - * \param col Pointer to the column. - * \param map_size The new size of the data file. - * \param object_size The size of the object just appended to the data file. - * - * This is called right after an object was appended to the data file for a - * mapped column. - * - * \sa get_row_index(). - */ -_static_inline_ void update_cell_index(char *row_index, struct osl_column *col, - uint32_t map_size, uint32_t object_size) -{ - write_u32(row_index + col->index_offset, map_size - object_size - 1); - write_u32(row_index + col->index_offset + 4, object_size + 1); -} - -/** - * Get the full path of a disk storage object - * - * \param t Pointer to an initialized table. - * \param col_num The number of the column the disk storage object belongs to. - * \param ds_name The disk storage name of the object. - * - * \return Pointer to the full path which must be freed by the caller. - * - * \sa column_filename(), disk_storage_name_of_row(). - */ -_static_inline_ char *disk_storage_path(const struct osl_table *t, - unsigned col_num, const char *ds_name) -{ - char *dirname = column_filename(t, col_num); - char *filename = make_message("%s/%s", dirname, ds_name); - free(dirname); - return filename; -} - -/** - * Get the column description of the next column of a given type. - * - * \param type the desired storage type. - * \param col_num the column to start the search. - * \param t the osl table. - * \param cd result is returned here. - * - * \return On success, \a cd contains the column description of the next column - * of type \a type, and the number of that column is returned. Otherwise, \a - * cd is set to \p NULL and the function returns t->num_columns + 1. - * - * \sa FOR_EACH_COLUMN_OF_TYPE, FOR_EACH_MAPPED_COLUMN, FOR_EACH_RBTREE_COLUMN, - * FOR_EACH_DISK_STORAGE_COLUMN, FOR_EACH_VOLATILE_COLUMN, osl_storage_type. - */ -_static_inline_ int next_column_of_type(enum osl_storage_type type, int col_num, - const struct osl_table *t, - const struct osl_column_description **cd) -{ - *cd = NULL; - while (col_num < t->desc->num_columns) { - *cd = get_column_description(t->desc, col_num); - if ((*cd)->storage_type == type) - break; - col_num++; - } - return col_num; -} - -/** - * Find the next column with an associated rbtree. - * - * \param col_num The column to start the search. - * \param t The osl table. - * \param cd Result is returned here. - - * \return On success, \a cd contains the column description of the next column - * with associated rbtree, and the number of that column is returned. - * Otherwise, \a cd is set to \p NULL and the function returns t->num_columns + - * 1. - * - * \sa FOR_EACH_COLUMN_OF_TYPE, FOR_EACH_MAPPED_COLUMN, FOR_EACH_RBTREE_COLUMN, - * FOR_EACH_DISK_STORAGE_COLUMN, FOR_EACH_VOLATILE_COLUMN, osl_storage_flags. - */ -_static_inline_ int next_rbtree_column(int col_num, const struct osl_table *t, - const struct osl_column_description **cd) -{ - *cd = NULL; - while (col_num < t->desc->num_columns) { - *cd = get_column_description(t->desc, col_num); - if ((*cd)->storage_flags & OSL_RBTREE) - break; - col_num++; - } - return col_num; -} - - -/* quite some dirty hacks */ - -/** Round up size of struct osl_row to multiple of 8 */ -#define RB_NODES_OFFSET ((sizeof(struct osl_row) + 7) / 8 * 8) - -/** - * Allocate a new osl row. - * - * \param num_rbtrees The number of rbtrees for this row. - * - * \return A pointer to a zeroed-out area suitable for holding an osl row - * with \a num_rbtrees rbtree columns. - */ -_static_inline_ struct osl_row *allocate_row(unsigned num_rbtrees) -{ - size_t s = RB_NODES_OFFSET + num_rbtrees * sizeof(struct rb_node); - return para_calloc(s); -} - -/** - * Compute the pointer to a rbtree node embedded in a osl row. - * - * \param row The row to extract the rbtree pointer from. - * \param rbtree_num The number of the rbtree node to extract. - * - * \return A pointer to the \a rbtree_num th node contained in \a row. - */ -_static_inline_ struct rb_node *get_rb_node_pointer(const struct osl_row *row, uint32_t rbtree_num) -{ - return ((struct rb_node *)(((char *)row) + RB_NODES_OFFSET)) + rbtree_num; -} - -/** - * Get a pointer to the struct containing the given rbtree node. - * - * \param node Pointer to the rbtree node. - * \param rbtree_num Number of \a node in the array of rbtree nodes. - * - * \return A pointer to the row containing \a node. - */ -_static_inline_ struct osl_row *get_row_pointer(const struct rb_node *node, - unsigned rbtree_num) -{ - node -= rbtree_num; - return (struct osl_row *)(((char *)node) - RB_NODES_OFFSET); -} - -/** - * Compute a cryptographic hash of an osl object. - * - * \param obj the Object to compute the hash value from. - * \param hash Result is returned here. - */ -_static_inline_ void hash_object(const struct osl_object *obj, HASH_TYPE *hash) -{ - hash_function(obj->data, obj->size, hash); -} - -/** - * Get the relative path of an object, given the hash value. - * - * \param t Pointer to an initialized osl table. - * \param hash An arbitrary hash value. - * - * This function is typically called with \a hash being the hash of the object - * stored in the disk storage name column of a row. If the OSL_LARGE_TABLE - * flag is set, the first two hex digits get separated with a slash from the - * remaining digits. - * - * \return The relative path for all disk storage objects corresponding to \a - * hash. - * - * \sa struct osl_table:disk_storage_name_column. - */ -_static_inline_ char *disk_storage_name_of_hash(const struct osl_table *t, HASH_TYPE *hash) -{ - char asc[2 * HASH_SIZE + 2]; - - hash_to_asc(hash, asc); - if (t->desc->flags & OSL_LARGE_TABLE) - return make_message("%.2s/%s", asc, asc + 2); - return para_strdup(asc); -} - -/** - * A wrapper for rename(2). - * - * \param old_path The source path. - * \param new_path The destination path. - * - * \return Standard. - * - * \sa rename(2). - */ -_static_inline_ int para_rename(const char *old_path, const char *new_path) -{ - if (rename(old_path, new_path) < 0) - return -ERRNO_TO_PARA_ERROR(errno); - return 1; -} - -/** - * Iterate over each column of an initialized table. - * - * \param col A pointer to a struct osl_column. - * \param desc Pointer to the table description. - * \param cd Pointer to struct osl_column_description. - * - * On each iteration, \a col will point to the next column of the table and \a - * cd will point to the column description of this column. - * - * \sa struct osl_column FOR_EACH_RBTREE_COLUMN, FOR_EACH_COLUMN_OF_TYPE, - * FOR_EACH_MAPPED_COLUMN, FOR_EACH_DISK_STORAGE_COLUMN, - * FOR_EACH_VOLATILE_COLUMN. - */ -#define FOR_EACH_COLUMN(col, desc, cd) \ - for (col = 0; col < (desc)->num_columns && \ - (cd = get_column_description(desc, col)); col++) - -/** - * Iterate over each column with associated rbtree. - * - * \param col Same meaning as in FOR_EACH_COLUMN(). - * \param table Same meaning as in FOR_EACH_COLUMN(). - * \param cd Same meaning as in FOR_EACH_COLUMN(). - * - * \sa osl_storage_flags::OSL_RBTREE, FOR_EACH_COLUMN, FOR_EACH_COLUMN_OF_TYPE, - * FOR_EACH_MAPPED_COLUMN, FOR_EACH_DISK_STORAGE_COLUMN, - * FOR_EACH_VOLATILE_COLUMN. - */ -#define FOR_EACH_RBTREE_COLUMN(col, table, cd) \ - for (col = next_rbtree_column(0, table, &cd); \ - col < table->desc->num_columns; \ - col = next_rbtree_column(++col, table, &cd)) - -/** - * Iterate over each column of given storage type. - * - * \param type The osl_storage_type to iterate over. - * \param col Same meaning as in FOR_EACH_COLUMN(). - * \param table Same meaning as in FOR_EACH_COLUMN(). - * \param cd Same meaning as in FOR_EACH_COLUMN(). - * - * \sa osl_storage_type, FOR_EACH_COLUMN, FOR_EACH_RBTREE_COLUMN, - * FOR_EACH_MAPPED_COLUMN, FOR_EACH_DISK_STORAGE_COLUMN, - * FOR_EACH_VOLATILE_COLUMN. - */ -#define FOR_EACH_COLUMN_OF_TYPE(type, col, table, cd) \ - for (col = next_column_of_type(type, 0, table, &cd); \ - col < table->desc->num_columns; \ - col = next_column_of_type(type, ++col, table, &cd)) - -/** - * Iterate over each mapped column. - * - * \param col Same meaning as in FOR_EACH_COLUMN(). - * \param table Same meaning as in FOR_EACH_COLUMN(). - * \param cd Same meaning as in FOR_EACH_COLUMN(). - * - * Just like FOR_EACH_COLUMN(), but skip columns which are - * not of type \p OSL_MAPPED_STORAGE. - * - * \sa osl_storage_flags::OSL_MAPPED_STORAGE, FOR_EACH_COLUMN, - * FOR_EACH_RBTREE_COLUMN, FOR_EACH_COLUMN_OF_TYPE, - * FOR_EACH_DISK_STORAGE_COLUMN, FOR_EACH_VOLATILE_COLUMN. - */ -#define FOR_EACH_MAPPED_COLUMN(col, table, cd) \ - FOR_EACH_COLUMN_OF_TYPE(OSL_MAPPED_STORAGE, col, table, cd) - -/** - * Iterate over each disk storage column. - * - * \param col Same meaning as in FOR_EACH_COLUMN(). - * \param table Same meaning as in FOR_EACH_COLUMN(). - * \param cd Same meaning as in FOR_EACH_COLUMN(). - * - * Just like FOR_EACH_COLUMN(), but skip columns which are - * not of type \p OSL_DISK_STORAGE. - * - * \sa osl_storage_flags::OSL_DISK_STORAGE, FOR_EACH_COLUMN, - * FOR_EACH_RBTREE_COLUMN, FOR_EACH_COLUMN_OF_TYPE, FOR_EACH_MAPPED_COLUMN, - * FOR_EACH_VOLATILE_COLUMN. - */ -#define FOR_EACH_DISK_STORAGE_COLUMN(col, table, cd) \ - FOR_EACH_COLUMN_OF_TYPE(OSL_DISK_STORAGE, col, table, cd) - -/** - * Iterate over each volatile column. - * - * \param col Same meaning as in FOR_EACH_COLUMN(). - * \param table Same meaning as in FOR_EACH_COLUMN(). - * \param cd Same meaning as in FOR_EACH_COLUMN(). - * - * Just like FOR_EACH_COLUMN(), but skip columns which are - * not of type \p OSL_NO_STORAGE. - * - * \sa osl_storage_flags::OSL_NO_STORAGE, FOR_EACH_COLUMN, - * FOR_EACH_RBTREE_COLUMN, FOR_EACH_COLUMN_OF_TYPE, FOR_EACH_MAPPED_COLUMN, - * FOR_EACH_DISK_STORAGE_COLUMN. - */ -#define FOR_EACH_VOLATILE_COLUMN(col, table, cd) \ - FOR_EACH_COLUMN_OF_TYPE(OSL_NO_STORAGE, col, table, cd) diff --git a/oss_write.c b/oss_write.c index 552d5b97..103b14ea 100644 --- a/oss_write.c +++ b/oss_write.c @@ -6,6 +6,7 @@ /** \file oss_write.c Paraslash's oss output plugin. */ +#include #include #include #include diff --git a/para.h b/para.h index e4e9a53d..66cf9e48 100644 --- a/para.h +++ b/para.h @@ -145,16 +145,17 @@ printf("%s", VERSION_TEXT(_prefix)); \ exit(EXIT_SUCCESS); \ } + +/* Sent by para_client to initiate the authentication procedure. */ +#define AUTH_REQUEST_MSG "auth rsa " /** sent by para_server for commands that expect a data file */ #define AWAITING_DATA_MSG "\nAwaiting Data." /** sent by para_server if authentication was successful */ -#define PROCEED_MSG "\nProceed.\n" +#define PROCEED_MSG "Proceed." /** length of the \p PROCEED_MSG string */ #define PROCEED_MSG_LEN strlen(PROCEED_MSG) /** sent by para_client to indicate the end of the command line */ #define EOC_MSG "\nEnd of Command." -/** sent by para_client, followed by the decrypted challenge number */ -#define CHALLENGE_RESPONSE_MSG "challenge_response:" /* exec */ int para_exec_cmdline_pid(pid_t *pid, const char *cmdline, int *fds); @@ -181,9 +182,11 @@ extern const char *status_item_list[]; /** Loop over each status item. */ #define FOR_EACH_STATUS_ITEM(i) for (i = 0; i < NUM_STAT_ITEMS; i++) int stat_item_valid(const char *item); -int stat_line_valid(const char *); -void stat_client_write(const char *msg, int itemnum); -int stat_client_add(int fd, uint64_t mask); +void stat_client_write_item(int item_num); +int stat_client_add(int fd, uint64_t mask, int parser_friendly); +int for_each_stat_item(char *item_buf, size_t num_bytes, + int (*item_handler)(int, char *)); +void clear_and_dump_items(void); __printf_2_3 void para_log(int, const char*, ...); @@ -224,7 +227,7 @@ __printf_2_3 void para_log(int, const char*, ...); * * \return An integer between zero and \p max - 1, inclusively. */ -static inline long int para_random(unsigned max) +_static_inline_ long int para_random(unsigned max) { return ((max + 0.0) * (random() / (RAND_MAX + 1.0))); } diff --git a/playlist.c b/playlist.c index a9925cd3..852ffd7a 100644 --- a/playlist.c +++ b/playlist.c @@ -4,6 +4,9 @@ * Licensed under the GPL v2. For licencing details see COPYING. */ +#include +#include + #include "para.h" #include "error.h" #include "string.h" @@ -174,7 +177,7 @@ int playlist_open(char *name) obj.data = name; obj.size = strlen(obj.data); - ret = osl_get_row(playlists_table, BLOBCOL_NAME, &obj, &row); + ret = osl(osl_get_row(playlists_table, BLOBCOL_NAME, &obj, &row)); if (ret < 0) { PARA_NOTICE_LOG("failed to load playlist %s\n", name); return ret; diff --git a/prebuffer_filter.c b/prebuffer_filter.c index 1ad5f446..f23e4e57 100644 --- a/prebuffer_filter.c +++ b/prebuffer_filter.c @@ -6,6 +6,8 @@ /** \file prebuffer_filter.c Paraslash's prebuffering filter. */ +#include + #include "para.h" #include "prebuffer_filter.cmdline.h" #include "list.h" diff --git a/rbtree.c b/rbtree.c deleted file mode 100644 index 7f200d1d..00000000 --- a/rbtree.c +++ /dev/null @@ -1,457 +0,0 @@ -/* - Red Black Trees - (C) 1999 Andrea Arcangeli - (C) 2002 David Woodhouse - (C) 2007 Andre Noll - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - linux/lib/rbtree.c -*/ - -/** \file rbtree.c Red-black tree implementation. */ - -#include "stddef.h" -#include "rbtree.h" - -static void __rb_rotate_left(struct rb_node *node, struct rb_root *root) -{ - struct rb_node *right = node->rb_right; - struct rb_node *parent = rb_parent(node); - - if ((node->rb_right = right->rb_left)) - rb_set_parent(right->rb_left, node); - right->rb_left = node; - - rb_set_parent(right, parent); - - if (parent) - { - if (node == parent->rb_left) - parent->rb_left = right; - else - parent->rb_right = right; - } - else - root->rb_node = right; - rb_set_parent(node, right); - right->size = node->size; - node->size = 1; - if (node->rb_right) - node->size += node->rb_right->size; - if (node->rb_left) - node->size += node->rb_left->size; -} - -static void __rb_rotate_right(struct rb_node *node, struct rb_root *root) -{ - struct rb_node *left = node->rb_left; - struct rb_node *parent = rb_parent(node); - - if ((node->rb_left = left->rb_right)) - rb_set_parent(left->rb_right, node); - left->rb_right = node; - - rb_set_parent(left, parent); - - if (parent) - { - if (node == parent->rb_right) - parent->rb_right = left; - else - parent->rb_left = left; - } - else - root->rb_node = left; - rb_set_parent(node, left); - left->size = node->size; - node->size = 1; - if (node->rb_right) - node->size += node->rb_right->size; - if (node->rb_left) - node->size += node->rb_left->size; -} - -void rb_insert_color(struct rb_node *node, struct rb_root *root) -{ - struct rb_node *parent, *gparent; - - while ((parent = rb_parent(node)) && rb_is_red(parent)) - { - gparent = rb_parent(parent); - - if (parent == gparent->rb_left) - { - { - register struct rb_node *uncle = gparent->rb_right; - if (uncle && rb_is_red(uncle)) - { - rb_set_black(uncle); - rb_set_black(parent); - rb_set_red(gparent); - node = gparent; - continue; - } - } - - if (parent->rb_right == node) - { - register struct rb_node *tmp; - __rb_rotate_left(parent, root); - tmp = parent; - parent = node; - node = tmp; - } - - rb_set_black(parent); - rb_set_red(gparent); - __rb_rotate_right(gparent, root); - } else { - { - register struct rb_node *uncle = gparent->rb_left; - if (uncle && rb_is_red(uncle)) - { - rb_set_black(uncle); - rb_set_black(parent); - rb_set_red(gparent); - node = gparent; - continue; - } - } - - if (parent->rb_left == node) - { - register struct rb_node *tmp; - __rb_rotate_right(parent, root); - tmp = parent; - parent = node; - node = tmp; - } - - rb_set_black(parent); - rb_set_red(gparent); - __rb_rotate_left(gparent, root); - } - } - - rb_set_black(root->rb_node); -} - -static void __rb_erase_color(struct rb_node *node, struct rb_node *parent, - struct rb_root *root) -{ - struct rb_node *other; - - while ((!node || rb_is_black(node)) && node != root->rb_node) - { - if (parent->rb_left == node) - { - other = parent->rb_right; - if (rb_is_red(other)) - { - rb_set_black(other); - rb_set_red(parent); - __rb_rotate_left(parent, root); - other = parent->rb_right; - } - if ((!other->rb_left || rb_is_black(other->rb_left)) && - (!other->rb_right || rb_is_black(other->rb_right))) - { - rb_set_red(other); - node = parent; - parent = rb_parent(node); - } - else - { - if (!other->rb_right || rb_is_black(other->rb_right)) - { - struct rb_node *o_left; - if ((o_left = other->rb_left)) - rb_set_black(o_left); - rb_set_red(other); - __rb_rotate_right(other, root); - other = parent->rb_right; - } - rb_set_color(other, rb_color(parent)); - rb_set_black(parent); - if (other->rb_right) - rb_set_black(other->rb_right); - __rb_rotate_left(parent, root); - node = root->rb_node; - break; - } - } - else - { - other = parent->rb_left; - if (rb_is_red(other)) - { - rb_set_black(other); - rb_set_red(parent); - __rb_rotate_right(parent, root); - other = parent->rb_left; - } - if ((!other->rb_left || rb_is_black(other->rb_left)) && - (!other->rb_right || rb_is_black(other->rb_right))) - { - rb_set_red(other); - node = parent; - parent = rb_parent(node); - } - else - { - if (!other->rb_left || rb_is_black(other->rb_left)) - { - register struct rb_node *o_right; - if ((o_right = other->rb_right)) - rb_set_black(o_right); - rb_set_red(other); - __rb_rotate_left(other, root); - other = parent->rb_left; - } - rb_set_color(other, rb_color(parent)); - rb_set_black(parent); - if (other->rb_left) - rb_set_black(other->rb_left); - __rb_rotate_right(parent, root); - node = root->rb_node; - break; - } - } - } - if (node) - rb_set_black(node); -} - -void rb_erase(struct rb_node *node, struct rb_root *root) -{ - struct rb_node *child, *parent; - int color; - - if (!node->rb_left) - child = node->rb_right; - else if (!node->rb_right) - child = node->rb_left; - else - { - struct rb_node *old = node, *left; - - node = node->rb_right; - while ((left = node->rb_left) != NULL) - node = left; - child = node->rb_right; - parent = rb_parent(node); - color = rb_color(node); - - if (child) - rb_set_parent(child, parent); - if (parent == old) { - parent->rb_right = child; - parent = node; - } else - parent->rb_left = child; - - node->rb_parent_color = old->rb_parent_color; - node->rb_right = old->rb_right; - node->rb_left = old->rb_left; - node->size = old->size; - - if (rb_parent(old)) - { - if (rb_parent(old)->rb_left == old) - rb_parent(old)->rb_left = node; - else - rb_parent(old)->rb_right = node; - } else - root->rb_node = node; - - rb_set_parent(old->rb_left, node); - if (old->rb_right) - rb_set_parent(old->rb_right, node); - goto color; - } - - parent = rb_parent(node); - color = rb_color(node); - - if (child) - rb_set_parent(child, parent); - if (parent) - { - if (parent->rb_left == node) - parent->rb_left = child; - else - parent->rb_right = child; - } - else - root->rb_node = child; - - color: - if (color == RB_BLACK) - __rb_erase_color(child, parent, root); -} - -/* - * This function returns the first node (in sort order) of the tree. - */ -struct rb_node *rb_first(const struct rb_root *root) -{ - struct rb_node *n; - - n = root->rb_node; - if (!n) - return NULL; - while (n->rb_left) - n = n->rb_left; - return n; -} - -struct rb_node *rb_last(const struct rb_root *root) -{ - struct rb_node *n; - - n = root->rb_node; - if (!n) - return NULL; - while (n->rb_right) - n = n->rb_right; - return n; -} - -struct rb_node *rb_next(const struct rb_node *node) -{ - struct rb_node *parent; - - if (rb_parent(node) == node) - return NULL; - - /* If we have a right-hand child, go down and then left as far - as we can. */ - if (node->rb_right) { - node = node->rb_right; - while (node->rb_left) - node=node->rb_left; - return (struct rb_node *)node; - } - - /* No right-hand children. Everything down and left is - smaller than us, so any 'next' node must be in the general - direction of our parent. Go up the tree; any time the - ancestor is a right-hand child of its parent, keep going - up. First time it's a left-hand child of its parent, said - parent is our 'next' node. */ - while ((parent = rb_parent(node)) && node == parent->rb_right) - node = parent; - - return parent; -} - -struct rb_node *rb_prev(const struct rb_node *node) -{ - struct rb_node *parent; - - if (rb_parent(node) == node) - return NULL; - - /* If we have a left-hand child, go down and then right as far - as we can. */ - if (node->rb_left) { - node = node->rb_left; - while (node->rb_right) - node=node->rb_right; - return (struct rb_node *)node; - } - - /* No left-hand children. Go up till we find an ancestor which - is a right-hand child of its parent */ - while ((parent = rb_parent(node)) && node == parent->rb_left) - node = parent; - - return parent; -} - -void rb_replace_node(struct rb_node *victim, struct rb_node *new, - struct rb_root *root) -{ - struct rb_node *parent = rb_parent(victim); - - /* Set the surrounding nodes to point to the replacement */ - if (parent) { - if (victim == parent->rb_left) - parent->rb_left = new; - else - parent->rb_right = new; - } else { - root->rb_node = new; - } - if (victim->rb_left) - rb_set_parent(victim->rb_left, new); - if (victim->rb_right) - rb_set_parent(victim->rb_right, new); - - /* Copy the pointers/colour from the victim to the replacement */ - *new = *victim; -} - -/** - * Get the n-th node (in sort order) of the tree. - * - * \param node The root of the subtree to consider. - * \param n The order statistic to compute. - * - * \return Pointer to the \a n th greatest node on success, \p NULL on errors. - */ -struct rb_node *rb_nth(const struct rb_node *node, unsigned n) -{ - unsigned size = 1; - - if (!node) - return NULL; - if (node->rb_left) - size += node->rb_left->size; - if (n == size) - return (struct rb_node *)node; - if (n < size) - return rb_nth(node->rb_left, n); - return rb_nth(node->rb_right, n - size); -} - -/** - * Get the rank of a node in O(log n) time. - * - * \param node The node to get the rank of. - * \param rank Result pointer. - * - * \return Positive on success, -1 on errors. - */ -int rb_rank(const struct rb_node *node, unsigned *rank) -{ - struct rb_node *parent; - - *rank = 1; - if (!node) - return -1; - if (node->rb_left) - *rank += node->rb_left->size; - while ((parent = rb_parent(node))) { - if (node == parent->rb_right) { - (*rank)++; - if (parent->rb_left) - *rank += parent->rb_left->size; - } - node = parent; - } - return 1; -} diff --git a/rbtree.h b/rbtree.h deleted file mode 100644 index 8295d2ad..00000000 --- a/rbtree.h +++ /dev/null @@ -1,175 +0,0 @@ -/* - Red Black Trees - (C) 1999 Andrea Arcangeli - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - linux/include/linux/rbtree.h - - To use rbtrees you'll have to implement your own insert and search cores. - This will avoid us to use callbacks and to drop drammatically performances. - I know it's not the cleaner way, but in C (not in C++) to get - performances and genericity... - - Some example of insert and search follows here. The search is a plain - normal search over an ordered tree. The insert instead must be implemented - int two steps: as first thing the code must insert the element in - order as a red leaf in the tree, then the support library function - rb_insert_color() must be called. Such function will do the - not trivial work to rebalance the rbtree if necessary. - ------------------------------------------------------------------------ -static inline struct page * rb_search_page_cache(struct inode * inode, - unsigned long offset) -{ - struct rb_node * n = inode->i_rb_page_cache.rb_node; - struct page * page; - - while (n) - { - page = rb_entry(n, struct page, rb_page_cache); - - if (offset < page->offset) - n = n->rb_left; - else if (offset > page->offset) - n = n->rb_right; - else - return page; - } - return NULL; -} - -static inline struct page * __rb_insert_page_cache(struct inode * inode, - unsigned long offset, - struct rb_node * node) -{ - struct rb_node ** p = &inode->i_rb_page_cache.rb_node; - struct rb_node * parent = NULL; - struct page * page; - - while (*p) - { - parent = *p; - page = rb_entry(parent, struct page, rb_page_cache); - - if (offset < page->offset) - p = &(*p)->rb_left; - else if (offset > page->offset) - p = &(*p)->rb_right; - else - return page; - } - - rb_link_node(node, parent, p); - - return NULL; -} - -static inline struct page * rb_insert_page_cache(struct inode * inode, - unsigned long offset, - struct rb_node * node) -{ - struct page * ret; - if ((ret = __rb_insert_page_cache(inode, offset, node))) - goto out; - rb_insert_color(node, &inode->i_rb_page_cache); - out: - return ret; -} ------------------------------------------------------------------------ -*/ - -/** \file rbtree.h Exported symbols from rbtree.h */ - -#ifndef _LINUX_RBTREE_H -#define _LINUX_RBTREE_H - - -/** get the struct this entry is embedded in */ -#define container_of(ptr, type, member) ({ \ - const typeof( ((type *)0)->member ) *__mptr = (ptr); \ - (type *)( (char *)__mptr - offsetof(type,member) );}) - - -struct rb_node -{ - unsigned long rb_parent_color; -#define RB_RED 0 -#define RB_BLACK 1 - struct rb_node *rb_right; - struct rb_node *rb_left; - unsigned size; -} __attribute__((aligned(sizeof(long)))); - /* The alignment might seem pointless, but allegedly CRIS needs it */ - -struct rb_root -{ - struct rb_node *rb_node; -}; - - -#define rb_parent(r) ((struct rb_node *)((r)->rb_parent_color & ~3)) -#define rb_color(r) ((r)->rb_parent_color & 1) -#define rb_is_red(r) (!rb_color(r)) -#define rb_is_black(r) rb_color(r) -#define rb_set_red(r) do { (r)->rb_parent_color &= ~1; } while (0) -#define rb_set_black(r) do { (r)->rb_parent_color |= 1; } while (0) - -static inline void rb_set_parent(struct rb_node *rb, struct rb_node *p) -{ - rb->rb_parent_color = (rb->rb_parent_color & 3) | (unsigned long)p; -} -static inline void rb_set_color(struct rb_node *rb, int color) -{ - rb->rb_parent_color = (rb->rb_parent_color & ~1) | color; -} - -#define RB_ROOT (struct rb_root) { NULL, } -#define rb_entry(ptr, type, member) container_of(ptr, type, member) - -#define RB_EMPTY_ROOT(root) ((root)->rb_node == NULL) -#define RB_EMPTY_NODE(node) (rb_parent(node) == node) -#define RB_CLEAR_NODE(node) (rb_set_parent(node, node)) - -extern void rb_insert_color(struct rb_node *, struct rb_root *); -extern void rb_erase(struct rb_node *, struct rb_root *); - -/* Find logical next and previous nodes in a tree */ -extern struct rb_node *rb_next(const struct rb_node *); -extern struct rb_node *rb_prev(const struct rb_node *); -extern struct rb_node *rb_first(const struct rb_root *); -extern struct rb_node *rb_last(const struct rb_root *); - -extern struct rb_node *rb_nth(const struct rb_node *node, unsigned n); -extern int rb_rank(const struct rb_node *node, unsigned *rank); - -/* Fast replacement of a single node without remove/rebalance/add/rebalance */ -extern void rb_replace_node(struct rb_node *victim, struct rb_node *new, - struct rb_root *root); - -static inline void rb_link_node(struct rb_node * node, struct rb_node * parent, - struct rb_node ** rb_link) -{ - node->size = 1; - node->rb_parent_color = (unsigned long )parent; - node->rb_left = node->rb_right = NULL; - - *rb_link = node; - /* Fixup the size fields in the tree */ - while ((node = rb_parent(node))) - node->size++; -} - -#endif /* _LINUX_RBTREE_H */ diff --git a/rc4.h b/rc4.h index 9ddd8224..1815e3b8 100644 --- a/rc4.h +++ b/rc4.h @@ -1,4 +1,4 @@ /** \file rc4.h common symbols of command.c and client_common.c */ -/** number of bytes of the rc4 session key */ -#define RC4_KEY_LEN 16 +/** Number of bytes of the rc4 session key. */ +#define RC4_KEY_LEN 32 diff --git a/recv.c b/recv.c index fc34279a..d01dab68 100644 --- a/recv.c +++ b/recv.c @@ -6,6 +6,7 @@ /** \file recv.c the stand-alone audio stream receiver */ +#include #include #include diff --git a/recv_common.c b/recv_common.c index 953e2a11..a5689390 100644 --- a/recv_common.c +++ b/recv_common.c @@ -6,8 +6,9 @@ /** \file recv_common.c common functions of para_recv and para_audiod */ -#include "para.h" +#include +#include "para.h" #include "list.h" #include "sched.h" #include "ggo.h" @@ -35,16 +36,17 @@ static void *parse_receiver_args(int receiver_num, char *options) void *conf; -// PARA_DEBUG_LOG("%s, options: %s\n", r->name, -// options? options : "(none)"); + PARA_DEBUG_LOG("%s, options: %s\n", r->name, + options? options : "(none)"); if (options) { PARA_DEBUG_LOG("options: %s\n", options); - argc = split_args(options, &argv, " \t"); + argc = create_argv(options, " \t", &argv); + if (argc < 0) + return NULL; for (i = argc - 1; i >= 0; i--) argv[i + 1] = argv[i]; - argv[0] = para_strdup(r->name); - argc += 1; - PARA_DEBUG_LOG("argc = %d, argv[0]: %s\n", argc, argv[0]); + argv[0] = NULL; + argc++; } else { argc = 1; argv = para_malloc(2 * sizeof(char*)); @@ -52,7 +54,8 @@ static void *parse_receiver_args(int receiver_num, char *options) argv[1] = NULL; } conf = r->parse_config(argc, argv); - free(argv[0]); + for (i = 1; i < argc; i++) + free(argv[i]); free(argv); return conf; } diff --git a/ringbuffer.c b/ringbuffer.c index b27fd8e8..eee2ca10 100644 --- a/ringbuffer.c +++ b/ringbuffer.c @@ -6,6 +6,8 @@ /** \file ringbuffer.c Simple ringbuffer implementation */ +#include + #include "para.h" #include "ringbuffer.h" #include "string.h" diff --git a/sched.c b/sched.c index 6f3f1faf..44e66c9b 100644 --- a/sched.c +++ b/sched.c @@ -6,6 +6,7 @@ /** \file sched.c Paraslash's scheduling functions. */ +#include #include /* readdir() */ #include #include diff --git a/score.c b/score.c index 51ec1e4a..bc40668f 100644 --- a/score.c +++ b/score.c @@ -5,6 +5,9 @@ */ /** \file score.c Scoring functions to determine the audio file streaming order. */ +#include +#include + #include "para.h" #include "error.h" #include "string.h" @@ -93,7 +96,7 @@ static struct osl_table_description score_table_desc = { */ int get_num_admissible_files(unsigned *num) { - return osl_get_num_rows(score_table, num); + return osl(osl_get_num_rows(score_table, num)); } /** @@ -109,7 +112,7 @@ int get_num_admissible_files(unsigned *num) static int get_score_of_row(void *score_row, long *score) { struct osl_object obj; - int ret = osl_get_object(score_table, score_row, SCORECOL_SCORE, &obj); + int ret = osl(osl_get_object(score_table, score_row, SCORECOL_SCORE, &obj)); if (ret >= 0) *score = *(long *)obj.data; @@ -141,7 +144,7 @@ int score_add(const struct osl_row *aft_row, long score) *(int *)(score_objs[SCORECOL_SCORE].data) = score; // PARA_DEBUG_LOG("adding %p\n", *(void **) (score_objs[SCORECOL_AFT_ROW].data)); - ret = osl_add_row(score_table, score_objs); + ret = osl(osl_add_row(score_table, score_objs)); if (ret < 0) { PARA_ERROR_LOG("%s\n", para_strerror(-ret)); free(score_objs[SCORECOL_SCORE].data); @@ -152,7 +155,7 @@ int score_add(const struct osl_row *aft_row, long score) static int get_nth_score(unsigned n, long *score) { struct osl_row *row; - int ret = osl_get_nth_row(score_table, SCORECOL_SCORE, n, &row); + int ret = osl(osl_get_nth_row(score_table, SCORECOL_SCORE, n, &row)); if (ret < 0) return ret; @@ -180,9 +183,9 @@ int score_update(const struct osl_row *aft_row, long percent) unsigned n, new_pos; struct osl_object obj = {.data = (struct osl_row *)aft_row, .size = sizeof(aft_row)}; - int ret = osl_get_row(score_table, SCORECOL_AFT_ROW, &obj, &row); + int ret = osl(osl_get_row(score_table, SCORECOL_AFT_ROW, &obj, &row)); - if (ret == -E_RB_KEY_NOT_FOUND) /* not an error */ + if (ret == -OSL_ERRNO_TO_PARA_ERROR(E_OSL_RB_KEY_NOT_FOUND)) /* not an error */ return 1; if (ret < 0) return ret; @@ -198,7 +201,7 @@ int score_update(const struct osl_row *aft_row, long percent) obj.data = para_malloc(obj.size); *(long *)obj.data = new_score; PARA_DEBUG_LOG("new score: %ld, rank %u/%u\n", new_score, new_pos, n); - return osl_update_object(score_table, row, SCORECOL_SCORE, &obj); + return osl(osl_update_object(score_table, row, SCORECOL_SCORE, &obj)); } /** @@ -219,7 +222,7 @@ int get_score_and_aft_row(struct osl_row *score_row, long *score, if (ret < 0) return ret; - ret = osl_get_object(score_table, score_row, SCORECOL_AFT_ROW, &obj); + ret = osl(osl_get_object(score_table, score_row, SCORECOL_AFT_ROW, &obj)); if (ret < 0) return ret; *aft_row = obj.data; @@ -231,8 +234,7 @@ static int get_score_row_from_aft_row(const struct osl_row *aft_row, { struct osl_object obj = {.data = (struct osl_row *)aft_row, .size = sizeof(aft_row)}; - return osl_get_row(score_table, SCORECOL_AFT_ROW, &obj, score_row); - + return osl(osl_get_row(score_table, SCORECOL_AFT_ROW, &obj, score_row)); } /** @@ -250,7 +252,7 @@ static int get_score_row_from_aft_row(const struct osl_row *aft_row, */ int admissible_file_loop(void *data, osl_rbtree_loop_func *func) { - return osl_rbtree_loop(score_table, SCORECOL_SCORE, data, func); + return osl(osl_rbtree_loop(score_table, SCORECOL_SCORE, data, func)); } /** @@ -265,7 +267,7 @@ int admissible_file_loop(void *data, osl_rbtree_loop_func *func) */ int admissible_file_loop_reverse(void *data, osl_rbtree_loop_func *func) { - return osl_rbtree_loop_reverse(score_table, SCORECOL_SCORE, data, func); + return osl(osl_rbtree_loop_reverse(score_table, SCORECOL_SCORE, data, func)); } /** @@ -281,11 +283,11 @@ int score_get_best(struct osl_row **aft_row, long *score) { struct osl_row *row; struct osl_object obj; - int ret = osl_rbtree_last_row(score_table, SCORECOL_SCORE, &row); + int ret = osl(osl_rbtree_last_row(score_table, SCORECOL_SCORE, &row)); if (ret < 0) return ret; - ret = osl_get_object(score_table, row, SCORECOL_AFT_ROW, &obj); + ret = osl(osl_get_object(score_table, row, SCORECOL_AFT_ROW, &obj)); if (ret < 0) return ret; *aft_row = obj.data; @@ -309,7 +311,7 @@ int score_delete(const struct osl_row *aft_row) if (ret < 0) return ret; - return osl_del_row(score_table, score_row); + return osl(osl_del_row(score_table, score_row)); } /** @@ -327,13 +329,13 @@ int row_belongs_to_score_table(const struct osl_row *aft_row, unsigned *rank) struct osl_row *score_row; int ret = get_score_row_from_aft_row(aft_row, &score_row); - if (ret == -E_RB_KEY_NOT_FOUND) + if (ret == -OSL_ERRNO_TO_PARA_ERROR(E_OSL_RB_KEY_NOT_FOUND)) return 0; if (ret < 0) return ret; if (!rank) return 1; - ret = osl_get_rank(score_table, score_row, SCORECOL_SCORE, rank); + ret = osl(osl_get_rank(score_table, score_row, SCORECOL_SCORE, rank)); if (ret < 0) return ret; return 1; @@ -356,7 +358,7 @@ static void score_close(void) static int score_open(__a_unused const char *dir) { score_table_desc.dir = NULL; /* this table has only volatile columns */ - return osl_open_table(&score_table_desc, &score_table); + return osl(osl_open_table(&score_table_desc, &score_table)); } /** diff --git a/send.h b/send.h index 7087c266..d3be46f0 100644 --- a/send.h +++ b/send.h @@ -131,7 +131,7 @@ void send_chunk(struct sender_client *sc, struct sender_status *ss, size_t header_len); void init_sender_status(struct sender_status *ss, char **access_arg, int num_access_args, int port, int max_clients, int default_deny); -char *get_sender_info(struct sender_status *ss, char *name); +char *get_sender_info(struct sender_status *ss, const char *name); void generic_com_allow(struct sender_command_data *scd, struct sender_status *ss); diff --git a/send_common.c b/send_common.c index 200e59ab..388fde03 100644 --- a/send_common.c +++ b/send_common.c @@ -6,7 +6,10 @@ /** \file send_common.c Functions used by more than one paraslash sender. */ +#include #include +#include + #include "para.h" #include "error.h" #include "string.h" @@ -214,7 +217,7 @@ void init_sender_status(struct sender_status *ss, char **access_arg, * * \return The string printed in the "si" command. */ -char *get_sender_info(struct sender_status *ss, char *name) +char *get_sender_info(struct sender_status *ss, const char *name) { char *clnts = NULL, *ret; struct sender_client *sc, *tmp_sc; diff --git a/server.c b/server.c index 2e5f5f74..be97516f 100644 --- a/server.c +++ b/server.c @@ -14,7 +14,7 @@ * * * - The main programs: \ref server.c, \ref audiod.c, \ref client.c, - * \ref audioc.c, \ref fsck.c, \ref afh.c + * \ref audioc.c, \ref afh.c * - Server: \ref server_command, \ref sender, * - Audio file selector: \ref audio_format_handler, \ref mood, \ref afs_table, * - Client: \ref receiver, \ref receiver_node, \ref filter, \ref filter_node. @@ -47,7 +47,6 @@ * - Time: \ref time.c, * - Spawning processes: \ref exec.c, * - Inter process communication: \ref ipc.c, - * - The object storage layer: \ref osl.c, * - Blob tables: \ref blob.c, * - The error subssystem: \ref error.h. * - Access control for paraslash senders: \ref acl.c, \ref acl.h. @@ -55,7 +54,6 @@ * Low-level data structures: * * - Doubly linked lists: \ref list.h, - * - Red-black trees: \ref rbtree.h, \ref rbtree.c, * - Ring buffer: \ref ringbuffer.c, \ref ringbuffer.h, * - Hashing: \ref hash.h, \ref sha1.h, \ref sha1.c, * - Crypto: \ref crypt.c. @@ -65,9 +63,13 @@ #include #include #include +#include +#include +#include #include "para.h" #include "error.h" +#include "crypt.h" #include "server.cmdline.h" #include "afh.h" #include "string.h" @@ -318,7 +320,6 @@ static void signal_post_select(struct sched *s, struct task *t) waitpid(mmd->afs_pid, NULL, 0); cleanup: free(mmd->afd.afhi.chunk_table); - free(mmd->afd.afhi.info_string); close_listed_fds(); mutex_destroy(mmd_mutex); shm_detach(mmd); @@ -360,7 +361,6 @@ static void command_post_select(struct sched *s, struct task *t) char *peer_name; pid_t child_pid; uint32_t *chunk_table; - char *info_string; if (!FD_ISSET(sct->listen_fd, &s->rfds)) return; @@ -372,16 +372,15 @@ static void command_post_select(struct sched *s, struct task *t) PARA_INFO_LOG("got connection from %s, forking\n", peer_name); mmd->num_connects++; mmd->active_connections++; - random(); - /* The chunk table and the info_string are pointers located in the - * mmd struct that point to dynamically allocated memory that must be - * freed by the parent and the child. However, as the mmd struct is in - * a shared memory area, there's no guarantee that after the fork these - * pointers are still valid in child context. As these two pointers are - * not used in the child anyway, we save them to local variables and - * free the memory via that copy in the child. + /* + * The chunk table is a pointer located in the mmd struct that points + * to dynamically allocated memory, i.e. it must be freed by the parent + * and the child. However, as the mmd struct is in a shared memory + * area, there's no guarantee that after the fork this pointer is still + * valid in child context. As it is not used in the child anyway, we + * save it to a local variable before the fork and free the memory via + * that copy in the child directly after the fork. */ - info_string = mmd->afd.afhi.info_string; chunk_table = mmd->afd.afhi.chunk_table; child_pid = fork(); if (child_pid < 0) { @@ -394,7 +393,6 @@ static void command_post_select(struct sched *s, struct task *t) return; } /* mmd might already have changed at this point */ - free(info_string); free(chunk_table); alarm(ALARM_TIMEOUT); close_listed_fds(); @@ -438,35 +436,6 @@ err: exit(EXIT_FAILURE); } -static void init_random_seed(void) -{ - unsigned int seed; - int fd, ret = para_open("/dev/urandom", O_RDONLY, 0); - - if (ret < 0) - goto err; - fd = ret; - ret = read(fd, &seed, sizeof(seed)); - if (ret < 0) { - ret = -ERRNO_TO_PARA_ERROR(errno); - goto out; - } - if (ret != sizeof(seed)) { - ret = -ERRNO_TO_PARA_ERROR(EIO); - goto out; - } - srandom(seed); - ret = 1; -out: - close(fd); - if (ret >= 0) - return; -err: - PARA_EMERG_LOG("can not seed pseudo random number generator: %s\n", - para_strerror(-ret)); - exit(EXIT_FAILURE); -} - static int init_afs(void) { int ret, afs_server_socket[2]; @@ -474,7 +443,8 @@ static int init_afs(void) ret = socketpair(PF_UNIX, SOCK_DGRAM, 0, afs_server_socket); if (ret < 0) exit(EXIT_FAILURE); - afs_socket_cookie = para_random((uint32_t)-1); + get_random_bytes_or_die((unsigned char *)&afs_socket_cookie, + sizeof(afs_socket_cookie)); mmd->afs_pid = fork(); if (mmd->afs_pid < 0) exit(EXIT_FAILURE); @@ -510,7 +480,7 @@ static void server_init(int argc, char **argv) int afs_socket; valid_fd_012(); - init_random_seed(); + init_random_seed_or_die(); /* parse command line options */ server_cmdline_parser_ext(argc, argv, &conf, ¶ms); HANDLE_VERSION_FLAG("server", conf); diff --git a/server.cmd b/server.cmd index a6b512fe..73ee3417 100644 --- a/server.cmd +++ b/server.cmd @@ -3,7 +3,8 @@ SF: command.c HC: prototypes for the server command handlers CC: array of server commands AT: server_command -IN: para error string afh afs server list user_list +SI: openssl/rc4 osl regex +IN: para error crypt command string afh afs server list user_list SN: list of server commands --- N: ff @@ -92,11 +93,14 @@ H: Print server uptime and other information. --- N: stat P: VSS_READ -D: Print status info for current audio file. -U: stat [n] -H: Without any arguments, stat continuously prints status messages -H: about the audio file being streamed. Use the optional number n -H: to let stat exit after having displayed status n times. +D: Print status info for the current audio file. +U: stat [-n num] [-p] +H: If -n is given, the command exits after having displayed the status n +H: times. Otherwise, the command runs in an endless loop. +H: +H: The -p option activates parser-friendly output: Each status item is +H: prefixed with its size in bytes and the status items identifiers are +H: printed as numerical values. --- N: stop P: VSS_READ | VSS_WRITE diff --git a/server.h b/server.h index cf17c0ef..a814aff0 100644 --- a/server.h +++ b/server.h @@ -12,23 +12,6 @@ /** The maximum length of the host component in an URL */ #define MAX_HOSTLEN 256 -/** - * Defines one command of para_server. - */ -struct server_command { - /** The name of the command. */ - const char *name; - /** Pointer to the function that handles the command. */ - int (*handler)(int, int, char * const * const); - /** The privileges a user must have to execute this command. */ - unsigned int perms; - /** One-line description of the command. */ - const char *description; - /** Summary of the command line options. */ - const char *usage; - /** The long help text. */ - const char *help; -}; /** Holds the arguments for the para_server's sender command. */ struct sender_command_data{ diff --git a/stat.c b/stat.c index 0306cba4..03d00d77 100644 --- a/stat.c +++ b/stat.c @@ -9,7 +9,7 @@ * and para_audiod. */ - +#include #include #include @@ -23,6 +23,14 @@ /** The maximal number of simultaneous connections. */ #define MAX_STAT_CLIENTS 50 +extern char *stat_item_values[NUM_STAT_ITEMS]; + +/** Flags used for the stat command of para_audiod. */ +enum stat_client_flags { + /** Enable parser-friendly output. */ + SCF_PARSER_FRIENDLY = 1, +}; + /** * Describes a status client of para_audiod. * @@ -36,6 +44,8 @@ struct stat_client { int fd; /** Bitmask of those status items the client is interested in. */ uint64_t item_mask; + /** See \ref stat_client flags. s*/ + unsigned flags; /** Its entry in the list of stat clients. */ struct list_head node; }; @@ -61,6 +71,7 @@ static void dump_stat_client_list(void) * * \param fd The file descriptor of the client. * \param mask Bitfield of status items for this client. + * \param parser_friendly Enable parser-friendly output mode. * * Only those status items having the bit set in \a mask will be * sent to the client. @@ -68,7 +79,7 @@ static void dump_stat_client_list(void) * \return Positive value on success, or -E_TOO_MANY_CLIENTS if * the number of connected clients exceeds #MAX_STAT_CLIENTS. */ -int stat_client_add(int fd, uint64_t mask) +int stat_client_add(int fd, uint64_t mask, int parser_friendly) { struct stat_client *new_client; @@ -82,9 +93,11 @@ int stat_client_add(int fd, uint64_t mask) initialized = 1; } PARA_INFO_LOG("adding client on fd %d\n", fd); - new_client = para_malloc(sizeof(struct stat_client)); + new_client = para_calloc(sizeof(struct stat_client)); new_client->fd = fd; new_client->item_mask = mask; + if (parser_friendly) + new_client->flags = SCF_PARSER_FRIENDLY; para_list_add(&new_client->node, &client_list); dump_stat_client_list(); num_clients++; @@ -93,29 +106,34 @@ int stat_client_add(int fd, uint64_t mask) /** * Write a message to all connected status clients. * - * \param msg A \p NULL terminated buffer. - * \param itemnum The number of the status item of \a msg. + * \param item_num The number of the status item of \a msg. * * On write errors, remove the status client from the client list and close its * file descriptor. */ -void stat_client_write(const char *msg, int itemnum) +void stat_client_write_item(int item_num) { struct stat_client *sc, *tmp; - size_t len = strlen(msg); + struct para_buffer pb = {.flags = 0}; + struct para_buffer pfpb = {.flags = PBF_SIZE_PREFIX}; const uint64_t one = 1; - if (!initialized || !len) + if (!initialized) return; list_for_each_entry_safe(sc, tmp, &client_list, node) { int fd = sc->fd, ret; - if (!((one << itemnum) & sc->item_mask)) + if (!((one << item_num) & sc->item_mask)) continue; if (write_ok(fd) > 0) { - ret = write(fd, msg, len); - // PARA_DEBUG_LOG("dumped %s to fd %d, ret = %d\n", msg, fd, ret); - if (ret == len) + struct para_buffer *b = + (sc->flags & SCF_PARSER_FRIENDLY)? &pfpb : &pb; + char *msg = stat_item_values[item_num]; + if (!b->buf) + WRITE_STATUS_ITEM(b, item_num, "%s\n", + msg? msg : ""); + ret = write(fd, b->buf, b->offset); + if (ret == b->offset) continue; } /* write error or fd not ready for writing */ @@ -126,6 +144,8 @@ void stat_client_write(const char *msg, int itemnum) free(sc); dump_stat_client_list(); } + free(pb.buf); + free(pfpb.buf); // if (num_clients) // PARA_DEBUG_LOG("%d client(s)\n", num_clients); } @@ -152,31 +172,68 @@ int stat_item_valid(const char *item) return -E_UNKNOWN_STAT_ITEM; } +/** The minimal length of a status item buffer. */ +#define MIN_STAT_ITEM_LEN 9 /* 5 + 2 + 2, e.g. '0005 00:\n' */ + /** - * Check if line starts with known status item. + * Call a function for each complete status item of a buffer. + * + * \param item_buf The source buffer. + * \param num_bytes The length of \a buf. + * \param item_handler Function to call for each complete item. * - * \param line Buffer containing the line. + * \return Negative on errors, the number of bytes _not_ passed to \a + * item_handler. * - * \return If the beginning of \a line matches any paraslash status item and is - * followed by a colon, the number of that status item is returned. Otherwise, - * this function returns \p -E_UNKNOWN_STAT_ITEM. + * Status items are expected in the format used by parser-friendly output mode + * of the stat command of para_client/para_audioc. */ -int stat_line_valid(const char *line) +int for_each_stat_item(char *item_buf, size_t num_bytes, + int (*item_handler)(int, char *)) { - int i; - size_t line_len; - - if (!line || !*line) - return -E_UNKNOWN_STAT_ITEM; - line_len = strlen(line); - FOR_EACH_STATUS_ITEM(i) { - const char *s = status_item_list[i]; - size_t item_len = strlen(s); - - if (line_len > item_len && line[item_len] == ':' && - !strncmp(line, s, item_len)) - return i; + char *buf = item_buf; + int len = num_bytes; + + for (;;) { + int i, ret, item_len, item_num = 0; + if (len < MIN_STAT_ITEM_LEN) + break; + ret = read_size_header(buf); + if (ret < 0) + return ret; + item_len = ret; + if (item_len > len - 5) /* item not complete */ + break; + for (i = 0; i < 2; i++) { + unsigned char c = buf[5 + i]; + item_num <<= 4; + if (c >= '0' && c <= '9') { + item_num += c - '0'; + continue; + } + if (c >= 'a' && c <= 'f') { + item_num += c - 'a' + 10; + continue; + } + return -E_STAT_ITEM_PARSE; + } + if (buf[7] != ':' || buf[5 + item_len - 1] != '\n') + return -E_STAT_ITEM_PARSE; + buf[5 + item_len - 1] = '\0'; + if (item_num >= NUM_STAT_ITEMS) + PARA_WARNING_LOG("unknown status item %d: %s\n", + item_num, buf + 8); + else { + ret = item_handler(item_num, buf + 8); + if (ret < 0) + return ret; + } + buf += 5 + item_len; + len -= 5 + item_len; + assert(len >= 0 && buf <= item_buf + num_bytes); } - return -E_UNKNOWN_STAT_ITEM; + assert(len >= 0); + if (len && len != num_bytes) + memmove(item_buf, item_buf + num_bytes - len, len); + return len; } - diff --git a/stdin.c b/stdin.c index fea30dd9..8eb86dee 100644 --- a/stdin.c +++ b/stdin.c @@ -9,9 +9,7 @@ #include /* readdir() */ #include - #include "para.h" -#include "string.h" #include "list.h" #include "sched.h" #include "fd.h" diff --git a/stdout.c b/stdout.c index e178fe0e..d64586bc 100644 --- a/stdout.c +++ b/stdout.c @@ -10,7 +10,6 @@ #include #include "para.h" -#include "string.h" #include "list.h" #include "sched.h" #include "fd.h" diff --git a/string.c b/string.c index f51f3574..bb631201 100644 --- a/string.c +++ b/string.c @@ -6,14 +6,14 @@ /** \file string.c Memory allocation and string handling functions. */ -#include "para.h" -#include "string.h" - #include /* gettimeofday */ #include #include /* uname() */ #include +#include +#include "para.h" +#include "string.h" #include "error.h" /** @@ -200,15 +200,15 @@ __must_check __malloc char *para_dirname(const char *name) * ends with a slash. Otherwise, a pointer within \a name is returned. Caller * must not free the result. */ -__must_check const char *para_basename(const char *name) +__must_check char *para_basename(const char *name) { - const char *ret; + char *ret; if (!name || !*name) return NULL; ret = strrchr(name, '/'); if (!ret) - return name; + return (char *)name; ret++; return ret; } @@ -231,27 +231,6 @@ void chop(char *buf) buf[n - 1] = '\0'; } -/** - * Get a random filename. - * - * This is by no means a secure way to create temporary files in a hostile - * directory like \p /tmp. However, it is OK to use for temp files, fifos, - * sockets that are created in ~/.paraslash. Result must be freed by the - * caller. - * - * \return A pointer to a random filename. - */ -__must_check __malloc char *para_tmpname(void) -{ - struct timeval now; - unsigned int seed; - - gettimeofday(&now, NULL); - seed = now.tv_usec; - srand(seed); - return make_message("%08i", rand()); -} - /** * Get the logname of the current user. * @@ -279,56 +258,6 @@ __must_check __malloc char *para_homedir(void) return para_strdup(pw? pw->pw_dir : "/tmp"); } -/** - * Split string and return pointers to its parts. - * - * \param args The string to be split. - * \param argv_ptr Pointer to the list of substrings. - * \param delim Delimiter. - * - * This function modifies \a args by replacing each occurrence of \a delim by - * zero. A \p NULL-terminated array of pointers to char* is allocated dynamically - * and these pointers are initialized to point to the broken-up substrings - * within \a args. A pointer to this array is returned via \a argv_ptr. - * - * \return The number of substrings found in \a args. - */ -unsigned split_args(char *args, char *** const argv_ptr, const char *delim) -{ - char *p; - char **argv; - size_t n = 0, i, j; - - p = args + strspn(args, delim); - for (;;) { - i = strcspn(p, delim); - if (!i) - break; - p += i; - n++; - p += strspn(p, delim); - } - *argv_ptr = para_malloc((n + 1) * sizeof(char *)); - argv = *argv_ptr; - i = 0; - p = args + strspn(args, delim); - while (p) { - argv[i] = p; - j = strcspn(p, delim); - if (!j) - break; - p += strcspn(p, delim); - if (*p) { - *p = '\0'; - p++; - p += strspn(p, delim); - } - i++; - } - argv[n] = NULL; - return n; -} - /** * Get the own hostname. * @@ -457,6 +386,40 @@ int for_each_line_ro(char *buf, size_t size, line_handler_t *line_handler, private_data); } +#define hex(a) (hexchar[(a) & 15]) +static void write_size_header(char *buf, int n) +{ + static char hexchar[] = "0123456789abcdef"; + + buf[0] = hex(n >> 12); + buf[1] = hex(n >> 8); + buf[2] = hex(n >> 4); + buf[3] = hex(n); + buf[4] = ' '; +} + +int read_size_header(const char *buf) +{ + int i, len = 0; + + for (i = 0; i < 4; i++) { + unsigned char c = buf[i]; + len <<= 4; + if (c >= '0' && c <= '9') { + len += c - '0'; + continue; + } + if (c >= 'a' && c <= 'f') { + len += c - 'a' + 10; + continue; + } + return -E_SIZE_PREFIX; + } + if (buf[4] != ' ') + return -E_SIZE_PREFIX; + return len; +} + /** * Safely print into a buffer at a given offset. * @@ -472,7 +435,8 @@ int for_each_line_ro(char *buf, size_t size, line_handler_t *line_handler, * private_data pointer of \a b are passed to the \a max_size_handler of \a b. * If this function succeeds, i.e. returns a non-negative value, the offset of * \a b is reset to zero and the given data is written to the beginning of the - * buffer. + * buffer. If \a max_size_handler() returns a negative value, this value is + * returned by \a para_printf(). * * Upon return, the offset of \a b is adjusted accordingly so that subsequent * calls to this function append data to what is already contained in the @@ -482,13 +446,15 @@ int for_each_line_ro(char *buf, size_t size, line_handler_t *line_handler, * initial buffer is allocated. * * \return The number of bytes printed into the buffer (not including the - * terminating \p NULL byte). + * terminating \p NULL byte) on success, negative on errors. If there is no + * size-bound on \a b, i.e. if \p b->max_size is zero, this function never + * fails. * * \sa make_message(), vsnprintf(3). */ __printf_2_3 int para_printf(struct para_buffer *b, const char *fmt, ...) { - int ret; + int ret, sz_off = (b->flags & PBF_SIZE_PREFIX)? 5 : 0; if (!b->buf) { b->buf = para_malloc(128); @@ -499,13 +465,16 @@ __printf_2_3 int para_printf(struct para_buffer *b, const char *fmt, ...) char *p = b->buf + b->offset; size_t size = b->size - b->offset; va_list ap; - if (size) { + + if (size > sz_off) { va_start(ap, fmt); - ret = vsnprintf(p, size, fmt, ap); + ret = vsnprintf(p + sz_off, size - sz_off, fmt, ap); va_end(ap); - if (ret > -1 && ret < size) { /* success */ - b->offset += ret; - return ret; + if (ret > -1 && ret < size - sz_off) { /* success */ + b->offset += ret + sz_off; + if (sz_off) + write_size_header(p, ret); + return ret + sz_off; } } /* check if we may grow the buffer */ @@ -618,3 +587,167 @@ int get_loglevel_by_name(const char *txt) return LL_EMERG; return -1; } + +static int get_next_word(const char *buf, const char *delim, char **word) +{ + enum line_state_flags {LSF_HAVE_WORD = 1, LSF_BACKSLASH = 2, + LSF_SINGLE_QUOTE = 4, LSF_DOUBLE_QUOTE = 8}; + const char *in; + char *out; + int ret, state = 0; + + out = para_malloc(strlen(buf) + 1); + *out = '\0'; + *word = out; + for (in = buf; *in; in++) { + const char *p; + + switch (*in) { + case '\\': + if (state & LSF_BACKSLASH) /* \\ */ + goto copy_char; + state |= LSF_BACKSLASH; + state |= LSF_HAVE_WORD; + continue; + case 'n': + case 't': + if (state & LSF_BACKSLASH) { /* \n or \t */ + *out++ = (*in == 'n')? '\n' : '\t'; + state &= ~LSF_BACKSLASH; + continue; + } + goto copy_char; + case '"': + if (state & LSF_BACKSLASH) /* \" */ + goto copy_char; + if (state & LSF_SINGLE_QUOTE) /* '" */ + goto copy_char; + if (state & LSF_DOUBLE_QUOTE) { + state &= ~LSF_DOUBLE_QUOTE; + continue; + } + state |= LSF_HAVE_WORD; + state |= LSF_DOUBLE_QUOTE; + continue; + case '\'': + if (state & LSF_BACKSLASH) /* \' */ + goto copy_char; + if (state & LSF_DOUBLE_QUOTE) /* "' */ + goto copy_char; + if (state & LSF_SINGLE_QUOTE) { + state &= ~LSF_SINGLE_QUOTE; + continue; + } + state |= LSF_HAVE_WORD; + state |= LSF_SINGLE_QUOTE; + continue; + } + for (p = delim; *p; p++) { + if (*in != *p) + continue; + if (state & LSF_BACKSLASH) + goto copy_char; + if (state & LSF_SINGLE_QUOTE) + goto copy_char; + if (state & LSF_DOUBLE_QUOTE) + goto copy_char; + if (state & LSF_HAVE_WORD) + goto success; + break; + } + if (*p) /* ignore delimiter at the beginning */ + continue; +copy_char: + state |= LSF_HAVE_WORD; + *out++ = *in; + state &= ~LSF_BACKSLASH; + } + ret = 0; + if (!(state & LSF_HAVE_WORD)) + goto out; + ret = -ERRNO_TO_PARA_ERROR(EINVAL); + if (state & LSF_BACKSLASH) { + PARA_ERROR_LOG("trailing backslash\n"); + goto out; + } + if ((state & LSF_SINGLE_QUOTE) || (state & LSF_DOUBLE_QUOTE)) { + PARA_ERROR_LOG("unmatched quote character\n"); + goto out; + } +success: + *out = '\0'; + return in - buf; +out: + free(*word); + *word = NULL; + return ret; +} + +/** + * Free an array of words created by create_argv(). + * + * \param argv A pointer previously obtained by \ref create_argv(). + */ +void free_argv(char **argv) +{ + int i; + + for (i = 0; argv[i]; i++) + free(argv[i]); + free(argv); +} + +/** + * Split a buffer into words. + * + * This parser honors single and double quotes, backslash-escaped characters + * and special characters like \p \\n. The result contains pointers to copies + * of the words contained in \a buf and has to be freed by using \ref + * free_argv(). + * + * \param buf The buffer to be split. + * \param delim Each character in this string is treated as a separator. + * \param result The array of words is returned here. + * + * \return Number of words in \a buf, negative on errors. + */ +int create_argv(const char *buf, const char *delim, char ***result) +{ + char *word, **argv = para_malloc(2 * sizeof(char *)); + const char *p; + int ret, num_words; + + for (p = buf, num_words = 0; ; p += ret, num_words++) { + ret = get_next_word(p, delim, &word); + if (ret < 0) + goto err; + if (!ret) + break; + argv = para_realloc(argv, (num_words + 2) * sizeof(char*)); + argv[num_words] = word; + } + argv[num_words] = NULL; + *result = argv; + return num_words; +err: + while (num_words > 0) + free(argv[--num_words]); + free(argv); + return ret; +} + +int para_regcomp(regex_t *preg, const char *regex, int cflags) +{ + char *buf; + size_t size; + int ret = regcomp(preg, regex, cflags); + + if (ret == 0) + return 1; + size = regerror(ret, preg, NULL, 0); + buf = para_malloc(size); + regerror(ret, preg, buf, size); + PARA_ERROR_LOG("%s\n", buf); + free(buf); + return -E_REGEX; +} diff --git a/string.h b/string.h index 272f107d..922ba696 100644 --- a/string.h +++ b/string.h @@ -6,6 +6,12 @@ /** \file string.h exported sybmols from string.c */ +/** Flags that change how content is printed into the buffer. */ +enum para_buffer_flags { + /** Prefix each buffer with its length. */ + PBF_SIZE_PREFIX = 1, +}; + /** A string buffer used for para_printf(). */ struct para_buffer { /** The buffer. May be \p NULL. */ @@ -14,6 +20,8 @@ struct para_buffer { size_t size; /** The maximal size this buffer may grow. Zero means unlimited. */ size_t max_size; + /** \sa para_buffer_flags. */ + unsigned flags; /** The next para_printf() will write at this offset. */ size_t offset; /** @@ -26,6 +34,28 @@ struct para_buffer { void *private_data; }; +/** + * Write the contents of a status item to a para_buffer. + * + * \param b The para_buffer. + * \param n The number of the status item. + * \param f A format string. + * + * \return The return value of the underlying call to para_printf(). + */ +#define WRITE_STATUS_ITEM(b, n, f, ...) (\ +{ \ + int _ret; \ + if ((b)->flags & PBF_SIZE_PREFIX) { \ + _ret = para_printf((b), "%02x:" f, n, ## __VA_ARGS__); \ + } else { \ + _ret = para_printf((b), "%s: " f, status_item_list[(n)], \ + ## __VA_ARGS__); \ + } \ + _ret; \ +} \ +) + __must_check __malloc void *para_realloc(void *p, size_t size); __must_check __malloc void *para_malloc(size_t size); __must_check __malloc void *para_calloc(size_t size); @@ -33,12 +63,10 @@ __must_check __malloc char *para_strdup(const char *s); __must_check __malloc __printf_1_2 char *make_message(const char *fmt, ...); __must_check __malloc char *para_strcat(char *a, const char *b); __must_check __malloc char *para_dirname(const char *name); -__must_check const char *para_basename(const char *name); +__must_check char *para_basename(const char *name); void chop(char *buf); -__must_check __malloc char *para_tmpname(void); __must_check __malloc char *para_logname(void); __must_check __malloc char *para_homedir(void); -unsigned split_args(char *args, char *** const argv_ptr, const char *delim); __malloc char *para_hostname(void); __printf_2_3 int para_printf(struct para_buffer *b, const char *fmt, ...); /** Used for for_each_line() and for_each_line_ro(). */ @@ -50,3 +78,7 @@ int for_each_line_ro(char *buf, size_t size, line_handler_t *line_handler, int para_atoi64(const char *str, int64_t *result); int para_atoi32(const char *str, int32_t *value); int get_loglevel_by_name(const char *txt); +int read_size_header(const char *buf); +int create_argv(const char *buf, const char *delim, char ***result); +void free_argv(char **argv); +int para_regcomp(regex_t *preg, const char *regex, int cflags); diff --git a/udp_recv.c b/udp_recv.c index ad8d51fe..7bbd4b0c 100644 --- a/udp_recv.c +++ b/udp_recv.c @@ -5,6 +5,7 @@ */ /** \file udp_recv.c Paraslash's udp receiver */ +#include #include #include #include diff --git a/udp_send.c b/udp_send.c index 7217c960..f8cce2b9 100644 --- a/udp_send.c +++ b/udp_send.c @@ -7,10 +7,12 @@ /** \file udp_send.c Para_server's udp sender. */ +#include #include #include #include #include +#include #include "server.cmdline.h" #include "para.h" diff --git a/user_list.c b/user_list.c index 63bfdfc2..3c0b4f85 100644 --- a/user_list.c +++ b/user_list.c @@ -6,11 +6,14 @@ /** \file user_list.c User handling for para_server. */ +#include #include #include +#include #include "para.h" #include "error.h" +#include "crypt.h" #include "fd.h" #include "string.h" #include "list.h" @@ -53,6 +56,11 @@ static void populate_user_list(char *user_list_file) para_strerror(-ret)); continue; } + if (ret < CHALLENGE_SIZE + 2 * CHALLENGE_SIZE + 41) { + PARA_WARNING_LOG("rsa key for %s too small\n", n); + rsa_free(rsa); + continue; + } u = para_malloc(sizeof(*u)); u->name = para_strdup(n); u->rsa = rsa; diff --git a/user_list.h b/user_list.h index 5baceacc..e95747c4 100644 --- a/user_list.h +++ b/user_list.h @@ -6,8 +6,6 @@ /** \file user_list.h exported functions from user_list.c */ -#include "crypt.h" - /** * permission flags that can be set individually for any server command * diff --git a/vss.c b/vss.c index 6304fc53..21f4cb4d 100644 --- a/vss.c +++ b/vss.c @@ -11,7 +11,9 @@ * senders. */ +#include #include +#include #include "para.h" #include "error.h" @@ -486,8 +488,7 @@ static void compute_slice_timeout(struct timeval *timeout) static void set_eof_barrier(struct vss_task *vsst) { struct fec_client *fc; - struct timeval timeout = mmd->afd.afhi.eof_tv, - *chunk_tv = vss_chunk_time(); + struct timeval timeout = {1, 0}, *chunk_tv = vss_chunk_time(); if (!chunk_tv) goto out; @@ -629,10 +630,6 @@ static void vss_eof(struct vss_task *vsst) mmd->afd.afhi.chunk_tv.tv_usec = 0; free(mmd->afd.afhi.chunk_table); mmd->afd.afhi.chunk_table = NULL; - free(mmd->afd.afhi.info_string); - mmd->afd.afhi.info_string = make_message("%s:\n%s:\n%s:\n", status_item_list[SI_AUDIO_FILE_INFO], - status_item_list[SI_TAGINFO1], status_item_list[SI_TAGINFO2]); - make_empty_status_items(mmd->afd.verbose_ls_output); mmd->mtime = 0; mmd->size = 0; mmd->events++; @@ -781,7 +778,6 @@ static void recv_afs_result(struct vss_task *vsst) if (passed_fd < 0) goto err; shmid = afs_data; - free(mmd->afd.afhi.info_string); ret = load_afd(shmid, &mmd->afd); if (ret < 0) goto err; @@ -956,7 +952,6 @@ void init_vss_task(int afs_socket) free(hn); free(home); mmd->sender_cmd_data.cmd_num = -1; - make_empty_status_items(mmd->afd.verbose_ls_output); if (conf.autoplay_given) { struct timeval tmp; mmd->vss_status_flags |= VSS_PLAYING; diff --git a/wav_filter.c b/wav_filter.c index 1c0da181..3c69311f 100644 --- a/wav_filter.c +++ b/wav_filter.c @@ -6,9 +6,10 @@ /** \file wav_filter.c A filter that inserts a wave header. */ +#include + #include "para.h" #include "error.h" - #include "list.h" #include "sched.h" #include "ggo.h" diff --git a/write.c b/write.c index c12736a6..34cea48c 100644 --- a/write.c +++ b/write.c @@ -6,6 +6,7 @@ /** \file write.c Paraslash's standalone wav/raw player. */ +#include #include #include diff --git a/write_common.c b/write_common.c index 476df8f0..5eb6b796 100644 --- a/write_common.c +++ b/write_common.c @@ -6,6 +6,8 @@ /** \file write_common.c common functions of para_audiod and para_write */ +#include + #include "para.h" #include "string.h" #include "list.h"