简体   繁体   中英

Why doesn't the execve command in C on macOS allow the 'which' command to work?

Why does the execve command in C on macOS not allow the 'which' command to work? It works on non-Mac devices.

#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

int main()
{
    int fd;
    char cmd[] = "/bin/cat";
    char cmd1[] = "/usr/bin/which";
    char *s[]={"which","ls",NULL};
    if (execve(cmd1, s, NULL) == -1)
        perror("oops ur wrong!!");
}

Expected output

 clang-7 -pthread -lm -o main main.c
 ./main
/bin/ls
 

but on a Mac, it returns nothing.

macOS

The code works. It doesn't work well, but it does work.

Given the null PATH in the environment (because you've used execve() and provided NULL as the environment), /usr/bin/which can't find ls — it has nowhere to look for it because PATH is not set.

On my machine (a MacBook Pro running macOS Big Sur 11.7.1 — it's a work machine and the company IT is behind the times), /usr/bin/which is a universal binary with two architectures. If I run /usr/bin/which ozymandias on the command line, there is no output (I don't have a command ozymandias anywhere), but the exit status is 1 (failure). That's an odd implementation — not reporting an error — but it works within its limits.

You can see this effect with:

$ (unset PATH; /usr/bin/which ls)
$ echo $?
1
$

If you use execv() instead of execve() and remove the , NULL from the argument list, the output is /bin/ls and the exit status is 0 .

Linux

Just for comparison, on a RHEL 7.4 machine, I get different results:

$ which -a which
which='alias | /usr/bin/which --tty-only --read-alias --show-dot --show-tilde'
    /usr/bin/alias
    /usr/bin/which
/usr/bin/which
$ file /usr/bin/which
/usr/bin/which: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=317ba624d2914607bf9246993446803a977fbc18, stripped
$ /usr/bin/which which
/usr/bin/which
$ (unset PATH; /usr/bin/which which)
/usr/bin/which: no which in ((null))
$ /usr/bin/which ozymandias
/usr/bin/which: no ozymandias in (/work2/jleffler/bin:/u/jleffler/bin:/usr/perl/v5.34.0/bin:/usr/gcc/v12.2.0/bin:/usr/local/bin:/usr/bin:/usr/sbin)
$ /usr/bin/which --help
Usage: /usr/bin/which [options] [--] COMMAND [...]
Write the full path of COMMAND(s) to standard output.

  --version, -[vV] Print version and exit successfully.
  --help,          Print this help and exit successfully.
  --skip-dot       Skip directories in PATH that start with a dot.
  --skip-tilde     Skip directories in PATH that start with a tilde.
  --show-dot       Don't expand a dot to current directory in output.
  --show-tilde     Output a tilde for HOME directory for non-root.
  --tty-only       Stop processing options on the right if not on tty.
  --all, -a        Print all matches in PATH, not just the first
  --read-alias, -i Read list of aliases from stdin.
  --skip-alias     Ignore option --read-alias; don't read stdin.
  --read-functions Read shell functions from stdin.
  --skip-functions Ignore option --read-functions; don't read stdin.

Recommended use is to write the output of (alias; declare -f) to standard
input, so that which can show aliases and shell functions. See which(1) for
examples.

If the options --read-alias and/or --read-functions are specified then the
output can be a full alias or function definition, optionally followed by
the full path of each command used inside of those.

Report bugs to <which-bugs@gnu.org>.
$

PATH sanitized — radically shortened.

The which command reports an error when it can't find the command. It is a standalone executable on this Linux machine, and the which alias feeds it the aliases so it can report on them. The -a option reports on all the things that could be known as which (the second which in which -a which ).

I found that adding the envp(path argument in main) to the arguments made it work

#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

int main(int argv, char *argc[],char *envp[])
{
    int fd;
    char cmd1[] = "/usr/bin/which";
    char *s[] = {"which", "ls", NULL};
    if (execve(cmd1, s, envp) == -1)
        perror("oops ur wrong!!");
}

thanks anyways

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM