c++clinuxapacheapache-modules

Apache module command parser prototype


I am creating an Apache2 module and experiencing a weird compilation problem. This is prototype of my function used to parse config command named "analytics_ip":

static const char *apr_cfg_set_analytics_ip(cmd_parms *cmd, void *config, const char *data);

This is array of command_rec structures containing pointers to this function:

static const command_rec apr_cmds[] =
{
 AP_INIT_TAKE1("analytics_ip", apr_cfg_set_analytics_ip, NULL, OR_ALL, ""),
 { NULL }
};

Structure command_rec is declared in header file http_config.h

typedef struct command_struct command_rec;
struct command_struct {
    /** Name of this command */
    const char *name;
    /** The function to be called when this directive is parsed */
    cmd_func func;
    /** Extra data, for functions which implement multiple commands... */
    void *cmd_data;
    /** What overrides need to be allowed to enable this command. */
    int req_override;
    /** What the command expects as arguments */
    enum cmd_how args_how;

    /** 'usage' message, in case of syntax errors */
    const char *errmsg;
};

When I follow cmd_func, it gets to the following declaration:

typedef const char *(*cmd_func) ();

If I am not mistaken, this means "pointer to function returning pointer to char and not accepting any arguments". How can this be possible? Command parsing function has to accept at least one parameter containing a module value of config variable corresponding to that function.

I am using g++ to compile this module. Error message:

mod_xxx.h:65:2: error: invalid conversion from ‘const char* (*)(cmd_parms*, void*, const char*) {aka const char* (*)(cmd_parms_struct*, void*, const char*)}’ to ‘cmd_func {aka const char* (*)()}’ [-fpermissive]
  };

Thanks in advance


Solution

  • cmd_func is a union, it is defined in http_config.h as follows:

    typedef union {
        /** function to call for a no-args */
        const char *(*no_args) (cmd_parms *parms, void *mconfig);
        /** function to call for a raw-args */
        const char *(*raw_args) (cmd_parms *parms, void *mconfig,
                                 const char *args);
        /** function to call for a argv/argc */
        const char *(*take_argv) (cmd_parms *parms, void *mconfig,
                                 int argc, char *const argv[]);
        /** function to call for a take1 */
        const char *(*take1) (cmd_parms *parms, void *mconfig, const char *w);
        /** function to call for a take2 */
        const char *(*take2) (cmd_parms *parms, void *mconfig, const char *w,
                              const char *w2);
        /** function to call for a take3 */
        const char *(*take3) (cmd_parms *parms, void *mconfig, const char *w,
                              const char *w2, const char *w3);
        /** function to call for a flag */
        const char *(*flag) (cmd_parms *parms, void *mconfig, int on);
    } cmd_func;
    

    enum cmd_how args_how; is responsible for choosing the correct version of the function.

    The switch handling it is located in server/config.c (in the invoke_cmd function).

    You seem to be using the "take1" version which corresponds to cmd->AP_TAKE1 or simply cmd->take1.

    The problem might be that C and C++ have differences regarding the union initialization. (AP_INIT_TAKE1 uses the { .take1=func } syntax which doesn't work in C++).

    You'll have to initialize static const command_rec apr_cmds in a C++-compatible way or move it to a separate object file compiled with C. Or if you're not using C++ then simply compile with gcc.