This article introduces how to configure Apache Solr as the full-text search backend for Dovecot. And on top of the recommended Solr configuration, a few tweaks are also applied to improve Chinese, Japanese, Korean, aka. CJK, support.

1. Environment

Ubuntu 18.04 LTS is used in this tutorial and at the time of writing, the latest Dovecot suite in the official repository is 2.2.33.2-1ubuntu4.5.

2. Prerequisites

  1. A runing Dovecot server, of course
  2. Docker, or alternatively Podman
  3. Your favourite text editor

3. Solr Configuration

Solr hasn’t got a DEB distribution but it can be easily set up via Docker. Before we fire it up though, a ‘core’ is needed to be created for Dovecot. If you come from a document-oriented database background, a ‘core’ is similar to a collection, but also holds some additional configurations, e.g. schema, language analysis settings and etc.

$ apt-get install dovecot-solr

# check out https://github.com/docker-solr/docker-solr/tree/909a2f021231e4fa1a2ef7ea77885b1530f80110 for details
$ docker pull solr:7  # currently the suggested version by Dovecot
$ mkdir -p /srv/solr  # this is where we are going to store the data
$ chown 8983:8983 $_  # update UID/GID to make sure Solr can write to it later
$ docker run --rm -it -v /srv/solr:/var/solr -e SOLR_HOME=/var/solr -e INIT_SOLR_HOME=yes solr:7 bash

# in container
$ init-solr-home
$ precreate-core dovecot  # you can change the core name
$ exit

# in host
$ cd /srv/solr/dovecot/conf
$ rm -f schema.xml managed-schema solrconfig.xml

続きを読む

Background

I would like to add crates.io to Firefox Quantum but got an error saying that it cannot add it via https://crates.io/opensearch.xml
So here comes a short note of manually adding a search engine to Firefox Quantum.

Step 1: Extract search.json.mozlz4

Definitions of installed search engines are stored in the file search.json.mozlz4 which is located in your profile folder. Under Linux it’s usually ~/.mozilla/firefox/XXXXXXXX.default. If you’re unsure about it, run firefox -profilemanager (or firefox-developer-edition -profilemanager) to find it out.

To decompress this file, a small Python script MozLz4a compression/decompression utility can do us a favour. Note that as mentioned in the comments, you may need to modify the script a little since Python lz4 has updated its API.

$ python mozlz4a.py -d search.json.mozlz4 search.json
$ cat search.json | json_reformat | tee search.json

続きを読む

KDE Plasma had been taking unbearably long to startup since an update one or two months ago for me. I thought it was caused by a bug of Qt but things turned out that this was not the fix to my problem.

Then I suddenly noticed that actually only the initial start after a boot would be slow, subsequent ones are way faster. So it must be a caching issue. I have mounted my /tmp in memory and moved a bunch of hot files into it and all of them will be lost after reboot. This is a common approach to make SSDs live longer and improve performance that could be found in many tutorials.

But some caches are designed to be reused after reboot. By default, they are often located in ~/.cache or /var/cache and you have to be really careful when relocating them, e.g. XDG_CACHE_HOME. Many Qt compilation caches are stored in this path and if you erase them after shutdown, it will have to re-compile them again on each boot.

Hence an easy fix here could be:

mkdir -p /tmp/xdg_cache
if [ -d $HOME/.cache/xdg_cache ]; then
    rsync -a $HOME/.cache/xdg_cache/ /tmp/xdg_cache
fi
#!/bin/bash
if [ -d /tmp/xdg_cache ]; then
    rsync --exclude='google-chrome' --exclude='pacaur' -a /tmp/xdg_cache/ $HOME/.cache/xdg_cache
fi

I ignored the caches of Chrome and Pacaur as they are not needed after reboot and usually quite large.

Git automatically calls less if the output is fairly long in the terminal. It is rather useful to avoid terminal history being violated and allows user to search through the output.

However, plain less isn’t the perfect solution. What I (and I believe many others also) want is:

  • Always colored output
  • Scrolling by touchpad or mouse
  • Auto-quit-if-one-screen

less -+F -+X -+S does everything except the last one. But if I remove -+F, there will be no output in case of one-screen; if I remove -+X as well, the output is back but it disables scrolling. I opened a thread in StackExchange but no satisfying answers came up.

So here’s a Bash script I wrote:

#!/bin/bash

