Воскресенье, 9 Ноябрь 2008

nginx programming guide: добавляем свой блок в конфигурацию

Давно я не писал про потроха nginx, пора исправляться.

Сегодня речь пойдет о создании своих блоков с директивами в конфиге.

Для примера напишем модуль, который позволит написать в конфиге:

dc sample {
    uri http://catap.ru/;
}

Начнем мы с анатомии модулей в nginx. Каждый модуль описывается структурой:

typedef struct ngx_module_s      ngx_module_t;
struct ngx_module_s {
    ngx_uint_t            ctx_index;
    ngx_uint_t            index;

    ngx_uint_t            spare0;
    ngx_uint_t            spare1;
    ngx_uint_t            spare2;
    ngx_uint_t            spare3;

    ngx_uint_t            version;

    void                 *ctx;
    ngx_command_t        *commands;
    ngx_uint_t            type;

    ngx_int_t           (*init_master)(ngx_log_t *log);

    ngx_int_t           (*init_module)(ngx_cycle_t *cycle);

    ngx_int_t           (*init_process)(ngx_cycle_t *cycle);
    ngx_int_t           (*init_thread)(ngx_cycle_t *cycle);
    void                (*exit_thread)(ngx_cycle_t *cycle);
    void                (*exit_process)(ngx_cycle_t *cycle);

    void                (*exit_master)(ngx_cycle_t *cycle);

    uintptr_t             spare_hook0;
    uintptr_t             spare_hook1;
    uintptr_t             spare_hook2;
    uintptr_t             spare_hook3;
    uintptr_t             spare_hook4;
    uintptr_t             spare_hook5;
    uintptr_t             spare_hook6;
    uintptr_t             spare_hook7;
};

Если детально расматривать поля ngx_module_t:

  • ctx_index — индекс данных модуля в контексте модуля, реализующего данный тип
  • index — индекс модуля в массиве модулей
  • version — версия модуля
  • ctx — представляет из себя указатель на контекст конфигурации данного типа модуля, для NGX_CORE_MODULE это ngx_core_module_t, для NGX_HTTP_MODULE — ngx_http_module_t.
  • commands — указатель на перечень комманд (ngx_command_t) для конфигурации
  • type — тип (NGX_CORE_MODULE, NGX_HTTP_MODULE и т.п.)
  • *init_master — хук, который сработает во время инициализации master процесса
  • *init_module — хук, который сработает во время инициализации модуля
  • *init_process — хук, который сработает во время инициализации рабочего (worker) процесса
  • *init_thread — хук, который сработает во время инициализации треда
  • *exit_thread — хук, который сработает во время завершения треда
  • *exit_process — хук, который сработает во время завершения рабочего (worker) процесса
  • *exit_master — хук, который сработает во время завершения master процесса

spare0spare3 и spare_hook0spare_hook7 зарезервированы на будущее.

Перечень комманд — это ngx_command_t, который выглядит так:

typedef struct ngx_module_s      ngx_module_t;
struct ngx_command_s {
    ngx_str_t             name;
    ngx_uint_t            type;
    char               *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
    ngx_uint_t            conf;
    ngx_uint_t            offset;
    void                 *post;
};

где:

  • name — имя директивы конфигурации
  • type — тип директивы. Например, количество аргументов или на каком уровне она располагается.
  • *set — указатель на функцию для инициализации значения
  • conf — смещение до конфига от начала контекста
  • offset — смещение относительно начала конфига до поля, которое настраиваем
  • post — указатель на список handlers, которые будут выполнены после конфигурации.

Типичный модуль инициализируется так:

ngx_module_t  ngx_dc_module = {
    NGX_MODULE_V1,
    &ngx_dc_module_ctx,                  /* module context */
    ngx_dc_commands,                     /* module directives */
    NGX_CORE_MODULE,                     /* module type */
    NULL,                                /* init master */
    NULL,                                /* init module */
    NULL,                                /* init process */
    NULL,                                /* init thread */
    NULL,                                /* exit thread */
    NULL,                                /* exit process */
    NULL,                                /* exit master */
    NGX_MODULE_V1_PADDING
};

