summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am126
-rw-r--r--src/Makefile.in768
-rw-r--r--src/ck-file-monitor-dummy.c118
-rw-r--r--src/ck-file-monitor-inotify.c669
-rw-r--r--src/ck-file-monitor.h84
-rw-r--r--src/ck-job.c391
-rw-r--r--src/ck-job.h76
-rw-r--r--src/ck-log.c197
-rw-r--r--src/ck-log.h41
-rw-r--r--src/ck-manager-glue.h226
-rw-r--r--src/ck-manager.c1622
-rw-r--r--src/ck-manager.h113
-rw-r--r--src/ck-manager.xml61
-rw-r--r--src/ck-marshal.c126
-rw-r--r--src/ck-marshal.h28
-rw-r--r--src/ck-marshal.list2
-rw-r--r--src/ck-seat-glue.h168
-rw-r--r--src/ck-seat.c889
-rw-r--r--src/ck-seat.h111
-rw-r--r--src/ck-seat.xml30
-rw-r--r--src/ck-session-glue.h185
-rw-r--r--src/ck-session.c1208
-rw-r--r--src/ck-session.h174
-rw-r--r--src/ck-session.xml78
-rw-r--r--src/ck-tty-idle-monitor.c389
-rw-r--r--src/ck-tty-idle-monitor.h71
-rw-r--r--src/ck-vt-monitor.c503
-rw-r--r--src/ck-vt-monitor.h71
-rw-r--r--src/getfd.c75
-rw-r--r--src/main.c354
-rw-r--r--src/proc-linux.c605
-rw-r--r--src/proc.h48
-rwxr-xr-xsrc/test-open-session46
-rwxr-xr-xsrc/test-open-session-with-parameters54
-rw-r--r--src/test-tty-idle-monitor.c82
-rwxr-xr-xsrc/valgrind.sh12
36 files changed, 9801 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..0977591
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,126 @@
+## We require new-style dependency handling.
+AUTOMAKE_OPTIONS = 1.7
+
+NULL =
+
+SUBDIRS = \
+ $(NULL)
+
+INCLUDES = \
+ -I. \
+ -I$(srcdir) \
+ $(CONSOLE_KIT_CFLAGS) \
+ $(DISABLE_DEPRECATED_CFLAGS) \
+ -DPREFIX=\""$(prefix)"\" \
+ -DBINDIR=\""$(bindir)"\" \
+ -DLIBDIR=\""$(libdir)"\" \
+ -DLIBEXECDIR=\""$(libexecdir)"\" \
+ -DDATADIR=\""$(datadir)"\" \
+ -DSYSCONFDIR=\""$(sysconfdir)"\" \
+ -DCONSOLE_KIT_PID_FILE=\""$(CONSOLE_KIT_PID_FILE)"\" \
+ $(WARN_CFLAGS) \
+ $(DEBUG_CFLAGS) \
+ $(DBUS_CFLAGS) \
+ $(NULL)
+
+sbin_PROGRAMS = \
+ console-kit-daemon \
+ $(NULL)
+
+BUILT_SOURCES = \
+ ck-manager-glue.h \
+ ck-seat-glue.h \
+ ck-session-glue.h \
+ ck-marshal.c \
+ ck-marshal.h \
+ $(NULL)
+
+ck-manager-glue.h: ck-manager.xml Makefile.am
+ dbus-binding-tool --prefix=ck_manager --mode=glib-server --output=ck-manager-glue.h ck-manager.xml
+ck-seat-glue.h: ck-seat.xml Makefile.am
+ dbus-binding-tool --prefix=ck_seat --mode=glib-server --output=ck-seat-glue.h ck-seat.xml
+ck-session-glue.h: ck-session.xml Makefile.am
+ dbus-binding-tool --prefix=ck_session --mode=glib-server --output=ck-session-glue.h ck-session.xml
+
+ck-marshal.c: ck-marshal.list
+ echo "#include \"ck-marshal.h\"" > $@ && \
+ @GLIB_GENMARSHAL@ $< --prefix=ck_marshal --body >> $@
+
+ck-marshal.h: ck-marshal.list
+ @GLIB_GENMARSHAL@ $< --prefix=ck_marshal --header > $@
+
+PLATFORM_SOURCES = \
+ proc-linux.c \
+ $(NULL)
+
+console_kit_daemon_SOURCES = \
+ main.c \
+ ck-manager.h \
+ ck-manager.c \
+ ck-vt-monitor.h \
+ ck-vt-monitor.c \
+ ck-tty-idle-monitor.h \
+ ck-tty-idle-monitor.c \
+ ck-file-monitor.h \
+ ck-job.h \
+ ck-job.c \
+ ck-seat.h \
+ ck-seat.c \
+ ck-session.h \
+ ck-session.c \
+ ck-log.h \
+ ck-log.c \
+ getfd.c \
+ proc.h \
+ $(PLATFORM_SOURCES) \
+ $(BUILT_SOURCES) \
+ $(NULL)
+
+if ENABLE_INOTIFY
+FILE_MONITOR_BACKEND = ck-file-monitor-inotify.c
+else
+FILE_MONITOR_BACKEND = ck-file-monitor-dummy.c
+endif
+
+console_kit_daemon_SOURCES += $(FILE_MONITOR_BACKEND)
+
+EXTRA_console_kit_daemon_SOURCES = \
+ ck-file-monitor-inotify.c \
+ ck-file-monitor-dummy.c \
+ $(NULL)
+
+console_kit_daemon_LDADD = \
+ $(CONSOLE_KIT_LIBS) \
+ $(NULL)
+
+noinst_PROGRAMS = \
+ test-tty-idle-monitor \
+ $(NULL)
+
+test_tty_idle_monitor_SOURCES = \
+ ck-tty-idle-monitor.h \
+ ck-tty-idle-monitor.c \
+ ck-file-monitor.h \
+ $(FILE_MONITOR_BACKEND) \
+ test-tty-idle-monitor.c \
+ $(NULL)
+
+test_tty_idle_monitor_LDADD = \
+ $(CONSOLE_KIT_LIBS) \
+ $(NULL)
+
+EXTRA_DIST = \
+ ck-marshal.list \
+ ck-manager.xml \
+ ck-seat.xml \
+ ck-session.xml \
+ valgrind.sh \
+ test-open-session \
+ test-open-session-with-parameters \
+ $(NULL)
+
+CLEANFILES = $(BUILT_SOURCES)
+
+MAINTAINERCLEANFILES = \
+ *~ \
+ Makefile.in
diff --git a/src/Makefile.in b/src/Makefile.in
new file mode 100644
index 0000000..336a570
--- /dev/null
+++ b/src/Makefile.in
@@ -0,0 +1,768 @@
+# Makefile.in generated by automake 1.9.6 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+top_builddir = ..
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+INSTALL = @INSTALL@
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+sbin_PROGRAMS = console-kit-daemon$(EXEEXT) $(am__EXEEXT_1)
+noinst_PROGRAMS = test-tty-idle-monitor$(EXEEXT) $(am__EXEEXT_1)
+subdir = src
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+am__EXEEXT_1 =
+am__installdirs = "$(DESTDIR)$(sbindir)"
+sbinPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
+PROGRAMS = $(noinst_PROGRAMS) $(sbin_PROGRAMS)
+am__console_kit_daemon_SOURCES_DIST = main.c ck-manager.h ck-manager.c \
+ ck-vt-monitor.h ck-vt-monitor.c ck-tty-idle-monitor.h \
+ ck-tty-idle-monitor.c ck-file-monitor.h ck-job.h ck-job.c \
+ ck-seat.h ck-seat.c ck-session.h ck-session.c ck-log.h \
+ ck-log.c getfd.c proc.h proc-linux.c ck-manager-glue.h \
+ ck-seat-glue.h ck-session-glue.h ck-marshal.c ck-marshal.h \
+ ck-file-monitor-dummy.c ck-file-monitor-inotify.c
+am__objects_1 =
+am__objects_2 = proc-linux.$(OBJEXT) $(am__objects_1)
+am__objects_3 = ck-marshal.$(OBJEXT) $(am__objects_1)
+@ENABLE_INOTIFY_FALSE@am__objects_4 = ck-file-monitor-dummy.$(OBJEXT)
+@ENABLE_INOTIFY_TRUE@am__objects_4 = \
+@ENABLE_INOTIFY_TRUE@ ck-file-monitor-inotify.$(OBJEXT)
+am_console_kit_daemon_OBJECTS = main.$(OBJEXT) ck-manager.$(OBJEXT) \
+ ck-vt-monitor.$(OBJEXT) ck-tty-idle-monitor.$(OBJEXT) \
+ ck-job.$(OBJEXT) ck-seat.$(OBJEXT) ck-session.$(OBJEXT) \
+ ck-log.$(OBJEXT) getfd.$(OBJEXT) $(am__objects_2) \
+ $(am__objects_3) $(am__objects_1) $(am__objects_4)
+console_kit_daemon_OBJECTS = $(am_console_kit_daemon_OBJECTS)
+am__DEPENDENCIES_1 =
+console_kit_daemon_DEPENDENCIES = $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+am__test_tty_idle_monitor_SOURCES_DIST = ck-tty-idle-monitor.h \
+ ck-tty-idle-monitor.c ck-file-monitor.h \
+ ck-file-monitor-dummy.c ck-file-monitor-inotify.c \
+ test-tty-idle-monitor.c
+am_test_tty_idle_monitor_OBJECTS = ck-tty-idle-monitor.$(OBJEXT) \
+ $(am__objects_4) test-tty-idle-monitor.$(OBJEXT) \
+ $(am__objects_1)
+test_tty_idle_monitor_OBJECTS = $(am_test_tty_idle_monitor_OBJECTS)
+test_tty_idle_monitor_DEPENDENCIES = $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+SOURCES = $(console_kit_daemon_SOURCES) \
+ $(EXTRA_console_kit_daemon_SOURCES) \
+ $(test_tty_idle_monitor_SOURCES)
+DIST_SOURCES = $(am__console_kit_daemon_SOURCES_DIST) \
+ $(EXTRA_console_kit_daemon_SOURCES) \
+ $(am__test_tty_idle_monitor_SOURCES_DIST)
+RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \
+ html-recursive info-recursive install-data-recursive \
+ install-exec-recursive install-info-recursive \
+ install-recursive installcheck-recursive installdirs-recursive \
+ pdf-recursive ps-recursive uninstall-info-recursive \
+ uninstall-recursive
+ETAGS = etags
+CTAGS = ctags
+DIST_SUBDIRS = $(SUBDIRS)
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMDEP_FALSE = @AMDEP_FALSE@
+AMDEP_TRUE = @AMDEP_TRUE@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CK_BACKEND = @CK_BACKEND@
+CK_COMPILE_FREEBSD_FALSE = @CK_COMPILE_FREEBSD_FALSE@
+CK_COMPILE_FREEBSD_TRUE = @CK_COMPILE_FREEBSD_TRUE@
+CK_COMPILE_LINUX_FALSE = @CK_COMPILE_LINUX_FALSE@
+CK_COMPILE_LINUX_TRUE = @CK_COMPILE_LINUX_TRUE@
+CK_COMPILE_SOLARIS_FALSE = @CK_COMPILE_SOLARIS_FALSE@
+CK_COMPILE_SOLARIS_TRUE = @CK_COMPILE_SOLARIS_TRUE@
+CONSOLE_KIT_CFLAGS = @CONSOLE_KIT_CFLAGS@
+CONSOLE_KIT_LIBS = @CONSOLE_KIT_LIBS@
+CONSOLE_KIT_PID_FILE = @CONSOLE_KIT_PID_FILE@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DBUS_CFLAGS = @DBUS_CFLAGS@
+DBUS_SYS_DIR = @DBUS_SYS_DIR@
+DEBUG_CFLAGS = @DEBUG_CFLAGS@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DOCBOOK_DOCS_ENABLED_FALSE = @DOCBOOK_DOCS_ENABLED_FALSE@
+DOCBOOK_DOCS_ENABLED_TRUE = @DOCBOOK_DOCS_ENABLED_TRUE@
+ECHO = @ECHO@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+ENABLE_INOTIFY_FALSE = @ENABLE_INOTIFY_FALSE@
+ENABLE_INOTIFY_TRUE = @ENABLE_INOTIFY_TRUE@
+ENABLE_PAM_MODULE_FALSE = @ENABLE_PAM_MODULE_FALSE@
+ENABLE_PAM_MODULE_TRUE = @ENABLE_PAM_MODULE_TRUE@
+EXEEXT = @EXEEXT@
+F77 = @F77@
+FFLAGS = @FFLAGS@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GREP = @GREP@
+HAVE_PAM = @HAVE_PAM@
+HAVE_PAM_FALSE = @HAVE_PAM_FALSE@
+HAVE_PAM_TRUE = @HAVE_PAM_TRUE@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+LDFLAGS = @LDFLAGS@
+LIBDBUS_CFLAGS = @LIBDBUS_CFLAGS@
+LIBDBUS_LIBS = @LIBDBUS_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAINTAINER_MODE_FALSE = @MAINTAINER_MODE_FALSE@
+MAINTAINER_MODE_TRUE = @MAINTAINER_MODE_TRUE@
+MAKEINFO = @MAKEINFO@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PAM_LIBS = @PAM_LIBS@
+PAM_MODULE_DIR = @PAM_MODULE_DIR@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+POFILES = @POFILES@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+RANLIB = @RANLIB@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+TOOLS_CFLAGS = @TOOLS_CFLAGS@
+TOOLS_LIBS = @TOOLS_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WARN_CFLAGS = @WARN_CFLAGS@
+XGETTEXT = @XGETTEXT@
+XMLTO = @XMLTO@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_F77 = @ac_ct_F77@
+am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
+am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
+am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@
+am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+AUTOMAKE_OPTIONS = 1.7
+NULL =
+SUBDIRS = \
+ $(NULL)
+
+INCLUDES = \
+ -I. \
+ -I$(srcdir) \
+ $(CONSOLE_KIT_CFLAGS) \
+ $(DISABLE_DEPRECATED_CFLAGS) \
+ -DPREFIX=\""$(prefix)"\" \
+ -DBINDIR=\""$(bindir)"\" \
+ -DLIBDIR=\""$(libdir)"\" \
+ -DLIBEXECDIR=\""$(libexecdir)"\" \
+ -DDATADIR=\""$(datadir)"\" \
+ -DSYSCONFDIR=\""$(sysconfdir)"\" \
+ -DCONSOLE_KIT_PID_FILE=\""$(CONSOLE_KIT_PID_FILE)"\" \
+ $(WARN_CFLAGS) \
+ $(DEBUG_CFLAGS) \
+ $(DBUS_CFLAGS) \
+ $(NULL)
+
+BUILT_SOURCES = \
+ ck-manager-glue.h \
+ ck-seat-glue.h \
+ ck-session-glue.h \
+ ck-marshal.c \
+ ck-marshal.h \
+ $(NULL)
+
+PLATFORM_SOURCES = \
+ proc-linux.c \
+ $(NULL)
+
+console_kit_daemon_SOURCES = main.c ck-manager.h ck-manager.c \
+ ck-vt-monitor.h ck-vt-monitor.c ck-tty-idle-monitor.h \
+ ck-tty-idle-monitor.c ck-file-monitor.h ck-job.h ck-job.c \
+ ck-seat.h ck-seat.c ck-session.h ck-session.c ck-log.h \
+ ck-log.c getfd.c proc.h $(PLATFORM_SOURCES) $(BUILT_SOURCES) \
+ $(NULL) $(FILE_MONITOR_BACKEND)
+@ENABLE_INOTIFY_FALSE@FILE_MONITOR_BACKEND = ck-file-monitor-dummy.c
+@ENABLE_INOTIFY_TRUE@FILE_MONITOR_BACKEND = ck-file-monitor-inotify.c
+EXTRA_console_kit_daemon_SOURCES = \
+ ck-file-monitor-inotify.c \
+ ck-file-monitor-dummy.c \
+ $(NULL)
+
+console_kit_daemon_LDADD = \
+ $(CONSOLE_KIT_LIBS) \
+ $(NULL)
+
+test_tty_idle_monitor_SOURCES = \
+ ck-tty-idle-monitor.h \
+ ck-tty-idle-monitor.c \
+ ck-file-monitor.h \
+ $(FILE_MONITOR_BACKEND) \
+ test-tty-idle-monitor.c \
+ $(NULL)
+
+test_tty_idle_monitor_LDADD = \
+ $(CONSOLE_KIT_LIBS) \
+ $(NULL)
+
+EXTRA_DIST = \
+ ck-marshal.list \
+ ck-manager.xml \
+ ck-seat.xml \
+ ck-session.xml \
+ valgrind.sh \
+ test-open-session \
+ test-open-session-with-parameters \
+ $(NULL)
+
+CLEANFILES = $(BUILT_SOURCES)
+MAINTAINERCLEANFILES = \
+ *~ \
+ Makefile.in
+
+all: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) all-recursive
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+ && exit 0; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --gnu src/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+clean-noinstPROGRAMS:
+ @list='$(noinst_PROGRAMS)'; for p in $$list; do \
+ f=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f $$p $$f"; \
+ rm -f $$p $$f ; \
+ done
+install-sbinPROGRAMS: $(sbin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ test -z "$(sbindir)" || $(mkdir_p) "$(DESTDIR)$(sbindir)"
+ @list='$(sbin_PROGRAMS)'; for p in $$list; do \
+ p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
+ if test -f $$p \
+ || test -f $$p1 \
+ ; then \
+ f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) --mode=install $(sbinPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(sbindir)/$$f'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) --mode=install $(sbinPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(sbindir)/$$f" || exit 1; \
+ else :; fi; \
+ done
+
+uninstall-sbinPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(sbin_PROGRAMS)'; for p in $$list; do \
+ f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \
+ echo " rm -f '$(DESTDIR)$(sbindir)/$$f'"; \
+ rm -f "$(DESTDIR)$(sbindir)/$$f"; \
+ done
+
+clean-sbinPROGRAMS:
+ @list='$(sbin_PROGRAMS)'; for p in $$list; do \
+ f=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f $$p $$f"; \
+ rm -f $$p $$f ; \
+ done
+console-kit-daemon$(EXEEXT): $(console_kit_daemon_OBJECTS) $(console_kit_daemon_DEPENDENCIES)
+ @rm -f console-kit-daemon$(EXEEXT)
+ $(LINK) $(console_kit_daemon_LDFLAGS) $(console_kit_daemon_OBJECTS) $(console_kit_daemon_LDADD) $(LIBS)
+test-tty-idle-monitor$(EXEEXT): $(test_tty_idle_monitor_OBJECTS) $(test_tty_idle_monitor_DEPENDENCIES)
+ @rm -f test-tty-idle-monitor$(EXEEXT)
+ $(LINK) $(test_tty_idle_monitor_LDFLAGS) $(test_tty_idle_monitor_OBJECTS) $(test_tty_idle_monitor_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ck-file-monitor-dummy.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ck-file-monitor-inotify.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ck-job.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ck-log.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ck-manager.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ck-marshal.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ck-seat.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ck-session.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ck-tty-idle-monitor.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ck-vt-monitor.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getfd.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/proc-linux.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-tty-idle-monitor.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ if $(LTCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Plo"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+distclean-libtool:
+ -rm -f libtool
+uninstall-info-am:
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run `make' without going through this Makefile.
+# To change the values of `make' variables: instead of editing Makefiles,
+# (1) if the variable is set in `config.status', edit `config.status'
+# (which will cause the Makefiles to be regenerated when you run `make');
+# (2) otherwise, pass the desired values on the `make' command line.
+$(RECURSIVE_TARGETS):
+ @failcom='exit 1'; \
+ for f in x $$MAKEFLAGS; do \
+ case $$f in \
+ *=* | --[!k]*);; \
+ *k*) failcom='fail=yes';; \
+ esac; \
+ done; \
+ dot_seen=no; \
+ target=`echo $@ | sed s/-recursive//`; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ dot_seen=yes; \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done; \
+ if test "$$dot_seen" = "no"; then \
+ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+ fi; test -z "$$fail"
+
+mostlyclean-recursive clean-recursive distclean-recursive \
+maintainer-clean-recursive:
+ @failcom='exit 1'; \
+ for f in x $$MAKEFLAGS; do \
+ case $$f in \
+ *=* | --[!k]*);; \
+ *k*) failcom='fail=yes';; \
+ esac; \
+ done; \
+ dot_seen=no; \
+ case "$@" in \
+ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+ *) list='$(SUBDIRS)' ;; \
+ esac; \
+ rev=''; for subdir in $$list; do \
+ if test "$$subdir" = "."; then :; else \
+ rev="$$subdir $$rev"; \
+ fi; \
+ done; \
+ rev="$$rev ."; \
+ target=`echo $@ | sed s/-recursive//`; \
+ for subdir in $$rev; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done && test -z "$$fail"
+tags-recursive:
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \
+ done
+ctags-recursive:
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \
+ done
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+ include_option=--etags-include; \
+ empty_fix=.; \
+ else \
+ include_option=--include; \
+ empty_fix=; \
+ fi; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test ! -f $$subdir/TAGS || \
+ tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \
+ fi; \
+ done; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$tags $$unique; \
+ fi
+ctags: CTAGS
+CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ test -z "$(CTAGS_ARGS)$$tags$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$tags $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && cd $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
+ list='$(DISTFILES)'; for file in $$list; do \
+ case $$file in \
+ $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
+ $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
+ esac; \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test "$$dir" != "$$file" && test "$$dir" != "."; then \
+ dir="/$$dir"; \
+ $(mkdir_p) "$(distdir)$$dir"; \
+ else \
+ dir=''; \
+ fi; \
+ if test -d $$d/$$file; then \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+ else \
+ test -f $(distdir)/$$file \
+ || cp -p $$d/$$file $(distdir)/$$file \
+ || exit 1; \
+ fi; \
+ done
+ list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test -d "$(distdir)/$$subdir" \
+ || $(mkdir_p) "$(distdir)/$$subdir" \
+ || exit 1; \
+ distdir=`$(am__cd) $(distdir) && pwd`; \
+ top_distdir=`$(am__cd) $(top_distdir) && pwd`; \
+ (cd $$subdir && \
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$$top_distdir" \
+ distdir="$$distdir/$$subdir" \
+ distdir) \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) check-recursive
+all-am: Makefile $(PROGRAMS)
+installdirs: installdirs-recursive
+installdirs-am:
+ for dir in "$(DESTDIR)$(sbindir)"; do \
+ test -z "$$dir" || $(mkdir_p) "$$dir"; \
+ done
+install: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+ -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES)
+ -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES)
+clean: clean-recursive
+
+clean-am: clean-generic clean-libtool clean-noinstPROGRAMS \
+ clean-sbinPROGRAMS mostlyclean-am
+
+distclean: distclean-recursive
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-libtool distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+info: info-recursive
+
+info-am:
+
+install-data-am:
+
+install-exec-am: install-sbinPROGRAMS
+
+install-info: install-info-recursive
+
+install-man:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am: uninstall-info-am uninstall-sbinPROGRAMS
+
+uninstall-info: uninstall-info-recursive
+
+.PHONY: $(RECURSIVE_TARGETS) CTAGS GTAGS all all-am check check-am \
+ clean clean-generic clean-libtool clean-noinstPROGRAMS \
+ clean-recursive clean-sbinPROGRAMS ctags ctags-recursive \
+ distclean distclean-compile distclean-generic \
+ distclean-libtool distclean-recursive distclean-tags distdir \
+ dvi dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-exec install-exec-am \
+ install-info install-info-am install-man install-sbinPROGRAMS \
+ install-strip installcheck installcheck-am installdirs \
+ installdirs-am maintainer-clean maintainer-clean-generic \
+ maintainer-clean-recursive mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool mostlyclean-recursive \
+ pdf pdf-am ps ps-am tags tags-recursive uninstall uninstall-am \
+ uninstall-info-am uninstall-sbinPROGRAMS
+
+
+ck-manager-glue.h: ck-manager.xml Makefile.am
+ dbus-binding-tool --prefix=ck_manager --mode=glib-server --output=ck-manager-glue.h ck-manager.xml
+ck-seat-glue.h: ck-seat.xml Makefile.am
+ dbus-binding-tool --prefix=ck_seat --mode=glib-server --output=ck-seat-glue.h ck-seat.xml
+ck-session-glue.h: ck-session.xml Makefile.am
+ dbus-binding-tool --prefix=ck_session --mode=glib-server --output=ck-session-glue.h ck-session.xml
+
+ck-marshal.c: ck-marshal.list
+ echo "#include \"ck-marshal.h\"" > $@ && \
+ @GLIB_GENMARSHAL@ $< --prefix=ck_marshal --body >> $@
+
+ck-marshal.h: ck-marshal.list
+ @GLIB_GENMARSHAL@ $< --prefix=ck_marshal --header > $@
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/src/ck-file-monitor-dummy.c b/src/ck-file-monitor-dummy.c
new file mode 100644
index 0000000..27efb01
--- /dev/null
+++ b/src/ck-file-monitor-dummy.c
@@ -0,0 +1,118 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
+ *
+ * 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 <stdlib.h>
+#include <stdio.h>
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "ck-file-monitor.h"
+
+#define CK_FILE_MONITOR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), CK_TYPE_FILE_MONITOR, CkFileMonitorPrivate))
+
+struct CkFileMonitorPrivate
+{
+ gpointer dummy;
+};
+
+static void ck_file_monitor_class_init (CkFileMonitorClass *klass);
+static void ck_file_monitor_init (CkFileMonitor *file_monitor);
+static void ck_file_monitor_finalize (GObject *object);
+
+G_DEFINE_TYPE (CkFileMonitor, ck_file_monitor, G_TYPE_OBJECT)
+
+static gpointer monitor_object = NULL;
+
+GQuark
+ck_file_monitor_error_quark (void)
+{
+ static GQuark ret = 0;
+ if (ret == 0) {
+ ret = g_quark_from_static_string ("ck_file_monitor_error");
+ }
+
+ return ret;
+}
+
+guint
+ck_file_monitor_add_notify (CkFileMonitor *monitor,
+ const char *path,
+ int mask,
+ CkFileMonitorNotifyFunc notify_func,
+ gpointer data)
+{
+ return 0;
+}
+
+void
+ck_file_monitor_remove_notify (CkFileMonitor *monitor,
+ guint id)
+{
+ return;
+}
+
+static void
+ck_file_monitor_class_init (CkFileMonitorClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = ck_file_monitor_finalize;
+
+ g_type_class_add_private (klass, sizeof (CkFileMonitorPrivate));
+}
+
+static void
+ck_file_monitor_init (CkFileMonitor *monitor)
+{
+ monitor->priv = CK_FILE_MONITOR_GET_PRIVATE (monitor);
+}
+
+static void
+ck_file_monitor_finalize (GObject *object)
+{
+ CkFileMonitor *monitor;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (CK_IS_FILE_MONITOR (object));
+
+ monitor = CK_FILE_MONITOR (object);
+
+ g_return_if_fail (monitor->priv != NULL);
+
+ G_OBJECT_CLASS (ck_file_monitor_parent_class)->finalize (object);
+}
+
+CkFileMonitor *
+ck_file_monitor_new (void)
+{
+ if (monitor_object != NULL) {
+ g_object_ref (monitor_object);
+ } else {
+ monitor_object = g_object_new (CK_TYPE_FILE_MONITOR, NULL);
+
+ g_object_add_weak_pointer (monitor_object,
+ (gpointer *) &monitor_object);
+ }
+
+ return CK_FILE_MONITOR (monitor_object);
+}
diff --git a/src/ck-file-monitor-inotify.c b/src/ck-file-monitor-inotify.c
new file mode 100644
index 0000000..6e775e2
--- /dev/null
+++ b/src/ck-file-monitor-inotify.c
@@ -0,0 +1,669 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
+ *
+ * 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 <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/inotify.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+#include <glib-object.h>
+
+#include "ck-file-monitor.h"
+
+#define CK_FILE_MONITOR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), CK_TYPE_FILE_MONITOR, CkFileMonitorPrivate))
+
+typedef struct
+{
+ int wd;
+ char *path;
+ GSList *notifies;
+} FileInotifyWatch;
+
+typedef struct
+{
+ guint id;
+ int mask;
+ CkFileMonitorNotifyFunc notify_func;
+ gpointer user_data;
+ FileInotifyWatch *watch;
+} FileMonitorNotify;
+
+typedef struct
+{
+ FileInotifyWatch *watch;
+ CkFileMonitorEvent event;
+ char *path;
+} FileMonitorEventInfo;
+
+#define DEFAULT_NOTIFY_BUFLEN (32 * (sizeof (struct inotify_event) + 16))
+#define MAX_NOTIFY_BUFLEN (32 * DEFAULT_NOTIFY_BUFLEN)
+
+struct CkFileMonitorPrivate
+{
+ guint serial;
+
+ gboolean initialized_inotify;
+
+ int inotify_fd;
+ guint io_watch;
+
+ GHashTable *wd_to_watch;
+ GHashTable *path_to_watch;
+ GHashTable *notifies;
+
+ guint buflen;
+ guchar *buffer;
+
+ guint events_idle_id;
+ GQueue *notify_events;
+};
+
+enum {
+ PROP_0,
+};
+
+static void ck_file_monitor_class_init (CkFileMonitorClass *klass);
+static void ck_file_monitor_init (CkFileMonitor *file_monitor);
+static void ck_file_monitor_finalize (GObject *object);
+
+G_DEFINE_TYPE (CkFileMonitor, ck_file_monitor, G_TYPE_OBJECT)
+
+static gpointer monitor_object = NULL;
+
+GQuark
+ck_file_monitor_error_quark (void)
+{
+ static GQuark ret = 0;
+ if (ret == 0) {
+ ret = g_quark_from_static_string ("ck_file_monitor_error");
+ }
+
+ return ret;
+}
+
+/* most of this is adapted from libgnome-menu */
+
+static int
+our_event_mask_to_inotify_mask (int our_mask)
+{
+ int mask;
+
+ mask = 0;
+
+ if (our_mask & CK_FILE_MONITOR_EVENT_ACCESS) {
+ mask |= IN_ACCESS;
+ }
+
+ if (our_mask & CK_FILE_MONITOR_EVENT_CREATE) {
+ mask |= IN_CREATE | IN_MOVED_TO;
+ }
+
+ if (our_mask & CK_FILE_MONITOR_EVENT_DELETE) {
+ mask |= IN_DELETE | IN_DELETE_SELF | IN_MOVED_FROM | IN_MOVE_SELF;
+ }
+
+ if (our_mask & CK_FILE_MONITOR_EVENT_CHANGE) {
+ mask |= IN_MODIFY | IN_ATTRIB;
+ }
+
+ return mask;
+}
+
+static char *
+imask_to_string (guint32 mask)
+{
+ GString *out;
+
+ out = g_string_new (NULL);
+
+ if (mask & IN_ACCESS) {
+ g_string_append (out, "ACCESS ");
+ }
+ if (mask & IN_MODIFY) {
+ g_string_append (out, "MODIFY ");
+ }
+ if (mask & IN_ATTRIB) {
+ g_string_append (out, "ATTRIB ");
+ }
+ if (mask & IN_CLOSE_WRITE) {
+ g_string_append (out, "CLOSE_WRITE ");
+ }
+ if (mask & IN_CLOSE_NOWRITE) {
+ g_string_append (out, "CLOSE_NOWRITE ");
+ }
+ if (mask & IN_OPEN) {
+ g_string_append (out, "OPEN ");
+ }
+ if (mask & IN_MOVED_FROM) {
+ g_string_append (out, "MOVED_FROM ");
+ }
+ if (mask & IN_MOVED_TO) {
+ g_string_append (out, "MOVED_TO ");
+ }
+ if (mask & IN_DELETE) {
+ g_string_append (out, "DELETE ");
+ }
+ if (mask & IN_CREATE) {
+ g_string_append (out, "CREATE ");
+ }
+ if (mask & IN_DELETE_SELF) {
+ g_string_append (out, "DELETE_SELF ");
+ }
+ if (mask & IN_UNMOUNT) {
+ g_string_append (out, "UNMOUNT ");
+ }
+ if (mask & IN_Q_OVERFLOW) {
+ g_string_append (out, "Q_OVERFLOW ");
+ }
+ if (mask & IN_IGNORED) {
+ g_string_append (out, "IGNORED ");
+ }
+
+ return g_string_free (out, FALSE);
+}
+
+static FileInotifyWatch *
+file_monitor_add_watch_for_path (CkFileMonitor *monitor,
+ const char *path,
+ int mask)
+
+{
+ FileInotifyWatch *watch;
+ int wd;
+ int imask;
+ char *mask_str;
+
+ imask = our_event_mask_to_inotify_mask (mask);
+
+ mask_str = imask_to_string (imask);
+ g_debug ("adding inotify watch %s", mask_str);
+ g_free (mask_str);
+
+ wd = inotify_add_watch (monitor->priv->inotify_fd, path, IN_MASK_ADD | imask);
+ if (wd < 0) {
+ /* FIXME: remove watch etc */
+ return NULL;
+ }
+
+ watch = g_hash_table_lookup (monitor->priv->path_to_watch, path);
+ if (watch == NULL) {
+ watch = g_new0 (FileInotifyWatch, 1);
+
+ watch->wd = wd;
+ watch->path = g_strdup (path);
+
+ g_hash_table_insert (monitor->priv->path_to_watch, watch->path, watch);
+ g_hash_table_insert (monitor->priv->wd_to_watch, GINT_TO_POINTER (wd), watch);
+ }
+
+ return watch;
+}
+
+static void
+monitor_release_watch (CkFileMonitor *monitor,
+ FileInotifyWatch *watch)
+{
+ g_slist_free (watch->notifies);
+ watch->notifies = NULL;
+
+ g_free (watch->path);
+ watch->path = NULL;
+
+ inotify_rm_watch (monitor->priv->inotify_fd, watch->wd);
+ watch->wd = -1;
+}
+
+static void
+file_monitor_remove_watch (CkFileMonitor *monitor,
+ FileInotifyWatch *watch)
+{
+ g_hash_table_remove (monitor->priv->path_to_watch,
+ watch->path);
+ g_hash_table_remove (monitor->priv->wd_to_watch,
+ GINT_TO_POINTER (watch->wd));
+ monitor_release_watch (monitor, watch);
+}
+
+static gboolean
+remove_watch_foreach (const char *path,
+ FileInotifyWatch *watch,
+ CkFileMonitor *monitor)
+{
+ monitor_release_watch (monitor, watch);
+ return TRUE;
+}
+
+static void
+close_inotify (CkFileMonitor *monitor)
+{
+ if (! monitor->priv->initialized_inotify) {
+ return;
+ }
+
+ monitor->priv->initialized_inotify = FALSE;
+
+ g_hash_table_foreach_remove (monitor->priv->path_to_watch,
+ (GHRFunc) remove_watch_foreach,
+ monitor);
+ monitor->priv->path_to_watch = NULL;
+
+ if (monitor->priv->wd_to_watch != NULL) {
+ g_hash_table_destroy (monitor->priv->wd_to_watch);
+ }
+ monitor->priv->wd_to_watch = NULL;
+
+ g_free (monitor->priv->buffer);
+ monitor->priv->buffer = NULL;
+ monitor->priv->buflen = 0;
+
+ if (monitor->priv->io_watch) {
+ g_source_remove (monitor->priv->io_watch);
+ }
+ monitor->priv->io_watch = 0;
+
+ if (monitor->priv->inotify_fd > 0) {
+ close (monitor->priv->inotify_fd);
+ }
+ monitor->priv->inotify_fd = 0;
+}
+
+static gboolean
+emit_events_in_idle (CkFileMonitor *monitor)
+{
+ FileMonitorEventInfo *event_info;
+
+ monitor->priv->events_idle_id = 0;
+
+ while ((event_info = g_queue_pop_head (monitor->priv->notify_events)) != NULL) {
+ GSList *l;
+ FileInotifyWatch *watch;
+
+ watch = event_info->watch;
+
+ for (l = watch->notifies; l != NULL; l = l->next) {
+ FileMonitorNotify *notify;
+
+ notify = g_hash_table_lookup (monitor->priv->notifies,
+ GUINT_TO_POINTER (l->data));
+ if (notify == NULL) {
+ continue;
+ }
+
+ if (! (notify->mask & event_info->event)) {
+ continue;
+ }
+
+ if (notify->notify_func) {
+ notify->notify_func (monitor, event_info->event, event_info->path, notify->user_data);
+ }
+ }
+
+ g_free (event_info->path);
+ event_info->path = NULL;
+
+ event_info->event = CK_FILE_MONITOR_EVENT_NONE;
+
+ g_free (event_info);
+ }
+
+ return FALSE;
+}
+
+static void
+file_monitor_queue_event (CkFileMonitor *monitor,
+ FileMonitorEventInfo *event_info)
+{
+ g_queue_push_tail (monitor->priv->notify_events, event_info);
+
+ if (monitor->priv->events_idle_id == 0) {
+ monitor->priv->events_idle_id = g_idle_add ((GSourceFunc) emit_events_in_idle, monitor);
+ }
+}
+
+static void
+queue_watch_event (CkFileMonitor *monitor,
+ FileInotifyWatch *watch,
+ CkFileMonitorEvent event,
+ const char *path)
+{
+ FileMonitorEventInfo *event_info;
+
+ event_info = g_new0 (FileMonitorEventInfo, 1);
+
+ event_info->watch = watch;
+ event_info->path = g_strdup (path);
+ event_info->event = event;
+
+ file_monitor_queue_event (monitor, event_info);
+}
+
+static void
+handle_inotify_event (CkFileMonitor *monitor,
+ FileInotifyWatch *watch,
+ struct inotify_event *ievent)
+{
+ CkFileMonitorEvent event;
+ const char *path;
+ char *freeme;
+ char *mask_str;
+
+ freeme = NULL;
+
+ if (ievent->len > 0) {
+ path = freeme = g_build_filename (watch->path, ievent->name, NULL);
+ } else {
+ path = watch->path;
+ }
+
+ mask_str = imask_to_string (ievent->mask);
+ g_debug ("handing inotify event %s for %s", mask_str, path);
+ g_free (mask_str);
+
+ event = CK_FILE_MONITOR_EVENT_NONE;
+
+ if (ievent->mask & (IN_CREATE | IN_MOVED_TO)) {
+ event = CK_FILE_MONITOR_EVENT_CREATE;
+ } else if (ievent->mask & (IN_DELETE | IN_DELETE_SELF | IN_MOVED_FROM | IN_MOVE_SELF)) {
+ event = CK_FILE_MONITOR_EVENT_DELETE;
+ } else if (ievent->mask & (IN_MODIFY | IN_ATTRIB)) {
+ event = CK_FILE_MONITOR_EVENT_CHANGE;
+ } else if (ievent->mask & IN_ACCESS) {
+ event = CK_FILE_MONITOR_EVENT_ACCESS;
+ }
+
+ if (event != CK_FILE_MONITOR_EVENT_NONE) {
+ queue_watch_event (monitor, watch, event, path);
+ }
+
+ if (ievent->mask & IN_IGNORED) {
+ file_monitor_remove_watch (monitor, watch);
+ }
+}
+
+static gboolean
+inotify_data_pending (GIOChannel *source,
+ GIOCondition condition,
+ CkFileMonitor *monitor)
+{
+ int len;
+ int i;
+
+ g_debug ("Inotify data pending");
+
+ g_assert (monitor->priv->inotify_fd > 0);
+ g_assert (monitor->priv->buffer != NULL);
+
+ do {
+ while ((len = read (monitor->priv->inotify_fd, monitor->priv->buffer, monitor->priv->buflen)) < 0 && errno == EINTR);
+
+ if (len > 0) {
+ break;
+ } else if (len < 0) {
+ g_warning ("Error reading inotify event: %s",
+ g_strerror (errno));
+ goto error_cancel;
+ }
+
+ g_assert (len == 0);
+
+ if ((monitor->priv->buflen << 1) > MAX_NOTIFY_BUFLEN) {
+ g_warning ("Error reading inotify event: Exceded maximum buffer size");
+ goto error_cancel;
+ }
+
+ g_debug ("Buffer size %u too small, trying again at %u\n",
+ monitor->priv->buflen, monitor->priv->buflen << 1);
+
+ monitor->priv->buflen <<= 1;
+ monitor->priv->buffer = g_realloc (monitor->priv->buffer, monitor->priv->buflen);
+ } while (TRUE);
+
+ g_debug ("Inotify buffer filled");
+
+ i = 0;
+ while (i < len) {
+ struct inotify_event *ievent = (struct inotify_event *) &monitor->priv->buffer [i];
+ FileInotifyWatch *watch;
+
+ g_debug ("Got event wd = %d, mask = 0x%x, cookie = %d, len = %d, name= %s\n",
+ ievent->wd,
+ ievent->mask,
+ ievent->cookie,
+ ievent->len,
+ ievent->len > 0 ? ievent->name : "<none>");
+
+ watch = g_hash_table_lookup (monitor->priv->wd_to_watch,
+ GINT_TO_POINTER (ievent->wd));
+ if (watch != NULL) {
+ handle_inotify_event (monitor, watch, ievent);
+ }
+
+ i += sizeof (struct inotify_event) + ievent->len;
+ }
+
+ return TRUE;
+
+ error_cancel:
+ monitor->priv->io_watch = 0;
+
+ close_inotify (monitor);
+
+ return FALSE;
+}
+
+static FileMonitorNotify *
+file_monitor_add_notify_for_path (CkFileMonitor *monitor,
+ const char *path,
+ int mask,
+ CkFileMonitorNotifyFunc notify_func,
+ gpointer data)
+{
+ FileMonitorNotify *notify;
+ FileInotifyWatch *watch;
+
+ notify = NULL;
+
+ watch = file_monitor_add_watch_for_path (monitor, path, mask);
+ if (watch != NULL) {
+ notify = g_new0 (FileMonitorNotify, 1);
+ notify->notify_func = notify_func;
+ notify->user_data = data;
+ notify->id = monitor->priv->serial++;
+ notify->watch = watch;
+ notify->mask = mask;
+
+ g_debug ("Adding notify for %s mask:%d", path, mask);
+
+ g_hash_table_insert (monitor->priv->notifies, GUINT_TO_POINTER (notify->id), notify);
+ watch->notifies = g_slist_prepend (watch->notifies, GUINT_TO_POINTER (notify->id));
+ }
+
+ return notify;
+}
+
+static void
+file_monitor_remove_notify (CkFileMonitor *monitor,
+ guint id)
+{
+ FileMonitorNotify *notify;
+
+ g_debug ("removing notify for %u", id);
+
+ notify = g_hash_table_lookup (monitor->priv->notifies,
+ GUINT_TO_POINTER (id));
+ if (notify == NULL) {
+ return;
+ }
+
+ g_hash_table_steal (monitor->priv->notifies,
+ GUINT_TO_POINTER (id));
+
+ notify->watch->notifies = g_slist_remove (notify->watch->notifies, GUINT_TO_POINTER (id));
+
+ if (g_slist_length (notify->watch->notifies) == 0) {
+ file_monitor_remove_watch (monitor, notify->watch);
+ g_free (notify->watch);
+ }
+
+ g_free (notify);
+}
+
+guint
+ck_file_monitor_add_notify (CkFileMonitor *monitor,
+ const char *path,
+ int mask,
+ CkFileMonitorNotifyFunc notify_func,
+ gpointer data)
+{
+ FileMonitorNotify *notify;
+
+ if (! monitor->priv->initialized_inotify) {
+ return 0;
+ }
+
+ notify = file_monitor_add_notify_for_path (monitor,
+ path,
+ mask,
+ notify_func,
+ data);
+ if (notify == NULL) {
+ g_warning ("Failed to add monitor on '%s': %s",
+ path,
+ g_strerror (errno));
+ return 0;
+ }
+
+ return notify->id;
+}
+
+void
+ck_file_monitor_remove_notify (CkFileMonitor *monitor,
+ guint id)
+{
+ if (! monitor->priv->initialized_inotify) {
+ return;
+ }
+
+ file_monitor_remove_notify (monitor, id);
+}
+
+static void
+ck_file_monitor_class_init (CkFileMonitorClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = ck_file_monitor_finalize;
+
+ g_type_class_add_private (klass, sizeof (CkFileMonitorPrivate));
+}
+
+
+static void
+setup_inotify (CkFileMonitor *monitor)
+{
+ GIOChannel *io_channel;
+ int fd;
+
+ if (monitor->priv->initialized_inotify) {
+ return;
+ }
+
+ if ((fd = inotify_init ()) < 0) {
+ g_warning ("Failed to initialize inotify: %s",
+ g_strerror (errno));
+ return;
+ }
+
+ monitor->priv->inotify_fd = fd;
+
+ io_channel = g_io_channel_unix_new (fd);
+ monitor->priv->io_watch = g_io_add_watch (io_channel,
+ G_IO_IN|G_IO_PRI,
+ (GIOFunc) inotify_data_pending,
+ monitor);
+ g_io_channel_unref (io_channel);
+
+ monitor->priv->buflen = DEFAULT_NOTIFY_BUFLEN;
+ monitor->priv->buffer = g_malloc (DEFAULT_NOTIFY_BUFLEN);
+
+ monitor->priv->notifies = g_hash_table_new (g_direct_hash,
+ g_direct_equal);
+
+ monitor->priv->wd_to_watch = g_hash_table_new (g_direct_hash,
+ g_direct_equal);
+ monitor->priv->path_to_watch = g_hash_table_new (g_str_hash,
+ g_str_equal);
+
+ monitor->priv->initialized_inotify = TRUE;
+}
+
+static void
+ck_file_monitor_init (CkFileMonitor *monitor)
+{
+ monitor->priv = CK_FILE_MONITOR_GET_PRIVATE (monitor);
+
+ monitor->priv->serial = 1;
+ monitor->priv->notify_events = g_queue_new ();
+
+ setup_inotify (monitor);
+}
+
+static void
+ck_file_monitor_finalize (GObject *object)
+{
+ CkFileMonitor *monitor;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (CK_IS_FILE_MONITOR (object));
+
+ monitor = CK_FILE_MONITOR (object);
+
+ g_return_if_fail (monitor->priv != NULL);
+
+ close_inotify (monitor);
+
+ g_hash_table_destroy (monitor->priv->notifies);
+ g_queue_free (monitor->priv->notify_events);
+
+ G_OBJECT_CLASS (ck_file_monitor_parent_class)->finalize (object);
+}
+
+CkFileMonitor *
+ck_file_monitor_new (void)
+{
+ if (monitor_object != NULL) {
+ g_object_ref (monitor_object);
+ } else {
+ monitor_object = g_object_new (CK_TYPE_FILE_MONITOR, NULL);
+
+ g_object_add_weak_pointer (monitor_object,
+ (gpointer *) &monitor_object);
+ }
+
+ return CK_FILE_MONITOR (monitor_object);
+}
diff --git a/src/ck-file-monitor.h b/src/ck-file-monitor.h
new file mode 100644
index 0000000..b04585e
--- /dev/null
+++ b/src/ck-file-monitor.h
@@ -0,0 +1,84 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
+ *
+ * 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.
+ *
+ */
+
+#ifndef __CK_FILE_MONITOR_H
+#define __CK_FILE_MONITOR_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define CK_TYPE_FILE_MONITOR (ck_file_monitor_get_type ())
+#define CK_FILE_MONITOR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CK_TYPE_FILE_MONITOR, CkFileMonitor))
+#define CK_FILE_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), CK_TYPE_FILE_MONITOR, CkFileMonitorClass))
+#define CK_IS_FILE_MONITOR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), CK_TYPE_FILE_MONITOR))
+#define CK_IS_FILE_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), CK_TYPE_FILE_MONITOR))
+#define CK_FILE_MONITOR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), CK_TYPE_FILE_MONITOR, CkFileMonitorClass))
+
+typedef struct CkFileMonitorPrivate CkFileMonitorPrivate;
+
+typedef struct
+{
+ GObject parent;
+ CkFileMonitorPrivate *priv;
+} CkFileMonitor;
+
+typedef struct
+{
+ GObjectClass parent_class;
+} CkFileMonitorClass;
+
+typedef enum
+{
+ CK_FILE_MONITOR_EVENT_NONE = 1 << 0,
+ CK_FILE_MONITOR_EVENT_ACCESS = 1 << 1,
+ CK_FILE_MONITOR_EVENT_CREATE = 1 << 2,
+ CK_FILE_MONITOR_EVENT_DELETE = 1 << 3,
+ CK_FILE_MONITOR_EVENT_CHANGE = 1 << 4,
+} CkFileMonitorEvent;
+
+typedef enum
+{
+ CK_FILE_MONITOR_ERROR_GENERAL
+} CkFileMonitorError;
+
+#define CK_FILE_MONITOR_ERROR ck_file_monitor_error_quark ()
+
+typedef void (*CkFileMonitorNotifyFunc) (CkFileMonitor *monitor,
+ CkFileMonitorEvent event,
+ const char *path,
+ gpointer user_data);
+
+GQuark ck_file_monitor_error_quark (void);
+GType ck_file_monitor_get_type (void);
+
+CkFileMonitor * ck_file_monitor_new (void);
+
+guint ck_file_monitor_add_notify (CkFileMonitor *monitor,
+ const char *path,
+ int mask,
+ CkFileMonitorNotifyFunc notify_func,
+ gpointer data);
+void ck_file_monitor_remove_notify (CkFileMonitor *monitor,
+ guint id);
+
+G_END_DECLS
+
+#endif /* __CK_FILE_MONITOR_H */
diff --git a/src/ck-job.c b/src/ck-job.c
new file mode 100644
index 0000000..1093629
--- /dev/null
+++ b/src/ck-job.c
@@ -0,0 +1,391 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
+ *
+ * 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 <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib-object.h>
+
+#include "ck-job.h"
+#include "ck-marshal.h"
+
+#define CK_JOB_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), CK_TYPE_JOB, CkJobPrivate))
+
+struct CkJobPrivate
+{
+ guint err_watch_id;
+ guint out_watch_id;
+
+ char *command;
+ GString *stdout;
+ GString *stderr;
+ GPid child_pid;
+
+};
+
+enum {
+ COMPLETED,
+ LAST_SIGNAL
+};
+
+static guint signals [LAST_SIGNAL] = { 0, };
+
+static void ck_job_class_init (CkJobClass *klass);
+static void ck_job_init (CkJob *job);
+static void ck_job_finalize (GObject *object);
+
+G_DEFINE_TYPE (CkJob, ck_job, G_TYPE_OBJECT)
+
+GQuark
+ck_job_error_quark (void)
+{
+ static GQuark ret = 0;
+ if (ret == 0) {
+ ret = g_quark_from_static_string ("ck_job_error");
+ }
+
+ return ret;
+}
+
+static int
+wait_on_child (int pid)
+{
+ int status;
+
+ wait_again:
+ if (waitpid (pid, &status, 0) < 0) {
+ if (errno == EINTR) {
+ goto wait_again;
+ } else if (errno == ECHILD) {
+ ; /* do nothing, child already reaped */
+ } else {
+ g_debug ("waitpid () should not fail");
+ }
+ }
+
+ return status;
+}
+
+static int
+wait_on_job (CkJob *job)
+{
+ int status;
+
+ status = wait_on_child (job->priv->child_pid);
+
+ g_spawn_close_pid (job->priv->child_pid);
+ job->priv->child_pid = 0;
+
+ return WEXITSTATUS(status);
+}
+
+static void
+maybe_complete_job (CkJob *job)
+{
+ if (job->priv->out_watch_id == 0
+ && job->priv->err_watch_id == 0) {
+ int status;
+
+ status = wait_on_job (job);
+
+ g_debug ("Emitting completed");
+ g_signal_emit (job, signals [COMPLETED], 0, status);
+ }
+}
+
+static gboolean
+error_watch (GIOChannel *source,
+ GIOCondition condition,
+ CkJob *job)
+{
+ gboolean finished = FALSE;
+
+ if (condition & G_IO_IN) {
+ GIOStatus status;
+ GError *error = NULL;
+ char *line;
+
+ line = NULL;
+ status = g_io_channel_read_line (source, &line, NULL, NULL, &error);
+
+ switch (status) {
+ case G_IO_STATUS_NORMAL:
+ g_debug ("command error output: %s", line);
+ g_string_append (job->priv->stderr, line);
+ break;
+ case G_IO_STATUS_EOF:
+ finished = TRUE;
+ break;
+ case G_IO_STATUS_ERROR:
+ finished = TRUE;
+ g_debug ("Error reading from child: %s\n", error->message);
+ break;
+ case G_IO_STATUS_AGAIN:
+ default:
+ break;
+ }
+ g_free (line);
+ } else if (condition & G_IO_HUP) {
+ finished = TRUE;
+ }
+
+ if (finished) {
+ job->priv->err_watch_id = 0;
+ maybe_complete_job (job);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+out_watch (GIOChannel *source,
+ GIOCondition condition,
+ CkJob *job)
+{
+ gboolean finished = FALSE;
+
+ if (condition & G_IO_IN) {
+ GIOStatus status;
+ GError *error = NULL;
+ char *line;
+
+ line = NULL;
+ status = g_io_channel_read_line (source, &line, NULL, NULL, &error);
+
+ switch (status) {
+ case G_IO_STATUS_NORMAL:
+ g_debug ("command output: %s", line);
+ g_string_append (job->priv->stdout, line);
+ break;
+ case G_IO_STATUS_EOF:
+ finished = TRUE;
+ break;
+ case G_IO_STATUS_ERROR:
+ finished = TRUE;
+ g_debug ("Error reading from child: %s\n", error->message);
+ break;
+ case G_IO_STATUS_AGAIN:
+ default:
+ break;
+ }
+ g_free (line);
+ } else if (condition & G_IO_HUP) {
+ finished = TRUE;
+ }
+
+ if (finished) {
+ job->priv->out_watch_id = 0;
+ maybe_complete_job (job);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gboolean
+ck_job_execute (CkJob *job,
+ GError **error)
+{
+ GError *local_error;
+ gboolean res;
+ GIOChannel *channel;
+ int standard_output;
+ int standard_error;
+ int argc;
+ char **argv;
+
+ g_debug ("Executing %s", job->priv->command);
+ local_error = NULL;
+ if (! g_shell_parse_argv (job->priv->command, &argc, &argv, &local_error)) {
+ g_debug ("Could not parse command: %s", local_error->message);
+ g_propagate_error (error, local_error);
+ return FALSE;
+ }
+
+ local_error = NULL;
+ res = g_spawn_async_with_pipes (NULL,
+ argv,
+ NULL,
+ G_SPAWN_DO_NOT_REAP_CHILD,
+ NULL,
+ NULL,
+ &job->priv->child_pid,
+ NULL,
+ &standard_output,
+ &standard_error,
+ &local_error);
+
+ g_strfreev (argv);
+ if (! res) {
+ g_debug ("Could not start command '%s': %s",
+ job->priv->command,
+ local_error->message);
+ g_propagate_error (error, local_error);
+ return FALSE;
+ }
+
+ /* output channel */
+ channel = g_io_channel_unix_new (standard_output);
+ g_io_channel_set_close_on_unref (channel, TRUE);
+ g_io_channel_set_flags (channel,
+ g_io_channel_get_flags (channel) | G_IO_FLAG_NONBLOCK,
+ NULL);
+ job->priv->out_watch_id = g_io_add_watch (channel,
+ G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ (GIOFunc)out_watch,
+ job);
+ g_io_channel_unref (channel);
+
+ /* error channel */
+ channel = g_io_channel_unix_new (standard_error);
+ g_io_channel_set_close_on_unref (channel, TRUE);
+ g_io_channel_set_flags (channel,
+ g_io_channel_get_flags (channel) | G_IO_FLAG_NONBLOCK,
+ NULL);
+ job->priv->err_watch_id = g_io_add_watch (channel,
+ G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ (GIOFunc)error_watch,
+ job);
+ g_io_channel_unref (channel);
+
+ return res;
+}
+
+gboolean
+ck_job_get_stdout (CkJob *job,
+ char **stdout)
+{
+ if (stdout != NULL) {
+ *stdout = g_strdup (job->priv->stdout->str);
+ }
+ return TRUE;
+}
+
+gboolean
+ck_job_set_command (CkJob *job,
+ const char *command)
+{
+ g_free (job->priv->command);
+ job->priv->command = g_strdup (command);
+ return TRUE;
+}
+
+gboolean
+ck_job_get_command (CkJob *job,
+ char **command)
+{
+ if (command != NULL) {
+ *command = g_strdup (job->priv->command);
+ }
+
+ return TRUE;
+}
+
+static void
+ck_job_class_init (CkJobClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = ck_job_finalize;
+
+ signals [COMPLETED] =
+ g_signal_new ("completed",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (CkJobClass, completed),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__INT,
+ G_TYPE_NONE,
+ 1, G_TYPE_INT);
+
+ g_type_class_add_private (klass, sizeof (CkJobPrivate));
+}
+
+static void
+ck_job_init (CkJob *job)
+{
+ job->priv = CK_JOB_GET_PRIVATE (job);
+
+ job->priv->stderr = g_string_new (NULL);
+ job->priv->stdout = g_string_new (NULL);
+}
+
+gboolean
+ck_job_cancel (CkJob *job)
+{
+ if (job->priv->child_pid > 0) {
+ kill (job->priv->child_pid, SIGTERM);
+ wait_on_job (job);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+ck_job_finalize (GObject *object)
+{
+ CkJob *job;
+
+ g_debug ("Finalizing job");
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (CK_IS_JOB (object));
+
+ job = CK_JOB (object);
+
+ g_return_if_fail (job->priv != NULL);
+
+ ck_job_cancel (job);
+
+ if (job->priv->out_watch_id > 0) {
+ g_source_remove (job->priv->out_watch_id);
+ }
+ if (job->priv->err_watch_id > 0) {
+ g_source_remove (job->priv->err_watch_id);
+ }
+ g_free (job->priv->command);
+ g_string_free (job->priv->stdout, TRUE);
+ g_string_free (job->priv->stderr, TRUE);
+
+ G_OBJECT_CLASS (ck_job_parent_class)->finalize (object);
+}
+
+CkJob *
+ck_job_new (void)
+{
+ GObject *object;
+
+ object = g_object_new (CK_TYPE_JOB, NULL);
+
+ return CK_JOB (object);
+}
diff --git a/src/ck-job.h b/src/ck-job.h
new file mode 100644
index 0000000..a12a262
--- /dev/null
+++ b/src/ck-job.h
@@ -0,0 +1,76 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
+ *
+ * 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.
+ *
+ */
+
+#ifndef __CK_JOB_H
+#define __CK_JOB_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define CK_TYPE_JOB (ck_job_get_type ())
+#define CK_JOB(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CK_TYPE_JOB, CkJob))
+#define CK_JOB_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), CK_TYPE_JOB, CkJobClass))
+#define CK_IS_JOB(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), CK_TYPE_JOB))
+#define CK_IS_JOB_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), CK_TYPE_JOB))
+#define CK_JOB_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), CK_TYPE_JOB, CkJobClass))
+
+typedef struct CkJobPrivate CkJobPrivate;
+
+typedef struct
+{
+ GObject parent;
+ CkJobPrivate *priv;
+} CkJob;
+
+typedef struct
+{
+ GObjectClass parent_class;
+
+ void (* completed) (CkJob *job,
+ int status);
+} CkJobClass;
+
+typedef enum
+{
+ CK_JOB_ERROR_GENERAL
+} CkJobError;
+
+#define CK_JOB_ERROR ck_job_error_quark ()
+
+GQuark ck_job_error_quark (void);
+GType ck_job_get_type (void);
+CkJob * ck_job_new (void);
+
+gboolean ck_job_set_command (CkJob *job,
+ const char *command);
+gboolean ck_job_get_command (CkJob *job,
+ char **command);
+gboolean ck_job_execute (CkJob *job,
+ GError **error);
+gboolean ck_job_get_stdout (CkJob *job,
+ char **std_output);
+gboolean ck_job_get_stderr (CkJob *job,
+ char **std_error);
+gboolean ck_job_cancel (CkJob *job);
+
+G_END_DECLS
+
+#endif /* __CK_JOB_H */
diff --git a/src/ck-log.c b/src/ck-log.c
new file mode 100644
index 0000000..9f49352
--- /dev/null
+++ b/src/ck-log.c
@@ -0,0 +1,197 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <mccann@jhu.edu>
+ *
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <unistd.h>
+
+#include <syslog.h>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+
+#include "ck-log.h"
+
+static gboolean initialized = FALSE;
+static int syslog_levels = (G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING);
+
+static void
+log_level_to_priority_and_prefix (GLogLevelFlags log_level,
+ int *priorityp,
+ const char **prefixp)
+{
+ int priority;
+ const char *prefix;
+
+ /* Process the message prefix and priority */
+ switch (log_level & G_LOG_LEVEL_MASK) {
+ case G_LOG_FLAG_FATAL:
+ priority = LOG_EMERG;
+ prefix = "FATAL";
+ break;
+ case G_LOG_LEVEL_ERROR:
+ priority = LOG_ERR;
+ prefix = "ERROR";
+ break;
+ case G_LOG_LEVEL_CRITICAL:
+ priority = LOG_CRIT;
+ prefix = "CRITICAL";
+ break;
+ case G_LOG_LEVEL_WARNING:
+ priority = LOG_WARNING;
+ prefix = "WARNING";
+ break;
+ case G_LOG_LEVEL_MESSAGE:
+ priority = LOG_NOTICE;
+ prefix = "MESSAGE";
+ break;
+ case G_LOG_LEVEL_INFO:
+ priority = LOG_INFO;
+ prefix = "INFO";
+ break;
+ case G_LOG_LEVEL_DEBUG:
+ /* if debug was requested then bump this up to ERROR
+ * to ensure it is seen in a log */
+ if (syslog_levels & G_LOG_LEVEL_DEBUG) {
+ priority = LOG_WARNING;
+ } else {
+ priority = LOG_DEBUG;
+ }
+ prefix = "DEBUG";
+ break;
+ default:
+ priority = LOG_DEBUG;
+ prefix = "UNKNOWN";
+ break;
+ }
+
+ if (priorityp != NULL) {
+ *priorityp = priority;
+ }
+ if (prefixp != NULL) {
+ *prefixp = prefix;
+ }
+}
+
+void
+ck_log_default_handler (const gchar *log_domain,
+ GLogLevelFlags log_level,
+ const gchar *message,
+ gpointer unused_data)
+{
+ GString *gstring;
+ int priority;
+ const char *level_prefix;
+ char *string;
+ gboolean do_log;
+ gboolean is_fatal;
+
+ is_fatal = (log_level & G_LOG_FLAG_FATAL) != 0;
+
+ do_log = (log_level & syslog_levels);
+ if (! do_log) {
+ return;
+ }
+
+ if (! initialized) {
+ ck_log_init ();
+ }
+
+ log_level_to_priority_and_prefix (log_level,
+ &priority,
+ &level_prefix);
+
+ gstring = g_string_new (NULL);
+
+ if (log_domain != NULL) {
+ g_string_append (gstring, log_domain);
+ g_string_append_c (gstring, '-');
+ }
+ g_string_append (gstring, level_prefix);
+
+ g_string_append (gstring, ": ");
+ if (message == NULL) {
+ g_string_append (gstring, "(NULL) message");
+ } else {
+ g_string_append (gstring, message);
+ }
+ if (is_fatal) {
+ g_string_append (gstring, "\naborting...\n");
+ } else {
+ g_string_append (gstring, "\n");
+ }
+
+ string = g_string_free (gstring, FALSE);
+
+ syslog (priority, "%s", string);
+
+ g_free (string);
+}
+
+void
+ck_log_toggle_debug (void)
+{
+ if (syslog_levels & G_LOG_LEVEL_DEBUG) {
+ g_debug ("Debugging disabled");
+ syslog_levels &= ~G_LOG_LEVEL_DEBUG;
+ } else {
+ syslog_levels |= G_LOG_LEVEL_DEBUG;
+ g_debug ("Debugging enabled");
+ }
+}
+
+void
+ck_log_set_debug (gboolean debug)
+{
+ if (debug) {
+ syslog_levels |= G_LOG_LEVEL_DEBUG;
+ g_debug ("Debugging enabled");
+ } else {
+ g_debug ("Debugging disabled");
+ syslog_levels &= ~G_LOG_LEVEL_DEBUG;
+ }
+}
+
+void
+ck_log_init (void)
+{
+ const char *prg_name;
+
+ g_log_set_default_handler (ck_log_default_handler, NULL);
+
+ prg_name = g_get_prgname ();
+
+ openlog (prg_name, LOG_PERROR|LOG_PID, LOG_DAEMON);
+
+ initialized = TRUE;
+}
+
+void
+ck_log_shutdown (void)
+{
+ closelog ();
+ initialized = FALSE;
+}
+
diff --git a/src/ck-log.h b/src/ck-log.h
new file mode 100644
index 0000000..0028598
--- /dev/null
+++ b/src/ck-log.h
@@ -0,0 +1,41 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <mccann@jhu.edu>
+ *
+ */
+
+#ifndef __CK_LOG_H
+#define __CK_LOG_H
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+void ck_log_default_handler (const gchar *log_domain,
+ GLogLevelFlags log_level,
+ const gchar *message,
+ gpointer unused_data);
+void ck_log_set_debug (gboolean debug);
+void ck_log_toggle_debug (void);
+void ck_log_init (void);
+void ck_log_shutdown (void);
+
+G_END_DECLS
+
+#endif /* __CK_LOG_H */
diff --git a/src/ck-manager-glue.h b/src/ck-manager-glue.h
new file mode 100644
index 0000000..6172f7d
--- /dev/null
+++ b/src/ck-manager-glue.h
@@ -0,0 +1,226 @@
+/* Generated by dbus-binding-tool; do not edit! */
+
+
+#ifndef __dbus_glib_marshal_ck_manager_MARSHAL_H__
+#define __dbus_glib_marshal_ck_manager_MARSHAL_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#ifdef G_ENABLE_DEBUG
+#define g_marshal_value_peek_boolean(v) g_value_get_boolean (v)
+#define g_marshal_value_peek_char(v) g_value_get_char (v)
+#define g_marshal_value_peek_uchar(v) g_value_get_uchar (v)
+#define g_marshal_value_peek_int(v) g_value_get_int (v)
+#define g_marshal_value_peek_uint(v) g_value_get_uint (v)
+#define g_marshal_value_peek_long(v) g_value_get_long (v)
+#define g_marshal_value_peek_ulong(v) g_value_get_ulong (v)
+#define g_marshal_value_peek_int64(v) g_value_get_int64 (v)
+#define g_marshal_value_peek_uint64(v) g_value_get_uint64 (v)
+#define g_marshal_value_peek_enum(v) g_value_get_enum (v)
+#define g_marshal_value_peek_flags(v) g_value_get_flags (v)
+#define g_marshal_value_peek_float(v) g_value_get_float (v)
+#define g_marshal_value_peek_double(v) g_value_get_double (v)
+#define g_marshal_value_peek_string(v) (char*) g_value_get_string (v)
+#define g_marshal_value_peek_param(v) g_value_get_param (v)
+#define g_marshal_value_peek_boxed(v) g_value_get_boxed (v)
+#define g_marshal_value_peek_pointer(v) g_value_get_pointer (v)
+#define g_marshal_value_peek_object(v) g_value_get_object (v)
+#else /* !G_ENABLE_DEBUG */
+/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API.
+ * Do not access GValues directly in your code. Instead, use the
+ * g_value_get_*() functions
+ */
+#define g_marshal_value_peek_boolean(v) (v)->data[0].v_int
+#define g_marshal_value_peek_char(v) (v)->data[0].v_int
+#define g_marshal_value_peek_uchar(v) (v)->data[0].v_uint
+#define g_marshal_value_peek_int(v) (v)->data[0].v_int
+#define g_marshal_value_peek_uint(v) (v)->data[0].v_uint
+#define g_marshal_value_peek_long(v) (v)->data[0].v_long
+#define g_marshal_value_peek_ulong(v) (v)->data[0].v_ulong
+#define g_marshal_value_peek_int64(v) (v)->data[0].v_int64
+#define g_marshal_value_peek_uint64(v) (v)->data[0].v_uint64
+#define g_marshal_value_peek_enum(v) (v)->data[0].v_long
+#define g_marshal_value_peek_flags(v) (v)->data[0].v_ulong
+#define g_marshal_value_peek_float(v) (v)->data[0].v_float
+#define g_marshal_value_peek_double(v) (v)->data[0].v_double
+#define g_marshal_value_peek_string(v) (v)->data[0].v_pointer
+#define g_marshal_value_peek_param(v) (v)->data[0].v_pointer
+#define g_marshal_value_peek_boxed(v) (v)->data[0].v_pointer
+#define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer
+#define g_marshal_value_peek_object(v) (v)->data[0].v_pointer
+#endif /* !G_ENABLE_DEBUG */
+
+
+/* BOOLEAN:POINTER,POINTER (/tmp/dbus-binding-tool-c-marshallers.87ODQT:1) */
+extern void dbus_glib_marshal_ck_manager_BOOLEAN__POINTER_POINTER (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+void
+dbus_glib_marshal_ck_manager_BOOLEAN__POINTER_POINTER (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data)
+{
+ typedef gboolean (*GMarshalFunc_BOOLEAN__POINTER_POINTER) (gpointer data1,
+ gpointer arg_1,
+ gpointer arg_2,
+ gpointer data2);
+ register GMarshalFunc_BOOLEAN__POINTER_POINTER callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+ gboolean v_return;
+
+ g_return_if_fail (return_value != NULL);
+ g_return_if_fail (n_param_values == 3);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_BOOLEAN__POINTER_POINTER) (marshal_data ? marshal_data : cc->callback);
+
+ v_return = callback (data1,
+ g_marshal_value_peek_pointer (param_values + 1),
+ g_marshal_value_peek_pointer (param_values + 2),
+ data2);
+
+ g_value_set_boolean (return_value, v_return);
+}
+
+/* NONE:UINT,POINTER (/tmp/dbus-binding-tool-c-marshallers.87ODQT:2) */
+#define dbus_glib_marshal_ck_manager_VOID__UINT_POINTER g_cclosure_marshal_VOID__UINT_POINTER
+#define dbus_glib_marshal_ck_manager_NONE__UINT_POINTER dbus_glib_marshal_ck_manager_VOID__UINT_POINTER
+
+/* NONE:STRING,POINTER (/tmp/dbus-binding-tool-c-marshallers.87ODQT:3) */
+extern void dbus_glib_marshal_ck_manager_VOID__STRING_POINTER (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+void
+dbus_glib_marshal_ck_manager_VOID__STRING_POINTER (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__STRING_POINTER) (gpointer data1,
+ gpointer arg_1,
+ gpointer arg_2,
+ gpointer data2);
+ register GMarshalFunc_VOID__STRING_POINTER callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+
+ g_return_if_fail (n_param_values == 3);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__STRING_POINTER) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_string (param_values + 1),
+ g_marshal_value_peek_pointer (param_values + 2),
+ data2);
+}
+#define dbus_glib_marshal_ck_manager_NONE__STRING_POINTER dbus_glib_marshal_ck_manager_VOID__STRING_POINTER
+
+/* NONE:POINTER (/tmp/dbus-binding-tool-c-marshallers.87ODQT:4) */
+#define dbus_glib_marshal_ck_manager_VOID__POINTER g_cclosure_marshal_VOID__POINTER
+#define dbus_glib_marshal_ck_manager_NONE__POINTER dbus_glib_marshal_ck_manager_VOID__POINTER
+
+/* NONE:BOXED,POINTER (/tmp/dbus-binding-tool-c-marshallers.87ODQT:5) */
+extern void dbus_glib_marshal_ck_manager_VOID__BOXED_POINTER (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+void
+dbus_glib_marshal_ck_manager_VOID__BOXED_POINTER (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__BOXED_POINTER) (gpointer data1,
+ gpointer arg_1,
+ gpointer arg_2,
+ gpointer data2);
+ register GMarshalFunc_VOID__BOXED_POINTER callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+
+ g_return_if_fail (n_param_values == 3);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__BOXED_POINTER) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_boxed (param_values + 1),
+ g_marshal_value_peek_pointer (param_values + 2),
+ data2);
+}
+#define dbus_glib_marshal_ck_manager_NONE__BOXED_POINTER dbus_glib_marshal_ck_manager_VOID__BOXED_POINTER
+
+G_END_DECLS
+
+#endif /* __dbus_glib_marshal_ck_manager_MARSHAL_H__ */
+
+#include <dbus/dbus-glib.h>
+static const DBusGMethodInfo dbus_glib_ck_manager_methods[] = {
+ { (GCallback) ck_manager_open_session, dbus_glib_marshal_ck_manager_NONE__POINTER, 0 },
+ { (GCallback) ck_manager_open_session_with_parameters, dbus_glib_marshal_ck_manager_NONE__BOXED_POINTER, 65 },
+ { (GCallback) ck_manager_close_session, dbus_glib_marshal_ck_manager_NONE__STRING_POINTER, 163 },
+ { (GCallback) ck_manager_get_seats, dbus_glib_marshal_ck_manager_BOOLEAN__POINTER_POINTER, 240 },
+ { (GCallback) ck_manager_get_session_for_cookie, dbus_glib_marshal_ck_manager_NONE__STRING_POINTER, 302 },
+ { (GCallback) ck_manager_get_session_for_unix_process, dbus_glib_marshal_ck_manager_NONE__UINT_POINTER, 384 },
+ { (GCallback) ck_manager_get_current_session, dbus_glib_marshal_ck_manager_NONE__POINTER, 468 },
+ { (GCallback) ck_manager_get_sessions_for_unix_user, dbus_glib_marshal_ck_manager_NONE__UINT_POINTER, 537 },
+ { (GCallback) ck_manager_get_sessions_for_user, dbus_glib_marshal_ck_manager_NONE__UINT_POINTER, 624 },
+ { (GCallback) ck_manager_get_system_idle_hint, dbus_glib_marshal_ck_manager_BOOLEAN__POINTER_POINTER, 707 },
+ { (GCallback) ck_manager_get_system_idle_since_hint, dbus_glib_marshal_ck_manager_BOOLEAN__POINTER_POINTER, 781 },
+};
+
+const DBusGObjectInfo dbus_glib_ck_manager_object_info = {
+ 0,
+ dbus_glib_ck_manager_methods,
+ 11,
+"org.freedesktop.ConsoleKit.Manager\0OpenSession\0A\0cookie\0O\0F\0N\0s\0\0org.freedesktop.ConsoleKit.Manager\0OpenSessionWithParameters\0A\0parameters\0I\0a(sv)\0cookie\0O\0F\0N\0s\0\0org.freedesktop.ConsoleKit.Manager\0CloseSession\0A\0cookie\0I\0s\0result\0O\0F\0N\0b\0\0org.freedesktop.ConsoleKit.Manager\0GetSeats\0S\0seats\0O\0F\0N\0ao\0\0org.freedesktop.ConsoleKit.Manager\0GetSessionForCookie\0A\0cookie\0I\0s\0ssid\0O\0F\0N\0o\0\0org.freedesktop.ConsoleKit.Manager\0GetSessionForUnixProcess\0A\0pid\0I\0u\0ssid\0O\0F\0N\0o\0\0org.freedesktop.ConsoleKit.Manager\0GetCurrentSession\0A\0ssid\0O\0F\0N\0o\0\0org.freedesktop.ConsoleKit.Manager\0GetSessionsForUnixUser\0A\0uid\0I\0u\0sessions\0O\0F\0N\0ao\0\0org.freedesktop.ConsoleKit.Manager\0GetSessionsForUser\0A\0uid\0I\0u\0sessions\0O\0F\0N\0ao\0\0org.freedesktop.ConsoleKit.Manager\0GetSystemIdleHint\0S\0idle_hint\0O\0F\0N\0b\0\0org.freedesktop.ConsoleKit.Manager\0GetSystemIdleSinceHint\0S\0iso8601_datetime\0O\0F\0N\0s\0\0\0",
+"org.freedesktop.ConsoleKit.Manager\0SeatAdded\0org.freedesktop.ConsoleKit.Manager\0SeatRemoved\0org.freedesktop.ConsoleKit.Manager\0SystemIdleHintChanged\0\0",
+"\0"
+};
+
diff --git a/src/ck-manager.c b/src/ck-manager.c
new file mode 100644
index 0000000..99eeed6
--- /dev/null
+++ b/src/ck-manager.c
@@ -0,0 +1,1622 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2006-2007 William Jon McCann <mccann@jhu.edu>
+ *
+ * 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 <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib-object.h>
+#define DBUS_API_SUBJECT_TO_CHANGE
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+
+#include "ck-manager.h"
+#include "ck-manager-glue.h"
+#include "ck-seat.h"
+#include "ck-session.h"
+#include "ck-job.h"
+#include "ck-marshal.h"
+
+#include "proc.h"
+
+#define CK_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), CK_TYPE_MANAGER, CkManagerPrivate))
+
+#define CK_DBUS_PATH "/org/freedesktop/ConsoleKit"
+#define CK_MANAGER_DBUS_PATH CK_DBUS_PATH "/Manager"
+#define CK_MANAGER_DBUS_NAME "org.freedesktop.ConsoleKit.Manager"
+
+#define CK_TYPE_PARAMETER_STRUCT (dbus_g_type_get_struct ("GValueArray", \
+ G_TYPE_STRING, \
+ G_TYPE_VALUE, \
+ G_TYPE_INVALID))
+#define CK_TYPE_PARAMETER_LIST (dbus_g_type_get_collection ("GPtrArray", \
+ CK_TYPE_PARAMETER_STRUCT))
+struct CkManagerPrivate
+{
+ GHashTable *seats;
+ GHashTable *sessions;
+ GHashTable *leaders;
+
+ DBusGProxy *bus_proxy;
+ DBusGConnection *connection;
+
+ guint32 session_serial;
+ guint32 seat_serial;
+
+ gboolean system_idle_hint;
+ GTimeVal system_idle_since_hint;
+};
+
+
+typedef struct {
+ int refcount;
+ gboolean cancelled;
+ uid_t uid;
+ pid_t pid;
+ char *service_name;
+ char *ssid;
+ char *cookie;
+ GList *pending_jobs;
+} LeaderInfo;
+
+enum {
+ SEAT_ADDED,
+ SEAT_REMOVED,
+ SYSTEM_IDLE_HINT_CHANGED,
+ LAST_SIGNAL
+};
+
+static guint signals [LAST_SIGNAL] = { 0, };
+
+static void ck_manager_class_init (CkManagerClass *klass);
+static void ck_manager_init (CkManager *manager);
+static void ck_manager_finalize (GObject *object);
+
+static gpointer manager_object = NULL;
+
+G_DEFINE_TYPE (CkManager, ck_manager, G_TYPE_OBJECT)
+
+static void
+remove_pending_job (CkJob *job)
+{
+ if (job != NULL) {
+ char *command;
+
+ command = NULL;
+ ck_job_get_command (job, &command);
+ g_debug ("Removing pending job: %s", command);
+ g_free (command);
+
+ ck_job_cancel (job);
+ g_object_unref (job);
+ }
+}
+
+static void
+_leader_info_free (LeaderInfo *info)
+{
+ g_debug ("Freeing leader info: %s", info->ssid);
+
+ g_free (info->ssid);
+ info->ssid = NULL;
+ g_free (info->cookie);
+ info->cookie = NULL;
+ g_free (info->service_name);
+ info->service_name = NULL;
+
+ g_free (info);
+}
+
+static void
+leader_info_cancel (LeaderInfo *info)
+{
+ if (info->pending_jobs != NULL) {
+ g_list_foreach (info->pending_jobs, (GFunc)remove_pending_job, NULL);
+ g_list_free (info->pending_jobs);
+ info->pending_jobs = NULL;
+ }
+
+ info->cancelled = TRUE;
+}
+
+static void
+leader_info_unref (LeaderInfo *info)
+{
+ /* Probably should use some kind of atomic op here */
+ info->refcount -= 1;
+ if (info->refcount == 0) {
+ _leader_info_free (info);
+ }
+}
+
+static LeaderInfo *
+leader_info_ref (LeaderInfo *info)
+{
+ info->refcount += 1;
+
+ return info;
+}
+
+GQuark
+ck_manager_error_quark (void)
+{
+ static GQuark ret = 0;
+ if (ret == 0) {
+ ret = g_quark_from_static_string ("ck_manager_error");
+ }
+
+ return ret;
+}
+
+static guint32
+get_next_session_serial (CkManager *manager)
+{
+ guint32 serial;
+
+ serial = manager->priv->session_serial++;
+
+ if ((gint32)manager->priv->session_serial < 0) {
+ manager->priv->session_serial = 1;
+ }
+
+ return serial;
+}
+
+static guint32
+get_next_seat_serial (CkManager *manager)
+{
+ guint32 serial;
+
+ serial = manager->priv->seat_serial++;
+
+ if ((gint32)manager->priv->seat_serial < 0) {
+ manager->priv->seat_serial = 1;
+ }
+
+ return serial;
+}
+
+static char *
+generate_session_cookie (CkManager *manager)
+{
+ guint32 num;
+ char *cookie;
+ GTimeVal tv;
+ char *uuid;
+
+ uuid = dbus_get_local_machine_id ();
+ if (uuid == NULL) {
+ uuid = g_strdup (g_get_host_name ());
+ }
+
+ /* We want this to be globally unique
+ or at least such that it won't cycle when there
+ may be orphan processes in a dead session. */
+
+ cookie = NULL;
+ again:
+ num = (guint32)g_random_int_range (1, G_MAXINT32);
+
+ g_get_current_time (&tv);
+
+ g_free (cookie);
+ cookie = g_strdup_printf ("%s-%ld.%ld-%u", uuid, tv.tv_sec, tv.tv_usec, num);
+
+ if (g_hash_table_lookup (manager->priv->leaders, cookie)) {
+ goto again;
+ }
+
+ g_free (uuid);
+
+ return cookie;
+}
+
+static char *
+generate_session_id (CkManager *manager)
+{
+ guint32 serial;
+ char *id;
+
+ id = NULL;
+ again:
+ serial = get_next_session_serial (manager);
+ g_free (id);
+ id = g_strdup_printf ("%s/Session%u", CK_DBUS_PATH, serial);
+
+ if (g_hash_table_lookup (manager->priv->sessions, id)) {
+ goto again;
+ }
+
+ return id;
+}
+
+static char *
+generate_seat_id (CkManager *manager)
+{
+ guint32 serial;
+ char *id;
+
+ id = NULL;
+ again:
+ serial = get_next_seat_serial (manager);
+ g_free (id);
+ id = g_strdup_printf ("%s/Seat%u", CK_DBUS_PATH, serial);
+
+ if (g_hash_table_lookup (manager->priv->seats, id)) {
+ goto again;
+ }
+
+ return id;
+}
+
+static CkSeat *
+add_new_seat (CkManager *manager,
+ CkSeatKind kind)
+{
+ char *sid;
+ CkSeat *seat;
+
+ sid = generate_seat_id (manager);
+
+ seat = ck_seat_new (sid, kind);
+ if (seat == NULL) {
+ /* returns null if connection to bus fails */
+ g_free (sid);
+ goto out;
+ }
+
+ g_hash_table_insert (manager->priv->seats, sid, seat);
+
+ g_debug ("Added seat: %s kind:%d", sid, kind);
+
+ g_signal_emit (manager, signals [SEAT_ADDED], 0, sid);
+
+ out:
+ return seat;
+}
+
+static void
+remove_seat (CkManager *manager,
+ CkSeat *seat)
+{
+ char *sid;
+
+ sid = NULL;
+ ck_seat_get_id (seat, &sid, NULL);
+
+ if (sid != NULL) {
+ g_hash_table_remove (manager->priv->seats, sid);
+ }
+
+ g_signal_emit (manager, signals [SEAT_REMOVED], 0, sid);
+
+ g_debug ("Removed seat: %s", sid);
+
+ g_free (sid);
+}
+
+#define IS_STR_SET(x) (x != NULL && x[0] != '\0')
+
+static CkSeat *
+find_seat_for_session (CkManager *manager,
+ CkSession *session)
+{
+ CkSeat *seat;
+ gboolean is_static_x11;
+ gboolean is_static_text;
+ char *display_device;
+ char *x11_display_device;
+ char *x11_display;
+ char *remote_host_name;
+ gboolean is_local;
+
+ is_static_text = FALSE;
+ is_static_x11 = FALSE;
+
+ seat = NULL;
+ display_device = NULL;
+ x11_display_device = NULL;
+ x11_display = NULL;
+ remote_host_name = NULL;
+ is_local = FALSE;
+
+ /* FIXME: use matching to group entries? */
+
+ ck_session_get_display_device (session, &display_device, NULL);
+ ck_session_get_x11_display_device (session, &x11_display_device, NULL);
+ ck_session_get_x11_display (session, &x11_display, NULL);
+ ck_session_get_remote_host_name (session, &remote_host_name, NULL);
+ ck_session_is_local (session, &is_local, NULL);
+
+ if (IS_STR_SET (x11_display)
+ && IS_STR_SET (x11_display_device)
+ && ! IS_STR_SET (remote_host_name)
+ && is_local == TRUE) {
+ is_static_x11 = TRUE;
+ } else if (! IS_STR_SET (x11_display)
+ && ! IS_STR_SET (x11_display_device)
+ && IS_STR_SET (display_device)
+ && ! IS_STR_SET (remote_host_name)
+ && is_local == TRUE) {
+ is_static_text = TRUE;
+ }
+
+ if (is_static_x11 || is_static_text) {
+ char *sid;
+ sid = g_strdup_printf ("%s/Seat%u", CK_DBUS_PATH, 1);
+ seat = g_hash_table_lookup (manager->priv->seats, sid);
+ g_free (sid);
+ }
+
+ g_free (display_device);
+ g_free (x11_display_device);
+ g_free (x11_display);
+ g_free (remote_host_name);
+
+ return seat;
+}
+
+/* adapted from PolicyKit */
+static gboolean
+get_caller_info (CkManager *manager,
+ const char *sender,
+ uid_t *calling_uid,
+ pid_t *calling_pid)
+{
+ gboolean res;
+ GError *error = NULL;
+
+ res = FALSE;
+
+ if (sender == NULL) {
+ goto out;
+ }
+
+ if (! dbus_g_proxy_call (manager->priv->bus_proxy, "GetConnectionUnixUser", &error,
+ G_TYPE_STRING, sender,
+ G_TYPE_INVALID,
+ G_TYPE_UINT, calling_uid,
+ G_TYPE_INVALID)) {
+ g_debug ("GetConnectionUnixUser() failed: %s", error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ if (! dbus_g_proxy_call (manager->priv->bus_proxy, "GetConnectionUnixProcessID", &error,
+ G_TYPE_STRING, sender,
+ G_TYPE_INVALID,
+ G_TYPE_UINT, calling_pid,
+ G_TYPE_INVALID)) {
+ g_debug ("GetConnectionUnixProcessID() failed: %s", error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ res = TRUE;
+
+ g_debug ("uid = %d", *calling_uid);
+ g_debug ("pid = %d", *calling_pid);
+
+out:
+ return res;
+}
+
+static gboolean
+manager_set_system_idle_hint (CkManager *manager,
+ gboolean idle_hint)
+{
+ if (manager->priv->system_idle_hint != idle_hint) {
+ manager->priv->system_idle_hint = idle_hint;
+
+ /* FIXME: can we get a time from the dbus message? */
+ g_get_current_time (&manager->priv->system_idle_since_hint);
+
+ g_debug ("Emitting system-idle-hint-changed: %d", idle_hint);
+ g_signal_emit (manager, signals [SYSTEM_IDLE_HINT_CHANGED], 0, idle_hint);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+is_session_busy (char *id,
+ CkSession *session,
+ gpointer data)
+{
+ gboolean idle_hint;
+
+ idle_hint = FALSE;
+
+ ck_session_get_idle_hint (session, &idle_hint, NULL);
+
+ /* return TRUE to stop search */
+ return !idle_hint;
+}
+
+static void
+manager_update_system_idle_hint (CkManager *manager)
+{
+ CkSession *session;
+ gboolean system_idle;
+
+ /* just look for any session that doesn't have the idle-hint set */
+ session = g_hash_table_find (manager->priv->sessions, (GHRFunc)is_session_busy, NULL);
+
+ /* if there aren't any busy sessions then the system is idle */
+ system_idle = (session == NULL);
+
+ manager_set_system_idle_hint (manager, system_idle);
+}
+
+static void
+session_idle_hint_changed (CkSession *session,
+ gboolean idle_hint,
+ CkManager *manager)
+{
+ manager_update_system_idle_hint (manager);
+}
+
+/*
+ Example:
+ dbus-send --system --dest=org.freedesktop.ConsoleKit \
+ --type=method_call --print-reply --reply-timeout=2000 \
+ /org/freedesktop/ConsoleKit/Manager \
+ org.freedesktop.ConsoleKit.Manager.GetSystemIdleHint
+*/
+gboolean
+ck_manager_get_system_idle_hint (CkManager *manager,
+ gboolean *idle_hint,
+ GError **error)
+{
+ g_return_val_if_fail (CK_IS_MANAGER (manager), FALSE);
+
+ if (idle_hint != NULL) {
+ *idle_hint = manager->priv->system_idle_hint;
+ }
+
+ return TRUE;
+}
+
+#if GLIB_CHECK_VERSION(2,12,0)
+#define _g_time_val_to_iso8601(t) g_time_val_to_iso8601(t)
+#else
+/* copied from GLib */
+static gchar *
+_g_time_val_to_iso8601 (GTimeVal *time_)
+{
+ gchar *retval;
+
+ g_return_val_if_fail (time_->tv_usec >= 0 && time_->tv_usec < G_USEC_PER_SEC, NULL);
+
+#define ISO_8601_LEN 21
+#define ISO_8601_FORMAT "%Y-%m-%dT%H:%M:%SZ"
+ retval = g_new0 (gchar, ISO_8601_LEN + 1);
+
+ strftime (retval, ISO_8601_LEN,
+ ISO_8601_FORMAT,
+ gmtime (&(time_->tv_sec)));
+
+ return retval;
+}
+#endif
+
+gboolean
+ck_manager_get_system_idle_since_hint (CkManager *manager,
+ char **iso8601_datetime,
+ GError **error)
+{
+ char *date_str;
+
+ g_return_val_if_fail (CK_IS_MANAGER (manager), FALSE);
+
+ date_str = NULL;
+ if (manager->priv->system_idle_hint) {
+ date_str = _g_time_val_to_iso8601 (&manager->priv->system_idle_since_hint);
+ }
+
+ if (iso8601_datetime != NULL) {
+ *iso8601_datetime = g_strdup (date_str);
+ }
+
+ g_free (date_str);
+
+ return TRUE;
+}
+
+static void
+open_session_for_leader_info (CkManager *manager,
+ LeaderInfo *leader_info,
+ const GPtrArray *parameters,
+ DBusGMethodInvocation *context)
+{
+ CkSession *session;
+ CkSeat *seat;
+
+ session = ck_session_new_with_parameters (leader_info->ssid,
+ leader_info->cookie,
+ parameters);
+
+ if (session == NULL) {
+ GError *error;
+ g_debug ("Unable to create new session");
+ error = g_error_new (CK_MANAGER_ERROR,
+ CK_MANAGER_ERROR_GENERAL,
+ "Unable to create new session");
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+
+ return;
+ }
+
+ g_hash_table_insert (manager->priv->sessions, g_strdup (leader_info->ssid), g_object_ref (session));
+
+ /* Add to seat */
+ seat = find_seat_for_session (manager, session);
+ if (seat == NULL) {
+ /* create a new seat */
+ seat = add_new_seat (manager, CK_SEAT_KIND_DYNAMIC);
+ }
+
+ ck_seat_add_session (seat, session, NULL);
+
+ /* FIXME: connect to signals */
+ /* FIXME: add weak ref */
+
+ manager_update_system_idle_hint (manager);
+ g_signal_connect (session, "idle-hint-changed",
+ G_CALLBACK (session_idle_hint_changed),
+ manager);
+
+ g_object_unref (session);
+
+ dbus_g_method_return (context, leader_info->cookie);
+}
+
+static void
+verify_and_open_session_for_leader_info (CkManager *manager,
+ LeaderInfo *leader_info,
+ const GPtrArray *parameters,
+ DBusGMethodInvocation *context)
+{
+ /* for now don't bother verifying since we protect OpenSessionWithParameters */
+ open_session_for_leader_info (manager,
+ leader_info,
+ parameters,
+ context);
+}
+
+static void
+add_param_int (GPtrArray *parameters,
+ const char *key,
+ const char *value)
+{
+ GValue val = { 0, };
+ GValue param_val = { 0, };
+ int num;
+
+ num = atoi (value);
+
+ g_value_init (&val, G_TYPE_INT);
+ g_value_set_int (&val, num);
+ g_value_init (&param_val, CK_TYPE_PARAMETER_STRUCT);
+ g_value_take_boxed (&param_val,
+ dbus_g_type_specialized_construct (CK_TYPE_PARAMETER_STRUCT));
+ dbus_g_type_struct_set (&param_val,
+ 0, key,
+ 1, &val,
+ G_MAXUINT);
+ g_value_unset (&val);
+
+ g_ptr_array_add (parameters, g_value_get_boxed (&param_val));
+}
+
+static void
+add_param_boolean (GPtrArray *parameters,
+ const char *key,
+ const char *value)
+{
+ GValue val = { 0, };
+ GValue param_val = { 0, };
+ gboolean b;
+
+ if (value != NULL && strcmp (value, "true") == 0) {
+ b = TRUE;
+ } else {
+ b = FALSE;
+ }
+
+ g_value_init (&val, G_TYPE_BOOLEAN);
+ g_value_set_boolean (&val, b);
+ g_value_init (&param_val, CK_TYPE_PARAMETER_STRUCT);
+ g_value_take_boxed (&param_val,
+ dbus_g_type_specialized_construct (CK_TYPE_PARAMETER_STRUCT));
+ dbus_g_type_struct_set (&param_val,
+ 0, key,
+ 1, &val,
+ G_MAXUINT);
+ g_value_unset (&val);
+
+ g_ptr_array_add (parameters, g_value_get_boxed (&param_val));
+}
+
+static void
+add_param_string (GPtrArray *parameters,
+ const char *key,
+ const char *value)
+{
+ GValue val = { 0, };
+ GValue param_val = { 0, };
+
+ g_value_init (&val, G_TYPE_STRING);
+ g_value_set_string (&val, value);
+
+ g_value_init (&param_val, CK_TYPE_PARAMETER_STRUCT);
+ g_value_take_boxed (&param_val,
+ dbus_g_type_specialized_construct (CK_TYPE_PARAMETER_STRUCT));
+
+ dbus_g_type_struct_set (&param_val,
+ 0, key,
+ 1, &val,
+ G_MAXUINT);
+ g_value_unset (&val);
+
+ g_ptr_array_add (parameters, g_value_get_boxed (&param_val));
+}
+
+typedef void (* CkAddParamFunc) (GPtrArray *arr,
+ const char *key,
+ const char *value);
+
+static struct {
+ char *key;
+ CkAddParamFunc func;
+} parse_ops[] = {
+ { "display-device", add_param_string },
+ { "x11-display-device", add_param_string },
+ { "x11-display", add_param_string },
+ { "remote-host-name", add_param_string },
+ { "session-type", add_param_string },
+ { "is-local", add_param_boolean },
+ { "unix-user", add_param_int },
+};
+
+static GPtrArray *
+parse_output (const char *output)
+{
+ GPtrArray *parameters;
+ char **lines;
+ int i;
+ int j;
+
+ lines = g_strsplit (output, "\n", -1);
+ if (lines == NULL) {
+ return NULL;
+ }
+
+ parameters = g_ptr_array_sized_new (10);
+
+ for (i = 0; lines[i] != NULL; i++) {
+ char **vals;
+
+ vals = g_strsplit (lines[i], " = ", 2);
+ if (vals == NULL || vals[0] == NULL) {
+ g_strfreev (vals);
+ continue;
+ }
+
+ for (j = 0; j < G_N_ELEMENTS (parse_ops); j++) {
+ if (strcmp (vals[0], parse_ops[j].key) == 0) {
+ parse_ops[j].func (parameters, vals[0], vals[1]);
+ break;
+ }
+ }
+ g_strfreev (vals);
+ }
+
+ g_strfreev (lines);
+
+ return parameters;
+}
+
+typedef struct {
+ CkManager *manager;
+ LeaderInfo *leader_info;
+ DBusGMethodInvocation *context;
+} JobData;
+
+static void
+job_data_free (JobData *data)
+{
+ leader_info_unref (data->leader_info);
+ g_free (data);
+}
+
+static void
+parameters_free (GPtrArray *parameters)
+{
+ int i;
+
+ for (i = 0; i < parameters->len; i++) {
+ gpointer data;
+ data = g_ptr_array_index (parameters, i);
+ if (data != NULL) {
+ g_boxed_free (CK_TYPE_PARAMETER_STRUCT, data);
+ }
+ }
+
+ g_ptr_array_free (parameters, TRUE);
+}
+
+static void
+job_completed (CkJob *job,
+ int status,
+ JobData *data)
+{
+ g_debug ("Job status: %d", status);
+ if (status == 0) {
+ char *output;
+ GPtrArray *parameters;
+
+ output = NULL;
+ ck_job_get_stdout (job, &output);
+ g_debug ("Job output: %s", output);
+
+ parameters = parse_output (output);
+ g_free (output);
+
+ verify_and_open_session_for_leader_info (data->manager,
+ data->leader_info,
+ parameters,
+ data->context);
+ parameters_free (parameters);
+ }
+
+ /* remove job from queue */
+ data->leader_info->pending_jobs = g_list_remove (data->leader_info->pending_jobs, job);
+
+ g_signal_handlers_disconnect_by_func (job, job_completed, data);
+ g_object_unref (job);
+}
+
+static void
+generate_session_for_leader_info (CkManager *manager,
+ LeaderInfo *leader_info,
+ DBusGMethodInvocation *context)
+{
+ GError *local_error;
+ char *command;
+ gboolean res;
+ CkJob *job;
+ JobData *data;
+
+ command = g_strdup_printf ("%s --uid %u --pid %u",
+ LIBEXECDIR "/ck-collect-session-info",
+ leader_info->uid,
+ leader_info->pid);
+ job = ck_job_new ();
+ ck_job_set_command (job, command);
+ g_free (command);
+
+ data = g_new0 (JobData, 1);
+ data->manager = manager;
+ data->leader_info = leader_info_ref (leader_info);
+ data->context = context;
+ g_signal_connect_data (job,
+ "completed",
+ G_CALLBACK (job_completed),
+ data,
+ (GClosureNotify)job_data_free,
+ 0);
+
+ local_error = NULL;
+ res = ck_job_execute (job, &local_error);
+ if (! res) {
+ GError *error;
+ error = g_error_new (CK_MANAGER_ERROR,
+ CK_MANAGER_ERROR_GENERAL,
+ "Unable to get information about the calling process");
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+
+ if (local_error != NULL) {
+ g_debug ("stat on pid %d failed: %s", leader_info->pid, local_error->message);
+ g_error_free (local_error);
+ }
+
+ g_object_unref (job);
+
+ return;
+ }
+
+ /* Add job to queue */
+ leader_info->pending_jobs = g_list_prepend (leader_info->pending_jobs, job);
+}
+
+static gboolean
+create_session_for_sender (CkManager *manager,
+ const char *sender,
+ const GPtrArray *parameters,
+ DBusGMethodInvocation *context)
+{
+ pid_t pid;
+ uid_t uid;
+ gboolean res;
+ char *cookie;
+ char *ssid;
+ LeaderInfo *leader_info;
+
+ res = get_caller_info (manager,
+ sender,
+ &uid,
+ &pid);
+ if (! res) {
+ GError *error;
+ error = g_error_new (CK_MANAGER_ERROR,
+ CK_MANAGER_ERROR_GENERAL,
+ "Unable to get information about the calling process");
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ return FALSE;
+ }
+
+ cookie = generate_session_cookie (manager);
+ ssid = generate_session_id (manager);
+
+ g_debug ("Creating new session ssid: %s", ssid);
+
+ leader_info = g_new0 (LeaderInfo, 1);
+ leader_info->uid = uid;
+ leader_info->pid = pid;
+ leader_info->service_name = g_strdup (sender);
+ leader_info->ssid = g_strdup (ssid);
+ leader_info->cookie = g_strdup (cookie);
+
+ /* need to store the leader info first so the pending request can be revoked */
+ g_hash_table_insert (manager->priv->leaders,
+ g_strdup (leader_info->cookie),
+ leader_info_ref (leader_info));
+
+ if (parameters == NULL) {
+ generate_session_for_leader_info (manager,
+ leader_info,
+ context);
+ } else {
+ verify_and_open_session_for_leader_info (manager,
+ leader_info,
+ parameters,
+ context);
+ }
+
+ g_free (cookie);
+ g_free (ssid);
+
+ return TRUE;
+}
+
+/*
+ Example:
+ dbus-send --system --dest=org.freedesktop.ConsoleKit \
+ --type=method_call --print-reply --reply-timeout=2000 \
+ /org/freedesktop/ConsoleKit/Manager \
+ org.freedesktop.ConsoleKit.Manager.GetSessionForCookie string:$XDG_SESSION_COOKIE
+*/
+gboolean
+ck_manager_get_session_for_cookie (CkManager *manager,
+ const char *cookie,
+ DBusGMethodInvocation *context)
+{
+ gboolean res;
+ char *sender;
+ uid_t calling_uid;
+ pid_t calling_pid;
+ proc_stat_t *stat;
+ char *ssid;
+ CkSession *session;
+ LeaderInfo *leader_info;
+ GError *local_error;
+
+ ssid = NULL;
+
+ sender = dbus_g_method_get_sender (context);
+
+ res = get_caller_info (manager,
+ sender,
+ &calling_uid,
+ &calling_pid);
+ g_free (sender);
+
+ if (! res) {
+ GError *error;
+ error = g_error_new (CK_MANAGER_ERROR,
+ CK_MANAGER_ERROR_GENERAL,
+ _("Unable to get information about the calling process"));
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ return FALSE;
+ }
+
+ local_error = NULL;
+ res = proc_stat_new_for_pid (calling_pid, &stat, &local_error);
+ if (! res) {
+ GError *error;
+ error = g_error_new (CK_MANAGER_ERROR,
+ CK_MANAGER_ERROR_GENERAL,
+ _("Unable to lookup information about calling process '%d'"),
+ calling_pid);
+ if (local_error != NULL) {
+ g_debug ("stat on pid %d failed: %s", calling_pid, local_error->message);
+ g_error_free (local_error);
+ }
+
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ return FALSE;
+ }
+
+ /* FIXME: should we restrict this by uid? */
+ proc_stat_free (stat);
+
+ leader_info = g_hash_table_lookup (manager->priv->leaders, cookie);
+ if (leader_info == NULL) {
+ GError *error;
+ error = g_error_new (CK_MANAGER_ERROR,
+ CK_MANAGER_ERROR_GENERAL,
+ _("Unable to find session for cookie"));
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ return FALSE;
+ }
+
+ session = g_hash_table_lookup (manager->priv->sessions, leader_info->ssid);
+ if (session == NULL) {
+ GError *error;
+ error = g_error_new (CK_MANAGER_ERROR,
+ CK_MANAGER_ERROR_GENERAL,
+ _("Unable to find session for cookie"));
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ return FALSE;
+ }
+
+ ck_session_get_id (session, &ssid, NULL);
+
+ dbus_g_method_return (context, ssid);
+
+ g_free (ssid);
+
+ return TRUE;
+}
+
+static char *
+get_cookie_for_pid (CkManager *manager,
+ guint pid)
+{
+ char *cookie;
+
+ /* FIXME: need a better way to get the cookie */
+
+ cookie = proc_pid_get_env (pid, "XDG_SESSION_COOKIE");
+
+ return cookie;
+}
+
+/*
+ Example:
+ dbus-send --system --dest=org.freedesktop.ConsoleKit \
+ --type=method_call --print-reply --reply-timeout=2000 \
+ /org/freedesktop/ConsoleKit/Manager \
+ org.freedesktop.ConsoleKit.Manager.GetSessionForUnixProcess uint32:`/sbin/pidof -s bash`
+*/
+gboolean
+ck_manager_get_session_for_unix_process (CkManager *manager,
+ guint pid,
+ DBusGMethodInvocation *context)
+{
+ gboolean res;
+ char *sender;
+ uid_t calling_uid;
+ pid_t calling_pid;
+ proc_stat_t *stat;
+ char *cookie;
+ GError *error;
+
+ sender = dbus_g_method_get_sender (context);
+
+ res = get_caller_info (manager,
+ sender,
+ &calling_uid,
+ &calling_pid);
+ g_free (sender);
+
+ if (! res) {
+ GError *error;
+ error = g_error_new (CK_MANAGER_ERROR,
+ CK_MANAGER_ERROR_GENERAL,
+ _("Unable to get information about the calling process"));
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ return FALSE;
+ }
+
+ error = NULL;
+ res = proc_stat_new_for_pid (calling_pid, &stat, &error);
+ if (! res) {
+ GError *error;
+ g_debug ("stat on pid %d failed", calling_pid);
+ error = g_error_new (CK_MANAGER_ERROR,
+ CK_MANAGER_ERROR_GENERAL,
+ _("Unable to lookup information about calling process '%d'"),
+ calling_pid);
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ return FALSE;
+ }
+
+ /* FIXME: check stuff? */
+
+ proc_stat_free (stat);
+
+ cookie = get_cookie_for_pid (manager, pid);
+ if (cookie == NULL) {
+ GError *error;
+ error = g_error_new (CK_MANAGER_ERROR,
+ CK_MANAGER_ERROR_GENERAL,
+ _("Unable to lookup session information for process '%d'"),
+ pid);
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ return FALSE;
+ }
+
+ res = ck_manager_get_session_for_cookie (manager, cookie, context);
+ g_free (cookie);
+
+ return res;
+}
+
+/*
+ Example:
+ dbus-send --system --dest=org.freedesktop.ConsoleKit \
+ --type=method_call --print-reply --reply-timeout=2000 \
+ /org/freedesktop/ConsoleKit/Manager \
+ org.freedesktop.ConsoleKit.Manager.GetCurrentSession
+*/
+gboolean
+ck_manager_get_current_session (CkManager *manager,
+ DBusGMethodInvocation *context)
+{
+ gboolean res;
+ char *sender;
+ uid_t calling_uid;
+ pid_t calling_pid;
+
+ sender = dbus_g_method_get_sender (context);
+
+ res = get_caller_info (manager,
+ sender,
+ &calling_uid,
+ &calling_pid);
+ g_free (sender);
+
+ if (! res) {
+ GError *error;
+ error = g_error_new (CK_MANAGER_ERROR,
+ CK_MANAGER_ERROR_GENERAL,
+ _("Unable to get information about the calling process"));
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ return FALSE;
+ }
+
+ res = ck_manager_get_session_for_unix_process (manager, calling_pid, context);
+
+ return res;
+}
+
+gboolean
+ck_manager_open_session (CkManager *manager,
+ DBusGMethodInvocation *context)
+{
+ char *sender;
+ gboolean ret;
+
+ sender = dbus_g_method_get_sender (context);
+ ret = create_session_for_sender (manager, sender, NULL, context);
+ g_free (sender);
+
+ return ret;
+}
+
+gboolean
+ck_manager_open_session_with_parameters (CkManager *manager,
+ const GPtrArray *parameters,
+ DBusGMethodInvocation *context)
+{
+ char *sender;
+ gboolean ret;
+
+ sender = dbus_g_method_get_sender (context);
+ ret = create_session_for_sender (manager, sender, parameters, context);
+ g_free (sender);
+
+ return ret;
+}
+
+static gboolean
+remove_session_for_cookie (CkManager *manager,
+ const char *cookie,
+ GError **error)
+{
+ CkSession *session;
+ LeaderInfo *leader_info;
+ char *ssid;
+ char *sid;
+
+ g_debug ("Removing session for cookie: %s", cookie);
+
+ leader_info = g_hash_table_lookup (manager->priv->leaders, cookie);
+
+ if (leader_info == NULL) {
+ g_set_error (error,
+ CK_MANAGER_ERROR,
+ CK_MANAGER_ERROR_GENERAL,
+ "Unable to find session for cookie");
+ return FALSE;
+ }
+
+ session = g_hash_table_lookup (manager->priv->sessions, leader_info->ssid);
+ if (session == NULL) {
+ g_set_error (error,
+ CK_MANAGER_ERROR,
+ CK_MANAGER_ERROR_GENERAL,
+ "Unable to find session for cookie");
+ return FALSE;
+ }
+
+ ssid = g_strdup (leader_info->ssid);
+
+ /* remove from seat */
+ ck_session_get_seat_id (session, &sid, NULL);
+ if (sid != NULL) {
+ CkSeat *seat;
+ seat = g_hash_table_lookup (manager->priv->seats, sid);
+ if (seat != NULL) {
+ CkSeatKind kind;
+
+ ck_seat_remove_session (seat, session, NULL);
+
+ kind = CK_SEAT_KIND_STATIC;
+ /* if dynamic seat has no sessions then remove it */
+ ck_seat_get_kind (seat, &kind, NULL);
+ if (kind == CK_SEAT_KIND_DYNAMIC) {
+ remove_seat (manager, seat);
+ }
+ }
+ }
+
+ g_hash_table_remove (manager->priv->sessions, ssid);
+
+ g_free (sid);
+ g_free (ssid);
+
+ manager_update_system_idle_hint (manager);
+
+ return TRUE;
+}
+
+static gboolean
+paranoia_check_is_cookie_owner (CkManager *manager,
+ const char *cookie,
+ uid_t calling_uid,
+ pid_t calling_pid,
+ GError **error)
+{
+ LeaderInfo *leader_info;
+
+ if (cookie == NULL) {
+ g_set_error (error,
+ CK_MANAGER_ERROR,
+ CK_MANAGER_ERROR_GENERAL,
+ "No cookie specified");
+ return FALSE;
+ }
+
+ leader_info = g_hash_table_lookup (manager->priv->leaders, cookie);
+ if (leader_info == NULL) {
+ g_set_error (error,
+ CK_MANAGER_ERROR,
+ CK_MANAGER_ERROR_GENERAL,
+ _("Unable to find session for cookie"));
+ return FALSE;
+ }
+
+ if (leader_info->uid != calling_uid) {
+ g_set_error (error,
+ CK_MANAGER_ERROR,
+ CK_MANAGER_ERROR_GENERAL,
+ _("User ID does not match the owner of cookie"));
+ return FALSE;
+
+ }
+
+ /* do we want to restrict to the same process? */
+ if (leader_info->pid != calling_pid) {
+ g_set_error (error,
+ CK_MANAGER_ERROR,
+ CK_MANAGER_ERROR_GENERAL,
+ _("Process ID does not match the owner of cookie"));
+ return FALSE;
+
+ }
+
+ return TRUE;
+}
+
+gboolean
+ck_manager_close_session (CkManager *manager,
+ const char *cookie,
+ DBusGMethodInvocation *context)
+{
+ gboolean res;
+ char *sender;
+ uid_t calling_uid;
+ pid_t calling_pid;
+ GError *error;
+
+ g_debug ("Closing session for cookie: %s", cookie);
+
+ sender = dbus_g_method_get_sender (context);
+ res = get_caller_info (manager,
+ sender,
+ &calling_uid,
+ &calling_pid);
+ g_free (sender);
+
+ if (! res) {
+ error = g_error_new (CK_MANAGER_ERROR,
+ CK_MANAGER_ERROR_GENERAL,
+ "Unable to get information about the calling process");
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+
+ return FALSE;
+ }
+
+ error = NULL;
+ res = paranoia_check_is_cookie_owner (manager, cookie, calling_uid, calling_pid, &error);
+ if (! res) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+
+ return FALSE;
+ }
+
+ error = NULL;
+ res = remove_session_for_cookie (manager, cookie, &error);
+ if (! res) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ return FALSE;
+ } else {
+ g_hash_table_remove (manager->priv->leaders, cookie);
+ }
+
+ dbus_g_method_return (context, res);
+
+ return TRUE;
+}
+
+typedef struct {
+ const char *service_name;
+ CkManager *manager;
+} RemoveLeaderData;
+
+static gboolean
+remove_leader_for_connection (const char *cookie,
+ LeaderInfo *info,
+ RemoveLeaderData *data)
+{
+ g_assert (info != NULL);
+ g_assert (data->service_name != NULL);
+
+ if (strcmp (info->service_name, data->service_name) == 0) {
+ remove_session_for_cookie (data->manager, cookie, NULL);
+ leader_info_cancel (info);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+remove_sessions_for_connection (CkManager *manager,
+ const char *service_name)
+{
+ guint n_removed;
+ RemoveLeaderData data;
+
+ data.service_name = service_name;
+ data.manager = manager;
+
+ g_debug ("Removing sessions for service name: %s", service_name);
+
+ n_removed = g_hash_table_foreach_remove (manager->priv->leaders,
+ (GHRFunc)remove_leader_for_connection,
+ &data);
+
+}
+
+static void
+bus_name_owner_changed (DBusGProxy *bus_proxy,
+ const char *service_name,
+ const char *old_service_name,
+ const char *new_service_name,
+ CkManager *manager)
+{
+ if (strlen (new_service_name) == 0) {
+ remove_sessions_for_connection (manager, old_service_name);
+ }
+
+ g_debug ("NameOwnerChanged: service_name='%s', old_service_name='%s' new_service_name='%s'",
+ service_name, old_service_name, new_service_name);
+}
+
+static gboolean
+register_manager (CkManager *manager)
+{
+ GError *error = NULL;
+
+ error = NULL;
+ manager->priv->connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error);
+ if (manager->priv->connection == NULL) {
+ if (error != NULL) {
+ g_critical ("error getting system bus: %s", error->message);
+ g_error_free (error);
+ }
+ exit (1);
+ }
+
+ manager->priv->bus_proxy = dbus_g_proxy_new_for_name (manager->priv->connection,
+ DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS);
+ dbus_g_proxy_add_signal (manager->priv->bus_proxy,
+ "NameOwnerChanged",
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_INVALID);
+ dbus_g_proxy_connect_signal (manager->priv->bus_proxy,
+ "NameOwnerChanged",
+ G_CALLBACK (bus_name_owner_changed),
+ manager,
+ NULL);
+
+ dbus_g_connection_register_g_object (manager->priv->connection, CK_MANAGER_DBUS_PATH, G_OBJECT (manager));
+
+ return TRUE;
+}
+
+static void
+ck_manager_class_init (CkManagerClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = ck_manager_finalize;
+
+ signals [SEAT_ADDED] =
+ g_signal_new ("seat-added",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (CkManagerClass, seat_added),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE,
+ 1, G_TYPE_STRING);
+ signals [SEAT_REMOVED] =
+ g_signal_new ("seat-removed",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (CkManagerClass, seat_removed),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE,
+ 1, G_TYPE_STRING);
+ signals [SYSTEM_IDLE_HINT_CHANGED] =
+ g_signal_new ("system-idle-hint-changed",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (CkManagerClass, system_idle_hint_changed),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__BOOLEAN,
+ G_TYPE_NONE,
+ 1, G_TYPE_BOOLEAN);
+
+ dbus_g_object_type_install_info (CK_TYPE_MANAGER, &dbus_glib_ck_manager_object_info);
+
+ g_type_class_add_private (klass, sizeof (CkManagerPrivate));
+}
+
+typedef struct {
+ guint uid;
+ GPtrArray *sessions;
+} GetSessionsData;
+
+static void
+get_sessions_for_unix_user_iter (char *id,
+ CkSession *session,
+ GetSessionsData *data)
+{
+ guint uid;
+ gboolean res;
+
+ res = ck_session_get_unix_user (session, &uid, NULL);
+
+ if (res && uid == data->uid) {
+ g_ptr_array_add (data->sessions, g_strdup (id));
+ }
+}
+
+gboolean
+ck_manager_get_sessions_for_unix_user (CkManager *manager,
+ guint uid,
+ DBusGMethodInvocation *context)
+{
+ GetSessionsData *data;
+
+ g_return_val_if_fail (CK_IS_MANAGER (manager), FALSE);
+
+ data = g_new0 (GetSessionsData, 1);
+ data->uid = uid;
+ data->sessions = g_ptr_array_new ();
+
+ g_hash_table_foreach (manager->priv->sessions, (GHFunc)get_sessions_for_unix_user_iter, data);
+
+ dbus_g_method_return (context, data->sessions);
+
+ g_ptr_array_foreach (data->sessions, (GFunc)g_free, NULL);
+ g_ptr_array_free (data->sessions, TRUE);
+ g_free (data);
+
+ return TRUE;
+}
+
+/* This is deprecated */
+gboolean
+ck_manager_get_sessions_for_user (CkManager *manager,
+ guint uid,
+ DBusGMethodInvocation *context)
+{
+ return ck_manager_get_sessions_for_unix_user (manager, uid, context);
+}
+
+static void
+listify_seat_ids (char *id,
+ CkSeat *seat,
+ GPtrArray **array)
+{
+ g_ptr_array_add (*array, g_strdup (id));
+}
+
+gboolean
+ck_manager_get_seats (CkManager *manager,
+ GPtrArray **seats,
+ GError **error)
+{
+ g_return_val_if_fail (CK_IS_MANAGER (manager), FALSE);
+
+ if (seats == NULL) {
+ return FALSE;
+ }
+
+ *seats = g_ptr_array_new ();
+ g_hash_table_foreach (manager->priv->seats, (GHFunc)listify_seat_ids, seats);
+
+ return TRUE;
+}
+
+static void
+create_seats (CkManager *manager)
+{
+ CkSeat *seat;
+
+ seat = add_new_seat (manager, CK_SEAT_KIND_STATIC);
+}
+
+static void
+ck_manager_init (CkManager *manager)
+{
+
+ manager->priv = CK_MANAGER_GET_PRIVATE (manager);
+
+ /* reserve zero */
+ manager->priv->session_serial = 1;
+ manager->priv->seat_serial = 1;
+
+ manager->priv->system_idle_hint = TRUE;
+
+ manager->priv->seats = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ (GDestroyNotify) g_object_unref);
+ manager->priv->sessions = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ (GDestroyNotify) g_object_unref);
+ manager->priv->leaders = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ (GDestroyNotify) leader_info_unref);
+
+ create_seats (manager);
+}
+
+static void
+ck_manager_finalize (GObject *object)
+{
+ CkManager *manager;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (CK_IS_MANAGER (object));
+
+ manager = CK_MANAGER (object);
+
+ g_return_if_fail (manager->priv != NULL);
+
+ g_hash_table_destroy (manager->priv->seats);
+ g_hash_table_destroy (manager->priv->sessions);
+ g_hash_table_destroy (manager->priv->leaders);
+ g_object_unref (manager->priv->bus_proxy);
+
+ G_OBJECT_CLASS (ck_manager_parent_class)->finalize (object);
+}
+
+CkManager *
+ck_manager_new (void)
+{
+ if (manager_object != NULL) {
+ g_object_ref (manager_object);
+ } else {
+ gboolean res;
+
+ manager_object = g_object_new (CK_TYPE_MANAGER, NULL);
+ g_object_add_weak_pointer (manager_object,
+ (gpointer *) &manager_object);
+ res = register_manager (manager_object);
+ if (! res) {
+ g_object_unref (manager_object);
+ return NULL;
+ }
+ }
+
+ return CK_MANAGER (manager_object);
+}
diff --git a/src/ck-manager.h b/src/ck-manager.h
new file mode 100644
index 0000000..a6fd670
--- /dev/null
+++ b/src/ck-manager.h
@@ -0,0 +1,113 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2006 William Jon McCann <mccann@jhu.edu>
+ *
+ * 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.
+ *
+ */
+
+
+#ifndef __CK_MANAGER_H
+#define __CK_MANAGER_H
+
+#include <glib-object.h>
+#include <dbus/dbus-glib.h>
+
+#include "ck-seat.h"
+
+G_BEGIN_DECLS
+
+#define CK_TYPE_MANAGER (ck_manager_get_type ())
+#define CK_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CK_TYPE_MANAGER, CkManager))
+#define CK_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), CK_TYPE_MANAGER, CkManagerClass))
+#define CK_IS_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), CK_TYPE_MANAGER))
+#define CK_IS_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), CK_TYPE_MANAGER))
+#define CK_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), CK_TYPE_MANAGER, CkManagerClass))
+
+typedef struct CkManagerPrivate CkManagerPrivate;
+
+typedef struct
+{
+ GObject parent;
+ CkManagerPrivate *priv;
+} CkManager;
+
+typedef struct
+{
+ GObjectClass parent_class;
+
+ void (* seat_added) (CkManager *manager,
+ const char *sid);
+ void (* seat_removed) (CkManager *manager,
+ const char *sid);
+ void (* system_idle_hint_changed) (CkManager *manager,
+ gboolean idle_hint);
+} CkManagerClass;
+
+typedef enum
+{
+ CK_MANAGER_ERROR_GENERAL
+} CkManagerError;
+
+#define CK_MANAGER_ERROR ck_manager_error_quark ()
+
+GQuark ck_manager_error_quark (void);
+GType ck_manager_get_type (void);
+
+CkManager * ck_manager_new (void);
+
+/* unprivileged methods */
+
+/* Authoritative properties */
+gboolean ck_manager_open_session (CkManager *manager,
+ DBusGMethodInvocation *context);
+gboolean ck_manager_get_seats (CkManager *manager,
+ GPtrArray **seats,
+ GError **error);
+gboolean ck_manager_close_session (CkManager *manager,
+ const char *cookie,
+ DBusGMethodInvocation *context);
+gboolean ck_manager_get_current_session (CkManager *manager,
+ DBusGMethodInvocation *context);
+gboolean ck_manager_get_session_for_cookie (CkManager *manager,
+ const char *cookie,
+ DBusGMethodInvocation *context);
+gboolean ck_manager_get_session_for_unix_process (CkManager *manager,
+ guint pid,
+ DBusGMethodInvocation *context);
+gboolean ck_manager_get_sessions_for_unix_user (CkManager *manager,
+ guint uid,
+ DBusGMethodInvocation *context);
+/* deprecated */
+gboolean ck_manager_get_sessions_for_user (CkManager *manager,
+ guint uid,
+ DBusGMethodInvocation *context);
+
+/* Non-authoritative properties */
+gboolean ck_manager_get_system_idle_hint (CkManager *manager,
+ gboolean *idle_hint,
+ GError **error);
+gboolean ck_manager_get_system_idle_since_hint (CkManager *manager,
+ char **iso8601_datetime,
+ GError **error);
+
+/* privileged methods - should be protected by D-Bus policy */
+gboolean ck_manager_open_session_with_parameters (CkManager *manager,
+ const GPtrArray *parameters,
+ DBusGMethodInvocation *context);
+
+G_END_DECLS
+
+#endif /* __CK_MANAGER_H */
diff --git a/src/ck-manager.xml b/src/ck-manager.xml
new file mode 100644
index 0000000..f40d6d5
--- /dev/null
+++ b/src/ck-manager.xml
@@ -0,0 +1,61 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<node name="/org/freedesktop/ConsoleKit/Manager">
+ <interface name="org.freedesktop.ConsoleKit.Manager">
+ <method name="OpenSession">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg name="cookie" direction="out" type="s"/>
+ </method>
+ <method name="OpenSessionWithParameters">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg name="parameters" direction="in" type="a(sv)"/>
+ <arg name="cookie" direction="out" type="s"/>
+ </method>
+ <method name="CloseSession">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg name="cookie" direction="in" type="s"/>
+ <arg name="result" direction="out" type="b"/>
+ </method>
+ <method name="GetSeats">
+ <arg name="seats" direction="out" type="ao"/>
+ </method>
+ <method name="GetSessionForCookie">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg name="cookie" direction="in" type="s"/>
+ <arg name="ssid" direction="out" type="o"/>
+ </method>
+ <method name="GetSessionForUnixProcess">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg name="pid" direction="in" type="u"/>
+ <arg name="ssid" direction="out" type="o"/>
+ </method>
+ <method name="GetCurrentSession">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg name="ssid" direction="out" type="o"/>
+ </method>
+ <method name="GetSessionsForUnixUser">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg name="uid" direction="in" type="u"/>
+ <arg name="sessions" direction="out" type="ao"/>
+ </method>
+ <method name="GetSessionsForUser">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg name="uid" direction="in" type="u"/>
+ <arg name="sessions" direction="out" type="ao"/>
+ </method>
+ <method name="GetSystemIdleHint">
+ <arg name="idle_hint" type="b" direction="out"/>
+ </method>
+ <method name="GetSystemIdleSinceHint">
+ <arg name="iso8601_datetime" type="s" direction="out"/>
+ </method>
+ <signal name="SeatAdded">
+ <arg name="sid" type="o"/>
+ </signal>
+ <signal name="SeatRemoved">
+ <arg name="sid" type="o"/>
+ </signal>
+ <signal name="SystemIdleHintChanged">
+ <arg name="hint" type="b"/>
+ </signal>
+ </interface>
+</node>
diff --git a/src/ck-marshal.c b/src/ck-marshal.c
new file mode 100644
index 0000000..6b77b77
--- /dev/null
+++ b/src/ck-marshal.c
@@ -0,0 +1,126 @@
+#include "ck-marshal.h"
+
+#include <glib-object.h>
+
+
+#ifdef G_ENABLE_DEBUG
+#define g_marshal_value_peek_boolean(v) g_value_get_boolean (v)
+#define g_marshal_value_peek_char(v) g_value_get_char (v)
+#define g_marshal_value_peek_uchar(v) g_value_get_uchar (v)
+#define g_marshal_value_peek_int(v) g_value_get_int (v)
+#define g_marshal_value_peek_uint(v) g_value_get_uint (v)
+#define g_marshal_value_peek_long(v) g_value_get_long (v)
+#define g_marshal_value_peek_ulong(v) g_value_get_ulong (v)
+#define g_marshal_value_peek_int64(v) g_value_get_int64 (v)
+#define g_marshal_value_peek_uint64(v) g_value_get_uint64 (v)
+#define g_marshal_value_peek_enum(v) g_value_get_enum (v)
+#define g_marshal_value_peek_flags(v) g_value_get_flags (v)
+#define g_marshal_value_peek_float(v) g_value_get_float (v)
+#define g_marshal_value_peek_double(v) g_value_get_double (v)
+#define g_marshal_value_peek_string(v) (char*) g_value_get_string (v)
+#define g_marshal_value_peek_param(v) g_value_get_param (v)
+#define g_marshal_value_peek_boxed(v) g_value_get_boxed (v)
+#define g_marshal_value_peek_pointer(v) g_value_get_pointer (v)
+#define g_marshal_value_peek_object(v) g_value_get_object (v)
+#else /* !G_ENABLE_DEBUG */
+/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API.
+ * Do not access GValues directly in your code. Instead, use the
+ * g_value_get_*() functions
+ */
+#define g_marshal_value_peek_boolean(v) (v)->data[0].v_int
+#define g_marshal_value_peek_char(v) (v)->data[0].v_int
+#define g_marshal_value_peek_uchar(v) (v)->data[0].v_uint
+#define g_marshal_value_peek_int(v) (v)->data[0].v_int
+#define g_marshal_value_peek_uint(v) (v)->data[0].v_uint
+#define g_marshal_value_peek_long(v) (v)->data[0].v_long
+#define g_marshal_value_peek_ulong(v) (v)->data[0].v_ulong
+#define g_marshal_value_peek_int64(v) (v)->data[0].v_int64
+#define g_marshal_value_peek_uint64(v) (v)->data[0].v_uint64
+#define g_marshal_value_peek_enum(v) (v)->data[0].v_long
+#define g_marshal_value_peek_flags(v) (v)->data[0].v_ulong
+#define g_marshal_value_peek_float(v) (v)->data[0].v_float
+#define g_marshal_value_peek_double(v) (v)->data[0].v_double
+#define g_marshal_value_peek_string(v) (v)->data[0].v_pointer
+#define g_marshal_value_peek_param(v) (v)->data[0].v_pointer
+#define g_marshal_value_peek_boxed(v) (v)->data[0].v_pointer
+#define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer
+#define g_marshal_value_peek_object(v) (v)->data[0].v_pointer
+#endif /* !G_ENABLE_DEBUG */
+
+
+/* VOID:UINT,STRING (ck-marshal.list:1) */
+void
+ck_marshal_VOID__UINT_STRING (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__UINT_STRING) (gpointer data1,
+ guint arg_1,
+ gpointer arg_2,
+ gpointer data2);
+ register GMarshalFunc_VOID__UINT_STRING callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+
+ g_return_if_fail (n_param_values == 3);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__UINT_STRING) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_uint (param_values + 1),
+ g_marshal_value_peek_string (param_values + 2),
+ data2);
+}
+
+/* BOOLEAN:POINTER (ck-marshal.list:2) */
+void
+ck_marshal_BOOLEAN__POINTER (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data)
+{
+ typedef gboolean (*GMarshalFunc_BOOLEAN__POINTER) (gpointer data1,
+ gpointer arg_1,
+ gpointer data2);
+ register GMarshalFunc_BOOLEAN__POINTER callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+ gboolean v_return;
+
+ g_return_if_fail (return_value != NULL);
+ g_return_if_fail (n_param_values == 2);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_BOOLEAN__POINTER) (marshal_data ? marshal_data : cc->callback);
+
+ v_return = callback (data1,
+ g_marshal_value_peek_pointer (param_values + 1),
+ data2);
+
+ g_value_set_boolean (return_value, v_return);
+}
+
diff --git a/src/ck-marshal.h b/src/ck-marshal.h
new file mode 100644
index 0000000..7553810
--- /dev/null
+++ b/src/ck-marshal.h
@@ -0,0 +1,28 @@
+
+#ifndef __ck_marshal_MARSHAL_H__
+#define __ck_marshal_MARSHAL_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+/* VOID:UINT,STRING (ck-marshal.list:1) */
+extern void ck_marshal_VOID__UINT_STRING (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+
+/* BOOLEAN:POINTER (ck-marshal.list:2) */
+extern void ck_marshal_BOOLEAN__POINTER (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+
+G_END_DECLS
+
+#endif /* __ck_marshal_MARSHAL_H__ */
+
diff --git a/src/ck-marshal.list b/src/ck-marshal.list
new file mode 100644
index 0000000..f9eed10
--- /dev/null
+++ b/src/ck-marshal.list
@@ -0,0 +1,2 @@
+VOID:UINT,STRING
+BOOLEAN:POINTER
diff --git a/src/ck-seat-glue.h b/src/ck-seat-glue.h
new file mode 100644
index 0000000..af823bc
--- /dev/null
+++ b/src/ck-seat-glue.h
@@ -0,0 +1,168 @@
+/* Generated by dbus-binding-tool; do not edit! */
+
+
+#ifndef __dbus_glib_marshal_ck_seat_MARSHAL_H__
+#define __dbus_glib_marshal_ck_seat_MARSHAL_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#ifdef G_ENABLE_DEBUG
+#define g_marshal_value_peek_boolean(v) g_value_get_boolean (v)
+#define g_marshal_value_peek_char(v) g_value_get_char (v)
+#define g_marshal_value_peek_uchar(v) g_value_get_uchar (v)
+#define g_marshal_value_peek_int(v) g_value_get_int (v)
+#define g_marshal_value_peek_uint(v) g_value_get_uint (v)
+#define g_marshal_value_peek_long(v) g_value_get_long (v)
+#define g_marshal_value_peek_ulong(v) g_value_get_ulong (v)
+#define g_marshal_value_peek_int64(v) g_value_get_int64 (v)
+#define g_marshal_value_peek_uint64(v) g_value_get_uint64 (v)
+#define g_marshal_value_peek_enum(v) g_value_get_enum (v)
+#define g_marshal_value_peek_flags(v) g_value_get_flags (v)
+#define g_marshal_value_peek_float(v) g_value_get_float (v)
+#define g_marshal_value_peek_double(v) g_value_get_double (v)
+#define g_marshal_value_peek_string(v) (char*) g_value_get_string (v)
+#define g_marshal_value_peek_param(v) g_value_get_param (v)
+#define g_marshal_value_peek_boxed(v) g_value_get_boxed (v)
+#define g_marshal_value_peek_pointer(v) g_value_get_pointer (v)
+#define g_marshal_value_peek_object(v) g_value_get_object (v)
+#else /* !G_ENABLE_DEBUG */
+/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API.
+ * Do not access GValues directly in your code. Instead, use the
+ * g_value_get_*() functions
+ */
+#define g_marshal_value_peek_boolean(v) (v)->data[0].v_int
+#define g_marshal_value_peek_char(v) (v)->data[0].v_int
+#define g_marshal_value_peek_uchar(v) (v)->data[0].v_uint
+#define g_marshal_value_peek_int(v) (v)->data[0].v_int
+#define g_marshal_value_peek_uint(v) (v)->data[0].v_uint
+#define g_marshal_value_peek_long(v) (v)->data[0].v_long
+#define g_marshal_value_peek_ulong(v) (v)->data[0].v_ulong
+#define g_marshal_value_peek_int64(v) (v)->data[0].v_int64
+#define g_marshal_value_peek_uint64(v) (v)->data[0].v_uint64
+#define g_marshal_value_peek_enum(v) (v)->data[0].v_long
+#define g_marshal_value_peek_flags(v) (v)->data[0].v_ulong
+#define g_marshal_value_peek_float(v) (v)->data[0].v_float
+#define g_marshal_value_peek_double(v) (v)->data[0].v_double
+#define g_marshal_value_peek_string(v) (v)->data[0].v_pointer
+#define g_marshal_value_peek_param(v) (v)->data[0].v_pointer
+#define g_marshal_value_peek_boxed(v) (v)->data[0].v_pointer
+#define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer
+#define g_marshal_value_peek_object(v) (v)->data[0].v_pointer
+#endif /* !G_ENABLE_DEBUG */
+
+
+/* BOOLEAN:POINTER,POINTER (/tmp/dbus-binding-tool-c-marshallers.NT25PT:1) */
+extern void dbus_glib_marshal_ck_seat_BOOLEAN__POINTER_POINTER (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+void
+dbus_glib_marshal_ck_seat_BOOLEAN__POINTER_POINTER (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data)
+{
+ typedef gboolean (*GMarshalFunc_BOOLEAN__POINTER_POINTER) (gpointer data1,
+ gpointer arg_1,
+ gpointer arg_2,
+ gpointer data2);
+ register GMarshalFunc_BOOLEAN__POINTER_POINTER callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+ gboolean v_return;
+
+ g_return_if_fail (return_value != NULL);
+ g_return_if_fail (n_param_values == 3);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_BOOLEAN__POINTER_POINTER) (marshal_data ? marshal_data : cc->callback);
+
+ v_return = callback (data1,
+ g_marshal_value_peek_pointer (param_values + 1),
+ g_marshal_value_peek_pointer (param_values + 2),
+ data2);
+
+ g_value_set_boolean (return_value, v_return);
+}
+
+/* NONE:BOXED,POINTER (/tmp/dbus-binding-tool-c-marshallers.NT25PT:2) */
+extern void dbus_glib_marshal_ck_seat_VOID__BOXED_POINTER (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+void
+dbus_glib_marshal_ck_seat_VOID__BOXED_POINTER (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__BOXED_POINTER) (gpointer data1,
+ gpointer arg_1,
+ gpointer arg_2,
+ gpointer data2);
+ register GMarshalFunc_VOID__BOXED_POINTER callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+
+ g_return_if_fail (n_param_values == 3);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__BOXED_POINTER) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_boxed (param_values + 1),
+ g_marshal_value_peek_pointer (param_values + 2),
+ data2);
+}
+#define dbus_glib_marshal_ck_seat_NONE__BOXED_POINTER dbus_glib_marshal_ck_seat_VOID__BOXED_POINTER
+
+G_END_DECLS
+
+#endif /* __dbus_glib_marshal_ck_seat_MARSHAL_H__ */
+
+#include <dbus/dbus-glib.h>
+static const DBusGMethodInfo dbus_glib_ck_seat_methods[] = {
+ { (GCallback) ck_seat_get_id, dbus_glib_marshal_ck_seat_BOOLEAN__POINTER_POINTER, 0 },
+ { (GCallback) ck_seat_get_sessions, dbus_glib_marshal_ck_seat_BOOLEAN__POINTER_POINTER, 53 },
+ { (GCallback) ck_seat_get_active_session, dbus_glib_marshal_ck_seat_BOOLEAN__POINTER_POINTER, 118 },
+ { (GCallback) ck_seat_can_activate_sessions, dbus_glib_marshal_ck_seat_BOOLEAN__POINTER_POINTER, 183 },
+ { (GCallback) ck_seat_activate_session, dbus_glib_marshal_ck_seat_NONE__BOXED_POINTER, 259 },
+};
+
+const DBusGObjectInfo dbus_glib_ck_seat_object_info = {
+ 0,
+ dbus_glib_ck_seat_methods,
+ 5,
+"org.freedesktop.ConsoleKit.Seat\0GetId\0S\0sid\0O\0F\0N\0o\0\0org.freedesktop.ConsoleKit.Seat\0GetSessions\0S\0sessions\0O\0F\0N\0ao\0\0org.freedesktop.ConsoleKit.Seat\0GetActiveSession\0S\0ssid\0O\0F\0N\0o\0\0org.freedesktop.ConsoleKit.Seat\0CanActivateSessions\0S\0can_activate\0O\0F\0N\0b\0\0org.freedesktop.ConsoleKit.Seat\0ActivateSession\0A\0ssid\0I\0o\0\0\0",
+"org.freedesktop.ConsoleKit.Seat\0ActiveSessionChanged\0org.freedesktop.ConsoleKit.Seat\0SessionAdded\0org.freedesktop.ConsoleKit.Seat\0SessionRemoved\0\0",
+"\0"
+};
+
diff --git a/src/ck-seat.c b/src/ck-seat.c
new file mode 100644
index 0000000..a77e0b1
--- /dev/null
+++ b/src/ck-seat.c
@@ -0,0 +1,889 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2006 William Jon McCann <mccann@jhu.edu>
+ *
+ * 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 <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#ifdef HAVE_PATHS_H
+#include <paths.h>
+#endif /* HAVE_PATHS_H */
+
+#ifndef _PATH_TTY
+#define _PATH_TTY "/dev/tty"
+#endif
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib-object.h>
+
+#define DBUS_API_SUBJECT_TO_CHANGE
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+
+#include "ck-seat.h"
+#include "ck-seat-glue.h"
+#include "ck-marshal.h"
+
+#include "ck-session.h"
+#include "ck-vt-monitor.h"
+
+#define CK_SEAT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), CK_TYPE_SEAT, CkSeatPrivate))
+
+#define CK_DBUS_PATH "/org/freedesktop/ConsoleKit"
+#define CK_DBUS_NAME "org.freedesktop.ConsoleKit"
+
+struct CkSeatPrivate
+{
+ char *id;
+ CkSeatKind kind;
+ GHashTable *sessions;
+
+ CkSession *active_session;
+
+ CkVtMonitor *vt_monitor;
+
+ DBusGConnection *connection;
+};
+
+enum {
+ ACTIVE_SESSION_CHANGED,
+ SESSION_ADDED,
+ SESSION_REMOVED,
+ LAST_SIGNAL
+};
+
+enum {
+ PROP_0,
+ PROP_ID,
+ PROP_KIND,
+};
+
+static guint signals [LAST_SIGNAL] = { 0, };
+
+static void ck_seat_class_init (CkSeatClass *klass);
+static void ck_seat_init (CkSeat *seat);
+static void ck_seat_finalize (GObject *object);
+
+G_DEFINE_TYPE (CkSeat, ck_seat, G_TYPE_OBJECT)
+
+GQuark
+ck_seat_error_quark (void)
+{
+ static GQuark ret = 0;
+ if (ret == 0) {
+ ret = g_quark_from_static_string ("ck_seat_error");
+ }
+
+ return ret;
+}
+
+#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
+
+GType
+ck_seat_kind_get_type (void)
+{
+ static GType etype = 0;
+
+ if (etype == 0) {
+ static const GEnumValue values[] = {
+ ENUM_ENTRY (CK_SEAT_KIND_STATIC, "Fixed single instance local seat"),
+ ENUM_ENTRY (CK_SEAT_KIND_DYNAMIC, "Transient seat"),
+ { 0, 0, 0 }
+ };
+
+ etype = g_enum_register_static ("CkSeatKindType", values);
+ }
+
+ return etype;
+}
+
+gboolean
+ck_seat_get_active_session (CkSeat *seat,
+ char **ssid,
+ GError **error)
+{
+ g_return_val_if_fail (CK_IS_SEAT (seat), FALSE);
+
+ if (seat->priv->active_session != NULL) {
+ ck_session_get_id (seat->priv->active_session, ssid, NULL);
+ } else {
+ if (ssid != NULL) {
+ *ssid = NULL;
+ }
+ }
+
+ return TRUE;
+}
+
+typedef struct
+{
+ gulong handler_id;
+ CkSeat *seat;
+ guint num;
+ DBusGMethodInvocation *context;
+} ActivateData;
+
+static void
+activated_cb (CkVtMonitor *vt_monitor,
+ guint num,
+ ActivateData *adata)
+{
+ if (adata->num == num) {
+ dbus_g_method_return (adata->context, TRUE);
+ } else {
+ GError *error;
+
+ error = g_error_new (CK_SEAT_ERROR,
+ CK_SEAT_ERROR_GENERAL,
+ _("Another session was activated while waiting"));
+ dbus_g_method_return_error (adata->context, error);
+ g_error_free (error);
+ }
+
+ g_signal_handler_disconnect (vt_monitor, adata->handler_id);
+}
+
+static gboolean
+_seat_activate_session (CkSeat *seat,
+ CkSession *session,
+ DBusGMethodInvocation *context)
+{
+ gboolean ret;
+ guint num;
+ char *device;
+ ActivateData *adata;
+ GError *vt_error;
+
+ device = NULL;
+ adata = NULL;
+ ret = FALSE;
+
+ /* for now, only support switching on static seat */
+ if (seat->priv->kind != CK_SEAT_KIND_STATIC) {
+ GError *error;
+ error = g_error_new (CK_SEAT_ERROR,
+ CK_SEAT_ERROR_GENERAL,
+ _("Activation is not supported for this kind of seat"));
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ goto out;
+ }
+
+ if (session == NULL) {
+ GError *error;
+ error = g_error_new (CK_SEAT_ERROR,
+ CK_SEAT_ERROR_GENERAL,
+ _("Unknown session id"));
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ goto out;
+ }
+
+ ck_session_get_display_device (session, &device, NULL);
+
+ if (device == NULL || (sscanf (device, _PATH_TTY "%u", &num) != 1)) {
+ GError *error;
+ error = g_error_new (CK_SEAT_ERROR,
+ CK_SEAT_ERROR_GENERAL,
+ _("Unable to activate session"));
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ goto out;
+ }
+
+ adata = g_new0 (ActivateData, 1);
+ adata->context = context;
+ adata->seat = seat;
+ adata->num = num;
+ adata->handler_id = g_signal_connect_data (seat->priv->vt_monitor,
+ "active-changed",
+ G_CALLBACK (activated_cb),
+ adata,
+ (GClosureNotify)g_free,
+ 0);
+
+
+ g_debug ("Attempting to activate VT %u", num);
+
+ vt_error = NULL;
+ ret = ck_vt_monitor_set_active (seat->priv->vt_monitor, num, &vt_error);
+ if (! ret) {
+ g_debug ("Unable to activate session: %s", vt_error->message);
+ dbus_g_method_return_error (context, vt_error);
+ g_signal_handler_disconnect (seat->priv->vt_monitor, adata->handler_id);
+ g_error_free (vt_error);
+ goto out;
+ }
+
+ out:
+ g_free (device);
+
+ return ret;
+}
+
+/*
+ Example:
+ dbus-send --system --dest=org.freedesktop.ConsoleKit \
+ --type=method_call --print-reply --reply-timeout=2000 \
+ /org/freedesktop/ConsoleKit/Seat1 \
+ org.freedesktop.ConsoleKit.Seat.ActivateSession \
+ objpath:/org/freedesktop/ConsoleKit/Session2
+*/
+
+gboolean
+ck_seat_activate_session (CkSeat *seat,
+ const char *ssid,
+ DBusGMethodInvocation *context)
+{
+ CkSession *session;
+ gboolean ret;
+
+ g_return_val_if_fail (CK_IS_SEAT (seat), FALSE);
+
+ session = NULL;
+
+ if (ssid != NULL) {
+ session = g_hash_table_lookup (seat->priv->sessions, ssid);
+ }
+
+ ret = _seat_activate_session (seat, session, context);
+
+ return ret;
+}
+
+static gboolean
+match_session_display_device (const char *key,
+ CkSession *session,
+ const char *display_device)
+{
+ char *device;
+ gboolean ret;
+
+ device = NULL;
+ ret = FALSE;
+
+ if (session == NULL) {
+ goto out;
+ }
+
+ ck_session_get_display_device (session, &device, NULL);
+
+ if (device != NULL
+ && display_device != NULL
+ && strcmp (device, display_device) == 0) {
+ g_debug ("Matched display-device %s to %s", display_device, key);
+ ret = TRUE;
+ }
+out:
+
+ g_free (device);
+
+ return ret;
+}
+
+static gboolean
+match_session_x11_display_device (const char *key,
+ CkSession *session,
+ const char *x11_display_device)
+{
+ char *device;
+ gboolean ret;
+
+ device = NULL;
+ ret = FALSE;
+
+ if (session == NULL) {
+ goto out;
+ }
+
+ ck_session_get_x11_display_device (session, &device, NULL);
+
+ if (device != NULL
+ && x11_display_device != NULL
+ && strcmp (device, x11_display_device) == 0) {
+ g_debug ("Matched x11-display-device %s to %s", x11_display_device, key);
+ ret = TRUE;
+ }
+out:
+
+ g_free (device);
+
+ return ret;
+}
+
+typedef struct
+{
+ GHRFunc predicate;
+ gpointer user_data;
+ GList *list;
+} HashTableFindAllData;
+
+static void
+find_all_func (gpointer key,
+ gpointer value,
+ HashTableFindAllData *data)
+{
+ gboolean res;
+
+ res = data->predicate (key, value, data->user_data);
+ if (res) {
+ data->list = g_list_prepend (data->list, value);
+ }
+}
+
+static GList *
+hash_table_find_all (GHashTable *hash_table,
+ GHRFunc predicate,
+ gpointer user_data)
+{
+ HashTableFindAllData *data;
+ GList *list;
+
+ data = g_new0 (HashTableFindAllData, 1);
+ data->predicate = predicate;
+ data->user_data = user_data;
+ g_hash_table_foreach (hash_table, (GHFunc) find_all_func, data);
+ list = data->list;
+ g_free (data);
+ return list;
+}
+
+static GList *
+find_sessions_for_display_device (CkSeat *seat,
+ const char *device)
+{
+ GList *sessions;
+
+ sessions = hash_table_find_all (seat->priv->sessions,
+ (GHRFunc) match_session_display_device,
+ (gpointer) device);
+ return sessions;
+}
+
+static GList *
+find_sessions_for_x11_display_device (CkSeat *seat,
+ const char *device)
+{
+ GList *sessions;
+
+ sessions = hash_table_find_all (seat->priv->sessions,
+ (GHRFunc) match_session_x11_display_device,
+ (gpointer) device);
+ return sessions;
+}
+
+static int
+sort_sessions_by_age (CkSession *a,
+ CkSession *b)
+{
+ char *iso_a;
+ char *iso_b;
+ int ret;
+
+ ck_session_get_creation_time (a, &iso_a, NULL);
+ ck_session_get_creation_time (b, &iso_b, NULL);
+
+ ret = strcmp (iso_a, iso_b);
+
+ g_free (iso_a);
+ g_free (iso_b);
+
+ return ret;
+}
+
+static CkSession *
+find_oldest_session (GList *sessions)
+{
+
+ sessions = g_list_sort (sessions,
+ (GCompareFunc) sort_sessions_by_age);
+ return sessions->data;
+}
+
+static CkSession *
+find_session_for_display_device (CkSeat *seat,
+ const char *device)
+{
+ GList *sessions;
+ CkSession *session;
+
+ sessions = find_sessions_for_display_device (seat, device);
+ if (sessions == NULL) {
+ sessions = find_sessions_for_x11_display_device (seat, device);
+ }
+
+ if (sessions == NULL) {
+ return NULL;
+ }
+
+ if (g_list_length (sessions) == 1) {
+ session = sessions->data;
+ } else {
+ session = find_oldest_session (sessions);
+ }
+
+ g_list_free (sessions);
+
+ return session;
+}
+
+static void
+change_active_session (CkSeat *seat,
+ CkSession *session)
+{
+ char *ssid;
+
+ if (seat->priv->active_session == session) {
+ return;
+ }
+
+ if (seat->priv->active_session != NULL) {
+ ck_session_set_active (seat->priv->active_session, FALSE, NULL);
+ g_object_unref (seat->priv->active_session);
+ }
+
+ seat->priv->active_session = session;
+
+ ssid = NULL;
+ if (session != NULL) {
+ g_object_ref (session);
+ ck_session_get_id (session, &ssid, NULL);
+ ck_session_set_active (session, TRUE, NULL);
+ }
+
+ g_debug ("Active session changed: %s", ssid);
+
+ g_signal_emit (seat, signals [ACTIVE_SESSION_CHANGED], 0, ssid);
+
+ g_free (ssid);
+}
+
+static void
+update_active_vt (CkSeat *seat,
+ guint num)
+{
+ CkSession *session;
+ char *device;
+
+ device = g_strdup_printf (_PATH_TTY "%u", num);
+
+ g_debug ("Active device: %s", device);
+
+ session = find_session_for_display_device (seat, device);
+ change_active_session (seat, session);
+
+ g_free (device);
+}
+
+static void
+maybe_update_active_session (CkSeat *seat)
+{
+ guint num;
+
+ if (seat->priv->kind != CK_SEAT_KIND_STATIC) {
+ return;
+ }
+
+ if (ck_vt_monitor_get_active (seat->priv->vt_monitor, &num, NULL)) {
+ update_active_vt (seat, num);
+ }
+}
+
+static gboolean
+session_activate (CkSession *session,
+ DBusGMethodInvocation *context,
+ CkSeat *seat)
+{
+ _seat_activate_session (seat, session, context);
+
+ /* always return TRUE to indicate that the signal was handled */
+ return TRUE;
+}
+
+gboolean
+ck_seat_remove_session (CkSeat *seat,
+ CkSession *session,
+ GError **error)
+{
+ char *ssid;
+ gboolean ret;
+
+ g_return_val_if_fail (CK_IS_SEAT (seat), FALSE);
+
+ ret = FALSE;
+ ssid = NULL;
+ ck_session_get_id (session, &ssid, NULL);
+
+ if (g_hash_table_lookup (seat->priv->sessions, ssid) == NULL) {
+ g_debug ("Session %s is not attached to seat %s", ssid, seat->priv->id);
+ g_set_error (error,
+ CK_SEAT_ERROR,
+ CK_SEAT_ERROR_GENERAL,
+ _("Session is not attached to this seat"));
+ goto out;
+ }
+
+ g_signal_handlers_disconnect_by_func (session, session_activate, seat);
+
+ g_debug ("Emitting removed signal: %s", ssid);
+
+ g_signal_emit (seat, signals [SESSION_REMOVED], 0, ssid);
+
+ g_hash_table_remove (seat->priv->sessions, ssid);
+
+ /* try to change the active session */
+ maybe_update_active_session (seat);
+
+ ret = TRUE;
+ out:
+ g_free (ssid);
+
+ return ret;
+}
+
+gboolean
+ck_seat_add_session (CkSeat *seat,
+ CkSession *session,
+ GError **error)
+{
+ char *ssid;
+
+ g_return_val_if_fail (CK_IS_SEAT (seat), FALSE);
+
+ ck_session_get_id (session, &ssid, NULL);
+
+ g_hash_table_insert (seat->priv->sessions, g_strdup (ssid), g_object_ref (session));
+
+ ck_session_set_seat_id (session, seat->priv->id, NULL);
+
+ g_signal_connect_object (session, "activate", G_CALLBACK (session_activate), seat, 0);
+ /* FIXME: attach to property notify signals? */
+
+ g_debug ("Emitting added signal: %s", ssid);
+
+ g_signal_emit (seat, signals [SESSION_ADDED], 0, ssid);
+
+ maybe_update_active_session (seat);
+
+ g_free (ssid);
+
+ return TRUE;
+}
+
+gboolean
+ck_seat_can_activate_sessions (CkSeat *seat,
+ gboolean *can_activate,
+ GError **error)
+{
+ g_return_val_if_fail (CK_IS_SEAT (seat), FALSE);
+
+ if (can_activate != NULL) {
+ *can_activate = (seat->priv->kind == CK_SEAT_KIND_STATIC);
+ }
+
+ return TRUE;
+}
+
+gboolean
+ck_seat_get_kind (CkSeat *seat,
+ CkSeatKind *kind,
+ GError **error)
+{
+ g_return_val_if_fail (CK_IS_SEAT (seat), FALSE);
+
+ if (kind != NULL) {
+ *kind = seat->priv->kind;
+ }
+
+ return TRUE;
+}
+
+gboolean
+ck_seat_get_id (CkSeat *seat,
+ char **id,
+ GError **error)
+{
+ g_return_val_if_fail (CK_IS_SEAT (seat), FALSE);
+
+ if (id != NULL) {
+ *id = g_strdup (seat->priv->id);
+ }
+
+ return TRUE;
+}
+
+static void
+active_vt_changed (CkVtMonitor *vt_monitor,
+ guint num,
+ CkSeat *seat)
+{
+ g_debug ("Active vt changed: %u", num);
+
+ update_active_vt (seat, num);
+}
+
+static gboolean
+register_seat (CkSeat *seat)
+{
+ GError *error = NULL;
+
+ error = NULL;
+ seat->priv->connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error);
+ if (seat->priv->connection == NULL) {
+ if (error != NULL) {
+ g_critical ("error getting system bus: %s", error->message);
+ g_error_free (error);
+ }
+ return FALSE;
+ }
+
+ dbus_g_connection_register_g_object (seat->priv->connection, seat->priv->id, G_OBJECT (seat));
+
+ return TRUE;
+}
+
+static void
+listify_session_ids (char *id,
+ CkSession *session,
+ GPtrArray **array)
+{
+ g_ptr_array_add (*array, g_strdup (id));
+}
+
+gboolean
+ck_seat_get_sessions (CkSeat *seat,
+ GPtrArray **sessions,
+ GError **error)
+{
+ g_return_val_if_fail (CK_IS_SEAT (seat), FALSE);
+
+ if (sessions == NULL) {
+ return FALSE;
+ }
+
+ *sessions = g_ptr_array_new ();
+ g_hash_table_foreach (seat->priv->sessions, (GHFunc)listify_session_ids, sessions);
+
+ return TRUE;
+}
+
+static void
+_ck_seat_set_id (CkSeat *seat,
+ const char *id)
+{
+ g_free (seat->priv->id);
+ seat->priv->id = g_strdup (id);
+}
+
+static void
+_ck_seat_set_kind (CkSeat *seat,
+ CkSeatKind kind)
+{
+ seat->priv->kind = kind;
+}
+
+static void
+ck_seat_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ CkSeat *self;
+
+ self = CK_SEAT (object);
+
+ switch (prop_id) {
+ case PROP_ID:
+ _ck_seat_set_id (self, g_value_get_string (value));
+ break;
+ case PROP_KIND:
+ _ck_seat_set_kind (self, g_value_get_enum (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+ck_seat_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ CkSeat *self;
+
+ self = CK_SEAT (object);
+
+ switch (prop_id) {
+ case PROP_ID:
+ g_value_set_string (value, self->priv->id);
+ break;
+ case PROP_KIND:
+ g_value_set_string (value, self->priv->id);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static GObject *
+ck_seat_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_properties)
+{
+ CkSeat *seat;
+ CkSeatClass *klass;
+
+ klass = CK_SEAT_CLASS (g_type_class_peek (CK_TYPE_SEAT));
+
+ seat = CK_SEAT (G_OBJECT_CLASS (ck_seat_parent_class)->constructor (type,
+ n_construct_properties,
+ construct_properties));
+
+ if (seat->priv->kind == CK_SEAT_KIND_STATIC) {
+ seat->priv->vt_monitor = ck_vt_monitor_new ();
+ g_signal_connect (seat->priv->vt_monitor, "active-changed", G_CALLBACK (active_vt_changed), seat);
+ }
+
+ return G_OBJECT (seat);
+}
+
+static void
+ck_seat_class_init (CkSeatClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->get_property = ck_seat_get_property;
+ object_class->set_property = ck_seat_set_property;
+ object_class->constructor = ck_seat_constructor;
+ object_class->finalize = ck_seat_finalize;
+
+ signals [ACTIVE_SESSION_CHANGED] = g_signal_new ("active-session-changed",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (CkSeatClass, active_session_changed),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE,
+ 1, G_TYPE_STRING);
+ signals [SESSION_ADDED] = g_signal_new ("session-added",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (CkSeatClass, session_added),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE,
+ 1, G_TYPE_STRING);
+ signals [SESSION_REMOVED] = g_signal_new ("session-removed",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (CkSeatClass, session_removed),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE,
+ 1, G_TYPE_STRING);
+
+ g_object_class_install_property (object_class,
+ PROP_ID,
+ g_param_spec_string ("id",
+ "id",
+ "id",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ g_object_class_install_property (object_class,
+ PROP_KIND,
+ g_param_spec_enum ("kind",
+ "kind",
+ "kind",
+ CK_TYPE_SEAT_KIND,
+ CK_SEAT_KIND_DYNAMIC,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_type_class_add_private (klass, sizeof (CkSeatPrivate));
+
+ dbus_g_object_type_install_info (CK_TYPE_SEAT, &dbus_glib_ck_seat_object_info);
+}
+
+static void
+ck_seat_init (CkSeat *seat)
+{
+ seat->priv = CK_SEAT_GET_PRIVATE (seat);
+
+ seat->priv->sessions = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ (GDestroyNotify) g_object_unref);
+}
+
+static void
+ck_seat_finalize (GObject *object)
+{
+ CkSeat *seat;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (CK_IS_SEAT (object));
+
+ seat = CK_SEAT (object);
+
+ g_return_if_fail (seat->priv != NULL);
+
+ if (seat->priv->vt_monitor != NULL) {
+ g_object_unref (seat->priv->vt_monitor);
+ }
+
+ if (seat->priv->active_session != NULL) {
+ g_object_unref (seat->priv->active_session);
+ }
+
+ g_hash_table_destroy (seat->priv->sessions);
+ g_free (seat->priv->id);
+
+ G_OBJECT_CLASS (ck_seat_parent_class)->finalize (object);
+}
+
+CkSeat *
+ck_seat_new (const char *sid,
+ CkSeatKind kind)
+{
+ GObject *object;
+ gboolean res;
+
+ object = g_object_new (CK_TYPE_SEAT,
+ "id", sid,
+ "kind", kind,
+ NULL);
+
+ res = register_seat (CK_SEAT (object));
+ if (! res) {
+ g_object_unref (object);
+ return NULL;
+ }
+
+ return CK_SEAT (object);
+}
diff --git a/src/ck-seat.h b/src/ck-seat.h
new file mode 100644
index 0000000..4707bf9
--- /dev/null
+++ b/src/ck-seat.h
@@ -0,0 +1,111 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2006 William Jon McCann <mccann@jhu.edu>
+ *
+ * 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.
+ *
+ */
+
+#ifndef __CK_SEAT_H
+#define __CK_SEAT_H
+
+#include <glib-object.h>
+#include <dbus/dbus-glib.h>
+
+#include "ck-session.h"
+
+G_BEGIN_DECLS
+
+#define CK_TYPE_SEAT (ck_seat_get_type ())
+#define CK_SEAT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CK_TYPE_SEAT, CkSeat))
+#define CK_SEAT_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), CK_TYPE_SEAT, CkSeatClass))
+#define CK_IS_SEAT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), CK_TYPE_SEAT))
+#define CK_IS_SEAT_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), CK_TYPE_SEAT))
+#define CK_SEAT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), CK_TYPE_SEAT, CkSeatClass))
+
+typedef struct CkSeatPrivate CkSeatPrivate;
+
+typedef struct
+{
+ GObject parent;
+ CkSeatPrivate *priv;
+} CkSeat;
+
+typedef struct
+{
+ GObjectClass parent_class;
+
+ void (* active_session_changed) (CkSeat *seat,
+ const char *ssid);
+ void (* session_added) (CkSeat *seat,
+ const char *ssid);
+ void (* session_removed) (CkSeat *seat,
+ const char *ssid);
+} CkSeatClass;
+
+typedef enum
+{
+ CK_SEAT_KIND_STATIC,
+ CK_SEAT_KIND_DYNAMIC,
+} CkSeatKind;
+
+GType ck_seat_kind_get_type (void);
+#define CK_TYPE_SEAT_KIND (ck_seat_kind_get_type ())
+
+typedef enum
+{
+ CK_SEAT_ERROR_GENERAL
+} CkSeatError;
+
+#define CK_SEAT_ERROR ck_seat_error_quark ()
+
+GQuark ck_seat_error_quark (void);
+GType ck_seat_get_type (void);
+CkSeat * ck_seat_new (const char *sid,
+ CkSeatKind kind);
+gboolean ck_seat_get_kind (CkSeat *seat,
+ CkSeatKind *kind,
+ GError **error);
+gboolean ck_seat_add_session (CkSeat *seat,
+ CkSession *session,
+ GError **error);
+gboolean ck_seat_remove_session (CkSeat *seat,
+ CkSession *session,
+ GError **error);
+gboolean ck_seat_set_active_session (CkSeat *seat,
+ CkSession *session,
+ GError **error);
+
+/* exported methods */
+gboolean ck_seat_get_id (CkSeat *seat,
+ char **sid,
+ GError **error);
+gboolean ck_seat_get_sessions (CkSeat *seat,
+ GPtrArray **sessions,
+ GError **error);
+gboolean ck_seat_get_active_session (CkSeat *seat,
+ char **ssid,
+ GError **error);
+
+gboolean ck_seat_can_activate_sessions (CkSeat *seat,
+ gboolean *can_activate,
+ GError **error);
+gboolean ck_seat_activate_session (CkSeat *seat,
+ const char *ssid,
+ DBusGMethodInvocation *context);
+
+G_END_DECLS
+
+#endif /* __CK_SEAT_H */
diff --git a/src/ck-seat.xml b/src/ck-seat.xml
new file mode 100644
index 0000000..5fda057
--- /dev/null
+++ b/src/ck-seat.xml
@@ -0,0 +1,30 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<node>
+ <interface name="org.freedesktop.ConsoleKit.Seat">
+ <method name="GetId">
+ <arg name="sid" direction="out" type="o"/>
+ </method>
+ <method name="GetSessions">
+ <arg name="sessions" direction="out" type="ao"/>
+ </method>
+ <method name="GetActiveSession">
+ <arg name="ssid" direction="out" type="o"/>
+ </method>
+ <method name="CanActivateSessions">
+ <arg name="can_activate" direction="out" type="b"/>
+ </method>
+ <method name="ActivateSession">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg name="ssid" direction="in" type="o"/>
+ </method>
+ <signal name="ActiveSessionChanged">
+ <arg name="ssid" type="o"/>
+ </signal>
+ <signal name="SessionAdded">
+ <arg name="ssid" type="o"/>
+ </signal>
+ <signal name="SessionRemoved">
+ <arg name="ssid" type="o"/>
+ </signal>
+ </interface>
+</node>
diff --git a/src/ck-session-glue.h b/src/ck-session-glue.h
new file mode 100644
index 0000000..9323c73
--- /dev/null
+++ b/src/ck-session-glue.h
@@ -0,0 +1,185 @@
+/* Generated by dbus-binding-tool; do not edit! */
+
+
+#ifndef __dbus_glib_marshal_ck_session_MARSHAL_H__
+#define __dbus_glib_marshal_ck_session_MARSHAL_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#ifdef G_ENABLE_DEBUG
+#define g_marshal_value_peek_boolean(v) g_value_get_boolean (v)
+#define g_marshal_value_peek_char(v) g_value_get_char (v)
+#define g_marshal_value_peek_uchar(v) g_value_get_uchar (v)
+#define g_marshal_value_peek_int(v) g_value_get_int (v)
+#define g_marshal_value_peek_uint(v) g_value_get_uint (v)
+#define g_marshal_value_peek_long(v) g_value_get_long (v)
+#define g_marshal_value_peek_ulong(v) g_value_get_ulong (v)
+#define g_marshal_value_peek_int64(v) g_value_get_int64 (v)
+#define g_marshal_value_peek_uint64(v) g_value_get_uint64 (v)
+#define g_marshal_value_peek_enum(v) g_value_get_enum (v)
+#define g_marshal_value_peek_flags(v) g_value_get_flags (v)
+#define g_marshal_value_peek_float(v) g_value_get_float (v)
+#define g_marshal_value_peek_double(v) g_value_get_double (v)
+#define g_marshal_value_peek_string(v) (char*) g_value_get_string (v)
+#define g_marshal_value_peek_param(v) g_value_get_param (v)
+#define g_marshal_value_peek_boxed(v) g_value_get_boxed (v)
+#define g_marshal_value_peek_pointer(v) g_value_get_pointer (v)
+#define g_marshal_value_peek_object(v) g_value_get_object (v)
+#else /* !G_ENABLE_DEBUG */
+/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API.
+ * Do not access GValues directly in your code. Instead, use the
+ * g_value_get_*() functions
+ */
+#define g_marshal_value_peek_boolean(v) (v)->data[0].v_int
+#define g_marshal_value_peek_char(v) (v)->data[0].v_int
+#define g_marshal_value_peek_uchar(v) (v)->data[0].v_uint
+#define g_marshal_value_peek_int(v) (v)->data[0].v_int
+#define g_marshal_value_peek_uint(v) (v)->data[0].v_uint
+#define g_marshal_value_peek_long(v) (v)->data[0].v_long
+#define g_marshal_value_peek_ulong(v) (v)->data[0].v_ulong
+#define g_marshal_value_peek_int64(v) (v)->data[0].v_int64
+#define g_marshal_value_peek_uint64(v) (v)->data[0].v_uint64
+#define g_marshal_value_peek_enum(v) (v)->data[0].v_long
+#define g_marshal_value_peek_flags(v) (v)->data[0].v_ulong
+#define g_marshal_value_peek_float(v) (v)->data[0].v_float
+#define g_marshal_value_peek_double(v) (v)->data[0].v_double
+#define g_marshal_value_peek_string(v) (v)->data[0].v_pointer
+#define g_marshal_value_peek_param(v) (v)->data[0].v_pointer
+#define g_marshal_value_peek_boxed(v) (v)->data[0].v_pointer
+#define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer
+#define g_marshal_value_peek_object(v) (v)->data[0].v_pointer
+#endif /* !G_ENABLE_DEBUG */
+
+
+/* BOOLEAN:POINTER,POINTER (/tmp/dbus-binding-tool-c-marshallers.CVG6PT:1) */
+extern void dbus_glib_marshal_ck_session_BOOLEAN__POINTER_POINTER (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+void
+dbus_glib_marshal_ck_session_BOOLEAN__POINTER_POINTER (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data)
+{
+ typedef gboolean (*GMarshalFunc_BOOLEAN__POINTER_POINTER) (gpointer data1,
+ gpointer arg_1,
+ gpointer arg_2,
+ gpointer data2);
+ register GMarshalFunc_BOOLEAN__POINTER_POINTER callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+ gboolean v_return;
+
+ g_return_if_fail (return_value != NULL);
+ g_return_if_fail (n_param_values == 3);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_BOOLEAN__POINTER_POINTER) (marshal_data ? marshal_data : cc->callback);
+
+ v_return = callback (data1,
+ g_marshal_value_peek_pointer (param_values + 1),
+ g_marshal_value_peek_pointer (param_values + 2),
+ data2);
+
+ g_value_set_boolean (return_value, v_return);
+}
+
+/* NONE:BOOLEAN,POINTER (/tmp/dbus-binding-tool-c-marshallers.CVG6PT:2) */
+extern void dbus_glib_marshal_ck_session_VOID__BOOLEAN_POINTER (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+void
+dbus_glib_marshal_ck_session_VOID__BOOLEAN_POINTER (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__BOOLEAN_POINTER) (gpointer data1,
+ gboolean arg_1,
+ gpointer arg_2,
+ gpointer data2);
+ register GMarshalFunc_VOID__BOOLEAN_POINTER callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+
+ g_return_if_fail (n_param_values == 3);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__BOOLEAN_POINTER) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_boolean (param_values + 1),
+ g_marshal_value_peek_pointer (param_values + 2),
+ data2);
+}
+#define dbus_glib_marshal_ck_session_NONE__BOOLEAN_POINTER dbus_glib_marshal_ck_session_VOID__BOOLEAN_POINTER
+
+/* NONE:POINTER (/tmp/dbus-binding-tool-c-marshallers.CVG6PT:3) */
+#define dbus_glib_marshal_ck_session_VOID__POINTER g_cclosure_marshal_VOID__POINTER
+#define dbus_glib_marshal_ck_session_NONE__POINTER dbus_glib_marshal_ck_session_VOID__POINTER
+
+G_END_DECLS
+
+#endif /* __dbus_glib_marshal_ck_session_MARSHAL_H__ */
+
+#include <dbus/dbus-glib.h>
+static const DBusGMethodInfo dbus_glib_ck_session_methods[] = {
+ { (GCallback) ck_session_get_id, dbus_glib_marshal_ck_session_BOOLEAN__POINTER_POINTER, 0 },
+ { (GCallback) ck_session_get_seat_id, dbus_glib_marshal_ck_session_BOOLEAN__POINTER_POINTER, 57 },
+ { (GCallback) ck_session_get_session_type, dbus_glib_marshal_ck_session_BOOLEAN__POINTER_POINTER, 117 },
+ { (GCallback) ck_session_get_user, dbus_glib_marshal_ck_session_BOOLEAN__POINTER_POINTER, 183 },
+ { (GCallback) ck_session_get_unix_user, dbus_glib_marshal_ck_session_BOOLEAN__POINTER_POINTER, 241 },
+ { (GCallback) ck_session_get_x11_display, dbus_glib_marshal_ck_session_BOOLEAN__POINTER_POINTER, 303 },
+ { (GCallback) ck_session_get_x11_display_device, dbus_glib_marshal_ck_session_BOOLEAN__POINTER_POINTER, 371 },
+ { (GCallback) ck_session_get_display_device, dbus_glib_marshal_ck_session_BOOLEAN__POINTER_POINTER, 456 },
+ { (GCallback) ck_session_get_remote_host_name, dbus_glib_marshal_ck_session_BOOLEAN__POINTER_POINTER, 534 },
+ { (GCallback) ck_session_is_active, dbus_glib_marshal_ck_session_BOOLEAN__POINTER_POINTER, 615 },
+ { (GCallback) ck_session_is_local, dbus_glib_marshal_ck_session_BOOLEAN__POINTER_POINTER, 677 },
+ { (GCallback) ck_session_get_creation_time, dbus_glib_marshal_ck_session_BOOLEAN__POINTER_POINTER, 737 },
+ { (GCallback) ck_session_activate, dbus_glib_marshal_ck_session_NONE__POINTER, 816 },
+ { (GCallback) ck_session_lock, dbus_glib_marshal_ck_session_NONE__POINTER, 863 },
+ { (GCallback) ck_session_unlock, dbus_glib_marshal_ck_session_NONE__POINTER, 906 },
+ { (GCallback) ck_session_get_idle_hint, dbus_glib_marshal_ck_session_BOOLEAN__POINTER_POINTER, 951 },
+ { (GCallback) ck_session_get_idle_since_hint, dbus_glib_marshal_ck_session_BOOLEAN__POINTER_POINTER, 1019 },
+ { (GCallback) ck_session_set_idle_hint, dbus_glib_marshal_ck_session_NONE__BOOLEAN_POINTER, 1099 },
+};
+
+const DBusGObjectInfo dbus_glib_ck_session_object_info = {
+ 0,
+ dbus_glib_ck_session_methods,
+ 18,
+"org.freedesktop.ConsoleKit.Session\0GetId\0S\0ssid\0O\0F\0N\0o\0\0org.freedesktop.ConsoleKit.Session\0GetSeatId\0S\0sid\0O\0F\0N\0o\0\0org.freedesktop.ConsoleKit.Session\0GetSessionType\0S\0type\0O\0F\0N\0s\0\0org.freedesktop.ConsoleKit.Session\0GetUser\0S\0uid\0O\0F\0N\0i\0\0org.freedesktop.ConsoleKit.Session\0GetUnixUser\0S\0uid\0O\0F\0N\0i\0\0org.freedesktop.ConsoleKit.Session\0GetX11Display\0S\0display\0O\0F\0N\0s\0\0org.freedesktop.ConsoleKit.Session\0GetX11DisplayDevice\0S\0x11_display_device\0O\0F\0N\0s\0\0org.freedesktop.ConsoleKit.Session\0GetDisplayDevice\0S\0display_device\0O\0F\0N\0s\0\0org.freedesktop.ConsoleKit.Session\0GetRemoteHostName\0S\0remote_host_name\0O\0F\0N\0s\0\0org.freedesktop.ConsoleKit.Session\0IsActive\0S\0active\0O\0F\0N\0b\0\0org.freedesktop.ConsoleKit.Session\0IsLocal\0S\0local\0O\0F\0N\0b\0\0org.freedesktop.ConsoleKit.Session\0GetCreationTime\0S\0iso8601_datetime\0O\0F\0N\0s\0\0org.freedesktop.ConsoleKit.Session\0Activate\0A\0\0org.freedesktop.ConsoleKit.Session\0Lock\0A\0\0org.freedesktop.ConsoleKit.Session\0Unlock\0A\0\0org.freedesktop.ConsoleKit.Session\0GetIdleHint\0S\0idle_hint\0O\0F\0N\0b\0\0org.freedesktop.ConsoleKit.Session\0GetIdleSinceHint\0S\0iso8601_datetime\0O\0F\0N\0s\0\0org.freedesktop.ConsoleKit.Session\0SetIdleHint\0A\0idle_hint\0I\0b\0\0\0",
+"org.freedesktop.ConsoleKit.Session\0ActiveChanged\0org.freedesktop.ConsoleKit.Session\0IdleHintChanged\0org.freedesktop.ConsoleKit.Session\0Lock\0org.freedesktop.ConsoleKit.Session\0Unlock\0\0",
+"org.freedesktop.ConsoleKit.Session\0unix-user\0org.freedesktop.ConsoleKit.Session\0user\0org.freedesktop.ConsoleKit.Session\0session-type\0org.freedesktop.ConsoleKit.Session\0remote-host-name\0org.freedesktop.ConsoleKit.Session\0display-device\0org.freedesktop.ConsoleKit.Session\0x11-display\0org.freedesktop.ConsoleKit.Session\0x11-display-device\0org.freedesktop.ConsoleKit.Session\0active\0org.freedesktop.ConsoleKit.Session\0is-local\0org.freedesktop.ConsoleKit.Session\0idle-hint\0\0"
+};
+
diff --git a/src/ck-session.c b/src/ck-session.c
new file mode 100644
index 0000000..c4e286a
--- /dev/null
+++ b/src/ck-session.c
@@ -0,0 +1,1208 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2006-2007 William Jon McCann <mccann@jhu.edu>
+ *
+ * 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 <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+#include <string.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+#include <glib-object.h>
+#define DBUS_API_SUBJECT_TO_CHANGE
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+
+#include "ck-tty-idle-monitor.h"
+#include "ck-session.h"
+#include "ck-session-glue.h"
+#include "ck-marshal.h"
+
+#define CK_SESSION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), CK_TYPE_SESSION, CkSessionPrivate))
+
+#define CK_DBUS_PATH "/org/freedesktop/ConsoleKit"
+#define CK_DBUS_NAME "org.freedesktop.ConsoleKit"
+
+#define IDLE_TIME_SECS 60
+
+struct CkSessionPrivate
+{
+ char *id;
+ char *cookie;
+ char *seat_id;
+
+ char *session_type;
+ char *display_device;
+ char *x11_display_device;
+ char *x11_display;
+ char *remote_host_name;
+ guint uid;
+
+ gboolean active;
+ gboolean is_local;
+
+ GTimeVal creation_time;
+
+ CkTtyIdleMonitor *idle_monitor;
+
+ gboolean idle_hint;
+ GTimeVal idle_since_hint;
+
+ DBusGConnection *connection;
+ DBusGProxy *bus_proxy;
+};
+
+enum {
+ ACTIVATE,
+ LOCK,
+ UNLOCK,
+ ACTIVE_CHANGED,
+ IDLE_HINT_CHANGED,
+ LAST_SIGNAL
+};
+
+enum {
+ PROP_0,
+ PROP_ID,
+ PROP_COOKIE,
+ PROP_USER,
+ PROP_UNIX_USER,
+ PROP_X11_DISPLAY,
+ PROP_X11_DISPLAY_DEVICE,
+ PROP_DISPLAY_DEVICE,
+ PROP_SESSION_TYPE,
+ PROP_REMOTE_HOST_NAME,
+ PROP_IS_LOCAL,
+ PROP_ACTIVE,
+ PROP_IDLE_HINT,
+};
+
+static guint signals [LAST_SIGNAL] = { 0, };
+
+static void ck_session_class_init (CkSessionClass *klass);
+static void ck_session_init (CkSession *session);
+static void ck_session_finalize (GObject *object);
+
+G_DEFINE_TYPE (CkSession, ck_session, G_TYPE_OBJECT)
+
+GQuark
+ck_session_error_quark (void)
+{
+ static GQuark ret = 0;
+ if (ret == 0) {
+ ret = g_quark_from_static_string ("ck_session_error");
+ }
+
+ return ret;
+}
+
+static gboolean
+register_session (CkSession *session)
+{
+ GError *error = NULL;
+
+ error = NULL;
+ session->priv->connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error);
+ if (session->priv->connection == NULL) {
+ if (error != NULL) {
+ g_critical ("error getting system bus: %s", error->message);
+ g_error_free (error);
+ }
+ return FALSE;
+ }
+
+ session->priv->bus_proxy = dbus_g_proxy_new_for_name (session->priv->connection,
+ DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS);
+
+ dbus_g_connection_register_g_object (session->priv->connection, session->priv->id, G_OBJECT (session));
+
+ return TRUE;
+}
+
+/*
+ lock and unlock are separate functions because:
+ 1. we don't maintain state for locked
+ 2. so security policy can be handled separately
+*/
+gboolean
+ck_session_lock (CkSession *session,
+ DBusGMethodInvocation *context)
+{
+ g_return_val_if_fail (CK_IS_SESSION (session), FALSE);
+
+ g_debug ("Emitting lock for session %s", session->priv->id);
+ g_signal_emit (session, signals [LOCK], 0);
+
+ dbus_g_method_return (context);
+
+ return TRUE;
+}
+
+gboolean
+ck_session_unlock (CkSession *session,
+ DBusGMethodInvocation *context)
+{
+ g_return_val_if_fail (CK_IS_SESSION (session), FALSE);
+
+ g_debug ("Emitting unlock for session %s", session->priv->id);
+ g_signal_emit (session, signals [UNLOCK], 0);
+
+ dbus_g_method_return (context);
+
+ return TRUE;
+}
+
+/* adapted from PolicyKit */
+static gboolean
+get_caller_info (CkSession *session,
+ const char *sender,
+ uid_t *calling_uid,
+ pid_t *calling_pid)
+{
+ gboolean res;
+ GError *error = NULL;
+
+ res = FALSE;
+
+ if (sender == NULL) {
+ goto out;
+ }
+
+ if (! dbus_g_proxy_call (session->priv->bus_proxy, "GetConnectionUnixUser", &error,
+ G_TYPE_STRING, sender,
+ G_TYPE_INVALID,
+ G_TYPE_UINT, calling_uid,
+ G_TYPE_INVALID)) {
+ g_warning ("GetConnectionUnixUser() failed: %s", error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ if (! dbus_g_proxy_call (session->priv->bus_proxy, "GetConnectionUnixProcessID", &error,
+ G_TYPE_STRING, sender,
+ G_TYPE_INVALID,
+ G_TYPE_UINT, calling_pid,
+ G_TYPE_INVALID)) {
+ g_warning ("GetConnectionUnixProcessID() failed: %s", error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ res = TRUE;
+
+ g_debug ("uid = %d", *calling_uid);
+ g_debug ("pid = %d", *calling_pid);
+
+out:
+ return res;
+}
+
+static gboolean
+session_set_idle_hint_internal (CkSession *session,
+ gboolean idle_hint)
+{
+ if (session->priv->idle_hint != idle_hint) {
+ session->priv->idle_hint = idle_hint;
+ g_object_notify (G_OBJECT (session), "idle-hint");
+
+ /* FIXME: can we get a time from the dbus message? */
+ g_get_current_time (&session->priv->idle_since_hint);
+
+ g_debug ("Emitting idle-changed for session %s", session->priv->id);
+ g_signal_emit (session, signals [IDLE_HINT_CHANGED], 0, idle_hint);
+ }
+
+ return TRUE;
+}
+
+/*
+ Example:
+ dbus-send --system --dest=org.freedesktop.ConsoleKit \
+ --type=method_call --print-reply --reply-timeout=2000 \
+ /org/freedesktop/ConsoleKit/Session1 \
+ org.freedesktop.ConsoleKit.Session.SetIdleHint boolean:TRUE
+*/
+gboolean
+ck_session_set_idle_hint (CkSession *session,
+ gboolean idle_hint,
+ DBusGMethodInvocation *context)
+{
+ char *sender;
+ uid_t calling_uid;
+ pid_t calling_pid;
+ gboolean res;
+
+ g_return_val_if_fail (CK_IS_SESSION (session), FALSE);
+
+ sender = dbus_g_method_get_sender (context);
+
+ res = get_caller_info (session,
+ sender,
+ &calling_uid,
+ &calling_pid);
+ g_free (sender);
+
+ if (! res) {
+ GError *error;
+ error = g_error_new (CK_SESSION_ERROR,
+ CK_SESSION_ERROR_GENERAL,
+ _("Unable to lookup information about calling process '%d'"),
+ calling_pid);
+ g_warning ("stat on pid %d failed", calling_pid);
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ return FALSE;
+ }
+
+ /* only restrict this by UID for now */
+ if (session->priv->uid != calling_uid) {
+ GError *error;
+ error = g_error_new (CK_SESSION_ERROR,
+ CK_SESSION_ERROR_GENERAL,
+ _("Only session owner may set idle hint state"));
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ return FALSE;
+ }
+
+ session_set_idle_hint_internal (session, idle_hint);
+ dbus_g_method_return (context);
+
+ return TRUE;
+}
+
+gboolean
+ck_session_get_idle_hint (CkSession *session,
+ gboolean *idle_hint,
+ GError **error)
+{
+ g_return_val_if_fail (CK_IS_SESSION (session), FALSE);
+
+ if (idle_hint != NULL) {
+ *idle_hint = session->priv->idle_hint;
+ }
+
+ return TRUE;
+}
+
+#if GLIB_CHECK_VERSION(2,12,0)
+#define _g_time_val_to_iso8601(t) g_time_val_to_iso8601(t)
+#else
+/* copied from GLib */
+static gchar *
+_g_time_val_to_iso8601 (GTimeVal *time_)
+{
+ gchar *retval;
+
+ g_return_val_if_fail (time_->tv_usec >= 0 && time_->tv_usec < G_USEC_PER_SEC, NULL);
+
+#define ISO_8601_LEN 21
+#define ISO_8601_FORMAT "%Y-%m-%dT%H:%M:%SZ"
+ retval = g_new0 (gchar, ISO_8601_LEN + 1);
+
+ strftime (retval, ISO_8601_LEN,
+ ISO_8601_FORMAT,
+ gmtime (&(time_->tv_sec)));
+
+ return retval;
+}
+#endif
+
+gboolean
+ck_session_get_idle_since_hint (CkSession *session,
+ char **iso8601_datetime,
+ GError **error)
+{
+ char *date_str;
+
+ g_return_val_if_fail (CK_IS_SESSION (session), FALSE);
+
+ date_str = NULL;
+ if (session->priv->idle_hint) {
+ date_str = _g_time_val_to_iso8601 (&session->priv->idle_since_hint);
+ }
+
+ if (iso8601_datetime != NULL) {
+ *iso8601_datetime = g_strdup (date_str);
+ }
+
+ g_free (date_str);
+
+ return TRUE;
+}
+
+gboolean
+ck_session_activate (CkSession *session,
+ DBusGMethodInvocation *context)
+{
+ gboolean res;
+
+ g_return_val_if_fail (CK_IS_SESSION (session), FALSE);
+
+ res = FALSE;
+ g_signal_emit (session, signals [ACTIVATE], 0, context, &res);
+ if (! res) {
+ GError *error;
+
+ /* if the signal is not handled then either:
+ a) aren't attached to seat
+ b) seat doesn't support activation changes */
+ g_debug ("Activate signal not handled");
+
+ error = g_error_new (CK_SESSION_ERROR,
+ CK_SESSION_ERROR_GENERAL,
+ _("Unable to activate session"));
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gboolean
+ck_session_set_active (CkSession *session,
+ gboolean active,
+ GError **error)
+{
+ g_return_val_if_fail (CK_IS_SESSION (session), FALSE);
+
+ if (session->priv->active != active) {
+ session->priv->active = active;
+ g_signal_emit (session, signals [ACTIVE_CHANGED], 0, active);
+ }
+
+ return TRUE;
+}
+
+gboolean
+ck_session_set_is_local (CkSession *session,
+ gboolean is_local,
+ GError **error)
+{
+ g_return_val_if_fail (CK_IS_SESSION (session), FALSE);
+
+ if (session->priv->is_local != is_local) {
+ session->priv->is_local = is_local;
+ }
+
+ return TRUE;
+}
+
+gboolean
+ck_session_get_id (CkSession *session,
+ char **id,
+ GError **error)
+{
+ g_return_val_if_fail (CK_IS_SESSION (session), FALSE);
+
+ if (id != NULL) {
+ *id = g_strdup (session->priv->id);
+ }
+
+ return TRUE;
+}
+
+gboolean
+ck_session_get_seat_id (CkSession *session,
+ char **id,
+ GError **error)
+{
+ g_return_val_if_fail (CK_IS_SESSION (session), FALSE);
+
+ if (id != NULL) {
+ *id = g_strdup (session->priv->seat_id);
+ }
+
+ return TRUE;
+}
+
+gboolean
+ck_session_get_unix_user (CkSession *session,
+ guint *uid,
+ GError **error)
+{
+ g_return_val_if_fail (CK_IS_SESSION (session), FALSE);
+
+ if (uid != NULL) {
+ *uid = session->priv->uid;
+ }
+
+ return TRUE;
+}
+
+/* deprecated */
+gboolean
+ck_session_get_user (CkSession *session,
+ guint *uid,
+ GError **error)
+{
+ return ck_session_get_unix_user (session, uid, error);
+}
+
+gboolean
+ck_session_get_x11_display (CkSession *session,
+ char **x11_display,
+ GError **error)
+{
+ g_return_val_if_fail (CK_IS_SESSION (session), FALSE);
+
+ if (x11_display != NULL) {
+ *x11_display = g_strdup (session->priv->x11_display);
+ }
+
+ return TRUE;
+}
+
+gboolean
+ck_session_get_display_device (CkSession *session,
+ char **display_device,
+ GError **error)
+{
+ g_return_val_if_fail (CK_IS_SESSION (session), FALSE);
+
+ if (display_device != NULL) {
+ *display_device = g_strdup (session->priv->display_device);
+ }
+
+ return TRUE;
+}
+
+gboolean
+ck_session_get_x11_display_device (CkSession *session,
+ char **x11_display_device,
+ GError **error)
+{
+ g_return_val_if_fail (CK_IS_SESSION (session), FALSE);
+
+ if (x11_display_device != NULL) {
+ *x11_display_device = g_strdup (session->priv->x11_display_device);
+ }
+
+ return TRUE;
+}
+
+gboolean
+ck_session_get_remote_host_name (CkSession *session,
+ char **remote_host_name,
+ GError **error)
+{
+ g_return_val_if_fail (CK_IS_SESSION (session), FALSE);
+
+ if (remote_host_name != NULL) {
+ *remote_host_name = g_strdup (session->priv->remote_host_name);
+ }
+
+ return TRUE;
+}
+
+gboolean
+ck_session_get_creation_time (CkSession *session,
+ char **iso8601_datetime,
+ GError **error)
+{
+ g_return_val_if_fail (CK_IS_SESSION (session), FALSE);
+
+ if (iso8601_datetime != NULL) {
+ *iso8601_datetime = _g_time_val_to_iso8601 (&session->priv->creation_time);
+ }
+
+ return TRUE;
+}
+
+gboolean
+ck_session_get_session_type (CkSession *session,
+ char **type,
+ GError **error)
+{
+ g_return_val_if_fail (CK_IS_SESSION (session), FALSE);
+
+ if (type != NULL) {
+ *type = g_strdup (session->priv->session_type);
+ }
+
+ return TRUE;
+}
+
+gboolean
+ck_session_is_active (CkSession *session,
+ gboolean *active,
+ GError **error)
+{
+ g_return_val_if_fail (CK_IS_SESSION (session), FALSE);
+
+ if (active != NULL) {
+ *active = session->priv->active;
+ }
+
+ return TRUE;
+}
+
+gboolean
+ck_session_is_local (CkSession *session,
+ gboolean *local,
+ GError **error)
+{
+ g_return_val_if_fail (CK_IS_SESSION (session), FALSE);
+
+ if (local != NULL) {
+ *local = session->priv->is_local;
+ }
+
+ return TRUE;
+}
+
+gboolean
+ck_session_set_id (CkSession *session,
+ const char *id,
+ GError **error)
+{
+ g_return_val_if_fail (CK_IS_SESSION (session), FALSE);
+
+ g_free (session->priv->id);
+ session->priv->id = g_strdup (id);
+
+ return TRUE;
+}
+
+gboolean
+ck_session_set_cookie (CkSession *session,
+ const char *cookie,
+ GError **error)
+{
+ g_return_val_if_fail (CK_IS_SESSION (session), FALSE);
+
+ g_free (session->priv->cookie);
+ session->priv->cookie = g_strdup (cookie);
+
+ return TRUE;
+}
+
+gboolean
+ck_session_set_seat_id (CkSession *session,
+ const char *id,
+ GError **error)
+{
+ g_return_val_if_fail (CK_IS_SESSION (session), FALSE);
+
+ g_free (session->priv->seat_id);
+ session->priv->seat_id = g_strdup (id);
+
+ return TRUE;
+}
+
+gboolean
+ck_session_set_unix_user (CkSession *session,
+ guint uid,
+ GError **error)
+{
+ g_return_val_if_fail (CK_IS_SESSION (session), FALSE);
+
+ session->priv->uid = uid;
+
+ return TRUE;
+}
+
+gboolean
+ck_session_set_x11_display (CkSession *session,
+ const char *x11_display,
+ GError **error)
+{
+ g_return_val_if_fail (CK_IS_SESSION (session), FALSE);
+
+ g_free (session->priv->x11_display);
+ session->priv->x11_display = g_strdup (x11_display);
+
+ return TRUE;
+}
+
+gboolean
+ck_session_set_display_device (CkSession *session,
+ const char *display_device,
+ GError **error)
+{
+ g_return_val_if_fail (CK_IS_SESSION (session), FALSE);
+
+ g_free (session->priv->display_device);
+ session->priv->display_device = g_strdup (display_device);
+
+ return TRUE;
+}
+
+gboolean
+ck_session_set_x11_display_device (CkSession *session,
+ const char *x11_display_device,
+ GError **error)
+{
+ g_return_val_if_fail (CK_IS_SESSION (session), FALSE);
+
+ g_free (session->priv->x11_display_device);
+ session->priv->x11_display_device = g_strdup (x11_display_device);
+
+ return TRUE;
+}
+
+gboolean
+ck_session_set_remote_host_name (CkSession *session,
+ const char *remote_host_name,
+ GError **error)
+{
+ g_return_val_if_fail (CK_IS_SESSION (session), FALSE);
+
+ g_free (session->priv->remote_host_name);
+ session->priv->remote_host_name = g_strdup (remote_host_name);
+
+ return TRUE;
+}
+
+gboolean
+ck_session_set_session_type (CkSession *session,
+ const char *type,
+ GError **error)
+{
+ g_return_val_if_fail (CK_IS_SESSION (session), FALSE);
+
+ g_free (session->priv->session_type);
+ session->priv->session_type = g_strdup (type);
+
+ return TRUE;
+}
+
+static void
+ck_session_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ CkSession *self;
+
+ self = CK_SESSION (object);
+
+ switch (prop_id) {
+ case PROP_ACTIVE:
+ ck_session_set_active (self, g_value_get_boolean (value), NULL);
+ break;
+ case PROP_IS_LOCAL:
+ ck_session_set_is_local (self, g_value_get_boolean (value), NULL);
+ break;
+ case PROP_ID:
+ ck_session_set_id (self, g_value_get_string (value), NULL);
+ break;
+ case PROP_COOKIE:
+ ck_session_set_cookie (self, g_value_get_string (value), NULL);
+ break;
+ case PROP_SESSION_TYPE:
+ ck_session_set_session_type (self, g_value_get_string (value), NULL);
+ break;
+ case PROP_X11_DISPLAY:
+ ck_session_set_x11_display (self, g_value_get_string (value), NULL);
+ break;
+ case PROP_X11_DISPLAY_DEVICE:
+ ck_session_set_x11_display_device (self, g_value_get_string (value), NULL);
+ break;
+ case PROP_DISPLAY_DEVICE:
+ ck_session_set_display_device (self, g_value_get_string (value), NULL);
+ break;
+ case PROP_UNIX_USER:
+ ck_session_set_unix_user (self, g_value_get_uint (value), NULL);
+ break;
+ case PROP_USER: /* deprecated */
+ ck_session_set_unix_user (self, g_value_get_uint (value), NULL);
+ break;
+ case PROP_REMOTE_HOST_NAME:
+ ck_session_set_remote_host_name (self, g_value_get_string (value), NULL);
+ break;
+ case PROP_IDLE_HINT:
+ session_set_idle_hint_internal (self, g_value_get_boolean (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+ck_session_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ CkSession *self;
+
+ self = CK_SESSION (object);
+
+ switch (prop_id) {
+ case PROP_ACTIVE:
+ g_value_set_boolean (value, self->priv->active);
+ break;
+ case PROP_IS_LOCAL:
+ g_value_set_boolean (value, self->priv->is_local);
+ break;
+ case PROP_ID:
+ g_value_set_string (value, self->priv->id);
+ break;
+ case PROP_COOKIE:
+ g_value_set_string (value, self->priv->cookie);
+ break;
+ case PROP_SESSION_TYPE:
+ g_value_set_string (value, self->priv->session_type);
+ break;
+ case PROP_X11_DISPLAY:
+ g_value_set_string (value, self->priv->x11_display);
+ break;
+ case PROP_X11_DISPLAY_DEVICE:
+ g_value_set_string (value, self->priv->x11_display_device);
+ break;
+ case PROP_DISPLAY_DEVICE:
+ g_value_set_string (value, self->priv->display_device);
+ break;
+ case PROP_UNIX_USER:
+ g_value_set_uint (value, self->priv->uid);
+ break;
+ case PROP_USER: /* deprecated */
+ g_value_set_uint (value, self->priv->uid);
+ break;
+ case PROP_REMOTE_HOST_NAME:
+ g_value_set_string (value, self->priv->remote_host_name);
+ break;
+ case PROP_IDLE_HINT:
+ g_value_set_boolean (value, self->priv->idle_hint);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+#define IS_STR_SET(x) (x != NULL && x[0] != '\0')
+static gboolean
+session_is_text (CkSession *session)
+{
+ gboolean ret;
+
+ ret = FALSE;
+
+ if (! IS_STR_SET (session->priv->x11_display_device)
+ && ! IS_STR_SET (session->priv->x11_display)
+ && IS_STR_SET (session->priv->display_device)) {
+ ret = TRUE;
+ }
+
+ g_debug ("Identified session '%s' as %s",
+ session->priv->id,
+ ret ? "text" : "graphical");
+
+ return ret;
+}
+
+static void
+tty_idle_changed_cb (CkTtyIdleMonitor *monitor,
+ gboolean idle_hint,
+ CkSession *session)
+{
+ session_set_idle_hint_internal (session, idle_hint);
+}
+
+static void
+session_add_activity_watch (CkSession *session)
+{
+ if (session->priv->idle_monitor == NULL) {
+ session->priv->idle_monitor = ck_tty_idle_monitor_new (session->priv->display_device);
+ g_signal_connect (session->priv->idle_monitor,
+ "idle-hint-changed",
+ G_CALLBACK (tty_idle_changed_cb),
+ session);
+
+ }
+ ck_tty_idle_monitor_start (session->priv->idle_monitor);
+}
+
+static void
+session_remove_activity_watch (CkSession *session)
+{
+ if (session->priv->idle_monitor == NULL) {
+ return;
+ }
+
+ ck_tty_idle_monitor_stop (session->priv->idle_monitor);
+ g_object_unref (session->priv->idle_monitor);
+ session->priv->idle_monitor = NULL;
+}
+
+static GObject *
+ck_session_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_properties)
+{
+ CkSession *session;
+ CkSessionClass *klass;
+
+ klass = CK_SESSION_CLASS (g_type_class_peek (CK_TYPE_SESSION));
+
+ session = CK_SESSION (G_OBJECT_CLASS (ck_session_parent_class)->constructor (type,
+ n_construct_properties,
+ construct_properties));
+ if (session_is_text (session)) {
+ session_add_activity_watch (session);
+ }
+
+ return G_OBJECT (session);
+}
+
+static void
+ck_session_class_init (CkSessionClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->constructor = ck_session_constructor;
+ object_class->get_property = ck_session_get_property;
+ object_class->set_property = ck_session_set_property;
+ object_class->finalize = ck_session_finalize;
+
+ signals [ACTIVE_CHANGED] =
+ g_signal_new ("active-changed",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (CkSessionClass, active_changed),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__BOOLEAN,
+ G_TYPE_NONE,
+ 1, G_TYPE_BOOLEAN);
+ signals [ACTIVATE] =
+ g_signal_new ("activate",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (CkSessionClass, activate),
+ NULL,
+ NULL,
+ ck_marshal_BOOLEAN__POINTER,
+ G_TYPE_BOOLEAN,
+ 1, G_TYPE_POINTER);
+ signals [LOCK] =
+ g_signal_new ("lock",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (CkSessionClass, lock),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+ signals [UNLOCK] =
+ g_signal_new ("unlock",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (CkSessionClass, unlock),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+ signals [IDLE_HINT_CHANGED] =
+ g_signal_new ("idle-hint-changed",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (CkSessionClass, idle_hint_changed),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__BOOLEAN,
+ G_TYPE_NONE,
+ 1, G_TYPE_BOOLEAN);
+
+ g_object_class_install_property (object_class,
+ PROP_ACTIVE,
+ g_param_spec_boolean ("active",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_IS_LOCAL,
+ g_param_spec_boolean ("is-local",
+ NULL,
+ NULL,
+ TRUE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+ g_object_class_install_property (object_class,
+ PROP_ID,
+ g_param_spec_string ("id",
+ "id",
+ "id",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ g_object_class_install_property (object_class,
+ PROP_COOKIE,
+ g_param_spec_string ("cookie",
+ "cookie",
+ "cookie",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property (object_class,
+ PROP_SESSION_TYPE,
+ g_param_spec_string ("session-type",
+ "session-type",
+ "session type",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+ g_object_class_install_property (object_class,
+ PROP_X11_DISPLAY,
+ g_param_spec_string ("x11-display",
+ "x11-display",
+ "X11 Display",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+ g_object_class_install_property (object_class,
+ PROP_X11_DISPLAY_DEVICE,
+ g_param_spec_string ("x11-display-device",
+ "x11-display-device",
+ "X11 Display device",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+ g_object_class_install_property (object_class,
+ PROP_DISPLAY_DEVICE,
+ g_param_spec_string ("display-device",
+ "display-device",
+ "Display device",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+ g_object_class_install_property (object_class,
+ PROP_REMOTE_HOST_NAME,
+ g_param_spec_string ("remote-host-name",
+ "remote-host-name",
+ "Remote host name",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class,
+ PROP_UNIX_USER,
+ g_param_spec_uint ("unix-user",
+ "POSIX User Id",
+ "POSIX User Id",
+ 0,
+ G_MAXINT,
+ 0,
+ G_PARAM_READWRITE));
+ /* deprecated */
+ g_object_class_install_property (object_class,
+ PROP_USER,
+ g_param_spec_uint ("user",
+ "User Id",
+ "User Id",
+ 0,
+ G_MAXINT,
+ 0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_ACTIVE,
+ g_param_spec_boolean ("idle-hint",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+
+ g_type_class_add_private (klass, sizeof (CkSessionPrivate));
+
+ dbus_g_object_type_install_info (CK_TYPE_SESSION, &dbus_glib_ck_session_object_info);
+}
+
+static void
+ck_session_init (CkSession *session)
+{
+ session->priv = CK_SESSION_GET_PRIVATE (session);
+
+ /* FIXME: should we have a property for this? */
+ g_get_current_time (&session->priv->creation_time);
+}
+
+static void
+ck_session_finalize (GObject *object)
+{
+ CkSession *session;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (CK_IS_SESSION (object));
+
+ session = CK_SESSION (object);
+
+ g_return_if_fail (session->priv != NULL);
+
+ session_remove_activity_watch (session);
+
+ g_free (session->priv->id);
+ g_free (session->priv->cookie);
+ g_free (session->priv->seat_id);
+ g_free (session->priv->session_type);
+ g_free (session->priv->x11_display);
+ g_free (session->priv->display_device);
+ g_free (session->priv->x11_display_device);
+ g_free (session->priv->remote_host_name);
+
+ G_OBJECT_CLASS (ck_session_parent_class)->finalize (object);
+}
+
+CkSession *
+ck_session_new (const char *ssid,
+ const char *cookie)
+{
+ GObject *object;
+ gboolean res;
+
+ object = g_object_new (CK_TYPE_SESSION,
+ "id", ssid,
+ "cookie", cookie,
+ NULL);
+ res = register_session (CK_SESSION (object));
+ if (! res) {
+ g_object_unref (object);
+ return NULL;
+ }
+
+ return CK_SESSION (object);
+}
+
+#define CK_TYPE_PARAMETER_STRUCT (dbus_g_type_get_struct ("GValueArray", \
+ G_TYPE_STRING, \
+ G_TYPE_VALUE, \
+ G_TYPE_INVALID))
+
+CkSession *
+ck_session_new_with_parameters (const char *ssid,
+ const char *cookie,
+ const GPtrArray *parameters)
+{
+ GObject *object;
+ gboolean res;
+ int i;
+ GParameter *params;
+ guint n_allocated_params;
+ guint n_params;
+ GObjectClass *class;
+ GType object_type;
+
+ object_type = CK_TYPE_SESSION;
+ class = g_type_class_ref (object_type);
+
+ n_allocated_params = 2;
+ if (parameters != NULL) {
+ n_allocated_params += parameters->len;
+ }
+
+ params = g_new0 (GParameter, n_allocated_params);
+
+ n_params = 0;
+ params[n_params].name = g_strdup ("id");
+ params[n_params].value.g_type = 0;
+ g_value_init (&params[n_params].value, G_TYPE_STRING);
+ g_value_set_string (&params[n_params].value, ssid);
+ n_params++;
+
+ params[n_params].name = g_strdup ("cookie");
+ params[n_params].value.g_type = 0;
+ g_value_init (&params[n_params].value, G_TYPE_STRING);
+ g_value_set_string (&params[n_params].value, cookie);
+ n_params++;
+
+ if (parameters != NULL) {
+ for (i = 0; i < parameters->len; i++) {
+ gboolean res;
+ GValue val_struct = { 0, };
+ char *prop_name;
+ GValue *prop_val;
+ GParamSpec *pspec;
+
+ g_value_init (&val_struct, CK_TYPE_PARAMETER_STRUCT);
+ g_value_set_static_boxed (&val_struct, g_ptr_array_index (parameters, i));
+
+ res = dbus_g_type_struct_get (&val_struct,
+ 0, &prop_name,
+ 1, &prop_val,
+ G_MAXUINT);
+ if (! res) {
+ g_debug ("Unable to extract parameter input");
+ goto cont;
+ }
+
+ if (prop_name == NULL) {
+ g_debug ("Skipping NULL parameter");
+ goto cont;
+ }
+
+ if (strcmp (prop_name, "id") == 0
+ || strcmp (prop_name, "cookie") == 0) {
+ g_debug ("Skipping restricted parameter: %s", prop_name);
+ goto cont;
+ }
+
+ pspec = g_object_class_find_property (class, prop_name);
+ if (! pspec) {
+ g_debug ("Skipping unknown parameter: %s", prop_name);
+ goto cont;
+ }
+
+ if (!(pspec->flags & G_PARAM_WRITABLE)) {
+ g_debug ("property '%s' is not writable", pspec->name);
+ goto cont;
+ }
+
+ params[n_params].name = g_strdup (prop_name);
+ params[n_params].value.g_type = 0;
+ g_value_init (&params[n_params].value, G_PARAM_SPEC_VALUE_TYPE (pspec));
+ res = g_value_transform (prop_val, &params[n_params].value);
+ if (! res) {
+ g_debug ("unable to transform property value for '%s'", pspec->name);
+ goto cont;
+ }
+
+ n_params++;
+ cont:
+ g_free (prop_name);
+ if (prop_val != NULL) {
+ g_value_unset (prop_val);
+ g_free (prop_val);
+ }
+ }
+ }
+
+ object = g_object_newv (object_type, n_params, params);
+
+ while (n_params--) {
+ g_free ((char *)params[n_params].name);
+ g_value_unset (&params[n_params].value);
+ }
+ g_free (params);
+ g_type_class_unref (class);
+
+ res = register_session (CK_SESSION (object));
+ if (! res) {
+ g_object_unref (object);
+ return NULL;
+ }
+
+ return CK_SESSION (object);
+}
diff --git a/src/ck-session.h b/src/ck-session.h
new file mode 100644
index 0000000..fea3a71
--- /dev/null
+++ b/src/ck-session.h
@@ -0,0 +1,174 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2006 William Jon McCann <mccann@jhu.edu>
+ *
+ * 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.
+ *
+ */
+
+
+#ifndef __CK_SESSION_H
+#define __CK_SESSION_H
+
+#include <glib-object.h>
+#include <dbus/dbus-glib.h>
+
+G_BEGIN_DECLS
+
+#define CK_TYPE_SESSION (ck_session_get_type ())
+#define CK_SESSION(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CK_TYPE_SESSION, CkSession))
+#define CK_SESSION_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), CK_TYPE_SESSION, CkSessionClass))
+#define CK_IS_SESSION(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), CK_TYPE_SESSION))
+#define CK_IS_SESSION_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), CK_TYPE_SESSION))
+#define CK_SESSION_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), CK_TYPE_SESSION, CkSessionClass))
+
+typedef struct CkSessionPrivate CkSessionPrivate;
+
+typedef struct
+{
+ GObject parent;
+ CkSessionPrivate *priv;
+} CkSession;
+
+typedef struct
+{
+ GObjectClass parent_class;
+
+ /* internal signals */
+ void (* activate) (CkSession *session,
+ DBusGMethodInvocation *context);
+
+ /* exported signals */
+ void (* lock) (CkSession *session);
+ void (* unlock) (CkSession *session);
+ void (* active_changed) (CkSession *session,
+ gboolean active);
+ void (* idle_hint_changed) (CkSession *session,
+ gboolean idle_hint);
+} CkSessionClass;
+
+typedef enum
+{
+ CK_SESSION_ERROR_GENERAL
+} CkSessionError;
+
+#define CK_SESSION_ERROR ck_session_error_quark ()
+
+GQuark ck_session_error_quark (void);
+GType ck_session_get_type (void);
+CkSession * ck_session_new (const char *ssid,
+ const char *cookie);
+CkSession * ck_session_new_with_parameters (const char *ssid,
+ const char *cookie,
+ const GPtrArray *parameters);
+
+gboolean ck_session_set_active (CkSession *session,
+ gboolean active,
+ GError **error);
+gboolean ck_session_set_is_local (CkSession *session,
+ gboolean is_local,
+ GError **error);
+gboolean ck_session_set_id (CkSession *session,
+ const char *ssid,
+ GError **error);
+gboolean ck_session_set_cookie (CkSession *session,
+ const char *cookie,
+ GError **error);
+gboolean ck_session_set_seat_id (CkSession *session,
+ const char *sid,
+ GError **error);
+gboolean ck_session_set_unix_user (CkSession *session,
+ guint uid,
+ GError **error);
+gboolean ck_session_set_x11_display (CkSession *session,
+ const char *xdisplay,
+ GError **error);
+gboolean ck_session_set_x11_display_device (CkSession *session,
+ const char *xdisplay,
+ GError **error);
+gboolean ck_session_set_display_device (CkSession *session,
+ const char *device,
+ GError **error);
+gboolean ck_session_set_remote_host_name (CkSession *session,
+ const char *remote_host_name,
+ GError **error);
+gboolean ck_session_set_session_type (CkSession *session,
+ const char *type,
+ GError **error);
+
+/* Exported methods */
+
+/* Authoritative properties */
+gboolean ck_session_get_id (CkSession *session,
+ char **ssid,
+ GError **error);
+gboolean ck_session_get_seat_id (CkSession *session,
+ char **sid,
+ GError **error);
+gboolean ck_session_is_active (CkSession *session,
+ gboolean *active,
+ GError **error);
+gboolean ck_session_is_local (CkSession *session,
+ gboolean *local,
+ GError **error);
+gboolean ck_session_get_unix_user (CkSession *session,
+ guint *uid,
+ GError **error);
+gboolean ck_session_get_x11_display (CkSession *session,
+ char **display,
+ GError **error);
+gboolean ck_session_get_x11_display_device (CkSession *session,
+ char **display,
+ GError **error);
+gboolean ck_session_get_display_device (CkSession *session,
+ char **display,
+ GError **error);
+gboolean ck_session_get_session_type (CkSession *session,
+ char **type,
+ GError **error);
+gboolean ck_session_get_remote_host_name (CkSession *session,
+ char **host_name,
+ GError **error);
+gboolean ck_session_get_creation_time (CkSession *session,
+ char **iso8601_datetime,
+ GError **error);
+/*deprecated*/
+gboolean ck_session_get_user (CkSession *session,
+ guint *uid,
+ GError **error);
+
+/* Non-authoritative properties */
+gboolean ck_session_get_idle_hint (CkSession *session,
+ gboolean *idle_hint,
+ GError **error);
+gboolean ck_session_get_idle_since_hint (CkSession *session,
+ char **iso8601_datetime,
+ GError **error);
+gboolean ck_session_set_idle_hint (CkSession *session,
+ gboolean idle_hint,
+ DBusGMethodInvocation *context);
+
+/* Privileged actions */
+gboolean ck_session_activate (CkSession *session,
+ DBusGMethodInvocation *context);
+gboolean ck_session_lock (CkSession *session,
+ DBusGMethodInvocation *context);
+gboolean ck_session_unlock (CkSession *session,
+ DBusGMethodInvocation *context);
+
+
+G_END_DECLS
+
+#endif /* __CK_SESSION_H */
diff --git a/src/ck-session.xml b/src/ck-session.xml
new file mode 100644
index 0000000..5fc85d0
--- /dev/null
+++ b/src/ck-session.xml
@@ -0,0 +1,78 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<node>
+ <interface name="org.freedesktop.ConsoleKit.Session">
+ <method name="GetId">
+ <arg name="ssid" direction="out" type="o"/>
+ </method>
+ <method name="GetSeatId">
+ <arg name="sid" direction="out" type="o"/>
+ </method>
+ <method name="GetSessionType">
+ <arg name="type" direction="out" type="s"/>
+ </method>
+ <method name="GetUser">
+ <arg name="uid" direction="out" type="i"/>
+ </method>
+ <method name="GetUnixUser">
+ <arg name="uid" direction="out" type="i"/>
+ </method>
+ <method name="GetX11Display">
+ <arg name="display" direction="out" type="s"/>
+ </method>
+ <method name="GetX11DisplayDevice">
+ <arg name="x11_display_device" direction="out" type="s"/>
+ </method>
+ <method name="GetDisplayDevice">
+ <arg name="display_device" direction="out" type="s"/>
+ </method>
+ <method name="GetRemoteHostName">
+ <arg name="remote_host_name" direction="out" type="s"/>
+ </method>
+ <method name="IsActive">
+ <arg name="active" direction="out" type="b"/>
+ </method>
+ <method name="IsLocal">
+ <arg name="local" direction="out" type="b"/>
+ </method>
+ <method name="GetCreationTime">
+ <arg name="iso8601_datetime" type="s" direction="out"/>
+ </method>
+ <method name="Activate">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ </method>
+ <method name="Lock">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ </method>
+ <method name="Unlock">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ </method>
+ <method name="GetIdleHint">
+ <arg name="idle_hint" type="b" direction="out"/>
+ </method>
+ <method name="GetIdleSinceHint">
+ <arg name="iso8601_datetime" type="s" direction="out"/>
+ </method>
+ <method name="SetIdleHint">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg name="idle_hint" type="b" direction="in"/>
+ </method>
+ <signal name="ActiveChanged">
+ <arg name="is_active" type="b"/>
+ </signal>
+ <signal name="IdleHintChanged">
+ <arg name="hint" type="b"/>
+ </signal>
+ <signal name="Lock"/>
+ <signal name="Unlock"/>
+ <property name="unix-user" type="u" access="readwrite"/>
+ <property name="user" type="u" access="readwrite"/>
+ <property name="session-type" type="s" access="readwrite"/>
+ <property name="remote-host-name" type="s" access="readwrite"/>
+ <property name="display-device" type="s" access="readwrite"/>
+ <property name="x11-display" type="s" access="readwrite"/>
+ <property name="x11-display-device" type="s" access="readwrite"/>
+ <property name="active" type="b" access="readwrite"/>
+ <property name="is-local" type="b" access="readwrite"/>
+ <property name="idle-hint" type="b" access="readwrite"/>
+ </interface>
+</node>
diff --git a/src/ck-tty-idle-monitor.c b/src/ck-tty-idle-monitor.c
new file mode 100644
index 0000000..eb071e2
--- /dev/null
+++ b/src/ck-tty-idle-monitor.c
@@ -0,0 +1,389 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
+ *
+ * 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 <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+#include <string.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+#include <glib-object.h>
+
+#include "ck-file-monitor.h"
+#include "ck-tty-idle-monitor.h"
+
+#define CK_TTY_IDLE_MONITOR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), CK_TYPE_TTY_IDLE_MONITOR, CkTtyIdleMonitorPrivate))
+
+#define DEFAULT_THRESHOLD_SECONDS 30
+
+struct CkTtyIdleMonitorPrivate
+{
+ char *device;
+ guint threshold;
+
+ guint timeout_id;
+ gboolean idle_hint;
+ GTimeVal idle_since_hint;
+
+ CkFileMonitor *file_monitor;
+ guint file_notify_id;
+};
+
+enum {
+ IDLE_HINT_CHANGED,
+ LAST_SIGNAL
+};
+
+enum {
+ PROP_0,
+ PROP_DEVICE,
+ PROP_THRESHOLD,
+};
+
+static guint signals [LAST_SIGNAL] = { 0, };
+
+static void ck_tty_idle_monitor_class_init (CkTtyIdleMonitorClass *klass);
+static void ck_tty_idle_monitor_init (CkTtyIdleMonitor *tty_idle_monitor);
+static void ck_tty_idle_monitor_finalize (GObject *object);
+
+static void schedule_tty_check (CkTtyIdleMonitor *monitor,
+ guint seconds);
+
+G_DEFINE_TYPE (CkTtyIdleMonitor, ck_tty_idle_monitor, G_TYPE_OBJECT)
+
+GQuark
+ck_tty_idle_monitor_error_quark (void)
+{
+ static GQuark ret = 0;
+ if (ret == 0) {
+ ret = g_quark_from_static_string ("ck_tty_idle_monitor_error");
+ }
+
+ return ret;
+}
+
+static gboolean
+tty_idle_monitor_set_idle_hint_internal (CkTtyIdleMonitor *tty_idle_monitor,
+ gboolean idle_hint)
+{
+ if (tty_idle_monitor->priv->idle_hint != idle_hint) {
+ tty_idle_monitor->priv->idle_hint = idle_hint;
+
+ g_debug ("Emitting idle-changed for tty_idle_monitor %s: %d", tty_idle_monitor->priv->device, idle_hint);
+ g_signal_emit (tty_idle_monitor, signals [IDLE_HINT_CHANGED], 0, idle_hint);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+ck_tty_idle_monitor_set_device (CkTtyIdleMonitor *monitor,
+ const char *device)
+{
+ g_return_if_fail (CK_IS_TTY_IDLE_MONITOR (monitor));
+
+ g_free (monitor->priv->device);
+ monitor->priv->device = g_strdup (device);
+}
+
+void
+ck_tty_idle_monitor_set_threshold (CkTtyIdleMonitor *monitor,
+ guint threshold)
+{
+ g_return_if_fail (CK_IS_TTY_IDLE_MONITOR (monitor));
+
+ monitor->priv->threshold = threshold;
+}
+
+static void
+ck_tty_idle_monitor_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ CkTtyIdleMonitor *self;
+
+ self = CK_TTY_IDLE_MONITOR (object);
+
+ switch (prop_id) {
+ case PROP_DEVICE:
+ ck_tty_idle_monitor_set_device (self, g_value_get_string (value));
+ break;
+ case PROP_THRESHOLD:
+ ck_tty_idle_monitor_set_threshold (self, g_value_get_uint (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+ck_tty_idle_monitor_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ CkTtyIdleMonitor *self;
+
+ self = CK_TTY_IDLE_MONITOR (object);
+
+ switch (prop_id) {
+ case PROP_DEVICE:
+ g_value_set_string (value, self->priv->device);
+ break;
+ case PROP_THRESHOLD:
+ g_value_set_uint (value, self->priv->threshold);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+remove_idle_hint_timeout (CkTtyIdleMonitor *tty_idle_monitor)
+{
+ if (tty_idle_monitor->priv->timeout_id > 0) {
+ g_source_remove (tty_idle_monitor->priv->timeout_id);
+ }
+}
+
+static void
+file_access_cb (CkFileMonitor *file_monitor,
+ CkFileMonitorEvent event,
+ const char *path,
+ CkTtyIdleMonitor *monitor)
+{
+ g_debug ("File access callback for %s", path);
+
+ tty_idle_monitor_set_idle_hint_internal (monitor, FALSE);
+
+ /* this resets timers */
+ ck_tty_idle_monitor_start (monitor);
+}
+
+static gboolean
+monitor_add_watch (CkTtyIdleMonitor *monitor)
+{
+ if (monitor->priv->file_monitor == NULL) {
+ monitor->priv->file_monitor = ck_file_monitor_new ();
+ }
+ monitor->priv->file_notify_id = ck_file_monitor_add_notify (monitor->priv->file_monitor,
+ monitor->priv->device,
+ CK_FILE_MONITOR_EVENT_ACCESS,
+ (CkFileMonitorNotifyFunc)file_access_cb,
+ monitor);
+ return monitor->priv->file_notify_id > 0;
+}
+
+static gboolean
+monitor_remove_watch (CkTtyIdleMonitor *monitor)
+{
+ if (monitor->priv->file_monitor != NULL &&
+ monitor->priv->file_notify_id > 0) {
+ g_debug ("Removing notify");
+ ck_file_monitor_remove_notify (monitor->priv->file_monitor,
+ monitor->priv->file_notify_id);
+ }
+
+ return FALSE;
+}
+
+static gboolean
+check_tty_idle (CkTtyIdleMonitor *monitor)
+{
+ struct stat sb;
+ gboolean is_idle;
+ gboolean changed;
+ time_t now;
+ time_t idletime;
+ time_t last_access;
+
+ if (monitor->priv->device == NULL) {
+ return FALSE;
+ }
+
+ if (g_stat (monitor->priv->device, &sb) < 0) {
+ g_debug ("Unable to stat: %s: %s", monitor->priv->device, g_strerror (errno));
+ return FALSE;
+ }
+
+ last_access = sb.st_atime;
+
+ time (&now);
+ if (last_access > now) {
+ last_access = now;
+ }
+
+ idletime = now - last_access;
+ is_idle = (idletime >= monitor->priv->threshold);
+
+ changed = tty_idle_monitor_set_idle_hint_internal (monitor, is_idle);
+
+ monitor->priv->timeout_id = 0;
+
+ if (is_idle) {
+ if (! monitor_add_watch (monitor)) {
+ /* if we can't add a watch just add a new timer */
+ g_debug ("Couldn't add watch: rescheduling check for %u sec", monitor->priv->threshold);
+ schedule_tty_check (monitor, monitor->priv->threshold);
+ }
+ } else {
+ guint remaining;
+ remaining = monitor->priv->threshold - idletime;
+ if (remaining > 0) {
+ g_debug ("Time left, rescheduling check for %u sec", remaining);
+ /* reschedule for time left */
+ schedule_tty_check (monitor, remaining);
+ } else {
+ g_debug ("restarting check for %u sec", monitor->priv->threshold);
+ schedule_tty_check (monitor, monitor->priv->threshold);
+
+ }
+ }
+
+ return FALSE;
+}
+
+static void
+schedule_tty_check (CkTtyIdleMonitor *monitor,
+ guint seconds)
+{
+ if (monitor->priv->timeout_id == 0) {
+#if GLIB_CHECK_VERSION(2,14,0)
+ monitor->priv->timeout_id = g_timeout_add_seconds (seconds,
+ (GSourceFunc)check_tty_idle,
+ monitor);
+#else
+ monitor->priv->timeout_id = g_timeout_add (seconds * 1000,
+ (GSourceFunc)check_tty_idle,
+ monitor);
+#endif
+ }
+}
+
+static void
+add_idle_hint_timeout (CkTtyIdleMonitor *monitor)
+{
+ schedule_tty_check (monitor, monitor->priv->threshold);
+}
+
+void
+ck_tty_idle_monitor_stop (CkTtyIdleMonitor *monitor)
+{
+ remove_idle_hint_timeout (monitor);
+ monitor_remove_watch (monitor);
+}
+
+void
+ck_tty_idle_monitor_start (CkTtyIdleMonitor *monitor)
+{
+ ck_tty_idle_monitor_stop (monitor);
+
+ add_idle_hint_timeout (monitor);
+}
+
+static void
+ck_tty_idle_monitor_class_init (CkTtyIdleMonitorClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->get_property = ck_tty_idle_monitor_get_property;
+ object_class->set_property = ck_tty_idle_monitor_set_property;
+ object_class->finalize = ck_tty_idle_monitor_finalize;
+
+ signals [IDLE_HINT_CHANGED] =
+ g_signal_new ("idle-hint-changed",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (CkTtyIdleMonitorClass, idle_hint_changed),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__BOOLEAN,
+ G_TYPE_NONE,
+ 1, G_TYPE_BOOLEAN);
+
+ g_object_class_install_property (object_class,
+ PROP_DEVICE,
+ g_param_spec_string ("device",
+ "device",
+ "device",
+ NULL,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_THRESHOLD,
+ g_param_spec_uint ("threshold",
+ "Threshold",
+ "Threshold",
+ 0,
+ G_MAXINT,
+ DEFAULT_THRESHOLD_SECONDS,
+ G_PARAM_READWRITE));
+
+ g_type_class_add_private (klass, sizeof (CkTtyIdleMonitorPrivate));
+}
+
+static void
+ck_tty_idle_monitor_init (CkTtyIdleMonitor *monitor)
+{
+ monitor->priv = CK_TTY_IDLE_MONITOR_GET_PRIVATE (monitor);
+
+ monitor->priv->threshold = DEFAULT_THRESHOLD_SECONDS;
+}
+
+static void
+ck_tty_idle_monitor_finalize (GObject *object)
+{
+ CkTtyIdleMonitor *monitor;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (CK_IS_TTY_IDLE_MONITOR (object));
+
+ monitor = CK_TTY_IDLE_MONITOR (object);
+
+ g_return_if_fail (monitor->priv != NULL);
+
+ ck_tty_idle_monitor_stop (monitor);
+
+ g_free (monitor->priv->device);
+
+ G_OBJECT_CLASS (ck_tty_idle_monitor_parent_class)->finalize (object);
+}
+
+CkTtyIdleMonitor *
+ck_tty_idle_monitor_new (const char *device)
+{
+ GObject *object;
+
+ object = g_object_new (CK_TYPE_TTY_IDLE_MONITOR,
+ "device", device,
+ NULL);
+
+ return CK_TTY_IDLE_MONITOR (object);
+}
diff --git a/src/ck-tty-idle-monitor.h b/src/ck-tty-idle-monitor.h
new file mode 100644
index 0000000..51cefe6
--- /dev/null
+++ b/src/ck-tty-idle-monitor.h
@@ -0,0 +1,71 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
+ *
+ * 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.
+ *
+ */
+
+
+#ifndef __CK_TTY_IDLE_MONITOR_H
+#define __CK_TTY_IDLE_MONITOR_H
+
+#include <glib-object.h>
+#include <dbus/dbus-glib.h>
+
+G_BEGIN_DECLS
+
+#define CK_TYPE_TTY_IDLE_MONITOR (ck_tty_idle_monitor_get_type ())
+#define CK_TTY_IDLE_MONITOR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CK_TYPE_TTY_IDLE_MONITOR, CkTtyIdleMonitor))
+#define CK_TTY_IDLE_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), CK_TYPE_TTY_IDLE_MONITOR, CkTtyIdleMonitorClass))
+#define CK_IS_TTY_IDLE_MONITOR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), CK_TYPE_TTY_IDLE_MONITOR))
+#define CK_IS_TTY_IDLE_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), CK_TYPE_TTY_IDLE_MONITOR))
+#define CK_TTY_IDLE_MONITOR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), CK_TYPE_TTY_IDLE_MONITOR, CkTtyIdleMonitorClass))
+
+typedef struct CkTtyIdleMonitorPrivate CkTtyIdleMonitorPrivate;
+
+typedef struct
+{
+ GObject parent;
+ CkTtyIdleMonitorPrivate *priv;
+} CkTtyIdleMonitor;
+
+typedef struct
+{
+ GObjectClass parent_class;
+
+ void (* idle_hint_changed) (CkTtyIdleMonitor *monitor,
+ gboolean idle_hint);
+} CkTtyIdleMonitorClass;
+
+typedef enum
+{
+ CK_TTY_IDLE_MONITOR_ERROR_GENERAL
+} CkTtyIdleMonitorError;
+
+#define CK_TTY_IDLE_MONITOR_ERROR ck_tty_idle_monitor_error_quark ()
+
+GQuark ck_tty_idle_monitor_error_quark (void);
+GType ck_tty_idle_monitor_get_type (void);
+
+CkTtyIdleMonitor * ck_tty_idle_monitor_new (const char *device);
+void ck_tty_idle_monitor_set_threshold (CkTtyIdleMonitor *monitor,
+ guint seconds);
+void ck_tty_idle_monitor_start (CkTtyIdleMonitor *monitor);
+void ck_tty_idle_monitor_stop (CkTtyIdleMonitor *monitor);
+
+G_END_DECLS
+
+#endif /* __CK_TTY_IDLE_MONITOR_H */
diff --git a/src/ck-vt-monitor.c b/src/ck-vt-monitor.c
new file mode 100644
index 0000000..40f834a
--- /dev/null
+++ b/src/ck-vt-monitor.c
@@ -0,0 +1,503 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2006-2007 William Jon McCann <mccann@jhu.edu>
+ *
+ * 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 <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/vt.h>
+
+#if defined (__linux__)
+#include <linux/tty.h>
+#include <linux/kd.h>
+#endif /* linux */
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+#include <glib-object.h>
+
+#define DBUS_API_SUBJECT_TO_CHANGE
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+
+#include "ck-vt-monitor.h"
+#include "ck-marshal.h"
+
+#define ERROR -1
+
+#define CK_VT_MONITOR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), CK_TYPE_VT_MONITOR, CkVtMonitorPrivate))
+
+struct CkVtMonitorPrivate
+{
+ int vfd;
+ GHashTable *vt_thread_hash;
+ guint active_num;
+
+ GAsyncQueue *event_queue;
+ guint process_queue_id;
+};
+
+enum {
+ ACTIVE_CHANGED,
+ LAST_SIGNAL
+};
+
+enum {
+ PROP_0,
+};
+
+static guint signals [LAST_SIGNAL] = { 0, };
+
+static void ck_vt_monitor_class_init (CkVtMonitorClass *klass);
+static void ck_vt_monitor_init (CkVtMonitor *vt_monitor);
+static void ck_vt_monitor_finalize (GObject *object);
+
+static void vt_add_watches (CkVtMonitor *vt_monitor);
+
+G_DEFINE_TYPE (CkVtMonitor, ck_vt_monitor, G_TYPE_OBJECT)
+
+G_LOCK_DEFINE_STATIC (hash_lock);
+G_LOCK_DEFINE_STATIC (schedule_lock);
+
+static gpointer vt_object = NULL;
+
+GQuark
+ck_vt_monitor_error_quark (void)
+{
+ static GQuark ret = 0;
+ if (ret == 0) {
+ ret = g_quark_from_static_string ("ck_vt_monitor_error");
+ }
+
+ return ret;
+}
+
+gboolean
+ck_vt_monitor_set_active (CkVtMonitor *vt_monitor,
+ guint32 num,
+ GError **error)
+{
+ gboolean ret;
+ int res;
+
+ g_return_val_if_fail (CK_IS_VT_MONITOR (vt_monitor), FALSE);
+
+ if (num == vt_monitor->priv->active_num) {
+ g_set_error (error,
+ CK_VT_MONITOR_ERROR,
+ CK_VT_MONITOR_ERROR_GENERAL,
+ _("Session is already active"));
+ return FALSE;
+ }
+
+ if (vt_monitor->priv->vfd == ERROR) {
+ g_set_error (error,
+ CK_VT_MONITOR_ERROR,
+ CK_VT_MONITOR_ERROR_GENERAL,
+ _("No consoles available"));
+ return FALSE;
+ }
+
+ res = ioctl (vt_monitor->priv->vfd, VT_ACTIVATE, num);
+ if (res == 0) {
+ ret = TRUE;
+ } else {
+ g_set_error (error,
+ CK_VT_MONITOR_ERROR,
+ CK_VT_MONITOR_ERROR_GENERAL,
+ _("Unable to activate session"));
+ ret = FALSE;
+ }
+
+ return ret;
+}
+
+gboolean
+ck_vt_monitor_get_active (CkVtMonitor *vt_monitor,
+ guint32 *num,
+ GError **error)
+{
+ if (num != NULL) {
+ *num = 0;
+ }
+
+ g_return_val_if_fail (CK_IS_VT_MONITOR (vt_monitor), FALSE);
+
+ if (vt_monitor->priv->vfd == ERROR) {
+ g_set_error (error,
+ CK_VT_MONITOR_ERROR,
+ CK_VT_MONITOR_ERROR_GENERAL,
+ _("No consoles available"));
+ return FALSE;
+ }
+
+ if (num != NULL) {
+ *num = vt_monitor->priv->active_num;
+ }
+
+ return TRUE;
+}
+
+static void
+change_active_num (CkVtMonitor *vt_monitor,
+ guint num)
+{
+
+ if (vt_monitor->priv->active_num != num) {
+ g_debug ("Changing active VT: %d", num);
+
+ vt_monitor->priv->active_num = num;
+
+ /* add a watch to every vt without a thread */
+ vt_add_watches (vt_monitor);
+
+ g_signal_emit (vt_monitor, signals[ACTIVE_CHANGED], 0, num);
+ } else {
+ g_debug ("VT activated but already active: %d", num);
+ }
+}
+
+typedef struct {
+ gint32 num;
+ CkVtMonitor *vt_monitor;
+} ThreadData;
+
+typedef struct {
+ gint32 num;
+} EventData;
+
+static void
+thread_data_free (ThreadData *data)
+{
+ if (data == NULL) {
+ return;
+ }
+
+ g_free (data);
+}
+
+static void
+event_data_free (EventData *data)
+{
+ if (data == NULL) {
+ return;
+ }
+
+ g_free (data);
+}
+
+static gboolean
+process_queue (CkVtMonitor *vt_monitor)
+{
+ int i;
+ int queue_length;
+ EventData *data;
+ EventData *d;
+
+ g_async_queue_lock (vt_monitor->priv->event_queue);
+
+ g_debug ("Processing VT event queue");
+
+ queue_length = g_async_queue_length_unlocked (vt_monitor->priv->event_queue);
+ data = NULL;
+
+ G_LOCK (hash_lock);
+
+ /* compress events in the queue */
+ for (i = 0; i < queue_length; i++) {
+ d = g_async_queue_try_pop_unlocked (vt_monitor->priv->event_queue);
+ if (d == NULL) {
+ continue;
+
+ }
+
+ if (data != NULL) {
+ g_debug ("Compressing queue; skipping event for VT %d", data->num);
+ event_data_free (data);
+ }
+
+ data = d;
+ }
+
+ G_UNLOCK (hash_lock);
+
+ if (data != NULL) {
+ change_active_num (vt_monitor, data->num);
+ event_data_free (data);
+ }
+
+ G_LOCK (schedule_lock);
+ vt_monitor->priv->process_queue_id = 0;
+ G_UNLOCK (schedule_lock);
+
+ g_async_queue_unlock (vt_monitor->priv->event_queue);
+
+ return FALSE;
+}
+
+static void
+schedule_process_queue (CkVtMonitor *vt_monitor)
+{
+ G_LOCK (schedule_lock);
+ if (vt_monitor->priv->process_queue_id == 0) {
+ vt_monitor->priv->process_queue_id = g_idle_add ((GSourceFunc)process_queue, vt_monitor);
+ }
+ G_UNLOCK (schedule_lock);
+}
+
+static void *
+vt_thread_start (ThreadData *data)
+{
+ CkVtMonitor *vt_monitor;
+ int ret;
+ gint32 num;
+
+ vt_monitor = data->vt_monitor;
+ num = data->num;
+
+ again:
+ g_debug ("VT_WAITACTIVE for vt %d", num);
+ ret = ioctl (vt_monitor->priv->vfd, VT_WAITACTIVE, num);
+
+ g_debug ("VT_WAITACTIVE for vt %d returned %d", num, ret);
+
+ if (ret == ERROR) {
+ const char *errmsg;
+
+ errmsg = g_strerror (errno);
+
+ if (errno == EINTR) {
+ g_debug ("Interrupted waiting for native console %d activation: %s",
+ num,
+ errmsg);
+ goto again;
+ } else {
+ g_warning ("Error waiting for native console %d activation: %s",
+ num,
+ errmsg);
+ }
+
+ g_free (data);
+ } else {
+ EventData *event;
+
+ /* add event to queue */
+ event = g_new0 (EventData, 1);
+ event->num = num;
+ g_debug ("Pushing activation event for VT %d onto queue", num);
+
+ g_async_queue_push (vt_monitor->priv->event_queue, event);
+
+ /* schedule processing of queue */
+ schedule_process_queue (vt_monitor);
+ }
+
+ G_LOCK (hash_lock);
+ if (vt_monitor->priv->vt_thread_hash != NULL) {
+ g_hash_table_remove (vt_monitor->priv->vt_thread_hash, GUINT_TO_POINTER (num));
+ }
+ G_UNLOCK (hash_lock);
+
+ g_thread_exit (NULL);
+ thread_data_free (data);
+
+ return NULL;
+}
+
+static void
+vt_add_watch_unlocked (CkVtMonitor *vt_monitor,
+ gint32 num)
+{
+ GThread *thread;
+ GError *error;
+ ThreadData *data;
+ gpointer id;
+
+ data = g_new0 (ThreadData, 1);
+ data->num = num;
+ data->vt_monitor = vt_monitor;
+
+ g_debug ("Creating thread for vt %d", num);
+
+ id = GINT_TO_POINTER (num);
+
+ error = NULL;
+ thread = g_thread_create_full ((GThreadFunc)vt_thread_start, data, 65536, FALSE, TRUE, G_THREAD_PRIORITY_NORMAL, &error);
+ if (thread == NULL) {
+ g_debug ("Unable to create thread: %s", error->message);
+ g_error_free (error);
+ } else {
+ g_hash_table_insert (vt_monitor->priv->vt_thread_hash, id, thread);
+ }
+}
+
+static void
+vt_add_watches (CkVtMonitor *vt_monitor)
+{
+ int i;
+ gint32 current_num;
+
+ G_LOCK (hash_lock);
+
+ current_num = vt_monitor->priv->active_num;
+
+ for (i = 1; i < MAX_NR_CONSOLES; i++) {
+ gpointer id;
+
+ /* don't wait on the active vc */
+ if (i == current_num) {
+ continue;
+ }
+
+ id = GINT_TO_POINTER (i);
+
+ /* add a watch to all other VTs that don't have threads */
+ if (g_hash_table_lookup (vt_monitor->priv->vt_thread_hash, id) == NULL) {
+ vt_add_watch_unlocked (vt_monitor, i);
+ }
+ }
+
+ G_UNLOCK (hash_lock);
+}
+
+static guint
+get_active_native (CkVtMonitor *vt_monitor)
+{
+ int ret;
+ struct vt_stat stat;
+
+ ret = ioctl (vt_monitor->priv->vfd, VT_GETSTATE, &stat);
+ if (ret == ERROR) {
+ perror ("ioctl VT_GETSTATE");
+ return -1;
+ }
+
+ {
+ int i;
+
+ g_debug ("Current VT: tty%d", stat.v_active);
+ for (i = 1; i <= 16; i++) {
+ gboolean is_on;
+ is_on = stat.v_state & (1 << i);
+
+ g_debug ("VT %d:%s", i, is_on ? "on" : "off");
+ }
+ }
+
+ return stat.v_active;
+}
+
+static void
+ck_vt_monitor_class_init (CkVtMonitorClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = ck_vt_monitor_finalize;
+
+ signals [ACTIVE_CHANGED] = g_signal_new ("active-changed",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (CkVtMonitorClass, active_changed),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__UINT,
+ G_TYPE_NONE,
+ 1, G_TYPE_UINT);
+
+ g_type_class_add_private (klass, sizeof (CkVtMonitorPrivate));
+}
+
+extern int getfd (void);
+
+static void
+ck_vt_monitor_init (CkVtMonitor *vt_monitor)
+{
+ int fd;
+
+ vt_monitor->priv = CK_VT_MONITOR_GET_PRIVATE (vt_monitor);
+
+ fd = getfd ();
+ vt_monitor->priv->vfd = fd;
+
+ if (fd == ERROR) {
+ const char *errmsg;
+ errmsg = g_strerror (errno);
+ g_warning ("Unable to open a console: %s", errmsg);
+ } else {
+ vt_monitor->priv->event_queue = g_async_queue_new ();
+ vt_monitor->priv->vt_thread_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
+
+ vt_monitor->priv->active_num = get_active_native (vt_monitor);
+
+ vt_add_watches (vt_monitor);
+ }
+}
+
+static void
+ck_vt_monitor_finalize (GObject *object)
+{
+ CkVtMonitor *vt_monitor;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (CK_IS_VT_MONITOR (object));
+
+ vt_monitor = CK_VT_MONITOR (object);
+
+ g_return_if_fail (vt_monitor->priv != NULL);
+
+ if (vt_monitor->priv->process_queue_id > 0) {
+ g_source_remove (vt_monitor->priv->process_queue_id);
+ }
+
+ if (vt_monitor->priv->event_queue != NULL) {
+ g_async_queue_unref (vt_monitor->priv->event_queue);
+ }
+
+ if (vt_monitor->priv->vt_thread_hash != NULL) {
+ g_hash_table_destroy (vt_monitor->priv->vt_thread_hash);
+ }
+
+ if (vt_monitor->priv->vfd != ERROR) {
+ close (vt_monitor->priv->vfd);
+ }
+
+ G_OBJECT_CLASS (ck_vt_monitor_parent_class)->finalize (object);
+}
+
+CkVtMonitor *
+ck_vt_monitor_new (void)
+{
+ if (vt_object != NULL) {
+ g_object_ref (vt_object);
+ } else {
+ vt_object = g_object_new (CK_TYPE_VT_MONITOR, NULL);
+
+ g_object_add_weak_pointer (vt_object,
+ (gpointer *) &vt_object);
+ }
+
+ return CK_VT_MONITOR (vt_object);
+}
diff --git a/src/ck-vt-monitor.h b/src/ck-vt-monitor.h
new file mode 100644
index 0000000..621be80
--- /dev/null
+++ b/src/ck-vt-monitor.h
@@ -0,0 +1,71 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2006 William Jon McCann <mccann@jhu.edu>
+ *
+ * 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.
+ *
+ */
+
+#ifndef __CK_VT_MONITOR_H
+#define __CK_VT_MONITOR_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define CK_TYPE_VT_MONITOR (ck_vt_monitor_get_type ())
+#define CK_VT_MONITOR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CK_TYPE_VT_MONITOR, CkVtMonitor))
+#define CK_VT_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), CK_TYPE_VT_MONITOR, CkVtMonitorClass))
+#define CK_IS_VT_MONITOR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), CK_TYPE_VT_MONITOR))
+#define CK_IS_VT_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), CK_TYPE_VT_MONITOR))
+#define CK_VT_MONITOR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), CK_TYPE_VT_MONITOR, CkVtMonitorClass))
+
+typedef struct CkVtMonitorPrivate CkVtMonitorPrivate;
+
+typedef struct
+{
+ GObject parent;
+ CkVtMonitorPrivate *priv;
+} CkVtMonitor;
+
+typedef struct
+{
+ GObjectClass parent_class;
+
+ void (* active_changed) (CkVtMonitor *vt_monitor,
+ guint num);
+} CkVtMonitorClass;
+
+typedef enum
+{
+ CK_VT_MONITOR_ERROR_GENERAL
+} CkVtMonitorError;
+
+#define CK_VT_MONITOR_ERROR ck_vt_monitor_error_quark ()
+
+GQuark ck_vt_monitor_error_quark (void);
+GType ck_vt_monitor_get_type (void);
+CkVtMonitor * ck_vt_monitor_new (void);
+
+gboolean ck_vt_monitor_set_active (CkVtMonitor *vt_monitor,
+ guint32 num,
+ GError **error);
+gboolean ck_vt_monitor_get_active (CkVtMonitor *vt_monitor,
+ guint32 *num,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* __CK_VT_MONITOR_H */
diff --git a/src/getfd.c b/src/getfd.c
new file mode 100644
index 0000000..69c37a3
--- /dev/null
+++ b/src/getfd.c
@@ -0,0 +1,75 @@
+/*
+ * Adapted from kbd-1.12
+ * License: GPL
+ *
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <linux/kd.h>
+#include <sys/ioctl.h>
+
+#ifdef HAVE_PATHS_H
+#include <paths.h>
+#endif /* HAVE_PATHS_H */
+
+/*
+ * getfd.c
+ *
+ * Get an fd for use with kbd/console ioctls.
+ * We try several things because opening /dev/console will fail
+ * if someone else used X (which does a chown on /dev/console).
+ */
+
+static int
+is_a_console (int fd)
+{
+ char arg;
+
+ arg = 0;
+ return (ioctl (fd, KDGKBTYPE, &arg) == 0
+ && ((arg == KB_101) || (arg == KB_84)));
+}
+
+static int
+open_a_console (char *fnam)
+{
+ int fd;
+
+ fd = open (fnam, O_RDONLY);
+ if (fd < 0 && errno == EACCES)
+ fd = open(fnam, O_WRONLY);
+ if (fd < 0 || ! is_a_console (fd))
+ return -1;
+ return fd;
+}
+
+int getfd (void)
+{
+ int fd;
+
+ fd = open_a_console (_PATH_TTY);
+ if (fd >= 0)
+ return fd;
+
+ fd = open_a_console ("/dev/tty");
+ if (fd >= 0)
+ return fd;
+
+ fd = open_a_console (_PATH_CONSOLE);
+ if (fd >= 0)
+ return fd;
+
+ fd = open_a_console ("/dev/console");
+ if (fd >= 0)
+ return fd;
+
+ for (fd = 0; fd < 3; fd++)
+ if (is_a_console (fd))
+ return fd;
+
+ return -1;
+}
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..90856db
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,354 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2006-2007 William Jon McCann <mccann@jhu.edu>
+ *
+ * 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 <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib-object.h>
+
+#define DBUS_API_SUBJECT_TO_CHANGE
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+
+#include "ck-manager.h"
+#include "ck-log.h"
+
+#define CK_DBUS_NAME "org.freedesktop.ConsoleKit"
+
+static void bus_proxy_destroyed_cb (DBusGProxy *bus_proxy,
+ CkManager *manager);
+
+static gboolean
+timed_exit_cb (GMainLoop *loop)
+{
+ g_main_loop_quit (loop);
+ return FALSE;
+}
+
+static DBusGProxy *
+get_bus_proxy (DBusGConnection *connection)
+{
+ DBusGProxy *bus_proxy;
+
+ bus_proxy = dbus_g_proxy_new_for_name (connection,
+ DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS);
+ return bus_proxy;
+}
+
+static gboolean
+acquire_name_on_proxy (DBusGProxy *bus_proxy)
+{
+ GError *error;
+ guint result;
+ gboolean res;
+ gboolean ret;
+
+ ret = FALSE;
+
+ if (bus_proxy == NULL) {
+ goto out;
+ }
+
+ error = NULL;
+ res = dbus_g_proxy_call (bus_proxy,
+ "RequestName",
+ &error,
+ G_TYPE_STRING, CK_DBUS_NAME,
+ G_TYPE_UINT, 0,
+ G_TYPE_INVALID,
+ G_TYPE_UINT, &result,
+ G_TYPE_INVALID);
+ if (! res) {
+ if (error != NULL) {
+ g_warning ("Failed to acquire %s: %s", CK_DBUS_NAME, error->message);
+ g_error_free (error);
+ } else {
+ g_warning ("Failed to acquire %s", CK_DBUS_NAME);
+ }
+ goto out;
+ }
+
+ if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
+ if (error != NULL) {
+ g_warning ("Failed to acquire %s: %s", CK_DBUS_NAME, error->message);
+ g_error_free (error);
+ } else {
+ g_warning ("Failed to acquire %s", CK_DBUS_NAME);
+ }
+ goto out;
+ }
+
+ ret = TRUE;
+
+ out:
+ return ret;
+}
+
+static DBusGConnection *
+get_system_bus (void)
+{
+ GError *error;
+ DBusGConnection *bus;
+ DBusConnection *connection;
+
+ error = NULL;
+ bus = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error);
+ if (bus == NULL) {
+ g_warning ("Couldn't connect to system bus: %s",
+ error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ connection = dbus_g_connection_get_connection (bus);
+ dbus_connection_set_exit_on_disconnect (connection, FALSE);
+
+ out:
+ return bus;
+}
+
+static gboolean
+bus_reconnect (CkManager *manager)
+{
+ DBusGConnection *bus;
+ DBusGProxy *bus_proxy;
+ gboolean ret;
+
+ ret = TRUE;
+
+ bus = get_system_bus ();
+ if (bus == NULL) {
+ goto out;
+ }
+
+ bus_proxy = get_bus_proxy (bus);
+ if (bus_proxy == NULL) {
+ g_warning ("Could not construct bus_proxy object; will retry");
+ goto out;
+ }
+
+ if (! acquire_name_on_proxy (bus_proxy) ) {
+ g_warning ("Could not acquire name; will retry");
+ goto out;
+ }
+
+ manager = ck_manager_new ();
+ if (manager == NULL) {
+ g_warning ("Could not construct manager object");
+ exit (1);
+ }
+
+ g_signal_connect (bus_proxy,
+ "destroy",
+ G_CALLBACK (bus_proxy_destroyed_cb),
+ manager);
+
+ g_debug ("Successfully reconnected to D-Bus");
+
+ ret = FALSE;
+
+ out:
+ return ret;
+}
+
+static void
+bus_proxy_destroyed_cb (DBusGProxy *bus_proxy,
+ CkManager *manager)
+{
+ g_debug ("Disconnected from D-Bus");
+
+ g_object_unref (manager);
+ manager = NULL;
+
+ g_timeout_add (3000, (GSourceFunc)bus_reconnect, manager);
+}
+
+static void
+delete_pid (void)
+{
+ unlink (CONSOLE_KIT_PID_FILE);
+}
+
+/* copied from nautilus */
+static int debug_log_pipes[2];
+
+static gboolean
+debug_log_io_cb (GIOChannel *io,
+ GIOCondition condition,
+ gpointer data)
+{
+ char a;
+
+ while (read (debug_log_pipes[0], &a, 1) != 1)
+ ;
+
+ ck_log_toggle_debug ();
+
+ return TRUE;
+}
+
+static void
+sigusr1_handler (int sig)
+{
+ while (write (debug_log_pipes[1], "a", 1) != 1)
+ ;
+}
+
+static void
+setup_debug_log_signals (void)
+{
+ struct sigaction sa;
+ GIOChannel *io;
+
+ if (pipe (debug_log_pipes) == -1) {
+ g_error ("Could not create pipe() for debug log");
+ }
+
+ io = g_io_channel_unix_new (debug_log_pipes[0]);
+ g_io_add_watch (io, G_IO_IN, debug_log_io_cb, NULL);
+
+ sa.sa_handler = sigusr1_handler;
+ sigemptyset (&sa.sa_mask);
+ sa.sa_flags = 0;
+ sigaction (SIGUSR1, &sa, NULL);
+}
+
+static void
+setup_debug_log (gboolean debug)
+{
+ ck_log_init ();
+ ck_log_set_debug (debug);
+ setup_debug_log_signals ();
+}
+
+int
+main (int argc,
+ char **argv)
+{
+ GMainLoop *loop;
+ CkManager *manager;
+ GOptionContext *context;
+ DBusGProxy *bus_proxy;
+ DBusGConnection *connection;
+ int ret;
+ int pf;
+ ssize_t written;
+ char pid[9];
+
+ static gboolean debug = FALSE;
+ static gboolean no_daemon = FALSE;
+ static gboolean do_timed_exit = FALSE;
+ static GOptionEntry entries [] = {
+ { "debug", 0, 0, G_OPTION_ARG_NONE, &debug, N_("Enable debugging code"), NULL },
+ { "no-daemon", 0, 0, G_OPTION_ARG_NONE, &no_daemon, N_("Don't become a daemon"), NULL },
+ { "timed-exit", 0, 0, G_OPTION_ARG_NONE, &do_timed_exit, N_("Exit after a time - for debugging"), NULL },
+ { NULL }
+ };
+
+ ret = 1;
+
+ if (! g_thread_supported ()) {
+ g_thread_init (NULL);
+ }
+ dbus_g_thread_init ();
+ g_type_init ();
+
+ context = g_option_context_new (_("Console kit daemon"));
+ g_option_context_add_main_entries (context, entries, NULL);
+ g_option_context_parse (context, &argc, &argv, NULL);
+ g_option_context_free (context);
+
+ if (! no_daemon && daemon (0, 0)) {
+ g_error ("Could not daemonize: %s", g_strerror (errno));
+ }
+
+ setup_debug_log (debug);
+
+ connection = get_system_bus ();
+ if (connection == NULL) {
+ goto out;
+ }
+
+ bus_proxy = get_bus_proxy (connection);
+ if (bus_proxy == NULL) {
+ g_warning ("Could not construct bus_proxy object; bailing out");
+ goto out;
+ }
+
+ if (! acquire_name_on_proxy (bus_proxy) ) {
+ g_warning ("Could not acquire name; bailing out");
+ goto out;
+ }
+
+ g_debug ("initializing console-kit-daemon %s", VERSION);
+
+ /* remove old pid file */
+ unlink (CONSOLE_KIT_PID_FILE);
+
+ /* make a new pid file */
+ if ((pf = open (CONSOLE_KIT_PID_FILE, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0644)) > 0) {
+ snprintf (pid, sizeof (pid), "%lu\n", (long unsigned) getpid ());
+ written = write (pf, pid, strlen (pid));
+ close (pf);
+ g_atexit (delete_pid);
+ }
+
+ manager = ck_manager_new ();
+
+ if (manager == NULL) {
+ goto out;
+ }
+
+ g_signal_connect (bus_proxy,
+ "destroy",
+ G_CALLBACK (bus_proxy_destroyed_cb),
+ manager);
+
+ loop = g_main_loop_new (NULL, FALSE);
+
+ if (do_timed_exit) {
+ g_timeout_add (1000 * 30, (GSourceFunc) timed_exit_cb, loop);
+ }
+
+ g_main_loop_run (loop);
+
+ g_object_unref (manager);
+
+ g_main_loop_unref (loop);
+
+ ret = 0;
+
+ out:
+
+ return ret;
+}
diff --git a/src/proc-linux.c b/src/proc-linux.c
new file mode 100644
index 0000000..d704e18
--- /dev/null
+++ b/src/proc-linux.c
@@ -0,0 +1,605 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2006 William Jon McCann <mccann@jhu.edu>
+ *
+ * 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 <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifdef HAVE_PATHS_H
+#include <paths.h>
+#endif /* HAVE_PATHS_H */
+
+#include "proc.h"
+
+/* adapted from procps */
+struct _proc_stat_t
+{
+ int pid;
+ int ppid; /* stat,status pid of parent process */
+ char state; /* stat,status single-char code for process state (S=sleeping) */
+ char cmd[16]; /* stat,status basename of executable file in call to exec(2) */
+ unsigned long long utime; /* stat user-mode CPU time accumulated by process */
+ unsigned long long stime; /* stat kernel-mode CPU time accumulated by process */
+ unsigned long long cutime; /* stat cumulative utime of process and reaped children */
+ unsigned long long cstime; /* stat cumulative stime of process and reaped children */
+ unsigned long long start_time; /* stat start time of process -- seconds since 1-1-70 */
+ unsigned long start_code; /* stat address of beginning of code segment */
+ unsigned long end_code; /* stat address of end of code segment */
+ unsigned long start_stack; /* stat address of the bottom of stack for the process */
+ unsigned long kstk_esp; /* stat kernel stack pointer */
+ unsigned long kstk_eip; /* stat kernel instruction pointer */
+ unsigned long wchan; /* stat (special) address of kernel wait channel proc is sleeping in */
+ long priority; /* stat kernel scheduling priority */
+ long nice; /* stat standard unix nice level of process */
+ long rss; /* stat resident set size from /proc/#/stat (pages) */
+ long alarm; /* stat ? */
+ unsigned long rtprio; /* stat real-time priority */
+ unsigned long sched; /* stat scheduling class */
+ unsigned long vsize; /* stat number of pages of virtual memory ... */
+ unsigned long rss_rlim; /* stat resident set size limit? */
+ unsigned long flags; /* stat kernel flags for the process */
+ unsigned long min_flt; /* stat number of minor page faults since process start */
+ unsigned long maj_flt; /* stat number of major page faults since process start */
+ unsigned long cmin_flt; /* stat cumulative min_flt of process and child processes */
+ unsigned long cmaj_flt; /* stat cumulative maj_flt of process and child processes */
+ int pgrp; /* stat process group id */
+ int session; /* stat session id */
+ int nlwp; /* stat number of threads, or 0 if no clue */
+ int tty; /* stat full device number of controlling terminal */
+ int tpgid; /* stat terminal process group id */
+ int exit_signal; /* stat might not be SIGCHLD */
+ int processor; /* stat current (or most recent?) CPU */
+};
+
+/* adapted from procps */
+#define MAJOR_OF(d) ( ((unsigned)(d)>>8u) & 0xfffu )
+#define MINOR_OF(d) ( ((unsigned)(d)&0xffu) | (((unsigned)(d)&0xfff00000u)>>12u) )
+
+typedef struct tty_map_node {
+ struct tty_map_node *next;
+ guint major_number;
+ guint minor_first;
+ guint minor_last;
+ char name[16];
+ char devfs_type;
+} tty_map_node;
+
+static tty_map_node *tty_map = NULL;
+
+/* adapted from procps */
+/* Load /proc/tty/drivers for device name mapping use. */
+static void
+load_drivers (void)
+{
+ char buf[10000];
+ char *p;
+ int fd;
+ int bytes;
+
+ fd = open ("/proc/tty/drivers", O_RDONLY);
+ if (fd == -1) {
+ goto fail;
+ }
+
+ bytes = read (fd, buf, sizeof (buf) - 1);
+ if (bytes == -1) {
+ goto fail;
+ }
+
+ buf[bytes] = '\0';
+ p = buf;
+ while ((p = strstr (p, " " _PATH_DEV))){
+ tty_map_node *tmn;
+ int len;
+ char *end;
+
+ p += 6;
+ end = strchr (p, ' ');
+ if (! end) {
+ continue;
+ }
+ len = end - p;
+ tmn = calloc (1, sizeof (tty_map_node));
+ tmn->next = tty_map;
+ tty_map = tmn;
+ /* if we have a devfs type name such as /dev/tts/%d then strip the %d but
+ keep a flag. */
+ if (len >= 3 && !strncmp (end - 2, "%d", 2)) {
+ len -= 2;
+ tmn->devfs_type = 1;
+ }
+ strncpy (tmn->name, p, len);
+ p = end; /* set p to point past the %d as well if there is one */
+ while (*p == ' ') {
+ p++;
+ }
+
+ tmn->major_number = atoi (p);
+ p += strspn (p, "0123456789");
+ while (*p == ' ') {
+ p++;
+ }
+ switch (sscanf (p, "%u-%u", &tmn->minor_first, &tmn->minor_last)) {
+ default:
+ /* Can't finish parsing this line so we remove it from the list */
+ tty_map = tty_map->next;
+ free (tmn);
+ break;
+ case 1:
+ tmn->minor_last = tmn->minor_first;
+ break;
+ case 2:
+ break;
+ }
+ }
+ fail:
+ if (fd != -1) {
+ close (fd);
+ }
+ if(! tty_map) {
+ tty_map = (tty_map_node *)-1;
+ }
+}
+
+/* adapted from procps */
+/* Try to guess the device name from /proc/tty/drivers info. */
+static char *
+driver_name (guint maj,
+ guint min)
+{
+ struct stat sbuf;
+ tty_map_node *tmn;
+ char *tty;
+
+ if (! tty_map) {
+ load_drivers ();
+ }
+ if (tty_map == (tty_map_node *) - 1) {
+ return 0;
+ }
+
+ tmn = tty_map;
+ for (;;) {
+ if (! tmn) {
+ return 0;
+ }
+ if (tmn->major_number == maj && tmn->minor_first <= min && tmn->minor_last >= min) {
+ break;
+ }
+ tmn = tmn->next;
+ }
+
+ tty = g_strdup_printf (_PATH_DEV "%s%d", tmn->name, min); /* like "/dev/ttyZZ255" */
+ if (stat (tty, &sbuf) < 0){
+ g_free (tty);
+
+ if (tmn->devfs_type) {
+ return NULL;
+ }
+
+ tty = g_strdup_printf (_PATH_DEV "%s", tmn->name); /* like "/dev/ttyZZ255" */
+
+ if (stat (tty, &sbuf) < 0) {
+ g_free (tty);
+ return NULL;
+ }
+ }
+
+ if (min != MINOR_OF (sbuf.st_rdev)) {
+ g_free (tty);
+ return NULL;
+ }
+
+ if (maj != MAJOR_OF (sbuf.st_rdev)) {
+ g_free (tty);
+ return NULL;
+ }
+
+ return tty;
+}
+
+/* adapted from procps */
+static char *
+link_name (guint maj,
+ guint min,
+ int pid,
+ const char *name)
+{
+ struct stat sbuf;
+ char *path;
+ char *tty;
+
+ path = g_strdup_printf ("/proc/%d/%s", pid, name);
+ tty = g_file_read_link (path, NULL);
+ g_free (path);
+
+ if (tty == NULL) {
+ goto out;
+ }
+
+ if (stat (tty, &sbuf) < 0) {
+ g_free (tty);
+ tty = NULL;
+ goto out;
+ }
+
+ if (min != MINOR_OF (sbuf.st_rdev)) {
+ g_free (tty);
+ tty = NULL;
+ goto out;
+
+ }
+ if (maj != MAJOR_OF (sbuf.st_rdev)) {
+ g_free (tty);
+ tty = NULL;
+ goto out;
+ }
+
+ out:
+ return tty;
+}
+
+pid_t
+proc_stat_get_ppid (proc_stat_t *stat)
+{
+ g_return_val_if_fail (stat != NULL, -1);
+
+ return stat->ppid;
+}
+
+char *
+proc_stat_get_cmd (proc_stat_t *stat)
+{
+ g_return_val_if_fail (stat != NULL, NULL);
+
+ return g_strdup (stat->cmd);
+}
+
+/* adapted from procps */
+char *
+proc_stat_get_tty (proc_stat_t *stat)
+{
+ guint dev;
+ char *tty;
+ guint dev_maj;
+ guint dev_min;
+ pid_t pid;
+
+ g_return_val_if_fail (stat != NULL, NULL);
+
+ pid = stat->pid;
+ dev = stat->tty;
+
+ if (dev == 0u) {
+ return NULL;
+ }
+
+ dev_maj = MAJOR_OF (dev);
+ dev_min = MINOR_OF (dev);
+
+ tty = link_name (dev_maj, dev_min, pid, "tty");
+ if (tty != NULL) {
+ goto out;
+ }
+
+ tty = driver_name (dev_maj, dev_min);
+ if (tty != NULL) {
+ goto out;
+ }
+
+ tty = link_name (dev_maj, dev_min, pid, "fd/2");
+ if (tty != NULL) {
+ goto out;
+ }
+
+ tty = link_name (dev_maj, dev_min, pid, "fd/255");
+ if (tty != NULL) {
+ goto out;
+ }
+
+ out:
+
+ return tty;
+}
+
+#define KLF "l"
+/* adapted from procps */
+static void
+stat2proc (const char *S,
+ proc_stat_t *P)
+{
+ unsigned num;
+ char * tmp;
+
+ /* fill in default values for older kernels */
+ P->processor = 0;
+ P->rtprio = -1;
+ P->sched = -1;
+ P->nlwp = 0;
+
+ S = strchr (S, '(') + 1;
+ tmp = strrchr (S, ')');
+ num = tmp - S;
+ if (G_UNLIKELY (num >= sizeof P->cmd)) {
+ num = sizeof P->cmd - 1;
+ }
+
+ memcpy (P->cmd, S, num);
+ P->cmd[num] = '\0';
+ S = tmp + 2; /* skip ") " */
+
+ num = sscanf (S,
+ "%c "
+ "%d %d %d %d %d "
+ "%lu %lu %lu %lu %lu "
+ "%Lu %Lu %Lu %Lu " /* utime stime cutime cstime */
+ "%ld %ld "
+ "%d "
+ "%ld "
+ "%Lu " /* start_time */
+ "%lu "
+ "%ld "
+ "%lu %"KLF"u %"KLF"u %"KLF"u %"KLF"u %"KLF"u "
+ "%*s %*s %*s %*s " /* discard, no RT signals & Linux 2.1 used hex */
+ "%"KLF"u %*lu %*lu "
+ "%d %d "
+ "%lu %lu",
+ &P->state,
+ &P->ppid, &P->pgrp, &P->session, &P->tty, &P->tpgid,
+ &P->flags, &P->min_flt, &P->cmin_flt, &P->maj_flt, &P->cmaj_flt,
+ &P->utime, &P->stime, &P->cutime, &P->cstime,
+ &P->priority, &P->nice,
+ &P->nlwp,
+ &P->alarm,
+ &P->start_time,
+ &P->vsize,
+ &P->rss,
+ &P->rss_rlim, &P->start_code, &P->end_code, &P->start_stack, &P->kstk_esp, &P->kstk_eip,
+ /* P->signal, P->blocked, P->sigignore, P->sigcatch, */ /* can't use */
+ &P->wchan, /* &P->nswap, &P->cnswap, */ /* nswap and cnswap dead for 2.4.xx and up */
+ /* -- Linux 2.0.35 ends here -- */
+ &P->exit_signal, &P->processor, /* 2.2.1 ends with "exit_signal" */
+ /* -- Linux 2.2.8 to 2.5.17 end here -- */
+ &P->rtprio, &P->sched /* both added to 2.5.18 */
+ );
+
+ if (!P->nlwp){
+ P->nlwp = 1;
+ }
+}
+
+gboolean
+proc_stat_new_for_pid (pid_t pid,
+ proc_stat_t **stat,
+ GError **error)
+{
+ char *path;
+ char *contents;
+ gsize length;
+ gboolean res;
+ GError *local_error;
+ proc_stat_t *proc;
+
+ g_return_val_if_fail (pid > 1, FALSE);
+
+ if (stat == NULL) {
+ return FALSE;
+ }
+
+ path = g_strdup_printf ("/proc/%d/stat", pid);
+
+ contents = NULL;
+ local_error = NULL;
+ res = g_file_get_contents (path,
+ &contents,
+ &length,
+ &local_error);
+ if (res) {
+ proc = g_new0 (proc_stat_t, 1);
+ proc->pid = pid;
+ stat2proc (contents, proc);
+ *stat = proc;
+ } else {
+ g_propagate_error (error, local_error);
+ *stat = NULL;
+ }
+
+ g_free (contents);
+ g_free (path);
+
+ return res;
+}
+
+void
+proc_stat_free (proc_stat_t *stat)
+{
+ g_free (stat);
+}
+
+GHashTable *
+proc_pid_get_env_hash (pid_t pid)
+{
+ char *path;
+ gboolean res;
+ char *contents;
+ gsize length;
+ GError *error;
+ GHashTable *hash;
+ int i;
+ gboolean last_was_null;
+
+ g_return_val_if_fail (pid > 1, NULL);
+
+ contents = NULL;
+ hash = NULL;
+
+ path = g_strdup_printf ("/proc/%u/environ", (guint)pid);
+
+ error = NULL;
+ res = g_file_get_contents (path,
+ &contents,
+ &length,
+ &error);
+ if (! res) {
+ g_warning ("Couldn't read %s: %s", path, error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ hash = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ g_free);
+
+ last_was_null = TRUE;
+ for (i = 0; i < length; i++) {
+ if (contents[i] == '\0') {
+ last_was_null = TRUE;
+ continue;
+ }
+ if (last_was_null) {
+ char **vals;
+ vals = g_strsplit (contents + i, "=", 2);
+ if (vals != NULL) {
+ g_hash_table_insert (hash, vals[0], vals[1]);
+ }
+ }
+ last_was_null = FALSE;
+ }
+
+ out:
+ g_free (contents);
+ g_free (path);
+
+ return hash;
+}
+
+char *
+proc_pid_get_env (pid_t pid,
+ const char *var)
+{
+ char *path;
+ gboolean res;
+ char *contents;
+ char *val;
+ gsize length;
+ GError *error;
+ int i;
+ char *prefix;
+ int prefix_len;
+ gboolean last_was_null;
+
+ g_return_val_if_fail (pid > 1, NULL);
+
+ val = NULL;
+ contents = NULL;
+ prefix = NULL;
+
+ path = g_strdup_printf ("/proc/%u/environ", (guint)pid);
+
+ error = NULL;
+ res = g_file_get_contents (path,
+ &contents,
+ &length,
+ &error);
+ if (! res) {
+ g_warning ("Couldn't read %s: %s", path, error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+
+ prefix = g_strdup_printf ("%s=", var);
+ prefix_len = strlen (prefix);
+
+ /* FIXME: make more robust */
+ last_was_null = TRUE;
+ for (i = 0; i < length; i++) {
+ if (contents[i] == '\0') {
+ last_was_null = TRUE;
+ continue;
+ }
+ if (last_was_null && g_str_has_prefix (contents + i, prefix)) {
+ val = g_strdup (contents + i + prefix_len);
+ break;
+ }
+ last_was_null = FALSE;
+ }
+
+ out:
+ g_free (prefix);
+ g_free (contents);
+ g_free (path);
+
+ return val;
+}
+
+uid_t
+proc_pid_get_uid (pid_t pid)
+{
+ struct stat st;
+ char *path;
+ int uid;
+ int res;
+
+ g_return_val_if_fail (pid > 1, 0);
+
+ uid = -1;
+
+ path = g_strdup_printf ("/proc/%u", (guint)pid);
+ res = stat (path, &st);
+ g_free (path);
+
+ if (res == 0) {
+ uid = st.st_uid;
+ }
+
+ return uid;
+}
+
+pid_t
+proc_pid_get_ppid (pid_t pid)
+{
+ int ppid;
+ gboolean res;
+ proc_stat_t *stat;
+
+ g_return_val_if_fail (pid > 1, 0);
+
+ ppid = -1;
+
+ res = proc_stat_new_for_pid (pid, &stat, NULL);
+ if (! res) {
+ goto out;
+ }
+
+ ppid = proc_stat_get_ppid (stat);
+
+ proc_stat_free (stat);
+
+ out:
+ return ppid;
+}
diff --git a/src/proc.h b/src/proc.h
new file mode 100644
index 0000000..8c3fc64
--- /dev/null
+++ b/src/proc.h
@@ -0,0 +1,48 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2006 William Jon McCann <mccann@jhu.edu>
+ *
+ * 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.
+ *
+ */
+
+#ifndef __PROC_H
+#define __PROC_H
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+typedef struct _proc_stat_t proc_stat_t;
+
+gboolean proc_stat_new_for_pid (pid_t pid,
+ proc_stat_t **stat,
+ GError **error);
+pid_t proc_stat_get_ppid (proc_stat_t *stat);
+char *proc_stat_get_tty (proc_stat_t *stat);
+char *proc_stat_get_cmd (proc_stat_t *stat);
+void proc_stat_free (proc_stat_t *stat);
+
+char *proc_pid_get_env (pid_t pid,
+ const char *var);
+
+GHashTable *proc_pid_get_env_hash (pid_t pid);
+
+pid_t proc_pid_get_ppid (pid_t pid);
+uid_t proc_pid_get_uid (pid_t pid);
+
+G_END_DECLS
+
+#endif /* __PROC_H */
diff --git a/src/test-open-session b/src/test-open-session
new file mode 100755
index 0000000..c037cee
--- /dev/null
+++ b/src/test-open-session
@@ -0,0 +1,46 @@
+#!/usr/bin/env python
+#
+# Test opening a new session
+#
+
+import os
+import gobject
+import dbus
+import dbus.glib
+
+print "uid=%d pid=%d" % (os.geteuid (), os.getpid ())
+
+bus = dbus.SystemBus ()
+
+manager_obj = bus.get_object ('org.freedesktop.ConsoleKit', '/org/freedesktop/ConsoleKit/Manager')
+
+manager = dbus.Interface (manager_obj, 'org.freedesktop.ConsoleKit.Manager')
+
+cookie = manager.OpenSession ()
+os.environ['XDG_SESSION_COOKIE'] = cookie
+print "Got cookie: %s" % cookie
+
+seats = manager.GetSeats ()
+for sid in seats:
+ seat_obj = bus.get_object ('org.freedesktop.ConsoleKit', sid)
+ seat = dbus.Interface (seat_obj, 'org.freedesktop.ConsoleKit.Seat')
+ sessions = seat.GetSessions ()
+ for ssid in sessions:
+ session_obj = bus.get_object ('org.freedesktop.ConsoleKit', ssid)
+ session = dbus.Interface (session_obj, 'org.freedesktop.ConsoleKit.Session')
+ uid = session.GetUser ()
+
+# Hmm looks like the proc environment doesn't get updated when
+# os.environ is set. So, we won't use GetCurrentSession.
+#current_session = manager.GetCurrentSession ()
+current_session = manager.GetSessionForCookie (cookie)
+print "Current session: %s" % current_session
+
+session_obj = bus.get_object ('org.freedesktop.ConsoleKit', ssid)
+session = dbus.Interface (session_obj, 'org.freedesktop.ConsoleKit.Session')
+
+mainloop = gobject.MainLoop ()
+
+gobject.timeout_add (30000, mainloop.quit)
+
+mainloop.run()
diff --git a/src/test-open-session-with-parameters b/src/test-open-session-with-parameters
new file mode 100755
index 0000000..e9e3358
--- /dev/null
+++ b/src/test-open-session-with-parameters
@@ -0,0 +1,54 @@
+#!/usr/bin/env python
+#
+# Test opening a new session
+#
+
+import os
+import gobject
+import dbus
+import dbus.glib
+
+bus = dbus.SystemBus ()
+
+manager_obj = bus.get_object ('org.freedesktop.ConsoleKit', '/org/freedesktop/ConsoleKit/Manager')
+
+manager = dbus.Interface (manager_obj, 'org.freedesktop.ConsoleKit.Manager')
+
+params = dbus.Array ([], signature = "sv")
+params.append (("user", dbus.Variant (730)))
+params.append (("session-type", dbus.Variant ("gnome-session")))
+#params.append (("x11-display", dbus.Variant (":0.0")))
+params.append (("display-device", dbus.Variant ("/dev/tty8")))
+params.append (("is-local", dbus.Variant (True)))
+params.append (("bogus-property", dbus.Variant ("Something bogus")))
+params.append (("cookie", dbus.Variant ("This should be ignored")))
+params.append (("id", dbus.Variant ("This should be ignored too")))
+
+cookie = manager.OpenSessionWithParameters (params)
+os.environ['XDG_SESSION_COOKIE'] = cookie
+print "Got cookie: %s" % cookie
+
+seats = manager.GetSeats ()
+for sid in seats:
+ seat_obj = bus.get_object ('org.freedesktop.ConsoleKit', sid)
+ seat = dbus.Interface (seat_obj, 'org.freedesktop.ConsoleKit.Seat')
+ sessions = seat.GetSessions ()
+ for ssid in sessions:
+ session_obj = bus.get_object ('org.freedesktop.ConsoleKit', ssid)
+ session = dbus.Interface (session_obj, 'org.freedesktop.ConsoleKit.Session')
+ uid = session.GetUser ()
+
+# Hmm looks like the proc environment doesn't get updated when
+# os.environ is set. So, we won't use GetCurrentSession.
+#current_session = manager.GetCurrentSession ()
+current_session = manager.GetSessionForCookie (cookie)
+print "Current session: %s" % current_session
+
+session_obj = bus.get_object ('org.freedesktop.ConsoleKit', ssid)
+session = dbus.Interface (session_obj, 'org.freedesktop.ConsoleKit.Session')
+
+mainloop = gobject.MainLoop ()
+
+gobject.timeout_add (30000, mainloop.quit)
+
+mainloop.run()
diff --git a/src/test-tty-idle-monitor.c b/src/test-tty-idle-monitor.c
new file mode 100644
index 0000000..7d44b26
--- /dev/null
+++ b/src/test-tty-idle-monitor.c
@@ -0,0 +1,82 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
+ *
+ * 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 <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <string.h>
+#include <errno.h>
+
+#include <locale.h>
+
+#include <glib.h>
+
+#include "ck-tty-idle-monitor.h"
+
+static void
+idle_changed_cb (CkTtyIdleMonitor *monitor,
+ gboolean idle_hint,
+ gpointer data)
+{
+ g_message ("idle hint changed: %s", idle_hint ? "idle" : "not idle");
+}
+
+int
+main (int argc, char **argv)
+{
+ GMainLoop *loop;
+ CkTtyIdleMonitor *monitor;
+ char *device;
+
+ g_type_init ();
+
+ if (argc < 2) {
+ device = g_file_read_link ("/proc/self/fd/0", NULL);
+ if (device == NULL) {
+ g_warning ("%s not a link", "/proc/self/fd/0");
+ exit (1);
+ }
+ } else {
+ device = g_strdup (argv[1]);
+ }
+
+ monitor = ck_tty_idle_monitor_new (device);
+
+ g_signal_connect (monitor,
+ "idle-hint-changed",
+ G_CALLBACK (idle_changed_cb),
+ NULL);
+ ck_tty_idle_monitor_set_threshold (monitor, 5);
+ ck_tty_idle_monitor_start (monitor);
+
+ loop = g_main_loop_new (NULL, FALSE);
+
+ g_main_loop_run (loop);
+
+ g_object_unref (monitor);
+
+ g_main_loop_unref (loop);
+
+ return 0;
+}
diff --git a/src/valgrind.sh b/src/valgrind.sh
new file mode 100755
index 0000000..a535c74
--- /dev/null
+++ b/src/valgrind.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+export G_DEBUG=gc-friendly
+export G_SLICE=always-malloc
+
+#valgrind --tool=massif --depth=5 --alloc-fn=g_malloc \
+# --alloc-fn=g_realloc --alloc-fn=g_try_malloc \
+# --alloc-fn=g_malloc0 --alloc-fn=g_mem_chunk_alloc \
+# console-kit-daemon --no-daemon --timed-exit
+
+valgrind --tool=memcheck --leak-check=full --show-reachable=yes \
+ console-kit-daemon --no-daemon --timed-exit