int main()

in turbonfs/src/main.cpp [616:942]


int main(int argc, char *argv[])
{
    // Initialize logger first thing.
    init_log();

    AZLogInfo("aznfsclient version {}.{}.{}",
               AZNFSCLIENT_VERSION_MAJOR,
               AZNFSCLIENT_VERSION_MINOR,
               AZNFSCLIENT_VERSION_PATCH);

    struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
    struct fuse_session *se = NULL;
    struct fuse_cmdline_opts opts;
    struct fuse_loop_config *loop_config = fuse_loop_cfg_create();
    int ret = -1;
    int wait_iter;
    std::string mount_source;
    std::string extra_options;

    /* 
     * There can only be 1 reader of this pipe. Hence, we should ensure we 
     * don't send messages multiple times to avoid wait loops.
     */
    bool status_pipe_closed = false;

    // Check if the status mount pipe is set.
    const char *pipe_name = std::getenv("MOUNT_STATUS_PIPE");
    if (!pipe_name) {
        status_pipe_closed = true;
        AZLogWarn("MOUNT_STATUS_PIPE environment variable is not set.");
    }

    /* Don't mask creation mode, kernel already did that */
    umask(0);

    /*
     * Parse general cmdline options first for properly honoring help
     * and debug level arguments.
     */
    if (fuse_parse_cmdline(&args, &opts) != 0) {
        goto err_out0;
    }

    if (opts.mountpoint == nullptr) {
        AZLogError("Mountpoint must be provided!");
        goto err_out0;
    }

    if (opts.show_help) {
        aznfsc_help(argv[0]);
        fuse_cmdline_help();
        fuse_lowlevel_help();
        ret = 0;
        goto err_out1;
    } else if (opts.show_version) {
        printf("FUSE library version %s\n", fuse_pkgversion());
        fuse_lowlevel_version();
        ret = 0;
        goto err_out1;
    }

    /*
     * If -d or "-o debug" cmdline option was passed, reset log level to
     * debug.
     */
    if (opts.debug) {
        enable_debug_logs = true;
        spdlog::set_level(spdlog::level::debug);
    }

    // Parse fuse_conn_info_opts options like -o writeback_cache.
    fuse_conn_info_opts_ptr = fuse_parse_conn_info_opts(&args);

    // Parse aznfsclient specific options.
    if (fuse_opt_parse(&args, &aznfsc_cfg, aznfsc_opts, NULL) == -1) {
        goto err_out1;
    }

    /*
     * TODO: Add validity checks for aznfsc_cfg cmdline options, similar to
     *       parse_config_yaml().
     */

    // Parse config yaml if --config-yaml option provided.
    if (!aznfsc_cfg.parse_config_yaml()) {
        goto err_out1;
    }

    /*
     * If config yaml had debug config set to true, reset log level to debug.
     */
    if (aznfsc_cfg.debug) {
        opts.debug = true;
    }

    if (opts.debug) {
        enable_debug_logs = true;
        spdlog::set_level(spdlog::level::debug);
    }

    /*
     * account and container are mandatory parameters which do not have a
     * default value, so ensure they are set before proceeding further.
     */
    if (aznfsc_cfg.account == nullptr) {
        AZLogError("Account name must be set either from cmdline or config yaml!");
        goto err_out1;
    }

    if (aznfsc_cfg.container == nullptr) {
        AZLogError("Container name must be set either from cmdline or config yaml!");
        goto err_out1;
    }

    aznfsc_cfg.mountpoint = opts.mountpoint;

    // Set default values for config variables not set using the above.
    if (!aznfsc_cfg.set_defaults_and_sanitize()) {
        AZLogError("Error setting one or more default config!");
        goto err_out1;
    }

    /*
     * Honour "-o max_threads=" cmdline option, else use the fuse_max_threads
     * value from the config, if set.
     */
    if (opts.max_threads == 10 /* FUSE_LOOP_MT_DEF_MAX_THREADS */) {
        if (aznfsc_cfg.fuse_max_threads != -1) {
            opts.max_threads = aznfsc_cfg.fuse_max_threads;
        }
    }

    /*
     * Honour "-o max_idle_threads=" cmdline option, else use the
     * fuse_max_idle_threads value from the config, if set.
     */
    if (opts.max_idle_threads == (UINT_MAX) -1 /* FUSE_LOOP_MT_DEF_IDLE_THREADS */) {
        if (aznfsc_cfg.fuse_max_idle_threads != -1) {
            opts.max_idle_threads = aznfsc_cfg.fuse_max_idle_threads;
        }
    }

    /*
     * Hide fuse'ism and behave like a normal POSIX fs.
     * Note that we ask fuse to do the permission checks instead of the NFS
     * server. This way we get 16+ groups handling for free.
     * TODO: Make this configurable?
     *
     * Also set fsname to the correct mount source for clearer mount output.
     * Also PID of the fuse process is useful to associate a mount with the
     * fuse process, which helps in debugging.
     */
    mount_source = aznfsc_cfg.server + ":" + aznfsc_cfg.export_path +
                    "[PID=" + std::to_string(::getpid()) + "][vers=" +
                              std::to_string(AZNFSCLIENT_VERSION_MAJOR) + "." +
                              std::to_string(AZNFSCLIENT_VERSION_MINOR) + "." +
                              std::to_string(AZNFSCLIENT_VERSION_PATCH) + "]";
    extra_options = std::string("-oallow_other,default_permissions,fsname=") + mount_source;

    if (fuse_opt_add_arg(&args, extra_options.c_str()) == -1) {
        goto err_out1;
    }

    se = fuse_session_new(&args, &aznfsc_ll_ops, sizeof(aznfsc_ll_ops),
                          &nfs_client::get_instance());
    if (se == NULL) {
        AZLogError("fuse_session_new failed");
        goto err_out1;
    }

    if (fuse_set_signal_handlers(se) != 0) {
        AZLogError("fuse_set_signal_handlers failed");
        goto err_out2;
    }

#ifdef ENABLE_RELEASE_BUILD
    block_termination_signals();
#endif

    /*
     * Setup SIGUSR1 handler for dumping RPC stats.
     */
    if (set_signal_handler(SIGUSR1, handle_usr1) != 0) {
        AZLogError("set_signal_handler(SIGUSR1) failed: {}", ::strerror(errno));
        goto err_out3;
    }

    if (fuse_session_mount(se, opts.mountpoint) != 0) {
        AZLogError("fuse_session_mount failed");
        goto err_out3;
    }

    if (fuse_daemonize(opts.foreground) != 0) {
        AZLogError("fuse_daemonize failed");
        goto err_out4;
    }

    if (aznfsc_cfg.auth) {
        // Set the auth token callback for this connection if auth is enabled.
        set_auth_token_callback(get_auth_token_and_setargs_cb);
    }

    /*
     * Initialize nfs_client singleton.
     * This creates the libnfs polling thread(s) and hence it MUST be called
     * after fuse_daemonize(), else those threads will get killed.
     */
    if (!nfs_client::get_instance().init()) {
        AZLogError("Failed to init the NFS client");
        goto err_out4;
    }

    client_started = true;
    AZLogInfo("==> Aznfsclient fuse driver ready to serve requests!");
    
    // Open the pipe for writing.
    if (!status_pipe_closed) {
        std::ofstream pipe(pipe_name);

        if (!pipe.is_open()) {
            AZLogError("Aznfsclient unable to send mount status on pipe.");
        } else {
            pipe << 0 << endl;
            status_pipe_closed = true;
        }
    }

    if (opts.singlethread) {
        ret = fuse_session_loop(se);
    } else {
        fuse_loop_cfg_set_clone_fd(loop_config, opts.clone_fd);
        fuse_loop_cfg_set_max_threads(loop_config, opts.max_threads);
        fuse_loop_cfg_set_idle_threads(loop_config, opts.max_idle_threads);

        ret = fuse_session_loop_mt(se, loop_config);
    }

    /*
     * We come here when user unmounts the fuse filesystem.
     */
    AZLogInfo("Shutting down!");

    /*
     * Clear the stats signal, else it may cause a crash if received while
     * we start cleaning up things.
     */
    if (set_signal_handler(SIGUSR1, SIG_DFL) != 0) {
        AZLogWarn("set_signal_handler(SIG_DFL) failed: {}", ::strerror(errno));
        /* Continue and hope we don't get the signal */
    }

    /*
     * After we exit the fuse session loop above, libfuse won't read any more
     * messages from kernel, but we may have some fuse messages that we have
     * received but still not responded. We must wait for those fuse messages
     * to be responded before proceeding with the tear down.
     */
    wait_iter = 0;
    while (rpc_stats_az::fuse_responses_awaited) {
        if (wait_iter++ == 100) {
            AZLogWarn("Giving up on {} pending fuse requests",
                      rpc_stats_az::fuse_responses_awaited.load());
            break;
        }

        AZLogWarn("Waiting for {} pending fuse requests to complete",
                  rpc_stats_az::fuse_responses_awaited.load());

        /*
         * 100ms wait should be large enough to let those requests complete
         * and small enough to not make unmount wait unnecessarily long.
         */
        ::usleep(100 * 1000);
    }

err_out4:
    fuse_loop_cfg_destroy(loop_config);
    /*
     * Note: fuse_session_unmount() calls kernel umount which causes a statfs()
     *       call. This causes statfs_callback() to be called in libnfs thread
     *       context. TSAN shows a data race with this thread which is winding
     *       down fuse data structures.
     */
    fuse_session_unmount(se);
err_out3:
    fuse_remove_signal_handlers(se);
err_out2:
    fuse_session_destroy(se);
err_out1:
    free(opts.mountpoint);
    fuse_opt_free_args(&args);
err_out0:
    if (!status_pipe_closed && ret != 0) {
        // Open the pipe for writing.
        std::ofstream pipe(pipe_name);

        if (!pipe.is_open()) {
            AZLogError("Aznfsclient unable to send mount status on pipe.");
        } else {
            // If is_azlogin_required is true, share the error code = -2 over the pipe.
            if (is_azlogin_required) {
                ret = -2;
                AZLogError("Not logged in using 'az login' when auth is enabled");
                pipe << ret << endl;
            } else if (!status_pipe_error_string.empty()) {
                ret = -3;
                AZLogError("Returing error string '-3 {}' on the pipe", status_pipe_error_string);
                pipe << "-3 " << status_pipe_error_string << endl;
            } else {
                // TODO: Extend this with meaningful error codes.
                pipe << ret << endl;
            }
            status_pipe_closed = true;
        }
        return 1;
    }

    /*
     * Shutdown the client after fuse cleanup is performed so that we don't
     * get any more requests from fuse.
     */
    if (client_started) {
        nfs_client::get_instance().shutdown();
    }

    return ret ? 1 : 0;
}