Create your own dyndns for your home computer

Setup

Some people want to reach their home computer from the internet but find themselves in the following setup:

  • A home computer with a public IP address that can change/is unreliable.
  • A host reachable by a reliable public IP address or even better: a domain name.
  • A router at home that supports port forwarding.
  • No want to register or pay for a dyndns providers.

In such scenarios, we can make the home computer accessible through the public host:

  1. Setup port forwarding on public host so traffic of a port goes to the home computers.
  2. Configure home router to forward traffic of one port to the home computer in the LAN.
  3. Ensure that the system is aware of a public IP address change of the home computer.

Home computer setup

This script is stored on the home computer we want to reach from the public internet, it will update the IP address on the remote computer (which performs the port forwarding).

/home/andre/bin/update-my-ip.sh

#!/bin/bash

# ensures a remote computer gets to know this computer's ip
# to implement an ip forwarding.
#
# this script determines this computer's public ip and compate it
# to an ip address stored in a file on a remote computer.
# if the stored ip is different to the current public ip,
# the new ip will be written into the file on the remote computer.

# configuration of ssh connections to remote host
REMOTE_SSH_USER="remoteUsername"
REMOTE_SSH_PORT="22"
REMOTE_SSH_HOST="example.com"

REMOTE_IP_FILE="ssh-forwarding-ip.txt" # assuming file is in home directory
REMOTE_EXEC="ssh $REMOTE_SSH_USER@$REMOTE_SSH_HOST -p $REMOTE_SSH_PORT"

check_ip() {
    if (echo "$1" | grep -qE '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}'); then
        return 0
    fi
    echo "ERROR: invalid ip: '$1'"
    return 1
}

# resolve public ip and see what is configured on the remote host:
current_ip="$( dig +short myip.opendns.com @resolver1.opendns.com )"
remote_ip="$( $REMOTE_EXEC "cat ~/$REMOTE_IP_FILE" )"
echo -n "$(date '+%F %T') current_ip: $current_ip remote_ip: $remote_ip "
check_ip $current_ip || exit 10 # quit if ip does not look valid
check_ip $remote_ip || exit 20 # quit if ip does not look valid

# compare ips, quit if no change is necessary:
if [ "$current_ip" = "$remote_ip" ]; then
    echo "Nothing to do, remote IP matches current."
    exit 0
fi

# writing new ip to remote file:
$REMOTE_EXEC "
    cp -v $REMOTE_IP_FILE .$REMOTE_IP_FILE-until-$(date +%F_%T) \
    && echo $current_ip > $REMOTE_IP_FILE \
    && echo $REMOTE_IP_FILE written"

To make sure the IP stored on the remote computer is always up to date,
create a cronjob that invokes the script above once a minute on the home computer:

andre@nairobi ~ % crontab -e
# once per minute: maintain the IP of this computer on bytesare.us, for the port forwarding:
* * * * * bash /home/andre/bin/update-my-ip.sh 2>&1 >> /home/andre/log/update-my-ip.log

Public computer setup

PROBLEM

in terminals, the home and end keys are not working when zsh is the shell. for bash it works in the same terminal, so must be possible to use those keys. must have been something that does bash differently than zsh.

INVESTIGATION

