Question

I'm writing a little shell for class. I call execvp for non-built-in commands. For a while in developing this, it returned normally with -1 and all was right. Now, however, I can type anything in to the shell, it starts a process which immediately terminates with status 11 (SIGSEGV, a segmentation fault).

Meta: I have tried cutting this down into a SSCCE but was unsuccessful: when I remove everything that surrounds the execvp, it functions normally. I'm happy to provide the full source, but at over 750 lines that seems excessive at this point.

Here is the function in which execvp is called.

void eval(char *cmdline)
{
char **argv = malloc(MAXLINE);
int bg = parseline(cmdline,argv);

if (argv[0] == 0)
{
    free(argv);
    return;
}
if(builtin_cmd(argv) == 0)
{
    pid_t pid;
    sigset_t set, oset;

    /* set mask */
    sigemptyset(&set);
    sigaddset(&set, SIGCHLD);
    sigprocmask(SIG_SETMASK, &set, &oset);

    /* proceed to fork */
    if ((pid = Fork()) == 0)
    {
        app_debug("execvp <%s>", argv[0]);
        setpgid(0,0);
        sigprocmask(SIG_UNBLOCK, &set, &oset);
        execvp(argv[0], argv);
        app_notify("%s command not found.", errno, argv[0]);
        exit(0);
    } else if (pid == -1) {
        app_notify("fork failed");
        return;
    } else {
        addjob(jobs, pid, bg + 1, cmdline);
        sigprocmask(SIG_UNBLOCK, &set, &oset);
        if (bg == 0) {
            app_debug("[%d] (%d) Waiting for %s", pid2jid(pid), pid, cmdline);
            waitfg(pid);
        } else {
            app_notify("[%d] (%d) %s", pid2jid(pid), pid, cmdline);
        }
    }
}
free(argv);
return;
}

Here is parseline.

int parseline(const char *cmdline, char **argv)
{
static char array[MAXLINE]; /* holds local copy of command line */
char *buf = array;          /* ptr that traverses command line */
char *delim;                /* points to first space delimiter */
int argc;                   /* number of args */
int bg;                     /* background job? */

strcpy(buf, cmdline);
buf[strlen(buf)-1] = ' ';  /* replace trailing '\n' with space */
while (*buf && (*buf == ' ')) /* ignore leading spaces */
buf++;

/* Build the argv list */
argc = 0;
if (*buf == '\'') {
buf++;
delim = strchr(buf, '\'');
}
else {
delim = strchr(buf, ' ');
}

while (delim) {
argv[argc++] = buf;
*delim = '\0';
buf = delim + 1;
while (*buf && (*buf == ' ')) /* ignore spaces */
       buf++;

if (*buf == '\'') {
    buf++;
    delim = strchr(buf, '\'');
}
else {
    delim = strchr(buf, ' ');
}
}
argv[argc] = NULL;

if (argc == 0)  /* ignore blank line */
return 1;

/* should the job run in the background? */
if ((bg = (*argv[argc-1] == '&')) != 0) {
argv[--argc] = NULL;
}
return bg;
}
Was it helpful?

Solution

Immediately after the execvp, app_notify("%s command not found.", errno, argv[0]); seg faulted. It essentially wraps a printf and should have only one argument following the quoted string, as there is only one substitution.

The key to debugging this was discovering set follow-fork-mode child for gdb.

Thanks for your help, Jim, et al.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top