int start_server() { int child_indx, pid, i, ctl_socket; int childs, freeservers, used, maxrequests, ret; char command_buffer[COMMANDS_BUFFER_SIZE]; int user_informed = 0; ctl_socket = ci_named_pipe_create(CONF.COMMANDS_SOCKET); if (ctl_socket < 0) { ci_debug_printf(1, "Error opening control socket %s. Fatal error, exiting!\n", CONF.COMMANDS_SOCKET); exit(0); } if (!ci_proc_mutex_init(&accept_mutex)) { ci_debug_printf(1, "Can't init mutex for accepting conenctions. Fatal error, exiting!\n"); exit(0); } childs_queue = malloc(sizeof(struct childs_queue)); if (!create_childs_queue(childs_queue, 2 * CONF.MAX_SERVERS)) { ci_proc_mutex_destroy(&accept_mutex); ci_debug_printf(1, "Can't init shared memory. Fatal error, exiting!\n"); exit(0); } init_commands(); pid = 1; #ifdef MULTICHILD if (CONF.START_SERVERS > CONF.MAX_SERVERS) CONF.START_SERVERS = CONF.MAX_SERVERS; for (i = 0; i < CONF.START_SERVERS; i++) { if (pid) pid = start_child(LISTEN_SOCKET); } if (pid != 0) { main_signals(); while (1) { if ((ret = wait_for_commands(ctl_socket, command_buffer, 1)) > 0) { ci_debug_printf(5, "I received the command: %s\n", command_buffer); handle_monitor_process_commands(command_buffer); } if (ret < 0) { /*Eof received on pipe. Going to reopen ... */ ci_named_pipe_close(ctl_socket); ctl_socket = ci_named_pipe_open(CONF.COMMANDS_SOCKET); if (ctl_socket < 0) { ci_debug_printf(1, "Error opening control socket. We are unstable and going down!"); c_icap_going_to_term = 1; } } if (c_icap_going_to_term) break; childs_queue_stats(childs_queue, &childs, &freeservers, &used, &maxrequests); ci_debug_printf(10, "Server stats: \n\t Children: %d\n\t Free servers: %d\n" "\tUsed servers:%d\n\tRequests served:%d\n", childs, freeservers, used, maxrequests); if (MAX_REQUESTS_PER_CHILD > 0 && (child_indx = find_a_child_nrequests (childs_queue, MAX_REQUESTS_PER_CHILD)) >= 0) { ci_debug_printf(8, "Max requests reached for child :%d of pid :%d\n", child_indx, childs_queue->childs[child_indx].pid); pid = start_child(LISTEN_SOCKET); // usleep(500); childs_queue->childs[child_indx].father_said = GRACEFULLY; /*kill a server ... */ kill(childs_queue->childs[child_indx].pid, SIGTERM); } else if ((freeservers <= CONF.MIN_SPARE_THREADS && childs < CONF.MAX_SERVERS) || childs < CONF.START_SERVERS) { ci_debug_printf(8, "Free Servers: %d, children: %d. Going to start a child .....\n", freeservers, childs); pid = start_child(LISTEN_SOCKET); } else if (freeservers >= CONF.MAX_SPARE_THREADS && childs > CONF.START_SERVERS && (freeservers - CONF.THREADS_PER_CHILD) > CONF.MIN_SPARE_THREADS) { if ((child_indx = find_an_idle_child(childs_queue)) >= 0) { childs_queue->childs[child_indx].father_said = GRACEFULLY; ci_debug_printf(8, "Free Servers: %d, children: %d. Going to stop child %d(index: %d)\n", freeservers, childs, childs_queue->childs[child_indx].pid, child_indx); /*kill a server ... */ kill(childs_queue->childs[child_indx].pid, SIGTERM); user_informed = 0; } } else if (childs == CONF.MAX_SERVERS && freeservers < CONF.MIN_SPARE_THREADS) { if(! user_informed) { ci_debug_printf(1, "ATTENTION!!!! Not enough available servers (children %d, free servers %d, used servers %d)!!!!! " "Maybe you should increase the MaxServers and the " "ThreadsPerChild values in c-icap.conf file!!!!!!!!!",childs , freeservers, used); user_informed = 1; } } if (c_icap_going_to_term) break; check_for_exited_childs(); if (c_icap_reconfigure) { c_icap_reconfigure = 0; if (!server_reconfigure()) { ci_debug_printf(1, "Error while reconfiguring, exiting!\n"); break; } } } /*Main process exit point */ ci_debug_printf(1, "Possibly a term signal received. Monitor process going to term all children\n"); kill_all_childs(); system_shutdown(); ci_debug_printf(1, "Exiting....\n"); return 1; } #else child_data = (child_shared_data_t *) malloc(sizeof(child_shared_data_t)); child_data->pid = 0; child_data->freeservers = CONF.THREADS_PER_CHILD; child_data->usedservers = 0; child_data->requests = 0; child_data->connections = 0; child_data->to_be_killed = 0; child_data->father_said = 0; child_data->idle = 1; child_data->stats_size = ci_stat_memblock_size(); child_data->stats = malloc(child_data->stats_size); child_data->stats->sig = MEMBLOCK_SIG; ci_stat_attach_mem(child_data->stats, child_data->stats_size, NULL); child_main(LISTEN_SOCKET, 0); ci_proc_mutex_destroy(&accept_mutex); destroy_childs_queue(childs_queue); #endif return 1; }
int main(int argc, char **argv) { /* Parse command line arguments. */ int c = 0, li = 0; int daemonize = 0; const char *config_fn = CONF_DEFAULT_FILE; const char *config_db = NULL; const char *daemon_root = "/"; /* Long options. */ struct option opts[] = { {"config", required_argument, 0, 'c' }, {"confdb", required_argument, 0, 'C' }, {"daemonize", optional_argument, 0, 'd'}, {"version", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'}, {0, 0, 0, 0} }; while ((c = getopt_long(argc, argv, "c:C:dVh", opts, &li)) != -1) { switch (c) { case 'c': config_fn = optarg; break; case 'C': config_db = optarg; break; case 'd': daemonize = 1; if (optarg) { daemon_root = optarg; } break; case 'V': printf("%s, version %s\n", "Knot DNS", PACKAGE_VERSION); return EXIT_SUCCESS; case 'h': case '?': help(); return EXIT_SUCCESS; default: help(); return EXIT_FAILURE; } } /* Check for non-option parameters. */ if (argc - optind > 0) { help(); return EXIT_FAILURE; } /* Now check if we want to daemonize. */ if (daemonize) { if (make_daemon(1, 0) != 0) { fprintf(stderr, "Daemonization failed, shutting down...\n"); return EXIT_FAILURE; } } /* Clear file creation mask. */ umask(0); /* Setup base signal handling. */ setup_signals(); /* Initialize cryptographic backend. */ dnssec_crypto_init(); atexit(dnssec_crypto_cleanup); /* Initialize pseudorandom number generator. */ srand(time(NULL)); /* POSIX 1003.1e capabilities. */ setup_capabilities(); /* Default logging to std out/err. */ log_init(); /* Open configuration. */ conf_t *new_conf = NULL; if (config_db == NULL) { int ret = conf_new(&new_conf, conf_scheme, NULL); if (ret != KNOT_EOK) { log_fatal("failed to initialize configuration database " "(%s)", knot_strerror(ret)); log_close(); return EXIT_FAILURE; } /* Import the configuration file. */ ret = conf_import(new_conf, config_fn, true); if (ret != KNOT_EOK) { log_fatal("failed to load configuration file (%s)", knot_strerror(ret)); conf_free(new_conf, false); log_close(); return EXIT_FAILURE; } new_conf->filename = strdup(config_fn); } else { /* Open configuration database. */ int ret = conf_new(&new_conf, conf_scheme, config_db); if (ret != KNOT_EOK) { log_fatal("failed to open configuration database '%s' " "(%s)", config_db, knot_strerror(ret)); log_close(); return EXIT_FAILURE; } } /* Run post-open config operations. */ int res = conf_post_open(new_conf); if (res != KNOT_EOK) { log_fatal("failed to use configuration (%s)", knot_strerror(res)); conf_free(new_conf, false); log_close(); return EXIT_FAILURE; } conf_update(new_conf); /* Initialize logging subsystem. */ log_reconfigure(conf(), NULL); /* Initialize server. */ server_t server; res = server_init(&server, conf_bg_threads(conf())); if (res != KNOT_EOK) { log_fatal("failed to initialize server (%s)", knot_strerror(res)); conf_free(conf(), false); log_close(); return EXIT_FAILURE; } /* Reconfigure server interfaces. * @note This MUST be done before we drop privileges. */ server_reconfigure(conf(), &server); log_info("configured %zu zones", conf_id_count(conf(), C_ZONE)); /* Alter privileges. */ int uid, gid; if (conf_user(conf(), &uid, &gid) != KNOT_EOK || log_update_privileges(uid, gid) != KNOT_EOK || proc_update_privileges(uid, gid) != KNOT_EOK) { log_fatal("failed to drop privileges"); server_deinit(&server); conf_free(conf(), false); log_close(); return EXIT_FAILURE; } /* Check and create PID file. */ long pid = (long)getpid(); char *pidfile = NULL; if (daemonize) { pidfile = pid_check_and_create(); if (pidfile == NULL) { server_deinit(&server); conf_free(conf(), false); log_close(); return EXIT_FAILURE; } log_info("PID stored in '%s'", pidfile); if (chdir(daemon_root) != 0) { log_warning("failed to change working directory to %s", daemon_root); } else { log_info("changed directory to %s", daemon_root); } } /* Now we're going multithreaded. */ rcu_register_thread(); /* Populate zone database. */ log_info("loading zones"); server_update_zones(conf(), &server); /* Check number of loaded zones. */ if (knot_zonedb_size(server.zone_db) == 0) { log_warning("no zones loaded"); } /* Start it up. */ log_info("starting server"); conf_val_t async_val = conf_get(conf(), C_SRV, C_ASYNC_START); res = server_start(&server, conf_bool(&async_val)); if (res != KNOT_EOK) { log_fatal("failed to start server (%s)", knot_strerror(res)); server_deinit(&server); rcu_unregister_thread(); pid_cleanup(pidfile); log_close(); conf_free(conf(), false); return EXIT_FAILURE; } if (daemonize) { log_info("server started as a daemon, PID %ld", pid); } else { log_info("server started in the foreground, PID %ld", pid); init_signal_started(); } /* Start the event loop. */ event_loop(&server); /* Teardown server and configuration. */ server_deinit(&server); /* Free configuration. */ conf_free(conf(), false); /* Unhook from RCU. */ rcu_unregister_thread(); /* Cleanup PID file. */ pid_cleanup(pidfile); log_info("shutting down"); log_close(); return EXIT_SUCCESS; }