2010年12月26日 星期日

Get Android Source Code and Setup the Build Environment

2010/12/17的一則消息:"Android 2.3 Gingerbread's source code now available", 從2.2開始, 其實預設就是用64bit OS來build, 之前為了不想重灌Linux, 在網路上找了許多如何在32bit OS上build code的方法, 花了不少時間才把froyo放上s3c6410, 但是仔細想想這樣好像也沒有比較厲害 , 這次還是乖乖照google建議的, 在64bit OS上build android. 為了怕日後換電腦, 又要全部重來一次, 把過程作個紀錄

1. Prepare
先裝些基本的工具與設定

1.1 apt-get
若需要設定proxy, 在/etc/apt/apt.conf.d/01xxxxx裡面加上proxy資訊
APT
{
    ......
}
Acquire::Http::Proxy "http://your_http_proxy:port";
#Acquire::Ftp::Proxy "ftp://your_ftp_proxy:port";
更新apt-get資料庫
sudo apt-get update
接著作個方便的alias
alias apt-install 'sudo apt-get -y install'

1.2 git
安裝git
apt-install git-core

p.s. 若無法直接連線, 需要透過proxy去避開firewall的話
先用apt-get裝"connect-proxy" (一般的狀況下, 不需要用到這個)
apt-install connect-proxy
接著寫個給git用的proxy script
my-git-proxy
#!/bin/sh
# You can get proxy from http://spys.ru/free-proxy-list/TW/
PROXY=IP:Port
connect-proxy -H http://$PROXY $@
設定git透過proxy連線
git config --global core.gitproxy ~/my-git-proxy

2. Required Packages
AOSP上面提到的一些必要安裝
sudo apt-get install git-core gnupg flex bison gperf build-essential zip curl valgrind sun-java6-jdk zlib1g-dev gcc-multilib g++-multilib libc6-dev-i386 lib32ncurses5-dev ia32-libs x11proto-core-dev libx11-dev lib32readline5-dev lib32z-dev
各個套件的功用, 可參考"ubuntu 9.04安裝Android注意事項"與"Build Android Platform"

其中裝JDK1.6時, 可能需要新增apt-get的source list
/etc/apt/sources.list
deb http://us.archive.ubuntu.com/ubuntu/ jaunty multiverse
deb http://us.archive.ubuntu.com/ubuntu/ jaunty-updates multiverse
接著再更新一下apt-get
sudo apt-get update
apt-install sun-java6-jdk
JDK安裝完後, 將相關的資訊export到環境變數裡
export JAVA_HOME=/usr/lib/jvm/java-6-sun
export PATH=/usr/lib/jvm/java-6-sun/bin:$PATH
export ANDROID_JAVA_HOME=$JAVA_HOME

[註] Java版本相關指令
查版本:java -version
列出已安裝版本:sudo update-java-alternatives -l
切換Java版本:sudo update-java-alternatives -s java-x-sun

3. Download Android Source Code
Download source code 只要照AOSP上面教的即可
curl http://android.git.kernel.org/repo >~/repo
chmod a+x ~/bin/repo
mkdir mydroid
cd mydroid
~/repo init -u git://android.git.kernel.org/platform/manifest.git
~/repo sync

4. Build Android
build code可以直接下make
make -j4
若是之後可能開發自己的product的話, 可以準備一個script
#!/bin/bash

function show_result()
{
        echo ""
        echo "#######################################################"
        echo " $1"
        echo "#######################################################"
        echo ""
}

CWD=$PWD
ANDROID_PATH=$CWD/my_android

export TARGET_PRODUCT=generic
export TARGET_BUILD_TYPE=release
export TARGET_BUILD_VARIANT=eng
export TARGET_SIMULATOR=false

cd ${ANDROID_PATH}

if ( make -j4 ); then
        show_result "Build Android Successfully!!"
        exit 0
else
        show_result "Build Android Failure!!"
        exit 1
fi
換成Ubuntu64順利很多, 只碰到一個error
make: *** [out/target/product/generic/obj/ SHARED_LIBRARIES/libwebcore_intermediates/LINKED/libwebcore.so] Error 1
拜了一下Google大神, 多數的說法都是開給VMware的記憶體不夠所造成, 照著網路上的作法先試著將swap加大後,
dd if=/dev/zero of=/swapfile bs=1024 count=1048576
mkswap /swapfile
swapon /swapfile
再加上相當長的build code時間, 總算成功得到android image了


參考資料:
[1] Android Open Source Project
[2] Build Android Platform
[3] Android获取源代码、编译、命令

