2013年5月13日星期一

自製 Ubuntu 13.04/12.10/12.04.2 與 Debian 7.0 測試硬體安裝使用的 USB 隨身碟

為了同時可以在 Legacy BIOS 與 UEFI BIOS 上使用的,首先使用 Ubuntu 12.04.2 以上內建的 usb-creator-gtk 工具來產生一個 Ubuntu 13.04 desktop amd64 的 USB 隨身碟來用。

$ usb-creator-gtk -n -i ubuntu-13.04-desktop-amd64.iso

接下來的動作,只要使用 Ubuntu 13.04 的 Live System (amd64) 就可以了,不用安裝到硬碟上面。

進入 Ubuntu 13.04 Live System (amd64) 之後,執行以下指令。

ubuntu@ubuntu:~$ sudo apt-get install grub-pc-bin grub-efi-amd64-bin                                                                                                                                       
正在讀取套件清單... 完成
正在重建相依關係
正在讀取狀態資料... 完成
grub-pc-bin 已經是最新版本了。
下列【新】套件將會被安裝:
  efibootmgr grub-efi-amd64-bin
升級 0 個,新安裝 2 個,移除 0 個,有 0 個未被升級。
需要下載 0 B/581 kB 的套件檔。
此操作完成之後,會多佔用 2,316 kB 的磁碟空間。
選取了原先未選的套件 efibootmgr。
(讀取資料庫 ... 目前共安裝了 161462 個檔案和目錄。)
解開 efibootmgr(從 .../efibootmgr_0.5.4-4ubuntu1_amd64.deb)...
選取了原先未選的套件 grub-efi-amd64-bin。
解開 grub-efi-amd64-bin(從 .../grub-efi-amd64-bin_2.00-13ubuntu3_amd64.deb)...
執行 man-db 的觸發程式 ...
設定 efibootmgr (0.5.4-4ubuntu1) ...
設定 grub-efi-amd64-bin (2.00-13ubuntu3) ...

將另外一隻 USB 隨身碟插入 USB 埠,執行以下指令。

ubuntu@ubuntu:~$ sudo grub-install --removable --target=i386-pc --root-directory=/media/ubuntu/UsbStick/ /dev/sdc
Installation finished. No error reported.
ubuntu@ubuntu:~$ sudo grub-install --removable --target=x86_64-efi --root-directory=/media/ubuntu/UsbStick/ /dev/sdc
/usr/sbin/grub-probe: error: failed to get canonical path of /cow.
Installation finished. No error reported.

請將以上的 /dev/sdc 與 /media/ubuntu/UsbStick/ 代換成符合實際使用環境上的字串。

接下來將 grub.cfg 放到 /media/ubuntu/UsbStick/boot/grub/ 底下。

最後再將相對應的 ISO 檔案都放到 /media/ubuntu/UsbStick/iso/ 底下就可以了。

之後只要針對 grub.cfg 的內容做調整加入需要的選項即可。

這樣一來就可以在安裝前先測試看看硬體的相容性,再來決定要不要安裝到硬碟裡面。

P.S. Ubuntu 上面有些無線網路的驅動程式是要安裝到硬碟裡面重新啟動才會正常運作的。

2013年3月15日星期五

關於 Thunderbird 在 IMAP 上使用郵件篩選中的自訂標頭的問題

過去一直很困擾這個問題,不過今天找到了一些解決方法,在此記錄一下。

首先來看 thunderbird-17.0.4+build1/mailnews/imap/src/nsImapProtocol.cpp 中的一段程式碼

// read in the accept languages preference
if (prefBranch)
{
  if (!gInitialized)
    GlobalInitialization(prefBranch);

  nsCOMPtr prefString;
  prefBranch->GetComplexValue("intl.accept_languages",
                              NS_GET_IID(nsIPrefLocalizedString),
                              getter_AddRefs(prefString));
  if (prefString)
    prefString->ToString(getter_Copies(mAcceptLanguages));

  nsCString customDBHeaders;
  prefBranch->GetCharPref("mailnews.customDBHeaders",
                          getter_Copies(customDBHeaders));

  ParseString(customDBHeaders, ' ', mCustomDBHeaders);                             
  prefBranch->GetBoolPref("mailnews.display.prefer_plaintext", &m_preferPlainText);
}

