I have this function that executes a command
int cmd2(char * const *cmd, char * std_out)
{
char tmp[4096];
int pipefds[2], r, status, x;
pid_t pid;
if (pipe(pipefds) == -1){
return -1;
}
if ( (pid = fork()) == -1){
return -1;
}
if (pid == 0)
{
dup2(pipefds[1], STDOUT_FILENO);
dup2(piepfds[1], STDERR_FILENO);
close(pipefds[0]);
close(pipefds[1]);
execvp(cmd[0] , cmd);
}
else
{
close(pipefds[1]);
x = read(pipefds[0], tmp, 4096);
printf("Got %d bytes\n",x);
wait(NULL);
}
return 0;
}
the error message that should be outputted
┌──(kali㉿kali)-[~/]
└─$ ./wow
zsh: no such file or directory: ./wow
when running another c code that fputs a buffer into stderr it is display by the cmd2 without any problems
I tried to redirect stderr to stdout using 2>&1 but this does not seem to have an effect
how to read any/all results from executing the command
If you try running a non-existent command with cmd2, execvp will set errno and return. It will not print anything anywhere. You need to check errno and print the error. Something as simple as:
execvp(cmd[0], cmd);
perror(cmd[0]);
exit(EXIT_FAILURE);
should usually suffice. zsh does something like that when you try to run a non-existent command with it, that's why you see an error message.
Note however that it is the child process that prints the message. You discard child process output and only print its length. You need to do something about it if you want to see the message.
Related
I'm trying to create a very basic telnet server to practice memory corruption exploits. When I try to issue a command, in the first iteration, nothing happens. Second iteration I am getting multiple bad file descriptor errors printing on my server side. On the client side, everything seems ok. I get all the required prompts. Here's my relevant code:
int piper[2];
pipe(piper);
...
while (1) {
n = write(newsockfd,"Enter a command...\n",21);
if (n < 0) error("ERROR writing to socket");
bzero(buffer,4096);
n = read(newsockfd,buffer,4095);
strcpy(command, buffer);
pid_t childpid;
childpid = fork();
if(childpid == -1) {
perror("Failed to fork");
return 1;
}
if(childpid == 0) { //child
printf("I am child %ld\n", (long)getpid());
if(dup2(piper[1], 1) < 0) {
perror("Failed to pipe in child process");
}
else {
close(piper[0]);
close(piper[1]);
char *args[] = {command, NULL};
execve(command, args, NULL);
}
}
else { // parent
if(dup2(piper[0], 0) < 0) {
perror("Failed to pipe in parent process");
}
else {
// read command output from child
while(fgets(command_out, sizeof(command_out), stdin)) {
printf("%s", command_out);
}
}
}
}
If I enter /bin/ls into my client, I get the following outputted onto my server:
I am child 26748
2nd time I do it, I get the following outputted to my server:
Failed to pipe in parent process: Bad file descriptor
0I am child 26749
Failed to pipe in child process: Bad file descriptor
There's a possibility that closing the pipe in the child process closes it in the parent process also. Consider moving your piper(pipe) in the beginning of the while loop. And to be safe, close the pipe at the end of the file loop not forgetting to test the return value of close.
Actually read puts a newline character at the end of input so your command could be for example testprog but in reality, when using read(), it is testprog\n so you have to get rid of the newline added or execve() will expect a program name with a newline in it.
#define STDIN 0
int n = read(STDIN, command, 4096);
command[n - 1] = '\0'; // get rid of newline
char *args = { command, NULL };
execve(buf, &args[0], NULL);
I'll post my code first, then explain the problem I'm having:
#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#define MAX_ARGS 20
#define BUFSIZE 1024
int get_args(char* cmdline, char* args[])
{
int i = 0;
/* if no args */
if((args[0] = strtok(cmdline, "\n\t ")) == NULL)
return 0;
while((args[++i] = strtok(NULL, "\n\t ")) != NULL) {
if(i >= MAX_ARGS) {
printf("Too many arguments!\n");
exit(1);
}
}
/* the last one is always NULL */
return i;
}
void execute(char* cmdline)
{
int pid, async, oneapp;
char* args[MAX_ARGS];
char* args2[] = {"-l", NULL};
int nargs = get_args(cmdline, args);
if(nargs <= 0) return;
if(!strcmp(args[0], "quit") || !strcmp(args[0], "exit")) {
exit(0);
}
printf("before the if\n");
printf("%s\n",args[nargs - 2]);
int i = 0;
// EDIT: THIS IS WHAT WAS SUPPOSED TO BE COMMENTED OUT
/*
while (args[i] != ">" && i < nargs - 1) {
printf("%s\n",args[i]);
i++;
}
*/
// Presence of ">" token in args
// causes errors in execvp() because ">" is not
// a built-in Unix command, so remove it from args
args[i - 1] = NULL;
printf("Escaped the while\n");
// File descriptor array for the pipe
int fd[2];
// PID for the forked process
pid_t fpid1;
// Open the pipe
pipe(fd);
// Here we fork
fpid1 = fork();
if (fpid1 < 0)
{
// The case where the fork fails
perror("Fork failed!\n");
exit(-1);
}
else if (fpid1 == 0)
{
//dup2(fd[1], STDOUT_FILENO);
close(fd[1]);
//close(fd[0]);
// File pointer for the file that'll be written to
FILE * file;
// freopen() redirects stdin to args[nargs - 1],
// which contains the name of the file we're writing to
file = freopen(args[nargs - 1], "w+", stdin);
// If we include this line, the functionality works
//execvp(args[0],args);
// We're done writing to the file, so close it
fclose(file);
// We're done using the pipe, so close it (unnecessary?)
//close(fd[1]);
}
else
{
// Wait for the child process to terminate
wait(0);
printf("This is the parent\n");
// Connect write end of pipe (fd[1]) to standard output
dup2(fd[1], STDOUT_FILENO);
// We don't need the read end, so close it
close(fd[0]);
// args[0] contains the command "ls", which is
// what we want to execute
execvp(args[0], args);
// This is just a test line I was using before to check
// whether anything was being written to stdout at all
printf("Exec was here\n");
}
// This is here to make sure program execution
// doesn't continue into the original code, which
// currently causes errors due to incomplete functionality
exit(0);
/* check if async call */
printf("Async call part\n");
if(!strcmp(args[nargs-1], "&")) { async = 1; args[--nargs] = 0; }
else async = 0;
pid = fork();
if(pid == 0) { /* child process */
execvp(args[0], args);
/* return only when exec fails */
perror("exec failed");
exit(-1);
} else if(pid > 0) { /* parent process */
if(!async) waitpid(pid, NULL, 0);
else printf("this is an async call\n");
} else { /* error occurred */
perror("fork failed");
exit(1);
}
}
int main (int argc, char* argv [])
{
char cmdline[BUFSIZE];
for(;;) {
printf("COP4338$ ");
if(fgets(cmdline, BUFSIZE, stdin) == NULL) {
perror("fgets failed");
exit(1);
}
execute(cmdline) ;
}
return 0;
}
So, what's the problem? Simple: the code above creates a file with the expected name, i.e. the name provided in the command line, which gets placed at args[nargs - 1]. For instance, running the program and then typing
ls > test.txt
Creates a file called test.txt... but it doesn't actually write anything to it. I did manage to get the program to print garbage characters to the file more than a few times, but this only happened during bouts of desperate hail mary coding where I was basically just trying to get the program to write SOMETHING to the file.
I do think I've managed to narrow down the cause of the problems to this area of the code:
else if (fpid1 == 0)
{
printf("This is the child.\n");
//dup2(fd[1], STDOUT_FILENO);
close(fd[1]);
//close(fd[0]);
// File pointer for the file that'll be written to
FILE * file;
// freopen() redirects stdin to args[nargs - 1],
// which contains the name of the file we're writing to
file = freopen(args[nargs - 1], "w+", stdout);
// If we include this line, the functionality works
//execvp(args[0],args);
// We're done writing to the file, so close it
fclose(file);
// We're done using the pipe, so close it (unnecessary?)
//close(fd[1]);
}
else
{
// Wait for the child process to terminate
wait(0);
printf("This is the parent\n");
// Connect write end of pipe (fd[1]) to standard output
dup2(fd[1], STDOUT_FILENO);
// We don't need the read end, so close it
close(fd[0]);
// args[0] contains the command "ls", which is
// what we want to execute
execvp(args[0], args);
// This is just a test line I was using before to check
// whether anything was being written to stdout at all
printf("Exec was here\n");
}
More specifically, I believe the problem is with the way I'm using (or trying to use) dup2() and the piping functionality. I basically found this out by process of elimination. I spent a few hours commenting things out, moving code around, adding and removing test code, and I've found the following things:
1.) Removing the calls to dup2() and using execvp(args[0], args) prints the result of the ls command to the console. The parent and child processes begin and end properly. So, the calls to execvp() are working properly.
2.) The line
file = freopen(args[nargs - 1], "w+", stdout)
Successfully creates a file with the correct name, so the call to freopen() isn't failing. While this doesn't immediately prove that this function is working properly as it's written now, consider fact #3:
3.) In the child process block, if we make freopen redirect to the output file from stdin (rather than stdout) and uncomment the call to execvp(args[0], args), like so:
// freopen() redirects stdin to args[nargs - 1],
// which contains the name of the file we're writing to
file = freopen(args[nargs - 1], "w+", stdin);
// If we include this line, the functionality works
execvp(args[0],args);
and run the program, then it works and result of the ls command is successfully written to the output file. Knowing this, it seems pretty safe to say that freopen() isn't the problem either.
In other words, the only thing I haven't been able to successfully do is pipe the output of the execvp() call that's done in the parent process to stdout, and then from stdout to the file using freopen().
Any help is appreciated. I've been at this since 10 AM yesterday and I'm completely out of ideas. I just don't know what I'm doing wrong. Why isn't this working?
In my C program, I am creating a child process and running execvp in the child. But I'm trying to change the error message to something else, for the execvp command (if there was an error).
I know that if it returned, then it was an error, then I can print my own custom error message on the next line. That's one type of error that can occur, for example this happens if I give the command "sdfsd" to execvp. This part is working for me.
But if I type, "find sdfsd" then it does not return and prints "find: `sdfsd': No such file or directory".
I want to change this message (and essentially any kind of error message coming from exevcp) to my own custom one.
I believe I can use dup2 to do this, but I'm not sure how...
In the child process I tried
dup2(STDERR_FILENO, 1);
fclose(stderr);
But this just stops the child process from writing any error messages. I still can't print my own message in all cases..
Does anyone know how to do this?
thanks
Since execvp never returns if it successfully starts the new program, you won't be able to print your own error message in the child process after the program run by execvp fails. One option would be to pipe stderr to the parent process, intercept the error message there, and then print your own error message.
For example:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char **argv)
{
int ff, p[2];
FILE *f;
char *vv[] = {"find", "garbage", (char *)NULL};
char msg[100];
if (pipe(p) != 0)
{
fprintf(stderr, "Pipe failed\n");
return 1;
}
if ((ff = fork()) == -1 )
{
fprintf(stderr, "Fork failed\n");
return 1;
}
if (ff == 0)
{
/* In the child process */
close(2);
close(p[0]);
dup2(p[1], 2);
execvp("find", vv);
return 1;
};
/* In the parent process */
close(p[1]);
f = fdopen(p[0], "r");
if (f == NULL)
{
fprintf(stderr, "Fdopen failed\n");
return 1;
}
if (fgets(msg, sizeof(msg), f) == NULL)
{
fprintf(stderr, "Fgets failed\n");
return 1;
}
printf("Error message was: %s", msg);
/* and so on */
return 0;
}
int handleCommand(char *command) {
pid_t pid;
pid = fork();
if (pid > 0) {
sleep(0.5);
} else if (pid == 0) {
execCommand(command);
//strcat(path[0], command);
//printf("%s", path[0]);
//execve(path[0], path, NULL);
//printf("\n");
} else {
printf("ERROR");
}
}
int execCommand(char *command) {
char* path[] = {"/bin/", NULL};
printf("in execCommand > %s", command );
strcat(path[0], command);
execve(path[0], path, NULL);
printf("\n");
}
at first as you can see i put the code directly in the function where the fork takes place. This would display the output expected when executing the ls command for example. But when I moved this to a function i no longer see any output.
First note that the printf() operations after the execve() are never executed if the command is executed - execve() only returns on error, never on success.
Next, unless you include a newline at the end of the output, the print won't be flushed to the terminal that is standard output (and there are circumstances, such as piping the standard output to another program, where even a newline is not necessarily sufficient to flush standard output). So, you should include a newline in the message, and still do fflush(stdout); before the execve().
I'm using the execl function to run a Linux process from C. When I do, for example:
int cmd_quem() {
int result;
result = fork();
if(result < 0) {
exit(-1);
}
if (result == 0) {
execl("/usr/bin/who", "who", NULL);
sleep(4); //checking if father is being polite
exit(1);
}
else {
// father's time
wait();
}
return 0;
}
I get on the console the result of doing "who" on the terminal. What I'd like to know is if there is any function to "catch" the output result from a command. What I mean is, if there is anyway to catch this:
feuplive tty5 2009-11-21 18:20
Which is one of the lines resulting from the who command.
To do this, you need to open a pipe. You then replace the child's stdout with the writing end of the pipe, and read from the reading end of the pipe in the parent. Like this modified version of your code:
int cmd_quem(void) {
int result;
int pipefd[2];
FILE *cmd_output;
char buf[1024];
int status;
result = pipe(pipefd);
if (result < 0) {
perror("pipe");
exit(-1);
}
result = fork();
if(result < 0) {
exit(-1);
}
if (result == 0) {
dup2(pipefd[1], STDOUT_FILENO); /* Duplicate writing end to stdout */
close(pipefd[0]);
close(pipefd[1]);
execl("/usr/bin/who", "who", NULL);
_exit(1);
}
/* Parent process */
close(pipefd[1]); /* Close writing end of pipe */
cmd_output = fdopen(pipefd[0], "r");
if (fgets(buf, sizeof buf, cmd_output)) {
printf("Data from who command: %s\n", buf);
} else {
printf("No data received.\n");
}
wait(&status);
printf("Child exit status = %d\n", status);
return 0;
}
First, execl does not return unless there's a problem like the executable is not found. That sleep(4) is probably never executed.
As for redirecting and getting the output, check out the Unix Programming FAQ. Look for spawn_background_command.
The exec() family of functions creates a new process image from a regular, executable file. This file is either an executable object file, or an interpreter script. There is no return from a successful call to an exec() function, because the calling process is functionally replaced by the new process.
So any code after exec() is never executed unless it is failed.
If you want to capture output of a shell command you need popen.