2011/05/19

UDP server over libevent - sample code

基本的sample code

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <event.h>
#include <event2/listener.h>

#define SVR_IP                          "127.0.0.1"
#define SVR_PORT                        10000
#define BUF_SIZE                        1024

void read_cb(int fd, short event, void *arg) {
    char                buf[BUF_SIZE];
    int                 len;
    int                 size = sizeof(struct sockaddr);
    struct sockaddr_in  client_addr;

    memset(buf, 0, sizeof(buf));
    len = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&client_addr, &size);

    if (len == -1) {
        perror("recvfrom()");
    } else if (len == 0) {
        printf("Connection Closed\n");
    } else {
        printf("Read: len [%d] - content [%s]\n", len, buf);
        
        /* Echo */
        sendto(fd, buf, len, 0, (struct sockaddr *)&client_addr, size);
    }
}

int bind_socket(struct event *ev) {
    int                 sock_fd;
    int                 flag = 1;
    struct sockaddr_in  sin;

    /* Create endpoint */
    if ((sock_fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
        perror("socket()");
        return -1;
    }

    /* Set socket option */
    if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(int)) < 0) {
        perror("setsockopt()");
        return 1;
    }

    /* Set IP, port */
    memset(&sin, 0, sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = inet_addr(SVR_IP);
    sin.sin_port = htons(SVR_PORT);

    /* Bind */
    if (bind(sock_fd, (struct sockaddr *)&sin, sizeof(struct sockaddr)) < 0) {
        perror("bind()");
        return -1;
    } else {
        printf("bind() success - [%s] [%u]\n", SVR_IP, SVR_PORT);
    }

    /* Init one event and add to active events */
    event_set(ev, sock_fd, EV_READ | EV_PERSIST, &read_cb, NULL);
    if (event_add(ev, NULL) == -1) {
        printf("event_add() failed\n");
    }

    return 0;
}

int main(void) {
    struct event  ev;

    /* Init. event */
    if (event_init() == NULL) {
        printf("event_init() failed\n");
        return -1;
    }

    /* Bind socket */
    if (bind_socket(&ev) != 0) {
        printf("bind_socket() failed\n");
        return -1;
    }

    /* Enter event loop */
    event_dispatch();

    return 0;
}

2011/05/17

mtrace - 檢查memory leak

Memory leak中文叫內存洩漏,也是我在開發程式中一個很難搞的問題。拜一些tool所賜,讓工程師可以更容易發現問題所在。

mtrace是glibc內提供的工具,其實它的原理很簡單,就是把你程式中malloc()與free()的位址全部下來,最後兩兩配對,殘留下來沒有配對到的就是leak。

1. 安裝glibc-utils

2. 在程式中include header file並在程式最前面call mtrace()
e.g. test.c
#include 
#include 

int main(void) {
    char *p;

    mtrace();
    p = malloc(5);  // 要一塊記憶體,但沒有釋放

    return 0;
}

3. compile
$ gcc -g -o test test.c
一定要加-g

4. run program
$ MALLOC_TRACE=output.log ./test
MALLOC_TRACE指向output file

5. 抓leak
$ mtrace ./test ./output.log
結果:
Memory not freed:
-----------------
           Address     Size     Caller
0x0000000000c2d460      0x5  at /tmp/test.c:8
很清楚看到test.c第8行allocate 5 byte未釋放

--

不過mtrace算是很陽春的工具,如果是間接allocte記憶體,如call object_new()這種init function,那mtrace就沒辦法表示得那麼清楚了。
e.g. test2.c
#include 
#include 
#include 

int main(void) {
    GHashTable *ht;

    mtrace();
    ht = g_hash_table_new(NULL, NULL);

    return 0;
}

執行mtrace結果:
Memory not freed:
-----------------
           Address     Size     Caller
0x0000000012a24460     0xfc  at 0x2b267c24f3b1
0x0000000012a24570    0x1f8  at 0x2b267c24f3b1
0x0000000012a24770    0x1f8  at 0x2b267c24f3b1
0x0000000012a24970    0x7f0  at 0x2b267c24f3b1
0x0000000012a25170     0xc0  at 0x2b267c24f3b1
0x0000000012a25400    0x3f0  at 0x2b267c262ce1
0x0000000012a25800    0x3f0  at 0x2b267c262ce1

這樣的訊息對我們來說沒什麼幫助,這時候可以借助更強大的工具valgrind或heap checker。

2011/05/16

