System() question

B

Bob

Howdy,

Why does a call to system() work by passing the command line string
supplied to sh -c? This is causing problems when running in a chroot
environment where no sh is available. Wouldn't it be better for C to
replace the system() call by a fork()-exec() pair? Is there some
compiler option that can change the behavior of system()?

Thanks in advance.
 
S

santosh

Bob said:
Howdy,

Why does a call to system() work by passing the command line string
supplied to sh -c? This is causing problems when running in a chroot
environment where no sh is available. Wouldn't it be better for C to
replace the system() call by a fork()-exec() pair? Is there some
compiler option that can change the behavior of system()?

Thanks in advance.

Fork() and exec() are not present on many systems where C is
implemented. Also they are not as generic as system(). As far as the C
Standard is concerned, everthing about the argument to system() is
implementation specific.

You can always find out if a shell is available by doing:

int rc = system(NULL);
if (rc) { /* shell present */ }
else { /* no shell */ }
 
F

Flash Gordon

santosh wrote, On 06/12/07 22:20:
Fork() and exec() are not present on many systems where C is
implemented. Also they are not as generic as system(). As far as the C
Standard is concerned, everthing about the argument to system() is
implementation specific.

You can always find out if a shell is available by doing:

int rc = system(NULL);
if (rc) { /* shell present */ }
else { /* no shell */ }

Not quite everything is system specific, and the answer to why system()
is having a problem if no shell is available is *not* system specific.

The standard *requires* that the system call pass the parameter to the
command processor (AKA shell), so if there is no shell available then it
is required to fail. Whether some other behaviour would be better is
another matter.

If you want the behaviour of fork/exec and your implementation provides
them (or some equivalent) then I recommend using these system specific
functions. Note that another standard (POSIX) might give the guarantees
required, if so then for information on using them see
comp.unix.programmer or some other group appropriate to the system(s) of
interest.
 
R

Richard Tobin

Bob said:
Why does a call to system() work by passing the command line string
supplied to sh -c? This is causing problems when running in a chroot
environment where no sh is available. Wouldn't it be better for C to
replace the system() call by a fork()-exec() pair? Is there some
compiler option that can change the behavior of system()?

As far as the C standard is concerned, it could work either way.

But in practice it's much more useful if commands passed to system()
are treated the same way as commands typed to the shell. For example,
I expect system("rm *") to remove all the files in the directory,
which wouldn't happen if you just executed the "rm" program with with
argv[1] set to "*".

If you're writing code to work in a chroot()ed environment you quite
likely want to take special care with any commands you execute: I
suggest writing your own system()-like function that uses fork()
and exec() (and probably wait()) directly.

-- Richard
 
R

Richard Tobin

Flash Gordon said:
The standard *requires* that the system call pass the parameter to the
command processor (AKA shell), so if there is no shell available then it
is required to fail. Whether some other behaviour would be better is
another matter.

Unix has several shells available, and no doubt so do many other
operating systems. The standard doesn't specify which one to use - it
just refers to "a command processor". If you wrote some code to
execute commands using fork() and exec(), that would be another
(trivial) command processor, and using it would satisfy the C
standard. For most purposes it would be less useful than using
/bin/sh or $SHELL.

-- Richard
 
F

Flash Gordon

Richard Tobin wrote, On 07/12/07 00:03:
Unix has several shells available, and no doubt so do many other
operating systems. The standard doesn't specify which one to use - it
just refers to "a command processor". If you wrote some code to
execute commands using fork() and exec(), that would be another
(trivial) command processor, and using it would satisfy the C
standard. For most purposes it would be less useful than using
/bin/sh or $SHELL.

Yes, that is all true. However the OP was specifically talking about
running the program in an environment without a command processor.
 
G

Gordon Burditt

The standard *requires* that the system call pass the parameter to the
Yes, that is all true. However the OP was specifically talking about
running the program in an environment without a command processor.

The minimum needed under UNIX to run a command line is to parse the
command line (possibly just splitting at every white-space character,
with no quoting conventions whatever, and no i/o redirection), form
argv[], and call fork() and some form of exec*(). That arguably
*IS* a command processor (for that matter, you can argue that exec()
alone is a command processor), even if it isn't a separate program.
It might even take less code than the existing system() function
does in the C library.
 
J

