[comp.lang.c] UNIX commands in C

jtanner@jack.sns.com (Jason Tanner) (04/28/91)

    I have been programming C for a while on IBM compats. using Turbo-C
from Borland. Now I would like to move up to programing C in the UNIX
enviroment. I know how to use all the regular C commands but I would like
to know how to use UNIX commands from within a C program. For example
to have the option to show who is online from within a program. I dont
want this done with a shell escape I want it to be an potion like from a
menu/list.
   Thank you in advance.  Please Email or post all replies.
-- 
|----------------------------------------------------------------------------|
| jtanner@jack.sns.com   -  -   Coke IS it! - -  Ask me, I don't work here   |
|                This space was intentionally  left filled.                  |
|------------------------------Dare to think!--------------------------------|

glenn@curie.ces.cwru.edu (Glenn Crocker) (04/29/91)

jtanner@jack.sns.com (Jason Tanner) writes:
   ...
   I know how to use all the regular C commands but I would like
   to know how to use UNIX commands from within a C program. For example
   to have the option to show who is online from within a program. I dont
   want this done with a shell escape I want it to be an potion like from a
   menu/list.

Check out the popen() function.  It allows you to open a pipe to a
specified program and then read from it or write to it just like a
normal file descriptor (FILE *).  If you'd like an example program
that uses it, mail me.

--
Glenn Crocker                   |  Your milage may vary.
glenn@ces.cwru.edu              |  Light bar not for occupant protection.
CWRU, Cleveland, OH             |  Don't drive on frozen lakes.
W (216)368-6133 H (216)754-1314 |  Do not taunt Happy Fun Ball.

weimer@garden.ssd.kodak.com (Gary Weimer (253-7796)) (04/29/91)

In article <1991Apr28.153127.24926@jack.sns.com> you write:
|> 
|>     I have been programming C for a while on IBM compats. using Turbo-C
|> from Borland. Now I would like to move up to programing C in the UNIX
|> enviroment. I know how to use all the regular C commands but I would like
|> to know how to use UNIX commands from within a C program. For example
|> to have the option to show who is online from within a program. I dont
|> want this done with a shell escape I want it to be an potion like from a
|> menu/list.

Since I don't see any other posts, I'll post and maybe save you some
mail.

The easiest thing to do is use system(3). Since you're new to UNIX, this
means the system command in section 3 of the man pages. If you want to
see the man page entry for this, type 'man 3 system' (without the
quotes).

You might also want to look at fork(2V) (use 'man 2 fork', not 'man 2V fork')
and execl(3V).

weimer@ssd.kodak.com ( Gary Weimer )

shap@shasta.Stanford.EDU (shap) (04/29/91)

In article <GLENN.91Apr29104336@curie.ces.cwru.edu> glenn@curie.ces.cwru.edu (Glenn Crocker) writes:
>
>Check out the popen() function.  It allows you to open a pipe to a
>specified program and then read from it or write to it just like a
>normal file descriptor (FILE *).  If you'd like an example program
>that uses it, mail me.

popen(3) also has an interesting "feature."  It is essentially useless
unless the program you run from it doesn't read from stdin.  Here's
why.

A typical filter works, logically, on the model

	read all input
	process (churn churn)
	write all output

A real UNIX filter, on the other hand, frequently operates on the
model

	read some input
	process it
	write result
	read some more input
	process it
	...

So here's what can happen if you aren't careful:

	1. Parent program does a popen(3), and happily starts writing
	   to the pipe, assuming that it should dump the whole of the
	   input into the child program.

	2. Child program (the filter) processes some input and writes 
	   results to it's output (which the parent will eventually
	   read).

	3. Pipe buffer on the child output fills, because the parent
	   hasn't done any reads of the results yet.

	4. Child blocks, trying to write output to the pipe.

	5. Pipe buffer on the child input fills, because the child is
	   no longer reading it.

	6. Parent blocks.

Et Voilla! instant deadlock.  For those of you who don't believe this
happens, talk to some people who have had to port various window
managers.

Now here's the bad news.  To work around this limitation, you either
have to

	1. Know the pipe buffer size and operate accordingly
	2. Use asynchronous IO and buffer up the return stuff yourself

