Cinan's world

GNU/Linux & free software, howtos, web development, scripts and other geek stuff

Powerline Round 2: Better Tmux

Give to your Tmux power with Powerline. Clone https://github.com/cinan/tmux-powerline, follow instructions and customize tmux.

My powerlined Tmux (and ZSH) looks like this:

Here’s my ~/.tmux.conf:

unbind C-b
bind C-a send-prefix

set -g base-index 1
set -g prefix C-a
set -g terminal-overrides 'xterm*:smcup@:rmcup@'
setw -g mode-mouse on
setw -g aggressive-resize off
set-option -g mouse-select-pane on
set-window-option -g mode-keys emacs
set-option -g visual-bell off
set -g set-titles-string ""

set -g mouse-select-window on
set -g mouse-select-pane on
set -g mouse-resize-pane on

set-option -g status on
set-option -g status-interval 2
set-option -g status-bg colour234
set-option -g status-utf8 on
set-option -g status-justify "left"
set-option -g status-right-length 90
set-option -g status-left ""
set-option -g status-right "#(~/system/misc/tmux-powerline/powerline.sh right)"

setw -g window-status-current-bg default
setw -g window-status-current-fg default
setw -g window-status-bg colour234
setw -g window-status-fg white
setw -g window-status-format '#[bg=colour234]#[fg=white] #I #[bg=default]#[fg=white] #W ⮁'
setw -g window-status-current-format '#[fg=black]#[bg=green] #I #[fg=black]#[bg=green] #W #[bg=colour234]#[fg=green]⮀'

bind-key r source ~/.tmux.conf

Powerline - Nice and Clean Terminal Prompt

I’ve always wanted a sexy terminal prompt (aka $PS1, dear power users). Finally I managed to find it. It’s called powerline-shell. Except nice look it offers git/svn/hg integration as you can see on the screenshot below.

It’s compatible with Bash, Zsh and Fish shells. Installation instructions are on the Github project page.

Add OpenSearch to Your Site

I’m a content Chromium user. One of the most used functions I use every day is searching from address bar (aka omnibox). You know, start writing site’s name, press tab and type search query. How to make this working on your site? Just two things.

Add a line to the <head> section:

<link rel="search" type="application/opensearchdescription+xml" title="Cinan's world" href="http://blog.cinan.sk/opensearch.xml">

Of course, change title and href as you need.

Create new file opensearch.xml in the root of your site. Paste there a few lines:

1
2
3
4
5
6
7
8
9
10
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
  <ShortName>Cinan's World</ShortName>
  <LongName>Cinan's World</LongName>
  <Description>Search in Cinan's World</Description>
  <InputEncoding>UTF-8</InputEncoding>
  <OutputEncoding>UTF-8</OutputEncoding>
  <Image height="16" width="16" type="image/x-icon">http://blog.cinan.sk/favicon.ico</Image>
  <Url type="text/html" method="GET" template="https://www.google.com/search?q=site:blog.cinan.sk {searchTerms}"/>
  <Query role="example" searchTerms="server"/>
</OpenSearchDescription>

Change names, favicon path and url path as you need. That’s all.

Encrypted Remote Backup With Rsync and Dm-crypt: Part 2/2

So, we have secure remote incremental backup solution here. What about data saved on our backup media (server)? I use dm-crypt – the standard device-mapper encryption functionality provided by the Linux kernel. I’ve encrypted my backup partition with an image from my gallery located on my work machine (passphrases could be weak). Learn more about encrypting partitions with a key here. What I need to do before every backup process is to open the encrypted partition. Obviously, after the backup process I close it.

Create encrypted partition

First modprobe kernel module: modprobe dm_mod. We need to create encrypted partition for our sensitive data. Assuming we already have a spare partition you can simply run the command:

cryptsetup -c aes-xts-plain -s 512 luksFormat <volume_to_encrypt> <secret_keyfile>

What does it mean?

  • -c switch: cipher
  • -s switch: key-size in bits
  • volume_to_encrypt: for example /dev/sda9
  • secret_keyfile: path to the keyfile

