static char *
ngx_http_groonga_conf_set_groonga_slot(ngx_conf_t *cf, ngx_command_t *cmd,
                                       void *conf)
{
  char *status;
  ngx_http_core_loc_conf_t *location_conf;
  ngx_http_groonga_loc_conf_t *groonga_location_conf = conf;

  status = ngx_conf_set_flag_slot(cf, cmd, conf);
  if (status != NGX_CONF_OK) {
    return status;
  }

  location_conf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
  if (groonga_location_conf->enabled) {
    location_conf->handler = ngx_http_groonga_handler;
    groonga_location_conf->name =
      ngx_str_null_terminate(cf->pool, &(location_conf->name));
    groonga_location_conf->config_file =
      ngx_str_null_terminate(cf->pool, &(cf->conf_file->file.name));
    groonga_location_conf->config_line = cf->conf_file->line;
  } else {
    location_conf->handler = NULL;
  }

  return NGX_CONF_OK;
}
/**
 * This function is called after forking and just before exec()ing the helper server.
 */
static void
starting_helper_server_after_fork(void *arg) {
    ngx_cycle_t *cycle = (void *) arg;
    char        *log_filename;
    FILE        *log_file;
    ngx_core_conf_t *ccf;
    ngx_uint_t   i;
    ngx_str_t   *envs;
    const char  *env;
    
    /* At this point, stdout and stderr may still point to the console.
     * Make sure that they're both redirected to the log file.
     */
    log_file = NULL;
    if (cycle->new_log.file->name.len > 0) {
        log_filename = ngx_str_null_terminate(&cycle->new_log.file->name);
        log_file = fopen((const char *) log_filename, "a");
        if (log_file == NULL) {
            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                          "could not open the error log file for writing");
        }
        free(log_filename);
    } else if (cycle->log != NULL && cycle->log->file->name.len > 0) {
        log_filename = ngx_str_null_terminate(&cycle->log->file->name);
        log_file = fopen((const char *) log_filename, "a");
        if (log_file == NULL) {
            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                          "could not open the error log file for writing");
        }
        free(log_filename);
    }
    if (log_file == NULL) {
        /* If the log file cannot be opened then we redirect stdout
         * and stderr to /dev/null, because if the user disconnects
         * from the console on which Nginx is started, then on Linux
         * any writes to stdout or stderr will result in an EIO error.
         */
        log_file = fopen("/dev/null", "w");
    }
    if (log_file != NULL) {
        dup2(fileno(log_file), 1);
        dup2(fileno(log_file), 2);
        fclose(log_file);
    }

    /* Set environment variables in Nginx config file. */
    ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
    envs = ccf->env.elts;
    for (i = 0; i < ccf->env.nelts; i++) {
        env = (const char *) envs[i].data;
        if (strchr(env, '=') != NULL) {
            putenv(strdup(env));
        }
    }
    
    /* Set SERVER_SOFTWARE so that application processes know what web
     * server they're running on during startup. */
    setenv("SERVER_SOFTWARE", NGINX_VER, 1);
}
/**
 * This function is called after forking and just before exec()ing the helper server.
 */
