void dispatch() { handler *cur; char *path_info = get_path_info(); if (path_info == NULL) { error_handler("NULL path_info"); return; } char *method_str = get_method(); if (method_str == NULL) { error_handler("NULL method_str"); return; } int method; if (strcmp(method_str, "GET") == 0) { method = GET; } else if (strcmp(method_str, "POST") == 0) { method = POST; } else if (strcmp(method_str, "PUT") == 0) { method = PUT; } else if (strcmp(method_str, "HEAD") == 0) { method = HEAD; } else if (strcmp(method_str, "DELETE") == 0) { method = DELETE; } else { error_handler("unknown request method"); return; } for (cur = head; cur != NULL; cur = cur->next) { if (cur->method == method) { regmatch_t *matches = malloc(sizeof(regmatch_t) * cur->nmatch); int m = regexec(&cur->regex, path_info, cur->nmatch, matches, 0); if (m == 0) { cur->func(matches); free(matches); return; } free(matches); } } error_handler("no match"); }
/* Serve the client that connected to the webserver */ static int serve_client(t_session *session) { int result, length, auth_result; char *qmark, chr, *header; t_host *host_record; t_access access; t_deny_body *deny_body; t_req_method request_method; t_ip_addr ip_addr; #ifdef ENABLE_XSLT char *xslt_file; #endif #ifdef ENABLE_TOOLKIT int i; t_toolkit_options toolkit_options; #endif #ifdef ENABLE_RPROXY t_rproxy *rproxy; #endif #ifdef ENABLE_DEBUG session->current_task = "fetch & parse request"; #endif if ((result = fetch_request(session)) != 200) { session->request_method = GET; return result; } else if ((result = parse_request(session, session->header_length + session->content_length)) != 200) { session->request_method = GET; return result; } #ifdef ENABLE_DEBUG session->current_task = "serve client"; #endif session->time = time(NULL); /* Hide reverse proxies */ if (in_iplist(session->config->hide_proxy, &(session->ip_address))) { if (last_forwarded_ip(session->http_headers, &ip_addr) == 0) { if (reposition_client(session, &ip_addr) != -1) { copy_ip(&(session->ip_address), &ip_addr); } } } /* SSH tunneling */ #ifdef ENABLE_RPROXY if (session->request_method == CONNECT) { if (in_iplist(session->config->tunnel_ssh, &(session->ip_address)) == false) { return 405; } #ifdef ENABLE_SSL if (session->binding->use_ssl) { return 405; } #endif if (strcmp(session->request_uri, "localhost:22") != 0) { if (strcmp(session->request_uri, "127.0.0.1:22") != 0) { if (strcmp(session->request_uri, "::1.22") != 0) { return 403; } } } log_system(session, "SSH tunnel requested"); if (tunnel_ssh_connection(session->client_socket) != 0) { log_system(session, "SSH tunnel failed"); } else { log_system(session, "SSH tunnel terminated"); } session->keep_alive = false; return 200; } #endif /* Find host record */ if (session->hostname != NULL) { if (remove_port_from_hostname(session) == -1) { log_error(session, "error removing port from hostname"); return 500; } if ((host_record = get_hostrecord(session->config->first_host, session->hostname, session->binding)) != NULL) { session->host = host_record; #ifdef ENABLE_TOMAHAWK session->last_host = host_record; #endif } } session->host->access_time = session->time; #ifdef ENABLE_SSL /* SSL client authentication */ if (session->binding->use_ssl) { if ((session->host->ca_certificate != NULL) && (ssl_has_peer_cert(&(session->ssl_context)) == false)) { log_error(session, "Missing client SSL certificate"); return 440; } } #endif /* Enforce usage of first hostname */ if (session->host->enforce_first_hostname && (session->hostname != NULL)) { if (**(session->host->hostname.item) != '*') { if (strcmp(session->hostname, *(session->host->hostname.item)) != 0) { session->cause_of_301 = enforce_first_hostname; return 301; } } } /* Enforce usage of SSL */ #ifdef ENABLE_SSL if (session->host->require_ssl && (session->binding->use_ssl == false)) { if ((qmark = strchr(session->uri, '?')) != NULL) { *qmark = '\0'; session->vars = qmark + 1; session->uri_len = strlen(session->uri); } session->cause_of_301 = require_ssl; return 301; } #endif /* Deny matching bodies */ if (session->body != NULL) { chr = *(session->body + session->content_length); *(session->body + session->content_length) = '\0'; deny_body = session->host->deny_body; while (deny_body != NULL) { if (strpcmp(session->body, &(deny_body->pattern)) == 0) { if ((session->config->ban_on_denied_body > 0) && (ip_allowed(&(session->ip_address), session->config->banlist_mask) != deny)) { ban_ip(&(session->ip_address), session->config->ban_on_denied_body, session->config->kick_on_ban); log_system(session, "Client banned because of denied body"); #ifdef ENABLE_MONITOR if (session->config->monitor_enabled) { monitor_count_ban(session); } #endif } log_exploit_attempt(session, "denied body", session->body); #ifdef ENABLE_TOMAHAWK increment_counter(COUNTER_EXPLOIT); #endif #ifdef ENABLE_MONITOR if (session->config->monitor_enabled) { monitor_count_exploit(session); monitor_event("Request body denied for %s", session->host->hostname.item[0]); } #endif *(session->body + session->content_length) = chr; return 403; } deny_body = deny_body->next; } *(session->body + session->content_length) = chr; } /* Websocket */ if (session->request_method == GET) { if ((header = get_http_header("Connection:", session->http_headers)) != NULL) { if (strcasestr(header, "upgrade") != NULL) { if ((header = get_http_header("Upgrade:", session->http_headers)) != NULL) { if (strcasecmp(header, "websocket") == 0) { switch (access = allow_client(session)) { case deny: log_error(session, fb_accesslist); return 403; case allow: break; case pwd: case unspecified: if ((auth_result = http_authentication_result(session, access == unspecified)) != 200) { return auth_result; } } session->keep_alive = false; if (forward_to_websocket(session) == -1) { return 500; } return 200; } } } } } #ifdef ENABLE_RPROXY /* Reverse proxy */ rproxy = session->host->rproxy; while (rproxy != NULL) { if (rproxy_match(rproxy, session->request_uri)) { if (rproxy_loop_detected(session->http_headers)) { return 508; } if ((qmark = strchr(session->uri, '?')) != NULL) { *qmark = '\0'; session->vars = qmark + 1; } if (validate_url(session) == false) { return -1; } if ((session->vars != NULL) && (session->host->secure_url)) { if (forbidden_chars_present(session->vars)) { log_error(session, "URL contains forbidden characters"); return 403; } } if (duplicate_host(session) == false) { log_error(session, "duplicate_host() error"); return 500; } if ((result = uri_to_path(session)) != 200) { return result; } if (session->host->ignore_dot_hiawatha == false) { if (load_user_config(session) == -1) { return 500; } } if ((result = copy_directory_settings(session)) != 200) { return result; } switch (access = allow_client(session)) { case deny: log_error(session, fb_accesslist); return 403; case allow: break; case pwd: case unspecified: if ((auth_result = http_authentication_result(session, access == unspecified)) != 200) { return auth_result; } } /* Prevent SQL injection */ if (session->host->prevent_sqli) { result = prevent_sqli(session); if (result == 1) { session->error_cause = ec_SQL_INJECTION; } if (result != 0) { return -1; } } /* Prevent Cross-site Scripting */ if (session->host->prevent_xss != p_no) { if (prevent_xss(session) > 0) { if (session->host->prevent_xss == p_block) { session->error_cause = ec_XSS; return -1; } } } /* Prevent Cross-site Request Forgery */ if (session->host->prevent_csrf != p_no) { if (prevent_csrf(session) > 0) { if (session->host->prevent_csrf == p_block) { session->error_cause = ec_CSRF; return -1; } } } return proxy_request(session, rproxy); } rproxy = rproxy->next; } #endif /* Actions based on request method */ switch (session->request_method) { case TRACE: if (session->binding->enable_trace == false) { return 501; } return handle_trace_request(session); case PUT: case DELETE: if ((session->binding->enable_alter == false) && (session->host->webdav_app == false)) { return 501; } break; case unknown: return 400; case unsupported: if (session->host->webdav_app == false) { return 501; } break; default: break; } if (duplicate_host(session) == false) { log_error(session, "duplicate_host() error"); return 500; } #ifdef ENABLE_TOOLKIT if (session->host->ignore_dot_hiawatha == false) { if (load_user_root_config(session) == -1) { return 500; } } /* URL toolkit */ init_toolkit_options(&toolkit_options); toolkit_options.method = session->method; toolkit_options.website_root = session->host->website_root; toolkit_options.url_toolkit = session->config->url_toolkit; toolkit_options.allow_dot_files = session->host->allow_dot_files; toolkit_options.http_headers = session->http_headers; #ifdef ENABLE_SSL toolkit_options.use_ssl = session->binding->use_ssl; #endif if (((session->request_method != PUT) && (session->request_method != DELETE)) || session->host->webdav_app) { for (i = 0; i < session->host->toolkit_rules.size; i++) { if ((result = use_toolkit(session->uri, session->host->toolkit_rules.item[i], &toolkit_options)) == UT_ERROR) { return 500; } if ((toolkit_options.ban > 0) && (ip_allowed(&(session->ip_address), session->config->banlist_mask) != deny)) { ban_ip(&(session->ip_address), toolkit_options.ban, session->config->kick_on_ban); log_system(session, "Client banned because of URL match in UrlToolkit rule"); #ifdef ENABLE_MONITOR if (session->config->monitor_enabled) { monitor_count_ban(session); } #endif return 403; } session->toolkit_fastcgi = toolkit_options.fastcgi_server; if (toolkit_options.new_url != NULL) { if (register_tempdata(&(session->tempdata), toolkit_options.new_url, tc_data) == -1) { free(toolkit_options.new_url); log_error(session, "error registering temporary data"); return 500; } session->uri = toolkit_options.new_url; } if (result == UT_REDIRECT) { if ((session->location = strdup(toolkit_options.new_url)) == NULL) { return -1; } session->cause_of_301 = location; return 301; } if (result == UT_DENY_ACCESS) { log_error(session, "access denied via URL toolkit rule"); return 403; } if (toolkit_options.expire > -1) { session->expires = toolkit_options.expire; session->caco_private = toolkit_options.caco_private; } } } #endif /* Find GET data */ if ((qmark = strchr(session->uri, '?')) != NULL) { *qmark = '\0'; session->vars = qmark + 1; } url_decode(session->uri); session->uri_len = strlen(session->uri); if ((session->vars != NULL) && (session->host->secure_url)) { if (forbidden_chars_present(session->vars)) { log_error(session, "URL contains forbidden characters"); return 403; } } if (validate_url(session) == false) { return -1; } if ((result = uri_to_path(session)) != 200) { return result; } /* Load configfile from directories */ if (session->host->ignore_dot_hiawatha == false) { if (load_user_config(session) == -1) { return 500; } } if ((result = copy_directory_settings(session)) != 200) { return result; } switch (access = allow_client(session)) { case deny: log_error(session, fb_accesslist); return 403; case allow: break; case pwd: case unspecified: if ((auth_result = http_authentication_result(session, access == unspecified)) != 200) { return auth_result; } } switch (is_directory(session->file_on_disk)) { case error: return 500; case yes: session->uri_is_dir = true; break; case no: if (((session->request_method != PUT) || session->host->webdav_app) && (session->host->enable_path_info)) { if ((result = get_path_info(session)) != 200) { return result; } } break; case no_access: log_error(session, fb_filesystem); return 403; case not_found: if (session->request_method == DELETE) { return 404; } } #ifdef ENABLE_TOOLKIT if ((session->toolkit_fastcgi == NULL) && session->uri_is_dir) { #else if (session->uri_is_dir) { #endif length = strlen(session->file_on_disk); if (*(session->file_on_disk + length - 1) == '/') { strcpy(session->file_on_disk + length, session->host->start_file); } else { return 301; } } if (get_target_extension(session) == -1) { log_error(session, "error getting extension"); return 500; } if (((session->request_method != PUT) && (session->request_method != DELETE)) || session->host->webdav_app) { check_target_is_cgi(session); } /* Handle request based on request method */ request_method = session->request_method; if (session->host->webdav_app) { if ((request_method == PUT) || (request_method == DELETE)) { request_method = POST; } } switch (request_method) { case GET: case HEAD: if (session->cgi_type != no_cgi) { session->body = NULL; result = execute_cgi(session); #ifdef ENABLE_XSLT } else if ((xslt_file = find_xslt_file(session)) != NULL) { result = handle_xml_file(session, xslt_file); free(xslt_file); #endif } else { result = send_file(session); } if (result == 404) { #ifdef ENABLE_XSLT if ((session->host->show_index != NULL) && (session->uri[session->uri_len - 1] == '/')) { result = show_index(session); } #endif #ifdef ENABLE_MONITOR } else if (session->config->monitor_enabled) { if ((result == 200) && (session->host->monitor_host)) { unlink(session->file_on_disk); } #endif } if ((session->request_method == GET) && (session->cgi_type == no_cgi) && (session->directory != NULL)) { if (session->directory->run_on_download != NULL) { run_program(session, session->directory->run_on_download, result); } } break; case POST: case unsupported: if (session->cgi_type != no_cgi) { result = execute_cgi(session); #ifdef ENABLE_XSLT } else if ((xslt_file = find_xslt_file(session)) != NULL) { result = handle_xml_file(session, xslt_file); free(xslt_file); #endif } else { result = 405; } break; case PUT: result = handle_put_request(session); if (((result == 201) || (result == 204)) && (session->host->run_on_alter != NULL)) { run_program(session, session->host->run_on_alter, result); } break; case DELETE: result = handle_delete_request(session); if ((result == 204) && (session->host->run_on_alter != NULL)) { run_program(session, session->host->run_on_alter, result); } break; case WHEN: send_code(session); break; default: result = 400; } return result; } /* Handle timeout upon sending request */ static void handle_timeout(t_session *session) { if ((session->config->ban_on_timeout > 0) && (ip_allowed(&(session->ip_address), session->config->banlist_mask) != deny)) { ban_ip(&(session->ip_address), session->config->ban_on_timeout, session->config->kick_on_ban); log_system(session, "Client banned because of connection timeout"); #ifdef ENABLE_MONITOR if (session->config->monitor_enabled) { monitor_count_ban(session); } #endif } else { log_system(session, "Timeout while waiting for first request"); } } /* Request has been handled, handle the return code. */ static void handle_request_result(t_session *session, int result) { char *hostname; #ifdef ENABLE_DEBUG session->current_task = "handle request result"; #endif if (result == -1) switch (session->error_cause) { case ec_MAX_REQUESTSIZE: log_system(session, "Maximum request size reached"); session->return_code = 413; send_code(session); if ((session->config->ban_on_max_request_size > 0) && (ip_allowed(&(session->ip_address), session->config->banlist_mask) != deny)) { ban_ip(&(session->ip_address), session->config->ban_on_max_request_size, session->config->kick_on_ban); log_system(session, "Client banned because of sending a too large request"); #ifdef ENABLE_MONITOR if (session->config->monitor_enabled) { monitor_count_ban(session); } #endif } break; case ec_TIMEOUT: if (session->kept_alive == 0) { session->return_code = 408; send_code(session); handle_timeout(session); } break; case ec_CLIENT_DISCONNECTED: if (session->kept_alive == 0) { log_system(session, "Silent client disconnected"); } break; case ec_SOCKET_READ_ERROR: if (errno != ECONNRESET) { log_system(session, "Error while reading request"); } break; case ec_SOCKET_WRITE_ERROR: log_request(session); break; case ec_FORCE_QUIT: log_system(session, "Client kicked"); break; case ec_SQL_INJECTION: if ((session->config->ban_on_sqli > 0) && (ip_allowed(&(session->ip_address), session->config->banlist_mask) != deny)) { ban_ip(&(session->ip_address), session->config->ban_on_sqli, session->config->kick_on_ban); hostname = (session->hostname != NULL) ? session->hostname : unknown_host; log_system(session, "Client banned because of SQL injection on %s", hostname); #ifdef ENABLE_MONITOR if (session->config->monitor_enabled) { monitor_count_ban(session); } #endif } session->return_code = 441; send_code(session); log_request(session); break; case ec_XSS: session->return_code = 442; send_code(session); log_request(session); break; case ec_CSRF: session->return_code = 443; send_code(session); log_request(session); break; case ec_INVALID_URL: if ((session->config->ban_on_invalid_url > 0) && (ip_allowed(&(session->ip_address), session->config->banlist_mask) != deny)) { ban_ip(&(session->ip_address), session->config->ban_on_invalid_url, session->config->kick_on_ban); hostname = (session->hostname != NULL) ? session->hostname : unknown_host; log_system(session, "Client banned because of invalid URL on %s", hostname); #ifdef ENABLE_MONITOR if (session->config->monitor_enabled) { monitor_count_ban(session); } #endif } send_code(session); break; default: if (session->data_sent == false) { session->return_code = 500; if (send_code(session) == -1) { session->keep_alive = false; } } } else switch (result) { case 200: break; case 201: case 204: case 304: case 412: if (session->data_sent == false) { session->return_code = result; if (send_header(session) == -1) { session->keep_alive = false; } else if (send_buffer(session, "Content-Length: 0\r\n\r\n", 21) == -1) { session->keep_alive = false; } } break; case 411: case 413: session->keep_alive = false; if (session->data_sent == false) { session->return_code = result; if (send_header(session) == -1) { session->keep_alive = false; } else if (send_buffer(session, "Content-Length: 0\r\n\r\n", 21) == -1) { session->keep_alive = false; } } break; case 400: log_garbage(session); if (session->data_sent == false) { session->return_code = 400; if (send_code(session) == -1) { session->keep_alive = false; } } if ((session->config->ban_on_garbage > 0) && (ip_allowed(&(session->ip_address), session->config->banlist_mask) != deny)) { ban_ip(&(session->ip_address), session->config->ban_on_garbage, session->config->kick_on_ban); log_system(session, "Client banned because of sending garbage"); #ifdef ENABLE_MONITOR if (session->config->monitor_enabled) { monitor_count_ban(session); } #endif } #ifdef ENABLE_MONITOR if (session->config->monitor_enabled) { monitor_count_bad_request(session); } #endif break; case 401: case 403: case 404: case 501: case 503: if (session->data_sent == false) { switch (handle_error(session, result)) { case -1: session->keep_alive = false; break; case 200: break; default: if (session->data_sent == false) { session->return_code = result; if (send_code(session) == -1) { session->keep_alive = false; } } } } break; case 500: session->keep_alive = false; default: if (session->data_sent == false) { session->return_code = result; send_code(session); } } if ((result > 0) && (result != 400)) { log_request(session); } else { session->keep_alive = false; } }
int config_init ( int argc, char **argv ) { const char *config_name = DEFAULT_CONFIG_FILE; // name of the used config file, can be overwritten via CLI const struct configOption_st *opt; const char *exe = argv[0]; int default_config = 1; int testparsing = 0; argc--; argv++; #ifdef __EMSCRIPTEN__ exe = strdup("/files/emscripten-virtual-executable"); #endif #ifdef _WIN32 console_open_window(); #endif SDL_VERSION(&sdlver_compiled); SDL_GetVersion(&sdlver_linked); if (sdlver_linked.major < 2 || (sdlver_linked.minor == 0 && sdlver_linked.patch < 4)) { ERROR_WINDOW("Too old SDL library linked, at least version 2.0.4 is required."); return 1; } /* SDL info on paths */ if (get_path_info()) return 1; /* ugly hack: pre-parse comand line to find debug statement (to be worse, it does not handle single argument options too well ... */ #ifdef DISABLE_DEBUG printf("DEBUG: disabled at compilation time." NL); #else while (testparsing < argc) { if (!strcmp(argv[testparsing], "-" DEBUGFILE_OPT) && testparsing != argc - 1 && strcmp(argv[testparsing + 1], "none")) { debug_fp = fopen(argv[testparsing + 1], "w"); DEBUGPRINT("DEBUG: enable logging into file: %s" NL, argv[testparsing + 1]); if (debug_fp == NULL) fprintf(stderr, "Cannot open debug logging file: %s" NL, argv[testparsing + 1]); break; } testparsing++; } testparsing = 0; #endif /* end of ugly hack */ /* let's continue with the info block ... */ DEBUGPRINT("%s %s v%s %s %s" NL "GIT %s compiled by (%s) at (%s) with (%s)-(%s)" NL "Platform: (%s) (%d-bit), video: (%s), audio: (%s), " "SDL version compiled: (%d.%d.%d) and linked: (%d.%d.%d) rev (%s)" NL NL, WINDOW_TITLE, DESCRIPTION, VERSION, COPYRIGHT, PROJECT_PAGE, XEMU_BUILDINFO_GIT, XEMU_BUILDINFO_ON, XEMU_BUILDINFO_AT, CC_TYPE, XEMU_BUILDINFO_CC, SDL_GetPlatform(), ARCH_BITS, SDL_GetCurrentVideoDriver(), SDL_GetCurrentAudioDriver(), sdlver_compiled.major, sdlver_compiled.minor, sdlver_compiled.patch, sdlver_linked.major, sdlver_linked.minor, sdlver_linked.patch, SDL_GetRevision() ); DEBUGPRINT("PATH: executable: %s" NL, exe); /* SDL path info block printout */ DEBUGPRINT("PATH: SDL base path: %s" NL, app_base_path); DEBUGPRINT("PATH: SDL pref path: %s" NL, app_pref_path); #ifndef _WIN32 DEBUGPRINT("PATH: data directory: %s/" NL, DATADIR); #endif DEBUGPRINT("PATH: Current directory: %s" NL NL, current_directory); /* Look the very basic command line switches first */ if (argc && is_help_request_option(argv[0])) { opt = configOptions; printf("USAGE:" NL NL "\t%s -optname optval -optname2 optval2 ..." NL NL "OPTIONS:" NL NL "-config" NL "\tUse config file (or do not use the default one, if \"none\" is specified). This must be the first option if used! [default: @config]" NL, exe ); while (opt->name) { printf("-%s" NL "\t%s [default: %s]" NL, opt->name, opt->help, opt->defval ? opt->defval : "-"); opt++; } printf(NL "%s" NL, disclaimer); #ifdef _WIN32 if (!console_is_open) ERROR_WINDOW("Could not dump help, since console couldn't be allocated."); #endif XEMUEXIT(0); } DEBUGPRINT("%s" NL NL, disclaimer); if (argc && !strcasecmp(argv[0], "-testparsing")) { testparsing = 1; argc--; argv++; } if (argc & 1) { fprintf(stderr, "FATAL: Bad command line: should be even number of parameters (two for an option as key and its value)" NL); return 1; } if (argc > 1 && !strcmp(argv[0], "-config")) { default_config = 0; config_name = argv[1]; argc -= 2; argv += 2; } /* Set default (built-in) values */ opt = configOptions; while (opt->name) { if (opt->defval) config_set_internal(opt->name, -1, opt->defval); opt++; } config_set_internal("rom", 0, COMBINED_ROM_FN); // set default "combined" ROM image set (from segment 0, starting with EXOS) /* Default values for the keyboard follows ... */ keymap_preinit_config_internal(); /* check if we have written sample config file, if there is not, let's create one */ save_sample_config(DEFAULT_CONFIG_SAMPLE_FILE); /* now parse config file (not the sample one!) if there is any */ if (strcasecmp(config_name, "none")) { char path[PATH_MAX + 1]; FILE *f = open_emu_file(config_name, "r", path); DEBUGPRINT("CONFIG: config file: %s (%s)" NL, config_name, f ? path : "*** CANNOT OPEN, NOT USING CONFIG FILE ***"); if (f) { if (load_config_file_stream(f, path)) { fclose(f); return 1; } fclose(f); } else if (!default_config) { fprintf(stderr, "FATAL: Cannot open requested config file: %s" NL, config_name); return 1; } else DEBUGPRINT("CONFIG: Skipping default config file (cannot open), using built-in defaults." NL); } else DEBUGPRINT("CONFIG: Using config file: DISABLED in command line" NL); /* parse command line ... */ if (parse_command_line(argc, argv)) return -1; /* open debug file, if it was not requested via command line at the beginning ... */ if (!debug_fp && strcmp(config_getopt_str(DEBUGFILE_OPT), "none")) { debug_fp = fopen(config_getopt_str(DEBUGFILE_OPT), "w"); DEBUGPRINT("DEBUG: enable logging into file: %s" NL, config_getopt_str(DEBUGFILE_OPT)); if (!debug_fp) ERROR_WINDOW("Cannot open debug messages log file requested: %s", config_getopt_str(DEBUGFILE_OPT)); } if (debug_fp) INFO_WINDOW("DEBUG: Debug messages logging is active"); else printf("DEBUG: No debug messages logging is active." NL); /* test parsing mode? */ if (testparsing) { printf(NL "--- TEST DUMP OF *PARSED* CONFIGURATION (requested)" NL NL); dump_config(stdout); printf(NL "--- END OF TEST PARSING MODE (requested)" NL); XEMUEXIT(0); } DEBUG("CONFIG: End of configuration step." NL NL); /* Close console, unless user requested it with the -console option */ #ifdef _WIN32 if (!config_getopt_int("console")) console_close_window(); #else if (config_getopt_int("console")) console_open_window(); // on non-windows, it only will mark console as open for monitor to be used .. #endif return 0; }