Tag Archives: script

Remote notifications

This post explains how to get notifications (libnotify) from a remote system. Typically this is useful with an IRC client accessible through SSH.

  • A notification daemon! (dunst, xfce4-notifyd, etc.)
  • socat
  • notify-send
apt-get install socat libnotify-bin

On the client, modify the SSH configuration to introduce two elements:
  • forward a TCP port,
  • execute a local command.

Example entry for ~/.ssh/config:
Host remote-host
Hostname remote-host.gandi.net
RemoteForward 12000 localhost:12000
PermitLocalCommand yes
LocalCommand socat -u tcp4-listen:12000,reuseaddr,fork,bind= exec:$HOME/.local/bin/notify-remote.sh 2>/dev/null &
The fowarded TCP port will be used to netcat notification messages to the local system.

socat is used to bind a port on the local system, it will take the notifcation messages, and write them to the executed shell script notify-remote.sh.

The shell script will then simply call notify-send to display a notification with the default notification daemon.


read line
read line
read line

if [ "$line" = "" ] && [ "$summary" != "" ]; then
[ -x "$(which notify-send)" ] && notify-send -u critical -t "$delay" -- "$summary" "$msg"

Now it is possible to connect to the remote host and "write" notifications:
local$ ssh remote-host
remote-host$ echo -e 'Summary\nBody\n\n' | nc 12000

Integrate into irssi

Copy the irssi script available bellow to get notifications from hilights, and private messages.

Once the script is copied, execute /script load rnotify.pl inside irssi.


# shamelessly copied from http://git.esaurito.net/?p=godog/bin.git;a=blob;f=rnotify.pl
use strict;
use Irssi;
use HTML::Entities;
use vars qw($VERSION %IRSSI);

$VERSION = "0.01";