2010年12月24日 星期五

L7-filter

第一次碰到要去filter一些application的時候, 都慢慢的去找每個application用到哪些port, 再用iptables把那些port擋掉. 但像ftp, telnet之類的, 雖然有預設的port, 但user卻能自己改成自己喜歡的, 這個時候L7 filter就派上用場了.

L7 filter的安裝可參考官網, 步驟為:
1. Patch kernel, 並打開kernel選項
2. 把l7 match加到iptables的extensions裡
3. 下載protocol pattern files

使用的指令:
iptables [-t table -A chain] -m layer7 --l7proto [protocol name] -j [action]
舉例來說, 若要擋yahoo即時通:
iptables -t mangle -A PREROUTING -m layer7 --l7proto yahoo -j DROP
net/netfilter/xt_layer7.c中可看出 (雖然不是很懂), 基本上他的作法就是抓TCP/UDP header後的payload跟protocol pattern file (*.pat) 內的regular expression利用regexec做比對.
在"L7-filter Pattern Writing HOWTO"中, 介紹如何透過regular expression去增加自己的filter. (題外話, 文中也講到regex API在kernel spaceuser space是有點差異的)

拿HTTP的一個封包來看


可以用下列的regex來辨識
http
http/(0\.9|1\.0|1\.1) [1-5][0-9][0-9]
但真的要自己寫出個堪用的pattern還是照L7-filter Pattern Writing HOWTO上的準則去做比較可行.


參考資料:
[1] Application Layer Packet Classifier for Linux
[2] 在 C 程式中,使用 Regex (Regular Expression) library

sscanf

今天遇到了要去抓某個interface的tx/rx數量的問題, 以前最常用的方法就利用多次的strtok_r去抓到自己要的欄位, 其實, sscanf有時候可以更簡單的達成這個目的, 但是過去苦於看不懂他format的用法, 而沒有利用sscanf來做, 但看完"sscanf 函數用法"後, 總算對sscanf表示式的寫法多了點概念.

在Linux上, 各個network interface的統計資料會被記錄在"/proc/net/dev"裡面
Receive/Transmit bytes分別位於第2欄與第10欄
假設經把eth0那列的string都存到buf內
用strtok來做
void eth_statistics_parser_strtok(char *buf)
{
        char *ptr=NULL, *saveptr=NULL;
        unsigned long tx=0, rx=0;

        strtok_r(buf, ":", &saveptr);
        ptr = strtok_r(NULL, " ", &saveptr);
        rx = atol(ptr);
        strtok_r(NULL, " ", &saveptr);
        strtok_r(NULL, " ", &saveptr);
        strtok_r(NULL, " ", &saveptr);
        strtok_r(NULL, " ", &saveptr);
        strtok_r(NULL, " ", &saveptr);
        strtok_r(NULL, " ", &saveptr);
        strtok_r(NULL, " ", &saveptr);
        ptr = strtok_r(NULL, " ", &saveptr);
        tx = atol(ptr);

        printf("Rx Bytes: %d; Tx Bytes: %d\n", rx, tx);
}
用sscanf
void eth_statistics_parser_sscanf(char *buf)
{
        unsigned long tx=0, rx=0;
        //sscanf(buf, "%*s%lu%*s%*s%*s%*s%*s%*s%*s%lu", &rx, &tx);
        sscanf(buf, "%*[^:]:%lu%*s%*s%*s%*s%*s%*s%*s%lu", &rx, &tx);

        printf("Rx Bytes: %d; Tx Bytes: %d\n", rx, tx);
}
雖然不知道有沒有比較好, 但是看起來酷很多.


參考資料:
[1] sscanf 函數用法

2010年12月23日 星期四

Android Init Language

在Linux下的rcS用的一般都是shell script的語法撰寫, 但Android裡的init.rc似乎有他自己的一套

Android init language內的描述可分成兩種類型: Action, Service

Actions
on <trigger>
   <command>
   <command>
   <command>
   ......
其意義表示當trigger發生或成立時, 要執行哪些command
例如
on boot
  export PATH /sbin:/system/sbin:/system/bin
  export LD_LIBRARY_PATH /system/lib

  mkdir /dev
  mkdir /proc
  mkdir /sys
表示當boot發生了, 要去export一些環境變數以及建一些目錄.
官網中定義了幾個trigger的種類,
其他預設的trigger (ex. init, fs), 可以在system/core/init/init.c裡面找到

