aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore5
-rw-r--r--README.md2
-rw-r--r--debian/README.Debian5
-rw-r--r--debian/changelog5
-rw-r--r--debian/compat1
-rw-r--r--debian/control17
-rw-r--r--debian/copyright29
-rw-r--r--debian/do-not-install1
-rw-r--r--debian/logout-manager.conffiles2
-rw-r--r--debian/logout-manager.dsc14
-rw-r--r--debian/logout-manager.install0
-rw-r--r--debian/logout-manager.lintian-overrides4
-rw-r--r--debian/logout-manager.postinst9
-rw-r--r--debian/logout-manager.prerm11
-rw-r--r--debian/patches/series1
-rwxr-xr-xdebian/rules18
-rw-r--r--debian/source/format1
-rw-r--r--debian/source/lintian-overrides2
-rw-r--r--debian/source/local-options2
-rw-r--r--debian/watch2
-rw-r--r--src/Makefile94
-rw-r--r--src/etc/logout-manager.conf16
-rw-r--r--src/etc/sudoers.d/30_logout-manager_sudo3
-rw-r--r--src/etc/sysconfig/logout-manager2
-rwxr-xr-xsrc/usr/bin/logout-manager-cli.py69
-rwxr-xr-xsrc/usr/bin/logout-manager-gtk.py (renamed from logout-manager-gtk.py)23
-rwxr-xr-xsrc/usr/bin/logout-manager-ncurses.py193
-rwxr-xr-xsrc/usr/bin/logout-manager-tcl.py (renamed from logout-manager-tcl.py)29
-rwxr-xr-xsrc/usr/libexec/logout-manager/lm-helper74
-rw-r--r--src/usr/share/applications/logout-manager.desktop12
-rw-r--r--src/usr/share/bash-completion/completions/logout-manager12
-rw-r--r--src/usr/share/doc/logout-manager/README.md40
-rw-r--r--src/usr/share/doc/logout-manager/logout-manager.conf.example (renamed from logout-manager.conf)0
-rw-r--r--src/usr/share/logout-manager/lmlib.py (renamed from lmlib.py)27
34 files changed, 706 insertions, 19 deletions
diff --git a/.gitignore b/.gitignore
index 8707833..371e499 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,8 @@
*.2019-*
*.swp
__pycache__
+*debhelper*
+debian/logout-manager/
+debian/files
+*.substvars
+gitmessage
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..aa626bb
--- /dev/null
+++ b/README.md
@@ -0,0 +1,2 @@
+# Overview for logout-manager
+See the [full readme](src/usr/share/doc/logout-manager/README.md) farther down in the source tree.
diff --git a/debian/README.Debian b/debian/README.Debian
new file mode 100644
index 0000000..810fad6
--- /dev/null
+++ b/debian/README.Debian
@@ -0,0 +1,5 @@
+logout-manager for Devuan
+
+No changes
+
+ -- Ben Stack <bgstack15@gmail.com> Wed, 11 Mar 2020 08:38:11 -0400
diff --git a/debian/changelog b/debian/changelog
new file mode 100644
index 0000000..b206280
--- /dev/null
+++ b/debian/changelog
@@ -0,0 +1,5 @@
+logout-manager (0.0.1-1) obs; urgency=low
+
+ * Initial release.
+
+ -- Ben Stack <bgstack15@gmail.com> Wed, 11 Mar 2020 08:38:11 -0400
diff --git a/debian/compat b/debian/compat
new file mode 100644
index 0000000..48082f7
--- /dev/null
+++ b/debian/compat
@@ -0,0 +1 @@
+12
diff --git a/debian/control b/debian/control
new file mode 100644
index 0000000..f7edf05
--- /dev/null
+++ b/debian/control
@@ -0,0 +1,17 @@
+Source: logout-manager
+Section: x11
+Priority: optional
+Maintainer: Ben Stack <bgstack15@gmail.com>
+Build-Depends: debhelper (>=12~)
+Standards-Version: 4.1.4
+Homepage: https://bgstack15.wordpress.com/
+
+Package: logout-manager
+Architecture: all
+Multi-Arch: foreign
+Depends: ${misc:Depends}, ${shlibs:Depends}
+Description: provide simple menu for logout-type actions
+ Designed for minimal DEs and window managers that
+ lack a menu for logging out, this tool provides
+ such a menu.
+
diff --git a/debian/copyright b/debian/copyright
new file mode 100644
index 0000000..6b39eaf
--- /dev/null
+++ b/debian/copyright
@@ -0,0 +1,29 @@
+Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Upstream-Name: logout-manager
+Source: <url://example.com>
+#
+# Please double check copyright with the licensecheck(1) command.
+
+Files: .gitignore
+ README.md
+ src/Makefile
+ src/etc/bash_completion.d/logout-manager
+ src/etc/logout-manager.conf
+ src/etc/sudoers.d/30_logout-manager_sudo
+ src/etc/sysconfig/logout-manager
+ src/usr/bin/logout-manager-cli.py
+ src/usr/bin/logout-manager-gtk.py
+ src/usr/bin/logout-manager-ncurses.py
+ src/usr/bin/logout-manager-tcl.py
+ src/usr/libexec/logout-manager/lm-helper
+ src/usr/share/applications/logout-manager.desktop
+ src/usr/share/doc/logout-manager/README.md
+ src/usr/share/doc/logout-manager/logout-manager.conf.example
+ src/usr/share/logout-manager/__pycache__/lmlib.cpython-37.pyc
+ src/usr/share/logout-manager/lmlib.py
+Copyright: __NO_COPYRIGHT_NOR_LICENSE__
+License: __NO_COPYRIGHT_NOR_LICENSE__
+
+#----------------------------------------------------------------------------
+# Files marked as NO_LICENSE_TEXT_FOUND may be covered by the following
+# license/copyright files.
diff --git a/debian/do-not-install b/debian/do-not-install
new file mode 100644
index 0000000..e752d8b
--- /dev/null
+++ b/debian/do-not-install
@@ -0,0 +1 @@
+usr/bin/logout-manager
diff --git a/debian/logout-manager.conffiles b/debian/logout-manager.conffiles
new file mode 100644
index 0000000..65e8592
--- /dev/null
+++ b/debian/logout-manager.conffiles
@@ -0,0 +1,2 @@
+etc/logout-manager.conf
+etc/default/logout-manager
diff --git a/debian/logout-manager.dsc b/debian/logout-manager.dsc
new file mode 100644
index 0000000..63ef881
--- /dev/null
+++ b/debian/logout-manager.dsc
@@ -0,0 +1,14 @@
+Format: 3.0 (quilt)
+Source: logout-manager
+Binary: logout-manager
+Architecture: all
+Version: 0.0.1-1
+Maintainer: Ben Stack <bgstack15@gmail.com>
+Homepage: https://bgstack15.wordpress.com/
+Standards-Version: 4.1.4
+Build-Depends: debhelper (>= 12~)
+Package-List:
+ logout-manager deb x11 optional arch=all
+Files:
+ 00000000000000000000000000000000 1 logout-manager.orig.tar.gz
+ 00000000000000000000000000000000 1 logout-manager.debian.tar.xz
diff --git a/debian/logout-manager.install b/debian/logout-manager.install
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/debian/logout-manager.install
diff --git a/debian/logout-manager.lintian-overrides b/debian/logout-manager.lintian-overrides
new file mode 100644
index 0000000..01f15e1
--- /dev/null
+++ b/debian/logout-manager.lintian-overrides
@@ -0,0 +1,4 @@
+binary-without-manpage
+copyright-has-url-from-dh_make-boilerplate
+copyright-without-copyright-notice
+script-with-language-extension
diff --git a/debian/logout-manager.postinst b/debian/logout-manager.postinst
new file mode 100644
index 0000000..b98d697
--- /dev/null
+++ b/debian/logout-manager.postinst
@@ -0,0 +1,9 @@
+#!/bin/sh -e
+#DEBHELPER#
+case "$1" in
+ configure|abort-upgrade|abort-remove|abort-deconfigure)
+ update-alternatives --install /usr/bin/logout-manager logout-manager /usr/bin/logout-manager-gtk.py 80
+ update-alternatives --install /usr/bin/logout-manager logout-manager /usr/bin/logout-manager-tcl.py 70
+ update-alternatives --install /usr/bin/logout-manager logout-manager /usr/bin/logout-manager-ncurses.py 60
+ ;;
+esac
diff --git a/debian/logout-manager.prerm b/debian/logout-manager.prerm
new file mode 100644
index 0000000..c0c50d3
--- /dev/null
+++ b/debian/logout-manager.prerm
@@ -0,0 +1,11 @@
+#!/bin/sh -e
+#DEBHELPER#
+case "$1" in
+ remove|deconfigure)
+ update-alternatives --remove logout-manager /usr/bin/logout-manager-gtk.py
+ update-alternatives --remove logout-manager /usr/bin/logout-manager-tcl.py
+ update-alternatives --remove logout-manager /usr/bin/logout-manager-ncurses.py
+ ;;
+ upgrade|failed-upgrade)
+ ;;
+esac
diff --git a/debian/patches/series b/debian/patches/series
new file mode 100644
index 0000000..4a97dfa
--- /dev/null
+++ b/debian/patches/series
@@ -0,0 +1 @@
+# You must remove unused comment lines for the released package.
diff --git a/debian/rules b/debian/rules
new file mode 100755
index 0000000..c1fee5d
--- /dev/null
+++ b/debian/rules
@@ -0,0 +1,18 @@
+#!/usr/bin/make -f
+# You must remove unused comment lines for the released package.
+#export DH_VERBOSE = 1
+#export DEB_BUILD_MAINT_OPTIONS = hardening=+all
+#export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic
+#export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed
+APPNAME=logout-manager
+
+%:
+ dh $@ --sourcedirectory=src
+
+override_dh_auto_install:
+ dh_auto_install -- prefix=/usr DEFAULTDIR='$$(DESTDIR)/etc/default'
+
+override_dh_gencontrol:
+ printf "misc:Depends=" > debian/${APPNAME}.substvars
+ make -C src deplist DISTRO=devuan SEPARATOR=',' | grep -vE 'make\[[0-9]' >> debian/${APPNAME}.substvars
+ dh_gencontrol
diff --git a/debian/source/format b/debian/source/format
new file mode 100644
index 0000000..163aaf8
--- /dev/null
+++ b/debian/source/format
@@ -0,0 +1 @@
+3.0 (quilt)
diff --git a/debian/source/lintian-overrides b/debian/source/lintian-overrides
new file mode 100644
index 0000000..2da37ed
--- /dev/null
+++ b/debian/source/lintian-overrides
@@ -0,0 +1,2 @@
+file-without-copyright-information
+missing-license-paragraph-in-dep5-copyright
diff --git a/debian/source/local-options b/debian/source/local-options
new file mode 100644
index 0000000..00131ee
--- /dev/null
+++ b/debian/source/local-options
@@ -0,0 +1,2 @@
+#abort-on-upstream-changes
+#unapply-patches
diff --git a/debian/watch b/debian/watch
new file mode 100644
index 0000000..fc70498
--- /dev/null
+++ b/debian/watch
@@ -0,0 +1,2 @@
+# You must remove unused comment lines for the released package.
+version=4
diff --git a/src/Makefile b/src/Makefile
new file mode 100644
index 0000000..96607d2
--- /dev/null
+++ b/src/Makefile
@@ -0,0 +1,94 @@
+# File: Makefile for logout-manager
+# Location: logout-manager source package
+# Author: bgstack15
+# Startdate: 2020-03-10
+# Title: Makefile for logout-manager source package
+# Purpose: To use traditional Unix make utility
+# History:
+# Usage:
+# Reference:
+# https://stackoverflow.com/questions/4219255/how-do-you-get-the-list-of-targets-in-a-makefile/26339924#26339924
+# https://stackoverflow.com/questions/19105241/how-do-you-conditionally-call-a-target-based-on-a-target-variable-makefile/19107231#19107231
+# https://stackoverflow.com/questions/5917576/sort-a-text-file-by-line-length-including-spaces
+# https://superuser.com/questions/352289/bash-scripting-test-for-empty-directory/667100#667100
+# bgscripts Makefile
+# Improve:
+# Document:
+# Includes a nice way to dynamically generate dependencies as self-reported by all the files.
+# Dependencies:
+
+APPNAME = logout-manager
+APPVERSION = 0.0.1
+SRCDIR = $(CURDIR)
+prefix = /usr
+SYSCONFDIR = $(DESTDIR)/etc
+DEFAULTDIR = $(DESTDIR)/etc/sysconfig
+# for debian use '$(DESTDIR)/etc/default'
+BINDIR = $(DESTDIR)$(prefix)/bin
+SHAREDIR = $(DESTDIR)$(prefix)/share
+LIBEXECDIR = $(DESTDIR)$(prefix)/libexec
+DOCDIR = $(SHAREDIR)/doc/$(APPNAME)
+APPDIR = $(SHAREDIR)/$(APPNAME)
+APPSDIR = $(SHAREDIR)/applications
+BASHCDIR = $(SHAREDIR)/bash-completion/completions
+SUDOERSDIR = $(SYSCONFDIR)/sudoers.d
+
+awkbin :=$(shell which awk)
+cpbin :=$(shell which cp)
+echobin :=$(shell which echo)
+findbin :=$(shell which find)
+grepbin :=$(shell which grep)
+installbin :=$(shell which install)
+lnbin :=$(shell which ln)
+rmbin :=$(shell which rm)
+sedbin :=$(shell which sed)
+sortbin :=$(shell which sort)
+truebin :=$(shell which true)
+uniqbin :=$(shell which uniq)
+xargsbin :=$(shell which xargs)
+
+all:
+ ${echobin} "No compilation in this package."
+
+.PHONY: clean install uninstall list deplist deplist_opts
+
+list:
+ @$(MAKE) -pRrq -f $(lastword $(MAKEFILE_LIST)) : 2>/dev/null | ${awkbin} -v RS= -F: '/^# File/,/^# Finished Make data base/ {if ($$1 !~ "^[#.]") {print $$1}}' | ${sortbin} | ${grepbin} -E -v -e '^[^[:alnum:]]' -e '^$@$$'
+
+deplist:
+ @if test -z "$(DISTRO)" ; then ${echobin} "Please run \`make deplist\` with DISTRO= one of: `make deplist_opts 2>&1 1>/dev/null | ${xargsbin}`. Aborted." ; exit 1 ; fi
+ @${grepbin} -h --exclude='Makefile' --exclude-dir='doc' -A5 -riIE dependencies $(SRCDIR) | ${awkbin} -v 'distro=$(DISTRO)' 'tolower($$0) ~ distro {$$1="";$$2="";print}' | ${awkbin} 'BEGIN{cmd="${xargsbin} -n1"} $$0 !~ /\(/{print $$0 | cmd ; close(cmd);} $$0 ~ /\(/{print;}' | ${sortbin} | ${uniqbin} | ${sedbin} -r -e 's/$$/$(SEPARATOR)/' | ${xargsbin}
+
+deplist_opts:
+ @${echobin} "el7" 1>&2
+ @${echobin} "devuan" 1>&2
+
+install:
+ @${echobin} Installing files to ${DESTDIR}
+ ${installbin} -d ${SYSCONFDIR} ${DEFAULTDIR} ${BINDIR} \
+ ${APPSDIR} ${APPDIR} ${DOCDIR} ${BASHCDIR} ${SUDOERSDIR} \
+ ${LIBEXECDIR}/${APPNAME}
+ ${cpbin} -pr ${SRCDIR}/etc/*.* ${SYSCONFDIR}
+ ${cpbin} -pr ${SRCDIR}/etc/sysconfig/* ${DEFAULTDIR}
+ ${cpbin} -pr ${SRCDIR}/usr/bin/* ${BINDIR}
+ ${cpbin} -pr ${SRCDIR}/usr/share/applications/* ${APPSDIR}
+ ${cpbin} -pr ${SRCDIR}/usr/share/${APPNAME}/*.* ${APPDIR}
+ ${cpbin} -pr ${SRCDIR}/usr/share/doc/${APPNAME}/* ${DOCDIR}
+ ${installbin} -m 0644 -t ${BASHCDIR} ${SRCDIR}/usr/share/bash-completion/completions/*
+ ${installbin} -m 0640 -t ${SUDOERSDIR} ${SRCDIR}/etc/sudoers.d/*
+ ${installbin} -m 0755 -t ${LIBEXECDIR}/${APPNAME} ${SRCDIR}/usr/libexec/${APPNAME}/*
+ # symlink, when alternatives is not being used
+ ${lnbin} -s logout-manager-gtk.py ${BINDIR}/logout-manager
+
+uninstall:
+ @${echobin} SRCDIR=${SRCDIR}
+ ${rmbin} -f $$( ${findbin} ${SRCDIR} -mindepth 1 ! -type d -printf '%p\n' | ${sedbin} -r -e "s:^${SRCDIR}:${DESTDIR}:" ) ${DEFAULTDIR}/${APPNAME} ${BINDIR}/logout-manager
+
+ # absolute minimum directories to remove
+ #${rmbin} -rf ${APPDIR} ${SYSCONFDIR}/${APPNAME} ${DOCDIR}
+
+ # remove all installed directories that are now blank.
+ rmdir ${DEFAULTDIR} 2>/dev/null ; for word in $$( ${findbin} ${SRCDIR} -mindepth 1 -type d -printf '%p\n' | ${sedbin} -r -e "s:^${SRCDIR}:${DESTDIR}:" | ${awkbin} '{ print length, $$0 }' | sort -rn | ${awkbin} '{print $$2}' ) ; do ${findbin} $${word} -mindepth 1 1>/dev/null 2>&1 | read 1>/dev/null 2>&1 || { rmdir "$${word}" 2>/dev/null || ${truebin} ; } ; done
+
+clean:
+ -${echobin} "target $@ not implemented yet! Gotta say unh."
diff --git a/src/etc/logout-manager.conf b/src/etc/logout-manager.conf
new file mode 100644
index 0000000..51b86d1
--- /dev/null
+++ b/src/etc/logout-manager.conf
@@ -0,0 +1,16 @@
+[logout-manager]
+lock_command="/usr/libexec/logout-manager/lm-helper lock"
+logout_command="/usr/libexec/logout-manager/lm-helper logout"
+hibernate_command="sudo /usr/libexec/logout-manager/lm-helper hibernate"
+reboot_command="sudo /usr/libexec/logout-manager/lm-helper reboot"
+shutdown_command="sudo /usr/libexec/logout-manager/lm-helper shutdown"
+
+[icons]
+size = 24
+#theme = default
+# use names as used by the icon theme, or give a full path here
+hibernate = system-hibernate
+lock = system-lock-screen
+logout = system-log-out
+reboot = system-reboot
+shutdown = system-shutdown
diff --git a/src/etc/sudoers.d/30_logout-manager_sudo b/src/etc/sudoers.d/30_logout-manager_sudo
new file mode 100644
index 0000000..ba621eb
--- /dev/null
+++ b/src/etc/sudoers.d/30_logout-manager_sudo
@@ -0,0 +1,3 @@
+# File: /etc/sudoers.d/30_logout-manager_sudo
+Defaults env_keep += "DRYRUN VERBOSE"
+ALL ALL = (root) NOPASSWD: /usr/libexec/logout-manager/lm-helper *
diff --git a/src/etc/sysconfig/logout-manager b/src/etc/sysconfig/logout-manager
new file mode 100644
index 0000000..1f4ecf6
--- /dev/null
+++ b/src/etc/sysconfig/logout-manager
@@ -0,0 +1,2 @@
+LOGOUT_MANAGER_LIBPATH=/usr/share/logout-manager
+LOGOUT_MANAGER_CONF=/etc/logout-manager.conf
diff --git a/src/usr/bin/logout-manager-cli.py b/src/usr/bin/logout-manager-cli.py
new file mode 100755
index 0000000..64ea133
--- /dev/null
+++ b/src/usr/bin/logout-manager-cli.py
@@ -0,0 +1,69 @@
+#!/usr/bin/env python3
+# File: logout-manager-cli.py
+# License: CC-BY-SA 4.0
+# Author: bgstack15
+# Startdate: 2020-03-10 18:40
+# Title: cli logout manager
+# Purpose: Feature completeness in this package
+# History:
+# Usage:
+# logout-manager-cli.py
+# Reference:
+# https://stackoverflow.com/questions/39092149/argparse-how-to-make-mutually-exclusive-arguments-optional/39092229#39092229
+# https://stackoverflow.com/questions/3061/calling-a-function-of-a-module-by-using-its-name-a-string/12025554#12025554
+# Improve:
+# Dependencies:
+# Devuan: python3-dotenv python3
+# Documentation:
+
+import os, platform, sys, argparse
+from dotenv import load_dotenv
+
+# all this to load the libpath
+try:
+ defaultdir="/etc/sysconfig"
+ thisplatform = platform.platform().lower()
+ if 'debian' in thisplatform or 'devuan' in thisplatform:
+ defaultdir="/etc/default"
+ # load_dotenv keeps existing environment variables as higher precedent
+ load_dotenv(os.path.join(defaultdir,"logout-manager"))
+except:
+ pass
+if 'LOGOUT_MANAGER_LIBPATH' in os.environ:
+ for i in os.environ['LOGOUT_MANAGER_LIBPATH'].split(":"):
+ sys.path.append(i)
+import lmlib
+
+def eprint(*args, **kwargs):
+ print(*args, file=sys.stderr, **kwargs)
+
+logout_manager_cli_version="2020-03-10"
+
+parser = argparse.ArgumentParser(description="run logout-manager commands using cli")
+parser.add_argument('action', help='which action to take',nargs='?', choices=('lock','logout','hibernate','shutdown','reboot'))
+parser.add_argument("-d","--debug", nargs='?', default=0, type=int, choices=range(0,11), help="Set debug level.")
+parser.add_argument("-n","--dryrun", action='store_true', help="only report. Useful for checking if hibernate is allowed.")
+parser.add_argument("-V","--version", action="version", version="%(prog)s " + logout_manager_cli_version)
+
+args = parser.parse_args()
+
+# load configs
+# in cli, must happen after arparse to benefit from debug value
+config = lmlib.Initialize_config(os.environ['LOGOUT_MANAGER_CONF'])
+actions = lmlib.Actions
+
+# MAIN LOOP
+allowed_actions=['lock','logout','shutdown','reboot']
+if config.can_hibernate:
+ allowed_actions.append('hibernate')
+
+if args.action in allowed_actions:
+ func = getattr(globals()['actions'],args.action)
+ func(config)
+elif args.action:
+ eprint("Unable to take action: %s" % str(args.action))
+ sys.exit(1)
+
+# if we get here, no action was used
+parser.print_help()
+sys.exit(2)
diff --git a/logout-manager-gtk.py b/src/usr/bin/logout-manager-gtk.py
index c6f7a6b..553fc41 100755
--- a/logout-manager-gtk.py
+++ b/src/usr/bin/logout-manager-gtk.py
@@ -24,15 +24,31 @@
# only show debug info when DEBUG=1 or similar.
# support global conf file, and user conf file
# far future: provide graphical way to change commands run
+# Dependencies:
+# Devuan: python3-dotenv
# Documentation:
-import gi, sys
+import gi, os, platform, sys
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
from gi.repository import Gdk
from gi.repository.GdkPixbuf import Pixbuf
from pathlib import Path
-sys.path.append("/home/bgirton/dev/logout-manager")
+from dotenv import load_dotenv
+
+# all this to load the libpath
+try:
+ defaultdir="/etc/sysconfig"
+ thisplatform = platform.platform().lower()
+ if 'debian' in thisplatform or 'devuan' in thisplatform:
+ defaultdir="/etc/default"
+ # load_dotenv keeps existing environment variables as higher precedent
+ load_dotenv(os.path.join(defaultdir,"logout-manager"))
+except:
+ pass
+if 'LOGOUT_MANAGER_LIBPATH' in os.environ:
+ for i in os.environ['LOGOUT_MANAGER_LIBPATH'].split(":"):
+ sys.path.append(i)
import lmlib
# graphical classes and functions
@@ -225,7 +241,8 @@ class MainWindow(Gtk.Window):
print("Cancel any logout action.")
Gtk.main_quit()
-config = lmlib.Initialize_config()
+# load configs
+config = lmlib.Initialize_config(os.environ['LOGOUT_MANAGER_CONF'])
actions = lmlib.Actions
# MAIN LOOP
diff --git a/src/usr/bin/logout-manager-ncurses.py b/src/usr/bin/logout-manager-ncurses.py
new file mode 100755
index 0000000..1500d85
--- /dev/null
+++ b/src/usr/bin/logout-manager-ncurses.py
@@ -0,0 +1,193 @@
+#!/usr/bin/env python3
+# File: logout-manager-ncurses.py
+# License: MIT
+# Author: adamlamers, bgstack15
+# Startdate: 2020-03-09 17:06
+# Title: ncurses based logout manager
+# Usage:
+# logout-manager-ncurses.py
+# Reference:
+# https://docs.python.org/3/howto/curses.html
+# ripped straight from http://adamlamers.com/post/FTPD9KNRA8CT
+# https://stackoverflow.com/questions/3061/calling-a-function-of-a-module-by-using-its-name-a-string/12025554#12025554
+# https://robinislam.me/blog/reading-environment-variables-in-python/
+# Improve:
+# Dependencies:
+# Devuan: python3-dotenv
+# Documentation:
+# Improvements for CursesMenu class over origin:
+# accepts number key inputs
+# accepts enabled attribute
+# add "zeroindex" bool
+
+import curses, os, platform, sys
+from dotenv import load_dotenv
+
+# all this to load the libpath
+try:
+ defaultdir="/etc/sysconfig"
+ thisplatform = platform.platform().lower()
+ if 'debian' in thisplatform or 'devuan' in thisplatform:
+ defaultdir="/etc/default"
+ # load_dotenv keeps existing environment variables as higher precedent
+ load_dotenv(os.path.join(defaultdir,"logout-manager"))
+except:
+ pass
+if 'LOGOUT_MANAGER_LIBPATH' in os.environ:
+ for i in os.environ['LOGOUT_MANAGER_LIBPATH'].split(":"):
+ sys.path.append(i)
+import lmlib
+
+class CursesMenu(object):
+
+ INIT = {'type' : 'init'}
+
+ def __init__(self, menu_options):
+ self.screen = curses.initscr()
+ self.menu_options = menu_options
+ self.selected_option = 0
+ self._previously_selected_option = None
+ self.running = True
+ self._zero_offset = 1
+ try:
+ self._zero_offset = 0 if bool(self.menu_options['zeroindex']) else 1
+ except:
+ pass
+
+ #init curses and curses input
+ curses.noecho()
+ curses.cbreak()
+ curses.start_color()
+ curses.curs_set(0) #Hide cursor
+ self.screen.keypad(1)
+
+ #set up color pair for highlighted option
+ curses.init_pair(1, curses.COLOR_BLACK, curses.COLOR_WHITE)
+ self.hilite_color = curses.color_pair(1)
+ self.normal_color = curses.A_NORMAL
+
+ def prompt_selection(self, parent=None):
+ if parent is None:
+ lastoption = "Cancel"
+ else:
+ lastoption = "Return to previous menu ({})".format(parent['title'])
+
+ option_count = len(self.menu_options['options'])
+
+ input_key = None
+
+ ENTER_KEY = ord('\n')
+ NUM_KEYS = [ord(str(i)) for i in range(self._zero_offset,option_count+1+self._zero_offset)]
+ done = False
+ while not done:
+ if self.selected_option != self._previously_selected_option:
+ self._previously_selected_option = self.selected_option
+
+ self.screen.border(0)
+ self._draw_title()
+ for option in range(option_count):
+ if self.selected_option == option:
+ self._draw_option(option, self.hilite_color)
+ else:
+ self._draw_option(option, self.normal_color)
+
+ if self.selected_option == option_count:
+ self.screen.addstr(4 + option_count, 4, "{:2} - {}".format(option_count+self._zero_offset,
+ lastoption), self.hilite_color)
+ else:
+ self.screen.addstr(4 + option_count, 4, "{:2} - {}".format(option_count+self._zero_offset,
+ lastoption), self.normal_color)
+
+ max_y, max_x = self.screen.getmaxyx()
+ if input_key is not None:
+ self.screen.addstr(max_y-3, max_x - 5, "{:3}".format(self.selected_option+self._zero_offset))
+ self.screen.refresh()
+
+
+ input_key = self.screen.getch()
+ down_keys = [curses.KEY_DOWN, ord('j')]
+ up_keys = [curses.KEY_UP, ord('k')]
+ exit_keys = [ord('q')]
+
+ if input_key in down_keys:
+ if self.selected_option < option_count:
+ self.selected_option += 1
+ else:
+ self.selected_option = 0
+
+ if input_key in up_keys:
+ if self.selected_option > 0:
+ self.selected_option -= 1
+ else:
+ self.selected_option = option_count
+
+ if input_key in exit_keys:
+ self.selected_option = option_count #auto select exit and return
+ break
+
+ if input_key == ENTER_KEY or input_key in NUM_KEYS:
+ if input_key in NUM_KEYS:
+ self.selected_option=int(chr(input_key))-self._zero_offset
+ done = True
+ try:
+ done = self.menu_options['options'][self.selected_option]['enabled']
+ except:
+ pass
+ return self.selected_option
+
+ def _draw_option(self, option_number, style):
+ thistext = self.menu_options['options'][option_number]['title']
+ try:
+ if self.menu_options['options'][option_number]['enabled'] == False: thistext += " (disabled)"
+ except:
+ pass
+ self.screen.addstr(4 + option_number,
+ 4,
+ "{:2} - {}".format(option_number+self._zero_offset, thistext),
+ style)
+
+ def _draw_title(self):
+ self.screen.addstr(2, 2, self.menu_options['title'], curses.A_STANDOUT)
+ self.screen.addstr(3, 2, self.menu_options['subtitle'], curses.A_BOLD)
+
+ def display(self):
+ selected_option = self.prompt_selection()
+ i, _ = self.screen.getmaxyx()
+ curses.endwin()
+ #os.system('clear')
+ if selected_option < len(self.menu_options['options']):
+ selected_opt = self.menu_options['options'][selected_option]
+ return selected_opt
+ else:
+ self.running = False
+ return {'title' : 'Cancel', 'type' : 'exitmenu'}
+
+# load configs
+config = lmlib.Initialize_config(os.environ['LOGOUT_MANAGER_CONF'])
+actions = lmlib.Actions
+
+# MAIN LOOP
+menu = {
+ 'title' : 'Logout Manager',
+ 'type' : 'menu',
+ 'subtitle' : 'Use arrows or number keys',
+ 'zeroindex' : False,
+ 'options' : [
+ {'title': 'Lock', 'type': 'action', 'action': 'lock'},
+ {'title': 'Logout', 'type': 'action', 'action': 'logout'},
+ {'title': 'Hibernate', 'type': 'action', 'action': 'hibernate', 'enabled': config.can_hibernate},
+ {'title': 'Shutdown', 'type': 'action', 'action': 'shutdown'},
+ {'title': 'Reboot', 'type': 'action', 'action': 'reboot'}
+ ]
+}
+m = CursesMenu(menu)
+selected_action = m.display()
+
+if selected_action['type'] == 'exitmenu':
+ print("Cancel any logout action.")
+elif selected_action['type'] == 'command':
+ os.system(selected_action['command'])
+elif selected_action['type'] == 'action':
+ #a = selected_action['action']:
+ func = getattr(globals()['actions'],selected_action['action'])
+ func(config)
diff --git a/logout-manager-tcl.py b/src/usr/bin/logout-manager-tcl.py
index 8c4bc6a..127bd54 100755
--- a/logout-manager-tcl.py
+++ b/src/usr/bin/logout-manager-tcl.py
@@ -7,6 +7,7 @@
# Purpose: A tcl/tk graphical program for selecting shutdown, logout, etc.
# History:
# Usage:
+# logout-manager-tcl.py
# References:
# http://effbot.org/tkinterbook/button.htm
# http://effbot.org/tkinterbook/tkinter-application-windows.htm
@@ -22,18 +23,17 @@
# tooltips https://stackoverflow.com/questions/3221956/how-do-i-display-tooltips-in-tkinter/41381685#41381685
# Improve:
# Dependencies:
-# devuan: python3-tk python3-pil.imagetk python3-cairosvg
+# Devuan: python3-tk python3-pil.imagetk python3-cairosvg
# el7: python36-tkinter python36-pillow-tk ( pip3 install cairosvg )
-import glob, re
+import glob, os, platform, re, sys
import tkinter as tk
from functools import partial
from pathlib import Path
from sys import path
+from dotenv import load_dotenv
# loading PIL.ImageTk after tkinter makes ImageTk use the PIL version, which supports PNG. This is important on tcl < 8.6 (that is, el7)
from PIL import Image, ImageTk
-path.append("/home/bgirton/dev/logout-manager")
-import lmlib
LM_USE_SVG = 0
try:
@@ -43,8 +43,20 @@ except:
print("WARNING: Unable to import cairosvg. No svg images will be displayed.")
LM_USE_SVG = 0
-config = lmlib.Initialize_config()
-actions = lmlib.Actions
+# all this to load the libpath
+try:
+ defaultdir="/etc/sysconfig"
+ thisplatform = platform.platform().lower()
+ if 'debian' in thisplatform or 'devuan' in thisplatform:
+ defaultdir="/etc/default"
+ # load_dotenv keeps existing environment variables as higher precedent
+ load_dotenv(os.path.join(defaultdir,"logout-manager"))
+except:
+ pass
+if 'LOGOUT_MANAGER_LIBPATH' in os.environ:
+ for i in os.environ['LOGOUT_MANAGER_LIBPATH'].split(":"):
+ sys.path.append(i)
+import lmlib
# graphical classes and functions
print("Loading graphics...")
@@ -388,9 +400,12 @@ class App:
#def something(event=None):
# print("Got here!")
-root = tk.Tk()
+# load configs
+config = lmlib.Initialize_config(os.environ['LOGOUT_MANAGER_CONF'])
+actions = lmlib.Actions
# MAIN LOOP
+root = tk.Tk()
root.title("Log out options")
imgicon = get_scaled_icon(config.get_logout_icon(),24,config.get_icon_theme())
root.tk.call('wm','iconphoto', root._w, imgicon)
diff --git a/src/usr/libexec/logout-manager/lm-helper b/src/usr/libexec/logout-manager/lm-helper
new file mode 100755
index 0000000..6372827
--- /dev/null
+++ b/src/usr/libexec/logout-manager/lm-helper
@@ -0,0 +1,74 @@
+#!/bin/sh
+# Dependencies:
+# Devuan: wmctrl sudo
+# el7: wmctrl sudo
+case "${1}" in
+ help) # show this help screen
+ {
+ echo "Usage: ${0}: [command]"
+ echo "used by logout-manager to perform actions like reboot, lock screen, etc."
+ echo ""
+ echo "Commands:"
+ grep -E '^\s{3}[A-Za-z]+\)' "${0}" | tr -dc '[A-Za-z\n ]' | sed -r -e 's/\s+/ /g;' | grep -v "HIDDEN\s*$" | while read a therest ; do echo " ${a}: ${therest}" ; done
+ }
+ ;;
+ options) # used by bash_completion function HIDDEN
+ grep -E '^\s{3}[A-Za-z]+\)' "${0}" | awk '{print $1}' | tr -dc '[A-Za-z\n]' | grep -vE 'options|help'
+ ;;
+ lock) # lock the current screen
+ if test -z "${DRYRUN}" ;
+ then
+ xscreensaver --locknow
+ else
+ echo "xscreensaver --locknow"
+ fi
+ ;;
+ logout) # log out the current user of the graphical session
+ # determine DE/WM and act accordingly
+ _wm="$( wmctrl -m | awk '/Name:/{$1="";print;}' | xargs )"
+ case "${_wm}" in
+ Fluxbox)
+ if test -z "${DRYRUN}" ;
+ then
+ fluxbox-remote exit
+ else
+ echo "fluxbox-remote exit"
+ fi
+ ;;
+ *)
+ echo "Gotta say unh! Feature not yet implemented for \"${_wm}\"" 1>&2
+ exit 1
+ ;;
+ esac
+ ;;
+ hibernate) # save system state to disk and power off
+ # this method is linux only
+ if test -z "${DRYRUN}" ;
+ then
+ printf 'disk' | tee /sys/power/state
+ else
+ echo "printf 'disk' | tee /sys/power/state"
+ fi
+ ;;
+ shutdown) # power off
+ if test -z "${DRYRUN}" ;
+ then
+ shutdown -h now
+ else
+ echo "shutdown -h now"
+ fi
+ ;;
+ reboot) # restart the system
+ if test -z "${DRYRUN}" ;
+ then
+ shutdown -r now
+ else
+ echo "shutdown -r now"
+ fi
+ ;;
+ *) # HIDE
+ echo "invalid choice: ${1}" 1>&2
+ exit 1
+ ;;
+esac
+:
diff --git a/src/usr/share/applications/logout-manager.desktop b/src/usr/share/applications/logout-manager.desktop
new file mode 100644
index 0000000..4522a66
--- /dev/null
+++ b/src/usr/share/applications/logout-manager.desktop
@@ -0,0 +1,12 @@
+[Desktop Entry]
+Categories=Settings;HardwareSettings;
+Comment=Prompt for common actions including lock screen, logout, etc.
+Exec=/usr/bin/logout-manager
+GenericName=Logout menu
+Icon=system-log-out
+Keywords=shutdown;hibernate;lockscreen;logout;reboot;
+Name=Logout...
+StartupNotify=true
+Terminal=false
+Type=Application
+Version=1.0
diff --git a/src/usr/share/bash-completion/completions/logout-manager b/src/usr/share/bash-completion/completions/logout-manager
new file mode 100644
index 0000000..fd1267f
--- /dev/null
+++ b/src/usr/share/bash-completion/completions/logout-manager
@@ -0,0 +1,12 @@
+# File: /etc/bash_completion.d/logout-manager
+# Reference:
+# bgscripts-core: /usr/bin/bp
+# man complete
+
+_lm_helper() {
+ local cur prev words cword;
+ _init_completion || return
+ COMPREPLY=($( compgen -W "$( ~/dev/logout-manager/src/usr/libexec/logout-manager/lm-helper options )" -- "$cur" ))
+ return 0
+} && \
+complete -F _lm_helper -o bashdefault lm-helper
diff --git a/src/usr/share/doc/logout-manager/README.md b/src/usr/share/doc/logout-manager/README.md
new file mode 100644
index 0000000..d34f02c
--- /dev/null
+++ b/src/usr/share/doc/logout-manager/README.md
@@ -0,0 +1,40 @@
+# README for logout-manager
+## Introduction
+Logout Manager is a python3 utility that provides a simple menu for logout-type actions. The supported actions are presented:
+ * Lock
+ * Logout
+ * Hibernate (if supported by hardware)
+ * Shutdown
+ * Reboot
+
+## Customization
+The `lm-helper` logout command needs to be customized for every desktop environment. Some may need extra configurationon the window manager/desktop environment side.
+
+### Fluxbox
+For Fluxbox, you need to set a value in ~/.fluxbox/init
+
+ session.screen0.allowRemoteActions: true
+
+Be aware that this is insecure. See man `fluxbox-remote(1)`.
+
+## Alternatives
+[oblogout](https://launchpad.net/oblogout) looks really old so I did not investigate personally, but it sounds like it does the same thing I am trying to do.
+`apt-cache search logout` shows [lxsession-logout](http://manpages.ubuntu.com/manpages/precise/en/man1/lxsession-logout.1.html) which was compiled, as well as does not provide configurable options for changing executed commands or icons.
+
+## License
+[logout-manager-ncurses.py](src/usr/bin/logout-manager-ncurses.py) is licensed under the [MIT license](http://choosealicense.com/licenses/mit) and is derived almost entirely from [adamlamers](http://adamlamers.com/post/FTPD9KNRA8CT).
+Everything else is licensed under [CC-BY-SA 4.0](https://choosealicense.com/licenses/cc-by-sa-4.0/).
+
+## Description
+This project is partially a programming playground for the [original author](https://bgstack15.wordpress.com) and partially a useful project for his migration to [Fluxbox](http://fluxbox.org/) on the desktop.
+
+## Upsides
+* This project is the first to [demonstrate SVG images in tkinter in python3](https://bgstack15.wordpress.com/2019/07/13/display-svg-in-tkinter-python3/) that I could find on the Internet.
+* This project demonstrates how to have the Makefile and debian/rules build a dependency list, from the Dependencies tags of the files themselves.
+* I have learned how to work with ncurses, gtk, and tcl in python3.
+* This will make Fluxbox systems easier to use for general users.
+* Does not use dbus!
+
+## Downsides
+* This whole thing is more complex than just logging out of my user session, and selecting a logout-type action from the display manager.
+* Depends on sudo instead of using native tools.
diff --git a/logout-manager.conf b/src/usr/share/doc/logout-manager/logout-manager.conf.example
index 1a14909..1a14909 100644
--- a/logout-manager.conf
+++ b/src/usr/share/doc/logout-manager/logout-manager.conf.example
diff --git a/lmlib.py b/src/usr/share/logout-manager/lmlib.py
index fe6d4a4..40ee3a0 100644
--- a/lmlib.py
+++ b/src/usr/share/logout-manager/lmlib.py
@@ -13,36 +13,45 @@
# Improve:
# Documentation:
-import configparser, platform, os
+import configparser, platform, os, subprocess
-logout_manager_version="2019-06-21a"
+logout_manager_version="2020-03-10a"
class Actions:
+ def __take_action(command):
+ print(command)
+ command=str(command).split()
+ command2=[]
+ for i in command:
+ command2.append(str(i.strip('"')))
+ command=command2
+ subprocess.run(command)
+
@staticmethod
def hibernate(config, event=None):
#print("need to run the /sys/power/state trick, if available")
- print(config.get_hibernate_command())
+ Actions.__take_action(config.get_hibernate_command())
@staticmethod
def lock(config, event=None):
#print("please lock the screen.")
- print(config.get_lock_command())
+ Actions.__take_action(config.get_lock_command())
@staticmethod
def logout(config, event=None):
#print("please log out of current session!")
- print(config.get_logout_command())
+ Actions.__take_action(config.get_logout_command())
@staticmethod
def reboot(config, event=None):
#print("please reboot.")
- print(config.get_reboot_command())
+ Actions.__take_action(config.get_reboot_command())
@staticmethod
def shutdown(config, event=None):
#print("please shut yourself down!")
- print(config.get_shutdown_command())
+ Actions.__take_action(config.get_shutdown_command())
class Config:
def __init__(self):
@@ -192,10 +201,10 @@ def get_gtk3_default_icon_theme():
print("Found gtk3 default theme:",name)
return name
-def Initialize_config():
+def Initialize_config(infile):
# Read config
config_in = configparser.ConfigParser()
- config_in.read('logout-manager.conf')
+ config_in.read(infile)
config = Config()
try:
ci = config_in['logout-manager']
bgstack15