1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
#![doc = include_str!("../README.md")]
pub mod config;
pub mod extension;
pub mod host;
pub mod port;

pub use config::read_and_resolve;

/// CLI argument parser.
#[cfg(feature = "bin")]
pub fn command() -> clap::Command {
    use clap::Arg;
    let c = clap::command!();
    c.long_about(
        "Mölla runns the Kvarn web server using plain-text configs.\n\
        See https://kvarn.org/moella/ for more details and how to write the config.\n\
        \n\
        Logging is controlled using the environment variable `KVARN_LOG`.\n\
        See https://docs.rs/env_logger/latest/env_logger/#example for log settings.",
    )
    .arg(
        Arg::new("config")
            .short('c')
            .long("config")
            .num_args(1)
            .required(true)
            .help("Main config file"),
    )
    .arg(
        Arg::new("high_ports")
            .long("high-ports")
            .help("Bind to higher ports (8080, 8443) to avoid permission issues")
            .action(clap::ArgAction::SetTrue),
    )
    .arg(
        Arg::new("host")
            .short('h')
            .long("host")
            .help("Set the default host to show")
            .num_args(1),
    )
    .arg(
        Arg::new("instance")
            .long("instance-path")
            .long("ctl-socket")
            .short('p')
            .help(
                "The path of the control socket. \
                If you want to start multiple instances of moella, \
                consider this to be an instance name, \
                and make it different for all instances. \
                If you are using kvarnctl, remember to specify this \
                when running the kvarnctl, else it won't find this instance!",
            )
            .default_value("kvarn.sock"),
    )
    .disable_help_flag(true)
    .arg(
        Arg::new("help")
            .long("help")
            .action(clap::ArgAction::Help)
            .help("Print help"),
    )
}

/// Sets up logging, starts the server, and returns the handle.
/// Also handles argument parsing.
///
/// Logging is controlled using the environment variable `KVARN_LOG`.
/// See [this page](https://docs.rs/env_logger/latest/env_logger/#example) for log settings.
#[cfg(feature = "bin")]
pub async fn run(
    custom_extensions: &config::CustomExtensions,
) -> std::sync::Arc<kvarn::shutdown::Manager> {
    let env_log = env_logger::Env::new().filter_or("KVARN_LOG", "rustls=off,info");
    env_logger::Builder::from_env(env_log).init();

    let matches = command().get_matches();

    let mut rc = match config::read_and_resolve(
        matches.get_one::<String>("config").expect("it's required"),
        custom_extensions,
        matches.get_flag("high_ports"),
        matches.get_one::<String>("host").map(String::as_str),
    )
    .await
    {
        Ok(rc) => rc,
        Err(s) => {
            log::error!("{s}");
            std::process::exit(1);
        }
    };

    let ctl_path = socket_path().join(
        matches
            .get_one::<String>("instance")
            .expect("we provided a default"),
    );
    rc = rc.set_ctl_path(ctl_path);

    rc.execute().await
}

#[cfg(feature = "bin")]
#[allow(unused_assignments)]
#[cfg_attr(windows, allow(unused_mut))]
pub(crate) fn socket_path() -> std::path::PathBuf {
    use std::path::Path;
    let mut p = Path::new("/run").to_path_buf();
    #[cfg(all(unix, target_os = "macos"))]
    {
        p = std::env::var_os("HOME")
            .map_or_else(|| Path::new("/Library/Caches").to_path_buf(), Into::into);
    }
    #[cfg(all(unix, not(target_os = "macos")))]
    {
        let user: u32 = unsafe { libc::getuid() };
        if user != 0 {
            p.push("user");
            p.push(user.to_string());
        }
    }
    p
}