From 212bafd6109eb01654065527a8dd55206ecaf535 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 23 Mar 2009 21:37:17 +0100 Subject: Detect forks Some really stupid applications (Hey, vim, that means you!) love to fork after initializing gtk/libcanberra. This is really bad style. We however have to deal with this cleanly, so we try to detect the forks making sure all our calls fail cleanly after the fork. --- src/Makefile.am | 3 ++- src/canberra-gtk.c | 8 +++++++- src/canberra.h | 3 ++- src/common.c | 17 ++++++++++++++++- src/fork-detect.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/fork-detect.h | 26 ++++++++++++++++++++++++++ 6 files changed, 104 insertions(+), 4 deletions(-) create mode 100644 src/fork-detect.c create mode 100644 src/fork-detect.h diff --git a/src/Makefile.am b/src/Makefile.am index 1fb5168..e40ec1f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -52,7 +52,8 @@ libcanberra_la_SOURCES = \ sound-theme-spec.c sound-theme-spec.h \ llist.h \ macro.h macro.c \ - malloc.c malloc.h + malloc.c malloc.h \ + fork-detect.c fork-detect.h libcanberra_la_CFLAGS = \ $(AM_CFLAGS) \ $(VORBIS_CFLAGS) diff --git a/src/canberra-gtk.c b/src/canberra-gtk.c index 70b8d7e..cb36a0f 100644 --- a/src/canberra-gtk.c +++ b/src/canberra-gtk.c @@ -31,6 +31,7 @@ #include "common.h" #include "malloc.h" #include "proplist.h" +#include "fork-detect.h" /** * SECTION:canberra-gtk @@ -93,7 +94,8 @@ ca_context *ca_gtk_context_get(void) { if ((c = g_static_private_get(&context_private))) return c; - ca_assert_se(ca_context_create(&c) == CA_SUCCESS); + if (ca_context_create(&c) != CA_SUCCESS) + return NULL; if ((name = g_get_application_name())) ca_context_change_props(c, CA_PROP_APPLICATION_NAME, name, NULL); @@ -154,6 +156,7 @@ int ca_gtk_proplist_set_for_widget(ca_proplist *p, GtkWidget *widget) { ca_return_val_if_fail(p, CA_ERROR_INVALID); ca_return_val_if_fail(widget, CA_ERROR_INVALID); + ca_return_val_if_fail(!ca_detect_fork(), CA_ERROR_FORKED); if (!(w = get_toplevel(widget))) return CA_ERROR_INVALID; @@ -224,6 +227,7 @@ int ca_gtk_proplist_set_for_event(ca_proplist *p, GdkEvent *e) { ca_return_val_if_fail(p, CA_ERROR_INVALID); ca_return_val_if_fail(e, CA_ERROR_INVALID); + ca_return_val_if_fail(!ca_detect_fork(), CA_ERROR_FORKED); if ((gw = e->any.window)) { gdk_window_get_user_data(gw, (gpointer*) &w); @@ -297,6 +301,7 @@ int ca_gtk_play_for_widget(GtkWidget *w, uint32_t id, ...) { ca_proplist *p; ca_return_val_if_fail(w, CA_ERROR_INVALID); + ca_return_val_if_fail(!ca_detect_fork(), CA_ERROR_FORKED); if ((ret = ca_proplist_create(&p)) < 0) return ret; @@ -346,6 +351,7 @@ int ca_gtk_play_for_event(GdkEvent *e, uint32_t id, ...) { ca_proplist *p; ca_return_val_if_fail(e, CA_ERROR_INVALID); + ca_return_val_if_fail(!ca_detect_fork(), CA_ERROR_FORKED); if ((ret = ca_proplist_create(&p)) < 0) return ret; diff --git a/src/canberra.h b/src/canberra.h index ff09146..9664ef2 100644 --- a/src/canberra.h +++ b/src/canberra.h @@ -414,7 +414,8 @@ enum { CA_ERROR_IO = -14, CA_ERROR_INTERNAL = -15, CA_ERROR_DISABLED = -16, - _CA_ERROR_MAX = -17 + CA_ERROR_FORKED = -17, + _CA_ERROR_MAX = -18 }; /** diff --git a/src/common.c b/src/common.c index e1445f4..ac8b982 100644 --- a/src/common.c +++ b/src/common.c @@ -30,6 +30,7 @@ #include "driver.h" #include "proplist.h" #include "macro.h" +#include "fork-detect.h" /** * SECTION:canberra @@ -127,6 +128,7 @@ int ca_context_create(ca_context **_c) { int ret; const char *d; + ca_return_val_if_fail(!ca_detect_fork(), CA_ERROR_FORKED); ca_return_val_if_fail(_c, CA_ERROR_INVALID); if (!(c = ca_new0(ca_context, 1))) @@ -171,6 +173,7 @@ int ca_context_create(ca_context **_c) { int ca_context_destroy(ca_context *c) { int ret = CA_SUCCESS; + ca_return_val_if_fail(!ca_detect_fork(), CA_ERROR_FORKED); ca_return_val_if_fail(c, CA_ERROR_INVALID); /* There's no locking necessary here, because the application is @@ -209,6 +212,7 @@ int ca_context_set_driver(ca_context *c, const char *driver) { char *n; int ret; + ca_return_val_if_fail(!ca_detect_fork(), CA_ERROR_FORKED); ca_return_val_if_fail(c, CA_ERROR_INVALID); ca_mutex_lock(c->mutex); ca_return_val_if_fail_unlock(!c->opened, CA_ERROR_STATE, c->mutex); @@ -250,6 +254,7 @@ int ca_context_change_device(ca_context *c, const char *device) { char *n; int ret; + ca_return_val_if_fail(!ca_detect_fork(), CA_ERROR_FORKED); ca_return_val_if_fail(c, CA_ERROR_INVALID); ca_mutex_lock(c->mutex); @@ -277,6 +282,7 @@ fail: static int context_open_unlocked(ca_context *c) { int ret; + ca_return_val_if_fail(!ca_detect_fork(), CA_ERROR_FORKED); ca_return_val_if_fail(c, CA_ERROR_INVALID); if (c->opened) @@ -302,6 +308,7 @@ static int context_open_unlocked(ca_context *c) { int ca_context_open(ca_context *c) { int ret; + ca_return_val_if_fail(!ca_detect_fork(), CA_ERROR_FORKED); ca_return_val_if_fail(c, CA_ERROR_INVALID); ca_mutex_lock(c->mutex); ca_return_val_if_fail_unlock(!c->opened, CA_ERROR_STATE, c->mutex); @@ -336,6 +343,7 @@ int ca_context_change_props(ca_context *c, ...) { int ret; ca_proplist *p = NULL; + ca_return_val_if_fail(!ca_detect_fork(), CA_ERROR_FORKED); ca_return_val_if_fail(c, CA_ERROR_INVALID); va_start(ap, c); @@ -368,6 +376,7 @@ int ca_context_change_props_full(ca_context *c, ca_proplist *p) { int ret; ca_proplist *merged; + ca_return_val_if_fail(!ca_detect_fork(), CA_ERROR_FORKED); ca_return_val_if_fail(c, CA_ERROR_INVALID); ca_return_val_if_fail(p, CA_ERROR_INVALID); @@ -438,6 +447,7 @@ int ca_context_play(ca_context *c, uint32_t id, ...) { va_list ap; ca_proplist *p = NULL; + ca_return_val_if_fail(!ca_detect_fork(), CA_ERROR_FORKED); ca_return_val_if_fail(c, CA_ERROR_INVALID); va_start(ap, id); @@ -478,6 +488,7 @@ int ca_context_play_full(ca_context *c, uint32_t id, ca_proplist *p, ca_finish_c const char *t; ca_bool_t enabled = TRUE; + ca_return_val_if_fail(!ca_detect_fork(), CA_ERROR_FORKED); ca_return_val_if_fail(c, CA_ERROR_INVALID); ca_return_val_if_fail(p, CA_ERROR_INVALID); ca_return_val_if_fail(!userdata || cb, CA_ERROR_INVALID); @@ -532,6 +543,7 @@ finish: int ca_context_cancel(ca_context *c, uint32_t id) { int ret; + ca_return_val_if_fail(!ca_detect_fork(), CA_ERROR_FORKED); ca_return_val_if_fail(c, CA_ERROR_INVALID); ca_mutex_lock(c->mutex); ca_return_val_if_fail_unlock(c->opened, CA_ERROR_STATE, c->mutex); @@ -566,6 +578,7 @@ int ca_context_cache(ca_context *c, ...) { va_list ap; ca_proplist *p = NULL; + ca_return_val_if_fail(!ca_detect_fork(), CA_ERROR_FORKED); ca_return_val_if_fail(c, CA_ERROR_INVALID); va_start(ap, c); @@ -599,6 +612,7 @@ int ca_context_cache(ca_context *c, ...) { int ca_context_cache_full(ca_context *c, ca_proplist *p) { int ret; + ca_return_val_if_fail(!ca_detect_fork(), CA_ERROR_FORKED); ca_return_val_if_fail(c, CA_ERROR_INVALID); ca_return_val_if_fail(p, CA_ERROR_INVALID); @@ -648,7 +662,8 @@ const char *ca_strerror(int code) { [-CA_ERROR_ACCESS] = "Access forbidden", [-CA_ERROR_IO] = "IO error", [-CA_ERROR_INTERNAL] = "Internal error", - [-CA_ERROR_DISABLED] = "Sound disabled" + [-CA_ERROR_DISABLED] = "Sound disabled", + [-CA_ERROR_FORKED] = "Process forked" }; ca_return_val_if_fail(code <= 0, NULL); diff --git a/src/fork-detect.c b/src/fork-detect.c new file mode 100644 index 0000000..622037c --- /dev/null +++ b/src/fork-detect.c @@ -0,0 +1,51 @@ +/*** + This file is part of libcanberra. + + Copyright 2009 Lennart Poettering + + libcanberra is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation, either version 2.1 of the + License, or (at your option) any later version. + + libcanberra is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with libcanberra. If not, see + . +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include "fork-detect.h" + +int ca_detect_fork(void) { + static volatile pid_t pid = (pid_t) -1; + pid_t v; + + /* Some really stupid applications (Hey, vim, that means you!) + * love to fork after initializing gtk/libcanberra. This is really + * bad style. We however have to deal with this cleanly, so we try + * to detect the forks making sure all our calls fail cleanly + * after the fork. */ + + /* Ideally we'd use atomic operations here, but we don't have them + * and this is not exactly crucial, so we don't care */ + + v = pid; + + if (v == getpid() || v == (pid_t) -1) { + pid = getpid(); + return 0; + } + + return 1; +} diff --git a/src/fork-detect.h b/src/fork-detect.h new file mode 100644 index 0000000..9050ef8 --- /dev/null +++ b/src/fork-detect.h @@ -0,0 +1,26 @@ +#ifndef foocanberraforkdetecth +#define foocanberraforkdetecth + +/*** + This file is part of libcanberra. + + Copyright 2009 Lennart Poettering + + libcanberra is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation, either version 2.1 of the + License, or (at your option) any later version. + + libcanberra is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with libcanberra. If not, see + . +***/ + +int ca_detect_fork(void); + +#endif -- cgit