├── .gitignore ├── LICENSE ├── README.md ├── failure-monitor ├── failure-monitor.py └── failure-monitor@.service ├── onfailure ├── failure-email@.service └── onfailure.sh ├── scripts ├── 98-cmsis-dap.rules ├── gnome-keyboard-mgr.py ├── gnome-numlock-mgr.py └── systemd-email └── units ├── amazon-cloud-drive.service ├── backup-daily.target ├── backup-daily.timer ├── bitcoind.service ├── btrfs-scrub.service ├── btrfs-scrub.timer ├── crashplan.timer ├── fstrim.service ├── gnome-numlock-mgr.service ├── gpg-agent.service ├── import-env.service ├── ipv6-default-route-del@.service ├── openocd-cmsis-dap.service ├── openocd-cmsis-dap@.service ├── paccache.service ├── paccache.timer ├── papertrail.service ├── ping6.service ├── ping6.timer ├── rssh.service ├── storj-dataserv-client.service ├── uber-fail.service ├── wm.target ├── xbmc.service └── youtube-tv.service /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Kyle Manna 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # systemd-utils 2 | 3 | Random systemd utilities and unit files. 4 | 5 | ## Usage 6 | 7 | Recommended location: 8 | 9 | cd $HOME/.config/systemd 10 | git clone https://github.com/kylemanna/systemd-utils.git utils 11 | 12 | Symlink or hardlink unit files into `$HOME/.config/systemd/user`. Systemd appears to have some issues with symlinked unit files at the time of writing. 13 | 14 | ## Scripts 15 | 16 | ### On Failure 17 | 18 | * Allows users to specify `OnFailure=failure-email@%i.service` under `[Unit]` section of systemd files. 19 | * The `failure-email` service will email the user when a service fails unexpectedly and include the `systemd status ` output. 20 | * Example configuration systemd file: 21 | 22 | [Unit] 23 | ... 24 | OnFailure=failure-email@%i.service 25 | 26 | 27 | ### Failure Monitor 28 | 29 | * Systemd service that runs and parses the output of journalctl. When a task fails, an email is sent to the user at the configured email address. 30 | * The `failure-monitor` service will email the user when a service fails unexpectedly and include the `systemd status ` output. 31 | * Example configuration: 32 | 33 | $ systemctl --user start failure-monitor@test@gmail.com.service 34 | 35 | ### Email Journal Log 36 | 37 | Upon completion of a script (i.e. a daily backup script), send an email of the log output. The following code should be used to save a cursor before the execution of the service and send all the data followign that cursor. 38 | 39 | ExecStartPre=/bin/sh -c 'journalctl -o cat -n 0 -u %n --show-cursor | cut -f3 -d" " > /run/%n.cursor' 40 | 41 | ExecStart=... 42 | 43 | ExecStopPost=/bin/sh -c '/etc/systemd/scripts/systemd-email you@example.com %n $(cat /run/%n.cursor)' 44 | ExecStopPost=/bin/rm -f /run/%n.cursor 45 | 46 | It would be nice if systemd provided a reference to a cursor prior to the most recent invocation of the service (like the --boot option refers to this boot). Until then hack around it. 47 | -------------------------------------------------------------------------------- /failure-monitor/failure-monitor.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Author: Kyle Manna 4 | # 5 | # Monitor systemd-journal for events that fail and email someone. 6 | # 7 | 8 | import argparse 9 | import json 10 | import logging 11 | import os 12 | import pwd 13 | import select 14 | import smtplib 15 | import sys 16 | import systemd.journal 17 | import email 18 | 19 | from email.mime.text import MIMEText 20 | 21 | logging.basicConfig(level=logging.INFO) 22 | log_name = os.path.basename(__file__) if __name__ == '__main__' else __name__ 23 | logger = logging.getLogger(log_name) 24 | 25 | # 26 | # Class to monitor a systemd journal for exiting services and email them 27 | # 28 | class FailureMonitor(object): 29 | def __init__(self, email): 30 | self.email = email 31 | 32 | def run(self): 33 | reader = systemd.journal.Reader() 34 | reader.this_boot() 35 | reader.seek_tail() 36 | reader.get_previous() 37 | reader.log_level(systemd.journal.LOG_WARNING) 38 | 39 | p = select.poll() 40 | p.register(reader.fileno(), reader.get_events()) 41 | 42 | while True: 43 | p.poll() 44 | 45 | if reader.process() == systemd.journal.APPEND: 46 | self.handle_journal_entries(reader) 47 | 48 | return True 49 | 50 | def handle_journal_entries(self, reader): 51 | for entry in reader: 52 | self.handle_journal_entry(entry) 53 | 54 | @staticmethod 55 | def failure_detected_msg(msg: str): 56 | # Currently unused, but common systemd failure strings 57 | search = ['entered failed state', 'Failed with result'] 58 | return any([s in msg for s in search]) 59 | 60 | @staticmethod 61 | def failure_detected(entry): 62 | # This seems to be the simplest check 63 | return (entry.get('CODE_FUNC', '') == 'unit_log_failure') 64 | 65 | def handle_journal_entry(self, entry): 66 | 67 | if not self.failure_detected(entry): 68 | return 69 | 70 | unit = entry.get('UNIT') 71 | hostname = entry.get('_HOSTNAME') 72 | invoke_id = entry.get('INVOCATION_ID') 73 | 74 | logger.warning(f'Unit "{unit}" failed with invocation id "{invoke_id}"') 75 | 76 | filter_list = [ 77 | 'CODE_FUNC', 78 | 'INVOCATION_ID' 79 | 'MESSAGE', 80 | 'PRIORITY', 81 | 'UNIT', 82 | 'UNIT_RESULT', 83 | '_BOOT_ID', 84 | '_HOSTNAME', 85 | '__REALTIME_TIMESTAMP' 86 | ] 87 | entry2 = { k: str(v) for k, v in entry.items() if k in filter_list} 88 | 89 | body = [''] 90 | body += ['Failure Info:'] 91 | body += [' ' + x for x in json.dumps(entry2, sort_keys=True, indent=4).split('\n')] 92 | body += [''] 93 | body += ['Logs:'] 94 | body += [' ' + x for x in self.fetch_logs_for_invocation_id(invoke_id)] 95 | 96 | body = '\n'.join(body) 97 | 98 | msg = MIMEText(body) 99 | msg['From'] = args.email 100 | msg['To'] = args.email 101 | msg['Subject'] = f"[{hostname}] systemd: Unit '{unit}' failed" 102 | 103 | server = smtplib.SMTP('localhost') 104 | #server.set_debuglevel(1) 105 | server.sendmail(args.email, args.email, msg.as_string()) 106 | server.quit() 107 | 108 | @staticmethod 109 | def format_logs(entry): 110 | msg = entry.get('MESSAGE') 111 | time = entry.get('__REALTIME_TIMESTAMP') 112 | unit = entry.get('_SYSTEMD_UNIT') 113 | pid = entry.get('_PID') 114 | return f'{time:%Y-%m-%d %H:%M:%m} {unit}[{pid}] {msg}' 115 | 116 | def fetch_logs_for_invocation_id(self, invoke_id): 117 | reader = systemd.journal.Reader() 118 | reader.this_boot() 119 | reader.add_match(_SYSTEMD_INVOCATION_ID=invoke_id) 120 | 121 | logs = [self.format_logs(entry) for entry in reader] 122 | 123 | reader.close() 124 | 125 | return logs 126 | 127 | 128 | if __name__ == '__main__': 129 | 130 | email = os.environ.get('EMAIL', pwd.getpwuid(os.getuid()).pw_name) 131 | 132 | parser = argparse.ArgumentParser() 133 | parser.add_argument('email', 134 | nargs='?', 135 | help='destination email address for notifications', 136 | default=email) 137 | args = parser.parse_args() 138 | 139 | logger.info(f'Email = {args.email}') 140 | 141 | monitor = FailureMonitor(args.email) 142 | success = monitor.run() 143 | 144 | sys.exit(0 if success else 1) 145 | -------------------------------------------------------------------------------- /failure-monitor/failure-monitor@.service: -------------------------------------------------------------------------------- 1 | # Author: Kyle Manna 2 | # 3 | # Example usage: 4 | # systemctl --user enable failure-monitor@youremail@domain.com.service 5 | # systemctl --user start failure-monitor@youremail@domain.com.service 6 | [Unit] 7 | Description=Failure Monitor for %i 8 | 9 | [Service] 10 | ExecStart=%h/.config/systemd/utils/failure-monitor/failure-monitor.py %i 11 | Restart=always 12 | RestartSec=3 13 | 14 | [Install] 15 | WantedBy=default.target 16 | -------------------------------------------------------------------------------- /onfailure/failure-email@.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=OnFailure for %i 3 | 4 | [Service] 5 | Type=oneshot 6 | ExecStart=/usr/bin/env %h/.config/systemd/utils/onfailure/onfailure.sh %i 7 | -------------------------------------------------------------------------------- /onfailure/onfailure.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Author: Kyle Manna 4 | # 5 | # Simple systemd script used to be called via something like: 6 | # 7 | # Example Unit section of a service file: 8 | # 9 | # [Unit] 10 | # ... 11 | # Onfailure=failure-email@%i.service 12 | # 13 | # 14 | # failure-email@.service: 15 | # 16 | # [Unit] 17 | # Description=OnFailure for %i 18 | # 19 | # [Service] 20 | # Type=oneshot 21 | # ExecStart=/path/to/onfailure.sh %i 22 | # 23 | # 24 | # Set EMAIL environemental variable to override destination, see 25 | # import-env.service for example of how to easily do this. 26 | # 27 | # Destiantion email priority: $2 -> $EMAIL -> $USER 28 | 29 | svc=${1:-unknown} 30 | #email=${2:-example@gmail.com} 31 | email=${2:-${EMAIL:-$USER}} 32 | 33 | cat < returned NULL without setting an error 34 | self.dev = None 35 | self.cleared_numlock = False 36 | 37 | 38 | def ui_thing(self): 39 | pass 40 | # Toggling numlock while the screensaver activates due to timeout will cause it to reset and 41 | # instantly wake and never sleep! Don't do this. 42 | #if not self.ui: 43 | # self.ui = evdev.UInput(name='GNOME NumLock Manager') 44 | # print(f"Created {self.ui}") 45 | # 46 | #self.ui.write(e.EV_KEY, e.KEY_NUMLOCK, 1) 47 | #self.ui.write(e.EV_KEY, e.KEY_NUMLOCK, 0) 48 | #self.ui.syn() 49 | 50 | def init_dev(self): 51 | if not self.dev: 52 | self.dev = evdev.InputDevice(self.device_path) 53 | print(f"Opened {self.dev}") 54 | 55 | # HACK Test until I can figure out why this needs to happen 56 | try: 57 | leds = self.dev.leds() 58 | except Exception as e: 59 | print(f"Failed to get LEDs, re-opening...") 60 | self.dev = evdev.InputDevice(self.device_path) 61 | 62 | 63 | def set_numlock(self, val = 1): 64 | # Hack, why? 65 | self.init_dev() 66 | # Seems that toggling numlock on the lock screen fails to turn off the LED, perhaps because the 67 | # terminal has changed and the user input fails? Will the LED and internal numlock state ever get 68 | # out of sync? 69 | # FIXME get rid of the following when it's better understood. 70 | self.dev.set_led(self.e.LED_NUML, val) 71 | self.cleared_numlock = not bool(val) 72 | 73 | 74 | def get_numlock(self): 75 | # Hack, why? 76 | self.init_dev() 77 | return self.e.LED_NUML in self.dev.leds() 78 | 79 | def signal_cb(self, screensaver_active): 80 | # Convert from dbus.boolean() 81 | screensaver_active = bool(screensaver_active) 82 | numlock = None 83 | 84 | if screensaver_active and self.get_numlock(): 85 | numlock = 0 86 | elif not screensaver_active and self.cleared_numlock: 87 | numlock = 1 88 | 89 | print(f"Screensaver status = {screensaver_active}, setting NumLock {numlock}") 90 | 91 | if numlock != None: 92 | self.set_numlock(numlock) 93 | 94 | if __name__ == '__main__': 95 | 96 | mgr = GnomeNumlockMgr('/dev/input/by-id/usb-Logitech_Logitech_G710_Keyboard-event-kbd') 97 | 98 | DBusGMainLoop(set_as_default=True) 99 | bus = dbus.SessionBus() 100 | bus.add_signal_receiver(mgr.signal_cb, 'ActiveChanged', 'org.gnome.ScreenSaver') 101 | 102 | loop = GLib.MainLoop() 103 | loop.run() 104 | -------------------------------------------------------------------------------- /scripts/systemd-email: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | args="--since=today" 6 | [ -n "$3" ] && args="--after-cursor=$3" 7 | 8 | 9 | /usr/bin/sendmail -t < 12 | Subject: $2 13 | Content-Transfer-Encoding: 8bit 14 | Content-Type: text/plain; charset=UTF-8 15 | 16 | $(journalctl -b0 $args -u "$2") 17 | ERRMAIL 18 | -------------------------------------------------------------------------------- /units/amazon-cloud-drive.service: -------------------------------------------------------------------------------- 1 | # $HOME/.config/systemd/user/amazon-cloud-drive.service 2 | # 3 | # Usage: 4 | # * Setup acd_cli normally and create $HOME/Cloud 5 | # * Copy file to: $HOME/.config/systemd/user/amazon-cloud-drive.serivce 6 | # * Reload systemd: systemctl --user daemon-reload 7 | # * Start service: systemctl --user start amazon-cloud-drive.serivce 8 | [Unit] 9 | Description=User Amazon Cloud Drive FUSE Mount 10 | Documentation=https://github.com/yadayada/acd_cli 11 | 12 | [Service] 13 | AssertPathIsDirectory=%h/Cloud 14 | ExecStart=/usr/bin/acd_cli -v mount --foreground %h/Cloud 15 | Restart=on-abort 16 | 17 | [Install] 18 | WantedBy=default.target 19 | -------------------------------------------------------------------------------- /units/backup-daily.target: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Daily Backups 3 | OnFailure=failure-email@%i.service 4 | StopWhenUnneeded=yes 5 | -------------------------------------------------------------------------------- /units/backup-daily.timer: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Daily Backups 3 | 4 | [Timer] 5 | OnCalendar=*-*-* 3:21:00 6 | Persistent=true 7 | Unit=backup-daily.target 8 | 9 | [Install] 10 | WantedBy=default.target 11 | -------------------------------------------------------------------------------- /units/bitcoind.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Run bitcoind and log to journalctl 3 | IgnoreOnIsolate=true 4 | 5 | [Service] 6 | ExecStart=/usr/bin/bitcoind -printtoconsole 7 | Restart=on-abort 8 | 9 | [Install] 10 | WantedBy=default.target 11 | -------------------------------------------------------------------------------- /units/btrfs-scrub.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Check volume for errors 3 | Documentation=man:btrfs-scrub 4 | After=fstrim.service 5 | 6 | [Service] 7 | Type=oneshot 8 | ExecStart=/usr/bin/sh -c 'for i in $(grep btrfs /proc/mounts | cut -f1 -d" " | uniq); do echo scrubbing $i; btrfs scrub start -Bd $i; done' 9 | IOSchedulingClass=idle 10 | CPUSchedulingPolicy=idle 11 | -------------------------------------------------------------------------------- /units/btrfs-scrub.timer: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Check volume for errors once a week 3 | Documentation=man:btrfs-scrub 4 | 5 | [Timer] 6 | OnCalendar=weekly 7 | AccuracySec=1h 8 | Persistent=true 9 | 10 | [Install] 11 | WantedBy=multi-user.target 12 | -------------------------------------------------------------------------------- /units/crashplan.timer: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Delay Crashplan at Boot-up 3 | 4 | [Timer] 5 | OnBootSec=30min 6 | 7 | [Install] 8 | WantedBy=multi-user.target 9 | -------------------------------------------------------------------------------- /units/fstrim.service: -------------------------------------------------------------------------------- 1 | # Called by /usr/lib/systemd/system/fstrim.timer from util-linux 2 | # Add `-v` flag so that results can be reviewed in jouranctl 3 | [Unit] 4 | Description=Discard unused blocks 5 | 6 | [Service] 7 | Type=oneshot 8 | ExecStart=/sbin/fstrim -av 9 | -------------------------------------------------------------------------------- /units/gnome-numlock-mgr.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=GNOME Numlock Manager 3 | After=gnome-session.target 4 | 5 | [Service] 6 | ExecStart=/usr/bin/python -u ${HOME}/.config/systemd/utils/scripts/gnome-numlock-mgr.py 7 | 8 | # Restart every >2 seconds to avoid StartLimitInterval failure 9 | #RestartSec=3 10 | #Restart=on-abort 11 | 12 | [Install] 13 | WantedBy=default.target 14 | -------------------------------------------------------------------------------- /units/gpg-agent.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=GPG private key agent 3 | IgnoreOnIsolate=true 4 | 5 | [Service] 6 | Type=forking 7 | # Start GPG manually so that precise arguments can be passed. Would be nice if 8 | # gpgconf added support for allow-preset-password 9 | ExecStart=/usr/bin/gpg-agent --daemon --allow-preset-passphrase --max-cache-ttl 8640000 10 | Restart=on-abort 11 | 12 | [Install] 13 | WantedBy=default.target 14 | -------------------------------------------------------------------------------- /units/import-env.service: -------------------------------------------------------------------------------- 1 | # Simple way to import user specific environmental variables 2 | # 3 | # Example env.conf file: 4 | # 5 | # EMAIL=user@gmail.com 6 | # 7 | [Unit] 8 | Description=Import systemd user env 9 | 10 | [Service] 11 | Type=oneshot 12 | ExecStart=/bin/sh -c 'xargs systemctl --user set-environment < $HOME/.config/systemd/env.conf' 13 | 14 | [Install] 15 | # Race condition with other default.target proccesses? 16 | # TODO: This service needs to run *before* all other services that depend on it 17 | # setting the environement. Think I need multiple targets. Sigh. 18 | WantedBy=default.target 19 | -------------------------------------------------------------------------------- /units/ipv6-default-route-del@.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Remove Default IPv6 Route 3 | After=sys-subsystem-net-devices-%i.device network.target 4 | 5 | [Service] 6 | ExecStart=/sbin/ip -6 route del default dev %i 7 | 8 | # Since we can't trigger after the specific device has finished 9 | # init, restart until success. 10 | Restart=on-failure 11 | RestartSec=2 12 | 13 | [Install] 14 | WantedBy=multi-user.target 15 | -------------------------------------------------------------------------------- /units/openocd-cmsis-dap.service: -------------------------------------------------------------------------------- 1 | # Author: Kyle Manna 2 | # 3 | # This service file depends on a systemd device alias that will trigger this 4 | # service to start (after enabled) and start openocd as appropriate. The 5 | # service will shutdown when the device is removed. 6 | # 7 | # To use, enable: 8 | # 1. Copy 98-cmsis-dap.rules to /etc/udev/rules.d 9 | # 2. systemctl --user enable openocd-cmsis-dap.service 10 | 11 | [Unit] 12 | Description=OpenOCD Daemon 13 | After=sys-devices-swd-cmsis-dap.device 14 | BindsTo=sys-devices-swd-cmsis-dap.device 15 | 16 | [Service] 17 | # Tested with FRDM-K64F 18 | ExecStart=/bin/sh -c 'sleep 1; exec openocd -f interface/cmsis-dap.cfg -f target/kx.cfg -c "kx.cpu configure -event gdb-attach { reset init }"' 19 | 20 | [Install] 21 | WantedBy=sys-devices-swd-cmsis-dap.device 22 | -------------------------------------------------------------------------------- /units/openocd-cmsis-dap@.service: -------------------------------------------------------------------------------- 1 | # Use this unit file if you have multiple debuggers attached 2 | 3 | [Unit] 4 | Description=OpenOCD Daemon for Single Serial # 5 | After=sys-devices-swd-cmsis-dap-%i.device 6 | BindsTo=sys-devices-swd-cmsis-dap-%i.device 7 | 8 | [Service] 9 | # Using FRDM-K64F 10 | ExecStart=/bin/sh -c 'sleep 1; exec openocd -f interface/cmsis-dap.cfg -f target/kx.cfg -c "kx.cpu configure -event gdb-attach { reset init }" -c "cmsis_dap_serial %i"' 11 | 12 | [Install] 13 | WantedBy=sys-devices-swd-cmsis-dap-%i.device 14 | -------------------------------------------------------------------------------- /units/paccache.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Clean-up Old Pacman Files 3 | Before=fstrim.service 4 | 5 | [Service] 6 | Type=oneshot 7 | 8 | IOSchedulingClass=idle 9 | CPUSchedulingPolicy=idle 10 | 11 | ExecStart=/usr/bin/paccache -r 12 | ExecStart=/usr/bin/paccache -ruk0 13 | -------------------------------------------------------------------------------- /units/paccache.timer: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Clean-up Old Pacman Files 3 | 4 | [Timer] 5 | OnCalendar=daily 6 | AccuracySec=1h 7 | 8 | [Install] 9 | WantedBy=multi-user.target 10 | -------------------------------------------------------------------------------- /units/papertrail.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Papertrail 3 | After=systemd-journald.service network-online.target 4 | Requires=systemd-journald.service 5 | 6 | [Service] 7 | DynamicUser=true 8 | Group=systemd-journal 9 | ProtectSystem=true 10 | ProtectHome=true 11 | ExecStart=/bin/sh -c 'journalctl -f | ncat --ssl logs999.papertrailapp.com 12345' 12 | Restart=on-failure 13 | RestartSec=1m 14 | 15 | [Install] 16 | WantedBy=multi-user.target 17 | -------------------------------------------------------------------------------- /units/ping6.service: -------------------------------------------------------------------------------- 1 | # Ping periodically to detect when Comcast breaks up our prefix delegation 2 | # routing. 3 | [Unit] 4 | Description=Verify IPv6 PD 5 | OnFailure=failure-email@%i.service 6 | 7 | [Service] 8 | Type=oneshot 9 | #ExecStart=/usr/bin/ping6 -c 2 -I lan0 2001:4860:4860::8844 10 | # Unreachable via Comcast on 2015.01.07, :8888 was reachable 11 | ExecStart=/usr/bin/ping6 -c 2 -I lan0 2001:4860:4860::8888 12 | -------------------------------------------------------------------------------- /units/ping6.timer: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Verify IPv6 3 | 4 | [Timer] 5 | #OnCalendar=*:0/15 6 | OnCalendar=hourly 7 | 8 | [Install] 9 | WantedBy=default.target 10 | -------------------------------------------------------------------------------- /units/rssh.service: -------------------------------------------------------------------------------- 1 | # Setup a reverse ssh tunnel and restart it if it fails. 2 | # 3 | # This helps to create reverse NAT tunnels. 4 | # 5 | # More details @ http://blog.kylemanna.com/linux/2014/02/20/ssh-reverse-tunnel-on-linux-with-systemd/ 6 | [Unit] 7 | Description=Reverse SSH Service 8 | After=network.target 9 | 10 | [Service] 11 | User=localuser 12 | ExecStart=/usr/bin/ssh -NTC -o ServerAliveInterval=60 -o ExitOnForwardFailure=yes -o StrictHostKeyChecking=no -i /home/localuser/.ssh/servername-home-key -R 12345:localhost:22 r@reverse.remotehost.tld 13 | 14 | # Restart every >2 seconds to avoid StartLimitInterval failure 15 | RestartSec=3 16 | Restart=always 17 | 18 | [Install] 19 | WantedBy=multi-user.target 20 | 21 | -------------------------------------------------------------------------------- /units/storj-dataserv-client.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Storj dataserv-client 3 | IgnoreOnIsolate=true 4 | 5 | [Service] 6 | Environment="MAX_SIZE=100GB" 7 | ExecStart=/usr/bin/dataserv-client --max_size=${MAX_SIZE} farm 8 | Restart=always 9 | 10 | [Install] 11 | WantedBy=default.target 12 | -------------------------------------------------------------------------------- /units/uber-fail.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Test Failure 3 | OnFailure=failure-email@%i.service 4 | 5 | [Service] 6 | Type=oneshot 7 | ExecStart=/bin/sh -c 'echo failing @ $(date); /usr/bin/false' 8 | -------------------------------------------------------------------------------- /units/wm.target: -------------------------------------------------------------------------------- 1 | # This target is to be started by the window manager, i.e. cinnamon 2 | # Use Startup Applications under settings to invoke this by running 3 | # systemctl --user start wm.target 4 | [Unit] 5 | Description=Window manager target 6 | -------------------------------------------------------------------------------- /units/xbmc.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Run XBMC / Kodi 3 | 4 | [Service] 5 | ExecStart=/usr/bin/xbmc 6 | Environment=DISPLAY=:0 7 | 8 | [Install] 9 | WantedBy=wm.target 10 | -------------------------------------------------------------------------------- /units/youtube-tv.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Run YouTube TV 3 | 4 | [Service] 5 | ExecStart=/usr/bin/chromium --start-fullscreen https://youtube.com/tv 6 | Environment=DISPLAY=:0 7 | 8 | [Install] 9 | WantedBy=wm.target 10 | --------------------------------------------------------------------------------