personal blog
git clone git://
Log | Files | Refs | README

commit ed080d90d1a324798a4b48cf6dde7991cb3f12df
parent e9e4c9fec2d55c5dbfbf53cb5da46204970d5417
Author: pyratebeard <>
Date:   Thu, 17 Feb 2022 14:08:41 +0000


Aentry/ | 99+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 99 insertions(+), 0 deletions(-)

diff --git a/entry/ b/entry/ @@ -0,0 +1,99 @@ +I have always been one to make todo lists. My process has changed over the years, from when I used actual paper to using only digital lists. My current setup is a bit hacky but works for me, so I thought I would share it. + +On my PC I started using a [simple script]({target="_blank" rel="noreferrer"} (thanks to z3bra) to add tasks to a file, by default ~/.todo. There is nothing special about this todo file, it is simple plain text. I am aware of the [todo.txt method]({target="_blank" rel="noreferrer"} but after trying it for a while I felt like it was not working for me. I have tried [kanban boards]({target="_blank" rel="noreferrer"} after using similar methods in work, but I didn't find it worked very well for my own personal tasks. + +My personal note and wiki tool of choice is [vimwiki]({target="_blank" rel="noreferrer"} so I tried keeping my todo list in that. This worked well for a while but I couldn't find a quick and comfortable workflow with it. + +Other notable attempts include [Taskwarrior]({target="_blank" rel="noreferrer"} and [calcurse]({target="_blank" rel="noreferrer"}. + +At the moment I am happy with the simple `todo` script. I spend a lot of time in [tmux]({target="_blank" rel="noreferrer"}, so I have added a couple of keybindings to my config. I have one keybind to display the todo list in a tmux popup and another to start a command prompt with a prefix so I can write out a task, close the single quotes, and hit enter. If you're interested the relevant config is +``` +# toggle todo list popup +unbind t +bind t display-popup -w 75 -h 13 ~/bin/todo + +# add task to todo list +unbind T +bind T command-prompt -I "run-shell '~/bin/todo " +``` + +Adjust the width and height of the popup to your preference, and change the path to your script if required. + +This workflow was working quite well when I was at my PC, but what happens when I am not? On my phone I run [termux]({target="_blank" rel="noreferrer"} so I can easily log in to my PC from anywhere (with a VPN) and add an entry to my todo list, but sometimes this process is a bit slow, or I may not have a network connection at all. + +I have [Markor]({target="_blank" rel="noreferrer"} installed for taking notes so I thought I could sync my todo file to my phone to modify in Markor. I didn't want to install a tool such as [syncthing]({target="_blank" rel="noreferrer"} on both my PC and phone for a single file so I started using `rsync` in a script periodically called from `crontab`. I set my `crontab` to push the todo from my PC to my phone at the end of the workday, then pull from my phone before work each workday. + +This worked well as long as I only edited the file on my PC during working hours and on my phone outside of working hours, which is _usually_ the case. I knew it would bite me in the ass at some point though so I started looking at a way to sync them properly. I came across `incron`, which is like `cron` but is triggered by filesystem events instead of at specified times. This looked like a good start, so I installed it on my PC and configured `incrontab` to push the todo file to my phone whenever it is modified. I immediately [hit a bug]({target="_blank" rel="noreferrer"} which caused `incron` to run once and then not run again. + +Disappointed by this I decided to hack together something similar myself using `inotifywait` from the [inotify-tools]({target="_blank" rel="noreferrer"} package. This tool is really easy to use, and is available in termux. I set a script on both my PC and my phone to watch the todo file and `rsync` it to the opposite device if it changes. +``` +#!/bin/sh + +LOCAL_TODO="~/.todo" +REMOTE_TODO="/path/to/markor/todo.txt" +REMOTE_HOST="pyratephone" + +exec inotifywait -e close_wait -m $LOCAL_TODO | while read TODOFILE ; do + rsync $LOCAL_TODO $REMOTE_HOST:$REMOTE_TODO +done +``` + +I daemonised this on my PC and created a service on termux using [termux-services]({target="_blank" rel="noreferrer"}. With the package installed creating a service is straight forward, create a service directory and `run` script +``` +mkdir -p $PREFIX/var/service/todod/log +ln -sf $PREFIX/share/termux-services/svlogger $PREFIX/var/service/todod/log/run +cat >> $PREFIX/var/service/todod/run << EOF +#!/data/data/com.termux/files/usr/bin/sh + +LOCAL_TODO="/path/to/markor/todo.txt" +REMOTE_TODO=".todo" +REMOTE_HOST="pyratepc" + +exec 2>&1 +exec inotifywait -e close_write -m $LOCAL_TODO | while read TODOFILE +do + rsync -e "ssh -i /path/to/sshkey" $LOCAL_TODO $REMOTE_HOST:$REMOTE_TODO +done +EOF +chmod +x $PREFIX/var/service/todod/run +sv start todod +``` + +I immediately hit another issue, the DELETE_SELF file event. When you pass the `-d` flag to the `todo` script to delete a line it uses the command +``` +sed -i "${1}d" $TODOFILE +``` + +Unfortunately this command causes the file to be replaced with a new file, which generates the DELETE_SELF event. This means `inotifywait` sees the original file it was monitoring as deleted and can't monitor the file anymore. It doesn't look at the filename therefore does not recognise that the new todo file is "the same". To overcome this I switched the use of `sed` with `ed`, the `delete` function in the `todo` script now looks like this +``` +delete() { + test -n "$1" || exit 1 + ed $TODO << EOF >/dev/null +${1}d +w +q +EOF +} +``` + +Using `ed` like this means the file is opened, the line deleted, and the file closed causing a CLOSE_WAIT event. You can find my version of the `todo` script on my [git server]({target="_blank" rel="noreferrer"}. + +The same issue occurs with `rsync`, the file is replaced with a new file causing a DELETE_SELF event. The quickest way I thought to fix this was to restart the daemon on the opposite device after the `rsync`. My script now looks like this +``` +#!/bin/sh + +LOCAL_TODO="~/.todo" +REMOTE_TODO="/path/to/markor/todo.txt" +REMOTE_HOST="pyratephone" +DAEMON_RESTART="SVDIR=/data/data/com.termux/files/usr/var/service sv restart todod" + +exec inotifywait -e close_wait -m $LOCAL_TODO | while read TODOFILE ; do + rsync $LOCAL_TODO $REMOTE_HOST:$REMOTE_TODO + ssh $REMOTE_HOST "${DAEMON_RESTART}" +done +``` + +The service `run` script on my phone has a different $DAEMON_RESTART variable, and specifies the IdentityFile like I did with the `rsync` command. + +So now I have a sync of sorts, and the workflow on my PC works well with the tmux keybindings. I expect at some point I will need to consider what happens when I make a change to the file and there is no network connection to the other device but that is a task for another day, it's on the list. +