static void
starting_helper_server_after_fork(void *arg) {
    ngx_cycle_t *cycle = (void *) arg;
    char        *log_filename;
    FILE        *log_file;
    
    /* At this point, stdout and stderr may still point to the console.
     * Make sure that they're both redirected to the log file.
     */
    log_file = NULL;
    if (cycle->new_log.file->name.len > 0) {
        log_filename = ngx_str_null_terminate(&cycle->new_log.file->name);
        log_file = fopen((const char *) log_filename, "a");
        if (log_file == NULL) {
            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                          "could not open the error log file for writing");
        }
        free(log_filename);
    } else if (cycle->log != NULL && cycle->log->file->name.len > 0) {
        log_filename = ngx_str_null_terminate(&cycle->log->file->name);
        log_file = fopen((const char *) log_filename, "a");
        if (log_file == NULL) {
            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                          "could not open the error log file for writing");
        }
        free(log_filename);
    }
    if (log_file == NULL) {
        /* If the log file cannot be opened then we redirect stdout
         * and stderr to /dev/null, because if the user disconnects
         * from the console on which Nginx is started, then on Linux
         * any writes to stdout or stderr will result in an EIO error.
         */
        log_file = fopen("/dev/null", "w");
    }
    if (log_file != NULL) {
        dup2(fileno(log_file), 1);
        dup2(fileno(log_file), 2);
        fclose(log_file);
    }
    
    /* Set SERVER_SOFTWARE so that application processes know what web
     * server they're running on during startup. */
    setenv("SERVER_SOFTWARE", NGINX_VER, 1);
}
static void
ngx_http_groonga_open_database_callback(ngx_http_groonga_loc_conf_t *location_conf,
                                        void *user_data)
{
  ngx_http_groonga_database_callback_data_t *data = user_data;
  grn_ctx *context;

  context = &(location_conf->context);
  data->rc = ngx_http_groonga_context_init(context, location_conf,
                                           data->pool, data->log);
  if (data->rc != NGX_OK) {
    return;
  }

  if (!location_conf->database_path.data) {
    ngx_log_error(NGX_LOG_EMERG, data->log, 0,
                  "%s: \"groonga_database\" must be specified in block at %s:%d",
                  location_conf->name,
                  location_conf->config_file,
                  location_conf->config_line);
    data->rc = NGX_ERROR;
    return;
  }

  if (!location_conf->database_path_cstr) {
    location_conf->database_path_cstr =
      ngx_str_null_terminate(data->pool, &(location_conf->database_path));
  }

  grn_db_open(context, location_conf->database_path_cstr);
  if (context->rc != GRN_SUCCESS) {
    if (location_conf->database_auto_create) {
      ngx_http_groonga_create_database(location_conf, data);
    } else {
      ngx_log_error(NGX_LOG_EMERG, data->log, 0,
                    "failed to open groonga database: %s",
                    context->errbuf);
      data->rc = NGX_ERROR;
      return;
    }
  }

  location_conf->cache = grn_cache_open(context);
  if (!location_conf->cache) {
    ngx_log_error(NGX_LOG_EMERG, data->log, 0,
                  "failed to open groonga cache: %s",
                  context->errbuf);
    data->rc = NGX_ERROR;
    return;
  }
  if (location_conf->cache_limit != NGX_CONF_UNSET_SIZE) {
    grn_cache_set_max_n_entries(context,
                                location_conf->cache,
                                location_conf->cache_limit);
  }
}
Esempio n. 5
0
static char *
ngx_http_groonga_conf_set_log_level_slot(ngx_conf_t *cf, ngx_command_t *cmd,
                                         void *conf)
{
  char *status = NGX_CONF_OK;
  ngx_http_groonga_loc_conf_t *groonga_location_conf = conf;
  char *value;

  value = ngx_str_null_terminate(cf->cycle->pool,
                                 ((ngx_str_t *)cf->args->elts) + 1);
  if (!grn_log_level_parse(value, &(groonga_location_conf->log_level))) {
    status = "must be one of 'none', 'emergency', 'alert', "
      "'critical', 'error', 'warning', 'notice', 'info', 'debug' and 'dump'";
  }
  ngx_pfree(cf->cycle->pool, value);

  return status;
}
Esempio n. 6
0
static void
ngx_http_groonga_open_database_callback(ngx_http_groonga_loc_conf_t *location_conf,
                                        void *user_data)
{
  ngx_http_groonga_database_callback_data_t *data = user_data;
  grn_ctx *context;

  context = &(location_conf->context);
  grn_ctx_init(context, GRN_NO_FLAGS);

  if (!location_conf->database_path.data) {
    ngx_log_error(NGX_LOG_EMERG, data->log, 0,
                  "%s: \"groonga_database\" must be specified in block at %s:%d",
                  location_conf->name,
                  location_conf->config_file,
                  location_conf->config_line);
    data->rc = NGX_ERROR;
    return;
  }

  if (!location_conf->database_path_cstr) {
    location_conf->database_path_cstr =
      ngx_str_null_terminate(data->pool, &(location_conf->database_path));
  }

  grn_db_open(context, location_conf->database_path_cstr);
  if (context->rc == GRN_SUCCESS) {
    return;
  }

  if (location_conf->database_auto_create) {
    ngx_http_groonga_create_database(location_conf, data);
  } else {
    ngx_log_error(NGX_LOG_EMERG, data->log, 0,
                  "failed to open groonga database: %s",
                  context->errbuf);
    data->rc = NGX_ERROR;
  }
}
static char *
ngx_http_groonga_conf_set_log_level_slot(ngx_conf_t *cf, ngx_command_t *cmd,
                                         void *conf)
{
  char *status = NGX_CONF_OK;
  ngx_http_groonga_loc_conf_t *groonga_location_conf = conf;
  char *value;

  value = ngx_str_null_terminate(cf->cycle->pool,
                                 ((ngx_str_t *)cf->args->elts) + 1);
  if (strcasecmp(value, "none") == 0) {
    groonga_location_conf->log_level = GRN_LOG_NONE;
  } else if (strcasecmp(value, "emergency") == 0) {
    groonga_location_conf->log_level = GRN_LOG_EMERG;
  } else if (strcasecmp(value, "alert") == 0) {
    groonga_location_conf->log_level = GRN_LOG_ALERT;
  } else if (strcasecmp(value, "critical") == 0) {
    groonga_location_conf->log_level = GRN_LOG_CRIT;
  } else if (strcasecmp(value, "error") == 0) {
    groonga_location_conf->log_level = GRN_LOG_ERROR;
  } else if (strcasecmp(value, "warning") == 0) {
    groonga_location_conf->log_level = GRN_LOG_WARNING;
  } else if (strcasecmp(value, "notice") == 0) {
    groonga_location_conf->log_level = GRN_LOG_NOTICE;
  } else if (strcasecmp(value, "info") == 0) {
    groonga_location_conf->log_level = GRN_LOG_INFO;
  } else if (strcasecmp(value, "debug") == 0) {
    groonga_location_conf->log_level = GRN_LOG_DEBUG;
  } else if (strcasecmp(value, "dump") == 0) {
    groonga_location_conf->log_level = GRN_LOG_DUMP;
  } else {
    status = "must be one of 'none', 'emergency', 'alert', "
      "'ciritical', 'error', 'warning', 'notice', 'info', 'debug' and 'dump'";
  }
  ngx_pfree(cf->cycle->pool, value);

  return status;
}
/**
 * Start the helper server and save its runtime information into various variables.
 *
 * @pre The helper server isn't already started.
 * @pre The Nginx configuration has been loaded.
 */
