Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🌠Signal handling #13

Open
6 tasks
saladuit opened this issue Feb 9, 2023 · 4 comments
Open
6 tasks

🌠Signal handling #13

saladuit opened this issue Feb 9, 2023 · 4 comments
Labels
💫enhancement New feature or request

Comments

@saladuit
Copy link
Owner

saladuit commented Feb 9, 2023

Research this page:

I think that for now canonical mode is the normal mode that the shell is in interactive mode
and for example non-canonical mode should be set when the user is in input mode.

Handle ctrl-C, ctrl-D and ctrl-\ which should behave like in bash.
• In interactive mode:
◦ ctrl-C displays a new prompt on a new line. ◦ ctrl-D exits the shell.
◦ ctrl-\ does nothing.

  • What are all of the options I can set?
  • What are all the options I should set?
  • How do we send the address of our exit_status variable to our signal handlers
  • Which various signal handlers should we make?
@saladuit saladuit converted this from a draft issue Feb 9, 2023
@saladuit saladuit added the 💫enhancement New feature or request label Feb 9, 2023
@saladuit saladuit changed the title Signal handling for "stdin mode" and "prompt mode" / interactive mode - ctrl-C - ctrl-D - ctrl-\ Signal handling Feb 9, 2023
@saladuit
Copy link
Owner Author

We probably need something like this to make it work without global variables

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

int exit_status;
pid_t child_pid;

void sig_handler(int sig) {
    int status;
    if (waitpid(child_pid, &status, 0) == -1) {
        perror("waitpid");
        exit(EXIT_FAILURE);
    }
    if (WIFEXITED(status)) {
        exit_status = WEXITSTATUS(status);
    } else {
        exit_status = -1;
    }
}

int main(int argc, char *argv[]) {
    struct sigaction act;
    act.sa_handler = sig_handler;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;
    if (sigaction(SIGCHLD, &act, NULL) == -1) {
        perror("sigaction");
        exit(EXIT_FAILURE);
    }

    child_pid = fork();
    if (child_pid == -1) {
        perror("fork");
        exit(EXIT_FAILURE);
    }
    if (child_pid == 0) {
        // Child process
        execl("/bin/ls", "ls", "-l", NULL);
        perror("execl");
        exit(EXIT_FAILURE);
    } else {
        // Parent process
        // Wait for the child process to terminate
        while (exit_status == 0) {
            pause();
        }
        printf("Child exited with status %d\n", exit_status);
    }
    return 0;
}

@saladuit
Copy link
Owner Author

Probably something like this would do the trick:
the signal handler is a sa_sigaction type instead of a sa_handler type, and it takes three arguments: the signal number, a pointer to the siginfo_t structure, and a pointer to a ucontext_t structure. The sigaction function is called with the SA_SIGINFO flag set, which indicates that the signal handler is of type sa_sigaction. When the SIGCHLD signal is delivered, the signal handler can retrieve the PID of the child process that generated the signal from the si_pid field of the siginfo_t structure, and use it to wait for the child process and retrieve its exit status using waitpid.

All hail chatgpt

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

void sig_handler(int sig, siginfo_t *info, void *context) {
    int status;
    if (waitpid(info->si_pid, &status, 0) == -1) {
        perror("waitpid");
        exit(EXIT_FAILURE);
    }
    if (WIFEXITED(status)) {
        printf("Child %d exited with status %d\n", info->si_pid, WEXITSTATUS(status));
    } else {
        printf("Child %d terminated abnormally\n", info->si_pid);
    }
    exit(EXIT_SUCCESS);
}

int main(int argc, char *argv[]) {
    struct sigaction act;
    act.sa_sigaction = sig_handler;
    sigemptyset(&act.sa_mask);
    act.sa_flags = SA_SIGINFO;
    if (sigaction(SIGCHLD, &act, NULL) == -1) {
        perror("sigaction");
        exit(EXIT_FAILURE);
    }

    pid_t child_pid = fork();
    if (child_pid == -1) {
        perror("fork");
        exit(EXIT_FAILURE);
    }
    if (child_pid == 0) {
        // Child process
        execl("/bin/ls", "ls", "-l", NULL);
        perror("execl");
        exit(EXIT_FAILURE);
    } else {
        // Parent process
        pause();
    }
    return 0;
}

@saladuit saladuit changed the title Signal handling 🌠Signal handling Feb 23, 2023
@saladuit
Copy link
Owner Author

To enable canonical mode, you would set the "ICANON" flag using bitwise OR operation. For example, the following code sets the "ICANON" flag and stores the updated termios structure back into the terminal:

#include <termios.h>
struct termios term;
tcgetattr(STDIN_FILENO, &term);
term.c_lflag |= ICANON;
tcsetattr(STDIN_FILENO, TCSANOW, &term);

To disable canonical mode and enable non-canonical mode, you would clear the "ICANON" flag using bitwise AND operation. For example, the following code clears the "ICANON" flag and stores the updated termios structure back into the terminal:

#include <termios.h>
struct termios term;
tcgetattr(STDIN_FILENO, &term);
term.c_lflag &= ~ICANON;
tcsetattr(STDIN_FILENO, TCSANOW, &term);

@saladuit
Copy link
Owner Author

Explanation of Canonical mode
In Unix-like operating systems, the termios structure is used to configure and control terminal I/O. One of the configuration options for termios is the input mode, which can be set to either canonical or non-canonical mode.

In canonical mode, the terminal driver buffers input until a line-ending character is received (usually the Enter key). Once a line-ending character is received, the entire line is returned to the program reading from the terminal. Canonical mode is typically used for command-line interfaces, where the user inputs a command and then presses Enter to execute it.

In non-canonical mode, input is not buffered by the terminal driver and is returned to the program as soon as it is received. Non-canonical mode is typically used for applications that require real-time input, such as games or other interactive programs.

In canonical mode, various editing features are provided by the terminal driver, such as backspacing and line editing. Non-canonical mode, on the other hand, provides no editing facilities, and it is up to the program to perform any required editing of input.

@saladuit saladuit moved this from 🔨In Progress to 👨‍💻Todo in Minishell Feb 24, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
💫enhancement New feature or request
Projects
Status: 👨‍💻Todo
Development

No branches or pull requests

1 participant