Returning from Signal Handlers - c

Am I not leaving my signal handler function in the correct way? It does not seem to return to the program normally. Instead it goes into the loop and where it should wait for user input, it skips and reads the length of the "user input" to -1 and errors out. (Will make more sense in code.)
void handle_SIGINT() {
int k = recent;
int count = 0;
int stop;
if (stringSize >= 10) {
stop = 10;
}
else {
stop = p;
}
printf("\nCommand History:\n");
for (count = 0; count < stop; count++) {
if (k < 0) {
k += 10;
}
printf("%s", string[abs(k)]);
k -= 1;
}
}
void setup(char inputBuffer[], char *args[],int *background)
{
//char inputBuffer[MAX_LINE];
int length, /* # of characters in the command line */
i, /* loop index for accessing inputBuffer array */
start, /* index where beginning of next command parameter is */
ct; /* index of where to place the next parameter into args[] */
int add = 1;
ct = 0;
/* read what the user enters on the command line */
length = read(STDIN_FILENO, inputBuffer, MAX_LINE);
printf("%i",length);
start = -1;
if (length == 0)
exit(0); /* ^d was entered, end of user command stream */
if (length < 0){
perror("error reading the commanddddddddd");
exit(-1); /* terminate with error code of -1 */
}
}
int main(void)
{
char inputBuffer[MAX_LINE]; /* buffer to hold the command entered */
int background; /* equals 1 if a command is followed by '&' */
char *args[MAX_LINE/2+1];/* command line (of 80) has max of 40 arguments */
FILE *inFile = fopen("pateljay.history", "r");
if (inFile != NULL) {
int count = 0;
char line[MAX_LINE];
while (fgets(line,sizeof line, inFile) != NULL) {
string[count] = strdup(line);
//string[count][strlen(line)] = '\n';
//string[count][strlen(line) + 1] = '\0';
printf("%s", string[count]);
count++;
stringSize++;
}
p = count % 10;
recent = abs(p - 1);
}
fclose(inFile);
/* set up the signal handler */
struct sigaction handler;
handler.sa_handler = handle_SIGINT;
sigaction(SIGINT, &handler, NULL);
while (1) {/* Program terminates normally inside setup */
background = 0;
printf("COMMAND->");
fflush(0);
setup(inputBuffer, args, &background);/* get next command */
}
}
So when ctrl+c is entered it should catch the signal and handle it. Once it returns back to main, it goes into setup and completely skips the read function and makes length equal to -1. This in turn errors out the program. I think the code inside handle_SIGINT is irrelevant as it is right now. Does anyone know any reason why it would be skipping the read function in setup?

read is blocking, waiting for input. SIGINT arrives. The kernel calls your signal handler. When your signal handler returns, the kernel makes read return -1 and set errno to EINTR. You need to check for this case and handle it by calling read again:
do {
length = read(STDIN_FILENO, inputBuffer, MAX_LINE);
} while (length == -1 && errno == EINTR);

The signal handler is supposed to take an int argument:
void handle_sigint(int signum) {}

Related

Is there any way to change sigaction flags during execution?

