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