Mount encrypted partition

Now, here’s my solution how to do this:

open and mount an encrypted partition
1
2
3
4
5
6
7
8
9
scp <path-to-key-file-eg-some-image-or-song-or-something-else>
user-with-sufficient-rights@remote-machine:
&& ssh user-with-sufficient-rights@remote-machine
"cryptsetup luksOpen <path-to-encrypted-partition> <name-of-open-partition>
-d <path-to-key-file> 
&& shred -u -z -n 26 <path-to-key-file> 
&& mkfs.ext4 /dev/mapper/<name-of-open-partition>
&& mount /mnt/somewhere 
&& echo OK"

What does this bloody script mean?

  1. copy the secret key file to user’s home directory. I prefer well-known images which you can find easily on the Internet. If you lose your key file, you won’t be able to decrypt your encrypted partition.
  2. run script over SSH (using an pubkey for verification)
  3. assuming the remote user is properly configured in sudoers file to run cryptsetup; open an encrypted device /dev/<path-to-encrypted-partition> (for example /dev/sda9) and call it for example “no_more_secrets” (name-of-open-partition). Use copied keyfile as a key.
  4. right after opening the encrypted device be sure to remove the secret keyfile (shred command).
  5. if opening the partition for the first time, you need to format it. Of course, you can choose another filesystem.
  6. mount “no_more_secrets” device. This step require adding a similar line to /etc/fstab: /dev/mapper/<name-of-open-partition> /mnt/somewhere ext4 rw,relatime,data=ordered,barrier=0,user,exec,suid,dev,noauto 0 0

All right, now we can access the encrypted partition, read & write data, whatever.

To sum up, there are two different paths to the encrypted devices. First, e.g. /dev/sda9 (path-to-encrypted-partition) is used only for “luksOpen” operation. Opened device is located in /dev/mapper/ directory. This path is in the script above used for mount, umount and mkfs.

Unmount encrypted partition

Just run these commands on remote machine:

unmount and close encrypted partition
1
2
3
4
ssh user-with-sufficient-rights@remote-machine 
"umount /mnt/somewhere 
&& cryptsetup luksClose /dev/mapper/<name-of-open-partition>
&& echo OK"

Simple, isn’t it?

XMPP4R: Get Client’s Complete Roster the Right Way

Connect to Jabber. With XMPP4R, a library for Ruby, it is possible, but not as easy as you could think.

One could ask “Why don’t you use xmpp4r-simple?”. My answer: “I’m not a pussy!”.

There are many little bastards you should know before fetching client’s roster with contacts’ vcard (nickname, full name, avatar, …), status (online, away, …) or status message. This is how I do this task. It works flawlessly.

This is simplified code I use in my chat application. Reading comments might be helpful.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# Enable to see requests and responses between your machine and your jabber 
# server (easy to read XML). Recommended.
Jabber::debug = true

# first, sign in to your jabber account with your Jabber ID and password
client = Jabber::Client.new("someuser@somejabberserver.tld")
client.connect()
client.auth("my password")

# now we are authenticated, let's fetch roster with all information we want

# initialize roster
roster = Jabber::Roster::Helper.new(client)

# request roster from jabber server, wait for response
roster.get_roster()
roster.wait_for_roster()

# now we know your friends' Jabber Ids
roster.items.each do |jid, contact|
  puts "In roster I have: " + jid
end

# we don't know their status and status message, let's do it

# First, add presence callback. If anyone in roster change his/her state 
# (e.g. come online), you will know it.
roster.add_presence_callback do |roster_item, old_presence, new_presence|
  puts "Somebody changed his/her state to " + new_presence.status.to_s
  # new_presence also offers status message and other stuff
end

# To start receiving presence messages from your jabber server, you have to 
# send your presence first. It's really important to send your presence 
# AFTER roster.add_presence_callback, because the callback will not catch 
# every presence message which your jabber server sent to you.
client.send(Jabber::Presence.new.set_type(:available))

