基於wpa_supplicant庫的WIFI連接功能實現--wpa_cli命令代碼改寫

上一篇博客我們一起看了怎樣使用wpas的命令後,接下來就利用這些命令來實現我們的代碼。這些命令的實現都在wpa_cli.c文件中,以status命令爲例,發生如下調用:

static int wpa_cli_cmd_status(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
    int verbose = argc > 0 && os_strcmp(argv[0], "verbose") == 0;
    return wpa_ctrl_command(ctrl, verbose ? "STATUS-VERBOSE" : "STATUS");
}
wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd)
    _wpa_ctrl_command(ctrl, cmd, 1);
        char buf[2048];
        wpa_ctrl_request(ctrl, cmd, os_strlen(cmd), buf,                                    
                                &len,wpa_cli_msg_cb);
        if (print)
            printf("%s", buf);

從上面可以看出,最終是調用wpa_ctrl_request函數完成命令,而最終結果是存在buf裏,通過函數參數print來覺得是否輸出到終端。所以我們最終需要取得的結果就是buf,只要拿到buf內容,我們就可以分析其內容實現我們需要的代碼。爲了實現上述目的,需要對源碼進行下修改:我們把輸出buf作爲參數傳入_wpa_ctrl_command函數,這樣就可以拿到輸出內容了。改寫後代碼如下:

static int wpa_ctrl_command(struct wpa_ctrl *ctrl, char *buf,  const char *cmd)
{
    return _wpa_ctrl_command(ctrl, buf, cmd, 0);
}
static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, char *buf, const char *cmd, int print)
{
    char ret_buf[2048];
    int ret;
    size_t ret_len;

    ret_len = sizeof(ret_buf) - 1;
    ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), ret_buf, &ret_len,
                           wpa_cli_msg_cb);
    if (ret == -2)
    {
        printf("'%s' command timed out.\n", cmd);
        return -2;
    }
    else if (ret < 0)
    {
        printf("'%s' command failed.\n", cmd);
        return -1;
    }

    ret_buf[ret_len] = '\0';
    memcpy(buf, ret_buf, 2048);

    if (print)
    {
        ret_buf[ret_len] = '\0';
        printf("%s", ret_buf);
    }
    return 0;
}
static void wpa_cli_msg_cb(char *msg, size_t len)
{
    printf("%s\n", msg);
}

對於各個命令呢,我們同樣需要加入buf,還是以status爲例:

static int wpa_cli_cmd_status(struct wpa_ctrl *ctrl, char *buf,  int argc, char *argv[])
{
    int verbose = argc > 0 && strcmp(argv[0], "verbose") == 0;
    return wpa_ctrl_command(ctrl, buf, verbose ? "STATUS-VERBOSE" : "STATUS");
}

這樣各個命令就達到了我們的目的。最後我們對命令進行一下封裝,方便統一管理。

#define CFG_MAXARGS 10

/**
 * @brief 對wpa_supplicant中處理命令進行封裝
 */
class HanleCmd : public QObject
{
    Q_OBJECT
public:
    HanleCmd();
    void printCmd();
    int hanleCmd(struct wpa_ctrl *ctrl, char *buf, char *cmd);

private:
    //命令封裝
    struct wpa_cli_cmd
    {
        const char *cmd;  //命令名稱
        int (*handler)(struct wpa_ctrl *ctrl, char *buf, int argc, char *argv[]);  //命令處理函數
    };

    QList<wpa_cli_cmd> listCmd;  //命令鏈表

    //私有函數
    void addCmd();
    int wpaRequest(struct wpa_ctrl *ctrl, char *buf, int argc, char *argv[]);
    int parseLine (char *line, char *argv[]);
};
HanleCmd::HanleCmd()
{
    qDebug("init cmd list");
    addCmd();
}
void HanleCmd::addCmd()
{
    struct wpa_cli_cmd cmd;

    //狀態命令
    cmd.cmd = "status";
    cmd.handler = wpa_cli_cmd_status;
    listCmd.append(cmd);

    //掃描命令
    cmd.cmd = "scan";
    cmd.handler = wpa_cli_cmd_scan;
    listCmd.append(cmd);

    //掃描結果命令
    cmd.cmd = "scan_results";
    cmd.handler = wpa_cli_cmd_scan_results;
    listCmd.append(cmd);

    //選擇AP命令
    cmd.cmd = "select_network";
    cmd.handler = wpa_cli_cmd_select_network;
    listCmd.append(cmd);

    //增加AP命令
    cmd.cmd = "add_network";
    cmd.handler = wpa_cli_cmd_add_network;
    listCmd.append(cmd);

    //列出配置文件中已經保存的AP信息
    cmd.cmd = "list_network";
    cmd.handler = wpa_cli_cmd_list_networks;
    listCmd.append(cmd);

    //設置AP
    cmd.cmd = "set_network";
    cmd.handler = wpa_cli_cmd_set_network;
    listCmd.append(cmd);

    //移除AP
    cmd.cmd = "remove_network";
    cmd.handler = wpa_cli_cmd_remove_network;
    listCmd.append(cmd);

    //使能某個AP
    cmd.cmd = "enable_network";
    cmd.handler = wpa_cli_cmd_enable_network;
    listCmd.append(cmd);

    //關閉某個AP
    cmd.cmd = "disable_network";
    cmd.handler = wpa_cli_cmd_disable_network;
    listCmd.append(cmd);

    //保存配置
    cmd.cmd = "save_config";
    cmd.handler = wpa_cli_cmd_save_config;
    listCmd.append(cmd);
}
int HanleCmd::parseLine (char *line, char *argv[])
{
    int nargs = 0;

    while (nargs < CFG_MAXARGS)
    {

        /* skip any white space */
        while ((*line == ' ') || (*line == '\t'))
        {
            ++line;
        }

        if (*line == '\0')      /* end of line, no more args    */
        {
            argv[nargs] = NULL;
            return (nargs);
        }

        argv[nargs++] = line;   /* begin of argument string */

        /* find end of string */
        while (*line && (*line != ' ') && (*line != '\t'))
        {
            ++line;
        }

        if (*line == '\0')      /* end of line, no more args    */
        {
            argv[nargs] = NULL;
            return (nargs);
        }

        *line++ = '\0';     /* terminate current arg     */
    }

    return (nargs);
}
int HanleCmd::hanleCmd(struct wpa_ctrl *ctrl, char *buf, char *cmd)
{
    int argc;
    char bufTmp[1024];
    char *argv[CFG_MAXARGS];

    strncpy(bufTmp, cmd, 1024);
    bufTmp[1023] = '\0';

    argc = parseLine(bufTmp, argv);
    return wpaRequest(ctrl, buf, argc, argv);
}

封裝完畢後,只需要在執行某個命令時候調用如下代碼即可:

char buf[2048];
handleCmd->hanleCmd(ctrl_conn, buf,  "status");

這樣buf中存入的就是status命令的結果了。

至於參數中的ctrl_conn是底層的操作接口,需要讀者自己去分析下wpa_cli的源碼,這裏只貼一下代碼:

int WifiService::initWpa()
{
    qDebug()<<"initWpa";
    //1.變量初始化
    int conectNum = 0;
    ctrl_iface = NULL;
    ctrl_conn = NULL;
    monitor_conn = NULL;
    ctrl_iface_dir = strdup("/var/run/wpa_supplicant");

    //2.與wpa_supplicant建立連接
    while(true)
    {
        wpa_cli_get_default_ifname();
        if(ctrl_iface == NULL){
            qDebug("failed to wpa_cli_get_default_ifname");
            return -1;
        }

        wpa_cli_open_connection(ctrl_iface);
        if (ctrl_conn || monitor_conn){  //ActionThread未啓用情況下只用ctrl_conn就可以
            qDebug("wpa_supplicant connection established");
            break;
        }else{
            if(conectNum++ >=2){  //最多試三次
                qDebug("wpa_supplicant connection established err");
                return -1;
            }
            qDebug("wpa_supplicant connection established err, we will try agin");
            usleep(10000);
            continue;
        }
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章