From 53f05329ea0b4a94a855719b622cabe5b5ce4be7 Mon Sep 17 00:00:00 2001 From: Michael Terry Date: Sat, 8 Feb 2014 18:36:27 -0500 Subject: [PATCH 1/1] Add shared data manager and test --- debian/lightdm.dirs | 3 +- src/Makefile.am | 3 + src/lightdm.c | 8 + src/shared-data-manager.c | 330 ++++++++++++++++++++++++++++ src/shared-data-manager.h | 45 ++++ tests/Makefile.am | 2 + tests/scripts/shared-data-dirs.conf | 37 ++++ tests/src/libsystem.c | 50 ++++- tests/src/test-runner.c | 84 ++++++- tests/test-shared-data-dirs | 2 + 10 files changed, 561 insertions(+), 3 deletions(-) create mode 100644 src/shared-data-manager.c create mode 100644 src/shared-data-manager.h create mode 100644 tests/scripts/shared-data-dirs.conf create mode 100755 tests/test-shared-data-dirs diff --git a/debian/lightdm.dirs b/debian/lightdm.dirs index 0bcf7abd..255ef1e7 100644 --- a/debian/lightdm.dirs +++ b/debian/lightdm.dirs @@ -1,3 +1,4 @@ /etc/X11 -/var/log/lightdm /var/cache/lightdm +/var/lib/lightdm-data +/var/log/lightdm diff --git a/src/Makefile.am b/src/Makefile.am index 08222cf3..ba35eeaf 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -45,6 +45,8 @@ lightdm_SOURCES = \ session-child.h \ session-config.c \ session-config.h \ + shared-data-manager.c \ + shared-data-manager.h \ surfaceflinger-server.c \ surfaceflinger-server.h \ unity-system-compositor.c \ @@ -77,6 +79,7 @@ lightdm_CFLAGS = \ -I"$(top_srcdir)/common" \ -DSBIN_DIR=\"$(sbindir)\" \ -DCONFIG_DIR=\"$(sysconfdir)/lightdm\" \ + -DUSERS_DIR=\"$(localstatedir)/lib/lightdm-data\" \ -DLOG_DIR=\"$(localstatedir)/log/lightdm\" \ -DRUN_DIR=\"$(localstatedir)/run/lightdm\" \ -DCACHE_DIR=\"$(localstatedir)/cache/lightdm\" \ diff --git a/src/lightdm.c b/src/lightdm.c index 42d47ee0..a45713dd 100644 --- a/src/lightdm.c +++ b/src/lightdm.c @@ -30,6 +30,7 @@ #include "x-server.h" #include "process.h" #include "session-child.h" +#include "shared-data-manager.h" #include "user-list.h" static gchar *config_path = NULL; @@ -989,6 +990,7 @@ main (int argc, char **argv) gchar *default_cache_dir = g_strdup (CACHE_DIR); gboolean show_version = FALSE; GList *link, *messages = NULL; + SharedDataManager *shared_data_manager = NULL; GOptionEntry options[] = { { "config", 'c', 0, G_OPTION_ARG_STRING, &config_path, @@ -1245,6 +1247,8 @@ main (int argc, char **argv) g_signal_connect (display_manager, "stopped", G_CALLBACK (display_manager_stopped_cb), NULL); g_signal_connect (display_manager, "seat-removed", G_CALLBACK (display_manager_seat_removed_cb), NULL); + shared_data_manager = shared_data_manager_new (); + /* Load the static display entries */ groups = config_get_groups (config_get_instance ()); for (i = groups; *i; i++) @@ -1320,6 +1324,10 @@ main (int argc, char **argv) g_main_loop_run (loop); + /* Clean up shared data manager */ + g_object_unref (shared_data_manager); + shared_data_manager = NULL; + /* Clean up user list */ common_user_list_cleanup (); diff --git a/src/shared-data-manager.c b/src/shared-data-manager.c new file mode 100644 index 00000000..dbdc3010 --- /dev/null +++ b/src/shared-data-manager.c @@ -0,0 +1,330 @@ +/* + * Copyright (C) 2014 Canonical, Ltd + * Author: Michael Terry + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. See http://www.gnu.org/copyleft/gpl.html the full text of the + * license. + */ + +#include +#include +#include +#include +#include + +#include "configuration.h" +#include "shared-data-manager.h" +#include "user-list.h" + +#define NUM_ENUMERATION_FILES 100 + +struct SharedDataManagerPrivate +{ + guint32 greeter_uid; + guint32 greeter_gid; + guint num_setup_users; + GHashTable *starting_dirs; +}; + +struct OwnerInfo +{ + SharedDataManager *manager; + guint32 uid; +}; + +G_DEFINE_TYPE (SharedDataManager, shared_data_manager, G_TYPE_OBJECT); + +SharedDataManager * +shared_data_manager_new (void) +{ + return g_object_new (SHARED_DATA_MANAGER_TYPE, NULL); +} + +static void +delete_unused_user (gpointer key, gpointer value, gpointer user_data) +{ + const gchar *user = (const gchar *)key; + GError *error = NULL; + + /* Listen, the rest of this file is nice async glib code and all, but + for this operation, we just need a fire and forget rm -rf. Since + recursively deleting in GIO is a huge pain in the butt, we'll just drop + to shell for this. */ + + gchar *path = g_build_filename (USERS_DIR, user, NULL); + gchar *quoted_path = g_shell_quote (path); + gchar *cmd = g_strdup_printf ("/bin/rm -rf %s", quoted_path); + + if (!g_spawn_command_line_async (cmd, &error)) + { + g_warning ("Could not delete unused user data directory %s: %s", path, error->message); + g_error_free (error); + } + + g_free (cmd); + g_free (quoted_path); + g_free (path); +} + +static void +chown_user_dir_cb (GObject *object, GAsyncResult *res, gpointer user_data) +{ + GFile *file = G_FILE (object); + GFileInfo *info = NULL; + GError *error = NULL; + + if (!g_file_set_attributes_finish (file, res, &info, &error)) + { + gchar *path = g_file_get_path (file); + g_warning ("Could not chown user data directory %s: %s", + path, error->message); + g_free (path); + g_error_free (error); + } + + if (info) + g_object_unref (info); +} + +static void +make_user_dir_cb (GObject *object, GAsyncResult *res, gpointer user_data) +{ + GFile *file = G_FILE (object); + struct OwnerInfo *owner = (struct OwnerInfo *)user_data; + GError *error = NULL; + + if (!g_file_make_directory_finish (file, res, &error)) + { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS)) + { + gchar *path = g_file_get_path (file); + g_warning ("Could not create user data directory %s: %s", + path, error->message); + g_free (path); + g_error_free (error); + owner->manager->priv->num_setup_users--; + g_object_unref (owner->manager); + g_free (owner); + return; + } + g_error_free (error); + } + + /* Even if the directory already exists, we want to re-affirm the owners + because the greeter gid is configuration based and may change between + runs. */ + GFileInfo *info = g_file_info_new (); + g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_UID, + owner->uid); + g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_GID, + owner->manager->priv->greeter_gid); + g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_MODE, 0770); + g_file_set_attributes_async (file, info, G_FILE_QUERY_INFO_NONE, + G_PRIORITY_DEFAULT, NULL, + chown_user_dir_cb, NULL); + + /* If we're the last user dir to be set up, delete unused user dirs */ + owner->manager->priv->num_setup_users--; + if (owner->manager->priv->starting_dirs != NULL && + owner->manager->priv->num_setup_users == 0) + { + g_hash_table_foreach (owner->manager->priv->starting_dirs, + delete_unused_user, owner->manager); + g_hash_table_destroy (owner->manager->priv->starting_dirs); + owner->manager->priv->starting_dirs = NULL; + } + + g_object_unref (owner->manager); + g_free (owner); +} + +static void +setup_user_dir (SharedDataManager *manager, guint32 uid) +{ + struct OwnerInfo *owner = g_malloc (sizeof (struct OwnerInfo)); + owner->manager = g_object_ref (manager); + owner->uid = uid; + + gchar *uidstr = g_strdup_printf ("%u", uid); + gchar *path = g_build_filename (USERS_DIR, uidstr, NULL); + GFile *file = g_file_new_for_path (path); + g_free (path); + + manager->priv->num_setup_users++; + if (manager->priv->starting_dirs != NULL) + g_hash_table_remove (manager->priv->starting_dirs, uidstr); + g_free (uidstr); + + g_file_make_directory_async (file, G_PRIORITY_DEFAULT, NULL, + make_user_dir_cb, owner); + + g_object_unref (file); +} + +static void +next_user_dirs_cb (GObject *object, GAsyncResult *res, gpointer user_data) +{ + GFileEnumerator *enumerator = G_FILE_ENUMERATOR (object); + SharedDataManager *manager = SHARED_DATA_MANAGER (user_data); + GList *link; + GError *error = NULL; + + GList *files = g_file_enumerator_next_files_finish (enumerator, res, + &error); + if (error != NULL) + { + g_warning ("Could not enumerate user data directory %s: %s", + USERS_DIR, error->message); + g_error_free (error); + g_object_unref (manager); + return; + } + + for (link = files; link; link = link->next) + { + GFileInfo *info = link->data; + g_hash_table_insert (manager->priv->starting_dirs, + g_strdup (g_file_info_get_name (info)), NULL); + } + + if (files != NULL) + { + g_list_free_full (files, g_object_unref); + g_file_enumerator_next_files_async (enumerator, NUM_ENUMERATION_FILES, + G_PRIORITY_DEFAULT, NULL, + next_user_dirs_cb, manager); + } + else + { + // We've finally assembled all the initial directories. Now let's + // iterate the current users and set them each up. As we go, we'll + // remove the users from the starting_dirs hash and thus see which + // users are obsolete. + GList *users = common_user_list_get_users (common_user_list_get_instance ()); + for (link = users; link; link = link->next) + { + CommonUser *user = link->data; + setup_user_dir (manager, common_user_get_uid (user)); + } + // Also set up our own greeter dir, so it has a place to dump its own files + // (imagine it holding some large files temporarily before shunting them + // to the next user to log in's specific directory). + setup_user_dir (manager, manager->priv->greeter_uid); + g_object_unref (manager); + } +} + +static void +list_user_dirs_cb (GObject *object, GAsyncResult *res, gpointer user_data) +{ + GFile *file = G_FILE (object); + SharedDataManager *manager = SHARED_DATA_MANAGER (user_data); + GFileEnumerator *enumerator; + GError *error = NULL; + + enumerator = g_file_enumerate_children_finish (file, res, &error); + if (enumerator == NULL) + { + g_warning ("Could not enumerate user data directory %s: %s", + USERS_DIR, error->message); + g_error_free (error); + g_object_unref (manager); + return; + } + + manager->priv->starting_dirs = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, NULL); + g_file_enumerator_next_files_async (enumerator, NUM_ENUMERATION_FILES, + G_PRIORITY_DEFAULT, NULL, + next_user_dirs_cb, manager); +} + +static void +user_added_cb (CommonUserList *list, CommonUser *user, + SharedDataManager *manager) +{ + setup_user_dir (manager, common_user_get_uid (user)); +} + +static void +user_removed_cb (CommonUserList *list, CommonUser *user, + SharedDataManager *manager) +{ + gchar *uid = g_strdup_printf ("%u", common_user_get_uid (user)); + delete_unused_user (uid, NULL, manager); + g_free (uid); +} + +static void +shared_data_manager_init (SharedDataManager *manager) +{ + manager->priv = G_TYPE_INSTANCE_GET_PRIVATE (manager, SHARED_DATA_MANAGER_TYPE, SharedDataManagerPrivate); + + // Grab current greeter-user gid + gchar *greeter_user; + struct passwd *greeter_entry; + greeter_user = config_get_string (config_get_instance (), "LightDM", "greeter-user"); + greeter_entry = getpwnam (greeter_user); + if (greeter_entry) + { + manager->priv->greeter_uid = greeter_entry->pw_uid; + manager->priv->greeter_gid = greeter_entry->pw_gid; + } + g_free (greeter_user); + + /* Grab list of all current directories, so we know if any exist that we + no longer need. */ + GFile *file = g_file_new_for_path (USERS_DIR); + g_file_enumerate_children_async (file, G_FILE_ATTRIBUTE_STANDARD_NAME, + G_FILE_QUERY_INFO_NONE, + G_PRIORITY_DEFAULT, NULL, + list_user_dirs_cb, g_object_ref (manager)); + g_object_unref (file); + + /* And listen for user changes. The chance of a race with the above + initial setup is so tiny, it's not worth worrying about. */ + g_signal_connect (common_user_list_get_instance (), "user-added", + G_CALLBACK (user_added_cb), manager); + g_signal_connect (common_user_list_get_instance (), "user-removed", + G_CALLBACK (user_removed_cb), manager); +} + +static void +shared_data_manager_dispose (GObject *object) +{ + SharedDataManager *self = SHARED_DATA_MANAGER (object); + + /* Should also cancel outstanding GIO operations, but whatever, let them + do their thing. */ + + g_signal_handlers_disconnect_by_data (common_user_list_get_instance (), + self); + + G_OBJECT_CLASS (shared_data_manager_parent_class)->dispose (object); +} + +static void +shared_data_manager_finalize (GObject *object) +{ + SharedDataManager *self = SHARED_DATA_MANAGER (object); + + if (self->priv->starting_dirs) + g_hash_table_destroy (self->priv->starting_dirs); + + G_OBJECT_CLASS (shared_data_manager_parent_class)->finalize (object); +} + +static void +shared_data_manager_class_init (SharedDataManagerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = shared_data_manager_dispose; + object_class->finalize = shared_data_manager_finalize; + + g_type_class_add_private (klass, sizeof (SharedDataManagerPrivate)); +} diff --git a/src/shared-data-manager.h b/src/shared-data-manager.h new file mode 100644 index 00000000..faa780ab --- /dev/null +++ b/src/shared-data-manager.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2014 Canonical, Ltd + * Author: Michael Terry + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. See http://www.gnu.org/copyleft/gpl.html the full text of the + * license. + */ + +#ifndef SHARED_DATA_MANAGER_H_ +#define SHARED_DATA_MANAGER_H_ + +#include + +typedef struct SharedDataManager SharedDataManager; + +G_BEGIN_DECLS + +#define SHARED_DATA_MANAGER_TYPE (shared_data_manager_get_type()) +#define SHARED_DATA_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHARED_DATA_MANAGER_TYPE, SharedDataManager)) +#define SHARED_DATA_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SHARED_DATA_MANAGER_TYPE, SharedDataManagerClass)) +#define SHARED_DATA_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SHARED_DATA_MANAGER_TYPE, SharedDataManagerClass)) + +typedef struct SharedDataManagerPrivate SharedDataManagerPrivate; + +struct SharedDataManager +{ + GObject parent_instance; + SharedDataManagerPrivate *priv; +}; + +typedef struct +{ + GObjectClass parent_class; +} SharedDataManagerClass; + +GType shared_data_manager_get_type (void); + +SharedDataManager *shared_data_manager_new (void); + +G_END_DECLS + +#endif /* SHARED_DATA_MANAGER_H_ */ diff --git a/tests/Makefile.am b/tests/Makefile.am index 4b387d4a..92ab7c4d 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -110,6 +110,7 @@ TESTS = \ test-script-hook-fail-display-setup \ test-script-hook-fail-greeter-setup \ test-script-hook-fail-session-setup \ + test-shared-data-dirs \ test-upstart-autologin \ test-upstart-login \ test-dbus \ @@ -406,6 +407,7 @@ EXTRA_DIST = \ scripts/plymouth-inactive-vt.conf \ scripts/plymouth-no-seat.conf \ scripts/restart-authentication.conf \ + scripts/shared-data-dirs.conf \ scripts/script-hooks.conf \ scripts/script-hook-fail-display-setup.conf \ scripts/script-hook-fail-greeter-setup.conf \ diff --git a/tests/scripts/shared-data-dirs.conf b/tests/scripts/shared-data-dirs.conf new file mode 100644 index 00000000..141dc6e4 --- /dev/null +++ b/tests/scripts/shared-data-dirs.conf @@ -0,0 +1,37 @@ +# +# Make sure we manage shared user data directories on startup and over time +# + +[test-runner-config] +accounts-service-user-filter=have-password1 have-password2 have-password3 +# One normal, one with bad permissions, one to create, one to delete +shared-data-dirs=1000:1000:100:0770 1001:1000:1000:0777 1004:1004:100:0770 + +#?RUNNER DAEMON-START + +# X server starts +#?XSERVER-0 START VT=7 SEAT=seat0 + +# Startup creation/deletion +#?*WAIT +#?*LIST-SHARED-DATA-DIRS +#?RUNNER LIST-SHARED-DATA-DIRS DIRS=100:100:100:0770,1000:1000:100:0770,1001:1001:100:0770,1002:1002:100:0770 + +# Delete one user +#?*DELETE-USER USERNAME=have-password1 +#?RUNNER DELETE-USER USERNAME=have-password1 +#?*WAIT +#?*LIST-SHARED-DATA-DIRS +#?RUNNER LIST-SHARED-DATA-DIRS DIRS=100:100:100:0770,1001:1001:100:0770,1002:1002:100:0770 + +# Add one user +#?*ADD-USER USERNAME=have-password4 +#?RUNNER ADD-USER USERNAME=have-password4 +#?*WAIT +#?*LIST-SHARED-DATA-DIRS +#?RUNNER LIST-SHARED-DATA-DIRS DIRS=100:100:100:0770,1001:1001:100:0770,1002:1002:100:0770,1003:1003:100:0770 + +# Cleanup +#?*STOP-DAEMON +#?XSERVER-0 TERMINATE SIGNAL=15 +#?RUNNER DAEMON-EXIT STATUS=0 diff --git a/tests/src/libsystem.c b/tests/src/libsystem.c index 157bb993..b4db0c35 100644 --- a/tests/src/libsystem.c +++ b/tests/src/libsystem.c @@ -179,7 +179,7 @@ redirect_path (const gchar *path) return g_strdup (path); if (g_str_has_prefix (path, "/tmp")) - return g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), "tmp", path + strlen ("tmp"), NULL); + return g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), "tmp", path + strlen ("/tmp"), NULL); return g_strdup (path); } @@ -255,6 +255,22 @@ fopen (const char *path, const char *mode) return result; } +int +unlinkat (int dirfd, const char *pathname, int flags) +{ + int (*_unlinkat) (int dirfd, const char *pathname, int flags); + gchar *new_path = NULL; + int result; + + _unlinkat = (int (*)(int dirfd, const char *pathname, int flags)) dlsym (RTLD_NEXT, "unlinkat"); + + new_path = redirect_path (pathname); + result = _unlinkat (dirfd, new_path, flags); + g_free (new_path); + + return result; +} + int creat (const char *pathname, mode_t mode) { @@ -371,6 +387,38 @@ __xstat64 (int version, const char *path, struct stat64 *buf) return ret; } +int +__fxstatat(int ver, int dirfd, const char *pathname, struct stat *buf, int flags) +{ + int (*___fxstatat) (int ver, int dirfd, const char *pathname, struct stat *buf, int flags); + gchar *new_path = NULL; + int ret; + + ___fxstatat = (int (*)(int ver, int dirfd, const char *pathname, struct stat *buf, int flags)) dlsym (RTLD_NEXT, "__fxstatat"); + + new_path = redirect_path (pathname); + ret = ___fxstatat (ver, dirfd, new_path, buf, flags); + g_free (new_path); + + return ret; +} + +int +__fxstatat64(int ver, int dirfd, const char *pathname, struct stat64 *buf, int flags) +{ + int (*___fxstatat64) (int ver, int dirfd, const char *pathname, struct stat64 *buf, int flags); + gchar *new_path = NULL; + int ret; + + ___fxstatat64 = (int (*)(int ver, int dirfd, const char *pathname, struct stat64 *buf, int flags)) dlsym (RTLD_NEXT, "__fxstatat64"); + + new_path = redirect_path (pathname); + ret = ___fxstatat64 (ver, dirfd, new_path, buf, flags); + g_free (new_path); + + return ret; +} + DIR * opendir (const char *name) { diff --git a/tests/src/test-runner.c b/tests/src/test-runner.c index c00a6290..fec53eae 100644 --- a/tests/src/test-runner.c +++ b/tests/src/test-runner.c @@ -319,6 +319,13 @@ get_script_line (const gchar *prefix) return NULL; } +static gboolean +stop_loop (gpointer user_data) +{ + g_main_loop_quit ((GMainLoop *)user_data); + return G_SOURCE_REMOVE; +} + static void handle_command (const gchar *command) { @@ -398,7 +405,50 @@ handle_command (const gchar *command) if (strcmp (name, "WAIT") == 0) { - sleep (1); + /* Use a main loop so that our DBus functions are still responsive */ + GMainLoop *loop = g_main_loop_new (NULL, FALSE); + g_timeout_add_seconds (1, stop_loop, loop); + g_main_loop_run (loop); + g_main_loop_unref (loop); + } + else if (strcmp (name, "LIST-SHARED-DATA-DIRS") == 0) + { + gchar *shared_dir; + GDir *dir; + const gchar *path; + GList *paths = NULL, *link; + GString *status; + + shared_dir = g_strdup_printf ("%s/var/lib/lightdm-data", temp_dir); + dir = g_dir_open (shared_dir, 0, NULL); + while ((path = g_dir_read_name (dir))) + { + gchar *full_path = g_build_filename (shared_dir, path, NULL); + paths = g_list_insert_sorted (paths, full_path, (GCompareFunc)g_strcmp0); + } + g_dir_close (dir); + g_free (shared_dir); + + status = g_string_new ("RUNNER LIST-SHARED-DATA-DIRS DIRS="); + for (link = paths; link; link = link->next) + { + path = (const gchar *)link->data; + GStatBuf buf; + if (g_stat (path, &buf) != 0) + continue; + + if (link != paths) + g_string_append (status, ","); + gchar *basename = g_path_get_basename (path); + g_string_append_printf (status, "%s:%u:%u:0%o", basename, + buf.st_uid, buf.st_gid, + buf.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)); + g_free (basename); + } + g_list_free_full (paths, g_free); + + check_status (status->str); + g_string_free (status, TRUE); } else if (strcmp (name, "LIST-SEATS") == 0) { @@ -2046,6 +2096,7 @@ main (int argc, char **argv) g_mkdir_with_parents (g_strdup_printf ("%s/usr/share/lightdm/remote-sessions", temp_dir), 0755); g_mkdir_with_parents (g_strdup_printf ("%s/usr/share/lightdm/greeters", temp_dir), 0755); g_mkdir_with_parents (g_strdup_printf ("%s/tmp", temp_dir), 0755); + g_mkdir_with_parents (g_strdup_printf ("%s/var/lib/lightdm-data", temp_dir), 0755); g_mkdir_with_parents (g_strdup_printf ("%s/var/run", temp_dir), 0755); g_mkdir_with_parents (g_strdup_printf ("%s/var/log", temp_dir), 0755); @@ -2083,6 +2134,37 @@ main (int argc, char **argv) g_strfreev (files); } + if (g_key_file_has_key (config, "test-runner-config", "shared-data-dirs", NULL)) + { + gchar *dir_string; + gchar **dirs; + gint i; + + dir_string = g_key_file_get_string (config, "test-runner-config", "shared-data-dirs", NULL); + dirs = g_strsplit (dir_string, " ", -1); + g_free (dir_string); + + for (i = 0; dirs[i]; i++) + { + gchar **fields = g_strsplit (dirs[i], ":", -1); + if (g_strv_length (fields) == 4) + { + gchar *path = g_strdup_printf ("%s/var/lib/lightdm-data/%s", temp_dir, fields[0]); + int uid = g_ascii_strtoll (fields[1], NULL, 10); + int gid = g_ascii_strtoll (fields[2], NULL, 10); + int mode = g_ascii_strtoll (fields[3], NULL, 8); + g_mkdir (path, mode); + g_chmod (path, mode); /* mkdir filters by umask, so make sure we have what we want */ + if (chown (path, uid, gid) < 0) + g_warning ("chown (%s) failed: %s", path, strerror (errno)); + g_free (path); + } + g_strfreev (fields); + } + + g_strfreev (dirs); + } + /* Always copy the script */ if (system (g_strdup_printf ("cp %s %s/script", config_path, temp_dir))) perror ("Failed to copy configuration"); diff --git a/tests/test-shared-data-dirs b/tests/test-shared-data-dirs new file mode 100755 index 00000000..e4c475e6 --- /dev/null +++ b/tests/test-shared-data-dirs @@ -0,0 +1,2 @@ +#!/bin/sh +./src/dbus-env ./src/test-runner shared-data-dirs test-gobject-greeter -- 2.39.2