Long technical IRC conversation

It’s been a long time ago since I took part in a technical IRC conversation. Today, a few core community members of the Apache MINA project got together in the IRC channel and discussed about providing a way to build an extremely high performance network applicatiion by providing means for zero-copy (close to zero copy to be strict). The discussion progressed purely from technical viewpoint and it was therefore a lot of pleasure for all of us.

Now, what’s remaining is to excute the agreed changes in a branch, review it and merge it back to the trunk. I think coding should be the easist part considering the length of the conversation we had today. 🙂

I was recently very sceptical about and fed up with too long conversation without quick action, but today’s conversation was an exciting experience all thanks to the participients – Emmanuel, Julien and David. Thanks guys, and I hope my proposal will satisfy all of you!

그 다음은 무엇이 되어야 좋을까?

Update: 아직은 조심스럽지만, 의외로 오해가 잘 풀려 계속해서 미나 프로젝트에 공헌할 수 있게 될 것 같습니다. 좀 더 기술적인 논의에 집중할 수 있게 되지 않을까 생각해 봅니다. 그래도 이젠 무엇이 되어야 좋을 지는 생각해 볼만한 문제가 아닌가 싶습니다.

세상엔 원치 않게 서로를 상처입히는 일이 많이 생긴다. 시간이 지나 그 모든 일들에 대한 기억이 희미해질 때가 오기만을 기다리게 된다. 또는 그 사람을 다시는 마주치지 않길 빌거나.

지난 몇 개월 동안 특정 한두 사람과 아파치 미나 프로젝트 관리 위원회 내부에서 다양한 갈등이 있었고, 그 결과 숙고 끝에 미나 프로젝트에 더 이상 참여하지 않기로 마음먹었다. 4년이라는 시간 동안 공들여 쌓아 온 프로젝트를 한 순간에 떠난다는 것이 어쩌면 섣부른 판단일 수 있겠다. 하지만 그만큼 이제는 좀 다른 뭔가를 만들어 볼 때가 온 것이 아닌가 생각이 든다.

물론 그 다른 뭔가는 아마 미나가 가진 근본적인 문제점을 해소한 네트워크 어플리케이션 프레임워크가 될테니, ‘다르다’ 라는 말은 어울리지 않을지 모르겠다. 하지만 긴 역사를 가진 프레임워크를 바꾸는 것보다는 근본적인 부분에서부터 다시 시작하는 것이 어쩌면 덜 피곤한 방법이 아닐까 싶다.

독재를 옹호하고 싶진 않다. 하지만 민주주의는 때때로 너무나 피곤하다. 물론 그들에겐 명백하게 느껴지겠지만, 이번 일이 내 잘못인지 그들의 잘못인지는 모호하다. 내성적인 성격 탓에 아직 협업 능력이 부족하기 때문인지도 모르겠다. 꼭 그렇다기 보다도, 그저 의욕적으로 무언가를 추진하고자 하는 시도가 계속해서 공격당할 때 나는 분노를 느낀다, 라고 표현하는 게 맞는 것 같다.

순수한 기술적 협업은 즐겁다. 미나를 하면서 가장 기뻤던 순간들은 그런 기술적 협업을 통한 더 높은 수준으로의 향상과 서로를 향한 인정이었다. 하지만 어느 한 쪽이 그 외의 무언가를 강요하기 시작하고 서로의 메시지를 감정적으로 해석하기 시작한 뒤로는 그 즐거움을 찾아볼 수 없게 되어버렸다. 나는 그 즐거움을 다시 느끼고 싶다. 하지만 미나 프로젝트에서는 이제 아니다.

한편으로는 지금까지 지나치게 빨리 야심가처럼 달려왔기 때문은 아닌가 싶다. 아파치 커미터, 탑레벨 프로젝트 승격, 프로젝트 관리 위원회 의장, 멤버, 그리고 풀 타임 오픈 소스 개발자. 그 다음은 무엇이 되어야 좋을까? 곰곰히 생각해 볼 시간이다.

PS: 한국인에 대한 미나 지원은 당분간 계속 할 예정이므로 직접 메일 주십시오.

