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.
+
[tarball](./releases/paraslash-git.tar.xz)
----------------------------------
#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)
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_select(int max_fileno, fd_set *readfds, fd_set *writefds,
- struct timeval *timeout_tv)
++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 para_select(max_fileno + 1, readfds, writefds, timeout_tv);
++ 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.select_function = afs_select;
- 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)
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);
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;
#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;
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;
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;
if (count > 5) {
int s = tv_diff(&diff, &stat_task->sa_time_diff, &tmp);
if (tv_diff(&max_deviation, &tmp, NULL) < 0)
- PARA_WARNING_LOG("time diff jump: %lums\n",
- s * tv2ms(&tmp));
+ PARA_WARNING_LOG("time diff jump: %c%lums\n",
+ s < 0? '-' : '+', tv2ms(&tmp));
}
count++;
sa_time_diff_sign = tv_convex_combination(
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_ERROR_LOG("%s\n", para_strerror(-ret));
+ PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
if (ret == -E_AUDIOD_TERM) {
task_notify_all(s, -ret);
return ret;
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);
}
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);
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)
{
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);
#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"
"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)
{
PARA_ERROR(BAD_CT, "invalid chunk table or bad FEC configuration"), \
PARA_ERROR(BAD_FEATURE, "invalid feature request"), \
PARA_ERROR(BAD_FEC_HEADER, "invalid fec header"), \
- PARA_ERROR(BAD_LL, "invalid loglevel"), \
PARA_ERROR(BAD_PATH, "invalid path"), \
PARA_ERROR(BAD_PRIVATE_KEY, "invalid private key"), \
PARA_ERROR(BAD_SAMPLE_FORMAT, "sample format not supported"), \
PARA_ERROR(MP3DEC_CORRUPT, "too many corrupt frames"), \
PARA_ERROR(MP3DEC_EOF, "mp3dec: end of file"), \
PARA_ERROR(MP3_INFO, "could not read mp3 info"), \
- PARA_ERROR(MP4FF_BAD_CHANNEL_COUNT, "mp4ff: invalid number of channels"), \
- PARA_ERROR(MP4FF_BAD_SAMPLE, "mp4ff: invalid sample number"), \
- PARA_ERROR(MP4FF_BAD_SAMPLERATE, "mp4ff: invalid sample rate"), \
- PARA_ERROR(MP4FF_BAD_SAMPLE_COUNT, "mp4ff: invalid number of samples"), \
- PARA_ERROR(MP4FF_META_READ, "mp4ff: could not read mp4 metadata"), \
- PARA_ERROR(MP4FF_META_WRITE, "mp4ff: could not update mp4 metadata"), \
- PARA_ERROR(MP4FF_OPEN, "mp4ff: open failed"), \
- PARA_ERROR(MP4FF_TRACK, "mp4ff: no audio track"), \
+ PARA_ERROR(MP4_READ, "mp4: read error or unexpected end of file"), \
+ PARA_ERROR(MP4_CORRUPT, "invalid/corrupt mp4 file"), \
+ PARA_ERROR(MP4_BAD_SAMPLE, "mp4: invalid sample number"), \
+ PARA_ERROR(MP4_BAD_SAMPLERATE, "mp4: invalid sample rate"), \
+ PARA_ERROR(MP4_BAD_SAMPLE_COUNT, "mp4: invalid number of samples"), \
+ PARA_ERROR(MP4_TRACK, "mp4: no audio track"), \
+ PARA_ERROR(MP4_MISSING_ATOM, "mp4: essential atom not found"), \
PARA_ERROR(MPI_SCAN, "could not scan multi-precision integer"), \
PARA_ERROR(NAME_TOO_LONG, "name too long for struct sockaddr_un"), \
PARA_ERROR(NO_AFHI, "audio format handler info required"), \
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;
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);
+ }
void i9e_close(void);
void i9e_signal_dispatch(int sig_num);
__printf_2_3 void i9e_log(int ll, const char* fmt,...);
-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 i9e_extract_completions(const char *word, char **string_list,
char ***result);
char **i9e_complete_commands(const char *word, struct i9e_completer *completers);
struct i9e_completion_result *cr);
int i9e_print_completions(struct i9e_completer *completers);
int i9e_get_error(void);
+ void i9e_ll_completer(struct i9e_completion_info *ci,
+ struct i9e_completion_result *cr);
#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->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);
return tmp;
}
-/**
- * Paraslash's version of dirname().
- *
- * \param name Pointer to the full path.
- *
- * Compute the directory component of \p name.
- *
- * \return If \a name is \p NULL or the empty string, return \p NULL.
- * Otherwise, Make a copy of \a name and return its directory component. Caller
- * is responsible to free the result.
- */
-__must_check __malloc char *para_dirname(const char *name)
-{
- char *p, *ret;
-
- if (!name || !*name)
- return NULL;
- ret = para_strdup(name);
- p = strrchr(ret, '/');
- if (!p)
- *ret = '\0';
- else
- *p = '\0';
- return ret;
-}
-
-/**
- * Paraslash's version of basename().
- *
- * \param name Pointer to the full path.
- *
- * Compute the filename component of \a name.
- *
- * \return \p NULL if (a) \a name is the empty string or \p NULL, or (b) name
- * ends with a slash. Otherwise, a pointer within \a name is returned. Caller
- * must not free the result.
- */
-__must_check char *para_basename(const char *name)
-{
- char *ret;
-
- if (!name || !*name)
- return NULL;
- ret = strrchr(name, '/');
- if (!ret)
- return (char *)name;
- ret++;
- return ret;
-}
-
/**
* Get the logname of the current user.
*
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,
__printf_2_3 unsigned xasprintf(char **result, const char *fmt, ...);
__must_check __malloc __printf_1_2 char *make_message(const char *fmt, ...);
__must_check __malloc char *para_strcat(char *a, const char *b);
-__must_check __malloc char *para_dirname(const char *name);
-__must_check char *para_basename(const char *name);
__must_check __malloc char *para_logname(void);
__must_check __malloc char *para_homedir(void);
__malloc char *para_hostname(void);
__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);