James Kuyper

Gordon Burditt wrote:
....
The minimum needed under UNIX to run a command line is to parse the
command line (possibly just splitting at every white-space character,
with no quoting conventions whatever, and no i/o redirection), form
argv[], and call fork() and some form of exec*(). That arguably
*IS* a command processor

It could be a command processor. I don't think it is one; because it
hasn't been written, yet. You could write it pretty easily though.
 
G

Gordon Burditt

The minimum needed under UNIX to run a command line is to parse the
command line (possibly just splitting at every white-space character,
with no quoting conventions whatever, and no i/o redirection), form
argv[], and call fork() and some form of exec*(). That arguably
*IS* a command processor

It could be a command processor. I don't think it is one; because it
hasn't been written, yet. You could write it pretty easily though.

I think something like this, although a little more sophisticated
(like knowing how to strip quotes off of quoted strings), was written
as an optimization. If the command doesn't contain any of certain
shell metacharacters (pipe, I/O redirection, $, *, {}, (), ?, \, and
~ at least would force use of a shell), it would use this instead.
I believe at least alphanumerics, white space, - and / were safe.
Otherwise it would use a shell.

I can't recall what used it, though: I'm thinking it was one of
sendmail, exim, certain versions of make, or perl. I learned about
it because the optimization had some side effect related to environment
variables that was tripping me up somehow.


exec() itself could be considered as a command procesor which accepts
the name of the program to run as the command (no arguments allowed).
Very, very crude, but the standard doesn't require much.
 
K

Keith Thompson

Bob said:
Why does a call to system() work by passing the command line string
supplied to sh -c? This is causing problems when running in a chroot
environment where no sh is available. Wouldn't it be better for C to
replace the system() call by a fork()-exec() pair? Is there some
compiler option that can change the behavior of system()?

The C standard doesn't specify sh -c; it merely requires the string to
be passed to a "command processor". (Also standard C doesn't specify
fork or exec.)

POSIX specifies "sh -c". You might try asking in
comp.unix.programmer. (Note that the job of splitting the command's
arguments on whitespace, handling wildcards, etc. is passed off to the
shell; a function that uses fork/exec directly would have to reproduce
that functionality.)
 
B

Bob

The C standard doesn't specify sh -c; it merely requires the string to
be passed to a "command processor". (Also standard C doesn't specify
fork or exec.)

POSIX specifies "sh -c". You might try asking in
comp.unix.programmer. (Note that the job of splitting the command's
arguments on whitespace, handling wildcards, etc. is passed off to the
shell; a function that uses fork/exec directly would have to reproduce
that functionality.)

But wouldn't it be better to first try "sh -c" and then if no shell is
available fall back to fork-exec, rather than failing the system() call?
 
R

Richard Tobin

But wouldn't it be better to first try "sh -c" and then if no shell is
available fall back to fork-exec, rather than failing the system() call?

Only if the fork()/exec() version does exactly the same thing;
executing the wrong command could be disastrous.

And if it does do exactly the same thing, why not use it in the first
place?

-- Richard
 
G

Gordon Burditt

POSIX specifies "sh -c". You might try asking in
But wouldn't it be better to first try "sh -c" and then if no shell is
available fall back to fork-exec, rather than failing the system() call?

No, unless you've got a *complete* shell emulation in the library, and if
that's the case, why bother loading another program when it's already
loaded? Use the emulation all the time.

fork-exec is missing a critical piece: turning a command string
into a program name and an argv[] array of command arguments. Along
with that go a bunch of other (POSIX) features invoked by the
parsing: I/O redirection, sequential command execution, pipelines,
wildcard expansion, shell variables, argument quoting, etc.

You can do a simple-minded implementation, for example, if the
command consists entirely of alphanumeric characters, white space,
hyphen, and slash, you can get pretty close with a parser that just
breaks arguments at white space, then does fork()/execvp().
This can go horribly wrong if the command intended to use some
of the features not implemented.

Example:
rm -f foo.o ; cc -c foo.c ; cc -o foo foo.o -lbar ; ./foo
when run by the simple-minded parser runs "rm" with lots of arguments
and will end up deleting foo.c, along with complaining about some
probably-nonexistent files.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,767
Messages
2,569,572
Members
45,046
Latest member
Gavizuho

Latest Threads

Top