/* * Copyright (c) 2004-2009 Sergey Lyubka * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * * $Id: main.c 518 2010-05-03 12:55:35Z valenok $ */ #if defined(_WIN32) #define _CRT_SECURE_NO_WARNINGS /* Disable deprecation warning in VS2005 */ #endif /* _WIN32 */ #include #include #include #include #include #include #include #include "mongoose.h" #ifdef _WIN32 #include #include #define DIRSEP '\\' #define snprintf _snprintf #if !defined(__LCC__) #define strdup(x) _strdup(x) #endif /* !MINGW */ #define sleep(x) Sleep((x) * 1000) #else #include #include /* For pause() */ #define DIRSEP '/' #endif /* _WIN32 */ static int exit_flag; /* Program termination flag */ #if !defined(CONFIG_FILE) #define CONFIG_FILE "mongoose.conf" #endif /* !CONFIG_FILE */ static void signal_handler(int sig_num) { #if !defined(_WIN32) if (sig_num == SIGCHLD) { do { } while (waitpid(-1, &sig_num, WNOHANG) > 0); } else #endif /* !_WIN32 */ { exit_flag = sig_num; } } /* * Edit the passwords file. */ static int mg_edit_passwords(const char *fname, const char *domain, const char *user, const char *pass) { struct mg_context *ctx; struct mg_config config; int retval; memset(&config, 0, sizeof(config)); config.auth_domain = (char *) domain; config.num_threads = "0"; config.listening_ports = ""; ctx = mg_start(&config); retval = mg_modify_passwords_file(ctx, fname, user, pass); mg_stop(ctx); return (retval); } #define OFFSET(x) offsetof(struct mg_config, x) static struct option_descriptor { const char *name; const char *description; size_t offset; } known_options[] = { {"root", "\tWeb root directory", OFFSET(document_root)}, {"index_files", "Index files", OFFSET(index_files)}, {"ssl_cert", "SSL certificate file", OFFSET(ssl_certificate)}, {"ports", "Listening ports", OFFSET(listening_ports)}, {"dir_list", "Directory listing", OFFSET(enable_directory_listing)}, {"protect", "URI to htpasswd mapping", OFFSET(protect)}, {"cgi_ext", "CGI extensions", OFFSET(cgi_extensions)}, {"cgi_interp", "CGI interpreter to use", OFFSET(cgi_interpreter)}, {"cgi_env", "Custom CGI enviroment variables", OFFSET(cgi_environment)}, {"ssi_ext", "SSI extensions", OFFSET(ssi_extensions)}, {"auth_realm", "Authentication domain name", OFFSET(auth_domain)}, {"auth_gpass", "Global passwords file", OFFSET(global_passwords_file)}, {"auth_PUT", "PUT,DELETE auth file", OFFSET(put_delete_passwords_file)}, {"uid", "\tRun as user", OFFSET(uid)}, {"access_log", "Access log file", OFFSET(access_log_file)}, {"error_log", "Error log file", OFFSET(error_log_file)}, {"acl", "\tAllow/deny IP addresses/subnets", OFFSET(acl)}, {"num_threads", "Threads to spawn", OFFSET(num_threads)}, {"mime_types", "Extra mime types to use", OFFSET(mime_types)}, {NULL, NULL, 0} }; static void show_usage_and_exit(const struct mg_config *config) { const struct option_descriptor *o; const char *value; (void) fprintf(stderr, "Mongoose version %s (c) Sergey Lyubka\n" "usage: mongoose [options] [config_file]\n", mg_version()); fprintf(stderr, " -A \n"); for (o = known_options; o->name != NULL; o++) { (void) fprintf(stderr, " -%s\t%s", o->name, o->description); value = * (char **) ((char *) config + o->offset); if (value != NULL) fprintf(stderr, " (default: \"%s\")", value); fputc('\n', stderr); } exit(EXIT_FAILURE); } static void set_option(struct mg_config *config, const char *name, char *value) { const struct option_descriptor *o; for (o = known_options; o->name != NULL; o++) if (strcmp(name, o->name) == 0) { * (char **) ((char *) config + o->offset) = value; break; } if (o->name == NULL) show_usage_and_exit(config); } static void process_command_line_arguments(struct mg_config *config, char *argv[]) { const char *config_file = CONFIG_FILE; char line[512], opt[512], *vals[100], val[512], path[FILENAME_MAX], *p; FILE *fp; size_t i, line_no = 0; /* First find out, which config file to open */ for (i = 1; argv[i] != NULL && argv[i][0] == '-'; i += 2) if (argv[i + 1] == NULL) show_usage_and_exit(config); if (argv[i] != NULL && argv[i + 1] != NULL) { /* More than one non-option arguments are given */ show_usage_and_exit(config); } else if (argv[i] != NULL) { /* Just one non-option argument is given, this is config file */ config_file = argv[i]; } else { /* No config file specified. Look for one where binary lives */ if ((p = strrchr(argv[0], DIRSEP)) != 0) { (void) snprintf(path, sizeof(path), "%.*s%s", (int) (p - argv[0]) + 1, argv[0], config_file); config_file = path; } } fp = fopen(config_file, "r"); /* If config file was set in command line and open failed, exit */ if (fp == NULL && argv[i] != NULL) { (void) fprintf(stderr, "cannot open config file %s: %s\n", config_file, strerror(errno)); exit(EXIT_FAILURE); } /* Reset temporary value holders */ (void) memset(vals, 0, sizeof(vals)); if (fp != NULL) { (void) printf("Loading config file %s, " "ignoring command line arguments\n", config_file); /* Loop over the lines in config file */ while (fgets(line, sizeof(line), fp) != NULL) { line_no++; /* Ignore empty lines and comments */ if (line[0] == '#' || line[0] == '\n') continue; if (sscanf(line, "%s %[^\r\n#]", opt, val) != 2) { fprintf(stderr, "%s: line %d is invalid\n", config_file, (int) line_no); exit(EXIT_FAILURE); } /* TODO(lsm): free this at some point */ p = malloc(strlen(val) + 1); (void) strcpy(p, val); set_option(config, opt, p); } (void) fclose(fp); } else { for (i = 1; argv[i] != NULL && argv[i][0] == '-'; i += 2) set_option(config, &argv[i][1], argv[i + 1]); } } int main(int argc, char *argv[]) { struct mg_config config; struct mg_context *ctx; /* Initialize configuration with default values */ (void) memset(&config, 0, sizeof(config)); config.document_root = "."; config.enable_directory_listing = "yes"; config.auth_domain = "mydomain.com"; config.num_threads = "20"; config.index_files = "index.html,index.htm,index.cgi"; config.cgi_extensions = ".cgi,.pl,.php"; config.ssi_extensions = ".shtml,.shtm"; config.listening_ports = "8080"; /* Edit passwords file if -A option is specified */ if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'A') { if (argc != 6) show_usage_and_exit(&config); exit(mg_edit_passwords(argv[2], argv[3], argv[4], argv[5]) == MG_SUCCESS ? EXIT_SUCCESS : EXIT_FAILURE); } /* Show usage if -h or --help options are specified */ if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))) show_usage_and_exit(&config); /* Update config based on command line arguments */ process_command_line_arguments(&config, argv); /* Setup signal handler: quit on Ctrl-C */ #ifndef _WIN32 (void) signal(SIGCHLD, signal_handler); #endif /* _WIN32 */ (void) signal(SIGTERM, signal_handler); (void) signal(SIGINT, signal_handler); /* Start Mongoose */ if ((ctx = mg_start(&config)) == NULL) { (void) printf("%s\n", "Cannot initialize Mongoose context"); exit(EXIT_FAILURE); } (void) printf("Mongoose %s started on port(s) %s " "with web root [%s]\n", mg_version(), config.listening_ports, config.document_root); fflush(stdout); while (exit_flag == 0) sleep(1); (void) printf("Exiting on signal %d, " "waiting for all threads to finish...", exit_flag); fflush(stdout); mg_stop(ctx); (void) printf("%s", " done.\n"); return (EXIT_SUCCESS); }