summaryrefslogtreecommitdiff
path: root/src/main/remote.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/remote.c')
-rw-r--r--src/main/remote.c208
1 files changed, 208 insertions, 0 deletions
diff --git a/src/main/remote.c b/src/main/remote.c
new file mode 100644
index 0000000..4be342d
--- /dev/null
+++ b/src/main/remote.c
@@ -0,0 +1,208 @@
+/*
+ * Remote-control functions.
+ *
+ * Copyright 2012 Andrew Wood, distributed under the Artistic License 2.0.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "options.h"
+#include "pv.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <signal.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/msg.h>
+
+
+struct remote_msg {
+ long mtype;
+ unsigned char progress; /* progress bar flag */
+ unsigned char timer; /* timer flag */
+ unsigned char eta; /* ETA flag */
+ unsigned char rate; /* rate counter flag */
+ unsigned char average_rate; /* average rate counter flag */
+ unsigned char bytes; /* bytes transferred flag */
+ unsigned long long rate_limit; /* rate limit, in bytes per second */
+ unsigned long long buffer_size; /* buffer size, in bytes (0=default) */
+ unsigned long long size; /* total size of data */
+ double interval; /* interval between updates */
+ unsigned int width; /* screen width */
+ unsigned int height; /* screen height */
+ char name[256]; /* RATS: ignore */
+};
+
+
+static opts_t remote__opts = NULL;
+
+
+/*
+ * Return a key for use with msgget() which will be unique to the current
+ * user.
+ *
+ * We can't just use ftok() because the queue needs to be user-specific
+ * so that a user cannot send messages to another user's process, and we
+ * can't easily find out the terminal a given process is connected to in a
+ * cross-platform way.
+ */
+static key_t remote__genkey(opts_t opts)
+{
+ int uid;
+ key_t key;
+
+ uid = geteuid();
+ if (uid < 0)
+ uid = 0;
+
+ key = ftok("/tmp", 'P') | uid;
+
+ return key;
+}
+
+
+/*
+ * Return a message queue ID that is unique to the current user and the
+ * given process ID, or -1 on error.
+ */
+static int remote__msgget(opts_t opts)
+{
+ return msgget(remote__genkey(opts), IPC_CREAT | 0600);
+}
+
+
+/*
+ * Set the options of a remote process by setting up an IPC message queue,
+ * sending a message containing the new options, and then sending a SIGUSR1
+ * so the process knows it has a message to read.
+ *
+ * Returns nonzero on error.
+ */
+int remote_set(opts_t opts)
+{
+ struct remote_msg msgbuf;
+ int msgid;
+
+ memset(&msgbuf, 0, sizeof(msgbuf));
+ msgbuf.mtype = opts->remote;
+ msgbuf.progress = opts->progress;
+ msgbuf.timer = opts->timer;
+ msgbuf.eta = opts->eta;
+ msgbuf.rate = opts->rate;
+ msgbuf.average_rate = opts->average_rate;
+ msgbuf.rate_limit = opts->rate_limit;
+ msgbuf.buffer_size = opts->buffer_size;
+ msgbuf.size = opts->size;
+ msgbuf.interval = opts->interval;
+ msgbuf.width = opts->width;
+ msgbuf.height = opts->height;
+ if (opts->name != NULL) {
+ strncpy(msgbuf.name, opts->name, sizeof(msgbuf.name) - 1);
+ }
+
+ msgid = remote__msgget(opts);
+ if (msgid < 0) {
+ fprintf(stderr, "%s: %s\n", opts->program_name,
+ strerror(errno));
+ return 1;
+ }
+
+ if (msgsnd(msgid, &msgbuf, sizeof(msgbuf) - sizeof(long), 0) != 0) {
+ fprintf(stderr, "%s: %s\n", opts->program_name,
+ strerror(errno));
+ return 1;
+ }
+
+ if (kill(opts->remote, SIGUSR1) != 0) {
+ fprintf(stderr, "%s: %s\n", opts->program_name,
+ strerror(errno));
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Handle SIGUSR1 by replacing the current process's options with those
+ * being passed in via IPC message. The message queue is deleted afterwards.
+ */
+static void remote__sig_usr1(int s)
+{
+ struct remote_msg msgbuf;
+ struct msqid_ds qbuf;
+ ssize_t got;
+ int msgid;
+
+ memset(&msgbuf, 0, sizeof(msgbuf));
+
+ msgid = remote__msgget(remote__opts);
+ if (msgid < 0) {
+ return;
+ }
+
+ got =
+ msgrcv(msgid, &msgbuf, sizeof(msgbuf) - sizeof(long), getpid(),
+ IPC_NOWAIT);
+ if (got < 0) {
+ msgctl(msgid, IPC_RMID, &qbuf);
+ return;
+ }
+
+ if (msgctl(msgid, IPC_RMID, &qbuf) == 0) {
+ if (qbuf.msg_qnum < 1) {
+ msgctl(msgid, IPC_RMID, &qbuf);
+ }
+ }
+
+ if ((got < 1) || (remote__opts == NULL)) {
+ return;
+ }
+
+
+ remote__opts->progress = msgbuf.progress;
+ remote__opts->timer = msgbuf.timer;
+ remote__opts->eta = msgbuf.eta;
+ remote__opts->rate = msgbuf.rate;
+ remote__opts->average_rate = msgbuf.average_rate;
+
+ if (msgbuf.rate_limit > 0)
+ remote__opts->rate_limit = msgbuf.rate_limit;
+ if (msgbuf.buffer_size > 0) {
+ remote__opts->buffer_size = msgbuf.buffer_size;
+ pv_set_buffer_size(msgbuf.buffer_size, 1);
+ }
+ if (msgbuf.size > 0)
+ remote__opts->size = msgbuf.size;
+ if (msgbuf.interval > 0)
+ remote__opts->interval = msgbuf.interval;
+ if (msgbuf.width > 0)
+ remote__opts->width = msgbuf.width;
+ if (msgbuf.height > 0)
+ remote__opts->height = msgbuf.height;
+ if (msgbuf.name[0] != 0)
+ remote__opts->name = strdup(msgbuf.name);
+}
+
+
+/*
+ * Initialise handling of SIGUSR1 so that remote control messages can be
+ * processed correctly.
+ */
+void remote_sig_init(opts_t opts)
+{
+ struct sigaction sa;
+
+ remote__opts = opts;
+
+ sa.sa_handler = remote__sig_usr1;
+ sigemptyset(&(sa.sa_mask));
+ sa.sa_flags = 0;
+ sigaction(SIGUSR1, &sa, NULL);
+}
+
+/* EOF */