Cooking for about one month.
* audiod: Allow to specify usernames in --user-allow.
- On Linux systems, local sockets are now created in the
abstract name space by default. This allows to get rid of
the socket specials in /var/paraslash.
++ - The --user-allow option of para_audiod now accepts also
++ usernames rather than only user IDs.
+ - New autoconf macros to avoid duplication in configure.ac.
+ - Status items (as shown by para_gui) are updated correctly
+ when the meta information of the current audio changes.
+ - para_server and para_audiod no longer refuse to start in
+ the background if no log file is given. Instead, all log
+ messages go to /dev/null in this case.
+
+
Download: ./releases/paraslash-git.tar.bz2 (tarball)
+
------------------------------------------
0.5.4 (2015-01-23) "exponential alignment"
------------------------------------------
#include <sys/un.h>
#include <netdb.h>
#include <signal.h>
+ #include <pwd.h>
#include "para.h"
#include "error.h"
struct timeval restart_barrier;
};
+/* Describes one instance of a receiver-filter-writer chain. */
+struct slot_info {
+ /* Number of the audio format in this slot. */
+ int format;
+ /* The stream_start status item announced by para_server. */
+ struct timeval server_stream_start;
+ /* The offset status item announced by para_server. */
+ unsigned offset_seconds;
+ /* The seconds_total status item announced by para_server. */
+ unsigned seconds_total;
+ /* The receiver info associated with this slot. */
+ struct receiver_node *receiver_node;
+ /* The array of filter nodes. */
+ struct filter_node *fns;
+ /* The array of writers attached to the last filter. */
+ struct writer_node *wns;
+};
+
+/** Maximal number of simultaneous instances. */
+#define MAX_STREAM_SLOTS 5
+
+/** Iterate over all slots. */
+#define FOR_EACH_SLOT(_slot) for (_slot = 0; _slot < MAX_STREAM_SLOTS; _slot++)
+
/**
* para_audiod uses \p MAX_STREAM_SLOTS different slots, each of which may
* be associated with a receiver/filter/writer triple. This array holds all
static struct status_task status_task_struct;
+ static uid_t *uid_whitelist;
+
/**
* the task that calls the status command of para_server
*
return -E_UNSUPPORTED_AUDIO_FORMAT;
}
+/**
+ * Return the flags for the \a decoder_flags status item.
+ *
+ * Allocates a string which contains one octal digit per slot. Bit zero (value
+ * 1) is set if a receiver is active. Bit one (value 2) and bit three (value 4)
+ * have the analogous meaning for filter and writer, respectively.
+ *
+ * \return String that must be freed by the caller.
+ */
+__malloc char *audiod_get_decoder_flags(void)
+{
+ int i;
+ char flags[MAX_STREAM_SLOTS + 1];
+
+ FOR_EACH_SLOT(i) {
+ struct slot_info *s = &slot[i];
+ char flag = '0';
+ if (s->receiver_node)
+ flag += 1;
+ if (s->fns)
+ flag += 2;
+ if (s->wns)
+ flag += 4;
+ flags[i] = flag;
+ }
+ flags[MAX_STREAM_SLOTS] = '\0';
+ return para_strdup(flags);
+}
+
static int get_matching_audio_format_nums(const char *re)
{
int i, ret;
return ret;
}
+static int get_play_time_slot_num(void)
+{
+ int i, oldest_slot = -1;
+ struct timeval oldest_wstime = {0, 0};
+
+ FOR_EACH_SLOT(i) {
+ struct slot_info *s = &slot[i];
+ struct timeval wstime;
+ if (!s->wns || !s->wns[0].btrn)
+ continue;
+ btr_get_node_start(s->wns[0].btrn, &wstime);
+ if (oldest_slot >= 0 && tv_diff(&wstime, &oldest_wstime, NULL) > 0)
+ continue;
+ oldest_wstime = wstime;
+ oldest_slot = i;
+ }
+ return oldest_slot;
+}
+
/**
- * Compute the play time based on information of the given slot.
- *
- * \param slot_num The slot number (negative means: no slot).
+ * Compute the play time based on information of the current slot.
*
* This computes a string of the form "0:07 [3:33] (3%/3:40)" using information
* from the status items received from para_server and the start time of the
- * (first) writer of the given slot.
+ * (first) writer of the current slot.
*
* It has to take into account that the stream was probably not started at
* the beginning of the file, that the clock between the server and the client
* host may differ and that playback of the stream was delayed, e.g. because
- * the prebuffer filter is used in the filter configuration of the given slot.
+ * the prebuffer filter is used in the filter configuration.
*
- * If no writer is active in the given slot, or \a slot_num is negative
- * (indicating that para_audiod runs in standby mode), an approximation based
- * only on the status items is computed and the returned string is prefixed
- * with "~".
+ * If no writer is active, for example because para_audiod runs in standby
+ * mode, an approximation based only on the status items is computed and the
+ * returned string is prefixed with "~".
*
* \return A string that must be freed by the caller.
*/
-char *get_time_string(int slot_num)
+char *get_time_string(void)
{
int ret, seconds = 0, length;
struct timeval *tmp, sum, sss, /* server stream start */
wstime, /* writer start time */
wtime, /* now - writer start */
rskip; /* receiver start - sss */
+ int slot_num = get_play_time_slot_num();
struct slot_info *s = slot_num < 0? NULL : &slot[slot_num];
char *msg;
static void parse_config_or_die(void)
{
- int ret;
+ int ret, i;
char *config_file;
struct audiod_cmdline_parser_params params = {
.override = 0,
ret = file_exists(config_file);
if (conf.config_file_given && !ret) {
PARA_EMERG_LOG("can not read config file %s\n", config_file);
+ free(config_file);
goto err;
}
if (ret) {
daemon_set_loglevel(conf.loglevel_arg);
}
free(config_file);
+ if (conf.user_allow_given > 0) {
+ uid_whitelist = para_malloc(conf.user_allow_given
+ * sizeof(uid_t));
+ for (i = 0; i < conf.user_allow_given; i++) {
+ int32_t val;
+ struct passwd *pw;
+ ret = para_atoi32(conf.user_allow_arg[i], &val);
+ if (ret >= 0) {
+ uid_whitelist[i] = val;
+ continue;
+ }
+ errno = 0; /* see getpwnam(3) */
+ pw = getpwnam(conf.user_allow_arg[i]);
+ if (!pw) {
+ PARA_EMERG_LOG("invalid username: %s\n",
+ conf.user_allow_arg[i]);
+ goto err;
+ }
+ uid_whitelist[i] = pw->pw_uid;
+ }
+ }
return;
err:
- free(config_file);
exit(EXIT_FAILURE);
}
for (i = 0; i < 2; i++) {
if (ct->fd[i] < 0)
continue;
- ret = handle_connect(ct->fd[i], &s->rfds);
+ ret = handle_connect(ct->fd[i], &s->rfds, uid_whitelist);
if (ret < 0) {
PARA_ERROR_LOG("%s\n", para_strerror(-ret));
if (ret == -E_AUDIOD_TERM) {
close_unused_slots();
audiod_cmdline_parser_free(&conf);
close_stat_clients();
+ free(uid_whitelist);
}
/*
/** array of audio format names supported by para_audiod */
extern const char *audio_formats[];
-/** maximal number of simultaneous instances */
-#define MAX_STREAM_SLOTS 5
-
/**
* the possible modes of operation
*
const char *help;
};
-/**
- * Describes one instance of a receiver-filter-writer chain.
- *
- * \sa receiver_node, receiver, filter, filter_node, writer, writer_node,
- * writer_node_group.
- */
-struct slot_info {
- /** Number of the audio format in this slot. */
- int format;
- /** The stream_start status item announced by para_server. */
- struct timeval server_stream_start;
- /** The offset status item announced by para_server. */
- unsigned offset_seconds;
- /** The seconds_total status item announced by para_server. */
- unsigned seconds_total;
- /** The receiver info associated with this slot. */
- struct receiver_node *receiver_node;
- /** The array of filter nodes. */
- struct filter_node *fns;
- /** The array of writers attached to the last filter. */
- struct writer_node *wns;
-};
-
-extern struct slot_info slot[MAX_STREAM_SLOTS];
+__malloc char *audiod_get_decoder_flags(void);
extern struct audiod_args_info conf;
extern int audiod_status;
- int handle_connect(int accept_fd, fd_set *rfds);
+ int handle_connect(int accept_fd, fd_set *rfds, uid_t *uid_whitelist);
void audiod_status_dump(bool force);
-char *get_time_string(int slot_num);
+char *get_time_string(void);
struct btr_node *audiod_get_btr_root(void);
void stat_client_write_item(int item_num);
void clear_and_dump_items(void);
void close_stat_clients(void);
-
-/** iterate over all slots */
-#define FOR_EACH_SLOT(_slot) for (_slot = 0; _slot < MAX_STREAM_SLOTS; _slot++)
return para_strdup(status);
}
-static int get_play_time_slot_num(void)
-{
- int i, oldest_slot = -1;
- struct timeval oldest_wstime = {0, 0};
-
- FOR_EACH_SLOT(i) {
- struct slot_info *s = &slot[i];
- struct timeval wstime;
- if (!s->wns || !s->wns[0].btrn)
- continue;
- btr_get_node_start(s->wns[0].btrn, &wstime);
- if (oldest_slot >= 0 && tv_diff(&wstime, &oldest_wstime, NULL) > 0)
- continue;
- oldest_wstime = wstime;
- oldest_slot = i;
- }
- //PARA_CRIT_LOG("oldest slot: %d\n", oldest_slot);
- return oldest_slot;
-}
-
-__malloc static char *decoder_flags(void)
-{
- int i;
- char flags[MAX_STREAM_SLOTS + 1];
-
- FOR_EACH_SLOT(i) {
- struct slot_info *s = &slot[i];
- char flag = '0';
- if (s->receiver_node)
- flag += 1;
- if (s->fns)
- flag += 2;
- if (s->wns)
- flag += 4;
- flags[i] = flag;
- }
- flags[MAX_STREAM_SLOTS] = '\0';
- return para_strdup(flags);
-}
-
static int dump_commands(int fd)
{
char *buf = para_strdup(""), *tmp = NULL;
return ret;
}
- static int check_perms(uid_t uid)
+ static int check_perms(uid_t uid, uid_t *whitelist)
{
int i;
if (!conf.user_allow_given)
return 1;
for (i = 0; i < conf.user_allow_given; i++)
- if (uid == conf.user_allow_arg[i])
+ if (uid == whitelist[i])
return 1;
return -E_UCRED_PERM;
}
*
* \param accept_fd The fd to accept connections on.
* \param rfds If \a accept_fd is not set in \a rfds, do nothing.
+ * \param uid_whitelist Array of UIDs which are allowed to connect.
*
* 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,
*
* \sa para_accept(), recv_cred_buffer()
* */
- int handle_connect(int accept_fd, fd_set *rfds)
+ int handle_connect(int accept_fd, fd_set *rfds, uid_t *uid_whitelist)
{
int i, argc, ret, clifd;
char buf[MAXLINE], **argv = NULL;
goto out;
uid = ret;
PARA_INFO_LOG("connection from user %i, buf: %s\n", ret, buf);
- ret = check_perms(uid);
+ ret = check_perms(uid, uid_whitelist);
if (ret < 0)
goto out;
ret = create_argv(buf, "\n", &argv);
*/
void audiod_status_dump(bool force)
{
- int slot_num = get_play_time_slot_num();
char *old, *new;
old = stat_item_values[SI_PLAY_TIME];
- new = get_time_string(slot_num);
+ new = get_time_string();
if (new) {
if (force || !old || strcmp(old, new)) {
free(old);
free(new);
old = stat_item_values[SI_DECODER_FLAGS];
- new = decoder_flags();
+ new = audiod_get_decoder_flags();
if (force || !old || strcmp(old, new)) {
free(old);
stat_item_values[SI_DECODER_FLAGS] = new;