2015/11/23

QNAP Ubuntu Station: 透過 NAS 上的 HDMI 輸出,使用 Ubuntu 桌面


今年年中敞部門第一項作品 Container Station 發佈後,年底第二項作品也終於要登場,Ubuntu Station。

QNAP NAS 是少數幾個有支援 HDMI 輸出的 NAS 品牌,過去公司開發了 HD Station,HD Station 針對了電視上使用的族群設計並結合一些常用應用程式,像 Kodi,YouTube TV。

Ubuntu Station 也是使用了 NAS HDMI 輸出,但輸出的是 Ubuntu 桌面,藉此讓 QNAP NAS 能有更多輕鬆的客制化方式。以下將對 Ubuntu Station 的功能做介紹。

完整的 Ubuntu 功能

此 Ubuntu 是由 LXC Ubuntu 14.04.3 (約 11 月初的版本) 客制化而成,原先 Ubuntu 可以使用的應用程式或套件管理系統 apt 皆能正常使用,因此過往在 NAS 很難安裝其他自由軟體在 NAS 上的問題也能迎刃而解。下圖是使用指令安裝 VLC 播放器的螢幕截圖:



除了一般的軟體外,QNAP 針對 DKMS (Ubuntu 內動態編譯 kernel module 的機制) 也有做修正;舉例來說若要安裝 VirtualBox ,按照官網說明即可安裝,與一般軟體不同的是,在安裝中會編譯 VirtualBox 需要的 kernel module (vbox*.ko) 並安裝至系統中,QNAP 有針對這個流程優化,下圖即是在 Ubuntu Station 使用 VirtualBox 安裝 Windows 7 的截圖:



整體來說 QNAP 在這裡是儘可能的減少客制化,以提供跟光碟安裝出來相同的使用情境。(其實 QNAP 做了不少 bug fixes)

QTS 網頁控制頁面

在第一次安裝只需在 QNAP NAS App Center 裡點選安裝 Ubuntu Station 即可,開啟 Ubuntu Station 網頁控制頁面,第一次使用時約需下載 700 MB root file system,以及從 Ubuntu repository 下載更新,完成安裝後網頁界面如下圖:



它除了可控制解析度,聲音來源,另外我們提供的網頁界面的 VNC 可直接遠端操作 Ubuntu。

資料匣同步

在 Ubuntu 內也有 /share 資料匣這部份完全跟 NAS shared folder 同步,此外開啟 Ubuntu 檔案總管 (nautilus),我們將 NAS shared folder 直接放在左邊的 Sidebar,讓使用者更容易操作 NAS 上的檔案,如下圖左上視窗,而在 NAS 上的 File Station 也多了個 Ubuntu 類別直接存取 Ubuntu 登入使用者的家目錄,如下圖右下:



藉此,像是 NAS 原本提供的服務,如 Backup Station 將可直接備份 Ubuntu 檔案到遠端電腦。

獨立 IP

原先在 LXC 網路設計是用 veth,這樣的網路架構對於要開 port 給別人存取的服務會很不方便,所以這部分 QNAP 故意改用 bridge 到 default gateway 的網卡,讓 Ubuntu 也能有跟 NAS 同一個網域的 IP。

LXC 原生效能,安全,硬體加速

LXC 提供了接近原生效能,也更安全,我想這 2 點不用多說。靠著原本 LXC 設計的架構 QNAP 授與它直接與 graphic 硬體存取能力,因此原本這張顯卡能提供多少的能力,在 Ubuntu Station 也能提供相同能力,下圖顯示了 glxinfo and VAAPI 的支援狀況:


總結

目前看來 Ubuntu Station 應能在 TS-X53A 推出時一同上市,而其它較新的機種(X51, X53, X70, X71...)應該在 12 月中一同支援。因為小弟家裡是 TVS-471,這台應該能得到更好的支援(誤)


最後我想學跟蛋黃哥一樣躺著說:好~累~喔~

2015/08/17

QNAP Container Station in COSCUP





週六,有幸能在 COSCUP 分享用 QNAP Container Station 打造 CI 環境。投影片如下:



伴隨著即將 release 的 NAS Firmware 4.2,Container Station 也終於能一同發佈了。

Container Station 有許多獨家功能,像是
  1. 同時支援 ARM and x86 平台,最近出的 NAS 都有支援這個功能。
  2. 除了當紅炸子雞 Docker 外,我們還支援 LXC,LXC 更適合做 OS 的虛擬化。
  3. 自動偵測 Docker Image 裡的設定,大部分 Image 不用多做設定就能直接使用。
