start_watchdog(ngx_cycle_t *cycle) {
    ngx_core_conf_t *core_conf;
    ngx_int_t        ret, result;
    ngx_uint_t       i;
    AfterForkData    after_fork_data;
    ngx_keyval_t    *ctl = NULL;
    ngx_str_t        str;
    PsgJsonValue    *w_config = NULL;
    PsgJsonValue    *j_log_target;
    u_char  filename[NGX_MAX_PATH], *last;
    char   *passenger_root = NULL;
    char   *error_message = NULL;
    passenger_autogenerated_main_conf_t *autogenerated_main_conf =
        &passenger_main_conf.autogenerated;

    core_conf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
    result    = NGX_OK;
    w_config  = psg_json_value_new_with_type(PSG_JSON_VALUE_TYPE_OBJECT);
    j_log_target = psg_json_value_new_with_type(PSG_JSON_VALUE_TYPE_OBJECT);
    after_fork_data.cycle = cycle;
    after_fork_data.log_fd = -1;
    passenger_root = ngx_str_null_terminate(&autogenerated_main_conf->root_dir);
    if (passenger_root == NULL) {
        goto error_enomem;
    }

    if (autogenerated_main_conf->stat_throttle_rate != NGX_CONF_UNSET_UINT) {
        pp_app_type_detector_set_throttle_rate(pp_app_type_detector,
            autogenerated_main_conf->stat_throttle_rate);
    }

    /* Note: WatchdogLauncher::start() sets a number of default values. */
    psg_json_value_set_str_ne    (w_config, "web_server_module_version", PASSENGER_VERSION, strlen(PASSENGER_VERSION));
    psg_json_value_set_str_ne    (w_config, "web_server_version", NGINX_VERSION, strlen(NGINX_VERSION));
    psg_json_value_set_str_ne    (w_config, "server_software", NGINX_VER, strlen(NGINX_VER));
    psg_json_value_set_bool      (w_config, "multi_app", 1);
    psg_json_value_set_bool      (w_config, "default_load_shell_envvars", 1);
    psg_json_value_set_value     (w_config, "config_manifest", -1, passenger_main_conf.manifest);
    psg_json_value_set_ngx_uint  (w_config, "log_level", autogenerated_main_conf->log_level);
    psg_json_value_set_ngx_str_ne(w_config, "file_descriptor_log_target", &autogenerated_main_conf->file_descriptor_log_file);
    psg_json_value_set_ngx_uint  (w_config, "core_file_descriptor_ulimit", autogenerated_main_conf->core_file_descriptor_ulimit);
    psg_json_value_set_ngx_uint  (w_config, "controller_socket_backlog", autogenerated_main_conf->socket_backlog);
    psg_json_value_set_ngx_str_ne(w_config, "controller_file_buffered_channel_buffer_dir", &autogenerated_main_conf->data_buffer_dir);
    psg_json_value_set_ngx_str_ne(w_config, "instance_registry_dir", &autogenerated_main_conf->instance_registry_dir);
    psg_json_value_set_ngx_flag  (w_config, "security_update_checker_disabled", autogenerated_main_conf->disable_security_update_check);
    psg_json_value_set_ngx_str_ne(w_config, "security_update_checker_proxy_url", &autogenerated_main_conf->security_update_check_proxy);
    psg_json_value_set_ngx_flag  (w_config, "user_switching", autogenerated_main_conf->user_switching);
    psg_json_value_set_ngx_flag  (w_config, "show_version_in_header", autogenerated_main_conf->show_version_in_header);
    psg_json_value_set_ngx_flag  (w_config, "turbocaching", autogenerated_main_conf->turbocaching);
    psg_json_value_set_ngx_str_ne(w_config, "default_user", &autogenerated_main_conf->default_user);
    psg_json_value_set_ngx_str_ne(w_config, "default_group", &autogenerated_main_conf->default_group);
    psg_json_value_set_ngx_str_ne(w_config, "default_ruby", &passenger_main_conf.default_ruby);
    psg_json_value_set_ngx_uint  (w_config, "max_pool_size", autogenerated_main_conf->max_pool_size);
    psg_json_value_set_ngx_uint  (w_config, "pool_idle_time", autogenerated_main_conf->pool_idle_time);
    psg_json_value_set_ngx_uint  (w_config, "max_instances_per_app", autogenerated_main_conf->max_instances_per_app);
    psg_json_value_set_ngx_uint  (w_config, "response_buffer_high_watermark", autogenerated_main_conf->response_buffer_high_watermark);
    psg_json_value_set_ngx_uint  (w_config, "stat_throttle_rate", autogenerated_main_conf->stat_throttle_rate);
    psg_json_value_set_ngx_str_ne(w_config, "admin_panel_url", &autogenerated_main_conf->admin_panel_url);
    psg_json_value_set_ngx_str_ne(w_config, "admin_panel_auth_type", &autogenerated_main_conf->admin_panel_auth_type);
    psg_json_value_set_ngx_str_ne(w_config, "admin_panel_username", &autogenerated_main_conf->admin_panel_username);
    psg_json_value_set_ngx_str_ne(w_config, "admin_panel_password", &autogenerated_main_conf->admin_panel_password);

    if (autogenerated_main_conf->prestart_uris != NGX_CONF_UNSET_PTR) {
        psg_json_value_set_strset(w_config, "prestart_urls", (ngx_str_t *) autogenerated_main_conf->prestart_uris->elts,
            autogenerated_main_conf->prestart_uris->nelts);
    }

    if (autogenerated_main_conf->log_file.len > 0) {
        psg_json_value_set_ngx_str_ne(j_log_target, "path", &autogenerated_main_conf->log_file);
    } else if (cycle->new_log.file == NULL) {
        ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, "Cannot initialize " PROGRAM_NAME
            " because Nginx is not configured with an error log file."
            " Please either configure Nginx with an error log file, or configure "
            PROGRAM_NAME " with a `passenger_log_file`");
        result = NGX_ERROR;
        goto cleanup;
    } else if (cycle->new_log.file->name.len > 0) {
        psg_json_value_set_ngx_str_ne(j_log_target, "path", &cycle->new_log.file->name);
    } else if (cycle->log->file->name.len > 0) {
        psg_json_value_set_ngx_str_ne(j_log_target, "path", &cycle->log->file->name);
    }

    if (autogenerated_main_conf->ctl != NULL) {
        ctl = (ngx_keyval_t *) autogenerated_main_conf->ctl->elts;
        for (i = 0; i < autogenerated_main_conf->ctl->nelts; i++) {
            psg_json_value_set_with_autodetected_data_type(w_config,
                (const char *) ctl[i].key.data, ctl[i].key.len,
                (const char *) ctl[i].value.data, ctl[i].value.len,
                &error_message);
            if (error_message != NULL) {
                str.data = ctl[i].key.data;
                str.len = ctl[i].key.len - 1;
                ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,
                    "Error parsing ctl %V as JSON data: %s",
                    &str, error_message);
                result = NGX_ERROR;
                goto cleanup;
            }
        }
    }

    open_log_file_for_after_forking(&after_fork_data, j_log_target);
    if (after_fork_data.stderr_equals_log_file) {
        psg_json_value_set_bool(j_log_target, "stderr", 1);
    }
    if (!psg_json_value_empty(j_log_target)) {
        psg_json_value_set_value(w_config, "log_target", -1, j_log_target);
    }

    ret = psg_watchdog_launcher_start(psg_watchdog_launcher,
        passenger_root,
        w_config,
        starting_watchdog_after_fork,
        &after_fork_data,
        &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 instance_dir + "/web_server_info/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/web_server_info/control_process.pid",
                        psg_watchdog_launcher_get_instance_dir(psg_watchdog_launcher, NULL));
    *last = (u_char) '\0';
    if (create_file(cycle, filename, (const u_char *) "", 0, (uid_t) core_conf->user, (gid_t) -1) != NGX_OK) {
        result = NGX_ERROR;
        goto cleanup;
    }
    if (ret == -1) {
        result = NGX_ERROR;
        goto cleanup;
    }

cleanup:
    psg_json_value_free(w_config);
    psg_json_value_free(j_log_target);
    free(passenger_root);
    free(error_message);

    if (after_fork_data.log_fd != -1) {
        close(after_fork_data.log_fd);
    }

    if (result == NGX_ERROR && autogenerated_main_conf->abort_on_startup_error) {
        exit(1);
    }

    return result;

error_enomem:
    ngx_log_error(NGX_LOG_ALERT, cycle->log, ENOMEM, "Cannot allocate memory");
    result = NGX_ERROR;
    goto cleanup;
}