summaryrefslogtreecommitdiff
path: root/libck-connector/ck-connector.c
diff options
context:
space:
mode:
Diffstat (limited to 'libck-connector/ck-connector.c')
-rw-r--r--libck-connector/ck-connector.c691
1 files changed, 691 insertions, 0 deletions
diff --git a/libck-connector/ck-connector.c b/libck-connector/ck-connector.c
new file mode 100644
index 0000000..34a8657
--- /dev/null
+++ b/libck-connector/ck-connector.c
@@ -0,0 +1,691 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * ck-connector.c : Code for login managers to register with ConsoleKit.
+ *
+ * Copyright (c) 2007 David Zeuthen <davidz@redhat.com>
+ * Copyright (c) 2007 William Jon McCann <mccann@jhu.edu>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <dbus/dbus.h>
+
+#include "ck-connector.h"
+
+#define N_ELEMENTS(arr) (sizeof (arr) / sizeof ((arr)[0]))
+
+#if defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
+#define _CK_FUNCTION_NAME __func__
+#elif defined(__GNUC__) || defined(_MSC_VER)
+#define _CK_FUNCTION_NAME __FUNCTION__
+#else
+#define _CK_FUNCTION_NAME "unknown function"
+#endif
+
+#define CK_CONNECTOR_ERROR "org.freedesktop.CkConnector.Error"
+
+#define _CK_WARNING_FORMAT "arguments to %s() were incorrect, assertion \"%s\" failed in file %s line %d.\n"
+#define _ck_return_if_fail(condition) do { \
+ if (!(condition)) { \
+ fprintf (stderr, _CK_WARNING_FORMAT, _CK_FUNCTION_NAME, #condition, __FILE__, __LINE__); \
+ return; \
+ } } while (0)
+
+#define _ck_return_val_if_fail(condition, val) do { \
+ if (!(condition)) { \
+ fprintf (stderr, _CK_WARNING_FORMAT, _CK_FUNCTION_NAME, #condition, __FILE__, __LINE__); \
+ return val; \
+ } } while (0)
+
+struct _CkConnector
+{
+ int refcount;
+ char *cookie;
+ dbus_bool_t session_created;
+ DBusConnection *connection;
+};
+
+static struct {
+ char *name;
+ int type;
+} parameter_lookup[] = {
+ { "display-device", DBUS_TYPE_STRING },
+ { "x11-display-device", DBUS_TYPE_STRING },
+ { "x11-display", DBUS_TYPE_STRING },
+ { "remote-host-name", DBUS_TYPE_STRING },
+ { "session-type", DBUS_TYPE_STRING },
+ { "is-local", DBUS_TYPE_BOOLEAN },
+ { "user", DBUS_TYPE_INT32 },
+};
+
+static int
+lookup_parameter_type (const char *name)
+{
+ int i;
+ int type;
+
+ type = DBUS_TYPE_INVALID;
+
+ for (i = 0; i < N_ELEMENTS (parameter_lookup); i++) {
+ if (strcmp (name, parameter_lookup[i].name) == 0) {
+ type = parameter_lookup[i].type;
+ break;
+ }
+ }
+
+ return type;
+}
+
+static dbus_bool_t
+add_param_basic (DBusMessageIter *iter_array,
+ const char *name,
+ int type,
+ const void *value)
+{
+ DBusMessageIter iter_struct;
+ DBusMessageIter iter_variant;
+ const char *container_type;
+
+ switch (type) {
+ case DBUS_TYPE_STRING:
+ container_type = DBUS_TYPE_STRING_AS_STRING;
+ break;
+ case DBUS_TYPE_BOOLEAN:
+ container_type = DBUS_TYPE_BOOLEAN_AS_STRING;
+ break;
+ case DBUS_TYPE_INT32:
+ container_type = DBUS_TYPE_INT32_AS_STRING;
+ break;
+ default:
+ goto oom;
+ break;
+ }
+
+ if (! dbus_message_iter_open_container (iter_array,
+ DBUS_TYPE_STRUCT,
+ NULL,
+ &iter_struct)) {
+ goto oom;
+ }
+
+ if (! dbus_message_iter_append_basic (&iter_struct,
+ DBUS_TYPE_STRING,
+ &name)) {
+ goto oom;
+ }
+
+ if (! dbus_message_iter_open_container (&iter_struct,
+ DBUS_TYPE_VARIANT,
+ container_type,
+ &iter_variant)) {
+ goto oom;
+ }
+
+ if (! dbus_message_iter_append_basic (&iter_variant,
+ type,
+ value)) {
+ goto oom;
+ }
+
+ if (! dbus_message_iter_close_container (&iter_struct,
+ &iter_variant)) {
+ goto oom;
+ }
+
+ if (! dbus_message_iter_close_container (iter_array,
+ &iter_struct)) {
+ goto oom;
+ }
+
+ return TRUE;
+oom:
+ return FALSE;
+}
+
+/* Frees all resources allocated and disconnects from the system
+ * message bus.
+ */
+static void
+_ck_connector_free (CkConnector *connector)
+{
+ if (connector->connection != NULL) {
+ /* it's a private connection so it's all good */
+ dbus_connection_close (connector->connection);
+ }
+
+ if (connector->cookie != NULL) {
+ free (connector->cookie);
+ }
+
+ free (connector);
+}
+
+/**
+ * Decrements the reference count of a CkConnector, disconnecting
+ * from the bus and freeing the connector if the count reaches 0.
+ *
+ * @param connector the connector
+ * @see ck_connector_ref
+ */
+void
+ck_connector_unref (CkConnector *connector)
+{
+ _ck_return_if_fail (connector != NULL);
+
+ /* Probably should use some kind of atomic op here */
+ connector->refcount -= 1;
+ if (connector->refcount == 0) {
+ _ck_connector_free (connector);
+ }
+}
+
+/**
+ * Increments the reference count of a CkConnector.
+ *
+ * @param connector the connector
+ * @returns the connector
+ * @see ck_connector_unref
+ */
+CkConnector *
+ck_connector_ref (CkConnector *connector)
+{
+ _ck_return_val_if_fail (connector != NULL, NULL);
+
+ /* Probably should use some kind of atomic op here */
+ connector->refcount += 1;
+
+ return connector;
+}
+
+/**
+ * Constructs a new Connector to communicate with the ConsoleKit
+ * daemon. Returns #NULL if memory can't be allocated for the
+ * object.
+ *
+ * @returns a new CkConnector, free with ck_connector_unref()
+ */
+CkConnector *
+ck_connector_new (void)
+{
+ CkConnector *connector;
+
+ connector = calloc (1, sizeof (CkConnector));
+ if (connector == NULL) {
+ goto oom;
+ }
+
+ connector->refcount = 1;
+ connector->connection = NULL;
+ connector->cookie = NULL;
+ connector->session_created = FALSE;
+oom:
+ return connector;
+}
+
+/**
+ * Connects to the D-Bus system bus daemon and issues the method call
+ * OpenSession on the ConsoleKit manager interface. The
+ * connection to the bus is private.
+ *
+ * Returns FALSE on OOM, if the system bus daemon is not running, if
+ * the ConsoleKit daemon is not running or if the caller doesn't have
+ * sufficient privileges.
+ *
+ * @returns #TRUE if the operation succeeds
+ */
+dbus_bool_t
+ck_connector_open_session (CkConnector *connector,
+ DBusError *error)
+{
+ DBusError local_error;
+ DBusMessage *message;
+ DBusMessage *reply;
+ dbus_bool_t ret;
+ char *cookie;
+
+ _ck_return_val_if_fail (connector != NULL, FALSE);
+ _ck_return_val_if_fail ((error) == NULL || !dbus_error_is_set ((error)), FALSE);
+
+ reply = NULL;
+ message = NULL;
+ ret = FALSE;
+
+ dbus_error_init (&local_error);
+ connector->connection = dbus_bus_get_private (DBUS_BUS_SYSTEM, &local_error);
+ if (connector->connection == NULL) {
+ if (dbus_error_is_set (&local_error)) {
+ dbus_set_error (error,
+ CK_CONNECTOR_ERROR,
+ "Unable to open session: %s",
+ local_error.message);
+ dbus_error_free (&local_error);
+ }
+
+ goto out;
+ }
+
+ dbus_connection_set_exit_on_disconnect (connector->connection, FALSE);
+
+ message = dbus_message_new_method_call ("org.freedesktop.ConsoleKit",
+ "/org/freedesktop/ConsoleKit/Manager",
+ "org.freedesktop.ConsoleKit.Manager",
+ "OpenSession");
+ if (message == NULL) {
+ goto out;
+ }
+
+ dbus_error_init (&local_error);
+ reply = dbus_connection_send_with_reply_and_block (connector->connection,
+ message,
+ -1,
+ &local_error);
+ if (reply == NULL) {
+ if (dbus_error_is_set (&local_error)) {
+ dbus_set_error (error,
+ CK_CONNECTOR_ERROR,
+ "Unable to open session: %s",
+ local_error.message);
+ dbus_error_free (&local_error);
+ goto out;
+ }
+ }
+
+ dbus_error_init (&local_error);
+ if (! dbus_message_get_args (reply,
+ &local_error,
+ DBUS_TYPE_STRING, &cookie,
+ DBUS_TYPE_INVALID)) {
+ if (dbus_error_is_set (&local_error)) {
+ dbus_set_error (error,
+ CK_CONNECTOR_ERROR,
+ "Unable to open session: %s",
+ local_error.message);
+ dbus_error_free (&local_error);
+ goto out;
+ }
+ }
+
+ connector->cookie = strdup (cookie);
+ if (connector->cookie == NULL) {
+ goto out;
+ }
+
+ connector->session_created = TRUE;
+ ret = TRUE;
+
+out:
+ if (reply != NULL) {
+ dbus_message_unref (reply);
+ }
+
+ if (message != NULL) {
+ dbus_message_unref (message);
+ }
+
+ return ret;
+}
+
+static dbus_bool_t
+ck_connector_open_session_with_parameters_valist (CkConnector *connector,
+ DBusError *error,
+ const char *first_parameter_name,
+ va_list var_args)
+{
+ DBusError local_error;
+ DBusMessage *message;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusMessageIter iter_array;
+ dbus_bool_t ret;
+ char *cookie;
+ const char *name;
+
+ _ck_return_val_if_fail (connector != NULL, FALSE);
+
+ reply = NULL;
+ message = NULL;
+ ret = FALSE;
+
+ dbus_error_init (&local_error);
+ connector->connection = dbus_bus_get_private (DBUS_BUS_SYSTEM, &local_error);
+ if (connector->connection == NULL) {
+ if (dbus_error_is_set (&local_error)) {
+ dbus_set_error (error,
+ CK_CONNECTOR_ERROR,
+ "Unable to open session: %s",
+ local_error.message);
+ dbus_error_free (&local_error);
+ }
+ goto out;
+ }
+
+ dbus_connection_set_exit_on_disconnect (connector->connection, FALSE);
+
+ message = dbus_message_new_method_call ("org.freedesktop.ConsoleKit",
+ "/org/freedesktop/ConsoleKit/Manager",
+ "org.freedesktop.ConsoleKit.Manager",
+ "OpenSessionWithParameters");
+ if (message == NULL) {
+ goto out;
+ }
+
+ dbus_message_iter_init_append (message, &iter);
+ if (! dbus_message_iter_open_container (&iter,
+ DBUS_TYPE_ARRAY,
+ "(sv)",
+ &iter_array)) {
+ goto out;
+ }
+
+ name = first_parameter_name;
+ while (name != NULL) {
+ int type;
+ const void *value;
+ dbus_bool_t res;
+
+ type = lookup_parameter_type (name);
+ value = va_arg (var_args, const void *);
+
+ if (type == DBUS_TYPE_INVALID) {
+ dbus_set_error (error,
+ CK_CONNECTOR_ERROR,
+ "Unknown parameter: %s",
+ name);
+ goto out;
+ }
+
+ res = add_param_basic (&iter_array, name, type, value);
+ if (! res) {
+ dbus_set_error (error,
+ CK_CONNECTOR_ERROR,
+ "Error adding parameter: %s",
+ name);
+ goto out;
+ }
+
+ name = va_arg (var_args, char *);
+ }
+
+ if (! dbus_message_iter_close_container (&iter, &iter_array)) {
+ goto out;
+ }
+
+ dbus_error_init (&local_error);
+ reply = dbus_connection_send_with_reply_and_block (connector->connection,
+ message,
+ -1,
+ &local_error);
+ if (reply == NULL) {
+ if (dbus_error_is_set (&local_error)) {
+ dbus_set_error (error,
+ CK_CONNECTOR_ERROR,
+ "Unable to open session: %s",
+ local_error.message);
+ dbus_error_free (&local_error);
+ goto out;
+ }
+ }
+
+ dbus_error_init (&local_error);
+ if (! dbus_message_get_args (reply,
+ &local_error,
+ DBUS_TYPE_STRING, &cookie,
+ DBUS_TYPE_INVALID)) {
+ if (dbus_error_is_set (&local_error)) {
+ dbus_set_error (error,
+ CK_CONNECTOR_ERROR,
+ "Unable to open session: %s",
+ local_error.message);
+ dbus_error_free (&local_error);
+ goto out;
+ }
+ }
+
+ connector->cookie = strdup (cookie);
+ if (connector->cookie == NULL) {
+ goto out;
+ }
+
+ connector->session_created = TRUE;
+ ret = TRUE;
+
+out:
+ if (reply != NULL) {
+ dbus_message_unref (reply);
+ }
+
+ if (message != NULL) {
+ dbus_message_unref (message);
+ }
+
+ return ret;
+}
+
+/**
+ * Opens a new session with parameter from variable argument list. The
+ * variable argument list should contain the name of each parameter
+ * followed by the value to append.
+ * For example:
+ *
+ * @code
+ *
+ * DBusError error;
+ * dbus_int32_t v_INT32 = 500;
+ * const char *v_STRING = "/dev/tty3";
+ *
+ * dbus_error_init (&error);
+ * ck_connector_open_session_with_parameters (connector,
+ * &error,
+ * "user", &v_INT32,
+ * "display-device", &v_STRING,
+ * NULL);
+ * @endcode
+ *
+ * @param error error output
+ * @param first_parameter_name name of the first parameter
+ * @param ... value of first parameter, list of additional name-value pairs
+ * @returns #TRUE on success
+ */
+dbus_bool_t
+ck_connector_open_session_with_parameters (CkConnector *connector,
+ DBusError *error,
+ const char *first_parameter_name,
+ ...)
+{
+ va_list var_args;
+ dbus_bool_t ret;
+
+ _ck_return_val_if_fail (connector != NULL, FALSE);
+ _ck_return_val_if_fail ((error) == NULL || !dbus_error_is_set ((error)), FALSE);
+
+ va_start (var_args, first_parameter_name);
+ ret = ck_connector_open_session_with_parameters_valist (connector,
+ error,
+ first_parameter_name,
+ var_args);
+ va_end (var_args);
+
+ return ret;
+}
+
+/**
+ * Connects to the D-Bus system bus daemon and issues the method call
+ * OpenSessionWithParameters on the ConsoleKit manager interface. The
+ * connection to the bus is private.
+ *
+ * The only parameter that is optional is x11_display - it may be set
+ * to NULL if there is no X11 server associated with the session.
+ *
+ * Returns FALSE on OOM, if the system bus daemon is not running, if
+ * the ConsoleKit daemon is not running or if the caller doesn't have
+ * sufficient privileges.
+ *
+ * @param user UID for the user owning the session
+ * @param display_device the tty device for the session
+ * @param x11_display the value of the X11 DISPLAY for the session
+ * @returns #TRUE if the operation succeeds
+ */
+dbus_bool_t
+ck_connector_open_session_for_user (CkConnector *connector,
+ uid_t user,
+ const char *display_device,
+ const char *x11_display,
+ DBusError *error)
+{
+ dbus_bool_t ret;
+
+ _ck_return_val_if_fail (connector != NULL, FALSE);
+ _ck_return_val_if_fail (display_device != NULL, FALSE);
+ _ck_return_val_if_fail ((error) == NULL || !dbus_error_is_set ((error)), FALSE);
+
+ ret = ck_connector_open_session_with_parameters (connector,
+ error,
+ "display-device", &display_device,
+ "x11-display", &x11_display,
+ "user", &user,
+ NULL);
+ return ret;
+}
+
+/**
+ * Gets the cookie for the current open session.
+ * Returns #NULL if no session is open.
+ *
+ * @returns a constant string with the cookie.
+ */
+const char *
+ck_connector_get_cookie (CkConnector *connector)
+{
+ _ck_return_val_if_fail (connector != NULL, NULL);
+
+ if (! connector->session_created) {
+ return NULL;
+ } else {
+ return connector->cookie;
+ }
+}
+
+/**
+ * Issues the CloseSession method call on the ConsoleKit manager
+ * interface.
+ *
+ * Returns FALSE on OOM, if the system bus daemon is not running, if
+ * the ConsoleKit daemon is not running, if the caller doesn't have
+ * sufficient privilege or if a session isn't open.
+ *
+ * @returns #TRUE if the operation succeeds
+ */
+dbus_bool_t
+ck_connector_close_session (CkConnector *connector,
+ DBusError *error)
+{
+ DBusError local_error;
+ DBusMessage *message;
+ DBusMessage *reply;
+ dbus_bool_t ret;
+ dbus_bool_t session_closed;
+
+ _ck_return_val_if_fail (connector != NULL, FALSE);
+ _ck_return_val_if_fail ((error) == NULL || !dbus_error_is_set ((error)), FALSE);
+
+ reply = NULL;
+ message = NULL;
+ ret = FALSE;
+
+ if (!connector->session_created || connector->cookie == NULL) {
+ dbus_set_error (error,
+ CK_CONNECTOR_ERROR,
+ "Unable to close session: %s",
+ "no session open");
+ goto out;
+ }
+
+ dbus_error_init (&local_error);
+ message = dbus_message_new_method_call ("org.freedesktop.ConsoleKit",
+ "/org/freedesktop/ConsoleKit/Manager",
+ "org.freedesktop.ConsoleKit.Manager",
+ "CloseSession");
+ if (message == NULL) {
+ goto out;
+ }
+
+ if (! dbus_message_append_args (message,
+ DBUS_TYPE_STRING, &(connector->cookie),
+ DBUS_TYPE_INVALID)) {
+ goto out;
+ }
+
+ dbus_error_init (&local_error);
+ reply = dbus_connection_send_with_reply_and_block (connector->connection,
+ message,
+ -1,
+ &local_error);
+ if (reply == NULL) {
+ if (dbus_error_is_set (&local_error)) {
+ dbus_set_error (error,
+ CK_CONNECTOR_ERROR,
+ "Unable to close session: %s",
+ local_error.message);
+ dbus_error_free (&local_error);
+ goto out;
+ }
+ }
+
+ dbus_error_init (&local_error);
+ if (! dbus_message_get_args (reply,
+ &local_error,
+ DBUS_TYPE_BOOLEAN, &session_closed,
+ DBUS_TYPE_INVALID)) {
+ if (dbus_error_is_set (&local_error)) {
+ dbus_set_error (error,
+ CK_CONNECTOR_ERROR,
+ "Unable to close session: %s",
+ local_error.message);
+ dbus_error_free (&local_error);
+ goto out;
+ }
+ }
+
+ if (! session_closed) {
+ goto out;
+ }
+
+ connector->session_created = FALSE;
+ ret = TRUE;
+
+out:
+ if (reply != NULL) {
+ dbus_message_unref (reply);
+ }
+
+ if (message != NULL) {
+ dbus_message_unref (message);
+ }
+
+ return ret;
+
+}