除此之外,為了廣大的開發者我們還提供了這些實用功能:
  1. 一鍵架設 Docker Registry,相信真的在用 Docker 的人都需要這個服務。
  2. Container Station 也可以存取已經架好的 Private Registry
  3. 用 Docker compose 來架 App Repository,而且也開放格式在 github 上。
  4. 可以輕易自己 fork 我們的 qnap-dev/container-app 來放你自己的 App,UI 上設一下就可擺脫 QNAP 的控制 (雖然我們很希望你提 PR 給我們)
  5. 進階使用者,我們已經為你提供直接存取 docker service 用到的 CA, Cert and Key,照著 UI 提示就行。
  6. 進階使用者,我們也提供了我們的 API,你可以輕易用 API 打造自己的工具。

此外,Container 也能跟原本 QNAP 獨家提供的 Virtualization (qemu-kvm) 也能結合應用。
以下影片是在 docker container 內,跑 qemu-kvm 的虛擬化,你只要給定 ISO 的位置,就可以輕易開出一台虛擬機,還附上了 HTML5 VNC 的功能,這種應用我想目前是拿來做 target under test 是還不錯方便。



最後,LXC 還有一些額外應用,COSCUP 現場,我們的攤位上,2 台電視上的 Ubuntu 是直接用 Container Station 內的 LXC container 輸出的,這個功能也讓你的 NAS 不再只是 NAS,說是台桌機也不為過,實際上我用 TS-471 拿來裝 Steam 玩遊戲,效果很不錯。




最後的最後,我相信我們產品是非常有趣且富競爭力,我們也還在徵人,辦公位址可以在忠孝新生捷運站附近或汐止,有 Python,Go,AngularJS/React 技能的人非常歡迎直接與我聯絡 dorowu_AT_qnap.com。

2013/11/14

使用 GDB 以樹狀方式將函式流程印出





若在 Linux Kernel 內,想追蹤每個函式的呼叫流程,第一個會想到的工具應該是 ftrace;那針對 user space 的程式是否有類似的東西?用 gdb,再加上一些簡單 script 就可以完成。

以 xdotool 為例,舉例來說,xdotool getmouselocation 將會得當前鼠標的位置,正常狀況下輸出結果如下:

$ xdotool getmouselocation
findclient: 65011716
findclient: 65011716
x:2769 y:656 screen:0 window:65011716

若搭配本文將介紹的方法,輸出結果如下:

(gdb) r
Starting program: /usr/bin/xdotool getmouselocation
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
| main at xdotool.c:287
| _start at ??:0
  | __libc_start_main at ??:0
    | xdotool_main at xdotool.c:290
    | xdotool_main at xdotool.c:316
      | args_main at xdotool.c:456
      | args_main at xdotool.c:493
        | xdo_new at xdo.c:89
        | xdo_new_with_opened_display at xdo.c:106
        | xdo_new_with_opened_display at xdo.c:131
          | xdo_enable_feature at xdo.c:2053
        | xdo_new_with_opened_display at xdo.c:140
          | _xdo_populate_charcode_map at xdo.c:1360
            | _keysym_to_char at xdo.c:1390
      | args_main at xdotool.c:509
        | context_execute at xdotool.c:519
        | context_execute at xdotool.c:536
          | cmd_getmouselocation at cmd_getmouselocation.c:3
          | cmd_getmouselocation at cmd_getmouselocation.c:38
            | consume_args at xdotool.c:46
          | cmd_getmouselocation at cmd_getmouselocation.c:40
            | xdo_mouselocation2 at xdo.c:858
            | xdo_mouselocation2 at xdo.c:886
              | xdo_window_find_client at xdo.c:1222
                | xdo_getwinprop at xdo.c:1541
            | xdo_mouselocation2 at xdo.c:889
              | xdo_window_find_client at xdo.c:1222
                | xdo_getwinprop at xdo.c:1541
              | xdo_window_find_client at xdo.c:1241
                | xdo_window_find_client at xdo.c:1222
                  | xdo_getwinprop at xdo.c:1541
                | xdo_window_find_client at xdo.c:1241
                  | xdo_window_find_client at xdo.c:1222
                    | xdo_getwinprop at xdo.c:1541
findclient: 65011716
findclient: 65011716
            | xdo_mouselocation2 at xdo.c:909
              | _is_success at xdo.c:1533
          | cmd_getmouselocation at cmd_getmouselocation.c:48
            | xdotool_output at xdotool.c:583
x:2262 y:689 screen:0 window:65011716
      | args_main at xdotool.c:511
        | xdo_free at xdo.c:144

是不是整個程式內函式呼叫順序都一目瞭然。


快速開始


先安裝 debug symbol,可以參考這裡將 debug symbol 來源設好。 接著安裝這 2 個套件 libxdo2-dbgsym xdotool-dbgsym

