Capturing output from execv() [duplicate] - c

I'm trying to write a C program that grabs command output and then i'll be passing that to another program.
I'm having an issue, I cant work out how to get the command output and store it. Below is a sample of what I have
if(fork() == 0){
execl("/bin/ls", "ls", "-1", (char *)0);
/* do something with the output here */
}
else{
//*other stuff goes here*
}
so basically im wondering if there is any way i can get the output from the "execl" and pass it to some thing else (e.g. via storing it in some kind of buffer).
Suggestions would be great.

You have to create a pipe from the parent process to the child, using pipe().
Then you must redirect standard ouput (STDOUT_FILENO) and error output (STDERR_FILENO) using dup or dup2 to the pipe, and in the parent process, read from the pipe.
It should work.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define die(e) do { fprintf(stderr, "%s\n", e); exit(EXIT_FAILURE); } while (0);
int main() {
int link[2];
pid_t pid;
char foo[4096];
if (pipe(link)==-1)
die("pipe");
if ((pid = fork()) == -1)
die("fork");
if(pid == 0) {
dup2 (link[1], STDOUT_FILENO);
close(link[0]);
close(link[1]);
execl("/bin/ls", "ls", "-1", (char *)0);
die("execl");
} else {
close(link[1]);
int nbytes = read(link[0], foo, sizeof(foo));
printf("Output: (%.*s)\n", nbytes, foo);
wait(NULL);
}
return 0;
}

Open a pipe, and change stdout to match that pipe.
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int pipes[2];
pipe(pipes); // Create the pipes
dup2(pipes[1],1); // Set the pipe up to standard output
After that, anything which goes to stdout,(such as through printf), comes out pipe[0].
FILE *input = fdopen(pipes[0],"r");
Now you can read the output like a normal file descriptor. For more details, look at this

Thanks Jonathan Leffler, and i optimize the above code for it can't read all response for one time.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#define die(e) do { fprintf(stderr, "%s\n", e); exit(EXIT_FAILURE); } while (0);
int main() {
int link[2];
pid_t pid;
char foo[4096 + 1];
memset(foo, 0, 4096);
if (pipe(link)==-1)
die("pipe");
if ((pid = fork()) == -1)
die("fork");
if(pid == 0) {
dup2 (link[1], STDOUT_FILENO);
close(link[0]);
close(link[1]);
execl("/bin/ls", "ls", "-1", (char *)0);
die("execl");
} else {
close(link[1]);
int nbytes = 0;
std::string totalStr;
while(0 != (nbytes = read(link[0], foo, sizeof(foo)))) {
totalStr = totalStr + foo;
printf("Output: (%.*s)\n", nbytes, foo);
memset(foo, 0, 4096);
}
wait(NULL);
}
return 0;
}

If you want the output in a string (char *), here's an option (for Linux at least):
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/uio.h>
#include <sys/wait.h>
#include <unistd.h>
char* qx(char** cmd, int inc_stderr) {
int stdout_fds[2];
pipe(stdout_fds);
int stderr_fds[2];
if (!inc_stderr) {
pipe(stderr_fds);
}
const pid_t pid = fork();
if (!pid) {
close(stdout_fds[0]);
dup2(stdout_fds[1], 1);
if (inc_stderr) {
dup2(stdout_fds[1], 2);
}
close(stdout_fds[1]);
if (!inc_stderr) {
close(stderr_fds[0]);
dup2(stderr_fds[1], 2);
close(stderr_fds[1]);
}
execvp(*cmd, cmd);
exit(0);
}
close(stdout_fds[1]);
const int buf_size = 4096;
char* out = malloc(buf_size);
int out_size = buf_size;
int i = 0;
do {
const ssize_t r = read(stdout_fds[0], &out[i], buf_size);
if (r > 0) {
i += r;
}
if (out_size - i <= 4096) {
out_size *= 2;
out = realloc(out, out_size);
}
} while (errno == EAGAIN || errno == EINTR);
close(stdout_fds[0]);
if (!inc_stderr) {
close(stderr_fds[1]);
do {
const ssize_t r = read(stderr_fds[0], &out[i], buf_size);
if (r > 0) {
i += r;
}
if (out_size - i <= 4096) {
out_size *= 2;
out = realloc(out, out_size);
}
} while (errno == EAGAIN || errno == EINTR);
close(stderr_fds[0]);
}
int r, status;
do {
r = waitpid(pid, &status, 0);
} while (r == -1 && errno == EINTR);
out[i] = 0;
return out;
}
int main() {
char* argv[3];
argv[0] = "ls";
argv[1] = "-la";
argv[2] = NULL;
char* out = qx(argv, 0);
printf("%s", out);
free(out);
}

Related

C program hangs after fork

I have made the following program :
The aim is to make 5 child processes and have parent process send a string to each child process
to print. If I pass the argument xyz then this program prints xyz 2 times and then hangs.
Not sure why that is happening.
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char *argv[])
{
int num_processes = 5;
int pipefd[num_processes][2];
pid_t cpid;
char buf;
if (argc != 2)
{
fprintf(stderr, "Usage: %s <string>\n", argv[0]);
exit(EXIT_FAILURE);
}
for (int i = 0; i < num_processes; i++)
{
if (pipe(pipefd[i]) == -1)
{
perror("pipe");
exit(EXIT_FAILURE);
}
}
for (int i = 0; i < num_processes; i++)
{
cpid = fork();
if (cpid == -1)
{
perror("fork");
exit(EXIT_FAILURE);
}
if (cpid == 0)
{ /* Child reads from pipe */
close(pipefd[i][1]); /* Close unused write end */
while (read(pipefd[i][0], &buf, 1) > 0)
write(STDOUT_FILENO, &buf, 1);
write(STDOUT_FILENO, "\n", 1);
close(pipefd[i][0]);
}
else
{ /* Parent writes argv[1] to pipe */
close(pipefd[i][0]); /* Close unused read end */
write(pipefd[i][1], argv[1], strlen(argv[1]));
close(pipefd[i][1]); /* Reader will see EOF */
wait(NULL); /* Wait for child */
}
}
_exit(EXIT_SUCCESS);
}

PseudoTerminal C - How To

I am currently trying to make an application that will receive information from a client and send it to be executed in a server. For that I need to use PseudoTerminals.
I followed the example from a book and I ended up with this code
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
int ptyMasterOpen(char *slavName, size_t snLen)
{
int masterFd, savedErrno;
char *p;
masterFd = posix_openpt(O_RDWR | O_NOCTTY);
printf("Master: %d \n", masterFd);
if(masterFd == -1)
return -1;
if(grantpt(masterFd) == -1 || unlockpt(masterFd) == -1)
{
savedErrno = errno;
close(masterFd);
errno = savedErrno;
return -1;
}
p = ptsname(masterFd); /*Get the name of the of the slave*/
if(p == NULL)
{
savedErrno = errno;
close(masterFd);
errno = savedErrno;
return -1;
}
printf("Slave: %s \n", p);
//*
if(strlen(p) < snLen)
{
strncpy(slavName, p, snLen);
}
else
{
close(masterFd);
errno = EOVERFLOW;
return -1;
}
//*/
return masterFd;
}
pid_t ptyFork(int *masterFd, char *slavName, size_t snLen)
{
int mfd, slaveFd, saveErrno;
pid_t childPid;
char slname[MAX_SNAME];
char cIn[100], cOut[100];
int out = -1, in = -1;
mfd = ptyMasterOpen(slname, MAX_SNAME);
if(mfd == -1)
return -1;
if(slavName != NULL)
{
if(strlen(slname) < snLen)
{
strncpy(slavName, slname, snLen);
}
else
{
close(mfd);
errno = EOVERFLOW;
return -1;
}
}
childPid = fork();
if(childPid == -1)
{
saveErrno = errno;
close(mfd);
errno = saveErrno;
return -1;
}
if(childPid != 0)
{
*masterFd = mfd;
return childPid;
}
close(mfd);
slaveFd = open(slname, O_RDWR);
if(slaveFd != -1)
{
sprintf(cOut, "/tmp/out%d", slaveFd);
sprintf(cIn, "/tmp/in%d", slaveFd);
mkfifo(cOut, S_IRUSR|S_IWUSR);
out = open(cOut, O_RDWR|O_TRUNC|O_NONBLOCK);
mkfifo(cIn, S_IRUSR|S_IWUSR);
in = open(cIn, O_RDONLY|O_TRUNC);dup2(out, STDERR_FILENO);
dup2(out, STDOUT_FILENO);
dup2(in, STDIN_FILENO);
}
else
{
printf("Problem with slave\n\r");
fflush(stdout);
}
//*
if(dup2(slaveFd, STDIN_FILENO) != STDIN_FILENO || dup2(slaveFd, STDERR_FILENO) != STDERR_FILENO || dup2(slaveFd, STDOUT_FILENO) != STDOUT_FILENO)
{
printf("Error on duplicating Files");
close(slaveFd);
exit(0);
}
else
{
dup2(out, STDERR_FILENO);
dup2(out, STDOUT_FILENO);
dup2(in, STDIN_FILENO);
}
//*/
return slaveFd;
}
The problem is:
sprintf(cOut, "/tmp/out%d", childPid);
sprintf(cIn, "/tmp/in%d", childPid);
always return 0 even when I print them in main and they are values like 952...
also using echo "ls" > "/tmp/inxx" and cat /tmp/outxx does nothing.
I believe the problem is when the fork is done, the variables lose their values or something, because, it prints the error messages now, without the while(1) on main
int main(int argc, const char * argv[])
{
// insert code here...
int masterFd;
pid_t slaveFd;
char slavName[MAX_SNAME];
slaveFd = ptyFork(&masterFd, slavName, MAX_SNAME);
//close(masterFd);
//while(1);
return 0;
}
Can anyone help?
Thanks