Services
service <name> <pathname> [ <argument> ]*
<option>
<option>
......
service定義的program, 除了option設為disable外, 會被init執行 (精確的說, 是action內的class_start default, 啟動了default的service)
例如
service zygote /system/bin/app_process -Xzygote /system/bin --zygote
  socket zygote 666
"zygote" 表示這個service的名稱
"/system/bin/app_process" 表示程式的路徑
"-Xzygote /system/bin --zygote" 表示要傳入程式的參數
"socket zygote 666" 是service的option, 表示開一個名為zygote的unix socket給這個service

合在一起看的例子
on device-added-/dev/compass
  start akmd

on device-removed-/dev/compass
  stop akmd

service akmd /sbin/akmd
  disabled
  user akmd
  group akmd
表示說akmd這個service並不會在init起來時被執行, 而是當/dev/compass產生時會被執行, 當/dev/compass移除時, 該service會被停掉

整個init.rc被執行的過程, 可參考"Android init 啟動過程分析"

參考資料:
[1] Android Init Language
[2] 如何去寫Android init.rc
[3] android init(system/core/init/init.c)分析
[4] Android init 啟動過程分析
[5] init.c

2010年12月21日 星期二

Hash Table in C

Hash Table應該上計概或資料結構的時候都會講到, 一般的用法就是傳入一個Key, 透過查表, 回傳相對應的Value.
看看wiki上"Hash table"的解釋, 感覺實做上似乎不是那麼容易, 尤其是hash function的設計, 但多數的程式語言, 如perl, java, c#, 都提供了hash相關的library. 在linux下, 也有一系列的"hash table management" APIs.

以下提供一個簡單的範例
my_hash.c
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <search.h>

typedef struct _node_t
{
    char key[16];
    char value[16];
}node_t;

static node_t *node_list = NULL;

static struct hsearch_data htab;

void set_node(char *name, char *value)
{
    ENTRY e, *ep;

    memset(&e, 0, sizeof(ENTRY));
    e.key = name;

    hsearch_r(e, FIND, &ep, &htab);
    if(ep)
        ep->data = value;
    else
    {
        e.data = value;
        hsearch_r(e, ENTER, &ep, &htab);
    }
}

void get_node(char *name, char *buf)
{
    ENTRY e={NULL,NULL}, *ep;

    e.key = name;
    hsearch_r(e, FIND, &ep, &htab);

    if (ep && ep->data)
        strcpy(buf, ep->data);
    else
        strcpy(buf, "");
}

void release_htab()
{
    if (node_list) free(node_list);
    hdestroy_r(&htab);
}

void create_htab(char *fpath)
{
    FILE *fp = NULL;
    int nel=0;   

    fp = fopen(fpath, "r");
    if (!fp)
    {
        CM_DBG("Cannot open %s\n", fpath);
        return;
    }

    release_htab();

    // Get the total number
    while(!feof(fp) && fgets(str, BUF_SIZE, fp))
    {
        nel++;
    }

    if(!hcreate_r(nel, &htab))
        return;

    node_list = malloc(sizeof(node_t)*nel);
    memset(node_list, 0, (sizeof(node_t)*nel));

    rewind(fp);

    char str[256] = {0};
    char *ptr=NULL, *saveptr=NULL;
    node_t *p_node = NULL;
    int i = 0;

    while(!feof(fp) && fgets(str, BUF_SIZE, fp))
    {
        p_node = (node_t *)(node_list+i);

        //The string format: Name,PhoneNo
        ptr = strtok_r(str, ",", &saveptr);        // Name
        if (ptr)
            strcpy(p_node->key, ptr);
        else
            continue;

        ptr = strtok_r(NULL, ",\n", &saveptr);    // Phone No
        if (ptr)
            strcat(p_node->value, ptr);
        else
            continue;

        set_node(p_node->key, p_node->value);    //Put into hash table
        i++;
    }
}
其中, 要注意的地方在於, 塞hash table時, 需要動態配置記憶體空間來儲存值組(key, value), 但hdestroy並不會釋放動態配置的記憶體空間, 使用者需要自己free. 在範例程式中, 單純開了一個大塊的空間去存值, 但比較好的作法,可能是用linked list去紀錄各個值組, 這樣比較不用怕浪費太多記憶體空間或是buffer不夠大的問題.

參考資料:
[1] hsearch_r.c

2010年12月17日 星期五

Unix Domain Socket

由Wiki上對"Unix domain socket"的解釋, 很清楚的知道它也是IPC的一種方式, 因為他所用的API與internet socket幾乎相同, 所以對於寫過socket程式的人, 應該很好上手.

這裡提供一個小範例, 將unix socket api再包一層, 讓開發程式時能更容易的達成IPC目的.

