Tomb icon indicating copy to clipboard operation
Tomb copied to clipboard

Tomb close automatically with a timer

Open vonpupp opened this issue 8 years ago • 8 comments

Hi,

Thank you for Tomb, it is just amazing!

I wonder if there is a feature already available to automatically close a tomb after X minutes. I have seen that feature in the pass-tomb plugin, but apparently it is not something within Tomb, but the plugin itself.

In pass-tomb is something like this:

pass tomb <gpg-id> --timer=10min

And after 10min the tomb automagically closes, which is very convenient IMHO for security.

Is there any way to achieve this with pure Tomb?

Thank you very much.

vonpupp avatar Dec 30 '17 01:12 vonpupp

Currently not. And looking at the timer solution in pass-tomb, I doubt it would be implemented like that as it uses a systemd timer. Maybe as an extra tool. An alternative would be a classic cron-job (and working with the issue, that this specific crontab entry needs to be dropped). Additionally, if only tombs should be closed if x min/h/whatever have gone without user interaction (plus using Xorg) then something could be done with xautolock (only working if tomb script can be used without entering a sudo password)

Narrat avatar Dec 31 '17 17:12 Narrat

Tomb currently does not support timer. I don't know if @jaromil likes this idea, but it could be easy to add it in tomb using a cron-job.

@Narrat For the record, I plan to rewrite the timer part in pass-tomb because indeed it is crappy.

roddhjav avatar Dec 31 '17 17:12 roddhjav

@roddhjav: Whatever gets the job done. :) I don't have a problem with systemd based solutions. The issue is more like, it requires distributions which use systemd. The rest is left behind. On the other hand relying on cron is of a similar problem. Distributions with systemd don't necessarily offer a cron implementation installed per default (although this case is easier to solve: installing one). And personally I uninstalled cronie after timer became available.

So whatever is chosen it should be optional (100% sure it would happen like that anyway), and if it is implemented, should it be in the script itself or put into extras/.

Narrat avatar Dec 31 '17 17:12 Narrat

Hello. This would be a nice usability feature indeed.

I see problems with both systemd and cron solutions, mostly related to minimalist design and to what cron is really meant for. FTR in UNIX systems the standard tool to schedule a single event in the future is ATD(8) which may come handy.

In my standard practice when in need to schedule a future event I do not use ATD myself, but use this little python script that runs alarms and other things while showing me a countdown (but can also be launched in background). In my own setup this script is called ding and accepts a human-friendly syntax like ding in 4m to count 4 minutes and then start an alarm.

#!/usr/bin/env python
"""Simple CLI beep tool"""

from __future__ import unicode_literals
from __future__ import print_function

import os
import sys
import time
import datetime


EXIT_MSG = """Invalid arguments: {}\n---------
$ ding at hh[:mm[:ss]]
$ ding in \d+[smh]( +\d+[smh])*

Examples:
    $ ding at 15:30
    $ ding in 5m 30s
"""

VERSION = '1.2.0'
N_BEEPS = 4
WAIT_BEEPS = 0.15


class InvalidArguments(Exception):
    pass


def check_input(args):
    """Validate user input"""
    if len(args) < 2 or args[0] not in ['in', 'at']:
        raise InvalidArguments('insufficient number of arguments')

    if args[0] == 'in':
        if not all(arg.endswith(('s', 'm', 'h')) for arg in args[1:]):
            raise InvalidArguments('please use s/m/h suffixes')
        if not all(arg[:-1].isdigit() for arg in args[1:]):
            raise InvalidArguments('please use numbers for specifying the duration of time units')

    if args[0] == 'at':
        if len(args) > 2:
            raise InvalidArguments('too many arguments')
        if not all([arg.isdigit() for arg in args[1].split(':')]):
            raise InvalidArguments('there should only be numbers optionally separated by ":"')
        # Valid time
        try:
            datetime.time(*map(int, args[1].split(':')))
        except ValueError as e:
            raise InvalidArguments(e)

    return args

class TimeParser():
    """Class helping with parsing user provided time into seconds"""
    time_map = {
        's': 1,
        'm': 60,
        'h': 60 * 60,
    }

    def __init__(self, time, relative):
        self.time = time
        self.relative = relative

    def get_seconds(self):
        return self._get_seconds_relative() if self.relative else self._get_seconds_absolute()

    def _get_seconds_relative(self):
        return sum([self.time_map[t[-1]] * int(t[:-1]) for t in self.time])

    def _get_seconds_absolute(self):
        now = datetime.datetime.now()
        user_time = (datetime.datetime.combine(datetime.date.today(),
                                               datetime.time(*map(int, self.time[0].split(':')))))
        return ((user_time - now).seconds if user_time > now
                else (user_time + datetime.timedelta(days=1) - now).seconds)


def print_time(seconds):
    """Print countdown for `seconds`"""
    while seconds > 0:
        start = time.time()
        os.system('cls' if os.name == 'nt' else 'clear') # accommodate Windows
        print(datetime.timedelta(seconds=seconds))
        seconds -= 1
        time.sleep(1 - time.time() + start)


def beep(seconds):
    """Make the beep noise"""
    for _ in range(N_BEEPS):
        # sys.stdout.write('\a')
	os.system('beep')
        sys.stdout.flush()
        time.sleep(WAIT_BEEPS)


def parse_time(args):
    """Figure out the number of seconds to wait"""
    relative = True if args[0] == 'in' else False
    user_time = args[1:]
    parser = TimeParser(user_time, relative)
    return parser.get_seconds()


def main(args=sys.argv[1:]):
    if args and args[0] == '--version':
        print(VERSION)
        sys.exit()
    try:
        seconds = parse_time(check_input(args))
    except InvalidArguments as e:
        sys.exit(EXIT_MSG.format(e))
    print_time(seconds)
    beep(seconds)

if __name__ == '__main__':
    main()

jaromil avatar Jan 02 '18 10:01 jaromil

Also see in cryptsetup 2.0:

* Deferral removal

  Cryptsetup now can mark device for deferred removal by using a new option
  --deferred. This means that close command will not fail if the device is still
  in use, but will instruct the kernel to remove the device automatically after
  use count drops to zero (for example, once the filesystem is unmounted).

from: https://www.kernel.org/pub/linux/utils/cryptsetup/v2.0/v2.0.0-rc0-ReleaseNotes

jaromil avatar Jan 02 '18 15:01 jaromil

Well, it seems cryptsetup might be the best solution to add a timer to tomb. We don't need to support systemd or cron, but can we require cryptsetup 2.0 for the timer to work in tomb? What about the distribution that will not have cryptsetup before long?

roddhjav avatar Jan 02 '18 16:01 roddhjav

I suspect this won't solve the issue. Cryptsetup close command is different from losetup -d which will fail anyway until the actual close is done, plus this is not a callback but a kernel-level flag inside dm-crypt. It can be part of the solution of this and another issue I still have with running processes preventing the local loopback device to detach.

jaromil avatar Jan 02 '18 16:01 jaromil

I was travelling due to a family medical issue and got off for some days.

Thanks for your comments and the healthy discussion.

vonpupp avatar Jan 17 '18 19:01 vonpupp