Тут используется два макроса для упрощения записи, которые выглядят так:

#define NGX_MODULE_V1          0, 0, 0, 0, 0, 0, 1
#define NGX_MODULE_V1_PADDING  0, 0, 0, 0, 0, 0, 0, 0

Между макросами распологаются «данные модуля».

Наш пример будет состоять из двух модулей. В первом мы реализуем блок и это будет модуль с типом NGX_CORE_MODULE, а во втором логику модуля и тип будет NGX_DC_MODULE.

В nginx есть 5 типа модулей, которые объявляются макросами:

#define NGX_HTTP_MODULE      0x50545448  /* "HTTP" */
#define NGX_MAIL_MODULE      0x4C49414D  /* "MAIL" */
#define NGX_CORE_MODULE      0x45524F43  /* "CORE" */
#define NGX_CONF_MODULE      0x464E4F43  /* "CONF" */
#define NGX_EVENT_MODULE     0x544E5645  /* "EVNT" */

Цифры в индексе — это ascii коды букв в комментариях ☺, тогда мы можем «объявить» наш тип:

#define NGX_DC_MODULE        0x00004443  /* "DC" */

Теперь нам нужен контекст модуля. Контекст модуля это ngx_core_module_t (зависит от типа модуля, у нас пока NGX_CORE_MODULE):

typedef struct {
    ngx_str_t             name;
    void               *(*create_conf)(ngx_cycle_t *cycle);
    char               *(*init_conf)(ngx_cycle_t *cycle, void *conf);
} ngx_core_module_t;

Тут все просто:

  • name — имя модуля для ругани, если не удается сконфигурироваться ☺
  • *create_conf — указатель на функцию создания конфигурации
  • *int_conf — указатель на функцию инициализации конфигурации

Для нашего модуля достаточно простой нужен контекст:

static ngx_core_module_t  ngx_dc_module_ctx = {
    ngx_string("dc"),
    NULL,
    NULL
};

Теперь мы можем сделать команду:

static ngx_command_t  ngx_dc_commands[] = {

    { ngx_string("dc"),
      NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE1,
      ngx_dc_block,
      0,
      0,
      NULL },

      ngx_null_command
};

Тут я использую ngx_null_command, который из себя представляет макрос‑sentinel:

#define ngx_null_command  { ngx_null_string, 0, NULL, 0, 0, NULL }

Теперь нам нужен контекст:

typedef struct {
    ngx_str_t   name;
    void      **conf;
} ngx_dc_conf_ctx_t;

и конфигурация:

typedef struct {
    ngx_str_t   uri;
} ngx_dc_core_conf_t;

и наш модуль:

typedef struct {
    void       *(*create_conf)(ngx_conf_t *cf);
    char       *(*init_conf)(ngx_conf_t *cf, void *conf);
} ngx_dc_module_t;

еще нам нужен тип для команд

#define NGX_DC_CONF          0x02000000

и т.к. у нас в контексте не только указатель на конфигурацию, то нам нужно смещение

#define NGX_DC_CONF_OFFSET   offsetof(ngx_dc_conf_ctx_t, conf)

Дальше нам надо только описать блок конфигурации (ngx_dc_block) — он будет простым — у нашего dc будет только один конфиг, а не три, как в NGX_HTTP_MODULE.

