0

it's some implementation of linux shell in c. Since i have added background process support i have some output that i fail to understand. Here is the code:

#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <signal.h>
#include <wait.h>

#define DEFAULT_PROMPT "\nLog710H2014%>"
#define EXIT_CMD "exit"
#define CD_CMD "cd"
#define HOME_ENV_VAR "HOME"
#define NEW_LINE "\n**************************************************\n"
#define BCG_CMD_FLAG "&"

void cd_handler(int argc, char *argv[]);
int lire(char *chaine, int longueur);
char** init_command(int* size,char *str);
int execProg(int *argc, char **argv);
int execProgBg(int *argc, char **argv);
void sigchldHandler(int sig_num);

struct beanProcess {
    pid_t pid;
    int job_num;
    char *command;
};

struct beanProcess *beans;

int jobCount = 1;

int main() {

    printf(NEW_LINE);
    printf("Bienvenue sur le shell de l'equipe 1");
    printf(NEW_LINE);

    while(1){
        char str[200];
        printf(DEFAULT_PROMPT);

        lire(str, 200);
        int commArgsC = 0, bg = 0;
        char** comms  = init_command(&commArgsC, str);

        if(commArgsC == 0){
            printf("Saisie vide, veuillez entrez une commande.");
            continue;
        }

        if(strcmp(comms[commArgsC-1], BCG_CMD_FLAG) == 0){
            bg = 1;
            comms[commArgsC-1] = 0;
        }
        if(strcmp(comms[0], CD_CMD) == 0){
            cd_handler(commArgsC, comms);
            commArgsC = commArgsC -1;
        }
        else if (strcmp(comms[0], EXIT_CMD) == 0){
            exit(0);
        }
        else {
            if(bg){
                execProgBg(&commArgsC, comms);
            }
            else{
                execProg(&commArgsC, comms);
            }
        }
    }
    exit;
}
void cd_handler(int argc, char *argv[]){
    char buff[512];
    char * directory;

    if(argc < 2){
        directory  = getenv(HOME_ENV_VAR);
    }else if (argc == 2){
        directory = argv[1];
    }else{
        exit(1);
    }

    if (chdir(directory) == -1) {
        printf ("Erreur de changement de repertoire actif", strerror (errno));
    }else{
        if (getcwd(buff, sizeof(buff)) == NULL)
            perror("Impossible d'afficher le repertoire courant");
        else
            printf("le repertoire courant est: %s\n", buff);
    }
}
//Cette fonction est adaptée a partir du code de refp sur http://stackoverflow.com/questions/11198604/c-split-string-into-an-array-of-strings
char** init_command(int* size, char* str){
    char ** res  = NULL;
    char *  p    = strtok (str, " ");
    int n_spaces = 0;

    while (p) {
        res = realloc (res, sizeof (char*) * ++n_spaces);

        if (res == NULL){
            exit (-1);
        }
        res[n_spaces-1] = p;
        p = strtok (NULL, " ");
    }
    res = realloc (res, sizeof (char*) * (n_spaces+1));
    res[n_spaces] = 0;
    *size = n_spaces;
    return res;
}
//cette fonction est tirée d'un exemple de http://fr.openclassrooms.com/informatique/cours/apprenez-a-programmer-en-c/recuperer-une-chaine-de-caracteres
int lire(char *chaine, int longueur)
{
    char *positionEntree = NULL;

    if (fgets(chaine, longueur, stdin) != NULL)
    {
        positionEntree = strchr(chaine, '\n');
        if (positionEntree != NULL)
        {
            *positionEntree = '\0';
        }
        return 1;
    }
    else
    {
        return 0;
    }
}
int execProg(int *argc, char **argv){
    char path[] = "/bin/";
    strcat(path,argv[0]);
    printf("\nThis is the %d process executing the code in non bg mode\n", getpid());
    printf("Voici le resultat de l'execution de votre commande\n");
    pid_t  pid;
    pid = fork();

    if (pid < 0) {
        perror("Creation de processus avec fork echouee");
        exit(-1);
    }
    else if (pid == 0) {
        if(execvp(argv[0], argv) == -1){
            printf("\nthis is the child process %d executing the command in non bg mode\n", getpid());
            perror("execv");
            return EXIT_FAILURE;
        }
    }
    else {
        printf("\nthis is the parent process %d showing the stats in non bg mode\n", getpid());
        struct rusage rusg;
        long temp, tempCpu;
        wait (NULL);
        getrusage(RUSAGE_CHILDREN, &rusg);
        printf("\nStatistique de la commande %s:\n", argv[0]);

        temp = (rusg.ru_utime.tv_sec * 1000) + (rusg.ru_utime.tv_usec / 1000);
        tempCpu = (rusg.ru_stime.tv_sec * 1000) + (rusg.ru_stime.tv_usec / 1000);

        printf("\nLe temps wall-clock (ms): %ld", temp);
        printf("\nLe temps CPU (ms) %ld", tempCpu);
        printf("\nNB interruptions volontaires: %ld", rusg.ru_nvcsw);
        printf("\nNB interruptions involontaires: %ld", rusg.ru_nivcsw);
        printf("\nNB defaults de pages: %ld", rusg.ru_majflt);
        printf("\nNB defaults de pages satifaits du noyau : %ld", rusg.ru_minflt);
    }
    return EXIT_SUCCESS;
}
int execProgBg(int *argc, char **argv){

    signal(SIGCHLD, sigchldHandler);
    printf("\nThis is the %d process executing the code in  bg mode\n", getpid());

    pid_t  pid;
    pid = fork();

    if (pid < 0) {
        perror("Creation de processus avec fork echouee");
        return EXIT_FAILURE;
    }
    else if (pid == 0) {
        //printf("This is the pid %d", getpid());
        printf("\nthis is the child process %d executing the command in  bg mode\n", getpid());

        if(execvp(argv[0], argv) == -1){
            perror("execvp");
            return EXIT_FAILURE;
        }
    }
    else {
        printf("\nthis is the parent process %d showing the queue in bg mode\n", getpid());

        printf("[%d] %d", jobCount, pid);
        jobCount++;
        //cleanJobList(childPid);

        //ajoutProcess();
    }
    return EXIT_SUCCESS;
}
void sigchldHandler(int sig_num)
{
    int status;
    int childPid;
    childPid = waitpid(-1, &status, 1);
    printf("Hello the process %d has died\n", childPid);
    //cleanJobList(childPid);
}

