Wednesday, March 16, 2011

Day 34 - rrd_graph needs ngx_create_temp_file, right values for ngx_command_t

So, to draw my pictures (the RRD graph) I need to call the rrd_graph fucntion with the arguments you usually give rrd on the command line. So, I need to create a temporary file for this. Luckily enough, nginx seems to have a nice function handy for that:
ngx_int_t
ngx_create_temp_file(ngx_file_t *file, ngx_path_t *path, ngx_pool_t *pool,
    ngx_uint_t persistent, ngx_uint_t clean, ngx_uint_t access)
  • The access argument is simple: it's what you would use as argument to chmod the temporary file.
  • The clean argument tells nginx whether it should clean the file when it does not need it anymore (which is usually at the end of the request). Interestingly enough the cleanup mechanism relies on the pool-cleanup feature and the pool argument. So, the cleanup could happen pretty much at any time you see fit provided you hand over the right pool
  • persistent seems to be atrick by which a file becomes effectively invisible to everybody else but its creator if this creator calls unlink just after calling open with O_CREAT
  • ngx_path_t is probably some kind of path but I actually got a headache trying to figure out how to create a ngx_path_t. So, instead of trying to figure it out, I decided to use the ngx_conf_set_path_slot function that lets you configure a path in a directive (the brand new rrd_image_temp_path directive in my case). I love it when everything falls in place...

While I was at it, I decided to review my good old rrd configuration directive. So far, it had been relying on ngx_conf_set_str to handle the string that indicates where the rrd db is. As a result the object stored in the ngx_http_rrd_conf_loc_t is a ngx_str_t. Unfortunately, most of the time, I need this object as a C-style (null terminated) string to call the rrd functions. Therefore, I decided to drop using ngx_conf_str_t and to perform the "conversion" to c-style once and for all at configuration time.

After that I tried to put together the new directive. And, once again, I tried to be smarter than I should have. Once again, it backfired... :( Here is the story...

The new directive had no reason to be limited to the location scope. It could as well be in the server or even the main scope. So, I decided to configure the corresponding ngx_command_t accordingly:
{ ngx_string("rrd_image_temp_path"),
      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1234,
      ngx_conf_set_path_slot,
Note, that pretty much summarizes all I said so far: directive is rrd_image_temp_path; it is allowed at main, server and location scopes; it takes 1 to 4 arguments which are handled by ngx_conf_set_path_slot (what I call the "ngx_path black magic").

And then, I had to figure out what should be the other fields of the structure:
ngx_uint_t            conf;
      ngx_uint_t            offset;
      void                 *post;
  • post was easy: no post-processing, so use NULL.
  • offset was the offset where the variable would be in my configuration object. So something like: offsetof(ngx_http_rrd_loc_conf_t, rrd_image_temp_path).
  • conf was a bit trickier and got me wondering. As the directive could be up to the main scope, I set it to NGX_HTTP_MAIN_CONF_OFFSET. And as Yoda would put it "That is why you fail"...

But before realising I had failed, I wrote my nice little configuration merging function:
static char *
ngx_http_rrd_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) {
    ngx_http_rrd_loc_conf_t* plcf = parent;
    ngx_http_rrd_loc_conf_t* clcf = child;

    if (ngx_conf_merge_path_value(cf, &clcf->rrd_image_temp_path,
                              plcf->rrd_image_temp_path,
                              &ngx_http_rrd_temp_path)
        != NGX_OK)
    {
        return NGX_CONF_ERROR;
    }
    return NGX_CONF_OK;
}
Pretty easy as long as you use the "out-of-the-box" tools of nginx like ngx_conf_merge_path_value.

Except that after getting the thing to compile, nothing was working. My nginx server would not even start. And after quite a bit of research I figured the culprit was the conf. What nginx expects here has absolutely nothing to do with where the directive is allowed (this is handled in one place only: the type field of ngx_command_t) it has to do with where the data is stored: the location configuration structure, the server configuration structure or the main configuration structure. In our example, as we want to be able to set this to the location in certain situations, the data must be in the location configuration structure. So, the right value is NGX_HTTP_LOC_CONF_OFFSET. So, with conf and offset, nginx is able to figure out where exactly it should put the data it read from the configuration file.

That's all folks.

No comments:

Post a Comment