上一次对字符串工具模块进行了封装,这次主要是对"参数配置模块"的封装,FTP中有很多配置相关的选项,不可能硬编码到代码中,而应该将它们配置到配置文件当中,像vsftpd的配置文件如下:
而对于miniftpd所有的参数配置项如下:
对于上面这些变量应该是与对应的配置项进行一一对应的,所以需要定义三张表格来进行一一对应:
配置文件中的配置项与配置项变量对应关系表 |
static struct parseconf_bool_setting { const char *p_setting_name; int *p_variable; } parseconf_bool_array[] = { { "pasv_enable", &tunable_pasv_enable }, { "port_enable", &tunable_port_enable }, { NULL, NULL } };
|
static struct parseconf_uint_setting { const char *p_setting_name; unsigned int *p_variable; } parseconf_uint_array[] = { { "listen_port", &tunable_listen_port }, { "max_clients", &tunable_max_clients }, { "max_per_ip", &tunable_max_per_ip }, { "accept_timeout", &tunable_accept_timeout }, { "connect_timeout", &tunable_connect_timeout }, { "idle_session_timeout", &tunable_idle_session_timeout }, { "data_connection_timeout", &tunable_data_connection_timeout }, { "local_umask", &tunable_local_umask }, { "upload_max_rate", &tunable_upload_max_rate }, { "download_max_rate", &tunable_download_max_rate }, { NULL, NULL } }; |
static struct parseconf_str_setting { const char *p_setting_name; const char **p_variable; } parseconf_str_array[] = { { "listen_address", &tunable_listen_address }, { NULL, NULL }//最后这个NULL是为了遍历用的 }; |
下面定义两个操作配置文件的函数:
函数 | 说明 |
void parseconf_load_file(const char *path); | 加载配置文件 |
void parseconf_load_setting(const char *setting); | 将配置项加载到相应的变量 |
先新建配置文件模块文:
tunable.h:对其变量进行声明:
//配置文件模块
#ifndef _TUNABLE_H_
#define _TUNABLE_H_
extern int tunable_pasv_enable;
extern int tunable_port_enable;
extern unsigned int tunable_listen_port;
extern unsigned int tunable_max_clients;
extern unsigned int tunable_max_per_ip;
extern unsigned int tunable_accept_timeout;
extern unsigned int tunable_connect_timeout;
extern unsigned int tunable_idle_session_timeout;
extern unsigned int tunable_data_connection_timeout;
extern unsigned int tunable_local_umask;
extern unsigned int tunable_upload_max_rate;
extern unsigned int tunable_download_max_rate;
extern const char *tunable_listen_address;
#endif /* _TUNABLE_H_ */
tunable.c:
//配置文件模块
#include "tunable.h"
int tunable_pasv_enable = 1; //是否开启被动模式
int tunable_port_enable = 1; //是否开启主动模式
unsigned int tunable_listen_port = 21; //FTP服务器端口
unsigned int tunable_max_clients = 2000; //最大连接数
unsigned int tunable_max_per_ip = 50; //每ip最大连接数
unsigned int tunable_accept_timeout = 60; //Accept超时间
unsigned int tunable_connect_timeout = 60; //Connect超时间
unsigned int tunable_idle_session_timeout = 300; //控制连接超时时间
unsigned int tunable_data_connection_timeout = 300; //数据连接超时时间
unsigned int tunable_local_umask = 077; //掩码
unsigned int tunable_upload_max_rate = 0; //最大上传速度
unsigned int tunable_download_max_rate = 0; //最大下载速度
const char *tunable_listen_address; //FTP服务器IP地址
另外新建一个配置文件:
接下来还要暴露两个接口出来,对文件和配置项的解析:
函数 | 说明 |
void parseconf_load_file(const char *path); | 加载配置文件 |
void parseconf_load_setting(const char *setting); | 将配置项加载到相应的变量 |
新建一个解析模块来做上面的解析工作:
parseconf.h:
//文件和配置项的解析
#ifndef _PARSECONF_H_
#define _PARSECONF_H_
void parseconf_load_file(const char *path);
void parseconf_load_setting(const char *setting);
#endif /* _PARSE_CONF_H_ */
parseconf.c:
#include "parseconf.h"
#include "common.h"
#include "tunable.h"
void parseconf_load_file(const char *path){
}
void parseconf_load_setting(const char *setting){
}
下面来实现这两个函数:
另外,由于fgets函数读取的一行字符包含'\n',所以需要将其去掉,可以用我们之前封装的现成方法:
接下来实现命令行的解析函数,在正式解析之前,需要将配置文件中的配置项与配置项变量对应关系表用代码定义出来,如下:
#include "parseconf.h"
#include "common.h"
#include "tunable.h"
static struct parseconf_bool_setting
{
const char *p_setting_name;
int *p_variable;
}
parseconf_bool_array[] =
{
{ "pasv_enable", &tunable_pasv_enable },
{ "port_enable", &tunable_port_enable },
{ NULL, NULL }
};
static struct parseconf_uint_setting
{
const char *p_setting_name;
unsigned int *p_variable;
}
parseconf_uint_array[] =
{
{ "listen_port", &tunable_listen_port },
{ "max_clients", &tunable_max_clients },
{ "max_per_ip", &tunable_max_per_ip },
{ "accept_timeout", &tunable_accept_timeout },
{ "connect_timeout", &tunable_connect_timeout },
{ "idle_session_timeout", &tunable_idle_session_timeout },
{ "data_connection_timeout", &tunable_data_connection_timeout },
{ "local_umask", &tunable_local_umask },
{ "upload_max_rate", &tunable_upload_max_rate },
{ "download_max_rate", &tunable_download_max_rate },
{ NULL, NULL }
};
static struct parseconf_str_setting
{
const char *p_setting_name;
const char **p_variable;
}
parseconf_str_array[] =
{
{ "listen_address", &tunable_listen_address },
{ NULL, NULL }
};
void parseconf_load_file(const char *path){
FILE *fp = fopen(path, "r");
if (fp == NULL)
ERR_EXIT("fopen");
char setting_line[1024] = {0};
while (fgets(setting_line, sizeof(setting_line), fp) != NULL)
{
if (strlen(setting_line) == 0
|| setting_line[0] == '#'
|| str_all_space(setting_line))
continue;
str_trim_crlf(setting_line);
parseconf_load_setting(setting_line);
memset(setting_line, 0, sizeof(setting_line));
}
fclose(fp);
}
void parseconf_load_setting(const char *setting){
}
其中各个变量是来自于全局变量tunable.c中。
可见有三种类型的参数,下面一个个来进行解析,对于"pasv_enable=YES"一个配置,可能会写成“ pasv_enable=YES”,所以先去掉左控格:
然后需要将key=pasv_enable;value=YES分隔开,这里可以用之前封装的现成的命令:
但也有可能用户没有配置value,如“pasv_enable=”,所以这是不合法的,也应该做下判断:
接下来,就需要拿这个key在上面的配置表格变量中进行搜索,如果找到了,则将其值赋值给该配置变量,如下:
如果说没有找到话,也就说明当前的配置项不是字符串类型的,这时,还得继续去其它类型的配置项中进行搜寻,如下:
而对于布尔类型,可以有以下几种形式:
AA=YES
AA=yes
AA=TRUE
AA=1
所以,首先将value统一成大写:
当遍历boolean类型配置项中也没有找到时,则需要在无符号整形中进行查找,其中无符号整形有两种形式:一种八进制,以0开头,比如"local_umask=077";另一种是十进制,如:"listen_port=21",所以需要做下判断,代码基本类似:
下面则来编译运行一下,在编译运行之前,需要把新加的模块加入到Makefile中:
上面看已经正常通过了,那还是看不出这些配置信息是否正常读取了,所以下面需要写一个测试程序来检验一下:
编译运行:
再次编译运行:
接下来可以应用某些配置项了:
编译运行:
用FTP客户端连接:
可见这样代码就变成可配置的了,另外配置文件的文件名可以做成宏:
加载时直接用该宏: