3

I want to execute composer as non root user. So I renamed main composer executable file to composer-call and then create another executable file called composer.

#composer.sh
su nginx -c "composer-call ${@}" 

Now when I execute composer --version it works but while pass some more complex parameters like composer create-project drupal-composer/drupal-project:8.x-dev some-dir --stability dev --no-interaction it shows below error

+ su nginx -c 'composer-call create-project' drupal-composer/drupal-project:8.x-dev some-dir --stability dev --no-interaction
su: unrecognized option: stability

Update-1

As per below answer by Jeff I tried his answer but it gives me another error. Expecting a better solution.

composer-call: line 2: can't open ?php: no such file
composer-call: line 3: /bin: Permission denied
+ su nginx -- composer-call composer-call docker-php-entrypoint docker-php-ext-configure docker-php-ext-enable docker-php-ext-install docker-php-source drupal drush pear peardev pecl phar phar.phar php php-config phpdbg phpize This file is part of Composer.
su: must be suid to work properly
+ su nginx -- composer-call composer-call docker-php-entrypoint docker-php-ext-configure docker-php-ext-enable docker-php-ext-install docker-php-source drupal drush pear peardev pecl phar phar.phar php php-config phpdbg phpize
su: must be suid to work properly
composer-call: line 6: syntax error: unexpected word (expecting ")")

NOTE: composer-call is a php cli script.

2
  • To tackle the (second) problem first, su: must be suid to work properly -- it appears your su is not setuid. Can you repair that?
    – Jeff Schaller
    Oct 18, 2018 at 12:47
  • The 3rd problem appears to be a typo of some sort in the composer-call script; perhaps you could share that portion of the script?
    – Jeff Schaller
    Oct 18, 2018 at 12:47

3 Answers 3

2

Use -- to separate the target user's command so that su doesn't try to parse additional arguments:

#composer.sh
su nginx -- composer-call "${@}" 

as an example:

user1's composer.sh:

#!/bin/sh
set -x
su otheruser -- /home/otheruser/composer-call "$@"

/home/otheruser/composer-call:

#!/bin/sh
echo Hi, I am composer-call, with arguments:
printf '%s\n' "$@"

In action:

./composer.sh create-project drupal-composer/drupal-project:8.x-dev some-dir --stability dev --no-interaction
+ su otheruser -- /home/otheruser/composer-call create-project drupal-composer/drupal-project:8.x-dev some-dir --stability dev --no-interaction
Password:
Hi, I am composer-call, with arguments:
create-project
drupal-composer/drupal-project:8.x-dev
some-dir
--stability
dev
--no-interaction
9
  • This gives me below error composer-call: line 2: can't open ?php: no such file composer-call: line 3: /bin: Permission denied su: must be suid to work properly su: must be suid to work properly composer-call: line 6: syntax error: unexpected word (expecting ")")
    – SkyRar
    Oct 18, 2018 at 0:50
  • It seems like it's invoking composer-call correctly, and you've maybe run into another hurdle?
    – Jeff Schaller
    Oct 18, 2018 at 0:51
  • In my code composer was getting called correctly also. E.g as per my code composer -V was showing proper output. But when passing multiple parameters it shows error.
    – SkyRar
    Oct 18, 2018 at 0:55
  • it was correctly calling compose-call and passing some arguments, but su was getting in the way when it saw arguments with leading dashes. Using a double-dash tells su that it should stop looking for options and pass everything else along to composer-call.
    – Jeff Schaller
    Oct 18, 2018 at 1:17
  • Okay. Fine but this answer doesn't solve my issue. Is it working for you ?
    – SkyRar
    Oct 18, 2018 at 1:19
2

This is because "$@" is expanded into separate words and the second word (--stability in your case) is considered an option to su. Here is an example using ls as a command:

#composer.sh
set -x
su nginx -c "ls $@"

Output 1:

$ ./composer.sh -l -a
++ su nginx -c 'ls -l' -a
su: invalid option -- 'a'
Try 'su --help' for more information.

Note that only the first parameter (-l) goes into the string passed to -c. This is why your --version case works. Instead of $@ you should use $* which is expanded into a single word when it is placed in double quotes:

#composer.sh
set -x
su nginx -c "ls $*"

Output 2:

$ ./composer.sh -l -a
++ su nginx -c 'ls -l -a'
total 12
drwxr-xr-x 2 root root 4096 Apr 22 11:05 .
drwxr-xr-x 1 root root 4096 Apr 22 11:05 ..
-rwxr-xr-x 1 root root   28 Apr 22 11:05 composer.sh
-rw-r--r-- 1 root root    0 Apr 22 11:05 test.txt

Note that now all parameters go to -c which produces the correct result.

You can read about it in detail in man bash in the Special Parameters section.

3
  • That does not work if a parameter has a space in it. E.g. ./composer.sh -l -a "directory name with spaces in it" will not work.
    – Toxiro
    Feb 12 at 20:51
  • Yes, of course, knowing that all parameters will be combined into one string and separated by spaces you should yourself take care of preserving spaces where needed in the input string, e.g. ./composer.sh -l -a "directory\ name\ with\ spaces\ in\ it" Feb 18 at 7:48
  • Ok, that works, but it is pretty uncomfortable. It means you cannot use the command as supposed to be. You cannot use auto complete for directories and you cannot copy paste commands without escaping every space.
    – Toxiro
    Feb 28 at 19:26
0

Finally encapsulating the composer command inside a here-doc block helped me

#!/bin/sh

su nginx -s /bin/sh <<EOF
composer-call "$@"
EOF

I use below code to create above wrapper script programmatically. :)

printf '%s\n' "#!/bin/sh" "su nginx -s /bin/sh <<EOF" "composer-call \$@" "EOF" > /usr/local/bin/composer

Apart from it su-exec is also a nice alternative. It is a tinny software and can be installed like apk add su-exec

6
  • 1
    That will only work as long as the arguments contain no spaces or special characters. Please see my answer here.
    – user313992
    Nov 4, 2018 at 12:01
  • It works perfectly for arguments like create-project drupal-composer/drupal-project:8.x-dev some-dir --stability dev --no-interaction
    – SkyRar
    Nov 4, 2018 at 12:04
  • 1
    It may work with those particular arguments in your case, but your answer is public, and that use of $@ is flawed. Try this instead: su nginx -c 'composer-call "$@"' -- argv0 "$@".
    – user313992
    Nov 4, 2018 at 12:14
  • Thanks @mosvy I will surely try your answer also.
    – SkyRar
    Nov 4, 2018 at 12:21
  • Your new version will pass everything as a single argument to composer-call -- ie. it won't work at all (but you could "split" it by passing arguments of the form "; reboot; " ;-)). And it will also expand backquotes, variables, $((...)), etc. in the arguments.
    – user313992
    Nov 4, 2018 at 13:21

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .