CPPFLAGS += @osl_cppflags@
man_pages := $(patsubst %, man/man1/%.1, @executables@)
-man_pages_in := $(patsubst %, web/%.man.in.html, @executables@)
ggo_dir := ggo
object_dir := objects
@[ -z "$(Q)" ] || echo 'GEN $@'
$(Q) ./command_util.sh man < $< > $@
-server_command_lists_ch = server_command_list.c afs_command_list.c \
- server_command_list.h afs_command_list.h
server_command_lists_man = server_command_list.man afs_command_list.man
man/man1/para_server.1: para_server $(server_command_lists_man) | $(man_dir)
@[ -z "$(Q)" ] || echo 'MAN $<'
@[ -z "$(Q)" ] || echo 'LD $@'
$(Q) $(CC) $(LDFLAGS) -o $@ $(gui_objs) -lcurses
-para_audiod: audiod_command_list.c audiod_command_list.h $(audiod_objs)
+para_audiod: $(audiod_objs)
@[ -z "$(Q)" ] || echo 'LD $@'
$(Q) $(CC) $(LDFLAGS) -o $@ $(audiod_objs) @audiod_ldflags@
@[ -z "$(Q)" ] || echo 'LD $@'
$(Q) $(CC) $(LDFLAGS) -o $@ $(fade_objs) @fade_ldflags@
-para_server: $(server_command_lists_ch) $(server_objs)
+para_server: $(server_objs)
@[ -z "$(Q)" ] || echo 'LD $@'
$(Q) $(CC) $(LDFLAGS) -o $@ $(server_objs) @server_ldflags@
- cleanup of the internal uptime API.
- para_server prefaults the mmapped audio file to avoid
delays on slow media.
+ - A new test for the test-suite that exercises the
+ communication between para_server and para_audiod.
+ - The alsa writer eats up less CPU cycles when configured to
+ use the DMIX plugin.
+ - Simplified and unified receiver code.
+ - Makefile cleanups.
--------------------------------------
0.4.8 (2011-08-19) "nested assignment"
snd_pcm_t *handle;
/** Determined and set by alsa_init(). */
int bytes_per_frame;
- /** The approximate maximum buffer duration in us. */
- unsigned buffer_time;
- /* Number of frames that fit into the buffer. */
- snd_pcm_uframes_t buffer_frames;
/**
* The sample rate given by command line option or the decoder
* of the writer node group.
snd_pcm_hw_params_t *hwparams;
snd_pcm_sw_params_t *swparams;
snd_pcm_uframes_t start_threshold, stop_threshold;
- snd_pcm_uframes_t period_size;
- int err;
+ snd_pcm_uframes_t buffer_size, period_size;
+ snd_output_t *log;
+ unsigned buffer_time;
+ int ret;
+ const char *msg;
PARA_INFO_LOG("opening %s\n", conf->device_arg);
- err = snd_pcm_open(&pad->handle, conf->device_arg,
+ msg = "unable to open pcm";
+ ret = snd_pcm_open(&pad->handle, conf->device_arg,
SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
- if (err < 0)
- return -E_PCM_OPEN;
-
+ if (ret < 0)
+ goto fail;
snd_pcm_hw_params_alloca(&hwparams);
- snd_pcm_sw_params_alloca(&swparams);
- if (snd_pcm_hw_params_any(pad->handle, hwparams) < 0)
- return -E_BROKEN_CONF;
- if (snd_pcm_hw_params_set_access(pad->handle, hwparams,
- SND_PCM_ACCESS_RW_INTERLEAVED) < 0)
- return -E_ACCESS_TYPE;
- if (snd_pcm_hw_params_set_format(pad->handle, hwparams,
- pad->sample_format) < 0)
- return -E_SAMPLE_FORMAT;
- if (snd_pcm_hw_params_set_channels(pad->handle, hwparams,
- pad->channels) < 0)
- return -E_CHANNEL_COUNT;
- if (snd_pcm_hw_params_set_rate_near(pad->handle, hwparams,
- &pad->sample_rate, NULL) < 0)
- return -E_SET_RATE;
- err = snd_pcm_hw_params_get_buffer_time_max(hwparams,
- &pad->buffer_time, NULL);
- if (err < 0 || !pad->buffer_time)
- return -E_GET_BUFFER_TIME;
- PARA_INFO_LOG("buffer time: %d\n", pad->buffer_time);
- if (snd_pcm_hw_params_set_buffer_time_near(pad->handle, hwparams,
- &pad->buffer_time, NULL) < 0)
- return -E_SET_BUFFER_TIME;
- if (snd_pcm_hw_params(pad->handle, hwparams) < 0)
- return -E_HW_PARAMS;
+ msg = "Broken alsa configuration";
+ ret = snd_pcm_hw_params_any(pad->handle, hwparams);
+ if (ret < 0)
+ goto fail;
+ msg = "access type not available";
+ ret = snd_pcm_hw_params_set_access(pad->handle, hwparams,
+ SND_PCM_ACCESS_RW_INTERLEAVED);
+ if (ret < 0)
+ goto fail;
+ msg = "sample format not available";
+ ret = snd_pcm_hw_params_set_format(pad->handle, hwparams,
+ pad->sample_format);
+ if (ret < 0)
+ goto fail;
+ msg = "channels count not available";
+ ret = snd_pcm_hw_params_set_channels(pad->handle, hwparams,
+ pad->channels);
+ if (ret < 0)
+ goto fail;
+ msg = "could not set sample rate";
+ ret = snd_pcm_hw_params_set_rate_near(pad->handle, hwparams,
+ &pad->sample_rate, NULL);
+ if (ret < 0)
+ goto fail;
+ msg = "unable to get buffer time";
+ ret = snd_pcm_hw_params_get_buffer_time_max(hwparams, &buffer_time,
+ NULL);
+ if (ret < 0 || buffer_time == 0)
+ goto fail;
+ msg = "could not set buffer time";
+ ret = snd_pcm_hw_params_set_buffer_time_near(pad->handle, hwparams,
+ &buffer_time, NULL);
+ if (ret < 0)
+ goto fail;
+ msg = "unable to install hw params";
+ ret = snd_pcm_hw_params(pad->handle, hwparams);
+ if (ret < 0)
+ goto fail;
snd_pcm_hw_params_get_period_size(hwparams, &period_size, NULL);
- snd_pcm_hw_params_get_buffer_size(hwparams, &pad->buffer_frames);
- PARA_INFO_LOG("buffer size: %lu, period_size: %lu\n", pad->buffer_frames,
- period_size);
- if (period_size == pad->buffer_frames)
- return -E_BAD_PERIOD;
+ snd_pcm_hw_params_get_buffer_size(hwparams, &buffer_size);
+ msg = "period size equals buffer size";
+ if (period_size == buffer_size)
+ goto fail;
+
+ /* software parameter setup */
+ snd_pcm_sw_params_alloca(&swparams);
snd_pcm_sw_params_current(pad->handle, swparams);
snd_pcm_sw_params_set_avail_min(pad->handle, swparams, period_size);
- if (pad->buffer_frames < 1)
+ if (buffer_size < 1)
start_threshold = 1;
else
- start_threshold = PARA_MIN(pad->buffer_frames,
+ start_threshold = PARA_MIN(buffer_size,
(snd_pcm_uframes_t)pad->sample_rate);
- if (snd_pcm_sw_params_set_start_threshold(pad->handle, swparams,
- start_threshold) < 0)
- return -E_START_THRESHOLD;
- stop_threshold = pad->buffer_frames;
- if (snd_pcm_sw_params_set_stop_threshold(pad->handle, swparams,
- stop_threshold) < 0)
- return -E_STOP_THRESHOLD;
- if (snd_pcm_sw_params(pad->handle, swparams) < 0)
- PARA_WARNING_LOG("unable to install sw params\n");
- pad->bytes_per_frame = snd_pcm_format_physical_width(pad->sample_format)
- * pad->channels / 8;
- if (pad->bytes_per_frame <= 0)
- return -E_PHYSICAL_WIDTH;
- PARA_INFO_LOG("bytes per frame: %d\n", pad->bytes_per_frame);
- if (snd_pcm_nonblock(pad->handle, 1))
- PARA_ERROR_LOG("failed to set nonblock mode\n");
+ msg = "could not set start threshold";
+ ret = snd_pcm_sw_params_set_start_threshold(pad->handle, swparams,
+ start_threshold);
+ if (ret < 0)
+ goto fail;
+ stop_threshold = buffer_size;
+ msg = "could not set stop threshold";
+ ret = snd_pcm_sw_params_set_stop_threshold(pad->handle, swparams,
+ stop_threshold);
+ if (ret < 0)
+ goto fail;
+ msg = "unable to install sw params";
+ ret = snd_pcm_sw_params(pad->handle, swparams);
+ if (ret < 0)
+ goto fail;
+ msg = "unable to determine bytes per frame";
+ ret = snd_pcm_format_physical_width(pad->sample_format);
+ if (ret <= 0)
+ goto fail;
+ pad->bytes_per_frame = ret * pad->channels / 8;
+ msg = "failed to set alsa handle to nonblock mode";
+ ret = snd_pcm_nonblock(pad->handle, 1);
+ if (ret < 0)
+ goto fail;
+ ret = snd_output_buffer_open(&log);
+ if (ret == 0) {
+ char *buf;
+ PARA_INFO_LOG("dumping alsa configuration\n");
+ snd_pcm_dump(pad->handle, log);
+ snd_output_buffer_string(log, &buf);
+ for (;;) {
+ char *p = strchr(buf, '\n');
+ if (!p) /* omit last output line, it's empty */
+ break;
+ *p = '\0';
+ PARA_INFO_LOG("%s\n", buf);
+ buf = p + 1;
+ }
+ snd_output_close(log);
+ }
return 1;
+fail:
+ if (ret < 0)
+ PARA_ERROR_LOG("%s: %s\n", msg, snd_strerror(-ret));
+ else
+ PARA_ERROR_LOG("%s\n", msg);
+ return -E_ALSA;
}
static void alsa_write_pre_select(struct sched *s, struct task *t)
}
ret = snd_pcm_poll_descriptors(pad->handle, &pfd, 1);
if (ret < 0) {
- PARA_ERROR_LOG("%s\n", snd_strerror(-ret));
- t->error = -E_ALSA_POLL_FDS;
+ PARA_ERROR_LOG("could not get alsa poll fd: %s\n",
+ snd_strerror(-ret));
+ t->error = -E_ALSA;
return;
}
pad->poll_fd = pfd.fd;
wn->min_iqs = pad->bytes_per_frame;
goto again;
}
- if (pad->poll_fd >= 0 && !FD_ISSET(pad->poll_fd, &s->rfds))
+ if (pad->poll_fd < 0 || !FD_ISSET(pad->poll_fd, &s->rfds))
return;
frames = bytes / pad->bytes_per_frame;
frames = snd_pcm_writei(pad->handle, data, frames);
- if (frames == 0 || frames == -EAGAIN)
+ if (frames == 0 || frames == -EAGAIN) {
+ /*
+ * The alsa poll fd was ready for IO but we failed to write to
+ * the alsa handle. This means another event is pending. We
+ * don't care about that but we have to dispatch the event in
+ * order to avoid a busy loop. So we simply read from the poll
+ * fd.
+ */
+ char buf[100];
+ if (read(pad->poll_fd, buf, 100))
+ do_nothing;
return;
+ }
if (frames > 0) {
btr_consume(btrn, frames * pad->bytes_per_frame);
goto again;
snd_pcm_prepare(pad->handle);
return;
}
- PARA_WARNING_LOG("%s\n", snd_strerror(-frames));
- ret = -E_ALSA_WRITE;
+ PARA_ERROR_LOG("alsa write error: %s\n", snd_strerror(-frames));
+ ret = -E_ALSA;
err:
assert(ret < 0);
btr_remove_node(btrn);
init_command_task(cmd_task);
if (conf.daemon_given)
- daemonize();
+ daemonize(false /* parent exits immediately */);
register_task(&sig_task->task);
register_task(&cmd_task->task);
#include <grp.h>
#include <sys/time.h>
#include <stdbool.h>
+#include <signal.h>
#include "para.h"
#include "daemon.h"
return me->flags & flag;
}
+static void dummy_sighandler(__a_unused int s)
+{
+}
+
/**
* Do the usual stuff to become a daemon.
*
- * Fork, become session leader, dup fd 0, 1, 2 to /dev/null.
+ * \param parent_waits Whether the parent process should pause before exit.
*
- * \sa fork(2), setsid(2), dup(2).
+ * Fork, become session leader, cd to /, and dup fd 0, 1, 2 to /dev/null. If \a
+ * parent_waits is false, the parent process terminates immediately.
+ * Otherwise, it calls pause() to sleep until it receives \p SIGTERM or \p
+ * SIGCHLD and exits successfully thereafter. This behaviour is useful if the
+ * daemon process should not detach from the console until the child process
+ * has completed its setup.
+ *
+ * \sa fork(2), setsid(2), dup(2), pause(2).
*/
-void daemonize(void)
+void daemonize(bool parent_waits)
{
pid_t pid;
int null;
pid = fork();
if (pid < 0)
goto err;
- if (pid)
+ if (pid) {
+ if (parent_waits) {
+ signal(SIGTERM, dummy_sighandler);
+ signal(SIGCHLD, dummy_sighandler);
+ pause();
+ }
exit(EXIT_SUCCESS); /* parent exits */
+ }
/* become session leader */
if (setsid() < 0)
goto err;
/** \file daemon.h exported symbols from daemon.c */
-void daemonize(void);
+void daemonize(bool parent_waits);
void daemon_open_log_or_die(void);
void daemon_close_log(void);
void log_welcome(const char *whoami);
#define ALSA_WRITE_ERRORS \
- PARA_ERROR(BROKEN_CONF, "Broken alsa configuration"), \
- PARA_ERROR(ACCESS_TYPE, "alsa access type not available"), \
- PARA_ERROR(SAMPLE_FORMAT, "sample format not available"), \
- PARA_ERROR(CHANNEL_COUNT, "channels count not available"), \
- PARA_ERROR(HW_PARAMS, "unable to install hw params"), \
- PARA_ERROR(BAD_PERIOD, "can not use period equal to buffer size"), \
- PARA_ERROR(ALSA_WRITE, "alsa write error"), \
- PARA_ERROR(PCM_OPEN, "unable to open pcm"), \
- PARA_ERROR(PHYSICAL_WIDTH, "unable to determine bytes per frame"), \
- PARA_ERROR(GET_BUFFER_TIME, "snd_pcm_hw_params_get_buffer_time_max() failed"), \
- PARA_ERROR(SET_BUFFER_TIME, "snd_pcm_hw_params_set_buffer_time_near() failed"), \
- PARA_ERROR(SET_RATE, "snd_pcm_hw_params_set_rate_near failed"), \
- PARA_ERROR(START_THRESHOLD, "snd_pcm_sw_params_set_start_threshold() failed"), \
- PARA_ERROR(STOP_THRESHOLD, "snd_pcm_sw_params_set_stop_threshold() failed"), \
- PARA_ERROR(ALSA_POLL_FDS, "could not get alsa poll fd"), \
+ PARA_ERROR(ALSA, "alsa error"), \
#define WRITE_COMMON_ERRORS \
return afs_server_socket[0];
}
-__noreturn static void tmp_sigchld_handler(__a_unused int s)
-{
- PARA_EMERG_LOG("caught early SIGCHLD\n");
- exit(EXIT_FAILURE);
-}
-
static void server_init(int argc, char **argv)
{
struct server_cmdline_parser_params params = {
init_user_list(user_list_file);
/* become daemon */
if (conf.daemon_given)
- daemonize();
+ daemonize(true /* parent waits for SIGTERM */);
PARA_NOTICE_LOG("initializing audio format handlers\n");
afh_init();
*/
para_sigaction(SIGUSR1, SIG_IGN);
/*
- * We have to install a SIGCHLD handler before the afs process is being
- * forked off. Otherwise, para_server does not notice if afs dies before
- * the SIGCHLD handler has been installed by init_signal_task() below.
+ * We have to block SIGCHLD before the afs process is being forked off.
+ * Otherwise, para_server does not notice if afs dies before the
+ * SIGCHLD handler has been installed for the parent process by
+ * init_signal_task() below.
*/
- para_sigaction(SIGCHLD, tmp_sigchld_handler);
+ para_block_signal(SIGCHLD);
PARA_NOTICE_LOG("initializing the audio file selector\n");
afs_socket = init_afs(argc, argv);
init_signal_task();
+ para_unblock_signal(SIGCHLD);
PARA_NOTICE_LOG("initializing virtual streaming system\n");
init_vss_task(afs_socket);
init_server_command_task(argc, argv);
+ if (conf.daemon_given)
+ kill(getppid(), SIGTERM);
PARA_NOTICE_LOG("server init complete\n");
}
para_sigaction(sig, &generic_signal_handler);
}
+/**
+ * Block a signal for the caller.
+ *
+ * \param sig The signal to block.
+ *
+ * This sets the given signal in the current signal mask of the calling process
+ * to prevent this signal from delivery.
+ *
+ * \sa \ref para_unblock_signal(), sigprocmask(2), sigaddset(3).
+ */
+void para_block_signal(int sig)
+{
+ sigset_t set;
+
+ PARA_DEBUG_LOG("blocking signal %d\n", sig);
+ sigemptyset(&set);
+ sigaddset(&set, sig);
+ sigprocmask(SIG_BLOCK, &set, NULL);
+}
+
+/**
+ * Unblock a signal.
+ *
+ * \param sig The signal to unblock.
+ *
+ * This function removes the given signal from the current set of blocked
+ * signals.
+ *
+ * \sa \ref para_block_signal(), sigprocmask(2), sigaddset(3).
+ */
+void para_unblock_signal(int sig)
+{
+ sigset_t set;
+
+ PARA_DEBUG_LOG("unblocking signal %d\n", sig);
+ sigemptyset(&set);
+ sigaddset(&set, sig);
+ sigprocmask(SIG_UNBLOCK, &set, NULL);
+}
+
/**
* Return the number of the next pending signal.
*
- * \param rfds Th fd_set containing the signal pipe.
+ * \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
int para_reap_child(pid_t *pid);
int para_next_signal(fd_set *rfds);
void para_signal_shutdown(void);
+void para_block_signal(int sig);
+void para_unblock_signal(int sig);
endif
endif
-tests := $(wildcard $(test_dir)/t[0-9][0-9][0-9][0-9]-*.sh)
+tests := $(sort $(wildcard $(test_dir)/t[0-9][0-9][0-9][0-9]-*.sh))
test: $(tests)
test_require_objects "oggdec_filter"
missing_objects="$result"
-test_require_executables "oggdec"
+test_require_executables oggdec sha1sum
missing_executables="$result"
get_audio_file_paths ogg
--- /dev/null
+#!/usr/bin/env bash
+
+test_description='Check if server command socket works.
+
+A new ssh key pair is generated, para_server is started and some commands are
+sent to the server by executing para_client. This is an implicit check of the
+crypto functions.
+'
+
+. ${0%/*}/test-lib.sh
+
+loglevel=debug
+port=2991
+stream_port=8001
+# need absolute paths here because server cds to / in daemon mode
+db=$(pwd)/db
+sock=$(pwd)/sock
+user_list=ul
+privkey=key
+pubkey=$privkey.pub
+serverlog=server.log
+
+get_audio_file_paths ogg
+oggs="$result"
+
+declare -a commands=() cmdline=() required_objects=() good=() bad=()
+i=0
+commands[$i]="help"
+cmdline[$i]="help"
+good[$i]='help server ----'
+
+let i++
+commands[$i]="init"
+cmdline[$i]="init"
+good[$i]='^successfully'
+bad[$i]='!^successfully'
+
+let i++
+commands[$i]="add_ogg"
+required_objects[$i]='ogg_afh'
+cmdline[$i]="add $oggs"
+bad[$i]='.'
+
+let i++
+commands[$i]="ls_ogg"
+required_objects[$i]='ogg_afh'
+cmdline[$i]="ls -lv -p $oggs"
+good[$i]='^path:'
+
+let i++
+commands[$i]="term"
+cmdline[$i]="term"
+bad[$i]='.'
+
+test_require_objects "server"
+missing_objects="$result"
+test_require_executables "ssh-keygen"
+missing_executables="$result"
+
+ssh-keygen -q -t rsa -b 2048 -N "" -f $privkey
+key_gen_result=$?
+
+read &>/dev/null < /dev/tcp/localhost/$port
+check_port_result=$?
+
+cat > $user_list << EOF
+user $LOGNAME $pubkey AFS_READ,AFS_WRITE,VSS_READ,VSS_WRITE
+EOF
+
+# para_server sends this signal to all processes in the current process group.
+trap "" SIGUSR1
+
+$PARA_SERVER \
+ --logfile "$serverlog" \
+ --config_file /dev/null \
+ --daemon \
+ --loglevel $loglevel \
+ --port $port \
+ --afs_database_dir "$db" \
+ --afs_socket "$sock" \
+ --user_list "$user_list" \
+ --http_port "$stream_port" \
+ --dccp_port "$stream_port"
+
+for ((i=0; i < ${#commands[@]}; i++)); do
+ command=${commands[$i]}
+ if [[ -n "$missing_objects" ]]; then
+ test_skip "$command" "missing object(s): $missing_objects"
+ continue
+ fi
+ if [[ -n "$missing_executables" ]]; then
+ test_skip "$command" \
+ "missing executables(s): $missing_executables"
+ continue
+ fi
+ if (($key_gen_result != 0)); then
+ test_skip "$command" "ssh-keygen failed"
+ continue
+ fi
+ if (($check_port_result == 0)); then
+ test_skip "$command" "port $port already in use"
+ continue
+ fi
+ if [[ -n "${required_objects[$i]}" ]]; then
+ test_require_objects "${required_objects[$i]}"
+ if [[ -n "$result" ]]; then
+ test_skip "$command" "requires object $result"
+ continue
+ fi
+ fi
+ test_expect_success "$command" "
+ $PARA_CLIENT \
+ --loglevel $loglevel \
+ --server_port $port \
+ --key_file $privkey \
+ --config_file /dev/null \
+ -- \
+ ${cmdline[$i]} > $command.out &&
+ { [[ -z \"${good[$i]}\" ]] || grep \"${good[$i]}\"; } < $command.out &&
+ { [[ -z \"${bad[$i]}\" ]] || ! grep \"${bad[$i]}\"; } < $command.out
+ "
+done
+
+trap SIGUSR1 # reset to the value it had upon entrance to the shell
+test_done
test_expect_success()
{
(($# != 2)) && error "bug: not 2 parameters to test_expect_success()"
- say >&3 "expecting success: $2"
+ echo >&3 "expecting success: $2"
_test_run "$1" "$2" "success"
echo >&3 ""
}
test_expect_failure()
{
(($# != 2)) && error "bug: not 2 parameters to test_expect_failure()"
- say >&3 "expecting failure: $2"
+ echo >&3 "expecting failure: $2"
_test_run "$1" "$2" "failure"
echo >&3 ""
}
static void compute_group_size(struct vss_task *vsst, struct fec_group *g,
int max_bytes)
{
+ char *buf;
+ size_t len;
int i, max_chunks = PARA_MAX(1LU, 150 / tv2ms(vss_chunk_time()));
+ if (g->first_chunk == 0) {
+ g->num_chunks = 1;
+ vss_get_chunk(0, vsst, &buf, &len);
+ g->bytes = len;
+ return;
+ }
+
g->num_chunks = 0;
g->bytes = 0;
/*
* of exactly one chunk for these audio formats.
*/
for (i = 0;; i++) {
- char *buf;
- size_t len;
int chunk_num = g->first_chunk + i;
if (g->bytes > 0 && i >= max_chunks) /* duration limit */
{
int ret, i, k, n, data_slices;
size_t len;
- char *buf;
+ char *buf, *p;
struct fec_group *g = &fc->group;
if (fc->state == FEC_STATE_NONE) {
assert(i == g->num_header_slices - 1);
}
- /* setup data slices */
+ /*
+ * Setup data slices. Note that for ogg streams chunk 0 points to a
+ * buffer on the heap rather than to the mapped audio file.
+ */
vss_get_chunk(g->first_chunk, vsst, &buf, &len);
- for (; i < g->num_header_slices + data_slices; i++) {
- if (buf + g->slice_bytes > vsst->map + mmd->size) {
+ for (p = buf; i < g->num_header_slices + data_slices; i++) {
+ if (p + g->slice_bytes > buf + g->bytes) {
/*
- * Can not use the memory mapped audio file for this
- * slice as it goes beyond the map.
+ * We must make a copy for this slice since using p
+ * directly would exceed the buffer.
*/
- uint32_t payload_size = vsst->map + mmd->size - buf;
- memcpy(fc->extra_src_buf, buf, payload_size);
+ uint32_t payload_size = buf + g->bytes - p;
+ assert(payload_size + FEC_HEADER_SIZE <= fc->mps);
+ memcpy(fc->extra_src_buf, p, payload_size);
if (payload_size < g->slice_bytes)
memset(fc->extra_src_buf + payload_size, 0,
g->slice_bytes - payload_size);
i++;
break;
}
- fc->src_data[i] = (const unsigned char *)buf;
- buf += g->slice_bytes;
+ fc->src_data[i] = (const unsigned char *)p;
+ p += g->slice_bytes;
}
if (i < k) {
/* use arbitrary data for all remaining slices */
senders[i].send(mmd->current_chunk, mmd->chunks_sent,
buf, len, vsst->header_buf, vsst->header_len);
}
- mmd->chunks_sent++;
- mmd->current_chunk++;
/*
* Prefault next chunk(s)
*
* eliminate the delays completely. Moreover, it is supported
* only on Linux. So we do our own read-ahead here.
*/
- buf += len;
- for (i = 0; i < 5 && buf < vsst->map + mmd->size; i++) {
- __a_unused volatile char x = *buf;
- buf += 4096;
+ if (mmd->current_chunk > 0) { /* chunk 0 might be on the heap */
+ buf += len;
+ for (i = 0; i < 5 && buf < vsst->map + mmd->size; i++) {
+ __a_unused volatile char x = *buf;
+ buf += 4096;
+ }
}
+ mmd->chunks_sent++;
+ mmd->current_chunk++;
}
}
<h1>Download</h1>
<hr>
-<p> Download the latest version from the
+<p> Clone the git repository by executing </p>
-<a href="versions/">download directory</a>
+<p> <b> git clone git://paraslash.systemlinux.org/git paraslash </b> </p>
-or grab a
-
-<a href="versions/paraslash-git.tar.bz2">tarball</a>
-
-of the current master branch. This version is expected to be more
-stable than any of the released versions.
-
-All regular releases are <a href="PUBLIC_KEY">cryptographically signed</a>.
-
-Anonymous (read-only)
-
-<a href="http://www.kernel.org/pub/software/scm/git/docs/">git</a>
-
-access is also available. Check out a copy with </p>
-
-<p>
-git clone git://paraslash.systemlinux.org/git paraslash
-</p>
+<p> Or grab the <a href="versions/paraslash-git.tar.bz2">tarball</a>
+of the current master branch, or download the latest version from the
+<a href="versions/">download directory</a>. All regular releases are
+<a href="PUBLIC_KEY">cryptographically signed</a>. Since development
+takes place in separate topic branches the master branch is expected
+to be more stable than any of the released versions. </p>