pstack - 列印出process stack trace

有時候遇到process hang住了,我們想知道各thread目前function call stack為何就可以利用pstack。使用前提是binary還保有symbol(還未strip)

用法︰
$  pstack pid

e.g.
$ pstack `pgrep syslog-ng`

Backtrace for pid 21374
A syntax error in expression, near `'.
#0  0x00000034bb6cb14f in poll () from /lib64/libc.so.6
#1  0x0000000000402957 in main_context_poll (ufds=0x9b1920, nfsd=22,
    timeout_=14303) at main.c:134
#2  0x00002b3ba086a90f in g_main_context_poll (context=0x966400, block=1,
    dispatch=1, self=) at gmain.c:3093
#3  g_main_context_iterate (context=0x966400, block=1, dispatch=1,
    self=) at gmain.c:2775
#4  0x00002b3ba086af0b in g_main_context_iteration (context=0x966400,
    may_block=1) at gmain.c:2843
#5  0x0000000000402421 in main_loop_run (cfg=0x7ffff43c6040) at main.c:170
#6  0x00000000004028d9 in main (argc=1, argv=0x7ffff43c6138) at main.c:448
這個訊息跟在gdb下thread apply all bt是一樣的。

下載︰
我只找到Oracol Project: GDB pstack

一些常用的 Linux 指令

1. 查看某個網路service的連線狀態
$ netstat -an | grep 127.0.0.1:5432 | awk '{print $6}' | sort | uniq -c
結果:
10 CLOSE_WAIT
36 ESTABLISHED
 1 LISTEN
33 TIME_WAIT


2. 在top中查看特定的process
$ top -p `pgrep sshd | sed -e :x -e '$!N;s/\n/,/;tx'`
# sed 的作用是把換行字元取代成","
結果︰
PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
2198 root      16   0 90128 3308 2580 S  0.0  0.0   0:02.97 sshd
2212 test      15   0 90228 1880 1104 S  0.0  0.0   0:20.82 sshd
2224 root      16   0 90128 3308 2580 S  0.0  0.0   0:00.03 sshd


3. 監看某一daemon的fd數量
$ watch -n 1 'ls /proc/`pgrep daemon`/fd | wc -l'
結果︰
Every 1.0s: ls /proc/`pgrep daemon`/fd | wc -l            Mon May 16 21:01:04 2011

50


4. sed取代換行字元
$ sed -e :x -e '$!N;s/\n/,/;tx'
或
$ sed '/^.*$/N;s/,/\n/g'


5. 列出server上的服務
$ netstat -ntulp
結果:
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address               Foreign Address             State       PID/Program name
tcp        0      0 0.0.0.0:80                  0.0.0.0:*                   LISTEN      2304/httpd
tcp        0      0 10.32.1.10:22               0.0.0.0:*                   LISTEN      2110/sshd
tcp        0      0 127.0.0.1:25                0.0.0.0:*                   LISTEN      2136/sendmail: acce
tcp        0      0 0.0.0.0:443                 0.0.0.0:*                   LISTEN      2304/httpd
udp        0      0 127.0.0.1:514               0.0.0.0:*                               10575/syslog-ng
udp        0      0 0.0.0.0:34224               0.0.0.0:*                               10614/collectd


6. 查看Linux distribution
$ lsb_release -a
or
$ cat /etc/issue
or
$ cat /proc/version

crontab 自動排程

安排例行性工作需要用到: backup, log rotate, clean tmp...

1. 編輯crontab
$ crontab -e
或
$ vim /etc/crontab

2. 設定執行時間
3  4  *  *  *    root      sh /home/backup.sh
分 時 日 月 週   身份      指令
上述指令為每天半夜4:03備份資料


參考資料︰
鳥哥的 Linux 私房菜 -- 例行性工作排程 (crontab)

在windows下使用NFS - Windows Services for Unix (SFU)

我的desktop使用WinXP的環境,長期以來都是在Windows下寫code,Linux下編譯。剛開始是透過samba來存取linux上的檔案,但samba的效能不知道為什麼這麼不盡理想,editor多開幾個file就開始hang住了,可能是editor會見一些function索引的關係吧?

試過一些samba參數設定,但是都沒有改善。想到NFS協定可以走,但在Windows上找一套free的NFS client實在不容易,大多要付錢而且採用自己的程式介面。最後採用了Windows Services for Unix(SFU) - 這套 Microsoft自己出的軟體。

1. 設定NFS

2. 安裝SFU

3. 將server上的/etc/group與/etc/passwd複製到C:\SFU\etc

4. 開啟Windows Services for UNIX->Services for UNIX Administration
選擇樹狀選單中的User Name Mapping->Configuration標籤
設定Password file與Group file (在剛剛放置的C:\SFU\etc)

5. 選擇Maps標籤
按List Windows Users -> 選擇帳號
按List UNIX Users -> 選擇帳號
按Add

6. 依照NFS設定的目錄掛載
$ mount \\hostname\home\brian\share z:


參考資料:
「轉貼」Windows下Linux开发环境设置(一)--TFTP服务器设置和NFS服务器设置
[Help] Windows mount NFS ?
Optimizing NFS Performance
在 Windows 系統掛載 UNIX NFS 分享目錄

輕鬆架設 NFS (Network file system)

1. 安裝必要工具
nfs-utils
portmap

2. 在server上設定分享目錄
$ vim /etc/exports
加入 /home/brian/share 192.168.0.100(rw,async,anonuid=123,anongid=123)
參數說明:
IP: 只允許192.168.0.100的IP存取
rw: 可讀可寫
async: 非同步存取加快速度
123: 是brian在/etc/passwd的id,也就是你將會在Windows會使用brian這個帳號透過NFS存取存取Linux檔案

2. 重啟NFS
$ /etc/init.d/portmap start
$ /etc/init.d/nfs start

3. 檢視一下分享目錄
$ showmount -e

4. 在其他主機掛載看看
$ mount -t nfs server_hostname:/home/brian/share /mnt/my_nfs


參考資料
鳥哥的 Linux 私房菜 -- NFS 伺服器
柏青哥的 SuSE Linux -- 架設 NFS 及 NIS Server
Optimizing NFS Performance

Excel 凍結窗格

在Office 2007 Excel中,如要在捲動時一部分的欄位固定不動,可以選擇『檢視』->『凍結窗格』。選項中有『凍結最上列』、『凍結最左欄』、『凍結窗格』

e.g.
如果想要freeze上面2列、左邊3欄,則先把游標移到『D3』然後再選『凍結窗格』即可。(按鈕的說明寫的不清楚不楚的)

Session在IE上沒有作用?

遇到一個很瞎的問題,用PHP寫一段session的code,偏偏IE上就是起不了作用

寫入
<?php
session_start();
$_SESSION['login'] = "YES";
?>

讀出
<?php
session_start();
echo $_SESSION['login'];
?>
結果竟然是空的? 也確定瀏覽器有開啟cookie...

最後發現竟然是因為hostname含有'_' (底線, underscore)
e.g. http://test_tool.myhome.com.tw

改了名字後就沒事了...

2011/05/15

SSH免密碼登入

使用ssh登入遠端server時會要求輸入密碼,有些時候想要略過此步驟(如執行auto script時),建立免密碼登入就可以派上用場。

1. 在local端建立public key與private key
$ ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/home/test/.ssh/id_rsa):     (按enter)
Enter passphrase (empty for no passphrase):                       (按enter)
Enter same passphrase again:                                      (按enter)
Your identification has been saved in /home/test/.ssh/id_rsa.     (按enter)
Your public key has been saved in /home/test/.ssh/id_rsa.pub.     (按enter)
The key fingerprint is:
fe:3f:63:3d:7d:34:06:53:a3:53:22:73:c3:c4:a3:f3 test@test-desktop.local
andomart image is:
+--[ RSA 2048]----+
|           o+  . |
|          .  +.E+|
|         .   ..=.|
|   . o  ..= . oo.|
|  . + = So.=. o. |
|   . . o  ...+   |
|           .     |
|                 |
|                 |
+-----------------+
在~/.ssh目錄下產生了id_rsa與id_rsa.pub

2. 將public key放到server端
$ cd ~/.ssh
$ scp id_rsa.pub username@server_host_name:~/.ssh

3. 進到server端~/.ssh目錄,將public key加到authorized_keys的結尾(authorized_keys可以放置多組),並修改權限為600
$ cd ~/.ssh
$ cat id_rsa.pub >> authorized_keys
$ chmod 600 authorized_keys

下次登入server時就不需要輸入密碼了。

Rsync - 聰明的資料備份

之前在linux都用scp來備份檔案到其他地方,scp(secure copy)簡單來說就是透過ssh來copy檔案,不過在複製的過程中沒有辨認檔案新舊或是否修改過,有點浪費頻寬。所以rsync就是更好的選擇摟!

例如我想把local端的/home/brian/program備份到遠端的/home/brian/backup
$ rsync -avz --delete -e ssh /home/brian/program brian@10.10.0.1:/home/brian/backup
參數說明:
--delete: 刪除遠端多餘的檔案,也就是說今天你把local的某個檔案刪除,rsync的時候遠端的檔案也會被刪除。所以在使用這個參數時請確認您的來源是對的,否則會誤刪遠端的檔案
-z: 傳輸過程壓縮,可以節省頻寬,但相對的壓縮也是需要時間的

思考:
這邊所介紹的只是單方向的備份,例如從local備份到remote,如果想從remote復原到local,只要把source跟destination交關即可。基本上remote端的資料不會手動去修改的,如果我想做像dropbox的樣子,local或remote修改時都會自動sync到對方,應該也是可以達到吧?

2011/05/14

SSH Reverse Tunnel - SSH反向通道

有時候會遇到想從一台有public IP的server登入到一台位於NAT底下的server,這時候reverse tunnel就可以派上用場。

e.g.
如果公司的server位於防火牆or多層router底下,而回家後又想從家裡連到公司work.

1. 在公司先建立company到home的reverse tunnel
$ ssh -R 8080:localhost:22 -o TCPKeepAlive=yes home_user@(家裡電腦的ip)
額外參數
-f: run在背景
-N: 連上後不能執行command

2. 回到家後只要ssh連線localhost:8080就可以導向公司server了
$ ssh -p 8080 com_user@localhost


e.g.
上述tunnel的目標位址為localhost:22,也就是公司當下那一台主機的sshd,如果今天是想連到公司其他台電腦的web服務,命令為
$ ssh -R 8080:192.168.0.8:80 -o TCPKeepAlive=yes home_user@(家裡電腦的ip)
回到家後只要開啓瀏覽器,在網址列打localhost:8080就能連到公司的192.168.0.8:80網頁了

Macbook 多個作業系統共用磁區

我的 Macbook pro 安裝了 MacOS X 還有 Windows 7, 另外還建立一個磁區用來交換兩邊的資料, 剛開始格式化成HFS, 不過 Windows 環境下無法做寫入的動作, 也沒找到相關的 freeware.
之後格式化成NTFS, 在MacOS上可以安裝 NtfsMounter 或 NTFS-3G 來存取 NTFS, 但似乎存取的效能超差, 每秒大概 3MB, 不知道是不是 NTFS-3G 軟體效能的關係? 後來看到一種新格式exFAT(Fat64), 雙邊都不用特別安裝軟體就可以支援.

後來就把共用磁區format為exFAT, 但進到Win7後發現沒有權限存取這個磁區, 相當奇怪. 想說重新format一次看看, 但Win7的格式化竟然只有NTFS一個選項, 這令我更不得其解, 抓了一套分割軟體format重開機, 更糟糕的事情來了, MacOS 開機選項竟然不見了.

開始我的修復之路
1. 開機時按住 shift 可以進入MacOS 安全模式進行磁碟修復, 問題是現在連磁區都讀不到了.
2. 使用安裝光碟開機, 使用磁碟工具來做修復, 但進去後發現磁碟工具把MacOS磁區當成空白區了.
3. 尋找多重開機軟體, 想說應該可以從Win7的開機選單導到MacOS, 裝了easyBCD還是沒用.

最後還是被迫要走到重新安裝的路, 好在重裝MacOS後, Win7有自動加進開機選項 (先前我的Win7是用bootCamp安裝的).

而那個共享磁區最後還是又回到NTFS格式.

2011/05/13

解决 ip_conntrack: table full, dropping packet

當linux系統連線來源ip過多時, /var/log/message 會出現 ip_conntrack: table full, dropping packet

解決方式:
加大tracking table, 下面三種方式都可以
$ echo 6553600 >/proc/sys/net/ipv4/netfilter/ip_conntrack_max

$ sysctl -w net.ipv4.ip_conntrack_max=6553600

$ vim /etc/sysctl.conf
  修改 netfilter/ip_conntrack_max 這一行

2011/05/10

PHP 只允許使用者輸入字母或數字

在實作會員登入機制時,使用者輸入帳號密碼若只想允許“英文字母”or"數字"可以使用preg_match()
if (preg_match("/^[A-Za-z0-9]*$/", $username) != 0) {
    // valid
} else {
    // invalid
}