| |
| /* |
| * Copyright (C) Ruslan Ermilov |
| * Copyright (C) Nginx, Inc. |
| */ |
| |
| |
| #include <ngx_config.h> |
| #include <ngx_core.h> |
| #include <ngx_stream.h> |
| |
| |
| static char *ngx_stream_upstream_zone(ngx_conf_t *cf, ngx_command_t *cmd, |
| void *conf); |
| static ngx_int_t ngx_stream_upstream_init_zone(ngx_shm_zone_t *shm_zone, |
| void *data); |
| static ngx_stream_upstream_rr_peers_t *ngx_stream_upstream_zone_copy_peers( |
| ngx_slab_pool_t *shpool, ngx_stream_upstream_srv_conf_t *uscf); |
| static ngx_stream_upstream_rr_peer_t *ngx_stream_upstream_zone_copy_peer( |
| ngx_stream_upstream_rr_peers_t *peers, ngx_stream_upstream_rr_peer_t *src); |
| |
| |
| static ngx_command_t ngx_stream_upstream_zone_commands[] = { |
| |
| { ngx_string("zone"), |
| NGX_STREAM_UPS_CONF|NGX_CONF_TAKE12, |
| ngx_stream_upstream_zone, |
| 0, |
| 0, |
| NULL }, |
| |
| ngx_null_command |
| }; |
| |
| |
| static ngx_stream_module_t ngx_stream_upstream_zone_module_ctx = { |
| NULL, /* preconfiguration */ |
| NULL, /* postconfiguration */ |
| |
| NULL, /* create main configuration */ |
| NULL, /* init main configuration */ |
| |
| NULL, /* create server configuration */ |
| NULL /* merge server configuration */ |
| }; |
| |
| |
| ngx_module_t ngx_stream_upstream_zone_module = { |
| NGX_MODULE_V1, |
| &ngx_stream_upstream_zone_module_ctx, /* module context */ |
| ngx_stream_upstream_zone_commands, /* module directives */ |
| NGX_STREAM_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 char * |
| ngx_stream_upstream_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) |
| { |
| ssize_t size; |
| ngx_str_t *value; |
| ngx_stream_upstream_srv_conf_t *uscf; |
| ngx_stream_upstream_main_conf_t *umcf; |
| |
| uscf = ngx_stream_conf_get_module_srv_conf(cf, ngx_stream_upstream_module); |
| umcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_upstream_module); |
| |
| value = cf->args->elts; |
| |
| if (!value[1].len) { |
| ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, |
| "invalid zone name \"%V\"", &value[1]); |
| return NGX_CONF_ERROR; |
| } |
| |
| if (cf->args->nelts == 3) { |
| size = ngx_parse_size(&value[2]); |
| |
| if (size == NGX_ERROR) { |
| ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, |
| "invalid zone size \"%V\"", &value[2]); |
| return NGX_CONF_ERROR; |
| } |
| |
| if (size < (ssize_t) (8 * ngx_pagesize)) { |
| ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, |
| "zone \"%V\" is too small", &value[1]); |
| return NGX_CONF_ERROR; |
| } |
| |
| } else { |
| size = 0; |
| } |
| |
| uscf->shm_zone = ngx_shared_memory_add(cf, &value[1], size, |
| &ngx_stream_upstream_module); |
| if (uscf->shm_zone == NULL) { |
| return NGX_CONF_ERROR; |
| } |
| |
| uscf->shm_zone->init = ngx_stream_upstream_init_zone; |
| uscf->shm_zone->data = umcf; |
| |
| uscf->shm_zone->noreuse = 1; |
| |
| return NGX_CONF_OK; |
| } |
| |
| |
| static ngx_int_t |
| ngx_stream_upstream_init_zone(ngx_shm_zone_t *shm_zone, void *data) |
| { |
| size_t len; |
| ngx_uint_t i; |
| ngx_slab_pool_t *shpool; |
| ngx_stream_upstream_rr_peers_t *peers, **peersp; |
| ngx_stream_upstream_srv_conf_t *uscf, **uscfp; |
| ngx_stream_upstream_main_conf_t *umcf; |
| |
| shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; |
| umcf = shm_zone->data; |
| uscfp = umcf->upstreams.elts; |
| |
| if (shm_zone->shm.exists) { |
| peers = shpool->data; |
| |
| for (i = 0; i < umcf->upstreams.nelts; i++) { |
| uscf = uscfp[i]; |
| |
| if (uscf->shm_zone != shm_zone) { |
| continue; |
| } |
| |
| uscf->peer.data = peers; |
| peers = peers->zone_next; |
| } |
| |
| return NGX_OK; |
| } |
| |
| len = sizeof(" in upstream zone \"\"") + shm_zone->shm.name.len; |
| |
| shpool->log_ctx = ngx_slab_alloc(shpool, len); |
| if (shpool->log_ctx == NULL) { |
| return NGX_ERROR; |
| } |
| |
| ngx_sprintf(shpool->log_ctx, " in upstream zone \"%V\"%Z", |
| &shm_zone->shm.name); |
| |
| |
| /* copy peers to shared memory */ |
| |
| peersp = (ngx_stream_upstream_rr_peers_t **) (void *) &shpool->data; |
| |
| for (i = 0; i < umcf->upstreams.nelts; i++) { |
| uscf = uscfp[i]; |
| |
| if (uscf->shm_zone != shm_zone) { |
| continue; |
| } |
| |
| peers = ngx_stream_upstream_zone_copy_peers(shpool, uscf); |
| if (peers == NULL) { |
| return NGX_ERROR; |
| } |
| |
| *peersp = peers; |
| peersp = &peers->zone_next; |
| } |
| |
| return NGX_OK; |
| } |
| |
| |
| static ngx_stream_upstream_rr_peers_t * |
| ngx_stream_upstream_zone_copy_peers(ngx_slab_pool_t *shpool, |
| ngx_stream_upstream_srv_conf_t *uscf) |
| { |
| ngx_str_t *name; |
| ngx_stream_upstream_rr_peer_t *peer, **peerp; |
| ngx_stream_upstream_rr_peers_t *peers, *backup; |
| |
| peers = ngx_slab_alloc(shpool, sizeof(ngx_stream_upstream_rr_peers_t)); |
| if (peers == NULL) { |
| return NULL; |
| } |
| |
| ngx_memcpy(peers, uscf->peer.data, sizeof(ngx_stream_upstream_rr_peers_t)); |
| |
| name = ngx_slab_alloc(shpool, sizeof(ngx_str_t)); |
| if (name == NULL) { |
| return NULL; |
| } |
| |
| name->data = ngx_slab_alloc(shpool, peers->name->len); |
| if (name->data == NULL) { |
| return NULL; |
| } |
| |
| ngx_memcpy(name->data, peers->name->data, peers->name->len); |
| name->len = peers->name->len; |
| |
| peers->name = name; |
| |
| peers->shpool = shpool; |
| |
| for (peerp = &peers->peer; *peerp; peerp = &peer->next) { |
| /* pool is unlocked */ |
| peer = ngx_stream_upstream_zone_copy_peer(peers, *peerp); |
| if (peer == NULL) { |
| return NULL; |
| } |
| |
| *peerp = peer; |
| } |
| |
| if (peers->next == NULL) { |
| goto done; |
| } |
| |
| backup = ngx_slab_alloc(shpool, sizeof(ngx_stream_upstream_rr_peers_t)); |
| if (backup == NULL) { |
| return NULL; |
| } |
| |
| ngx_memcpy(backup, peers->next, sizeof(ngx_stream_upstream_rr_peers_t)); |
| |
| backup->name = name; |
| |
| backup->shpool = shpool; |
| |
| for (peerp = &backup->peer; *peerp; peerp = &peer->next) { |
| /* pool is unlocked */ |
| peer = ngx_stream_upstream_zone_copy_peer(backup, *peerp); |
| if (peer == NULL) { |
| return NULL; |
| } |
| |
| *peerp = peer; |
| } |
| |
| peers->next = backup; |
| |
| done: |
| |
| uscf->peer.data = peers; |
| |
| return peers; |
| } |
| |
| |
| static ngx_stream_upstream_rr_peer_t * |
| ngx_stream_upstream_zone_copy_peer(ngx_stream_upstream_rr_peers_t *peers, |
| ngx_stream_upstream_rr_peer_t *src) |
| { |
| ngx_slab_pool_t *pool; |
| ngx_stream_upstream_rr_peer_t *dst; |
| |
| pool = peers->shpool; |
| |
| dst = ngx_slab_calloc_locked(pool, sizeof(ngx_stream_upstream_rr_peer_t)); |
| if (dst == NULL) { |
| return NULL; |
| } |
| |
| if (src) { |
| ngx_memcpy(dst, src, sizeof(ngx_stream_upstream_rr_peer_t)); |
| dst->sockaddr = NULL; |
| dst->name.data = NULL; |
| dst->server.data = NULL; |
| } |
| |
| dst->sockaddr = ngx_slab_calloc_locked(pool, sizeof(ngx_sockaddr_t)); |
| if (dst->sockaddr == NULL) { |
| goto failed; |
| } |
| |
| dst->name.data = ngx_slab_calloc_locked(pool, NGX_SOCKADDR_STRLEN); |
| if (dst->name.data == NULL) { |
| goto failed; |
| } |
| |
| if (src) { |
| ngx_memcpy(dst->sockaddr, src->sockaddr, src->socklen); |
| ngx_memcpy(dst->name.data, src->name.data, src->name.len); |
| |
| dst->server.data = ngx_slab_alloc_locked(pool, src->server.len); |
| if (dst->server.data == NULL) { |
| goto failed; |
| } |
| |
| ngx_memcpy(dst->server.data, src->server.data, src->server.len); |
| } |
| |
| return dst; |
| |
| failed: |
| |
| if (dst->server.data) { |
| ngx_slab_free_locked(pool, dst->server.data); |
| } |
| |
| if (dst->name.data) { |
| ngx_slab_free_locked(pool, dst->name.data); |
| } |
| |
| if (dst->sockaddr) { |
| ngx_slab_free_locked(pool, dst->sockaddr); |
| } |
| |
| ngx_slab_free_locked(pool, dst); |
| |
| return NULL; |
| } |