- Shared memory areas are no longer restricted to 64K. We now
detect the maximal size of a shared memory area at runtime.
- 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.
--------------------------------------
0.4.8 (2011-08-19) "nested assignment"
/** \file net.h exported symbols from net.c */
/**
- * the buffer size of the sun_path component of struct sockaddr_un
+ * The buffer size of the sun_path component of struct sockaddr_un.
*
- * While glibc doesn't define \p UNIX_PATH_MAX, it
- * documents it has being limited to 108 bytes.
+ * While glibc doesn't define \p UNIX_PATH_MAX, it documents it has being
+ * limited to 108 bytes. On NetBSD it is only 104 bytes though. We trust \p
+ * UNIX_PATH_MAX if it is defined and use the size of the ->sun_path member
+ * otherwise. This should be safe everywhere.
*/
#ifndef UNIX_PATH_MAX
-#define UNIX_PATH_MAX 108
+#define UNIX_PATH_MAX (sizeof(((struct sockaddr_un *)0)->sun_path))
#endif
/* Userland defines for Linux DCCP support. */
exit(EXIT_FAILURE);
}
-static int init_afs(void)
+static int init_afs(int argc, char **argv)
{
int ret, afs_server_socket[2];
pid_t afs_pid;
if (afs_pid < 0)
exit(EXIT_FAILURE);
if (afs_pid == 0) { /* child (afs) */
+ int i;
+ for (i = argc - 1; i >= 0; i--)
+ memset(argv[i], 0, strlen(argv[i]));
+ sprintf(argv[0], "para_server (afs)");
close(afs_server_socket[0]);
afs_init(afs_socket_cookie, afs_server_socket[1]);
}
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 = {
gettimeofday(now, NULL);
set_server_start_time(now);
init_user_list(user_list_file);
+ init_server_command_task(argc, argv);
/* become daemon */
if (conf.daemon_given)
daemonize();
*/
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();
+ 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);
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 ""
}
return 1;
}
+#ifndef MAP_POPULATE
+#define MAP_POPULATE 0
+#endif
+
static void recv_afs_result(struct vss_task *vsst, fd_set *rfds)
{
int ret, passed_fd, shmid;
}
mmd->size = statbuf.st_size;
mmd->mtime = statbuf.st_mtime;
- ret = para_mmap(mmd->size, PROT_READ, MAP_PRIVATE, passed_fd,
- 0, &vsst->map);
+ ret = para_mmap(mmd->size, PROT_READ, MAP_PRIVATE | MAP_POPULATE,
+ passed_fd, 0, &vsst->map);
if (ret < 0)
goto err;
close(passed_fd);
}
mmd->chunks_sent++;
mmd->current_chunk++;
+ /*
+ * Prefault next chunk(s)
+ *
+ * If the backing device of the memory-mapped audio file is
+ * slow and read-ahead is turned off or prevented for some
+ * reason, e.g. due to memory pressure, it may take much longer
+ * than the chunk interval to get the next chunk on the wire,
+ * causing buffer underruns on the client side. Mapping the
+ * file with MAP_POPULATE seems to help a bit, but it does not
+ * 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;
+ }
}
}
<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>