popen 程式範例

最近公司有個案子在做 WiFi 管理系統,透過集中式的控制器(AC,Access Controller)來管理轄下的 AP(Access Points,CAPWAP 的用語是 WTP)。嗯,有些 sense 的人應該就會知道說:「啊,你在做 CAPWAP」,Bingo!不過這篇沒打算要介紹 CAPWAP,下次有機會來講講 CAPWAP 的故事(不是技術,因為技術的部份我覺得很無聊,所以來講講故事和我的一些心得)。在做的時候遇到一個問題,廠商提供的平台,但卻沒提供人來進行系統整合。簡單來說,就是版子往我桌上一扔,就說:「給我在上面做這個功能」,然後時間短的不像話。額外抱怨一下,他選的 AC 平台比 raspberry pi 的效能還差,居然還敢跟我開口要一堆功能 ... 。理論上來說,AC 會把 WTP 的相關資訊帶到各 WTP,然後這時候 WTP 的 capwap daemon 會對核心層的 driver 進行設定。

一般說來,這種設定的方式是透過 ioctl 的溝通管道,最常見的範例程式就是 ifconfig,透過上下層定義好的 SIOCSIFFLAGS(Socket I/O Control Set Interface Flags)來進行溝通,所以一般來說這是屬於系統平台人員所要處理的問題。可是對於那種把版子跟一堆文件丟在我桌上,又不給我足夠時間的傢伙,我才懶得幫他查 WiFi driver 上需要的 SIOCSFFLAGS 勒。但案子又要進行,那要怎麼辦呢?所以我決定採取最單純的作法:直接呼叫系統的 WiFi 控制指令,如 iwpriv、wlanconfig 等,透過這些指令直接呼叫來取得並設定相關參數。缺點很明顯,就是效能會比較差,但一來是差不了多少(在意效能就給我拿比較像話的版子來!),二來是我相信對方提供的這些指令應該是測試過了,代表系統應該已經整合完畢了(我覺得是我一廂情願,但起碼對方無法還口,要合作也拿點誠意來吧)。

下一個問題是,要怎麼執行系統指令?最直覺的作法,用 system 的函式,問題是我要怎麼撈回傳的資料呢?因此這篇文章就來紀錄另外一支系統函式:popen 的用法。先來看看 man 的說明吧。

The  popen()  function  opens  a  process by creating a pipe, forking, and invoking the shell.  Since a pipe is by definition unidirectional, the  type  argument  may  specify only reading or writing, not both; the resulting stream is correspondingly read-only or write-only.

簡單來說,popen 會開一個 process 來執行 shell 的指令,然後將結果 pipe 回來主程式。直接看看範例:

#include 
#include 
#include 

static void printCmd( char * );
static void printIp( void );
static void printMask( void );

int main()
{
    printCmd( "pwd" );
    printIp();
    printMask();
        
    return 0;
}

static void printCmd( char *pCmd )
{
    static char buffer[256];
    FILE *pFile = popen( pCmd, "r" );
    
    memset( buffer, 0, 256 );
    
    while( fgets( buffer, 256, pFile ) != NULL )
    {
        printf( "%s", buffer );
        memset( buffer, 0, 256 );
    }
    
    pclose( pFile );
    
    return;
}

static void printIp( void )
{
    static char buffer[256];
    FILE *pFile = popen( "ifconfig eth0 | grep \"inet addr\" | awk '{print $2}' | awk -F : '{print $2}'", "r" );
    
    memset( buffer, 0, 256 );
    
    while( fgets( buffer, 256, pFile ) != NULL )
    {
        printf( "%s", buffer );
        memset( buffer, 0, 256 );
    }
    
    pclose( pFile );
    
    return;
}

static void printMask( void )
{
    static char buffer[256];
    FILE *pFile = popen( "ifconfig eth0 | grep Mask | awk '{print $4}' | awk -F : '{print $2}'", "r" );
    
    memset( buffer, 0, 256 );
    
    while( fgets( buffer, 256, pFile ) != NULL )
    {
        printf( "%s", buffer );
        memset( buffer, 0, 256 );
    }
    
    pclose( pFile );
    
    return;
}


程式很簡單,就不多做說明了。還有一個問題是,read 很容易理解,那 write 是什麼意思呢? 想的簡單一點就是 standard input。至於實際的情況等我想到有意義的情況以後再來寫 example code。

這篇文章沒什麼技術難度,只是純粹做個紀錄。

留言

這個網誌中的熱門文章

我弟家的新居感恩禮拜分享:善頌善禱

如何將Linux打造成OpenFlow Switch:Openvswitch

Openssl 範例程式:建立SSL連線