From d8f13006614dca80de8903f5380b7cb718f26fcc Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 15 Jul 2004 20:51:55 +0000 Subject: add pactl tool git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@71 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/Makefile.am | 6 +- src/pacat.c | 8 ++- src/pactl.c | 141 +++++++++++++++++++++++++++++++++++++++++++++ src/polyp.c | 61 ++++++++++++++++++++ src/polyp.h | 3 + src/protocol-native-spec.h | 1 + src/protocol-native.c | 26 +++++++++ src/todo | 7 ++- 8 files changed, 246 insertions(+), 7 deletions(-) create mode 100644 src/pactl.c (limited to 'src') 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 +#include +#include +#include +#include +#include +#include + +#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 -- cgit