diff options
author | Robert Ancell <robert.ancell@canonical.com> | 2011-09-29 14:40:35 +1000 |
---|---|---|
committer | Robert Ancell <robert.ancell@canonical.com> | 2011-09-29 14:40:35 +1000 |
commit | 51d0875e3f79651b8f6b749a403b2b75dad5961d (patch) | |
tree | 4331c7d0a063db8fe43c325dd9c02b7d6ee01560 /tests | |
parent | a676961629ef46200f528c44f4b77e9c6e1f58d4 (diff) | |
download | lightdm-51d0875e3f79651b8f6b749a403b2b75dad5961d.tar.gz |
Use LD_PRELOAD to intercept system calls for testing
Removed the --passwd-file option as not required for testing anymore
Diffstat (limited to 'tests')
-rw-r--r-- | tests/src/Makefile.am | 14 | ||||
-rw-r--r-- | tests/src/initctl.c | 17 | ||||
-rw-r--r-- | tests/src/libsystem.c | 554 | ||||
-rw-r--r-- | tests/src/test-runner.c | 107 |
4 files changed, 647 insertions, 45 deletions
diff --git a/tests/src/Makefile.am b/tests/src/Makefile.am index d0b92f65..814810cb 100644 --- a/tests/src/Makefile.am +++ b/tests/src/Makefile.am @@ -1,4 +1,9 @@ -noinst_PROGRAMS = test-runner test-xserver test-gobject-greeter test-guest-account test-session test-script-hook plymouth +noinst_PROGRAMS = test-runner test-xserver test-gobject-greeter test-guest-account test-session test-script-hook initctl plymouth +lib_LTLIBRARIES = libsystem.la + +libsystem_la_SOURCES = libsystem.c +libsystem_la_CFLAGS = $(GLIB_CFLAGS) +libsystem_la_LIBADD = -ldl if COMPILE_LIBLIGHTDM_QT noinst_PROGRAMS += test-qt-greeter @@ -77,6 +82,13 @@ test_session_LDADD = \ $(GLIB_LIBS) \ $(XCB_LIBS) +initctl_SOURCES = initctl.c status.c status.h +initctl_CFLAGS = \ + $(WARN_CFLAGS) \ + $(GLIB_CFLAGS) +initctl_LDADD = \ + $(GLIB_LIBS) + plymouth_SOURCES = plymouth.c status.c status.h plymouth_CFLAGS = \ $(WARN_CFLAGS) \ diff --git a/tests/src/initctl.c b/tests/src/initctl.c new file mode 100644 index 00000000..a6590b3f --- /dev/null +++ b/tests/src/initctl.c @@ -0,0 +1,17 @@ +#include <stdlib.h> +#include <string.h> +#include <glib.h> + +#include "status.h" + +static GKeyFile *config; + +int +main (int argc, char **argv) +{ + config = g_key_file_new (); + if (g_getenv ("LIGHTDM_TEST_CONFIG")) + g_key_file_load_from_file (config, g_getenv ("LIGHTDM_TEST_CONFIG"), G_KEY_FILE_NONE, NULL); + + return EXIT_SUCCESS; +} diff --git a/tests/src/libsystem.c b/tests/src/libsystem.c new file mode 100644 index 00000000..7d9c0f26 --- /dev/null +++ b/tests/src/libsystem.c @@ -0,0 +1,554 @@ +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <pwd.h> +#include <security/pam_appl.h> +#include <unistd.h> +#define __USE_GNU +#include <dlfcn.h> +#ifdef __linux__ +#include <linux/vt.h> +#endif +#include <glib.h> + +#define LOGIN_PROMPT "login:" + +static int console_fd = -1; + +static GList *user_entries = NULL; +static GList *getpwent_link = NULL; + +struct pam_handle +{ + char *service_name; + char *user; + char *tty; + struct pam_conv conversation; +}; + +uid_t +getuid (void) +{ + return 0; +} + +/*uid_t +geteuid (void) +{ + return 0; +}*/ + +int +initgroups (const char *user, gid_t group) +{ + return 0; +} + +int +setgid (gid_t gid) +{ + return 0; +} + +int +setuid (uid_t uid) +{ + return 0; +} + +#ifdef __linux__ +int +open (const char *pathname, int flags, mode_t mode) +{ + int (*_open) (const char * pathname, int flags, mode_t mode); + + _open = (int (*)(const char * pathname, int flags, mode_t mode)) dlsym (RTLD_NEXT, "open"); + if (strcmp (pathname, "/dev/console") == 0) + { + if (console_fd < 0) + console_fd = _open ("/dev/null", 0, 0); + return console_fd; + } + else + return _open(pathname, flags, mode); +} + +int +ioctl (int d, int request, void *data) +{ + int (*_ioctl) (int d, int request, void *data); + + _ioctl = (int (*)(int d, int request, void *data)) dlsym (RTLD_NEXT, "ioctl"); + if (d > 0 && d == console_fd) + { + struct vt_stat *console_state; + + switch (request) + { + case VT_GETSTATE: + console_state = data; + console_state->v_active = 1; + break; + case VT_ACTIVATE: + break; + } + return 0; + } + else + return _ioctl (d, request, data); +} + +int +close (int fd) +{ + int (*_close) (int fd); + + if (fd > 0 && fd == console_fd) + return 0; + + _close = (int (*)(int fd)) dlsym (RTLD_NEXT, "close"); + return _close (fd); +} +#endif + +static void +free_user (gpointer data) +{ + struct passwd *entry = data; + + g_free (entry->pw_name); + g_free (entry->pw_passwd); + g_free (entry->pw_gecos); + g_free (entry->pw_dir); + g_free (entry->pw_shell); + g_free (entry); +} + +static void +load_passwd_file () +{ + gchar *data = NULL, **lines; + gint i; + GError *error = NULL; + + g_list_free_full (user_entries, free_user); + user_entries = NULL; + getpwent_link = NULL; + + g_file_get_contents (g_getenv ("LIGHTDM_TEST_PASSWD_FILE"), &data, NULL, &error); + if (error) + g_warning ("Error loading passwd file: %s", error->message); + g_clear_error (&error); + + if (!data) + return; + + lines = g_strsplit (data, "\n", -1); + g_free (data); + + for (i = 0; lines[i]; i++) + { + gchar *line, **fields; + + line = g_strstrip (lines[i]); + fields = g_strsplit (line, ":", -1); + if (g_strv_length (fields) == 7) + { + struct passwd *entry = malloc (sizeof (struct passwd)); + + entry->pw_name = g_strdup (fields[0]); + entry->pw_passwd = g_strdup (fields[1]); + entry->pw_uid = atoi (fields[2]); + entry->pw_gid = atoi (fields[3]); + entry->pw_gecos = g_strdup (fields[4]); + entry->pw_dir = g_strdup (fields[5]); + entry->pw_shell = g_strdup (fields[6]); + user_entries = g_list_append (user_entries, entry); + } + g_strfreev (fields); + } + g_strfreev (lines); +} + +struct passwd * +getpwent (void) +{ + if (getpwent_link == NULL) + { + load_passwd_file (); + if (user_entries == NULL) + return NULL; + getpwent_link = user_entries; + } + else + { + if (getpwent_link->next == NULL) + return NULL; + getpwent_link = getpwent_link->next; + } + + return getpwent_link->data; +} + +void +setpwent (void) +{ + getpwent_link = NULL; +} + +void +endpwent (void) +{ + getpwent_link = NULL; +} + +struct passwd * +getpwnam (const char *name) +{ + GList *link; + + if (name == NULL) + return NULL; + + load_passwd_file (); + + for (link = user_entries; link; link = link->next) + { + struct passwd *entry = link->data; + if (strcmp (entry->pw_name, name) == 0) + break; + } + if (!link) + return NULL; + + return link->data; +} + +struct passwd * +getpwuid (uid_t uid) +{ + GList *link; + + load_passwd_file (); + + for (link = user_entries; link; link = link->next) + { + struct passwd *entry = link->data; + if (entry->pw_uid == uid) + break; + } + if (!link) + return NULL; + + return link->data; +} + +int +pam_start (const char *service_name, const char *user, const struct pam_conv *conversation, pam_handle_t **pamh) +{ + pam_handle_t *handle; + + if (service_name == NULL || conversation == NULL || pamh == NULL) + return PAM_SYSTEM_ERR; + + handle = *pamh = malloc (sizeof (pam_handle_t)); + if (handle == NULL) + return PAM_BUF_ERR; + + handle->service_name = strdup (service_name); + handle->user = user ? strdup (user) : NULL; + handle->tty = NULL; + handle->conversation.conv = conversation->conv; + handle->conversation.appdata_ptr = conversation->appdata_ptr; + + return PAM_SUCCESS; +} + +int +pam_authenticate (pam_handle_t *pamh, int flags) +{ + struct passwd *entry; + int password_matches = 0; + + if (pamh == NULL) + return PAM_SYSTEM_ERR; + + /* Prompt for username */ + if (pamh->user == NULL) + { + int result; + struct pam_message **msg; + struct pam_response *resp = NULL; + + msg = malloc (sizeof (struct pam_message *) * 1); + msg[0] = malloc (sizeof (struct pam_message)); + msg[0]->msg_style = PAM_PROMPT_ECHO_ON; + msg[0]->msg = LOGIN_PROMPT; + result = pamh->conversation.conv (1, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr); + free (msg[0]); + free (msg); + if (result != PAM_SUCCESS) + return result; + + if (resp == NULL) + return PAM_CONV_ERR; + if (resp[0].resp == NULL) + { + free (resp); + return PAM_CONV_ERR; + } + + pamh->user = strdup (resp[0].resp); + free (resp[0].resp); + free (resp); + } + + /* Look up password database */ + entry = getpwnam (pamh->user); + + /* Prompt for password if required */ + if (entry && (strcmp (pamh->service_name, "lightdm-autologin") == 0 || strcmp (entry->pw_passwd, "") == 0)) + password_matches = 1; + else + { + int result; + struct pam_message **msg; + struct pam_response *resp = NULL; + + msg = malloc (sizeof (struct pam_message *) * 1); + msg[0] = malloc (sizeof (struct pam_message)); + msg[0]->msg_style = PAM_PROMPT_ECHO_OFF; + msg[0]->msg = "Password:"; + result = pamh->conversation.conv (1, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr); + free (msg[0]); + free (msg); + if (result != PAM_SUCCESS) + return result; + + if (resp == NULL) + return PAM_CONV_ERR; + if (resp[0].resp == NULL) + { + free (resp); + return PAM_CONV_ERR; + } + + if (entry) + password_matches = strcmp (entry->pw_passwd, resp[0].resp) == 0; + free (resp[0].resp); + free (resp); + } + + if (password_matches) + return PAM_SUCCESS; + else + return PAM_AUTH_ERR; +} + +const char * +pam_getenv (pam_handle_t *pamh, const char *name) +{ + if (pamh == NULL || name == NULL) + return NULL; + + return NULL; +} + +char ** +pam_getenvlist (pam_handle_t *pamh) +{ + if (pamh == NULL) + return NULL; + + return NULL; +} + +int +pam_set_item (pam_handle_t *pamh, int item_type, const void *item) +{ + if (pamh == NULL || item == NULL) + return PAM_SYSTEM_ERR; + + switch (item_type) + { + case PAM_TTY: + if (pamh->tty) + free (pamh->tty); + pamh->tty = strdup ((const char *) item); + return PAM_SUCCESS; + + default: + return PAM_BAD_ITEM; + } +} + +int +pam_get_item (const pam_handle_t *pamh, int item_type, const void **item) +{ + if (pamh == NULL || item == NULL) + return PAM_SYSTEM_ERR; + + switch (item_type) + { + case PAM_SERVICE: + *item = pamh->service_name; + return PAM_SUCCESS; + + case PAM_USER: + *item = pamh->user; + return PAM_SUCCESS; + + case PAM_USER_PROMPT: + *item = LOGIN_PROMPT; + return PAM_SUCCESS; + + case PAM_TTY: + *item = pamh->tty; + return PAM_SUCCESS; + + case PAM_CONV: + *item = &pamh->conversation; + return PAM_SUCCESS; + + default: + return PAM_BAD_ITEM; + } +} + +int +pam_open_session (pam_handle_t *pamh, int flags) +{ + if (pamh == NULL) + return PAM_SYSTEM_ERR; + + return PAM_SUCCESS; +} + +int +pam_close_session (pam_handle_t *pamh, int flags) +{ + if (pamh == NULL) + return PAM_SYSTEM_ERR; + + return PAM_SUCCESS; +} + +int +pam_acct_mgmt (pam_handle_t *pamh, int flags) +{ + if (pamh == NULL) + return PAM_SYSTEM_ERR; + + return PAM_SUCCESS; +} + +int +pam_chauthtok (pam_handle_t *pamh, int flags) +{ + if (pamh == NULL) + return PAM_SYSTEM_ERR; + + return PAM_SUCCESS; +} + +int +pam_setcred (pam_handle_t *pamh, int flags) +{ + if (pamh == NULL) + return PAM_SYSTEM_ERR; + + return PAM_SUCCESS; +} + +int +pam_end (pam_handle_t *pamh, int pam_status) +{ + if (pamh == NULL) + return PAM_SYSTEM_ERR; + + free (pamh->service_name); + if (pamh->user) + free (pamh->user); + if (pamh->tty) + free (pamh->tty); + free (pamh); + + return PAM_SUCCESS; +} + +const char * +pam_strerror (pam_handle_t *pamh, int errnum) +{ + if (pamh == NULL) + return NULL; + + switch (errnum) + { + case PAM_SUCCESS: + return "Success"; + case PAM_ABORT: + return "Critical error - immediate abort"; + case PAM_OPEN_ERR: + return "Failed to load module"; + case PAM_SYMBOL_ERR: + return "Symbol not found"; + case PAM_SERVICE_ERR: + return "Error in service module"; + case PAM_SYSTEM_ERR: + return "System error"; + case PAM_BUF_ERR: + return "Memory buffer error"; + case PAM_PERM_DENIED: + return "Permission denied"; + case PAM_AUTH_ERR: + return "Authentication failure"; + case PAM_CRED_INSUFFICIENT: + return "Insufficient credentials to access authentication data"; + case PAM_AUTHINFO_UNAVAIL: + return "Authentication service cannot retrieve authentication info"; + case PAM_USER_UNKNOWN: + return "User not known to the underlying authentication module"; + case PAM_MAXTRIES: + return "Have exhausted maximum number of retries for service"; + case PAM_NEW_AUTHTOK_REQD: + return "Authentication token is no longer valid; new one required"; + case PAM_ACCT_EXPIRED: + return "User account has expired"; + case PAM_SESSION_ERR: + return "Cannot make/remove an entry for the specified session"; + case PAM_CRED_UNAVAIL: + return "Authentication service cannot retrieve user credentials"; + case PAM_CRED_EXPIRED: + return "User credentials expired"; + case PAM_CRED_ERR: + return "Failure setting user credentials"; + case PAM_NO_MODULE_DATA: + return "No module specific data is present"; + case PAM_BAD_ITEM: + return "Bad item passed to pam_*_item()"; + case PAM_CONV_ERR: + return "Conversation error"; + case PAM_AUTHTOK_ERR: + return "Authentication token manipulation error"; + case PAM_AUTHTOK_RECOVERY_ERR: + return "Authentication information cannot be recovered"; + case PAM_AUTHTOK_LOCK_BUSY: + return "Authentication token lock busy"; + case PAM_AUTHTOK_DISABLE_AGING: + return "Authentication token aging disabled"; + case PAM_TRY_AGAIN: + return "Failed preliminary check by password service"; + case PAM_IGNORE: + return "The return value should be ignored by PAM dispatch"; + case PAM_MODULE_UNKNOWN: + return "Module is unknown"; + case PAM_AUTHTOK_EXPIRED: + return "Authentication token expired"; + case PAM_CONV_AGAIN: + return "Conversation is waiting for event"; + case PAM_INCOMPLETE: + return "Application needs to call libpam again"; + default: + return "Unknown PAM error"; + } +} diff --git a/tests/src/test-runner.c b/tests/src/test-runner.c index df78ff45..2f61fe5a 100644 --- a/tests/src/test-runner.c +++ b/tests/src/test-runner.c @@ -422,19 +422,54 @@ load_script (const gchar *filename) g_strfreev (lines); } +static gchar * +create_bus (void) +{ + int name_pipe[2]; + gchar *command, address[1024]; + gchar **argv; + GPid pid; + ssize_t n_read; + GError *error = NULL; + + if (pipe (name_pipe) < 0) + { + g_warning ("Error creating pipe: %s", strerror (errno)); + quit (EXIT_FAILURE); + } + command = g_strdup_printf ("dbus-daemon --session --print-address=%d", name_pipe[1]); + if (!g_shell_parse_argv (command, NULL, &argv, &error)) + { + g_warning ("Error parsing command line: %s", error->message); + quit (EXIT_FAILURE); + } + g_clear_error (&error); + if (!g_spawn_async (NULL, argv, NULL, G_SPAWN_SEARCH_PATH | G_SPAWN_LEAVE_DESCRIPTORS_OPEN, NULL, NULL, &pid, &error)) + { + g_warning ("Error launching LightDM: %s", error->message); + quit (EXIT_FAILURE); + } + children = g_list_append (children, GINT_TO_POINTER (pid)); + n_read = read (name_pipe[0], address, 1023); + if (n_read < 0) + { + g_warning ("Error reading D-Bus address: %s", strerror (errno)); + quit (EXIT_FAILURE); + } + address[n_read] = '\0'; + + return g_strdup (address); +} + int main (int argc, char **argv) { GMainLoop *loop; - gchar *greeter = NULL, *script_name, *config_file, *config_path, *path, *path1, *path2, *ld_library_path, *home_dir; + gchar *greeter = NULL, *script_name, *config_file, *config_path, *path, *path1, *path2, *ld_preload, *ld_library_path, *home_dir; GString *passwd_data; int status_socket; - gchar *dbus_command, dbus_address[1024]; - GPid pid; + gchar *bus_address; GString *command_line; - int dbus_pipe[2]; - ssize_t n_read; - gchar **dbus_argv; gchar **lightdm_argv; gchar cwd[1024]; GError *error = NULL; @@ -481,10 +516,22 @@ main (int argc, char **argv) /* Don't contact our X server */ g_unsetenv ("DISPLAY"); - /* Use locally built libraries and binaries */ + /* Run local D-Bus daemons */ + bus_address = create_bus (); + g_setenv ("DBUS_SYSTEM_BUS_ADDRESS", bus_address, TRUE); + g_setenv ("DBUS_SESSION_BUS_ADDRESS", bus_address, TRUE); + + /* Override system calls */ + ld_preload = g_build_filename (BUILDDIR, "tests", "src", ".libs", "libsystem.so", NULL); + g_setenv ("LD_PRELOAD", ld_preload, TRUE); + g_free (ld_preload); + + /* Run test programs */ path = g_strdup_printf ("%s/tests/src/.libs:%s/tests/src:%s/tests/src:%s", BUILDDIR, BUILDDIR, SRCDIR, g_getenv ("PATH")); g_setenv ("PATH", path, TRUE); g_free (path); + + /* Use locally built libraries */ path1 = g_build_filename (BUILDDIR, "liblightdm-gobject", ".libs", NULL); path2 = g_build_filename (BUILDDIR, "liblightdm-qt", ".libs", NULL); ld_library_path = g_strdup_printf ("%s:%s", path1, path2); @@ -497,34 +544,6 @@ main (int argc, char **argv) if (config_path) g_setenv ("LIGHTDM_TEST_CONFIG", config_path, TRUE); - /* Run local D-Bus daemon */ - if (pipe (dbus_pipe) < 0) - { - g_warning ("Error creating pipe: %s", strerror (errno)); - quit (EXIT_FAILURE); - } - dbus_command = g_strdup_printf ("dbus-daemon --session --print-address=%d", dbus_pipe[1]); - if (!g_shell_parse_argv (dbus_command, NULL, &dbus_argv, &error)) - { - g_warning ("Error parsing command line: %s", error->message); - quit (EXIT_FAILURE); - } - g_clear_error (&error); - if (!g_spawn_async (NULL, dbus_argv, NULL, G_SPAWN_SEARCH_PATH | G_SPAWN_LEAVE_DESCRIPTORS_OPEN, NULL, NULL, &pid, &error)) - { - g_warning ("Error launching LightDM: %s", error->message); - quit (EXIT_FAILURE); - } - children = g_list_append (children, GINT_TO_POINTER (pid)); - n_read = read (dbus_pipe[0], dbus_address, 1023); - if (n_read < 0) - { - g_warning ("Error reading D-Bus address: %s", strerror (errno)); - quit (EXIT_FAILURE); - } - dbus_address[n_read] = '\0'; - g_setenv ("DBUS_SESSION_BUS_ADDRESS", dbus_address, TRUE); - /* Open socket for status */ status_socket_name = g_build_filename (cwd, ".status-socket", NULL); g_setenv ("LIGHTDM_TEST_STATUS_SOCKET", status_socket_name, TRUE); @@ -537,7 +556,7 @@ main (int argc, char **argv) } g_io_add_watch (g_io_channel_unix_new (status_socket), G_IO_IN, status_message_cb, NULL); - /* Make fake users */ + /* Run from a temporary directory */ temp_dir = g_build_filename (cwd, "lightdm-test-XXXXXX", NULL); if (!mkdtemp (temp_dir)) { @@ -548,6 +567,7 @@ main (int argc, char **argv) g_setenv ("LIGHTDM_TEST_HOME_DIR", home_dir, TRUE); passwd_data = g_string_new (""); + /* Make fake users */ struct { gchar *user_name; @@ -556,8 +576,10 @@ main (int argc, char **argv) gint uid; } users[] = { - {"alice", "password", "Alice User", 1000}, - {"bob", "", "Bob User", 1001}, + {"root", "", "root", 0}, + {"lightdm", "", "", 100}, + {"alice", "password", "Alice User", 1000}, + {"bob", "", "Bob User", 1001}, {NULL, NULL, 0} }; int i; @@ -569,7 +591,6 @@ main (int argc, char **argv) g_string_append_printf (passwd_data, "%s:%s:%d:%d:%s:%s/home/%s:/bin/sh\n", users[i].user_name, users[i].password, users[i].uid, users[i].uid, users[i].real_name, temp_dir, users[i].user_name); } - path = g_build_filename (temp_dir, "passwd", NULL); g_setenv ("LIGHTDM_TEST_PASSWD_FILE", path, TRUE); g_file_set_contents (path, passwd_data->str, -1, NULL); @@ -577,7 +598,7 @@ main (int argc, char **argv) g_string_free (passwd_data, TRUE); run_commands (); - + status_timeout = g_timeout_add (2000, status_timeout_cb, NULL); command_line = g_string_new ("../src/lightdm"); @@ -585,20 +606,18 @@ main (int argc, char **argv) g_string_append (command_line, " --debug"); if (config_path) g_string_append_printf (command_line, " --config %s", config_path); - g_string_append (command_line, " --test-mode"); g_string_append(command_line, " --xserver-command=test-xserver"); if (greeter) g_string_append_printf (command_line, " --greeter-session=%s", greeter); g_string_append (command_line, " --session-wrapper="); - g_string_append_printf (command_line, " --passwd-file %s/passwd", temp_dir); g_string_append_printf (command_line, " --cache-dir %s/cache", temp_dir); g_string_append_printf (command_line, " --xsessions-dir=%s/tests/data/xsessions", SRCDIR); g_string_append_printf (command_line, " --xgreeters-dir=%s/tests/data/xgreeters", SRCDIR); g_string_append (command_line, " --minimum-vt=0"); g_string_append (command_line, " --minimum-display-number=50"); - g_print ("Start daemon with command: PATH=%s LD_LIBRARY_PATH=%s LIGHTDM_TEST_STATUS_SOCKET=%s DBUS_SESSION_BUS_ADDRESS=%s %s\n", - g_getenv ("PATH"), g_getenv ("LD_LIBRARY_PATH"), g_getenv ("LIGHTDM_TEST_STATUS_SOCKET"), g_getenv ("DBUS_SESSION_BUS_ADDRESS"), + g_print ("Start daemon with command: PATH=%s LD_PRELOAD=%s LD_LIBRARY_PATH=%s LIGHTDM_TEST_STATUS_SOCKET=%s DBUS_SESSION_BUS_ADDRESS=%s %s\n", + g_getenv ("PATH"), g_getenv ("LD_PRELOAD"), g_getenv ("LD_LIBRARY_PATH"), g_getenv ("LIGHTDM_TEST_STATUS_SOCKET"), g_getenv ("DBUS_SESSION_BUS_ADDRESS"), command_line->str); if (!g_shell_parse_argv (command_line->str, NULL, &lightdm_argv, &error)) |