2

I had this simple shell like program that works both in interactive and non-interactive mode. I have simplified the code as much as I can to present my question, but it is still a bit long, so sorry for that!

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


/**
 *main-entry point for gbk
 *Return: returns the index of 0 on sucess
 */
int main(void)
{

    char *cmd = malloc(1 * sizeof(char)), *cmdargs[2];
    size_t cmdlen = 0;
    int childid, len;
    struct stat cmdinfo;

    while (1)
    {
        printf("#cisfun$ ");
        len = getline(&cmd, &cmdlen, stdin);
        if (len == -1)
        {
            free(cmd);
            exit(-1);
        }
        /*replace the ending new line with \0*/
        cmd[len - 1] = '\0';
        cmdargs[0] = cmd;
        cmdargs[1] = NULL;

        childid = fork();
        if (childid == 0)
        {
            if (stat(*cmdargs, &cmdinfo) == 0 && cmdinfo.st_mode & S_IXUSR)
                execve(cmdargs[0], cmdargs, NULL);
            else
                printf("%s: command not found\n", *cmdargs);
            exit(0);
        }
        else
            wait(NULL);
    }
    free(cmd);
    exit(EXIT_SUCCESS);
}

To summarize what this program does, it will first print the prompt #cisfun$ , waits for an input in interactive mode and takes the piped value in non-interactive mode, creates a child process, the child process checks if the string passed is a valid executable binary, and if it is, it executes it other wise it prints a command not found message and prompts again.

I have got this program to work fine for most of the scenarios in interactive mode, but when I run it in non-interactive mode all sorts of crazy (unexpected) things start to happen.

For example, when I run echo "/bin/ls"|./a.out, (a.out is the name of the compiled program) you would first expect the #cisfun$ message to be printed since that is the first thing performed in the while loop, and then the output of the /bin/ls command, and finally #cisfun$ prompt, but that isn't what actually happens. Here is what happens,

enter image description here

  1. It is very weird the ls command is run even before the first print message. I, at first, thought there was some threading going on and the printf was slower than the child process executing the ls command. But I am not sure if that is true as I am a noob. and also things get a bit crazier if I was printing a message with '\n' at the end rather than just a string. (if I change printf("#cisfun$ "); to printf("#cisfun$\n");) the following happens,

enter image description here

It works as it should, so it got me thinking what is the relation between '\n', fork and speed of printf. Just in short what is the explanation for this.

  1. The second question I have is, why doesn't my program execute the first command and go to an interactive mode, I don't understand why it terminates after printing the second #cisfun$ message. By checking the status code (255) after exit I have realized that the effect is the same as pressing ctr+D in the interactive mode, which I believe is exited by the getline function. But I dont understand why EOF is being inserted in the second prompt.
EHM
  • 877
  • 1
  • 7
  • 28
  • 1
    1. output is usually _line buffered_, that's why appending a newline makes your prompt appear before the output of ls. – Peter Nov 14 '20 at 19:27
  • 2
    In practice, *always* call [fflush(3)](https://www.man7.org/linux/man-pages/man3/fflush.3.html) before [fork(2)](https://www.man7.org/linux/man-pages/man2/fork.2.html) – Basile Starynkevitch Nov 14 '20 at 19:36
  • @Peter Thank you, but I am sorry can you be a little bit more noob friendly about what line buffered means. – EHM Nov 14 '20 at 19:42
  • @BasileStarynkevitch, Thank you so much, let me read the manuals for fflush – EHM Nov 14 '20 at 19:43
  • @BasileStarynkevitch Thank you again, I didn't completely understand what fflush does from the man pages, but fflush(stdout); seems to fix the first problem. I will do more reading to understand what and how fflush do its thing – EHM Nov 14 '20 at 20:01
  • https://stackoverflow.com/questions/1716296/why-does-printf-not-flush-after-the-call-unless-a-newline-is-in-the-format-strin/1716621#1716621 may help. – Nate Eldredge Nov 14 '20 at 23:00
  • @NateEldredge Thank you, that shure did help. Does anyone have an idea why the 2 question could be resolved – EHM Nov 15 '20 at 05:58
  • When you pipe input into your program, its standard input is a pipe. When it has read all the data from the pipe, end-of-file is signaled on standard input, exactly as if you pressed Ctrl-D on the terminal. If you think about it, this is what you want from a pipe; it would be weird if you tried to pipe data into `sort` and after processing the data, it started expecting you to type more data on the terminal. – Nate Eldredge Nov 15 '20 at 06:40
  • If you really want to become interactive after the pipe data is finished, you'll have to manually open something like `/dev/tty` and start reading on it instead of standard input, but that's probably not a good design. Rather, if you want your shell to run interactively, don't run it in a pipeline in the first place. – Nate Eldredge Nov 15 '20 at 06:40
  • @NateEldredge, Ok that is the information I needed. Thank you so much! Just, One more question, why isn't the EOF signaled during the first getline call. or does a piped data comes in two packets, such that the first one reads the actual data, and the EOF when tried to be read for the second time. – EHM Nov 15 '20 at 07:15
  • 1
    @EHM: If there is data available to be read, `getline` will read it. If there is no data but the writer still has the pipe open, `getline` will block until there is data. If there is no data and the other end of the pipe has closed, `getline` indicates EOF. I don't know what you mean by "two packets". – Nate Eldredge Nov 15 '20 at 16:15
  • 1
    @EHM: But this is the same behavior you see when reading from a terminal. `getline` has no way to both return data and indicate EOF on a single return. You have to call it again to find out that EOF has been reached. – Nate Eldredge Nov 15 '20 at 16:17
  • Thanks makes sense now – EHM Nov 15 '20 at 16:34

0 Answers0