# Now you know who is online in your roster. If somebody from your 
# roster won't be caught in roster.add_presence_callback block, then 
# it just means he/she is offline.

# get vcard

roster.items.each do |jid, contact|
  # According to documentation, fetching vcard can take longer time.
  # Use threads if you think it is a good idea.
  Thread.new do
    vcard = Jabber::Vcard::Helper.get(client, jid)
    nickname = vcard && vcard["FN"] || ''
    #get avatar or other information here
  end
end

What I’ve learned from using XMPP4R library in my project – callbacks are good thing and use them.

Colorful Logging in Rails 3.2

The default Rails logger is really messy. Write somewhere logger.debug some_object.inspect and then for an hour search where the goddamn message is in a log file. Fortunately we can format and colorize logger output.

Create config/initializers/log_formatting.rb file and paste this code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class ActiveSupport::BufferedLogger
    def formatter=(formatter)
        @log.formatter = formatter
    end
end

class Formatter
    SEVERITY_TO_COLOR_MAP   = {'DEBUG'=>'33', 'INFO'=>'0;37', 'WARN'=>'36', 'ERROR'=>'31', 'FATAL'=>'31', 'UNKNOWN'=>'37'}

    def call(severity, time, progname, msg)
        formatted_severity = sprintf("%s","#{severity}")

        formatted_time = time.strftime("%Y-%m-%d %H:%M:%S.") << time.usec.to_s[0..2].rjust(3)
        color = SEVERITY_TO_COLOR_MAP[severity]

        if msg.empty?
            "\n"
        else
            "\033[0;37m#{formatted_time}\033[0m [#{formatted_severity}] \033[#{color}m#{msg.strip}\033[0m\n"
        end
    end

end

Rails.logger.formatter = Formatter.new

I found original code here. I’ve just made small changes – adjusted colors and formatting (and removed humorous stuff).

This is how it looks in tail -f log/development.log:

Style HTML Input Placeholders

Sometimes the default light gray placeholder color isn’t suitable for our needs.

It is really easy to fix it, however the workaround doesn’t work with Opera (but who cares, Opera’s burying Presto and moving to Webkit) and Internet Explorer < 10.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/* webkit browsers */
::-webkit-input-placeholder {
  color: #24a396;
  opacity: 1;
}

/* gecko browsers */
::-moz-placeholder {
  color: #24a396;
  opacity: 1;
}

/* IE 10 */
:-ms-input-placeholder {
  color: #24a396;
  opacity: 1;
}

As you can see I also added opacity property to make it look exactly the same in all supported browsers.

You can ask yourself “Why couldn’t I write more space-efficient solution like this?”:

1
2
3
4
5
6
::-moz-placeholder,
::-webkit-input-placeholder,
:-ms-input-placeholder {
  color: #24a396;
  opacity: 1;
}

When a browser doesn’t understand a selector, it invalidates the entire line of selectors. So that’s why.

Result in Firefox:

Encrypted Remote Backup With Rsync and Dm-crypt: Part 1/2

Choose the right tool

Rsync is an ultimate tool for backup purposes. It offers transmitting data remotely and securely over SSH. Also, it offers --link-dest option which guarantees files are not duplicated in a filesystem thanks to hard links. By the way, the same way it does proprietary Apple Time Machine.

Start writing a script

Sure, we will use many more other useful options, not just --link-dest. Here’s a simplified version of my backup script:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/bin/env bash
date=`date "+%Y-%m-%d_%H:%M"`

rsync -av \
 --delete \
 --delete-excluded \
 --compress-level=9 \
 --numeric-ids \
 --rsync-path="sudo rsync" \
 --exclude-from=/root/.rsync/home-cinan \
 --link-dest=/mnt/current-backup/home \
 /home/cinan -e ssh
 sync-user@machine:incomplete_backup-$date/home/

mv incomplete_backup-$date backup-$date && rm -rf current-backup && ln -s backup-$date current-backup

Moving to Octopress - Time to Customize Your Blog