When i execute a bg command like "ls &", here's the output:

**************************************************
Bienvenue sur le shell de l'equipe 1
**************************************************

Log710H2014%>ls &

This is the 23464 process executing the code in  bg mode

this is the parent process 23464 showing the queue in bg mode
[1] 23472
Log710H2014%>
this is the child process 23472 executing the command in  bg mode
Debug  PARTIE3.c
Hello the process 23472 has died

This is the 23464 process executing the code in non bg mode
Voici le resultat de l'execution de votre commande

this is the parent process 23464 showing the stats in non bg mode
Debug  PARTIE3.c

Statistique de la commande ls:

Le temps wall-clock (ms): 0
Le temps CPU (ms) 2
NB interruptions volontaires: 2
NB interruptions involontaires: 9
NB defaults de pages: 0
NB defaults de pages satifaits du noyau : 644
Log710H2014%>

Why is that the parent process overlapping the lire() function and going directly to execProg function after the first execution ?

user2966439
  • 307
  • 1
  • 5
  • 14

1 Answers1

1

You are in a while loop. When your execProgBg function returns, no matter what it returns, the loop keeps going. If you want to stop you need to break or call exit from execProgBg.

Why is that the parent process overlapping the lire() function and going directly to execProg function after the first execution

I don't know how you are executing your program but it looks like the second time around fgets fails, which you don't notice since you don't check the return code of the lire function. SO you go on and reuse the buffer from the previous call. It seems likely that you pass EOF to the program - perhaps by hitting ctrl-d.


I decided to run the code and obtained the same result as the OP does by hitting CTRL-D after the first ls &.


It is besides the point but warrants some explanation:

exit;

This evaluates the function exit converting it to a function pointer and throws away the result. It does not call the function. More importantly, calling exit as the last statement of main is pointless, since main is exiting anyway. You should just return some_code to indicate success of failure.

cnicutar
  • 178,505
  • 25
  • 365
  • 392
  • I have corrected the exit issue but still why is my prog not blocking in the lire function (which contains fgets from the stdin) in the second iteration of the loop ? – user2966439 Feb 11 '14 at 01:08
  • @Nabla The question was edited in the meantime and the first time around I didn't see the second part. Looking. – cnicutar Feb 11 '14 at 01:10
  • @Nabla `fgets` is failing. It looks like EOF but could be something subtler. – cnicutar Feb 11 '14 at 01:20
  • I thought maybe the signal handler for SIGCHLD could interrupt fgets. But I am not sure whether fgets is supposed to continue after signals or not. –  Feb 11 '14 at 01:30
  • There can be some debate whether the underlying `read(2)` call is restarted (there is a whole discussion on SA_RESTART and like in all good things in life there is no general consensus). TL;DR on linux a `signal(2)` implies `SA_RESTART` and reading from the terminal is considered reading from a "slow" device so it gets restarted. It turns out it doesn't matter: any `fgets` implementation should watch out for `errno = EINTR` and restart the call manually if need be. – cnicutar Feb 11 '14 at 01:34
  • I think we are on something because commeting the signal catch makes it run like it should, any idea on how to correct this issue ? I need the handler because i eventually need to remove dead process from beans array – user2966439 Feb 11 '14 at 01:43
  • no, i have finally decided to empty the string after each execution, it's doing the job. – user2966439 Feb 11 '14 at 05:25