Installing paraslash from git
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- autoconf && autoheader && make && sudo make install
+ autoconf && autoheader && ./configure && make && sudo make install
Example for cross-compiling
~~~~~~~~~~~~~~~~~~~~~~~~~~~
uname_rs := $(shell uname -rs)
cc_version := $(shell $(CC) --version | head -n 1)
GIT_VERSION := $(shell ./GIT-VERSION-GEN git-version.h)
-COPYRIGHT_YEAR := 2020
+COPYRIGHT_YEAR := 2021
ifeq ("$(origin O)", "command line")
build_dir := $(O)
NEWS
====
+---------------------------------------
+0.7.0 (to be announced) "seismic orbit"
+---------------------------------------
+
+- Paraslash writers handle early end-of-file more gracefully.
+- The alsa writer no longer warns about spurious underruns.
+- The score formula of the audio file selector has been reworked.
+- Cleanups of the doubly linked lists code.
+
+--------------------------------------
+0.6.4 (2021-11-04) "fuzzy calibration"
+--------------------------------------
+
+This point release contains a fair number of fixes but no new features.
+This marks the end of the 0.6 development, although paraslash-0.6 will
+still be supported for some time and subsequent maintenance releases
+may follow.
+
+- The udp sender no longer crashes when empty chunks are encountered.
+- Fix a double-free bug in the exit path of the server.
+- The "jmp" command now errors out when given a negative percentage.
+- A fix for a bug in para_afh which triggered on the attempt to modify
+ the tags of an invalid mp4 file.
+- A memory leak in para_afh has been fixed.
+- The udp sender no longer sends multiple EOF packets.
+- Some gcc warnings have been silenced.
+- Minor log level adjustments and documentation improvements.
+
+Downloads:
+[tarball](./releases/paraslash-0.6.4.tar.xz),
+[signature](./releases/paraslash-0.6.4.tar.xz.asc)
+
+---------------------------------------
+0.5.9 (2021-11-04) "reversed dimension"
+---------------------------------------
+
+This release contains a few important fixes which have accumulated in
+the maint branch. The paraslash-0.5.x series has now reached its end
+of life and will no longer be supported. All users should upgrade to
+a more recent version at this point.
+
+- Fix an issue with the bash completion script.
+- Initialize the random seed also when using libgrypt.
+- Fix some compiler warnings in the resample filter
+- Don't return spurious errors from the ff server command.
+
+Downloads:
+[tarball](./releases/paraslash-0.5.9.tar.bz2),
+[signature](./releases/paraslash-0.5.9.tar.bz2.asc)
+
----------------------------------------
0.6.3 (2021-02-18) "generalized activity"
-----------------------------------------
mp4ff_tag_t *tag = metadata.tags + i;
ret = -E_MP4FF_META_READ;
- if (mp4ff_meta_get_by_index(mp4ff, i,
- &tag->item, &tag->value) < 0)
+ if (!mp4ff_meta_get_by_index(mp4ff, i, &tag->item, &tag->value))
goto free_tags;
PARA_INFO_LOG("found: %s: %s\n", tag->item, tag->value);
if (!strcmp(tag->item, "artist"))
if (!found_comment)
add_tag(&metadata, "comment", tags->comment);
ret = -E_MP4FF_META_WRITE;
- if (mp4ff_meta_update(&cb, &metadata) < 0)
+ if (!mp4ff_meta_update(&cb, &metadata))
goto free_tags;
ret = 1;
free_tags:
PARA_ERROR_LOG("%s\n", errctx);
if (ret < 0)
PARA_EMERG_LOG("%s\n", para_strerror(-ret));
+ lls_free_parse_result(lpr, CMD_PTR);
return ret < 0? EXIT_FAILURE : EXIT_SUCCESS;
}
/** Data about the current audio file, passed from afs to server. */
struct audio_file_data {
- /** The open file descriptor to the current audio file. */
- int fd;
/** Vss needs this for streaming. */
struct afh_info afhi;
/**
*/
static int open_next_audio_file(void)
{
- struct audio_file_data afd;
- int ret, shmid;
+ int ret, shmid, fd;
char buf[8];
- ret = open_and_update_audio_file(&afd);
+ ret = open_and_update_audio_file(&fd);
if (ret < 0) {
- PARA_ERROR_LOG("%s\n", para_strerror(-ret));
+ if (ret != -OSL_ERRNO_TO_PARA_ERROR(E_OSL_RB_KEY_NOT_FOUND))
+ PARA_ERROR_LOG("%s\n", para_strerror(-ret));
goto no_admissible_files;
}
shmid = ret;
}
*(uint32_t *)buf = NEXT_AUDIO_FILE;
*(uint32_t *)(buf + 4) = (uint32_t)shmid;
- ret = pass_afd(afd.fd, buf, 8);
- close(afd.fd);
+ ret = pass_afd(fd, buf, 8);
+ close(fd);
if (ret >= 0)
return ret;
destroy:
int i, ret;
register_signal_task(&s);
- INIT_LIST_HEAD(&afs_client_list);
+ init_list_head(&afs_client_list);
for (i = 0; i < NUM_AFS_TABLES; i++)
afs_tables[i].init(&afs_tables[i]);
ret = open_afs_tables();
void aft_init(struct afs_table *t);
int aft_get_row_of_path(const char *path, struct osl_row **row);
int aft_check_attributes(uint64_t att_mask, struct para_buffer *pb);
-int open_and_update_audio_file(struct audio_file_data *afd);
+int open_and_update_audio_file(int *fd);
int load_afd(int shmid, struct audio_file_data *afd);
int get_afsi_of_row(const struct osl_row *row, struct afs_info *afsi);
int get_afhi_of_row(const struct osl_row *row, struct afh_info *afhi);
/**
* Open the audio file with highest score and set up an afd structure.
*
- * \param afd Result pointer.
+ * This determines and opens the next audio file, verifies that it did not
+ * change by comparing the recomputed the hash value of the file contents
+ * against the value stored in the audio file table. If all goes well, it
+ * creates a shared memory area containing the serialized version of the afd
+ * structure, including the chunk table, if any. The caller can then send the
+ * ID of this area and the open fd to the server process.
+ *
+ * \param fd Result pointer for the file descriptor of the audio file.
*
* On success, the numplayed field of the audio file selector info is increased
* and the lastplayed time is set to the current time. Finally, the score of
*
* \return Positive shmid on success, negative on errors.
*/
-int open_and_update_audio_file(struct audio_file_data *afd)
+int open_and_update_audio_file(int *fd)
{
unsigned char file_hash[HASH_SIZE];
struct osl_object afsi_obj;
struct osl_object map, chunk_table_obj;
struct ls_data *d = &status_item_ls_data;
unsigned char *tmp_hash;
+ struct audio_file_data afd;
again:
ret = score_get_best(¤t_aft_row, &d->score);
if (ret < 0)
ret = load_afsi(&d->afsi, &afsi_obj);
if (ret < 0)
return ret;
- ret = get_afhi_of_row(current_aft_row, &afd->afhi);
+ ret = get_afhi_of_row(current_aft_row, &afd.afhi);
if (ret < 0)
return ret;
- d->afhi = afd->afhi;
- d->afhi.chunk_table = afd->afhi.chunk_table = NULL;
+ d->afhi = afd.afhi;
+ d->afhi.chunk_table = afd.afhi.chunk_table = NULL;
ret = osl(osl_open_disk_object(audio_file_table, current_aft_row,
AFTCOL_CHUNKS, &chunk_table_obj));
if (ret < 0) {
} else {
PARA_INFO_LOG("chunk table: %zu bytes\n", chunk_table_obj.size);
}
- ret = mmap_full_file(d->path, O_RDONLY, &map.data, &map.size, &afd->fd);
+ ret = mmap_full_file(d->path, O_RDONLY, &map.data, &map.size, fd);
if (ret < 0)
goto out;
hash_function(map.data, map.size, file_hash);
new_afsi.last_played = time(NULL);
save_afsi(&new_afsi, &afsi_obj); /* in-place update */
- afd->audio_format_id = d->afsi.audio_format_id;
- load_chunk_table(&afd->afhi, &chunk_table_obj);
+ afd.audio_format_id = d->afsi.audio_format_id;
+ load_chunk_table(&afd.afhi, &chunk_table_obj);
aced.aft_row = current_aft_row;
aced.old_afsi = &d->afsi;
/*
ret = afs_event(AFSI_CHANGE, NULL, &aced);
if (ret < 0)
goto out;
- ret = save_afd(afd);
+ ret = save_afd(&afd);
out:
- free(afd->afhi.chunk_table);
+ free(afd.afhi.chunk_table);
if (chunk_table_obj.data)
osl_close_disk_object(&chunk_table_obj);
if (ret < 0) {
if (!pad)
return;
+ if (!pad->handle)
+ goto free_pad;
/*
* It's OK to have a blocking operation here because we already made
* sure that the PCM output buffer is (nearly) empty.
snd_pcm_drain(pad->handle);
snd_pcm_close(pad->handle);
snd_config_update_free_global();
+free_pad:
free(pad);
}
if (bytes == 0) /* no data available */
return 0;
pad = wn->private_data = para_calloc(sizeof(*pad));
- get_btr_sample_rate(btrn, &val);
+ ret = get_btr_sample_rate(btrn, &val);
+ if (ret < 0)
+ goto err;
pad->sample_rate = val;
- get_btr_channels(btrn, &val);
+ ret = get_btr_channels(btrn, &val);
+ if (ret < 0)
+ goto err;
pad->channels = val;
- get_btr_sample_format(btrn, &val);
+ ret = get_btr_sample_format(btrn, &val);
+ if (ret < 0)
+ goto err;
pad->sample_format = get_alsa_pcm_format(val);
-
PARA_INFO_LOG("%u channel(s), %uHz\n", pad->channels,
pad->sample_rate);
ret = alsa_init(wn);
- if (ret < 0) {
- free(wn->private_data);
- wn->private_data = NULL;
+ if (ret < 0)
goto err;
- }
wn->min_iqs = pad->bytes_per_frame;
goto again;
}
goto again;
}
if (frames == -EPIPE) {
- PARA_WARNING_LOG("underrun (tried to write %zu bytes)\n", bytes);
+ snd_pcm_status_t *status;
+ snd_pcm_status_malloc(&status);
+ if (snd_pcm_status_get_state(status) == SND_PCM_STATE_XRUN)
+ PARA_WARNING_LOG("underrun\n");
+ snd_pcm_status_free(status);
snd_pcm_prepare(pad->handle);
return 0;
}
goto remove_btrn;
if (ret == 0)
return 0;
- get_btr_sample_rate(wn->btrn, &rate);
- get_btr_channels(wn->btrn, &ch);
- get_btr_sample_format(wn->btrn, &format);
+ ret = get_btr_sample_rate(wn->btrn, &rate);
+ if (ret < 0)
+ goto remove_btrn;
+ ret = get_btr_channels(wn->btrn, &ch);
+ if (ret < 0)
+ goto remove_btrn;
+ ret = get_btr_sample_format(wn->btrn, &format);
+ if (ret < 0)
+ goto remove_btrn;
ret = aow_init(wn, rate, ch, format);
if (ret < 0)
goto remove_btrn;
return -E_MISSING_COLON;
*cmd = p + 1;
len = p - txt;
- re = malloc(len + 1);
+ re = para_malloc(len + 1);
strncpy(re, txt, len);
re[len] = '\0';
ret = get_matching_audio_format_nums(re);
btrn->context = bnd->context;
btrn->start.tv_sec = 0;
btrn->start.tv_usec = 0;
- INIT_LIST_HEAD(&btrn->children);
- INIT_LIST_HEAD(&btrn->input_queue);
+ init_list_head(&btrn->children);
+ init_list_head(&btrn->input_queue);
if (!bnd->child) {
if (bnd->parent) {
list_add_tail(&btrn->node, &bnd->parent->children);
struct chunk_queue *cq_new(size_t max_pending)
{
struct chunk_queue *cq = para_malloc(sizeof(*cq));
- INIT_LIST_HEAD(&cq->q);
+ init_list_head(&cq->q);
cq->max_pending = max_pending;
cq->num_pending = 0;
return cq;
/*
* The original lpr for the interactive session has no non-option
* arguments. We create a fresh lpr from the words in "line" and merge
- * it with the orignal lpr.
+ * it with the original lpr.
*/
ret = lls(lls_parse(argc, argv, cmd, &argv_lpr, &errctx));
free_argv(argv);
}
/*
- * The preselect hook for server commands.
+ * This function asks the scheduler to monitor a file descriptor which
+ * corresponds to an active connection. The descriptor is monitored for either
+ * reading or writing, depending on the state of the connection.
*
- * The task pointer must contain a pointer to the initialized client data
- * structure as it is returned by client_open().
- *
- * This function checks the state of the connection and adds the file descriptor
- * of the connection to the read or write fd set of s accordingly.
+ * The context pointer is assumed to refer to a client task structure that was
+ * initialized earlier by client_open().
*/
static void client_pre_select(struct sched *s, void *context)
{
}
/*
- * The post select hook for client commands.
+ * This function reads or writes to the socket file descriptor which
+ * corresponds to an established connection between the client and the server.
+ * It depends on the current state of the connection and on the readiness of
+ * the socket file descriptor which type of I/O is going to be performed.
+ * Besides the initial handshake and authentication, the function sends the
+ * server command and receives the output from the server, if any.
*
- * Depending on the current state of the connection and the status of the read
- * and write fd sets of s, this function performs the necessary steps to
- * authenticate the connection, to send the command given by t->private_data
- * and to receive para_server's output, if any.
+ * The context pointer refers to a client task structure that was initialized
+ * earlier by client_open().
*/
static int client_post_select(struct sched *s, void *context)
{
struct close_on_fork *cof = para_malloc(sizeof(struct close_on_fork));
if (!initialized) {
- INIT_LIST_HEAD(&close_on_fork_list);
+ init_list_head(&close_on_fork_list);
initialized = 1;
}
cof->fd = fd;
}
EXPORT_SERVER_CMD_HANDLER(stat);
-const char *aux_info_cb(unsigned cmd_num, bool verbose)
+static const char *aux_info_cb(unsigned cmd_num, bool verbose)
{
static char result[80];
unsigned perms = server_command_perms[cmd_num];
static int com_jmp(struct command_context *cc, struct lls_parse_result *lpr)
{
- long unsigned int i;
- int ret;
+ int i, ret;
char *errctx;
ret = lls(lls_check_arg_count(lpr, 1, 1, &errctx));
send_errctx(cc, errctx);
return ret;
}
- if (sscanf(lls_input(0, lpr), "%lu", &i) <= 0)
+ if (sscanf(lls_input(0, lpr), "%d", &i) <= 0)
+ return -ERRNO_TO_PARA_ERROR(EINVAL);
+ if (i < 0 || i > 100)
return -ERRNO_TO_PARA_ERROR(EINVAL);
mutex_lock(mmd_mutex);
ret = -E_NO_AUDIO_FILE;
if (!mmd->afd.afhi.chunks_total)
goto out;
- if (i > 100)
- i = 100;
- PARA_INFO_LOG("jumping to %lu%%\n", i);
+ PARA_INFO_LOG("jumping to %d%%\n", i);
mmd->repos_request = (mmd->afd.afhi.chunks_total * i + 50) / 100;
- PARA_INFO_LOG("sent: %lu, offset before jmp: %li\n",
- mmd->chunks_sent, mmd->offset);
mmd->new_vss_status_flags |= VSS_REPOS;
mmd->new_vss_status_flags &= ~VSS_NEXT;
ret = 1;
sample *= pcd->current_gain;
sample >>= inertia + 1;
if (sample > 32767) { /* clip */
- PARA_WARNING_LOG("clip: %d\n", sample);
+ PARA_NOTICE_LOG("clip: %d\n", sample);
sample = 32767;
pcd->current_gain = (3 * pcd->current_gain +
(1 << inertia)) / 4;
PARA_ERROR(OGGDEC_VERSION, "vorbis version mismatch"), \
PARA_ERROR(OGG_EMPTY, "no ogg pages found"), \
PARA_ERROR(OGG_PACKET_IN, "ogg_stream_packetin() failed"), \
- PARA_ERROR(OGG_STREAM_FLUSH, "ogg_stream_flush() failed"), \
PARA_ERROR(OGG_SYNC, "internal ogg storage overflow"), \
PARA_ERROR(OPENSSH_PARSE, "could not parse openssh private key"), \
PARA_ERROR(OPUS_COMMENT, "invalid or corrupted opus comment"), \
* running.
*
* \return A negative return value of zero means the i9e task terminated. Only
- * in this case it is safe to call ie9_close().
+ * in this case it is safe to call i9e_close().
*/
int i9e_get_error(void)
{
* the given text. If the length of this text exceeds the width of the
* terminal, the text is shortened by leaving out a part in the middle.
*/
-void ie9_print_status_bar(char *buf, unsigned len)
+void i9e_print_status_bar(char *buf, unsigned len)
{
size_t x = i9ep->num_columns, y = (x - 4) / 2;
int i9e_open(struct i9e_client_info *ici, struct sched *s);
void i9e_attach_to_stdout(struct btr_node *producer);
-void ie9_print_status_bar(char *buf, unsigned len);
+void i9e_print_status_bar(char *buf, unsigned len);
void i9e_close(void);
void i9e_signal_dispatch(int sig_num);
__printf_2_3 void i9e_log(int ll, const char* fmt,...);
* Copied from the Linux kernel source tree, version 2.6.13.
*
* Licensed under the GPL v2 as per the whole kernel source tree.
- *
*/
-/** \file list.h doubly linked list implementation */
+/** \file list.h Doubly linked list implementation. */
#include <stddef.h> /* offsetof */
-/** get the struct this entry is embedded in */
+/** 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) );})
-/**
- * Non-NULL pointers that will result in page faults under normal
- * circumstances, used to verify that nobody uses non-initialized list entries.
- * Used for poisoning the \a next pointer of struct list_head.
- */
-#define LIST_POISON1 ((void *) 0x00100100)
-/** Non-null pointer, used for poisoning the \a prev pointer of struct
- * list_head
- */
-#define LIST_POISON2 ((void *) 0x00200200)
-
-/** Simple doubly linked list implementation. */
+/** A list head is just a pair of pointers. */
struct list_head {
- /** pointer to the next list entry */
+ /** Pointer to the next list entry. */
struct list_head *next;
- /** pointer to the previous list entry */
+ /** Pointer to the previous list entry. */
struct list_head *prev;
};
/** Define an initialized list head. */
-#define INITIALIZED_LIST_HEAD(name) struct list_head name = { &(name), &(name) }
-
-
-/** must be called before using any other list functions */
-#define INIT_LIST_HEAD(ptr) do { \
- (ptr)->next = (ptr); (ptr)->prev = (ptr); \
-} while (0)
+#define INITIALIZED_LIST_HEAD(name) struct list_head name = {&(name), &(name)}
-
-/*
- * Some of the internal functions ("__xxx") are useful when
- * manipulating whole lists rather than single entries, as
- * sometimes we already know the next/prev entries and we can
- * generate better code by using them directly rather than
- * using the generic single-entry routines.
- */
-
-
-/*
- * Insert a new entry between two known consecutive entries.
- *
- * This is only for internal list manipulation where we know
- * the prev/next entries already!
- */
-static inline void __list_add(struct list_head *new,
- struct list_head *prev,
- struct list_head *next)
+/** This must be called before using any other list functions. */
+static inline void init_list_head(struct list_head *head)
{
- next->prev = new;
- new->next = next;
- new->prev = prev;
- prev->next = new;
+ head->next = head;
+ head->prev = head;
}
/**
- * add a new entry
+ * Insert a new entry after the specified head.
*
- * \param new new entry to be added
- * \param head list head to add it after
+ * \param entry The new entry to add.
+ * \param head The list head to add it after.
*
- * Insert a new entry after the specified head.
* This is good for implementing stacks.
*/
-static inline void para_list_add(struct list_head *new, struct list_head *head)
+static inline void para_list_add(struct list_head *entry, struct list_head *head)
{
- __list_add(new, head, head->next);
+ entry->prev = head;
+ entry->next = head->next;
+ head->next->prev = entry;
+ head->next = entry;
}
/**
- * add a new entry
+ * Insert a new entry before the specified head.
*
- * \param new new entry to be added
- * \param head list head to add it before
+ * \param entry The new entry to add.
+ * \param head list head to add it before.
*
- * Insert a new entry before the specified head.
* This is useful for implementing queues.
*/
-static inline void list_add_tail(struct list_head *new, struct list_head *head)
+static inline void list_add_tail(struct list_head *entry, struct list_head *head)
{
- __list_add(new, head->prev, head);
+ entry->prev = head->prev;
+ entry->next = head;
+ head->prev->next = entry;
+ head->prev = entry;
}
-/*
- * Delete a list entry by making the prev/next entries
- * point to each other.
+/**
+ * Delete an entry from a list.
+ *
+ * \param entry The element to delete.
*
- * This is only for internal list manipulation where we know
- * the prev/next entries already!
+ * The list entry is in an undefined state after this and \ref list_empty()
+ * does not return true.
*/
-static inline void __list_del(struct list_head * prev, struct list_head * next)
+static inline void list_del(struct list_head *entry)
{
- next->prev = prev;
- prev->next = next;
+ entry->prev->next = entry->next;
+ entry->next->prev = entry->prev;
+ /*
+ * These non-NULL pointers result in page faults when dereferenced.
+ * This helps to catch bugs resulting from using deleted list heads.
+ */
+ entry->next = (void *)0x00100100;
+ entry->prev = (void *)0x00200200;
}
/**
- * Delete entry from list.
- *
- * \param entry the element to delete from the list.
+ * Delete an entry from one list and add it as another list's head.
*
- * Note: list_empty on entry does not return true after this, the entry is
- * in an undefined state.
+ * \param entry The entry to move.
+ * \param head The head that will precede our entry.
*/
-static inline void list_del(struct list_head *entry)
+static inline void list_move(struct list_head *entry, struct list_head *head)
{
- __list_del(entry->prev, entry->next);
- entry->next = LIST_POISON1;
- entry->prev = LIST_POISON2;
+ list_del(entry);
+ para_list_add(entry, head);
}
/**
- * delete from one list and add as another's head
+ * Test whether a list contains no entries.
*
- * \param list: the entry to move
- * \param head: the head that will precede our entry
+ * \param head The list to test.
*/
-static inline void list_move(struct list_head *list, struct list_head *head)
+static inline int list_empty(const struct list_head *head)
{
- __list_del(list->prev, list->next);
- para_list_add(list, head);
+ return head->next == head;
}
/**
- * test whether a list is empty
+ * Test whether a list has just one entry.
*
- * \param head the list to test.
+ * \param head The list to test.
*/
-static inline int list_empty(const struct list_head *head)
+static inline int list_is_singular(const struct list_head *head)
{
- return head->next == head;
+ return !list_empty(head) && (head->next == head->prev);
}
/**
- * get the struct for this entry
+ * Get the struct in which this entry is embedded in.
*
- * \param ptr the &struct list_head pointer.
- * \param type the type of the struct this is embedded in.
- * \param member the name of the list_struct within the struct.
+ * \param ptr The list head pointer.
+ * \param type The type of containing structure.
+ * \param member The name of the list head member within the structure.
*/
-#define list_entry(ptr, type, member) \
- container_of(ptr, type, member)
+#define list_entry(ptr, type, member) container_of(ptr, type, member)
/**
- * iterate over list of given type
+ * Iterate over a list.
*
- * \param pos the type * to use as a loop counter.
- * \param head the head for your list.
- * \param member the name of the list_struct within the struct.
+ * \param pos A struct pointer which serves as the iterator.
+ * \param head The head of the list.
+ * \param member The name of the list head member within the structure.
*/
#define list_for_each_entry(pos, head, member) \
for (pos = list_entry((head)->next, typeof(*pos), member); \
pos = list_entry(pos->member.next, typeof(*pos), member))
/**
- * iterate over list of given type safe against removal of list entry
+ * Iterate over list, safe against removal of list entry.
*
- * \param pos the type * to use as a loop counter.
- * \param n another type * to use as temporary storage
- * \param head the head for your list.
- * \param member the name of the list_struct within the struct.
+ * \param pos The iterator struct pointer.
+ * \param n A second struct pointer which is used as temporary storage.
+ * \param head The head of the list.
+ * \param member The name of the list head member within the structure.
*/
#define list_for_each_entry_safe(pos, n, head, member) \
for (pos = list_entry((head)->next, typeof(*pos), member), \
n = list_entry(pos->member.next, typeof(*pos), member); \
&pos->member != (head); \
pos = n, n = list_entry(n->member.next, typeof(*n), member))
-/**
- * iterate backwards over list of given type safe against removal of list entry
- * \param pos the type * to use as a loop counter.
- * \param n another type * to use as temporary storage
- * \param head the head for your list.
- * \param member the name of the list_struct within the struct.
- */
-#define list_for_each_entry_safe_reverse(pos, n, head, member) \
- for (pos = list_entry((head)->prev, typeof(*pos), member), \
- n = list_entry(pos->member.prev, typeof(*pos), member); \
- &pos->member != (head); \
- pos = n, n = list_entry(n->member.prev, typeof(*n), member))
/**
- * Get the first element from a list
- * \param ptr the list head to take the element from.
+ * Get the first element of a list.
+ *
+ * \param ptr The list head to take the element from.
* \param type The type of the struct this is embedded in.
* \param member The name of the list_struct within the struct.
*
- * Note that list is expected to be not empty.
+ * Note that the list is expected to be non-empty.
*/
#define list_first_entry(ptr, type, member) \
- list_entry((ptr)->next, type, member)
-
-/**
- * Test whether a list has just one entry.
- *
- * \param head The list to test.
- */
-static inline int list_is_singular(const struct list_head *head)
-{
- return !list_empty(head) && (head->next == head->prev);
-}
-
+ list_entry((ptr)->next, type, member)
struct mood *m = para_calloc(sizeof(struct mood));
if (name)
m->name = para_strdup(name);
- INIT_LIST_HEAD(&m->accept_list);
- INIT_LIST_HEAD(&m->deny_list);
- INIT_LIST_HEAD(&m->score_list);
+ init_list_head(&m->accept_list);
+ init_list_head(&m->deny_list);
+ init_list_head(&m->score_list);
return m;
}
{
struct flowopts *new = para_malloc(sizeof(*new));
- INIT_LIST_HEAD(&new->sockopts);
+ init_list_head(&new->sockopts);
return new;
}
if (sound_device_is_busy())
return 0;
- get_btr_sample_rate(btrn, &rate);
- get_btr_channels(btrn, &ch);
- get_btr_sample_format(btrn, &format);
+ ret = get_btr_sample_rate(btrn, &rate);
+ if (ret < 0)
+ goto out;
+ ret = get_btr_channels(btrn, &ch);
+ if (ret < 0)
+ goto out;
+ ret = get_btr_sample_format(btrn, &format);
+ if (ret < 0)
+ goto out;
ret = oss_init(wn, rate, ch, format);
if (ret < 0)
goto out;
struct play_command_info {
play_cmd_handler_t handler;
};
-#define EXPORT_PLAY_CMD_HANDLER(_cmd) \
- const struct play_command_info lsg_play_cmd_com_ ## _cmd ## _user_data = { \
- .handler = com_ ## _cmd \
- };
static int loglevel = LL_WARNING;
btr_remove_node(&pt->btrn);
}
+#define EXPORT_PLAY_CMD_HANDLER(_cmd) \
+ const struct play_command_info lsg_play_cmd_com_ ## _cmd ## _user_data = { \
+ .handler = com_ ## _cmd \
+ };
+
static int com_quit(__a_unused struct lls_parse_result *lpr)
{
pt->rq = CRT_TERM_RQ;
history_file = para_strdup(OPT_STRING_VAL(HISTORY_FILE));
else {
char *home = para_homedir();
- history_file = make_message("%s/.paraslash/play.history",
- home);
+ char *dot_para = make_message("%s/.paraslash", home);
+
free(home);
+ ret = para_mkdir(dot_para, 0777);
+ /* warn, but otherwise ignore mkdir error */
+ if (ret < 0 && ret != -ERRNO_TO_PARA_ERROR(EEXIST))
+ PARA_WARNING_LOG("Can not create %s: %s\n", dot_para,
+ para_strerror(-ret));
+ history_file = make_message("%s/play.history", dot_para);
+ free(dot_para);
}
ici.history_file = history_file;
ici.loglevel = loglevel;
if (btr_get_input_queue_size(pt->btrn) > 0)
return;
}
- ie9_print_status_bar(str, len);
+ i9e_print_status_bar(str, len);
}
/*
assert(info->post_select);
if (!s->task_list.next)
- INIT_LIST_HEAD(&s->task_list);
+ init_list_head(&s->task_list);
t->info = *info;
t->name = para_strdup(info->name);
}
ss->default_port = default_port;
- INIT_LIST_HEAD(&ss->client_list);
+ init_list_head(&ss->client_list);
/* Initialize an access control list */
- INIT_LIST_HEAD(&ss->acl);
+ init_list_head(&ss->acl);
for (i = 0; i < lls_opt_given(acl_opt_result); i++) {
const char *arg = lls_string_val(i, acl_opt_result);
char addr[16];
/** The process id of the audio file selector process. */
pid_t afs_pid = 0;
-/* The the main server process (parent of afs and the command handlers). */
+/* The main server process (parent of afs and the command handlers). */
static pid_t server_pid;
/**
if (pid != afs_pid)
continue;
PARA_EMERG_LOG("fatal: afs died\n");
- kill(0, SIGTERM);
- goto cleanup;
+ goto genocide;
}
break;
/* die on sigint/sigterm. Kill all children too. */
case SIGINT:
case SIGTERM:
PARA_EMERG_LOG("terminating on signal %d\n", signum);
+genocide:
kill(0, SIGTERM);
/*
* We must wait for all of our children to die. For the afs
while (wait(NULL) != -1 || errno != ECHILD)
; /* still at least one child alive */
mutex_lock(mmd_mutex);
-cleanup:
free(mmd->afd.afhi.chunk_table);
task_notify_all(s, E_DEADLY_SIGNAL);
return -E_DEADLY_SIGNAL;
} \
)
-__must_check __malloc void *para_realloc(void *p, size_t size);
+__must_check 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);
__must_check __malloc char *para_strdup(const char *s);
const struct lls_opt_result *r_b;
ctx = fn->private_data = para_calloc(sizeof(*ctx));
- INIT_LIST_HEAD(&ctx->buddies);
+ init_list_head(&ctx->buddies);
/* create socket to listen for incoming packets */
ret = makesock(
ret = -E_SYNC_COMPLETE; /* success */
goto out;
fail:
- PARA_WARNING_LOG("%s\n", para_strerror(-ret));
+ if (ret != -E_BTR_EOF)
+ PARA_WARNING_LOG("%s\n", para_strerror(-ret));
out:
sync_close_buddies(ctx);
btr_splice_out_node(&fn->btrn);
size_t len;
struct udp_target *ut = sc->private_data;
+ if (process_is_command_handler())
+ return;
if (ut->sent_fec_eof)
return;
PARA_NOTICE_LOG("sending FEC EOF\n");
struct sender_command_data scd;
int i;
- INIT_LIST_HEAD(&targets);
+ init_list_head(&targets);
for (i = 0; i < OPT_GIVEN(UDP_TARGET); i++) {
const char *arg = lls_string_val(i, OPT_RESULT(UDP_TARGET));
if (udp_resolve_target(arg, &scd) < 0)
/* Initialize the list of udp targets. */
static void udp_send_init(void)
{
- INIT_LIST_HEAD(&targets);
+ init_list_head(&targets);
sender_status = SENDER_off;
udp_init_target_list();
if (!OPT_GIVEN(UDP_NO_AUTOSTART))
g->bytes += len;
g->num_chunks++;
}
- assert(g->num_chunks);
+ if (g->num_chunks == 0)
+ return -E_EOF;
PARA_DEBUG_LOG("group #%u: %u chunks, %u bytes total\n", g->num,
g->num_chunks, g->bytes);
return 1;
if (ret < 0)
goto err;
vsst->afsss = AFS_SOCKET_READY;
+ if (afs_code == NO_ADMISSIBLE_FILES) {
+ PARA_NOTICE_LOG("no admissible files\n");
+ ret = 0;
+ goto err;
+ }
ret = -E_NOFD;
if (afs_code != NEXT_AUDIO_FILE) {
PARA_ERROR_LOG("afs code: %u, expected: %d\n", afs_code,
return;
err:
free(mmd->afd.afhi.chunk_table);
+ mmd->afd.afhi.chunk_table = NULL;
if (passed_fd >= 0)
close(passed_fd);
- PARA_ERROR_LOG("%s\n", para_strerror(-ret));
+ if (ret < 0)
+ PARA_ERROR_LOG("%s\n", para_strerror(-ret));
mmd->new_vss_status_flags = VSS_NEXT;
}
vsst->afs_socket = afs_socket;
ms2tv(announce_time, &vsst->announce_tv);
PARA_INFO_LOG("announce timeval: %lums\n", tv2ms(&vsst->announce_tv));
- INIT_LIST_HEAD(&fec_client_list);
+ init_list_head(&fec_client_list);
FOR_EACH_SENDER(i) {
PARA_NOTICE_LOG("initializing %s sender\n", senders[i]->name);
senders[i]->init();
[<a href="para_filter.man.html">para_filter</a>]
[<a href="para_write.man.html">para_write</a>]
[<a href="para_gui.man.html">para_gui</a>]
- [<a href="para_mixer.man.html">para_mixere</a>]
+ [<a href="para_mixer.man.html">para_mixer</a>]
[<a href="para_play.man.html">para_play</a>]
</p>
checkout of any of the four integration branches maint,
master, next, pu (see the
- <a href="manual.html#Git.branches">Git branches</a>
+ <a href="manual.html#Git-branches">Git branches</a>
section of the manual). All previous releases
correspond to tagged commits and may be checked out
cd osl && make && sudo make install && sudo ldconfig
sudo apt-get install autoconf libssl-dev m4 \
libmad0-dev libid3tag0-dev libasound2-dev libvorbis-dev \
- libfaad-dev libspeex-dev libFLAC-dev libsamplerate-dev realpath \
+ libfaad-dev libspeex-dev libflac-dev libsamplerate-dev \
libasound2-dev libao-dev libreadline-dev libncurses-dev \
libopus-dev
const char *writer_name(int wid);
void register_writer_node(struct writer_node *wn, struct btr_node *parent,
struct sched *s);
-void get_btr_sample_rate(struct btr_node *btrn, int32_t *result);
-void get_btr_channels(struct btr_node *btrn, int32_t *result);
-void get_btr_sample_format(struct btr_node *btrn, int32_t *result);
+int get_btr_sample_rate(struct btr_node *btrn, int32_t *result);
+int get_btr_channels(struct btr_node *btrn, int32_t *result);
+int get_btr_sample_format(struct btr_node *btrn, int32_t *result);
void print_writer_helps(bool detailed);
}
}
-static void get_btr_value(struct btr_node *btrn, const char *cmd,
+static int get_btr_value(struct btr_node *btrn, const char *cmd,
int32_t *result)
{
char *buf = NULL;
int ret = btr_exec_up(btrn, cmd, &buf);
- if (ret < 0) {
- /*
- * This really should not happen. It means one of our parent
- * nodes died unexpectedly. Proceed with fingers crossed.
- */
- PARA_CRIT_LOG("cmd %s: %s\n", cmd, para_strerror(-ret));
- *result = 0;
- return;
- }
+ *result = 0;
+ /*
+ * Errors may happen when the decoder returns EOF before the writer had
+ * a chance to query the buffer tree for the channel count, sample rate
+ * etc.
+ */
+ if (ret < 0)
+ return ret;
ret = para_atoi32(buf, result);
assert(ret >= 0);
free(buf);
+ return ret;
}
/**
* \param btrn Where to start the search.
* \param result Filled in by this function.
*
- * This function is assumed to succeed and terminates on errors.
+ * \return Standard.
*/
-void get_btr_sample_rate(struct btr_node *btrn, int32_t *result)
+int get_btr_sample_rate(struct btr_node *btrn, int32_t *result)
{
- get_btr_value(btrn, "sample_rate", result);
+ return get_btr_value(btrn, "sample_rate", result);
}
/**
*
* \param btrn See \ref get_btr_sample_rate.
* \param result See \ref get_btr_sample_rate.
+ *
+ * \return Standard.
*/
-void get_btr_channels(struct btr_node *btrn, int32_t *result)
+int get_btr_channels(struct btr_node *btrn, int32_t *result)
{
- get_btr_value(btrn, "channels", result);
+ return get_btr_value(btrn, "channels", result);
}
/**
*
* \param btrn See \ref get_btr_sample_rate.
* \param result Contains the sample format as an enum sample_format type.
+ *
+ * \return Standard.
*/
-void get_btr_sample_format(struct btr_node *btrn, int32_t *result)
+int get_btr_sample_format(struct btr_node *btrn, int32_t *result)
{
- get_btr_value(btrn, "sample_format", result);
+ return get_btr_value(btrn, "sample_format", result);
}