This is the mail archive of the cygwin mailing list for the Cygwin project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

two execve() inconsistencies [was: cygwin-bash compat/regression bug... startup line prob]


On 05/01/2014 12:11 PM, Linda Walsh wrote:

> 
> On linux, (and, thus on cygwin?), "#!/bin/bash -u -x -a -b -c"
> is passed as 1 argument to bash.  I.e. the spaces don't break things
> into separate arguments on linux.

Correct.  So follow it through to it's logical conclusion:

/bin/bash "-u -x -a -b -c"

is trying to invoke bash with one GIANT short option string as argv[1].
 Per getopt() parsing rules, it is as if you were trying to invoke:

/bin/bash "-u" "- " "--" "-x" "- " "--" "-a" "- " "--" "-b" "- " "--" "-c"

But bash does NOT have a "- " short option (that is, the short option
whose name is a space character).  You have formed an illegal command
line, and bash is trying to tell you that.  It would be nicer if bash
quoted the first option it doesn't recognize (/usr/bin/bash: "- ":
invalid option) rather than hoping you are smart enough to detect the
trailing space meant that you were trying to pass a space as the option
name (/usr/bin/bash: - : invalid option); but such is life.

> 
> I.e. the above is valid as well -- but it was the single "-u" switch
> that doesn't work.

But my point remains - you WEREN'T passing a single "-u" switch, but
MUST have had a trailing space on the line, and were passing "-u " which
bash is dutifully trying to parse as "-u" "- " and erroring out on the "- ".

However, now that I have further tested on both Linux and cygwin
systems, I CAN state that you have indeed found an inconsistency between
how Cygwin execve() and Linux execve() behave - so if someone is willing
to write a patch to cygwin1.dll to eliminate that inconsistency, it
would be nice.

On Linux, execve() strips trailing space before forming the command line
that gets substituted during shebang execution.  Sadly, this aspect of
Linux is NOT documented in the man page; I had to learn it by
experimentation.  Observe this Linux transcript:

$ cd /tmp
$ printf '#!/bin/sh -u \necho hi\n' > blah.sh
$ chmod +x blah.sh
$ ./blah.sh
hi
$ printf '#!/bin/sh -u -\necho hi\n' > blah.sh
$ ./blah.sh 2>&1 |head -n1
/bin/sh: - : invalid option

Note that when the shebang had "-u ", the trailing space was stripped,
and bash was run with "-u"; but when it had "-u -", there was no
trailing space to strip, and bash given the literal argument "-u -"
dutifully choked on the attempt to parse "- " as a short option.

Cygwin, on the other hand, does not (yet) strip trailing spaces when
constructing the replacement command line from a shebang.  Patches
thoughtfully considered.  Particularly true since some people use
carriage returns on script files under cygwin (I personally detest the
practice, but have at least patched bash to try to allow others to use
it) - stripping trailing whitespace when forming the argv[1] of a
shebang line would allow the shebang to do the right thing of NOT
passing the carriage return to the actual command line of the
interpreter, even if the script is accidentally executed from a binary
mount rather than a text mount.

>> Because you weren't running /bin/bash at that point in time, but
>> /usr/bin/bash.  Again, you snipped the relevant portion of your original
> ----
> No...I was... the output at the top was from "t.sh", which had
> #!/bin/bash.
> 
> But the error message says /usr/bin/bash.

Okay, now that I have tested on a Cygwin machine, I have indeed
reproduced what you are seeing.  When you invoke './t.sh', which has a
shebang calling out '#!/bin/sh', but in a way that provokes an error,
the error calls out /usr/bin/sh.  That means that the execve() logic for
constructing the alternative command line is passing a different argv[0]
to the replacement interpreter than what you actually specified in your
shebang line.  On cygwin, /bin/bash and /usr/bin/bash are ALWAYS the
same file (two different spellings, but the cygwin mount code treats
/bin/ and /usr/bin/ as mounts to the same single Windows directory).
The execve() code responsible for locating the replacement application
to run is first locating "/bin/bash" as written in your shebang line,
then constructing the argv[] to pass to that application - and somewhere
in that process, it ends up rewriting argv[0] to match what it considers
to be the canonical name.  Observe this cygwin transcript:

$ cygpath -w /bin/bash
C:\cygwin\bin\bash
$ cygpath -w /usr/bin/bash
C:\cygwin\bin\bash
$ cygpath -u "C:\cygwin\bin\bash"
/usr/bin/bash

So, although both Unix names are usable, it is as if the execve code has
run your spelling through a conversion to a windows name, then converted
that windows name into the argv[0] for use, rather than preserving your
original argv[0].  Not the end of the world for bash (although bash DOES
care about the spelling of argv[0], it is mostly on whether the basename
was 'sh' or 'bash', and NOT on what directory name it detected) - but it
COULD be problematic for other apps (remember the recent traffic on this
list about how '/bin/gcc' failed while '/usr/bin/gcc' succeeded, all
because the program used argv[0] to construct relative names to
resources where /usr/bin/../share exists but /bin/../share does not
exist?).  So, a patch to cygwin execve() code to preserve the user's
original argv[0] spelling would be appreciated.

> So why doesn't a single argument work (-u?)

Because you didn't pass a single argument, but left trailing space which
bash tried to interpret as a second argument, and because no one has yet
provided the patch for the two execve() inconsistencies that this thread
has uncovered.

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org

Attachment: signature.asc
Description: OpenPGP digital signature


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]