這段程式碼裡面使用的是 mailnews.customDBHeaders 但是在使用自訂標頭後是卻是儲存在 mailnews.customHeaders

然後再看一下同一個檔案中的另外一段程式碼

if (!downloadAllHeaders)  // if it's ok -- no filters on any header, etc.
{                                                                                        
  char *headersToDL = nullptr;
  char *what = nullptr;
  const char *dbHeaders = (gUseEnvelopeCmd) ? IMAP_DB_HEADERS : IMAP_ENV_AND_DB_HEADERS;
  nsCString arbitraryHeaders;
  GetArbitraryHeadersToDownload(arbitraryHeaders);
  for (uint32_t i = 0; i < mCustomDBHeaders.Length(); i++)
  {
    if (arbitraryHeaders.Find(mCustomDBHeaders[i], CaseInsensitiveCompare) == kNotFound)
    {
      if (!arbitraryHeaders.IsEmpty())
        arbitraryHeaders.Append(' ');
      arbitraryHeaders.Append(mCustomDBHeaders[i]);
    }
  }
  if (arbitraryHeaders.IsEmpty())
    headersToDL = strdup(dbHeaders);
  else
    headersToDL = PR_smprintf("%s %s",dbHeaders, arbitraryHeaders.get());

  if (gUseEnvelopeCmd)
    what = PR_smprintf(" ENVELOPE BODY.PEEK[HEADER.FIELDS (%s)])", headersToDL);
  else
    what = PR_smprintf(" BODY.PEEK[HEADER.FIELDS (%s)])",headersToDL);
  NS_Free(headersToDL);
  if (what)
  {
    commandString.Append(" %s (UID ");
     if (m_isGmailServer)
      commandString.Append("X-GM-MSGID X-GM-THRID X-GM-LABELS ");
    if (aolImapServer)
      commandString.Append(" XAOL.SIZE") ;
    else
      commandString.Append("RFC822.SIZE");
    commandString.Append(" FLAGS");
    commandString.Append(what);
    PR_Free(what);
  }
  else
  {
    commandString.Append(" %s (UID RFC822.SIZE BODY.PEEK[HEADER] FLAGS)");
  }
}

搭配前一段程式碼一起來看,應該是使用空白當分隔符號來進行不分大小寫的比對,於是我就手動將 mailnews.customHeaders 的內容複製到 mailnews.customDBHeaders 裡面並且去掉了冒號,接著重新啟動 Thunderbird 了。

不過卻發現收件匣當中的郵件還是無法篩選到,於是就注意到前面第二段程式碼最前面的 downloadAllHeaders,想說會不會是因為 IMAP 的關係,之前已經儲存在本地端的郵件的 DB 並沒有儲存這些資訊,應該要設法再讓 Thunderbird 重新抓取郵件的標頭, 於是我就將郵件移往其它的目錄再做篩選,果然就可以正常運作了。

另外我找到了一個似乎有相關的修正 Bug 363238 - saved searches fail for searches on x-headers, r=irving,只是目前它並不存在 Thunderbird 裡面就是了。

2013年3月4日星期一

關於 Bash 的 Redirection 使用的心得

一般常見的方式是把標準輸出 (stdout) 訊息跟標準錯誤 (stderr) 訊息全部都導進一個檔案裡面,例如以下指令:

$ sudo fdisk -l >fdisk.log 2>&1

如果是寫在一個 Shell Script 的檔案裡面,就可以寫成像是下面這樣:

#!/bin/sh

exec >fdisk.log # 0 (stdin) 1 (fdisk.log) 2 (stderr)
exec 2>&1       # 0 (stdin) 1 (fdisk.log) 2 (fdisk.log)

sudo fdisk -l

但是其實我們也可以使用更複雜的 Redirection,從 bash need STDOUT+STDERR in log, and STDERR to its normal destination 改出來的例子:

$ ((sudo fdisk -l 3>&1 1>&2 2>&3- | tee /dev/fd/2) 3>&1 1>&2 2>&3-) | cat >fdisk.log