the documentation says zsh uses "terminfo" to configure its keybindings:
andre@nairobi ~ % vim /etc/zsh/zshrc
key=(
    Home     "${terminfo[khome]}"
    End      "${terminfo[kend]}"
    Insert   "${terminfo[kich1]}"
    Delete   "${terminfo[kdch1]}"
    Up       "${terminfo[kcuu1]}"
    Down     "${terminfo[kcud1]}"
    Left     "${terminfo[kcub1]}"
    Right    "${terminfo[kcuf1]}"
    PageUp   "${terminfo[kpp]}"
    PageDown "${terminfo[knp]}"
    BackTab  "${terminfo[kcbt]}"
)
on my system, the khome and kend variables are not set:
andre@nairobi ~ % for k in "${(@k)terminfo}"; do echo $k; done | sort | grep -E '(khome|kend|kich1|kbs|kdch1|kcuu1|kcud1|kcub1|kcuf1|kpp|knp)'
kbs
kcub1
kcud1
kcuf1
kcuu1
kdch1
kich1
knp
kpp
but why? lets see how the terminfo database is built.
andre@nairobi /etc/terminfo % cat README
This directory is for system-local terminfo descriptions. By default,
ncurses will search ${HOME}/.terminfo first, then /etc/terminfo (this
directory), then /lib/terminfo, and last not least /usr/share/terminfo.
let's see what terminfo applies currently. that one we will try to fix.
andre@nairobi /lib/terminfo % echo $TERM
xterm-color
find the config file:
andre@nairobi /lib/terminfo % for i in ${HOME}/.terminfo /etc/terminfo /lib/terminfo /usr/share/terminfo; do echo; echo "$i"; find $i -type f -name "xterm-color" -ls; done

/home/andre/.terminfo
find: `/home/andre/.terminfo': No such file or directory

/etc/terminfo

/lib/terminfo
1049857    4 -rw-r--r--   1 root     root         1569 Sep 17  2014 /lib/terminfo/x/xterm-color

/usr/share/terminfo
check what is set in this terminfo entry:
we can see the same keys as they are listed above.
andre@nairobi ~ % infocmp
============ xterm-color | grep -Eo '(khome|kend|kich1|kbs|kdch1|kcuu1|kcud1|kcub1|kcuf1|kpp|knp)=[^,]*,' | sort
kbs=\177,
kcub1=\EOD,
kcud1=\EOB,
kcuf1=\EOC,
kcuu1=\EOA,
kdch1=\E[3~,
kich1=\E[2~,
knp=\E[6~,
kpp=\E[5~,

SOLUTION

lets see if we can add the missing capability into the terminfo database.
as shown here: https://www.jbase.com/r5/knowledgebase/howto/general/common/CreateTerminfo/modify.htm

we can start with a copy in ${HOME}/.terminfo dir to start experimenting, without modifying system files.

but first we need to find out what are the proper sequences we add.
when pressing left-arrow, right-arrow, bild-auf, bild-ab, home and end using showkey -a, i get this output:
andre@nairobi ~/tmp % showkey -a
Press any keys - Ctrl-D will terminate this program
^[[D     27 0033 0x1b    # left-arrow
^[[C     27 0033 0x1b    # right-arrow
^[[5~    27 0033 0x1b    # page up
^[[6~    27 0033 0x1b    # page down
^[[H     27 0033 0x1b    # home
^[[F     27 0033 0x1b    # end
looks like we can add the entries of home and end like this.
e.g. Left-Arrow cursor key:
output of showkey -a:              "^[[D"
corresponding entry in dump: "kcub1=\EOD,"
i guess we can replace the "^[[" by "\EO" to obtain the correct values for khome and kend. lets dump the entry and modify it:
andre@nairobi ~ % cd ~/tmp/
andre@nairobi ~/tmp % infocmp xterm-color > xterm-color.ti
andre@nairobi ~/tmp % cp xterm-color.ti xterm-color.ti.orig
andre@nairobi ~/tmp % vim xterm-color.ti
the keys in the dump file are sorted, lets add it in order:
for Home: add "khome=\EOH, " between "kfnd=\E[1~, kich1=\E[2~":
for End:  and "kend=\EOF, "  between "kdch1=\E[3~, kf1=\E[11~,":
in my case, the modifications were:
andre@nairobi ~/tmp % diff xterm-color.ti xterm-color.ti.orig
15c15
<       kdch1=\E[3~, kend=\EOF, kf1=\E[11~, kf10=\E[21~, kf11=\E[23~,
---
>       kdch1=\E[3~, kf1=\E[11~, kf10=\E[21~, kf11=\E[23~,
20c20
<       kfnd=\E[1~, khome=\EOH, kich1=\E[2~, kmous=\E[M, knp=\E[6~, kpp=\E[5~,
---
>       kfnd=\E[1~, kich1=\E[2~, kmous=\E[M, knp=\E[6~, kpp=\E[5~,
compile the .ti file and check if it was created in the users terminfo:
andre@nairobi ~/tmp % mkdir -p ${HOME}/.terminfo/x
andre@nairobi ~/tmp % tic xterm-color.ti
andre@nairobi ~/tmp % find ~/.terminfo -type f -ls
1329052    4 -rw-r--r--   1 andre    andre        1443 Mar 18 13:20 /home/andre/.terminfo/x/xterm-color
make sure you have this in your .zshrc or /etc/zsh/zshrc:
# put cursor to end of line when end is pressed
bindkey "${key[End]}" end-of-line

# put cursor to start of line when home is pressed
bindkey "${key[Home]}" beginning-of-line
start a new xterm (! note, not only a new shell in the same terminal) and check if the keys are now present:
andre@nairobi ~ % infocmp xterm-color | grep -Eo '(khome|kend|kcub1)=[^,]*,' | sort
kcub1=\EOD,
kend=\EOF,
khome=\EOH,
now, check if your home and end keys are finally working! !!!!! YES, AFTER SO MANY YEARS !!!!!
Acer TravelMate 290 - How to install acerhk to make the wlan switch working in ubuntu? http://wiki.ubuntuusers.de/Acer_Hotkeys lets try this with 10.04/10.10: http://forum.ubuntuusers.de/topic/acer-travelmate-291lci-funknetzwerk-nicht-akti/2/#post-2670605 # install ubuntu 10.04, then follow these steps: add-apt-repository ppa:gruenertee/acerhk apt-get install --reinstall linux-headers-$(uname -r) build-essential acerhk-source tar -xvjf acerhk.tar.bz2 vim /usr/src/linux-headers-$(uname -r)/Makefile # look for this near line 564: ifdef CONFIG_FUNCTION_TRACER # KBUILD_CFLAGS += -pg <------ comment this line endif cd /usr/src/modules/acerhk make cp -v acerhk.ko /lib/modules/$(uname -r)/kernel/ubuntu/ depmod -a # a good idea is to look at the log files in a second terminal during next step: find /var/log -type f | grep -v -e ".gz$" -e ".[0-9]*$" | xargs tail -fqn # enable wifi module modprobe acerhk force_series=290 usedritek=1 verbose=1 echo 1 > /proc/driver/acerhk/wirelessled

After almost every holiday i find myself in the same problem:

I have made pictures with multiple cameras, phones and maybe there are some pics sent by friends.
Every camera has its own pattern how the files are named, if you want to watch them chronologically, you can sort them by hand or you use the jpeg timestamp and some lines of shell code:

#!/bin/bash

TARGET="/tmp/chronosort-`date +%F`-$RANDOM"
mkdir "$TARGET"
echo "target directory: $TARGET"


find . -type f | while read PIC; do
    echo PIC: ${PIC}
    if [ -f "$PIC" ]; then # see if PIC is a file
        if [[ "$(file -b --mime-type "$PIC" )" == "image/jpeg" ]]; then
            TIMESTAMP_STRING=$(exiv2 "$PIC" | grep -i timestamp | grep -Eo "[0-9].*[0-9]")
#            echo "timestamp string from exif data: $TIMESTAMP_STRING"

            # convert the exif string to a date:
            DATE="$(
                date -d "$(
                    echo ${TIMESTAMP_STRING} \
                    | sed 's|\([0-9]*\):\([0-9]*\):\([0-9]*\) \([0-9]*\):\([0-9]*\):\([0-9]*\)|\1/\2/\3 \4:\5:\6|g'
                )"
            )"

            # apply offset for specific camera model (optional)
            if ( exiv2 "$PIC" | grep "Camera" | grep -qi "casio" ); then
                echo modifying time for casio camera...
#                DATE="$( date -d "$DATE -31 minutes" )"
            fi
            echo "DATE: ${DATE}"


            DATE_FILENAME=$( date -d "$DATE" "+%Y-%m-%d_%H:%M:%S" )
            TARGET_NAME="${DATE_FILENAME}__$(basename "$PIC" )"
            TARGET_FILE="$TARGET/$TARGET_NAME"
#            echo TARGET_FILE: $TARGET_FILE

            # create dir if needed:
            mkdir -pv "$(dirname "$TARGET_FILE" )"

            cp -v "$PIC" "$TARGET_FILE"
        else
            echo "Not a file, skipping $PIC....."
        fi
        echo
    fi
done

Debian package manager: apt-get / dpkg / aptitude

Show if a package "xbmc" is installed:
andre@nairobi ~ % dpkg -s xbmc | grep -i version
Version: 2:11.0~git20120510.82388d5-1

andre@nairobi ~ % aptitude search xbmc -F "%c %p %d %V" | grep -E "^i"
i xbmc                       XBMC Media Center (arch-independent  2:11.0~git2012
i xbmc-bin                   XBMC Media Center (binary data packa 2:11.0~git2012

andre@nairobi ~ % aptitude versions xbmc | grep -EB1 "^i"
Package xbmc:
i   2:11.0~git20120510.82388d5-1                  stable                    500
--
Package xbmc-bin:
i A 2:11.0~git20120510.82388d5-1+b1               stable                    500

Run a filesystem check manually

Check the partition sda1:
root@sysresccd / % fsck -c -v -f -p /dev/sda1

Read a damaged DVD to image file:

See also ddrescue manual

Using a logfile is important, you may continue the process and even read a broken dvd with multiple drives.
root@nairobi /tmp/ # ddrescue /dev/sr0 image.iso
    ddrescue.log

Mount a cd/dvd iso image on filesystem

root@nairobi /home/andre # mount -o loop -t iso9660 /path/to/dvd.iso /path/to/mountpoint

Merge multiple .VOB files to a single video file:

Without compression or encoding, just concatenate:
andre@nairobi /tmp/video-dvd % avconv -i 'concat:VTS_01_1.VOB|VTS_01_2.VOB|VTS_01_3.VOB|VTS_01_4.VOB|VTS_01_5.VOB' -acodec copy -vcodec copy merged-output.mpeg

List the last modified file in a folder:

Works only with zsh, not bash.
"." stands for files (optional, other qualifiers: "@" - links, "/" - dirs)
"om" means order by modification date
"[1]" selects the first entry from the list
andre@nairobi ~ % ls -l ~/Downloads/*(.om[1])
-rw-r--r-- 1 andre andre 34796931 Jul 29 18:49 /home/andre/Downloads/software-dlan-cockpit-linux-v4-2-3.run

Add an existing group to an existing users supplementary groups

The group vboxsf will be added to tester's suppl. groups:
root@testbox ~ % usermod -a -G vboxsf tester
root@testbox ~ % groups tester
tester: tester cdrom sudo vboxsf

Sort images by exif metadata timestamp

Creates a links for each parseable file of current directory into /tmp/cronolinks.
The link name will be like timestamp + "__" + originalfilename, so you can sort it easily with your file explorer.
# prepare targetdir:
andre@nairobi ~ % mkdir /tmp/cronolinks
andre@nairobi ~ % rm -fr /tmp/cronolinks/* 

# go to the directory with the images and collect metadata to a tempfile:

andre@nairobi /opt/ipad-photos % exiv2 print *(.) | grep -Ei 'Image timestamp' | sed 's/\s*Image timestamp ://g' > /tmp/chronolinks.txt

# /tmp/chronolinks.txt looks like:
DSCN3020.JPG 2014:07:24 14:18:23
GOPR0145.JPG 2014:07:18 21:09:18
DSCN3023.JPG 2014:07:24 14:34:24
IMG_3950.JPG 2014:07:24 14:46:49


# translate output to shell script using sed and let bash execute it.
andre@nairobi /opt/ipad-photos % cat /tmp/chronolinks.txt | sed 's|^\s*\(\S\S*\)\s\s*\(\S\S*\)\s\s*\(\S\S*\)|cp -v "\1" "/tmp/cronolinks/\2_\3__\1"|g' | bash

# bash will execute commands like:
cp -v "DSCN3056.JPG" "/tmp/chronolinks/2014:07:26_13:29:37__DSCN3056.JPG"
cp -v "GOPR0068.JPG" "/tmp/chronolinks/2014:07:13_14:39:03__GOPR0068.JPG"
cp -v "GOPR0294.JPG" "/tmp/chronolinks/2014:07:27_04:51:20__GOPR0294.JPG"
cp -v "IMG_0019.JPG" "/tmp/chronolinks/2014:07:09_11:03:22__IMG_0019.JPG"
cp -v "IMG_0036.JPG" "/tmp/chronolinks/2014:07:09_19:47:14__IMG_0036.JPG"

Or, use my shell script. Allows to apply offsets by camera model, for example when you have forgotten to adjust the watch on the camera in a different time zone.

#!/bin/bash

TARGET="/tmp/chronosort-`date +%F`-$RANDOM"
mkdir "$TARGET"
echo "target directory: $TARGET"


find . -type f | while read PIC; do
    echo PIC: ${PIC}
    if [ -f "$PIC" ]; then # see if PIC is a file
        if [[ "$(file -b --mime-type "$PIC" )" == "image/jpeg" ]]; then
            TIMESTAMP_STRING=$(exiv2 "$PIC" | grep -i timestamp | grep -Eo "[0-9].*[0-9]")
#            echo "timestamp string from exif data: $TIMESTAMP_STRING"

            # convert the exif string to a date:
            DATE="$(
                date -d "$(
                    echo ${TIMESTAMP_STRING} \
                    | sed 's|\([0-9]*\):\([0-9]*\):\([0-9]*\) \([0-9]*\):\([0-9]*\):\([0-9]*\)|\1/\2/\3 \4:\5:\6|g'
                )"
            )"

            # apply offset for specific camera model (optional, uncomment if needed)
#            if ( exiv2 "$PIC" | grep "Camera" | grep -qi "casio" ); then
#                echo modifying time for casio camera...
#                DATE="$( date -d "$DATE -31 minutes" )"
#            fi
            echo "DATE: ${DATE}"


            DATE_FILENAME=$( date -d "$DATE" "+%Y-%m-%d_%H:%M:%S" )
            TARGET_NAME="${DATE_FILENAME}__$(basename "$PIC" )"
            TARGET_FILE="$TARGET/$TARGET_NAME"
#            echo TARGET_FILE: $TARGET_FILE

            # create dir if needed:
            mkdir -pv "$(dirname "$TARGET_FILE" )"

            cp -v "$PIC" "$TARGET_FILE"
        else
            echo "Not a file, skipping $PIC....."
        fi
        echo
    fi
done

Resize image(s)

Resizes the image original.jpeg to a new file resized.jpeg to a maximum of 900x900 pixel size and allow compression to produce small files for web sites.
andre@nairobi ~ % convert -resize '900x900>' -quality 80 -verbose original.jpeg resized.jpeg
I used this in a one-liner like this to bulk-resize a set of folders into a folder WEB_FORMAT:
andre@nairobi ~ % for i in 2014*; do targetdir="./WEB_FORMAT/$i"; mkdir -p "$targetdir"; for f in "$i"/*; do convert -resize '900x900>' -quality 80 -verbose "$f" "$targetdir/$( basename "$f" )"; done; done;