static ngx_int_t
start_helper_server(ngx_cycle_t *cycle) {
    ngx_core_conf_t *core_conf;
    ngx_int_t        ret, result;
    ngx_uint_t       i;
    ngx_str_t       *prestart_uris;
    char           **prestart_uris_ary = NULL;
    u_char  filename[NGX_MAX_PATH], *last;
    char   *debug_log_file = NULL;
    char   *default_user = NULL;
    char   *default_group = NULL;
    char   *passenger_root = NULL;
    char   *analytics_log_user;
    char   *analytics_log_group;
    char   *union_station_gateway_address;
    char   *union_station_gateway_cert;
    char   *union_station_proxy_address;
    char   *error_message = NULL;
    
    core_conf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
    result    = NGX_OK;
    
    /* Create null-terminated versions of some strings. */
    debug_log_file = ngx_str_null_terminate(&passenger_main_conf.debug_log_file);
    default_user   = ngx_str_null_terminate(&passenger_main_conf.default_user);
    default_group  = ngx_str_null_terminate(&passenger_main_conf.default_group);
    passenger_root = ngx_str_null_terminate(&passenger_main_conf.root_dir);
    analytics_log_user = ngx_str_null_terminate(&passenger_main_conf.analytics_log_user);
    analytics_log_group = ngx_str_null_terminate(&passenger_main_conf.analytics_log_group);
    union_station_gateway_address = ngx_str_null_terminate(&passenger_main_conf.union_station_gateway_address);
    union_station_gateway_cert = ngx_str_null_terminate(&passenger_main_conf.union_station_gateway_cert);
    union_station_proxy_address = ngx_str_null_terminate(&passenger_main_conf.union_station_proxy_address);
    
    prestart_uris = (ngx_str_t *) passenger_main_conf.prestart_uris->elts;
    prestart_uris_ary = calloc(sizeof(char *), passenger_main_conf.prestart_uris->nelts);
    for (i = 0; i < passenger_main_conf.prestart_uris->nelts; i++) {
        prestart_uris_ary[i] = malloc(prestart_uris[i].len + 1);
        if (prestart_uris_ary[i] == NULL) {
            ngx_log_error(NGX_LOG_ALERT, cycle->log, ENOMEM, "Cannot allocate memory");
            result = NGX_ERROR;
            goto cleanup;
        }
        memcpy(prestart_uris_ary[i], prestart_uris[i].data, prestart_uris[i].len);
        prestart_uris_ary[i][prestart_uris[i].len] = '\0';
    }
    
    ret = agents_starter_start(passenger_agents_starter,
        passenger_main_conf.log_level, debug_log_file, getpid(),
        "", passenger_main_conf.user_switching,
        default_user, default_group,
        core_conf->user, core_conf->group,
        passenger_root, "ruby", passenger_main_conf.max_pool_size,
        passenger_main_conf.max_instances_per_app,
        passenger_main_conf.pool_idle_time,
        "",
        analytics_log_user, analytics_log_group,
        union_station_gateway_address,
        passenger_main_conf.union_station_gateway_port,
        union_station_gateway_cert,
        union_station_proxy_address,
        (const char **) prestart_uris_ary, passenger_main_conf.prestart_uris->nelts,
        starting_helper_server_after_fork,
        cycle,
        &error_message);
    if (!ret) {
        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "%s", error_message);
        result = NGX_ERROR;
        goto cleanup;
    }
    
    /* Create the file passenger_temp_dir + "/control_process.pid"
     * and make it writable by the worker processes. This is because
     * save_master_process_pid is run after Nginx has lowered privileges.
     */
    last = ngx_snprintf(filename, sizeof(filename) - 1,
                        "%s/control_process.pid",
                        agents_starter_get_server_instance_dir(passenger_agents_starter));
    *last = (u_char) '\0';
    if (create_file(cycle, filename, (const u_char *) "", 0) != NGX_OK) {
        result = NGX_ERROR;
        goto cleanup;
    }
    do {
        ret = chown((const char *) filename, (uid_t) core_conf->user, (gid_t) -1);
    } while (ret == -1 && errno == EINTR);
    if (ret == -1) {
        result = NGX_ERROR;
        goto cleanup;
    }

    /* Create various other info files. */
    last = ngx_snprintf(filename, sizeof(filename) - 1,
                        "%s/web_server.txt",
                        agents_starter_get_generation_dir(passenger_agents_starter));
    *last = (u_char) '\0';
    if (create_file(cycle, filename, (const u_char *) NGINX_VER, strlen(NGINX_VER)) != NGX_OK) {
        result = NGX_ERROR;
        goto cleanup;
    }

    last = ngx_snprintf(filename, sizeof(filename) - 1,
                        "%s/config_files.txt",
                        agents_starter_get_generation_dir(passenger_agents_starter));
    *last = (u_char) '\0';
    if (create_file(cycle, filename, cycle->conf_file.data, cycle->conf_file.len) != NGX_OK) {
        result = NGX_ERROR;
        goto cleanup;
    }
    