I have this child process in infinite loop and i want it to stop the loop when recive SIGUSR1 from parent pid.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
int GameOver = 0;
jmp_buf here; // <------- After Joshua's Answer
void trataSIGUSR1(int sig, siginfo_t *info, void *extra);
int main(int argc, char** argv){
int someNumber = 0, score = 0;
char word[15],c;
struct sigaction new_action;
// new_action.sa_flags = SA_SIGINFO; // <------- Before Joshua's Answer
new_action.sa_flags = SA_SIGINFO | SA_RESTART; // <------- After Joshua's Answer
new_action.sa_sigaction = &trataSIGUSR1;
sigfillset(&new_action.sa_mask);
if (sigaction(SIGUSR1, &new_action, NULL) == -1){
perror("Error: cannot handle SIGUSR1"); // não deve acontecer
return EXIT_FAILURE;
}
FILE *f;
f = fopen("randomfile.txt", "r");
if (f == NULL){
printf("Errr Opening File!\n");
return EXIT_FAILURE;
}
// setjmp(here); // <------- After Joshua's Answer
sigsetjmp(here,1); // <-- After wildplasser's Answer
while (!GameOver){
fscanf(f, "%s", word);
printf("\nWord -> %s\n", word);
if(!scanf("%d", &someNumber)){
puts("Invalid Value!");
while ((c = getchar()) != '\n' && c != EOF);
continue;
}
if(someNumber == strlen(word) && !GameOver)
score ++;
if(feof(f)){
printf("\nEnd of file.\n");
break;
}
}
if( GameOver )
puts("\nAcabou o tempo!"); // <-- After wildplasser's Answer
fclose(f);
return score;
}
void trataSIGUSR1(int sig, siginfo_t *info, void *extra){
if (info->si_pid == getppid()){ // only end when parent send SIGUSR1
// puts("\nAcabou o tempo!"); // <-- Before wildplasser's Answer
GameOver = 1;
// longjmp(here,1); // <------- After Joshua's Answer
siglongjmp(here,1); // <---- After wildplasser's Answer
}
}
It works fine but if i send SIGUSR1 to child pid from another process scanf get interupted... I want to interupt the scanf and automaticly stop the loop only when signal come from parent, in other case just ignore. Is there any way to change the flag to new_action.sa_flags = SA_RESTART; when signal comes from other process?!
There are several possibilities, ranging from a huge hack, to proper (but complicated).
The simplest thing is to have the SIGUSR1 from parent reopen standard input to /dev/null. Then, when scanf() fails, instead of complaining and retrying, you can break out of the loop if feof(stdin) is true. Unfortunately, freopen() is not async-signal safe, so this is not a standards (POSIX, in this case) compliant way of doing things.
The standards-compliant way of doing things is to implement your own read input line into a dynamically allocated string -type of function, which detects when the signal handler sets the flag. The flag should also be of volatile sig_atomic_t type, not an int; the volatile in particular tells the compiler that the value may be changed unexpectedly (by the signal handler), so whenever referenced, the compiler must re-read the variable value, instead of remembering it from a previous access. The sig_atomic_t type is an atomic integer type: the process and the signal handler will only ever see either the new, or the old value, never a mix of the two, but might have as small valid range as 0 to 127, inclusive.
Signal delivery to an userspace handler (installed without SA_RESTART) does interrupt a blocking I/O operation (like read or write; in the thread used for signal delivery – you only have one, so that will always be used), but it might occur between the flag check and the scanf(), so in this case, it is not reliable.
The proper solution here is to not use stdin at all, and instead use the low-level <unistd.h> I/O for this. Note that it is imperative to not mix stdin/scanf() and low-level I/O for the same stream. You can safely use printf(), fprintf(stdout, ...), fprintf(stderr, ...), and so on. The reason is that the C library internal stdin stream structure will not be updated correctly by our low-level access, and will be out-of-sync with reality if we mix both (for the same stream).
Here is an example program showing one implementation (licensed under Creative Commons Zero v1.0 International – do as you wish with it, no guarantees though):
// SPDX-License-Identifier: CC0-1.0
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <poll.h>
#include <signal.h>
#include <time.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
/* Maximum poll() timeout, in milliseconds, so that done flag is checked often enough.
*/
#ifndef DONE_POLL_INTERVAL_MS
#define DONE_POLL_INTERVAL_MS 100
#endif
static volatile sig_atomic_t done = 0;
static void handle_done(int signum, siginfo_t *info, void *context)
{
/* This silences warnings about context not being used. It does nothing. */
(void)context;
if (signum == SIGUSR1 && info->si_pid == getppid()) {
/* SIGUSR1 is only accepted if it comes from the parent process */
done = 1;
} else {
/* All other signals are accepted from all processes (that have the necessary privileges) */
done = 1;
}
}
static int install_done(const int signum)
{
struct sigaction act;
memset(&act, 0, sizeof act);
sigemptyset(&(act.sa_mask));
act.sa_sigaction = handle_done;
act.sa_flags = SA_SIGINFO;
return sigaction(signum, &act, NULL);
}
/* Our own input stream structure type. */
struct input {
int descriptor;
char *data;
size_t size;
size_t head;
size_t tail;
};
/* Associating an input stream with a file descriptor.
Do not mix stdin use and input stream on descriptor STDIN_FILENO!
*/
static int input_use(struct input *const in, const int descriptor)
{
/* Check that the parameters are not obviously invalid. */
if (!in || descriptor == -1) {
errno = EINVAL;
return -1;
}
/* Set the descriptor nonblocking. */
{
int flags = fcntl(descriptor, F_GETFL);
if (flags == -1) {
/* errno set by fcntl(). */
return -1;
}
if (fcntl(descriptor, F_SETFL, flags | O_NONBLOCK) == -1) {
/* errno set by fcntl(). */
return -1;
}
}
/* Initialize the stream structure. */
in->descriptor = descriptor;
in->data = NULL;
in->size = 0;
in->head = 0;
in->tail = 0;
/* Success. */
return 0;
}
/* Read until delimiter from an input stream.
* If 'done' is set at any point, will return 0 with errno==EINTR.
* Returns 0 if an error occurs, with errno set.
* Returns 0 with errno==0 when end of input stream.
*/
static size_t input_getdelim(struct input *const in,
int const delim,
char **const dataptr,
size_t *const sizeptr,
const double timeout)
{
const clockid_t timeout_clk = CLOCK_BOOTTIME;
struct timespec then;
/* Verify none of the pointers are NULL. */
if (!in || !dataptr || !sizeptr) {
errno = EINVAL;
return 0;
}
/* Record current time for timeout measurement. */
clock_gettime(timeout_clk, &then);
char *line_data = *dataptr;
size_t line_size = *sizeptr;
/* If (*sizeptr) is zero, then we ignore dataptr value, like getline() does. */
if (!line_size)
line_data = NULL;
while (1) {
struct timespec now;
struct pollfd fds[1];
ssize_t n;
int ms = DONE_POLL_INTERVAL_MS;
/* Done flag set? */
if (done) {
errno = EINTR;
return 0;
}
/* Is there a complete line in the input buffer? */
if (in->tail > in->head) {
const char *ptr = memchr(in->data + in->head, delim, in->tail - in->head);
if (ptr) {
const size_t len = ptr - (in->data + in->head);
if (len + 2 > line_size) {
/* Since we do not have any meaningful data in line_data,
and it would be overwritten anyway if there was,
instead of reallocating it we just free an allocate it. */
free(line_data); /* Note: free(null) is safe. */
line_size = len + 2;
line_data = malloc(line_size);
if (!line_data) {
/* Oops, we lost the buffer. */
*dataptr = NULL;
*sizeptr = 0;
errno = ENOMEM;
return 0;
}
*dataptr = line_data;
*sizeptr = line_size;
}
/* Copy the line, including the separator, */
memcpy(line_data, in->data + in->head, len + 1);
/* add a terminating nul char, */
line_data[len + 1] = '\0';
/* and update stream buffer state. */
in->head += len + 1;
return len + 1;
}
/* No, we shall read more data. Prepare the buffer. */
if (in->head > 0) {
memmove(in->data, in->data + in->head, in->tail - in->head);
in->tail -= in->head;
in->head = 0;
}
} else {
/* Input buffer is empty. */
in->head = 0;
in->tail = 0;
}
/* Do we need to grow input stream buffer? */
if (in->head >= in->tail) {
/* TODO: Better buffer size growth policy! */
const size_t size = (in->tail + 65535) | 65537;
char *data;
data = realloc(in->data, size);
if (!data) {
errno = ENOMEM;
return 0;
}
in->data = data;
in->size = size;
}
/* Try to read additional data. It is imperative that the descriptor
has been marked nonblocking, as otherwise this will block. */
n = read(in->descriptor, in->data + in->tail, in->size - in->tail);
if (n > 0) {
/* We read more data without blocking. */
in->tail += n;
continue;
} else
if (n == 0) {
/* End of input mark (Ctrl+D at the beginning of line, if a terminal) */
const size_t len = in->tail - in->head;
if (len < 1) {
/* No data buffered, read end of input. */
if (line_size < 1) {
line_size = 1;
line_data = malloc(line_size);
if (!line_data) {
errno = ENOMEM;
return 0;
}
*dataptr = line_data;
*sizeptr = line_size;
}
line_data[0] = '\0';
errno = 0;
return 0;
}
if (len + 1 > line_size) {
/* Since we do not have any meaningful data in line_data,
and it would be overwritten anyway if there was,
instead of reallocating it we just free an allocate it. */
free(line_data); /* Note: free(null) is safe. */
line_size = len + 1;
line_data = malloc(line_size);
if (!line_data) {
/* Oops, we lost the buffer. */
*dataptr = NULL;
*sizeptr = 0;
errno = ENOMEM;
return 0;
}
*dataptr = line_data;
*sizeptr = line_size;
}
memmove(line_data, in->data, len);
line_data[len] = '\0';
in->head = 0;
in->tail = 0;
return 0;
} else
if (n != -1) {
/* This should never occur; it would be a C library bug. */
errno = EIO;
return 0;
} else {
const int err = errno;
if (err != EAGAIN && err != EWOULDBLOCK && err != EINTR)
return 0;
/* EAGAIN, EWOULDBLOCK, and EINTR are not real errors. */
}
/* Nonblocking operation, with timeout == 0.0? */
if (timeout == 0.0) {
errno = ETIMEDOUT;
return 0;
} else
if (timeout > 0.0) {
/* Obtain current time. */
clock_gettime(timeout_clk, &now);
const double elapsed = (double)(now.tv_sec - then.tv_sec)
+ (double)(now.tv_nsec - then.tv_nsec) / 1000000000.0;
/* Timed out? */
if (elapsed >= (double)timeout / 1000.0) {
errno = ETIMEDOUT;
return 0;
}
if (timeout - elapsed < (double)DONE_POLL_INTERVAL_MS / 1000.0) {
ms = (int)(1000 * (timeout - elapsed));
if (ms < 1) {
errno = ETIMEDOUT;
return 0;
}
}
}
/* Negative timeout values means no timeout check,
and ms retains its initialized value. */
/* Another done check; it's cheap. */
if (done) {
errno = 0;
return EINTR;
}
/* Wait for input, but not longer than ms milliseconds. */
fds[0].fd = in->descriptor;
fds[0].events = POLLIN;
fds[0].revents = 0;
poll(fds, 1, ms);
/* We don't actually care about the result at this point. */
}
/* Never reached. */
}
static inline size_t input_getline(struct input *const in,
char **const dataptr,
size_t *const sizeptr,
const double timeout)
{
return input_getdelim(in, '\n', dataptr, sizeptr, timeout);
}
int main(void)
{
struct input in;
char *line = NULL;
size_t size = 0;
size_t len;
if (install_done(SIGINT) == -1 ||
install_done(SIGHUP) == -1 ||
install_done(SIGTERM) == -1 ||
install_done(SIGUSR1) == -1) {
fprintf(stderr, "Cannot install signal handlers: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
if (input_use(&in, STDIN_FILENO)) {
fprintf(stderr, "BUG in input_use(): %s.\n", strerror(errno));
return EXIT_FAILURE;
}
while (!done) {
/* Wait for input for five seconds. */
len = input_getline(&in, &line, &size, 5000);
if (len > 0) {
/* Remove the newline at end, if any. */
line[strcspn(line, "\n")] = '\0';
printf("Received: \"%s\" (%zu chars)\n", line, len);
fflush(stdout);
continue;
} else
if (errno == 0) {
/* This is the special case: input_getline() returns 0 with
errno == 0 when there is no more input. */
fprintf(stderr, "End of standard input.\n");
return EXIT_SUCCESS;
} else
if (errno == ETIMEDOUT) {
printf("(No input for five seconds.)\n");
fflush(stdout);
} else
if (errno == EINTR) {
/* Break or continue works here, since input_getline() only
returns 0 with errno==EINTR if done==1. */
break;
} else {
fprintf(stderr, "Error reading from standard input: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
}
printf("Signal received; done.\n");
return EXIT_SUCCESS;
}
Save it as e.g. example.c, compile using e.g. gcc -Wall -Wextra -O2 example.c -o example, and run using ./example. Type input and enter to supply lines, or Ctrl+D at the beginning of a line to end input, or Ctrl+C to send the process a SIGINT signal.
Note the compile-time constant DONE_POLL_INTERVAL_MS. If the signal is delivered between a done check and poll(), this is the maximum delay, in milliseconds (1000ths of a second), that the poll may block; and therefore is roughly the maximum delay from receiving the signal and acting upon it.
To make the example more interesting, it also implements a timeout on reading a full line also. The above example prints when it is reached, but that messes up how the user sees the input they're typing. (It does not affect the input.)
This is by no means a perfect example of such functions, but I hope it is a readable one, with the comments explaining the reasoning behind each code block.
Historically we solved this problem by always setting SA_RESTART and calling longjump() to get out of the signal handler when the condition is met.
The standard makes this undefined but I think this does the right thing when stdin is connected to the keyboard. Don't try it with redirected handles. It won't work well. At least you can check for this condition with isatty(0).
If it doesn't work and you are bent on using signals like this, you'll need to abandon scanf() and friends and get all your input using read().

Copying a c-string from 1 buffer to another in a child process using memcpy or strcpy does not seem to work

I am trying to write a mock shell that saves command line history and overrides the signal action for SIGINT to trigger printing the previous 10 commands entered by the user. As far as I am aware, everything from handling all of the signals to updating cursor and running commands using execvp works fine.
I however am running into 2 problems that I am having a hard time trying to wrap my head around.
Trouble trying to actually copy the contents of the input buffer to my own c-string vector, namely histv. After calling read and storing user input in buf, I try to copy the contents of buf to the location at histv[cursor % MAX_HISTORY] (the reason I am using modulus is because it seems easier to use a kind of circular array instead of handling the case that cursor rises to some number greater than MAX_HISTORY)
When I try running the process as a background process I always get an execvp error, even when the command is valid. I know it's happing after the parent process gets the SIGUSR2 signal and creates a new child to run the commands. So I am assuming it has something to do with what happens to argv after the child process kills itself and raises SIGUSR2
Below is the code for the entire program. It is a bit messy in some spots, but overall it's rather simple. I have used all of memcpy, strcpy, and strncpy to no avail. They all compile and run without errors, but none of them seem to do anything.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/wait.h>
// limits
#define MAX_LINE 80
#define MAX_HISTORY 10
// function headers
void print_history(unsigned int const, char**);
void handler_func(int);
void read_func(char*[], char[], char**, unsigned int const);
int parse_args(char*, char**, size_t);
// globals
volatile sig_atomic_t sig_caught = 0;
void print_history (const unsigned int cursor, char **histv) {
int temp = (cursor > MAX_HISTORY) ? (cursor - MAX_HISTORY) : 0;
puts("\n\nprinting the previous ten commands...");
printf("cursor %d", cursor);
for (int i = 1; temp < cursor; temp++) {
printf("%d%s\n", i++, histv[temp % MAX_HISTORY]);
}
}
void handler_func(int sig)
{
/* update loop control variable */
sig_caught = 1;
}
int main(void)
{
// declare sigaction struct
struct sigaction sigactor;
// initialize sigaction struct
sigactor.sa_handler = handler_func;
sigemptyset(&sigactor.sa_mask);
sigactor.sa_flags = 0;
// set up sigaction for SIGINT
if (sigaction(SIGINT, &sigactor, NULL) == -1) {
perror("siagction() failed");
_exit(EXIT_FAILURE);
}
// set the buffer to no buffering
setvbuf(stdout, NULL, _IONBF, 0);
unsigned int cursor = 0;
/* initlialize history vector */
char **histv = (char**)malloc(sizeof(char*) * MAX_HISTORY);
for (int i = 0; i < MAX_HISTORY; i++)
histv[i] = (char*)malloc(sizeof(char) * MAX_LINE);
// enter shell loop
while (1) {
/* fork process and get child pid */
int cpid;
char *argsv[MAX_LINE/2+1];
char buf[MAX_LINE];
while(!sig_caught) {
cpid = fork();
/* child */
if (cpid == 0) {
read_func(argsv, buf, histv, cursor);
}
/* fork error */
else if (cpid < 0) {
perror("Error forking process");
_exit(EXIT_FAILURE);
}
/* parent process begins here */
else {
/* variable to store status returned from child*/
int cstatus;
/* suspend parent until child exits *
* store return status in cstatus */
waitpid(cpid, &cstatus, 0);
/* get status from child process and check for SIGTERM *
* SIGTERM is raised by child when someone enters '!q' */
switch(WTERMSIG(cstatus))
{
/* user wants to quit */
case SIGTERM:
puts("User issued quit command");
for (int i = 0; i < MAX_HISTORY; i++)
free((void *)histv[i]);
free((void *)histv);
_exit(EXIT_SUCCESS);
/* invalid string length */
case SIGUSR1:
puts("Please enter a valid string");
break;
/* background process */
case SIGUSR2:
cpid = fork();
if (cpid < 0) perror("Error forking process...");
else if (cpid == 0) {
if (execvp(argsv[0], argsv) < 0) {
--cursor;
perror("execvp");
kill(getpid(), SIGUSR1);
}
}
}
if (!sig_caught) cursor++;
}
}// signal loop
kill (cpid, SIGTERM);
print_history(cursor, histv);
fflush(stdout);
sig_caught = 0;
}
}
void read_func(char *argsv[], char buf[], char *histv[], unsigned int const cursor)
{
printf("\nCMD > ");
int background = 0;
size_t length = read(STDIN_FILENO, buf, MAX_LINE);
if (length > 80 || length <= 0) kill(getpid(), SIGUSR1);
/* copy buffer into history and update cursor */
memcpy(histv[cursor % MAX_HISTORY], buf, length);
printf("cursor %d", cursor);
/* parse arguments and return number of arguments */
background = parse_args(buf, argsv, length);
/* user entered quit command or string is invalid */
if (background == -1) kill(getpid(), SIGTERM);
/* signal parent to run process in the background */
if (background == 1) kill(getpid(), SIGUSR2);
/* run command */
if (execvp(argsv[0], argsv) < 0) {
perror("execvp");
_exit(EXIT_FAILURE);
}
}
int parse_args(char buf[], char *argsv[], size_t length)
{
int i, /* loop index for accessing buf array */
start, /* index where beginning of next command parameter is */
ct, /* index of where to place the next parameter into args[] */
bckg; /* background flag */
/* read what the user enters on the command line */
ct = 0;
start = -1;
bckg = 0;
if (buf[0] == '!' && buf[1] == 'q') return -1;
/* examine every character in the buf */
for (i = 0; i < length; i++) {
switch (buf[i]){
case ' ':
case '\t': /* argument separators */
if(start != -1){
argsv[ct] = &buf[start]; /* set up pointer */
ct++;
}
buf[i] = '\0'; /* add a null char; make a C string */
start = -1;
break;
case '\n': /* should be the final char examined */
if (start != -1){
argsv[ct] = &buf[start];
ct++;
}
buf[i] = '\0';
argsv[ct] = NULL; /* no more arguments to this command */
break;
case '&':
bckg = 1;
buf[i] = '\0';
break;
default: /* some other character */
if (start == -1)
start = i;
}
}
argsv[ct] = NULL; /* just in case the input line was > 80 */
return bckg;
}
Side note, the parse_args function was initially given to us and I changed it a bit to work with the rest of program, but for the most part I did not write that function.
Please go easy on me, it's been a long time since I've used C for anything and a lot of what I am doing here took a lot of effort to begin understanding how this program behaves. (~:
So both of the problems was with the fact that the memory wasn't being shared, as o11c described. I fixed the issue by using mmap instead of malloc which in turn simplified my program as I no longer had to handle the memory management. Changes are described below.
char **histv = (char**)malloc(sizeof(char*) * MAX_HISTORY);
for (int i = 0; i < MAX_HISTORY; i++)
histv[i] = (char*)malloc(sizeof(char) * MAX_LINE);
was changed to
char **histv = (char**)mmap(NULL, (sizeof(char*) * MAX_HISTORY), (PROT_READ | PROT_WRITE), (MAP_SHARED | MAP_ANONYMOUS), -1, 0);
for (int i = 0; i < MAX_HISTORY; i++) histv[i] = (char*)mmap(NULL, (sizeof(char) * MAX_LINE), (PROT_READ | PROT_WRITE), (MAP_SHARED | MAP_ANONYMOUS), -1, 0);
While I do not know if this is the best way to do this, it solved my problem.
Also note that I did explicitly unmap the memory using munmap even though it technically should be handled automatically on exit.

Can unexecuted code cause a segfault?

Yeah I know that sounds crazy but that is the only way I can describe it at this point. I’m writing a program for a class that mimics a terminal, in that it takes commands as inputs and executes them. (I’ll put some code below) As you will see, the program holds a history of commands historyArgs so that the user can execute recent commands.
Command history is listed when the user performs Ctrl-C. Recent commands are accessed with the command 'r' (for most recent) and r 'x' (where x is matches the first letter of a command in recent history). When I started implementing the 'r' command, I started getting this segfault. I then reverted all my changes and added one line at a time. I found that adding even primitive variable declaration causes a segfault (int temp = 10;) But this is where it gets stranger. I believe the line that causes a segfault (int temp = 10;) is never accessed. I put printf statements and flush the output at the beginning of the if block to see if the block has been entered, but they don't execute.
setup was provided for us. It takes the user input and puts it in char *args[] i.e. input = ls -a -C, args = {"ls", "-a", "-C", NULL, ... NULL}. I marked the line in main that somehow leads to a segfault.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#define BUFFER_SIZE 50
static char buffer[BUFFER_SIZE];
#define MAX_LINE 80 /* 80 chars per line, per command, should be enough. */
char *historyArgs[10][MAX_LINE/2 + 1];
int historyCount = 0;
int indexOfLatestCommand = 0;
/* the signal handler function */
void handle_SIGINT() {
//write(STDOUT_FILENO,buffer,strlen(buffer));
if(historyCount > 0){
printf("\n%i command(s), printing most recent:\n", historyCount);
int i;
for(i = 0; i < historyCount && i < 10; i++){
printf("%i.] %s ", i+1, historyArgs[i][0]);
//print args
int j = 1;
while(historyArgs[i][j] != NULL){
printf("%s ", historyArgs[i][j]);
j++;
}
printf("\n");
}
}
else{
printf("\nNo recent commands.\n");
}
fflush(stdout);
}
/**
* setup() reads in the next command line, separating it into distinct tokens
* using whitespace as delimiters. setup() sets the args parameter as a
* null-terminated string.
*/
void setup(char inputBuffer[], char *args[],int *background)
{
int length, /* # of characters in the command line */
i, /* loop index for accessing inputBuffer array */
start, /* index where beginning of next command parameter is */
ct; /* index of where to place the next parameter into args[] */
ct = 0;
/* read what the user enters on the command line */
length = read(STDIN_FILENO, inputBuffer, MAX_LINE);
start = -1;
if (length == 0)
//exit(0); /* ^d was entered, end of user command stream */
if (length < 0){
perror("error reading the command");
exit(-1); /* terminate with error code of -1 */
}
/* examine every character in the inputBuffer */
for (i = 0; i < length; i++) {
switch (inputBuffer[i]){
case ' ':
case '\t' : /* argument separators */
if(start != -1){
args[ct] = &inputBuffer[start]; /* set up pointer */
ct++;
}
inputBuffer[i] = '\0'; /* add a null char; make a C string */
start = -1;
break;
case '\n': /* should be the final char examined */
if (start != -1){
args[ct] = &inputBuffer[start];
ct++;
}
inputBuffer[i] = '\0';
args[ct] = NULL; /* no more arguments to this command */
break;
case '&':
*background = 1;
inputBuffer[i] = '\0';
break;
default : /* some other character */
if (start == -1)
start = i;
}
}
args[ct] = NULL; /* just in case the input line was > 80 */
}
int main(void)
{
int i;
char inputBuffer[MAX_LINE]; /* buffer to hold the command entered */
int background; /* equals 1 if a command is followed by '&' */
char* args[MAX_LINE/2+1];/* command line (of 80) has max of 40 arguments */
int status;
struct sigaction handler;
handler.sa_handler = handle_SIGINT;
sigaction(SIGINT, &handler, NULL);
strcpy(buffer,"Caught <ctrl><c>\n");
while (1){ /* Program terminates normally inside setup */
background = 0;
printf("COMMAND->");
fflush(0);
setup(inputBuffer, args, &background); /* get next command */
//If command wasn't empty
if(args[0] != NULL){
if(strcmp(args[0], "r") != 0){
//Copy into history if not a recent call
for(i = 0; i < MAX_LINE/2+1 && args[i] != NULL; i++){
historyArgs[historyCount%10][i] = malloc(strlen(args[i]));
strcpy(historyArgs[historyCount%10][i], args[i]);
}
indexOfLatestCommand = historyCount%10;
historyCount++;
}
//Create child process
int pid = fork();
//In child process
if(pid == 0){
if(strcmp(args[0], "r") == 0){
//If only "r" was entered, execute most recent command
if(args[1] == NULL){
printf("Entering recent?\n");
fflush(stdout);
int temp = 10; //SEGFAULTS HERE IF THIS IS INCLUDED
execvp(historyArgs[indexOfLatestCommand][0], &historyArgs[indexOfLatestCommand][0]);
}
else{
//Find in args[1][0] history, run if found
for(i = indexOfLatestCommand; i >= 0; i--){
if(historyArgs[i][0][0] == args[1][0]){
execvp(historyArgs[i][0], &historyArgs[i][0]);
break;
}
}
if(i == -1){
for(i = historyCount > HISTORY_SIZE ? HISTORY_SIZE : historyCount; i > indexOfLatestCommand; i--){
if(historyArgs[i][0][0] == args[1][0])
execvp(historyArgs[i][0], &historyArgs[i][0]);
break;
}
}
}
}
else execvp(args[0], &args[0]);
}
//In parent process
else if (pid > 0){
/*If child isn't a background process,
wait for child to terminate*/
if(background == 0)
while(wait(&status) != pid);
}
}
}
}
Another thing worth mentioning is that declaring a variable in that spot doesn't cause a segfault. Only assigning a value to a new variable does. Reassigning globals in that section also doesn't cause a segfault.
EDIT: What triggers a crash. Commands execute correctly. When you run it, you can type in any command, and that should work. It isn't until I perform Ctrl-C and print out the history that the program segfaults.
Example input:
ls
ls -a
grep
Ctrl-C
HEADS UP: if you decide to run this, know that to end the task, you will probably need to use the kill command because I haven't implement "q" to quit.
Symptoms like you see (unrelated code changes appear to affect the nature of a crash) usually mean that your program caused undefined behaviour earlier. The nature of the behaviour changes because your program is relying on garbage values it has read or written at some stage.
To debug it, try and remove all sources of undefined behaviour in your program. The most obvious one is the content of your void handle_SIGINT() function. The only things you can portably do in a signaler are:
Set a variable of type volatile sig_atomic_t, or other lock-free type, and return
Do other stuff and call _Exit, abort or similar.
Especially, you cannot call any library functions as they may not be re-entrant.
For a full specification see section 7.14.1 of the current C Standard. If you are also following some other standard, e.g. POSIX, it may specify some other things which are permitted in a signal handler.
If you do not intend to exit then you must set a flag , and then test that flag from your main "thread" later to see if a signal arose.

background process in shell

I want to implement my own shell. I'm stucked on implementing background process . Actually ,I write some piece of code but I'm not sure if it is work or not .
my code :
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define MAX_LINE 80 /* 80 chars per line, per command, should be enough. */
#define HISTORY_SIZE 5 /*keep track of 5 most recent commands*/
int cmd_count; /*global to keep track of most recent commands entered*/
char history[HISTORY_SIZE][MAX_LINE]; /* global so it can be accessed in interrupt handler. */
void viewHistory()
{
int i;
if (cmd_count < 1)
printf("No command history to show. \n");
else {
printf("\n\n");
for (i = (cmd_count >= HISTORY_SIZE) ? cmd_count - HISTORY_SIZE:0;
i < cmd_count; i++)
printf("%d: %s\n",i+1,history[i%HISTORY_SIZE]);
}
//printf("SystemsIIShell->");
}
int setup(char inputBuffer[], char *args[],int *background)
{
int length, /* # of characters in the command line */
i, /* loop index for accessing inputBuffer array */
start, /* index where beginning of next command parameter is */
ct; /* index of where to place the next parameter into args[] */
int temp;
ct = 0;
/* read what the user enters on the command line */
length = read(STDIN_FILENO, inputBuffer, MAX_LINE);
start = -1;
if (length == 0)
exit(0); /* ^d was entered, end of user command stream */
if (length < 0){
perror("error reading the command");
exit(-1); /* terminate with error code of -1 */
}else{
inputBuffer[length]='\0';
if(inputBuffer[0]=='r'){
if(inputBuffer[1]=='r'){
if(cmd_count==0){
printf("No recent command can be found in the history. \n");
return 0;
}
strcpy(inputBuffer,history[(cmd_count)% HISTORY_SIZE]);
}else{
temp = atoi(&inputBuffer[1]);
if(temp < 1 || temp > cmd_count || temp <= cmd_count -HISTORY_SIZE){
printf("Command number cannot be found. \n");
return 0;
}
strcpy(inputBuffer,history[(temp-1)%HISTORY_SIZE]);
}
length = strlen(inputBuffer);
}
cmd_count++;
strcpy(history[(cmd_count-1)%HISTORY_SIZE], inputBuffer);
for (i = 0; i < length; i++) {
if (inputBuffer[i] == '&') {
inputBuffer[i] = '\0';
*background = 1;
--length;
break;
}
}
}
/* examine every character in the inputBuffer */
for (i = 0; i < length; i++) {
switch (inputBuffer[i]){
case ' ':
case '\t' : /* argument separators */
if(start != -1){
args[ct] = &inputBuffer[start]; /* set up pointer */
ct++;
}
inputBuffer[i] = '\0'; /* add a null char; make a C string */
start = -1;
break;
case '\n': /* should be the final char examined */
if (start != -1){
args[ct] = &inputBuffer[start];
ct++;
}
inputBuffer[i] = '\0';
args[ct] = NULL; /* no more arguments to this command */
break;
case '&':
*background = 1;
inputBuffer[i] = '\0';
break;
default : /* some other character */
if (start == -1)
start = i;
}
}
args[ct] = NULL; /* just in case the input line was > 80 */
}
int main(void)
{
char inputBuffer[MAX_LINE]; /* buffer to hold the command entered */
int background; /* equals 1 if a command is followed by '&' */
char *args[MAX_LINE/2+1];/* command line (of 80) has max of 40 arguments */
while (1){ /* Program terminates normally inside setup */
background = 0;
printf("SystemsIIShell->");
fflush(0);
setup(inputBuffer, args, &background); /* get next command */
pid_t child; /* process id for child */
int status; /* status for execvp */
child = fork(); /* create a child process*/
if(child < 0){ /* if the child process didn't return 0, the fork is failed */
printf("Fork failed! \n");
}else if(child==0){ /* child process */
if(inputBuffer[0]=='history' || inputBuffer[0] =='h'){
viewHistory();
return 0;
}
status = execvp(args[0],args);
if(status !=0){
printf("%s: command not found. \n", args[0]);
}
}else{ /* parent process */
if(background == 0)
waitpid(child,&background,0);
}
/* the steps are:
(1) fork a child process using fork()
(2) the child process will invoke execvp()
(3) if background == 0, the parent will wait,
otherwise returns to the setup() function. */
}return 0;
}
I don't add whole code but the other things are true. I call execv and it works. When I write on the console :
output terminal :
$ gedit ------->it works correctly because it is a foreground
$ gedit & -----> it opens a gedit file which name is "&"
$ firefox ---> it works correctly
$ firefox & ---> it opens a firefox window which url is www.&.com
How can fix it ?any suggestions ?
editing part : https://github.com/iqbalhasnan/CSE2431-System-II/blob/master/lab2/lab2.c --> I use this code as a reference
you're not implementing background processes, you're trying to start a background process using the syntax of the already-implemented-shell on your computer (but honestly, it's pretty hard to tell what's going on with that indentation. That's really bad. Can you make it readable, please?). That '&' character is recognised by your shell, not by execvp. Have a look at this similar looking question which was the first hit in a google search for your problem.

Process started by execvp() exits with some of commands

I use this code to run some of shell commands, but it exits after ls command.:
where is my wrong?
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#define MAX_LINE 80 /* The maximum length command */
void setup(char inputBuffer[], char *args[],int *background)
{
int length, i, start, ct;
ct = 0;
/* read what the user enters on the command line */
length = read(STDIN_FILENO,inputBuffer,MAX_LINE);
start = -1;
if (length == 0)
exit(0); /* ^d was entered, end of user command stream */
if ( (length < 0) && (errno != EINTR) ) {
perror("error reading the command");
exit(-1); /* terminate with error code of -1 */
}
printf(">>%s<<",inputBuffer);
for (i=0;i<length;i++){ /* examine every character in the inputBuffer */
switch (inputBuffer[i]){
case ' ':
case '\t' : /* argument separators */
if(start != -1){
args[ct] = &inputBuffer[start]; /* set up pointer */
ct++;
}
inputBuffer[i] = '\0'; /* add a null char; make a C string */
start = -1;
break;
case '\n': /* should be the final char examined */
if (start != -1){
args[ct] = &inputBuffer[start];
ct++;
}
inputBuffer[i] = '\0';
args[ct] = NULL; /* no more arguments to this command */
break;
default : /* some other character */
if (start == -1)
start = i;
if (inputBuffer[i] == '&'){
*background = 1;
inputBuffer[i-1] = '\0';
}
} /* end of switch */
} /* end of for */
args[ct] = NULL; /* just in case the input line was > 80 */
for (i = 0; i <= ct; i++)
printf("args %d = %s\n",i,args[i]);
} /* end of setup routine */
int main(void)
{
char inputBuffer[MAX_LINE]; /*buffer to hold command entered */
int background; /* equals 1 if a command is followed by '&' */
char *args[MAX_LINE/2 + 1]; /*command line arguments */
int should_run = 1; /* flag to determine when to exit program */
while (should_run) {
//background=0;
printf("Msh>");
fflush(stdout);
setup(inputBuffer, args, &background);
execvp(args[0], args);
}
return 0;
}
As Kerrek SB already said, execvp does not return.
To add a little more info: the execv-family of functions replaces your process (running program) with another. This, in cooperation with fork is what happens inside a system() call.
To put it more bluntly:
If you want to run a system command from your C program, and carry on based on "return" value, you should use the system() call. See example.
If you want to spawn a child process, which should run another executable, you should fork, and inside the child process use execv. See the following example.
Remember that execvp does not return.

Resources