# BSD/OSX compatibility
[[ $(type -p gsed) ]] && SED=$(type -p gsed) || SED=$(type -p sed)
[[ -f "$1" ]] && CONTEXT=$(expand < "$1") || CONTEXT=$(expand <&0)
[[ ${#CONTEXT} -eq 0 ]] && exit 0
CONTEXT_NONCOLOR=$( $SED -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[mGK]//g" <<< "$CONTEXT")
LINE_COUNT=$( (fold -w $(tput cols) | wc -l) <<< "$CONTEXT_NONCOLOR" )

[[ $LINE_COUNT -ge $(tput lines) ]] && less -+X -+S -R "$@" <<< "$CONTEXT" || echo "$CONTEXT"

It works fine with Bash. But when things come to Zsh/Fish the case could be more complicated as the prompt itself may take multiple lines. I’m still looking for a proper solution for this.

1. Bluetooth

Ubuntu can now normally pair with MX Anywhere 2 so you don’t have to care about this kind of stuff. Simply use bluetoothctl or GUI to connect it. But after that, open a console, run

hciconfig hci0 sspmode 1
hciconfig hci0 down
hciconfig hci0 up

otherwise it cannot work correctly. Don’t forget to check your adapter address and replace hci0 if needed.

2. Button Binding

  1. Open a console
  2. Run apt -y install xautomation xbindkeys to install dependencies
  3. Run xev | tee mouse.log and a black box will appear
  4. Move cursor into the black box, press all the function buttons in a certain order (don’t forget it!)
  5. Search mouse.log for ButtonPress and you’ll find the “state” and “button” for each of the buttons

For example,

ButtonPress event, serial 40, synthetic NO, window 0x7800001,
    root 0x1c9, subw 0x0, time 1311199, (71,71), root:(1201,100),
    state 0x10, button 9, same_screen YES

this is the scroll left button and the “state” of it is “0x10” and “button” is “9”

Then, edit ~/.xbindkeysrc to specify the bindings, this is my ~/.xbindkeysrc file:

"xte 'keydown Alt_L' 'key Left' 'keyup Alt_L'"
    m:0x10 + b:6
"xte 'keydown Alt_L' 'key Right' 'keyup Alt_L'"
    m:0x10 + b:7
"xte 'keydown Control_L' 'key F10' 'keyup Control_L'"
    m:0x10 + b:2
"xte 'keydown Alt_L' 'key F1' 'keyup Alt_L'"
    m:0x10 + b:9
"xte 'keydown Control_L' 'key F12' 'keyup Control_L'"
    m:0x10 + b:8

Finally, add /usr/bin/xbindkeys to auto-start program list, this depends on the desktop environment you use.

Refers:
http://askubuntu.com/questions/636712/logitech-mx-anywhere-2-mouse-pairs-but-doesnt-do-anything
https://wiki.archlinux.org/index.php/Xbindkeys
https://bbs.archlinux.org/viewtopic.php?id=119636#p938801

Briefly:

Cause: The names of local and remote branches are not the same
Solution: git push remote_repo HEAD:remote_branch OR (suggested) git config push.default upstream

I’m currently working as an intern developer in an eHealth startup. They’re using the hosts from Aptible to provide their services to customers.

Aptible is generally a Docker (heroku) based hosting and every time when you push the code into a certain Git repository, which is corresponding to a container, Aptible will automatically deploy your new contents. And as we’ve got different environments, such as development, pre-production, production, I have to set up multiple remote repositories so that I can apply different Git branches to different applications.

However, despite of multiple remote repositories, names of remote branches are always “master”. So while pushing, I got

error: src refspec master does not match any.
error: failed to push some refs to 'git@example.com:username/whatever.git'

I Googled the error message and the solutions were all “before pushing, commit something in your EMPTY repository”, which was obviously not the reason of my error. Actually, it was caused by branch names mismatching between local and remote repositories. Rather than git push remote_repo master, git push remote_repo HEAD:master should be used, or simply git config push.default upstream to suppress the name check.

php-bencode is a PHP Bencode extension which supports encoding, decoding and editing Bencode strings or files. The previous version of php-bencode, which supports PHP 5, needs PHP-CPP to run while the new one now requires no other external libraries.

The location of the source code remains the same, on both GitHub and my personal TsundereGit:
https://github.com/Frederick888/php-bencode
https://git.tsundere.moe/Frederick888/php-bencode

Written in C/C++, php-bencode gives an unbelievable performance boost for manipulations of Bencode. I’ve run a benchmark through different implements by decoding a Bencode file with 1M sub-nodes in my box (8 cores, 1G memory). The sample file can be downloaded here: Bencode Sample.

Obviously, the time needed to decode the same file is shortened dramatically by using php-bencode. The memory usage is a little higher than the pure PHP library with PHP 7 but the object array can give you real data safety rather than a plain array. (For example, if the original Bencode file contains an empty dictionary or list, data loss may be caused after decoding and re-encoding.) What’s more, because the memory usage gap between php-bencode and other pure PHP libraries goes up with the number of nodes, as the Bencode files we usually need to handle contain only less than 1,000 nodes, the gap can be just ignored.

Still, there’s much work to do. The previous version supported to get/set a node by path and search through the whole tree but they have not been implemented in the new one yet. Additionally, the new php-bencode supports only PHP 7 by now and I’m considering to support PHP 5.6 as well.
続きを読む

昨日久々に自分のGitLabを開けたら、まさか403エラーが起こってしまった。

最近は何の配置をも変更しなかったのに、どうして自分勝手にエラーになってしまうんでしょう。
心当たりというなら、だいたい数日前のLinode Xenシステムアップデートしかあるまい。
でもホストの更新とこっちゲストの方の問題は何の関係もないと思った。

サーバーのログをチェックしたら、こんなのを見つけった:

AH01276: Cannot serve directory /opt/gitlab/embedded/service/gitlab-rails/public/: No matching DirectoryIndex (none) found, and server-generated directory index forbidden by Options directive

それに、この問題はホームページを開ける時にしか起こらなかった。
他のプロジェクトページやユーザーページやには問題がなかった。

配置の問題であれば、一般的には複数のページで問題を起こすべきだ、ただのホームページではない。
それでさきのログをグーグルしたら、やっぱソフト自身のバグだった。詳しいのはApacheのBugzillaに掲載されてる。

一時的な解決として、

a2dismod autoindex

でAutoIndexモジュールを無効化すればいいんだ。
Phusion Passengerは次のリリース(5.0.22)でこの問題を徹底的に解決する予定がある。

The source is now being hosted at both GitHub and my personal TsundereGit.
https://github.com/Frederick888/php-bencode
https://git.tsundere.moe/Frederick888/php-bencode

From Wikipedia
Bencode is the encoding used by the peer-to-peer file sharing system BitTorrent for storing and transmitting loosely structured data.

Apart from the “torrent” files, it is also used to store local data in some BitTorrent clients instead of other lightweight databases such as SQLite. However, if one gets hundreds of torrents, decoding the file would be extremely slow by some pure PHP libraries. This is why I decided to implement a PHP extension to boost the process.

There is an existed extension on Google Code, which you can also find an exportation to GitHub with some patches. But it seems that it can only parse a Bencode into plain array or convert an array back which may cause data loss during the conversion, e.g. empty arrays or lists.

Bencode should be handled by objects. But I can hardly find any introductions about handling objects in a PHP extension (reading the header file may be a better choice but it may cost a little long). Then I found PHP-CPP, which is quite friendly to new PHP extension developers. It’s a pity that it cannot be packed as a PECL package but it would be easy to build a binary package instead.

The development is not finished yet and I’m considering adding more useful features. Also, there may be lots of redundant codes to clean up 😉

“vcgencmd” is a useful command on Raspberry Pi provided by Raspbian.

By taking advantage of this command, users can easily get the temperature of Pi, check the frequency of CPU and etc. There’re many options for this command but it lacks a bash completion script. So I wrote one based on the document by elinux.

Here it is. Place it under /etc/bash_completion.d/. (Download: vcgencmd-complete.zip)

_vcgencmd () {
    local cur prev commands opts_clock opts_volts opts_codec opts_config opts_mem opts_display
    commands=$(vcgencmd commands)
    commands=${commands:10:${#commands}-11}
    commands="${commands//,} commands"
    opts_clock="arm core h264 isp v3d uart pwm emmc pixel vec hdmi dpi "
    opts_clock="45  1    28   42  43  22   25  47   29    10  9    4 ""$opts_clock"
    opts_volts="core sdram_c sdram_i sdram_p"
    opts_codec="H264 MPG2 WVC1 MJPG WMV9"
    opts_config="int str
        arm_freq
        config_hdmi_boost
        core_freq
        disable_commandline_tags
        disable_l2cache
        emmc_pll_core
        force_eeprom_read
        force_pwm_open
        framebuffer_ignore_alpha
        framebuffer_swap
        hdmi_force_cec_address
        over_voltage
        over_voltage_avs
        overscan_bottom
        overscan_left
        overscan_right
        overscan_top
        pause_burst_frames
        program_serial_random
        sdram_freq
        temp_limit"
    opts_mem="arm gpu"
    opts_display="0 1"

    COMPREPLY=()
    cur=$(_get_cword)
    prev=${COMP_WORDS[COMP_CWORD-1]}

    case "$prev" in
        "vcgencmd")
            COMPREPLY=($(compgen -W "$commands" "$cur"))
            ;;
        "measure_clock")
            COMPREPLY=($(compgen -W "$opts_clock" "$cur"))
            ;;
        "measure_volts")
            COMPREPLY=($(compgen -W "$opts_volts" "$cur"))
            ;;
        "codec_enabled")
            COMPREPLY=($(compgen -W "$opts_codec" "$cur"))
            ;;
        "get_config")
            COMPREPLY=($(compgen -W "$opts_config" "$cur"))
            ;;
        "get_mem")
            COMPREPLY=($(compgen -W "$opts_mem" "$cur"))
            ;;
        "display_power")
            COMPREPLY=($(compgen -W "$opts_display" "$cur"))
            ;;
    esac

    return 0
}
complete -F _vcgencmd vcgencmd