這個例子的結果是將標準輸出訊息跟標準錯誤訊息都導進 fdisk.log 裡面,然後將標準錯誤訊息顯示出來。

但是它做了什麼事情?

首先是

sudo fdisk -l 3>&1 1>&2 2>&3-

如果直接用 Shell Script 檔案來寫,會看起來像是這樣:

#!/bin/sh

exec 3>&1  # 0 (stdin) 1 (stdout) 2 (stderr) 3 (stdout)
exec 1>&2  # 0 (stdin) 1 (stderr) 2 (stderr) 3 (stdout)
exec 2>&3- # 0 (stdin) 1 (stderr) 2 (stdout) 3 (closed)

sudo fdisk -l

看起來像是

`sudo fdisk -l` --> stdout ` '-> stdout
                |           X
                `-> stderr ' `-> stderr

它將標準輸出訊息跟標準錯誤訊息互換了,接下來

| tee /dev/fd/2
會收到 pipe 符號前面傳來的標準輸出訊息也就是 `sudo fdisk -l` 的 2 (stdout) 會變成 `tee` 的 0 (stdin),然後 `tee` 會將其內容導到 `tee` 的 2 (stderr) 並且顯示在 `tee` 的 1 (stdout) 於是乎 `sudo fdisk -l` 的標準輸出訊息跟標準錯誤訊息都會被導到 stderr 而標準錯誤訊息則被導到 stdout

看起來像是

                                       `tee`
`sudo fdisk -l` --> stdout ` '-> stdout --> stdout
                |           X           `-> stderr
                `-> stderr ' `-> stderr

接下來再經歷一次的標準輸出訊息跟標準錯誤訊息互換

((...) 3>&1 1>&2 2>&3-)

看起來像是

                                       `tee`
`sudo fdisk -l` --> stdout    -> stdout --> stdout ` '-> stdout
                |          ` '          |           X
                |           X           `-> stderr ' `-> stderr
                `-> stderr ' `-> stderr --> stdout

從最後一張圖就可以看出最後的兩個 stdout 分別來自於 `sudo fdisk -l` 的 stdout 跟 stderr 而最後的一個 stderr 還是來自於 `sudo fdisk -l` 的 stderr

最後一步就是透過 `| cat >fdisk.log` 將兩個 stdout 都導進 fdisk.log 檔案就大功告成啦!:D

2012年12月24日星期一

Quick - an installation helper for GNU/Linux.

我自己平時都是使用 Debian/Ubuntu 裡面的 APT 來安裝軟體套件,就連自己寫了一些小軟體也是會放到 PPA 上面,然後再透過 APT 下載回來安裝,但是並不是所有的軟體都有 Open Source 或是有人有準備好 PPA 可以直接透過 APT 來安裝。

於是乎,我花了兩三天的時間製作了這個簡單的工具用來管理這些額外的軟體。

Quick - an installation helper for GNU/Linux.

使用方式就是執行下面這段指令來安裝:

$ wget http://bit.ly/quick-installer -O - | bash -

安裝完成之後要先登出,再重新登入系統。

接下來執行以下指令來安裝更新套件列表:

$ quick update

然後再執行以下指令來安裝軟體套件:

$ quick install sublime-text

安裝後,想要移除掉就執行:

$ quick remove sublime-text

其它使用方式執行以下指令來看說明。

$ quick

2012年11月29日星期四

apt-get install ubuntu-desktop^

為什麼要在 package 名稱後面加上一個 '^' 的符號?

$ sudo apt-get install ubuntu-desktop^

花了一點時間才知道,在名稱後面加上 '^' 會使得名稱本身變成了 task 而非 package

至於系統上有什麼 task 可以使用,則可以透過下面這個指令找出來

$ tasksel --list-tasks

而下面這個指令則可以找出 task 裡面的 packages

$ tasksel --task-packages ubuntu-desktop

Ubuntu 自訂的 Tasks 放在 /usr/share/tasksel/ubuntu-tasks.desc 裡面

可以透過以下指令列出清單

$ grep Task /usr/share/tasksel/ubuntu-tasks.desc
參考資料 Tasksel - Community Ubuntu Documentation

2012年11月2日星期五

淺談 Launchpad 上面的 Bazaar 分支架構

