Search code examples
linuxmakefilec

Endless wait(NULL) on C if I use exec function


I am using C programming language, and gcc compiler of C 99 standard. A program of mine is stuck during wait(NULL) of a parent process. There are three programs I am using. All are using command-line arguments.

Because I am also using file-redirection:

  1. Adding prints does not necessarily helps, because some prints are not printed from a certain
  2. There is a feeling that some characters are corrupted.

The program of the parent process is called foo. It executes another program called goo, and goo executes a third program called hoo.

If I just execute goo, this works fine. I can provide test files for example that I get valid resulsts with them. This is weird, because I would have expect that goo would be in an endless run, what does not happen.

I am adding code in case this is not some fundamental issue. Currently my main issue is that wait() is stuck within foo and not the functionality!

Foo.c

#define _GNU_SOURCE

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <math.h>
#include <string.h>

int main(int argc, char* argv[])
{
    // Program use: there are m files. 
    // It needs to execute goo for each different file.
    int i = 0, output_fd = open(file_name, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (output_fd < 0)
    {
        fprintf(stderr, "*** ERROR: *** open failed!\n");
        return 1;
    }

    for(i = 2; i < argc; i++)
    {
            int pid = fork();
            if (pid == 0)
            {
                char* arg_vec[3] = { "goo", argv[i], NULL };
                dup2(output_fd, STDOUT_FILENO);
                execve("goo", arg_vec, NULL); // Check why it's execution does not end?
                fprintf(stderr, "*** ERROR: *** execve failed!\n");
                return 1;
            }
            else if (pid < 0)
            {
                fprintf(stderr, "*** ERROR: *** fork failed!\n");
                return 1;
            }
    }

    while(wait(NULL) > 0)
    {
    }

    close(output_fd);
    return 0;
}

goo.c

#define _GNU_SOURCE

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <math.h>
#include <string.h>

char** create_argv(char* student_details_line);

int main(int argc, char* argv[])
{
    // Program use: read a file of n lines, each lines consists details for n different students. 
    // For each student, a fork() called, and then executes hoo program.
    
    char* student_details_line = NULL;
    size_t len;
    int num_stud = 0, input_fd = open("hoo", stdin), output_fd = open(file_name, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (input_fd < 0 || output_fd < 0)
    {
        fprintf(stderr, "*** ERROR: *** file descriptor failed!\n");
        return 1;
    }
    // Reading input from stdin
    FILE* input_file = fdopen(input_fd, "r");
    if (input_file == NULL) {
        perror("Error opening input file");
        exit(EXIT_FAILURE);
    }
    while (getline(&student_details_line, &len, input_file) != -1)
    {
        int pid = fork();
        if (pid == 0)
        {
            char** arg_vec = create_argv(student_details_line);
            dup2(input_fd, STDIN_FILENO);
            dup2(output_fd, STDOUT_FILENO);
            execve("hoo", arg_vec, NULL);
            fprintf(stderr, "*** ERROR: *** execve failed!\n");
            return 1;
        }
        else if (pid < 0)
        {
            fprintf(stderr, "*** ERROR: *** fork failed!\n");
            return -1;
        }
        else
            num_stud++;
    }
    while(wait(NULL) > 0)
    {
    }
    fclose(input_file);
    close(input_fd);
    close(output_fd);
    printf("There are %d students\n", num_stud);

    return 1;
}

char** create_argv(char* student_details_line)
{
    int pid = getpid(), size = 0, max_arguments = 2;

    char** arguments_arr = (char**)malloc(max_arguments * sizeof(char*));
    if (arguments_arr == NULL)
    {
        fprintf(stderr, "*** ERROR: *** malloc failed!\n");
        _exit(1);
    }
    arguments_arr[size++] = strdup("hoo"); // Program name

    // Get the first token
    char* token = strtok(student_details_line, "\n\b\t ");
    arguments_arr[size++] = strdup(token);
    
    // Walk through other tokens
    while (1) 
    {
        // Reallocate array
        if (max_arguments == size)
        {
            max_arguments = max_arguments * 2;
            char** new_arguments_arr = (char**)realloc(arguments_arr, max_arguments * sizeof(char*));
            if (new_arguments_arr == NULL)
            {
                fprintf(stderr, "*** ERROR: *** realloc failed!\n");
                free(arguments_arr);
                _exit(1);
            }
            arguments_arr = new_arguments_arr;
        }
        // Get new token, add it to arguments vector and update the amount of arguments
        if ((token = strtok(NULL, "\n\b\t ")) != NULL)
            arguments_arr[size++] = strdup(token);
        // No more tokens
        else
            break;
    }
    
    // Reallocate array
    if (max_arguments == size)
    {
        max_arguments = max_arguments * 2;
        char** new_arguments_arr = (char**)realloc(arguments_arr, max_arguments * sizeof(char*));
        if (new_arguments_arr == NULL)
        {
            fprintf(stderr, "*** ERROR: *** realloc failed!\n");
            free(arguments_arr);
            _exit(1);
        }
        arguments_arr = new_arguments_arr;
    }
    // Function's output
    return arguments_arr;
}

hoo.c

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

int main(int argc, char* argv[])
{
    // Program use: returns a name of a student and his average grade in one-liner
    double average_grade = 0;
    int total_grades = 0;

    // Iterate over other arguments (grades only)
    for (int i = 2; 0 < argc && i < argc; i++)
    {
        char* end_ptr = NULL;
        int grade = strtol(argv[i], &end_ptr, 10);
        int ptr_size = strlen(end_ptr);
        for (int j = 0; j < ptr_size; j++)
        {
            if (14 <= end_ptr[j])
            {
                fprintf(stderr, "Argument %s is non-integer\n", argv[i]);
                return 1;
            }
        }

        average_grade += grade;
        total_grades++;
    }
    // Prints student name and his average grade
    if (total_grades == 0)
        fprintf(stdout, "%s NaN\n", argv[1]);
    else
    {
        average_grade = average_grade / total_grades;
        fprintf(stdout, "%s %.1f\n", argv[1], average_grade);
    }

    return 0;
}

Makefile

# Makefile for ex2_q1 spring 2023B

CFLAGS   = -Wall -std=c99
LDFLAGS  = -lm
CC       = gcc
ECHO     = @echo "going to build target $@ (dependent on $^)"

PROG1 = one_student
PROG2 = read_grades
PROG3 = ex2_q1
PROGS = $(PROG1) $(PROG2) $(PROG3)

all: $(PROGS) test


$(PROG1): $(PROG1).c $(PROG3).h
    $(ECHO)
    $(CC) $(CFLAGS) $(LDFLAGS) $< -o $@ 

$(PROG2): $(PROG2).c $(PROG3).h
    $(ECHO)
    $(CC) $(CFLAGS) $(LDFLAGS) $< -o $@ 

$(PROG3): $(PROG3).c $(PROG3).h
    $(ECHO)
    $(CC) $(CFLAGS) $(LDFLAGS) $< -o $@ 

clean:
    rm -vf *.o ${PROG} all_std.log *.temp

test: $(PROGS)
    @echo going to run test...
    ./$(PROG3) readers_msg.txt gr_1.txt

gr_1.txt

Avi 80 90 75
Beni 90
Gadi 70 9 90 100

I am sorry for the amount of code lines. Really thankful for helpers...


Solution

  • As I understand it, foo is waiting endlessly because the various goo processes aren’t exiting: they’re stuck waiting for input. If you either change foo to redirect goo’s standard input to read from the file it’s supposed to process, or change goo to read from the file given in its arguments instead of its standard input, everything should work.