Skip to content

Commit d6e529a

Browse files
authored
Merge pull request #54 from leev/auto_reload
Add support for auto reloading the database if it has changed
2 parents 7eaa956 + 39a8abf commit d6e529a

File tree

3 files changed

+204
-3
lines changed

3 files changed

+204
-3
lines changed

README.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ The free GeoLite2 databases are available from [Maxminds website](http://dev.max
4848
http {
4949
...
5050
geoip2 /etc/maxmind-country.mmdb {
51+
auto_reload 5m;
5152
$geoip2_metadata_country_build metadata build_epoch;
5253
$geoip2_data_country_code default=US source=$variable_with_ip country iso_code;
5354
$geoip2_data_country_name country names en;
@@ -78,7 +79,17 @@ Retrieve metadata regarding the geoip database.
7879
```
7980
$variable_name metadata <field>
8081
```
81-
Currently the only metadata field supported is build_epoch.
82+
Available fields:
83+
- build_epoch: the build timestamp of the maxmind database.
84+
- last_check: the last time the database was checked for changes (when using auto_reload)
85+
- last_reload: the last time the database was reloaded (when using auto_reload)
86+
87+
##### Autoreload (default: disabled):
88+
Enabling auto reload will have nginx check the modification time of the database at the specified
89+
interval and reload it if it has changed.
90+
```
91+
auto_reload <interval>
92+
```
8293

8394
##### GeoIP:
8495
```

ngx_http_geoip2_module.c

Lines changed: 95 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
typedef struct {
1616
MMDB_s mmdb;
1717
MMDB_lookup_result_s result;
18+
time_t last_check;
19+
time_t last_change;
20+
time_t check_interval;
1821
#if (NGX_HAVE_INET6)
1922
uint8_t address[16];
2023
#else
@@ -41,6 +44,8 @@ typedef struct {
4144
} ngx_http_geoip2_metadata_t;
4245

4346

47+
static ngx_int_t ngx_http_geoip2_reload(ngx_http_geoip2_db_t *database,
48+
ngx_log_t *log);
4449
static ngx_int_t ngx_http_geoip2_variable(ngx_http_request_t *r,
4550
ngx_http_variable_value_t *v, uintptr_t data);
4651
static ngx_int_t ngx_http_geoip2_metadata(ngx_http_request_t *r,
@@ -49,6 +54,8 @@ static void *ngx_http_geoip2_create_conf(ngx_conf_t *cf);
4954
static char *ngx_http_geoip2_init_conf(ngx_conf_t *cf, void *conf);
5055
static char *ngx_http_geoip2(ngx_conf_t *cf, ngx_command_t *cmd,
5156
void *conf);
57+
static char *ngx_http_geoip2_parse_config(ngx_conf_t *cf, ngx_command_t *dummy,
58+
void *conf);
5259
static char *ngx_http_geoip2_add_variable(ngx_conf_t *cf, ngx_command_t *dummy,
5360
void *conf);
5461
static char *ngx_http_geoip2_add_variable_geodata(ngx_conf_t *cf,
@@ -129,6 +136,41 @@ ngx_module_t ngx_http_geoip2_module = {
129136
};
130137

131138

139+
static ngx_int_t
140+
ngx_http_geoip2_reload(ngx_http_geoip2_db_t *database, ngx_log_t *log)
141+
{
142+
struct stat attr;
143+
MMDB_s tmpdb;
144+
int status;
145+
146+
if (database->check_interval > 0
147+
&& database->last_check + database->check_interval <= ngx_time()) {
148+
database->last_check = ngx_time();
149+
stat(database->mmdb.filename, &attr);
150+
151+
if (attr.st_mtime > database->last_change) {
152+
status = MMDB_open(database->mmdb.filename, MMDB_MODE_MMAP, &tmpdb);
153+
154+
if (status != MMDB_SUCCESS) {
155+
ngx_log_error(NGX_LOG_ERR, log, 0,
156+
"MMDB_open(\"%s\") failed to reload - %s",
157+
database->mmdb.filename, MMDB_strerror(status));
158+
return NGX_ERROR;
159+
}
160+
161+
database->last_change = attr.st_mtime;
162+
MMDB_close(&database->mmdb);
163+
database->mmdb = tmpdb;
164+
165+
ngx_log_error(NGX_LOG_INFO, log, 0, "Reload MMDB \"%s\"",
166+
tmpdb.filename);
167+
}
168+
}
169+
170+
return NGX_OK;
171+
}
172+
173+
132174
static ngx_int_t
133175
ngx_http_geoip2_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
134176
uintptr_t data)
@@ -149,6 +191,8 @@ ngx_http_geoip2_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
149191
unsigned long address;
150192
#endif
151193

194+
ngx_http_geoip2_reload(database, r->connection->log);
195+
152196
if (geoip2->source.value.len > 0) {
153197
if (ngx_http_complex_value(r, &geoip2->source, &val) != NGX_OK) {
154198
goto not_found;
@@ -294,8 +338,14 @@ ngx_http_geoip2_metadata(ngx_http_request_t *r, ngx_http_variable_value_t *v,
294338
ngx_http_geoip2_db_t *database = metadata->database;
295339
u_char *p;
296340

341+
ngx_http_geoip2_reload(database, r->connection->log);
342+
297343
if (ngx_strncmp(metadata->metavalue.data, "build_epoch", 11) == 0) {
298344
FORMAT("%uL", database->mmdb.metadata.build_epoch);
345+
} else if (ngx_strncmp(metadata->metavalue.data, "last_check", 10) == 0) {
346+
FORMAT("%T", database->last_check);
347+
} else if (ngx_strncmp(metadata->metavalue.data, "last_change", 11) == 0) {
348+
FORMAT("%T", database->last_change);
299349
} else {
300350
v->not_found = 1;
301351
return NGX_OK;
@@ -376,6 +426,8 @@ ngx_http_geoip2(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
376426
return NGX_CONF_ERROR;
377427
}
378428

429+
database->last_check = database->last_change = ngx_time();
430+
379431
status = MMDB_open((char *) value[1].data, MMDB_MODE_MMAP, &database->mmdb);
380432

381433
if (status != MMDB_SUCCESS) {
@@ -392,7 +444,7 @@ ngx_http_geoip2(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
392444
#endif
393445

394446
save = *cf;
395-
cf->handler = ngx_http_geoip2_add_variable;
447+
cf->handler = ngx_http_geoip2_parse_config;
396448
cf->handler_conf = (void *) database;
397449

398450
rv = ngx_conf_parse(cf, NULL);
@@ -401,6 +453,48 @@ ngx_http_geoip2(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
401453
}
402454

403455

456+
static char *
457+
ngx_http_geoip2_parse_config(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
458+
{
459+
ngx_http_geoip2_db_t *database;
460+
ngx_str_t *value;
461+
time_t interval;
462+
463+
value = cf->args->elts;
464+
465+
if (value[0].data[0] == '$') {
466+
return ngx_http_geoip2_add_variable(cf, dummy, conf);
467+
}
468+
469+
if (value[0].len == 11
470+
&& ngx_strncmp(value[0].data, "auto_reload", 11) == 0) {
471+
if ((int) cf->args->nelts != 2) {
472+
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
473+
"invalid number of arguments for auto_reload");
474+
return NGX_CONF_ERROR;
475+
}
476+
477+
interval = ngx_parse_time(&value[1], true);
478+
479+
if (interval == (time_t) NGX_ERROR) {
480+
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
481+
"invalid interval for auto_reload \"%V\"",
482+
value[1]);
483+
return NGX_CONF_ERROR;
484+
}
485+
486+
487+
database = (ngx_http_geoip2_db_t *) conf;
488+
database->check_interval = interval;
489+
return NGX_CONF_OK;
490+
}
491+
492+
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
493+
"invalid setting \"%V\"", &value[0]);
494+
return NGX_CONF_ERROR;
495+
}
496+
497+
404498
static char *
405499
ngx_http_geoip2_add_variable(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
406500
{

ngx_stream_geoip2_module.c

Lines changed: 97 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
typedef struct {
1717
MMDB_s mmdb;
1818
MMDB_lookup_result_s result;
19+
time_t last_check;
20+
time_t last_change;
21+
time_t check_interval;
1922
#if (NGX_HAVE_INET6)
2023
uint8_t address[16];
2124
#else
@@ -40,11 +43,17 @@ typedef struct {
4043
} ngx_stream_geoip2_metadata_t;
4144

4245

46+
static ngx_int_t ngx_stream_geoip2_reload(ngx_stream_geoip2_db_t *database,
47+
ngx_log_t *log);
4348
static ngx_int_t ngx_stream_geoip2_variable(ngx_stream_session_t *s,
4449
ngx_stream_variable_value_t *v, uintptr_t data);
4550
static ngx_int_t ngx_stream_geoip2_metadata(ngx_stream_session_t *s,
4651
ngx_stream_variable_value_t *v, uintptr_t data);
4752
static void *ngx_stream_geoip2_create_conf(ngx_conf_t *cf);
53+
static char *ngx_stream_geoip2(ngx_conf_t *cf, ngx_command_t *cmd,
54+
void *conf);
55+
static char *ngx_stream_geoip2_parse_config(ngx_conf_t *cf, ngx_command_t *dummy,
56+
void *conf);
4857
static char *ngx_stream_geoip2(ngx_conf_t *cf, ngx_command_t *cmd,
4958
void *conf);
5059
static char *ngx_stream_geoip2_add_variable(ngx_conf_t *cf, ngx_command_t *dummy,
@@ -106,6 +115,41 @@ ngx_module_t ngx_stream_geoip2_module = {
106115
};
107116

108117

118+
static ngx_int_t
119+
ngx_stream_geoip2_reload(ngx_stream_geoip2_db_t *database, ngx_log_t *log)
120+
{
121+
struct stat attr;
122+
MMDB_s tmpdb;
123+
int status;
124+
125+
if (database->check_interval > 0
126+
&& database->last_check + database->check_interval <= ngx_time()) {
127+
database->last_check = ngx_time();
128+
stat(database->mmdb.filename, &attr);
129+
130+
if (attr.st_mtime > database->last_change) {
131+
status = MMDB_open(database->mmdb.filename, MMDB_MODE_MMAP, &tmpdb);
132+
133+
if (status != MMDB_SUCCESS) {
134+
ngx_log_error(NGX_LOG_ERR, log, 0,
135+
"MMDB_open(\"%s\") failed to reload - %s",
136+
database->mmdb.filename, MMDB_strerror(status));
137+
return NGX_ERROR;
138+
}
139+
140+
database->last_change = attr.st_mtime;
141+
MMDB_close(&database->mmdb);
142+
database->mmdb = tmpdb;
143+
144+
ngx_log_error(NGX_LOG_INFO, log, 0, "Reload MMDB \"%s\"",
145+
tmpdb.filename);
146+
}
147+
}
148+
149+
return NGX_OK;
150+
}
151+
152+
109153
static ngx_int_t
110154
ngx_stream_geoip2_variable(ngx_stream_session_t *s, ngx_stream_variable_value_t *v,
111155
uintptr_t data)
@@ -124,6 +168,8 @@ ngx_stream_geoip2_variable(ngx_stream_session_t *s, ngx_stream_variable_value_t
124168
unsigned long address;
125169
#endif
126170

171+
ngx_stream_geoip2_reload(database, s->connection->log);
172+
127173
if (geoip2->source.value.len > 0) {
128174
if (ngx_stream_complex_value(s, &geoip2->source, &val) != NGX_OK) {
129175
goto not_found;
@@ -263,8 +309,14 @@ ngx_stream_geoip2_metadata(ngx_stream_session_t *s, ngx_stream_variable_value_t
263309
ngx_stream_geoip2_db_t *database = metadata->database;
264310
u_char *p;
265311

312+
ngx_stream_geoip2_reload(database, s->connection->log);
313+
266314
if (ngx_strncmp(metadata->metavalue.data, "build_epoch", 11) == 0) {
267315
FORMAT("%uL", database->mmdb.metadata.build_epoch);
316+
} else if (ngx_strncmp(metadata->metavalue.data, "last_check", 10) == 0) {
317+
FORMAT("%T", database->last_check);
318+
} else if (ngx_strncmp(metadata->metavalue.data, "last_change", 11) == 0) {
319+
FORMAT("%T", database->last_change);
268320
} else {
269321
v->not_found = 1;
270322
return NGX_OK;
@@ -343,6 +395,8 @@ ngx_stream_geoip2(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
343395
return NGX_CONF_ERROR;
344396
}
345397

398+
database->last_check = database->last_change = ngx_time();
399+
346400
status = MMDB_open((char *) value[1].data, MMDB_MODE_MMAP, &database->mmdb);
347401

348402
if (status != MMDB_SUCCESS) {
@@ -359,7 +413,7 @@ ngx_stream_geoip2(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
359413
#endif
360414

361415
save = *cf;
362-
cf->handler = ngx_stream_geoip2_add_variable;
416+
cf->handler = ngx_stream_geoip2_parse_config;
363417
cf->handler_conf = (void *) database;
364418

365419
rv = ngx_conf_parse(cf, NULL);
@@ -368,6 +422,48 @@ ngx_stream_geoip2(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
368422
}
369423

370424

425+
static char *
426+
ngx_stream_geoip2_parse_config(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
427+
{
428+
ngx_stream_geoip2_db_t *database;
429+
ngx_str_t *value;
430+
time_t interval;
431+
432+
value = cf->args->elts;
433+
434+
if (value[0].data[0] == '$') {
435+
return ngx_stream_geoip2_add_variable(cf, dummy, conf);
436+
}
437+
438+
if (value[0].len == 11
439+
&& ngx_strncmp(value[0].data, "auto_reload", 11) == 0) {
440+
if ((int) cf->args->nelts != 2) {
441+
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
442+
"invalid number of arguments for auto_reload");
443+
return NGX_CONF_ERROR;
444+
}
445+
446+
interval = ngx_parse_time(&value[1], true);
447+
448+
if (interval == (time_t) NGX_ERROR) {
449+
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
450+
"invalid interval for auto_reload \"%V\"",
451+
value[1]);
452+
return NGX_CONF_ERROR;
453+
}
454+
455+
456+
database = (ngx_stream_geoip2_db_t *) conf;
457+
database->check_interval = interval;
458+
return NGX_CONF_OK;
459+
}
460+
461+
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
462+
"invalid setting \"%V\"", &value[0]);
463+
return NGX_CONF_ERROR;
464+
}
465+
466+
371467
static char *
372468
ngx_stream_geoip2_add_variable(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
373469
{

0 commit comments

Comments
 (0)