user7732640
user7732640

Reputation:

Running BASH command via C program

I'm trying to run BASH commands via a C program, but i'm struggling with the function execv. I don't really know how to write the first parameter of that function. I tried with the strcat function to append the string "/bin/" with the 1st element of the argv tab, which is the command i write when I run my program, but it just doesn't work. I get a "Segmentation fault". Instead of using the strcat function I tried with strdup, but I don't know how to use it right.

Any help would be appreciated. My program is below.

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>

int main(int argc, char *argv[], char *envp[]){

char *tab[] = {argv[1],NULL};

if(execve(strcat("/bin/",argv[1]), tab, envp)==-1)
{
    perror("execve");
    return EXIT_FAILURE;
}

return EXIT_SUCCESS;
}

Upvotes: 1

Views: 5378

Answers (4)

user7732640
user7732640

Reputation:

I finally got to find a way to do what I wanted at the first place. Thanks to all of you guys for your help & advices ! Here's the solution !

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>

int main(int argc, char *argv[], char *envp[]){

char *tab[] = {argv[1],argv[2],NULL};

char test[20] = "/bin/";

if(execve(strcat(test,argv[1]), tab, envp)==-1)
{
    perror("execve");
    return EXIT_FAILURE;
}

return EXIT_SUCCESS;
}

Upvotes: 0

To run a shell command from a C program, you should use system(3). If you want to get its stdout (or give its stdin, but not both) use popen(3) (don't forget to pclose such a stream).

The shell used (by system and popen) is not exactly bash but the standard POSIX /bin/sh (quite similar to bash with some restrictions).

To build that shell command (but beware of code injections in it) you can use common string functions such as snprintf and asprintf.

Notice that execve(2) does not return when it is successful, and it does not run a command thru a shell, but directly an executable program. Actually Unix shells (such as bash or  /bin/sh) are using fork(2), execve(2), waitpid(2) very often and are implementing globbing. BTW system & popen are also using fork and execve on /bin/sh -c.

strcat("/bin/",argv[1])

is horribly wrong, the first argument to strcat is the overwritten destination buffer (so cannot be a string literal), and you don't check against buffer overflow.

You might want to code:

char progbuf[80];
int ln = snprintf(progbuf, sizeof(progbuf), "/bin/%s", argv[1]);

and you should check later that ln<(int)sizeof(progbuf)

BTW, your program, when you'll improve it, is not using Bash; it is directly executing some command.


I tried with strdup, but I don't know how to use it right.

Before using any function, you need to carefully read its documentation, for example strdup(3) (or type man strdup in a terminal).

Upvotes: 1

user3629249
user3629249

Reputation: 16540

regarding:

if(execve(strcat("/bin/",argv[1]), tab, envp)==-1)  

This will not work ,

  1. the literal "/bin/" is in read only memory, so cannot be changed (need a char buffer large enough to hold the full string, similar to `char string[100] = "/bin/";

Suggest:

#include <stdio.h>      // perror()
#include <stdlib.h>     // exit(), EXIT_FAILURE
#include <sys/types.h>
#include <sys/wait.h>   // waitpid()

#include <unistd.h>     // fork(), execvp()
#include <string.h>     // strlen(), strcpy(), strcat()

int main( int argc, char *argv[], char *env[] )
{
    (void)argc;

    char *tab[] = { argv[1], NULL };

    char string[strlen(argv[1]) + strlen( "/bin/" ) +1 ];
    strcpy( string, "/bin/" );
    strcat( string, argv[1] );

    int status;

    pid_t pid = fork();
    switch( pid )
    {
        case -1:   // fork failed
            perror( "fork failed" );
            exit( EXIT_FAILURE );
            break;

        case 0:    // child process
            execve( string, tab, env );   // does not return unless an error
            perror("execve failed");
            exit( EXIT_FAILURE );
            break;

        default:
            waitpid( pid, &status, 0 );
            break;
    }

}

Caveat: the proposed code just hides the parameter: argc rather than properly checking it to assure the command line does contain a parameter.

Caveat: the parameter to main: env[] is not portable and should not be used. Suggest using :

extern char *environ[];

Upvotes: 1

Andrew Pikul
Andrew Pikul

Reputation: 251

Melpomene is right- you can't use strcat like that. In C, you can't return strings. What you do is pass a memory address (pointer) as the first argument in strcat, and then strcat modifies the memory pointed to by it so that it also contains the second argument of strcat. This is a process you will repeat over and over again in C, so you should understand it. Also, that strcat doesn't look safe, I bet there is a strcatn function or something like that.

Upvotes: 0

Related Questions