summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2009-03-23 21:37:17 +0100
committerLennart Poettering <lennart@poettering.net>2009-03-23 21:37:17 +0100
commit212bafd6109eb01654065527a8dd55206ecaf535 (patch)
tree6ffc9a7449c2bb4dd8f9862bb439c358152d265a
parentfebbf740799d85dfbdca965efa9867fd70fa2cfc (diff)
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.
-rw-r--r--src/Makefile.am3
-rw-r--r--src/canberra-gtk.c8
-rw-r--r--src/canberra.h3
-rw-r--r--src/common.c17
-rw-r--r--src/fork-detect.c51
-rw-r--r--src/fork-detect.h26
6 files changed, 104 insertions, 4 deletions
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
+ <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <sys/types.h>
+
+#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
+ <http://www.gnu.org/licenses/>.
+***/
+
+int ca_detect_fork(void);
+
+#endif