Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to run a program from terminal input(linux)?

Tags:

c

linux

While I think I have the grasp on how fork(), exec(), wait() and pid work in C, I have yet to find a way how to run a personal program from within a program.

Here's my code:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h> /* for fork() */
#include<sys/types.h> /* for pid_t */
#include<sys/wait.h> /* fpr wait() */

int main(int argc, char* argv[])
{
    char  fileName[255];
    pid_t pid;

    switch (pid = fork()) {
    case -1: //Did not fork properly
        perror("fork");
        break;

    case 0: //child
        execv(fileName[0],fileName);

        puts("Oh my. If this prints, execv() must have failed");
        exit(EXIT_FAILURE);
        break;
    default: //parent
        //Infinite Loop
        while (1) {
            printf(" %s > ", argv[0]);
            scanf("%s", fileName); // gets filename
            if (fileName[0] == '\0') continue;
            printf("\n Entered file: %s",fileName); // prints the fileName
            waitpid(pid,0,0); /* wait for child to exit() */
            break;
        }
    }

    return 0;
}

My questions are the following:

  1. I want to take a string as an input and I want to limit its scope to 255 characters. Is char fileName[255] and then scanf("%s", fileName); the way to go? Should I use getLine() or some other function instead?

  2. Let's say that the input is taken correctly. How do I execute say an existing hello world program. Will the input be stored in *argv[] ? I found out that in a different program I could use

    static char *argv[] = { "echo", "Foo is my name." , NULL };
    execv("/bin/echo", argv);
    

    in order to echo "Foo is my name.". Can I do something similar with a helloWorld program?

like image 564
Joseph Athan Avatar asked Oct 31 '22 06:10

Joseph Athan


1 Answers

You're passing a single character as the command name, and the name string as the start of a list of arguments — as if the prototype for execv() were int execv(char cmd, char *args).

The actual prototype is: int execv(char *cmd, char **args), so you need:

char *args[2] = { fileName, 0 };

execv(args[0], args);

I assume you set fileName to a meaningful value somewhere — that isn't shown. For example, it might be "./local_program". It will be treated as the pathname of the executable.

If you want to read the name, then you can use fgets() or getline(), but you'll need to remove the newline:

if (fgets(fileName, sizeof(fileName), stdin) != 0)
{
    fileName[strcspn(fileName, "\n")] = '\0';
    …as before…
}

or:

char *fileName = 0;
size_t length = 0;
if (getline(&fileName, &length, stdin) != -1)  /* Not EOF! */
{
    fileName[strcspn(fileName, "\n")] = '\0';
    …as before…
}
free(fileName);

The use of strcspn() avoids having to special case overly long command lines such that there isn't a newline in the fileName. Note that this does not attempt to split the input line into a command name and arguments at spaces or anything fancy like that. That's the next level of shell implementation:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int main(void)
{
    char fileName[256];
    if (fgets(fileName, sizeof(fileName), stdin) != 0)
    {
        fileName[strcspn(fileName, "\n")] = '\0';
        char *args[129];
        char **argv = args;
        char *cmd = fileName;
        const char *whisp = " \t\f\r\b\n";
        /* Ugh — strtok()?  OK while only handling white space separators */
        char *token;
        while ((token = strtok(cmd, whisp)) != 0)
        {
            *argv++ = token;
            cmd = 0;
        }
        *argv = 0;

        execv(args[0], args);
        fprintf(stderr, "Oops!\n");
    }
    return 1;
}

I don't need to check for overflow of the args array because 256 characters of input, minus terminating null, cannot be split to produce more than 128 single-character arguments, each separated from the adjacent ones by a single white space character. Using strtok() is a temporary band-aid. As soon as you need to deal with real shell syntax (pipes, I/O redirections, quoted strings with spaces, etc), strtok() is woefully the wrong tool. (It — strtok() — is also the wrong function to use in any library function whatsoever. Use POSIX strtok_r() on Unix or Microsoft's strtok_s() on Windows if you must use a strtok()-like function in library code.)

like image 64
Jonathan Leffler Avatar answered Nov 02 '22 09:11

Jonathan Leffler