diff --git a/.gitignore b/.gitignore index 62d02f8b..22480c5c 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,5 @@ tags .idea .vscode +# Windows logs +*.log diff --git a/Cargo.lock b/Cargo.lock index f617be11..6cd59a65 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -796,6 +796,27 @@ dependencies = [ "subtle", ] +[[package]] +name = "directories" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys", +] + [[package]] name = "enum_dispatch" version = "0.3.12" @@ -2141,6 +2162,12 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + [[package]] name = "ordered-stream" version = "0.2.0" @@ -2452,6 +2479,17 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "redox_users" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +dependencies = [ + "getrandom", + "redox_syscall 0.2.16", + "thiserror", +] + [[package]] name = "regex" version = "1.9.4" @@ -2922,6 +2960,7 @@ dependencies = [ "dbus", "dbus-crossroads", "dbus-tokio", + "directories", "env_logger", "fern", "futures", @@ -2945,7 +2984,6 @@ dependencies = [ "toml", "url", "whoami", - "xdg", ] [[package]] @@ -3700,12 +3738,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "xdg" -version = "2.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213b7324336b53d2414b2db8537e56544d981803139155afa84f76eeebb7a546" - [[package]] name = "xdg-home" version = "1.0.0" diff --git a/Cargo.toml b/Cargo.toml index e377d089..184d9677 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,6 @@ rust-version = "1.67" [dependencies] alsa = { version = "0.7", optional = true } chrono = "0.4" -daemonize = "0.5" dbus = { version = "0.9", optional = true } dbus-tokio = { version = "0.7.3", optional = true } dbus-crossroads = { version = "0.5.0", optional = true } @@ -26,11 +25,9 @@ rspotify = { version = "0.12.0", features = ["client-ureq", "ureq-rustls-tls"], serde = { version = "1.0.115", features = ["derive"] } sha-1 = "0.10" structopt = "0.3.17" -syslog = "6" tokio = {version = "1.26.0", features = ["signal", "rt-multi-thread", "process", "io-std"] } tokio-stream = "0.1.7" url = "2.2.2" -xdg = "2.2" librespot-audio = { version = "0.4", default-features = false } librespot-playback = { version = "0.4", default-features = false } librespot-core = { version = "0.4" } @@ -38,6 +35,11 @@ librespot-discovery = { version = "0.4" } librespot-connect = { version = "0.4" } toml = "0.7" color-eyre = "0.6" +directories = "5.0.1" + +[target."cfg(unix)".dependencies] +daemonize = "0.5" +syslog = "6" [target."cfg(target_os = \"macos\")".dependencies] whoami = "1" @@ -56,11 +58,7 @@ rodio_backend = ["librespot-playback/rodio-backend"] [package.metadata.deb] depends = "$auto, systemd, pulseaudio" -features = [ - "pulseaudio_backend", - "dbus_keyring", - "dbus_mpris", -] +features = ["pulseaudio_backend", "dbus_keyring", "dbus_mpris"] assets = [ ["target/release/spotifyd", "usr/bin/", "755"], ["README.md", "usr/share/doc/spotifyd/README", "644"], diff --git a/src/config.rs b/src/config.rs index d81fb167..d5748d9a 100644 --- a/src/config.rs +++ b/src/config.rs @@ -14,7 +14,7 @@ use librespot_playback::config::{ use log::{error, info, warn}; use serde::{de::Error, de::Unexpected, Deserialize, Deserializer}; use sha1::{Digest, Sha1}; -use std::{fmt, fs, path::PathBuf, str::FromStr, string::ToString}; +use std::{fmt, fs, path::Path, path::PathBuf, str::FromStr, string::ToString}; use structopt::{clap::AppSettings, StructOpt}; use url::Url; @@ -655,16 +655,19 @@ impl SharedConfigValues { pub(crate) fn get_config_file() -> Option { let etc_conf = format!("/etc/{}", CONFIG_FILE_NAME); - let xdg_dirs = xdg::BaseDirectories::with_prefix("spotifyd").ok()?; - xdg_dirs.find_config_file(CONFIG_FILE_NAME).or_else(|| { - fs::metadata(&*etc_conf).ok().and_then(|meta| { - if meta.is_file() { - Some(etc_conf.into()) - } else { - None - } - }) - }) + let dirs = directories::BaseDirs::new()?; + let mut path = dirs.config_dir().to_path_buf(); + path.push("spotifyd"); + path.push(CONFIG_FILE_NAME); + + if path.exists() { + Some(path) + } else if Path::new(&etc_conf).exists() { + let path: PathBuf = etc_conf.into(); + Some(path) + } else { + None + } } fn device_id(name: &str) -> String { @@ -693,6 +696,7 @@ pub(crate) struct SpotifydConfig { pub(crate) player_config: PlayerConfig, pub(crate) session_config: SessionConfig, pub(crate) onevent: Option, + #[allow(unused)] pub(crate) pid: Option, pub(crate) shell: String, pub(crate) zeroconf_port: Option, diff --git a/src/main.rs b/src/main.rs index 1188e1a0..708f02fb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,17 @@ -#![cfg(unix)] - use crate::config::CliConfig; +#[cfg(unix)] +use color_eyre::eyre::eyre; use color_eyre::{ - eyre::{self, eyre, Context}, + eyre::{self, Context}, Help, SectionExt, }; +#[cfg(unix)] use daemonize::Daemonize; -use log::{error, info, trace, LevelFilter}; +#[cfg(unix)] +use log::error; +use log::{info, trace, LevelFilter}; +#[cfg(windows)] +use std::fs; use structopt::StructOpt; use tokio::runtime::Runtime; @@ -42,6 +47,7 @@ fn setup_logger(log_target: LogTarget, verbose: bool) -> eyre::Result<()> { let logger = match log_target { LogTarget::Terminal => logger.chain(std::io::stdout()), + #[cfg(unix)] LogTarget::Syslog => { let log_format = syslog::Formatter3164 { facility: syslog::Facility::LOG_DAEMON, @@ -54,6 +60,25 @@ fn setup_logger(log_target: LogTarget, verbose: bool) -> eyre::Result<()> { .map_err(|e| eyre!("Couldn't connect to syslog instance: {}", e))?, ) } + #[cfg(target_os = "windows")] + LogTarget::Syslog => { + let dirs = directories::BaseDirs::new().unwrap(); + let mut log_file = dirs.data_local_dir().to_path_buf(); + log_file.push("spotifyd"); + log_file.push(".spotifyd.log"); + + if let Some(p) = log_file.parent() { + fs::create_dir_all(p)? + }; + logger.chain( + fs::OpenOptions::new() + .write(true) + .create(true) + .truncate(true) + .open(log_file) + .expect("Couldn't initialize logger"), + ) + } }; logger.apply().wrap_err("Couldn't initialize logger") @@ -67,9 +92,27 @@ fn main() -> eyre::Result<()> { let is_daemon = !cli_config.no_daemon; let log_target = if is_daemon { - LogTarget::Syslog + #[cfg(unix)] + { + LogTarget::Syslog + } + #[cfg(target_os = "windows")] + { + LogTarget::Terminal + } } else { - LogTarget::Terminal + #[cfg(unix)] + { + LogTarget::Terminal + } + #[cfg(target_os = "windows")] + { + if std::env::var("SPOTIFYD_CHILD").is_ok() { + LogTarget::Syslog + } else { + LogTarget::Terminal + } + } }; setup_logger(log_target, cli_config.verbose)?; @@ -92,14 +135,35 @@ fn main() -> eyre::Result<()> { if is_daemon { info!("Daemonizing running instance"); - let mut daemonize = Daemonize::new(); - if let Some(pid) = internal_config.pid.as_ref() { - daemonize = daemonize.pid_file(pid); + #[cfg(unix)] + { + let mut daemonize = Daemonize::new(); + if let Some(pid) = internal_config.pid.as_ref() { + daemonize = daemonize.pid_file(pid); + } + match daemonize.start() { + Ok(_) => info!("Detached from shell, now running in background."), + Err(e) => error!("Something went wrong while daemonizing: {}", e), + }; + } + #[cfg(target_os = "windows")] + { + use std::os::windows::process::CommandExt; + use std::process::{exit, Command}; + + let mut args = std::env::args().collect::>(); + args.remove(0); + args.push("--no-daemon".to_string()); + + Command::new(std::env::current_exe().unwrap()) + .args(args) + .env("SPOTIFYD_CHILD", "1") + .creation_flags(8 /* DETACHED_PROCESS */) + .spawn() + .expect("Couldn't spawn daemon"); + + exit(0); } - match daemonize.start() { - Ok(_) => info!("Detached from shell, now running in background."), - Err(e) => error!("Something went wrong while daemonizing: {}", e), - }; } let runtime = Runtime::new().unwrap(); diff --git a/src/utils.rs b/src/utils.rs index f42a14d5..44fd91bc 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -65,6 +65,11 @@ fn get_shell_ffi() -> Option { None } +#[cfg(target_os = "windows")] +fn get_shell_ffi() -> Option { + Some(String::from("cmd")) +} + pub(crate) fn get_shell() -> Option { let shell = env::var("SHELL").ok().or_else(get_shell_ffi); trace!("Found user shell: {:?}", &shell);