%IRSSI = (
authors => 'Luke Macken, Paul W. Frields',
contact => 'lewk@csh.rit.edu, stickster@gmail.com',
name => 'rnotify',
description => 'Use libnotify to alert user to hilighted messages',
license => 'GNU General Public License',
url => 'http://lewk.org/log/code/irssi-notify',

Irssi::settings_add_str('misc', $IRSSI{'name'} . '_port', '12000');
Irssi::settings_add_bool('misc', $IRSSI{'name'} . '_if_away', 0);

sub is_port_owner {
my ($port, $uid) = @_;
my $wanted = sprintf("0100007F:%04X", $port);

# XXX linux-specific
open HANDLE, "< /proc/net/tcp" || return 0;
# sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode
my @splitted = split /\s+/;
my $local = $splitted[2];
my $remote = $splitted[3];
my $uid = $splitted[8];

return 1 if $local eq $wanted and $uid == $<;
close HANDLE;
return 0;

sub notify {
my ($server, $summary, $message) = @_;

$message = HTML::Entities::encode($message);
$summary = HTML::Entities::encode($summary);

# echo \ escaping
$message =~ s/\\/\\\\/g;
$summary =~ s/\\/\\\\/g;

my $port = Irssi::settings_get_str($IRSSI{'name'} . '_port');

return if ! is_port_owner($port, $<);

# check for being away in every server?
return if $server->{usermode_away} &&
(Irssi::settings_get_bool($IRSSI{'name'} . '_if_away') == 0);

# XXX test for other means of doing TCP
#print("echo '$summary\n$message\n\n' | /bin/nc $port");
system("echo '$summary\n$message\n\n' | /bin/nc $port &");

#my $pid = open(FH, "|-");
#if( $pid ){
# print FH "$summary\n$message\n\n";
# close(FH) || warn "exited $?";
# exec("/bin/nc $port") || warn "can't exec $!";

sub print_text_notify {
my ($dest, $text, $stripped) = @_;
my $server = $dest->{server};

return if (!$server || !($dest->{level} & MSGLEVEL_HILIGHT));
my $sender = $stripped;
$sender =~ s/^\<.([^\>]+)\>.+/\1/ ;
$stripped =~ s/^\<.[^\>]+\>.// ;
my $summary = "Message on $dest->{target}";
notify($server, $summary, $stripped);

sub message_private_notify {
my ($server, $msg, $nick, $address) = @_;

return if (!$server);
notify($server, "Private message from ".$nick, $msg);

sub dcc_request_notify {
my ($dcc, $sendaddr) = @_;
my $server = $dcc->{server};

return if (!$dcc);
notify($server, "DCC ".$dcc->{type}." request", $dcc->{nick});

Irssi::signal_add('print text', 'print_text_notify');
Irssi::signal_add('message private', 'message_private_notify');
Irssi::signal_add('dcc request', 'dcc_request_notify');

# vim: et


So I pimped up my .screenrc, and since it's been a long time I didn't care about my hardstatus I keep the content here just in case I need it again in a few years...

defscrollback 2048
startup_message off
caption always "%{= Wk}%-w%{= KW}%f%n %t%{-}%+w"
hardstatus off
hardstatus alwayslastline
hardstatus string "%{= ky}[ %H %l ]%=%{= kg}%{+b}[ %n %t ]%-=%{= ky}[ %D %d.%m.%Y %0c ]"

screen -t irssi 0
screen -t mutt 1
screen -t bubbie 2

Xfce 4.8 with Conky

I have been following a short discussion on the IRC channel #xfce regarding an issue with the use of Conky and transparency. I didn't use Conky for a very long time, but since I knew it was possible to have Conky perfectly running, I gave it a shot again and since I did a fresh reinitialization of Xfce on my workstation, I tweaked the configuration file to my need. Now I have it running in the background and I'll most probably keep it.

The configuration I was able to get for a good working Conky window with transparency is bellow. Of course I could tell you which combination doesn't work, with the why, but since there are so many of them I simply put a working one.
own_window yes # create a separate XWindow over the one from Xfdesktop
own_window_type desktop # the window cannot be moved or resized
own_window_hints undecorated,below,sticky,skip_taskbar,skip_pager # make it behave like it belongs to the desktop
own_window_argb_visual yes # true transparency, a compositor has to be active
own_window_argb_value 100 # make the background semi-transparent
double_buffer yes # avoid flickering

Here is a screenshot of the desktop with Conky in the bottom right corner, I made sure there is some I/O activity going on :-)

Xfce with Conky
Now if you want you can steal my .conkyrc file.


I'm sharing a small script that allows to scan IPs against Whois and GeoIP databases. It allows to quickly retrieve the geolocation of the IPs and print statistics, so that you know from where the connections are originating from. The Whois information is stored inside text files named whois.xxx.yyy.zzz.bbb.

You can download the script here.

 • Usage
$ spam-ips.rb --help
Usage: /home/mike/.local/bin/spam-ips.rb ip|filename [[ip|filename] ...]

• First we retrieve some IPs
$ awk '{print $6}' /var/log/httpd/access.log > /tmp/ip-list.txt

• Now we run the script with the list of IPs inside the text file
$ cd /tmp
$ spam-ips.rb ip-list.txt
Scanning 18 IPs... done.
xxx.zzz.yyy.bbb GeoIP Country Edition: IP Address not found
xxx.zzz.yyy.bbb GeoIP Country Edition: BR, Brazil
xxx.zzz.yyy.bbb GeoIP Country Edition: AR, Argentina
xxx.zzz.yyy.bbb GeoIP Country Edition: SE, Sweden
xxx.zzz.yyy.bbb GeoIP Country Edition: CA, Canada
xxx.zzz.yyy.bbb GeoIP Country Edition: US, United States
xxx.zzz.yyy.bbb GeoIP Country Edition: DE, Germany
xxx.zzz.yyy.bbb GeoIP Country Edition: BE, Belgium
xxx.zzz.yyy.bbb GeoIP Country Edition: FR, France
xxx.zzz.yyy.bbb GeoIP Country Edition: NL, Netherlands
xxx.zzz.yyy.bbb GeoIP Country Edition: NO, Norway
xxx.zzz.yyy.bbb GeoIP Country Edition: FI, Finland
xxx.zzz.yyy.bbb GeoIP Country Edition: DE, Germany
xxx.zzz.yyy.bbb GeoIP Country Edition: FR, France
xxx.zzz.yyy.bbb GeoIP Country Edition: FR, France
xxx.zzz.yyy.bbb GeoIP Country Edition: DE, Germany
xxx.zzz.yyy.bbb GeoIP Country Edition: RU, Russian Federation
xxx.zzz.yyy.bbb GeoIP Country Edition: RU, Russian Federation
3 FR, France
3 DE, Germany
2 RU, Russian Federation
1 US, United States
1 NL, Netherlands
1 IP Address not found
1 NO, Norway
1 FI, Finland
1 SE, Sweden
1 CA, Canada
1 BR, Brazil
1 BE, Belgium
1 AR, Argentina
Total: 18

I wrote this script when I noticed Wiki SPAM and concluded that SPAM originated from a single Bot master but of course I was unable to figure out which one. The script can still be useful from times to times.

XTerm as root-tail

The idea behind this title is to use XTerm as a log viewer over the desktop, just like root-tail works. The tool root-tail paints text on the root window by default or any other XWindow when used with the -id parameter.

Using XTerm comes with little advantage, it is possible to scroll into the “backlog” and make text selections. On a downside, it won't let you click through into the desktop, therefore it is rather useful for people without desktop icons for example.

We will proceed with a first simple example, by writing a Shell script that will use the combo DevilsPie and XTerm. The terminals will all be kept in the background below other windows and never take the focus thanks to DevilsPie. DevilsPie is a tool watching the creation of new windows and applies special rules over them.

Obviously, you need to install the command line tool devilspie. It's a command to run in the background as a daemon. Configuration files with a .ds extensions contain matches for windows and rules that are put within the ~/.devilspie directory.

First example

The first example shows how to match only one specific XTerm window.

The DevilsPie configuration:
(is (window_class) "DesktopLog")
(wintype "dock")
(geometry "+20+45")
(opacity 80)
The Shell script making sure devilspie is running, and spawning a single xterm process:
test `pidof devilspie` || devilspie &
xterm -geometry 164x73 -uc -class DesktopLog -T daemon.log -e sudo tail -f /var/log/daemon.log &
NB: You can notice the size of the XTerm window is set through the Shell script while the position is set through the DevilsPie rules file, and there is a simple reason for this. By default XTerm has a size of 80 columns and 24 lines and text with too long lines will be wrapped on the next line. If afterwards you resize the window the wrapped text won't move up and the result will be ugly. Therefore it's better to set the initial size of the terminal correctly.

To try the example, save the DevilsPie snippet inside the directory ~/.devilspie, and download and execute the Shell script. Make sure to quit any previous DevilsPie process whenever you modify or install a new .ds file.

Second example

The second example is a little more complete, it starts three terminals of which one is coloured in black.
(matches (window_class) "DesktopLog[0-9]+")
(wintype "dock")
(opacity 80)
(is (window_class) "DesktopLog1")
(geometry "+480+20")
(is (window_class) "DesktopLog2")
(geometry "+20+20")
(is (window_class) "DesktopLog3")
(geometry "+20+330")
test `pidof devilspie` || devilspie &
xterm -geometry 88x40 -uc -class DesktopLog1 -T daemon.log -e sudo -s tail -f /var/log/daemon.log &
xterm -geometry 70x20 -uc -class DesktopLog2 -T auth.log -e sudo -s tail -f /var/log/auth.log &
xterm -fg grey -bg black -geometry 70x16 -uc -class DesktopLog3 -T pacman.log -e sudo -s tail -f /var/log/pacman.log &

NB: You will probably notice that setting the geometry is awkward, specially since position and size are in two different files, getting it right needs several tweakings.

This blog post was cross-posted to the Xfce Wiki.

CLI tool to review PO files

If there is something annoying about reviewing PO files is that it is impossible. When there are two hundred messages in a PO file, how are you going to know which messages changed? Well, that's the way it works currently for Transifex but there are very good news, first a review board is already available which is a good step forward but second it is going to get some good kick to make it awesome. But until this happens, I have written two scripts to make such a review.

A shell script msgdiff.sh

Pros: tools available on every system
Cons: ugly output, needs template file



# Usage
if test "$1" = "" -o "$2" = "" -o "$3" = ""; then
echo Usage: $0 orig.po review.po template.pot
exit 1

# Merge

# Diff

# Clean up files

$ ./msgdiff.sh fr.po fr.review.po thunar.pot
#: ../thunar-vcs-plugin/tvp-git-action.c:265
-#, fuzzy
msgid "Menu|Bisect"
-msgstr "Différences détaillées"
+msgstr "Menu|Couper en deux"

#: ../thunar-vcs-plugin/tvp-git-action.c:265
msgid "Bisect"
-msgstr ""
+msgstr "Couper en deux"

A Python script podiff.py

Pros: programmable output
Cons: external dependency

The script depends on polib that can be installed with the setuptools scripts. Make sure setuptools is installed and than run the command sudo easy_install polib.

#!/usr/bin/env python
import polib

def podiff(path_po_orig, path_po_review):
po_orig = polib.pofile(path_po_orig)
po_review = polib.pofile(path_po_review)
po_diff = polib.POFile()
po_diff.header = "PO Diff Header"
for entry in po_review:
orig_entry = po_orig.find(entry.msgid)
if not entry.obsolete and (orig_entry.msgstr != entry.msgstr \
or ("fuzzy" in orig_entry.flags) != ("fuzzy" in entry.flags)):
return po_diff

if __name__ == "__main__":
import sys
import os.path

# Usage
if len(sys.argv) != 3 \
or not os.path.isfile(sys.argv[1]) \
or not os.path.isfile(sys.argv[2]):
print "Usage: %s orig.po review.po" % sys.argv[0]

# Retrieve diff
path_po_orig = sys.argv[1]
path_po_review = sys.argv[2]
po_diff = podiff(path_po_orig, path_po_review)

# Print out orig v. review messages
po = polib.pofile(path_po_orig)
for entry in po_diff:
orig_entry = po.find(entry.msgid)
orig_fuzzy = review_fuzzy = "fuzzy"
if "fuzzy" not in orig_entry.flags:
orig_fuzzy = "not fuzzy"
if "fuzzy" not in entry.flags:
review_fuzzy = "not fuzzy"
print "'%s' was %s is %s\n\tOriginal => '%s'\n\tReviewed => '%s'\n" % (entry.msgid, orig_fuzzy, review_fuzzy, orig_entry.msgstr, entry.msgstr)

$ ./podiff.py fr.po fr.review.po
'Menu|Bisect' was fuzzy is not fuzzy
Original => 'Différences détaillées'
Reviewed => 'Menu|Couper en deux'

'Bisect' was not fuzzy is not fuzzy
Original => ''
Reviewed => 'Couper en deux'

Easy remote access with Thunar

Update: This is deprecated in favor of gvfs.

Networking support is a hot topic in Thunar, and the most probable answer to a user will be one of the many FUSE tools. For Samba you can use fusesmb. For fish protocol and the like you can use sshfs. The benefit of a FUSE tool is that your remote data is accessible as it were on your file system. This results in the fact that all your applications can access the data, and not only the applications that implement a special API. There is never a need for high privilege, so every user can mount remote data from machines where they already have access. To unmount a fuse file system use the command “fusermount -u /mount/point”.


Here is a little usage example for fusesmb to show how easy it works.
% fusesmb samba
% ls samba
% ls samba/MASSONNET
% ls samba/MASSONNET/M8T
share music movies cdrom
And if the listed networks are empty or outdated run the fusesmb.cache command:
% fusesmb.cache samba


As a prerequisite use an authentication with keys — there are tons of HOWTOs for that and I just picked up that one — otherwise you will be prompted for your password and this is a no-go for a Thunar custom action, unless you set up ssh-askpass…

Sshfs works as simple as “sshfs user@host: /mount/point” to mount the home directory from user on host. I arranged me a tricky Shell script to ease the mount even more. The script takes the hostname — the SSH server — and no further arguments. The hostname is also used as mount point, so you can basically have one directory with several sub-directories, each named after your remote hosts. If the mount point doesn't exist it is created on the fly.
% sshfs.sh /sshfs/m8t
% sshfs.sh /sshfs/someuser@funkyhost
% sshfs.sh myhost.mine.nu

Thunar's Custom Actions

Thanks to the custom actions in Thunar, you can implement a lot of features on your own. I use the sshfs wrapper script to mount remote hosts. See how easy it is to set the next custom action up:

The custom action simply calls “sshfs.sh %n” with the name of the directory. To unmount, you would create a custom action with the command “fusermount -u %f”.

Use Epiphany to set your xfdesktop background

Hi ladies ang guys,

As of my blog entry auto-update-background-list-for-xfdesktop I used to tell how I update my background with a uniq file in the background list. That worked with a shell script that 1) took as argument an image file 2) updated the background list 3) and reloaded xfdesktop.

I updated that script and put it in my git (git.m8t.mine.nu). It can now take a remote file as argument. That combined with the web browser Epiphany, you can apply a background within the browser.

Download that script and make it executable inside your $PATH.

Now run Epiphany with the extensions then:
  1. go to Tools > Extension... and enable the Actions extension
  2. After the Actions are enabled go to Edit > Actions
  3. Press Add and create an action called "Update xfdesktop background"
  4. And set the command to "set-xfdesktop-image.sh"
  5. Check Images in the Applies to
  6. Click Add.

Now go to my wallpapers gallery, choose a wallpaper (click on it to get the fullsize), and right click it. You can apply it by choosing the item "Update xfdesktop background" in the context menu.

Have fun with my hack around xfdesktop,

Auto update the background list for Xfdestkop

I have written a quick script to update the list of the images for the background in Xfdesktop.

# Update the background.list so the new downloaded wallpapers are available
# to show up in Xfdesktop.
echo "# xfce backdrop list" > $LIST
find $HOME/images/background/ -type f >> $LIST

Note: change in the last line the folder to the backgrounds.

This script can be either putted in an .zshrc file. Or better, if you use Xfce, save the script, under the name update-desktop-list.sh for example, to your personal bin folder (in my case $HOME/.local/bin) which is correctly set in the PATH, and run xfce4-autostart-editor. You can add the command update-desktop-list.sh.

Finally you have to set up Xfdesktop to use the list (located at $LIST) of backgrounds. Go to Settings > Desktop manager, and select the list as the file. If it doesn't exist run the script a first time.