diff --git a/libsecret/mock-service.c b/libsecret/mock-service.c index 68baec4..f890f1d 100644 --- a/libsecret/mock-service.c +++ b/libsecret/mock-service.c @@ -23,108 +23,127 @@ #include #include -static GTestDBus *test_bus = NULL; +#ifdef __linux +#include +#endif + +static gchar *service_name = NULL; static GPid pid = 0; -static gboolean +static void +on_python_child_setup (gpointer user_data) +{ +#ifdef __linux + prctl (PR_SET_PDEATHSIG, 15); +#endif +} + +static gchar * +read_until_end (int fd, + GError **error) +{ + GString *data; + gsize len; + gssize ret; + gchar *pos; + + data = g_string_new (""); + + for (;;) { + len = data->len; + g_string_set_size (data, len + 256); + ret = read (fd, data->str + len, 256); + if (ret < 0) { + if (errno != EAGAIN) { + g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), + "Couldn't read from mock service: %s", g_strerror (errno)); + g_string_free (data, TRUE); + return NULL; + } + } else if (ret == 0) { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Remote closed the output"); + g_string_free (data, TRUE); + return NULL; + } else { + data->len = len + ret; + data->str[data->len] = '\0'; + } + + pos = strchr (data->str, '\n'); + if (pos) { + g_string_set_size (data, pos - data->str); + break; + } + } + + return g_string_free (data, FALSE); +} + +static const gchar * service_start (const gchar *mock_script, GError **error) { - gchar ready[8] = { 0, }; GSpawnFlags flags; - int wait_pipe[2]; - GPollFD poll_fd; gboolean ret; - gint polled; + gint output; gchar *argv[] = { "python", (gchar *)mock_script, - "--name", MOCK_SERVICE_NAME, - "--ready", ready, NULL }; g_return_val_if_fail (mock_script != NULL, FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - test_bus = g_test_dbus_new (G_TEST_DBUS_NONE); - g_test_dbus_up (test_bus); + g_free (service_name); + service_name = NULL; - g_setenv ("SECRET_SERVICE_BUS_NAME", MOCK_SERVICE_NAME, TRUE); - - if (pipe (wait_pipe) < 0) { - g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (errno), - "Couldn't create pipe for mock service"); - return FALSE; - } - - snprintf (ready, sizeof (ready), "%d", wait_pipe[1]); - - flags = G_SPAWN_SEARCH_PATH | G_SPAWN_LEAVE_DESCRIPTORS_OPEN; - ret = g_spawn_async (SRCDIR, argv, NULL, flags, NULL, NULL, &pid, error); - - close (wait_pipe[1]); + flags = G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD; + ret = g_spawn_async_with_pipes (SRCDIR, argv, NULL, flags, on_python_child_setup, NULL, &pid, + NULL, &output, NULL, error); if (ret) { - poll_fd.events = G_IO_IN | G_IO_HUP | G_IO_ERR; - poll_fd.fd = wait_pipe[0]; - poll_fd.revents = 0; - - polled = g_poll (&poll_fd, 1, 2000); - if (polled < -1) - g_warning ("couldn't poll file descirptor: %s", g_strerror (errno)); - if (polled != 1) - g_warning ("couldn't wait for mock service"); + service_name = read_until_end (output, error); + if (service_name) { + g_strstrip (service_name); + g_assert_cmpstr (service_name, !=, ""); + g_setenv ("SECRET_SERVICE_BUS_NAME", service_name, TRUE); + } + close (output); } - close (wait_pipe[0]); - return ret; + return service_name; } -gboolean +const gchar * mock_service_start (const gchar *mock_script, GError **error) { gchar *path; - gboolean ret; + const gchar *name; path = g_build_filename (SRCDIR, "libsecret", mock_script, NULL); - ret = service_start (path, error); + name = service_start (path, error); g_free (path); - return ret; + return name; } void mock_service_stop (void) { - const gchar *prgname; - - if (!pid) - return; - - if (kill (pid, SIGTERM) < 0) { - if (errno != ESRCH) - g_warning ("kill() failed: %s", g_strerror (errno)); - } - - g_spawn_close_pid (pid); - pid = 0; - while (g_main_context_iteration (NULL, FALSE)); - /* - * HACK: Don't worry about leaking tests when running under gjs. - * Waiting for the connection to go away is hopeless due to - * the way gjs garbage collects. - */ - prgname = g_get_prgname (); - if (prgname && strstr (prgname, "gjs")) { - g_test_dbus_stop (test_bus); + if (pid) { + if (kill (pid, SIGTERM) < 0) { + if (errno != ESRCH) + g_warning ("kill() failed: %s", g_strerror (errno)); + } - } else { - g_test_dbus_down (test_bus); - g_object_unref (test_bus); + g_spawn_close_pid (pid); + pid = 0; } - test_bus = NULL; + + while (g_main_context_iteration (NULL, FALSE)); + g_unsetenv ("SECRET_SERVICE_BUS_NAME"); } diff --git a/libsecret/mock-service.h b/libsecret/mock-service.h index a160de9..f098c32 100644 --- a/libsecret/mock-service.h +++ b/libsecret/mock-service.h @@ -17,9 +17,7 @@ #include -#define MOCK_SERVICE_NAME "org.mock.Service" - -gboolean mock_service_start (const gchar *mock_script, +const gchar * mock_service_start (const gchar *mock_script, GError **error); void mock_service_stop (void); diff --git a/libsecret/mock/service.py b/libsecret/mock/service.py index 14661a1..d8f7e66 100644 --- a/libsecret/mock/service.py +++ b/libsecret/mock/service.py @@ -28,8 +28,6 @@ import gobject COLLECTION_PREFIX = "/org/freedesktop/secrets/collection/" -bus_name = 'org.freedesktop.Secret.MockService' -ready_pipe = -1 objects = { } class NotSupported(dbus.exceptions.DBusException): @@ -132,7 +130,7 @@ class SecretPrompt(dbus.service.Object): self.path = "/org/freedesktop/secrets/prompts/%s" % prompt_name else: self.path = "/org/freedesktop/secrets/prompts/%s" % next_identifier('p') - dbus.service.Object.__init__(self, service.bus_name, self.path) + dbus.service.Object.__init__(self, service.bus, self.path) service.add_prompt(self) assert self.path not in objects objects[self.path] = self @@ -166,7 +164,7 @@ class SecretSession(dbus.service.Object): self.algorithm = algorithm self.key = key self.path = "/org/freedesktop/secrets/sessions/%s" % next_identifier('s') - dbus.service.Object.__init__(self, service.bus_name, self.path) + dbus.service.Object.__init__(self, service.bus, self.path) service.add_session(self) objects[self.path] = self @@ -206,7 +204,7 @@ class SecretItem(dbus.service.Object): self.path = "%s/%s" % (collection.path, identifier) self.confirm = confirm self.created = self.modified = time.time() - dbus.service.Object.__init__(self, collection.service.bus_name, self.path) + dbus.service.Object.__init__(self, collection.service.bus, self.path) self.collection.add_item(self) objects[self.path] = self @@ -327,7 +325,7 @@ class SecretCollection(dbus.service.Object): self.created = self.modified = time.time() self.aliased = set() self.path = "%s%s" % (COLLECTION_PREFIX, identifier) - dbus.service.Object.__init__(self, service.bus_name, self.path) + dbus.service.Object.__init__(self, service.bus, self.path) self.service.add_collection(self) objects[self.path] = self @@ -476,12 +474,9 @@ class SecretService(dbus.service.Object): "dh-ietf1024-sha256-aes128-cbc-pkcs7": AesAlgorithm(), } - def __init__(self, name=None): - if name == None: - name = bus_name - bus = dbus.SessionBus() - self.bus_name = dbus.service.BusName(name, allow_replacement=True, replace_existing=True) - dbus.service.Object.__init__(self, self.bus_name, '/org/freedesktop/secrets') + def __init__(self): + self.bus = dbus.SessionBus() + dbus.service.Object.__init__(self, self.bus, '/org/freedesktop/secrets') self.sessions = { } self.prompts = { } self.collections = { } @@ -493,9 +488,7 @@ class SecretService(dbus.service.Object): for session in list(self.sessions.get(old_owner, [])): session.Close() - bus.add_signal_receiver(on_name_owner_changed, - 'NameOwnerChanged', - 'org.freedesktop.DBus') + self.bus.add_signal_receiver(on_name_owner_changed, 'NameOwnerChanged', 'org.freedesktop.DBus') def add_standard_objects(self): collection = SecretCollection(self, "english", label="Collection One", locked=False) @@ -532,12 +525,12 @@ class SecretService(dbus.service.Object): self.set_alias('session', collection) def listen(self): - global ready_pipe loop = gobject.MainLoop() - if ready_pipe >= 0: - os.write(ready_pipe, "GO") - os.close(ready_pipe) - ready_pipe = -1 + name = self.bus.get_unique_name() + if not name: + raise NameError("No unique name available") + print name + sys.stdout.flush() loop.run() def add_session(self, session): @@ -696,19 +689,13 @@ class SecretService(dbus.service.Object): def parse_options(args): - global bus_name, ready_pipe try: - opts, args = getopt.getopt(args, "nr", ["name=", "ready="]) + opts, args = getopt.getopt(args, "", []) except getopt.GetoptError, err: print str(err) sys.exit(2) for o, a in opts: - if o in ("-n", "--name"): - bus_name = a - elif o in ("-r", "--ready"): - ready_pipe = int(a) - else: - assert False, "unhandled option" + assert False, "unhandled option" return args parse_options(sys.argv[1:]) diff --git a/libsecret/test-service.c b/libsecret/test-service.c index aa1a395..c081275 100644 --- a/libsecret/test-service.c +++ b/libsecret/test-service.c @@ -47,8 +47,17 @@ static void teardown_mock (Test *test, gconstpointer unused) { + /* + * Because the entire model of GDBus using another worker-thread + * is shit and racy as hell. If I had more time I would fix GDBus not to segfault + * during tests, but for now this is the best I can do. + */ + g_usleep (G_USEC_PER_SEC); + secret_service_disconnect (); mock_service_stop (); + + while (g_main_context_iteration (NULL, FALSE)); } static void @@ -98,6 +107,10 @@ test_get_sync (Test *test, g_object_add_weak_pointer (G_OBJECT (service3), (gpointer *)&service3); g_object_unref (service3); + + /* Because the entire model of GDBus using another worker-thread is shite */ + g_usleep (G_USEC_PER_SEC); + secret_service_disconnect (); g_assert (service3 == NULL); }