Changing the default sound card automatically in Linux

Many people including me usually use a USB sound card or a USB speaker to enjoy noise-free high-fidelity sound. I simply don’t understand why all the main board manufacturers ship with a built-in sound chipset which just sucks. It’s not an exception for all laptops.

In a non-portable system such as a desktop PC, you usually don’t need to change your default sound card because your USB sound card is always connected. However, it’s a whole different story for a laptop computer. USB sound card is often disconnected and connected again. For example, I connected my USB speaker to the docking station. The expected behaviour is that the default sound card is chosen automatically – the sound system should be reconfigured so that my USB speaker becomes the default sound card when I dock to the docking station.

Currently, there’s no desktop environment that addresses this problem AFAIK, so I wrote a quick and dirty script file that reconfigures the sound system automatically when a new sound card is detected. The script assumes that you are running HAL and DBUS, which are very common in modern Linux distributions.

#!/bin/sh
# Path: /usr/local/bin/alsa-watch

# Exit if running already.
if [ "x`pgrep -of 'alsa-watch'`" != "x$" ]; then
  exit 1
fi

# Configure the sound card to the default.
/usr/local/bin/alsa-reconfigure

# Begin monitering.
{
dbus-monitor --system --monitor "type='signal',path='/org/freedesktop/Hal/Manager',interface='org.freedesktop.Hal.Manager'" | while read -r EVT; do
  echo "$EVT" | egrep -qi "(DeviceAdded|DeviceRemoved)"
  if [ "$?" = '0' ]; then
    read -r EVT_VAL
    echo "$EVT_VAL" | egrep -qi '(alsa_playback|sound_card)_[0-9]+"'
    if [ "$?" = '0' ]; then
      # Reconfigure the sound card if a sound card is plugged in or out.
      /usr/local/bin/alsa-reconfigure
    fi
  fi
done
} &

Another required script is alsa-reconfigure. The following is what I put into the alsa-reconfigure script. I reset the volume level here:

#!/bin/sh
# Path: /usr/local/bin/alsa-reconfigure
# Exit if there's no sound card.
[ -f /proc/asound/cards ] || exit 0
cat /proc/asound/cards | grep -qi 'no soundcard' && exit 0

# Prefer USB audio device to other sound cards.
cat /proc/asound/cards | grep -qi USB-Audio
if [ "$?" == "0" ]; then
  CARD=`cat /proc/asound/cards | grep USB-Audio | head -1 | perl -pi -e "s/\s*([0-9])+.*/\1/"`
  USB='y'
else
  CARD=`cat /proc/asound/cards | head -1 | perl -pi -e "s/\s*([0-9])+.*/\1/"`
  USB='n'
fi

# Update ALSA settings.
echo "pcm.foo {
  type dmix
  slave.pcm "hw:$CARD"
  ipc_key 1024
}

pcm.!default {
  type plug
  slave.pcm "foo"
}

ctl.!default {
  type hw
  card $CARD
}
" > /etc/asound.conf
chmod 644 /etc/asound.conf

# Reset the volume. (optional)
if [ "$USB" == 'y' ]; then
  amixer sset 'PCM' 80% > /dev/null 2>&1
else
  amixer sset 'Master' 30% > /dev/null 2>&1
fi

You could also do something different such as restarting PulseAudio daemon:

#!/bin/sh
# Path: /usr/local/bin/alsa-reconfigure
# Exit if there's no sound card.
[ -f /proc/asound/cards ] || exit 0
cat /proc/asound/cards | grep -qi 'no soundcard' && exit 0

# Prefer USB audio device to other sound cards.
cat /proc/asound/cards | grep -qi USB-Audio
if [ "$?" == "0" ]; then
  CARD=`cat /proc/asound/cards | grep USB-Audio | head -1 | perl -pi -e "s/\s*([0-9])+.*/\1/"`
else
  CARD=`cat /proc/asound/cards | head -1 | perl -pi -e "s/\s*([0-9])+.*/\1/"`
fi

# Update ALSA settings. (optional if your Linux distribution uses PulseAudio by default)
echo "pcm.!default {
  type pulse
}

