diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 41 | ||||
-rw-r--r-- | src/Makefile.in | 283 | ||||
-rw-r--r-- | src/ck-event-logger.c | 420 | ||||
-rw-r--r-- | src/ck-event-logger.h | 66 | ||||
-rw-r--r-- | src/ck-log-event.c | 1284 | ||||
-rw-r--r-- | src/ck-log-event.h | 148 | ||||
-rw-r--r-- | src/ck-manager-glue.h | 51 | ||||
-rw-r--r-- | src/ck-manager.c | 2013 | ||||
-rw-r--r-- | src/ck-manager.h | 17 | ||||
-rw-r--r-- | src/ck-manager.xml | 9 | ||||
-rw-r--r-- | src/ck-marshal.c | 8 | ||||
-rw-r--r-- | src/ck-run-programs.c | 230 | ||||
-rw-r--r-- | src/ck-run-programs.h | 38 | ||||
-rw-r--r-- | src/ck-seat-glue.h | 12 | ||||
-rw-r--r-- | src/ck-seat.c | 158 | ||||
-rw-r--r-- | src/ck-seat.h | 4 | ||||
-rw-r--r-- | src/ck-session-glue.h | 14 | ||||
-rw-r--r-- | src/ck-session-leader.c | 567 | ||||
-rw-r--r-- | src/ck-session-leader.h | 97 | ||||
-rw-r--r-- | src/ck-session.c | 107 | ||||
-rw-r--r-- | src/ck-session.h | 3 | ||||
-rw-r--r-- | src/ck-sysdeps-solaris.c | 45 | ||||
-rw-r--r-- | src/ck-sysdeps-unix.c | 40 | ||||
-rw-r--r-- | src/ck-vt-monitor.c | 61 | ||||
-rw-r--r-- | src/main.c | 66 | ||||
-rw-r--r-- | src/test-event-logger.c | 94 | ||||
-rwxr-xr-x | src/test-open-session-with-parameters | 2 |
27 files changed, 5236 insertions, 642 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 3f142a3..8d5d83b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -10,6 +10,7 @@ INCLUDES = \ -I. \ -I$(srcdir) \ $(CONSOLE_KIT_CFLAGS) \ + $(POLKIT_CFLAGS) \ $(DISABLE_DEPRECATED_CFLAGS) \ -DPREFIX=\""$(prefix)"\" \ -DBINDIR=\""$(bindir)"\" \ @@ -17,6 +18,7 @@ INCLUDES = \ -DLIBEXECDIR=\""$(libexecdir)"\" \ -DDATADIR=\""$(datadir)"\" \ -DSYSCONFDIR=\""$(sysconfdir)"\" \ + -DLOCALSTATEDIR=\""$(localstatedir)"\" \ -DCONSOLE_KIT_PID_FILE=\""$(CONSOLE_KIT_PID_FILE)"\" \ $(WARN_CFLAGS) \ $(DEBUG_CFLAGS) \ @@ -24,9 +26,15 @@ INCLUDES = \ $(NULL) noinst_LTLIBRARIES = \ - libck.la \ + libck.la \ + libck-event-log.la \ $(NULL) +libck_event_log_la_SOURCES = \ + ck-log-event.h \ + ck-log-event.c \ + $(NULL) + libck_la_SOURCES = \ ck-sysdeps.h \ ck-sysdeps-unix.c \ @@ -94,10 +102,16 @@ console_kit_daemon_SOURCES = \ ck-job.c \ ck-seat.h \ ck-seat.c \ + ck-session-leader.h \ + ck-session-leader.c \ ck-session.h \ ck-session.c \ ck-log.h \ ck-log.c \ + ck-run-programs.c \ + ck-run-programs.h \ + ck-event-logger.c \ + ck-event-logger.h \ $(BUILT_SOURCES) \ $(NULL) @@ -116,14 +130,31 @@ EXTRA_console_kit_daemon_SOURCES = \ console_kit_daemon_LDADD = \ $(CONSOLE_KIT_LIBS) \ + $(POLKIT_LIBS) \ + $(RBAC_LIBS) \ libck.la \ + libck-event-log.la \ $(NULL) noinst_PROGRAMS = \ + test-event-logger \ test-tty-idle-monitor \ test-vt-monitor \ $(NULL) +test_event_logger_SOURCES = \ + ck-event-logger.h \ + ck-event-logger.c \ + test-event-logger.c \ + $(NULL) + +test_event_logger_LDADD = \ + $(CONSOLE_KIT_LIBS) \ + $(POLKIT_LIBS) \ + $(RBAC_LIBS) \ + libck-event-log.la \ + $(NULL) + test_vt_monitor_SOURCES = \ ck-vt-monitor.h \ ck-vt-monitor.c \ @@ -132,6 +163,7 @@ test_vt_monitor_SOURCES = \ test_vt_monitor_LDADD = \ $(CONSOLE_KIT_LIBS) \ + $(POLKIT_LIBS) \ libck.la \ $(NULL) @@ -145,6 +177,7 @@ test_tty_idle_monitor_SOURCES = \ test_tty_idle_monitor_LDADD = \ $(CONSOLE_KIT_LIBS) \ + $(POLKIT_LIBS) \ libck.la \ $(NULL) @@ -163,3 +196,9 @@ CLEANFILES = $(BUILT_SOURCES) MAINTAINERCLEANFILES = \ *~ \ Makefile.in + +install-data-local: + -mkdir -p $(DESTDIR)$(sysconfdir)/ConsoleKit/run-session.d + -mkdir -p $(DESTDIR)$(libdir)/ConsoleKit/run-session.d + -mkdir -p $(DESTDIR)$(localstatedir)/run/ConsoleKit + -mkdir -p $(DESTDIR)$(localstatedir)/log/ConsoleKit diff --git a/src/Makefile.in b/src/Makefile.in index 2e660af..7021cd8 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -1,8 +1,8 @@ -# Makefile.in generated by automake 1.9.6 from Makefile.am. +# Makefile.in generated by automake 1.10 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, -# 2003, 2004, 2005 Free Software Foundation, Inc. +# 2003, 2004, 2005, 2006 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. @@ -15,15 +15,11 @@ @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 @@ -50,8 +46,9 @@ host_triplet = @host@ @CK_COMPILE_FREEBSD_TRUE@ $(NULL) sbin_PROGRAMS = console-kit-daemon$(EXEEXT) $(am__EXEEXT_1) -noinst_PROGRAMS = test-tty-idle-monitor$(EXEEXT) \ - test-vt-monitor$(EXEEXT) $(am__EXEEXT_1) +noinst_PROGRAMS = test-event-logger$(EXEEXT) \ + test-tty-idle-monitor$(EXEEXT) test-vt-monitor$(EXEEXT) \ + $(am__EXEEXT_1) subdir = src DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 @@ -63,12 +60,15 @@ mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = LTLIBRARIES = $(noinst_LTLIBRARIES) +libck_event_log_la_LIBADD = +am__objects_1 = +am_libck_event_log_la_OBJECTS = ck-log-event.lo $(am__objects_1) +libck_event_log_la_OBJECTS = $(am_libck_event_log_la_OBJECTS) am__DEPENDENCIES_1 = @CK_COMPILE_FREEBSD_TRUE@libck_la_DEPENDENCIES = \ @CK_COMPILE_FREEBSD_TRUE@ $(am__DEPENDENCIES_1) am__libck_la_SOURCES_DIST = ck-sysdeps.h ck-sysdeps-unix.c \ ck-sysdeps-linux.c ck-sysdeps-solaris.c ck-sysdeps-freebsd.c -am__objects_1 = @CK_COMPILE_LINUX_TRUE@am__objects_2 = ck-sysdeps-linux.lo \ @CK_COMPILE_LINUX_TRUE@ $(am__objects_1) @CK_COMPILE_SOLARIS_TRUE@am__objects_3 = ck-sysdeps-solaris.lo \ @@ -85,8 +85,10 @@ 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 ck-manager-glue.h ck-seat-glue.h ck-session-glue.h \ + ck-seat.h ck-seat.c ck-session-leader.h ck-session-leader.c \ + ck-session.h ck-session.c ck-log.h ck-log.c ck-run-programs.c \ + ck-run-programs.h ck-event-logger.c ck-event-logger.h \ + 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_5 = ck-marshal.$(OBJEXT) $(am__objects_1) @@ -95,11 +97,19 @@ am__objects_5 = ck-marshal.$(OBJEXT) $(am__objects_1) @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) $(am__objects_5) $(am__objects_1) \ - $(am__objects_6) + ck-job.$(OBJEXT) ck-seat.$(OBJEXT) ck-session-leader.$(OBJEXT) \ + ck-session.$(OBJEXT) ck-log.$(OBJEXT) \ + ck-run-programs.$(OBJEXT) ck-event-logger.$(OBJEXT) \ + $(am__objects_5) $(am__objects_1) $(am__objects_6) console_kit_daemon_OBJECTS = $(am_console_kit_daemon_OBJECTS) -console_kit_daemon_DEPENDENCIES = $(am__DEPENDENCIES_1) libck.la \ +console_kit_daemon_DEPENDENCIES = $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) libck.la \ + libck-event-log.la $(am__DEPENDENCIES_1) +am_test_event_logger_OBJECTS = ck-event-logger.$(OBJEXT) \ + test-event-logger.$(OBJEXT) $(am__objects_1) +test_event_logger_OBJECTS = $(am_test_event_logger_OBJECTS) +test_event_logger_DEPENDENCIES = $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) libck-event-log.la \ $(am__DEPENDENCIES_1) am__test_tty_idle_monitor_SOURCES_DIST = ck-tty-idle-monitor.h \ ck-tty-idle-monitor.c ck-file-monitor.h \ @@ -109,46 +119,51 @@ am_test_tty_idle_monitor_OBJECTS = ck-tty-idle-monitor.$(OBJEXT) \ $(am__objects_6) 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) libck.la \ - $(am__DEPENDENCIES_1) +test_tty_idle_monitor_DEPENDENCIES = $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) libck.la $(am__DEPENDENCIES_1) am_test_vt_monitor_OBJECTS = ck-vt-monitor.$(OBJEXT) \ test-vt-monitor.$(OBJEXT) $(am__objects_1) test_vt_monitor_OBJECTS = $(am_test_vt_monitor_OBJECTS) -test_vt_monitor_DEPENDENCIES = $(am__DEPENDENCIES_1) libck.la \ - $(am__DEPENDENCIES_1) -DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +test_vt_monitor_DEPENDENCIES = $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) libck.la $(am__DEPENDENCIES_1) +DEFAULT_INCLUDES = -I. -I$(top_builddir)@am__isrc@ 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) +LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --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 = $(libck_la_SOURCES) $(EXTRA_libck_la_SOURCES) \ - $(console_kit_daemon_SOURCES) \ +LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +SOURCES = $(libck_event_log_la_SOURCES) $(libck_la_SOURCES) \ + $(EXTRA_libck_la_SOURCES) $(console_kit_daemon_SOURCES) \ $(EXTRA_console_kit_daemon_SOURCES) \ - $(test_tty_idle_monitor_SOURCES) $(test_vt_monitor_SOURCES) -DIST_SOURCES = $(am__libck_la_SOURCES_DIST) $(EXTRA_libck_la_SOURCES) \ + $(test_event_logger_SOURCES) $(test_tty_idle_monitor_SOURCES) \ + $(test_vt_monitor_SOURCES) +DIST_SOURCES = $(libck_event_log_la_SOURCES) \ + $(am__libck_la_SOURCES_DIST) $(EXTRA_libck_la_SOURCES) \ $(am__console_kit_daemon_SOURCES_DIST) \ $(EXTRA_console_kit_daemon_SOURCES) \ + $(test_event_logger_SOURCES) \ $(am__test_tty_idle_monitor_SOURCES_DIST) \ $(test_vt_monitor_SOURCES) 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 + install-dvi-recursive install-exec-recursive \ + install-html-recursive install-info-recursive \ + install-pdf-recursive install-ps-recursive install-recursive \ + installcheck-recursive installdirs-recursive pdf-recursive \ + ps-recursive uninstall-recursive +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-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@ @@ -161,12 +176,6 @@ 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@ @@ -183,17 +192,11 @@ 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@ @@ -203,8 +206,10 @@ GMOFILES = @GMOFILES@ GMSGFMT = @GMSGFMT@ GREP = @GREP@ HAVE_PAM = @HAVE_PAM@ -HAVE_PAM_FALSE = @HAVE_PAM_FALSE@ -HAVE_PAM_TRUE = @HAVE_PAM_TRUE@ +HAVE_POLKIT = @HAVE_POLKIT@ +HISTORY_CFLAGS = @HISTORY_CFLAGS@ +HISTORY_LIBS = @HISTORY_LIBS@ +INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ @@ -221,9 +226,8 @@ LIBTOOL = @LIBTOOL@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAINT = @MAINT@ -MAINTAINER_MODE_FALSE = @MAINTAINER_MODE_FALSE@ -MAINTAINER_MODE_TRUE = @MAINTAINER_MODE_TRUE@ MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ MKINSTALLDIRS = @MKINSTALLDIRS@ MSGFMT = @MSGFMT@ MSGFMT_OPTS = @MSGFMT_OPTS@ @@ -239,10 +243,13 @@ PAM_MODULE_DIR = @PAM_MODULE_DIR@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ POFILES = @POFILES@ +POLKIT_CFLAGS = @POLKIT_CFLAGS@ +POLKIT_LIBS = @POLKIT_LIBS@ POSUB = @POSUB@ PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@ PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@ RANLIB = @RANLIB@ +RBAC_LIBS = @RBAC_LIBS@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ @@ -253,13 +260,14 @@ VERSION = @VERSION@ WARN_CFLAGS = @WARN_CFLAGS@ XGETTEXT = @XGETTEXT@ XMLTO = @XMLTO@ +Z_LIBS = @Z_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ 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@ @@ -271,6 +279,7 @@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ +builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ @@ -298,8 +307,11 @@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ AUTOMAKE_OPTIONS = 1.7 NULL = SUBDIRS = \ @@ -309,6 +321,7 @@ INCLUDES = \ -I. \ -I$(srcdir) \ $(CONSOLE_KIT_CFLAGS) \ + $(POLKIT_CFLAGS) \ $(DISABLE_DEPRECATED_CFLAGS) \ -DPREFIX=\""$(prefix)"\" \ -DBINDIR=\""$(bindir)"\" \ @@ -316,6 +329,7 @@ INCLUDES = \ -DLIBEXECDIR=\""$(libexecdir)"\" \ -DDATADIR=\""$(datadir)"\" \ -DSYSCONFDIR=\""$(sysconfdir)"\" \ + -DLOCALSTATEDIR=\""$(localstatedir)"\" \ -DCONSOLE_KIT_PID_FILE=\""$(CONSOLE_KIT_PID_FILE)"\" \ $(WARN_CFLAGS) \ $(DEBUG_CFLAGS) \ @@ -323,9 +337,15 @@ INCLUDES = \ $(NULL) noinst_LTLIBRARIES = \ - libck.la \ + libck.la \ + libck-event-log.la \ $(NULL) +libck_event_log_la_SOURCES = \ + ck-log-event.h \ + ck-log-event.c \ + $(NULL) + libck_la_SOURCES = ck-sysdeps.h ck-sysdeps-unix.c $(NULL) \ $(am__append_1) $(am__append_2) $(am__append_3) @CK_COMPILE_FREEBSD_TRUE@libck_la_LIBADD = $(KVM_LIBS) @@ -346,8 +366,10 @@ BUILT_SOURCES = \ 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 $(BUILT_SOURCES) $(NULL) $(FILE_MONITOR_BACKEND) + ck-seat.h ck-seat.c ck-session-leader.h ck-session-leader.c \ + ck-session.h ck-session.c ck-log.h ck-log.c ck-run-programs.c \ + ck-run-programs.h ck-event-logger.c ck-event-logger.h \ + $(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 = \ @@ -357,7 +379,23 @@ EXTRA_console_kit_daemon_SOURCES = \ console_kit_daemon_LDADD = \ $(CONSOLE_KIT_LIBS) \ + $(POLKIT_LIBS) \ + $(RBAC_LIBS) \ libck.la \ + libck-event-log.la \ + $(NULL) + +test_event_logger_SOURCES = \ + ck-event-logger.h \ + ck-event-logger.c \ + test-event-logger.c \ + $(NULL) + +test_event_logger_LDADD = \ + $(CONSOLE_KIT_LIBS) \ + $(POLKIT_LIBS) \ + $(RBAC_LIBS) \ + libck-event-log.la \ $(NULL) test_vt_monitor_SOURCES = \ @@ -368,6 +406,7 @@ test_vt_monitor_SOURCES = \ test_vt_monitor_LDADD = \ $(CONSOLE_KIT_LIBS) \ + $(POLKIT_LIBS) \ libck.la \ $(NULL) @@ -381,6 +420,7 @@ test_tty_idle_monitor_SOURCES = \ test_tty_idle_monitor_LDADD = \ $(CONSOLE_KIT_LIBS) \ + $(POLKIT_LIBS) \ libck.la \ $(NULL) @@ -442,8 +482,10 @@ clean-noinstLTLIBRARIES: echo "rm -f \"$${dir}/so_locations\""; \ rm -f "$${dir}/so_locations"; \ done +libck-event-log.la: $(libck_event_log_la_OBJECTS) $(libck_event_log_la_DEPENDENCIES) + $(LINK) $(libck_event_log_la_OBJECTS) $(libck_event_log_la_LIBADD) $(LIBS) libck.la: $(libck_la_OBJECTS) $(libck_la_DEPENDENCIES) - $(LINK) $(libck_la_LDFLAGS) $(libck_la_OBJECTS) $(libck_la_LIBADD) $(LIBS) + $(LINK) $(libck_la_OBJECTS) $(libck_la_LIBADD) $(LIBS) clean-noinstPROGRAMS: @list='$(noinst_PROGRAMS)'; for p in $$list; do \ @@ -453,7 +495,7 @@ clean-noinstPROGRAMS: done install-sbinPROGRAMS: $(sbin_PROGRAMS) @$(NORMAL_INSTALL) - test -z "$(sbindir)" || $(mkdir_p) "$(DESTDIR)$(sbindir)" + 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 \ @@ -481,13 +523,16 @@ clean-sbinPROGRAMS: 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) + $(LINK) $(console_kit_daemon_OBJECTS) $(console_kit_daemon_LDADD) $(LIBS) +test-event-logger$(EXEEXT): $(test_event_logger_OBJECTS) $(test_event_logger_DEPENDENCIES) + @rm -f test-event-logger$(EXEEXT) + $(LINK) $(test_event_logger_OBJECTS) $(test_event_logger_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) + $(LINK) $(test_tty_idle_monitor_OBJECTS) $(test_tty_idle_monitor_LDADD) $(LIBS) test-vt-monitor$(EXEEXT): $(test_vt_monitor_OBJECTS) $(test_vt_monitor_DEPENDENCIES) @rm -f test-vt-monitor$(EXEEXT) - $(LINK) $(test_vt_monitor_LDFLAGS) $(test_vt_monitor_OBJECTS) $(test_vt_monitor_LDADD) $(LIBS) + $(LINK) $(test_vt_monitor_OBJECTS) $(test_vt_monitor_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) @@ -495,13 +540,17 @@ mostlyclean-compile: distclean-compile: -rm -f *.tab.c +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ck-event-logger.Po@am__quote@ @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-event.Plo@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-run-programs.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ck-seat.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ck-session-leader.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ck-session.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ck-sysdeps-freebsd.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ck-sysdeps-linux.Plo@am__quote@ @@ -510,26 +559,27 @@ distclean-compile: @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)/main.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-event-logger.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-tty-idle-monitor.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-vt-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 +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @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 +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @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 +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @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 $@ $< @@ -540,10 +590,6 @@ mostlyclean-libtool: 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, @@ -575,8 +621,7 @@ $(RECURSIVE_TARGETS): $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ fi; test -z "$$fail" -mostlyclean-recursive clean-recursive distclean-recursive \ -maintainer-clean-recursive: +$(RECURSIVE_CLEAN_TARGETS): @failcom='exit 1'; \ for f in x $$MAKEFLAGS; do \ case $$f in \ @@ -677,22 +722,21 @@ 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; \ + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ 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 \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ fi; \ @@ -706,7 +750,7 @@ distdir: $(DISTFILES) list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test -d "$(distdir)/$$subdir" \ - || $(mkdir_p) "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ || exit 1; \ distdir=`$(am__cd) $(distdir) && pwd`; \ top_distdir=`$(am__cd) $(top_distdir) && pwd`; \ @@ -714,6 +758,8 @@ distdir: $(DISTFILES) $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$$top_distdir" \ distdir="$$distdir/$$subdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ distdir) \ || exit 1; \ fi; \ @@ -725,7 +771,7 @@ all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) installdirs: installdirs-recursive installdirs-am: for dir in "$(DESTDIR)$(sbindir)"; do \ - test -z "$$dir" || $(mkdir_p) "$$dir"; \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) install-recursive @@ -764,7 +810,7 @@ distclean: distclean-recursive -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ - distclean-libtool distclean-tags + distclean-tags dvi: dvi-recursive @@ -776,14 +822,22 @@ info: info-recursive info-am: -install-data-am: +install-data-am: install-data-local + +install-dvi: install-dvi-recursive install-exec-am: install-sbinPROGRAMS +install-html: install-html-recursive + install-info: install-info-recursive install-man: +install-pdf: install-pdf-recursive + +install-ps: install-ps-recursive + installcheck-am: maintainer-clean: maintainer-clean-recursive @@ -804,24 +858,27 @@ 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-noinstLTLIBRARIES \ - 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 \ +uninstall-am: uninstall-sbinPROGRAMS + +.MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) install-am \ + install-strip + +.PHONY: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) CTAGS GTAGS \ + all all-am check check-am clean clean-generic clean-libtool \ + clean-noinstLTLIBRARIES clean-noinstPROGRAMS \ + clean-sbinPROGRAMS ctags ctags-recursive distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am \ + install-data-local install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-ps install-ps-am install-sbinPROGRAMS install-strip \ + installcheck installcheck-am installdirs installdirs-am \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-recursive uninstall uninstall-am \ - uninstall-info-am uninstall-sbinPROGRAMS + uninstall-sbinPROGRAMS ck-manager-glue.h: ck-manager.xml Makefile.am @@ -837,6 +894,12 @@ ck-marshal.c: ck-marshal.list ck-marshal.h: ck-marshal.list @GLIB_GENMARSHAL@ $< --prefix=ck_marshal --header > $@ + +install-data-local: + -mkdir -p $(DESTDIR)$(sysconfdir)/ConsoleKit/run-session.d + -mkdir -p $(DESTDIR)$(libdir)/ConsoleKit/run-session.d + -mkdir -p $(DESTDIR)$(localstatedir)/run/ConsoleKit + -mkdir -p $(DESTDIR)$(localstatedir)/log/ConsoleKit # 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-event-logger.c b/src/ck-event-logger.c new file mode 100644 index 0000000..2fded87 --- /dev/null +++ b/src/ck-event-logger.c @@ -0,0 +1,420 @@ +/* -*- 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 <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> + +#include <glib.h> +#include <glib/gi18n.h> +#include <glib/gstdio.h> +#include <glib-object.h> + +#include "ck-event-logger.h" +#include "ck-log-event.h" + +#define CK_EVENT_LOGGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), CK_TYPE_EVENT_LOGGER, CkEventLoggerPrivate)) + +#define DEFAULT_LOG_FILENAME LOCALSTATEDIR "/log/ConsoleKit/history" + +struct CkEventLoggerPrivate +{ + int fd; + FILE *file; + GThread *writer_thread; + GAsyncQueue *event_queue; + char *log_filename; +}; + +enum { + PROP_0, + PROP_LOG_FILENAME +}; + +static void ck_event_logger_class_init (CkEventLoggerClass *klass); +static void ck_event_logger_init (CkEventLogger *event_logger); +static void ck_event_logger_finalize (GObject *object); + +G_DEFINE_TYPE (CkEventLogger, ck_event_logger, G_TYPE_OBJECT) + +GQuark +ck_event_logger_error_quark (void) +{ + static GQuark ret = 0; + if (ret == 0) { + ret = g_quark_from_static_string ("ck_event_logger_error"); + } + + return ret; +} + +gboolean +ck_event_logger_queue_event (CkEventLogger *event_logger, + CkLogEvent *event, + GError **error) +{ + CkLogEvent *event_copy; + gboolean ret; + + g_return_val_if_fail (CK_IS_EVENT_LOGGER (event_logger), FALSE); + g_return_val_if_fail (event != NULL, FALSE); + + event_copy = ck_log_event_copy (event); + + g_async_queue_push (event_logger->priv->event_queue, + event_copy); + + ret = TRUE; + + return ret; +} + +/* Adapted from auditd auditd-event.c */ +static gboolean +open_log_file (CkEventLogger *event_logger) +{ + int flags; + int fd; + int res; + char *dirname; + + /* + * Likely errors on rotate: ENFILE, ENOMEM, ENOSPC + */ + flags = O_WRONLY | O_APPEND; +#ifdef O_NOFOLLOW + flags |= O_NOFOLLOW; +#endif + + dirname = g_path_get_dirname (event_logger->priv->log_filename); + /* always make sure we have a directory */ + errno = 0; + res = g_mkdir_with_parents (dirname, + S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); + if (res < 0) { + g_warning ("Unable to create directory %s (%s)", + dirname, + g_strerror (errno)); + g_free (dirname); + return FALSE; + } + g_free (dirname); + +retry: + errno = 0; + fd = g_open (event_logger->priv->log_filename, flags, 0600); + if (fd < 0) { + if (errno == ENOENT) { + fd = g_open (event_logger->priv->log_filename, + O_CREAT | O_EXCL | O_APPEND, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (fd < 0) { + g_warning ("Couldn't create log file %s (%s)", + event_logger->priv->log_filename, + g_strerror (errno)); + return FALSE; + } + + close (fd); + fd = g_open (event_logger->priv->log_filename, flags, 0600); + } else if (errno == ENFILE) { + /* All system descriptors used, try again... */ + goto retry; + } + if (fd < 0) { + g_warning ("Couldn't open log file %s (%s)", + event_logger->priv->log_filename, + g_strerror (errno)); + return FALSE; + } + } + + if (fcntl (fd, F_SETFD, FD_CLOEXEC) == -1) { + close (fd); + g_warning ("Error setting log file CLOEXEC flag (%s)", + g_strerror (errno)); + return FALSE; + } + + fchown (fd, 0, 0); + + event_logger->priv->file = fdopen (fd, "a"); + if (event_logger->priv->file == NULL) { + g_warning ("Error setting up log descriptor (%s)", + g_strerror (errno)); + close (fd); + return FALSE; + } + event_logger->priv->fd = fd; + + /* Set it to line buffering */ + setlinebuf (event_logger->priv->file); + + return TRUE; +} + +static void +reopen_file_stream (CkEventLogger *event_logger) +{ + close (event_logger->priv->fd); + fclose (event_logger->priv->file); + + /* FIXME: retries */ + open_log_file (event_logger); +} + +static void +check_file_stream (CkEventLogger *event_logger) +{ + int old_fd; + struct stat old_stats; + int new_fd; + struct stat new_stats; + + old_fd = event_logger->priv->fd; + if (fstat (old_fd, &old_stats) != 0) { + g_warning ("Unable to stat file: %s", + g_strerror (errno)); + reopen_file_stream (event_logger); + return; + } + + new_fd = g_open (event_logger->priv->log_filename, O_RDONLY | O_NONBLOCK, 0600); + if (new_fd == -1 || fstat (new_fd, &new_stats) < 0) { + close (new_fd); + g_debug ("Unable to open or stat %s - will try to reopen", event_logger->priv->log_filename); + reopen_file_stream (event_logger); + return; + } + close (new_fd); + + if (old_stats.st_ino != new_stats.st_ino || old_stats.st_dev != new_stats.st_dev) { + g_debug ("File %s has been replaced; writing to end of new file", event_logger->priv->log_filename); + reopen_file_stream (event_logger); + return; + } +} + +static gboolean +write_log_for_event (CkEventLogger *event_logger, + CkLogEvent *event) +{ + GString *str; + + str = g_string_new (NULL); + + ck_log_event_to_string (event, str); + + g_debug ("Writing log for event: %s", str->str); + check_file_stream (event_logger); + + if (event_logger->priv->file != NULL) { + int rc; + + rc = fprintf (event_logger->priv->file, "%s\n", str->str); + if (rc <= 0) { + g_warning ("Record was not written to disk (%s)", + g_strerror (errno)); + } + } else { + g_warning ("Log file not open for writing"); + } + + g_string_free (str, TRUE); + + return TRUE; +} + +static void * +writer_thread_start (CkEventLogger *event_logger) +{ + CkLogEvent *event; + + while ((event = g_async_queue_pop (event_logger->priv->event_queue)) != NULL) { + write_log_for_event (event_logger, event); + ck_log_event_free (event); + } + + g_thread_exit (NULL); + return NULL; +} + +static void +create_writer_thread (CkEventLogger *event_logger) +{ + GError *error; + + g_debug ("Creating thread for log writing"); + + error = NULL; + event_logger->priv->writer_thread = g_thread_create_full ((GThreadFunc)writer_thread_start, + event_logger, + 65536, + FALSE, + TRUE, + G_THREAD_PRIORITY_NORMAL, + &error); + if (event_logger->priv->writer_thread == NULL) { + g_debug ("Unable to create thread: %s", error->message); + g_error_free (error); + } +} + +static GObject * +ck_event_logger_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_properties) +{ + CkEventLogger *event_logger; + CkEventLoggerClass *klass; + + klass = CK_EVENT_LOGGER_CLASS (g_type_class_peek (CK_TYPE_EVENT_LOGGER)); + + event_logger = CK_EVENT_LOGGER (G_OBJECT_CLASS (ck_event_logger_parent_class)->constructor (type, + n_construct_properties, + construct_properties)); + + if (open_log_file (event_logger)) { + create_writer_thread (event_logger); + } + + return G_OBJECT (event_logger); +} + +static void +_ck_event_logger_set_log_filename (CkEventLogger *event_logger, + const char *filename) +{ + g_free (event_logger->priv->log_filename); + event_logger->priv->log_filename = g_strdup (filename); +} + +static void +ck_event_logger_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + CkEventLogger *self; + + self = CK_EVENT_LOGGER (object); + + switch (prop_id) { + case PROP_LOG_FILENAME: + _ck_event_logger_set_log_filename (self, g_value_get_string (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +ck_event_logger_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + CkEventLogger *self; + + self = CK_EVENT_LOGGER (object); + + switch (prop_id) { + case PROP_LOG_FILENAME: + g_value_set_string (value, self->priv->log_filename); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +ck_event_logger_class_init (CkEventLoggerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = ck_event_logger_finalize; + object_class->constructor = ck_event_logger_constructor; + object_class->get_property = ck_event_logger_get_property; + object_class->set_property = ck_event_logger_set_property; + + g_object_class_install_property (object_class, + PROP_LOG_FILENAME, + g_param_spec_string ("log-filename", + "log-filename", + "log-filename", + DEFAULT_LOG_FILENAME, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_type_class_add_private (klass, sizeof (CkEventLoggerPrivate)); +} + +static void +ck_event_logger_init (CkEventLogger *event_logger) +{ + event_logger->priv = CK_EVENT_LOGGER_GET_PRIVATE (event_logger); + + event_logger->priv->event_queue = g_async_queue_new (); +} + +static void +ck_event_logger_finalize (GObject *object) +{ + CkEventLogger *event_logger; + + g_return_if_fail (object != NULL); + g_return_if_fail (CK_IS_EVENT_LOGGER (object)); + + event_logger = CK_EVENT_LOGGER (object); + + g_return_if_fail (event_logger->priv != NULL); + + if (event_logger->priv->event_queue != NULL) { + g_async_queue_unref (event_logger->priv->event_queue); + } + + if (event_logger->priv->fd != -1) { + close (event_logger->priv->fd); + } + + g_free (event_logger->priv->log_filename); + + G_OBJECT_CLASS (ck_event_logger_parent_class)->finalize (object); +} + +CkEventLogger * +ck_event_logger_new (const char *filename) +{ + GObject *object; + + object = g_object_new (CK_TYPE_EVENT_LOGGER, + "log-filename", filename, + NULL); + + return CK_EVENT_LOGGER (object); +} diff --git a/src/ck-event-logger.h b/src/ck-event-logger.h new file mode 100644 index 0000000..d4645f7 --- /dev/null +++ b/src/ck-event-logger.h @@ -0,0 +1,66 @@ +/* -*- 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_EVENT_LOGGER_H +#define __CK_EVENT_LOGGER_H + +#include <glib-object.h> +#include "ck-log-event.h" + +G_BEGIN_DECLS + +#define CK_TYPE_EVENT_LOGGER (ck_event_logger_get_type ()) +#define CK_EVENT_LOGGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CK_TYPE_EVENT_LOGGER, CkEventLogger)) +#define CK_EVENT_LOGGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), CK_TYPE_EVENT_LOGGER, CkEventLoggerClass)) +#define CK_IS_EVENT_LOGGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), CK_TYPE_EVENT_LOGGER)) +#define CK_IS_EVENT_LOGGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), CK_TYPE_EVENT_LOGGER)) +#define CK_EVENT_LOGGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), CK_TYPE_EVENT_LOGGER, CkEventLoggerClass)) + +typedef struct CkEventLoggerPrivate CkEventLoggerPrivate; + +typedef struct +{ + GObject parent; + CkEventLoggerPrivate *priv; +} CkEventLogger; + +typedef struct +{ + GObjectClass parent_class; +} CkEventLoggerClass; + +typedef enum +{ + CK_EVENT_LOGGER_ERROR_GENERAL +} CkEventLoggerError; + +#define CK_EVENT_LOGGER_ERROR ck_event_logger_error_quark () + +GQuark ck_event_logger_error_quark (void); +GType ck_event_logger_get_type (void); +CkEventLogger * ck_event_logger_new (const char *filename); + +gboolean ck_event_logger_queue_event (CkEventLogger *event_logger, + CkLogEvent *event, + GError **error); + +G_END_DECLS + +#endif /* __CK_EVENT_LOGGER_H */ diff --git a/src/ck-log-event.c b/src/ck-log-event.c new file mode 100644 index 0000000..7e8ce10 --- /dev/null +++ b/src/ck-log-event.c @@ -0,0 +1,1284 @@ +/* -*- 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 <string.h> + +#include <glib.h> + +#include "ck-log-event.h" + +static void +event_seat_added_free (CkLogSeatAddedEvent *event) +{ + g_assert (event != NULL); + + g_free (event->seat_id); + event->seat_id = NULL; +} + +static void +event_seat_removed_free (CkLogSeatRemovedEvent *event) +{ + g_assert (event != NULL); + + g_free (event->seat_id); + event->seat_id = NULL; +} + +static void +event_system_stop_free (CkLogSystemStopEvent *event) +{ + g_assert (event != NULL); +} + +static void +event_system_restart_free (CkLogSystemRestartEvent *event) +{ + g_assert (event != NULL); +} + + +static void +event_system_start_free (CkLogSystemStartEvent *event) +{ + g_assert (event != NULL); +} + +static void +event_seat_session_added_free (CkLogSeatSessionAddedEvent *event) +{ + g_assert (event != NULL); + + g_free (event->seat_id); + event->seat_id = NULL; + + g_free (event->session_id); + event->session_id = NULL; + g_free (event->session_type); + event->session_type = NULL; + g_free (event->session_x11_display); + event->session_x11_display = NULL; + g_free (event->session_x11_display_device); + event->session_x11_display_device = NULL; + g_free (event->session_display_device); + event->session_display_device = NULL; + g_free (event->session_remote_host_name); + event->session_remote_host_name = NULL; + g_free (event->session_creation_time); + event->session_creation_time = NULL; +} + +static void +event_seat_session_removed_free (CkLogSeatSessionRemovedEvent *event) +{ + g_assert (event != NULL); + + g_free (event->seat_id); + event->seat_id = NULL; + + g_free (event->session_id); + event->session_id = NULL; + g_free (event->session_type); + event->session_type = NULL; + g_free (event->session_x11_display); + event->session_x11_display = NULL; + g_free (event->session_x11_display_device); + event->session_x11_display_device = NULL; + g_free (event->session_display_device); + event->session_display_device = NULL; + g_free (event->session_remote_host_name); + event->session_remote_host_name = NULL; + g_free (event->session_creation_time); + event->session_creation_time = NULL; +} + +static void +event_seat_active_session_changed_free (CkLogSeatActiveSessionChangedEvent *event) +{ + g_assert (event != NULL); + + g_free (event->seat_id); + event->seat_id = NULL; + + g_free (event->session_id); + event->session_id = NULL; +} + +static void +event_seat_device_added_free (CkLogSeatDeviceAddedEvent *event) +{ + g_assert (event != NULL); + + g_free (event->seat_id); + event->seat_id = NULL; + g_free (event->device_id); + event->device_id = NULL; + g_free (event->device_type); + event->device_type = NULL; +} + +static void +event_seat_device_removed_free (CkLogSeatDeviceRemovedEvent *event) +{ + g_assert (event != NULL); + + g_free (event->seat_id); + event->seat_id = NULL; + g_free (event->device_id); + event->device_id = NULL; + g_free (event->device_type); + event->device_type = NULL; +} + +static void +event_seat_added_copy (CkLogSeatAddedEvent *event, + CkLogSeatAddedEvent *event_copy) +{ + g_assert (event != NULL); + g_assert (event_copy != NULL); + + event_copy->seat_id = g_strdup (event->seat_id); + event_copy->seat_kind = event->seat_kind; +} + +static void +event_seat_removed_copy (CkLogSeatRemovedEvent *event, + CkLogSeatRemovedEvent *event_copy) +{ + g_assert (event != NULL); + g_assert (event_copy != NULL); + + event_copy->seat_id = g_strdup (event->seat_id); + event_copy->seat_kind = event->seat_kind; +} + +static void +event_system_stop_copy (CkLogSystemStopEvent *event, + CkLogSystemStopEvent *event_copy) +{ + g_assert (event != NULL); + g_assert (event_copy != NULL); +} + +static void +event_system_restart_copy (CkLogSystemRestartEvent *event, + CkLogSystemRestartEvent *event_copy) +{ + g_assert (event != NULL); + g_assert (event_copy != NULL); +} + + +static void +event_system_start_copy (CkLogSystemStartEvent *event, + CkLogSystemStartEvent *event_copy) +{ + g_assert (event != NULL); + g_assert (event_copy != NULL); +} + +static void +event_seat_session_added_copy (CkLogSeatSessionAddedEvent *event, + CkLogSeatSessionAddedEvent *event_copy) +{ + g_assert (event != NULL); + g_assert (event_copy != NULL); + + event_copy->seat_id = g_strdup (event->seat_id); + event_copy->session_id = g_strdup (event->session_id); + event_copy->session_type = g_strdup (event->session_type); + event_copy->session_x11_display = g_strdup (event->session_x11_display); + event_copy->session_x11_display_device = g_strdup (event->session_x11_display_device); + event_copy->session_display_device = g_strdup (event->session_display_device); + event_copy->session_remote_host_name = g_strdup (event->session_remote_host_name); + event_copy->session_is_local = event->session_is_local; + event_copy->session_unix_user = event->session_unix_user; + event_copy->session_creation_time = g_strdup (event->session_creation_time); +} + +static void +event_seat_session_removed_copy (CkLogSeatSessionRemovedEvent *event, + CkLogSeatSessionRemovedEvent *event_copy) +{ + g_assert (event != NULL); + g_assert (event_copy != NULL); + + event_copy->seat_id = g_strdup (event->seat_id); + event_copy->session_id = g_strdup (event->session_id); + event_copy->session_type = g_strdup (event->session_type); + event_copy->session_x11_display = g_strdup (event->session_x11_display); + event_copy->session_x11_display_device = g_strdup (event->session_x11_display_device); + event_copy->session_display_device = g_strdup (event->session_display_device); + event_copy->session_remote_host_name = g_strdup (event->session_remote_host_name); + event_copy->session_is_local = event->session_is_local; + event_copy->session_unix_user = event->session_unix_user; + event_copy->session_creation_time = g_strdup (event->session_creation_time); +} + +static void +event_seat_active_session_changed_copy (CkLogSeatActiveSessionChangedEvent *event, + CkLogSeatActiveSessionChangedEvent *event_copy) +{ + g_assert (event != NULL); + g_assert (event_copy != NULL); + + event_copy->seat_id = g_strdup (event->seat_id); + event_copy->session_id = g_strdup (event->session_id); +} + +static void +event_seat_device_added_copy (CkLogSeatDeviceAddedEvent *event, + CkLogSeatDeviceAddedEvent *event_copy) +{ + g_assert (event != NULL); + g_assert (event_copy != NULL); + + event_copy->seat_id = g_strdup (event->seat_id); + event_copy->device_id = g_strdup (event->device_id); + event_copy->device_type = g_strdup (event->device_type); +} + +static void +event_seat_device_removed_copy (CkLogSeatDeviceRemovedEvent *event, + CkLogSeatDeviceRemovedEvent *event_copy) +{ + g_assert (event != NULL); + g_assert (event_copy != NULL); + + event_copy->seat_id = g_strdup (event->seat_id); + event_copy->device_id = g_strdup (event->device_id); + event_copy->device_type = g_strdup (event->device_type); +} + +CkLogEvent * +ck_log_event_copy (CkLogEvent *event) +{ + CkLogEvent *event_copy; + + if (event == NULL) { + return NULL; + } + + event_copy = g_new0 (CkLogEvent, 1); + + event_copy->type = event->type; + event_copy->timestamp = event->timestamp; + + switch (event->type) { + case CK_LOG_EVENT_SEAT_ADDED: + event_seat_added_copy ((CkLogSeatAddedEvent *) event, + (CkLogSeatAddedEvent *) event_copy); + break; + case CK_LOG_EVENT_SEAT_REMOVED: + event_seat_removed_copy ((CkLogSeatRemovedEvent *) event, + (CkLogSeatRemovedEvent *) event_copy); + break; + case CK_LOG_EVENT_SYSTEM_STOP: + event_system_stop_copy ((CkLogSystemStopEvent *) event, + (CkLogSystemStopEvent *) event_copy); + break; + case CK_LOG_EVENT_SYSTEM_RESTART: + event_system_restart_copy ((CkLogSystemRestartEvent *) event, + (CkLogSystemRestartEvent *) event_copy); + break; + case CK_LOG_EVENT_SYSTEM_START: + event_system_start_copy ((CkLogSystemStartEvent *) event, + (CkLogSystemStartEvent *) event_copy); + break; + case CK_LOG_EVENT_SEAT_SESSION_ADDED: + event_seat_session_added_copy ((CkLogSeatSessionAddedEvent *) event, + (CkLogSeatSessionAddedEvent *) event_copy); + break; + case CK_LOG_EVENT_SEAT_SESSION_REMOVED: + event_seat_session_removed_copy ((CkLogSeatSessionRemovedEvent *) event, + (CkLogSeatSessionRemovedEvent *) event_copy); + break; + case CK_LOG_EVENT_SEAT_DEVICE_ADDED: + event_seat_device_added_copy ((CkLogSeatDeviceAddedEvent *) event, + (CkLogSeatDeviceAddedEvent *) event_copy); + break; + case CK_LOG_EVENT_SEAT_DEVICE_REMOVED: + event_seat_device_removed_copy ((CkLogSeatDeviceRemovedEvent *) event, + (CkLogSeatDeviceRemovedEvent *) event_copy); + break; + case CK_LOG_EVENT_SEAT_ACTIVE_SESSION_CHANGED: + event_seat_active_session_changed_copy ((CkLogSeatActiveSessionChangedEvent *) event, + (CkLogSeatActiveSessionChangedEvent *) event_copy); + break; + default: + g_assert_not_reached (); + break; + } + + return event_copy; +} + +void +ck_log_event_free (CkLogEvent *event) +{ + switch (event->type) { + case CK_LOG_EVENT_SEAT_ADDED: + event_seat_added_free ((CkLogSeatAddedEvent *) event); + break; + case CK_LOG_EVENT_SEAT_REMOVED: + event_seat_removed_free ((CkLogSeatRemovedEvent *) event); + break; + case CK_LOG_EVENT_SYSTEM_STOP: + event_system_stop_free ((CkLogSystemStopEvent *) event); + break; + case CK_LOG_EVENT_SYSTEM_RESTART: + event_system_restart_free ((CkLogSystemRestartEvent *) event); + break; + case CK_LOG_EVENT_SYSTEM_START: + event_system_start_free ((CkLogSystemStartEvent *) event); + break; + case CK_LOG_EVENT_SEAT_SESSION_ADDED: + event_seat_session_added_free ((CkLogSeatSessionAddedEvent *) event); + break; + case CK_LOG_EVENT_SEAT_SESSION_REMOVED: + event_seat_session_removed_free ((CkLogSeatSessionRemovedEvent *) event); + break; + case CK_LOG_EVENT_SEAT_DEVICE_ADDED: + event_seat_device_added_free ((CkLogSeatDeviceAddedEvent *) event); + break; + case CK_LOG_EVENT_SEAT_DEVICE_REMOVED: + event_seat_device_removed_free ((CkLogSeatDeviceRemovedEvent *) event); + break; + case CK_LOG_EVENT_SEAT_ACTIVE_SESSION_CHANGED: + event_seat_active_session_changed_free ((CkLogSeatActiveSessionChangedEvent *) event); + break; + default: + g_assert_not_reached (); + break; + } + + g_free (event); +} + +static void +add_log_for_seat_added (GString *str, + CkLogEvent *event) +{ + CkLogSeatAddedEvent *e; + + e = (CkLogSeatAddedEvent *)event; + g_string_append_printf (str, + " seat-id='%s' seat-kind=%d", + e->seat_id, + e->seat_kind); +} + +static void +add_log_for_seat_removed (GString *str, + CkLogEvent *event) +{ + CkLogSeatRemovedEvent *e; + + e = (CkLogSeatRemovedEvent *)event; + g_string_append_printf (str, + " seat-id='%s' seat-kind=%d", + e->seat_id, + e->seat_kind); +} + +static void +add_log_for_seat_session_added (GString *str, + CkLogEvent *event) +{ + CkLogSeatSessionAddedEvent *e; + + e = (CkLogSeatSessionAddedEvent *)event; + g_string_append_printf (str, + " seat-id='%s' session-id='%s' session-type='%s' session-x11-display='%s' session-x11-display-device='%s' session-display-device='%s' session-remote-host-name='%s' session-is-local=%s session-unix-user=%u session-creation-time='%s'", + e->seat_id ? e->seat_id : "", + e->session_id ? e->session_id : "", + e->session_type ? e->session_type : "", + e->session_x11_display ? e->session_x11_display : "", + e->session_x11_display_device ? e->session_x11_display_device : "", + e->session_display_device ? e->session_display_device : "", + e->session_remote_host_name ? e->session_remote_host_name : "", + e->session_is_local ? "TRUE" : "FALSE", + e->session_unix_user, + e->session_creation_time ? e->session_creation_time : ""); +} + +static void +add_log_for_seat_session_removed (GString *str, + CkLogEvent *event) +{ + CkLogSeatSessionRemovedEvent *e; + + e = (CkLogSeatSessionRemovedEvent *)event; + g_string_append_printf (str, + " seat-id='%s' session-id='%s' session-type='%s' session-x11-display='%s' session-x11-display-device='%s' session-display-device='%s' session-remote-host-name='%s' session-is-local=%s session-unix-user=%u session-creation-time='%s'", + e->seat_id ? e->seat_id : "", + e->session_id ? e->session_id : "", + e->session_type ? e->session_type : "", + e->session_x11_display ? e->session_x11_display : "", + e->session_x11_display_device ? e->session_x11_display_device : "", + e->session_display_device ? e->session_display_device : "", + e->session_remote_host_name ? e->session_remote_host_name : "", + e->session_is_local ? "TRUE" : "FALSE", + e->session_unix_user, + e->session_creation_time ? e->session_creation_time : ""); +} + +static void +add_log_for_system_stop (GString *str, + CkLogEvent *event) +{ + CkLogSystemStopEvent *e; + + e = (CkLogSystemStopEvent *)event; +} + +static void +add_log_for_system_restart (GString *str, + CkLogEvent *event) +{ + CkLogSystemRestartEvent *e; + + e = (CkLogSystemRestartEvent *)event; +} + + +static void +add_log_for_system_start (GString *str, + CkLogEvent *event) +{ + CkLogSystemStartEvent *e; + + e = (CkLogSystemStartEvent *)event; +} + +static void +add_log_for_seat_active_session_changed (GString *str, + CkLogEvent *event) +{ + CkLogSeatActiveSessionChangedEvent *e; + + e = (CkLogSeatActiveSessionChangedEvent *)event; + g_string_append_printf (str, + " seat-id='%s' session-id='%s'", + e->seat_id ? e->seat_id : "", + e->session_id ? e->session_id : ""); +} + +static void +add_log_for_seat_device_added (GString *str, + CkLogEvent *event) +{ + CkLogSeatDeviceAddedEvent *e; + + e = (CkLogSeatDeviceAddedEvent *)event; + g_string_append_printf (str, + " seat-id='%s' device-id='%s' device-type='%s'", + e->seat_id ? e->seat_id : "", + e->device_id ? e->device_id : "", + e->device_type ? e->device_type : ""); +} + +static void +add_log_for_seat_device_removed (GString *str, + CkLogEvent *event) +{ + CkLogSeatDeviceRemovedEvent *e; + + e = (CkLogSeatDeviceRemovedEvent *)event; + g_string_append_printf (str, + " seat-id='%s' device-id='%s' device-type='%s'", + e->seat_id ? e->seat_id : "", + e->device_id ? e->device_id : "", + e->device_type ? e->device_type : ""); +} + +static const char * +event_type_to_name (CkLogEventType event_type) +{ + const char *str; + switch (event_type) { + case CK_LOG_EVENT_SEAT_ADDED: + str = "SEAT_ADDED"; + break; + case CK_LOG_EVENT_SEAT_REMOVED: + str = "SEAT_REMOVED"; + break; + case CK_LOG_EVENT_SYSTEM_STOP: + str = "SYSTEM_STOP"; + break; + case CK_LOG_EVENT_SYSTEM_RESTART: + str = "SYSTEM_RESTART"; + break; + case CK_LOG_EVENT_SYSTEM_START: + str = "SYSTEM_START"; + break; + case CK_LOG_EVENT_SEAT_SESSION_ADDED: + str = "SEAT_SESSION_ADDED"; + break; + case CK_LOG_EVENT_SEAT_SESSION_REMOVED: + str = "SEAT_SESSION_REMOVED"; + break; + case CK_LOG_EVENT_SEAT_DEVICE_ADDED: + str = "SEAT_DEVICE_ADDED"; + break; + case CK_LOG_EVENT_SEAT_DEVICE_REMOVED: + str = "SEAT_DEVICE_REMOVED"; + break; + case CK_LOG_EVENT_SEAT_ACTIVE_SESSION_CHANGED: + str = "SEAT_ACTIVE_SESSION_CHANGED"; + break; + default: + str = "UNKNOWN"; + break; + } + return str; +} + +static gboolean +event_name_to_type (const char *event_name, + CkLogEventType *event_type) +{ + gboolean ret; + + ret = TRUE; + + if (strcmp (event_name, "SEAT_ADDED") == 0) { + *event_type = CK_LOG_EVENT_SEAT_ADDED; + } else if (strcmp (event_name, "SEAT_REMOVED") == 0) { + *event_type = CK_LOG_EVENT_SEAT_REMOVED; + } else if (strcmp (event_name, "SYSTEM_STOP") == 0) { + *event_type = CK_LOG_EVENT_SYSTEM_STOP; + } else if (strcmp (event_name, "SYSTEM_RESTART") == 0) { + *event_type = CK_LOG_EVENT_SYSTEM_RESTART; + } else if (strcmp (event_name, "SYSTEM_START") == 0) { + *event_type = CK_LOG_EVENT_SYSTEM_START; + } else if (strcmp (event_name, "SEAT_SESSION_ADDED") == 0) { + *event_type = CK_LOG_EVENT_SEAT_SESSION_ADDED; + } else if (strcmp (event_name, "SEAT_SESSION_REMOVED") == 0) { + *event_type = CK_LOG_EVENT_SEAT_SESSION_REMOVED; + } else if (strcmp (event_name, "SEAT_DEVICE_ADDED") == 0) { + *event_type = CK_LOG_EVENT_SEAT_DEVICE_ADDED; + } else if (strcmp (event_name, "SEAT_DEVICE_REMOVED") == 0) { + *event_type = CK_LOG_EVENT_SEAT_DEVICE_REMOVED; + } else if (strcmp (event_name, "SEAT_ACTIVE_SESSION_CHANGED") == 0) { + *event_type = CK_LOG_EVENT_SEAT_ACTIVE_SESSION_CHANGED; + } else { + ret = FALSE; + } + + return ret; +} + +static void +add_log_for_any (GString *str, + CkLogEvent *event) +{ + g_string_append_printf (str, + "%lu.%03u type=%s :", + (gulong)event->timestamp.tv_sec, + (guint)(event->timestamp.tv_usec / 1000), + event_type_to_name (event->type)); +} + +void +ck_log_event_to_string (CkLogEvent *event, + GString *str) +{ + + add_log_for_any (str, event); + + switch (event->type) { + case CK_LOG_EVENT_SEAT_ADDED: + add_log_for_seat_added (str, event); + break; + case CK_LOG_EVENT_SEAT_REMOVED: + add_log_for_seat_removed (str, event); + break; + case CK_LOG_EVENT_SYSTEM_STOP: + add_log_for_system_stop (str, event); + break; + case CK_LOG_EVENT_SYSTEM_RESTART: + add_log_for_system_restart (str, event); + break; + case CK_LOG_EVENT_SYSTEM_START: + add_log_for_system_start (str, event); + break; + case CK_LOG_EVENT_SEAT_SESSION_ADDED: + add_log_for_seat_session_added (str, event); + break; + case CK_LOG_EVENT_SEAT_SESSION_REMOVED: + add_log_for_seat_session_removed (str, event); + break; + case CK_LOG_EVENT_SEAT_DEVICE_ADDED: + add_log_for_seat_device_added (str, event); + break; + case CK_LOG_EVENT_SEAT_DEVICE_REMOVED: + add_log_for_seat_device_removed (str, event); + break; + case CK_LOG_EVENT_SEAT_ACTIVE_SESSION_CHANGED: + add_log_for_seat_active_session_changed (str, event); + break; + default: + g_assert_not_reached (); + break; + } +} + +static const char * +skip_header (const char *str, + gssize len) +{ + char *r; + r = g_strstr_len (str, + len, + " : "); + if (r != NULL) { + r += 3; + } + + return r; +} + +static gboolean +parse_value_as_ulong (const char *value, + gulong *intval) +{ + char *end_of_valid_int; + glong ulong_value; + + ulong_value = strtoul (value, &end_of_valid_int, 10); + + if (*value == '\0' || *end_of_valid_int != '\0') { + return FALSE; + } + + *intval = ulong_value; + + return TRUE; +} + +static gboolean +parse_log_for_seat_added (const GString *str, + CkLogEvent *event) +{ + gboolean ret; + const char *s; + GRegex *re; + GMatchInfo *match_info; + gboolean res; + GError *error; + char *tmp; + CkLogSeatAddedEvent *e; + + re = NULL; + match_info = NULL; + ret = FALSE; + + s = skip_header (str->str, str->len); + if (s == NULL) { + goto out; + } + + error = NULL; + re = g_regex_new ("seat-id='(?P<seatid>[a-zA-Z0-9/]+)' seat-kind=(?P<sessionid>[0-9]*)", 0, 0, &error); + if (re == NULL) { + g_warning (error->message); + goto out; + } + + g_regex_match (re, s, 0, &match_info); + + res = g_match_info_matches (match_info); + if (! res) { + g_warning ("Unable to parse seat added event: %s", s); + goto out; + } + + e = (CkLogSeatAddedEvent *)event; + e->seat_id = g_match_info_fetch_named (match_info, "seatid"); + + tmp = g_match_info_fetch_named (match_info, "seatkind"); + if (tmp != NULL) { + gulong l; + if (parse_value_as_ulong (tmp, &l)) { + e->seat_kind = l; + } + } + + ret = TRUE; + out: + if (match_info != NULL) { + g_match_info_free (match_info); + } + if (re != NULL) { + g_regex_unref (re); + } + + return ret; +} + +static gboolean +parse_log_for_seat_removed (const GString *str, + CkLogEvent *event) +{ + gboolean ret; + const char *s; + GRegex *re; + GMatchInfo *match_info; + gboolean res; + GError *error; + char *tmp; + CkLogSeatRemovedEvent *e; + + re = NULL; + match_info = NULL; + ret = FALSE; + + s = skip_header (str->str, str->len); + if (s == NULL) { + goto out; + } + + error = NULL; + re = g_regex_new ("seat-id='(?P<seatid>[a-zA-Z0-9/]+)' seat-kind=(?P<sessionid>[0-9]*)", 0, 0, &error); + if (re == NULL) { + g_warning (error->message); + goto out; + } + + g_regex_match (re, s, 0, &match_info); + + res = g_match_info_matches (match_info); + if (! res) { + g_warning ("Unable to parse seat removed event: %s", s); + goto out; + } + + e = (CkLogSeatRemovedEvent *)event; + e->seat_id = g_match_info_fetch_named (match_info, "seatid"); + + tmp = g_match_info_fetch_named (match_info, "seatkind"); + if (tmp != NULL) { + gulong l; + if (parse_value_as_ulong (tmp, &l)) { + e->seat_kind = l; + } + } + + ret = TRUE; + out: + if (match_info != NULL) { + g_match_info_free (match_info); + } + if (re != NULL) { + g_regex_unref (re); + } + + return ret; +} + +static gboolean +parse_log_for_system_stop (const GString *str, + CkLogEvent *event) +{ + gboolean ret; + const char *s; + CkLogSystemStopEvent *e; + + ret = FALSE; + + s = skip_header (str->str, str->len); + if (s == NULL) { + goto out; + } + + e = (CkLogSystemStopEvent *)event; + + ret = TRUE; + out: + + return ret; +} + +static gboolean +parse_log_for_system_restart (const GString *str, + CkLogEvent *event) +{ + gboolean ret; + const char *s; + CkLogSystemRestartEvent *e; + + ret = FALSE; + + s = skip_header (str->str, str->len); + if (s == NULL) { + goto out; + } + + e = (CkLogSystemRestartEvent *)event; + + ret = TRUE; + out: + + return ret; +} + +static gboolean +parse_log_for_system_start (const GString *str, + CkLogEvent *event) +{ + gboolean ret; + const char *s; + CkLogSystemStartEvent *e; + + ret = FALSE; + + s = skip_header (str->str, str->len); + if (s == NULL) { + goto out; + } + + e = (CkLogSystemStartEvent *)event; + + ret = TRUE; + out: + + return ret; +} + +static gboolean +parse_log_for_seat_session_added (const GString *str, + CkLogEvent *event) +{ + gboolean ret; + const char *s; + GRegex *re; + GMatchInfo *match_info; + gboolean res; + GError *error; + char *tmp; + CkLogSeatSessionAddedEvent *e; + + re = NULL; + match_info = NULL; + ret = FALSE; + + s = skip_header (str->str, str->len); + if (s == NULL) { + goto out; + } + + error = NULL; + re = g_regex_new ("seat-id='(?P<seatid>[a-zA-Z0-9/]+)' session-id='(?P<sessionid>[a-zA-Z0-9/]+)' session-type='(?P<sessiontype>[a-zA-Z0-9 ]*)' session-x11-display='(?P<sessionx11display>[0-9a-zA-Z.:]*)' session-x11-display-device='(?P<sessionx11displaydevice>[^']*)' session-display-device='(?P<sessiondisplaydevice>[^']*)' session-remote-host-name='(?P<sessionremovehostname>[^']*)' session-is-local=(?P<sessionislocal>[a-zA-Z]*) session-unix-user=(?P<sessionunixuser>[0-9]*) session-creation-time='(?P<sessioncreationtime>[^']*)'", 0, 0, &error); + if (re == NULL) { + g_warning (error->message); + goto out; + } + + g_regex_match (re, s, 0, &match_info); + + res = g_match_info_matches (match_info); + if (! res) { + g_warning ("Unable to parse session added event: %s", s); + goto out; + } + + e = (CkLogSeatSessionAddedEvent *)event; + e->seat_id = g_match_info_fetch_named (match_info, "seatid"); + e->session_id = g_match_info_fetch_named (match_info, "sessionid"); + e->session_type = g_match_info_fetch_named (match_info, "sessiontype"); + e->session_x11_display = g_match_info_fetch_named (match_info, "sessionx11display"); + e->session_x11_display_device = g_match_info_fetch_named (match_info, "sessionx11displaydevice"); + e->session_display_device = g_match_info_fetch_named (match_info, "sessiondisplaydevice"); + e->session_remote_host_name = g_match_info_fetch_named (match_info, "sessionremotehostname"); + e->session_creation_time = g_match_info_fetch_named (match_info, "sessioncreationtime"); + + tmp = g_match_info_fetch_named (match_info, "sessionislocal"); + if (tmp != NULL && strcmp (tmp, "TRUE") == 0) { + e->session_is_local = TRUE; + } else { + e->session_is_local = FALSE; + } + g_free (tmp); + + tmp = g_match_info_fetch_named (match_info, "sessionunixuser"); + if (tmp != NULL) { + gulong l; + if (parse_value_as_ulong (tmp, &l)) { + e->session_unix_user = l; + } + } + + ret = TRUE; + out: + if (match_info != NULL) { + g_match_info_free (match_info); + } + if (re != NULL) { + g_regex_unref (re); + } + + return ret; +} + +static gboolean +parse_log_for_seat_session_removed (const GString *str, + CkLogEvent *event) +{ + gboolean ret; + const char *s; + GRegex *re; + GMatchInfo *match_info; + gboolean res; + GError *error; + char *tmp; + CkLogSeatSessionRemovedEvent *e; + + re = NULL; + match_info = NULL; + ret = FALSE; + + s = skip_header (str->str, str->len); + if (s == NULL) { + goto out; + } + + error = NULL; + re = g_regex_new ("seat-id='(?P<seatid>[a-zA-Z0-9/]+)' session-id='(?P<sessionid>[a-zA-Z0-9/]+)' session-type='(?P<sessiontype>[a-zA-Z0-9 ]*)' session-x11-display='(?P<sessionx11display>[0-9a-zA-Z.:]*)' session-x11-display-device='(?P<sessionx11displaydevice>[^']*)' session-display-device='(?P<sessiondisplaydevice>[^']*)' session-remote-host-name='(?P<sessionremovehostname>[^']*)' session-is-local=(?P<sessionislocal>[a-zA-Z]*) session-unix-user=(?P<sessionunixuser>[0-9]*) session-creation-time='(?P<sessioncreationtime>[^']*)'", 0, 0, &error); + if (re == NULL) { + g_warning (error->message); + goto out; + } + + g_regex_match (re, s, 0, &match_info); + + res = g_match_info_matches (match_info); + if (! res) { + g_warning ("Unable to parse session removed event: %s", s); + goto out; + } + + e = (CkLogSeatSessionRemovedEvent *)event; + e->seat_id = g_match_info_fetch_named (match_info, "seatid"); + e->session_id = g_match_info_fetch_named (match_info, "sessionid"); + e->session_type = g_match_info_fetch_named (match_info, "sessiontype"); + e->session_x11_display = g_match_info_fetch_named (match_info, "sessionx11display"); + e->session_x11_display_device = g_match_info_fetch_named (match_info, "sessionx11displaydevice"); + e->session_display_device = g_match_info_fetch_named (match_info, "sessiondisplaydevice"); + e->session_remote_host_name = g_match_info_fetch_named (match_info, "sessionremotehostname"); + e->session_creation_time = g_match_info_fetch_named (match_info, "sessioncreationtime"); + + tmp = g_match_info_fetch_named (match_info, "sessionislocal"); + if (tmp != NULL && strcmp (tmp, "TRUE") == 0) { + e->session_is_local = TRUE; + } else { + e->session_is_local = FALSE; + } + g_free (tmp); + + tmp = g_match_info_fetch_named (match_info, "sessionunixuser"); + if (tmp != NULL) { + gulong l; + if (parse_value_as_ulong (tmp, &l)) { + e->session_unix_user = l; + } + } + + ret = TRUE; + out: + if (match_info != NULL) { + g_match_info_free (match_info); + } + if (re != NULL) { + g_regex_unref (re); + } + + return ret; +} + +static gboolean +parse_log_for_seat_active_session_changed (const GString *str, + CkLogEvent *event) +{ + gboolean ret; + const char *s; + GRegex *re; + GMatchInfo *match_info; + gboolean res; + GError *error; + CkLogSeatActiveSessionChangedEvent *e; + + re = NULL; + match_info = NULL; + ret = FALSE; + + s = skip_header (str->str, str->len); + if (s == NULL) { + goto out; + } + + error = NULL; + re = g_regex_new ("seat-id='(?P<seatid>[a-zA-Z0-9/]+)' session-id='(?P<sessionid>[a-zA-Z0-9/]*)'", 0, 0, &error); + if (re == NULL) { + g_warning (error->message); + goto out; + } + + g_regex_match (re, s, 0, &match_info); + + res = g_match_info_matches (match_info); + if (! res) { + g_warning ("Unable to parse session changed event: %s", s); + goto out; + } + + e = (CkLogSeatActiveSessionChangedEvent *)event; + e->seat_id = g_match_info_fetch_named (match_info, "seatid"); + e->session_id = g_match_info_fetch_named (match_info, "sessionid"); + + ret = TRUE; + out: + if (match_info != NULL) { + g_match_info_free (match_info); + } + if (re != NULL) { + g_regex_unref (re); + } + + return ret; +} + +static gboolean +parse_log_for_seat_device_added (const GString *str, + CkLogEvent *event) +{ + gboolean ret; + const char *s; + GRegex *re; + GMatchInfo *match_info; + gboolean res; + GError *error; + CkLogSeatDeviceAddedEvent *e; + + re = NULL; + match_info = NULL; + ret = FALSE; + + s = skip_header (str->str, str->len); + if (s == NULL) { + goto out; + } + + error = NULL; + re = g_regex_new ("seat-id='(?P<seatid>[a-zA-Z0-9/]+)' device-id='(?P<deviceid>[^']+)' device-type='(?P<devicetype>[^']+)'", 0, 0, &error); + if (re == NULL) { + g_warning (error->message); + goto out; + } + + g_regex_match (re, s, 0, &match_info); + + res = g_match_info_matches (match_info); + if (! res) { + g_warning ("Unable to parse device added event: %s", s); + goto out; + } + + e = (CkLogSeatDeviceAddedEvent *)event; + e->seat_id = g_match_info_fetch_named (match_info, "seatid"); + e->device_id = g_match_info_fetch_named (match_info, "deviceid"); + e->device_type = g_match_info_fetch_named (match_info, "devicetype"); + + ret = TRUE; + out: + if (match_info != NULL) { + g_match_info_free (match_info); + } + if (re != NULL) { + g_regex_unref (re); + } + + return ret; +} + +static gboolean +parse_log_for_seat_device_removed (const GString *str, + CkLogEvent *event) +{ + gboolean ret; + const char *s; + GRegex *re; + GMatchInfo *match_info; + gboolean res; + GError *error; + CkLogSeatDeviceRemovedEvent *e; + + re = NULL; + match_info = NULL; + ret = FALSE; + + s = skip_header (str->str, str->len); + if (s == NULL) { + goto out; + } + + error = NULL; + re = g_regex_new ("seat-id='(?P<seatid>[a-zA-Z0-9/]+)' device-id='(?P<deviceid>[^']+)' device-type='(?P<devicetype>[^']+)'", 0, 0, &error); + if (re == NULL) { + g_warning (error->message); + goto out; + } + + g_regex_match (re, s, 0, &match_info); + + res = g_match_info_matches (match_info); + if (! res) { + g_warning ("Unable to parse device removed event: %s", s); + goto out; + } + + e = (CkLogSeatDeviceRemovedEvent *)event; + e->seat_id = g_match_info_fetch_named (match_info, "seatid"); + e->device_id = g_match_info_fetch_named (match_info, "deviceid"); + e->device_type = g_match_info_fetch_named (match_info, "devicetype"); + + ret = TRUE; + out: + if (match_info != NULL) { + g_match_info_free (match_info); + } + if (re != NULL) { + g_regex_unref (re); + } + + return ret; +} + +static gboolean +parse_log_for_any (const GString *str, + CkLogEvent *event) +{ + gboolean ret; + int res; + gulong sec; + guint frac; + char buf[32]; + + ret = FALSE; + + res = sscanf (str->str, "%lu.%u type=%30s :", + &sec, + &frac, + buf); + if (res == 3) { + res = event_name_to_type (buf, &event->type); + if (! res) { + goto out; + } + + event->timestamp.tv_sec = sec; + event->timestamp.tv_usec = 1000 * frac; + + ret = TRUE; + } + out: + return ret; +} + +gboolean +ck_log_event_fill_from_string (CkLogEvent *event, + const GString *str) +{ + gboolean res; + gboolean ret; + + g_return_val_if_fail (str != NULL, FALSE); + + ret = FALSE; + + res = parse_log_for_any (str, event); + if (! res) { + goto out; + } + + switch (event->type) { + case CK_LOG_EVENT_SEAT_ADDED: + res = parse_log_for_seat_added (str, event); + break; + case CK_LOG_EVENT_SEAT_REMOVED: + res = parse_log_for_seat_removed (str, event); + break; + case CK_LOG_EVENT_SYSTEM_STOP: + res = parse_log_for_system_stop (str, event); + break; + case CK_LOG_EVENT_SYSTEM_RESTART: + res = parse_log_for_system_restart (str, event); + break; + case CK_LOG_EVENT_SYSTEM_START: + res = parse_log_for_system_start (str, event); + break; + case CK_LOG_EVENT_SEAT_SESSION_ADDED: + res = parse_log_for_seat_session_added (str, event); + break; + case CK_LOG_EVENT_SEAT_SESSION_REMOVED: + res = parse_log_for_seat_session_removed (str, event); + break; + case CK_LOG_EVENT_SEAT_DEVICE_ADDED: + res = parse_log_for_seat_device_added (str, event); + break; + case CK_LOG_EVENT_SEAT_DEVICE_REMOVED: + res = parse_log_for_seat_device_removed (str, event); + break; + case CK_LOG_EVENT_SEAT_ACTIVE_SESSION_CHANGED: + res = parse_log_for_seat_active_session_changed (str, event); + break; + default: + g_assert_not_reached (); + break; + } + if (! res) { + goto out; + } + ret = TRUE; + out: + return ret; +} + +CkLogEvent * +ck_log_event_new_from_string (const GString *str) +{ + CkLogEvent *event; + gboolean res; + + g_return_val_if_fail (str != NULL, NULL); + + event = g_new0 (CkLogEvent, 1); + res = ck_log_event_fill_from_string (event, str); + if (! res) { + g_free (event); + event = NULL; + } + + return event; +} diff --git a/src/ck-log-event.h b/src/ck-log-event.h new file mode 100644 index 0000000..149f49b --- /dev/null +++ b/src/ck-log-event.h @@ -0,0 +1,148 @@ +/* -*- 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_LOG_EVENT_H +#define __CK_LOG_EVENT_H + +#include <glib.h> + +G_BEGIN_DECLS + +typedef enum +{ + CK_LOG_EVENT_START = 0, + CK_LOG_EVENT_STOP, + CK_LOG_EVENT_SYSTEM_START, + CK_LOG_EVENT_SYSTEM_STOP, + CK_LOG_EVENT_SYSTEM_RESTART, + CK_LOG_EVENT_SYSTEM_RUNLEVEL_CHANGED, + CK_LOG_EVENT_SEAT_ADDED, + CK_LOG_EVENT_SEAT_REMOVED, + CK_LOG_EVENT_SEAT_SESSION_ADDED, + CK_LOG_EVENT_SEAT_SESSION_REMOVED, + CK_LOG_EVENT_SEAT_DEVICE_ADDED, + CK_LOG_EVENT_SEAT_DEVICE_REMOVED, + CK_LOG_EVENT_SEAT_ACTIVE_SESSION_CHANGED, +} CkLogEventType; + +typedef struct +{ +} CkLogSystemStopEvent; + +typedef struct +{ +} CkLogSystemRestartEvent; + +typedef struct +{ +} CkLogSystemStartEvent; + +typedef struct +{ + char *seat_id; + int seat_kind; +} CkLogSeatAddedEvent; + +typedef struct +{ + char *seat_id; + int seat_kind; +} CkLogSeatRemovedEvent; + +typedef struct +{ + char *seat_id; + char *session_id; + char *session_type; + char *session_x11_display; + char *session_x11_display_device; + char *session_display_device; + char *session_remote_host_name; + gboolean session_is_local; + guint session_unix_user; + char *session_creation_time; +} CkLogSeatSessionAddedEvent; + +typedef struct +{ + char *seat_id; + char *session_id; + char *session_type; + char *session_x11_display; + char *session_x11_display_device; + char *session_display_device; + char *session_remote_host_name; + gboolean session_is_local; + guint session_unix_user; + char *session_creation_time; +} CkLogSeatSessionRemovedEvent; + +typedef struct +{ + char *seat_id; + char *session_id; +} CkLogSeatActiveSessionChangedEvent; + +typedef struct +{ + char *seat_id; + char *device_type; + char *device_id; +} CkLogSeatDeviceAddedEvent; + +typedef struct +{ + char *seat_id; + char *device_type; + char *device_id; +} CkLogSeatDeviceRemovedEvent; + +typedef struct +{ + union { + CkLogSystemRestartEvent system_start; + CkLogSystemStopEvent system_stop; + CkLogSystemRestartEvent system_restart; + CkLogSeatAddedEvent seat_added; + CkLogSeatRemovedEvent seat_removed; + CkLogSeatSessionAddedEvent seat_session_added; + CkLogSeatSessionRemovedEvent seat_session_removed; + CkLogSeatActiveSessionChangedEvent seat_active_session_changed; + CkLogSeatDeviceAddedEvent seat_device_added; + CkLogSeatDeviceRemovedEvent seat_device_removed; + } event; + + GTimeVal timestamp; + CkLogEventType type; +} CkLogEvent; + +CkLogEvent * ck_log_event_copy (CkLogEvent *event); +void ck_log_event_free (CkLogEvent *event); + +CkLogEvent * ck_log_event_new_from_string (const GString *str); +gboolean ck_log_event_fill_from_string (CkLogEvent *event, + const GString *str); + +void ck_log_event_to_string (CkLogEvent *event, + GString *str); + +G_END_DECLS + +#endif /* __CK_LOG_EVENT_H */ diff --git a/src/ck-manager-glue.h b/src/ck-manager-glue.h index 0def872..b82aabc 100644 --- a/src/ck-manager-glue.h +++ b/src/ck-manager-glue.h @@ -53,7 +53,7 @@ G_BEGIN_DECLS #endif /* !G_ENABLE_DEBUG */ -/* BOOLEAN:POINTER,POINTER (/tmp/dbus-binding-tool-c-marshallers.2JLIYT:1) */ +/* BOOLEAN:POINTER,POINTER (/tmp/dbus-binding-tool-c-marshallers.D3GI6T:1) */ extern void dbus_glib_marshal_ck_manager_BOOLEAN__POINTER_POINTER (GClosure *closure, GValue *return_value, guint n_param_values, @@ -62,10 +62,10 @@ extern void dbus_glib_marshal_ck_manager_BOOLEAN__POINTER_POINTER (GClosure gpointer marshal_data); void dbus_glib_marshal_ck_manager_BOOLEAN__POINTER_POINTER (GClosure *closure, - GValue *return_value, + GValue *return_value G_GNUC_UNUSED, guint n_param_values, const GValue *param_values, - gpointer invocation_hint, + gpointer invocation_hint G_GNUC_UNUSED, gpointer marshal_data) { typedef gboolean (*GMarshalFunc_BOOLEAN__POINTER_POINTER) (gpointer data1, @@ -100,11 +100,11 @@ dbus_glib_marshal_ck_manager_BOOLEAN__POINTER_POINTER (GClosure *closure, g_value_set_boolean (return_value, v_return); } -/* NONE:UINT,POINTER (/tmp/dbus-binding-tool-c-marshallers.2JLIYT:2) */ +/* NONE:UINT,POINTER (/tmp/dbus-binding-tool-c-marshallers.D3GI6T: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.2JLIYT:3) */ +/* NONE:STRING,POINTER (/tmp/dbus-binding-tool-c-marshallers.D3GI6T:3) */ extern void dbus_glib_marshal_ck_manager_VOID__STRING_POINTER (GClosure *closure, GValue *return_value, guint n_param_values, @@ -113,10 +113,10 @@ extern void dbus_glib_marshal_ck_manager_VOID__STRING_POINTER (GClosure *clo gpointer marshal_data); void dbus_glib_marshal_ck_manager_VOID__STRING_POINTER (GClosure *closure, - GValue *return_value, + GValue *return_value G_GNUC_UNUSED, guint n_param_values, const GValue *param_values, - gpointer invocation_hint, + gpointer invocation_hint G_GNUC_UNUSED, gpointer marshal_data) { typedef void (*GMarshalFunc_VOID__STRING_POINTER) (gpointer data1, @@ -148,11 +148,11 @@ dbus_glib_marshal_ck_manager_VOID__STRING_POINTER (GClosure *closure, } #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.2JLIYT:4) */ +/* NONE:POINTER (/tmp/dbus-binding-tool-c-marshallers.D3GI6T: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.2JLIYT:5) */ +/* NONE:BOXED,POINTER (/tmp/dbus-binding-tool-c-marshallers.D3GI6T:5) */ extern void dbus_glib_marshal_ck_manager_VOID__BOXED_POINTER (GClosure *closure, GValue *return_value, guint n_param_values, @@ -161,10 +161,10 @@ extern void dbus_glib_marshal_ck_manager_VOID__BOXED_POINTER (GClosure *clos gpointer marshal_data); void dbus_glib_marshal_ck_manager_VOID__BOXED_POINTER (GClosure *closure, - GValue *return_value, + GValue *return_value G_GNUC_UNUSED, guint n_param_values, const GValue *param_values, - gpointer invocation_hint, + gpointer invocation_hint G_GNUC_UNUSED, gpointer marshal_data) { typedef void (*GMarshalFunc_VOID__BOXED_POINTER) (gpointer data1, @@ -202,24 +202,27 @@ G_END_DECLS #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 }, + { (GCallback) ck_manager_restart, dbus_glib_marshal_ck_manager_NONE__POINTER, 0 }, + { (GCallback) ck_manager_stop, dbus_glib_marshal_ck_manager_NONE__POINTER, 46 }, + { (GCallback) ck_manager_open_session, dbus_glib_marshal_ck_manager_NONE__POINTER, 89 }, + { (GCallback) ck_manager_open_session_with_parameters, dbus_glib_marshal_ck_manager_NONE__BOXED_POINTER, 154 }, + { (GCallback) ck_manager_close_session, dbus_glib_marshal_ck_manager_NONE__STRING_POINTER, 252 }, + { (GCallback) ck_manager_get_seats, dbus_glib_marshal_ck_manager_BOOLEAN__POINTER_POINTER, 329 }, + { (GCallback) ck_manager_get_sessions, dbus_glib_marshal_ck_manager_BOOLEAN__POINTER_POINTER, 391 }, + { (GCallback) ck_manager_get_session_for_cookie, dbus_glib_marshal_ck_manager_NONE__STRING_POINTER, 459 }, + { (GCallback) ck_manager_get_session_for_unix_process, dbus_glib_marshal_ck_manager_NONE__UINT_POINTER, 541 }, + { (GCallback) ck_manager_get_current_session, dbus_glib_marshal_ck_manager_NONE__POINTER, 625 }, + { (GCallback) ck_manager_get_sessions_for_unix_user, dbus_glib_marshal_ck_manager_NONE__UINT_POINTER, 694 }, + { (GCallback) ck_manager_get_sessions_for_user, dbus_glib_marshal_ck_manager_NONE__UINT_POINTER, 781 }, + { (GCallback) ck_manager_get_system_idle_hint, dbus_glib_marshal_ck_manager_BOOLEAN__POINTER_POINTER, 864 }, + { (GCallback) ck_manager_get_system_idle_since_hint, dbus_glib_marshal_ck_manager_BOOLEAN__POINTER_POINTER, 938 }, }; 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", + 14, +"org.freedesktop.ConsoleKit.Manager\0Restart\0A\0\0org.freedesktop.ConsoleKit.Manager\0Stop\0A\0\0org.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\0GetSessions\0S\0sessions\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 index 377cef0..bf4926f 100644 --- a/src/ck-manager.c +++ b/src/ck-manager.c @@ -28,45 +28,57 @@ #include <signal.h> #include <sys/stat.h> #include <sys/types.h> +#include <errno.h> +#include <pwd.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> +#ifdef HAVE_POLKIT +#include <polkit/polkit.h> +#endif + +#ifdef ENABLE_RBAC_SHUTDOWN +#include <auth_attr.h> +#include <secdb.h> +#endif + #include "ck-manager.h" #include "ck-manager-glue.h" #include "ck-seat.h" +#include "ck-session-leader.h" #include "ck-session.h" -#include "ck-job.h" #include "ck-marshal.h" +#include "ck-event-logger.h" #include "ck-sysdeps.h" #define CK_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), CK_TYPE_MANAGER, CkManagerPrivate)) -#define CK_SEAT_DIR SYSCONFDIR "/ConsoleKit/seats.d" - +#define CK_SEAT_DIR SYSCONFDIR "/ConsoleKit/seats.d" +#define LOG_FILE LOCALSTATEDIR "/log/ConsoleKit/history" #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 { +#ifdef HAVE_POLKIT + PolKitContext *pol_ctx; +#endif + GHashTable *seats; GHashTable *sessions; GHashTable *leaders; DBusGProxy *bus_proxy; DBusGConnection *connection; + CkEventLogger *logger; guint32 session_serial; guint32 seat_serial; @@ -75,18 +87,6 @@ struct CkManagerPrivate 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, @@ -105,64 +105,136 @@ static gpointer manager_object = NULL; G_DEFINE_TYPE (CkManager, ck_manager, G_TYPE_OBJECT) static void -remove_pending_job (CkJob *job) +dump_state_seat_iter (char *id, + CkSeat *seat, + GKeyFile *key_file) { - 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); - } + ck_seat_dump (seat, key_file); } static void -_leader_info_free (LeaderInfo *info) +dump_state_session_iter (char *id, + CkSession *session, + GKeyFile *key_file) { - 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); + ck_session_dump (session, key_file); } static void -leader_info_cancel (LeaderInfo *info) +dump_state_leader_iter (char *id, + CkSessionLeader *leader, + GKeyFile *key_file) { - 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; + ck_session_leader_dump (leader, key_file); +} + +static gboolean +do_dump (CkManager *manager, + int fd) +{ + char *str; + gsize str_len; + GKeyFile *key_file; + GError *error; + gboolean ret; + + str = NULL; + error = NULL; + ret = FALSE; + + key_file = g_key_file_new (); + + g_hash_table_foreach (manager->priv->seats, (GHFunc) dump_state_seat_iter, key_file); + g_hash_table_foreach (manager->priv->sessions, (GHFunc) dump_state_session_iter, key_file); + g_hash_table_foreach (manager->priv->leaders, (GHFunc) dump_state_leader_iter, key_file); + + str = g_key_file_to_data (key_file, &str_len, &error); + g_key_file_free (key_file); + if (str != NULL) { + ssize_t written; + + written = 0; + while (written < str_len) { + ssize_t ret; + ret = write (fd, str + written, str_len - written); + if (ret < 0) { + if (errno == EAGAIN || errno == EINTR) { + continue; + } else { + g_warning ("Error writing state file: %s", strerror (errno)); + goto out; + } + } + written += ret; + } + } else { + g_warning ("Couldn't construct state file: %s", error->message); + g_error_free (error); } - info->cancelled = TRUE; + ret = TRUE; + +out: + g_free (str); + return ret; } static void -leader_info_unref (LeaderInfo *info) +ck_manager_dump (CkManager *manager) { - /* Probably should use some kind of atomic op here */ - info->refcount -= 1; - if (info->refcount == 0) { - _leader_info_free (info); + int fd; + int res; + const char *filename = LOCALSTATEDIR "/run/ConsoleKit/database"; + const char *filename_tmp = LOCALSTATEDIR "/run/ConsoleKit/database~"; + + if (manager == NULL) { + return; } -} -static LeaderInfo * -leader_info_ref (LeaderInfo *info) -{ - info->refcount += 1; + /* always make sure we have a directory */ + errno = 0; + res = g_mkdir_with_parents (LOCALSTATEDIR "/run/ConsoleKit", + S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); + if (res < 0) { + g_warning ("Unable to create directory %s (%s)", + LOCALSTATEDIR "/run/ConsoleKit", + g_strerror (errno)); + return; + } + + fd = g_open (filename_tmp, O_CREAT | O_WRONLY, 0600); + if (fd == -1) { + g_warning ("Cannot create file %s: %s", filename_tmp, g_strerror (errno)); + goto error; + } + + if (! do_dump (manager, fd)) { + g_warning ("Cannot write to file %s", filename_tmp); + close (fd); + goto error; + } + again: + if (close (fd) != 0) { + if (errno == EINTR) + goto again; + else { + g_warning ("Cannot close fd for %s: %s", filename_tmp, g_strerror (errno)); + goto error; + } + } - return info; + if (g_rename (filename_tmp, filename) != 0) { + g_warning ("Cannot rename %s to %s: %s", filename_tmp, filename, g_strerror (errno)); + goto error; + } + + return; +error: + /* For security reasons; unlink the existing file since it + contains outdated information */ + if (g_unlink (filename) != 0) { + g_warning ("Cannot unlink %s: %s", filename, g_strerror (errno)); + } } GQuark @@ -176,6 +248,28 @@ ck_manager_error_quark (void) return ret; } +#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC } + +GType +ck_manager_error_get_type (void) +{ + static GType etype = 0; + + if (etype == 0) { + static const GEnumValue values[] = { + ENUM_ENTRY (CK_MANAGER_ERROR_GENERAL, "GeneralError"), + ENUM_ENTRY (CK_MANAGER_ERROR_NOT_PRIVILEGED, "NotPrivileged"), + { 0, 0, 0 } + }; + + g_assert (CK_MANAGER_NUM_ERRORS == G_N_ELEMENTS (values) - 1); + + etype = g_enum_register_static ("CkManagerError", values); + } + + return etype; +} + static guint32 get_next_session_serial (CkManager *manager) { @@ -277,6 +371,1140 @@ generate_seat_id (CkManager *manager) return id; } +static const char * +get_object_id_basename (const char *id) +{ + const char *base; + + if (id != NULL && g_str_has_prefix (id, CK_DBUS_PATH "/")) { + base = id + strlen (CK_DBUS_PATH "/"); + } else { + base = id; + } + + return base; +} + +static void +log_seat_added_event (CkManager *manager, + CkSeat *seat) +{ + CkLogEvent event; + gboolean res; + GError *error; + char *sid; + CkSeatKind seat_kind; + + memset (&event, 0, sizeof (CkLogEvent)); + + event.type = CK_LOG_EVENT_SEAT_ADDED; + g_get_current_time (&event.timestamp); + + sid = NULL; + ck_seat_get_id (seat, &sid, NULL); + ck_seat_get_kind (seat, &seat_kind, NULL); + + event.event.seat_added.seat_id = (char *)get_object_id_basename (sid); + event.event.seat_added.seat_kind = (int)seat_kind; + + error = NULL; + res = ck_event_logger_queue_event (manager->priv->logger, &event, &error); + if (! res) { + g_debug ("Unable to log event: %s", error->message); + g_error_free (error); + } + + g_free (sid); +} + +static void +log_seat_removed_event (CkManager *manager, + CkSeat *seat) +{ + CkLogEvent event; + gboolean res; + GError *error; + char *sid; + CkSeatKind seat_kind; + + memset (&event, 0, sizeof (CkLogEvent)); + + event.type = CK_LOG_EVENT_SEAT_REMOVED; + g_get_current_time (&event.timestamp); + + sid = NULL; + ck_seat_get_id (seat, &sid, NULL); + ck_seat_get_kind (seat, &seat_kind, NULL); + + event.event.seat_removed.seat_id = (char *)get_object_id_basename (sid); + event.event.seat_removed.seat_kind = (int)seat_kind; + + error = NULL; + res = ck_event_logger_queue_event (manager->priv->logger, &event, &error); + if (! res) { + g_debug ("Unable to log event: %s", error->message); + g_error_free (error); + } + + g_free (sid); +} + +static void +log_system_stop_event (CkManager *manager) +{ + CkLogEvent event; + gboolean res; + GError *error; + + memset (&event, 0, sizeof (CkLogEvent)); + + event.type = CK_LOG_EVENT_SYSTEM_STOP; + g_get_current_time (&event.timestamp); + + error = NULL; + res = ck_event_logger_queue_event (manager->priv->logger, &event, &error); + if (! res) { + g_debug ("Unable to log event: %s", error->message); + g_error_free (error); + } + + /* FIXME: in this case we should block and wait for log to flush */ +} + +static void +log_system_restart_event (CkManager *manager) +{ + CkLogEvent event; + gboolean res; + GError *error; + + memset (&event, 0, sizeof (CkLogEvent)); + + event.type = CK_LOG_EVENT_SYSTEM_RESTART; + g_get_current_time (&event.timestamp); + + error = NULL; + res = ck_event_logger_queue_event (manager->priv->logger, &event, &error); + if (! res) { + g_debug ("Unable to log event: %s", error->message); + g_error_free (error); + } + + /* FIXME: in this case we should block and wait for log to flush */ +} + +static void +log_seat_session_added_event (CkManager *manager, + CkSeat *seat, + const char *ssid) +{ + CkLogEvent event; + gboolean res; + GError *error; + char *sid; + CkSession *session; + + memset (&event, 0, sizeof (CkLogEvent)); + + event.type = CK_LOG_EVENT_SEAT_SESSION_ADDED; + g_get_current_time (&event.timestamp); + + sid = NULL; + ck_seat_get_id (seat, &sid, NULL); + + event.event.seat_session_added.seat_id = (char *)get_object_id_basename (sid); + event.event.seat_session_added.session_id = (char *)get_object_id_basename (ssid); + + session = g_hash_table_lookup (manager->priv->sessions, ssid); + if (session != NULL) { + g_object_get (session, + "session-type", &event.event.seat_session_added.session_type, + "x11-display", &event.event.seat_session_added.session_x11_display, + "x11-display-device", &event.event.seat_session_added.session_x11_display_device, + "display-device", &event.event.seat_session_added.session_display_device, + "remote-host-name", &event.event.seat_session_added.session_remote_host_name, + "is-local", &event.event.seat_session_added.session_is_local, + "unix-user", &event.event.seat_session_added.session_unix_user, + NULL); + ck_session_get_creation_time (session, &event.event.seat_session_added.session_creation_time, NULL); + g_debug ("Got uid: %u", event.event.seat_session_added.session_unix_user); + } else { + } + + error = NULL; + res = ck_event_logger_queue_event (manager->priv->logger, &event, &error); + if (! res) { + g_debug ("Unable to log event: %s", error->message); + g_error_free (error); + } + + g_free (sid); + + g_free (event.event.seat_session_added.session_type); + g_free (event.event.seat_session_added.session_x11_display); + g_free (event.event.seat_session_added.session_x11_display_device); + g_free (event.event.seat_session_added.session_display_device); + g_free (event.event.seat_session_added.session_remote_host_name); + g_free (event.event.seat_session_added.session_creation_time); +} + +static void +log_seat_session_removed_event (CkManager *manager, + CkSeat *seat, + const char *ssid) +{ + CkLogEvent event; + gboolean res; + GError *error; + char *sid; + CkSession *session; + + memset (&event, 0, sizeof (CkLogEvent)); + + event.type = CK_LOG_EVENT_SEAT_SESSION_REMOVED; + g_get_current_time (&event.timestamp); + + sid = NULL; + ck_seat_get_id (seat, &sid, NULL); + + event.event.seat_session_removed.seat_id = (char *)get_object_id_basename (sid); + event.event.seat_session_removed.session_id = (char *)get_object_id_basename (ssid); + + session = g_hash_table_lookup (manager->priv->sessions, ssid); + if (session != NULL) { + g_object_get (session, + "session-type", &event.event.seat_session_removed.session_type, + "x11-display", &event.event.seat_session_removed.session_x11_display, + "x11-display-device", &event.event.seat_session_removed.session_x11_display_device, + "display-device", &event.event.seat_session_removed.session_display_device, + "remote-host-name", &event.event.seat_session_removed.session_remote_host_name, + "is-local", &event.event.seat_session_removed.session_is_local, + "unix-user", &event.event.seat_session_removed.session_unix_user, + NULL); + ck_session_get_creation_time (session, &event.event.seat_session_removed.session_creation_time, NULL); + g_debug ("Got uid: %u", event.event.seat_session_removed.session_unix_user); + } + + error = NULL; + res = ck_event_logger_queue_event (manager->priv->logger, &event, &error); + if (! res) { + g_debug ("Unable to log event: %s", error->message); + g_error_free (error); + } + + g_free (sid); + + g_free (event.event.seat_session_removed.session_type); + g_free (event.event.seat_session_removed.session_x11_display); + g_free (event.event.seat_session_removed.session_x11_display_device); + g_free (event.event.seat_session_removed.session_display_device); + g_free (event.event.seat_session_removed.session_remote_host_name); + g_free (event.event.seat_session_removed.session_creation_time); +} + +static void +log_seat_active_session_changed_event (CkManager *manager, + CkSeat *seat, + const char *ssid) +{ + CkLogEvent event; + gboolean res; + GError *error; + char *sid; + + memset (&event, 0, sizeof (CkLogEvent)); + + event.type = CK_LOG_EVENT_SEAT_ACTIVE_SESSION_CHANGED; + g_get_current_time (&event.timestamp); + + sid = NULL; + ck_seat_get_id (seat, &sid, NULL); + + event.event.seat_active_session_changed.seat_id = (char *)get_object_id_basename (sid); + event.event.seat_active_session_changed.session_id = (char *)get_object_id_basename (ssid); + + error = NULL; + res = ck_event_logger_queue_event (manager->priv->logger, &event, &error); + if (! res) { + g_debug ("Unable to log event: %s", error->message); + g_error_free (error); + } + + g_free (sid); +} + +static void +log_seat_device_added_event (CkManager *manager, + CkSeat *seat, + GValueArray *device) +{ + CkLogEvent event; + gboolean res; + GError *error; + char *sid; + GValue val_struct = { 0, }; + char *device_id; + char *device_type; + + memset (&event, 0, sizeof (CkLogEvent)); + + event.type = CK_LOG_EVENT_SEAT_DEVICE_ADDED; + g_get_current_time (&event.timestamp); + + sid = NULL; + device_type = NULL; + device_id = NULL; + + ck_seat_get_id (seat, &sid, NULL); + + g_value_init (&val_struct, CK_TYPE_DEVICE); + g_value_set_static_boxed (&val_struct, device); + res = dbus_g_type_struct_get (&val_struct, + 0, &device_type, + 1, &device_id, + G_MAXUINT); + + event.event.seat_device_added.seat_id = (char *)get_object_id_basename (sid); + + event.event.seat_device_added.device_id = device_id; + event.event.seat_device_added.device_type = device_type; + + error = NULL; + res = ck_event_logger_queue_event (manager->priv->logger, &event, &error); + if (! res) { + g_debug ("Unable to log event: %s", error->message); + g_error_free (error); + } + + g_free (sid); + g_free (device_type); + g_free (device_id); +} + +static void +log_seat_device_removed_event (CkManager *manager, + CkSeat *seat, + GValueArray *device) +{ + CkLogEvent event; + gboolean res; + GError *error; + char *sid; + GValue val_struct = { 0, }; + char *device_id; + char *device_type; + + memset (&event, 0, sizeof (CkLogEvent)); + + event.type = CK_LOG_EVENT_SEAT_DEVICE_REMOVED; + g_get_current_time (&event.timestamp); + + sid = NULL; + device_type = NULL; + device_id = NULL; + + ck_seat_get_id (seat, &sid, NULL); + + g_value_init (&val_struct, CK_TYPE_DEVICE); + g_value_set_static_boxed (&val_struct, device); + res = dbus_g_type_struct_get (&val_struct, + 0, &device_type, + 1, &device_id, + G_MAXUINT); + + event.event.seat_device_removed.seat_id = (char *)get_object_id_basename (sid); + + event.event.seat_device_removed.device_id = device_id; + event.event.seat_device_removed.device_type = device_type; + + error = NULL; + res = ck_event_logger_queue_event (manager->priv->logger, &event, &error); + if (! res) { + g_debug ("Unable to log event: %s", error->message); + g_error_free (error); + } + + g_free (sid); + g_free (device_type); + g_free (device_id); +} + +static char * +get_cookie_for_pid (CkManager *manager, + guint pid) +{ + char *cookie; + + /* FIXME: need a better way to get the cookie */ + + cookie = ck_unix_pid_get_env (pid, "XDG_SESSION_COOKIE"); + + return cookie; +} + +static CkSession * +get_session_for_unix_process (CkManager *manager, + guint pid) +{ + CkSessionLeader *leader; + CkSession *session; + char *cookie; + + session = NULL; + leader = NULL; + + cookie = get_cookie_for_pid (manager, pid); + if (cookie == NULL) { + goto out; + } + + leader = g_hash_table_lookup (manager->priv->leaders, cookie); + if (leader == NULL) { + goto out; + } + + session = g_hash_table_lookup (manager->priv->sessions, ck_session_leader_peek_session_id (leader)); + + out: + g_free (cookie); + + return session; +} + +#ifdef HAVE_POLKIT +static PolKitSession * +new_polkit_session_from_session (CkManager *manager, + CkSession *ck_session) +{ + PolKitSession *pk_session; + PolKitSeat *pk_seat; + uid_t uid; + gboolean is_active; + gboolean is_local; + char *sid; + char *ssid; + char *remote_host; + + sid = NULL; + ssid = NULL; + remote_host = NULL; + + ck_session_get_seat_id (ck_session, &sid, NULL); + + g_object_get (ck_session, + "active", &is_active, + "is-local", &is_local, + "id", &ssid, + "unix-user", &uid, + "remote-host-name", &remote_host, + NULL); + + pk_session = polkit_session_new (); + if (pk_session == NULL) { + goto out; + } + if (!polkit_session_set_uid (pk_session, uid)) { + polkit_session_unref (pk_session); + pk_session = NULL; + goto out; + } + if (!polkit_session_set_ck_objref (pk_session, ssid)) { + polkit_session_unref (pk_session); + pk_session = NULL; + goto out; + } + if (!polkit_session_set_ck_is_active (pk_session, is_active)) { + polkit_session_unref (pk_session); + pk_session = NULL; + goto out; + } + if (!polkit_session_set_ck_is_local (pk_session, is_local)) { + polkit_session_unref (pk_session); + pk_session = NULL; + goto out; + } + if (!is_local) { + if (!polkit_session_set_ck_remote_host (pk_session, remote_host)) { + polkit_session_unref (pk_session); + pk_session = NULL; + goto out; + } + + } + + + pk_seat = polkit_seat_new (); + if (pk_seat == NULL) { + polkit_session_unref (pk_session); + pk_session = NULL; + goto out; + } + if (!polkit_seat_set_ck_objref (pk_seat, sid)) { + polkit_seat_unref (pk_seat); + pk_seat = NULL; + polkit_session_unref (pk_session); + pk_session = NULL; + goto out; + } + if (!polkit_seat_validate (pk_seat)) { + polkit_seat_unref (pk_seat); + pk_seat = NULL; + polkit_session_unref (pk_session); + pk_session = NULL; + goto out; + } + + if (!polkit_session_set_seat (pk_session, pk_seat)) { + polkit_seat_unref (pk_seat); + pk_seat = NULL; + polkit_session_unref (pk_session); + pk_session = NULL; + goto out; + } + polkit_seat_unref (pk_seat); /* session object now owns this object */ + pk_seat = NULL; + + if (!polkit_session_validate (pk_session)) { + polkit_session_unref (pk_session); + pk_session = NULL; + goto out; + } + +out: + g_free (ssid); + g_free (sid); + g_free (remote_host); + + return pk_session; +} + +static PolKitCaller * +new_polkit_caller_from_dbus_name (CkManager *manager, + const char *dbus_name) +{ + PolKitCaller *caller; + pid_t pid; + uid_t uid; + char *selinux_context; + PolKitSession *pk_session; + DBusMessage *message; + DBusMessage *reply; + DBusMessageIter iter; + DBusMessageIter sub_iter; + char *str; + int num_elems; + DBusConnection *con; + DBusError error; + CkSession *ck_session; + + dbus_error_init (&error); + + con = dbus_g_connection_get_connection (manager->priv->connection); + + g_return_val_if_fail (con != NULL, NULL); + g_return_val_if_fail (dbus_name != NULL, NULL); + + selinux_context = NULL; + + caller = NULL; + ck_session = NULL; + pk_session = NULL; + + uid = dbus_bus_get_unix_user (con, dbus_name, &error); + if (dbus_error_is_set (&error)) { + g_warning ("Could not get uid for connection: %s %s", + error.name, + error.message); + dbus_error_free (&error); + goto out; + } + + message = dbus_message_new_method_call ("org.freedesktop.DBus", + "/org/freedesktop/DBus/Bus", + "org.freedesktop.DBus", + "GetConnectionUnixProcessID"); + dbus_message_iter_init_append (message, &iter); + dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &dbus_name); + reply = dbus_connection_send_with_reply_and_block (con, message, -1, &error); + + if (reply == NULL || dbus_error_is_set (&error)) { + g_warning ("Error doing GetConnectionUnixProcessID on Bus: %s: %s", + error.name, + error.message); + dbus_message_unref (message); + if (reply != NULL) { + dbus_message_unref (reply); + } + dbus_error_free (&error); + goto out; + } + dbus_message_iter_init (reply, &iter); + dbus_message_iter_get_basic (&iter, &pid); + dbus_message_unref (message); + dbus_message_unref (reply); + + message = dbus_message_new_method_call ("org.freedesktop.DBus", + "/org/freedesktop/DBus/Bus", + "org.freedesktop.DBus", + "GetConnectionSELinuxSecurityContext"); + dbus_message_iter_init_append (message, &iter); + dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &dbus_name); + reply = dbus_connection_send_with_reply_and_block (con, message, -1, &error); + /* SELinux might not be enabled */ + if (dbus_error_is_set (&error) && + strcmp (error.name, "org.freedesktop.DBus.Error.SELinuxSecurityContextUnknown") == 0) { + dbus_message_unref (message); + if (reply != NULL) { + dbus_message_unref (reply); + } + dbus_error_init (&error); + } else if (reply == NULL || dbus_error_is_set (&error)) { + g_warning ("Error doing GetConnectionSELinuxSecurityContext on Bus: %s: %s", error.name, error.message); + dbus_message_unref (message); + if (reply != NULL) { + dbus_message_unref (reply); + } + goto out; + } else { + /* TODO: verify signature */ + dbus_message_iter_init (reply, &iter); + dbus_message_iter_recurse (&iter, &sub_iter); + dbus_message_iter_get_fixed_array (&sub_iter, (void *) &str, &num_elems); + if (str != NULL && num_elems > 0) { + selinux_context = g_strndup (str, num_elems); + } + dbus_message_unref (message); + dbus_message_unref (reply); + } + + ck_session = get_session_for_unix_process (manager, pid); + if (ck_session == NULL) { + /* OK, this is not a catastrophe; just means the caller is not a + * member of any session or that ConsoleKit is not available.. + */ + goto not_in_session; + } + + pk_session = new_polkit_session_from_session (manager, ck_session); + if (pk_session == NULL) { + g_warning ("Got a session but couldn't construct polkit session object!"); + goto out; + } + if (!polkit_session_validate (pk_session)) { + polkit_session_unref (pk_session); + pk_session = NULL; + goto out; + } + +not_in_session: + + caller = polkit_caller_new (); + if (caller == NULL) { + if (pk_session != NULL) { + polkit_session_unref (pk_session); + pk_session = NULL; + } + goto out; + } + + if (!polkit_caller_set_dbus_name (caller, dbus_name)) { + if (pk_session != NULL) { + polkit_session_unref (pk_session); + pk_session = NULL; + } + polkit_caller_unref (caller); + caller = NULL; + goto out; + } + if (!polkit_caller_set_uid (caller, uid)) { + if (pk_session != NULL) { + polkit_session_unref (pk_session); + pk_session = NULL; + } + polkit_caller_unref (caller); + caller = NULL; + goto out; + } + if (!polkit_caller_set_pid (caller, pid)) { + if (pk_session != NULL) { + polkit_session_unref (pk_session); + pk_session = NULL; + } + polkit_caller_unref (caller); + caller = NULL; + goto out; + } + if (selinux_context != NULL) { + if (!polkit_caller_set_selinux_context (caller, selinux_context)) { + if (pk_session != NULL) { + polkit_session_unref (pk_session); + pk_session = NULL; + } + polkit_caller_unref (caller); + caller = NULL; + goto out; + } + } + if (pk_session != NULL) { + if (!polkit_caller_set_ck_session (caller, pk_session)) { + if (pk_session != NULL) { + polkit_session_unref (pk_session); + pk_session = NULL; + } + polkit_caller_unref (caller); + caller = NULL; + goto out; + } + polkit_session_unref (pk_session); /* caller object now own this object */ + pk_session = NULL; + } + + if (!polkit_caller_validate (caller)) { + polkit_caller_unref (caller); + caller = NULL; + goto out; + } + +out: + g_free (selinux_context); + + return caller; +} + +static gboolean +_check_polkit_for_action (CkManager *manager, + DBusGMethodInvocation *context, + const char *action) +{ + const char *sender; + GError *error; + DBusError dbus_error; + PolKitCaller *pk_caller; + PolKitAction *pk_action; + PolKitResult pk_result; + + error = NULL; + + g_debug ("constructing polkit data"); + + /* Check that caller is privileged */ + sender = dbus_g_method_get_sender (context); + dbus_error_init (&dbus_error); + + pk_caller = new_polkit_caller_from_dbus_name (manager, sender); + if (pk_caller == NULL) { + error = g_error_new (CK_MANAGER_ERROR, + CK_MANAGER_ERROR_GENERAL, + "Error getting information about caller: %s: %s", + dbus_error.name, + dbus_error.message); + dbus_error_free (&dbus_error); + dbus_g_method_return_error (context, error); + g_error_free (error); + return FALSE; + } + + pk_action = polkit_action_new (); + polkit_action_set_action_id (pk_action, action); + + g_debug ("checking if caller %s is authorized", sender); + + /* this version crashes if error is used */ + pk_result = polkit_context_is_caller_authorized (manager->priv->pol_ctx, + pk_action, + pk_caller, + TRUE, + NULL); + g_debug ("answer is: %s", (pk_result == POLKIT_RESULT_YES) ? "yes" : "no"); + + polkit_caller_unref (pk_caller); + polkit_action_unref (pk_action); + + if (pk_result != POLKIT_RESULT_YES) { + error = g_error_new (CK_MANAGER_ERROR, + CK_MANAGER_ERROR_NOT_PRIVILEGED, + "Not privileged for action: %s", + action); + dbus_error_free (&dbus_error); + dbus_g_method_return_error (context, error); + g_error_free (error); + return FALSE; + } + + return TRUE; +} +#endif + +/* 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 char * +get_user_name (uid_t uid) +{ + struct passwd *pwent; + char *name; + + name = NULL; + + pwent = getpwuid (uid); + + if (pwent != NULL) { + name = g_strdup (pwent->pw_name); + } + + return name; +} + +static gboolean +session_is_real_user (CkSession *session, + char **userp) +{ + int uid; + char *username; + char *session_type; + gboolean ret; + + ret = FALSE; + session_type = NULL; + username = NULL; + + session_type = NULL; + + g_object_get (session, + "unix-user", &uid, + "session-type", session_type, + NULL); + + username = get_user_name (uid); + + /* filter out GDM user */ + if (username != NULL && strcmp (username, "gdm") == 0) { + ret = FALSE; + goto out; + } + + if (userp != NULL) { + *userp = g_strdup (username); + } + + ret = TRUE; + + out: + g_free (username); + g_free (session_type); + + return ret; +} + +static void +collect_users (const char *ssid, + CkSession *session, + GHashTable *hash) +{ + char *username; + + if (session_is_real_user (session, &username)) { + if (username != NULL) { + g_hash_table_insert (hash, username, NULL); + } + } +} + +static guint +get_system_num_users (CkManager *manager) +{ + guint num_users; + GHashTable *hash; + + hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + + g_hash_table_foreach (manager->priv->sessions, (GHFunc)collect_users, hash); + + num_users = g_hash_table_size (hash); + + g_hash_table_destroy (hash); + + g_debug ("found %u unique users", num_users); + + return num_users; +} + +#ifdef ENABLE_RBAC_SHUTDOWN +static gboolean +check_rbac_permissions (CkManager *manager, + DBusGMethodInvocation *context) +{ + const char *sender; + char *username; + gboolean res; + uid_t uid; + pid_t pid; + + username = NULL; + sender = dbus_g_method_get_sender (context); + res = get_caller_info (manager, + sender, + &uid, + &pid); + if (!res) { + goto out; + } + + username = get_user_name (uid); + + if (username == NULL || + !chkauthattr (RBAC_SHUTDOWN_KEY, username)) { + res = FALSE; + goto out; + } + +out: + + if (res == TRUE) { + g_debug ("User %s has RBAC permission to stop/restart", username); + } else { + g_debug ("User %s does not have RBAC permission to stop/restart", username); + } + + g_free (username); + return res; +} +#endif + +/* + Example: + dbus-send --system --dest=org.freedesktop.ConsoleKit \ + --type=method_call --print-reply --reply-timeout=2000 \ + /org/freedesktop/ConsoleKit/Manager \ + org.freedesktop.ConsoleKit.Manager.Restart +*/ +gboolean +ck_manager_restart (CkManager *manager, + DBusGMethodInvocation *context) +{ + gboolean ret; + gboolean res; + const char *action; + GError *error; + + ret = FALSE; + + if (get_system_num_users (manager) > 1) { + action = "org.freedesktop.consolekit.system.restart-multiple-users"; + } else { + action = "org.freedesktop.consolekit.system.restart"; + } + + g_debug ("ConsoleKit Restart: %s", action); + +#ifdef HAVE_POLKIT + res = _check_polkit_for_action (manager, context, action); + if (! res) { + goto out; + } +#endif + +#ifdef ENABLE_RBAC_SHUTDOWN + if (! check_rbac_permissions (manager, context)) { + goto out; + } +#endif + + g_debug ("ConsoleKit preforming Restart: %s", action); + + log_system_restart_event (manager); + + error = NULL; + res = g_spawn_command_line_async (LIBDIR "/ConsoleKit/scripts/ck-system-restart", + &error); + if (! res) { + GError *new_error; + + g_warning ("Unable to restart system: %s", error->message); + + new_error = g_error_new (CK_MANAGER_ERROR, + CK_MANAGER_ERROR_GENERAL, + "Unable to restart system: %s", error->message); + dbus_g_method_return_error (context, new_error); + g_error_free (new_error); + + g_error_free (error); + } else { + ret = TRUE; + dbus_g_method_return (context); + } + + out: + + return ret; +} + +gboolean +ck_manager_stop (CkManager *manager, + DBusGMethodInvocation *context) +{ + gboolean ret; + gboolean res; + const char *action; + GError *error; + + ret = TRUE; + + if (get_system_num_users (manager) > 1) { + action = "org.freedesktop.consolekit.system.stop-multiple-users"; + } else { + action = "org.freedesktop.consolekit.system.stop"; + } + +#ifdef HAVE_POLKIT + res = _check_polkit_for_action (manager, context, action); + if (! res) { + goto out; + } +#endif + +#ifdef ENABLE_RBAC_SHUTDOWN + if (!check_rbac_permissions (manager, context)) + goto out; +#endif + + g_debug ("Stopping system"); + + log_system_stop_event (manager); + + error = NULL; + res = g_spawn_command_line_async (LIBDIR "/ConsoleKit/scripts/ck-system-stop", + &error); + if (! res) { + GError *new_error; + + g_warning ("Unable to stop system: %s", error->message); + + new_error = g_error_new (CK_MANAGER_ERROR, + CK_MANAGER_ERROR_GENERAL, + "Unable to stop system: %s", error->message); + dbus_g_method_return_error (context, new_error); + g_error_free (new_error); + + g_error_free (error); + } else { + ret = TRUE; + dbus_g_method_return (context); + } + + out: + return ret; +} + +static void +on_seat_active_session_changed (CkSeat *seat, + const char *ssid, + CkManager *manager) +{ + ck_manager_dump (manager); + log_seat_active_session_changed_event (manager, seat, ssid); +} + +static void +on_seat_session_added (CkSeat *seat, + const char *ssid, + CkManager *manager) +{ + ck_manager_dump (manager); + log_seat_session_added_event (manager, seat, ssid); +} + +static void +on_seat_session_removed (CkSeat *seat, + const char *ssid, + CkManager *manager) +{ + ck_manager_dump (manager); + log_seat_session_removed_event (manager, seat, ssid); +} + +static void +on_seat_device_added (CkSeat *seat, + GValueArray *device, + CkManager *manager) +{ + ck_manager_dump (manager); + log_seat_device_added_event (manager, seat, device); +} + +static void +on_seat_device_removed (CkSeat *seat, + GValueArray *device, + CkManager *manager) +{ + ck_manager_dump (manager); + log_seat_device_removed_event (manager, seat, device); +} + +static void +connect_seat_signals (CkManager *manager, + CkSeat *seat) +{ + g_signal_connect (seat, "active-session-changed", G_CALLBACK (on_seat_active_session_changed), manager); + g_signal_connect (seat, "session-added", G_CALLBACK (on_seat_session_added), manager); + g_signal_connect (seat, "session-removed", G_CALLBACK (on_seat_session_removed), manager); + g_signal_connect (seat, "device-added", G_CALLBACK (on_seat_device_added), manager); + g_signal_connect (seat, "device-removed", G_CALLBACK (on_seat_device_removed), manager); +} + +static void +disconnect_seat_signals (CkManager *manager, + CkSeat *seat) +{ + g_signal_handlers_disconnect_by_func (seat, on_seat_active_session_changed, manager); + g_signal_handlers_disconnect_by_func (seat, on_seat_session_added, manager); + g_signal_handlers_disconnect_by_func (seat, on_seat_session_removed, manager); + g_signal_handlers_disconnect_by_func (seat, on_seat_device_added, manager); + g_signal_handlers_disconnect_by_func (seat, on_seat_device_removed, manager); +} + static CkSeat * add_new_seat (CkManager *manager, CkSeatKind kind) @@ -293,12 +1521,18 @@ add_new_seat (CkManager *manager, goto out; } + connect_seat_signals (manager, seat); + g_hash_table_insert (manager->priv->seats, sid, seat); g_debug ("Added seat: %s kind:%d", sid, kind); + ck_manager_dump (manager); + g_signal_emit (manager, signals [SEAT_ADDED], 0, sid); + log_seat_added_event (manager, seat); + out: return seat; } @@ -307,19 +1541,49 @@ static void remove_seat (CkManager *manager, CkSeat *seat) { - char *sid; + char *sid; + char *orig_sid; + CkSeat *orig_seat; + gboolean res; sid = NULL; ck_seat_get_id (seat, &sid, NULL); + /* Need to get the original key/value */ + res = g_hash_table_lookup_extended (manager->priv->seats, + sid, + (gpointer *)&orig_sid, + (gpointer *)&orig_seat); + if (! res) { + g_debug ("Seat %s is not attached", sid); + goto out; + } + + /* Remove the seat from the list but don't call + * unref until the signal is emitted */ + g_hash_table_steal (manager->priv->seats, sid); + + disconnect_seat_signals (manager, orig_seat); + if (sid != NULL) { g_hash_table_remove (manager->priv->seats, sid); } + ck_manager_dump (manager); + + g_debug ("Emitting seat-removed: %s", sid); g_signal_emit (manager, signals [SEAT_REMOVED], 0, sid); + log_seat_removed_event (manager, orig_seat); + g_debug ("Removed seat: %s", sid); + if (orig_seat != NULL) { + g_object_unref (orig_seat); + } + g_free (orig_sid); + + out: g_free (sid); } @@ -384,51 +1648,6 @@ find_seat_for_session (CkManager *manager, 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) @@ -552,16 +1771,21 @@ ck_manager_get_system_idle_since_hint (CkManager *manager, } static void -open_session_for_leader_info (CkManager *manager, - LeaderInfo *leader_info, - const GPtrArray *parameters, - DBusGMethodInvocation *context) +open_session_for_leader (CkManager *manager, + CkSessionLeader *leader, + const GPtrArray *parameters, + DBusGMethodInvocation *context) { CkSession *session; CkSeat *seat; + const char *ssid; + const char *cookie; + + ssid = ck_session_leader_peek_session_id (leader); + cookie = ck_session_leader_peek_cookie (leader); - session = ck_session_new_with_parameters (leader_info->ssid, - leader_info->cookie, + session = ck_session_new_with_parameters (ssid, + cookie, parameters); if (session == NULL) { @@ -576,7 +1800,9 @@ open_session_for_leader_info (CkManager *manager, return; } - g_hash_table_insert (manager->priv->sessions, g_strdup (leader_info->ssid), g_object_ref (session)); + g_hash_table_insert (manager->priv->sessions, + g_strdup (ssid), + g_object_ref (session)); /* Add to seat */ seat = find_seat_for_session (manager, session); @@ -597,247 +1823,55 @@ open_session_for_leader_info (CkManager *manager, g_object_unref (session); - dbus_g_method_return (context, leader_info->cookie); + dbus_g_method_return (context, cookie); } static void -verify_and_open_session_for_leader_info (CkManager *manager, - LeaderInfo *leader_info, - const GPtrArray *parameters, - DBusGMethodInvocation *context) +verify_and_open_session_for_leader (CkManager *manager, + CkSessionLeader *leader, + 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 (¶m_val, CK_TYPE_PARAMETER_STRUCT); - g_value_take_boxed (¶m_val, - dbus_g_type_specialized_construct (CK_TYPE_PARAMETER_STRUCT)); - dbus_g_type_struct_set (¶m_val, - 0, key, - 1, &val, - G_MAXUINT); - g_value_unset (&val); - - g_ptr_array_add (parameters, g_value_get_boxed (¶m_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 (¶m_val, CK_TYPE_PARAMETER_STRUCT); - g_value_take_boxed (¶m_val, - dbus_g_type_specialized_construct (CK_TYPE_PARAMETER_STRUCT)); - dbus_g_type_struct_set (¶m_val, - 0, key, - 1, &val, - G_MAXUINT); - g_value_unset (&val); - - g_ptr_array_add (parameters, g_value_get_boxed (¶m_val)); + open_session_for_leader (manager, + leader, + parameters, + context); } 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 (¶m_val, CK_TYPE_PARAMETER_STRUCT); - g_value_take_boxed (¶m_val, - dbus_g_type_specialized_construct (CK_TYPE_PARAMETER_STRUCT)); - - dbus_g_type_struct_set (¶m_val, - 0, key, - 1, &val, - G_MAXUINT); - g_value_unset (&val); - - g_ptr_array_add (parameters, g_value_get_boxed (¶m_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) +collect_parameters_cb (CkSessionLeader *leader, + GPtrArray *parameters, + DBusGMethodInvocation *context, + CkManager *manager) { - 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); + if (parameters == NULL) { + 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; } - /* 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); + verify_and_open_session_for_leader (manager, + leader, + parameters, + context); } static void -generate_session_for_leader_info (CkManager *manager, - LeaderInfo *leader_info, - DBusGMethodInvocation *context) +generate_session_for_leader (CkManager *manager, + CkSessionLeader *leader, + 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); + gboolean res; - local_error = NULL; - res = ck_job_execute (job, &local_error); + res = ck_session_leader_collect_parameters (leader, + context, + (CkSessionLeaderDoneFunc)collect_parameters_cb, + manager); if (! res) { GError *error; error = g_error_new (CK_MANAGER_ERROR, @@ -845,19 +1879,7 @@ generate_session_for_leader_info (CkManager *manager, "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 @@ -866,12 +1888,14 @@ create_session_for_sender (CkManager *manager, const GPtrArray *parameters, DBusGMethodInvocation *context) { - pid_t pid; - uid_t uid; - gboolean res; - char *cookie; - char *ssid; - LeaderInfo *leader_info; + pid_t pid; + uid_t uid; + gboolean res; + char *cookie; + char *ssid; + CkSessionLeader *leader; + + g_debug ("CkManager: create session for sender: %s", sender); res = get_caller_info (manager, sender, @@ -892,27 +1916,27 @@ create_session_for_sender (CkManager *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); + leader = ck_session_leader_new (); + ck_session_leader_set_uid (leader, uid); + ck_session_leader_set_pid (leader, pid); + ck_session_leader_set_service_name (leader, sender); + ck_session_leader_set_session_id (leader, ssid); + ck_session_leader_set_cookie (leader, 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)); + g_strdup (cookie), + g_object_ref (leader)); if (parameters == NULL) { - generate_session_for_leader_info (manager, - leader_info, - context); + generate_session_for_leader (manager, + leader, + context); } else { - verify_and_open_session_for_leader_info (manager, - leader_info, - parameters, - context); + verify_and_open_session_for_leader (manager, + leader, + parameters, + context); } g_free (cookie); @@ -933,18 +1957,20 @@ 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; - CkProcessStat *stat; - char *ssid; - CkSession *session; - LeaderInfo *leader_info; - GError *local_error; + gboolean res; + char *sender; + uid_t calling_uid; + pid_t calling_pid; + CkProcessStat *stat; + char *ssid; + CkSession *session; + CkSessionLeader *leader; + GError *local_error; ssid = NULL; + g_debug ("CkManager: get session for cookie"); + sender = dbus_g_method_get_sender (context); res = get_caller_info (manager, @@ -984,8 +2010,8 @@ ck_manager_get_session_for_cookie (CkManager *manager, /* FIXME: should we restrict this by uid? */ ck_process_stat_free (stat); - leader_info = g_hash_table_lookup (manager->priv->leaders, cookie); - if (leader_info == NULL) { + leader = g_hash_table_lookup (manager->priv->leaders, cookie); + if (leader == NULL) { GError *error; error = g_error_new (CK_MANAGER_ERROR, CK_MANAGER_ERROR_GENERAL, @@ -995,7 +2021,7 @@ ck_manager_get_session_for_cookie (CkManager *manager, return FALSE; } - session = g_hash_table_lookup (manager->priv->sessions, leader_info->ssid); + session = g_hash_table_lookup (manager->priv->sessions, ck_session_leader_peek_session_id (leader)); if (session == NULL) { GError *error; error = g_error_new (CK_MANAGER_ERROR, @@ -1015,19 +2041,6 @@ ck_manager_get_session_for_cookie (CkManager *manager, return TRUE; } -static char * -get_cookie_for_pid (CkManager *manager, - guint pid) -{ - char *cookie; - - /* FIXME: need a better way to get the cookie */ - - cookie = ck_unix_pid_get_env (pid, "XDG_SESSION_COOKIE"); - - return cookie; -} - /* Example: dbus-send --system --dest=org.freedesktop.ConsoleKit \ @@ -1044,12 +2057,12 @@ ck_manager_get_session_for_unix_process (CkManager *manager, char *sender; uid_t calling_uid; pid_t calling_pid; - CkProcessStat *stat; char *cookie; - GError *error; sender = dbus_g_method_get_sender (context); + g_debug ("CkManager: get session for unix process: %u", pid); + res = get_caller_info (manager, sender, &calling_uid, @@ -1066,27 +2079,12 @@ ck_manager_get_session_for_unix_process (CkManager *manager, return FALSE; } - error = NULL; - res = ck_process_stat_new_for_unix_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? */ - - ck_process_stat_free (stat); - cookie = get_cookie_for_pid (manager, pid); if (cookie == NULL) { GError *error; + + g_debug ("CkManager: unable to lookup session for unix process: %u", pid); + error = g_error_new (CK_MANAGER_ERROR, CK_MANAGER_ERROR_GENERAL, _("Unable to lookup session information for process '%d'"), @@ -1120,6 +2118,8 @@ ck_manager_get_current_session (CkManager *manager, sender = dbus_g_method_get_sender (context); + g_debug ("CkManager: get current session"); + res = get_caller_info (manager, sender, &calling_uid, @@ -1175,43 +2175,59 @@ remove_session_for_cookie (CkManager *manager, const char *cookie, GError **error) { - CkSession *session; - LeaderInfo *leader_info; - char *ssid; - char *sid; + CkSession *orig_session; + char *orig_ssid; + CkSessionLeader *leader; + char *sid; + gboolean res; + gboolean ret; + + ret = FALSE; + orig_ssid = NULL; + orig_session = NULL; g_debug ("Removing session for cookie: %s", cookie); - leader_info = g_hash_table_lookup (manager->priv->leaders, cookie); + leader = g_hash_table_lookup (manager->priv->leaders, cookie); - if (leader_info == NULL) { + if (leader == NULL) { g_set_error (error, CK_MANAGER_ERROR, CK_MANAGER_ERROR_GENERAL, "Unable to find session for cookie"); - return FALSE; + goto out; } - session = g_hash_table_lookup (manager->priv->sessions, leader_info->ssid); - if (session == NULL) { + /* Need to get the original key/value */ + res = g_hash_table_lookup_extended (manager->priv->sessions, + ck_session_leader_peek_session_id (leader), + (gpointer *)&orig_ssid, + (gpointer *)&orig_session); + if (! res) { g_set_error (error, CK_MANAGER_ERROR, CK_MANAGER_ERROR_GENERAL, "Unable to find session for cookie"); - return FALSE; + goto out; } - ssid = g_strdup (leader_info->ssid); + /* Must keep a reference to the session in the manager until + * all events for seats are cleared. So don't remove + * or steal the session from the master list until + * it is removed from all seats. Otherwise, event logging + * for seat removals doesn't work. + */ /* remove from seat */ - ck_session_get_seat_id (session, &sid, NULL); + sid = NULL; + ck_session_get_seat_id (orig_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); + ck_seat_remove_session (seat, orig_session, NULL); kind = CK_SEAT_KIND_STATIC; /* if dynamic seat has no sessions then remove it */ @@ -1221,15 +2237,25 @@ remove_session_for_cookie (CkManager *manager, } } } + g_free (sid); - g_hash_table_remove (manager->priv->sessions, ssid); + /* Remove the session from the list but don't call + * unref until we are done with it */ + g_hash_table_steal (manager->priv->sessions, + ck_session_leader_peek_session_id (leader)); - g_free (sid); - g_free (ssid); + ck_manager_dump (manager); manager_update_system_idle_hint (manager); - return TRUE; + ret = TRUE; + out: + if (orig_session != NULL) { + g_object_unref (orig_session); + } + g_free (orig_ssid); + + return ret; } static gboolean @@ -1239,7 +2265,7 @@ paranoia_check_is_cookie_owner (CkManager *manager, pid_t calling_pid, GError **error) { - LeaderInfo *leader_info; + CkSessionLeader *leader; if (cookie == NULL) { g_set_error (error, @@ -1249,8 +2275,8 @@ paranoia_check_is_cookie_owner (CkManager *manager, return FALSE; } - leader_info = g_hash_table_lookup (manager->priv->leaders, cookie); - if (leader_info == NULL) { + leader = g_hash_table_lookup (manager->priv->leaders, cookie); + if (leader == NULL) { g_set_error (error, CK_MANAGER_ERROR, CK_MANAGER_ERROR_GENERAL, @@ -1258,7 +2284,7 @@ paranoia_check_is_cookie_owner (CkManager *manager, return FALSE; } - if (leader_info->uid != calling_uid) { + if (ck_session_leader_get_uid (leader) != calling_uid) { g_set_error (error, CK_MANAGER_ERROR, CK_MANAGER_ERROR_GENERAL, @@ -1268,7 +2294,7 @@ paranoia_check_is_cookie_owner (CkManager *manager, } /* do we want to restrict to the same process? */ - if (leader_info->pid != calling_pid) { + if (ck_session_leader_get_pid (leader) != calling_pid) { g_set_error (error, CK_MANAGER_ERROR, CK_MANAGER_ERROR_GENERAL, @@ -1341,15 +2367,18 @@ typedef struct { static gboolean remove_leader_for_connection (const char *cookie, - LeaderInfo *info, + CkSessionLeader *leader, RemoveLeaderData *data) { - g_assert (info != NULL); + const char *name; + + g_assert (leader != NULL); g_assert (data->service_name != NULL); - if (strcmp (info->service_name, data->service_name) == 0) { + name = ck_session_leader_peek_service_name (leader); + if (strcmp (name, data->service_name) == 0) { remove_session_for_cookie (data->manager, cookie, NULL); - leader_info_cancel (info); + ck_session_leader_cancel (leader); return TRUE; } @@ -1389,11 +2418,65 @@ bus_name_owner_changed (DBusGProxy *bus_proxy, service_name, old_service_name, new_service_name); } +#ifdef HAVE_POLKIT +static gboolean +pk_io_watch_have_data (GIOChannel *channel, + GIOCondition condition, + gpointer user_data) +{ + int fd; + PolKitContext *pk_context = user_data; + + fd = g_io_channel_unix_get_fd (channel); + polkit_context_io_func (pk_context, fd); + return TRUE; +} + +static int +pk_io_add_watch (PolKitContext *pk_context, + int fd) +{ + guint id = 0; + GIOChannel *channel; + + channel = g_io_channel_unix_new (fd); + if (channel == NULL) { + goto out; + } + + id = g_io_add_watch (channel, G_IO_IN, pk_io_watch_have_data, pk_context); + if (id == 0) { + g_io_channel_unref (channel); + goto out; + } + g_io_channel_unref (channel); + +out: + return id; +} + +static void +pk_io_remove_watch (PolKitContext *pk_context, + int watch_id) +{ + g_source_remove (watch_id); +} +#endif + static gboolean register_manager (CkManager *manager) { GError *error = NULL; +#ifdef HAVE_POLKIT + manager->priv->pol_ctx = polkit_context_new (); + polkit_context_set_io_watch_functions (manager->priv->pol_ctx, pk_io_add_watch, pk_io_remove_watch); + if (! polkit_context_init (manager->priv->pol_ctx, NULL)) { + g_critical ("cannot initialize libpolkit"); + return FALSE; + } +#endif + error = NULL; manager->priv->connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error); if (manager->priv->connection == NULL) { @@ -1464,6 +2547,7 @@ ck_manager_class_init (CkManagerClass *klass) 1, G_TYPE_BOOLEAN); dbus_g_object_type_install_info (CK_TYPE_MANAGER, &dbus_glib_ck_manager_object_info); + dbus_g_error_domain_register (CK_MANAGER_ERROR, NULL, CK_MANAGER_TYPE_ERROR); g_type_class_add_private (klass, sizeof (CkManagerPrivate)); } @@ -1547,6 +2631,31 @@ ck_manager_get_seats (CkManager *manager, } static void +listify_session_ids (char *id, + CkSession *session, + GPtrArray **array) +{ + g_ptr_array_add (*array, g_strdup (id)); +} + +gboolean +ck_manager_get_sessions (CkManager *manager, + GPtrArray **sessions, + GError **error) +{ + g_return_val_if_fail (CK_IS_MANAGER (manager), FALSE); + + if (sessions == NULL) { + return FALSE; + } + + *sessions = g_ptr_array_new (); + g_hash_table_foreach (manager->priv->sessions, (GHFunc)listify_session_ids, sessions); + + return TRUE; +} + +static void add_seat_for_file (CkManager *manager, const char *filename) { @@ -1562,11 +2671,17 @@ add_seat_for_file (CkManager *manager, return; } + connect_seat_signals (manager, seat); + g_hash_table_insert (manager->priv->seats, sid, seat); g_debug ("Added seat: %s", sid); + ck_manager_dump (manager); + g_signal_emit (manager, signals [SEAT_ADDED], 0, sid); + + log_seat_added_event (manager, seat); } static gboolean @@ -1627,7 +2742,9 @@ ck_manager_init (CkManager *manager) manager->priv->leaders = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, - (GDestroyNotify) leader_info_unref); + (GDestroyNotify) g_object_unref); + + manager->priv->logger = ck_event_logger_new (LOG_FILE); create_seats (manager); } @@ -1647,7 +2764,13 @@ ck_manager_finalize (GObject *object) 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); + if (manager->priv->bus_proxy != NULL) { + g_object_unref (manager->priv->bus_proxy); + } + + if (manager->priv->logger != NULL) { + g_object_unref (manager->priv->logger); + } G_OBJECT_CLASS (ck_manager_parent_class)->finalize (object); } diff --git a/src/ck-manager.h b/src/ck-manager.h index a6fd670..45910b7 100644 --- a/src/ck-manager.h +++ b/src/ck-manager.h @@ -58,11 +58,16 @@ typedef struct typedef enum { - CK_MANAGER_ERROR_GENERAL + CK_MANAGER_ERROR_GENERAL, + CK_MANAGER_ERROR_NOT_PRIVILEGED, + CK_MANAGER_NUM_ERRORS } CkManagerError; #define CK_MANAGER_ERROR ck_manager_error_quark () +GType ck_manager_error_get_type (void); +#define CK_MANAGER_TYPE_ERROR (ck_manager_error_get_type ()) + GQuark ck_manager_error_quark (void); GType ck_manager_get_type (void); @@ -70,9 +75,19 @@ CkManager * ck_manager_new (void); /* unprivileged methods */ + +/* System actions */ +gboolean ck_manager_stop (CkManager *manager, + DBusGMethodInvocation *context); +gboolean ck_manager_restart (CkManager *manager, + DBusGMethodInvocation *context); + /* Authoritative properties */ gboolean ck_manager_open_session (CkManager *manager, DBusGMethodInvocation *context); +gboolean ck_manager_get_sessions (CkManager *manager, + GPtrArray **sessions, + GError **error); gboolean ck_manager_get_seats (CkManager *manager, GPtrArray **seats, GError **error); diff --git a/src/ck-manager.xml b/src/ck-manager.xml index f40d6d5..0ca74fa 100644 --- a/src/ck-manager.xml +++ b/src/ck-manager.xml @@ -1,6 +1,12 @@ <!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="Restart"> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + </method> + <method name="Stop"> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + </method> <method name="OpenSession"> <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> <arg name="cookie" direction="out" type="s"/> @@ -18,6 +24,9 @@ <method name="GetSeats"> <arg name="seats" direction="out" type="ao"/> </method> + <method name="GetSessions"> + <arg name="sessions" direction="out" type="ao"/> + </method> <method name="GetSessionForCookie"> <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> <arg name="cookie" direction="in" type="s"/> diff --git a/src/ck-marshal.c b/src/ck-marshal.c index 6b77b77..8b08794 100644 --- a/src/ck-marshal.c +++ b/src/ck-marshal.c @@ -51,10 +51,10 @@ /* VOID:UINT,STRING (ck-marshal.list:1) */ void ck_marshal_VOID__UINT_STRING (GClosure *closure, - GValue *return_value, + GValue *return_value G_GNUC_UNUSED, guint n_param_values, const GValue *param_values, - gpointer invocation_hint, + gpointer invocation_hint G_GNUC_UNUSED, gpointer marshal_data) { typedef void (*GMarshalFunc_VOID__UINT_STRING) (gpointer data1, @@ -88,10 +88,10 @@ ck_marshal_VOID__UINT_STRING (GClosure *closure, /* BOOLEAN:POINTER (ck-marshal.list:2) */ void ck_marshal_BOOLEAN__POINTER (GClosure *closure, - GValue *return_value, + GValue *return_value G_GNUC_UNUSED, guint n_param_values, const GValue *param_values, - gpointer invocation_hint, + gpointer invocation_hint G_GNUC_UNUSED, gpointer marshal_data) { typedef gboolean (*GMarshalFunc_BOOLEAN__POINTER) (gpointer data1, diff --git a/src/ck-run-programs.c b/src/ck-run-programs.c new file mode 100644 index 0000000..f9d62c7 --- /dev/null +++ b/src/ck-run-programs.c @@ -0,0 +1,230 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 David Zeuthen <david@fubar.dk> + * + * 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 <sys/types.h> +#include <signal.h> + +#include <glib.h> +#include <glib/gi18n.h> +#include <glib/gstdio.h> + +#include "ck-run-programs.h" + +/* The number of wall-clock seconds a program is allowed to run before we kill it */ +#define TIMEOUT_SECONDS 15 + +/* Guaranteed by POSIX; see 'man environ' for details */ +extern char **environ; + +typedef struct { + int refcount; + + char *path; + + gboolean child_is_running; + guint watch_id; + guint timeout_id; + GPid pid; +} ChildData; + +static ChildData * +_child_data_new (void) +{ + ChildData *cd; + + cd = g_new0 (ChildData, 1); + cd->refcount = 1; + g_debug ("Allocated ChildData %p", cd); + + return cd; +} + +static ChildData * +_child_data_ref (ChildData *cd) +{ + cd->refcount++; + return cd; +} + + +static void +_child_data_unref (ChildData *cd) +{ + cd->refcount--; + if (cd->refcount == 0) { + g_free (cd->path); + g_free (cd); + g_debug ("Freeing ChildData %p", cd); + } +} + + +static void +_child_watch (GPid pid, + int status, + ChildData *cd) +{ + g_debug ("In _child_watch for pid %d", pid); + + g_spawn_close_pid (pid); + g_source_remove (cd->timeout_id); + + cd->child_is_running = FALSE; + + _child_data_unref (cd); +} + +static gboolean +_child_timeout (ChildData *cd) +{ + /* The program we ran timed out; this is a bug in the program */ + g_warning ("The program %s didn't exit within %d seconds; killing it", cd->path, TIMEOUT_SECONDS); + + kill (cd->pid, SIGTERM); + + cd->child_is_running = FALSE; + return FALSE; +} + +/** + * ck_run_programs: + * @dirpath: Path to a directory containing programs to run + * @action: Argument to pass to each program + * @extra_env: Extra environment to pass + * + * Synchronously run all scripts with suffix .ck in the given + * directory. + */ +void +ck_run_programs (const char *dirpath, + const char *action, + char **extra_env) +{ + GDir *dir; + GError *error; + const char *name; + char **env_for_child; + int environ_len; + int extra_env_len; + int n; + int m; + + g_return_if_fail (dirpath != NULL); + g_return_if_fail (action != NULL); + + g_debug ("Running programs in %s for action %s", dirpath, action); + + /* Construct an environment consisting of the existing and the given environment */ + environ_len = environ != NULL ? g_strv_length (environ) : 0; + extra_env_len = extra_env != NULL ? g_strv_length (extra_env) : 0; + env_for_child = g_new0 (char *, environ_len + extra_env_len + 2); + m = 0; + for (n = 0; n < environ_len; n++) { + env_for_child [m++] = g_strdup (environ[n]); + } + for (n = 0; n < extra_env_len; n++) { + env_for_child [m++] = g_strdup (extra_env[n]); + } + env_for_child[m] = NULL; + + error = NULL; + + dir = g_dir_open (dirpath, 0, &error); + if (dir == NULL) { + /* This is unexpected; it means ConsoleKit isn't properly installed */ + g_warning ("Unable to open directory %s: %s", dirpath, error->message); + g_error_free (error); + goto out; + } + + while ((name = g_dir_read_name (dir)) != NULL) { + char *child_argv[3]; + ChildData *cd; + gboolean res; + + if (!g_str_has_suffix (name, ".ck")) + continue; + + child_argv[0] = g_strdup_printf ("%s/%s", dirpath, name); + child_argv[1] = (char *) action; + child_argv[2] = NULL; + + error = NULL; + cd = _child_data_new (); + cd->path = g_strdup (child_argv[0]); + + /* The ChildData instance is also unreffed in _child_watch; we only ref + * it here to prevent cd from being destroyed while checking it in + * the mainloop + */ + _child_data_ref (cd); + + res = g_spawn_async (NULL, + child_argv, + env_for_child, + G_SPAWN_DO_NOT_REAP_CHILD, + NULL, + NULL, + &cd->pid, + &error); + if (! res) { + /* This is unexpected; it means the program to run isn't installed correctly */ + g_warning ("Unable to spawn %s: %s", child_argv[0], error->message); + g_error_free (error); + _child_data_unref (cd); + _child_data_unref (cd); + goto out_loop; + } + cd->child_is_running = TRUE; + + g_debug ("Waiting for child with pid %d", cd->pid); + + cd->watch_id = g_child_watch_add (cd->pid, + (GChildWatchFunc)_child_watch, + cd); + cd->timeout_id = g_timeout_add (TIMEOUT_SECONDS * 1000, + (GSourceFunc)_child_timeout, + cd); + + /* run the mainloop; this allows the main daemon to + * continue serving clients (including the program we + * just launched) */ + while (cd->child_is_running) { + g_main_context_iteration (NULL, TRUE); + } + + g_debug ("Done waiting for child with pid %d", cd->pid); + _child_data_unref (cd); + + out_loop: + g_free (child_argv[0]); + } +out: + g_strfreev (env_for_child); +} diff --git a/src/ck-run-programs.h b/src/ck-run-programs.h new file mode 100644 index 0000000..77cdf48 --- /dev/null +++ b/src/ck-run-programs.h @@ -0,0 +1,38 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 David Zeuthen <david@fubar.dk> + * + * 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_RUN_PROGRAMS_H +#define __CK_RUN_PROGRAMS_H + +#include <glib-object.h> +#include <dbus/dbus-glib.h> +#include "ck-session.h" + +G_BEGIN_DECLS + +void ck_run_programs (const char *dirpath, const char *action, char **extra_env); + +void ck_session_run_programs (CkSession *session, const char *action); + + +G_END_DECLS + +#endif /* __CK_RUN_PROGRAMS_H */ diff --git a/src/ck-seat-glue.h b/src/ck-seat-glue.h index d6cbca1..264ba13 100644 --- a/src/ck-seat-glue.h +++ b/src/ck-seat-glue.h @@ -53,7 +53,7 @@ G_BEGIN_DECLS #endif /* !G_ENABLE_DEBUG */ -/* BOOLEAN:POINTER,POINTER (/tmp/dbus-binding-tool-c-marshallers.33NIYT:1) */ +/* BOOLEAN:POINTER,POINTER (/tmp/dbus-binding-tool-c-marshallers.G1XI6T:1) */ extern void dbus_glib_marshal_ck_seat_BOOLEAN__POINTER_POINTER (GClosure *closure, GValue *return_value, guint n_param_values, @@ -62,10 +62,10 @@ extern void dbus_glib_marshal_ck_seat_BOOLEAN__POINTER_POINTER (GClosure *cl gpointer marshal_data); void dbus_glib_marshal_ck_seat_BOOLEAN__POINTER_POINTER (GClosure *closure, - GValue *return_value, + GValue *return_value G_GNUC_UNUSED, guint n_param_values, const GValue *param_values, - gpointer invocation_hint, + gpointer invocation_hint G_GNUC_UNUSED, gpointer marshal_data) { typedef gboolean (*GMarshalFunc_BOOLEAN__POINTER_POINTER) (gpointer data1, @@ -100,7 +100,7 @@ dbus_glib_marshal_ck_seat_BOOLEAN__POINTER_POINTER (GClosure *closure, g_value_set_boolean (return_value, v_return); } -/* NONE:BOXED,POINTER (/tmp/dbus-binding-tool-c-marshallers.33NIYT:2) */ +/* NONE:BOXED,POINTER (/tmp/dbus-binding-tool-c-marshallers.G1XI6T:2) */ extern void dbus_glib_marshal_ck_seat_VOID__BOXED_POINTER (GClosure *closure, GValue *return_value, guint n_param_values, @@ -109,10 +109,10 @@ extern void dbus_glib_marshal_ck_seat_VOID__BOXED_POINTER (GClosure *closure gpointer marshal_data); void dbus_glib_marshal_ck_seat_VOID__BOXED_POINTER (GClosure *closure, - GValue *return_value, + GValue *return_value G_GNUC_UNUSED, guint n_param_values, const GValue *param_values, - gpointer invocation_hint, + gpointer invocation_hint G_GNUC_UNUSED, gpointer marshal_data) { typedef void (*GMarshalFunc_VOID__BOXED_POINTER) (gpointer data1, diff --git a/src/ck-seat.c b/src/ck-seat.c index e04b569..009452e 100644 --- a/src/ck-seat.c +++ b/src/ck-seat.c @@ -42,12 +42,14 @@ #include "ck-session.h" #include "ck-vt-monitor.h" +#include "ck-run-programs.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" +#define NONULL_STRING(x) ((x) != NULL ? (x) : "") struct CkSeatPrivate { @@ -122,17 +124,37 @@ ck_seat_get_active_session (CkSeat *seat, char **ssid, GError **error) { + gboolean ret; + char *session_id; + g_return_val_if_fail (CK_IS_SEAT (seat), FALSE); + g_debug ("CkSeat: get active session"); + session_id = NULL; + ret = FALSE; if (seat->priv->active_session != NULL) { - ck_session_get_id (seat->priv->active_session, ssid, NULL); + gboolean res; + res = ck_session_get_id (seat->priv->active_session, &session_id, NULL); + if (res) { + ret = TRUE; + } + } else { + g_debug ("CkSeat: seat has no active session"); + } + + if (! ret) { + g_set_error (error, + CK_SEAT_ERROR, + CK_SEAT_ERROR_GENERAL, + "%s", "Seat has no active session"); } else { if (ssid != NULL) { - *ssid = NULL; + *ssid = g_strdup (session_id); } } - return TRUE; + g_free (session_id); + return ret; } typedef struct @@ -200,8 +222,11 @@ _seat_activate_session (CkSeat *seat, goto out; } - ck_session_get_display_device (session, &device, NULL); - + device = NULL; + ck_session_get_x11_display_device (session, &device, NULL); + if (device == NULL) { + ck_session_get_display_device (session, &device, NULL); + } res = ck_get_console_num_from_device (device, &num); if (! res) { GError *error; @@ -264,6 +289,8 @@ ck_seat_activate_session (CkSeat *seat, session = NULL; + g_debug ("Trying to activate session: %s", ssid); + if (ssid != NULL) { session = g_hash_table_lookup (seat->priv->sessions, ssid); } @@ -473,7 +500,7 @@ change_active_session (CkSeat *seat, ck_session_set_active (session, TRUE, NULL); } - g_debug ("Active session changed: %s", ssid); + g_debug ("Active session changed: %s", ssid ? ssid : "(null)"); g_signal_emit (seat, signals [ACTIVE_SESSION_CHANGED], 0, ssid); @@ -527,8 +554,11 @@ ck_seat_remove_session (CkSeat *seat, CkSession *session, GError **error) { - char *ssid; - gboolean ret; + char *ssid; + char *orig_ssid; + CkSession *orig_session; + gboolean res; + gboolean ret; g_return_val_if_fail (CK_IS_SEAT (seat), FALSE); @@ -536,7 +566,12 @@ ck_seat_remove_session (CkSeat *seat, ssid = NULL; ck_session_get_id (session, &ssid, NULL); - if (g_hash_table_lookup (seat->priv->sessions, ssid) == NULL) { + /* Need to get the original key/value */ + res = g_hash_table_lookup_extended (seat->priv->sessions, + ssid, + (gpointer *)&orig_ssid, + (gpointer *)&orig_session); + if (! res) { g_debug ("Session %s is not attached to seat %s", ssid, seat->priv->id); g_set_error (error, CK_SEAT_ERROR, @@ -547,15 +582,23 @@ ck_seat_remove_session (CkSeat *seat, g_signal_handlers_disconnect_by_func (session, session_activate, seat); - g_debug ("Emitting removed signal: %s", ssid); + /* Remove the session from the list but don't call + * unref until the signal is emitted */ + g_hash_table_steal (seat->priv->sessions, ssid); - g_signal_emit (seat, signals [SESSION_REMOVED], 0, ssid); + ck_session_run_programs (session, "session_removed"); - g_hash_table_remove (seat->priv->sessions, ssid); + g_debug ("Emitting session-removed: %s", ssid); + g_signal_emit (seat, signals [SESSION_REMOVED], 0, ssid); /* try to change the active session */ maybe_update_active_session (seat); + if (orig_session != NULL) { + g_object_unref (orig_session); + } + g_free (orig_ssid); + ret = TRUE; out: g_free (ssid); @@ -581,6 +624,8 @@ ck_seat_add_session (CkSeat *seat, g_signal_connect_object (session, "activate", G_CALLBACK (session_activate), seat, 0); /* FIXME: attach to property notify signals? */ + ck_session_run_programs (session, "session_added"); + g_debug ("Emitting added signal: %s", ssid); g_signal_emit (seat, signals [SESSION_ADDED], 0, ssid); @@ -637,7 +682,6 @@ ck_seat_add_device (CkSeat *seat, g_ptr_array_add (seat->priv->devices, g_boxed_copy (CK_TYPE_DEVICE, device)); g_debug ("Emitting device added signal"); - g_signal_emit (seat, signals [DEVICE_ADDED], 0, device); return TRUE; @@ -653,7 +697,6 @@ ck_seat_remove_device (CkSeat *seat, /* FIXME: check if already present */ if (0) { g_debug ("Emitting device removed signal"); - g_signal_emit (seat, signals [DEVICE_REMOVED], 0, device); } @@ -1103,3 +1146,90 @@ ck_seat_new_from_file (const char *sid, return seat; } + +static void +dump_seat_session_iter (char *id, + CkSession *session, + GString *str) +{ + char *session_id; + GError *error; + + error = NULL; + if (! ck_session_get_id (session, &session_id, &error)) { + g_warning ("Cannot get session id from seat: %s", error->message); + g_error_free (error); + } else { + if (str->len > 0) { + g_string_append_c (str, ' '); + } + g_string_append (str, session_id); + g_free (session_id); + } +} + +void +ck_seat_dump (CkSeat *seat, + GKeyFile *key_file) +{ + char *group_name; + GString *str; + char *s; + int n; + + group_name = g_strdup_printf ("Seat %s", seat->priv->id); + + g_key_file_set_integer (key_file, group_name, "kind", seat->priv->kind); + + str = g_string_new (NULL); + g_hash_table_foreach (seat->priv->sessions, (GHFunc) dump_seat_session_iter, str); + s = g_string_free (str, FALSE); + g_key_file_set_string (key_file, group_name, "sessions", s); + g_free (s); + + str = g_string_new (NULL); + if (seat->priv->devices != NULL) { + for (n = 0; n < seat->priv->devices->len; n++) { + int m; + GValueArray *va; + + va = seat->priv->devices->pdata[n]; + + if (str->len > 0) + g_string_append_c (str, ' '); + for (m = 0; m < va->n_values; m++) { + if (m > 0) + g_string_append_c (str, ':'); + g_string_append (str, g_value_get_string ((const GValue *) &((va->values)[m]))); + } + + g_debug ("foo %d", va->n_values); + } + } + s = g_string_free (str, FALSE); + g_key_file_set_string (key_file, group_name, "devices", s); + g_free (s); + + + if (seat->priv->active_session != NULL) { + char *session_id; + GError *error; + + error = NULL; + if (! ck_session_get_id (seat->priv->active_session, &session_id, &error)) { + g_warning ("Cannot get session id for active session on seat %s: %s", + seat->priv->id, + error->message); + g_error_free (error); + } else { + g_key_file_set_string (key_file, + group_name, + "active_session", + NONULL_STRING (session_id)); + g_free (session_id); + } + } + + g_free (group_name); +} + diff --git a/src/ck-seat.h b/src/ck-seat.h index 63ab3d5..5977781 100644 --- a/src/ck-seat.h +++ b/src/ck-seat.h @@ -90,6 +90,10 @@ CkSeat * ck_seat_new_from_file (const char *sid, CkSeat * ck_seat_new_with_devices (const char *sid, CkSeatKind kind, GPtrArray *devices); + +void ck_seat_dump (CkSeat *seat, + GKeyFile *key_file); + gboolean ck_seat_get_kind (CkSeat *seat, CkSeatKind *kind, GError **error); diff --git a/src/ck-session-glue.h b/src/ck-session-glue.h index cabded2..c06173f 100644 --- a/src/ck-session-glue.h +++ b/src/ck-session-glue.h @@ -53,7 +53,7 @@ G_BEGIN_DECLS #endif /* !G_ENABLE_DEBUG */ -/* BOOLEAN:POINTER,POINTER (/tmp/dbus-binding-tool-c-marshallers.8HEIYT:1) */ +/* BOOLEAN:POINTER,POINTER (/tmp/dbus-binding-tool-c-marshallers.P39I6T:1) */ extern void dbus_glib_marshal_ck_session_BOOLEAN__POINTER_POINTER (GClosure *closure, GValue *return_value, guint n_param_values, @@ -62,10 +62,10 @@ extern void dbus_glib_marshal_ck_session_BOOLEAN__POINTER_POINTER (GClosure gpointer marshal_data); void dbus_glib_marshal_ck_session_BOOLEAN__POINTER_POINTER (GClosure *closure, - GValue *return_value, + GValue *return_value G_GNUC_UNUSED, guint n_param_values, const GValue *param_values, - gpointer invocation_hint, + gpointer invocation_hint G_GNUC_UNUSED, gpointer marshal_data) { typedef gboolean (*GMarshalFunc_BOOLEAN__POINTER_POINTER) (gpointer data1, @@ -100,7 +100,7 @@ dbus_glib_marshal_ck_session_BOOLEAN__POINTER_POINTER (GClosure *closure, g_value_set_boolean (return_value, v_return); } -/* NONE:BOOLEAN,POINTER (/tmp/dbus-binding-tool-c-marshallers.8HEIYT:2) */ +/* NONE:BOOLEAN,POINTER (/tmp/dbus-binding-tool-c-marshallers.P39I6T:2) */ extern void dbus_glib_marshal_ck_session_VOID__BOOLEAN_POINTER (GClosure *closure, GValue *return_value, guint n_param_values, @@ -109,10 +109,10 @@ extern void dbus_glib_marshal_ck_session_VOID__BOOLEAN_POINTER (GClosure *cl gpointer marshal_data); void dbus_glib_marshal_ck_session_VOID__BOOLEAN_POINTER (GClosure *closure, - GValue *return_value, + GValue *return_value G_GNUC_UNUSED, guint n_param_values, const GValue *param_values, - gpointer invocation_hint, + gpointer invocation_hint G_GNUC_UNUSED, gpointer marshal_data) { typedef void (*GMarshalFunc_VOID__BOOLEAN_POINTER) (gpointer data1, @@ -144,7 +144,7 @@ dbus_glib_marshal_ck_session_VOID__BOOLEAN_POINTER (GClosure *closure, } #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.8HEIYT:3) */ +/* NONE:POINTER (/tmp/dbus-binding-tool-c-marshallers.P39I6T: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 diff --git a/src/ck-session-leader.c b/src/ck-session-leader.c new file mode 100644 index 0000000..756feac --- /dev/null +++ b/src/ck-session-leader.c @@ -0,0 +1,567 @@ +/* -*- 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-session-leader.h" +#include "ck-job.h" + +#define CK_SESSION_LEADER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), CK_TYPE_SESSION_LEADER, CkSessionLeaderPrivate)) + +#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 CkSessionLeaderPrivate +{ + char *id; + uid_t uid; + pid_t pid; + char *service_name; + char *session_id; + char *cookie; + GList *pending_jobs; + gboolean cancelled; +}; + +enum { + PROP_0, +}; + +static void ck_session_leader_class_init (CkSessionLeaderClass *klass); +static void ck_session_leader_init (CkSessionLeader *session_leader); +static void ck_session_leader_finalize (GObject *object); + +G_DEFINE_TYPE (CkSessionLeader, ck_session_leader, G_TYPE_OBJECT) + +GQuark +ck_session_leader_error_quark (void) +{ + static GQuark ret = 0; + if (ret == 0) { + ret = g_quark_from_static_string ("ck_session_leader_error"); + } + + return ret; +} + +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); + } +} + +void +ck_session_leader_cancel (CkSessionLeader *leader) +{ + g_return_if_fail (CK_IS_SESSION_LEADER (leader)); + + if (leader->priv->pending_jobs != NULL) { + g_list_foreach (leader->priv->pending_jobs, (GFunc)remove_pending_job, NULL); + g_list_free (leader->priv->pending_jobs); + leader->priv->pending_jobs = NULL; + } + + leader->priv->cancelled = TRUE; +} + + +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 (¶m_val, CK_TYPE_PARAMETER_STRUCT); + g_value_take_boxed (¶m_val, + dbus_g_type_specialized_construct (CK_TYPE_PARAMETER_STRUCT)); + dbus_g_type_struct_set (¶m_val, + 0, key, + 1, &val, + G_MAXUINT); + g_value_unset (&val); + + g_ptr_array_add (parameters, g_value_get_boxed (¶m_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 (¶m_val, CK_TYPE_PARAMETER_STRUCT); + g_value_take_boxed (¶m_val, + dbus_g_type_specialized_construct (CK_TYPE_PARAMETER_STRUCT)); + dbus_g_type_struct_set (¶m_val, + 0, key, + 1, &val, + G_MAXUINT); + g_value_unset (&val); + + g_ptr_array_add (parameters, g_value_get_boxed (¶m_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 (¶m_val, CK_TYPE_PARAMETER_STRUCT); + g_value_take_boxed (¶m_val, + dbus_g_type_specialized_construct (CK_TYPE_PARAMETER_STRUCT)); + + dbus_g_type_struct_set (¶m_val, + 0, key, + 1, &val, + G_MAXUINT); + g_value_unset (&val); + + g_ptr_array_add (parameters, g_value_get_boxed (¶m_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; +} + + +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); +} + +typedef struct { + CkSessionLeader *leader; + CkSessionLeaderDoneFunc done_cb; + gpointer user_data; + DBusGMethodInvocation *context; +} JobData; + +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); + + data->done_cb (data->leader, + parameters, + data->context, + data->user_data); + parameters_free (parameters); + } else { + data->done_cb (data->leader, + NULL, + data->context, + data->user_data); + } + + /* remove job from queue */ + data->leader->priv->pending_jobs = g_list_remove (data->leader->priv->pending_jobs, job); + + g_signal_handlers_disconnect_by_func (job, job_completed, data); + g_object_unref (job); +} + +static void +job_data_free (JobData *data) +{ + g_free (data); +} + +gboolean +ck_session_leader_collect_parameters (CkSessionLeader *session_leader, + DBusGMethodInvocation *context, + CkSessionLeaderDoneFunc done_cb, + gpointer user_data) +{ + GError *local_error; + char *command; + gboolean res; + gboolean ret; + CkJob *job; + JobData *data; + + ret = FALSE; + + data = g_new0 (JobData, 1); + data->leader = session_leader; + data->done_cb = done_cb; + data->user_data = user_data; + data->context = context; + + command = g_strdup_printf ("%s --uid %u --pid %u", + LIBEXECDIR "/ck-collect-session-info", + session_leader->priv->uid, + session_leader->priv->pid); + job = ck_job_new (); + ck_job_set_command (job, command); + g_free (command); + + 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) { + if (local_error != NULL) { + g_debug ("stat on pid %d failed: %s", session_leader->priv->pid, local_error->message); + g_error_free (local_error); + } + + g_object_unref (job); + + goto out; + } + + /* Add job to queue */ + session_leader->priv->pending_jobs = g_list_prepend (session_leader->priv->pending_jobs, job); + ret = TRUE; + + out: + return ret; +} + + +const char * +ck_session_leader_peek_session_id (CkSessionLeader *session_leader) +{ + g_return_val_if_fail (CK_IS_SESSION_LEADER (session_leader), NULL); + return session_leader->priv->session_id; +} + +const char * +ck_session_leader_peek_cookie (CkSessionLeader *session_leader) +{ + g_return_val_if_fail (CK_IS_SESSION_LEADER (session_leader), NULL); + return session_leader->priv->cookie; +} + +const char * +ck_session_leader_peek_service_name (CkSessionLeader *session_leader) +{ + g_return_val_if_fail (CK_IS_SESSION_LEADER (session_leader), NULL); + return session_leader->priv->service_name; +} + +uid_t +ck_session_leader_get_uid (CkSessionLeader *session_leader) +{ + g_return_val_if_fail (CK_IS_SESSION_LEADER (session_leader), -1); + return session_leader->priv->uid; +} + +pid_t +ck_session_leader_get_pid (CkSessionLeader *session_leader) +{ + g_return_val_if_fail (CK_IS_SESSION_LEADER (session_leader), -1); + return session_leader->priv->pid; +} + +void +ck_session_leader_set_pid (CkSessionLeader *session_leader, + pid_t pid) +{ + g_return_if_fail (CK_IS_SESSION_LEADER (session_leader)); + session_leader->priv->pid = pid; +} + +void +ck_session_leader_set_uid (CkSessionLeader *session_leader, + uid_t uid) +{ + g_return_if_fail (CK_IS_SESSION_LEADER (session_leader)); + session_leader->priv->uid = uid; +} + +void +ck_session_leader_set_session_id (CkSessionLeader *session_leader, + const char *session_id) +{ + g_return_if_fail (CK_IS_SESSION_LEADER (session_leader)); + g_free (session_leader->priv->session_id); + session_leader->priv->session_id = g_strdup (session_id); +} + +void +ck_session_leader_set_cookie (CkSessionLeader *session_leader, + const char *cookie) +{ + g_return_if_fail (CK_IS_SESSION_LEADER (session_leader)); + g_free (session_leader->priv->cookie); + session_leader->priv->cookie = g_strdup (cookie); +} + +void +ck_session_leader_set_service_name (CkSessionLeader *session_leader, + const char *service_name) +{ + g_return_if_fail (CK_IS_SESSION_LEADER (session_leader)); + g_free (session_leader->priv->service_name); + session_leader->priv->service_name = g_strdup (service_name); +} + +static void +ck_session_leader_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + CkSessionLeader *self; + + self = CK_SESSION_LEADER (object); + + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +ck_session_leader_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + CkSessionLeader *self; + + self = CK_SESSION_LEADER (object); + + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static GObject * +ck_session_leader_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_properties) +{ + CkSessionLeader *session_leader; + CkSessionLeaderClass *klass; + + klass = CK_SESSION_LEADER_CLASS (g_type_class_peek (CK_TYPE_SESSION_LEADER)); + + session_leader = CK_SESSION_LEADER (G_OBJECT_CLASS (ck_session_leader_parent_class)->constructor (type, + n_construct_properties, + construct_properties)); + + return G_OBJECT (session_leader); +} + +static void +ck_session_leader_class_init (CkSessionLeaderClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->constructor = ck_session_leader_constructor; + object_class->get_property = ck_session_leader_get_property; + object_class->set_property = ck_session_leader_set_property; + object_class->finalize = ck_session_leader_finalize; + + g_type_class_add_private (klass, sizeof (CkSessionLeaderPrivate)); +} + +static void +ck_session_leader_init (CkSessionLeader *session_leader) +{ + session_leader->priv = CK_SESSION_LEADER_GET_PRIVATE (session_leader); +} + +static void +ck_session_leader_finalize (GObject *object) +{ + CkSessionLeader *session_leader; + + g_return_if_fail (object != NULL); + g_return_if_fail (CK_IS_SESSION_LEADER (object)); + + session_leader = CK_SESSION_LEADER (object); + + g_return_if_fail (session_leader->priv != NULL); + + g_free (session_leader->priv->session_id); + session_leader->priv->session_id = NULL; + g_free (session_leader->priv->cookie); + session_leader->priv->cookie = NULL; + g_free (session_leader->priv->service_name); + session_leader->priv->service_name = NULL; + + G_OBJECT_CLASS (ck_session_leader_parent_class)->finalize (object); +} + +CkSessionLeader * +ck_session_leader_new (void) +{ + GObject *object; + + object = g_object_new (CK_TYPE_SESSION_LEADER, + NULL); + + return CK_SESSION_LEADER (object); +} + +void +ck_session_leader_dump (CkSessionLeader *session_leader, + GKeyFile *key_file) +{ + char *group_name; + + group_name = g_strdup_printf ("SessionLeader %s", session_leader->priv->session_id); + g_key_file_set_string (key_file, group_name, "session", session_leader->priv->session_id); + g_key_file_set_integer (key_file, group_name, "uid", session_leader->priv->uid); + g_key_file_set_integer (key_file, group_name, "pid", session_leader->priv->pid); + g_key_file_set_string (key_file, group_name, "cookie", session_leader->priv->cookie); + g_key_file_set_string (key_file, group_name, "service_name", session_leader->priv->service_name); + + g_free (group_name); +} diff --git a/src/ck-session-leader.h b/src/ck-session-leader.h new file mode 100644 index 0000000..4eb8857 --- /dev/null +++ b/src/ck-session-leader.h @@ -0,0 +1,97 @@ +/* -*- 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. + * + */ + + +#ifndef __CK_SESSION_LEADER_H +#define __CK_SESSION_LEADER_H + +#include <glib-object.h> +#include <dbus/dbus-glib.h> + +G_BEGIN_DECLS + +#define CK_TYPE_SESSION_LEADER (ck_session_leader_get_type ()) +#define CK_SESSION_LEADER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CK_TYPE_SESSION_LEADER, CkSessionLeader)) +#define CK_SESSION_LEADER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), CK_TYPE_SESSION_LEADER, CkSessionLeaderClass)) +#define CK_IS_SESSION_LEADER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), CK_TYPE_SESSION_LEADER)) +#define CK_IS_SESSION_LEADER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), CK_TYPE_SESSION_LEADER)) +#define CK_SESSION_LEADER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), CK_TYPE_SESSION_LEADER, CkSessionLeaderClass)) + +typedef struct CkSessionLeaderPrivate CkSessionLeaderPrivate; + +typedef struct +{ + GObject parent; + CkSessionLeaderPrivate *priv; +} CkSessionLeader; + +typedef struct +{ + GObjectClass parent_class; + +} CkSessionLeaderClass; + +typedef enum +{ + CK_SESSION_LEADER_ERROR_GENERAL +} CkSessionLeaderError; + +#define CK_SESSION_LEADER_ERROR ck_session_leader_error_quark () + +typedef void (* CkSessionLeaderDoneFunc) (CkSessionLeader *session_leader, + GPtrArray *parameters, + DBusGMethodInvocation *context, + gpointer data); + +GQuark ck_session_leader_error_quark (void); +GType ck_session_leader_get_type (void); +CkSessionLeader * ck_session_leader_new (void); + +void ck_session_leader_set_pid (CkSessionLeader *session_leader, + pid_t pid); +void ck_session_leader_set_uid (CkSessionLeader *session_leader, + uid_t uid); +void ck_session_leader_set_session_id (CkSessionLeader *session_leader, + const char *session_id); +void ck_session_leader_set_cookie (CkSessionLeader *session_leader, + const char *cookie); +void ck_session_leader_set_service_name (CkSessionLeader *session_leader, + const char *sender); + +const char * ck_session_leader_peek_session_id (CkSessionLeader *session_leader); +const char * ck_session_leader_peek_cookie (CkSessionLeader *session_leader); +const char * ck_session_leader_peek_service_name (CkSessionLeader *session_leader); +uid_t ck_session_leader_get_uid (CkSessionLeader *session_leader); +pid_t ck_session_leader_get_pid (CkSessionLeader *session_leader); + + +gboolean ck_session_leader_collect_parameters (CkSessionLeader *session_leader, + DBusGMethodInvocation *context, + CkSessionLeaderDoneFunc done_cb, + gpointer data); +void ck_session_leader_cancel (CkSessionLeader *session_leader); + +void ck_session_leader_dump (CkSessionLeader *session_leader, + GKeyFile *key_file); + + +G_END_DECLS + +#endif /* __CK_SESSION_LEADER_H */ diff --git a/src/ck-session.c b/src/ck-session.c index f136333..d9e32be 100644 --- a/src/ck-session.c +++ b/src/ck-session.c @@ -40,12 +40,15 @@ #include "ck-session.h" #include "ck-session-glue.h" #include "ck-marshal.h" +#include "ck-run-programs.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 NONULL_STRING(x) ((x) != NULL ? (x) : "") + #define IDLE_TIME_SECS 60 struct CkSessionPrivate @@ -394,6 +397,7 @@ ck_session_set_active (CkSession *session, if (session->priv->active != active) { session->priv->active = active; + ck_session_run_programs (session, "session_active_changed"); g_signal_emit (session, signals [ACTIVE_CHANGED], 0, active); } @@ -1206,3 +1210,106 @@ ck_session_new_with_parameters (const char *ssid, return CK_SESSION (object); } + +void +ck_session_run_programs (CkSession *session, + const char *action) +{ + int n; + char *extra_env[11]; /* be sure to adjust this as needed */ + + n = 0; + + extra_env[n++] = g_strdup_printf ("CK_SESSION_ID=%s", session->priv->id); + if (session->priv->session_type != NULL) { + extra_env[n++] = g_strdup_printf ("CK_SESSION_TYPE=%s", session->priv->session_type); + } + extra_env[n++] = g_strdup_printf ("CK_SESSION_SEAT_ID=%s", session->priv->seat_id); + extra_env[n++] = g_strdup_printf ("CK_SESSION_USER_UID=%d", session->priv->uid); + if (session->priv->display_device != NULL && strlen (session->priv->display_device) > 0) { + extra_env[n++] = g_strdup_printf ("CK_SESSION_DISPLAY_DEVICE=%s", session->priv->display_device); + } + if (session->priv->x11_display_device != NULL && strlen (session->priv->x11_display_device) > 0) { + extra_env[n++] = g_strdup_printf ("CK_SESSION_X11_DISPLAY_DEVICE=%s", session->priv->x11_display_device); + } + extra_env[n++] = g_strdup_printf ("CK_SESSION_X11_DISPLAY=%s", + session->priv->x11_display ? session->priv->x11_display : ""); + if (session->priv->remote_host_name != NULL && strlen (session->priv->remote_host_name) > 0) { + extra_env[n++] = g_strdup_printf ("CK_SESSION_REMOTE_HOST_NAME=%s", session->priv->remote_host_name); + } + extra_env[n++] = g_strdup_printf ("CK_SESSION_IS_ACTIVE=%s", session->priv->active ? "true" : "false"); + extra_env[n++] = g_strdup_printf ("CK_SESSION_IS_LOCAL=%s", session->priv->is_local ? "true" : "false"); + extra_env[n++] = NULL; + + ck_run_programs (SYSCONFDIR "/ConsoleKit/run-session.d", action, extra_env); + ck_run_programs (LIBDIR "/ConsoleKit/run-session.d", action, extra_env); + + for (n = 0; extra_env[n] != NULL; n++) { + g_free (extra_env[n]); + } +} + +void +ck_session_dump (CkSession *session, + GKeyFile *key_file) +{ + char *s; + char *group_name; + + group_name = g_strdup_printf ("Session %s", session->priv->id); + g_key_file_set_integer (key_file, group_name, "uid", session->priv->uid); + g_key_file_set_string (key_file, + group_name, + "seat", + NONULL_STRING (session->priv->seat_id)); + g_key_file_set_string (key_file, + group_name, + "cookie", + NONULL_STRING (session->priv->cookie)); + if (session->priv->session_type != NULL) { + g_key_file_set_string (key_file, + group_name, + "type", + NONULL_STRING (session->priv->session_type)); + } + if (session->priv->display_device != NULL && strlen (session->priv->display_device) > 0) { + g_key_file_set_string (key_file, + group_name, + "display_device", + NONULL_STRING (session->priv->display_device)); + } + if (session->priv->x11_display_device != NULL && strlen (session->priv->x11_display_device) > 0) { + g_key_file_set_string (key_file, + group_name, + "x11_display_device", + NONULL_STRING (session->priv->x11_display_device)); + } + if (session->priv->x11_display != NULL && strlen (session->priv->x11_display) > 0) { + g_key_file_set_string (key_file, + group_name, + "x11_display", + NONULL_STRING (session->priv->x11_display)); + } + if (session->priv->remote_host_name != NULL && strlen (session->priv->remote_host_name) > 0) { + g_key_file_set_string (key_file, + group_name, + "remote_host_name", + NONULL_STRING (session->priv->remote_host_name)); + } + g_key_file_set_string (key_file, + group_name, + "remote_host_name", + NONULL_STRING (session->priv->remote_host_name)); + g_key_file_set_boolean (key_file, group_name, "is_active", session->priv->active); + g_key_file_set_boolean (key_file, group_name, "is_local", session->priv->is_local); + + s = g_time_val_to_iso8601 (&(session->priv->creation_time)); + g_key_file_set_string (key_file, + group_name, + "creation_time", + NONULL_STRING (s)); + g_free (s); + + g_free (group_name); +} + diff --git a/src/ck-session.h b/src/ck-session.h index fea3a71..8c5c00e 100644 --- a/src/ck-session.h +++ b/src/ck-session.h @@ -74,6 +74,9 @@ CkSession * ck_session_new_with_parameters (const char *ss const char *cookie, const GPtrArray *parameters); +void ck_session_dump (CkSession *session, + GKeyFile *key_file); + gboolean ck_session_set_active (CkSession *session, gboolean active, GError **error); diff --git a/src/ck-sysdeps-solaris.c b/src/ck-sysdeps-solaris.c index 9cfec2e..87bf4fd 100644 --- a/src/ck-sysdeps-solaris.c +++ b/src/ck-sysdeps-solaris.c @@ -30,6 +30,10 @@ #include <sys/stat.h> #include <sys/ioctl.h> +#ifdef HAVE_SYS_VT_H +#include <sys/vt.h> +#endif + #define DEV_ENCODE(M,m) ( \ ( (M&0xfff) << 8) | ( (m&0xfff00) << 12) | (m&0xff) \ ) @@ -173,16 +177,30 @@ stat2proc (pid_t pid, snprintf (P->tty_text, sizeof P->tty_text, "%3d,%-3d", tty_maj, tty_min); + if (tty_maj == 15) { + snprintf (P->tty_text, sizeof P->tty_text, "/dev/vt/%u", tty_min); + } if (tty_maj == 24) { - snprintf (P->tty_text, sizeof P->tty_text, "pts/%-3u", tty_min); + snprintf (P->tty_text, sizeof P->tty_text, "/dev/pts/%u", tty_min); } if (P->tty == NO_TTY_VALUE) { +#ifdef HAVE_SYS_VT_H memcpy (P->tty_text, " ? ", 8); +#else + /* + * This is a bit of a hack. On Solaris, pre-VT integration, the + * Xorg process is not assigned a TTY. So, just assign the value + * to "/dev/console" if running without VT support. This will + * allow people using Solaris pre-VT integration to use + * ConsoleKit. + */ + memcpy (P->tty_text, "/dev/console", 12); +#endif } if (P->tty == DEV_ENCODE(0,0)) { - memcpy (P->tty_text, "console", 8); + memcpy (P->tty_text, "/dev/console", 12); } if (P->pid != pid) { @@ -256,7 +274,7 @@ ck_unix_pid_get_env_hash (pid_t pid) if (skip_prefix != NULL) { char **vals; - vals = g_strsplit (buf, "=", 2); + vals = g_strsplit (skip_prefix + 1, "=", 2); if (vals != NULL) { g_hash_table_insert (hash, g_strdup (vals[0]), @@ -360,7 +378,7 @@ ck_get_max_num_consoles (guint *num) error = NULL; svcprop_stdout = NULL; status = 0; - res = g_spawn_command_line_sync ("/usr/bin/svcprop -p options/vtnodecount vtdaemon", + res = g_spawn_command_line_sync ("/usr/bin/svcprop -p options/nodecount vtdaemon", &svcprop_stdout, NULL, &status, @@ -395,7 +413,10 @@ ck_get_console_device_for_num (guint num) { char *device; - device = g_strdup_printf ("/dev/vt/%u", num); + if (num == 1) + device = g_strdup_printf ("/dev/console", num); + else + device = g_strdup_printf ("/dev/vt/%u", num); return device; } @@ -414,7 +435,9 @@ ck_get_console_num_from_device (const char *device, return FALSE; } - if (sscanf (device, "/dev/vt/%u", &n) == 1) { + if (strcmp (device, "/dev/console") == 0) { + *num = 1; + } else if (sscanf (device, "/dev/vt/%u", &n) == 1) { ret = TRUE; } @@ -432,6 +455,8 @@ ck_get_active_console_num (int console_fd, gboolean ret; int res; guint active; + +#ifdef VT_GETSTATE struct vt_stat stat; g_assert (console_fd != -1); @@ -464,6 +489,14 @@ ck_get_active_console_num (int console_fd, if (num != NULL) { *num = active; } +#else + /* + * If not using VT, not really an active number, but return 1, + * which maps to "/dev/console". + */ + ret = TRUE; + *num = 1; +#endif return ret; } diff --git a/src/ck-sysdeps-unix.c b/src/ck-sysdeps-unix.c index 0001b6b..e4ab16b 100644 --- a/src/ck-sysdeps-unix.c +++ b/src/ck-sysdeps-unix.c @@ -33,10 +33,13 @@ #ifdef __linux__ #include <linux/kd.h> +#endif + +#ifdef HAVE_SYS_VT_H #include <sys/vt.h> #endif -#ifdef __FreeBSD__ +#if HAVE_SYS_CONSIO_H #include <sys/consio.h> #endif @@ -124,15 +127,15 @@ gboolean ck_fd_is_a_console (int fd) { #ifdef __linux__ - char arg = 0; + struct vt_stat vts; #elif defined(__FreeBSD__) int vers; #endif int kb_ok; + errno = 0; #ifdef __linux__ - kb_ok = (ioctl (fd, KDGKBTYPE, &arg) == 0 - && ((arg == KB_101) || (arg == KB_84))); + kb_ok = (ioctl (fd, VT_GETSTATE, &vts) == 0); #elif defined(__FreeBSD__) kb_ok = (ioctl (fd, CONS_GETVERS, &vers) == 0); #else @@ -193,6 +196,11 @@ ck_get_a_console_fd (void) goto done; } + fd = open_a_console ("/dev/tty0"); + if (fd >= 0) { + goto done; + } + #ifdef _PATH_CONSOLE fd = open_a_console (_PATH_CONSOLE); if (fd >= 0) { @@ -252,11 +260,15 @@ ck_wait_for_active_console_num (int console_fd, again: ret = FALSE; - g_debug ("VT_WAITACTIVE for vt %d", num); errno = 0; +#ifdef VT_WAITACTIVE + g_debug ("VT_WAITACTIVE for vt %d", num); res = ioctl (console_fd, VT_WAITACTIVE, num); - - g_debug ("VT_WAITACTIVE for vt %d returned %d", num, ret); + g_debug ("VT_WAITACTIVE for vt %d returned %d", num, res); +#else + res = ERROR; + errno = ENOTSUP; +#endif if (res == ERROR) { const char *errmsg; @@ -268,6 +280,8 @@ ck_wait_for_active_console_num (int console_fd, num, errmsg); goto again; + } else if (errno == ENOTSUP) { + g_debug ("Console activation not supported on this system"); } else { g_warning ("Error waiting for native console %d activation: %s", num, @@ -294,11 +308,21 @@ ck_activate_console_num (int console_fd, ret = FALSE; errno = 0; +#ifdef VT_ACTIVATE res = ioctl (console_fd, VT_ACTIVATE, num); +#else + res = ERROR; + errno = ENOTSUP; +#endif + if (res == 0) { ret = TRUE; } else { - g_warning ("Unable to activate console: %s", g_strerror (errno)); + if (errno == ENOTSUP) { + g_debug ("Console activation not supported on this system"); + } else { + g_warning ("Unable to activate console: %s", g_strerror (errno)); + } } return ret; diff --git a/src/ck-vt-monitor.c b/src/ck-vt-monitor.c index 59d3d1c..d21026b 100644 --- a/src/ck-vt-monitor.c +++ b/src/ck-vt-monitor.c @@ -40,6 +40,12 @@ #include "ck-sysdeps.h" #include "ck-marshal.h" +#if defined (__sun) && defined (HAVE_SYS_VT_H) +#include <sys/vt.h> +#include <signal.h> +#include <stropts.h> +#endif + #ifndef ERROR #define ERROR -1 #endif @@ -157,6 +163,44 @@ ck_vt_monitor_get_active (CkVtMonitor *vt_monitor, return TRUE; } +#if defined (__sun) && defined (HAVE_SYS_VT_H) +static void +handle_vt_active (void) +{ + struct vt_stat state; + guint num; + CkVtMonitor *vt_monitor = CK_VT_MONITOR (vt_object); + + g_return_if_fail (CK_IS_VT_MONITOR (vt_monitor)); + + /* + * state.v_active value: [1 .. N] + * + * VT device file VT # + * + * /dev/console --- VT #1 + * /dev/vt/2 --- VT #2 + * /dev/vt/3 --- VT #3 + * /dev/vt/N --- VT #4 + */ + if (ioctl (vt_monitor->priv->vfd, VT_GETSTATE, &state) != -1) { + num = state.v_active; + } else { + g_debug ("Fails to ioctl VT_GETSTATE"); + } + + if (vt_monitor->priv->active_num != num) { + g_debug ("Changing active VT: %d", num); + + vt_monitor->priv->active_num = num; + + g_signal_emit (vt_monitor, signals [ACTIVE_CHANGED], 0, num); + } else { + g_debug ("VT activated but already active: %d", num); + } +} +#endif + static void change_active_num (CkVtMonitor *vt_monitor, guint num) @@ -338,6 +382,22 @@ vt_add_watches (CkVtMonitor *vt_monitor) int i; gint32 current_num; +#if defined (__sun) && !defined (HAVE_SYS_VT_H) + /* Best to do nothing if VT is not supported */ +#elif defined (__sun) && defined (HAVE_SYS_VT_H) + /* + * Solaris supports synchronous event notification in STREAMS. + * Applications that open the virtual console device can + * get a asynchronous notification of VT switching by setting + * the S_MSG flag in an I_SETSIG STREAMS ioctl. Such processes + * receive a SIGPOLL signal when a VT switching succeeds. + */ + struct sigaction act; + act.sa_handler = handle_vt_active; + sigaction (SIGPOLL, &act, NULL); + + ioctl (vt_monitor->priv->vfd, I_SETSIG, S_MSG); +#else G_LOCK (hash_lock); current_num = vt_monitor->priv->active_num; @@ -365,6 +425,7 @@ vt_add_watches (CkVtMonitor *vt_monitor) } G_UNLOCK (hash_lock); +#endif } static void @@ -252,6 +252,42 @@ setup_debug_log (gboolean debug) setup_debug_log_signals (); } +static void +create_pid_file (void) +{ + char *dirname; + int res; + int pf; + ssize_t written; + char pid[9]; + + /* remove old pid file */ + unlink (CONSOLE_KIT_PID_FILE); + + dirname = g_path_get_dirname (CONSOLE_KIT_PID_FILE); + errno = 0; + res = g_mkdir_with_parents (dirname, + S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); + if (res < 0) { + g_warning ("Unable to create directory %s (%s)", + dirname, + g_strerror (errno)); + } + g_free (dirname); + + /* 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); + } else { + g_warning ("Unable to write pid file %s: %s", + CONSOLE_KIT_PID_FILE, + g_strerror (errno)); + } +} + int main (int argc, char **argv) @@ -261,11 +297,9 @@ main (int argc, GOptionContext *context; DBusGProxy *bus_proxy; DBusGConnection *connection; + GError *error; int ret; - int pf; - ssize_t written; - char pid[9]; - + gboolean res; static gboolean debug = FALSE; static gboolean no_daemon = FALSE; static gboolean do_timed_exit = FALSE; @@ -289,10 +323,21 @@ main (int argc, exit (1); } + if (debug) { + g_setenv ("G_DEBUG", "fatal_criticals", FALSE); + g_log_set_always_fatal (G_LOG_LEVEL_CRITICAL); + } + 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); + error = NULL; + res = g_option_context_parse (context, &argc, &argv, &error); g_option_context_free (context); + if (! res) { + g_warning (error->message); + g_error_free (error); + goto out; + } if (! no_daemon && daemon (0, 0)) { g_error ("Could not daemonize: %s", g_strerror (errno)); @@ -318,16 +363,7 @@ main (int argc, 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); - } + create_pid_file (); manager = ck_manager_new (); diff --git a/src/test-event-logger.c b/src/test-event-logger.c new file mode 100644 index 0000000..e3d7db0 --- /dev/null +++ b/src/test-event-logger.c @@ -0,0 +1,94 @@ +/* -*- 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 <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <fcntl.h> +#include <pwd.h> +#include <string.h> +#include <errno.h> + +#include <locale.h> + +#include <glib.h> + +#include "ck-event-logger.h" + +static gboolean +write_to_log (CkEventLogger *logger) +{ + CkLogEvent event; + GError *error; + gboolean res; + + memset (&event, 0, sizeof (CkLogEvent)); + + event.type = CK_LOG_EVENT_SEAT_SESSION_ADDED; + g_get_current_time (&event.timestamp); + + event.event.seat_session_added.session_id = "Session1"; + + error = NULL; + res = ck_event_logger_queue_event (logger, &event, &error); + if (! res) { + g_warning ("Unable to queue event: %s", error->message); + g_error_free (error); + } + + return TRUE; +} + +int +main (int argc, char **argv) +{ + GMainLoop *loop; + CkEventLogger *logger; + char *filename; + + if (! g_thread_supported ()) { + g_thread_init (NULL); + } + g_type_init (); + + filename = g_build_filename (g_get_tmp_dir (), "ck-logger-test.log", NULL); + + g_message ("Testing the event logger.\n Should write messages to: %s", filename); + logger = ck_event_logger_new (filename); + g_free (filename); + + g_timeout_add (1000, (GSourceFunc)write_to_log, logger); + + loop = g_main_loop_new (NULL, FALSE); + + g_main_loop_run (loop); + + g_object_unref (logger); + + g_main_loop_unref (loop); + + return 0; +} diff --git a/src/test-open-session-with-parameters b/src/test-open-session-with-parameters index e9e3358..ce622ce 100755 --- a/src/test-open-session-with-parameters +++ b/src/test-open-session-with-parameters @@ -15,7 +15,7 @@ manager_obj = bus.get_object ('org.freedesktop.ConsoleKit', '/org/freedesktop/Co manager = dbus.Interface (manager_obj, 'org.freedesktop.ConsoleKit.Manager') params = dbus.Array ([], signature = "sv") -params.append (("user", dbus.Variant (730))) +params.append (("unix-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"))) |