Bazaar 是由維護及贊助 Ubuntu 開發的 Canonical 公司所贊助開發的,它的使用方式比較接近 Subversion,因此在本地端的 Bazaar 分支,就只會有一個分支,如果要管理多個分支則要使用

$ bzr init-repo LOCATION
來建立一個 shared repository 的目錄,然後在底下使用
$ bzr branch FROM_LOCATION [TO_LOCATION]
來建立本地分支。

舉例來說,如果今天要對 betaradio 這個軟體專案進行 Ubuntu 上面的開發工作,一般來說,可以在 lp:betaradio 找到最上游 (也就是這個軟體本身的開發) 上面最新開發中的程式碼。

$ mkdir -p ~/projects/betaradio
$ bzr init-repo ~/projects/betaradio
$ cd ~/projects/betaradio
$ bzr branch lp:betaradio trunk

而 Debian 上面的最新版本則可以在 lp:debian/betaradio 找到,而 Ubuntu 上面的最新版本則可以在 lp:ubuntu/betaradio 找到。

$ bzr branch lp:debian/betaradio debian
$ bzr branch lp:ubuntu/betaradio ubuntu

通常,如果軟體本身不是一開始就是只有讓 Debian/Ubuntu 本身可以使用的話,lp:betaradio 與 lp:debian/betaradio 及 lp:ubuntu/betaradio 的差異就會是為了製作 Debian package 以及針對 Debian/Ubuntu 所做的一些修補。

另外就是 Debian 有分 unstable/testing/stable/old-stable 也就是 codename 為 sid/wheezy/squeeze/lenny 的不同版本,因為 betaradio 沒有進到 stable 及 old-stable,所以目前只會有 lp:debian/wheezy/betaradio 以及對應到 sid 的 lp:debian/betaradio 這兩個分支。

$ bzr branch lp:debian/betaradio sid
$ bzr branch lp:debian/wheezy/betaradio wheezy

而 Ubuntu 目前有在維護及開發的版本是 10.04/11.04/11.10/12.04/12.10/13.04 也就是 codename 為 lucid/natty/oneiric/precise/quantal/raring,因為 betaradio 只有在 oneiric 之後才進去,所以有 lp:ubuntu/quantal/betaradio lp:ubuntu/precise/betaradio lp:ubuntu/oneiric/betaradio 以及對應到開發中 raring 的 lp:ubuntu/betaradio 這四個分支。

$ bzr branch lp:ubuntu/betaradio raring
$ bzr branch lp:ubuntu/quantal/betaradio quantal
$ bzr branch lp:ubuntu/precise/betaradio precise
$ bzr branch lp:ubuntu/oneiric/betaradio oneiric

如果在 Ubuntu 釋出之後的修改,以 precise 為例,則會出現在 lp:ubuntu/precise-updates/betaradio lp:ubuntu/precise-security/betaradio lp:ubuntu/precise-proposed/betaradio lp:ubuntu/precise-backports/betaradio 這幾個分支上面,要看實際上使用到才會產生出來。

只不過 betaradio 目前沒有特別去修正 precise 上面的版本,所以就沒有上面提到的這些分支了,假設今天在有 precise 的更新又跟 security 無關,那麼則會產生出 lp:ubuntu/precise-updates/betaradio 以及 lp:ubuntu/precise-proposed/betaradio 來使用。

$ bzr branch lp:ubuntu/precise-updates/betaradio precise-updates
$ bzr branch lp:ubuntu/precise-proposed/betaradio precise-proposed

常常會有開發者搞不清楚到底應該使用哪一個分支,所以寫了這一篇文章來解釋一下。

2012年10月21日星期日

betaradio v1.5 released

BetaRadio 是一款使用 Vala 程式語言寫出來的網路收音機程式,專門用來收聽台灣的網路電台。

專案網址:
http://code.google.com/p/betaradio/

新功能:
1. 保持單一程式實體

錯誤修正:
1. 修正無法在 gnome session 底下使用
2. 修正記憶體洩漏
3. 修正 GTK+ thread 使用

原始碼下載:
http://code.google.com/p/betaradio/downloads/detail?name=betaradio-1.5.tar.bz2