ctl.!default {
  type pulse
}
" > /etc/asound.conf

pkill -f '(^|/)pulseaudio( |$)' > /dev/null 2>&1
sleep 1
pkill -9 -f '(^|/)pulseaudio( |$)' > /dev/null 2>&1

# Restart PulseAudio daemon
pulseaudio \
  --system --daemonize --high-priority --realtime --log-target=syslog \
  --disallow-module-loading --disallow-exit \
  --resample-method=src-sinc-best-quality --no-cpu-limit -n \
  -L "module-native-protocol-unix auth-anonymous=1" \
  -L "module-native-protocol-tcp auth-ip-acl=192.168.0.0/16;127.0.0.0/8" \
  -L "module-rescue-streams" \
  -L "module-alsa-sink device=hw:$CARD" \

I execute alsa-watch in my /etc/rc.local file and it works perfectly for me. 🙂

Switching from GNOME to XFCE 4.4

I recently switched from GNOME to XFCE + SLiM, which is more light-weight. I am very satisfied with XFCE because of smaller footprint and less unnecessary applications.

However, I have encountered a few small issues.

Problem with opening a URL from Thunderbird

Clicking a URL in Thunderbird doesn’t open a browser at all. I was able to find a solution here. The more elegant and XFCE-friendly solution is to place the following file into /usr/lib/mozilla-thunderbird/defaults/pref/:

# /usr/lib/mozilla-thunderbird-defaults/pref/xfce.js

user_pref("network.protocol-handler.app.http","/usr/bin/exo-open");
user_pref("network.protocol-handler.app.https","/usr/bin/exo-open");
user_pref("network.protocol-handler.app.ftp","/usr/bin/exo-open");

Evince still requires GNOME.

Evince is a great document viewer, but it depends on many GNOME libraries. Therefore, installing Evince again means that the complete switchover to XFCE doesn’t make a sense. Instead, I installed the official Adobe Acrobat Reader as an alternative. (epdfview was also an option, but it lacks some essential features.)

I use QtCurve theme for GTK2, KDE3 and 4. The problem is that acroread is still a 32-bit application, and there’s no libqtcurve.so in Gentoo emul-* packages. The result was working but ugly and inconsistent look.

To make acroread have the same look with other 64-bit GUI applications, I have set up chrooted Gentoo x86 installation, emerged gtk-engines-qtcurve there and copied the 32-bit libqtcurve.so into /usr/lib32/gtk-2.0/2.10.0/engines/.

To be honest, it was painful to build all x86 stuff just for a single shared object, but it is also nice to see all applications has the same look and feel.

Notes applet loses the recent changes.

XFCE Notes applet saves its files a few minutes after keystroke, and it often leads to the loss of recent changes in my to-do list. There’s no workaround other than modifying the source code AFAIK. I hope the save timeout value becomes configurable in the near future.

XWFM doesn’t have a shortcut for cycling windows list backward.