my_unix_socket.h
#ifndef __MY_UNIX_SOCKET_H__
#define __MY_UNIX_SOCKET_H__

int create_listener(char* name, void* funp(char*));
int send_to_listener(char* app_name, void* buf, int buf_size);

#endif /* __MY_UNIX_SOCKET_H__ */

my_unix_socket.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/un.h>

#define MAX_BUF_SIZE  (1024)

static void first_handler(int fd, char* buf)
{
  int connected_fd;
  struct sockaddr_un from;
  int lenfrom = sizeof(from);
 
  bzero(&from, sizeof(from));
 
  if ((connected_fd = accept(fd, (struct sockaddr *)&from, &lenfrom)) < 0 )
  {
    printf("accept failed\n");
    return;
  }

  if (recv(connected_fd, buf, MAX_BUF_SIZE, 0) < 0)
  {
    printf("recv failed\n");
    return;
  }

  close(connected_fd);

  return;
}

int create_listener(char* name, void* funp(char*))
{
  int fd, ret=-1;
  struct sockaddr_un saddr;
  struct sockaddr_un from;
  char buf[MAX_BUF_SIZE];

  /* open a socket */
  if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
  {
    printf("socket failed\n");
    goto fun_exit;
  }
 
  /* set the addr */
  unlink(name);
  bzero(&saddr, sizeof(saddr));
  saddr.sun_family = AF_UNIX;
  strcpy(saddr.sun_path, name);

  /* bind socket with addr */
  if (bind(fd, (struct sockaddr*)&saddr, sizeof(saddr)) < 0)
  {
    printf("bind failed\n");
    goto fun_exit;
  }
 
  /* listen fd */
  if (listen(fd, 10) < 0)
  {
    printf("listen failed\n");
    goto fun_exit;
  }

  fd_set rset;
  FD_ZERO(&rset);
  FD_SET(fd, &rset);

  while (1)
  {
    select(fd+1, &rset, NULL, NULL, NULL);
    if (FD_ISSET(fd, &rset))
    {
      first_handler(fd, buf);
      funp(buf);
    }
  }
  ret = 0;

fun_exit:
  if (fd>0) close(fd);
  return ret;
}

int send_to_listener(char* app_name, void* buf, int buf_size)
{
  int fd, ret=-1;
  struct sockaddr_un saddr;

  if (buf_size > MAX_BUF_SIZE)
  {
    printf("The max message size is %d\n", MAX_BUF_SIZE);
    goto fun_exit;
  }

  /* open a socket */
  if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
  {
    printf("socket failed\n");
    goto fun_exit;
  }
 
  /* set the saddr */
  bzero(&saddr, sizeof(saddr));
  saddr.sun_family = AF_UNIX;
  strcpy(saddr.sun_path, app_name);

  /* connect to server */
  if (connect(fd, (struct sockaddr*)&saddr, sizeof(saddr)) < 0)
  {
    printf("connect failed\n");
    goto fun_exit;
  }

  /* send data */
  if (send(fd, buf, buf_size, 0) < 0)
  {
    printf("send failed\n");
    goto fun_exit;
  }
  ret = 0;

fun_exit:
  if (fd>0) close(fd);
  return ret;
}

兩隻簡單的應用程式:

server.c
#include <stdio.h>

void my_recv(char* buf)
{
  printf("%s,%s: %s\n", __FILE__, __FUNCTION__, buf);
}

int main()
{
  create_listener("my_server", my_recv);
}

client.c
#include <stdio.h>
#include "my_unix_socket.h"

int main()
{
  char buf[] = "hello unix socket";
  send_to_listener("my_server", buf, sizeof(buf));
}


參考資料:
[1] Unix Sockets
[2] Unix domain sockets vs. internet sockets

2010年12月11日 星期六

SyntaxHighlighter

SyntaxHighlighter的功用就跟他的名字一樣, Syntax Highlighter
他提供Javascript/CSS, 讓你輕鬆的幫網頁上的程式碼做語法上色
如:
#include <stdio.h>
int main()
{
    printf("Hello\n");
}

基本上, 使用方法就是將你的程式碼包在<pre>裡面, 再去設定<pre>的屬性, 套用不同的syntax
但不同SyntanxHighlighter版本, 對<pre>所定義的屬性似乎不太一樣
v1.5的用法可參考http://sharedderrick.blogspot.com/2007/12/blogger-syntaxhighlighter.html
v.2.1的用法可參考http://www.dotblogs.com.tw/emiel/archive/2010/07/23/16728.aspx