-
Notifications
You must be signed in to change notification settings - Fork 1.9k
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
Quoting for start arguments #10099
Quoting for start arguments #10099
Conversation
backport of #10090 Signed-off-by: gregw <[email protected]>
Do not quote comma Signed-off-by: gregw <[email protected]>
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.
I like the idea of being minimal to quote, and only if necessary.
jetty-start/src/main/java/org/eclipse/jetty/start/CommandLineBuilder.java
Outdated
Show resolved
Hide resolved
jetty-start/src/main/java/org/eclipse/jetty/start/CommandLineBuilder.java
Outdated
Show resolved
Hide resolved
jetty-start/src/main/java/org/eclipse/jetty/start/CommandLineBuilder.java
Outdated
Show resolved
Hide resolved
jetty-start/src/main/java/org/eclipse/jetty/start/CommandLineBuilder.java
Outdated
Show resolved
Hide resolved
jetty-start/src/main/java/org/eclipse/jetty/start/CommandLineBuilder.java
Show resolved
Hide resolved
jetty-start/src/main/java/org/eclipse/jetty/start/CommandLineBuilder.java
Outdated
Show resolved
Hide resolved
if (arg != null) | ||
if (commandLine.length() > 0) | ||
commandLine.append(separator); | ||
commandLine.append(option); |
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.
The logic to add option
, name
and value
to commandLine
and args
seems different.
Should they not be paired and be equal?
E.g. above we always add option
to commandLine
, why not to args
too?
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.
We do. All three branches below add option to the args one way or another. The code is a bit convoluted because it is minimal. Let me make it more explicit even if it duplicates a few lines....
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.
is that better?
o "java" - the JVM to run | ||
o "opts" - the JVM options (e.g. -D, -X and -XX flags) | ||
o "path" - the JVM class-path and/or the JPMS module-path | ||
o "main" - the main class to run | ||
o "args" - the arguments passed to the main class | ||
o "multipart" - a fake part that causes the command to be |
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.
It's multiline
! 😂
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.
multiline
is not necessary.
If you are working on a shell you can accomplish this output on any already released version of Jetty now.
$ java -jar /path/to/jetty-home/start.jar --dry-run | xargs printf "%s\n"
A useful technique when troubleshooting the behavior of our quoting on various shells too (as the string to arg conversion for that shell is performed through the pipe to xargs)
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.
neat technique, but the multiline output is correctly slashed so it is still executable. Frequently in docker style processing it is necessary to modify the output of the dry-run in some way, and having it broken up into multiple lines will make scripts like sed simpler to apply
jetty-start/src/test/java/org/eclipse/jetty/start/CommandLineBuilderTest.java
Outdated
Show resolved
Hide resolved
Updates from review Signed-off-by: gregw <[email protected]>
This branch has broken
Attached is the jetty.base to reproduce this. |
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.
Don't use Double Quotes if you want to support sh
Use "Strong Quoting" instead, which is supported across the board.
And then you have only 1 character to worry about to escape.
bash
[joakim@hyperion ~]$ java -jar ~/java/args-dump-1.1.jar 'Hello internal quotes' foo bar
args.length=3
args[0]: [Hello internal quotes]
args[1]: [foo]
args[2]: [bar]
[joakim@hyperion ~]$ java -jar ~/java/args-dump-1.1.jar 'Hello '\''internal'\'' quotes' foo bar
args.length=3
args[0]: [Hello 'internal' quotes]
args[1]: [foo]
args[2]: [bar]
[joakim@hyperion ~]$ java -jar ~/java/args-dump-1.1.jar 'Hello $USER env var' zed foo
args.length=3
args[0]: [Hello $USER env var]
args[1]: [zed]
args[2]: [foo]
sh
$ java -jar ~/java/args-dump-1.1.jar 'Hello internal quotes' foo bar
args.length=3
args[0]: [Hello internal quotes]
args[1]: [foo]
args[2]: [bar]
$ java -jar ~/java/args-dump-1.1.jar 'Hello '\''internal'\'' quotes' foo bar
args.length=3
args[0]: [Hello 'internal' quotes]
args[1]: [foo]
args[2]: [bar]
$ java -jar ~/java/args-dump-1.1.jar 'Hello $USER env var' zed foo
args.length=3
args[0]: [Hello $USER env var]
args[1]: [zed]
args[2]: [foo]
zsh
joakim@hyperion ~ % java -jar ~/java/args-dump-1.1.jar 'Hello internal quotes' foo bar
args.length=3
args[0]: [Hello internal quotes]
args[1]: [foo]
args[2]: [bar]
joakim@hyperion ~ % java -jar ~/java/args-dump-1.1.jar 'Hello '\''internal'\'' quotes' foo bar
args.length=3
args[0]: [Hello 'internal' quotes]
args[1]: [foo]
args[2]: [bar]
joakim@hyperion ~ % java -jar ~/java/args-dump-1.1.jar 'Hello $USER env var' zed foo
args.length=3
args[0]: [Hello $USER env var]
args[1]: [zed]
args[2]: [foo]
jetty-start/src/main/java/org/eclipse/jetty/start/CommandLineBuilder.java
Outdated
Show resolved
Hide resolved
* Quote a string suitable for use with a command line shell using double quotes. | ||
* <p>This method applies doubles quoting as described for the unix {@code sh} commands: | ||
* Enclosing characters within double quotes preserves the literal meaning of all characters except | ||
* dollarsign ($), backquote (`), and backslash (\). | ||
* The backslash inside double quotes is historically weird, and serves | ||
* to quote only the following characters: {@code $ ` " \ newline}. | ||
* Otherwise, it remains literal. |
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.
* Quote a string suitable for use with a command line shell using double quotes. | |
* <p>This method applies doubles quoting as described for the unix {@code sh} commands: | |
* Enclosing characters within double quotes preserves the literal meaning of all characters except | |
* dollarsign ($), backquote (`), and backslash (\). | |
* The backslash inside double quotes is historically weird, and serves | |
* to quote only the following characters: {@code $ ` " \ newline}. | |
* Otherwise, it remains literal. | |
* Quote a string suitable for use with a command line shell using strong quotes {@code '}. | |
* <p>This method applies strong quoting as described for the unix {@code sh} commands: | |
* Enclosing characters within strong quotes preserves the literal meaning of all characters. |
We don't want to use double quotes with sh / bash / zsh, we want to use strong quotes '
, as that will not interpret the contents between the quotes.
Examples:
sh
on ubuntu
[joakim@hyperion ~]$ which sh
/usr/bin/sh
[joakim@hyperion ~]$ ls -la /usr/bin/sh
lrwxrwxrwx 1 root root 4 Mar 23 2022 /usr/bin/sh -> dash
[joakim@hyperion ~]$ sh -i -l
$ echo "Hello $USER"
Hello joakim
$ echo 'Hello $USER'
Hello $USER
bash
on ubuntu
[joakim@hyperion ~]$ echo "Hello $USER"
Hello joakim
[joakim@hyperion ~]$ echo 'Hello $USER'
Hello $USER
zsh
on ubuntu
[joakim@hyperion ~]$ zsh -i -l
joakim@hyperion ~ % echo "Hello $USER"
Hello joakim
joakim@hyperion ~ % echo 'Hello $USER'
Hello $USER
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.
Using single quotes is ugly/complex because we have to escape literal single quotes outside of the scope of the quotes, so quoting something like my.property='foo bar'
(where the single quotes are literal) will end up with the unreadable my.property=\''foo bar'\'
Double quotes a simpler and with 4 characters that need escaping are well enough defined and simple to support.
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.
The example property my.property='foo bar'
would, as if it appeared in start.d/my.ini
would mean that the end user wants the single quotes in the value as the end result (meaning if they do properties.get("my.property")
they would expect a String with those single-tick characters: "'foo bar'"
)
The end result would be for strong quotes ...
'-Dmy.property='\''foo bar'\'''
The entire argument is strong quoted, not just a section.
Example:
[joakim@hyperion ~]$ java -Dmy.property='foo bar' -XshowSettings:properties --version 2>&1 | grep my.prop
my.property = foo bar
[joakim@hyperion ~]$ java '-Dmy.property='\''foo bar'\''' -XshowSettings:properties --version 2>&1 | grep my.prop
my.property = 'foo bar'
This Strong Quoting also eliminates ALL interpretation of the contents of the quoted string by the shell (on all shells)
From the GNU dash man page (what the standard sh
is on Ubuntu)
Single Quotes
Enclosing characters in single quotes preserves the literal meaning
of all the characters (except single quotes, making it impossible
to put single-quotes in a single-quoted string).
Something that Double Quotes does not do, and escaping will need to be SUPER robust (things you are ignoring in this PR will fail on various shells) if we go that route.
Yes, I'm aware that dash
only mentions a handful of characters for Double Quotes, the other shells have far more special characters when encountered within Double Quotes (a behavior that Strong Quoting doesn't have)
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.
@joakime I think what you have been trying to say is: "xargs doesn't grok double quotes like y'd think". Once I worked that out, then OK let's go with single quotes. updated.
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.
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.
@joakime I think what you have been trying to say is: "xargs doesn't grok double quotes like y'd think". Once I worked that out, then OK let's go with single quotes. updated.
It's not xargs
doing that.
It is bash
doing that, bash
(and zsh
) will evaluate on any redirection/pipe before redirection/pipe (unlike dash
).
This was a design decision by bash
to get around deficiencies in sh
not allowing variables to be set on redirection/pipe.
jetty-start/src/main/java/org/eclipse/jetty/start/CommandLineBuilder.java
Outdated
Show resolved
Hide resolved
jetty-start/src/main/java/org/eclipse/jetty/start/CommandLineBuilder.java
Outdated
Show resolved
Hide resolved
jetty-start/src/main/java/org/eclipse/jetty/start/CommandLineBuilder.java
Outdated
Show resolved
Hide resolved
jetty-start/src/main/java/org/eclipse/jetty/start/CommandLineBuilder.java
Show resolved
Hide resolved
jetty-start/src/main/java/org/eclipse/jetty/start/CommandLineBuilder.java
Show resolved
Hide resolved
jetty-start/src/main/resources/org/eclipse/jetty/start/usage.txt
Outdated
Show resolved
Hide resolved
jetty-start/src/test/java/org/eclipse/jetty/start/CommandLineBuilderTest.java
Outdated
Show resolved
Hide resolved
@joakime your custom requestlog base is broken. It contains the line:
So the unmatched quotes are from your ini file! If I take the OPs reproducer from #9309 and put it in my ini file as:
then it starts fine and --dry-run=multiline reports:
This looks perfectly correct |
I think we are posix shell compliant:
|
I'll change our doco to say posix shell... |
Updates from review Signed-off-by: gregw <[email protected]>
The current released versions of |
@joakime ultimately, both double quotes and single quotes are well documented in I chose double quotes because I prefer the simplicity of just 5 characters needing escape within the quotes, whilst single quotes needs to have two mechanism: single quotes; escaped chars outside of quotes; and sometimes a combination. Ultimately for the few difficult cases we might have, double quotes result in a lot more readable and understandable output. Ultimately, for our purposes single and double quotes are equivalent, as we can convert one to the other, since we never include any unexpanded parameters. Thus there is no compelling reason to change. |
Then who knows how it implements single quotes? If they are not POSIX compliant, then neither quoting style is ok. |
jetty-start/src/main/java/org/eclipse/jetty/start/StartArgs.java
Outdated
Show resolved
Hide resolved
Unmatched quotes are a perfectly legitimate thing to appear in one of our INIs. BTW, this example base is supported properly on PR #10100 now. |
Revert to single quotes for the sake of xargs in jetty.sh Signed-off-by: gregw <[email protected]>
restored deprecated APIs Signed-off-by: gregw <[email protected]>
fixed JPMS args Signed-off-by: gregw <[email protected]>
options cleaned up Signed-off-by: gregw <[email protected]>
jetty-start/src/main/java/org/eclipse/jetty/start/CommandLineBuilder.java
Outdated
Show resolved
Hide resolved
jetty-start/src/main/java/org/eclipse/jetty/start/CommandLineBuilder.java
Outdated
Show resolved
Hide resolved
jetty-start/src/main/java/org/eclipse/jetty/start/CommandLineBuilder.java
Outdated
Show resolved
Hide resolved
jetty-start/src/main/java/org/eclipse/jetty/start/CommandLineBuilder.java
Show resolved
Hide resolved
jetty-start/src/main/java/org/eclipse/jetty/start/CommandLineBuilder.java
Outdated
Show resolved
Hide resolved
jetty-start/src/main/java/org/eclipse/jetty/start/CommandLineBuilder.java
Outdated
Show resolved
Hide resolved
jetty-start/src/main/java/org/eclipse/jetty/start/StartArgs.java
Outdated
Show resolved
Hide resolved
jetty-start/src/test/java/org/eclipse/jetty/start/CommandLineBuilderTest.java
Outdated
Show resolved
Hide resolved
jetty-start/src/test/java/org/eclipse/jetty/start/CommandLineBuilderTest.java
Show resolved
Hide resolved
jetty-start/src/test/java/org/eclipse/jetty/start/CommandLineBuilderTest.java
Show resolved
Hide resolved
jetty-start/src/main/java/org/eclipse/jetty/start/StartArgs.java
Outdated
Show resolved
Hide resolved
jetty-start/src/test/java/org/eclipse/jetty/start/CommandLineBuilderTest.java
Outdated
Show resolved
Hide resolved
Co-authored-by: Simone Bordet <[email protected]>
updates from review Signed-off-by: gregw <[email protected]>
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.
Small javadocs nits if you want to fix them, but LGTM.
jetty-start/src/main/java/org/eclipse/jetty/start/CommandLineBuilder.java
Outdated
Show resolved
Hide resolved
jetty-start/src/main/java/org/eclipse/jetty/start/CommandLineBuilder.java
Outdated
Show resolved
Hide resolved
Co-authored-by: Simone Bordet <[email protected]>
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.
Is it intentional that all --<value>
arguments are passed to the JVM side of the --dry-run
?
Example:
start.d/foo.ini
--long-arg
--zed=zora
Those two will be present in --dry-run
before the classpath.
If we have a directory with an equals sign, this is interpreted as a property, not an XML.
start.d/mode.ini
etc/mode=dev/example.xml
I think we check for equals before we check for endsWith(".xml")
?
It's not possible to have an xml or lib with a directory with a backslash defined in an start.d/<name>.ini
Example:
etc/oh\no/example.xml
--lib=lib/rut\roh/main.jar
An argument that starts with an equals sign produces some odd output on --dry-run
start.d/zed.ini
=1234
results in the output
''=1234
If there is no key with the use of the =
sign, that quoting is not necessary.
jetty-start/src/main/java/org/eclipse/jetty/start/CommandLineBuilder.java
Show resolved
Hide resolved
jetty-start/src/main/java/org/eclipse/jetty/start/CommandLineBuilder.java
Show resolved
Hide resolved
jetty-start/src/main/java/org/eclipse/jetty/start/CommandLineBuilder.java
Outdated
Show resolved
Hide resolved
@@ -208,6 +208,7 @@ public class StartArgs | |||
private boolean listConfig = false; | |||
private boolean version = false; | |||
private boolean dryRun = false; | |||
private boolean multiLine = false; |
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.
This should be a separate concept, not as a "part" or "section" on --dry-run=<parts>
Consider instead a --dry-run-format=<type>
where we have a few options.
sh
- what we do now (and is the default format)windows
- an alternate mode for windowsnewline
- what thismultiLine
is doing
And in the future we might even be able to support the various Daemon tooling and produce JSON or XML of the args that those tools consume.
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.
But how do you select which parts to output with this new option?
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.
The existing --dry-run=<parts>
still exists.
Select what you want from that.
How the parts are presented are impacted by the new arg.
default behavior: (as --dry-run-format=sh
is default, that's what is used)
$ java -jar start.jar --dry-run=opts,path,main,args
or on windows
> java -jar start.jar --dry-run=opts,args --dry-run-format=windows
or for new lines
$ java -jar start.jar --dry-run=opts,path,main,args --dry-run-format=newline
and in the future
$ java -jar start.jar --dry-run=opts,path,main,args --dry-run-format=json
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.
This is too complex and verbose for a minor feature
updates from review Signed-off-by: gregw <[email protected]>
These may well be issues, but they have nothing to do with quoting or
Any unknown options are considered JVM options and are thus ordered as they are.
Indeed, any arg with an = will be recognised as a property and it has been like this since the beginning.
Ditto. If this ever becomes an issue, then we can add an optional
Well perhaps we should make this an error, but it has not been a problem because there is no need for an arg starting with = |
@sbordet I've run out of time to deal with this PR. Can you take over this PR to get it approved and merged 10,11, and to 12. The PR #10090 is out of date and I've not updated whilst waiting for this to be approved. It might be simpler to abandon that and just merge this to 12 and fix up the env handling differences. With regards to the multiline feature, I do not think this should be turned into anything as complex as has been suggested. It is just a simple little tool and we have no need to support json or non posix shells. If there is no agreement on it, then just remove it. We don't want to hold up progress over such a simple thing. |
// Single quotes are used because double quotes are processed differently by some shells and the xarg | ||
// command used by jetty.sh |
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.
// Single quotes are used because double quotes are processed differently by some shells and the xarg | |
// command used by jetty.sh | |
// Single quotes are used because double quotes are evaluated differently by some shells such as bash | |
// which is used by jetty.sh and the Jetty docker image |
* A couple more test cases. Signed-off-by: Simone Bordet <[email protected]>
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.
LGTM, lets merge this to jetty-12.0.x
asap
backport of #10090
probably should be merged first and then merged through 11 & 12 before applying #10090