summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/Makefile.am6
-rw-r--r--src/pacat.c8
-rw-r--r--src/pactl.c141
-rw-r--r--src/polyp.c61
-rw-r--r--src/polyp.h3
-rw-r--r--src/protocol-native-spec.h1
-rw-r--r--src/protocol-native.c26
-rw-r--r--src/todo7
8 files changed, 246 insertions, 7 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index c973af52..7aa07215 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -18,7 +18,7 @@
AM_CFLAGS=-ansi -D_GNU_SOURCE
-bin_PROGRAMS = polypaudio pacat pacat-simple parec-simple
+bin_PROGRAMS = polypaudio pacat pactl pacat-simple parec-simple
pkglib_LTLIBRARIES=libiochannel.la \
libsocket-server.la \
@@ -246,6 +246,10 @@ pacat_SOURCES = pacat.c #$(libpolyp_la_SOURCES) $(libpolyp_error_la_SOURCES)
pacat_LDADD = libpolyp.la libpolyp-error.la
pacat_CFLAGS = $(AM_CFLAGS)
+pactl_SOURCES = pactl.c #$(libpolyp_la_SOURCES) $(libpolyp_error_la_SOURCES)
+pactl_LDADD = libpolyp.la libpolyp-error.la
+pactl_CFLAGS = $(AM_CFLAGS)
+
pacat_simple_SOURCES = pacat-simple.c #$(libpolyp_la_SOURCES) $(libpolyp_simple_la_SOURCES) $(libpolyp_error_la_SOURCES)
pacat_simple_LDADD = libpolyp-simple.la libpolyp-error.la
pacat_simple_CFLAGS = $(AM_CFLAGS)
diff --git a/src/pacat.c b/src/pacat.c
index 80d4835f..c9257d03 100644
--- a/src/pacat.c
+++ b/src/pacat.c
@@ -222,7 +222,7 @@ static void exit_signal_callback(void *id, int sig, void *userdata) {
}
int main(int argc, char *argv[]) {
- struct pa_mainloop* m;
+ struct pa_mainloop* m = NULL;
int ret = 1, r;
char *bn;
@@ -279,9 +279,11 @@ quit:
if (context)
pa_context_free(context);
- pa_signal_done();
- if (m)
+ if (m) {
+ pa_signal_done();
pa_mainloop_free(m);
+ }
+
if (buffer)
free(buffer);
diff --git a/src/pactl.c b/src/pactl.c
new file mode 100644
index 00000000..688e1104
--- /dev/null
+++ b/src/pactl.c
@@ -0,0 +1,141 @@
+#include <signal.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "polyp.h"
+#include "polyp-error.h"
+#include "mainloop.h"
+#include "mainloop-signal.h"
+
+static struct pa_context *context = NULL;
+static struct pa_mainloop_api *mainloop_api = NULL;
+
+static enum {
+ NONE,
+ EXIT,
+ STAT
+} action = NONE;
+
+static void quit(int ret) {
+ assert(mainloop_api);
+ mainloop_api->quit(mainloop_api, ret);
+}
+
+static void context_die_callback(struct pa_context *c, void *userdata) {
+ assert(c);
+ fprintf(stderr, "Connection to server shut down, exiting.\n");
+ quit(1);
+}
+
+static void context_drain_complete(struct pa_context *c, void *userdata) {
+ assert(c);
+ fprintf(stderr, "Connection to server shut down, exiting.\n");
+ quit(0);
+}
+
+static void drain(void) {
+ if (pa_context_drain(context, context_drain_complete, NULL) < 0)
+ quit(0);
+}
+
+static void stat_callback(struct pa_context *c, uint32_t blocks, uint32_t total, void *userdata) {
+ if (blocks == (uint32_t) -1) {
+ fprintf(stderr, "Failed to get statistics: %s\n", pa_strerror(pa_context_errno(c)));
+ quit(1);
+ return;
+ }
+
+ fprintf(stderr, "Currently in use: %u blocks containing %u bytes total.\n", blocks, total);
+ drain();
+}
+
+static void context_complete_callback(struct pa_context *c, int success, void *userdata) {
+ assert(c);
+
+ if (!success) {
+ fprintf(stderr, "Connection failed: %s\n", pa_strerror(pa_context_errno(c)));
+ goto fail;
+ }
+
+ fprintf(stderr, "Connection established.\n");
+
+ if (action == STAT)
+ pa_context_stat(c, stat_callback, NULL);
+ else {
+ assert(action == EXIT);
+ pa_context_exit(c);
+ drain();
+ }
+
+ return;
+
+fail:
+ quit(1);
+}
+
+static void exit_signal_callback(void *id, int sig, void *userdata) {
+ fprintf(stderr, "Got SIGINT, exiting.\n");
+ quit(0);
+
+}
+
+int main(int argc, char *argv[]) {
+ struct pa_mainloop* m = NULL;
+ int ret = 1, r;
+
+ if (argc >= 2) {
+ if (!strcmp(argv[1], "stat"))
+ action = STAT;
+ else if (!strcmp(argv[1], "exit"))
+ action = EXIT;
+ }
+
+ if (action == NONE) {
+ fprintf(stderr, "No valid action specified. Use one of: stat, exit\n");
+ goto quit;
+ }
+
+ if (!(m = pa_mainloop_new())) {
+ fprintf(stderr, "pa_mainloop_new() failed.\n");
+ goto quit;
+ }
+
+ mainloop_api = pa_mainloop_get_api(m);
+
+ r = pa_signal_init(mainloop_api);
+ assert(r == 0);
+ pa_signal_register(SIGINT, exit_signal_callback, NULL);
+ signal(SIGPIPE, SIG_IGN);
+
+ if (!(context = pa_context_new(mainloop_api, argv[0]))) {
+ fprintf(stderr, "pa_context_new() failed.\n");
+ goto quit;
+ }
+
+ if (pa_context_connect(context, NULL, context_complete_callback, NULL) < 0) {
+ fprintf(stderr, "pa_context_connext() failed.\n");
+ goto quit;
+ }
+
+ pa_context_set_die_callback(context, context_die_callback, NULL);
+
+ if (pa_mainloop_run(m, &ret) < 0) {
+ fprintf(stderr, "pa_mainloop_run() failed.\n");
+ goto quit;
+ }
+
+quit:
+ if (context)
+ pa_context_free(context);
+
+ if (m) {
+ pa_signal_done();
+ pa_mainloop_free(m);
+ }
+
+ return ret;
+}
diff --git a/src/polyp.c b/src/polyp.c
index 75187d79..bc6bff5a 100644
--- a/src/polyp.c
+++ b/src/polyp.c
@@ -53,6 +53,9 @@ struct pa_context {
void (*die_callback)(struct pa_context*c, void *userdata);
void *die_userdata;
+
+ void (*stat_callback)(struct pa_context*c, uint32_t count, uint32_t total, void *userdata);
+ void *stat_userdata;
uint8_t auth_cookie[PA_NATIVE_COOKIE_LENGTH];
};
@@ -133,6 +136,9 @@ struct pa_context *pa_context_new(struct pa_mainloop_api *mainloop, const char *
c->die_callback = NULL;
c->die_userdata = NULL;
+ c->stat_callback = NULL;
+ c->stat_userdata = NULL;
+
pa_check_for_sigpipe();
return c;
}
@@ -834,3 +840,58 @@ void pa_stream_drain(struct pa_stream *s, void (*complete) (struct pa_stream*s,
pa_pstream_send_tagstruct(s->context->pstream, t);
pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, stream_drain_callback, s);
}
+
+void pa_context_exit(struct pa_context *c) {
+ struct pa_tagstruct *t;
+ t = pa_tagstruct_new(NULL, 0);
+ assert(t);
+ pa_tagstruct_putu32(t, PA_COMMAND_EXIT);
+ pa_tagstruct_putu32(t, c->ctag++);
+ pa_pstream_send_tagstruct(c->pstream, t);
+}
+
+static void context_stat_callback(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
+ struct pa_context *c = userdata;
+ uint32_t total, count;
+ assert(pd && c);
+
+ if (command != PA_COMMAND_REPLY) {
+ if (handle_error(c, command, t) < 0) {
+ context_dead(c);
+ return;
+ }
+
+ if (c->stat_callback)
+ c->stat_callback(c, (uint32_t) -1, (uint32_t) -1, c->stat_userdata);
+ return;
+ }
+
+ if (pa_tagstruct_getu32(t, &count) < 0 ||
+ pa_tagstruct_getu32(t, &total) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ c->error = PA_ERROR_PROTOCOL;
+ context_dead(c);
+ return;
+ }
+
+ if (c->stat_callback)
+ c->stat_callback(c, count, total, c->stat_userdata);
+}
+
+void pa_context_stat(struct pa_context *c, void (*cb)(struct pa_context *c, uint32_t count, uint32_t total, void *userdata), void *userdata) {
+ uint32_t tag;
+ struct pa_tagstruct *t;
+
+ c->stat_callback = cb;
+ c->stat_userdata = userdata;
+
+ if (cb == NULL)
+ return;
+
+ t = pa_tagstruct_new(NULL, 0);
+ assert(t);
+ pa_tagstruct_putu32(t, PA_COMMAND_STAT);
+ pa_tagstruct_putu32(t, tag = c->ctag++);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_stat_callback, c);
+}
diff --git a/src/polyp.h b/src/polyp.h
index c49a72b2..5b730302 100644
--- a/src/polyp.h
+++ b/src/polyp.h
@@ -32,6 +32,9 @@ int pa_context_errno(struct pa_context *c);
int pa_context_is_pending(struct pa_context *c);
+void pa_context_exit(struct pa_context *c);
+void pa_context_stat(struct pa_context *c, void (*cb)(struct pa_context *c, uint32_t count, uint32_t total, void *userdata), void *userdata);
+
struct pa_stream;
struct pa_stream* pa_stream_new(
diff --git a/src/protocol-native-spec.h b/src/protocol-native-spec.h
index 78dc06c2..0a60fd80 100644
--- a/src/protocol-native-spec.h
+++ b/src/protocol-native-spec.h
@@ -18,6 +18,7 @@ enum {
PA_COMMAND_DRAIN_PLAYBACK_STREAM,
PA_COMMAND_PLAYBACK_STREAM_KILLED,
PA_COMMAND_RECORD_STREAM_KILLED,
+ PA_COMMAND_STAT,
PA_COMMAND_MAX
};
diff --git a/src/protocol-native.c b/src/protocol-native.c
index abd17026..56395e98 100644
--- a/src/protocol-native.c
+++ b/src/protocol-native.c
@@ -75,6 +75,7 @@ static void command_delete_record_stream(struct pa_pdispatch *pd, uint32_t comma
static void command_auth(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
static void command_set_name(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
static void command_lookup(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
+static void command_stat(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
static const struct pa_pdispatch_command command_table[PA_COMMAND_MAX] = {
[PA_COMMAND_ERROR] = { NULL },
@@ -91,6 +92,7 @@ static const struct pa_pdispatch_command command_table[PA_COMMAND_MAX] = {
[PA_COMMAND_SET_NAME] = { command_set_name },
[PA_COMMAND_LOOKUP_SINK] = { command_lookup },
[PA_COMMAND_LOOKUP_SOURCE] = { command_lookup },
+ [PA_COMMAND_STAT] = { command_stat },
};
/* structure management */
@@ -638,6 +640,30 @@ static void command_drain_playback_stream(struct pa_pdispatch *pd, uint32_t comm
}
}
+static void command_stat(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
+ struct connection *c = userdata;
+ assert(c && t);
+ struct pa_tagstruct *reply;
+
+ if (!pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ return;
+ }
+
+ if (!c->authorized) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
+ return;
+ }
+
+ reply = pa_tagstruct_new(NULL, 0);
+ assert(reply);
+ pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
+ pa_tagstruct_putu32(reply, tag);
+ pa_tagstruct_putu32(reply, pa_memblock_get_count());
+ pa_tagstruct_putu32(reply, pa_memblock_get_total());
+ pa_pstream_send_tagstruct(c->pstream, reply);
+}
+
/*** pstream callbacks ***/
static void pstream_packet_callback(struct pa_pstream *p, struct pa_packet *packet, void *userdata) {
diff --git a/src/todo b/src/todo
index 5eb4329d..107fa787 100644
--- a/src/todo
+++ b/src/todo
@@ -1,12 +1,11 @@
-- clean secure directory handling (with username)
+- pactl
- native library/protocol:
more functions (esp. latency)
-- prefix modules/libraries with pa_
-
- xmms+esound latency testing
+- prefix modules/libraries with pa_
- rename files
- svn-id and license in every file
- documentation
@@ -21,6 +20,8 @@
- make mcalign merge chunks
- modinfo
- move the global memblock statistics variables to the core
+- unix socket directories include user name
+
drivers:
- libao