kill client/...
autoload management
- more complete pactl
-- cleanup tagstruct and modargs (add s32, pa_volume_t, pa_usec_t)
-- remove all gcc warnings
-- make fragments settings runtime configurable
- logging
-- automatic termination of daemon if unused
- add sample directory
-- paman: show scache and sample size
- add timing parameter to write callback of stream in client API
** later ***
- xmlrpc/http
+- dbus
- slp/rendezvous
- modinfo
- make alsa modules use mmap
fprintf(stderr, "client: created %u \"%s\"\n", c->index, c->name);
pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_NEW, c->index);
+
+ pa_core_check_quit(core);
return c;
}
assert(c && c->core);
pa_idxset_remove_by_data(c->core->clients, c, NULL);
+
+ pa_core_check_quit(c->core);
+
fprintf(stderr, "client: freed %u \"%s\"\n", c->index, c->name);
pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_REMOVE, c->index);
pa_xfree(c->name);
pa_xfree(c);
+
}
void pa_client_kill(struct pa_client *c) {
" -d Disallow module loading after startup\n"
" -f Dont quit when the startup fails\n"
" -v Verbose startup\n"
+ " -X SECS Terminate the daemon after the last client quit and this time passed\n"
" -h Show this help\n"
" -V Show version\n", e, cfg);
cmdline->version =
cmdline->disallow_module_loading = 0;
cmdline->fail = 1;
+ cmdline->quit_after_last_client_time = -1;
buf = pa_strbuf_new();
assert(buf);
- while ((c = getopt(argc, argv, "L:F:CDhfvrRVnd")) != -1) {
+ while ((c = getopt(argc, argv, "L:F:CDhfvrRVndX:")) != -1) {
switch (c) {
case 'L':
pa_strbuf_printf(buf, "load %s\n", optarg);
case 'd':
cmdline->disallow_module_loading = 1;
break;
+ case 'X':
+ cmdline->quit_after_last_client_time = atoi(optarg);
+ break;
default:
goto fail;
}
struct pa_cmdline {
- int daemonize, help, fail, verbose, high_priority, stay_root, version, disallow_module_loading;
+ int daemonize, help, fail, verbose, high_priority, stay_root, version, disallow_module_loading, quit_after_last_client_time;
char *cli_commands;
};
c->memblock_stat = pa_memblock_stat_new();
c->disallow_module_loading = 0;
+
+ c->quit_event = NULL;
+ c->quit_after_last_client_time = -1;
pa_check_for_sigpipe();
pa_namereg_free(c);
pa_autoload_free(c);
pa_subscription_free_all(c);
+
+ if (c->quit_event) {
+ c->mainloop->time_free(c->quit_event);
+ c->quit_event = NULL;
+ }
pa_xfree(c->default_source_name);
pa_xfree(c->default_sink_name);
pa_xfree(c);
}
+static void quit_callback(struct pa_mainloop_api*m, struct pa_time_event *e, const struct timeval *tv, void *userdata) {
+ struct pa_core *c = userdata;
+ assert(c->quit_event = e);
+
+ m->quit(m, 0);
+}
+
+void pa_core_check_quit(struct pa_core *c) {
+ assert(c);
+
+ if (!c->quit_event && c->quit_after_last_client_time >= 0 && pa_idxset_ncontents(c->clients) == 0) {
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ tv.tv_sec+= c->quit_after_last_client_time;
+ c->quit_event = c->mainloop->time_new(c->mainloop, &tv, quit_callback, c);
+ } else if (c->quit_event && pa_idxset_ncontents(c->clients) > 0) {
+ c->mainloop->time_free(c->quit_event);
+ c->quit_event = NULL;
+ }
+}
struct pa_memblock_stat *memblock_stat;
int disallow_module_loading;
+ int quit_after_last_client_time;
+
+ struct pa_time_event *quit_event;
+
};
struct pa_core* pa_core_new(struct pa_mainloop_api *m);
void pa_core_free(struct pa_core*c);
+void pa_core_check_quit(struct pa_core *c);
+
#endif
pa_loop_write(daemon_pipe[1], &retval, sizeof(retval));
c->disallow_module_loading = cmdline->disallow_module_loading;
+ c->quit_after_last_client_time = cmdline->quit_after_last_client_time;
fprintf(stderr, __FILE__": mainloop entry.\n");
if (pa_mainloop_run(mainloop, &retval) < 0)
return 0;
}
+int pa_modargs_get_value_s32(struct pa_modargs *ma, const char *key, int32_t *value) {
+ const char *v;
+ char *e;
+ signed long l;
+ assert(ma && key && value);
+
+ if (!(v = pa_modargs_get_value(ma, key, NULL)))
+ return 0;
+
+ if (!*v)
+ return -1;
+
+ l = strtol(v, &e, 0);
+ if (*e)
+ return -1;
+
+ *value = (int32_t) l;
+ return 0;
+}
+
+int pa_modargs_get_value_boolean(struct pa_modargs *ma, const char *key, int *value) {
+ const char *v;
+ assert(ma && key && value);
+
+ if (!(v = pa_modargs_get_value(ma, key, NULL)))
+ return 0;
+
+ if (!*v)
+ return -1;
+
+ if (!strcmp(v, "1") || !strcasecmp(v, "yes") || !strcasecmp(v, "y") || !strcasecmp(v, "on"))
+ *value = 1;
+ else if (!strcmp(v, "0") || !strcasecmp(v, "no") || !strcasecmp(v, "n") || !strcasecmp(v, "off"))
+ *value = 0;
+ else
+ return -1;
+
+ return 0;
+}
+
int pa_modargs_get_sample_spec(struct pa_modargs *ma, struct pa_sample_spec *rss) {
const char *format;
uint32_t channels;
const char *pa_modargs_get_value(struct pa_modargs *ma, const char *key, const char *def);
int pa_modargs_get_value_u32(struct pa_modargs *ma, const char *key, uint32_t *value);
+int pa_modargs_get_value_s32(struct pa_modargs *ma, const char *key, int32_t *value);
+int pa_modargs_get_value_boolean(struct pa_modargs *ma, const char *key, int *value);
int pa_modargs_get_sample_spec(struct pa_modargs *ma, struct pa_sample_spec *ss);
goto finish;
}
- if (pa_modargs_get_value_u32(ma, "fd", &fd) < 0) {
+ if (pa_modargs_get_value_s32(ma, "fd", &fd) < 0) {
fprintf(stderr, __FILE__": invalid file descriptor.\n");
goto finish;
}
goto fail;
}
- if (pa_modargs_get_value_u32(ma, "record", &record) < 0 || pa_modargs_get_value_u32(ma, "playback", &playback) < 0) {
+ if (pa_modargs_get_value_boolean(ma, "record", &record) < 0 || pa_modargs_get_value_boolean(ma, "playback", &playback) < 0) {
fprintf(stderr, __FILE__": record= and playback= expect numeric arguments.\n");
goto fail;
}
- mode = (playback&&record) ? O_RDWR : (playback ? O_WRONLY : (record ? O_RDONLY : 0));
- if (mode == 0) {
+ if (!playback && !record) {
fprintf(stderr, __FILE__": neither playback nor record enabled for device.\n");
goto fail;
}
+ mode = (playback&&record) ? O_RDWR : (playback ? O_WRONLY : (record ? O_RDONLY : 0));
+
nfrags = 12;
frag_size = 1024;
- if (pa_modargs_get_value_u32(ma, "fragments", &nfrags) < 0 || nfrags < 2 || pa_modargs_get_value_u32(ma, "fragment_size", &frag_size) < 0 || frag_size < 1) {
+ if (pa_modargs_get_value_s32(ma, "fragments", &nfrags) < 0 || nfrags < 2 || pa_modargs_get_value_s32(ma, "fragment_size", &frag_size) < 0 || frag_size < 1) {
fprintf(stderr, __FILE__": failed to parse fragments arguments\n");
goto fail;
}
int fd = -1;
int nfrags, frag_size, in_frag_size, out_frag_size;
int mode;
- uint32_t record = 1, playback = 1;
+ int record = 1, playback = 1;
struct pa_sample_spec ss;
struct pa_modargs *ma = NULL;
assert(c && m);
goto fail;
}
- if (pa_modargs_get_value_u32(ma, "record", &record) < 0 || pa_modargs_get_value_u32(ma, "playback", &playback) < 0) {
+ if (pa_modargs_get_value_boolean(ma, "record", &record) < 0 || pa_modargs_get_value_boolean(ma, "playback", &playback) < 0) {
fprintf(stderr, __FILE__": record= and playback= expect numeric argument.\n");
goto fail;
}
- mode = (playback&&record) ? O_RDWR : (playback ? O_WRONLY : (record ? O_RDONLY : 0));
- if (mode == 0) {
+ if (!playback && !record) {
fprintf(stderr, __FILE__": neither playback nor record enabled for device.\n");
goto fail;
}
+ mode = (playback&&record) ? O_RDWR : (playback ? O_WRONLY : (record ? O_RDONLY : 0));
+
nfrags = 12;
frag_size = 1024;
- if (pa_modargs_get_value_u32(ma, "fragments", &nfrags) < 0 || nfrags < 2 || pa_modargs_get_value_u32(ma, "fragment_size", &frag_size) < 0 || frag_size < 1) {
+ if (pa_modargs_get_value_s32(ma, "fragments", &nfrags) < 0 || nfrags < 2 || pa_modargs_get_value_s32(ma, "fragment_size", &frag_size) < 0 || frag_size < 1) {
fprintf(stderr, __FILE__": failed to parse fragments arguments\n");
goto fail;
}
static struct pa_socket_server *create_socket_server(struct pa_core *c, struct pa_modargs *ma) {
struct pa_socket_server *s;
#ifdef USE_TCP_SOCKETS
- uint32_t loopback = 1, port = IPV4_PORT;
+ int loopback = 1;
+ uint32_t port = IPV4_PORT;
- if (pa_modargs_get_value_u32(ma, "loopback", &loopback) < 0) {
+ if (pa_modargs_get_value_boolean(ma, "loopback", &loopback) < 0) {
fprintf(stderr, "loopback= expects a numerical argument.\n");
return NULL;
}
- if (pa_modargs_get_value_u32(ma, "port", &port) < 0) {
- fprintf(stderr, "port= expects a numerical argument.\n");
+ if (pa_modargs_get_value_u32(ma, "port", &port) < 0 || port < 1 || port > 0xFFFF) {
+ fprintf(stderr, "port= expects a numerical argument between 1 and 65535.\n");
return NULL;
}
static void pa_module_free(struct pa_module *m) {
assert(m && m->done && m->core);
+
+ if (m->core->disallow_module_loading)
+ return;
+
m->done(m->core, m);
lt_dlclose(m->dl);
pa_xfree(m);
}
-
void pa_module_unload(struct pa_core *c, struct pa_module *m) {
assert(c && m);
pa_usec_t buffer_usec; /**< Time in usecs the current buffer takes to play */
pa_usec_t sink_usec; /**< Time in usecs a sample takes to be played on the sink. */
int playing; /**< Non-zero when the stream is currently playing */
- int queue_length; /**< Queue size in bytes. */
+ uint32_t queue_length; /**< Queue size in bytes. */
};
PA_C_DECL_END
pa_tagstruct_gets(t, &i.name) < 0 ||
pa_tagstruct_gets(t, &i.argument) < 0 ||
pa_tagstruct_getu32(t, &i.n_used) < 0 ||
- pa_tagstruct_getu32(t, &i.auto_unload) < 0) {
+ pa_tagstruct_get_boolean(t, &i.auto_unload) < 0) {
pa_context_fail(o->context, PA_ERROR_PROTOCOL);
goto finish;
}
uint32_t index; /**< Index of the module */
const char*name, /**< Name of the module */
*argument; /**< Argument string of the module */
- uint32_t n_used, /**< Usage counter or PA_INVALID_INDEX */
- auto_unload; /**< Non-zero if this is an autoloaded module */
+ uint32_t n_used; /**< Usage counter or PA_INVALID_INDEX */
+ int auto_unload; /**< Non-zero if this is an autoloaded module */
};
/** Get some information about a module by its index */
} else if (pa_tagstruct_getu32(t, &i.buffer_usec) < 0 ||
pa_tagstruct_getu32(t, &i.sink_usec) < 0 ||
- pa_tagstruct_getu32(t, &i.playing) < 0 ||
+ pa_tagstruct_get_boolean(t, &i.playing) < 0 ||
pa_tagstruct_getu32(t, &i.queue_length) < 0 ||
!pa_tagstruct_eof(t)) {
pa_context_fail(o->context, PA_ERROR_PROTOCOL);
pa_tagstruct_putu32(reply, tag);
pa_tagstruct_putu32(reply, pa_sink_input_get_latency(s->sink_input));
pa_tagstruct_putu32(reply, pa_sink_get_latency(s->sink_input->sink));
- pa_tagstruct_putu32(reply, pa_memblockq_is_readable(s->memblockq));
+ pa_tagstruct_put_boolean(reply, pa_memblockq_is_readable(s->memblockq));
pa_tagstruct_putu32(reply, pa_memblockq_get_length(s->memblockq));
pa_pstream_send_tagstruct(c->pstream, reply);
}
pa_tagstruct_puts(t, module->name);
pa_tagstruct_puts(t, module->argument ? module->argument : "");
pa_tagstruct_putu32(t, module->n_used);
- pa_tagstruct_putu32(t, module->auto_unload);
+ pa_tagstruct_put_boolean(t, module->auto_unload);
}
static void sink_input_fill_tagstruct(struct pa_tagstruct *t, struct pa_sink_input *s) {
static struct pa_protocol_native* protocol_new_internal(struct pa_core *c, struct pa_module *m, struct pa_modargs *ma) {
struct pa_protocol_native *p;
- uint32_t public;
+ int public;
assert(c && ma);
- if (pa_modargs_get_value_u32(ma, "public", &public) < 0) {
+ if (pa_modargs_get_value_boolean(ma, "public", &public) < 0) {
fprintf(stderr, __FILE__": public= expects numeric argument.\n");
return NULL;
}
struct pa_protocol_simple* pa_protocol_simple_new(struct pa_core *core, struct pa_socket_server *server, struct pa_module *m, struct pa_modargs *ma) {
struct pa_protocol_simple* p = NULL;
- uint32_t enable;
+ int enable;
assert(core && server && ma);
p = pa_xmalloc0(sizeof(struct pa_protocol_simple));
p->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));
enable = 0;
- if (pa_modargs_get_value_u32(ma, "record", &enable) < 0) {
+ if (pa_modargs_get_value_boolean(ma, "record", &enable) < 0) {
fprintf(stderr, __FILE__": record= expects a numeric argument.\n");
goto fail;
}
p->mode = enable ? RECORD : 0;
enable = 1;
- if (pa_modargs_get_value_u32(ma, "playback", &enable) < 0) {
+ if (pa_modargs_get_value_boolean(ma, "playback", &enable) < 0) {
fprintf(stderr, __FILE__": playback= expects a numeric argument.\n");
goto fail;
}
uint32_t pa_scache_total_size(struct pa_core *c) {
struct pa_scache_entry *e;
uint32_t index;
- uint32_t sum;
+ uint32_t sum = 0;
if (!c->scache)
return 0;
TAG_U8 = 'B',
TAG_S8 = 'b',
TAG_SAMPLE_SPEC = 'a',
- TAG_ARBITRARY = 'x'
+ TAG_ARBITRARY = 'x',
+ TAG_BOOLEAN_TRUE = '1',
+ TAG_BOOLEAN_FALSE = '0',
};
struct pa_tagstruct {
t->length += 7;
}
-
void pa_tagstruct_put_arbitrary(struct pa_tagstruct *t, const void *p, size_t length) {
assert(t && p);
t->length += 5+length;
}
+void pa_tagstruct_put_boolean(struct pa_tagstruct*t, int b) {
+ assert(t);
+ extend(t, 1);
+ t->data[t->length] = b ? TAG_BOOLEAN_TRUE : TAG_BOOLEAN_FALSE;
+ t->length += 1;
+}
+
int pa_tagstruct_gets(struct pa_tagstruct*t, const char **s) {
int error = 0;
size_t n;
return t->data;
}
+int pa_tagstruct_get_boolean(struct pa_tagstruct*t, int *b) {
+ assert(t && b);
+
+ if (t->rindex+1 > t->length)
+ return -1;
+
+ if (t->data[t->rindex] == TAG_BOOLEAN_TRUE)
+ *b = 1;
+ else if (t->data[t->rindex] == TAG_BOOLEAN_FALSE)
+ *b = 0;
+ else
+ return -1;
+
+ t->rindex +=1;
+ return 0;
+}
+
+
void pa_tagstruct_putu8(struct pa_tagstruct*t, uint8_t c);
void pa_tagstruct_put_sample_spec(struct pa_tagstruct *t, const struct pa_sample_spec *ss);
void pa_tagstruct_put_arbitrary(struct pa_tagstruct*t, const void *p, size_t length);
+void pa_tagstruct_put_boolean(struct pa_tagstruct*t, int b);
int pa_tagstruct_gets(struct pa_tagstruct*t, const char **s);
int pa_tagstruct_getu32(struct pa_tagstruct*t, uint32_t *i);
int pa_tagstruct_getu8(struct pa_tagstruct*t, uint8_t *c);
int pa_tagstruct_get_sample_spec(struct pa_tagstruct *t, struct pa_sample_spec *ss);
int pa_tagstruct_get_arbitrary(struct pa_tagstruct *t, const void **p, size_t length);
+int pa_tagstruct_get_boolean(struct pa_tagstruct *t, int *b);
int pa_tagstruct_eof(struct pa_tagstruct*t);
const uint8_t* pa_tagstruct_data(struct pa_tagstruct*t, size_t *l);