-
Notifications
You must be signed in to change notification settings - Fork 756
parse environment variables #886
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
Haven't done the changelog yet because I didn't want to deal with any merge conflicts between this and #883. After that is merged I'll rebase and edit the changelog. |
|
This is very handy for daemonizing librespot with systemd as you can specify an environment file and use that as a no cost config file and keep configuration details out of the service unit file. It's also a security win in that case since https://www.freedesktop.org/software/systemd/man/systemd.exec.html#EnvironmentFile= |
|
In terms of your last comment specifically, other projects use a method where the service unit has something like and then also define an The benefit of this method is you don't need any special code to handle reading environment variables. |
|
@kingosticks I know this, see the linked Raspotify issue. And generally that withstanding it's pretty common for command line apps to read environment variables. I don't understand the push back? |
|
The roll your own var method is a kludge for apps that can't read env vars. |
|
@kingosticks did you look at the code? It's actually a very small change that moves IMHO this PR isn't about adding a feature but fixing an omission. |
|
It wasn't meant to be push-back. I was highlighting the alternative because although it might be mentioned in dtcooper/raspotify#460 it's not as clear in your comment here that the specific use-case of hiding arg values can be accomplished without changing librespot. You call that alternative a kludge and that opinion is fine. This change might actually have as much benefit to those running from the command line (i.e. not a service) as it would allow them to more easily keep librespot settings in their dotfiles. If/when you make this change, probably need to document somewhere which of the two config methods takes precedence when both are set. |
Ok fair enough.
If both are present the passed command line arg takes precedence. That seemed like the logical order of things to me. |
fn arg_to_var(arg: &str) -> String {
// To avoid name collisions environment variables must be prepended
// with `LIBRESPOT_` so option/flag `foo-bar` becomes `LIBRESPOT_FOO_BAR`.
format!("LIBRESPOT_{}", arg.to_uppercase().replace("-", "_"))
}
fn env_var_present(arg: &str) -> bool {
env::var(arg_to_var(arg)).is_ok()
}
fn env_var_opt_str(option: &str) -> Option<String> {
match env::var(arg_to_var(option)) {
Ok(value) => Some(value),
Err(_) => None,
}
} let opt_present = |opt| matches.opt_present(opt) || env_var_present(opt);
let opt_str = |opt| {
if matches.opt_present(opt) {
matches.opt_str(opt)
} else {
env_var_opt_str(opt)
}
};env vars aren't even evaluated if command line args are present. |
|
I know (I did read the code) and I agree that's the correct order. It was a reminder that it needs to be specified in the wiki/whatever. |
|
The caveat being that flags are set if present (env and command line). The only way to override a flag set with an env var is to remove the env var. You can't override a set flag with another command line arg. If that makes sense? |
I will note it in the change log when the time comes and in the wiki. |
Yes, I was thinking about that too. I think it has the potential to muddy the water when debugging as we often ask for the exact command used to invoke librespot in order to understand the settings they are running with. You can imagine someone setting an envvar, forgetting about it and later wondering why it's doing something odd. A solution would be to ensure there is (at least verbose) logging for every setting value. Or, have a mode that just prints the configured value for every setting and exits. The latter is quite nice as you can ask for that self-contained output in the bug report template etc. |
|
Adding a trace level message would be pretty easy. An arg that outputs all args is kinda meta though. It's kinda like altering what you're trying to observe. |
|
Meta not necessarily in a bad way but just in a way that it's an arg about arg. |
|
You could see it that way but that's not an argument against doing it. It's similar to Anyways, it's out of scope here. Just something to keep in mind for later if we find ourselves getting tricked by the multiple sources for options. No need to fix something that isn't a problem (yet)! |
|
I'm not arguing against it but I agree that we can cross that bridge if we come to it. |
Make librespot able to parse environment variables for options and flags. To avoid name collisions environment variables must be prepended with `LIBRESPOT_` so option/flag `foo-bar` becomes `LIBRESPOT_FOO_BAR`.
|
There the change log is updated and as noted after this is merged I will update the wiki. |
|
You guys better step up your game because so far that |
|
@kingosticks I just re-read your comment about the alternative method aka "the arg bucket". To clarify the result would be:
And your "secret" being completely visible to any and all user with |
and PS: I like french windows, and a red door on my bike shed :-D |
That won't do anything. The vars set in a service file aren't visible from the outside. |
TIL! |
|
My point was not to bikeshed but to clarify that the secret in the example is anything but a secret. The example seems to imply that it is. All the environment file does in that case is make it easier to change the args. It makes a faux config file. |
|
I'm also not implying that proper env vars are bullet proof security. They're just marginally better. And they make for a better faux config file. |
Sorry, my comment was a drive-by - no offense meant! EDIT: |
I was mainly just trying to keep it out of the view of your average Lookie-loo. Realistically we're not talking about state secrets. |
|
Not having garbage littering the service file is the primary reason. As you mention it makes updating much easier when/if args are added/removed and/or change names. |
|
Args are expended. Environment variables are not. Whatever you set as env vars is not visible. In your example you are passing the vars as args. That is why they are visible. You can set vars with a env var file and they will not be visible as long as you don't pass them as args. |
|
This has kinda gone off the rails a bit. But I think I would be correct in say that everyone so far agrees that this is a good addition @roderickvd what do you think? Ready to merge? |
|
Yeah I was lurking to see how this all unfolded 😆
I'll review it soon, this evening I'm keen to iron out the last things in my |
I'm more of the cleanup crew at this point, while you guys are the builders. You guys do the heavy lifting and then I go in and smooth the rough edges and put a little polish on it. I look forward to the new (new-new?) API. I think that once that is ironed out and maybe libmdns is cleaned up (if it even needs to be?) |
|
There @kingosticks |
|
Creds are redacted. |
Verbose logging mode (`-v`, `--verbose`) now logs all parsed environment variables and command line arguments (credentials are redacted).
|
Again I'm following this, please let me know when you're ready for final review and are all in agreement. |
|
I'm ready. I'm still waiting for @kingosticks as he mentioned the idea of logging all the command line args and env vars so I added that. |
|
Anyone that would like to see an implementation of this can checkout https://github.com/dtcooper/raspotify/tree/new-config |
|
Sorry, I've not got time to actually look at the code right now but the example in the comment looks great. Nice one. |
|
@kingosticks the code isn't particularly clever. It just loops though the vars and args and outputs a trace level log message if there are any. |
|
@roderickvd and @kingosticks and @ashthespy what say you? Merge? |
|
LGTM :-) Thanks for your work! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just one small thing and we're good to merge!
* commit 'e66cc5508cee0413829aa347c7a31bd0293eb856': parse environment variables (librespot-org#886) Harden systemd service, update restart policy (librespot-org#888) Improve `--device ?` functionality for the alsa backend # Conflicts: # src/main.rs
Make librespot able to parse environment variables for options and flags. To avoid name collisions environment variables must be prepended with `LIBRESPOT_` so option/flag `foo-bar` becomes `LIBRESPOT_FOO_BAR`. Verbose logging mode (`-v`, `--verbose`) logs all parsed environment variables and command line arguments (credentials are redacted).
Fix up for librespot-org#886 Closes: librespot-org#898 And... * Don't silently ignore non-Unicode while parsing env vars. * Iterating over `std::env::args` will panic! on invalid unicode. Let's not do that. `getopts` will catch missing args and exit if those args are required after our error message about the arg not being valid unicode. * Gaurd against empty strings. There are a few places while parsing options strings that we don't immediately evaluate their validity let's at least makes sure that they are not empty if present. * `args` is only used in `get_setup` it doesn't need to be in main. * Nicer help header. * Get rid of `use std::io::{stderr, Write};` and just use `rpassword::prompt_password_stderr`. * Get rid of `get_credentials` it was clunky, ugly and only used once. There is no need for it to be a separate function. * Handle an empty password prompt and password prompt parsing errors. * + Other random misc clean ups.
Make librespot able to parse environment variables for options and flags. To avoid name collisions environment variables must be prepended with
LIBRESPOT_so option/flagfoo-barbecomesLIBRESPOT_FOO_BAR.Verbose logging mode (
-v,--verbose,LIBRESPOT_VERBOSE=) now logs all parsed environment variables and command line arguments (credentials are redacted).