Не нужно создавать дочерний процесс с помощью fork для обработки сигнала Ctrl- C в пользовательской оболочке. Одна возможность - использовать обработчик сигнала вместе с sigsetjmp / siglongjmp.
Процедура будет такой:
- установлен обработчик сигнала
- до запуска основного l oop, среда вызова сохраняется в env используя sigsetjmp
- в обработчике сигнала, вызов siglongjmp () восстанавливает среду, сохраненную sigsetjmp (), и выполняет нелокальный переход к месту, где sigsetjmp был вызван
, так как обработчики сигналов всегда можно вызывать, возможно, даже перед вызовом sigsetjmp () необходимо убедиться, что siglongjmp () уже может быть вызван. Это делается путем установки изменчивой переменной sig_atomic_t с именем jmp_set.
Функция process
знает только одну внутреннюю команду с именем exit
. Как уже отмечалось в комментариях к вопросу, если пользователь вводит Ctrl-D в качестве первого символа в начале строки, это автоматически приводит к EOF в вызове getchar. Функция get_cmd затем возвращает здесь команду exit
. Кроме того, пользователь может ввести команду exit
для завершения программы.
В функции process
позиция помечена комментарием, в котором вы, вероятно, захотите вызывать внешние программы с помощью fork / exe c. , Возвращает bool, должна ли программа быть закрыта или нет. Возможно, здесь можно было бы также оценить код состояния по вызовам внешних программ.
Вот небольшая, автономная примерная программа с вашим ctrlc_handler, get_cmd и компоновкой функций процесса, расширенная с помощью sigsetjmp () / siglongjmp (), конечно, не завершено, но может быть отправной точкой:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <setjmp.h>
#define MAX_CMD_LENGTH 1024
static const char *const EXIT_CMD = "exit";
static sigjmp_buf env;
static volatile sig_atomic_t jmp_set;
static void ctrlc_handler(int signal) {
if (jmp_set == 0)
return;
if (signal == SIGINT) {
siglongjmp(env, 1);
}
}
static char *get_cmd(void) {
char *cmd_buf = calloc(MAX_CMD_LENGTH, sizeof(char));
if (!cmd_buf) {
perror("malloc failed\n");
exit(1);
}
char *ptr = cmd_buf;
int ch = getchar();
while (ch != EOF && ch != '\n' && ptr < cmd_buf + MAX_CMD_LENGTH - 1) {
*ptr++ = (char) ch;
ch = getchar();
}
if (ch == EOF) {
strcpy(cmd_buf, EXIT_CMD);
}
return cmd_buf;
}
static bool process(char *cmd) {
if (strcmp(cmd, EXIT_CMD) == 0) {
return true;
}
// a call to fork together with a function from the
// exec family could be used to call external programs
printf("process '%s'\n", cmd);
return false;
}
static int cnt = 0;
int main(void) {
if (signal(SIGINT, ctrlc_handler) == SIG_ERR) {
perror("signal error");
exit(1);
}
if (sigsetjmp(env, 1)) {
printf("\n");
cnt++;
}
jmp_set = 1;
bool exit;
do {
printf("%d> ", cnt);
char *cmd = get_cmd();
exit = process(cmd);
free(cmd);
} while (!exit);
return 0;
}