diff --git a/NEWS b/NEWS
index 81c3225..cf06a31 100644
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,11 @@
==============
+Version 0.4.5
+==============
+
+ * Loop around opening /dev/console to deal with BKL-less kernels (Colin Watson)
+ * systemd fixes (Lennart Poettering)
+
+==============
Version 0.4.4
==============
diff --git a/configure.ac b/configure.ac
index 0efc489..7a3b33f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,7 +1,7 @@
-AC_PREREQ(2.59c)
+AC_PREREQ([2.68])
AC_INIT([ConsoleKit],
- [0.4.5],
+ [0.4.6],
[https://bugs.freedesktop.org/enter_bug.cgi?product=ConsoleKit],
[ConsoleKit])
@@ -12,13 +12,14 @@ m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
AM_MAINTAINER_MODE
# for O_NOFOLLOW support
-AC_GNU_SOURCE
+AC_USE_SYSTEM_EXTENSIONS
-AC_ISC_POSIX
+AC_SEARCH_LIBS([strerror],[cposix])
AC_PROG_CC
-AC_STDC_HEADERS
+AM_PROG_CC_C_O
+AC_HEADER_STDC
AC_DISABLE_STATIC
-AC_PROG_LIBTOOL
+LT_INIT
AC_HEADER_STDC
@@ -88,50 +89,15 @@ case "$host" in
;;
esac
-# Find out the version of DBUS we're using
-
-dbus_version=`pkg-config --modversion dbus-1`
-DBUS_VERSION_MAJOR=`echo $dbus_version | awk -F. '{print $1}'`
-DBUS_VERSION_MINOR=`echo $dbus_version | awk -F. '{print $2}'`
-DBUS_VERSION_MICRO=`echo $dbus_version | awk -F. '{print $3}'`
-if test "z$DBUS_VERSION_MAJOR" = "z"; then
- DBUS_VERSION_MAJOR="0"
-fi
-if test "z$DBUS_VERSION_MINOR" = "z"; then
- DBUS_VERSION_MINOR="0"
-fi
-if test "z$DBUS_VERSION_MICRO" = "z"; then
- DBUS_VERSION_MICRO="0"
-fi
-
-if test "z$DBUS_VERSION_MAJOR" = "z0" -a "z$DBUS_VERSION_MINOR" = "z0" -a "z$DBUS_VERSION_MICRO" = "z0"; then echo "Error: Couldn't determine the version of your DBUS package."
- echo " This is probably an error in this script, please report it"
- echo " along with the following information:"
- echo " Base DBUS version ='$dbus_version'"
- echo " DBUS_VERSION_MAJOR='$DBUS_VERSION_MAJOR'"
- echo " DBUS_VERSION_MINOR='$DBUS_VERSION_MINOR'"
- echo " DBUS_VERSION_MICRO='$DBUS_VERSION_MICRO'"
- exit 1
-else
-
- echo "Your dbus version is $DBUS_VERSION_MAJOR,$DBUS_VERSION_MINOR,$DBUS_VERSION_MICRO."
- DBUS_CFLAGS="$DBUS_CFLAGS -DDBUS_VERSION_MAJOR=$DBUS_VERSION_MAJOR"
- DBUS_CFLAGS="$DBUS_CFLAGS -DDBUS_VERSION_MINOR=$DBUS_VERSION_MINOR"
- DBUS_CFLAGS="$DBUS_CFLAGS -DDBUS_VERSION_MICRO=$DBUS_VERSION_MICRO"
-
- AC_SUBST(DBUS_CFLAGS)
-fi
dnl ---------------------------------------------------------------------------
dnl - Are we specifying a different dbus root ?
dnl ---------------------------------------------------------------------------
AC_ARG_WITH(dbus-sys,
- [AC_HELP_STRING([--with-dbus-sys=
],
- [where D-BUS system.d directory is])])
+ [AS_HELP_STRING([--with-dbus-sys=],[where D-BUS system.d directory is])])
AC_ARG_WITH(dbus-services,
- [AC_HELP_STRING([--with-dbus-services=],
- [where D-BUS services directory is])])
+ [AS_HELP_STRING([--with-dbus-services=],[where D-BUS services directory is])])
if ! test -z "$with_dbus_sys" ; then
DBUS_SYS_DIR="$with_dbus_sys"
else
@@ -158,8 +124,7 @@ dnl - PID file
dnl ---------------------------------------------------------------------------
AC_ARG_WITH(pid-file,
- [AC_HELP_STRING([--with-pid-file=],
- [pid file location])])
+ [AS_HELP_STRING([--with-pid-file=],[pid file location])])
if ! test -z "$with_pid_file"; then
CONSOLE_KIT_PID_FILE=$with_pid_file
@@ -190,6 +155,9 @@ case "$host" in
*-*-solaris*)
CK_BACKEND="solaris"
;;
+ *-*-gnu*)
+ CK_BACKEND="gnu"
+ ;;
*)
AC_MSG_ERROR([No sysdeps back-end implemented for host $host])
;;
@@ -200,6 +168,7 @@ AC_SUBST(KVM_LIBS)
AM_CONDITIONAL(CK_COMPILE_LINUX, test x$CK_BACKEND = xlinux, [Compiling for Linux])
AM_CONDITIONAL(CK_COMPILE_FREEBSD, test x$CK_BACKEND = xfreebsd, [Compiling for FreeBSD])
AM_CONDITIONAL(CK_COMPILE_SOLARIS, test x$CK_BACKEND = xsolaris, [Compiling for Solaris])
+AM_CONDITIONAL(CK_COMPILE_GNU, test x$CK_BACKEND = xgnu, [Compiling for GNU])
AC_SUBST(CK_BACKEND)
dnl ---------------------------------------------------------------------------
@@ -221,8 +190,7 @@ AC_CHECK_LIB(pam, pam_syslog, [AC_DEFINE(HAVE_PAM_SYSLOG, [], [Define to 1 if yo
# Check if we should build the PAM module
msg_pam_module=no
AC_ARG_ENABLE(pam-module,
- [AC_HELP_STRING([--enable-pam-module],
- [build PAM module])],
+ [AS_HELP_STRING([--enable-pam-module],[build PAM module])],
, enable_pam_module=no)
if test "x$enable_pam_module" = "xyes"; then
if test "x$have_pam" = "xno"; then
@@ -233,13 +201,32 @@ if test "x$enable_pam_module" = "xyes"; then
fi
AM_CONDITIONAL(ENABLE_PAM_MODULE, test "x$enable_pam_module" = "xyes")
+dnl ------------------------------------------------------------------------------
+dnl udev-acl - apply ACLs for users with local forground sessions
+dnl ------------------------------------------------------------------------------
+AC_ARG_ENABLE([udev-acl],
+ AS_HELP_STRING([--enable-udev-acl], [enable local user acl permissions support @<:@default=disabled@:>@]),
+ [], [enable_udev_acl=no])
+AS_IF([test "x$enable_udev_acl" = "xyes"], [
+
+ PKG_CHECK_MODULES([UDEV_ACL], [glib-2.0 >= 2.22.0 gobject-2.0 >= 2.22.0 libudev])
+ AC_CHECK_LIB([acl], [acl_init], [UDEV_ACL_LIBS="$UDEV_ACL_LIBS -lacl"], AC_MSG_ERROR([libacl not found]))
+ AC_CHECK_HEADER([acl/libacl.h], [:], AC_MSG_ERROR([libacl header not found]))
+ UDEVDIR=`$PKG_CONFIG --variable udevdir udev`
+ if test -z "$UDEVDIR" ; then
+ UDEVDIR="/lib/udev"
+ fi
+ AC_SUBST(UDEVDIR)
+])
+AM_CONDITIONAL([ENABLE_UDEV_ACL], [test "x$enable_udev_acl" = "xyes"])
+
+
dnl ---------------------------------------------------------------------------
dnl - Install directory for PAM security module
dnl ---------------------------------------------------------------------------
AC_ARG_WITH(pam-module-dir,
- [AC_HELP_STRING([--with-pam-module-dir=],
- [directory to install PAM security module])])
+ [AS_HELP_STRING([--with-pam-module-dir=],[directory to install PAM security module])])
if ! test -z "$with_pam_module_dir"; then
PAM_MODULE_DIR="$with_pam_module_dir"
else
@@ -252,8 +239,7 @@ dnl - DocBook Documentation
dnl ---------------------------------------------------------------------------
AC_ARG_ENABLE(docbook-docs,
- [AC_HELP_STRING([--enable-docbook-docs],
- [build documentation (requires xmlto)])],
+ [AS_HELP_STRING([--enable-docbook-docs],[build documentation (requires xmlto)])],
enable_docbook_docs=$enableval,enable_docbook_docs=no)
AC_PATH_PROG(XMLTO, xmlto, no)
AC_MSG_CHECKING([whether to build DocBook documentation])
@@ -308,8 +294,7 @@ dnl ---------------------------------------------------------------------------
msg_rbac_shutdown=no
AC_ARG_ENABLE(rbac-shutdown,
- [AC_HELP_STRING([--enable-rbac-shutdown=],
- [Build with RBAC support specifying shutdown/reboot RBAC authentication key])],
+ [AS_HELP_STRING([--enable-rbac-shutdown=],[Build with RBAC support specifying shutdown/reboot RBAC authentication key])],
enable_rbac_shutdown=$enableval,enable_rbac_shutdown=no)
if test "x$enable_rbac_shutdown" != "xno"; then
RBAC_LIBS="-lsecdb -lsocket -lnsl"
@@ -326,8 +311,7 @@ dnl ---------------------------------------------------------------------------
# Turn on the additional warnings last, so -Werror doesn't affect other tests.
AC_ARG_ENABLE(more-warnings,
- [AC_HELP_STRING([--enable-more-warnings],
- [Maximum compiler warnings])],
+ [AS_HELP_STRING([--enable-more-warnings],[Maximum compiler warnings])],
set_more_warnings="$enableval",[
if test -d $srcdir/.git; then
set_more_warnings=yes
@@ -349,9 +333,7 @@ if test "$GCC" = "yes" -a "$set_more_warnings" != "no"; then
SAVE_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS $option"
AC_MSG_CHECKING([whether gcc understands $option])
- AC_TRY_COMPILE([], [],
- has_option=yes,
- has_option=no,)
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])],[has_option=yes],[has_option=no])
if test $has_option = no; then
CFLAGS="$SAVE_CFLAGS"
fi
@@ -368,8 +350,7 @@ fi
# Enable Debug
#
AC_ARG_ENABLE(debug,
- [AC_HELP_STRING([--enable-debug],
- [turn on debugging])],
+ [AS_HELP_STRING([--enable-debug],[turn on debugging])],
, enable_debug=yes)
if test "$enable_debug" = "yes"; then
DEBUG_CFLAGS="-DG_ENABLE_DEBUG"
@@ -395,10 +376,9 @@ AC_SUBST(LDFLAGS)
AC_ARG_WITH([systemdsystemunitdir],
AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files]),
- [],
- [with_systemdsystemunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd)])
-AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir])
-AM_CONDITIONAL(HAVE_SYSTEMD, [test -n "$with_systemdsystemunitdir"])
+ [], [with_systemdsystemunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd)])
+AS_IF([test "x$with_systemdsystemunitdir" != "xno"], [ AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir])])
+AM_CONDITIONAL(HAVE_SYSTEMD, [test -n "$with_systemdsystemunitdir" -a "x$with_systemdsystemunitdir" != "xno"])
# Files
@@ -445,6 +425,7 @@ echo "
Build backend: ${CK_BACKEND}
PAM module dir: ${PAM_MODULE_DIR}
Build PAM module: ${msg_pam_module}
+ Build udev-acl: ${enable_udev_acl}
Build docs: ${enable_docbook_docs}
PolicyKit support ${have_polkit}
diff --git a/pam-ck-connector/pam-ck-connector.c b/pam-ck-connector/pam-ck-connector.c
index 9bdef51..949a9b0 100644
--- a/pam-ck-connector/pam-ck-connector.c
+++ b/pam-ck-connector/pam-ck-connector.c
@@ -244,12 +244,12 @@ pam_sm_open_session (pam_handle_t *pamh,
const char *s;
uid_t uid;
char buf[256];
- char ttybuf[PATH_MAX];
+ char *ttybuf;
DBusError error;
dbus_bool_t is_local;
ret = PAM_IGNORE;
-
+ ttybuf = NULL;
is_local = TRUE;
_parse_pam_args (pamh, flags, argc, argv);
@@ -295,7 +295,13 @@ pam_sm_open_session (pam_handle_t *pamh,
x11_display = display_device;
display_device = "";
} else if (strncmp (_PATH_DEV, display_device, 5) != 0) {
- snprintf (ttybuf, sizeof (ttybuf), _PATH_DEV "%s", display_device);
+ int len = strlen (_PATH_DEV) + strlen (display_device) + 1;
+ ttybuf = malloc (len);
+ if (ttybuf == NULL) {
+ ck_pam_syslog (pamh, LOG_ERR, "oom allocating ttybuf");
+ goto out;
+ }
+ snprintf (ttybuf, len, _PATH_DEV "%s", display_device);
display_device = ttybuf;
}
@@ -411,6 +417,8 @@ pam_sm_open_session (pam_handle_t *pamh,
ret = PAM_SUCCESS;
out:
+ free (ttybuf);
+
return ret;
}
diff --git a/src/Makefile.am b/src/Makefile.am
index 869decd..217e531 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -57,11 +57,18 @@ libck_la_SOURCES += \
$(NULL)
libck_la_LIBADD = $(KVM_LIBS)
endif
+if CK_COMPILE_GNU
+libck_la_SOURCES += \
+ ck-sysdeps-gnu.c \
+ $(NULL)
+libck_la_LIBADD = -lps
+endif
EXTRA_libck_la_SOURCES = \
ck-sysdeps-linux.c \
ck-sysdeps-solaris.c \
ck-sysdeps-freebsd.c \
+ ck-sysdeps-gnu.c \
$(NULL)
sbin_PROGRAMS = \
diff --git a/src/ck-sysdeps-gnu.c b/src/ck-sysdeps-gnu.c
new file mode 100644
index 0000000..254f7ef
--- /dev/null
+++ b/src/ck-sysdeps-gnu.c
@@ -0,0 +1,402 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2009 Pino Toscano
+ *
+ * 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "config.h"
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#ifdef HAVE_PATHS_H
+#include
+#endif /* HAVE_PATHS_H */
+
+#include "ck-sysdeps.h"
+
+struct _CkProcessStat
+{
+ struct proc_stat *ps; /* the statistics of a process */
+};
+
+static struct ps_context *pc = NULL;
+
+static gboolean
+get_proc_stat_from_pid (pid_t pid,
+ ps_flags_t flags,
+ struct proc_stat **res_ps)
+{
+ error_t err;
+ struct proc_stat *ps;
+
+ g_assert (pid >= 0);
+ g_assert (res_ps != NULL);
+
+ if (pc == NULL) {
+ err = ps_context_create (getproc (), &pc);
+ if (err) {
+ return FALSE;
+ }
+ }
+
+ err = _proc_stat_create (pid, pc, &ps);
+ if (err) {
+ return FALSE;
+ }
+
+ err = proc_stat_set_flags (ps, PSTAT_PID | flags);
+ if (err) {
+ return FALSE;
+ }
+
+ *res_ps = ps;
+ return TRUE;
+}
+
+
+pid_t
+ck_process_stat_get_ppid (CkProcessStat *stat)
+{
+ g_return_val_if_fail (stat != NULL, -1);
+
+ return proc_stat_pid (stat->ps);
+}
+
+char *
+ck_process_stat_get_cmd (CkProcessStat *stat)
+{
+ g_return_val_if_fail (stat != NULL, NULL);
+
+ return g_strdup (proc_stat_args (stat->ps));
+}
+
+char *
+ck_process_stat_get_tty (CkProcessStat *stat)
+{
+ struct ps_tty *tty;
+
+ g_return_val_if_fail (stat != NULL, NULL);
+
+ tty = proc_stat_tty (stat->ps);
+
+ return tty ? g_strdup (ps_tty_name (tty)) : NULL;
+}
+
+gboolean
+ck_process_stat_new_for_unix_pid (pid_t pid,
+ CkProcessStat **stat,
+ GError **error)
+{
+ gboolean res;
+ struct proc_stat *ps;
+ CkProcessStat *proc;
+
+ g_return_val_if_fail (pid > 1, FALSE);
+
+ if (stat == NULL) {
+ return FALSE;
+ }
+
+ *stat = NULL;
+
+ res = get_proc_stat_from_pid (pid, PSTAT_ARGS | PSTAT_TTY, &ps);
+ if (!res) {
+ return FALSE;
+ }
+
+ proc = g_new0 (CkProcessStat, 1);
+ proc->ps = ps;
+ *stat = proc;
+
+ return TRUE;
+}
+
+void
+ck_process_stat_free (CkProcessStat *stat)
+{
+ _proc_stat_free (stat->ps);
+
+ g_free (stat);
+}
+
+GHashTable *
+ck_unix_pid_get_env_hash (pid_t pid)
+{
+ struct proc_stat *ps;
+ char *env_p;
+ size_t env_index;
+ size_t env_l;
+ gboolean res;
+ GHashTable *hash;
+
+ g_return_val_if_fail (pid > 1, NULL);
+
+ res = get_proc_stat_from_pid (pid, PSTAT_ENV, &ps);
+ if (!res) {
+ return NULL;
+ }
+
+ hash = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ g_free);
+
+ env_index = 0;
+ env_l = 0;
+ env_p = proc_stat_env (ps);
+ while (env_index < proc_stat_env_len (ps)) {
+ env_l = strlen (env_p);
+ env_index += env_l + 1;
+ if (env_l) {
+ char **vals;
+ vals = g_strsplit (env_p, "=", 2);
+ if (vals != NULL) {
+ g_hash_table_insert (hash,
+ g_strdup (vals[0]),
+ g_strdup (vals[1]));
+ g_strfreev (vals);
+ }
+ }
+ env_p = env_p + env_l + 1;
+ }
+
+ _proc_stat_free (ps);
+
+ return hash;
+}
+
+char *
+ck_unix_pid_get_env (pid_t pid,
+ const char *var)
+{
+ struct proc_stat *ps;
+ char *env_p;
+ size_t env_index;
+ size_t env_l;
+ char *prefix;
+ int prefix_len;
+ char *val;
+ gboolean res;
+
+ g_return_val_if_fail (pid > 1, NULL);
+
+ val = NULL;
+
+ res = get_proc_stat_from_pid (pid, PSTAT_ENV, &ps);
+ if (!res) {
+ return NULL;
+ }
+
+ prefix = g_strdup_printf ("%s=", var);
+ prefix_len = strlen (prefix);
+
+ env_index = 0;
+ env_l = 0;
+ env_p = proc_stat_env (ps);
+ while (env_index < proc_stat_env_len (ps)) {
+ env_l = strlen (env_p);
+ env_index += env_l + 1;
+ if (env_l && g_str_has_prefix (env_p, prefix)) {
+ val = g_strdup (env_p + prefix_len);
+ break;
+ }
+ env_p = env_p + env_l + 1;
+ }
+
+ g_free (prefix);
+
+ _proc_stat_free (ps);
+
+ return val;
+}
+
+uid_t
+ck_unix_pid_get_uid (pid_t pid)
+{
+ struct proc_stat *ps;
+ gboolean res;
+ uid_t uid;
+
+ g_return_val_if_fail (pid > 1, 0);
+
+ res = get_proc_stat_from_pid (pid, PSTAT_OWNER_UID, &ps);
+ if (!res) {
+ return 0;
+ }
+
+ uid = proc_stat_owner_uid (ps);
+
+ _proc_stat_free (ps);
+
+ return uid;
+}
+
+pid_t
+ck_unix_pid_get_ppid (pid_t pid)
+{
+ struct proc_stat *ps;
+ gboolean res;
+ pid_t ppid;
+
+ g_return_val_if_fail (pid > 1, 0);
+
+ res = get_proc_stat_from_pid (pid, PSTAT_PROC_INFO, &ps);
+ if (!res) {
+ return 0;
+ }
+
+ ppid = proc_stat_proc_info (ps)->ppid;
+
+ _proc_stat_free (ps);
+
+ return ppid;
+}
+
+gboolean
+ck_unix_pid_get_login_session_id (pid_t pid,
+ char **idp)
+{
+ g_return_val_if_fail (pid > 1, FALSE);
+
+ return FALSE;
+}
+
+gboolean
+ck_get_max_num_consoles (guint *num)
+{
+ int max_consoles;
+ int res;
+ gboolean ret;
+ struct ttyent *t;
+
+ ret = FALSE;
+ max_consoles = 0;
+
+ res = setttyent ();
+ if (res == 0) {
+ goto done;
+ }
+
+ while ((t = getttyent ()) != NULL) {
+ if (t->ty_status & TTY_ON && strncmp (t->ty_name, "tty", 3) == 0)
+ max_consoles++;
+ }
+
+ /* Increment one more so that all consoles are properly counted
+ * this is arguable a bug in vt_add_watches().
+ */
+ max_consoles++;
+
+ ret = TRUE;
+
+ endttyent ();
+
+done:
+ if (num != NULL) {
+ *num = max_consoles;
+ }
+
+ return ret;
+}
+
+gboolean
+ck_supports_activatable_consoles (void)
+{
+ return TRUE;
+}
+
+char *
+ck_get_console_device_for_num (guint num)
+{
+ char *device;
+
+ device = g_strdup_printf (_PATH_TTY "%u", num);
+
+ return device;
+}
+
+gboolean
+ck_get_console_num_from_device (const char *device,
+ guint *num)
+{
+ guint n;
+ gboolean ret;
+
+ n = 0;
+ ret = FALSE;
+
+ if (device == NULL) {
+ return FALSE;
+ }
+
+ if (sscanf (device, _PATH_TTY "%u", &n) == 1) {
+ ret = TRUE;
+ }
+
+ if (num != NULL) {
+ *num = n;
+ }
+
+ return ret;
+}
+
+gboolean
+ck_get_active_console_num (int console_fd,
+ guint *num)
+{
+ gboolean ret;
+ int res;
+ long cur_active;
+ char buf[30];
+ guint active;
+
+ g_assert (console_fd != -1);
+
+ active = 0;
+ ret = FALSE;
+
+ res = readlink ("/dev/cons/vcs", buf, sizeof (buf));
+ if (res > 0) {
+ /* the resolved path is like "/dev/vcs/$number", so skip
+ the non-number part at the start */
+ const char *p = buf;
+ while ((*p) && ((*p < '0') || (*p > '9'))) {
+ ++p;
+ }
+ if (*p) {
+ cur_active = strtol (p, NULL, 10);
+ g_debug ("Current VT: tty%ld", cur_active);
+ active = cur_active;
+ ret = TRUE;
+ }
+ }
+
+ if (num != NULL) {
+ *num = active;
+ }
+
+ return ret;
+}
diff --git a/tools/70-udev-acl.rules b/tools/70-udev-acl.rules
new file mode 100644
index 0000000..2dac283
--- /dev/null
+++ b/tools/70-udev-acl.rules
@@ -0,0 +1,76 @@
+# do not edit this file, it will be overwritten on update
+
+# Do not use TAG+="udev-acl" outside of this file. This variable is private to
+# udev-acl of this udev release and may be replaced at any time.
+
+ENV{MAJOR}=="", GOTO="acl_end"
+ACTION=="remove", GOTO="acl_apply"
+
+# systemd replaces udev-acl entirely, skip if active
+TEST=="/sys/fs/cgroup/systemd", TAG=="uaccess", GOTO="acl_end"
+
+# PTP/MTP protocol devices, cameras, portable media players
+SUBSYSTEM=="usb", ENV{ID_USB_INTERFACES}=="*:060101:*", TAG+="udev-acl"
+
+# digicams with proprietary protocol
+ENV{ID_GPHOTO2}=="*?", TAG+="udev-acl"
+
+# SCSI and USB scanners
+ENV{libsane_matched}=="yes", TAG+="udev-acl"
+
+# HPLIP devices (necessary for ink level check and HP tool maintenance)
+ENV{ID_HPLIP}=="1", TAG+="udev-acl"
+
+# optical drives
+SUBSYSTEM=="block", ENV{ID_CDROM}=="1", TAG+="udev-acl"
+SUBSYSTEM=="scsi_generic", SUBSYSTEMS=="scsi", ATTRS{type}=="4|5", TAG+="udev-acl"
+
+# sound devices
+SUBSYSTEM=="sound", TAG+="udev-acl"
+
+# ffado is an userspace driver for firewire sound cards
+SUBSYSTEM=="firewire", ENV{ID_FFADO}=="1", TAG+="udev-acl"
+
+# webcams, frame grabber, TV cards
+SUBSYSTEM=="video4linux", TAG+="udev-acl"
+SUBSYSTEM=="dvb", TAG+="udev-acl"
+
+# IIDC devices: industrial cameras and some webcams
+SUBSYSTEM=="firewire", ATTR{units}=="*0x00a02d:0x00010*", TAG+="udev-acl"
+SUBSYSTEM=="firewire", ATTR{units}=="*0x00b09d:0x00010*", TAG+="udev-acl"
+# AV/C devices: camcorders, set-top boxes, TV sets, audio devices, and more
+SUBSYSTEM=="firewire", ATTR{units}=="*0x00a02d:0x010001*", TAG+="udev-acl"
+SUBSYSTEM=="firewire", ATTR{units}=="*0x00a02d:0x014001*", TAG+="udev-acl"
+
+# DRI video devices
+SUBSYSTEM=="drm", KERNEL=="card*", TAG+="udev-acl"
+
+# KVM
+SUBSYSTEM=="misc", KERNEL=="kvm", TAG+="udev-acl"
+
+# smart-card readers
+ENV{ID_SMARTCARD_READER}=="*?", TAG+="udev-acl"
+
+# PDA devices
+ENV{ID_PDA}=="*?", TAG+="udev-acl"
+
+# Programmable remote control
+ENV{ID_REMOTE_CONTROL}=="1", TAG+="udev-acl"
+
+# joysticks
+SUBSYSTEM=="input", ENV{ID_INPUT_JOYSTICK}=="?*", TAG+="udev-acl"
+
+# color measurement devices
+ENV{COLOR_MEASUREMENT_DEVICE}=="*?", TAG+="udev-acl"
+
+# DDC/CI device, usually high-end monitors such as the DreamColor
+ENV{DDC_DEVICE}=="*?", TAG+="udev-acl"
+
+# media player raw devices (for user-mode drivers, Android SDK, etc.)
+SUBSYSTEM=="usb", ENV{ID_MEDIA_PLAYER}=="?*", TAG+="udev-acl"
+
+# apply ACL for all locally logged in users
+LABEL="acl_apply", TAG=="udev-acl", TEST=="/var/run/ConsoleKit/database", \
+ RUN+="udev-acl --action=$env{ACTION} --device=$env{DEVNAME}"
+
+LABEL="acl_end"
diff --git a/tools/Makefile.am b/tools/Makefile.am
index 13c191f..88f3c91 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -143,6 +143,21 @@ ck_get_x11_display_device_LDADD = \
$(top_builddir)/src/libck.la \
$(NULL)
+if ENABLE_UDEV_ACL
+udevdir = $(UDEVDIR)
+udevrulesdir = $(UDEVDIR)/rules.d
+
+dist_udevrules_DATA = 70-udev-acl.rules
+udev_PROGRAMS = udev-acl
+
+udev_acl_SOURCES = udev-acl.c
+udev_acl_LDADD = $(UDEV_ACL_LIBS)
+udev_acl_CFLAGS = $(UDEV_ACL_CFLAGS)
+
+install-exec-hook:
+ mkdir -p $(DESTDIR)$(prefix)/lib/ConsoleKit/run-seat.d
+ ln -sf $(UDEVDIR)/udev-acl $(DESTDIR)$(prefix)/lib/ConsoleKit/run-seat.d/udev-acl.ck
+endif
EXTRA_DIST = \
$(NULL)
diff --git a/tools/ck-history.c b/tools/ck-history.c
index d02caaa..85d9e6f 100644
--- a/tools/ck-history.c
+++ b/tools/ck-history.c
@@ -804,7 +804,7 @@ generate_report_frequent (int uid,
data = user_counts->data;
username = get_user_name_for_uid (data->uid);
- g_print ("%-8.8s %u\n", username, data->count);
+ g_print ("%-8s %u\n", username, data->count);
g_free (data);
user_counts = g_list_delete_link (user_counts, user_counts);
g_free (username);
diff --git a/tools/udev-acl.c b/tools/udev-acl.c
new file mode 100644
index 0000000..628cfbe
--- /dev/null
+++ b/tools/udev-acl.c
@@ -0,0 +1,430 @@
+/*
+ * Copyright (C) 2009 Kay Sievers
+ *
+ * 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 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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
+ * General Public License for more details:
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+static int debug;
+
+enum{
+ ACTION_NONE = 0,
+ ACTION_REMOVE,
+ ACTION_ADD,
+ ACTION_CHANGE
+};
+
+static int set_facl(const char* filename, uid_t uid, int add)
+{
+ int get;
+ acl_t acl;
+ acl_entry_t entry = NULL;
+ acl_entry_t e;
+ acl_permset_t permset;
+ int ret;
+
+ /* don't touch ACLs for root */
+ if (uid == 0)
+ return 0;
+
+ /* read current record */
+ acl = acl_get_file(filename, ACL_TYPE_ACCESS);
+ if (!acl)
+ return -1;
+
+ /* locate ACL_USER entry for uid */
+ get = acl_get_entry(acl, ACL_FIRST_ENTRY, &e);
+ while (get == 1) {
+ acl_tag_t t;
+
+ acl_get_tag_type(e, &t);
+ if (t == ACL_USER) {
+ uid_t *u;
+
+ u = (uid_t*)acl_get_qualifier(e);
+ if (u == NULL) {
+ ret = -1;
+ goto out;
+ }
+ if (*u == uid) {
+ entry = e;
+ acl_free(u);
+ break;
+ }
+ acl_free(u);
+ }
+
+ get = acl_get_entry(acl, ACL_NEXT_ENTRY, &e);
+ }
+
+ /* remove ACL_USER entry for uid */
+ if (!add) {
+ if (entry == NULL) {
+ ret = 0;
+ goto out;
+ }
+ acl_delete_entry(acl, entry);
+ goto update;
+ }
+
+ /* create ACL_USER entry for uid */
+ if (entry == NULL) {
+ ret = acl_create_entry(&acl, &entry);
+ if (ret != 0)
+ goto out;
+ acl_set_tag_type(entry, ACL_USER);
+ acl_set_qualifier(entry, &uid);
+ }
+
+ /* add permissions for uid */
+ acl_get_permset(entry, &permset);
+ acl_add_perm(permset, ACL_READ|ACL_WRITE);
+update:
+ /* update record */
+ if (debug)
+ printf("%c%u %s\n", add ? '+' : '-', uid, filename);
+ acl_calc_mask(&acl);
+ ret = acl_set_file(filename, ACL_TYPE_ACCESS, acl);
+ if (ret != 0)
+ goto out;
+out:
+ acl_free(acl);
+ return ret;
+}
+
+/* check if a given uid is listed */
+static int uid_in_list(GSList *list, uid_t uid)
+{
+ GSList *l;
+
+ for (l = list; l != NULL; l = g_slist_next(l))
+ if (uid == GPOINTER_TO_UINT(l->data))
+ return 1;
+ return 0;
+}
+
+/* return list of current uids of local active sessions */
+static GSList *uids_with_local_active_session(const char *own_id)
+{
+ GSList *list = NULL;
+ GKeyFile *keyfile;
+
+ keyfile = g_key_file_new();
+ if (g_key_file_load_from_file(keyfile, "/var/run/ConsoleKit/database", 0, NULL)) {
+ gchar **groups;
+
+ groups = g_key_file_get_groups(keyfile, NULL);
+ if (groups != NULL) {
+ int i;
+
+ for (i = 0; groups[i] != NULL; i++) {
+ uid_t u;
+
+ if (!g_str_has_prefix(groups[i], "Session "))
+ continue;
+ if (own_id != NULL &&g_str_has_suffix(groups[i], own_id))
+ continue;
+ if (!g_key_file_get_boolean(keyfile, groups[i], "is_local", NULL))
+ continue;
+ if (!g_key_file_get_boolean(keyfile, groups[i], "is_active", NULL))
+ continue;
+ u = g_key_file_get_integer(keyfile, groups[i], "uid", NULL);
+ if (u > 0 && !uid_in_list(list, u))
+ list = g_slist_prepend(list, GUINT_TO_POINTER(u));
+ }
+ g_strfreev(groups);
+ }
+ }
+ g_key_file_free(keyfile);
+
+ return list;
+}
+
+/* ConsoleKit calls us with special variables */
+static int consolekit_called(const char *ck_action, uid_t *uid, uid_t *uid2, const char **remove_session_id, int *action)
+{
+ int a = ACTION_NONE;
+ uid_t u = 0;
+ uid_t u2 = 0;
+ const char *s;
+ const char *s2;
+ const char *old_session = NULL;
+
+ if (ck_action == NULL || strcmp(ck_action, "seat_active_session_changed") != 0)
+ return -1;
+
+ /* We can have one of: remove, add, change, no-change */
+ s = getenv("CK_SEAT_OLD_SESSION_ID");
+ s2 = getenv("CK_SEAT_SESSION_ID");
+ if (s == NULL && s2 == NULL) {
+ return -1;
+ } else if (s2 == NULL) {
+ a = ACTION_REMOVE;
+ } else if (s == NULL) {
+ a = ACTION_ADD;
+ } else {
+ a = ACTION_CHANGE;
+ }
+
+ switch (a) {
+ case ACTION_ADD:
+ s = getenv("CK_SEAT_SESSION_USER_UID");
+ if (s == NULL)
+ return -1;
+ u = strtoul(s, NULL, 10);
+
+ s = getenv("CK_SEAT_SESSION_IS_LOCAL");
+ if (s == NULL)
+ return -1;
+ if (strcmp(s, "true") != 0)
+ return 0;
+
+ break;
+ case ACTION_REMOVE:
+ s = getenv("CK_SEAT_OLD_SESSION_USER_UID");
+ if (s == NULL)
+ return -1;
+ u = strtoul(s, NULL, 10);
+
+ s = getenv("CK_SEAT_OLD_SESSION_IS_LOCAL");
+ if (s == NULL)
+ return -1;
+ if (strcmp(s, "true") != 0)
+ return 0;
+
+ old_session = getenv("CK_SEAT_OLD_SESSION_ID");
+ if (old_session == NULL)
+ return -1;
+
+ break;
+ case ACTION_CHANGE:
+ s = getenv("CK_SEAT_OLD_SESSION_USER_UID");
+ if (s == NULL)
+ return -1;
+ u = strtoul(s, NULL, 10);
+ s = getenv("CK_SEAT_SESSION_USER_UID");
+ if (s == NULL)
+ return -1;
+ u2 = strtoul(s, NULL, 10);
+
+ s = getenv("CK_SEAT_OLD_SESSION_IS_LOCAL");
+ s2 = getenv("CK_SEAT_SESSION_IS_LOCAL");
+ if (s == NULL || s2 == NULL)
+ return -1;
+ /* don't process non-local session changes */
+ if (strcmp(s, "true") != 0 && strcmp(s2, "true") != 0)
+ return 0;
+
+ if (strcmp(s, "true") == 0 && strcmp(s, "true") == 0) {
+ /* process the change */
+ if (u == u2) {
+ /* special case: we noop if we are
+ * changing between local sessions for
+ * the same uid */
+ a = ACTION_NONE;
+ }
+ old_session = getenv("CK_SEAT_OLD_SESSION_ID");
+ if (old_session == NULL)
+ return -1;
+ } else if (strcmp(s, "true") == 0) {
+ /* only process the removal */
+ a = ACTION_REMOVE;
+ old_session = getenv("CK_SEAT_OLD_SESSION_ID");
+ if (old_session == NULL)
+ return -1;
+ } else if (strcmp(s2, "true") == 0) {
+ /* only process the addition */
+ a = ACTION_ADD;
+ u = u2;
+ }
+ break;
+ }
+
+ *remove_session_id = old_session;
+ *uid = u;
+ *uid2 = u2;
+ *action = a;
+ return 0;
+}
+
+/* add or remove a ACL for a given uid from all matching devices */
+static void apply_acl_to_devices(uid_t uid, int add)
+{
+ struct udev *udev;
+ struct udev_enumerate *enumerate;
+ struct udev_list_entry *list_entry;
+
+ /* iterate over all devices tagged with ACL_SET */
+ udev = udev_new();
+ enumerate = udev_enumerate_new(udev);
+ udev_enumerate_add_match_tag(enumerate, "udev-acl");
+ udev_enumerate_scan_devices(enumerate);
+ udev_list_entry_foreach(list_entry, udev_enumerate_get_list_entry(enumerate)) {
+ struct udev_device *device;
+ const char *node;
+
+ device = udev_device_new_from_syspath(udev_enumerate_get_udev(enumerate),
+ udev_list_entry_get_name(list_entry));
+ if (device == NULL)
+ continue;
+ node = udev_device_get_devnode(device);
+ if (node == NULL) {
+ udev_device_unref(device);
+ continue;
+ }
+ set_facl(node, uid, add);
+ udev_device_unref(device);
+ }
+ udev_enumerate_unref(enumerate);
+ udev_unref(udev);
+}
+
+static void
+remove_uid (uid_t uid, const char *remove_session_id)
+{
+ /*
+ * Remove ACL for given uid from all matching devices
+ * when there is currently no local active session.
+ */
+ GSList *list;
+
+ list = uids_with_local_active_session(remove_session_id);
+ if (!uid_in_list(list, uid))
+ apply_acl_to_devices(uid, 0);
+ g_slist_free(list);
+}
+
+int main (int argc, char* argv[])
+{
+ static const struct option options[] = {
+ { "action", required_argument, NULL, 'a' },
+ { "device", required_argument, NULL, 'D' },
+ { "user", required_argument, NULL, 'u' },
+ { "debug", no_argument, NULL, 'd' },
+ { "help", no_argument, NULL, 'h' },
+ {}
+ };
+ int action = -1;
+ const char *device = NULL;
+ bool uid_given = false;
+ uid_t uid = 0;
+ uid_t uid2 = 0;
+ const char* remove_session_id = NULL;
+ int rc = 0;
+
+ /* valgrind is more important to us than a slice allocator */
+ g_slice_set_config (G_SLICE_CONFIG_ALWAYS_MALLOC, 1);
+
+ while (1) {
+ int option;
+
+ option = getopt_long(argc, argv, "+a:D:u:dh", options, NULL);
+ if (option == -1)
+ break;
+
+ switch (option) {
+ case 'a':
+ if (strcmp(optarg, "remove") == 0)
+ action = ACTION_REMOVE;
+ else
+ action = ACTION_ADD;
+ break;
+ case 'D':
+ device = optarg;
+ break;
+ case 'u':
+ uid_given = true;
+ uid = strtoul(optarg, NULL, 10);
+ break;
+ case 'd':
+ debug = 1;
+ break;
+ case 'h':
+ printf("Usage: udev-acl --action=ACTION [--device=DEVICEFILE] [--user=UID]\n\n");
+ goto out;
+ }
+ }
+
+ if (action < 0 && device == NULL && !uid_given)
+ if (!consolekit_called(argv[optind], &uid, &uid2, &remove_session_id, &action))
+ uid_given = true;
+
+ if (action < 0) {
+ fprintf(stderr, "missing action\n\n");
+ rc = 2;
+ goto out;
+ }
+
+ if (device != NULL && uid_given) {
+ fprintf(stderr, "only one option, --device=DEVICEFILE or --user=UID expected\n\n");
+ rc = 3;
+ goto out;
+ }
+
+ if (uid_given) {
+ switch (action) {
+ case ACTION_ADD:
+ /* Add ACL for given uid to all matching devices. */
+ apply_acl_to_devices(uid, 1);
+ break;
+ case ACTION_REMOVE:
+ remove_uid(uid, remove_session_id);
+ break;
+ case ACTION_CHANGE:
+ remove_uid(uid, remove_session_id);
+ apply_acl_to_devices(uid2, 1);
+ break;
+ case ACTION_NONE:
+ goto out;
+ break;
+ default:
+ g_assert_not_reached();
+ break;
+ }
+ } else if (device != NULL) {
+ /*
+ * Add ACLs for all current session uids to a given device.
+ *
+ * Or remove ACLs for uids which do not have any current local
+ * active session. Remove is not really interesting, because in
+ * most cases the device node is removed anyway.
+ */
+ GSList *list;
+ GSList *l;
+
+ list = uids_with_local_active_session(NULL);
+ for (l = list; l != NULL; l = g_slist_next(l)) {
+ uid_t u;
+
+ u = GPOINTER_TO_UINT(l->data);
+ if (action == ACTION_ADD || !uid_in_list(list, u))
+ set_facl(device, u, action == ACTION_ADD);
+ }
+ g_slist_free(list);
+ } else {
+ fprintf(stderr, "--device=DEVICEFILE or --user=UID expected\n\n");
+ rc = 3;
+ }
+out:
+ return rc;
+}