Segmentation fault while system() in C

Viewed 990

I want to use the "base64" script of linux to encode the data and get it in C. When I try to compile

char a[200];
strcpy(a, "Hello");
printf("%s", a);

I get the output

Hello

Now whenever I try the code

char a[200];
strcpy(a, system("echo Hello | base64"));
printf("%s", a);

I get the output

aGVsbG8K
Segmentation fault

Even when I remove the "printf" statement, I get the same

aGVsbG8K
Segmentation fault

I want to save the value of the output of

system("echo Hello | base64")

in 'a' and not display it. Please help

2 Answers

If you read the documentation for system you'll discover that it doesn't return a string - it's defined as:

int system(const char *command);

The return value is the return status of the command or -1 if there's an error. You can't get the output using system - the output of the command(s) you run will go straight to stdout.

To get the output from another command you could use something like popen.

FILE *myfile;
char buffer[1024];

myfile=popen("echo Hello | base64","r");
if(myfile)
  {
  while(fgets(buffer,1024,myfile))
    {
    printf("%s",buffer);
    }

  pclose(myfile);
  }

Here

strcpy(a, system("echo Hello | base64"));

system() doesn't stores it's result into array a as system() job is to execute the command provided in the argument & print it on console i.e stdout buffer. From the manual page of system

system() executes a command specified in command by calling /bin/sh -c command, and returns after the command has been completed.

There is one way to solve the problem i.e instead of printing system() output on stdout you can redirect its output to a file & then read that from file & print. For example

int main(void) {
        close(1); /* stdout file descriptor is avilable now */
        /* create the file if doesn't exist, if exist truncate the content to 0 length */
        int fd = open("data.txt",O_CREAT|O_TRUNC|O_RDWR,0664); /* fd gets assigned with lowest 
                                                  available fd i.e 1 i.e nowonwards stdout output 
                                                  gets rediredcted to file */
        if(fd == -1) {
                /* @TODO error handling */
                return 0;
        }
        system("echo Hello | base64"); /* system output gets stored in file */
        int max_char = lseek(fd,0,2);/* make fd to point to end, get the max no of char */
        char *a = malloc(max_char + 1); /* to avoid buffer overflow or 
                underflow, allocate memory only equal to the max no of char in file */
        if(a == NULL) {
                /* @TODO error handling if malloc fails */
                return 0;
        }
        lseek(fd,0,0);/* from beginning of file */
        int ret = read(fd,a,max_char);/* now read out put of system() from
                                          file as array and print it */
        if(ret == -1) {
                /* @TODO error handling */
                return 0;
        }
        a[ret] = '\0';/* \0 terminated array */
        dup2(0,fd);/*fd 0 duplicated to file descriptor where fd points i.e */
        printf("output : %s \n", a);
        /* to avoid memory leak, free the dynamic memory */
        free(a);
        return 0;
}

My above suggestion is a temporary fix & I won't recommend this, instead use [popen] as suggested by @chris Turner (http://man7.org/linux/man-pages/man3/popen.3.html) which says

The popen() function opens a process by creating a pipe, forking, and invoking the shell. Since a pipe is by definition unidirectional, the type argument may specify only reading or writing, not both; the resulting stream is correspondingly read-only or write-only.

For example

int main(void) {
        char buf[1024];
        FILE *fp = popen("echo Hello | base64","r");
        printf("%s\n",fgets(buf,sizeof(buf),fp));
        return 0;
}
Related