Exemple #1
0
struct json_object *na_get_conf (const char *conf_file_json)
{
    struct json_object *conf_obj;

    char json_buf[NA_JSON_BUF_MAX + 1];
    int json_fd, size;

    if ((json_fd = open(conf_file_json, O_RDONLY)) < 0) {
        NA_DIE_WITH_ERROR(NA_ERROR_INVALID_CONFPATH);
    }

    memset(json_buf, 0, NA_JSON_BUF_MAX + 1);
    if ((size = read(json_fd, json_buf, NA_JSON_BUF_MAX)) < 0) {
        NA_DIE_WITH_ERROR(NA_ERROR_INVALID_CONFPATH);
    }

    conf_obj = json_tokener_parse(json_buf);
    if (is_error(conf_obj)) {
        NA_DIE_WITH_ERROR(NA_ERROR_PARSE_JSON_CONFIG);
    }

    close(json_fd);

    return conf_obj;
}
Exemple #2
0
static void na_setup_signals (void)
{
    struct sigaction sig_exit_handler;
    struct sigaction sig_clear_handler;
    struct sigaction sig_reconf_handler;

    sigemptyset(&sig_exit_handler.sa_mask);
    sigemptyset(&sig_clear_handler.sa_mask);
    sigemptyset(&sig_reconf_handler.sa_mask);
    sig_exit_handler.sa_handler   = na_signal_exit_handler;
    sig_clear_handler.sa_handler  = na_signal_clear_handler;
    sig_reconf_handler.sa_handler = na_signal_reconf_handler;
    sig_exit_handler.sa_flags     = 0;
    sig_clear_handler.sa_flags    = 0;
    sig_reconf_handler.sa_flags   = 0;

    if (sigaction(SIGTERM, &sig_exit_handler,   NULL) == -1 ||
        sigaction(SIGINT,  &sig_exit_handler,   NULL) == -1 ||
        sigaction(SIGALRM, &sig_exit_handler,   NULL) == -1 ||
        sigaction(SIGHUP,  &sig_exit_handler,   NULL) == -1 ||
        sigaction(SIGUSR1, &sig_clear_handler,  NULL) == -1 ||
        sigaction(SIGUSR2, &sig_reconf_handler, NULL) == -1)
    {
        NA_DIE_WITH_ERROR(NA_ERROR_FAILED_SETUP_SIGNAL);
    }

    if (sigignore(SIGPIPE) == -1) {
        NA_DIE_WITH_ERROR(NA_ERROR_FAILED_IGNORE_SIGNAL);
    }

    SigExit   = 0;
    SigClear  = 0;
    SigReconf = 0;

}
Exemple #3
0
void na_connpool_init (na_env_t *env)
{
    for (int i=0;i<env->connpool_max;++i) {
        env->connpool_active.fd_pool[i] = na_target_server_tcpsock_init();
        na_target_server_tcpsock_setup(env->connpool_active.fd_pool[i], true);
        if (env->connpool_active.fd_pool[i] <= 0) {
            NA_DIE_WITH_ERROR(env, NA_ERROR_INVALID_FD);
        }
    }
}
Exemple #4
0
void na_connpool_assign_internal (na_env_t *env, na_connpool_t *connpool, int i, int *cur, int *fd, na_server_t *server)
{
    if (connpool->active[i] == 0) {
        connpool->active[i] = 1;
        if (!na_server_connect(connpool->fd_pool[i], &server->addr)) {
            if (errno != EINPROGRESS && errno != EALREADY) {
                NA_DIE_WITH_ERROR(env, NA_ERROR_CONNECTION_FAILED);
            }
        }
    }
    connpool->mark[i] = 1;
    *fd  = connpool->fd_pool[i];
    *cur = i;
}
Exemple #5
0
void na_connpool_switch (na_env_t *env)
{
    na_connpool_t *connpool;
    if (env->is_refused_active) {
        na_connpool_deactivate(&env->connpool_active);
        connpool = &env->connpool_backup;
    } else {
        na_connpool_deactivate(&env->connpool_backup);
        connpool = &env->connpool_active;
    }

    for (int i=0;i<env->connpool_max;++i) {
        connpool->fd_pool[i] = na_target_server_tcpsock_init();
        na_target_server_tcpsock_setup(connpool->fd_pool[i], true);
        if (connpool->fd_pool[i] <= 0) {
            NA_DIE_WITH_ERROR(env, NA_ERROR_INVALID_FD);
        }
    }
}
Exemple #6
0
void na_conf_env_init(struct json_object *environments_obj, na_env_t *na_env,
                      int idx, bool reconf)
{
    char *e;
    char host_buf[NA_HOSTNAME_MAX + 1];
    na_host_t host;
    struct json_object *environment_obj;
    struct json_object *param_obj;

    environment_obj = json_object_array_get_idx(environments_obj, idx);

    for (int i=0;i<NA_PARAM_MAX;++i) {

        param_obj = json_object_object_get(environment_obj, na_param_name(i));

        if (param_obj == NULL) {
            continue;
        }

        // runtime-reconfigurable parameters
        switch (i) {
        case NA_PARAM_IS_CONNPOOL_ONLY:
            NA_PARAM_TYPE_CHECK(param_obj, json_type_boolean);
            na_env->is_connpool_only = json_object_get_boolean(param_obj) == 1 ? true : false;
            continue;
        case NA_PARAM_REQUEST_BUFSIZE:
            NA_PARAM_TYPE_CHECK(param_obj, json_type_int);
            na_env->request_bufsize = json_object_get_int(param_obj);
            continue;
        case NA_PARAM_REQUEST_BUFSIZE_MAX:
            NA_PARAM_TYPE_CHECK(param_obj, json_type_int);
            na_env->request_bufsize_max = json_object_get_int(param_obj);
            continue;
        case NA_PARAM_RESPONSE_BUFSIZE:
            NA_PARAM_TYPE_CHECK(param_obj, json_type_int);
            na_env->response_bufsize = json_object_get_int(param_obj);
            continue;
        case NA_PARAM_RESPONSE_BUFSIZE_MAX:
            NA_PARAM_TYPE_CHECK(param_obj, json_type_int);
            na_env->response_bufsize_max = json_object_get_int(param_obj);
            continue;
        case NA_PARAM_CONN_MAX:
            NA_PARAM_TYPE_CHECK(param_obj, json_type_int);
            na_env->conn_max = json_object_get_int(param_obj);
            continue;
        case NA_PARAM_ERROR_COUNT_MAX:
            NA_PARAM_TYPE_CHECK(param_obj, json_type_int);
            na_env->error_count_max = json_object_get_int(param_obj);
            continue;
        default:
            break;
        }

        // if we didn't find a reconfigurable parameter, try the others
        if (!reconf) {
            switch (i) {
            case NA_PARAM_NAME:
                NA_PARAM_TYPE_CHECK(param_obj, json_type_string);
                strncpy(na_env->name, json_object_get_string(param_obj), NA_NAME_MAX);
                break;
            case NA_PARAM_SOCKPATH:
                NA_PARAM_TYPE_CHECK(param_obj, json_type_string);
                strncpy(na_env->fssockpath, json_object_get_string(param_obj), NA_SOCKPATH_MAX);
                break;
            case NA_PARAM_TARGET_SERVER:
                NA_PARAM_TYPE_CHECK(param_obj, json_type_string);
                strncpy(host_buf, json_object_get_string(param_obj), NA_HOSTNAME_MAX);
                host = na_create_host(host_buf);
                memcpy(&na_env->target_server.host, &host, sizeof(host));
                na_set_sockaddr(&host, &na_env->target_server.addr);
                break;
            case NA_PARAM_BACKUP_SERVER:
                NA_PARAM_TYPE_CHECK(param_obj, json_type_string);
                strncpy(host_buf, json_object_get_string(param_obj), NA_HOSTNAME_MAX);
                host = na_create_host(host_buf);
                memcpy(&na_env->backup_server.host, &host, sizeof(host));
                na_set_sockaddr(&host, &na_env->backup_server.addr);
                na_env->is_use_backup = true;
                break;
            case NA_PARAM_PORT:
                NA_PARAM_TYPE_CHECK(param_obj, json_type_int);
                na_env->fsport = json_object_get_int(param_obj);
                break;
            case NA_PARAM_STPORT:
                NA_PARAM_TYPE_CHECK(param_obj, json_type_int);
                na_env->stport = json_object_get_int(param_obj);
                break;
            case NA_PARAM_STSOCKPATH:
                NA_PARAM_TYPE_CHECK(param_obj, json_type_string);
                strncpy(na_env->stsockpath, json_object_get_string(param_obj), NA_SOCKPATH_MAX);
                break;
            case NA_PARAM_ACCESS_MASK:
                NA_PARAM_TYPE_CHECK(param_obj, json_type_string);
                na_env->access_mask = (mode_t)strtol(json_object_get_string(param_obj), &e, 8);
                break;
            case NA_PARAM_WORKER_MAX:
                NA_PARAM_TYPE_CHECK(param_obj, json_type_int);
                na_env->worker_max = json_object_get_int(param_obj);
                break;
            case NA_PARAM_CONNPOOL_MAX:
                NA_PARAM_TYPE_CHECK(param_obj, json_type_int);
                na_env->connpool_max = json_object_get_int(param_obj);
                break;
            case NA_PARAM_CONNPOOL_USE_MAX:
                NA_PARAM_TYPE_CHECK(param_obj, json_type_int);
                na_env->connpool_use_max = json_object_get_int(param_obj);
                break;
            case NA_PARAM_CLIENT_POOL_MAX:
                NA_PARAM_TYPE_CHECK(param_obj, json_type_int);
                na_env->client_pool_max = json_object_get_int(param_obj);
                break;
            case NA_PARAM_LOOP_MAX:
                NA_PARAM_TYPE_CHECK(param_obj, json_type_int);
                na_env->loop_max = json_object_get_int(param_obj);
                break;
            case NA_PARAM_EVENT_MODEL:
                NA_PARAM_TYPE_CHECK(param_obj, json_type_string);
                na_env->event_model = na_detect_event_model(json_object_get_string(param_obj));
                if (na_env->event_model == NA_EVENT_MODEL_UNKNOWN) {
                    NA_DIE_WITH_ERROR(NA_ERROR_INVALID_JSON_CONFIG);
                }
                break;
            default:
                // no through
                assert(false);
                break;
            }
        }
    }

    na_env->is_extensible_request_buf  = na_env->request_bufsize  < na_env->request_bufsize_max  ? true : false;
    na_env->is_extensible_response_buf = na_env->response_bufsize < na_env->response_bufsize_max ? true : false;

}
Exemple #7
0
int main (int argc, char *argv[])
{
    pthread_t           th[NA_ENV_MAX];
    na_env_t           *env[NA_ENV_MAX];
    mpool_t            *env_pool;
    int                 c;
    int                 env_cnt          = 0;
    bool                is_daemon        = false;
    struct json_object *conf_obj         = NULL;
    struct json_object *environments_obj = NULL;

    while (-1 != (c = getopt(argc, argv,
           "f:" /* configuration file with JSON */
           "t:" /* check configuration file */
           "d"  /* go to background */
           "v"  /* show version and information */
           "h"  /* show help */
    )))
    {
        switch (c) {
        case 'd':
            is_daemon = true;
            break;
        case 'f':
            ConfFile         = optarg;
            conf_obj         = na_get_conf(optarg);
            environments_obj = na_get_environments(conf_obj, &env_cnt);
            break;
        case 't':
            conf_obj         = na_get_conf(optarg);
            environments_obj = na_get_environments(conf_obj, &env_cnt);
            printf("JSON configuration is OK\n");
            return 0;
            break;
        case 'v':
            na_version();
            return 0;
            break;
        case 'h':
            na_usage();
            return 0;
            break;
        default:
            break;
        }
    }

    if (is_daemon && daemon(0, 0) == -1) {
        NA_DIE_WITH_ERROR(NA_ERROR_FAILED_DAEMONIZE);
    }

    if (env_cnt > NA_ENV_MAX) {
        NA_DIE_WITH_ERROR(NA_ERROR_TOO_MANY_ENVIRONMENTS);
    }

    StartTimestamp = time(NULL);
    na_setup_signals();
    na_memproto_bm_skip_init();

    env_pool = mpool_create(0);
    if (env_cnt == 0) {
        env_cnt = 1;
        env[0]  = na_env_add(&env_pool);
        na_env_setup_default(env[0], 0);
    } else {
        for (int i=0;i<env_cnt;++i) {
            env[i] = na_env_add(&env_pool);
            na_env_setup_default(env[i], i);
            na_conf_env_init(environments_obj, env[i], i, false);
        }
    }

    json_object_put(conf_obj);

    for (int i=0;i<env_cnt;++i) {
        env[i]->current_conn      = 0;
        env[i]->is_refused_active = false;
        env[i]->is_refused_accept = false;
        env[i]->is_worker_busy    = calloc(sizeof(bool), env[i]->worker_max);
        for (int j=0;j<env[i]->worker_max;++j) {
            env[i]->is_worker_busy[j] = false;
        }
        env[i]->error_count      = 0;
        env[i]->current_conn_max = 0;
        pthread_mutex_init(&env[i]->lock_connpool, NULL);
        pthread_mutex_init(&env[i]->lock_current_conn, NULL);
        pthread_mutex_init(&env[i]->lock_tid, NULL);
        pthread_mutex_init(&env[i]->lock_loop, NULL);
        pthread_mutex_init(&env[i]->lock_error_count, NULL);
        pthread_rwlock_init(&env[i]->lock_refused, NULL);
        pthread_rwlock_init(&env[i]->lock_request_bufsize_max, NULL);
        pthread_rwlock_init(&env[i]->lock_response_bufsize_max, NULL);
        pthread_rwlock_init(&LockReconf, NULL);
        env[i]->lock_worker_busy = calloc(sizeof(pthread_rwlock_t), env[i]->worker_max);
        for (int j=0;j<env[i]->worker_max;++j) {
            pthread_rwlock_init(&env[i]->lock_worker_busy[j], NULL);
        }
        na_connpool_create(&env[i]->connpool_active, env[i]->connpool_max);
        if (env[i]->is_use_backup) {
            na_connpool_create(&env[i]->connpool_backup, env[i]->connpool_max);
        }
    }

    for (int i=0;i<env_cnt;++i) {
        pthread_create(&th[i], NULL, na_event_loop, env[i]);
    }

    // monitoring signal
    while (true) {

        if (SigExit == 1) {
            break;
        }

        if (SigReconf == 1) {
            conf_obj         = na_get_conf(ConfFile);
            environments_obj = na_get_environments(conf_obj, &env_cnt);

            pthread_rwlock_wrlock(&LockReconf);
            for (int i=0;i<env_cnt;++i) {
                na_conf_env_init(environments_obj, env[i], i, true);
            }
            pthread_rwlock_unlock(&LockReconf);

            json_object_put(conf_obj);
            SigReconf = 0;
        }

        // XXX we should sleep forever and only wake upon a signal
        sleep(1);
    }

    for (int i=0;i<env_cnt;++i) {
        na_connpool_destroy(&env[i]->connpool_active);
        if (env[i]->is_use_backup) {
            na_connpool_destroy(&env[i]->connpool_backup);
        }
        pthread_mutex_destroy(&env[i]->lock_connpool);
        pthread_mutex_destroy(&env[i]->lock_current_conn);
        pthread_mutex_destroy(&env[i]->lock_tid);
        pthread_mutex_destroy(&env[i]->lock_error_count);
        pthread_rwlock_destroy(&env[i]->lock_refused);
        pthread_rwlock_destroy(&env[i]->lock_request_bufsize_max);
        pthread_rwlock_destroy(&env[i]->lock_response_bufsize_max);
        pthread_rwlock_destroy(&LockReconf);
        for (int j=0;j<env[i]->worker_max;++j) {
            pthread_rwlock_destroy(&env[i]->lock_worker_busy[j]);
        }
        NA_FREE(env[i]->is_worker_busy);
        NA_FREE(env[i]->lock_worker_busy);
        pthread_detach(th[i]);
    }

    mpool_destroy(env_pool);

    return 0;
}
Exemple #8
0
void na_conf_env_init(struct json_object *environments_obj, na_env_t *na_env, int idx)
{
    char *e;
    char host_buf[NA_HOSTNAME_MAX + 1];
    na_host_t host;
    struct json_object *environment_obj;
    struct json_object *param_obj;
    bool have_log_path_opt;
    bool have_slow_query_log_path_opt;

    have_log_path_opt            = false;
    have_slow_query_log_path_opt = false;

    environment_obj = json_object_array_get_idx(environments_obj, idx);

    for (int i=0;i<NA_PARAM_MAX;++i) {
        double slow_query_sec;

        param_obj = json_object_object_get(environment_obj, na_param_name(i));

        if (param_obj == NULL) {
            continue;
        }

        // runtime-reconfigurable parameters
        switch (i) {
        case NA_PARAM_REQUEST_BUFSIZE:
            NA_PARAM_TYPE_CHECK(param_obj, json_type_int);
            na_env->request_bufsize = json_object_get_int(param_obj);
            break;
        case NA_PARAM_RESPONSE_BUFSIZE:
            NA_PARAM_TYPE_CHECK(param_obj, json_type_int);
            na_env->response_bufsize = json_object_get_int(param_obj);
            break;
        case NA_PARAM_CONN_MAX:
            NA_PARAM_TYPE_CHECK(param_obj, json_type_int);
            na_env->conn_max = json_object_get_int(param_obj);
            break;
        case NA_PARAM_LOG_ACCESS_MASK:
            NA_PARAM_TYPE_CHECK(param_obj, json_type_string);
            na_env->log_access_mask = (mode_t)strtol(json_object_get_string(param_obj), &e, 8);
            break;
        case NA_PARAM_SLOW_QUERY_SEC:
            NA_PARAM_TYPE_CHECK(param_obj, json_type_double);
            slow_query_sec = json_object_get_double(param_obj);
            na_env->slow_query_sec.tv_sec = slow_query_sec;
            na_env->slow_query_sec.tv_nsec = 1000000000L * (slow_query_sec - (long)slow_query_sec);
            break;
        case NA_PARAM_SLOW_QUERY_LOG_PATH:
            NA_PARAM_TYPE_CHECK(param_obj, json_type_string);
            strncpy(na_env->slow_query_log_path, json_object_get_string(param_obj), NA_PATH_MAX);
            have_slow_query_log_path_opt = true;
            break;
        case NA_PARAM_LOGPATH:
            NA_PARAM_TYPE_CHECK(param_obj, json_type_string);
            strncpy(na_env->logpath, json_object_get_string(param_obj), NA_PATH_MAX);
            have_log_path_opt = true;
            break;
        case NA_PARAM_SLOW_QUERY_LOG_ACCESS_MASK:
            NA_PARAM_TYPE_CHECK(param_obj, json_type_string);
            na_env->slow_query_log_access_mask = (mode_t)strtol(json_object_get_string(param_obj), &e, 8);
            break;
        case NA_PARAM_NAME:
            NA_PARAM_TYPE_CHECK(param_obj, json_type_string);
            strncpy(na_env->name, json_object_get_string(param_obj), NA_NAME_MAX);
            break;
        case NA_PARAM_SOCKPATH:
            NA_PARAM_TYPE_CHECK(param_obj, json_type_string);
            strncpy(na_env->fssockpath, json_object_get_string(param_obj), NA_PATH_MAX);
            break;
        case NA_PARAM_TARGET_SERVER:
            NA_PARAM_TYPE_CHECK(param_obj, json_type_string);
            strncpy(host_buf, json_object_get_string(param_obj), NA_HOSTNAME_MAX);
            host = na_create_host(host_buf);
            memcpy(&na_env->target_server.host, &host, sizeof(host));
            na_set_sockaddr(&host, &na_env->target_server.addr);
            break;
        case NA_PARAM_BACKUP_SERVER:
            NA_PARAM_TYPE_CHECK(param_obj, json_type_string);
            strncpy(host_buf, json_object_get_string(param_obj), NA_HOSTNAME_MAX);
            host = na_create_host(host_buf);
            memcpy(&na_env->backup_server.host, &host, sizeof(host));
            na_set_sockaddr(&host, &na_env->backup_server.addr);
            na_env->is_use_backup = true;
            break;
        case NA_PARAM_PORT:
            NA_PARAM_TYPE_CHECK(param_obj, json_type_int);
            na_env->fsport = json_object_get_int(param_obj);
            break;
        case NA_PARAM_STPORT:
            NA_PARAM_TYPE_CHECK(param_obj, json_type_int);
            na_env->stport = json_object_get_int(param_obj);
            break;
        case NA_PARAM_STSOCKPATH:
            NA_PARAM_TYPE_CHECK(param_obj, json_type_string);
            strncpy(na_env->stsockpath, json_object_get_string(param_obj), NA_PATH_MAX);
            break;
        case NA_PARAM_ACCESS_MASK:
            NA_PARAM_TYPE_CHECK(param_obj, json_type_string);
            na_env->access_mask = (mode_t)strtol(json_object_get_string(param_obj), &e, 8);
            break;
        case NA_PARAM_WORKER_MAX:
            NA_PARAM_TYPE_CHECK(param_obj, json_type_int);
            na_env->worker_max = json_object_get_int(param_obj);
            break;
        case NA_PARAM_CONNPOOL_MAX:
            NA_PARAM_TYPE_CHECK(param_obj, json_type_int);
            na_env->connpool_max = json_object_get_int(param_obj);
            break;
        case NA_PARAM_CLIENT_POOL_MAX:
            NA_PARAM_TYPE_CHECK(param_obj, json_type_int);
            na_env->client_pool_max = json_object_get_int(param_obj);
            break;
        case NA_PARAM_LOOP_MAX:
            NA_PARAM_TYPE_CHECK(param_obj, json_type_int);
            na_env->loop_max = json_object_get_int(param_obj);
            break;
        case NA_PARAM_EVENT_MODEL:
            NA_PARAM_TYPE_CHECK(param_obj, json_type_string);
            na_env->event_model = na_detect_event_model(json_object_get_string(param_obj));
            if (na_env->event_model == NA_EVENT_MODEL_UNKNOWN) {
                NA_DIE_WITH_ERROR(na_env, NA_ERROR_INVALID_JSON_CONFIG);
            }
            break;
        case NA_PARAM_SLOW_QUERY_LOG_FORMAT:
            NA_PARAM_TYPE_CHECK(param_obj, json_type_string);
            na_env->slow_query_log_format = na_detect_log_format(json_object_get_string(param_obj));
            if (na_env->slow_query_log_format == NA_LOG_FORMAT_UNKNOWN) {
                NA_DIE_WITH_ERROR(na_env, NA_ERROR_INVALID_JSON_CONFIG);
            }
            break;
        case NA_PARAM_TRY_MAX:
            NA_PARAM_TYPE_CHECK(param_obj, json_type_int);
            na_env->try_max = json_object_get_int(param_obj);
            break;
        default:
            // no through
            assert(false);
            break;
        }
    }

    // open log, if enabled
    if (have_log_path_opt) {
        na_log_open(na_env);
    }

    // open slow query log, if enabled
    if (((na_env->slow_query_sec.tv_sec  != 0)  ||
         (na_env->slow_query_sec.tv_nsec != 0))) {
        if (!have_slow_query_log_path_opt) {
            NA_DIE_WITH_ERROR(na_env, NA_ERROR_INVALID_JSON_CONFIG);
        } else {
            na_slow_query_open(na_env);
        }
    }
}
Exemple #9
0
int main (int argc, char *argv[])
{
    int                 c;
    na_env_t            env;              // for child
    na_env_t            envs[NA_ENV_MAX]; // for master
    na_ctl_env_t        env_ctl;
    pthread_t           event_th, ctl_th;
    int                 env_cnt          = 0;
    bool                is_daemon        = false;
    struct json_object *conf_obj         = NULL;
    struct json_object *environments_obj = NULL;
    struct json_object *ctl_obj          = NULL;
    pid_t pids[NA_PID_MAX];
    int pidx;
    fnv_ent_t ent_env[NA_PID_MAX];
    fnv_tbl_t *tbl_env;
    char conf_file[NA_PATH_MAX + 1];
    sigset_t ss;
    int sig;

    GracefulPhase = NA_GRACEFUL_PHASE_DISABLED;

    while (-1 != (c = getopt(argc, argv,
                             "f:" /* configuration file with JSON */
                             "t:" /* check configuration file */
                             "d"  /* go to background */
                             "v"  /* show version and information */
                             "h"  /* show help */
                             )))
    {
        switch (c) {
        case 'd':
            is_daemon = true;
            break;
        case 'f':
            strncpy(conf_file, optarg, NA_PATH_MAX + 1);
            conf_obj         = na_get_conf(optarg);
            environments_obj = na_get_environments(conf_obj, &env_cnt);
            break;
        case 't':
            conf_obj         = na_get_conf(optarg);
            environments_obj = na_get_environments(conf_obj, &env_cnt);
            printf("JSON configuration is OK\n");
            return 0;
            break;
        case 'v':
            na_version();
            return 0;
            break;
        case 'h':
            na_usage();
            return 0;
            break;
        default:
            break;
        }
    }

    if (is_daemon && daemon(0, 0) == -1) {
        NA_DIE_WITH_ERROR(NULL, NA_ERROR_FAILED_DAEMONIZE);
    }

    if (env_cnt > NA_ENV_MAX) {
        NA_DIE_WITH_ERROR(NULL, NA_ERROR_TOO_MANY_ENVIRONMENTS);
    }

    StartTimestamp = time(NULL);
    na_setup_ignore_signals();

    tbl_env   = fnv_tbl_create(ent_env, NA_PID_MAX);
    MasterPid = getpid();
    pidx      = 0;
    for (int i=0;i<env_cnt;++i) {
        pidx++;
        pid_t pid = fork();
        if (pid == -1) {
            NA_DIE_WITH_ERROR(NULL, NA_ERROR_FAILED_CREATE_PROCESS);
        } else if (pid == 0) { // child
            break;
        } else {
            pids[i] = pid;
        }
    }

    if (na_is_master_process()) {
        if (conf_obj == NULL) {
            NA_DIE_WITH_ERROR(NULL, NA_ERROR_INVALID_CONFPATH);
        }
        na_set_env_proc_name(argv[0], "master");
        for (int i=0;i<env_cnt;++i) {
            na_env_setup_default(&envs[i], i);
            na_conf_env_init(environments_obj, &envs[i], i);
            fnv_put(tbl_env, envs[i].name, &pids[i], strlen(envs[i].name), sizeof(pid_t));
        }
        memset(&env_ctl, 0, sizeof(na_ctl_env_t));
        na_ctl_env_setup_default(&env_ctl);
        ctl_obj = na_get_ctl(conf_obj);
        na_conf_ctl_init(ctl_obj, &env_ctl);
        env_ctl.tbl_env = tbl_env;
        pthread_mutex_init(&env_ctl.lock_restart, NULL);
        pthread_create(&ctl_th, NULL, na_ctl_loop, &env_ctl);
        goto MASTER_CYCLE;
    }

    na_memproto_bm_skip_init();

    memset(&env, 0, sizeof(env));
    if (env_cnt == 0) {
        na_env_setup_default(&env, 0);
    } else {
        na_env_setup_default(&env, pidx);
        na_conf_env_init(environments_obj, &env, pidx - 1);
    }

    na_set_env_proc_name(argv[0], env.name);
    na_env_init(&env);
    pthread_create(&event_th, NULL, na_event_loop, &env);

 MASTER_CYCLE:

    json_object_put(conf_obj);

    if (na_is_master_process()) {
        na_setup_signals_for_master(&ss);
    } else {
        na_setup_signals_for_worker(&ss);
    }

    // monitoring signal
    while (true) {

        if (!na_is_master_process()) {
            if (sigwait(&ss, &sig) != 0) {
                continue;
            }
            switch (sig) {
            case SIGTERM:
            case SIGINT:
            case SIGHUP:
                goto exit;
            case SIGUSR2:
                if (GracefulPhase == NA_GRACEFUL_PHASE_DISABLED) {
                    sleep(5);
                    GracefulPhase = NA_GRACEFUL_PHASE_ENABLED;
                }
                // wait until available connection becomes zero
                while (true) {
                    pthread_mutex_lock(&env.lock_current_conn);
                    if (env.current_conn == 0) {
                        pthread_mutex_unlock(&env.lock_current_conn);
                        goto exit;
                    }
                    pthread_mutex_unlock(&env.lock_current_conn);
                    sleep(1);
                }
                break;
            default:
                break;
            }
            continue;
        }

        // for master process
        if (sigwait(&ss, &sig) != 0) {
            continue;
        }

        switch (sig) {
        case SIGTERM:
        case SIGINT:
        case SIGHUP:
            na_process_shutdown(pids, env_cnt);
            goto exit;
        case SIGCONT:
            if (strlen(env_ctl.restart_envname) > 0) {
                pid_t pid = fork();
                void *th_ret;
                int ridx;
                conf_obj         = na_get_conf(conf_file);
                environments_obj = na_get_environments(conf_obj, &env_cnt);
                ridx             = na_conf_get_environment_idx(environments_obj, env_ctl.restart_envname);
                if (pid == -1) {
                    pthread_mutex_unlock(&env_ctl.lock_restart);
                    NA_CTL_DIE_WITH_ERROR(&env_ctl, NA_ERROR_FAILED_CREATE_PROCESS);
                } else if (pid == 0) { // child
                    pthread_cancel(ctl_th);
                    pthread_join(ctl_th, &th_ret);

                    na_setup_signals_for_worker(&ss);
                    
                    na_memproto_bm_skip_init();
                    
                    memset(&env, 0, sizeof(env));
                    na_env_setup_default(&env, ridx);
                    na_conf_env_init(environments_obj, &env, ridx);
                    argv[0][strlen(argv[0]) - (sizeof(": master") - 1)] = '\0';
                    na_set_env_proc_name(argv[0], env.name);
                    na_env_init(&env);
                    pthread_create(&event_th, NULL, na_event_loop, &env);
                    json_object_put(conf_obj);
                } else { // master
                    int status;
                    waitpid(pids[ridx], &status, 0);
                    pids[ridx] = pid;
                }
                env_ctl.restart_envname[0] = '\0';
            }
            break;
        case SIGWINCH:
            if (env_cnt >= NA_ENV_MAX) {
                continue;
            }
            pid_t pid = fork();
            int env_cnt_prev;
            int status;
            env_cnt_prev = env_cnt;
            conf_obj         = na_get_conf(conf_file);
            environments_obj = na_get_environments(conf_obj, &env_cnt);
            if (env_cnt_prev != env_cnt - 1) {
                if (pid == 0) {
                    NA_CTL_DIE_WITH_ERROR(&env_ctl, NA_ERROR_UNKNOWN);
                } else {
                    waitpid(pid, &status, 0);
                }
            } else {
                if (pid == -1) {
                    NA_CTL_DIE_WITH_ERROR(&env_ctl, NA_ERROR_FAILED_CREATE_PROCESS);
                } else if (pid == 0) { // child
                    na_setup_signals_for_worker(&ss);
                    na_memproto_bm_skip_init();
                    memset(&env, 0, sizeof(env));
                    na_env_setup_default(&env, env_cnt - 1);
                    na_conf_env_init(environments_obj, &env, env_cnt - 1);
                    argv[0][strlen(argv[0]) - (sizeof(": master") - 1)] = '\0';
                    na_set_env_proc_name(argv[0], env.name);
                    na_env_init(&env);
                    pthread_create(&event_th, NULL, na_event_loop, &env);
                    json_object_put(conf_obj);
                } else { // master
                    char *envname = na_conf_get_environment_name(environments_obj, env_cnt - 1);
                    pids[env_cnt - 1] = pid;
                    fnv_put(tbl_env, envname, &pids[env_cnt - 1], strlen(envname), sizeof(pid_t));
                }
            }
            break;
        case SIGUSR1:
            na_on_the_fly_update(&env_ctl, conf_file, pids, env_cnt);
            goto exit;
        case SIGCHLD:
            {
                int status;
                pid_t pid = wait(&status);
                int idx = -1;
                if (status == 0) {
                    continue;
                }
                for (int i=0;i<env_cnt;i++) {
                    if (pid == pids[i]) {
                        idx = i;
                        break;
                    }
                }
                if (idx == -1) {
                    NA_CTL_DIE_WITH_ERROR(&env_ctl, NA_ERROR_UNKNOWN);
                }
                pid = fork();
                conf_obj         = na_get_conf(conf_file);
                environments_obj = na_get_environments(conf_obj, &env_cnt);
                if (pid == -1) {
                    NA_CTL_DIE_WITH_ERROR(&env_ctl, NA_ERROR_FAILED_CREATE_PROCESS);
                } else if (pid == 0) {
                    na_setup_signals_for_worker(&ss);
                    na_memproto_bm_skip_init();
                    memset(&env, 0, sizeof(env));
                    na_env_setup_default(&env, idx);
                    na_conf_env_init(environments_obj, &env, idx);
                    argv[0][strlen(argv[0]) - (sizeof(": master") - 1)] = '\0';
                    na_set_env_proc_name(argv[0], env.name);
                    na_env_init(&env);
                    pthread_create(&event_th, NULL, na_event_loop, &env);
                    json_object_put(conf_obj);
                } else {
                    pids[idx] = pid;
                }
            }
            break;
        default:
            assert(false);
            break;
        }

    }

 exit:

    return 0;
}