static char *ngx_dc_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    char               *rv;
    ngx_str_t          *value;
    ngx_uint_t          i;
    ngx_uint_t          mi;
    ngx_conf_t          pcf;
    ngx_dc_module_t    *m;
    ngx_dc_conf_ctx_t  *ctx;

    /* the dc context */

    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_dc_conf_ctx_t));
    if (ctx == NULL) {
        return NGX_CONF_ERROR;
    }

    *(ngx_dc_conf_ctx_t **) conf = ctx;

    ngx_dc_max_module = 0;
    for (i = 0; ngx_modules[i]; i++) {
        if (ngx_modules[i]->type != NGX_DC_MODULE) {
            continue;
        }

        ngx_modules[i]->ctx_index = ngx_dc_max_module++;
    }

    value = cf->args->elts;

    ctx->name = value[1];

    /* the dc conf context, it is the same in the all dc contexts */

    ctx->conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_dc_max_module);
    if (ctx->conf == NULL) {
        return NGX_CONF_ERROR;
    }

    for (i = 0; ngx_modules[i]; i++) {
        if (ngx_modules[i]->type != NGX_DC_MODULE) {
            continue;
        }

        m = ngx_modules[i]->ctx;
        mi = ngx_modules[i]->ctx_index;

        if (m->create_conf) {
            ctx->conf[mi] = m->create_conf(cf);
            if (ctx->conf[mi] == NULL) {
                return NGX_CONF_ERROR;
            }
        }
    }

    pcf = *cf;
    cf->ctx = ctx;

    cf->module_type = NGX_DC_MODULE;
    cf->cmd_type = NGX_DC_CONF;

    rv = ngx_conf_parse(cf, NULL);

    *cf = pcf;

    if (rv != NGX_CONF_OK) {
        *cf = pcf;
        return rv;
    }

    for (i = 0; ngx_modules[i]; i++) {
        if (ngx_modules[i]->type != NGX_DC_MODULE) {
            continue;
        }

        m = ngx_modules[i]->ctx;
        mi = ngx_modules[i]->ctx_index;

        if (m->init_conf) {
            rv = m->init_conf(cf, ctx->conf[mi]);
            if (rv != NGX_CONF_OK) {
            return rv;
            }
        }
    }

    return NGX_CONF_OK;
}

Теперь мы можем написать директивы привычным способом:

static void *ngx_dc_core_create_conf(ngx_conf_t *cf);

static ngx_command_t  ngx_dc_core_commands[] = {

    { ngx_string("uri"),
      NGX_DC_CONF|NGX_CONF_TAKE1,
      ngx_conf_set_str_slot,
      NGX_DC_CONF_OFFSET,
      offsetof(ngx_dc_core_conf_t, uri),
      NULL },

      ngx_null_command
};

ngx_dc_module_t ngx_dc_core_module_ctx = {
    ngx_dc_core_create_conf,    /* create configuration */
    NULL            /* init configuration */
};

ngx_module_t  ngx_dc_core_module = {
    NGX_MODULE_V1,
    &ngx_dc_core_module_ctx,    /* module context */
    ngx_dc_core_commands,   /* module directives */
    NGX_DC_MODULE,      /* module type */
    NULL,           /* init master */
    NULL,           /* init module */
    NULL,           /* init process */
    NULL,           /* init thread */
    NULL,           /* exit thread */
    NULL,           /* exit process */
    NULL,           /* exit master */
    NGX_MODULE_V1_PADDING
};

static void *
ngx_dc_core_create_conf(ngx_conf_t *cf)
{
    ngx_dc_core_conf_t *conf;

    conf = ngx_pcalloc(cf->pool, sizeof(ngx_dc_core_conf_t));
    if (conf == NULL) {
        return NGX_CONF_ERROR;
    }

    return conf;
}

Вот пример этого кода, собранный в кучу: config, ngx_dc.c и ngx_dc.h.

Написано в: 2:47 | 0 комментариев | | теги: , , , , | постоянная ссылка |
Добавить пост в:   Delicious Reddit Slashdot Digg Technorati Google


Последние комментарии

Комментарии

К этой публикации комментариев нет

Форма комментирования для «nginx programming guide: добавляем свой блок в конфигурацию»

Обязательное поле. Не больше 30 символов.

Обязательное поле