1
2/*
3 * Copyright (C) Ruslan Ermilov
4 * Copyright (C) Nginx, Inc.
5 */
6
7
8#include <ngx_config.h>
9#include <ngx_core.h>
10#include <ngx_stream.h>
11
12
13static char *ngx_stream_upstream_zone(ngx_conf_t *cf, ngx_command_t *cmd,
14    void *conf);
15static ngx_int_t ngx_stream_upstream_init_zone(ngx_shm_zone_t *shm_zone,
16    void *data);
17static ngx_stream_upstream_rr_peers_t *ngx_stream_upstream_zone_copy_peers(
18    ngx_slab_pool_t *shpool, ngx_stream_upstream_srv_conf_t *uscf);
19
20
21static ngx_command_t  ngx_stream_upstream_zone_commands[] = {
22
23    { ngx_string("zone"),
24      NGX_STREAM_UPS_CONF|NGX_CONF_TAKE12,
25      ngx_stream_upstream_zone,
26      0,
27      0,
28      NULL },
29
30      ngx_null_command
31};
32
33
34static ngx_stream_module_t  ngx_stream_upstream_zone_module_ctx = {
35    NULL,                                  /* preconfiguration */
36    NULL,                                  /* postconfiguration */
37
38    NULL,                                  /* create main configuration */
39    NULL,                                  /* init main configuration */
40
41    NULL,                                  /* create server configuration */
42    NULL                                   /* merge server configuration */
43};
44
45
46ngx_module_t  ngx_stream_upstream_zone_module = {
47    NGX_MODULE_V1,
48    &ngx_stream_upstream_zone_module_ctx,  /* module context */
49    ngx_stream_upstream_zone_commands,     /* module directives */
50    NGX_STREAM_MODULE,                     /* module type */
51    NULL,                                  /* init master */
52    NULL,                                  /* init module */
53    NULL,                                  /* init process */
54    NULL,                                  /* init thread */
55    NULL,                                  /* exit thread */
56    NULL,                                  /* exit process */
57    NULL,                                  /* exit master */
58    NGX_MODULE_V1_PADDING
59};
60
61
62static char *
63ngx_stream_upstream_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
64{
65    ssize_t                           size;
66    ngx_str_t                        *value;
67    ngx_stream_upstream_srv_conf_t   *uscf;
68    ngx_stream_upstream_main_conf_t  *umcf;
69
70    uscf = ngx_stream_conf_get_module_srv_conf(cf, ngx_stream_upstream_module);
71    umcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_upstream_module);
72
73    value = cf->args->elts;
74
75    if (!value[1].len) {
76        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
77                           "invalid zone name \"%V\"", &value[1]);
78        return NGX_CONF_ERROR;
79    }
80
81    if (cf->args->nelts == 3) {
82        size = ngx_parse_size(&value[2]);
83
84        if (size == NGX_ERROR) {
85            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
86                               "invalid zone size \"%V\"", &value[2]);
87            return NGX_CONF_ERROR;
88        }
89
90        if (size < (ssize_t) (8 * ngx_pagesize)) {
91            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
92                               "zone \"%V\" is too small", &value[1]);
93            return NGX_CONF_ERROR;
94        }
95
96    } else {
97        size = 0;
98    }
99
100    uscf->shm_zone = ngx_shared_memory_add(cf, &value[1], size,
101                                           &ngx_stream_upstream_module);
102    if (uscf->shm_zone == NULL) {
103        return NGX_CONF_ERROR;
104    }
105
106    uscf->shm_zone->init = ngx_stream_upstream_init_zone;
107    uscf->shm_zone->data = umcf;
108
109    uscf->shm_zone->noreuse = 1;
110
111    return NGX_CONF_OK;
112}
113
114
115static ngx_int_t
116ngx_stream_upstream_init_zone(ngx_shm_zone_t *shm_zone, void *data)
117{
118    size_t                            len;
119    ngx_uint_t                        i;
120    ngx_slab_pool_t                  *shpool;
121    ngx_stream_upstream_rr_peers_t   *peers, **peersp;
122    ngx_stream_upstream_srv_conf_t   *uscf, **uscfp;
123    ngx_stream_upstream_main_conf_t  *umcf;
124
125    shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
126    umcf = shm_zone->data;
127    uscfp = umcf->upstreams.elts;
128
129    if (shm_zone->shm.exists) {
130        peers = shpool->data;
131
132        for (i = 0; i < umcf->upstreams.nelts; i++) {
133            uscf = uscfp[i];
134
135            if (uscf->shm_zone != shm_zone) {
136                continue;
137            }
138
139            uscf->peer.data = peers;
140            peers = peers->zone_next;
141        }
142
143        return NGX_OK;
144    }
145
146    len = sizeof(" in upstream zone \"\"") + shm_zone->shm.name.len;
147
148    shpool->log_ctx = ngx_slab_alloc(shpool, len);
149    if (shpool->log_ctx == NULL) {
150        return NGX_ERROR;
151    }
152
153    ngx_sprintf(shpool->log_ctx, " in upstream zone \"%V\"%Z",
154                &shm_zone->shm.name);
155
156
157    /* copy peers to shared memory */
158
159    peersp = (ngx_stream_upstream_rr_peers_t **) (void *) &shpool->data;
160
161    for (i = 0; i < umcf->upstreams.nelts; i++) {
162        uscf = uscfp[i];
163
164        if (uscf->shm_zone != shm_zone) {
165            continue;
166        }
167
168        peers = ngx_stream_upstream_zone_copy_peers(shpool, uscf);
169        if (peers == NULL) {
170            return NGX_ERROR;
171        }
172
173        *peersp = peers;
174        peersp = &peers->zone_next;
175    }
176
177    return NGX_OK;
178}
179
180
181static ngx_stream_upstream_rr_peers_t *
182ngx_stream_upstream_zone_copy_peers(ngx_slab_pool_t *shpool,
183    ngx_stream_upstream_srv_conf_t *uscf)
184{
185    ngx_stream_upstream_rr_peer_t   *peer, **peerp;
186    ngx_stream_upstream_rr_peers_t  *peers, *backup;
187
188    peers = ngx_slab_alloc(shpool, sizeof(ngx_stream_upstream_rr_peers_t));
189    if (peers == NULL) {
190        return NULL;
191    }
192
193    ngx_memcpy(peers, uscf->peer.data, sizeof(ngx_stream_upstream_rr_peers_t));
194
195    peers->shpool = shpool;
196
197    for (peerp = &peers->peer; *peerp; peerp = &peer->next) {
198        /* pool is unlocked */
199        peer = ngx_slab_calloc_locked(shpool,
200                                      sizeof(ngx_stream_upstream_rr_peer_t));
201        if (peer == NULL) {
202            return NULL;
203        }
204
205        ngx_memcpy(peer, *peerp, sizeof(ngx_stream_upstream_rr_peer_t));
206
207        *peerp = peer;
208    }
209
210    if (peers->next == NULL) {
211        goto done;
212    }
213
214    backup = ngx_slab_alloc(shpool, sizeof(ngx_stream_upstream_rr_peers_t));
215    if (backup == NULL) {
216        return NULL;
217    }
218
219    ngx_memcpy(backup, peers->next, sizeof(ngx_stream_upstream_rr_peers_t));
220
221    backup->shpool = shpool;
222
223    for (peerp = &backup->peer; *peerp; peerp = &peer->next) {
224        /* pool is unlocked */
225        peer = ngx_slab_calloc_locked(shpool,
226                                      sizeof(ngx_stream_upstream_rr_peer_t));
227        if (peer == NULL) {
228            return NULL;
229        }
230
231        ngx_memcpy(peer, *peerp, sizeof(ngx_stream_upstream_rr_peer_t));
232
233        *peerp = peer;
234    }
235
236    peers->next = backup;
237
238done:
239
240    uscf->peer.data = peers;
241
242    return peers;
243}
244