$ sudo apt-get install libxdo2-dbgsym xdotool-dbgsym
$ apt-get source xdotool
$ cd path/to/xdotool/source
$ wget https://gist.github.com/fcwu/7466813/raw/69daa7ad2227eb99e66b87e832e04ad8a8798385/supertrace.gdb
$ wget https://gist.github.com/fcwu/7466823/raw/fb9fee4cf987aaa291bef7fb50959e9eb0f5247b/supertrace.py

準備工作完成。把 gdb 跑起來吧!

$ gdb -x supertrace.gdb --args xdotool getmouselocation
(gdb) c
Starting program: /usr/bin/xdotool getmouselocation
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
| main at xdotool.c:287
| _start at ??:0
  | __libc_start_main at ??:0
    | xdotool_main at xdotool.c:290
    | xdotool_main at xdotool.c:316
      | args_main at xdotool.c:456
      | args_main at xdotool.c:493
以下略...


深入一點


GDB 指令檔:supertrace.gdb



裡頭會載入 supertrace.py (本文末),supertrace.py 內,提供了 2 個指令 breakall and supertrace 和 1 個函式 lastbp。breakall 會把所有有 debug info 的函式當做追蹤目標,指令 xdotool 因為安裝了 xdotool-dbgsym 確定有 debug info 外,shared library 的部分可以用 info sharedlibrary 來觀察:

(gdb) info sharedlibrary 
From                To                  Syms Read   Shared Object Library
0x00007ffff7ddaaf0  0x00007ffff7df4eba  Yes (*)     /lib64/ld-linux-x86-64.so.2
0x00007ffff7bd12d0  0x00007ffff7bd5f28  Yes         /usr/lib/libxdo.so.2
0x00007ffff78d7570  0x00007ffff793ffe8  Yes (*)     /lib/x86_64-linux-gnu/libm.so.6
0x00007ffff75b5a70  0x00007ffff763d6c8  Yes (*)     /usr/lib/x86_64-linux-gnu/libX11.so.6
0x00007ffff73971e0  0x00007ffff739a6b8  Yes (*)     /lib/x86_64-linux-gnu/librt.so.1
0x00007ffff6ff3fa0  0x00007ffff71385d0  Yes (*)     /lib/x86_64-linux-gnu/libc.so.6
0x00007ffff6dd0360  0x00007ffff6dd2d88  Yes (*)     /usr/lib/x86_64-linux-gnu/libXtst.so.6
0x00007ffff6bcc9f0  0x00007ffff6bcd318  Yes (*)     /usr/lib/x86_64-linux-gnu/libXinerama.so.1
0x00007ffff69b7350  0x00007ffff69c36d8  Yes (*)     /usr/lib/x86_64-linux-gnu/libxcb.so.1
0x00007ffff67aade0  0x00007ffff67ab918  Yes (*)     /lib/x86_64-linux-gnu/libdl.so.2
0x00007ffff6592740  0x00007ffff659e358  Yes (*)     /lib/x86_64-linux-gnu/libpthread.so.0
0x00007ffff637f490  0x00007ffff63894f8  Yes (*)     /usr/lib/x86_64-linux-gnu/libXext.so.6
0x00007ffff6179d90  0x00007ffff617aae8  Yes (*)     /usr/lib/x86_64-linux-gnu/libXau.so.6
0x00007ffff5f74090  0x00007ffff5f75aa8  Yes (*)     /usr/lib/x86_64-linux-gnu/libXdmcp.so.6
(*): Shared library is missing debugging information.

值得注意的是若是觀察的 library 太多,會因為 debug info 太大,啟動速度變很慢。接著在 supertrace.gdb 裡用了 lastbp 函式取得最後的 breakpoint number,等會設 breakpoint command 會用。

最後,在 breakpoint command 裡,最重要的就是 supertrace 這個命令,它主要會收集 backtrace 的輸出,並拿來跟之前輸出比較,將最後結果印出。



2013/11/12

使用 Arduino 及 Android 自製萬能遙控器 (4) - Arduino on ASUSTOR's NAS and Demo



(圖:在電視前多放了一塊板,還好不會太突兀)

還是稍微提一下前三篇提到的四隻主要程式

  1. web.py
  2. iremote (Android App)
  3. serial_tx.c
  4. ir-tx.cpp

現在只需再將 Arduino 放到 ASUSTOR NAS 上執行就完成了。

因為我在 NAS 已經放了個 Ubuntu 12.04 的環境了,所以需要完成的只剩 serial driver 的部分。Arduino 所要用的 driver 為 cdc-acm.c。在編成 .ko 時,會發生以前曾經遇到的問題 Unknown symbol and disagree about version of,所有的解決方法可參考以前寫的這篇這篇即可。

這次用很髒的方法來解,直接將有問題的函式用它的位址直接取代,如下:

// doro <<
#undef dev_info
#define dev_info(dev, fmt, arg...) _dev_info2(dev, fmt, ##arg)
int (*_dev_info2)(const struct device *dev, const char *fmt, ...) = (void *)0xffffffff8140a280;
int (*dev_warn2)(const struct device *dev, const char *fmt, ...) = (void *)0xffffffff8140a340;
int (*device_create_file2)(struct device *device, const struct device_attribute *entry) = (void *)0xffffffff8140a150;
void (*device_remove_file2)(struct device *dev, const struct device_attribute *attr) = (void *)0xffffffff8140a060;
int (*dev_err2)(const struct device *dev, const char *fmt, ...) = (void *)0xffffffff8140a3a0;
// doro >>

完整程式可參考 cdc-acm.c

最後 demo 影片:用手機操作電視


2013/11/11

使用 Arduino 及 Android 自製萬能遙控器 (3) - Arduino 環境 及 Serial 程式




接下來終於要進入高潮了,這篇會有點長,要介紹的主題包含硬體架設,在 Ubuntu 上開發 Arduino,Arduino 紅外線發送接收程式,及 Ubuntu 的 Serial 程式。


準備硬體



接法及紅外線簡介可參考這篇,非常詳細。其中要注意的是每個人用的紅外線接收零件可能略有不同,請一定要照規格上的腳位接。

以我這裡用的 TL1838




接收部分,接完長這樣



發送時,接完長這樣


(這張圖好像接反了,總之 LED 長腳接 pin 3,短腳接地)

開發環境


安裝 Arduino 開發環境,在 Ubuntu 12.04 上只需

sudo apt-get install arduino arduino-core arduino-mk

此外,還需要紅外線控制(編解碼)的 library Arduino-IRemote

cd /usr/share/arduino/libraries/
sudo git clone https://github.com/coopermaa/Arduino-IRremote IRemote



接收 Go!


選擇 Arduino IDE 的 File —> Examples —> IRemote —> IRecvDump 程式就跑出來了,在前幾行的地方,有個 RECV_PIN 從 11 改成 4,如下:

int RECV_PIN = 4;

程式上傳後,打開內建的 Serial Monitor,再拿著遙控器對著接收器隨便按個幾個鍵,紅外線的編碼即會顥示出來,接下來要做的就是將等會要用到按鍵都按過一輪,並記下來,如下圖:




發送 Go!


我家電視的遙控器是 NEC 32 bits 編碼,並且加入從 serial 讀進指定字元,傳出對應的紅外線:

if ((d = Serial.read()) != -1) {
        switch (d) {
            case '1': code = 0x1CE3807F; break;
            case '2': code = 0x1CE340BF; break;
            case '3': code = 0x1CE3C03F; break;
            case '4': code = 0x1CE320DF; break;
            case '5': code = 0x1CE3A05F; break;
            case '6': code = 0x1CE3609F; break;
            case '7': code = 0x1CE3E01F; break;
            case '8': code = 0x1CE310EF; break;
            case '9': code = 0x1CE3906F; break;
            case '0': code = 0x1CE300FF; break;
            case 'q': code = 0x1CE3708F; break; // volume up
            case 'a': code = 0x1CE3F00F; break; // volume down
            case 'w': code = 0x1CE350AF; break; // channel up
            case 's': code = 0x1CE3D02F; break; // channel down
            case 'e': code = 0x1CE3C837; break; // source
            case 'd': code = 0x1CE3A25D; break; // enter
            case 'p': code = 0x1CE348B7; break; // power
            default: code = 0;
        }
        if (code != 0) {
            irsend.sendNEC(code, 32);
        }

完整的程式可參考 github 上的 ir-tx.cpp


使用指令編繹、上傳


這支程式計畫是要在 NAS 上跑,NAS 上用指令編譯上傳會比較方便,所以若是需要用指令編譯的話,要先準備一隻 Makefile,內容如下:

BOARD_TAG = uno
ARDUINO_PORT = /dev/ttyACM0
ARDUINO_LIBS = IRremote
ARDUINO_DIR = /usr/share/arduino
include /usr/share/arduino/Arduino.mk

.PHONY: minicom

minicom:
    sudo minicom -D ${ARDUINO_PORT} -b 9600 -o

在原本的 Arduino 程式內需加上一些 header,舉例來說之前的 ir-tx.cpp 需在開頭加入:

#include <arduino.h>
#include <hardwareserial.h>

接著在放程式及 Makefile 目錄下:

make  # 編譯
make upload  #上傳
make minicom  # 打開 minicom

即可編譯,上傳。


Serial 程式


在確定用 minicom 傳接都沒問題後,在上一篇內有提到一隻 web.py 裡頭呼叫了外部程式 serial_tx 來傳送 serial 字元給 arduino,這程式請參考 serial_tx.c

若是發生 minicom 可正常傳接收串列資料,但自己的程式不行,可用 stty 來輔助除錯,它可將串列設定全都印出。