With Metacity, I mapped ‘alt-grave(`)’ to cycle windows list backward. This mapping is very useful when you pressed alt-tab one or two more times by mistake – you can always go backward with just a few more alt-grave instead of cycling the long windows list with many alt-tab keystrokes.

Time Out applet counts down even if there’s no user activity.

XFCE Time Out applet is a good alternative to Workrave, but it doesn’t stop counting down even if I am away from the computer.

Conclusion

I mentioned a few problems I’ve found while I use XFCE 4.4 in this post and provided workarounds for some of them. You might feel that XFCE has a lot of problem from this post, but you got it wrong if you felt so. In my opinion, XFCE is an extremely stable and fast desktop environment with nice features and better customizability.

URL Monitor – Getting Notified When a Web Page is Updated

I’ve been using RSS Generator for a while to generate RSS for web pages which don’t provide RSS. However, the service often goes unreliable probably due to enormous load from various RSS readers. Another caveat was that the URL of the generated RSS is so long that it’s not accepted by the input form of some web-based RSS readers.

So, I rather chose to write a simple shell script which sends me an e-mail message when the web pages in my watch list change. It’s name is ‘URL Monitor’:

#!/bin/sh
# Path: /usr/local/bin/url-monitor

mkdir -p /var/cache/url-monitor
cat /etc/url-monitor.conf | while read -r NAME; do
  read -r URL || exit 1
  read -r INTERVAL || exit 2
  read -r STRIP_REGEX || exit 3
  read -r NEEDLE || exit 4
  read -r REPLACEMENT || exit 5

  if [ -f "/var/cache/url-monitor/$NAME.html" ]; then
    MTIME=`stat --format=%Z "/var/cache/url-monitor/$NAME.html"`
    NOW=`date +%s`
    AGE=$(($NOW - $MTIME))
    if [ $AGE -lt $INTERVAL ]; then
      continue;
    fi
  fi

  wget -q -T 60 -O - "$URL" | perl -pi -e 's/[rn]/ /g' | perl -pi -e "s/$STRIP_REGEX//gi" | perl -pi -e 's/s+/ /g' | perl -pi -e "s/$NEEDLE/$REPLACEMENT/gi" > "/var/cache/url-monitor/$NAME.html.new"

  if [ ! -f "/var/cache/url-monitor/$NAME.html.new" ] || [ `stat --format=%s "/var/cache/url-monitor/$NAME.html.new"` == "0" ]; then
    echo "Failed to fetch - $NAME" >&2
    rm -f "/var/cache/url-monitor/$NAME.html.new"
    touch "/var/cache/url-monitor/$NAME.html"
    exit 6
  fi

  if [ -f "/var/cache/url-monitor/$NAME.html" ]; then
    diff -q "/var/cache/url-monitor/$NAME.html" "/var/cache/url-monitor/$NAME.html.new" > /dev/null 2>&1
    if [ "$?" == "0" ]; then
      rm -f "/var/cache/url-monitor/$NAME.html.new"
      touch "/var/cache/url-monitor/$NAME.html"
      continue
    else
      mv -f "/var/cache/url-monitor/$NAME.html.new" "/var/cache/url-monitor/$NAME.html"
    fi
  else
    mv -f "/var/cache/url-monitor/$NAME.html.new" "/var/cache/url-monitor/$NAME.html"
  fi

  # Send notification
  {
    echo 'From: URL Monitor <[email protected]>'
    echo 'To: Trustin Lee <[email protected]>'
    echo "Subject: $NAME - updated"
    echo 'Content-Type: text/html; charset=euc-kr'
    echo
    cat "/var/cache/url-monitor/$NAME.html"
    echo
  } | sendmail trustin
done

This quick and dirty shell script simply strips out unnecessary part from the fetched web page, caches it and notifies me (local user ‘trustin’) via an e-mail when the newly fetched stuff differs from the cached one. The following is the example configuration file (/etc/url-monitor.conf):

JavaWorld: Featured Tutorials
http://www.javaworld.com/features/index.html
86400
(^.*<div id="toplist">|<p><a class="findmore".*$)
/javaworld/
http://www.javaworld.com/javaworld/
DDJ.com: High Performance Computing
http://www.ddj.com/hpc-high-performance-computing/archives.jhtml
86400
(^.*Feature Articless*-->|<br clear="left">.*$)
/hpc-high-performance-computing/
http://www.ddj.com/hpc-high-performance-computing/
Lono.pe.kr
http://lono.pe.kr/src/
86400
(^.*[[Start]]-->|<!--[[.*$)
/src/
http://www.lono.pe.kr/src/

Each line has the following meaning:

  • 1st line – the subject of the web page
  • 2nd line – revisit interval (in seconds)
  • 3rd line – what to strip out (in regex)
  • 4th line – something to replace (in regex), probably relative URLs
  • 5th line – what you want to replace the expression specified in the 4th line with

Once configured, url-monitor should be executed periodically. I added the following line to my crontab:

# Path: /etc/cron.d/url-monitor.cron
SHELL=/bin/bash
PATH=/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=trustin
HOME=/root

# Run the URL monitor every three minutes
*/3 * * * *  root  /usr/local/bin/url-monitor

As you noticed, it’s very primitive and requires you to modify the script itself to configure certain parameters. However, I think it’s just OK as long as the number of the web pages I have to monitor (read: which doesn’t provide RSS) is small.

나의 메일 관리 원정기

글을 쓰려고 마음을 먹으면 항상 먼저 떠오르는 할 말이 바로 시간이 너무 빠르게 흘러간다는 것이다. 그럴 때마다 내가 뭐 그리 바쁜 위인이라도 되는지 허탈한 웃음이 나오곤 한다. 그런데 요즘에는 정말 하는 일도 없이 시간이 흐르는 것 같다. 늦잠 자랴 아기 돌보랴 식사하고 한숨 돌리랴. 이런 삶의 사치를 누리다 보면 언젠가 이런 밑천도 바닥나는 게 아닌가 싶다. 가뜩이나 절약이라는 것을 잘 모르는 나에겐 그렇다.

그래도 아무 것도 하지 않은 것은 아니고, 남들이 보면 소소하다고 할만한 일들에 상당한 시간을 할애했다. 오늘은 완벽한 메일 체계 구축을 위한 나만의 여정에 대해 이야기하고자 한다.

어느 날 문득 2000년 부터 쌓인 11만여 통의 메일을 폴더를 나누지 않고 회사 내부 메일과 *함께* 관리하고 싶었다. 회사 보안 정책상 GMail과 같은 공용 서비스에 내부 메일을 담을 수 없으므로 자체적으로 메일 서버를 세팅해 IDC 에 입주시켰다. Dovecot IMAP 서버fetchmail을 사용해 하나의 계정에 모든 메시지를 긁어오도록 설정했다.

11만통의 메시지를 긁어 오는 모습을 멍하니 바라보다 보니 엔코딩이나 제목 포매팅에 문제가 있는 것들이 많이 보였다. 그래서 수십여개의 정규표현식과 문자셋 자동인식 모듈, JavaMail API을 이용해 Exim MTA 측에 필터를 설치했다. (처음에는 PERL로 했지만 나중에는 그 규칙이 너무 복잡해져서 Java로 전부 다시 작성했다.) 그 과정에서 메일을 대량으로 받아 오면서 실험하다 보니 일종의 트래픽 초과 덕택에 GMail 계정이 24시간 동안 막히는 사태가 발생했다. 처음엔 어찌나 당황했는지…

이렇게 우여곡절 끝에 받아 온 11만여 통의 메일을 주제나 연도별로 쪼개기가 싫어서 – 이걸 왜 사람이 해야 하나? – 검색 폴더 기능을 이용하기로 했다. 검색 폴더 기능이 지원되는 메일 클라이언트는 Evolution, Claws-mail (mairix를 통해), Thunderbird, Opera 9.5 베타인데, 모두 결격 사유가 있었다. Claws-mail은 mairix라는 외부 어플리케이션을 사용하기 때문에 한계가 있었고, 무엇보다도 IMAP 지원이 너무 부실해서 탈락. Thunderbird는 검색 폴더의 검색 조건이 너무 단순해서 탈락. Evolution은 조건식은 원하는 만큼 복잡하게 설정할 수 있었지만 11만개를 폴더를 열 때마다 다시 처리해서 탈락. 마지막 후보 Opera 9.5 베타는 극도로 강력한 실시간 검색, 검색 폴더, 메일링 리스트 관리 등을 제공하여 너무나 만족스러웠다. 브라우저와 메일 클라이언트, 그리고 IRC 클라이언트까지 하나로 통합되어 있는데다가 메모리 사용량도 적어 금상첨화였지만, 결정적으로 불안정했다. 아아… 지구에는 내 요구를 만족하는 메일 클라이언트는 *아직* 없었다.

한 가지 희망이 있다면 GNOME 2.22와 함께 딸려 오는 Evolution은 그래도 검색 폴더 기능의 성능에 향상이 있을 뿐만 아니라 오히려 메모리 사용량도 더 적다는 것이었다. 하지만 내가 사용하던 Fedora 8에서 GNOME만 2.20으로 유지하고 Evolution만 업그레이드하는 것은 불가능했다. 혹시나 하는 심정으로 Fedora 9 베타를 설치했지만 내 랩탑은 아직 지원이 제대로 되지 않아 많은 부분이 불편해 다시 Fedora 8을 설치하고 말았다.

이쯤 되서 포기했다면 어땠을까 하는 생각도 들지만 언제나 마음에 들 때까지 바꾸고 또 바꾸는게 내 성격인지라, 결국 GNOME 2.20을 쓰면서 Evolution 2.22도 쓸 수 있는 Gentoo Linux로 재설치를 하게 된다. Gentoo Linux는 10번 정도는 깔아 본 적이 있기 때문에 쉽게 세팅을 완료할 수 있었다. 내 속을 태우는 기나긴 컴파일 시간과 잘 잡히지 않는 무선랜 문제에 대한 해결책을 찾는 데 할애한 시간을 제외한다면…

하지만 Evolution 2.22는 기대만큼 빠르지 않았다. 역시 *아직* 지구의 메일 클라이언트들에게 11만통은 무리인 것 같다. Opera 9.5 안정 버전이 아직 나오지 않은 것을 못내 아쉬워하는 순간이었다.

결국 타협책으로, 볼일이 끝난 메일은 ‘Archive’라는 하나의 폴더로 전부 옮기고 나머지 메시지에 대해서만 검색 폴더를 생성하게 되었고, 나름 만족스러웠다. 내가 일일이 ‘Archive’ 폴더로 옮겨야 한다는 것만 제외한다면… 이걸 왜 내가 해야 되는건지.

그런데 이 검색 폴더라는 것에서 제시할 수 있는 검색 조건이라는 것이 우리가 일반적으로 사용하는 메일 필터 규칙 기능에서 제공하는 조건의 종류에 비교하면 그 수가 매우 적다. 예를 들어 Evolution에서는 정규 표현식을 메일 필터 규칙에서는 사용할 수 있지만 검색 폴더에서는 사용할 수 없다. 성능을 위한 나름의 궁여지책이겠거니 이해가 가지만, 근본적으로 인덱싱을 제대로 안해서 사용자에게 불편을 가하고 있는 게 아닌가? Opera 9.5 안정 버전이 아직 나오지 않은 것을 못내 아쉬워하는 *두 번째* 순간이었다.

정규 표현식을 통한 정교한 필터링이 필요했던 나는 결국 검색 폴더를 포기했다. Archive 폴더의 도입 이후 또 한 번의 타협이었다. 어쨌든 메일 필터 규칙을 작성하여 메일이 들어오는 순간 원하는 폴더에 쌓이게 하는 데 성공했고, 필터가 정교하니 나름 검색 폴더의 필요성을 느끼지 못하게 해 주어 그나마 아쉬움은 크지 않았다. 다만 지금까지 돌아온 나의 길이 너무나 멀었다는 사실을 뼈저리게 느끼지 않을 수 없었다.

그런데 여기서 이야기는 끝나지 않는다. Firefox 3 베타와 GNOME 2.22를 쓰고 싶어진 것이다. 여러 의존성 문제 때문에 Evolution 2.22나 GNOME 2.22의 일부 컴포넌트는 Firefox 2를 반드시 필요로 한다. GNOME 2.22의 일부 컴포넌트야 그냥 설치하지 않으면 그만이지만, 지금까지 힘들게 규칙을 설정한 Evolution은 어쩌란 말인가. 여기서 보통 포기하겠지만 나는 좀처럼 포기를 하지 않는 못된 버릇이 있다.

그래서 결국 sieve 라는 서버측에서 사용할 수 있는 메일 필터링 언어를 배웠다. 문법은 아주 간단하고 Dovecat IMAP 서버와 연동도 쉽지만 언어에 대한 깔끔한 문서를 찾기가 힘들고 정규 표현식 매칭 등의 기능을 사용한 예가 없어서 관련 RFC를 직접 읽어야 했다. 몇 번의 실수도 있었지만 결국에는 Evolution에서 작성했던 모든 규칙을 서버 측에서 처리할 수 있게 되었다. 즉, 어떤 메일 클라이언트를 써도 메일 필터링이 이미 다 된 상태로 계층화된 메일함을 열람할 수 있게 된 것이다. 여기까지 다다른