diff --git a/examples/README.md b/examples/README.md index be74091f712b69f3616ed24af1346c590e08becd..40964178f58f5c4e2eb9d26f48fa1eb050ceec75 100644 --- a/examples/README.md +++ b/examples/README.md @@ -3,6 +3,7 @@ |Example|Note| |---|---| |[template](template)|Complete documented empty config| -|[archlinux-gnome](archlinux-gnome)|An example config with archlinux and gnome| -|[ubuntu-server](ubuntu-server)|An example config for someone's ubuntu server| +|[archlinux-gnome](archlinux-gnome)|An example multi-user config with archlinux and gnome| +|[ubuntu-server](ubuntu-server)|An example root-user config for ubuntu server| +|[non-root](non-root)|An example config for machine without root access| diff --git a/examples/archlinux-gnome/conf.d/basic.sh b/examples/archlinux-gnome/conf.d/basic.sh index 211d855f501f6039d16724999d3874e42ffa6c94..1c8e83f5e1ebaba8e40547c5faedeb67a6a26f9d 100644 --- a/examples/archlinux-gnome/conf.d/basic.sh +++ b/examples/archlinux-gnome/conf.d/basic.sh @@ -2,11 +2,11 @@ lc_assert_user_is root desktop_related_setup () { # to install & enable gnome - pacman -Sy --noconfirm gnome networkmanager power-profiles-daemon nextcloud-client firefox + pacman -Sy --noconfirm gnome networkmanager power-profiles-daemon systemctl enable gdm NetworkManager power-profiles-daemon # more customization... - pacman -Sy --needed --noconfirm base-devel nextcloud-client firefox telegram-desktop docker shadowsocks-rust v2ray proxychains xclip adobe-source-han-sans-cn-fonts pcsclite ccid git inetutils wget ttf-fira-code htop tmux dos2unix nfs-utils python-pip gnome-tweaks fcitx5-im man-db man-pages kolourpaint breeze + pacman -Sy --needed --noconfirm base-devel telegram-desktop docker shadowsocks-rust v2ray proxychains xclip adobe-source-han-sans-cn-fonts pcsclite ccid git inetutils wget ttf-fira-code htop tmux dos2unix nfs-utils fcitx5-im firefox pacman -Sy --needed --noconfirm recolic-aur/gnome-terminal-transparency recolic-aur/oreo-cursors-git echo ' diff --git a/examples/archlinux-gnome/conf.d/desktop.sh b/examples/archlinux-gnome/conf.d/desktop.sh index 5b7acdf10ab27ead4832e31b8b1669fc8df6e108..3bbeff0b8255603120a1f4c71463de9e09d0b662 100644 --- a/examples/archlinux-gnome/conf.d/desktop.sh +++ b/examples/archlinux-gnome/conf.d/desktop.sh @@ -1,5 +1,9 @@ lc_assert_user_is_not root +lc_fsmap files/config.fish $HOME/.config/fish/config.fish +lc_fsmap files/ssh_config $HOME/.ssh/config +lc_fsmap files/vimrc $HOME/.vimrc + config_gsettings () { echo "## gnome desktop config" gsettings set org.gnome.settings-daemon.plugins.power sleep-inactive-ac-type nothing @@ -23,8 +27,6 @@ config_gsettings () { gsettings set org.gnome.desktop.wm.keybindings switch-windows-backward "['<Primary><Shift>Tab']" gsettings set org.gnome.desktop.wm.keybindings switch-applications "['<Super>Tab', '<Alt>Tab']" gsettings set org.gnome.desktop.wm.keybindings switch-applications-backward "['<Shift><Super>Tab', '<Shift><Alt>Tab']" - - } lc_init () { @@ -34,8 +36,6 @@ lc_init () { config_gsettings } -lc_fsmap $HOME/sh/mybin /usr/mybin - lc_startup () { firefox_config=' user_pref("browser.tabs.tabmanager.enabled", false); @@ -50,5 +50,7 @@ user_pref("browser.tabs.hoverPreview.enabled", false);' lc_login () { # echo _:1 | bash /usr/mybin/unlock_keyrings + echo "$(date) test-only: lc_login called" >> /tmp/note + chmod 777 /tmp/note } diff --git a/examples/archlinux-gnome/linuxconf.wrapper b/examples/archlinux-gnome/linuxconf.wrapper index dc8517c146d3db9bdb08d24793ced65482ebd809..e1cadf45bd426b1cbb3c671011b27f5afb02bf88 100755 --- a/examples/archlinux-gnome/linuxconf.wrapper +++ b/examples/archlinux-gnome/linuxconf.wrapper @@ -1,4 +1,9 @@ #!/bin/bash -TODO: this is a wrapper. it should download & install real linuxconf binary. +if [ ! -f /usr/bin/linuxconf ]; then + curl "https://git.recolic.net/root/linuxconf/-/raw/master/linuxconf?ref_type=heads" -o /usr/bin/linuxconf || + ! echo "Unable to download linuxconf executable." || exit 1 + chmod +x /usr/bin/linuxconf +fi +/usr/bin/linuxconf "$@" diff --git a/examples/template/linuxconf.wrapper b/examples/template/linuxconf.wrapper index dc8517c146d3db9bdb08d24793ced65482ebd809..e1cadf45bd426b1cbb3c671011b27f5afb02bf88 100755 --- a/examples/template/linuxconf.wrapper +++ b/examples/template/linuxconf.wrapper @@ -1,4 +1,9 @@ #!/bin/bash -TODO: this is a wrapper. it should download & install real linuxconf binary. +if [ ! -f /usr/bin/linuxconf ]; then + curl "https://git.recolic.net/root/linuxconf/-/raw/master/linuxconf?ref_type=heads" -o /usr/bin/linuxconf || + ! echo "Unable to download linuxconf executable." || exit 1 + chmod +x /usr/bin/linuxconf +fi +/usr/bin/linuxconf "$@" diff --git a/examples/template/masterconf.sh b/examples/template/masterconf.sh index dc0425abe4530aac58be20c7db5c3b4fd0476aff..66179eecfedade7ee628354d3f33665212ea8cda 100644 --- a/examples/template/masterconf.sh +++ b/examples/template/masterconf.sh @@ -37,8 +37,10 @@ function lc_startup () { } function lc_login () { - # warning: less useful. happens again if user logout/login again. - # (no plan to support in first ver) + # Your desktop environment must implement "XDG autostart". Ref: https://wiki.archlinux.org/title/XDG_Autostart + # Otherwise... Use lc_init to your task into wherever u'd like. + # + # Warning: Could be called multiple times if user logout/login again. lc_login_is_x11? } diff --git a/linuxconf b/linuxconf index 635fad56b4400a95748bbef28a15f25860dd4fe2..81789c14e8ef724db38d891768e6c72d3ea772ba 100755 --- a/linuxconf +++ b/linuxconf @@ -109,7 +109,7 @@ function lci_overwrite_conf () { [[ "$oldpath" = "$newpath" ]] && return 0 fi - echo "masterconf=$newpath" | tee "$fname" > /dev/null || err "lci_overwrite_conf: unable to create $fname" || return $? + echo -e "#autogenerated config, could be overwritten without warning.\nmasterconf=$newpath" | tee "$fname" > /dev/null || err "lci_overwrite_conf: unable to create $fname" || return $? chmod ugo+rw "$fname" } @@ -122,18 +122,22 @@ function lci_register () { lci_overwrite_conf /etc/linuxconf.conf "$confpath" || die "lci_register cannot write new conf" } -function lci_init_if_needed () { - local uname="$(whoami)" - lci_state_file_contains /etc/linuxconf.conf init_done "$uname" && return 0 - echo2 "RDEBUG: init needed for $uname" - - # call lc_init() - export LCI_SUBSHELL_OP=lc_init +function lci_call () { + # calls an lc function in masterconf (and included subconf) + [ "$1" = "" ] && die "logic error: lci_call without arg" + export LCI_SUBSHELL_OP="$1" local masterconf="$(lci_conf_get_masterconf_path /etc/linuxconf.conf)" || die "unable to call lc_init. Cannot read masterconf path from /etc/linuxconf.conf" local workdir="$(dirname "$masterconf")" cd "$workdir" || die "unable to enter config directory: $workdir" lc_include "$masterconf" export LCI_SUBSHELL_OP=__lc_operation_undefined +} + +function lci_init_if_needed () { + local uname="$(whoami)" + lci_state_file_contains /etc/linuxconf.conf init_done "$uname" && return 0 + + lci_call lc_init lci_state_file_append /etc/linuxconf.conf init_done "$uname" || die "lc_init functions succeeded, but unable to update /etc/linuxconf.conf" } @@ -148,14 +152,8 @@ function lci_startup_if_needed () { lci_state_file_contains $state_file startup_done "$uname" && return 0 - # call lc_startup() - export LCI_SUBSHELL_OP=lc_startup - local masterconf="$(lci_conf_get_masterconf_path /etc/linuxconf.conf)" || die "unable to call lc_init. Cannot read masterconf path from /etc/linuxconf.conf" - local workdir="$(dirname "$masterconf")" - cd "$workdir" || die "unable to enter config directory: $workdir" - lc_include "$masterconf" - export LCI_SUBSHELL_OP=__lc_operation_undefined - + lci_call lc_startup + lci_state_file_append $state_file startup_done "$uname" || die "lc_startup functions succeeded, but unable to update $state_file" } @@ -166,21 +164,81 @@ function lci_usage () { exit 1 } -lci_version=0.1.0 +# All install hook function should: +# be safe for duplicate call, +# works for root / non-root, +# clears non-root alternative when called with root. +function lci_install_startup_hook () { + # if is root: + # if root config exists, overwrite it + # if non-root config exists, clear it + # else: + # if root config exists, return + # if non-root config exists, overwrite it + local service_file=W1VuaXRdCkRlc2NyaXB0aW9uPWxpbnV4Y29uZiBzdGFydHVwIGhvb2sgYXMgcm9vdCB1c2VyCkFmdGVyPW5ldHdvcmsudGFyZ2V0CltTZXJ2aWNlXQpUeXBlPW9uZXNob3QKRXhlY1N0YXJ0PS91c3IvYmluL2xpbnV4Y29uZiBfc3RhcnR1cF9hbGwKUmVtYWluQWZ0ZXJFeGl0PXllcwpbSW5zdGFsbF0KV2FudGVkQnk9bXVsdGktdXNlci50YXJnZXQK + + if [[ "$(whoami)" = root ]]; then + if command -v systemctl > /dev/null ; then + echo "$service_file" | base64 -d > /etc/systemd/system/lc-hook.service && + systemctl daemon-reload && + systemctl enable lc-hook.service + elif [ -f /etc/rc.local ]; then + echo '/usr/bin/linuxconf _startup_all' >> /usr/bin/linuxconf _startup_all + else + err "neither systemd nor /etc/rc.local available." + fi || return $? + #TODO: check if any user installed non-root startup hook + else + [ -f /etc/systemd/system/lc-hook.service ] && return 0 + grep '/usr/bin/linuxconf _startup_all' /etc/rc.local >/dev/null 2>&1 && return 0 + # TODO: install non-root startup hook + die "non-root startup hook not supported yet" + fi +} +function lci_install_login_hook () { + # https://wiki.archlinux.org/title/XDG_Autostart + [ "$XDG_CONFIG_DIRS" = "" ] && local XDG_CONFIG_DIRS=/etc/xdg + [ "$XDG_CONFIG_HOME" = "" ] && local XDG_CONFIG_HOME="$HOME/.config" + local desktop_file=W0Rlc2t0b3AgRW50cnldClR5cGU9QXBwbGljYXRpb24KTmFtZT1sY194ZGdfbG9naW4KRXhlYz0vdXNyL2Jpbi9saW51eGNvbmYgX3hkZ19sb2dpbgpYLUdOT01FLUF1dG9zdGFydC1lbmFibGVkPXRydWUK + + # if is root: + # if root config exists, overwrite it + # if non-root config exists, clear it + # else: + # if root config exists, return + # if non-root config exists, overwrite it + + if [[ "$(whoami)" = root ]]; then + mkdir -p "$XDG_CONFIG_DIRS/autostart" && + echo "$desktop_file" | base64 -d > "$XDG_CONFIG_DIRS/autostart/lc-hook.desktop" || return $? + + for uconfig in /home/*/.config/autostart/lc-hook.desktop "$XDG_CONFIG_HOME/autostart/lc-hook.desktop"; do + [ -f "$uconfig" ] && rm -f "$uconfig" + done + else + [ -f "$XDG_CONFIG_DIRS/autostart/lc-hook.desktop" ] && return 0 + + mkdir -p "$XDG_CONFIG_HOME/autostart" && + echo "$desktop_file" | base64 -d > "$XDG_CONFIG_HOME/autostart/lc-hook.desktop" || return $? + fi +} + +lci_version=0.1.1 subcommand="$1" if [[ "$subcommand" != register ]] && [[ "$subcommand" != "" ]]; then [[ ! -f /etc/linuxconf.conf ]] && die "Please run '$0 register <path/to/masterconf.sh>' at least once" fi if [[ "$subcommand" = register ]]; then lci_register "$2" + lci_install_startup_hook || die "failed to install on_startup hook" + lci_install_login_hook || die "failed to install on_login hook" lci_init_if_needed lci_startup_if_needed elif [[ "$subcommand" = _cron ]]; then # TODO: implement cron. with crontab or same systemd service? : elif [[ "$subcommand" = _startup ]]; then - lci_startup_if_needed # TODO: no need to check "if needed" - # TODO: for current user, check if desktop environment 'autostart' dir exists. If so, update autostart/linuxsync_on_login.desktop + lci_startup_if_needed elif [[ "$subcommand" = _startup_all ]]; then # systemd should call this service as root, and it will spawn subprocess for all users with sudo [[ "$(whoami)" != root ]] && die "$0 _startup_all started as non-root. Exit because sudo might fail." @@ -189,9 +247,8 @@ elif [[ "$subcommand" = _startup_all ]]; then echo2 "Spawn subprocess '$0 _startup' as user $uname..." sudo -u "$uname" "$0" _startup done -elif [[ "$subcommand" = _de_login ]]; then - # TODO: call lc_login. no need to check "if needed" - : +elif [[ "$subcommand" = _xdg_login ]]; then + lci_call lc_login else lci_usage exit