Skip to content

Commit

Permalink
Merge pull request leev#54 from leev/auto_reload
Browse files Browse the repository at this point in the history
Add support for auto reloading the database if it has changed
  • Loading branch information
leev authored Jul 2, 2018
2 parents 7eaa956 + 39a8abf commit d6e529a
Show file tree
Hide file tree
Showing 3 changed files with 204 additions and 3 deletions.
13 changes: 12 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ The free GeoLite2 databases are available from [Maxminds website](http://dev.max
http {
...
geoip2 /etc/maxmind-country.mmdb {
auto_reload 5m;
$geoip2_metadata_country_build metadata build_epoch;
$geoip2_data_country_code default=US source=$variable_with_ip country iso_code;
$geoip2_data_country_name country names en;
Expand Down Expand Up @@ -78,7 +79,17 @@ Retrieve metadata regarding the geoip database.
```
$variable_name metadata <field>
```
Currently the only metadata field supported is build_epoch.
Available fields:
- build_epoch: the build timestamp of the maxmind database.
- last_check: the last time the database was checked for changes (when using auto_reload)
- last_reload: the last time the database was reloaded (when using auto_reload)

##### Autoreload (default: disabled):
Enabling auto reload will have nginx check the modification time of the database at the specified
interval and reload it if it has changed.
```
auto_reload <interval>
```

##### GeoIP:
```
Expand Down
96 changes: 95 additions & 1 deletion ngx_http_geoip2_module.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
typedef struct {
MMDB_s mmdb;
MMDB_lookup_result_s result;
time_t last_check;
time_t last_change;
time_t check_interval;
#if (NGX_HAVE_INET6)
uint8_t address[16];
#else
Expand All @@ -41,6 +44,8 @@ typedef struct {
} ngx_http_geoip2_metadata_t;


static ngx_int_t ngx_http_geoip2_reload(ngx_http_geoip2_db_t *database,
ngx_log_t *log);
static ngx_int_t ngx_http_geoip2_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_geoip2_metadata(ngx_http_request_t *r,
Expand All @@ -49,6 +54,8 @@ static void *ngx_http_geoip2_create_conf(ngx_conf_t *cf);
static char *ngx_http_geoip2_init_conf(ngx_conf_t *cf, void *conf);
static char *ngx_http_geoip2(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char *ngx_http_geoip2_parse_config(ngx_conf_t *cf, ngx_command_t *dummy,
void *conf);
static char *ngx_http_geoip2_add_variable(ngx_conf_t *cf, ngx_command_t *dummy,
void *conf);
static char *ngx_http_geoip2_add_variable_geodata(ngx_conf_t *cf,
Expand Down Expand Up @@ -129,6 +136,41 @@ ngx_module_t ngx_http_geoip2_module = {
};


static ngx_int_t
ngx_http_geoip2_reload(ngx_http_geoip2_db_t *database, ngx_log_t *log)
{
struct stat attr;
MMDB_s tmpdb;
int status;

if (database->check_interval > 0
&& database->last_check + database->check_interval <= ngx_time()) {
database->last_check = ngx_time();
stat(database->mmdb.filename, &attr);

if (attr.st_mtime > database->last_change) {
status = MMDB_open(database->mmdb.filename, MMDB_MODE_MMAP, &tmpdb);

if (status != MMDB_SUCCESS) {
ngx_log_error(NGX_LOG_ERR, log, 0,
"MMDB_open(\"%s\") failed to reload - %s",
database->mmdb.filename, MMDB_strerror(status));
return NGX_ERROR;
}

database->last_change = attr.st_mtime;
MMDB_close(&database->mmdb);
database->mmdb = tmpdb;

ngx_log_error(NGX_LOG_INFO, log, 0, "Reload MMDB \"%s\"",
tmpdb.filename);
}
}

return NGX_OK;
}


static ngx_int_t
ngx_http_geoip2_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
uintptr_t data)
Expand All @@ -149,6 +191,8 @@ ngx_http_geoip2_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
unsigned long address;
#endif

ngx_http_geoip2_reload(database, r->connection->log);

if (geoip2->source.value.len > 0) {
if (ngx_http_complex_value(r, &geoip2->source, &val) != NGX_OK) {
goto not_found;
Expand Down Expand Up @@ -294,8 +338,14 @@ ngx_http_geoip2_metadata(ngx_http_request_t *r, ngx_http_variable_value_t *v,
ngx_http_geoip2_db_t *database = metadata->database;
u_char *p;

ngx_http_geoip2_reload(database, r->connection->log);

if (ngx_strncmp(metadata->metavalue.data, "build_epoch", 11) == 0) {
FORMAT("%uL", database->mmdb.metadata.build_epoch);
} else if (ngx_strncmp(metadata->metavalue.data, "last_check", 10) == 0) {
FORMAT("%T", database->last_check);
} else if (ngx_strncmp(metadata->metavalue.data, "last_change", 11) == 0) {
FORMAT("%T", database->last_change);
} else {
v->not_found = 1;
return NGX_OK;
Expand Down Expand Up @@ -376,6 +426,8 @@ ngx_http_geoip2(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
return NGX_CONF_ERROR;
}

database->last_check = database->last_change = ngx_time();

status = MMDB_open((char *) value[1].data, MMDB_MODE_MMAP, &database->mmdb);

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

save = *cf;
cf->handler = ngx_http_geoip2_add_variable;
cf->handler = ngx_http_geoip2_parse_config;
cf->handler_conf = (void *) database;

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


static char *
ngx_http_geoip2_parse_config(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
{
ngx_http_geoip2_db_t *database;
ngx_str_t *value;
time_t interval;

value = cf->args->elts;

if (value[0].data[0] == '$') {
return ngx_http_geoip2_add_variable(cf, dummy, conf);
}

if (value[0].len == 11
&& ngx_strncmp(value[0].data, "auto_reload", 11) == 0) {
if ((int) cf->args->nelts != 2) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid number of arguments for auto_reload");
return NGX_CONF_ERROR;
}

interval = ngx_parse_time(&value[1], true);

if (interval == (time_t) NGX_ERROR) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid interval for auto_reload \"%V\"",
value[1]);
return NGX_CONF_ERROR;
}


database = (ngx_http_geoip2_db_t *) conf;
database->check_interval = interval;
return NGX_CONF_OK;
}

ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid setting \"%V\"", &value[0]);
return NGX_CONF_ERROR;
}


static char *
ngx_http_geoip2_add_variable(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
{
Expand Down
98 changes: 97 additions & 1 deletion ngx_stream_geoip2_module.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
typedef struct {
MMDB_s mmdb;
MMDB_lookup_result_s result;
time_t last_check;
time_t last_change;
time_t check_interval;
#if (NGX_HAVE_INET6)
uint8_t address[16];
#else
Expand All @@ -40,11 +43,17 @@ typedef struct {
} ngx_stream_geoip2_metadata_t;


static ngx_int_t ngx_stream_geoip2_reload(ngx_stream_geoip2_db_t *database,
ngx_log_t *log);
static ngx_int_t ngx_stream_geoip2_variable(ngx_stream_session_t *s,
ngx_stream_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_stream_geoip2_metadata(ngx_stream_session_t *s,
ngx_stream_variable_value_t *v, uintptr_t data);
static void *ngx_stream_geoip2_create_conf(ngx_conf_t *cf);
static char *ngx_stream_geoip2(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char *ngx_stream_geoip2_parse_config(ngx_conf_t *cf, ngx_command_t *dummy,
void *conf);
static char *ngx_stream_geoip2(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char *ngx_stream_geoip2_add_variable(ngx_conf_t *cf, ngx_command_t *dummy,
Expand Down Expand Up @@ -106,6 +115,41 @@ ngx_module_t ngx_stream_geoip2_module = {
};


static ngx_int_t
ngx_stream_geoip2_reload(ngx_stream_geoip2_db_t *database, ngx_log_t *log)
{
struct stat attr;
MMDB_s tmpdb;
int status;

if (database->check_interval > 0
&& database->last_check + database->check_interval <= ngx_time()) {
database->last_check = ngx_time();
stat(database->mmdb.filename, &attr);

if (attr.st_mtime > database->last_change) {
status = MMDB_open(database->mmdb.filename, MMDB_MODE_MMAP, &tmpdb);

if (status != MMDB_SUCCESS) {
ngx_log_error(NGX_LOG_ERR, log, 0,
"MMDB_open(\"%s\") failed to reload - %s",
database->mmdb.filename, MMDB_strerror(status));
return NGX_ERROR;
}

database->last_change = attr.st_mtime;
MMDB_close(&database->mmdb);
database->mmdb = tmpdb;

ngx_log_error(NGX_LOG_INFO, log, 0, "Reload MMDB \"%s\"",
tmpdb.filename);
}
}

return NGX_OK;
}


static ngx_int_t
ngx_stream_geoip2_variable(ngx_stream_session_t *s, ngx_stream_variable_value_t *v,
uintptr_t data)
Expand All @@ -124,6 +168,8 @@ ngx_stream_geoip2_variable(ngx_stream_session_t *s, ngx_stream_variable_value_t
unsigned long address;
#endif

ngx_stream_geoip2_reload(database, s->connection->log);

if (geoip2->source.value.len > 0) {
if (ngx_stream_complex_value(s, &geoip2->source, &val) != NGX_OK) {
goto not_found;
Expand Down Expand Up @@ -263,8 +309,14 @@ ngx_stream_geoip2_metadata(ngx_stream_session_t *s, ngx_stream_variable_value_t
ngx_stream_geoip2_db_t *database = metadata->database;
u_char *p;

ngx_stream_geoip2_reload(database, s->connection->log);

if (ngx_strncmp(metadata->metavalue.data, "build_epoch", 11) == 0) {
FORMAT("%uL", database->mmdb.metadata.build_epoch);
} else if (ngx_strncmp(metadata->metavalue.data, "last_check", 10) == 0) {
FORMAT("%T", database->last_check);
} else if (ngx_strncmp(metadata->metavalue.data, "last_change", 11) == 0) {
FORMAT("%T", database->last_change);
} else {
v->not_found = 1;
return NGX_OK;
Expand Down Expand Up @@ -343,6 +395,8 @@ ngx_stream_geoip2(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
return NGX_CONF_ERROR;
}

database->last_check = database->last_change = ngx_time();

status = MMDB_open((char *) value[1].data, MMDB_MODE_MMAP, &database->mmdb);

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

save = *cf;
cf->handler = ngx_stream_geoip2_add_variable;
cf->handler = ngx_stream_geoip2_parse_config;
cf->handler_conf = (void *) database;

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


static char *
ngx_stream_geoip2_parse_config(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
{
ngx_stream_geoip2_db_t *database;
ngx_str_t *value;
time_t interval;

value = cf->args->elts;

if (value[0].data[0] == '$') {
return ngx_stream_geoip2_add_variable(cf, dummy, conf);
}

if (value[0].len == 11
&& ngx_strncmp(value[0].data, "auto_reload", 11) == 0) {
if ((int) cf->args->nelts != 2) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid number of arguments for auto_reload");
return NGX_CONF_ERROR;
}

interval = ngx_parse_time(&value[1], true);

if (interval == (time_t) NGX_ERROR) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid interval for auto_reload \"%V\"",
value[1]);
return NGX_CONF_ERROR;
}


database = (ngx_stream_geoip2_db_t *) conf;
database->check_interval = interval;
return NGX_CONF_OK;
}

ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid setting \"%V\"", &value[0]);
return NGX_CONF_ERROR;
}


static char *
ngx_stream_geoip2_add_variable(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
{
Expand Down

0 comments on commit d6e529a

Please sign in to comment.