cleanup:
    free(debug_log_file);
    free(default_user);
    free(default_group);
    free(passenger_root);
    free(analytics_log_user);
    free(analytics_log_group);
    free(union_station_gateway_address);
    free(union_station_gateway_cert);
    free(union_station_proxy_address);
    free(error_message);
    if (prestart_uris_ary != NULL) {
        for (i = 0; i < passenger_main_conf.prestart_uris->nelts; i++) {
            free(prestart_uris_ary[i]);
        }
        free(prestart_uris_ary);
    }
    
    if (result == NGX_ERROR && passenger_main_conf.abort_on_startup_error) {
        exit(1);
    }
    
    return result;
}
/**
 * Start the helper server and save its runtime information into various variables.
 *
 * @pre The helper server isn't already started.
 * @pre The Nginx configuration has been loaded.
 */
static ngx_int_t
start_helper_server(ngx_cycle_t *cycle) {
    ngx_core_conf_t *core_conf;
    ngx_int_t        ret, result;
    ngx_uint_t       i;
    ngx_str_t       *prestart_uris;
    char           **prestart_uris_ary = NULL;
    ngx_keyval_t    *ctl = NULL;
    PP_VariantMap  *params = NULL;
    u_char  filename[NGX_MAX_PATH], *last;
    char   *passenger_root = NULL;
    char   *error_message = NULL;
    
    core_conf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
    result    = NGX_OK;
    params    = pp_variant_map_new();
    passenger_root = ngx_str_null_terminate(&passenger_main_conf.root_dir);
    
    prestart_uris = (ngx_str_t *) passenger_main_conf.prestart_uris->elts;
    prestart_uris_ary = calloc(sizeof(char *), passenger_main_conf.prestart_uris->nelts);
    for (i = 0; i < passenger_main_conf.prestart_uris->nelts; i++) {
        prestart_uris_ary[i] = malloc(prestart_uris[i].len + 1);
        if (prestart_uris_ary[i] == NULL) {
            ngx_log_error(NGX_LOG_ALERT, cycle->log, ENOMEM, "Cannot allocate memory");
            result = NGX_ERROR;
            goto cleanup;
        }
        memcpy(prestart_uris_ary[i], prestart_uris[i].data, prestart_uris[i].len);
        prestart_uris_ary[i][prestart_uris[i].len] = '\0';
    }

    pp_variant_map_set_int    (params, "web_server_pid", getpid());
    pp_variant_map_set_int    (params, "web_server_worker_uid", core_conf->user);
    pp_variant_map_set_int    (params, "web_server_worker_gid", core_conf->group);
    pp_variant_map_set_int    (params, "log_level", passenger_main_conf.log_level);
    pp_variant_map_set_ngx_str(params, "debug_log_file", &passenger_main_conf.debug_log_file);
    pp_variant_map_set_ngx_str(params, "temp_dir", &passenger_main_conf.temp_dir);
    pp_variant_map_set_bool   (params, "user_switching", passenger_main_conf.user_switching);
    pp_variant_map_set_ngx_str(params, "default_user", &passenger_main_conf.default_user);
    pp_variant_map_set_ngx_str(params, "default_group", &passenger_main_conf.default_group);
    pp_variant_map_set_ngx_str(params, "default_ruby", &passenger_main_conf.default_ruby);
    pp_variant_map_set_int    (params, "max_pool_size", passenger_main_conf.max_pool_size);
    pp_variant_map_set_int    (params, "pool_idle_time", passenger_main_conf.pool_idle_time);
    pp_variant_map_set_ngx_str(params, "analytics_log_user", &passenger_main_conf.analytics_log_user);
    pp_variant_map_set_ngx_str(params, "analytics_log_group", &passenger_main_conf.analytics_log_group);
    pp_variant_map_set_ngx_str(params, "union_station_gateway_address", &passenger_main_conf.union_station_gateway_address);
    pp_variant_map_set_int    (params, "union_station_gateway_port", passenger_main_conf.union_station_gateway_port);
    pp_variant_map_set_ngx_str(params, "union_station_gateway_cert", &passenger_main_conf.union_station_gateway_cert);
    pp_variant_map_set_ngx_str(params, "union_station_proxy_address", &passenger_main_conf.union_station_proxy_address);
    pp_variant_map_set_strset (params, "prestart_urls", (const char **) prestart_uris_ary, passenger_main_conf.prestart_uris->nelts);

    ctl = (ngx_keyval_t *) passenger_main_conf.ctl->elts;
    for (i = 0; i < passenger_main_conf.ctl->nelts; i++) {
        pp_variant_map_set2(params,
            (const char *) ctl[i].key.data, ctl[i].key.len - 1,
            (const char *) ctl[i].value.data, ctl[i].value.len - 1);
    }
    
    ret = pp_agents_starter_start(pp_agents_starter,
        passenger_root,
        params,
        starting_helper_server_after_fork,
        cycle,
        &error_message);
    if (!ret) {
        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "%s", error_message);
        result = NGX_ERROR;
        goto cleanup;
    }
    
    /* Create the file passenger_temp_dir + "/control_process.pid"
     * and make it writable by the worker processes. This is because
     * save_master_process_pid is run after Nginx has lowered privileges.
     */
    last = ngx_snprintf(filename, sizeof(filename) - 1,
                        "%s/control_process.pid",
                        pp_agents_starter_get_server_instance_dir(pp_agents_starter));
    *last = (u_char) '\0';
    if (create_file(cycle, filename, (const u_char *) "", 0) != NGX_OK) {
        result = NGX_ERROR;
        goto cleanup;
    }
    do {
        ret = chown((const char *) filename, (uid_t) core_conf->user, (gid_t) -1);
    } while (ret == -1 && errno == EINTR);
    if (ret == -1) {
        result = NGX_ERROR;
        goto cleanup;
    }

    /* Create various other info files. */
    last = ngx_snprintf(filename, sizeof(filename) - 1,
                        "%s/web_server.txt",
                        pp_agents_starter_get_generation_dir(pp_agents_starter));
    *last = (u_char) '\0';
    if (create_file(cycle, filename, (const u_char *) NGINX_VER, strlen(NGINX_VER)) != NGX_OK) {
        result = NGX_ERROR;
        goto cleanup;
    }

    last = ngx_snprintf(filename, sizeof(filename) - 1,
                        "%s/config_files.txt",
                        pp_agents_starter_get_generation_dir(pp_agents_starter));
    *last = (u_char) '\0';
    if (create_file(cycle, filename, cycle->conf_file.data, cycle->conf_file.len) != NGX_OK) {
        result = NGX_ERROR;
        goto cleanup;
    }
    
cleanup:
    pp_variant_map_free(params);
    free(passenger_root);
    free(error_message);
    if (prestart_uris_ary != NULL) {
        for (i = 0; i < passenger_main_conf.prestart_uris->nelts; i++) {
            free(prestart_uris_ary[i]);
        }
        free(prestart_uris_ary);
    }
    
    if (result == NGX_ERROR && passenger_main_conf.abort_on_startup_error) {
        exit(1);
    }
    
    return result;
}