linux - exec'd program not terminating

I am writing program in C on Linux which has to fork 2 children.
First child will send two random numbers over pipe to the second child. It will listen for SIGUSR1 signal and will then terminate.
The second child will duplicate(dup2) pipe input as STDIN and file fp as STDOUT. It will then execl program which will print out some data according to its input and end.
My problem is, that the execl'd program will never terminate and I don't know why. Any help or tips will be appreciated.
main.c (parent):
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/types.h>
const int BUFFER_SIZE = 30;
int pipefd[2] = {0,0};
int parent_pid = 0;
int first_pid = 0;
int second_pid = 0;
int sleep_time = 5;
int debug = 0;
FILE *fp;
void parent_func() {
int wstatus = 0;
sleep(sleep_time);
kill(first_pid, SIGUSR1);
wait(&wstatus);
waitpid(second_pid, &wstatus, 0);
}
static void sigusr1_handler(int sig) {
if (sig == SIGUSR1) {
fputs("TERMINATED", stderr);
close(pipefd[1]);
exit(0);
}
}
void first_func() {
struct sigaction act;
char buffer[BUFFER_SIZE];
close(pipefd[0]);
memset(&act, '\0', sizeof(act)); // clear the sigaction struct
act.sa_handler = &sigusr1_handler; // sets function to run on signal
if (sigaction(SIGUSR1, &act, NULL) < 0) { // assign sigaction
fputs("cannot assign sigaction - exiting...", stderr);
exit(1);
}
while (1) {
sprintf(buffer, "%d %d\n", rand(), rand());
write(pipefd[1], buffer, strlen(buffer));
puts(buffer);
sleep(1);
}
}
void second_func() {
close(pipefd[1]);
fp = fopen("out.txt", "w");
char buf[30];
dup2(pipefd[0], STDIN_FILENO);
close(pipefd[0]);
//dup2(fileno(fp), STDOUT_FILENO);
execl("./test", "", NULL);
perror("Error");
}
int main(int argc, char *argv[]) {
int fork_val = 0;
parent_pid = getpid();
if (pipe(pipefd)) {
fputs("cannot create pipe - exiting...", stderr);
return 1;
}
if (debug) {
sleep_time *= 10;
}
if ((fork_val = fork()) == -1) {
fputs("cannot fork process - exiting...", stderr);
return 1;
} else if (fork_val == 0) {
first_func();
} else {
first_pid = fork_val;
if ((fork_val = fork()) == -1) {
fputs("cannot fork process - exiting...", stderr);
return 1;
} else if (fork_val == 0) {
second_func();
} else {
second_pid = fork_val;
parent_func();
}
}
fclose(fp);
exit(0);
}
test.c (the execl'd file):
#include "nd.h"
#include "nsd.h"
#include <stdio.h>
#include <stdlib.h>
int main() {
int num1 = 0;
int num2 = 0;
char buffer[100];
while (fgets(buffer, 100, stdin) != NULL) {
if (sscanf(buffer, "%d %d", &num1, &num2) == 2) {
(num1 < 0) ? num1 = (num1 * -1) : num1;
(num2 < 0) ? num2 = (num2 * -1) : num2;
if (num1 == 1 || num2 == 1) {
puts("1");
} else if (num1 == num2) {
if (nd(num1) == 1) {
puts("prime");
} else {
printf("%d\n", num1);
}
} else if (nd(num1) == 1 && nd(num2) == 1) {
puts("prime");
} else {
printf("%d\n", nsd(num1, num2));
}
} else {
fputs("error\n", stderr);
}
}
fputs("DONE", stderr);
exit(0);
}
To be able to detect an end of file from a pipe you need to read from a empty pipe with no writer (no process with an open for writing descriptor).
As your writer (first_func()) never closes its descriptor and always writes something in a never ending loop the reader will either wait for some data or read some data.
Be also careful about closing non useful descriptors, if not you may encounter some problems with pipes, such has a single process that is a reader and a writer, so being unable to detect the end of file...

Can I use STDIN for IPC?

Can I use standard input for interprocess communication? I wrote the following gnu c code as an experiment, but the program hangs waiting for input after printing the character defined as val. Neither a newline nor fflush in the sending process seem to alleviate the problem.
#include <unistd.h>
#include <stdio.h>
int main(void) {
char val = '!';
int proc = fork();
if (proc < 0)
return -1;
if (proc == 0) {
write(0, &val, 1);
return 0;
}
else {
char ch[2] = { 0 };
read(0, ch, 1);
printf("%s\n", ch);
return 0;
}
return -2;
}
You can use pipe for IPC. Now if you want to use STDIN_FILENO and STDOUT_FILENO it would look like this:
#include <unistd.h>
#include <stdio.h>
int main(void) {
char val = '!';
int filedes[2];
pipe(filedes);
int proc = fork();
if (proc < 0)
return -1;
if (proc == 0) {
close(1);
dup(filedes[1]);
close(filedes[0]);
close(filedes[1]);
write(1, &val, 1);
return 0;
}
else {
char ch[2] = { 0 };
close(0);
dup(filedes[0]);
close(filedes[0]);
close(filedes[1]);
read(0, ch, 1);
printf("%s\n", ch);
return 0;
}
return -2;
}
Combination close(x) and dup(filedes[x]) closes STDOUT/STDIN makes copy of filedes[x] into first available descriptor, what you just closed. As suggested by Jonathan example is now closing both filedes ends and without any doubts is using STDIN/STDOUT.

making shell for homework

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
void exec(char **args){
pid_t pid;
int status;
if ((pid = fork()) < 0) {
printf("*** ERROR: forking child process failed\n");
exit(1);
}
else if (pid == 0) {
if(execvp(args[0],args)<0)//{
//printf("argv[0]=%s argv[1]=%s",args[0],args[1]);
printf("**error in exec\n");
}
else {
while (wait(&status) != pid);
}
}
void exec2(char **args, char *file){
printf("file =%s\n",file);
int fd;
pid_t pid;
int status;
if ((pid = fork()) < 0) {
printf("*** ERROR: forking child process failed\n");
exit(1);
}
else if (pid == 0) {
fd = open(file, O_RDWR | O_CREAT, (mode_t)0600);
close(1);
dup2(fd, 1);
if(execvp(args[0],args)<0){
printf("**error in exec");
}
else {
printf("\nhere\n");
close(fd);
while (wait(&status) != pid){
fflush(stdout) ;
}
}
}
close (fd);
}
void main(){
char *command;
char inp[512];
char *filepath;
size_t size=0;
char *substr;
char *args[512];
command = (char *) malloc(sizeof(char *) * 512);
int flag=0;
int redirect=0;
int i=0;
while (1){
printf("$ ");
command = fgets(inp, 512/*sizeof(char *)*/, stdin);
command[strlen(command)-1]='\0';
if (strchr(command,'>')){
redirect=1;
strtok_r(command,">",&filepath);
}
size_t siz=4;
//printf("command=%s\n",command);
int i=0;
while(1){
//printf("i=%d\n",i);
char *tok = strtok_r(command," ",&substr);
if (tok==NULL){
break;
}
args[i++] = tok;
/* printf("tok=%s\n",tok);
printf("len tok = %d\n",(int)strlen(tok));
printf("command=%s\n",command);
printf("substr=%s\n",substr);
*/ command = substr;
}
//printf("args[0]=%s",args[0]);
if (!strncasecmp(args[0],"exit",siz) || !strncasecmp(args[0],"quit",siz))
{
printf("\nBye\n");
exit(0);
}
else if(strcmp(args[0],"cd")==0){
chdir(args[1]);
//printf("chdir") ;
//system("pwd");
}
else if (redirect==1){
exec2(args,filepath);
}
else exec(args);
}
}
Okay this is my code for my shell. When i run it, i put ls and it gives correct output. Then i put ls -l and then ls again and it gives
ls: cannot access : No such file or directory
Also when i use cd, ls doesnt give output and pwd says "ignoring unused arguments"
ALso cat doesnt work.
Though mkdir, ps and ls -l works.
Don't close stdout!
Do it like this, after the fork and before the exec:
if (child) {
int fd = open(file, O_RDWR | O_CREAT, (mode_t)0600);
close(1);
dup2(fd, 1);
if(execvp(args[0],args)<0){
printf("**error in exec");
}
}

Resources