main函数的前面部分做好了传入参数的处理、config文件的读取、初始化等准备工作,这里将进入最主要的部分,hostapd_global_run函数。
这里就不贴繁琐的代码了,这个函数主要分三步:
1. 调用tncs_global_init完成tnc相关的初始化,这里就不详细说了
2. 调用os_daemonize函数实现将该程序以后台进程运行,它主要实现过程是调用os_daemon将标准输入、标准输出和标准出错都重定向到/dev/null文件下,这样就不能通过
终端进行交互了,但是交互过程是使用hostapd_cli这个进程实现的,前面章节有介绍;然后检查pid_file文件的合法性。
3. eloop_run核心函数,这个函数很重要,所以下面将详细介绍。
-
-
void eloop_run(void)
-
{
-
#ifdef CONFIG_ELOOP_POLL
-
int num_poll_fds;
-
int timeout_ms =
0;
-
#endif /* CONFIG_ELOOP_POLL */
-
#ifdef CONFIG_ELOOP_SELECT
-
fd_set *rfds, *wfds, *efds;
-
struct timeval _tv;
-
#endif /* CONFIG_ELOOP_SELECT */
-
#ifdef CONFIG_ELOOP_EPOLL
-
int timeout_ms =
-1;
-
#endif /* CONFIG_ELOOP_EPOLL */
-
int res;
-
struct os_reltime tv, now;
-
-
#ifdef CONFIG_ELOOP_SELECT
-
rfds = os_malloc(
sizeof(*rfds));
-
wfds = os_malloc(
sizeof(*wfds));
-
efds = os_malloc(
sizeof(*efds));
-
if (rfds ==
NULL || wfds ==
NULL || efds ==
NULL)
-
goto out;
-
#endif /* CONFIG_ELOOP_SELECT */
-
-
while (!eloop.terminate &&
-
(!dl_list_empty(&eloop.timeout) || eloop.readers.count >
0 ||
-
eloop.writers.count >
0 || eloop.exceptions.count >
0)) {
-
struct eloop_timeout *timeout;
-
timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
-
list);
-
if (timeout) {
-
os_get_reltime(&now);
-
if (os_reltime_before(&now, &timeout->time))
-
os_reltime_sub(&timeout->time, &now, &tv);
-
else
-
tv.sec = tv.usec =
0;
-
#if defined(CONFIG_ELOOP_POLL) || defined(CONFIG_ELOOP_EPOLL)
-
timeout_ms = tv.sec *
1000 + tv.usec /
1000;
-
#endif /* defined(CONFIG_ELOOP_POLL) || defined(CONFIG_ELOOP_EPOLL) */
-
#ifdef CONFIG_ELOOP_SELECT
-
_tv.tv_sec = tv.sec;
-
_tv.tv_usec = tv.usec;
-
#endif /* CONFIG_ELOOP_SELECT */
-
}
-
-
#ifdef CONFIG_ELOOP_POLL
-
num_poll_fds = eloop_sock_table_set_fds(
-
&eloop.readers, &eloop.writers, &eloop.exceptions,
-
eloop.pollfds, eloop.pollfds_map,
-
eloop.max_pollfd_map);
-
res = poll(eloop.pollfds, num_poll_fds,
-
timeout ? timeout_ms :
-1);
-
#endif /* CONFIG_ELOOP_POLL */
-
#ifdef CONFIG_ELOOP_SELECT
-
eloop_sock_table_set_fds(&eloop.readers, rfds);
-
eloop_sock_table_set_fds(&eloop.writers, wfds);
-
eloop_sock_table_set_fds(&eloop.exceptions, efds);
-
res = select(eloop.max_sock +
1, rfds, wfds, efds,
-
timeout ? &_tv :
NULL);
-
#endif /* CONFIG_ELOOP_SELECT */
-
#ifdef CONFIG_ELOOP_EPOLL
-
if (eloop.count ==
0) {
-
res =
0;
-
}
else {
-
res = epoll_wait(eloop.epollfd, eloop.epoll_events,
-
eloop.count, timeout_ms);
-
}
-
#endif /* CONFIG_ELOOP_EPOLL */
-
if (res <
0 && errno != EINTR && errno !=
0) {
-
wpa_printf(MSG_ERROR,
"eloop: %s: %s",
-
#ifdef CONFIG_ELOOP_POLL
-
"poll"
-
#endif
/* CONFIG_ELOOP_POLL */
-
#ifdef CONFIG_ELOOP_SELECT
-
"select"
-
#endif
/* CONFIG_ELOOP_SELECT */
-
#ifdef CONFIG_ELOOP_EPOLL
-
"epoll"
-
#endif
/* CONFIG_ELOOP_EPOLL */
-
, strerror(errno));
-
goto out;
-
}
-
eloop_process_pending_signals();
-
-
/* check if some registered timeouts have occurred */
-
timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
-
list);
-
if (timeout) {
-
os_get_reltime(&now);
-
if (!os_reltime_before(&now, &timeout->time)) {
-
void *eloop_data = timeout->eloop_data;
-
void *user_data = timeout->user_data;
-
eloop_timeout_handler handler =
-
timeout->handler;
-
eloop_remove_timeout(timeout);
-
handler(eloop_data, user_data);
-
}
-
-
}
-
-
if (res <=
0)
-
continue;
-
-
#ifdef CONFIG_ELOOP_POLL
-
eloop_sock_table_dispatch(&eloop.readers, &eloop.writers,
-
&eloop.exceptions, eloop.pollfds_map,
-
eloop.max_pollfd_map);
-
#endif /* CONFIG_ELOOP_POLL */
-
#ifdef CONFIG_ELOOP_SELECT
-
eloop_sock_table_dispatch(&eloop.readers, rfds);
-
eloop_sock_table_dispatch(&eloop.writers, wfds);
-
eloop_sock_table_dispatch(&eloop.exceptions, efds);
-
#endif /* CONFIG_ELOOP_SELECT */
-
#ifdef CONFIG_ELOOP_EPOLL
-
eloop_sock_table_dispatch(eloop.epoll_events, res);
-
#endif /* CONFIG_ELOOP_EPOLL */
-
}
-
-
eloop.terminate =
0;
-
out:
-
#ifdef CONFIG_ELOOP_SELECT
-
os_free(rfds);
-
os_free(wfds);
-
os_free(efds);
-
#endif /* CONFIG_ELOOP_SELECT */
-
return;
-
-
-
首先为三个文件描述符集申请空间:
rfds = os_malloc(sizeof(*rfds));
wfds = os_malloc(sizeof(*wfds));
efds = os_malloc(sizeof(*efds));
然后进入while循环:
while (!eloop.terminate && (eloop.timeout || eloop.readers.count > 0 || eloop.writers.count > 0 || eloop.exceptions.count > 0))
它的循环条件如括号里面描述,正常情况都在这里面循环,除非terminate为1,而这个有信号处理设置,参见
eloop_register_signal_terminate(handle_term, NULL);
static void handle_term(int sig, void *eloop_ctx, void *signal_ctx)
{
wpa_printf(MSG_DEBUG, "Signal %d received - terminating", sig);
eloop_terminate();
}
void eloop_terminate(void)
{
eloop.terminate = 1;
}
接下来对超时时间进行设置timeout,主要是为下面调用的select函数会用到超时时间做准备。
if (timeout) {
os_get_reltime(&now);
if (os_reltime_before(&now, &timeout->time))
os_reltime_sub(&timeout->time, &now, &tv);
else
tv.sec = tv.usec = 0;
_tv.tv_sec = tv.sec;
_tv.tv_usec = tv.usec;
}
将申请的文件描述符集与eloop对象相结合,并调用select函数对这些文件发生异常进行监听:
eloop_sock_table_set_fds(&eloop.readers, rfds);
eloop_sock_table_set_fds(&eloop.writers, wfds);
eloop_sock_table_set_fds(&eloop.exceptions, efds);
res = select(eloop.max_sock + 1, rfds, wfds, efds, timeout ? &_tv : NULL);
最后eloop_process_pending_signals对发生的信号进行处理:
-
static void eloop_process_pending_signals(void)
-
{
-
int i;
-
-
if (eloop.signaled ==
0)
//有没有信号产生,如果有,那么这个标志位将为1,说明有信号需要处理,如果为0,那么没有信号要处理,函数返回
-
return;
-
eloop.signaled =
0;
//将信号标示为置0,以便下次有信号产生时,置1
-
-
if (eloop.pending_terminate) {
//如果不用处理后面将会产生的信号,则立即向进程发送一个SIGALARM信号,然后将这个标志置0
-
#ifndef CONFIG_NATIVE_WINDOWS
-
alarm(
0);
-
#endif /* CONFIG_NATIVE_WINDOWS */
-
eloop.pending_terminate =
0;
-
}
-
-
for (i =
0; i < eloop.signal_count; i++) {
//对信号标示进行处理
-
if (eloop.signals[i].signaled) {
-
eloop.signals[i].signaled =
0;
-
-
#ifndef CONFIG_NATIVE_WINDOWS
-
eloop_register_signal(SIGHUP, handle_reload,
NULL);
//对中断信号和中断处理函数进行注册
-
eloop_register_signal(SIGUSR1, handle_dump_state,
NULL);
-
#endif /* CONFIG_NATIVE_WINDOWS */
-
eloop_register_signal_terminate(handle_term,
NULL);
-
-
eloop.signals[i].handler(eloop.signals[i].sig,
//调用处理函数对相应的信号进行处理,那么到底调用的是什么处理函数呢?这些处理函数的注册是在
-
eloop.user_data,
//什么地方呢?这个进程是怎么样对数据包进行处理的呢?在哪里处理呢?
-
eloop.signals[i].user_data);
-
}
-
}
-
}
到这里,应该对hostapd这个程序的整体有了一定的把握,应该能看懂第一篇中的那张结构图了,但也有局限的地方,比如好多细节的地方没有讲清楚,
比如:数据包是在哪里接收的? 数据包是在哪里发送的? 数据包是这样存放的?这些处理函数是在哪里注册的? 客户选择的加密方式是怎么起作用的?
hostapd怎么将一块网卡切换到了ap模式? 等等。
接下来将尽力弄清楚这些问题。