I’ve been a long-time Wordpress user. Easy blogging – want to write a new post? Click there and there and you can start writing. Too easy and boring for hackers. Welcome to Octopress blogging (and reserve a few hours before starting blogging :) ).

It is really helpful if you know Git, HTML and Markdown.

Installing and setup is really easy (the official guide is here).

What will probably take a long time is customization. You can do whatever you want to. No more restrictive wordpress/blogger/…

  1. recommendation: Learn from others. This site is open source. My Git.

  2. recommendation: Read this superb ewal guide. It describes how to remove “blog” slug in URL; how to create categories list; how to use fancybox gallery for images in articles; how to notify Google and Bing about updating your site and some other things.

  3. recommendation: SEO fixes. Here.

  4. recommendation: Setup 404 pages in Nginx. Here

  5. recommendation: Autopublish new articles on Facebook, Twitter and others sites. Sign up for ifttt.com and create a recipe.

  6. recommendation: Add Open Graph meta tags to your source/_includes/head.html. Useful if you want to publish your posts on Facebook or Reddit. These tags will set appearance of links on Facebook (and maybe other sites). Facebook debugger will check your settings.

    This is my source/_includes/head.html (notice “description”, “keywords” and opengraph tags on lines 30-35)”:

source/_includes/head.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
 <!DOCTYPE html>
  <!--[if IEMobile 7 ]><html class="no-js iem7"><![endif]-->
  <!--[if lt IE 9]><html class="no-js lte-ie8"><![endif]-->
  <!--[if (gt IE 8)|(gt IEMobile 7)|!(IEMobile)|!(IE)]><!--><html class="no-js" lang="en"><!--<![endif]-->
  <head>
    <meta charset="utf-8">
    <title>{% if page.title %}{{ page.title }} - {% endif %}{{ site.title }\}</title>
    <meta name="author" content="{{ site.author }}">
    {% capture description %}{% if page.description %}{{ page.description }}{% else %}{{ site.description }}{% endif %}{% endcapture %}
    <meta name="description" content="{{ description | strip_html | condense_spaces | truncate:150 }}">
    {% capture keywords %}{% if page.keywords %}{{ page.keywords }}{% else %}{{ site.keywords }}{% endif %}{% endcapture %}
    <meta name="keywords" content="{{ keywords }}">
  
    <!-- http://t.co/dKP3o1e -->
    <meta name="HandheldFriendly" content="True">
    <meta name="MobileOptimized" content="320">
    <meta name="viewport" content="width=device-width, initial-scale=1">
  
    {% capture canonical %}{{ site.url }}{% if site.permalink contains '.html' %}{{ page.url }}{% else %}{% if page.url contains site.category_dir %}/{% endif %}{{ page.url | remove:'index.html' }}{% endif %}{% endcapture %}
    <link rel="canonical" href="{{ canonical }}">
    <link href="{{ root_url }}/favicon.png" rel="icon">
    <link href="{{ root_url }}/stylesheets/screen.css" media="screen, projection" rel="stylesheet" type="text/css">
    <script src="{{ root_url }}/javascripts/modernizr-2.0.js"></script>
    <script src="{{ root_url }}/javascripts/ender.js"></script>
    <script src="{{ root_url }}/javascripts/octopress.js" type="text/javascript"></script>
    <link href="{{ site.subscribe_rss }}" rel="alternate" title="{{site.title}}" type="application/atom+xml">
    {% include custom/head.html %}
    {% include google_analytics.html %}
  
    <meta property="og:site_name" content="{{ site.title }}">
    <meta property="og:type" content="article">
    <meta property="og:url" content="{{ site.url }}{{ page.url }}">
    <meta property="og:image" content="{{ site.url }}{% if page.cover %}{{ page.cover }}{% else %}{{ site.cover }}{% endif %}">
    <meta property="og:title" content="{% if page.title %}{{ page.title }} - {% endif %}{{ site.title }}">
    <meta property="og:description" content="{{ description }}">
  </head>