.SHELLFLAGS := -ec
LOGLEVELS := LL_DEBUG,LL_INFO,LL_NOTICE,LL_WARNING,LL_ERROR,LL_CRIT,LL_EMERG
+SEVERITIES := \"debug\",\"info\",\"notice\",\"warning\",\"error\",\"crit\",\"emerg\"
vardir := /var/paraslash
mandir := $(datarootdir)/man/man1
MKDIR_P := mkdir -p
CPPFLAGS += -DCOPYRIGHT_YEAR='"$(COPYRIGHT_YEAR)"'
CPPFLAGS += -DBUILD_DATE='"$(build_date)"'
CPPFLAGS += -DLOGLEVELS='$(LOGLEVELS)'
+CPPFLAGS += -DSEVERITIES=$(SEVERITIES)
CPPFLAGS += -DUNAME_RS='"$(uname_rs)"'
CPPFLAGS += -DCC_VERSION='"$(cc_version)"'
CPPFLAGS += -I$(lls_suite_dir)
CPPFLAGS += $(lopsub_cppflags)
STRICT_CFLAGS += -fno-strict-aliasing
+ STRICT_CFLAGS += -ftrapv
STRICT_CFLAGS += -g
STRICT_CFLAGS += -Os
STRICT_CFLAGS += -Wundef -W -Wuninitialized
0.7.1 (to be announced) "digital spindrift"
-------------------------------------------
+- The autogen.sh script now only creates the autoconf specific files
+ but no longer runs configure, make and the test suite.
+- A stripped down copy of the discontinued libmp4ff library has become
+ part of the paraslash code base. As a result it is no longer necessary
+ to install faad from source to get support for aac/m4a files. The
+ faad decoder package must still be installed.
+- The log level of the running daemon can now be changed with the
+ new ll command. It is available for para_server and para_audiod.
+- All calls to select(2) have been replaced by calls to poll(2)
+ to avoid known shortcomings of the select API.
++- All allocation functions now check for integer overflow. Since this
++ requires support from the compiler, the oldest supported gcc version
++ has been bumped to gcc-5.4 (released in 2015).
+
[tarball](./releases/paraslash-git.tar.xz)
----------------------------------
#include <neaacdec.h>
#include "para.h"
-
-/* To get the mp4ff_tag_t and mp4ff_metadata_t typedefs. */
-#define USE_TAGGING
-#include <mp4ff.h>
-
+#include "mp4.h"
#include "error.h"
#include "portable_io.h"
#include "afh.h"
const void *map;
size_t mapsize;
size_t fpos;
- int32_t track;
- mp4ff_t *mp4ff;
- mp4AudioSpecificConfig masc;
- mp4ff_callback_t cb;
+ struct mp4 *mp4;
+ struct mp4_callback cb;
};
-static uint32_t aac_afh_read_cb(void *user_data, void *dest, uint32_t want)
+static ssize_t aac_afh_read_cb(void *user_data, void *dest, size_t want)
{
struct aac_afh_context *c = user_data;
size_t have, rv;
if (want == 0 || c->fpos >= c->mapsize)
return 0;
have = c->mapsize - c->fpos;
- rv = PARA_MIN(have, (size_t)want);
+ rv = PARA_MIN(have, want);
PARA_DEBUG_LOG("reading %zu bytes @%zu\n", rv, c->fpos);
memcpy(dest, c->map + c->fpos, rv);
c->fpos += rv;
return rv;
}
-static uint32_t aac_afh_seek_cb(void *user_data, uint64_t pos)
+static off_t aac_afh_seek_cb(void *user_data, off_t offset, int whence)
{
struct aac_afh_context *c = user_data;
- c->fpos = pos;
- return 0;
-}
-static int32_t aac_afh_get_track(mp4ff_t *mp4ff, mp4AudioSpecificConfig *masc)
-{
- int32_t i, rc, num_tracks = mp4ff_total_tracks(mp4ff);
-
- assert(num_tracks >= 0);
- for (i = 0; i < num_tracks; i++) {
- unsigned char *buf = NULL;
- unsigned buf_size = 0;
-
- mp4ff_get_decoder_config(mp4ff, i, &buf, &buf_size);
- if (buf) {
- rc = NeAACDecAudioSpecificConfig(buf, buf_size, masc);
- free(buf);
- if (rc < 0)
- continue;
- return i;
- }
- }
- return -1; /* no audio track */
+ if (whence == SEEK_SET)
+ c->fpos = offset;
+ else if (whence == SEEK_CUR)
+ c->fpos += offset;
+ else if (whence == SEEK_END)
+ c->fpos = c->mapsize + offset;
+ else
+ assert(false);
+ return c->fpos;
}
static int aac_afh_open(const void *map, size_t mapsize, void **afh_context)
{
int ret;
- struct aac_afh_context *c = para_malloc(sizeof(*c));
+ struct aac_afh_context *c = alloc(sizeof(*c));
c->map = map;
c->mapsize = mapsize;
c->cb.seek = aac_afh_seek_cb;
c->cb.user_data = c;
- ret = -E_MP4FF_OPEN;
- c->mp4ff = mp4ff_open_read(&c->cb);
- if (!c->mp4ff)
+ ret = mp4_open(&c->cb, &c->mp4);
+ if (ret < 0)
goto free_ctx;
- c->track = aac_afh_get_track(c->mp4ff, &c->masc);
- ret = -E_MP4FF_TRACK;
- if (c->track < 0)
- goto close_mp4ff;
*afh_context = c;
return 0;
-close_mp4ff:
- mp4ff_close(c->mp4ff);
free_ctx:
free(c);
*afh_context = NULL;
static void aac_afh_close(void *afh_context)
{
struct aac_afh_context *c = afh_context;
- mp4ff_close(c->mp4ff);
+ mp4_close(c->mp4);
free(c);
}
-/**
- * Libmp4ff function to reposition the file to the given sample.
- *
- * \param f The opaque handle returned by mp4ff_open_read().
- * \param track The number of the (audio) track.
- * \param sample Destination.
- *
- * We need this function to obtain the offset of the sample within the audio
- * file. Unfortunately, it is not exposed in the mp4ff header.
- *
- * \return This function always returns 0.
- */
-int32_t mp4ff_set_sample_position(mp4ff_t *f, const int32_t track, const int32_t sample);
-
static int aac_afh_get_chunk(uint32_t chunk_num, void *afh_context,
const char **buf, uint32_t *len)
{
struct aac_afh_context *c = afh_context;
- int32_t ss;
+ uint32_t ss;
size_t offset;
+ int ret;
- assert(chunk_num <= INT_MAX);
- /* this function always returns zero */
- mp4ff_set_sample_position(c->mp4ff, c->track, chunk_num);
+ ret = mp4_set_sample_position(c->mp4, chunk_num);
+ if (ret < 0)
+ return ret;
offset = c->fpos;
- ss = mp4ff_read_sample_getsize(c->mp4ff, c->track, chunk_num);
- if (ss <= 0)
- return -E_MP4FF_BAD_SAMPLE;
- assert(ss + offset <= c->mapsize);
+ ret = mp4_get_sample_size(c->mp4, chunk_num, &ss);
+ if (ret < 0)
+ return ret;
+ if (ss + offset > c->mapsize) /* file got truncated?! */
+ return -E_MP4_CORRUPT;
*buf = c->map + offset;
*len = ss;
return 1;
}
-static void _aac_afh_get_taginfo(const mp4ff_t *mp4ff, struct taginfo *tags)
+static void aac_afh_get_taginfo(const struct mp4 *mp4, struct taginfo *tags)
{
- mp4ff_meta_get_artist(mp4ff, &tags->artist);
- mp4ff_meta_get_title(mp4ff, &tags->title);
- mp4ff_meta_get_date(mp4ff, &tags->year);
- mp4ff_meta_get_album(mp4ff, &tags->album);
- mp4ff_meta_get_comment(mp4ff, &tags->comment);
+ tags->artist = mp4_get_tag_value(mp4, "artist");
+ tags->title = mp4_get_tag_value(mp4, "title");
+ tags->year = mp4_get_tag_value(mp4, "date");
+ tags->album = mp4_get_tag_value(mp4, "album");
+ tags->comment = mp4_get_tag_value(mp4, "comment");
}
/*
struct afh_info *afhi)
{
int ret;
- int32_t rv;
struct aac_afh_context *c;
- int64_t tmp;
+ uint64_t milliseconds;
const char *buf;
uint32_t n, len;
if (ret < 0)
return ret;
- ret = -E_MP4FF_BAD_SAMPLERATE;
- rv = mp4ff_get_sample_rate(c->mp4ff, c->track);
- if (rv <= 0)
- goto close;
- afhi->frequency = rv;
-
- ret = -E_MP4FF_BAD_CHANNEL_COUNT;
- rv = mp4ff_get_channel_count(c->mp4ff, c->track);
- if (rv <= 0)
- goto close;
- afhi->channels = rv;
-
- ret = -E_MP4FF_BAD_SAMPLE_COUNT;
- rv = mp4ff_num_samples(c->mp4ff, c->track);
- if (rv <= 0)
- goto close;
- afhi->chunks_total = rv;
+ afhi->frequency = mp4_get_sample_rate(c->mp4);
+ assert(afhi->frequency > 0);
+ afhi->channels = mp4_get_channel_count(c->mp4);
+ assert(afhi->channels > 0);
+ afhi->chunks_total = mp4_num_samples(c->mp4);
+ assert(afhi->chunks_total > 0);
+
afhi->max_chunk_size = 0;
for (n = 0; n < afhi->chunks_total; n++) {
- if (aac_afh_get_chunk(n, c, &buf, &len) < 0)
- break;
+ ret = aac_afh_get_chunk(n, c, &buf, &len);
+ if (ret < 0)
+ goto out;
afhi->max_chunk_size = PARA_MAX(afhi->max_chunk_size, len);
}
-
- tmp = c->masc.sbr_present_flag == 1? 2048 : 1024;
- afhi->seconds_total = tmp * afhi->chunks_total / afhi->frequency;
- ms2tv(1000 * tmp / afhi->frequency, &afhi->chunk_tv);
-
- if (aac_afh_get_chunk(0, c, &buf, &len) >= 0)
- numbytes -= buf - map;
+ milliseconds = mp4_get_duration(c->mp4);
+ afhi->seconds_total = milliseconds / 1000;
+ ms2tv(milliseconds / afhi->chunks_total, &afhi->chunk_tv);
+ if (aac_afh_get_chunk(0, c, &buf, &len) < 0)
+ goto out;
+ numbytes -= buf - map;
afhi->bitrate = 8 * numbytes / afhi->seconds_total / 1000;
- _aac_afh_get_taginfo(c->mp4ff, &afhi->tags);
+ aac_afh_get_taginfo(c->mp4, &afhi->tags);
ret = 1;
-close:
+out:
aac_afh_close(c);
return ret;
}
-static uint32_t aac_afh_meta_read_cb(void *user_data, void *dest, uint32_t want)
+static ssize_t aac_afh_meta_read_cb(void *user_data, void *dest, size_t want)
{
int fd = *(int *)user_data;
return read(fd, dest, want);
}
-static uint32_t aac_afh_meta_seek_cb(void *user_data, uint64_t pos)
+static off_t aac_afh_meta_seek_cb(void *user_data, off_t offset, int whence)
{
int fd = *(int *)user_data;
- return lseek(fd, pos, SEEK_SET);
+ off_t ret = lseek(fd, offset, whence);
+
+ assert(ret != (off_t)-1);
+ return ret;
}
-static uint32_t aac_afh_meta_write_cb(void *user_data, void *dest, uint32_t want)
+static ssize_t aac_afh_meta_write_cb(void *user_data, void *dest, size_t count)
{
int fd = *(int *)user_data;
- return write(fd, dest, want);
+ return write(fd, dest, count);
}
-static uint32_t aac_afh_meta_truncate_cb(void *user_data)
+static int aac_afh_meta_truncate_cb(void *user_data)
{
int fd = *(int *)user_data;
off_t offset = lseek(fd, 0, SEEK_CUR);
return ftruncate(fd, offset);
}
-static void replace_tag(mp4ff_tag_t *tag, const char *new_val, bool *found)
-{
- free(tag->value);
- tag->value = para_strdup(new_val);
- *found = true;
-}
-
-static void add_tag(mp4ff_metadata_t *md, const char *item, const char *value)
+static void replace_or_add_tag(const char *item, const char *value,
+ struct mp4_metadata *meta)
{
- md->tags[md->count].item = para_strdup(item);
- md->tags[md->count].value = para_strdup(value);
- md->count++;
+ uint32_t n;
+ struct mp4_tag *t;
+
+ for (n = 0; n < meta->count; n++) {
+ t = meta->tags + n;
+ if (strcasecmp(t->item, item))
+ continue;
+ free(t->value);
+ t->value = para_strdup(value);
+ return;
+ }
+ /* item not found, add new tag */
+ meta->tags = para_realloc(meta->tags, (meta->count + 1)
+ * sizeof(struct mp4_tag));
+ t = meta->tags + meta->count;
+ t->item = para_strdup(item);
+ t->value = para_strdup(value);
+ meta->count++;
}
static int aac_afh_rewrite_tags(const char *map, size_t mapsize,
struct taginfo *tags, int fd, __a_unused const char *filename)
{
- int ret, i;
- int32_t rv;
- mp4ff_metadata_t metadata;
- mp4ff_t *mp4ff;
- mp4ff_callback_t cb = {
+ int ret;
+ struct mp4_metadata *metadata;
+ struct mp4 *mp4;
+ struct mp4_callback cb = {
.read = aac_afh_meta_read_cb,
.seek = aac_afh_meta_seek_cb,
.write = aac_afh_meta_write_cb,
.truncate = aac_afh_meta_truncate_cb,
.user_data = &fd
};
- bool found_artist = false, found_title = false, found_album = false,
- found_year = false, found_comment = false;
ret = write_all(fd, map, mapsize);
if (ret < 0)
return ret;
lseek(fd, 0, SEEK_SET);
- mp4ff = mp4ff_open_read_metaonly(&cb);
- if (!mp4ff)
- return -E_MP4FF_OPEN;
-
- ret = -E_MP4FF_META_READ;
- rv = mp4ff_meta_get_num_items(mp4ff);
- if (rv < 0)
- goto close;
- metadata.count = rv;
- PARA_NOTICE_LOG("%d metadata item(s) found\n", rv);
-
- metadata.tags = arr_alloc(metadata.count + 5, sizeof(mp4ff_tag_t));
- for (i = 0; i < metadata.count; i++) {
- mp4ff_tag_t *tag = metadata.tags + i;
-
- ret = -E_MP4FF_META_READ;
- 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"))
- replace_tag(tag, tags->artist, &found_artist);
- else if (!strcmp(tag->item, "title"))
- replace_tag(tag, tags->title, &found_title);
- else if (!strcmp(tag->item, "album"))
- replace_tag(tag, tags->album, &found_album);
- else if (!strcmp(tag->item, "date"))
- replace_tag(tag, tags->year, &found_year);
- else if (!strcmp(tag->item, "comment"))
- replace_tag(tag, tags->comment, &found_comment);
- }
- if (!found_artist)
- add_tag(&metadata, "artist", tags->artist);
- if (!found_title)
- add_tag(&metadata, "title", tags->title);
- if (!found_album)
- add_tag(&metadata, "album", tags->album);
- if (!found_year)
- add_tag(&metadata, "date", tags->year);
- if (!found_comment)
- add_tag(&metadata, "comment", tags->comment);
- ret = -E_MP4FF_META_WRITE;
- if (!mp4ff_meta_update(&cb, &metadata))
- goto free_tags;
- ret = 1;
-free_tags:
- for (; i > 0; i--) {
- free(metadata.tags[i - 1].item);
- free(metadata.tags[i - 1].value);
- }
- free(metadata.tags);
-close:
- mp4ff_close(mp4ff);
+ ret = mp4_open_meta(&cb, &mp4);
+ if (ret < 0)
+ return ret;
+ metadata = mp4_get_meta(mp4);
+ PARA_NOTICE_LOG("%u metadata item(s) found\n", metadata->count);
+ replace_or_add_tag("artist", tags->artist, metadata);
+ replace_or_add_tag("title", tags->title, metadata);
+ replace_or_add_tag("album", tags->album, metadata);
+ replace_or_add_tag("date", tags->year, metadata);
+ replace_or_add_tag("comment", tags->comment, metadata);
+ ret = mp4_update_meta(mp4);
+ mp4_close(mp4);
return ret;
}
static void aacdec_open(struct filter_node *fn)
{
NeAACDecConfigurationPtr c;
- struct private_aacdec_data *padd = para_calloc(sizeof(*padd));
+ struct private_aacdec_data *padd = zalloc(sizeof(*padd));
padd->handle = NeAACDecOpen();
c = NeAACDecGetCurrentConfiguration(padd->handle);
fn->private_data = NULL;
}
-static int aacdec_post_select(__a_unused struct sched *s, void *context)
+static int aacdec_post_monitor(__a_unused struct sched *s, void *context)
{
struct filter_node *fn = context;
struct btr_node *btrn = fn->btrn;
consumed += frame_info.bytesconsumed;
if (!frame_info.samples)
goto success;
- btrbuf = para_malloc(2 * frame_info.samples);
+ btrbuf = arr_alloc(2, frame_info.samples);
for (i = 0; i < frame_info.samples; i++) {
short sh = ((short *)outbuf)[i];
write_int16_host_endian(btrbuf + loaded, sh);
const struct filter lsg_filter_cmd_com_aacdec_user_data = {
.open = aacdec_open,
.close = aacdec_close,
- .pre_select = generic_filter_pre_select,
- .post_select = aacdec_post_select,
+ .pre_monitor = generic_filter_pre_monitor,
+ .post_monitor = aacdec_post_monitor,
.execute = aacdec_execute
};
if (!fn || *fn == '\0')
return -E_AFH_RECV_BAD_FILENAME;
- rn->private_data = pard = para_calloc(sizeof(*pard));
+ rn->private_data = pard = zalloc(sizeof(*pard));
afhi = &pard->afhi;
ret = mmap_full_file(fn, O_RDONLY, &pard->map,
&pard->map_size, &pard->fd);
freep(&rn->private_data);
}
-static void afh_recv_pre_select(struct sched *s, void *context)
+static void afh_recv_pre_monitor(struct sched *s, void *context)
{
struct receiver_node *rn = context;
struct private_afh_recv_data *pard = rn->private_data;
struct afh_info *afhi = &pard->afhi;
struct lls_parse_result *lpr = rn->lpr;
struct timeval chunk_time;
- int state = generic_recv_pre_select(s, rn);
+ int state = generic_recv_pre_monitor(s, rn);
unsigned j_given = RECV_CMD_OPT_GIVEN(AFH, JUST_IN_TIME, lpr);
if (state <= 0)
sched_request_barrier_or_min_delay(&chunk_time, s);
}
-static int afh_recv_post_select(__a_unused struct sched *s, void *context)
+static int afh_recv_post_monitor(__a_unused struct sched *s, void *context)
{
struct receiver_node *rn = context;
struct lls_parse_result *lpr = rn->lpr;
pard->map_size, &header, &size);
if (size > 0) {
PARA_INFO_LOG("writing header (%zu bytes)\n", size);
- buf = para_malloc(size);
+ buf = alloc(size);
memcpy(buf, header, size);
btr_add_output(buf, size, btrn);
afh_free_header(header, pard->audio_format_num);
const struct receiver lsg_recv_cmd_com_afh_user_data = {
.open = afh_recv_open,
.close = afh_recv_close,
- .pre_select = afh_recv_pre_select,
- .post_select = afh_recv_post_select,
+ .pre_monitor = afh_recv_pre_monitor,
+ .post_monitor = afh_recv_post_monitor,
.execute = afh_execute,
};
#include "afs.h"
#include "net.h"
#include "server.h"
+#include "daemon.h"
#include "ipc.h"
#include "list.h"
#include "sched.h"
return ret;
}
-static int afs_signal_post_select(struct sched *s, __a_unused void *context)
+static int afs_signal_post_monitor(struct sched *s, __a_unused void *context)
{
int signum, ret;
PARA_EMERG_LOG("para_server died\n");
goto shutdown;
}
- signum = para_next_signal(&s->rfds);
+ signum = para_next_signal();
if (signum == 0)
return 0;
if (signum == SIGHUP) {
signal_task->task = task_register(&(struct task_info) {
.name = "signal",
- .pre_select = signal_pre_select,
- .post_select = afs_signal_post_select,
+ .pre_monitor = signal_pre_monitor,
+ .post_monitor = afs_signal_post_monitor,
.context = signal_task,
}, s);
struct timeval connect_time;
};
-static void command_pre_select(struct sched *s, void *context)
+static void command_pre_monitor(struct sched *s, void *context)
{
struct command_task *ct = context;
struct afs_client *client;
- para_fd_set(server_socket, &s->rfds, &s->max_fileno);
- para_fd_set(ct->fd, &s->rfds, &s->max_fileno);
+ sched_monitor_readfd(server_socket, s);
+ sched_monitor_readfd(ct->fd, s);
list_for_each_entry(client, &afs_client_list, node)
- para_fd_set(client->fd, &s->rfds, &s->max_fileno);
+ sched_monitor_readfd(client->fd, s);
}
/**
return ret;
}
-static int execute_server_command(fd_set *rfds)
+static int execute_server_command(void)
{
char buf[8];
size_t n;
- int ret = read_nonblock(server_socket, buf, sizeof(buf) - 1, rfds, &n);
+ int ret = read_nonblock(server_socket, buf, sizeof(buf) - 1, &n);
if (ret < 0 || n == 0)
return ret;
}
/* returns 0 if no data available, 1 else */
-static int execute_afs_command(int fd, fd_set *rfds)
+static int execute_afs_command(int fd)
{
uint32_t cookie;
int query_shmid;
char buf[sizeof(cookie) + sizeof(query_shmid)];
size_t n;
- int ret = read_nonblock(fd, buf, sizeof(buf), rfds, &n);
+ int ret = read_nonblock(fd, buf, sizeof(buf), &n);
if (ret < 0)
goto err;
/** Shutdown connection if query has not arrived until this many seconds. */
#define AFS_CLIENT_TIMEOUT 3
-static int command_post_select(struct sched *s, void *context)
+static int command_post_monitor(struct sched *s, void *context)
{
struct command_task *ct = context;
struct sockaddr_un unix_addr;
ret = task_get_notification(ct->task);
if (ret < 0)
return ret;
- ret = execute_server_command(&s->rfds);
+ ret = execute_server_command();
if (ret < 0) {
PARA_EMERG_LOG("%s\n", para_strerror(-ret));
task_notify_all(s, -ret);
}
/* Check the list of connected clients. */
list_for_each_entry_safe(client, tmp, &afs_client_list, node) {
- ret = execute_afs_command(client->fd, &s->rfds);
+ ret = execute_afs_command(client->fd);
if (ret == 0) { /* prevent bogus connection flooding */
struct timeval diff;
tv_diff(now, &client->connect_time, &diff);
free(client);
}
/* Accept connections on the local socket. */
- ret = para_accept(ct->fd, &s->rfds, &unix_addr, sizeof(unix_addr), &fd);
+ ret = para_accept(ct->fd, &unix_addr, sizeof(unix_addr), &fd);
if (ret < 0)
PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
if (ret <= 0)
close(fd);
return 0;
}
- client = para_malloc(sizeof(*client));
+ client = alloc(sizeof(*client));
client->fd = fd;
client->connect_time = *now;
para_list_add(&client->node, &afs_client_list);
ct->task = task_register(&(struct task_info) {
.name = "afs command",
- .pre_select = command_pre_select,
- .post_select = command_post_select,
+ .pre_monitor = command_pre_monitor,
+ .post_monitor = command_post_monitor,
.context = ct,
}, s);
}
+static int afs_poll(struct pollfd *fds, nfds_t nfds, int timeout)
+{
+ mutex_lock(mmd_mutex);
+ daemon_set_loglevel(mmd->loglevel);
+ mutex_unlock(mmd_mutex);
+ return xpoll(fds, nfds, timeout);
+}
+
/**
* Initialize the audio file selector process.
*
PARA_INFO_LOG("server_socket: %d\n", server_socket);
init_admissible_files(OPT_STRING_VAL(AFS_INITIAL_MODE));
register_command_task(&s);
- s.default_timeout.tv_sec = 0;
- s.default_timeout.tv_usec = 999 * 1000;
+ s.default_timeout = 1000;
+ s.poll_function = afs_poll;
ret = write(socket_fd, "\0", 1);
if (ret != 1) {
if (ret == 0)
/* time until buffer underrun occurs, in milliseconds */
unsigned buffer_time;
struct timeval drain_barrier;
- /* File descriptor for select(). */
+ /* File descriptor to monitor for reading. */
int poll_fd;
};
return ret;
}
-static void alsa_write_pre_select(struct sched *s, void *context)
+static void alsa_write_pre_monitor(struct sched *s, void *context)
{
struct pollfd pfd;
struct writer_node *wn = context;
return;
}
pad->poll_fd = pfd.fd;
- para_fd_set(pfd.fd, &s->rfds, &s->max_fileno);
+ sched_monitor_readfd(pfd.fd, s);
}
static void alsa_close(struct writer_node *wn)
free(pad);
}
-static int alsa_write_post_select(__a_unused struct sched *s, void *context)
+static int alsa_write_post_monitor(__a_unused struct sched *s, void *context)
{
struct writer_node *wn = context;
struct private_alsa_write_data *pad = wn->private_data;
if (bytes == 0) /* no data available */
return 0;
- pad = wn->private_data = para_calloc(sizeof(*pad));
+ pad = wn->private_data = zalloc(sizeof(*pad));
ret = get_btr_sample_rate(btrn, &val);
if (ret < 0)
goto err;
frames = snd_pcm_writei(pad->handle, data, frames);
if (frames == 0 || frames == -EAGAIN) {
char buf[100];
- if (pad->poll_fd >= 0 && FD_ISSET(pad->poll_fd, &s->rfds))
+ if (pad->poll_fd >= 0 && sched_read_ok(pad->poll_fd, s))
if (read(pad->poll_fd, buf, 100))
do_nothing;
return 0;
struct writer lsg_write_cmd_com_alsa_user_data = {
- .pre_select = alsa_write_pre_select,
- .post_select = alsa_write_post_select,
+ .pre_monitor = alsa_write_pre_monitor,
+ .post_monitor = alsa_write_post_monitor,
.close = alsa_close,
};
static void amp_open(struct filter_node *fn)
{
- struct private_amp_data *pad = para_calloc(sizeof(*pad));
+ struct private_amp_data *pad = zalloc(sizeof(*pad));
unsigned given = FILTER_CMD_OPT_GIVEN(AMP, AMP, fn->lpr);
uint32_t amp_arg = FILTER_CMD_OPT_UINT32_VAL(AMP, AMP, fn->lpr);
pad->amp, pad->amp / 64.0 + 1.0);
}
-static int amp_post_select(__a_unused struct sched *s, void *context)
+static int amp_post_monitor(__a_unused struct sched *s, void *context)
{
struct filter_node *fn = context;
struct private_amp_data *pad = fn->private_data;
if (inplace)
out = in;
else
- out = para_malloc(len * 2);
+ out = alloc(len * 2);
for (i = 0; i < len; i++) {
int x = (in[i] * factor) >> 6;
const struct filter lsg_filter_cmd_com_amp_user_data = {
.open = amp_open,
.close = amp_close,
- .pre_select = generic_filter_pre_select,
- .post_select = amp_post_select,
+ .pre_monitor = generic_filter_pre_monitor,
+ .post_monitor = amp_post_monitor,
};
ao_shutdown();
}
-static void aow_pre_select(struct sched *s, void *context)
+static void aow_pre_monitor(struct sched *s, void *context)
{
struct writer_node *wn = context;
struct private_aow_data *pawd = wn->private_data;
ao_info *info;
const struct lls_opt_result *r;
unsigned n;
- struct private_aow_data *pawd = para_malloc(sizeof(*pawd));
+ struct private_aow_data *pawd = alloc(sizeof(*pawd));
ao_initialize();
aow_show_drivers();
return -E_AO_PTHREAD;
}
-static int aow_post_select(__a_unused struct sched *s, void *context)
+static int aow_post_monitor(__a_unused struct sched *s, void *context)
{
struct writer_node *wn = context;
struct private_aow_data *pawd = wn->private_data;
struct writer lsg_write_cmd_com_ao_user_data = {
.close = aow_close,
- .pre_select = aow_pre_select,
- .post_select = aow_post_select,
+ .pre_monitor = aow_pre_monitor,
+ .post_monitor = aow_post_monitor,
};
cr->matches = i9e_complete_commands(ci->word, audiod_completers);
}
+static void ll_completer(struct i9e_completion_info *ci,
+ struct i9e_completion_result *cr)
+{
+ i9e_ll_completer(ci, cr);
+}
+
static void version_completer(struct i9e_completion_info *ci,
struct i9e_completion_result *cr)
{
{.name = NULL}
};
-static void audioc_pre_select(struct sched *s, void *context)
+static void audioc_pre_monitor(struct sched *s, void *context)
{
struct audioc_task *at = context;
int ret = btr_node_status(at->btrn, 0, BTR_NT_ROOT);
if (ret < 0)
sched_min_delay(s);
- para_fd_set(at->fd, &s->rfds, &s->max_fileno);
+ sched_monitor_readfd(at->fd, s);
}
-static int audioc_post_select(struct sched *s, void *context)
+static int audioc_post_monitor(struct sched *s, void *context)
{
char *buf = NULL;
struct audioc_task *at = context;
if (ret < 0)
goto out;
- if (!FD_ISSET(at->fd, &s->rfds))
+ if (!sched_read_ok(at->fd, s))
return 0;
bufsize = PARA_MAX(1024U, OPT_UINT32_VAL(BUFSIZE));
- buf = para_malloc(bufsize);
+ buf = alloc(bufsize);
ret = recv_bin_buffer(at->fd, buf, bufsize);
PARA_DEBUG_LOG("recv: %d\n", ret);
if (ret == 0)
EMBRACE(.name = "audioc line handler"));
at->task = task_register(&(struct task_info) {
.name = "audioc",
- .pre_select = audioc_pre_select,
- .post_select = audioc_post_select,
+ .pre_monitor = audioc_pre_monitor,
+ .post_monitor = audioc_post_monitor,
.context = at,
}, &sched);
i9e_attach_to_stdout(at->btrn);
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGINT, &act, NULL);
- sched.select_function = i9e_select;
+ sched.poll_function = i9e_poll;
- sched.default_timeout.tv_sec = 1;
+ sched.default_timeout = 1000;
ret = i9e_open(&ici, &sched);
if (ret < 0)
goto out;
if (ret < 0)
goto out;
bufsize = PARA_MAX(1024U, OPT_UINT32_VAL(BUFSIZE));
- buf = para_malloc(bufsize);
+ buf = alloc(bufsize);
do {
size_t n = ret = recv_bin_buffer(fd, buf, bufsize);
if (ret <= 0)
#define OPT_GIVEN(_name) (lls_opt_given(OPT_RESULT(_name)))
#define OPT_STRING_VAL(_name) (lls_string_val(0, OPT_RESULT(_name)))
#define OPT_UINT32_VAL(_name) (lls_uint32_val(0, OPT_RESULT(_name)))
-#define ENUM_STRING_VAL(_name) (lls_enum_string_val(OPT_UINT32_VAL(_name), \
- lls_opt(LSG_AUDIOD_PARA_AUDIOD_OPT_ ## _name, CMD_PTR)))
__printf_2_3 void (*para_log)(int, const char*, ...) = daemon_log;
/** define the array containing all supported audio formats */
* This is needed also in audiod_command.c (for the tasks command), so it can
* not be made static.
*/
-struct sched sched = {.max_fileno = 0};
+struct sched sched = {.timeout = 0};
/* The task for obtaining para_server's status (para_client stat). */
struct status_task {
para_strerror(-ret));
exit(EXIT_FAILURE);
}
- daemon_set_loglevel(ENUM_STRING_VAL(LOGLEVEL));
+ daemon_set_loglevel(OPT_UINT32_VAL(LOGLEVEL));
n = OPT_GIVEN(USER_ALLOW);
if (n == 0)
return;
- uid_whitelist = para_malloc(n * sizeof(uid_t));
+ uid_whitelist = arr_alloc(n, sizeof(uid_t));
for (i = 0; i < n; i++) {
const char *arg = lls_string_val(i, OPT_RESULT(USER_ALLOW));
int32_t val;
return;
PARA_INFO_LOG("opening %s filters\n", audio_formats[s->format]);
assert(s->fns == NULL);
- s->fns = para_calloc(nf * sizeof(struct filter_node));
+ s->fns = zalloc(nf * sizeof(struct filter_node));
parent = s->receiver_node->btrn;
for (i = 0; i < nf; i++) {
char buf[20];
sprintf(buf, "%s (slot %d)", name, (int)(s - slot));
fn->task = task_register(&(struct task_info) {
.name = buf,
- .pre_select = f->pre_select,
- .post_select = f->post_select,
+ .pre_monitor = f->pre_monitor,
+ .post_monitor = f->post_monitor,
.context = fn,
}, &sched);
parent = fn->btrn;
struct btr_node *parent = s->fns[a->num_filters - 1].btrn;
assert(s->wns == NULL);
- s->wns = para_calloc(PARA_MAX(1U, a->num_writers)
+ s->wns = zalloc(PARA_MAX(1U, a->num_writers)
* sizeof(struct writer_node));
for (i = 0; i < a->num_writers; i++) {
wn = s->wns + i;
if (ret < 0)
return ret;
slot_num = ret;
- rn = para_calloc(sizeof(*rn));
+ rn = zalloc(sizeof(*rn));
rn->receiver = r;
rn->lpr = a->receiver_lpr;
rn->btrn = btr_new_node(&(struct btr_node_description)
audio_formats[format], name, slot_num);
rn->task = task_register(&(struct task_info) {
.name = name,
- .pre_select = r->pre_select,
- .post_select = r->post_select,
+ .pre_monitor = r->pre_monitor,
+ .post_monitor = r->post_monitor,
.context = rn,
}, &sched);
return slot_num;
return -E_MISSING_COLON;
*cmd = p + 1;
len = p - txt;
- re = para_malloc(len + 1);
+ re = alloc(len + 1);
strncpy(re, txt, len);
re[len] = '\0';
ret = get_matching_audio_format_nums(re);
struct lls_parse_result *flpr;
filter_num = filter_setup(cmdline, &cfg, &flpr);
- a->filter_lpr = para_realloc(a->filter_lpr,
- (nf + 1) * sizeof(flpr));
- a->filter_conf = para_realloc(a->filter_conf,
- (nf + 1) * sizeof(void *));
- a->filter_nums = para_realloc(a->filter_nums,
- (nf + 1) * sizeof(unsigned));
+ a->filter_lpr = arr_realloc(a->filter_lpr, nf + 1, sizeof(flpr));
+ a->filter_conf = arr_realloc(a->filter_conf, nf + 1, sizeof(void *));
+ a->filter_nums = arr_realloc(a->filter_nums, nf + 1, sizeof(unsigned));
a->filter_nums[nf] = filter_num;
a->filter_conf[nf] = cfg;
if (a->num_writers > 0)
continue; /* already set up */
a->num_writers = 1;
- a->wids = para_malloc(sizeof(int));
- a->writer_lpr = para_malloc(sizeof(struct lls_parse_result *));
+ a->wids = alloc(sizeof(int));
+ a->writer_lpr = alloc(sizeof(struct lls_parse_result *));
a->wids[0] = check_writer_arg_or_die(NULL, a->writer_lpr);
PARA_INFO_LOG("%s writer: %s (default)\n", audio_formats[i],
writer_name(a->wids[0]));
exit(EXIT_FAILURE);
}
-static int signal_post_select(struct sched *s, void *context)
+static int signal_post_monitor(struct sched *s, void *context)
{
struct signal_task *st = context;
int ret, signum;
ret = task_get_notification(st->task);
if (ret < 0)
return ret;
- signum = para_next_signal(&s->rfds);
+ signum = para_next_signal();
switch (signum) {
case SIGINT:
case SIGTERM:
return 0;
}
-static void command_pre_select(struct sched *s, void *context)
+static void command_pre_monitor(struct sched *s, void *context)
{
struct command_task *ct = context;
- para_fd_set(ct->fd, &s->rfds, &s->max_fileno);
+ sched_monitor_readfd(ct->fd, s);
}
-static int command_post_select(struct sched *s, void *context)
+static int command_post_monitor(struct sched *s, void *context)
{
int ret;
struct command_task *ct = context;
ret = task_get_notification(ct->task);
if (ret < 0)
return ret;
- ret = handle_connect(ct->fd, &s->rfds);
+ ret = dispatch_local_connection(ct->fd);
if (ret < 0) {
PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
if (ret == -E_AUDIOD_TERM) {
ct->task = task_register(&(struct task_info) {
.name = "command",
- .pre_select = command_pre_select,
- .post_select = command_post_select,
+ .pre_monitor = command_pre_monitor,
+ .post_monitor = command_post_monitor,
.context = ct,
}, &sched);
}
audiod_status_dump(true);
}
-static void status_pre_select(struct sched *s, void *context)
+static void status_pre_monitor(struct sched *s, void *context)
{
struct status_task *st = context;
int i, ret, cafn = stat_task->current_audio_format_num;
}
/* restart the client task if necessary */
-static int status_post_select(struct sched *s, void *context)
+static int status_post_monitor(struct sched *s, void *context)
{
struct status_task *st = context;
int ret;
stat_task->task = task_register(&(struct task_info) {
.name = "stat",
- .pre_select = status_pre_select,
- .post_select = status_post_select,
+ .pre_monitor = status_pre_monitor,
+ .post_monitor = status_post_monitor,
.context = stat_task,
}, &sched);
}
ret = lls(lls_parse(argc, argv, CMD_PTR, &lpr, &errctx));
if (ret < 0)
goto out;
- daemon_set_loglevel(ENUM_STRING_VAL(LOGLEVEL));
+ daemon_set_loglevel(OPT_UINT32_VAL(LOGLEVEL));
daemon_drop_privileges_or_die(OPT_STRING_VAL(USER),
OPT_STRING_VAL(GROUP));
version_handle_flag("audiod", OPT_GIVEN(VERSION));
signal_task->task = task_register(&(struct task_info) {
.name = "signal",
- .pre_select = signal_pre_select,
- .post_select = signal_post_select,
+ .pre_monitor = signal_pre_monitor,
+ .post_monitor = signal_post_monitor,
.context = signal_task,
}, &sched);
- sched.default_timeout.tv_sec = 2;
- sched.default_timeout.tv_usec = 999 * 1000;
+ sched.default_timeout = 2999;
ret = schedule(&sched);
audiod_cleanup();
sched_shutdown(&sched);
ret = dup(fd);
if (ret < 0)
return -ERRNO_TO_PARA_ERROR(errno);
- new_client = para_calloc(sizeof(*new_client));
+ new_client = zalloc(sizeof(*new_client));
new_client->fd = ret;
PARA_INFO_LOG("adding client on fd %d\n", new_client->fd);
new_client->item_mask = mask;
}
EXPORT_AUDIOD_CMD_HANDLER(help)
+static int com_ll(int fd, struct lls_parse_result *lpr)
+{
+ unsigned ll;
+ char *errctx;
+ const char *sev[] = {SEVERITIES};
+ const char *arg;
+ int ret = lls(lls_check_arg_count(lpr, 0, 1, &errctx));
+
+ if (ret < 0) {
+ char *tmp = make_message("%s\n", errctx);
+ free(errctx);
+ client_write(fd, tmp);
+ free(tmp);
+ return ret;
+ }
+ if (lls_num_inputs(lpr) == 0) {
+ char *msg;
+ ll = daemon_get_loglevel();
+ msg = make_message("%s\n", sev[ll]);
+ ret = client_write(fd, msg);
+ free(msg);
+ return ret;
+ }
+ arg = lls_input(0, lpr);
+ for (ll = 0; ll < NUM_LOGLEVELS; ll++) {
+ if (!strcmp(arg, sev[ll]))
+ break;
+ }
+ if (ll >= NUM_LOGLEVELS)
+ return -ERRNO_TO_PARA_ERROR(EINVAL);
+ PARA_INFO_LOG("new log level: %s\n", sev[ll]);
+ daemon_set_loglevel(ll);
+ return 1;
+}
+EXPORT_AUDIOD_CMD_HANDLER(ll)
+
static int com_tasks(int fd, __a_unused struct lls_parse_result *lpr)
{
int ret;
* Handle arriving connections on the local socket.
*
* \param accept_fd The fd to accept connections on.
- * \param rfds If \a accept_fd is not set in \a rfds, do nothing.
*
- * This is called in each iteration of the select loop. If there is an incoming
- * connection on \a accept_fd, this function reads the command sent by the peer,
+ * This is called in each iteration of the main loop of the scheduler. If there
+ * is an incoming connection, the function reads the command sent by the peer,
* checks the connecting user's permissions by using unix socket credentials
* (if supported by the OS) and calls the corresponding command handler if
* permissions are OK.
* connection to accept.
*
* \sa \ref para_accept(), \ref recv_cred_buffer().
- * */
-int handle_connect(int accept_fd, fd_set *rfds)
+ */
+int dispatch_local_connection(int accept_fd)
{
int argc, ret, clifd;
char buf[MAXLINE], **argv = NULL;
char *errctx = NULL;
const struct audiod_command_info *aci;
- ret = para_accept(accept_fd, rfds, &unix_addr, sizeof(struct sockaddr_un), &clifd);
+ ret = para_accept(accept_fd, &unix_addr, sizeof(struct sockaddr_un), &clifd);
if (ret <= 0)
return ret;
ret = recv_cred_buffer(clifd, buf, sizeof(buf) - 1);
struct btr_pool *btrp;
PARA_INFO_LOG("%s, %zu bytes\n", name, area_size);
- btrp = para_malloc(sizeof(*btrp));
- btrp->area_start = para_malloc(area_size);
+ btrp = alloc(sizeof(*btrp));
+ btrp->area_start = alloc(area_size);
btrp->area_end = btrp->area_start + area_size;
btrp->rhead = btrp->area_start;
btrp->whead = btrp->area_start;
*/
struct btr_node *btr_new_node(struct btr_node_description *bnd)
{
- struct btr_node *btrn = para_malloc(sizeof(*btrn));
+ struct btr_node *btrn = alloc(sizeof(*btrn));
btrn->name = para_strdup(bnd->name);
btrn->parent = bnd->parent;
*/
static struct btr_buffer *new_btrb(char *buf, size_t size)
{
- struct btr_buffer *btrb = para_calloc(sizeof(*btrb));
+ struct btr_buffer *btrb = zalloc(sizeof(*btrb));
btrb->buf = buf;
btrb->size = size;
if (btrn->start.tv_sec == 0)
btrn->start = *now;
FOR_EACH_CHILD(ch, btrn) {
- struct btr_buffer_reference *br = para_calloc(sizeof(*br));
+ struct btr_buffer_reference *br = zalloc(sizeof(*br));
br->btrb = btrb;
br->consumed = consumed;
list_add_tail(&br->node, &ch->input_queue);
* buffer.
*
* Since the buffer tree may change at any time, this function should be called
- * during each post_select call.
+ * during each post_monitor call.
*
* \return True if \a btrn has no siblings.
*/
if (!wbr) {
/* Make a new wrap buffer combining buf1 and buf2. */
sz = sz1 + sz2;
- buf = para_malloc(sz);
+ buf = alloc(sz);
PARA_DEBUG_LOG("merging input buffers: (%p:%zu, %p:%zu) -> %p:%zu\n",
buf1, sz1, buf2, sz2, buf, sz);
memcpy(buf, buf1, sz1);
memcpy(buf + sz1, buf2, sz2);
- br = para_calloc(sizeof(*br));
+ br = zalloc(sizeof(*br));
br->btrb = new_btrb(buf, sz);
br->btrb->refcount = 1;
br->consumed = 0;
assert(i == 2);
/* make a new btrb that combines the two buffers and a br to it. */
sz = szs[0] + szs[1];
- buf = para_malloc(sz);
+ buf = alloc(sz);
PARA_DEBUG_LOG("%s: memory merging input buffers: (%zu, %zu) -> %zu\n",
btrn->name, szs[0], szs[1], sz);
memcpy(buf, bufs[0], szs[0]);
memcpy(buf + szs[0], bufs[1], szs[1]);
- br = para_calloc(sizeof(*br));
+ br = zalloc(sizeof(*br));
br->btrb = new_btrb(buf, sz);
br->btrb->refcount = 1;
* \param type The supposed type of \a btrn.
*
* Most users of the buffer tree subsystem call this function from both
- * their pre_select and the post_select methods.
+ * their ->pre_monitor() and ->post_monitor() methods.
*
* \return Negative if an error condition was detected, zero if there
* is nothing to do and positive otherwise.
};
/**
- * Set select timeout according to the given context.
+ * Request a minimal timeout if not idle.
*
- * \param s Contains the timeval that should be set.
- * \param cwc Contains a pointer to the buffer tree node.
+ * \param s The scheduler instance.
+ * \param cwc The buffer tree node is derived from this.
*
- * This requests a minimal timeout from the scheduler if btrn of \a cwc is not
- * idle.
+ * If no data is available and the buffer tree node is not in error state, the
+ * function does nothing.
*/
-void check_wav_pre_select(struct sched *s, struct check_wav_context *cwc)
+void check_wav_pre_monitor(struct sched *s, struct check_wav_context *cwc)
{
int ret = btr_node_status(cwc->btrn, cwc->min_iqs, BTR_NT_INTERNAL);
if (ret != 0)
*
* \return Standard.
*/
-int check_wav_post_select(struct check_wav_context *cwc)
+int check_wav_post_monitor(struct check_wav_context *cwc)
{
struct btr_node *btrn = cwc->btrn;
unsigned char *a;
* children of this node can figure out channel count, sample rate, etc.
*
* \return The (opaque) handle of the newly created check_wav instance. It is
- * supposed to be passed to \ref check_wav_pre_select() and \ref
- * check_wav_post_select().
+ * supposed to be passed to \ref check_wav_pre_monitor() and \ref
+ * check_wav_post_monitor().
*
* \sa \ref btr_new_node.
*/
struct btr_node *child, struct wav_params *params,
struct btr_node **cw_btrn)
{
- struct check_wav_context *cwc = para_calloc(sizeof(*cwc));
+ struct check_wav_context *cwc = zalloc(sizeof(*cwc));
cwc->state = CWS_NEED_HEADER;
cwc->min_iqs = WAV_HEADER_LEN;
*
* \param cwc Determines the instance to shut down.
*
- * This function may only be called after check_wav_post_select() has returned
+ * This function may only be called after check_wav_post_monitor() has returned
* negative.
*/
void check_wav_shutdown(struct check_wav_context *cwc)
size_t result_size;
};
-static void exec_pre_select(struct sched *s, void *context)
+static void exec_pre_monitor(struct sched *s, void *context)
{
struct exec_task *et = context;
int ret = btr_node_status(et->btrn, 0, BTR_NT_LEAF);
sched_min_delay(s);
}
-static int exec_post_select(__a_unused struct sched *s, void *context)
+static int exec_post_monitor(__a_unused struct sched *s, void *context)
{
struct exec_task *et = context;
struct btr_node *btrn = et->btrn;
static int execute_client_command(const char *cmd, char **result)
{
int ret;
- struct sched command_sched = {.default_timeout = {.tv_sec = 1}};
+ struct sched command_sched = {.default_timeout = 1000};
struct exec_task exec_task = {
.result_buf = para_strdup(""),
.result_size = 1,
EMBRACE(.name = "exec_collect"));
exec_task.task = task_register(&(struct task_info) {
.name = "client exec",
- .pre_select = exec_pre_select,
- .post_select = exec_post_select,
+ .pre_monitor = exec_pre_monitor,
+ .post_monitor = exec_post_monitor,
.context = &exec_task,
}, &command_sched);
ret = client_connect(ct, &command_sched, NULL, exec_task.btrn);
static struct i9e_completer completers[];
+static void ll_completer(struct i9e_completion_info *ci,
+ struct i9e_completion_result *cr)
+{
+ i9e_ll_completer(ci, cr);
+}
+
static void help_completer(struct i9e_completion_info *ci,
struct i9e_completion_result *cr)
{
if (ret < 0)
goto out;
num_atts = ret;
- sl = para_realloc(sl, (2 * num_atts + 1) * sizeof(char *));
+ sl = arr_realloc(sl, 2 * num_atts + 1, sizeof(char *));
for (i = 0; i < num_atts; i++) {
char *orig = sl[i];
sl[i] = make_message("%s+", orig);
goto free_moods;
num_pl = ret;
n = num_moods + num_pl;
- mops = para_malloc((n + 1) * sizeof(char *));
+ mops = arr_alloc(n + 1, sizeof(char *));
for (i = 0; i < num_moods; i++)
mops[i] = make_message("m/%s", moods[i]);
for (i = 0; i < num_pl; i++)
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGINT, &act, NULL);
- sched.select_function = i9e_select;
+ sched.poll_function = i9e_poll;
ret = i9e_open(&ici, &sched);
if (ret < 0)
struct task *task;
};
-static int supervisor_post_select(struct sched *s, void *context)
+static int supervisor_post_monitor(struct sched *s, void *context)
{
struct supervisor_task *svt = context;
int ret = task_status(ct->task);
int ret;
crypt_init();
- sched.default_timeout.tv_sec = 1;
+ sched.default_timeout = 1000;
ret = client_parse_config(argc, argv, &ct, &client_loglevel);
if (ret < 0)
EMBRACE(.name = "stdout", .parent = ct->btrn[0]));
supervisor_task.task = task_register(&(struct task_info) {
.name = "supervisor",
- .post_select = supervisor_post_select,
+ .post_monitor = supervisor_post_monitor,
.context = &supervisor_task,
}, &sched);
* 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)
+static void client_pre_monitor(struct sched *s, void *context)
{
int ret;
struct client_task *ct = context;
case CL_CONNECTED:
case CL_SENT_AUTH:
case CL_SENT_CH_RESPONSE:
- para_fd_set(ct->scc.fd, &s->rfds, &s->max_fileno);
+ sched_monitor_readfd(ct->scc.fd, s);
return;
case CL_RECEIVED_WELCOME:
case CL_RECEIVED_PROCEED:
case CL_RECEIVED_CHALLENGE:
- para_fd_set(ct->scc.fd, &s->wfds, &s->max_fileno);
+ sched_monitor_writefd(ct->scc.fd, s);
return;
case CL_SENDING:
if (ret < 0)
sched_min_delay(s);
else if (ret > 0)
- para_fd_set(ct->scc.fd, &s->wfds, &s->max_fileno);
+ sched_monitor_writefd(ct->scc.fd, s);
}
__attribute__ ((fallthrough));
case CL_EXECUTING:
if (ret < 0)
sched_min_delay(s);
else if (ret > 0)
- para_fd_set(ct->scc.fd, &s->rfds, &s->max_fileno);
+ sched_monitor_readfd(ct->scc.fd, s);
}
return;
}
return 0;
}
-static int recv_sb(struct client_task *ct, fd_set *rfds,
- struct sb_buffer *result)
+static int recv_sb(struct client_task *ct, struct sb_buffer *result)
{
int ret;
size_t n;
void *trafo_context;
struct iovec iov;
- if (!FD_ISSET(ct->scc.fd, rfds))
- return 0;
if (ct->status < CL_SENT_CH_RESPONSE)
trafo = trafo_context = NULL;
else {
ct->sbc[0] = sb_new_recv(0, trafo, trafo_context);
again:
sb_get_recv_buffer(ct->sbc[0], &iov);
- ret = read_nonblock(ct->scc.fd, iov.iov_base, iov.iov_len, rfds, &n);
+ ret = read_nonblock(ct->scc.fd, iov.iov_base, iov.iov_len, &n);
if (ret < 0) {
sb_free(ct->sbc[0]);
ct->sbc[0] = NULL;
for (i = 0; i < num_inputs; i++)
len += strlen(lls_input(i, ct->lpr)) + 1;
- p = command = para_malloc(len);
+ p = command = alloc(len);
for (i = 0; i < num_inputs; i++) {
const char *str = lls_input(i, ct->lpr);
strcpy(p, str);
* 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)
+static int client_post_monitor(struct sched *s, void *context)
{
struct client_task *ct = context;
int ret = 0;
return 0;
switch (ct->status) {
case CL_CONNECTED: /* receive welcome message */
- ret = read_nonblock(ct->scc.fd, buf, sizeof(buf), &s->rfds, &n);
+ ret = read_nonblock(ct->scc.fd, buf, sizeof(buf), &n);
if (ret < 0 || n == 0)
goto out;
ct->features = parse_features(buf);
* 0.8.0 we no longer need to request the feature.
*/
bool has_sha256;
- if (!FD_ISSET(ct->scc.fd, &s->wfds))
+ if (!sched_write_ok(ct->scc.fd, s))
return 0;
has_sha256 = has_feature("sha256", ct);
sprintf(buf, AUTH_REQUEST_MSG "%s%s", ct->user, has_sha256?
unsigned char crypt_buf[1024];
struct sb_buffer sbb;
- ret = recv_sb(ct, &s->rfds, &sbb);
+ ret = recv_sb(ct, &sbb);
if (ret <= 0)
goto out;
if (sbb.band != SBD_CHALLENGE) {
free(sbb.iov.iov_base);
if (ret < 0)
goto out;
- ct->challenge_hash = para_malloc(HASH2_SIZE);
-
+ ct->challenge_hash = alloc(HASH2_SIZE);
if (has_feature("sha256", ct)) {
hash2_function((char *)crypt_buf, APC_CHALLENGE_SIZE, ct->challenge_hash);
hash2_to_asc(ct->challenge_hash, buf);
case CL_SENT_CH_RESPONSE: /* read server response */
{
struct sb_buffer sbb;
- ret = recv_sb(ct, &s->rfds, &sbb);
+ ret = recv_sb(ct, &sbb);
if (ret <= 0)
goto out;
free(sbb.iov.iov_base);
}
case CL_RECEIVED_PROCEED: /* concat args and send command */
{
- if (!FD_ISSET(ct->scc.fd, &s->wfds))
+ if (!sched_write_ok(ct->scc.fd, s))
return 0;
ret = send_sb_command(ct);
if (ret <= 0)
}
if (ret < 0)
goto close1;
- if (ret > 0 && FD_ISSET(ct->scc.fd, &s->wfds)) {
+ if (ret > 0 && sched_write_ok(ct->scc.fd, s)) {
sz = btr_next_buffer(ct->btrn[1], &buf2);
assert(sz);
ret = send_sb(ct, 1, buf2, sz, SBD_BLOB_DATA, true);
ret = btr_node_status(ct->btrn[0], 0, BTR_NT_ROOT);
if (ret < 0)
goto close0;
- if (ret > 0 && FD_ISSET(ct->scc.fd, &s->rfds)) {
+ if (ret > 0 && sched_read_ok(ct->scc.fd, s)) {
struct sb_buffer sbb;
- ret = recv_sb(ct, &s->rfds, &sbb);
+ ret = recv_sb(ct, &sbb);
if (ret < 0)
goto close0;
if (ret > 0) {
ct->task = task_register(&(struct task_info) {
.name = "client",
- .pre_select = client_pre_select,
- .post_select = client_post_select,
+ .pre_monitor = client_pre_monitor,
+ .post_monitor = client_post_monitor,
.context = ct,
}, s);
return 1;
PARA_INFO_LOG("user: %s\n", user);
PARA_INFO_LOG("key file: %s\n", kf);
PARA_INFO_LOG("loglevel: %d\n", ll);
- ct = para_calloc(sizeof(*ct));
+ ct = zalloc(sizeof(*ct));
ct->scc.fd = -1;
ct->lpr = lpr;
ct->key_file = kf;
#include <netdb.h>
#include <lopsub.h>
-#include "server.lsg.h"
#include "para.h"
#include "error.h"
#include "lsu.h"
#include "net.h"
#include "server.h"
#include "list.h"
-#include "send.h"
#include "sched.h"
+#include "send.h"
#include "vss.h"
#include "daemon.h"
#include "fd.h"
*/
static char *vss_get_status_flags(unsigned int flags)
{
- char *msg = para_malloc(5 * sizeof(char));
+ char *msg = alloc(5 * sizeof(char));
msg[0] = (flags & VSS_PLAYING)? 'P' : '_';
msg[1] = (flags & VSS_NOMORE)? 'O' : '_';
"server_pid: %d\n"
"afs_pid: %d\n"
"connections (active/accepted/total): %u/%u/%u\n"
- "current loglevel: %s\n"
"supported audio formats: %s\n",
ut, mmd->num_played,
(int)getppid(),
mmd->active_connections,
mmd->num_commands,
mmd->num_connects,
- ENUM_STRING_VAL(LOGLEVEL),
AUDIO_FORMAT_HANDLERS
);
mutex_unlock(mmd_mutex);
}
EXPORT_SERVER_CMD_HANDLER(hup);
+static int com_ll(struct command_context *cc, struct lls_parse_result *lpr)
+{
+ unsigned ll, perms;
+ char *errctx;
+ const char *sev[] = {SEVERITIES}, *arg;
+ int ret = lls(lls_check_arg_count(lpr, 0, 1, &errctx));
+
+ if (ret < 0) {
+ send_errctx(cc, errctx);
+ return ret;
+ }
+ if (lls_num_inputs(lpr) == 0) { /* reporting is an unprivileged op. */
+ const char *severity;
+ mutex_lock(mmd_mutex);
+ severity = sev[mmd->loglevel];
+ mutex_unlock(mmd_mutex);
+ return send_sb_va(&cc->scc, SBD_OUTPUT, "%s\n", severity);
+ }
+ /*
+ * Changing the loglevel changes the state of both the afs and the vss,
+ * so we require both AFS_WRITE and VSS_WRITE.
+ */
+ perms = AFS_WRITE | VSS_WRITE;
+ if ((cc->u->perms & perms) != perms)
+ return -ERRNO_TO_PARA_ERROR(EPERM);
+ arg = lls_input(0, lpr);
+ for (ll = 0; ll < NUM_LOGLEVELS; ll++)
+ if (!strcmp(arg, sev[ll]))
+ break;
+ if (ll >= NUM_LOGLEVELS)
+ return -ERRNO_TO_PARA_ERROR(EINVAL);
+ PARA_INFO_LOG("new log level: %s\n", sev[ll]);
+ /* Ask the server and afs processes to adjust their log level. */
+ mutex_lock(mmd_mutex);
+ mmd->loglevel = ll;
+ mutex_unlock(mmd_mutex);
+ return 1;
+}
+EXPORT_SERVER_CMD_HANDLER(ll);
+
static int com_term(__a_unused struct command_context *cc,
__a_unused struct lls_parse_result *lpr)
{
for (i = 0; p < end; i++)
p += strlen(p) + 1;
argc = i;
- argv = para_malloc((argc + 1) * sizeof(char *));
+ argv = arr_alloc(argc + 1, sizeof(char *));
for (i = 0, p = iov->iov_base; p < end; i++) {
argv[i] = para_strdup(p);
p += strlen(p) + 1;
int ret;
unsigned char rand_buf[APC_CHALLENGE_SIZE + 2 * SESSION_KEY_LEN];
unsigned char challenge_hash[HASH2_SIZE];
- char *command = NULL, *buf = para_malloc(HANDSHAKE_BUFSIZE) /* must be on the heap */;
+ char *command = NULL, *buf = alloc(HANDSHAKE_BUFSIZE) /* must be on the heap */;
size_t numbytes;
struct command_context cc_struct = {.u = NULL}, *cc = &cc_struct;
struct iovec iov;
free(fn->private_data);
}
-static int compress_post_select(__a_unused struct sched *s, void *context)
+static int compress_post_monitor(__a_unused struct sched *s, void *context)
{
struct filter_node *fn = context;
struct private_compress_data *pcd = fn->private_data;
if (inplace)
op = ip;
else
- op = para_malloc(length);
+ op = alloc(length);
for (i = 0; i < length / 2; i++) {
/* be careful in that heat, my dear */
int sample = *ip++;
static void compress_open(struct filter_node *fn)
{
- struct private_compress_data *pcd = para_calloc(sizeof(*pcd));
+ struct private_compress_data *pcd = zalloc(sizeof(*pcd));
uint32_t inertia = U32_OPTVAL(INERTIA, fn->lpr);
uint32_t aggressiveness = U32_OPTVAL(AGGRESSIVENESS, fn->lpr);
.setup = compress_setup,
.open = compress_open,
.close = compress_close,
- .pre_select = generic_filter_pre_select,
- .post_select = compress_post_select,
+ .pre_monitor = generic_filter_pre_monitor,
+ .post_monitor = compress_post_monitor,
};
/* Copy CCID preference list (u8 array required) */
given = lls_opt_given(r_c);
if (given) {
- ccids = para_malloc(given);
+ ccids = alloc(given);
fo = flowopt_new();
for (i = 0; i < given; i++)
ccids[i] = lls_int32_val(i, r_c);
return ret;
}
-static void dccp_recv_pre_select(struct sched *s, void *context)
+static void dccp_recv_pre_monitor(struct sched *s, void *context)
{
struct receiver_node *rn = context;
- if (generic_recv_pre_select(s, rn) <= 0)
+ if (generic_recv_pre_monitor(s, rn) <= 0)
return;
- para_fd_set(rn->fd, &s->rfds, &s->max_fileno);
+ sched_monitor_readfd(rn->fd, s);
}
-static int dccp_recv_post_select(struct sched *s, void *context)
+static int dccp_recv_post_monitor(__a_unused struct sched *s, void *context)
{
struct receiver_node *rn = context;
struct btr_node *btrn = rn->btrn;
ret = -E_DCCP_OVERRUN;
if (iovcnt == 0)
goto out;
- ret = readv_nonblock(rn->fd, iov, iovcnt, &s->rfds, &num_bytes);
+ ret = readv_nonblock(rn->fd, iov, iovcnt, &num_bytes);
if (num_bytes == 0)
goto out;
if (num_bytes <= iov[0].iov_len) /* only the first buffer was filled */
const struct receiver lsg_recv_cmd_com_dccp_user_data = {
.open = dccp_recv_open,
.close = dccp_recv_close,
- .pre_select = dccp_recv_pre_select,
- .post_select = dccp_recv_post_select,
+ .pre_monitor = dccp_recv_pre_monitor,
+ .post_monitor = dccp_recv_post_monitor,
};
#include "net.h"
#include "server.h"
#include "list.h"
-#include "send.h"
#include "sched.h"
+#include "send.h"
#include "vss.h"
#include "fd.h"
struct fec_client *fc;
};
-static void dccp_pre_select(int *max_fileno, fd_set *rfds,
- __a_unused fd_set *wfds)
+static void dccp_pre_monitor(struct sched *s)
{
unsigned n;
FOR_EACH_LISTEN_FD(n, dss)
if (dss->listen_fds[n] >= 0)
- para_fd_set(dss->listen_fds[n], rfds, max_fileno);
+ sched_monitor_readfd(dss->listen_fds[n], s);
}
/**
dccp_shutdown_client(sc);
}
-static void dccp_post_select(fd_set *rfds, __a_unused fd_set *wfds)
+static void dccp_post_monitor(__a_unused struct sched *s)
{
struct sender_client *sc;
struct dccp_fec_client *dfc;
int tx_ccid;
uint32_t k, n;
- sc = accept_sender_client(dss, rfds);
+ sc = accept_sender_client(dss);
if (!sc)
return;
shutdown_client(sc, dss);
return;
}
- dfc = para_calloc(sizeof(*dfc));
+ dfc = zalloc(sizeof(*dfc));
sc->private_data = dfc;
k = OPT_UINT32_VAL(DCCP_DATA_SLICES_PER_GROUP);
n = OPT_UINT32_VAL(DCCP_SLICES_PER_GROUP);
.name = "dccp",
.init = dccp_send_init,
.shutdown = dccp_shutdown,
- .pre_select = dccp_pre_select,
- .post_select = dccp_post_select,
+ .pre_monitor = dccp_pre_monitor,
+ .post_monitor = dccp_post_monitor,
.shutdown_clients = dccp_shutdown_clients,
.client_cmds = {
[SENDER_on] = dccp_com_on,
* \param fd The file descriptor to read from.
* \param iov Scatter/gather array used in readv().
* \param iovcnt Number of elements in \a iov.
- * \param rfds An optional fd set pointer.
* \param num_bytes Result pointer. Contains the number of bytes read from \a fd.
*
- * If rfds is not NULL and the (non-blocking) file descriptor fd is not set in
- * rfds, this function returns early without doing anything. Otherwise it tries
- * to read up to sz bytes from fd, where sz is the sum of the lengths of all
- * vectors in iov. Like \ref xwrite(), EAGAIN and EINTR are not considered
- * error conditions. However, EOF is.
+ * This function tries to read up to sz bytes from fd, where sz is the sum of
+ * the lengths of all vectors in iov. Like \ref xwrite(), EAGAIN and EINTR are
+ * not considered error conditions. However, EOF is.
*
* \return Zero or a negative error code. If the underlying call to readv(2)
* returned zero (indicating an end of file condition) or failed for some
*
* \sa \ref xwrite(), read(2), readv(2).
*/
-int readv_nonblock(int fd, struct iovec *iov, int iovcnt, fd_set *rfds,
- size_t *num_bytes)
+int readv_nonblock(int fd, struct iovec *iov, int iovcnt, size_t *num_bytes)
{
int ret, i, j;
*num_bytes = 0;
- /*
- * Avoid a shortcoming of select(): Reads from a non-blocking fd might
- * return EAGAIN even if FD_ISSET() returns true. However, FD_ISSET()
- * returning false definitely means that no data can currently be read.
- * This is the common case, so it is worth to avoid the overhead of the
- * read() system call in this case.
- */
- if (rfds && !FD_ISSET(fd, rfds))
- return 0;
-
for (i = 0, j = 0; i < iovcnt;) {
-
/* fix up the first iov */
assert(j < iov[i].iov_len);
iov[i].iov_base += j;
* \param fd The file descriptor to read from.
* \param buf The buffer to read data to.
* \param sz The size of \a buf.
- * \param rfds \see \ref readv_nonblock().
* \param num_bytes \see \ref readv_nonblock().
*
* This is a simple wrapper for readv_nonblock() which uses an iovec with a single
*
* \return The return value of the underlying call to readv_nonblock().
*/
-int read_nonblock(int fd, void *buf, size_t sz, fd_set *rfds, size_t *num_bytes)
+int read_nonblock(int fd, void *buf, size_t sz, size_t *num_bytes)
{
struct iovec iov = {.iov_base = buf, .iov_len = sz};
- return readv_nonblock(fd, &iov, 1, rfds, num_bytes);
+ return readv_nonblock(fd, &iov, 1, num_bytes);
}
/**
* \param fd The file descriptor to receive from.
* \param pattern The expected pattern.
* \param bufsize The size of the internal buffer.
- * \param rfds Passed to read_nonblock().
*
* This function tries to read at most \a bufsize bytes from the non-blocking
* file descriptor \a fd. If at least \p strlen(\a pattern) bytes have been
*
* \sa \ref read_nonblock(), \sa strncasecmp(3).
*/
-int read_pattern(int fd, const char *pattern, size_t bufsize, fd_set *rfds)
+int read_pattern(int fd, const char *pattern, size_t bufsize)
{
size_t n, len;
- char *buf = para_malloc(bufsize + 1);
+ char *buf = alloc(bufsize + 1);
- int ret = read_nonblock(fd, buf, bufsize, rfds, &n);
+ int ret = read_nonblock(fd, buf, bufsize, &n);
buf[n] = '\0';
if (ret < 0)
return !stat(fn, &statbuf);
}
-/**
- * Paraslash's wrapper for select(2).
- *
- * It calls select(2) (with no exceptfds) and starts over if select() was
- * interrupted by a signal.
- *
- * \param n The highest-numbered descriptor in any of the two sets, plus 1.
- * \param readfds fds that should be checked for readability.
- * \param writefds fds that should be checked for writablility.
- * \param timeout_tv upper bound on the amount of time elapsed before select()
- * returns.
- *
- * \return The return value of the underlying select() call on success, the
- * negative system error code on errors.
- *
- * All arguments are passed verbatim to select(2).
- * \sa select(2) select_tut(2).
- */
-int para_select(int n, fd_set *readfds, fd_set *writefds,
- struct timeval *timeout_tv)
-{
- int ret;
- do
- ret = select(n, readfds, writefds, NULL, timeout_tv);
- while (ret < 0 && errno == EINTR);
- if (ret < 0)
- return -ERRNO_TO_PARA_ERROR(errno);
- return ret;
-}
-
/**
* Set a file descriptor to blocking mode.
*
return 1;
}
-/**
- * Set a file descriptor in a fd_set.
- *
- * \param fd The file descriptor to be set.
- * \param fds The file descriptor set.
- * \param max_fileno Highest-numbered file descriptor.
- *
- * This wrapper for FD_SET() passes its first two arguments to \p FD_SET. Upon
- * return, \a max_fileno contains the maximum of the old_value and \a fd.
- *
- * \sa \ref para_select.
-*/
-void para_fd_set(int fd, fd_set *fds, int *max_fileno)
-{
- assert(fd >= 0 && fd < FD_SETSIZE);
-#if 0
- {
- int flags = fcntl(fd, F_GETFL);
- if (!(flags & O_NONBLOCK)) {
- PARA_EMERG_LOG("fd %d is a blocking file descriptor\n", fd);
- exit(EXIT_FAILURE);
- }
- }
-#endif
- FD_SET(fd, fds);
- *max_fileno = PARA_MAX(*max_fileno, fd);
-}
-
/**
* Paraslash's wrapper for mmap.
*
return -ERRNO_TO_PARA_ERROR(err);
}
+/**
+ * Simple wrapper for poll(2).
+ *
+ * It calls poll(2) and starts over if the call was interrupted by a signal.
+ *
+ * \param fds See poll(2).
+ * \param nfds See poll(2).
+ * \param timeout See poll(2).
+ *
+ * \return The return value of the underlying poll() call on success, the
+ * negative paraslash error code on errors.
+ *
+ * All arguments are passed verbatim to poll(2).
+ */
+int xpoll(struct pollfd *fds, nfds_t nfds, int timeout)
+{
+ int ret;
+
+ do
+ ret = poll(fds, nfds, timeout);
+ while (ret < 0 && errno == EINTR);
+ return ret < 0? -ERRNO_TO_PARA_ERROR(errno) : ret;
+}
+
+/**
+ * Check a file descriptor for readability.
+ *
+ * \param fd The file descriptor.
+ *
+ * \return positive if fd is ready for reading, zero if it isn't, negative if
+ * an error occurred.
+ *
+ * \sa \ref write_ok().
+ */
+int read_ok(int fd)
+{
+ struct pollfd pfd = {.fd = fd, .events = POLLIN};
+ int ret = xpoll(&pfd, 1, 0);
+ return ret < 0? ret : pfd.revents & POLLIN;
+}
+
/**
* Check a file descriptor for writability.
*
*
* \return positive if fd is ready for writing, zero if it isn't, negative if
* an error occurred.
+ *
+ * \sa \ref read_ok().
*/
-
int write_ok(int fd)
{
- struct timeval tv;
- fd_set wfds;
-
- FD_ZERO(&wfds);
- FD_SET(fd, &wfds);
- tv.tv_sec = 0;
- tv.tv_usec = 0;
- return para_select(fd + 1, NULL, &wfds, &tv);
+ struct pollfd pfd = {.fd = fd, .events = POLLOUT};
+ int ret = xpoll(&pfd, 1, 0);
+ return ret < 0? ret : pfd.revents & POLLOUT;
}
/**
}
if (fg->num_slices == 0) {
fg->num_slices = fg->h.slices_per_group;
- fg->idx = para_malloc(fg->num_slices * sizeof(int));
- fg->data = para_calloc(fg->num_slices * sizeof(unsigned char *));
+ fg->idx = arr_alloc(fg->num_slices, sizeof(int));
+ fg->data = arr_zalloc(fg->num_slices, sizeof(unsigned char *));
}
r = fg->num_received_slices;
/* Check if we already have this slice. */
return 0;
}
fg->idx[r] = slice_num;
- fg->data[r] = para_malloc(fg->h.slice_bytes);
+ fg->data[r] = alloc(fg->h.slice_bytes);
memcpy(fg->data[r], buf, fg->h.slice_bytes);
fg->num_received_slices++;
return 1;
fn->private_data = NULL;
}
-static int fecdec_post_select(__a_unused struct sched *s, void *context)
+static int fecdec_post_monitor(__a_unused struct sched *s, void *context)
{
struct filter_node *fn = context;
struct btr_node *btrn = fn->btrn;
static void fecdec_open(struct filter_node *fn)
{
struct private_fecdec_data *pfd;
- pfd = para_calloc(sizeof(*pfd));
+ pfd = zalloc(sizeof(*pfd));
fn->private_data = pfd;
fn->min_iqs = FEC_HEADER_SIZE;
}
const struct filter lsg_filter_cmd_com_fecdec_user_data = {
.open = fecdec_open,
- .pre_select = generic_filter_pre_select,
- .post_select = fecdec_post_select,
+ .pre_monitor = generic_filter_pre_monitor,
+ .post_monitor = fecdec_post_monitor,
.close = fecdec_close,
};
close(fd);
return ret;
}
- pfwd = wn->private_data = para_calloc(sizeof(*pfwd));
+ pfwd = wn->private_data = zalloc(sizeof(*pfwd));
pfwd->fd = fd;
return 1;
}
-static void file_write_pre_select(struct sched *s, void *context)
+static void file_write_pre_monitor(struct sched *s, void *context)
{
struct writer_node *wn = context;
struct private_file_write_data *pfwd = wn->private_data;
return;
if (ret < 0 || !pfwd)
return sched_min_delay(s);
- para_fd_set(pfwd->fd, &s->wfds, &s->max_fileno);
+ sched_monitor_writefd(pfwd->fd, s);
}
static void file_write_close(struct writer_node *wn)
free(pfwd);
}
-static int file_write_post_select(__a_unused struct sched *s, void *context)
+static int file_write_post_monitor(__a_unused struct sched *s, void *context)
{
struct writer_node *wn = context;
struct private_file_write_data *pfwd = wn->private_data;
ret = prepare_output_file(wn);
goto out;
}
- if (!FD_ISSET(pfwd->fd, &s->wfds))
+ if (!sched_write_ok(pfwd->fd, s))
return 0;
bytes = btr_next_buffer(btrn, &buf);
assert(bytes > 0);
/** the init function of the file writer */
struct writer lsg_write_cmd_com_file_user_data = {
- .pre_select = file_write_pre_select,
- .post_select = file_write_post_select,
+ .pre_monitor = file_write_pre_monitor,
+ .post_monitor = file_write_post_monitor,
.close = file_write_close,
};
EMBRACE(.name = "stdin"));
stdin_task_register(sit, &s);
- fns = para_malloc(OPT_GIVEN(FILTER) * sizeof(*fns));
+ fns = arr_alloc(OPT_GIVEN(FILTER), sizeof(*fns));
for (i = 0, parent = sit->btrn; i < OPT_GIVEN(FILTER); i++) {
const char *fa = lls_string_val(i, OPT_RESULT(FILTER));
const char *name;
struct filter_node *fn;
struct task_info ti;
- fn = fns[i] = para_calloc(sizeof(*fn));
+ fn = fns[i] = zalloc(sizeof(*fn));
fn->filter_num = filter_setup(fa, &fn->conf, &filter_lpr);
name = filter_name(fn->filter_num);
fn->lpr = filter_lpr;
EMBRACE(.name = name, .parent = parent,
.handler = f->execute, .context = fn));
ti.name = name;
- ti.pre_select = f->pre_select;
- ti.post_select = f->post_select;
+ ti.pre_monitor = f->pre_monitor;
+ ti.post_monitor = f->post_monitor;
ti.context = fn;
if (f->open)
f->open(fn);
EMBRACE(.name = "stdout", .parent = parent));
stdout_task_register(sot, &s);
- s.default_timeout.tv_sec = 1;
- s.default_timeout.tv_usec = 0;
+ s.default_timeout = 1000;
btr_log_tree(sit->btrn, LL_INFO);
ret = schedule(&s);
sched_shutdown(&s);
struct btr_node *btrn = fn->btrn;
size_t k, n = frame->header.blocksize;
unsigned channels = FLAC__stream_decoder_get_channels(decoder);
- char *outbuffer = para_malloc(n * channels * 2);
+ char *outbuffer = arr_alloc(n, channels * 2);
if (channels == 1) {
for (k = 0; k < n; k++) {
return btr_get_output_queue_size(btrn) > FLACDEC_MAX_OUTPUT_SIZE;
}
-static void flacdec_pre_select(struct sched *s, void *context)
+static void flacdec_pre_monitor(struct sched *s, void *context)
{
struct filter_node *fn = context;
struct private_flacdec_data *pfd = fn->private_data;
return sched_min_delay(s);
}
-static int flacdec_post_select(__a_unused struct sched *s, void *context)
+static int flacdec_post_monitor(__a_unused struct sched *s, void *context)
{
struct filter_node *fn = context;
struct private_flacdec_data *pfd = fn->private_data;
static void flacdec_open(struct filter_node *fn)
{
- struct private_flacdec_data *pfd = para_calloc(sizeof(*pfd));
+ struct private_flacdec_data *pfd = zalloc(sizeof(*pfd));
fn->private_data = pfd;
fn->min_iqs = 0;
}
const struct filter lsg_filter_cmd_com_flacdec_user_data = {
.open = flacdec_open,
.close = flacdec_close,
- .pre_select = flacdec_pre_select,
- .post_select = flacdec_post_select,
+ .pre_monitor = flacdec_pre_monitor,
+ .post_monitor = flacdec_post_monitor,
.execute = flacdec_execute,
};
return -E_GC_WRITE;
}
-static void gc_pre_select(struct sched *s, void *context)
+static void gc_pre_monitor(struct sched *s, void *context)
{
struct grab_client *gc = context;
int ret = btr_node_status(gc->btrn, 0, BTR_NT_LEAF);
return;
if (ret < 0)
sched_min_delay(s);
- para_fd_set(gc->fd, &s->wfds, &s->max_fileno);
+ sched_monitor_writefd(gc->fd, s);
}
/*
- * We need this forward declaration as post_select() needs
+ * We need this forward declaration as gc_post_monitor() needs
* activate_grab_client and vice versa.
*/
-static int gc_post_select(struct sched *s, void *context);
+static int gc_post_monitor(struct sched *s, void *context);
/**
* Move a grab client to the active list and start it.
gc->task = task_register(&(struct task_info) {
.name = name,
- .pre_select = gc_pre_select,
- .post_select = gc_post_select,
+ .pre_monitor = gc_pre_monitor,
+ .post_monitor = gc_post_monitor,
.context = gc,
}, s);
}
/*
* We must not free the gc structure here as it contains ->task
* which is still used because this function is called from
- * post_select().
+ * post_monitor().
*/
close(gc->fd);
gc->fd = -1;
return 0;
}
-static int gc_post_select(__a_unused struct sched *s, void *context)
+static int gc_post_monitor(__a_unused struct sched *s, void *context)
{
struct grab_client *gc = context;
struct btr_node *btrn = gc->btrn;
int grab_client_new(int fd, struct lls_parse_result *lpr, struct sched *s)
{
int ret;
- struct grab_client *gc = para_calloc(sizeof(struct grab_client));
+ struct grab_client *gc = zalloc(sizeof(struct grab_client));
ret = gc_check_args(lpr, gc);
if (ret < 0)
if (strwidth(msg, &len) < 0)
return;
- new = para_malloc(sizeof(struct rb_entry));
+ new = alloc(sizeof(struct rb_entry));
new->color = color;
new->len = len;
new->msg = msg;
}
}
-static void status_pre_select(struct sched *s, void *context)
+static void status_pre_monitor(struct sched *s, void *context)
{
struct status_task *st = context;
if (st->fd >= 0)
- para_fd_set(st->fd, &s->rfds, &s->max_fileno);
+ sched_monitor_readfd(st->fd, s);
if (task_get_notification(st->task) < 0)
return sched_min_delay(s);
if (st->fd < 0)
sched_request_barrier_or_min_delay(&st->next_exec, s);
}
-static int status_post_select(struct sched *s, void *context)
+static int status_post_monitor(__a_unused struct sched *s, void *context)
{
struct status_task *st = context;
size_t sz;
}
assert(st->loaded < st->bufsize);
ret = read_nonblock(st->fd, st->buf + st->loaded,
- st->bufsize - st->loaded, &s->rfds, &sz);
+ st->bufsize - st->loaded, &sz);
st->loaded += sz;
ret2 = for_each_stat_item(st->buf, st->loaded, update_item);
if (ret < 0 || ret2 < 0) {
}
/* React to various signal-related events. */
-static int signal_post_select(struct sched *s, __a_unused void *context)
+static int signal_post_monitor(struct sched *s, __a_unused void *context)
{
- int ret = para_next_signal(&s->rfds);
+ int ret = para_next_signal();
if (ret <= 0)
return 0;
return EXEC_IDLE;
}
-static void exec_pre_select(struct sched *s, void *context)
+static void exec_pre_monitor(struct sched *s, void *context)
{
struct exec_task *et = context;
if (exec_fds[0] >= 0)
- para_fd_set(exec_fds[0], &s->rfds, &s->max_fileno);
+ sched_monitor_readfd(exec_fds[0], s);
if (exec_fds[1] >= 0)
- para_fd_set(exec_fds[1], &s->rfds, &s->max_fileno);
+ sched_monitor_readfd(exec_fds[1], s);
if (task_get_notification(et->task) < 0)
sched_min_delay(s);
}
-static int exec_post_select(struct sched *s, void *context)
+static int exec_post_monitor(__a_unused struct sched *s, void *context)
{
struct exec_task *ct = context;
int i, ret;
continue;
ret = read_nonblock(exec_fds[i],
ct->command_buf[i] + ct->cbo[i],
- COMMAND_BUF_SIZE - 1 - ct->cbo[i], &s->rfds, &sz);
+ COMMAND_BUF_SIZE - 1 - ct->cbo[i], &sz);
ct->cbo[i] += sz;
sz = ct->cbo[i];
ct->cbo[i] = for_each_line(ct->flags[i], ct->command_buf[i],
return 0;
}
-static void input_pre_select(struct sched *s, __a_unused void *context)
+static void input_pre_monitor(struct sched *s, __a_unused void *context)
{
if (exec_status() != EXEC_XCMD)
- para_fd_set(STDIN_FILENO, &s->rfds, &s->max_fileno);
+ sched_monitor_readfd(STDIN_FILENO, s);
if (window_update_needed())
sched_min_delay(s);
}
keyname);
}
-static int input_post_select(__a_unused struct sched *s,
+static int input_post_monitor(__a_unused struct sched *s,
__a_unused void *context)
{
int ret;
ret = wgetch(top.win);
if (ret == ERR)
return 0;
- if (ret == KEY_RESIZE) /* already handled in signal_post_select() */
+ if (ret == KEY_RESIZE) /* already handled in signal_post_monitor() */
return 0;
if (exs == EXEC_IDLE)
handle_command(ret);
struct status_task status_task = {.fd = -1};
struct input_task input_task = {.task = NULL};
struct signal_task *signal_task;
- struct sched sched = {.default_timeout = {.tv_sec = 1}};
+ struct sched sched = {.default_timeout = 1000};
exec_task.task = task_register(&(struct task_info) {
.name = "exec",
- .pre_select = exec_pre_select,
- .post_select = exec_post_select,
+ .pre_monitor = exec_pre_monitor,
+ .post_monitor = exec_post_monitor,
.context = &exec_task,
}, &sched);
status_task.task = task_register(&(struct task_info) {
.name = "status",
- .pre_select = status_pre_select,
- .post_select = status_post_select,
+ .pre_monitor = status_pre_monitor,
+ .post_monitor = status_post_monitor,
.context = &status_task,
}, &sched);
input_task.task = task_register(&(struct task_info) {
.name = "input",
- .pre_select = input_pre_select,
- .post_select = input_post_select,
+ .pre_monitor = input_pre_monitor,
+ .post_monitor = input_post_monitor,
.context = &input_task,
}, &sched);
para_install_sighandler(SIGWINCH);
signal_task->task = task_register(&(struct task_info) {
.name = "signal",
- .pre_select = signal_pre_select,
- .post_select = signal_post_select,
+ .pre_monitor = signal_pre_monitor,
+ .post_monitor = signal_post_monitor,
.context = signal_task,
}, &sched);
ret = schedule(&sched);
return ret;
}
-static void http_recv_pre_select(struct sched *s, void *context)
+static void http_recv_pre_monitor(struct sched *s, void *context)
{
struct receiver_node *rn = context;
struct private_http_recv_data *phd = rn->private_data;
- if (generic_recv_pre_select(s, rn) <= 0)
+ if (generic_recv_pre_monitor(s, rn) <= 0)
return;
if (phd->status == HTTP_CONNECTED)
- para_fd_set(rn->fd, &s->wfds, &s->max_fileno);
+ sched_monitor_writefd(rn->fd, s);
else
- para_fd_set(rn->fd, &s->rfds, &s->max_fileno);
+ sched_monitor_readfd(rn->fd, s);
}
/*
* area with data read from the socket. In any case, update the state of the
* connection if necessary.
*/
-static int http_recv_post_select(struct sched *s, void *context)
+static int http_recv_post_monitor(struct sched *s, void *context)
{
struct receiver_node *rn = context;
struct private_http_recv_data *phd = rn->private_data;
return 0;
if (phd->status == HTTP_CONNECTED) {
char *rq;
- if (!FD_ISSET(rn->fd, &s->wfds))
+ if (!sched_write_ok(rn->fd, s))
return 0;
rq = make_request_msg();
PARA_INFO_LOG("sending http request\n");
return 0;
}
if (phd->status == HTTP_SENT_GET_REQUEST) {
- ret = read_pattern(rn->fd, HTTP_OK_MSG, strlen(HTTP_OK_MSG), &s->rfds);
+ ret = read_pattern(rn->fd, HTTP_OK_MSG, strlen(HTTP_OK_MSG));
if (ret < 0) {
PARA_ERROR_LOG("did not receive HTTP OK message\n");
goto out;
iovcnt = btr_pool_get_buffers(rn->btrp, iov);
if (iovcnt == 0)
goto out;
- ret = readv_nonblock(rn->fd, iov, iovcnt, &s->rfds, &num_bytes);
+ ret = readv_nonblock(rn->fd, iov, iovcnt, &num_bytes);
if (num_bytes == 0)
goto out;
if (num_bytes <= iov[0].iov_len) /* only the first buffer was filled */
close(fd);
return ret;
}
- rn->private_data = phd = para_calloc(sizeof(struct private_http_recv_data));
+ rn->private_data = phd = zalloc(sizeof(struct private_http_recv_data));
rn->fd = fd;
phd->status = HTTP_CONNECTED;
rn->btrp = btr_pool_new("http_recv", 320 * 1024);
const struct receiver lsg_recv_cmd_com_http_user_data = {
.open = http_recv_open,
.close = http_recv_close,
- .pre_select = http_recv_pre_select,
- .post_select = http_recv_post_select,
+ .pre_monitor = http_recv_pre_monitor,
+ .post_monitor = http_recv_post_monitor,
};
#include "server.h"
#include "http.h"
#include "list.h"
-#include "send.h"
#include "sched.h"
+#include "send.h"
#include "vss.h"
#include "close_on_fork.h"
#include "fd.h"
}
}
-static void http_post_select(fd_set *rfds, __a_unused fd_set *wfds)
+static void http_post_monitor(__a_unused struct sched *s)
{
struct sender_client *sc, *tmp;
struct private_http_sender_data *phsd;
case HTTP_STREAMING: /* nothing to do */
break;
case HTTP_CONNECTED: /* need to recv get request */
- ret = read_pattern(sc->fd, HTTP_GET_MSG, MAXLINE, rfds);
+ ret = read_pattern(sc->fd, HTTP_GET_MSG, MAXLINE);
if (ret < 0)
phsd->status = HTTP_INVALID_GET_REQUEST;
else if (ret > 0) {
break;
}
}
- sc = accept_sender_client(hss, rfds);
+ sc = accept_sender_client(hss);
if (!sc)
return;
- phsd = para_malloc(sizeof(*phsd));
+ phsd = alloc(sizeof(*phsd));
sc->private_data = phsd;
phsd->status = HTTP_CONNECTED;
}
-static void http_pre_select(int *max_fileno, fd_set *rfds, fd_set *wfds)
+static void http_pre_monitor(struct sched *s)
{
struct sender_client *sc, *tmp;
unsigned n;
FOR_EACH_LISTEN_FD(n, hss) {
if (hss->listen_fds[n] < 0)
continue;
- para_fd_set(hss->listen_fds[n], rfds, max_fileno);
+ sched_monitor_readfd(hss->listen_fds[n], s);
}
list_for_each_entry_safe(sc, tmp, &hss->client_list, node) {
struct private_http_sender_data *phsd = sc->private_data;
if (phsd->status == HTTP_CONNECTED) /* need to recv get request */
- para_fd_set(sc->fd, rfds, max_fileno);
+ sched_monitor_readfd(sc->fd, s);
if (phsd->status == HTTP_GOT_GET_REQUEST ||
phsd->status == HTTP_INVALID_GET_REQUEST)
- para_fd_set(sc->fd, wfds, max_fileno);
+ sched_monitor_writefd(sc->fd, s);
}
}
.name = "http",
.init = http_send_init,
.shutdown = http_shutdown,
- .pre_select = http_pre_select,
- .post_select = http_post_select,
+ .pre_monitor = http_pre_monitor,
+ .post_monitor = http_post_monitor,
.send = http_send,
.shutdown_clients = http_shutdown_clients,
.client_cmds = {
*
* This function attaches the i9e input queue to an output queue of \a
* producer.
- *
- * \return Standard.
*/
void i9e_attach_to_stdout(struct btr_node *producer)
{
rl_point = point;
}
-static bool input_available(void)
-{
- fd_set rfds;
- struct timeval tv = {0, 0};
- int ret;
-
- FD_ZERO(&rfds);
- FD_SET(i9ep->ici->fds[0], &rfds);
- ret = para_select(1, &rfds, NULL, &tv);
- return ret > 0;
-}
-
static void i9e_line_handler(char *line)
{
int ret;
free(line);
}
-static int i9e_post_select(__a_unused struct sched *s, __a_unused void *context)
+static int i9e_post_monitor(__a_unused struct sched *s, __a_unused void *context)
{
int ret;
struct i9e_client_info *ici = i9ep->ici;
ret = 0;
if (i9ep->caught_sigint)
goto rm_btrn;
- while (input_available()) {
+ while (read_ok(i9ep->ici->fds[0]) > 0) {
if (i9ep->stdout_btrn) {
- unsigned len = i9ep->key_sequence_length;
- assert(len < sizeof(i9ep->key_sequence) - 1);
- buf = i9ep->key_sequence + len;
- ret = read(i9ep->ici->fds[0], buf, 1);
- if (ret < 0) {
- ret = -ERRNO_TO_PARA_ERROR(errno);
- goto rm_btrn;
+ while (i9ep->key_sequence_length < sizeof(i9ep->key_sequence) - 1) {
+ buf = i9ep->key_sequence + i9ep->key_sequence_length;
+ ret = read(i9ep->ici->fds[0], buf, 1);
+ if (ret < 0) {
+ ret = -ERRNO_TO_PARA_ERROR(errno);
+ goto rm_btrn;
+ }
+ if (ret == 0) {
+ ret = -E_I9E_EOF;
+ goto rm_btrn;
+ }
+ buf[1] = '\0';
+ i9ep->key_sequence_length++;
+ rl_stuff_char((int)(unsigned char)*buf);
+ rl_callback_read_char();
+ if (read_ok(i9ep->ici->fds[0]) <= 0)
+ break;
}
- ret = -E_I9E_EOF;
- if (ret == 0)
- goto rm_btrn;
- buf[1] = '\0';
- i9ep->key_sequence_length++;
- rl_stuff_char((int)(unsigned char)*buf);
- }
- rl_callback_read_char();
+ i9ep->key_sequence_length = 0;
+ } else
+ rl_callback_read_char();
ret = 0;
}
if (!i9ep->stdout_btrn)
return ret;
}
-static void i9e_pre_select(struct sched *s, __a_unused void *context)
+static void i9e_pre_monitor(struct sched *s, __a_unused void *context)
{
int ret;
return;
}
if (ret > 0)
- para_fd_set(i9ep->ici->fds[1], &s->wfds, &s->max_fileno);
+ sched_monitor_writefd(i9ep->ici->fds[1], s);
}
/*
* fd[0] might have been reset to blocking mode if our job was moved to
if (ret < 0)
PARA_WARNING_LOG("set to nonblock failed: (fd0 %d, %s)\n",
i9ep->ici->fds[0], para_strerror(-ret));
- para_fd_set(i9ep->ici->fds[0], &s->rfds, &s->max_fileno);
+ sched_monitor_readfd(i9ep->ici->fds[0], s);
}
static void update_winsize(void)
return ret;
i9ep->task = task_register(&(struct task_info) {
.name = "i9e",
- .pre_select = i9e_pre_select,
- .post_select = i9e_post_select,
+ .pre_monitor = i9e_pre_monitor,
+ .post_monitor = i9e_post_monitor,
.context = i9ep,
}, s);
}
/**
- * Wrapper for select(2) which does not restart on interrupts.
+ * Wrapper for poll(2) which handles EINTR and returns paraslash error codes.
*
- * \param n \sa \ref para_select().
- * \param readfds \sa \ref para_select().
- * \param writefds \sa \ref para_select().
- * \param timeout_tv \sa \ref para_select().
+ * \param fds See poll(2).
+ * \param nfds See poll(2).
+ * \param timeout See poll(2).
*
- * \return \sa \ref para_select().
+ * \return See poll(2).
*
- * The only difference between this function and \ref para_select() is that
- * \ref i9e_select() returns zero if the select call returned \p EINTR.
+ * The only difference between this function and \ref xpoll() is that \ref
+ * i9e_poll() returns zero if the system call was interrupted while xpoll()
+ * restarts the system call in this case.
*/
-int i9e_select(int n, fd_set *readfds, fd_set *writefds,
- struct timeval *timeout_tv)
+int i9e_poll(struct pollfd *fds, nfds_t nfds, int timeout)
{
- int ret = select(n, readfds, writefds, NULL, timeout_tv);
-
+ int ret = poll(fds, nfds, timeout);
if (ret < 0) {
if (errno == EINTR)
ret = 0;
int i9e_extract_completions(const char *word, char **string_list,
char ***result)
{
- char **matches = para_malloc(sizeof(char *));
+ char **matches = alloc(sizeof(char *));
int match_count = 0, matches_len = 1;
char **p;
int len = strlen(word);
match_count++;
if (match_count >= matches_len) {
matches_len *= 2;
- matches = para_realloc(matches,
- matches_len * sizeof(char *));
+ matches = arr_realloc(matches, matches_len,
+ sizeof(char *));
}
matches[match_count - 1] = para_strdup(*p);
}
if (is_prefix(word, cmd, len))
match_count++;
}
- matches = para_malloc((match_count + 1) * sizeof(*matches));
+ matches = arr_alloc(match_count + 1, sizeof(*matches));
for (i = 0, match_count = 0; (cmd = completers[i].name); i++)
if (is_prefix(word, cmd, len))
matches[match_count++] = para_strdup(cmd);
if (*p == ' ')
p++;
n = end - p + 1;
- ci.word = para_malloc(n + 1);
+ ci.word = alloc(n + 1);
strncpy(ci.word, p, n);
ci.word[n] = '\0';
create_matches:
free(ci.word);
return ret;
}
+
+/**
+ * Complete on severity strings.
+ *
+ * \param ci See struct \ref i9e_completer.
+ * \param cr See struct \ref i9e_completer.
+ *
+ * This is used by para_client and para_audioc which need the same completion
+ * primitive for the ll server/audiod command. Both define their own completer
+ * which is implemented as a trivial wrapper that calls this function.
+ */
+void i9e_ll_completer(struct i9e_completion_info *ci,
+ struct i9e_completion_result *cr)
+{
+ char *sev[] = {SEVERITIES, NULL};
+
+ if (ci->word_num != 1) {
+ cr->matches = NULL;
+ return;
+ }
+ i9e_extract_completions(ci->word, sev, &cr->matches);
+}
#define MP3DEC_MAX_FRAME 8192
-static int mp3dec_post_select(__a_unused struct sched *s, void *context)
+static int mp3dec_post_monitor(__a_unused struct sched *s, void *context)
{
struct filter_node *fn = context;
int i, ret;
btr_merge(btrn, fn->min_iqs);
len = btr_next_buffer(btrn, &inbuffer);
/*
- * Decode at most 8K in one go to give the post_select() functions of
+ * Decode at most 8K in one go to give the post_monitor() functions of
* other buffer tree nodes a chance to run. This is necessary to avoid
* buffer underruns on slow machines.
*/
}
fn->min_iqs = 0;
mad_synth_frame(&pmd->synth, &pmd->frame);
- outbuffer = para_malloc(pmd->synth.pcm.length * 2 * pmd->channels);
+ outbuffer = arr_alloc(pmd->synth.pcm.length, 2 * pmd->channels);
loaded = 0;
for (i = 0; i < pmd->synth.pcm.length; i++) {
int sample = MAD_TO_SHORT(pmd->synth.pcm.samples[0][i]);
static void mp3dec_open(struct filter_node *fn)
{
- struct private_mp3dec_data *pmd = para_calloc(sizeof(*pmd));
+ struct private_mp3dec_data *pmd = zalloc(sizeof(*pmd));
fn->private_data = pmd;
mad_stream_init(&pmd->stream);
const struct filter lsg_filter_cmd_com_mp3dec_user_data = {
.open = mp3dec_open,
.close = mp3dec_close,
- .pre_select = generic_filter_pre_select,
- .post_select = mp3dec_post_select,
+ .pre_monitor = generic_filter_pre_monitor,
+ .post_monitor = mp3dec_post_monitor,
.execute = mp3dec_execute,
};
--- /dev/null
- t->stsz_table = para_malloc(t->stsz_sample_count * sizeof(int32_t));
+/*
+ * Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com
+ * FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding
+ *
+ * See file COPYING.
+ */
+
+/** \file mp4.c Paraslash's internal mp4 parser. */
+
+/*
+ * This is a stripped down version of the former mp4ff library which used to be
+ * part of the faad decoder project but was removed from the faad code base in
+ * 2017. The original code has been cleaned up substantially and the public API
+ * has been documented. See the git commit log for details.
+ */
+
+#include <regex.h>
+
+#include "para.h"
+#include "error.h"
+#include "portable_io.h"
+#include "string.h"
+#include "mp4.h"
+
+/**
+ * The three states of the mp4 parser. The parser only loads the audio specific
+ * values and tables when it is in the second state.
+ */
+enum audio_track_state {
+ /** We haven't encountered an mp4a atom so far. */
+ ATS_INITIAL,
+ /** We have seen an mp4a atom but no subsequent trak atom yet. */
+ ATS_SEEN_MP4A,
+ /** A trak atom was seen *after* the mp4a atom. */
+ ATS_TRACK_CHANGE,
+};
+
+struct mp4_track {
+ /* determines which atoms we still need to parse. */
+ enum audio_track_state state;
+
+ /* mp4a */
+ uint16_t channel_count;
+ uint16_t sample_rate;
+
+ /* stsz */
+ uint32_t stsz_sample_size;
+ uint32_t stsz_sample_count;
+ uint32_t *stsz_table;
+
+ /* stts */
+ uint32_t stts_entry_count;
+ uint32_t *stts_sample_count;
+
+ /* stsc */
+ uint32_t stsc_entry_count;
+ uint32_t *stsc_first_chunk;
+ uint32_t *stsc_samples_per_chunk;
+
+ /* stsc */
+ uint32_t stco_entry_count;
+ uint32_t *stco_chunk_offset;
+
+ /* mdhd */
+ uint32_t time_scale;
+ uint64_t duration;
+};
+
+struct mp4 {
+ const struct mp4_callback *cb;
+
+ uint64_t moov_offset;
+ uint64_t moov_size;
+ uint64_t meta_offset;
+ uint32_t meta_size;
+ uint64_t ilst_offset;
+ uint32_t ilst_size;
+ uint64_t udta_offset;
+ uint32_t udta_size;
+
+ uint8_t last_atom;
+ struct mp4_track track;
+ struct mp4_metadata meta;
+};
+
+/*
+ * Returns -E_MP4_READ, 0, or 1 on errors/EOF/success. Partial reads followed
+ * by EOF or read errors are treated as errors.
+ */
+static int read_data(struct mp4 *f, void *data, size_t size)
+{
+ while (size > 0) {
+ ssize_t ret = f->cb->read(f->cb->user_data, data, size);
+ if (ret < 0 && errno == EINTR)
+ continue;
+ /* regard EAGAIN as an error as reads should be blocking. */
+ if (ret <= 0)
+ return ret < 0? -E_MP4_READ : 0;
+ size -= ret;
+ }
+ return 1;
+}
+
+static int read_int64(struct mp4 *f, uint64_t *result)
+{
+ uint8_t data[8];
+ int ret = read_data(f, data, 8);
+
+ if (ret > 0)
+ *result = read_u64_be(data);
+ return ret;
+}
+
+static int read_int32(struct mp4 *f, uint32_t *result)
+{
+ uint8_t data[4];
+ int ret = read_data(f, data, 4);
+
+ if (ret > 0)
+ *result = read_u32_be(data);
+ return ret;
+}
+
+static int read_int16(struct mp4 *f, uint16_t *result)
+{
+ uint8_t data[2];
+ int ret = read_data(f, data, 2);
+
+ if (ret > 0)
+ *result = read_u16_be(data);
+ return ret;
+}
+
+/** A macro defining the atoms we care about. It gets expanded twice. */
+#define ATOM_ITEMS \
+ ATOM_ITEM(MOOV, 'm', 'o', 'o', 'v') /* movie (top-level container) */ \
+ ATOM_ITEM(TRAK, 't', 'r', 'a', 'k') /* container for a single track */ \
+ ATOM_ITEM(MDIA, 'm', 'd', 'i', 'a') /* media information */ \
+ ATOM_ITEM(MINF, 'm', 'i', 'n', 'f') /* extends mdia */ \
+ ATOM_ITEM(STBL, 's', 't', 'b', 'l') /* sample table container */ \
+ ATOM_ITEM(UDTA, 'u', 'd', 't', 'a') /* user data */ \
+ ATOM_ITEM(ILST, 'i', 'l', 's', 't') /* iTunes Metadata list */ \
+ ATOM_ITEM(ARTIST, 0xa9, 'A', 'R', 'T') /* artist */ \
+ ATOM_ITEM(TITLE, 0xa9, 'n', 'a', 'm') /* title */ \
+ ATOM_ITEM(ALBUM, 0xa9, 'a', 'l', 'b') /* album */ \
+ ATOM_ITEM(DATE, 0xa9, 'd', 'a', 'y') /* date */ \
+ ATOM_ITEM(COMMENT, 0xa9, 'c', 'm', 't') /* comment */ \
+ ATOM_ITEM(MDHD, 'm', 'd', 'h', 'd') /* track header */ \
+ ATOM_ITEM(STSD, 's', 't', 's', 'd') /* sample description box */ \
+ ATOM_ITEM(STTS, 's', 't', 't', 's') /* time to sample box */ \
+ ATOM_ITEM(STSZ, 's', 't', 's', 'z') /* sample size box */ \
+ ATOM_ITEM(STCO, 's', 't', 'c', 'o') /* chunk offset box */ \
+ ATOM_ITEM(STSC, 's', 't', 's', 'c') /* sample to chunk box */ \
+ ATOM_ITEM(MP4A, 'm', 'p', '4', 'a') /* mp4 audio */ \
+ ATOM_ITEM(META, 'm', 'e', 't', 'a') /* iTunes Metadata box */ \
+ ATOM_ITEM(DATA, 'd', 'a', 't', 'a') /* iTunes Metadata data box */ \
+
+/** For the C enumeration we concatenate ATOM_ with the first argument. */
+#define ATOM_ITEM(_name, a, b, c, d) ATOM_ ## _name,
+/** The enumeration of interesting atoms. */
+enum atom {ATOM_ITEMS};
+#undef ATOM_ITEM
+
+/** A cpp version of read_u32_be(). */
+#define ATOM_VALUE(a, b, c, d) ((a << 24) + (b << 16) + (c << 8) + d)
+
+static uint8_t atom_name_to_type(uint8_t *p)
+{
+ /** Expands to an instance of the following unnamed structure. */
+ #define ATOM_ITEM(_name, a, b, c, d) \
+ {.name = # _name, .val = ATOM_VALUE(a, b, c, d)},
+ static const struct {
+ const char *name;
+ uint32_t val;
+ } atom_table[] = {ATOM_ITEMS};
+ #undef ATOM_ITEM
+ uint32_t val = read_u32_be(p);
+
+ for (uint8_t n = 0; n < ARRAY_SIZE(atom_table); n++)
+ if (val == atom_table[n].val)
+ return n;
+ return 255;
+}
+
+/* read atom header, atom size is returned with header included. */
+static int atom_read_header(struct mp4 *f, uint8_t *atom_type,
+ uint8_t *header_size, uint64_t *atom_size)
+{
+ uint32_t size;
+ int ret;
+ uint8_t atom_header[8];
+
+ ret = read_data(f, atom_header, 8);
+ if (ret <= 0)
+ return ret;
+ size = read_u32_be(atom_header);
+ if (size == 1) { /* 64 bit atom size */
+ if (header_size)
+ *header_size = 16;
+ ret = read_int64(f, atom_size);
+ if (ret <= 0)
+ return ret;
+ } else {
+ if (header_size)
+ *header_size = 8;
+ *atom_size = size;
+ }
+ *atom_type = atom_name_to_type(atom_header + 4);
+ return 1;
+}
+
+static off_t get_position(const struct mp4 *f)
+{
+ return f->cb->seek(f->cb->user_data, 0, SEEK_CUR);
+}
+
+static void set_position(struct mp4 *f, off_t position)
+{
+ f->cb->seek(f->cb->user_data, position, SEEK_SET);
+}
+
+static void skip_bytes(struct mp4 *f, off_t num_skip)
+{
+ f->cb->seek(f->cb->user_data, num_skip, SEEK_CUR);
+}
+
+static int read_stsz(struct mp4 *f)
+{
+ int ret;
+ struct mp4_track *t = &f->track;
+
+ if (t->state != ATS_SEEN_MP4A || t->stsz_table)
+ return 1;
+ skip_bytes(f, 4); /* version (1), flags (3) */
+ ret = read_int32(f, &t->stsz_sample_size);
+ if (ret <= 0)
+ return ret;
+ ret = read_int32(f, &t->stsz_sample_count);
+ if (ret <= 0)
+ return ret;
+ if (t->stsz_sample_size != 0)
+ return 1;
- t->stts_sample_count = para_malloc(t->stts_entry_count
- * sizeof(int32_t));
++ t->stsz_table = arr_alloc(t->stsz_sample_count, sizeof(int32_t));
+ for (uint32_t n = 0; n < t->stsz_sample_count; n++) {
+ ret = read_int32(f, &t->stsz_table[n]);
+ if (ret <= 0)
+ return ret;
+ }
+ return 1;
+}
+
+static int read_stts(struct mp4 *f)
+{
+ int ret;
+ struct mp4_track *t = &f->track;
+
+ if (t->state != ATS_SEEN_MP4A || t->stts_sample_count)
+ return 1;
+ skip_bytes(f, 4); /* version (1), flags (3) */
+ ret = read_int32(f, &t->stts_entry_count);
+ if (ret <= 0)
+ return ret;
- t->stsc_first_chunk = para_malloc(t->stsc_entry_count * sizeof(int32_t));
- t->stsc_samples_per_chunk = para_malloc(t->stsc_entry_count
- * sizeof (int32_t));
++ t->stts_sample_count = arr_alloc(t->stts_entry_count, sizeof(int32_t));
+ for (uint32_t n = 0; n < t->stts_entry_count; n++) {
+ ret = read_int32(f, &t->stts_sample_count[n]);
+ if (ret <= 0)
+ return ret;
+ skip_bytes(f, 4); /* sample delta */
+ }
+ return 1;
+}
+
+static int read_stsc(struct mp4 *f)
+{
+ int ret;
+ struct mp4_track *t = &f->track;
+
+ if (t->state != ATS_SEEN_MP4A)
+ return 1;
+ if (t->stsc_first_chunk || t->stsc_samples_per_chunk)
+ return 1;
+ skip_bytes(f, 4); /* version (1), flags (3) */
+ ret = read_int32(f, &t->stsc_entry_count);
+ if (ret <= 0)
+ return ret;
- t->stco_chunk_offset = para_malloc(t->stco_entry_count
- * sizeof(int32_t));
++ t->stsc_first_chunk = arr_alloc(t->stsc_entry_count, sizeof(int32_t));
++ t->stsc_samples_per_chunk = arr_alloc(t->stsc_entry_count,
++ sizeof (int32_t));
+ for (uint32_t n = 0; n < t->stsc_entry_count; n++) {
+ ret = read_int32(f, &t->stsc_first_chunk[n]);
+ if (ret <= 0)
+ return ret;
+ ret = read_int32(f, &t->stsc_samples_per_chunk[n]);
+ if (ret <= 0)
+ return ret;
+ skip_bytes(f, 4); /* sample desc index */
+ }
+ return 1;
+}
+
+static int read_stco(struct mp4 *f)
+{
+ int ret;
+ struct mp4_track *t = &f->track;
+
+ if (t->state != ATS_SEEN_MP4A || t->stco_chunk_offset)
+ return 1;
+ skip_bytes(f, 4); /* version (1), flags (3) */
+ ret = read_int32(f, &t->stco_entry_count);
+ if (ret <= 0)
+ return ret;
- value = para_malloc(len + 1);
++ t->stco_chunk_offset = arr_alloc(t->stco_entry_count, sizeof(int32_t));
+ for (uint32_t n = 0; n < t->stco_entry_count; n++) {
+ ret = read_int32(f, &t->stco_chunk_offset[n]);
+ if (ret <= 0)
+ return ret;
+ }
+ return 1;
+}
+
+static int read_stsd(struct mp4 *f)
+{
+ int ret;
+ uint32_t entry_count;
+
+ if (f->track.state != ATS_INITIAL)
+ return 1;
+ skip_bytes(f, 4); /* version (1), flags (3) */
+ ret = read_int32(f, &entry_count);
+ if (ret <= 0)
+ return ret;
+ for (uint32_t n = 0; n < entry_count; n++) {
+ uint64_t skip = get_position(f);
+ uint64_t size;
+ uint8_t atom_type = 0;
+ ret = atom_read_header(f, &atom_type, NULL, &size);
+ if (ret <= 0)
+ return ret;
+ skip += size;
+ if (atom_type == ATOM_MP4A) {
+ f->track.state = ATS_SEEN_MP4A;
+ /* reserved (6), data reference index (2), reserved (8) */
+ skip_bytes(f, 16);
+ ret = read_int16(f, &f->track.channel_count);
+ if (ret <= 0)
+ return ret;
+ skip_bytes(f, 6);
+ ret = read_int16(f, &f->track.sample_rate);
+ if (ret <= 0)
+ return ret;
+ }
+ set_position(f, skip);
+ }
+ return 1;
+}
+
+static const char *get_metadata_name(uint8_t atom_type)
+{
+ switch (atom_type) {
+ case ATOM_TITLE: return "title";
+ case ATOM_ARTIST: return "artist";
+ case ATOM_ALBUM: return "album";
+ case ATOM_DATE: return "date";
+ case ATOM_COMMENT: return "comment";
+ default: return "unknown";
+ }
+}
+
+static int parse_tag(struct mp4 *f, uint8_t parent, int32_t size)
+{
+ int ret;
+ uint64_t subsize, sumsize;
+ char *value = NULL;
+ uint32_t len = 0;
+ uint64_t destpos;
+ struct mp4_tag *tag;
+
+ for (
+ sumsize = 0;
+ sumsize < size;
+ set_position(f, destpos), sumsize += subsize
+ ) {
+ uint8_t atom_type;
+ uint8_t header_size = 0;
+ ret = atom_read_header(f, &atom_type, &header_size, &subsize);
+ if (ret <= 0)
+ goto fail;
+ destpos = get_position(f) + subsize - header_size;
+ if (atom_type != ATOM_DATA)
+ continue;
+ skip_bytes(f, 8); /* version (1), flags (3), reserved (4) */
+ ret = -E_MP4_CORRUPT;
+ if (subsize < header_size + 8 || subsize > UINT_MAX)
+ goto fail;
+ len = subsize - (header_size + 8);
+ free(value);
- struct mp4 *f = para_calloc(sizeof(*f));
++ value = alloc(len + 1);
+ ret = read_data(f, value, len);
+ if (ret <= 0)
+ goto fail;
+ value[len] = '\0';
+ }
+ if (!value)
+ return -E_MP4_CORRUPT;
+ f->meta.tags = para_realloc(f->meta.tags, (f->meta.count + 1)
+ * sizeof(struct mp4_tag));
+ tag = f->meta.tags + f->meta.count;
+ tag->item = para_strdup(get_metadata_name(parent));
+ tag->value = value;
+ f->meta.count++;
+ return 1;
+fail:
+ free(value);
+ return ret;
+}
+
+static int read_mdhd(struct mp4 *f)
+{
+ int ret;
+ uint32_t version;
+ struct mp4_track *t = &f->track;
+
+ if (t->state != ATS_INITIAL)
+ return 1;
+ ret = read_int32(f, &version);
+ if (ret <= 0)
+ return ret;
+ if (version == 1) {
+ skip_bytes(f, 16); /* creation time (8), modification time (8) */
+ ret = read_int32(f, &t->time_scale);
+ if (ret <= 0)
+ return ret;
+ ret = read_int64(f, &t->duration);
+ if (ret <= 0)
+ return ret;
+ } else { /* version == 0 */
+ uint32_t temp;
+
+ skip_bytes(f, 8); /* creation time (4), modification time (4) */
+ ret = read_int32(f, &t->time_scale);
+ if (ret <= 0)
+ return ret;
+ ret = read_int32(f, &temp);
+ if (ret <= 0)
+ return ret;
+ t->duration = (temp == (uint32_t) (-1))?
+ (uint64_t) (-1) : (uint64_t) (temp);
+ }
+ skip_bytes(f, 4);
+ return 1;
+}
+
+static int read_ilst(struct mp4 *f, int32_t size)
+{
+ int ret;
+ uint64_t sumsize = 0;
+
+ while (sumsize < size) {
+ uint8_t atom_type;
+ uint64_t subsize, destpos;
+ uint8_t header_size = 0;
+ ret = atom_read_header(f, &atom_type, &header_size, &subsize);
+ if (ret <= 0)
+ return ret;
+ destpos = get_position(f) + subsize - header_size;
+ switch (atom_type) {
+ case ATOM_ARTIST:
+ case ATOM_TITLE:
+ case ATOM_ALBUM:
+ case ATOM_COMMENT:
+ case ATOM_DATE:
+ ret = parse_tag(f, atom_type, subsize - header_size);
+ if (ret <= 0)
+ return ret;
+ }
+ set_position(f, destpos);
+ sumsize += subsize;
+ }
+ return 1;
+}
+
+static int read_meta(struct mp4 *f, uint64_t size)
+{
+ int ret;
+ uint64_t subsize, sumsize = 0;
+ uint8_t atom_type;
+ uint8_t header_size = 0;
+
+ skip_bytes(f, 4); /* version (1), flags (3) */
+ while (sumsize < (size - (header_size + 4))) {
+ ret = atom_read_header(f, &atom_type, &header_size, &subsize);
+ if (ret <= 0)
+ return ret;
+ if (subsize <= header_size + 4)
+ return 1;
+ if (atom_type == ATOM_ILST) {
+ f->ilst_offset = get_position(f) - header_size;
+ f->ilst_size = subsize;
+ ret = read_ilst(f, subsize - (header_size + 4));
+ if (ret <= 0)
+ return ret;
+ } else
+ set_position(f, get_position(f) + subsize - header_size);
+ sumsize += subsize;
+ }
+ return 1;
+}
+
+static bool need_atom(uint8_t atom_type, bool meta_only)
+{
+ /* these are needed in any case */
+ switch (atom_type) {
+ case ATOM_STSD:
+ case ATOM_META:
+ case ATOM_TRAK:
+ case ATOM_MDIA:
+ case ATOM_MINF:
+ case ATOM_STBL:
+ case ATOM_UDTA:
+ return true;
+ }
+ /* meta-only opens don't need anything else */
+ if (meta_only)
+ return false;
+ /* these are only required for regular opens */
+ switch (atom_type) {
+ case ATOM_STTS:
+ case ATOM_STSZ:
+ case ATOM_STCO:
+ case ATOM_STSC:
+ case ATOM_MDHD:
+ return true;
+ }
+ return false;
+}
+
+/* parse atoms that are sub atoms of other atoms */
+static int parse_sub_atoms(struct mp4 *f, uint64_t total_size, bool meta_only)
+{
+ int ret;
+ uint64_t dest, size, end = get_position(f) + total_size;
+
+ for (dest = get_position(f); dest < end; set_position(f, dest)) {
+ uint8_t header_size, atom_type;
+ ret = atom_read_header(f, &atom_type, &header_size, &size);
+ if (ret <= 0)
+ return ret;
+ if (size == 0)
+ return -E_MP4_CORRUPT;
+ dest = get_position(f) + size - header_size;
+ if (atom_type == ATOM_TRAK && f->track.state == ATS_SEEN_MP4A) {
+ f->track.state = ATS_TRACK_CHANGE;
+ continue;
+ }
+ if (atom_type == ATOM_UDTA) {
+ f->udta_offset = get_position(f) - header_size;
+ f->udta_size = size;
+ }
+ if (!need_atom(atom_type, meta_only))
+ continue;
+ switch (atom_type) {
+ case ATOM_STSZ: ret = read_stsz(f); break;
+ case ATOM_STTS: ret = read_stts(f); break;
+ case ATOM_STSC: ret = read_stsc(f); break;
+ case ATOM_STCO: ret = read_stco(f); break;
+ case ATOM_STSD: ret = read_stsd(f); break;
+ case ATOM_MDHD: ret = read_mdhd(f); break;
+ case ATOM_META:
+ f->meta_offset = get_position(f) - header_size;
+ f->meta_size = size;
+ ret = read_meta(f, size);
+ break;
+ default:
+ ret = parse_sub_atoms(f, size - header_size, meta_only);
+ }
+ if (ret <= 0)
+ return ret;
+ }
+ return 1;
+}
+
+/**
+ * Deallocate all resources associated with an mp4 file handle.
+ *
+ * \param f File handle returned by \ref mp4_open() or \ref mp4_open_meta().
+ *
+ * This frees the metadata items and various tables which were allocated when
+ * the file was opened. The given file handle must not be NULL.
+ */
+void mp4_close(struct mp4 *f)
+{
+ free(f->track.stsz_table);
+ free(f->track.stts_sample_count);
+ free(f->track.stsc_first_chunk);
+ free(f->track.stsc_samples_per_chunk);
+ free(f->track.stco_chunk_offset);
+ for (uint32_t n = 0; n < f->meta.count; n++) {
+ free(f->meta.tags[n].item);
+ free(f->meta.tags[n].value);
+ }
+ free(f->meta.tags);
+ free(f);
+}
+
+static int open_file(const struct mp4_callback *cb, bool meta_only, struct mp4 **result)
+{
+ int ret;
+ uint64_t size;
+ uint8_t atom_type, header_size;
- out_buffer = para_malloc(*out_size);
++ struct mp4 *f = zalloc(sizeof(*f));
+
+ f->cb = cb;
+ while ((ret = atom_read_header(f, &atom_type, &header_size, &size)) > 0) {
+ f->last_atom = atom_type;
+ if (atom_type != ATOM_MOOV || size <= header_size) { /* skip */
+ set_position(f, get_position(f) + size - header_size);
+ continue;
+ }
+ f->moov_offset = get_position(f) - header_size;
+ f->moov_size = size;
+ ret = parse_sub_atoms(f, size - header_size, meta_only);
+ if (ret <= 0)
+ break;
+ }
+ if (ret < 0)
+ goto fail;
+ ret = -E_MP4_TRACK;
+ if (f->track.channel_count == 0)
+ goto fail;
+ ret = -E_MP4_BAD_SAMPLERATE;
+ if (f->track.sample_rate == 0)
+ goto fail;
+ ret = -E_MP4_MISSING_ATOM;
+ if (f->udta_size == 0 || f->meta_size == 0 || f->ilst_size == 0)
+ goto fail;
+ *result = f;
+ return 1;
+fail:
+ *result = NULL;
+ mp4_close(f);
+ return ret;
+}
+
+/**
+ * Read the audio track and the metadata of an mp4 file.
+ *
+ * \param cb Only the ->read() and ->seek() methods need to be supplied.
+ * \param result Initialized to a non-NULL pointer iff the function succeeds.
+ *
+ * This detects and parses the first audio track and the metadata information
+ * of the mp4 file. Various error checks are performed after the mp4 atoms have
+ * been parsed successfully.
+ *
+ * This function does not modify the file. However, if the caller intents to
+ * update the metadata later, the ->write() and ->truncate() methods must be
+ * supplied in the callback structure.
+ *
+ * \return Standard. Several errors are possible.
+ *
+ * \sa \ref mp4_open_meta().
+ */
+int mp4_open(const struct mp4_callback *cb, struct mp4 **result)
+{
+ struct mp4 *f;
+ int ret;
+
+ *result = NULL;
+ ret = open_file(cb, false, &f);
+ if (ret < 0)
+ return ret;
+ ret = -E_MP4_BAD_SAMPLE_COUNT;
+ if (f->track.stsz_sample_count == 0)
+ goto fail;
+ ret = -E_MP4_CORRUPT;
+ if (f->track.time_scale == 0)
+ goto fail;
+ *result = f;
+ return 1;
+fail:
+ mp4_close(f);
+ return ret;
+}
+
+static int32_t chunk_of_sample(const struct mp4 *f, int32_t sample,
+ int32_t *chunk)
+{
+ const struct mp4_track *t = &f->track;
+ uint32_t *fc = t->stsc_first_chunk, *spc = t->stsc_samples_per_chunk;
+ uint32_t chunk1, chunk1samples, n, total, k;
+
+ for (k = 1, total = 0; k < t->stsc_entry_count; k++, total += n) {
+ n = (fc[k] - fc[k - 1]) * spc[k - 1]; /* number of samples */
+ if (sample < total + n)
+ break;
+ }
+ chunk1 = fc[k - 1];
+ chunk1samples = spc[k - 1];
+ if (chunk1samples != 0)
+ *chunk = (sample - total) / chunk1samples + chunk1;
+ else
+ *chunk = 1;
+ return total + (*chunk - chunk1) * chunk1samples;
+}
+
+/**
+ * Compute the duration of an mp4 file.
+ *
+ * \param f See \ref mp4_close().
+ *
+ * \return The number of milliseconds of the audio track. This function never
+ * fails.
+ */
+uint64_t mp4_get_duration(const struct mp4 *f)
+{
+ const struct mp4_track *t = &f->track;
+
+ return t->duration * 1000 / t->time_scale;
+}
+
+/**
+ * Reposition the read/write file offset.
+ *
+ * \param f See \ref mp4_close().
+ * \param sample The number of the sample to reposition to.
+ *
+ * The given sample number must be within range, i.e., strictly less than the
+ * value returned by \ref mp4_num_samples().
+ *
+ * \return Standard. The only possible error is an invalid sample number.
+ */
+int mp4_set_sample_position(struct mp4 *f, uint32_t sample)
+{
+ const struct mp4_track *t = &f->track;
+ int32_t offset, chunk, chunk_sample;
+ uint32_t n, srs; /* sample range size */
+
+ if (sample >= t->stsz_sample_count)
+ return -ERRNO_TO_PARA_ERROR(EINVAL);
+ chunk_sample = chunk_of_sample(f, sample, &chunk);
+ if (t->stsz_sample_size > 0)
+ srs = (sample - chunk_sample) * t->stsz_sample_size;
+ else {
+ for (srs = 0, n = chunk_sample; n < sample; n++)
+ srs += t->stsz_table[n];
+ }
+ if (t->stco_entry_count > 0 && chunk > t->stco_entry_count)
+ offset = t->stco_chunk_offset[t->stco_entry_count - 1];
+ else if (t->stco_entry_count > 0)
+ offset = t->stco_chunk_offset[chunk - 1];
+ else
+ offset = 8;
+ set_position(f, offset + srs);
+ return 1;
+}
+
+/**
+ * Look up and return the size of the given sample in the stsz table.
+ *
+ * \param f See \ref mp4_close().
+ * \param sample The sample number of interest.
+ * \param result Sample size is returned here.
+ *
+ * For the sample argument the restriction mentioned in the documentation of
+ * \ref mp4_set_sample_position() applies as well.
+ *
+ * \return Standard. Like for \ref mp4_set_sample_position(), EINVAL is the
+ * only possible error.
+ */
+int mp4_get_sample_size(const struct mp4 *f, uint32_t sample, uint32_t *result)
+{
+ const struct mp4_track *t = &f->track;
+
+ if (sample >= t->stsz_sample_count)
+ return -ERRNO_TO_PARA_ERROR(EINVAL);
+ if (t->stsz_sample_size != 0)
+ *result = t->stsz_sample_size;
+ else
+ *result = t->stsz_table[sample];
+ return 1;
+}
+
+/**
+ * Return the sample rate stored in the stsd atom.
+ *
+ * \param f See \ref mp4_close().
+ *
+ * The sample rate is a property of the audio track of the mp4 file and is thus
+ * independent of the sample number.
+ *
+ * \return The function always returns a positive value because the open
+ * operation fails if the sample rate happens to be zero. A typical value is
+ * 44100.
+ */
+uint16_t mp4_get_sample_rate(const struct mp4 *f)
+{
+ return f->track.sample_rate;
+}
+
+/**
+ * Return the number of channels of the audio track.
+ *
+ * \param f See \ref mp4_close().
+ *
+ * \return The returned channel count is guaranteed to be positive because the
+ * open operation fails if the mp4a atom is missing or contains a zero channel
+ * count.
+ */
+uint16_t mp4_get_channel_count(const struct mp4 *f)
+{
+ return f->track.channel_count;
+}
+
+/**
+ * Return the number of samples of the audio track.
+ *
+ * \param f See \ref mp4_close().
+ *
+ * \return The sample count is read from the stsz atom during open.
+ */
+uint32_t mp4_num_samples(const struct mp4 *f)
+{
+ return f->track.stsz_sample_count;
+}
+
+/**
+ * Open an mp4 file in metadata-only mode.
+ *
+ * \param cb See \ref mp4_open().
+ * \param result See \ref mp4_open().
+ *
+ * This is similar to \ref mp4_open() but is cheaper because it only parses the
+ * metadata of the mp4 file. The only functions that can subsequently be called
+ * with the file handle returned here are \ref mp4_get_meta() and \ref
+ * mp4_update_meta().
+ *
+ * \return Standard.
+ *
+ * \sa \ref mp4_open(). The comment about ->write() and ->truncate() applies to
+ * this function as well.
+ */
+int mp4_open_meta(const struct mp4_callback *cb, struct mp4 **result)
+{
+ struct mp4 *f;
+ int ret = open_file(cb, true, &f);
+
+ if (ret < 0)
+ return ret;
+ *result = f;
+ return 1;
+}
+
+/**
+ * Return the metadata of an mp4 file.
+ *
+ * \param f See \ref mp4_close().
+ *
+ * The caller is allowed to add, delete or modify the entries of the returned
+ * structure with the intention to pass the modified version to \ref
+ * mp4_update_meta().
+ *
+ * \return This never returns NULL, even if the file contains no metadata tag
+ * items. However, the meta count will be zero and the ->tags pointer NULL in
+ * this case.
+ */
+struct mp4_metadata *mp4_get_meta(struct mp4 *f)
+{
+ return &f->meta;
+}
+
+/** Total length of an on-disk metadata tag. */
+#define TAG_LEN(_len) (24 + (_len))
+static void create_ilst(const struct mp4_metadata *meta, uint8_t *out)
+{
+ for (unsigned n = 0; n < meta->count; n++) {
+ struct mp4_tag *tag = meta->tags + n;
+ unsigned len = strlen(tag->value);
+ const char *atom_name;
+
+ if (!strcasecmp(tag->item, "title"))
+ atom_name = "\xA9" "nam";
+ else if (!strcasecmp(tag->item, "artist"))
+ atom_name = "\xA9" "ART";
+ else if (!strcasecmp(tag->item, "album"))
+ atom_name = "\xA9" "alb";
+ else if (!strcasecmp(tag->item, "date"))
+ atom_name = "\xA9" "day";
+ else if (!strcasecmp(tag->item, "comment"))
+ atom_name = "\xA9" "cmt";
+ else
+ assert(false);
+ write_u32_be(out, TAG_LEN(len));
+ memcpy(out + 4, atom_name, 4);
+ write_u32_be(out + 8, 8 /* data atom header */
+ + 8 /* flags + reserved */
+ + len);
+ memcpy(out + 12, "data", 4);
+ write_u32_be(out + 16, 1); /* flags */
+ write_u32_be(out + 20, 0); /* reserved */
+ memcpy(out + 24, tag->value, len);
+ out += TAG_LEN(len);
+ }
+}
+
+static void *modify_moov(struct mp4 *f, uint32_t *out_size)
+{
+ int ret;
+ uint64_t total_base = f->moov_offset + 8;
+ uint32_t total_size = f->moov_size - 8;
+ uint32_t new_ilst_size = 0;
+ void *out_buffer;
+ uint8_t *p_out;
+ int32_t size_delta;
+ uint32_t tmp;
+
+ for (unsigned n = 0; n < f->meta.count; n++)
+ new_ilst_size += TAG_LEN(strlen(f->meta.tags[n].value));
+ size_delta = new_ilst_size - (f->ilst_size - 8);
+ *out_size = total_size + size_delta;
++ out_buffer = alloc(*out_size);
+ p_out = out_buffer;
+ set_position(f, total_base);
+ ret = read_data(f, p_out, f->udta_offset - total_base);
+ if (ret <= 0)
+ return NULL;
+ p_out += f->udta_offset - total_base;
+ ret = read_int32(f, &tmp);
+ if (ret <= 0)
+ return NULL;
+ write_u32_be(p_out, tmp + size_delta);
+ p_out += 4;
+ ret = read_data(f, p_out, 4);
+ if (ret <= 0)
+ return NULL;
+ p_out += 4;
+ ret = read_data(f, p_out, f->meta_offset - f->udta_offset - 8);
+ if (ret <= 0)
+ return NULL;
+ p_out += f->meta_offset - f->udta_offset - 8;
+ ret = read_int32(f, &tmp);
+ if (ret <= 0)
+ return NULL;
+ write_u32_be(p_out, tmp + size_delta);
+ p_out += 4;
+ ret = read_data(f, p_out, 4);
+ if (ret <= 0)
+ return NULL;
+ p_out += 4;
+ ret = read_data(f, p_out, f->ilst_offset - f->meta_offset - 8);
+ if (ret <= 0)
+ return NULL;
+ p_out += f->ilst_offset - f->meta_offset - 8;
+ ret = read_int32(f, &tmp);
+ if (ret <= 0)
+ return NULL;
+ write_u32_be(p_out, tmp + size_delta);
+ p_out += 4;
+ ret = read_data(f, p_out, 4);
+ if (ret <= 0)
+ return NULL;
+ p_out += 4;
+ create_ilst(&f->meta, p_out);
+ p_out += new_ilst_size;
+ set_position(f, f->ilst_offset + f->ilst_size);
+ ret = read_data(f, p_out, total_size - (f->ilst_offset - total_base)
+ - f->ilst_size);
+ if (ret <= 0)
+ return NULL;
+ return out_buffer;
+}
+
+static int write_data(struct mp4 *f, void *data, size_t size)
+{
+ while (size > 0) {
+ ssize_t ret = f->cb->write(f->cb->user_data, data, size);
+ if (ret < 0) {
+ if (errno == EINTR)
+ continue;
+ return -ERRNO_TO_PARA_ERROR(errno);
+ }
+ size -= ret;
+ }
+ return 1;
+}
+
+/**
+ * Write back the modified metadata items to the mp4 file.
+ *
+ * This is the only public function which modifies the contents of an mp4 file.
+ * This is achieved by calling the ->write() and ->truncate() methods of the
+ * callback structure passed to \ref mp4_open() or \ref mp4_open_meta().
+ *
+ * \param f See \ref mp4_close().
+ *
+ * The modified metadata structure does not need to be supplied to this
+ * function because it is part of the mp4 structure.
+ *
+ * \return Standard.
+ */
+int mp4_update_meta(struct mp4 *f)
+{
+ void *new_moov_data;
+ uint32_t new_moov_size;
+ uint8_t buf[8] = "----moov";
+ int ret;
+
+ set_position(f, 0);
+ new_moov_data = modify_moov(f, &new_moov_size);
+ if (!new_moov_data ) {
+ mp4_close(f);
+ return 0;
+ }
+ if (f->last_atom != ATOM_MOOV) {
+ set_position(f, f->moov_offset + 4);
+ ret = write_data(f, "free", 4); /* rename old moov to free */
+ if (ret < 0)
+ goto free_moov;
+ /* write new moov atom at EOF */
+ f->cb->seek(f->cb->user_data, 0, SEEK_END);
+ } else /* overwrite old moov atom */
+ set_position(f, f->moov_offset);
+ write_u32_be(buf, new_moov_size + 8);
+ ret = write_data(f, buf, sizeof(buf));
+ if (ret < 0)
+ goto free_moov;
+ ret = write_data(f, new_moov_data, new_moov_size);
+ if (ret < 0)
+ goto free_moov;
+ ret = f->cb->truncate(f->cb->user_data);
+ if (ret < 0)
+ ret = -ERRNO_TO_PARA_ERROR(errno);
+free_moov:
+ free(new_moov_data);
+ return ret;
+}
+
+/**
+ * Return the value of the given tag item.
+ *
+ * \param f See \ref mp4_close().
+ * \param item "artist", "title", "album", "comment", or "date".
+ *
+ * \return The function returns NULL if the given item is not in the above
+ * list. Otherwise, if the file does not contain a tag for the given item, the
+ * function also returns NULL. Otherwise a copy of the tag value is returned
+ * and the caller should free this memory when it is no longer needed.
+ */
+char *mp4_get_tag_value(const struct mp4 *f, const char *item)
+{
+ for (unsigned n = 0; n < f->meta.count; n++)
+ if (!strcasecmp(f->meta.tags[n].item, item))
+ return para_strdup(f->meta.tags[n].value);
+ return NULL;
+}
*/
struct flowopts *flowopt_new(void)
{
- struct flowopts *new = para_malloc(sizeof(*new));
+ struct flowopts *new = alloc(sizeof(*new));
init_list_head(&new->sockopts);
return new;
void flowopt_add(struct flowopts *fo, int lev, int opt,
const char *name, const void *val, int len)
{
- struct pre_conn_opt *new = para_malloc(sizeof(*new));
+ struct pre_conn_opt *new = alloc(sizeof(*new));
new->sock_option = opt;
new->sock_level = lev;
new->opt_val = NULL;
new->opt_len = 0;
} else {
- new->opt_val = para_malloc(len);
+ new->opt_val = alloc(len);
new->opt_len = len;
memcpy(new->opt_val, val, len);
}
* Wrapper around the accept system call.
*
* \param fd The listening socket.
- * \param rfds An optional fd_set pointer.
* \param addr Structure which is filled in with the address of the peer socket.
* \param size Should contain the size of the structure pointed to by \a addr.
* \param new_fd Result pointer.
*
- * Accept incoming connections on \a addr, retry if interrupted. If \a rfds is
- * not \p NULL, return 0 if \a fd is not set in \a rfds without calling accept().
+ * Accept incoming connections on addr, retry if interrupted.
*
* \return Negative on errors, zero if no connections are present to be accepted,
* one otherwise.
*
* \sa accept(2).
*/
-int para_accept(int fd, fd_set *rfds, void *addr, socklen_t size, int *new_fd)
+int para_accept(int fd, void *addr, socklen_t size, int *new_fd)
{
int ret;
- if (rfds && !FD_ISSET(fd, rfds))
- return 0;
do
ret = accept(fd, (struct sockaddr *) addr, &size);
while (ret < 0 && errno == EINTR);
static void ogg_open(struct filter_node *fn)
{
- fn->private_data = para_calloc(sizeof(struct private_oggdec_data));
+ fn->private_data = zalloc(sizeof(struct private_oggdec_data));
fn->min_iqs = 8000;
}
struct btr_node *btrn = fn->btrn;
int ret, oret;
size_t iqs;
- struct OggVorbis_File *vf = para_malloc(sizeof(*vf));
+ struct OggVorbis_File *vf = alloc(sizeof(*vf));
PARA_NOTICE_LOG("iqs: %zu, min_iqs: %zu, opening ov callbacks\n",
btr_get_input_queue_size(btrn), fn->min_iqs);
/**
* Allocate chunks of this size and produce at most one chunk of output per
- * ->post_select() invocation. If the buffer could only be filled partially
+ * ->post_monitor() invocation. If the buffer could only be filled partially
* due to insufficient input being available, it is shrunk to the real output
* size and the resized buffer is fed into the output queue.
*/
#define OGGDEC_OUTPUT_CHUNK_SIZE (32 * 1024)
-static void ogg_pre_select(struct sched *s, void *context)
+static void ogg_pre_monitor(struct sched *s, void *context)
{
struct filter_node *fn = context;
struct private_oggdec_data *pod = fn->private_data;
sched_min_delay(s);
}
-static int ogg_post_select(__a_unused struct sched *s, void *context)
+static int ogg_post_monitor(__a_unused struct sched *s, void *context)
{
struct filter_node *fn = context;
struct private_oggdec_data *pod = fn->private_data;
goto out;
}
have = 0;
- buf = para_malloc(OGGDEC_OUTPUT_CHUNK_SIZE);
+ buf = alloc(OGGDEC_OUTPUT_CHUNK_SIZE);
for (;;) {
ret = ov_read(pod->vf, buf + have, OGGDEC_OUTPUT_CHUNK_SIZE - have,
ENDIAN, 2 /* 16 bit */, 1 /* signed */, NULL);
const struct filter lsg_filter_cmd_com_oggdec_user_data = {
.open = ogg_open,
.close = ogg_close,
- .pre_select = ogg_pre_select,
- .post_select = ogg_post_select,
+ .pre_monitor = ogg_pre_monitor,
+ .post_monitor = ogg_post_monitor,
.execute = oggdec_execute
};
static void opusdec_open(struct filter_node *fn)
{
- struct opusdec_context *ctx = para_calloc(sizeof(*ctx));
+ struct opusdec_context *ctx = zalloc(sizeof(*ctx));
ogg_sync_init(&ctx->oy);
fn->private_data = ctx;
if (tmp_skip > 0) {
short *in = pcm + ctx->channels * tmp_skip;
- short *out = para_malloc(bytes);
+ short *out = alloc(bytes);
memcpy(out, in, bytes);
free(pcm);
pcm = out;
/* don't care for anything except opus eos */
if (op->e_o_s && ctx->os.serialno == ctx->opus_serialno)
ctx->eos = true;
- output = para_malloc(sizeof(short) * MAX_FRAME_SIZE * ctx->channels);
+ output = arr_alloc(sizeof(short) * ctx->channels, MAX_FRAME_SIZE);
ret = opus_multistream_decode(ctx->st, (unsigned char *)op->packet,
op->bytes, output, MAX_FRAME_SIZE, 0);
if (ret < 0) {
#define OPUSDEC_MAX_OUTPUT_SIZE (1024 * 1024)
-static int opusdec_post_select(__a_unused struct sched *s, void *context)
+static int opusdec_post_monitor(__a_unused struct sched *s, void *context)
{
struct filter_node *fn = context;
struct opusdec_context *ctx = fn->private_data;
return ret;
}
-static void opusdec_pre_select(struct sched *s, void *context)
+static void opusdec_pre_monitor(struct sched *s, void *context)
{
struct filter_node *fn = context;
struct opusdec_context *ctx = fn->private_data;
const struct filter lsg_filter_cmd_com_opusdec_user_data = {
.open = opusdec_open,
.close = opusdec_close,
- .pre_select = opusdec_pre_select,
- .post_select = opusdec_post_select,
+ .pre_monitor = opusdec_pre_monitor,
+ .post_monitor = opusdec_post_monitor,
.execute = opusdec_execute,
};
}
}
-static void oss_pre_select(struct sched *s, void *context)
+static void oss_pre_monitor(struct sched *s, void *context)
{
struct writer_node *wn = context;
struct private_oss_write_data *powd = wn->private_data;
return;
if (ret < 0 || !powd)
return sched_min_delay(s);
- para_fd_set(powd->fd, &s->wfds, &s->max_fileno);
+ sched_monitor_writefd(powd->fd, s);
}
static void oss_close(struct writer_node *wn)
{
int ret, format;
unsigned ch, rate;
- struct private_oss_write_data *powd = para_calloc(sizeof(*powd));
+ struct private_oss_write_data *powd = zalloc(sizeof(*powd));
const char *dev = WRITE_CMD_OPT_STRING_VAL(OSS, DEVICE, wn->lpr);
PARA_INFO_LOG("opening %s\n", dev);
return ret;
}
-static int oss_post_select(__a_unused struct sched *s, void *context)
+static int oss_post_monitor(__a_unused struct sched *s, void *context)
{
struct writer_node *wn = context;
struct private_oss_write_data *powd = wn->private_data;
goto out;
}
ret = 0;
- if (!FD_ISSET(powd->fd, &s->wfds))
+ if (!sched_write_ok(powd->fd, s))
goto out;
/* get maximal number of bytes that can be written */
ret = ioctl(powd->fd, SNDCTL_DSP_GETOSPACE, &abi);
}
const struct writer lsg_write_cmd_com_oss_user_data = {
- .pre_select = oss_pre_select,
- .post_select = oss_post_select,
+ .pre_monitor = oss_pre_monitor,
+ .post_monitor = oss_post_monitor,
.close = oss_close,
};
#include <stdbool.h>
#include <inttypes.h>
#include <sys/uio.h>
+#include <poll.h>
+
#include "gcc-compat.h"
/** used in various contexts */
typeof(x) _x = (x); \
_x > 0? _x : -_x; })
-
extern __printf_2_3 void (*para_log)(int, const char*, ...);
/**
* Define a standard log function that always writes to stderr.
* Describes a request to change the state of para_play.
*
* There is only one variable of this type: \a rq of the global play task
- * structure. Command handlers only set this variable and the post_select()
+ * structure. Command handlers only set this variable and the post_monitor()
* function of the play task investigates its value during each iteration of
* the scheduler run and performs the actual work.
*/
char *stat_item_values[NUM_STAT_ITEMS] = {NULL};
-static struct sched sched = {.max_fileno = 0};
+static struct sched sched;
static struct play_task play_task, *pt = &play_task;
#define AFH_RECV_CMD (lls_cmd(LSG_RECV_CMD_CMD_AFH, recv_cmd_suite))
static void init_shuffle_map(void)
{
unsigned n, num_inputs = lls_num_inputs(play_lpr);
- shuffle_map = para_malloc(num_inputs * sizeof(unsigned));
+ shuffle_map = arr_alloc(num_inputs, sizeof(unsigned));
for (n = 0; n < num_inputs; n++)
shuffle_map[n] = n;
if (!OPT_GIVEN(RANDOMIZE))
pt->rn.task = task_register(
&(struct task_info) {
.name = lls_command_name(AFH_RECV_CMD),
- .pre_select = AFH_RECV->pre_select,
- .post_select = AFH_RECV->post_select,
+ .pre_monitor = AFH_RECV->pre_monitor,
+ .post_monitor = AFH_RECV->post_monitor,
.context = &pt->rn
}, &sched);
sprintf(buf, "%s decoder", af);
pt->fn.task = task_register(
&(struct task_info) {
.name = buf,
- .pre_select = decoder->pre_select,
- .post_select = decoder->post_select,
+ .pre_monitor = decoder->pre_monitor,
+ .post_monitor = decoder->post_monitor,
.context = &pt->fn
}, &sched);
register_writer_node(&pt->wn, pt->fn.btrn, &sched);
if (!p)
return NULL;
len = p - kma;
- result = para_malloc(len + 1);
+ result = alloc(len + 1);
memcpy(result, kma, len);
result[len] = '\0';
return result;
if (len == 1 && isprint(*seq))
return seq;
- sseq = para_malloc(2 + 2 * len + 1);
+ sseq = alloc(2 + 2 * len + 1);
sseq[0] = '0';
sseq[1] = 'x';
for (n = 0; n < len; n++) {
char **result;
int i;
- result = para_malloc((NUM_MAPPED_KEYS + 1) * sizeof(char *));
+ result = arr_alloc(NUM_MAPPED_KEYS + 1, sizeof(char *));
FOR_EACH_MAPPED_KEY(i) {
char *seq = get_key_map_seq(i);
result[i] = seq;
ss = PARA_MAX(ss, 0UL);
ss = PARA_MIN(ss, pt->num_chunks);
pt->start_chunk = ss;
+ pt->rq = CRT_REPOS;
kill_stream();
return 0;
}
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGWINCH, &act, NULL);
- sched.select_function = i9e_select;
+ sched.poll_function = i9e_poll;
ici.bound_keyseqs = get_mapped_keyseqs();
pt->btrn = ici.producer = btr_new_node(&(struct btr_node_description)
/*
* If we are about to die we must call i9e_close() to reset the terminal.
* However, i9e_close() must be called in *this* context, i.e. from
- * play_task.post_select() rather than from i9e_post_select(), because
+ * play_task.post_monitor() rather than from i9e_post_monitor(), because
* otherwise i9e would access freed memory upon return. So the play task must
* stay alive until the i9e task terminates.
*
* We achieve this by sending a fake SIGTERM signal via i9e_signal_dispatch()
- * and reschedule. In the next iteration, i9e->post_select returns an error and
+ * and reschedule. In the next iteration, i9e->post_monitor returns an error and
* terminates. Subsequent calls to i9e_get_error() then return negative and we
* are allowed to call i9e_close() and terminate as well.
*/
-static int session_post_select(__a_unused struct sched *s)
+static int session_post_monitor(__a_unused struct sched *s)
{
int ret;
#else /* HAVE_READLINE */
-static int session_post_select(struct sched *s)
+static int session_post_monitor(struct sched *s)
{
char c;
- if (!FD_ISSET(STDIN_FILENO, &s->rfds))
+ if (!sched_read_ok(STDIN_FILENO, s))
return 0;
if (read(STDIN_FILENO, &c, 1))
do_nothing;
}
#endif /* HAVE_READLINE */
-static void play_pre_select(struct sched *s, __a_unused void *context)
+static void play_pre_monitor(struct sched *s, __a_unused void *context)
{
char state;
- para_fd_set(STDIN_FILENO, &s->rfds, &s->max_fileno);
+ sched_monitor_readfd(STDIN_FILENO, s);
state = get_playback_state();
if (state == 'R' || state == 'F' || state == 'X')
return sched_min_delay(s);
);
}
-static int play_post_select(struct sched *s, __a_unused void *context)
+static int play_post_monitor(struct sched *s, __a_unused void *context)
{
int ret;
pt->rq = CRT_TERM_RQ;
return 0;
}
- ret = session_post_select(s);
+ ret = session_post_monitor(s);
if (ret < 0)
goto out;
if (!pt->wn.btrn && !pt->fn.btrn) {
int ret;
unsigned num_inputs;
- sched.default_timeout.tv_sec = 5;
+ sched.default_timeout = 5000;
parse_config_or_die(argc, argv);
session_open();
num_inputs = lls_num_inputs(play_lpr);
init_shuffle_map();
- pt->invalid = para_calloc(sizeof(*pt->invalid) * num_inputs);
+ pt->invalid = arr_zalloc(num_inputs, sizeof(*pt->invalid));
pt->rq = CRT_FILE_CHANGE;
pt->playing = true;
pt->task = task_register(&(struct task_info){
.name = "play",
- .pre_select = play_pre_select,
- .post_select = play_post_select,
+ .pre_monitor = play_pre_monitor,
+ .post_monitor = play_post_monitor,
.context = pt,
}, &sched);
ret = schedule(&sched);
struct timeval barrier;
};
-static void prebuffer_pre_select(struct sched *s, void *context)
+static void prebuffer_pre_monitor(struct sched *s, void *context)
{
struct filter_node *fn = context;
struct btr_node *btrn = fn->btrn;
free(fn->private_data);
}
-static int prebuffer_post_select(__a_unused struct sched *s, void *context)
+static int prebuffer_post_monitor(__a_unused struct sched *s, void *context)
{
struct filter_node *fn = context;
struct btr_node *btrn = fn->btrn;
static void prebuffer_open(struct filter_node *fn)
{
- struct private_prebuffer_data *ppd = para_calloc(sizeof(*ppd));
+ struct private_prebuffer_data *ppd = zalloc(sizeof(*ppd));
fn->private_data = ppd;
}
const struct filter lsg_filter_cmd_com_prebuffer_user_data = {
.open = prebuffer_open,
.close = prebuffer_close,
- .pre_select = prebuffer_pre_select,
- .post_select = prebuffer_post_select,
+ .pre_monitor = prebuffer_pre_monitor,
+ .post_monitor = prebuffer_post_monitor,
};
*lprp = NULL;
if (!ra || !*ra) {
argc = 1;
- argv = para_malloc(2 * sizeof(char*));
+ argv = alloc(2 * sizeof(char*));
argv[0] = para_strdup("http");
argv[1] = NULL;
} else {
}
/**
- * Simple pre-select hook, used by all receivers.
+ * Request a minimal timeout in case of buffer tree errors.
*
- * \param s Scheduler info.
- * \param rn The receiver node.
+ * \param s The scheduler instance.
+ * \param rn The buffer tree node is derived from this.
*
- * This requests a minimal delay from the scheduler if the status of the buffer
- * tree node indicates an error/eof condition. No file descriptors are added to
- * the fd sets of \a s.
+ * If the buffer tree node of the given receiver node is in error or EOF state,
+ * a minimal I/O timeout is requested from the scheduler. Otherwise, the
+ * function does nothing. No file descriptors are asked to be monitored.
*
- * \return The status of the btr node of the receiver node, i.e. the return
- * value of the underlying call to \ref btr_node_status().
+ * \return The status of of the receiver node's buffer tree node. That is, the
+ * return value of the underlying call to \ref btr_node_status().
*/
-int generic_recv_pre_select(struct sched *s, struct receiver_node *rn)
+int generic_recv_pre_monitor(struct sched *s, struct receiver_node *rn)
{
int ret = btr_node_status(rn->btrn, 0, BTR_NT_ROOT);
static void resample_open(struct filter_node *fn)
{
- struct resample_context *ctx = para_calloc(sizeof(*ctx));
+ struct resample_context *ctx = zalloc(sizeof(*ctx));
struct btr_node *btrn = fn->btrn;
struct wav_params wp;
btr_log_tree(btr_parent(btr_parent(btrn)), LL_INFO);
}
-static void resample_pre_select(struct sched *s, void *context)
+static void resample_pre_monitor(struct sched *s, void *context)
{
struct filter_node *fn = context;
struct resample_context *ctx = fn->private_data;
if (ret != 0)
return sched_min_delay(s);
- check_wav_pre_select(s, ctx->cwc);
+ check_wav_pre_monitor(s, ctx->cwc);
}
static int get_btr_val(const char *what, struct btr_node *btrn)
data.output_frames = num_frames * ctx->ratio + 1;
out_samples = data.output_frames * ctx->channels;
- in_float = para_malloc(num_samples * sizeof(float));
+ in_float = arr_alloc(num_samples, sizeof(float));
src_short_to_float_array(in, in_float, num_samples);
data.data_in = in_float;
- data.data_out = para_malloc(out_samples * sizeof(float));
+ data.data_out = arr_alloc(out_samples, sizeof(float));
ret = src_process(ctx->src_state, &data);
free(in_float);
if (ret != 0) {
return -E_LIBSAMPLERATE;
}
out_samples = data.output_frames_gen * ctx->channels;
- out = para_malloc(out_samples * sizeof(short));
+ out = arr_alloc(out_samples, sizeof(short));
src_float_to_short_array(data.data_out, out, out_samples);
free(data.data_out);
*result = out;
return data.input_frames_used;
}
-static int resample_post_select(__a_unused struct sched *s, void *context)
+static int resample_post_monitor(__a_unused struct sched *s, void *context)
{
int ret;
struct filter_node *fn = context;
size_t in_bytes, num_frames;
bool have_more;
- ret = check_wav_post_select(ctx->cwc);
+ ret = check_wav_post_monitor(ctx->cwc);
if (ret < 0)
goto out;
ret = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL);
if (ret < 0) {
btr_remove_node(&fn->btrn);
/* This releases the check_wav btr node */
- check_wav_post_select(ctx->cwc);
+ check_wav_post_monitor(ctx->cwc);
}
return ret;
}
const struct filter lsg_filter_cmd_com_resample_user_data = {
.setup = resample_setup,
.open = resample_open,
- .pre_select = resample_pre_select,
- .post_select = resample_post_select,
+ .pre_monitor = resample_pre_monitor,
+ .post_monitor = resample_post_monitor,
.close = resample_close,
.teardown = resample_teardown,
.execute = resample_execute
* The possible states of a task.
*
* In addition to the states listed here, a task may also enter zombie state.
- * This happens when its ->post_select function returns negative, the ->status
+ * This happens when its ->post_monitor function returns negative, the ->status
* field is then set to this return value. Such tasks are not scheduled any
- * more (i.e. ->pre_select() and ->post_select() are no longer called), but
+ * more (i.e. ->pre_monitor() and ->post_monitor() are no longer called), but
* they stay on the scheduler task list until \ref task_reap() or
* \ref sched_shutdown() is called.
*/
static struct timeval now_struct;
const struct timeval *now = &now_struct;
-static void sched_preselect(struct sched *s)
+static void sched_pre_monitor(struct sched *s)
{
struct task *t, *tmp;
continue;
if (t->notification != 0)
sched_min_delay(s);
- if (t->info.pre_select)
- t->info.pre_select(s, t->info.context);
+ if (t->info.pre_monitor)
+ t->info.pre_monitor(s, t->info.context);
}
}
}
//#define SCHED_DEBUG 1
-static inline void call_post_select(struct sched *s, struct task *t)
+static inline void call_post_monitor(struct sched *s, struct task *t)
{
int ret;
#ifndef SCHED_DEBUG
- ret = t->info.post_select(s, t->info.context);
+ ret = t->info.post_monitor(s, t->info.context);
#else
struct timeval t1, t2, diff;
unsigned long pst;
clock_get_realtime(&t1);
- ret = t->info.post_select(s, t->info.context);
+ ret = t->info.post_monitor(s, t->info.context);
clock_get_realtime(&t2);
tv_diff(&t1, &t2, &diff);
pst = tv2ms(&diff);
if (pst > 50)
- PARA_WARNING_LOG("%s: post_select time: %lums\n",
+ PARA_WARNING_LOG("%s: post_monitor time: %lums\n",
t->name, pst);
#endif
t->status = ret < 0? ret : TS_RUNNING;
}
-static unsigned sched_post_select(struct sched *s)
+static unsigned sched_post_monitor(struct sched *s)
{
struct task *t, *tmp;
unsigned num_running_tasks = 0;
if (t->status == TS_DEAD) /* task has been reaped */
unlink_and_free_task(t);
else if (t->status == TS_RUNNING) {
- call_post_select(s, t); /* sets t->status */
+ call_post_monitor(s, t); /* sets t->status */
t->notification = 0;
if (t->status == TS_RUNNING)
num_running_tasks++;
*
* \param s Pointer to the scheduler struct.
*
- * This function updates the global \a now pointer, calls all registered
- * pre_select hooks which may set the timeout and add any file descriptors to
- * the fd sets of \a s. Next, it calls para_select() and makes the result available
- * to the registered tasks by calling their post_select hook.
+ * This function updates the global now pointer, calls all registered
+ * pre_monitor hooks which may set the timeout and add any file descriptors to
+ * the pollfd array. Next, it calls the poll function and makes the result
+ * available to the registered tasks by calling their post_monitor hook.
*
* \return Zero if no more tasks are left in the task list, negative if the
- * select function returned an error.
+ * poll function returned an error.
*
* \sa \ref now.
*/
int ret;
unsigned num_running_tasks;
- if (!s->select_function)
- s->select_function = para_select;
+ if (!s->poll_function)
+ s->poll_function = xpoll;
again:
- FD_ZERO(&s->rfds);
- FD_ZERO(&s->wfds);
- s->select_timeout = s->default_timeout;
- s->max_fileno = -1;
+ s->num_pfds = 0;
+ if (s->pidx)
+ memset(s->pidx, 0xff, s->pidx_array_len * sizeof(unsigned));
+ s->timeout = s->default_timeout;
clock_get_realtime(&now_struct);
- sched_preselect(s);
- ret = s->select_function(s->max_fileno + 1, &s->rfds, &s->wfds,
- &s->select_timeout);
+ sched_pre_monitor(s);
+ ret = s->poll_function(s->pfd, s->num_pfds, s->timeout);
if (ret < 0)
return ret;
- if (ret == 0) {
- /*
- * APUE: Be careful not to check the descriptor sets on return
- * unless the return value is greater than zero. The return
- * state of the descriptor sets is implementation dependent if
- * either a signal is caught or the timer expires.
- */
- FD_ZERO(&s->rfds);
- FD_ZERO(&s->wfds);
- }
clock_get_realtime(&now_struct);
- num_running_tasks = sched_post_select(s);
+ num_running_tasks = sched_post_monitor(s);
if (num_running_tasks == 0)
return 0;
goto again;
/*
* With list_for_each_entry_safe() it is only safe to remove the
* _current_ list item. Since we are being called from the loop in
- * schedule() via some task's ->post_select() function, freeing the
+ * schedule() via some task's ->post_monitor() function, freeing the
* given task here would result in use-after-free bugs in schedule().
* So we only set the task status to TS_DEAD which tells schedule() to
* free the task in the next iteration of its loop.
t->name);
unlink_and_free_task(t);
}
+ free(s->pfd);
+ free(s->pidx);
}
/**
*/
struct task *task_register(struct task_info *info, struct sched *s)
{
- struct task *t = para_malloc(sizeof(*t));
+ struct task *t = alloc(sizeof(*t));
- assert(info->post_select);
+ assert(info->post_monitor);
if (!s->task_list.next)
init_list_head(&s->task_list);
* \param err A positive error code.
*
* Tasks which honor notifications are supposed to call \ref
- * task_get_notification() in their post_select function and act on the
+ * task_get_notification() in their post_monitor function and act on the
* returned notification value.
*
- * If the scheduler detects during its pre_select loop that at least one task
- * has been notified, the loop terminates, and the post_select methods of all
+ * If the scheduler detects during its pre_monitor loop that at least one task
+ * has been notified, the loop terminates, and the post_monitor methods of all
* taks are immediately called again.
*
- * The notification for a task is reset after the call to its post_select
+ * The notification for a task is reset after the call to its post_monitor
* method.
*
* \sa \ref task_get_notification().
*
* \return The notification value. If this is negative, the task has been
* notified by another task. Tasks are supposed to check for notifications by
- * calling this function from their post_select method.
+ * calling this function from their post_monitor method.
*
* \sa \ref task_notify().
*/
}
/**
- * Set the select timeout to the minimal possible value.
+ * Set the I/O timeout to the minimal possible value.
*
* \param s Pointer to the scheduler struct.
*
- * This causes the next select() call to return immediately.
+ * This causes the next poll() call to return immediately.
*/
void sched_min_delay(struct sched *s)
{
- s->select_timeout.tv_sec = s->select_timeout.tv_usec = 0;
+ s->timeout = 0;
}
/**
- * Impose an upper bound for the timeout of the next select() call.
+ * Impose an upper bound for the I/O timeout.
*
* \param to Maximal allowed timeout.
* \param s Pointer to the scheduler struct.
*
- * If the current scheduler timeout is already smaller than \a to, this
- * function does nothing. Otherwise the timeout for the next select() call is
- * set to the given value.
+ * If the current I/O timeout is already smaller than to, this function does
+ * nothing. Otherwise the timeout is set to the given value.
*
* \sa \ref sched_request_timeout_ms().
*/
void sched_request_timeout(struct timeval *to, struct sched *s)
{
- if (tv_diff(&s->select_timeout, to, NULL) > 0)
- s->select_timeout = *to;
+ long unsigned ms = tv2ms(to);
+ if (s->timeout > ms)
+ s->timeout = ms;
}
/**
- * Force the next select() call to return before the given amount of milliseconds.
+ * Bound the I/O timeout to at most the given amount of milliseconds.
*
* \param ms The maximal allowed timeout in milliseconds.
* \param s Pointer to the scheduler struct.
*
- * Like sched_request_timeout() this imposes an upper bound on the timeout
- * value for the next select() call.
+ * Like \ref sched_request_timeout() this imposes an upper bound on the I/O
+ * timeout.
*/
void sched_request_timeout_ms(long unsigned ms, struct sched *s)
{
}
/**
- * Force the next select() call to return before the given future time.
+ * Bound the I/O timeout by an absolute time in the future.
*
- * \param barrier Absolute time before select() should return.
+ * \param barrier Defines the upper bound for the timeout.
* \param s Pointer to the scheduler struct.
*
- * \return If \a barrier is in the past, this function does nothing and returns
- * zero. Otherwise it returns one.
+ * \return If the barrier is in the past, this function does nothing and
+ * returns zero. Otherwise it returns one.
*
* \sa \ref sched_request_barrier_or_min_delay().
*/
}
/**
- * Force the next select() call to return before the given time.
+ * Bound the I/O timeout or request a minimal delay.
*
- * \param barrier Absolute time before select() should return.
+ * \param barrier Absolute time as in \ref sched_request_barrier().
* \param s Pointer to the scheduler struct.
*
- * \return If \a barrier is in the past, this function requests a minimal
+ * \return If the barrier is in the past, this function requests a minimal
* timeout and returns zero. Otherwise it returns one.
*
* \sa \ref sched_min_delay(), \ref sched_request_barrier().
sched_request_timeout(&diff, s);
return 1;
}
+
+static void add_pollfd(int fd, struct sched *s, short events)
+{
+ assert(fd >= 0);
+#if 0
+ {
+ int flags = fcntl(fd, F_GETFL);
+ if (!(flags & O_NONBLOCK)) {
+ PARA_EMERG_LOG("fd %d is a blocking file descriptor\n", fd);
+ exit(EXIT_FAILURE);
+ }
+ }
+#endif
+ if (s->pidx_array_len > fd) { /* is fd already registered? */
+ if (s->pidx[fd] < s->pfd_array_len) { /* yes, it is */
+ assert(s->pfd[s->pidx[fd]].fd == fd);
+ s->pfd[s->pidx[fd]].events |= events;
+ return;
+ }
+ } else { /* need to extend the index array */
+ unsigned old_len = s->pidx_array_len;
+ while (s->pidx_array_len <= fd)
+ s->pidx_array_len = s->pidx_array_len * 2 + 1;
+ PARA_INFO_LOG("pidx array len: %u\n", s->pidx_array_len);
+ s->pidx = para_realloc(s->pidx,
+ s->pidx_array_len * sizeof(unsigned));
+ memset(s->pidx + old_len, 0xff,
+ (s->pidx_array_len - old_len) * sizeof(unsigned));
+ }
+ /*
+ * The given fd is not part of the pfd array yet. Initialize pidx[fd]
+ * to point at the next unused slot of this array and initialize the
+ * slot.
+ */
+ s->pidx[fd] = s->num_pfds;
+ if (s->pfd_array_len <= s->num_pfds) {
+ unsigned old_len = s->pfd_array_len;
+ s->pfd_array_len = old_len * 2 + 1;
+ PARA_INFO_LOG("pfd array len: %u\n", s->pfd_array_len);
+ s->pfd = para_realloc(s->pfd,
+ s->pfd_array_len * sizeof(struct pollfd));
+ memset(s->pfd + old_len, 0,
+ (s->pfd_array_len - old_len) * sizeof(struct pollfd));
+ }
+ s->pfd[s->num_pfds].fd = fd;
+ s->pfd[s->num_pfds].events = events;
+ s->pfd[s->num_pfds].revents = 0;
+ s->num_pfds++;
+}
+
+/**
+ * Instruct the scheduler to monitor an fd for readiness for reading.
+ *
+ * \param fd The file descriptor.
+ * \param s The scheduler.
+ *
+ * \sa \ref sched_monitor_writefd().
+ */
+void sched_monitor_readfd(int fd, struct sched *s)
+{
+ add_pollfd(fd, s, POLLIN);
+}
+
+/**
+ * Instruct the scheduler to monitor an fd for readiness for writing.
+ *
+ * \param fd The file descriptor.
+ * \param s The scheduler.
+ *
+ * \sa \ref sched_monitor_readfd().
+ */
+void sched_monitor_writefd(int fd, struct sched *s)
+{
+ add_pollfd(fd, s, POLLOUT);
+}
+
+static int get_revents(int fd, const struct sched *s)
+{
+ if (fd < 0)
+ return 0;
+ if (fd >= s->pidx_array_len)
+ return 0;
+ if (s->pidx[fd] >= s->num_pfds)
+ return 0;
+ if (s->pfd[s->pidx[fd]].fd != fd)
+ return 0;
+ assert((s->pfd[s->pidx[fd]].revents & POLLNVAL) == 0);
+ return s->pfd[s->pidx[fd]].revents;
+}
+
+/**
+ * Check whether there is data to read on the given fd.
+ *
+ * To be called from the ->post_monitor() method of a task.
+ *
+ * \param fd Should have been monitored with \ref sched_monitor_readfd().
+ * \param s The scheduler instance.
+ *
+ * \return True if the file descriptor is ready for reading, false otherwise.
+ * If fd is negative, or has not been monitored in the current iteration of the
+ * scheduler's main loop, the function also returns false.
+ *
+ * \sa \ref sched_write_ok().
+ */
+bool sched_read_ok(int fd, const struct sched *s)
+{
+ return get_revents(fd, s) & (POLLIN | POLLERR | POLLHUP);
+}
+
+/**
+ * Check whether writing is possible (i.e., does not block).
+ *
+ * \param fd Should have been monitored with \ref sched_monitor_writefd().
+ * \param s The scheduler instance.
+ *
+ * \return True if the file descriptor is ready for writing, false otherwise.
+ * The comment in \ref sched_read_ok() about invalid file descriptors applies
+ * to this function as well.
+ */
+bool sched_write_ok(int fd, const struct sched *s)
+{
+ return get_revents(fd, s) & (POLLOUT | POLLERR | POLLHUP);
+}
#include "afs.h"
#include "server.h"
#include "acl.h"
+#include "sched.h"
#include "send.h"
#include "close_on_fork.h"
#include "chunk_queue.h"
-#include "sched.h"
#include "vss.h"
/** Clients will be kicked if there are more than that many bytes pending. */
if (n == 0) {
ss->num_listen_fds = 1;
- ss->listen_addresses = para_malloc(sizeof(char *));
+ ss->listen_addresses = alloc(sizeof(char *));
ss->listen_addresses[0] = NULL;
- ss->listen_fds = para_malloc(sizeof(int));
+ ss->listen_fds = alloc(sizeof(int));
ss->listen_fds[0] = -1;
} else {
ss->num_listen_fds = n;
- ss->listen_addresses = para_malloc(n * sizeof(char *));
- ss->listen_fds = para_malloc(n * sizeof(int));
+ ss->listen_addresses = alloc(n * sizeof(char *));
+ ss->listen_fds = alloc(n * sizeof(int));
FOR_EACH_LISTEN_FD(i, ss) {
ss->listen_addresses[i] = para_strdup(lls_string_val(i,
listen_address_opt_result));
* Accept a connection on the socket(s) this server is listening on.
*
* \param ss The sender whose listening fd is ready for reading.
- * \param rfds Passed to para_accept(),
*
* This accepts incoming connections on any of the listening sockets of the
* server. If there is a connection pending, the function
* \sa \ref para_accept(), \ref mark_fd_nonblocking(), \ref acl_check_access(),
* \ref cq_new(), \ref add_close_on_fork_list().
*/
-struct sender_client *accept_sender_client(struct sender_status *ss, fd_set *rfds)
+struct sender_client *accept_sender_client(struct sender_status *ss)
{
struct sender_client *sc;
int fd, ret;
FOR_EACH_LISTEN_FD(n, ss) {
if (ss->listen_fds[n] < 0)
continue;
- ret = para_accept(ss->listen_fds[n], rfds, NULL, 0, &fd);
+ ret = para_accept(ss->listen_fds[n], NULL, 0, &fd);
if (ret < 0)
goto warn;
if (ret == 0)
if (ret < 0)
goto close_fd_and_warn;
ss->num_clients++;
- sc = para_calloc(sizeof(*sc));
+ sc = zalloc(sizeof(*sc));
sc->fd = fd;
sc->name = para_strdup(remote_name(fd));
sc->cq = cq_new(MAX_CQ_BYTES);
#include "net.h"
#include "server.h"
#include "list.h"
-#include "send.h"
#include "sched.h"
+#include "send.h"
#include "vss.h"
#include "config.h"
#include "close_on_fork.h"
mmd->active_connections = 0;
mmd->vss_status_flags = VSS_NEXT;
mmd->new_vss_status_flags = VSS_NEXT;
+ mmd->loglevel = OPT_UINT32_VAL(LOGLEVEL);
return;
destroy_mmd_mutex:
mutex_destroy(mmd_mutex);
exit(EXIT_FAILURE);
}
+/** Get a reference to the supercommand of para_server. */
+#define CMD_PTR (lls_cmd(0, server_suite))
+
/**
* (Re-)read the server configuration files.
*
para_strerror(-ret));
exit(EXIT_FAILURE);
}
- daemon_set_loglevel(ENUM_STRING_VAL(LOGLEVEL));
+ daemon_set_loglevel(OPT_UINT32_VAL(LOGLEVEL));
if (OPT_GIVEN(LOGFILE)) {
daemon_set_logfile(OPT_STRING_VAL(LOGFILE));
daemon_open_log_or_die();
kill(afs_pid, SIGHUP);
}
-static int signal_post_select(struct sched *s, __a_unused void *context)
+static int signal_post_monitor(struct sched *s, __a_unused void *context)
{
int ret, signum;
ret = task_get_notification(signal_task->task);
if (ret < 0)
return ret;
- signum = para_next_signal(&s->rfds);
+ signum = para_next_signal();
switch (signum) {
case 0:
return 0;
add_close_on_fork_list(signal_task->fd);
signal_task->task = task_register(&(struct task_info) {
.name = "signal",
- .pre_select = signal_pre_select,
- .post_select = signal_post_select,
+ .pre_monitor = signal_pre_monitor,
+ .post_monitor = signal_post_monitor,
.context = signal_task,
}, &sched);
}
-static void command_pre_select(struct sched *s, void *context)
+static void command_pre_monitor(struct sched *s, void *context)
{
unsigned n;
struct server_command_task *sct = context;
for (n = 0; n < sct->num_listen_fds; n++)
- para_fd_set(sct->listen_fds[n], &s->rfds, &s->max_fileno);
+ sched_monitor_readfd(sct->listen_fds[n], s);
}
static int command_task_accept(unsigned listen_idx, struct sched *s,
pid_t child_pid;
uint32_t *chunk_table;
- ret = para_accept(sct->listen_fds[listen_idx], &s->rfds, NULL, 0, &new_fd);
+ ret = para_accept(sct->listen_fds[listen_idx], NULL, 0, &new_fd);
if (ret <= 0)
goto out;
mmd->num_connects++;
/*
* After we return, the scheduler calls server_select() with a minimal
* timeout value, because the remaining tasks have a notification
- * pending. Next it calls the ->post_select method of these tasks,
+ * pending. Next it calls the ->post_monitor method of these tasks,
* which will return negative in view of the notification. This causes
* schedule() to return as there are no more runnable tasks.
*
* Note that semaphores are not inherited across a fork(), so we don't
- * hold the lock at this point. Since server_select() drops the lock
- * prior to calling para_select(), we need to acquire it here.
+ * hold the lock at this point. Since server_poll() drops the lock
+ * prior to calling poll(), we need to acquire it here.
*/
mutex_lock(mmd_mutex);
return -E_CHILD_CONTEXT;
return 0;
}
-static int command_post_select(struct sched *s, void *context)
+static int command_post_monitor(struct sched *s, void *context)
{
struct server_command_task *sct = context;
unsigned n;
sct->argv = argv;
if (!OPT_GIVEN(LISTEN_ADDRESS)) {
sct->num_listen_fds = 1;
- sct->listen_fds = para_malloc(sizeof(int));
+ sct->listen_fds = alloc(sizeof(int));
ret = para_listen_simple(IPPROTO_TCP, port);
if (ret < 0)
goto err;
sct->listen_fds[0] = ret;
} else {
sct->num_listen_fds = OPT_GIVEN(LISTEN_ADDRESS);
- sct->listen_fds = para_malloc(sct->num_listen_fds * sizeof(int));
+ sct->listen_fds = alloc(sct->num_listen_fds * sizeof(int));
for (n = 0; n < OPT_GIVEN(LISTEN_ADDRESS); n++) {
const char *arg;
arg = lls_string_val(n, OPT_RESULT(LISTEN_ADDRESS));
sct->task = task_register(&(struct task_info) {
.name = "server command",
- .pre_select = command_pre_select,
- .post_select = command_post_select,
+ .pre_monitor = command_pre_monitor,
+ .post_monitor = command_post_monitor,
.context = sct,
}, &sched);
/*
if (ret < 0)
goto fail;
server_lpr = cmdline_lpr;
- daemon_set_loglevel(ENUM_STRING_VAL(LOGLEVEL));
+ daemon_set_loglevel(OPT_UINT32_VAL(LOGLEVEL));
daemon_drop_privileges_or_die(OPT_STRING_VAL(USER),
OPT_STRING_VAL(GROUP));
version_handle_flag("server", OPT_GIVEN(VERSION));
killpg(0, SIGUSR1);
}
-static int server_select(int max_fileno, fd_set *readfds, fd_set *writefds,
- struct timeval *timeout_tv)
+static int server_poll(struct pollfd *fds, nfds_t nfds, int timeout)
{
int ret;
+ daemon_set_loglevel(mmd->loglevel);
status_refresh();
mutex_unlock(mmd_mutex);
- ret = para_select(max_fileno + 1, readfds, writefds, timeout_tv);
+ ret = xpoll(fds, nfds, timeout);
mutex_lock(mmd_mutex);
return ret;
}
struct server_command_task server_command_task_struct,
*sct = &server_command_task_struct;
- sched.default_timeout.tv_sec = 1;
- sched.select_function = server_select;
+ sched.default_timeout = 1000;
+ sched.poll_function = server_poll;
server_init(argc, argv, sct);
mutex_lock(mmd_mutex);
ret = schedule(&sched);
/*
- * We hold the mmd lock: it was re-acquired in server_select()
- * after the select call.
+ * We hold the mmd lock: it was re-acquired in server_poll()
+ * after the poll(2) call.
*/
mutex_unlock(mmd_mutex);
sched_shutdown(&sched);
* signal arrives, the signal handler writes the number of the signal received
* to one end of the signal pipe. The application can test for pending signals
* by checking if the file descriptor of the other end of the signal pipe is
- * ready for reading, see select(2).
+ * ready for reading.
*
* \return This function either succeeds or calls exit(3) to terminate the
* current process. On success, a signal task structure is returned.
ret = mark_fd_nonblocking(signal_pipe[1]);
if (ret < 0)
goto err_out;
- st = para_calloc(sizeof(*st));
+ st = zalloc(sizeof(*st));
st->fd = signal_pipe[0];
return st;
err_out:
/**
* Return the number of the next pending signal.
*
- * \param rfds The fd_set containing the signal pipe.
- *
* \return On success, the number of the received signal is returned. If there
* is no signal currently pending, the function returns zero. On read errors
* from the signal pipe, the process is terminated.
*/
-int para_next_signal(fd_set *rfds)
+int para_next_signal(void)
{
size_t n;
- int s, ret = read_nonblock(signal_pipe[0], &s, sizeof(s), rfds, &n);
+ int s, ret = read_nonblock(signal_pipe[0], &s, sizeof(s), &n);
if (ret < 0) {
PARA_EMERG_LOG("%s\n", para_strerror(-ret));
static void spxdec_open(struct filter_node *fn)
{
- struct private_spxdec_data *psd = para_calloc(sizeof(*psd));
+ struct private_spxdec_data *psd = zalloc(sizeof(*psd));
fn->private_data = psd;
fn->min_iqs = 200;
if (new_frame_size <= 0)
continue;
samples = new_frame_size * psd->shi.channels;
- btr_output = para_malloc(2 * samples);
+ btr_output = arr_alloc(samples, 2);
for (i = 0; i < samples; i++)
btr_output[i] = read_u16(output + i + skip_idx);
btr_add_output((char *)btr_output, samples * 2, btrn);
return ret;
}
-static int speexdec_post_select(__a_unused struct sched *s, void *context)
+static int speexdec_post_monitor(__a_unused struct sched *s, void *context)
{
struct filter_node *fn = context;
struct private_spxdec_data *psd = fn->private_data;
const struct filter lsg_filter_cmd_com_spxdec_user_data = {
.open = spxdec_open,
.close = speexdec_close,
- .pre_select = generic_filter_pre_select,
- .post_select = speexdec_post_select,
+ .pre_monitor = generic_filter_pre_monitor,
+ .post_monitor = speexdec_post_monitor,
.execute = speexdec_execute,
};
#include "error.h"
/**
- * Paraslash's version of realloc().
+ * Reallocate an array, abort on failure or bugs.
*
- * \param p Pointer to the memory block, may be \p NULL.
- * \param size The desired new size.
+ * \param ptr Pointer to the memory block, may be NULL.
+ * \param nmemb Number of elements.
+ * \param size The size of one element in bytes.
*
- * A wrapper for realloc(3). It calls \p exit(\p EXIT_FAILURE) on errors,
- * i.e. there is no need to check the return value in the caller.
+ * A wrapper for realloc(3) which aborts on invalid arguments or integer
+ * overflow. The wrapper also terminates the current process on allocation
+ * errors, so the caller does not need to check for failure.
*
* \return A pointer to newly allocated memory which is suitably aligned for
- * any kind of variable and may be different from \a p.
+ * any kind of variable and may be different from ptr.
*
* \sa realloc(3).
*/
- __must_check void *para_realloc(void *p, size_t size)
+ __must_check void *arr_realloc(void *ptr, size_t nmemb, size_t size)
+ {
+ size_t pr;
+
+ assert(size > 0);
+ assert(nmemb > 0);
+ assert(!__builtin_mul_overflow(nmemb, size, &pr));
+ assert(pr != 0);
+ ptr = realloc(ptr, pr);
+ assert(ptr);
+ return ptr;
+ }
+
+ /**
+ * Allocate an array, abort on failure or bugs.
+ *
+ * \param nmemb See \ref arr_realloc().
+ * \param size See \ref arr_realloc().
+ *
+ * Like \ref arr_realloc(), this aborts on invalid arguments, integer overflow
+ * and allocation errors.
+ *
+ * \return A pointer to newly allocated memory which is suitably aligned for
+ * any kind of variable.
+ *
+ * \sa See \ref arr_realloc().
+ */
+ __must_check __malloc void *arr_alloc(size_t nmemb, size_t size)
+ {
+ return arr_realloc(NULL, nmemb, size);
+ }
+
+ /**
+ * Allocate and initialize an array, abort on failure or bugs.
+ *
+ * \param nmemb See \ref arr_realloc().
+ * \param size See \ref arr_realloc().
+ *
+ * This calls \ref arr_alloc() and zeroes-out the array.
+ *
+ * \return See \ref arr_alloc().
+ */
+ __must_check __malloc void *arr_zalloc(size_t nmemb, size_t size)
{
+ void *ptr = arr_alloc(nmemb, size);
+
/*
- * No need to check for NULL pointers: If p is NULL, the call
- * to realloc is equivalent to malloc(size)
+ * This multiplication can not overflow because the above call to \ref
+ * arr_alloc() aborts on overflow.
*/
- assert(size);
- if (!(p = realloc(p, size))) {
- PARA_EMERG_LOG("realloc failed (size = %zu), aborting\n",
- size);
- exit(EXIT_FAILURE);
- }
- return p;
+ memset(ptr, 0, nmemb * size);
+ return ptr;
}
/**
- * Paraslash's version of malloc().
+ * Allocate and initialize memory.
*
* \param size The desired new size.
*
- * A wrapper for malloc(3) which exits on errors.
- *
- * \return A pointer to the allocated memory, which is suitably aligned for any
- * kind of variable.
+ * \return A pointer to the allocated and zeroed-out memory, which is suitably
+ * aligned for any kind of variable.
*
- * \sa malloc(3).
+ * \sa \ref alloc(), calloc(3).
*/
- __must_check __malloc void *para_malloc(size_t size)
+ __must_check void *zalloc(size_t size)
{
- void *p;
-
- assert(size);
- p = malloc(size);
- if (!p) {
- PARA_EMERG_LOG("malloc failed (size = %zu), aborting\n",
- size);
- exit(EXIT_FAILURE);
- }
- return p;
+ return arr_zalloc(1, size);
}
/**
- * Paraslash's version of calloc().
+ * Paraslash's version of realloc().
*
+ * \param p Pointer to the memory block, may be \p NULL.
* \param size The desired new size.
*
- * A wrapper for calloc(3) which exits on errors.
+ * A wrapper for realloc(3). It calls \p exit(\p EXIT_FAILURE) on errors,
+ * i.e. there is no need to check the return value in the caller.
*
- * \return A pointer to the allocated and zeroed-out memory, which is suitably
- * aligned for any kind of variable.
+ * \return A pointer to newly allocated memory which is suitably aligned for
+ * any kind of variable and may be different from \a p.
*
- * \sa calloc(3)
+ * \sa realloc(3).
*/
- __must_check __malloc void *para_calloc(size_t size)
+ __must_check void *para_realloc(void *p, size_t size)
{
- void *ret = para_malloc(size);
+ return arr_realloc(p, 1, size);
+ }
- memset(ret, 0, size);
- return ret;
+ /**
+ * Paraslash's version of malloc().
+ *
+ * \param size The desired new size.
+ *
+ * A wrapper for malloc(3) which exits on errors.
+ *
+ * \return A pointer to the allocated memory, which is suitably aligned for any
+ * kind of variable.
+ *
+ * \sa malloc(3).
+ */
+ __must_check __malloc void *alloc(size_t size)
+ {
+ return arr_alloc(1, size);
}
/**
*
* \param s The string to be duplicated.
*
- * A wrapper for strdup(3). It calls \p exit(EXIT_FAILURE) on errors, i.e.
- * there is no need to check the return value in the caller.
+ * A strdup(3)-like function which aborts if insufficient memory was available
+ * to allocate the duplicated string, absolving the caller from the
+ * responsibility to check for failure.
*
- * \return A pointer to the duplicated string. If \a s was the \p NULL pointer,
- * an pointer to an empty string is returned.
+ * \return A pointer to the duplicated string. Unlike strdup(3), the caller may
+ * pass NULL, in which case the function returns a pointer to an empty string.
+ * Regardless of whether or not NULL was passed, the returned string is
+ * allocated on the heap and has to be freed by the caller.
*
- * \sa strdup(3)
+ * \sa strdup(3).
*/
__must_check __malloc char *para_strdup(const char *s)
{
- char *ret;
+ char *dupped_string = strdup(s? s: "");
- if ((ret = strdup(s? s: "")))
- return ret;
- PARA_EMERG_LOG("strdup failed, aborting\n");
- exit(EXIT_FAILURE);
+ assert(dupped_string);
+ return dupped_string;
}
/**
size_t size = 150;
va_list aq;
- *result = para_malloc(size + 1);
+ *result = alloc(size + 1);
va_copy(aq, ap);
ret = vsnprintf(*result, size, fmt, aq);
va_end(aq);
if (!(flags & FELF_DISCARD_FIRST) || start != buf) {
if (flags & FELF_READ_ONLY) {
size_t s = end - start;
- char *b = para_malloc(s + 1);
+ char *b = alloc(s + 1);
memcpy(b, start, s);
b[s] = '\0';
ret = line_handler(b, private_data);
int ret, sz_off = (b->flags & PBF_SIZE_PREFIX)? 5 : 0;
if (!b->buf) {
- b->buf = para_malloc(128);
+ b->buf = alloc(128);
b->size = 128;
b->offset = 0;
}
return 1;
}
-static inline int loglevel_equal(const char *arg, const char * const ll)
-{
- return !strncasecmp(arg, ll, strlen(ll));
-}
-
-/**
- * Compute the loglevel number from its name.
- *
- * \param txt The name of the loglevel (debug, info, ...).
- *
- * \return The numeric representation of the loglevel name.
- */
-int get_loglevel_by_name(const char *txt)
-{
- if (loglevel_equal(txt, "debug"))
- return LL_DEBUG;
- if (loglevel_equal(txt, "info"))
- return LL_INFO;
- if (loglevel_equal(txt, "notice"))
- return LL_NOTICE;
- if (loglevel_equal(txt, "warning"))
- return LL_WARNING;
- if (loglevel_equal(txt, "error"))
- return LL_ERROR;
- if (loglevel_equal(txt, "crit"))
- return LL_CRIT;
- if (loglevel_equal(txt, "emerg"))
- return LL_EMERG;
- return -E_BAD_LL;
-}
-
static int get_next_word(const char *buf, const char *delim, char **word)
{
enum line_state_flags {LSF_HAVE_WORD = 1, LSF_BACKSLASH = 2,
char *out;
int ret, state = 0;
- out = para_malloc(strlen(buf) + 1);
+ out = alloc(strlen(buf) + 1);
*out = '\0';
*word = out;
for (in = buf; *in; in++) {
static int create_argv_offset(int offset, const char *buf, const char *delim,
char ***result)
{
- char *word, **argv = para_malloc((offset + 1) * sizeof(char *));
+ char *word, **argv = arr_alloc(offset + 1, sizeof(char *));
const char *p;
int i, ret;
goto err;
if (!ret)
break;
- argv = para_realloc(argv, (i + 2) * sizeof(char*));
+ argv = arr_realloc(argv, i + 2, sizeof(char*));
argv[i] = word;
}
argv[i] = NULL;
if (ret == 0)
return 1;
size = regerror(ret, preg, NULL, 0);
- buf = para_malloc(size);
+ buf = alloc(size);
regerror(ret, preg, buf, size);
PARA_ERROR_LOG("%s\n", buf);
free(buf);
char *p;
assert(len < (size_t)-1);
- p = para_malloc(len + 1);
+ p = alloc(len + 1);
if (len > 0)
memcpy(p, src, len);
p[len] = '\0';
return -ERRNO_TO_PARA_ERROR(errno);
if (num_wchars == 0)
return 0;
- dest = para_malloc((num_wchars + 1) * sizeof(*dest));
+ dest = arr_alloc(num_wchars + 1, sizeof(*dest));
src = s;
memset(&state, 0, sizeof(state));
num_wchars = mbsrtowcs(dest, &src, num_wchars, &state);
num_wchars = mbsrtowcs(NULL, &src, 0, &state);
if (num_wchars == (size_t)-1)
return -ERRNO_TO_PARA_ERROR(errno);
- wcs = para_malloc((num_wchars + 1) * sizeof(*wcs));
+ wcs = arr_alloc(num_wchars + 1, sizeof(*wcs));
memset(&state, 0, sizeof(state));
num_wchars = mbsrtowcs(wcs, &src, num_wchars + 1, &state);
assert(num_wchars != (size_t)-1);
}
wcs[n] = L'\0';
n = wcstombs(NULL, wcs, 0) + 1;
- *result = para_malloc(n);
+ *result = alloc(n);
num_wchars = wcstombs(*result, wcs, n);
assert(num_wchars != (size_t)-1);
free(wcs);
} \
)
+ __must_check void *arr_realloc(void *ptr, size_t nmemb, 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 void *alloc(size_t size);
+ __must_check __malloc void *zalloc(size_t size);
+ __must_check __malloc void *arr_alloc(size_t nmemb, size_t size);
+ __must_check __malloc void *arr_zalloc(size_t nmemb, size_t size);
__must_check __malloc char *para_strdup(const char *s);
__printf_2_0 unsigned xvasprintf(char **result, const char *fmt, va_list ap);
__printf_2_3 int para_printf(struct para_buffer *b, const char *fmt, ...);
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);
int create_shifted_argv(const char *buf, const char *delim, char ***result);
unsigned buddy_given;
const struct lls_opt_result *r_b;
- ctx = fn->private_data = para_calloc(sizeof(*ctx));
+ ctx = fn->private_data = zalloc(sizeof(*ctx));
init_list_head(&ctx->buddies);
/* create socket to listen for incoming packets */
close(fd);
goto fail;
}
- buddy = para_malloc(sizeof(*buddy));
+ buddy = alloc(sizeof(*buddy));
buddy->fd = fd;
buddy->sbi = sbi + i;
buddy->ping_received = false;
r_b = FILTER_CMD_OPT_RESULT(SYNC, BUDDY, lpr);
n = lls_opt_given(r_b);
- sbi = para_malloc(n * sizeof(*sbi));
+ sbi = arr_alloc(n, sizeof(*sbi));
PARA_INFO_LOG("initializing buddy info array of length %u\n", n);
for (i = 0; i < n; i++) {
const char *url = lls_string_val(i, r_b);
size_t len = strlen(url);
- char *host = para_malloc(len + 1);
+ char *host = alloc(len + 1);
int port;
struct addrinfo *ai;
tv_add(now, &to, &ctx->timeout);
}
-static void sync_pre_select(struct sched *s, void *context)
+static void sync_pre_monitor(struct sched *s, void *context)
{
int ret;
struct filter_node *fn = context;
ret = btr_node_status(fn->btrn, 0, BTR_NT_INTERNAL);
if (ret < 0)
return sched_min_delay(s);
- para_fd_set(ctx->listen_fd, &s->rfds, &s->max_fileno);
+ sched_monitor_readfd(ctx->listen_fd, s);
if (ret == 0)
return;
if (ctx->timeout.tv_sec == 0) { /* must ping buddies */
return NULL;
}
-static int sync_post_select(__a_unused struct sched *s, void *context)
+static int sync_post_monitor(__a_unused struct sched *s, void *context)
{
int ret;
struct filter_node *fn = context;
}
ctx->ping_sent = true;
}
- if (FD_ISSET(ctx->listen_fd, &s->rfds)) {
+ if (sched_read_ok(ctx->listen_fd, s)) {
char c;
for (;;) {
struct sockaddr src_addr;
const struct filter lsg_filter_cmd_com_sync_user_data = {
.setup = sync_setup,
.open = sync_open,
- .pre_select = sync_pre_select,
- .post_select = sync_post_select,
+ .pre_monitor = sync_pre_monitor,
+ .post_monitor = sync_post_monitor,
.close = sync_close,
.teardown = sync_teardown
};
#include "net.h"
#include "server.h"
#include "list.h"
-#include "send.h"
#include "sched.h"
+#include "send.h"
#include "vss.h"
#include "portable_io.h"
#include "fd.h"
sc->name);
return -E_TARGET_EXISTS;
}
- ut = para_calloc(sizeof(*ut));
- sc = para_calloc(sizeof(*sc));
+ ut = zalloc(sizeof(*ut));
+ sc = zalloc(sizeof(*sc));
ut->fcp.slices_per_group = scd->slices_per_group;
ut->fcp.data_slices_per_group = scd->data_slices_per_group;
ut->fcp.init_fec = udp_init_fec;
#include "net.h"
#include "server.h"
#include "list.h"
-#include "send.h"
#include "sched.h"
+#include "send.h"
#include "vss.h"
#include "ipc.h"
#include "fd.h"
enum afs_socket_status {
/** Socket is inactive. */
AFS_SOCKET_READY,
- /** Socket fd was included in the write fd set for select(). */
+ /** Socket fd was monitored for writing. */
AFS_SOCKET_CHECK_FOR_WRITE,
/** vss wrote a request to the socket and waits for reply from afs. */
AFS_SOCKET_AFD_PENDING
ret = fec_new(k, n, &fc->parms);
if (ret < 0)
return ret;
- fc->src_data = para_malloc(k * sizeof(char *));
+ fc->src_data = arr_alloc(k, sizeof(char *));
for (i = 0; i < k; i++)
- fc->src_data[i] = para_malloc(fc->mps);
- fc->enc_buf = para_malloc(fc->mps);
+ fc->src_data[i] = alloc(fc->mps);
+ fc->enc_buf = alloc(fc->mps);
fc->state = FEC_STATE_READY_TO_RUN;
fc->next_header_time.tv_sec = 0;
struct fec_client *vss_add_fec_client(struct sender_client *sc,
struct fec_client_parms *fcp)
{
- struct fec_client *fc = para_calloc(sizeof(*fc));
+ struct fec_client *fc = zalloc(sizeof(*fc));
fc->sc = sc;
fc->fcp = fcp;
if (sched_request_barrier(&vsst->data_send_barrier, s) == 1)
return;
/*
- * Compute the select timeout as the minimal time until the next
+ * Compute the I/O timeout as the minimal time until the next
* chunk/slice is due for any client.
*/
compute_chunk_time(mmd->chunks_sent, &mmd->afd.afhi.chunk_tv,
mmd->offset = tv2ms(&offset);
}
-static void vss_pre_select(struct sched *s, void *context)
+static void vss_pre_monitor(struct sched *s, void *context)
{
int i;
struct vss_task *vsst = context;
if (need_to_request_new_audio_file(vsst)) {
PARA_DEBUG_LOG("ready and playing, but no audio file\n");
- para_fd_set(vsst->afs_socket, &s->wfds, &s->max_fileno);
+ sched_monitor_writefd(vsst->afs_socket, s);
vsst->afsss = AFS_SOCKET_CHECK_FOR_WRITE;
} else
- para_fd_set(vsst->afs_socket, &s->rfds, &s->max_fileno);
+ sched_monitor_readfd(vsst->afs_socket, s);
FOR_EACH_SENDER(i) {
- if (!senders[i]->pre_select)
+ if (!senders[i]->pre_monitor)
continue;
- senders[i]->pre_select(&s->max_fileno, &s->rfds, &s->wfds);
+ senders[i]->pre_monitor(s);
}
vss_compute_timeout(s, vsst);
}
#define MAP_POPULATE 0
#endif
-static void recv_afs_result(struct vss_task *vsst, fd_set *rfds)
+static void recv_afs_result(struct vss_task *vsst, const struct sched *s)
{
int ret, passed_fd, shmid;
uint32_t afs_code = 0, afs_data = 0;
struct stat statbuf;
- if (!FD_ISSET(vsst->afs_socket, rfds))
+ if (!sched_read_ok(vsst->afs_socket, s))
return;
ret = recv_afs_msg(vsst->afs_socket, &passed_fd, &afs_code, &afs_data);
if (ret == -ERRNO_TO_PARA_ERROR(EAGAIN))
/**
* Main sending function.
*
- * This function gets called from vss_post_select(). It checks whether the next
+ * This function gets called from vss_post_monitor(). It checks whether the next
* chunk of data should be pushed out. It obtains a pointer to the data to be
* sent out as well as its length from mmd->afd.afhi. This information is then
* passed to each supported sender's send() function as well as to the send()
mmd->current_chunk++;
}
-static int vss_post_select(struct sched *s, void *context)
+static int vss_post_monitor(struct sched *s, void *context)
{
int ret, i;
struct vss_task *vsst = context;
mmd->sender_cmd_data.cmd_num = -1;
}
if (vsst->afsss != AFS_SOCKET_CHECK_FOR_WRITE)
- recv_afs_result(vsst, &s->rfds);
- else if (FD_ISSET(vsst->afs_socket, &s->wfds)) {
+ recv_afs_result(vsst, s);
+ else if (sched_write_ok(vsst->afs_socket, s)) {
PARA_INFO_LOG("requesting new fd from afs\n");
ret = write_buffer(vsst->afs_socket, "new");
if (ret < 0)
vsst->afsss = AFS_SOCKET_AFD_PENDING;
}
FOR_EACH_SENDER(i) {
- if (!senders[i]->post_select)
+ if (!senders[i]->post_monitor)
continue;
- senders[i]->post_select(&s->rfds, &s->wfds);
+ senders[i]->post_monitor(s);
}
if ((vss_playing() && !(mmd->vss_status_flags & VSS_PLAYING)) ||
(vss_next() && vss_playing()))
}
vsst->task = task_register(&(struct task_info) {
.name = "vss",
- .pre_select = vss_pre_select,
- .post_select = vss_post_select,
+ .pre_monitor = vss_pre_monitor,
+ .post_monitor = vss_post_monitor,
.context = vsst,
}, s);
}
{
int *bof;
- fn->private_data = para_malloc(sizeof(int));
+ fn->private_data = alloc(sizeof(int));
bof = fn->private_data;
*bof = 1;
}
-static void wav_pre_select(struct sched *s, void *context)
+static void wav_pre_monitor(struct sched *s, void *context)
{
struct filter_node *fn = context;
size_t iqs = btr_get_input_queue_size(fn->btrn);
sched_min_delay(s);
}
-static int wav_post_select(__a_unused struct sched *s, void *context)
+static int wav_post_monitor(__a_unused struct sched *s, void *context)
{
struct filter_node *fn = context;
struct btr_node *btrn = fn->btrn;
free(buf);
if (ret < 0)
goto err;
- header = para_malloc(WAV_HEADER_LEN);
+ header = alloc(WAV_HEADER_LEN);
make_wav_header(ch, rate, header);
btr_add_output(header, WAV_HEADER_LEN, btrn);
ret = -E_WAV_SUCCESS;
const struct filter lsg_filter_cmd_com_wav_user_data = {
.close = wav_close,
.open = wav_open,
- .pre_select = wav_pre_select,
- .post_select = wav_post_select,
+ .pre_monitor = wav_pre_monitor,
+ .post_monitor = wav_post_monitor,
};
#include <math.h>
#include <regex.h>
-#include <sys/select.h>
#include "para.h"
#include "error.h"
int i, l, j, k, level, n = src->n;
init_vlc(dst, VLCBITS, n, src->huffbits, src->huffcodes, 4);
- pwd->run_table[didx] = para_malloc(n * sizeof(uint16_t));
- pwd->level_table[didx] = para_malloc(n * sizeof(uint16_t));
+ pwd->run_table[didx] = arr_alloc(n, sizeof(uint16_t));
+ pwd->level_table[didx] = arr_alloc(n, sizeof(uint16_t));
i = 2;
level = 1;
k = 0;
int ret, i;
PARA_NOTICE_LOG("initial buf: %d bytes\n", len);
- pwd = para_calloc(sizeof(*pwd));
+ pwd = zalloc(sizeof(*pwd));
ret = read_asf_header(initial_buf, len, &pwd->ahi);
if (ret <= 0) {
free(pwd);
#define WMA_OUTPUT_BUFFER_SIZE (128 * 1024)
-static int wmadec_post_select(__a_unused struct sched *s, void *context)
+static int wmadec_post_monitor(__a_unused struct sched *s, void *context)
{
struct filter_node *fn = context;
int ret, converted, out_size;
if (fn->min_iqs > len)
goto success;
out_size = WMA_OUTPUT_BUFFER_SIZE;
- out = para_malloc(out_size);
+ out = alloc(out_size);
ret = wma_decode_superframe(pwd, out, &out_size,
(uint8_t *)in + WMA_FRAME_SKIP);
if (ret < 0) {
.open = wmadec_open,
.close = wmadec_close,
.execute = wmadec_execute,
- .pre_select = generic_filter_pre_select,
- .post_select = wmadec_post_select,
+ .pre_monitor = generic_filter_pre_monitor,
+ .post_monitor = wmadec_post_monitor,
};
struct check_wav_context *cwc;
};
-static void write_pre_select(struct sched *s, void *context)
+static void write_pre_monitor(struct sched *s, void *context)
{
struct write_task *wt = context;
- check_wav_pre_select(s, wt->cwc);
+ check_wav_pre_monitor(s, wt->cwc);
}
-static int write_post_select(__a_unused struct sched *s, void *context)
+static int write_post_monitor(__a_unused struct sched *s, void *context)
{
struct write_task *wt = context;
- return check_wav_post_select(wt->cwc);
+ return check_wav_post_monitor(wt->cwc);
}
static int setup_and_schedule(struct lls_parse_result *lpr)
wt.cwc = check_wav_init(sit.btrn, NULL, &wp, &cw_btrn);
wt.task = task_register(&(struct task_info) {
.name = "write",
- .pre_select = write_pre_select,
- .post_select = write_post_select,
+ .pre_monitor = write_pre_monitor,
+ .post_monitor = write_post_monitor,
.context = &wt,
}, &s);
n = writer_given? writer_given : 1;
- wns = para_calloc(n * sizeof(*wns));
+ wns = arr_zalloc(n, sizeof(*wns));
for (i = 0; i < n; i++) {
const char *arg = i < writer_given?
lls_string_val(i, OPT_RESULT(WRITER, lpr)) : NULL;
wns[i].wid = check_writer_arg_or_die(arg, &wns[i].lpr);
register_writer_node(wns + i, cw_btrn, &s);
}
- s.default_timeout.tv_sec = 10;
- s.default_timeout.tv_usec = 50000;
+ s.default_timeout = 10500;
ret = schedule(&s);
if (ret >= 0) {
int j, ts;
if (!wa || !*wa) {
writer_num = default_writer_id();
cmd = WRITE_CMD(writer_num);
- argv = para_malloc(2 * sizeof(char *));
+ argv = alloc(2 * sizeof(char *));
argc = 1;
argv[0] = para_strdup(lls_command_name(cmd));
argv[1] = NULL;
.handler = w->execute, .context = wn));
wn->task = task_register(&(struct task_info) {
.name = writer_name(wn->wid),
- .pre_select = w->pre_select,
- .post_select = w->post_select,
+ .pre_monitor = w->pre_monitor,
+ .post_monitor = w->post_monitor,
.context = wn,
}, s);
}