both of which are machine dependent (buf size clearly, not all
machines have asynch I/O).

The portable solution is to check how many bytes are writeable to the
pipe at any given time and if there aren't enough switch to buffering
up the input for a while to allow the child to run.  Needless to say,
this solution is UGLY.

Expert quiz question for the week: write a version of popen(3) that
doesn't have this feature.

[This problem was first brought to my attention by Mike Bianchi.  I
think he may have been the author of popen(3).]


Jonathan Shapiro

torek@elf.ee.lbl.gov (Chris Torek) (04/30/91)

(Having just written a new man page for popen, I cannot resist following
up, even though I should be off getting some food....)

In article <174@shasta.Stanford.EDU> shap@shasta.Stanford.EDU (shap) writes:
>popen(3) also has an interesting "feature." ... Et Voilla! instant deadlock.

This cannot happen directly because of popen().  The reason is
trivially simple: popen opens either for reading or for writing, never
both.  Thus popen() is unable to create a loop, and deadlock occurs
only in the presence of a pipe loop.  (The situation described in `...'
is the simplest case, where A writes to B and then reads back from B:
A->B->A.  The problem also occurs in longer loops such as A->B->C->D->A.)

>For those of you who don't believe this happens, talk to some people
>who have had to port various window managers.

These generally use bidirectional entities such as ptys and must
therefore provide their own synchronization mechanisms.
-- 
In-Real-Life: Chris Torek, Lawrence Berkeley Lab CSE/EE (+1 415 486 5427)
Berkeley, CA		Domain:	torek@ee.lbl.gov

ron@well.sf.ca.us (Ronald Hayden) (05/01/91)

jtanner@jack.sns.com (Jason Tanner) writes:


>    I have been programming C for a while on IBM compats. using Turbo-C
>from Borland. Now I would like to move up to programing C in the UNIX
>enviroment. I know how to use all the regular C commands but I would like
>to know how to use UNIX commands from within a C program. For example
>to have the option to show who is online from within a program. I dont
>want this done with a shell escape I want it to be an potion like from a
>menu/list.
>   Thank you in advance.  Please Email or post all replies.
>-- 

There are actually several ways to do this (check out the book C
Programming In a UNIX Environment for a complete discussion), but for
a simple command such as "who", you can simply:


#include <stdio.h>

main ()
{
 printf("\nTesting the UNIX 'who' command --\n");
 system("who");
 printf("\nDone.\n");
 exit(1);
}

rearl@gnu.ai.mit.edu (Robert Earl) (05/01/91)

In article <24527@well.sf.ca.us> ron@well.sf.ca.us (Ronald Hayden) writes:
|
|   There are actually several ways to do this (check out the book C
|   Programming In a UNIX Environment for a complete discussion), but for
|   a simple command such as "who", you can simply:
|
|
|   #include <stdio.h>
|
|   main ()
|   {
|    printf("\nTesting the UNIX 'who' command --\n");
|    system("who");
|    printf("\nDone.\n");
|    exit(1);
|   }

Since system() [and popen()] does an implicit fork, it's good practice
to explicitly flush output buffers before you call this routine;
otherwise you end up with possibly duplicated or misleading output.
For instance, here's what I get when I redirect the output of this
program to a file (block buffered):

	guest    ttyp1    May  1 09:59 (192.35.86.25)
	rearl    ttyp4    May  1 11:05 (dialin.ucsd.edu)
	ggray    ttyp6    May  1 10:43 (geech)
	guest    ttyp8    May  1 11:20 (192.35.86.26)
	
	Testing the UNIX 'who' command --
	
	Done.
	
Probably not what you intended.  Add "fflush(stdout)" or
"fflush(NULL)" (to flush all buffers) before calling system().  I
think this is a FAQ in one group or another...

By the way, any reason why this program was made to return failure?

--robert
rearl@gnu.ai.mit.edu
rearl@watnxt3.ucr.edu

mike@bria.UUCP (Michael Stefanik) (05/02/91)

In article <174@shasta.Stanford.EDU> you write:
|1. Parent program does a popen(3), and happily starts writing
|   to the pipe, assuming that it should dump the whole of the
|   input into the child program.
|
| [ etc, etc on how the process will eventually block ]

The popen() will either create the pipe for reading *or* for writing,
not both.  Therefore, unless your popened command does some pipelining
or is blocked on a device, the deadlock situation that you describe
cannot occur. (ie: if the parent popens a filter for writing, the parent
may only write and never read on the pipe, while the child may only read
and never write on the pipe).

Michael Stefanik, MGI Inc, Los Angeles | Opinions stated are never realistic
Title of the week: Systems Engineer    | UUCP: ...!uunet!bria!mike
-------------------------------------------------------------------------------
If MS-DOS didn't exist, who would UNIX programmers have to make fun of?

shap@shasta.Stanford.EDU (shap) (05/03/91)

Save postage - the function I was thinking of was a local hack.  Not
popen(3).  sorry for the confusion

imc@prg.ox.ac.uk (Ian Collier) (05/04/91)

In article <REARL.91May1113957@nutrimat.gnu.ai.mit.edu>, rearl@gnu.ai.mit.edu (Robert Earl) wrote:
>In article <24527@well.sf.ca.us> ron@well.sf.ca.us (Ronald Hayden) writes:
>|   #include <stdio.h>
>|
>|   main ()
>|   {
>|    printf("\nTesting the UNIX 'who' command --\n");
>|    system("who");
>|    printf("\nDone.\n");
>|    exit(1);
>|   }

>Since system() [and popen()] does an implicit fork, it's good practice
>to explicitly flush output buffers before you call this routine;

[etc]

Also, that should really be "/bin/who" rather than just "who", unless
you are going to set the path explicitly in the program. Otherwise the
program could break on someone else's machine if they do not have /bin
in their path (unlikely) or if some other random program called "who"
appears before /bin/who in the path. If you do this in an suid program
be absolutely certain to specify the path, or else this creates a
security loophole.

Ian Collier
Ian.Collier@prg.ox.ac.uk | imc@ecs.ox.ac.uk

subbarao@phoenix.Princeton.EDU (Kartik Subbarao) (05/09/91)

In article <751.imc@uk.ac.ox.prg> imc@prg.ox.ac.uk (Ian Collier) writes:
>In article <REARL.91May1113957@nutrimat.gnu.ai.mit.edu>, rearl@gnu.ai.mit.edu (Robert Earl) wrote:
>>In article <24527@well.sf.ca.us> ron@well.sf.ca.us (Ronald Hayden) writes:
>>|   #include <stdio.h>
>>|
>>|   main ()
>>|   {
>>|    printf("\nTesting the UNIX 'who' command --\n");
>>|    system("who");
>>|    printf("\nDone.\n");
>>|    exit(1);
>>|   }

>Also, that should really be "/bin/who" rather than just "who", unless
>you are going to set the path explicitly in the program. Otherwise the
>program could break on someone else's machine if they do not have /bin
>in their path (unlikely) or if some other random program called "who"

>appears before /bin/who in the path. If you do this in an suid program
>be absolutely certain to specify the path, or else this creates a
>security loophole.

Ha! Using system() in any setuid program itself, regardless of how you invoke
the program, leaves a major security hole.


			-Kartik

--
internet% ypwhich

subbarao@phoenix.Princeton.EDU -| Internet
kartik@silvertone.Princeton.EDU (NeXT mail)  
SUBBARAO@PUCC.BITNET			          - Bitnet

les@chinet.chi.il.us (Leslie Mikesell) (05/11/91)

In article <azXkYHbe/UUYI@idunno.Princeton.EDU> subbarao@phoenix.Princeton.EDU (Kartik Subbarao) writes:

>Ha! Using system() in any setuid program itself, regardless of how you invoke
>the program, leaves a major security hole.

In what way is doing a fork() and having the child do a setuid(getuid())
before the system() call any less secure than it would be if the program
were not setuid?  Some unix versions offer less drastic ways to do it,
but that way should work even from a setuid root program under SysV.

Les Mikesell
  les@chinet.chi.il.us