16.Nginx解析配置文件之ngx_conf_parse

来源:互联网 发布:贵阳行知学校 编辑:程序博客网 时间:2024/06/03 22:42

上一篇讲的ngx_conf_read_token用于从配置文件内容中提取token,通常在一个配置行结束或者一个配置块开始时返回。

提取了这些token后,我们还需要找到对应的选项并且进行赋值,这就是ngx_conf_parse的作用。


/* core/ngx_conf_file.c *//* The ten fixed arguments */static int argument_number[] = {    NGX_CONF_NOARGS,    NGX_CONF_TAKE1,    NGX_CONF_TAKE2,    NGX_CONF_TAKE3,    NGX_CONF_TAKE4,    NGX_CONF_TAKE5,    NGX_CONF_TAKE6,    NGX_CONF_TAKE7};char *ngx_conf_parse(ngx_conf_t *cf, ngx_str_t *filename){    int               m, rc, found, valid;    char             *rv;    void             *conf, **confp;    ngx_fd_t          fd;    ngx_str_t        *name;    ngx_conf_file_t  *prev;    ngx_command_t    *cmd;#if (NGX_SUPPRESS_WARN)    fd = NGX_INVALID_FILE;    prev = NULL;#endif    if (filename) {        // 以只读模式打开配置文件        fd = ngx_open_file(filename->data, NGX_FILE_RDONLY, NGX_FILE_OPEN);        if (fd == NGX_INVALID_FILE) {            // 如果打开失败, 直接返回NGX_CONF_ERROR            ngx_log_error(NGX_LOG_EMERG, cf->log, ngx_errno,                          ngx_open_file_n " %s failed", filename->data);            return NGX_CONF_ERROR;        }        // prev用来保存当前cf的conf_file指针        prev = cf->conf_file;        // 从内存池cf->pool申请一块ngx_conf_file_t大小的内存空间, 并使cf->conf_file指向它        if (!(cf->conf_file = ngx_palloc(cf->pool, sizeof(ngx_conf_file_t)))) {            return NGX_CONF_ERROR;        }        // ngx_fd_info宏就是调用fstat从文件描述符获取文件状态        // 这里就是获取配置文件的文件状态, 并保存在cf->conf_file->file.info中        if (ngx_fd_info(fd, &cf->conf_file->file.info) == -1) {            ngx_log_error(NGX_LOG_EMERG, cf->log, ngx_errno,                          ngx_fd_info_n " %s failed", filename->data);        }        // 从内存池cf->pool中申请一块大小为1024字节的内存空间, 用来作为cf->conf_file的缓冲区,        // 接下来配置文件的内容就会读取至该缓冲区, 这也意味着每次我们最多只能从配置文件中读取1024字节的内容        if (!(cf->conf_file->buffer = ngx_create_temp_buf(cf->pool, 1024))) {            return NGX_CONF_ERROR;        }        cf->conf_file->file.fd = fd;        cf->conf_file->file.name.len = filename->len;        cf->conf_file->file.name.data = filename->data;        cf->conf_file->file.offset = 0;        cf->conf_file->file.log = cf->log;;        cf->conf_file->line = 1;    }    for ( ;; ) {        // ngx_conf_read_token的返回值为NGX_OK/NGX_ERROR/NGX_CONF_FILE_DONE/NGX_CONF_BLOCK_DONE        // 这里调用ngx_conf_read_token从配置文件中读取内容并解析token        rc = ngx_conf_read_token(cf);#if 0ngx_log_debug(cf->log, "token %d" _ rc);#endif        if (rc == NGX_ERROR) {            // 读取或者解析出错, 退出循环            break;        }        if (rc != NGX_OK) {            // rc为NGX_CONF_FILE_DONE/NGX_CONF_BLOCK_DONE是也会退出循环            // NGX_CONF_FILE_DONE好理解, 意味着配置文件已解析完毕;            // NGX_CONF_BLOCK_DON意味着配置块解析完毕, 也就是遇到"}"            break;        }        if (cf->handler) {            // cf的handler是一个函数指针, 用来注册自定义的token处理器            // 当cf的handler指针不为空时            // 调用cf->handler处理器            rv = (*cf->handler)(cf, NULL, cf->handler_conf);            if (rv == NGX_CONF_OK) {                continue;            } else if (rv == NGX_CONF_ERROR) {                rc = NGX_ERROR;                break;            } else {                ngx_log_error(NGX_LOG_EMERG, cf->log, 0,                             "%s in %s:%d",                             rv,                             cf->conf_file->file.name.data,                             cf->conf_file->line);                rc = NGX_ERROR;                break;            }        }        // cf->args数组中存放的是此次解析到的全部token        name = (ngx_str_t *) cf->args->elts;        found = 0;        // 对ngx_modules数组进行遍历, 也就是对Nginx模块进行遍历        for (m = 0; rc != NGX_ERROR && !found && ngx_modules[m]; m++) {            if (ngx_modules[m]->type != NGX_CONF_MODULE                && ngx_modules[m]->type != cf->module_type)            {                // 这里意味模块类型或者cf->module_type不为NGX_CONF_MODULE时, 直接跳过                continue;            }            // commands为模块的指令集, 一个指令对应一个配置选项            cmd = ngx_modules[m]->commands;            if (cmd == NULL) {                // 如果模块的指令集为空, 那么直接跳过                continue;            }            // 遍历模块的指令集            while (cmd->name.len) {                if (name->len == cmd->name.len                    && ngx_strcmp(name->data, cmd->name.data) == 0)                {                    // 如果解析到的token与指令名一致                                        // 置found为1                    found = 1;#if 0ngx_log_debug(cf->log, "command '%s'" _ cmd->name.data);#endif                    if ((cmd->type & cf->cmd_type) == 0) {                        // 如果指令的类型不在cf->cmd_type指定的类型中, 那么退出当前循环                        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,                                      "directive \"%s\" in %s:%d "                                      "is not allowed here",                                      name->data,                                      cf->conf_file->file.name.data,                                      cf->conf_file->line);                        rc = NGX_ERROR;                        break;                    }                    // 判断解析到的token格式有效性                    if (cmd->type & NGX_CONF_ANY) {                        // 如果指令的类型为NGX_CONF_ANY, 意味着无论如何解析到的token格式都是有效的                        valid = 1;                    } else if (cmd->type & NGX_CONF_FLAG) {                        // 如果指令的类型为NGX_CONF_FLAG, 那么解析到的token数量为2时, token格式才是有效的                        // 譬如"multi_accept on"就是有效的                        if (cf->args->nelts == 2) {                            valid = 1;                        } else {                            valid = 0;                        }                    } else if (cmd->type & NGX_CONF_1MORE) {                        // 如果指令的类型为NGX_CONF_1MORE, 那么解析到的token数量至少为1 + 1时, token格式才是有效的;                        // 也就是"<配置选项> <值> <...>"                        if (cf->args->nelts > 1) {                            valid = 1;                        } else {                            valid = 0;                        }                    } else if (cmd->type & NGX_CONF_2MORE) {                        // 如果指令的类型为NGX_CONF_2MORE, 那么解析到的token数量至少为1 + 2时, token格式才是有效的;                        // 也就是"<配置选项> <值1> <值2> <...>"                        if (cf->args->nelts > 2) {                            valid = 1;                        } else {                            valid = 0;                        }                    } else if (cf->args->nelts <= 10                               && (cmd->type                                   & argument_number[cf->args->nelts - 1]))                    {                        /* 如果解析到的token数量不超过10, 且指令的类型为支持相应数量的参数, 那么token格式为有效的                           note: 可以看到argument_number数组的元素个数为8, 那么当解析到的token数量超过8时,                                  这里会出现数组访问越界的Bug, 所幸的是这种参数超过7的指令是很罕见的 */                        valid = 1;                    } else {                        // 其他情况下的token格式是无效的                        valid = 0;                    }                    if (!valid) {                        // 如果token格式无效, 那么退出当前循环                        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,                                      "invalid number arguments in "                                      "directive \"%s\" in %s:%d",                                      name->data,                                      cf->conf_file->file.name.data,                                      cf->conf_file->line);                        rc = NGX_ERROR;                        break;                    }                    conf = NULL;                    if (cmd->type & NGX_DIRECT_CONF) {                        conf = ((void **) cf->ctx)[ngx_modules[m]->index];                    } else if (cmd->type & NGX_MAIN_CONF) {                        conf = &(((void **) cf->ctx)[ngx_modules[m]->index]);                    } else if (cf->ctx) {                        confp = *(void **) ((char *) cf->ctx + cmd->conf);                        if (confp) {                            conf = confp[ngx_modules[m]->ctx_index];                        }                    }                    // 调用指令的set函数设置该指令                    rv = cmd->set(cf, cmd, conf);#if 0ngx_log_debug(cf->log, "rv: %d" _ rv);#endif                    if (rv == NGX_CONF_OK) {                        break;                    } else if (rv == NGX_CONF_ERROR) {                        rc = NGX_ERROR;                        break;                    } else {                        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,                                      "the \"%s\" directive %s in %s:%d",                                      name->data, rv,                                      cf->conf_file->file.name.data,                                      cf->conf_file->line);                        rc = NGX_ERROR;                        break;                    }                }                // cmd指向下一个指令                cmd++;            }        }        if (!found) {            // 如果在此轮解析中, 解析到的token中的选项不与任何一个模块的任何一个指令匹配,            // 说明配置项有误, 退出当前循环, 停止解析过程;            // 这也意味着在配置文件中不可以出现任何一个无意义的配置行或配置块            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,                          "unknown directive \"%s\" in %s:%d",                          name->data,                          cf->conf_file->file.name.data,                          cf->conf_file->line);            rc = NGX_ERROR;            break;        }        if (rc == NGX_ERROR) {            break;        }    }    if (filename) {        // 还原cf->conf_file为之前保存的值        cf->conf_file = prev;        // 关闭打开的配置文件        if (ngx_close_file(fd) == NGX_FILE_ERROR) {            ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,                          ngx_close_file_n " %s failed",                          cf->conf_file->file.name.data);            return NGX_CONF_ERROR;        }    }    if (rc == NGX_ERROR) {        return NGX_CONF_ERROR;    }    return NGX_CONF_OK;}


原创粉丝点击