/* returns: 0 in case of succes 1 in ... (still not in use) 2 in syntax error -1 in unexpected error */ int parse_cmd_line (cmd_line_t *cmd_line, const char *cmd) { size_t prv_cmd_beg = 0, i, cmd_len; /*used to get return of functions*/ int raux; /*STDIN, STDOUT, STDERR*/ int io_redir_seen=-1; /*return status*/ int ret = 0; sysfail (cmd_line==NULL, -1); cmd_len = strlen (cmd); /*assuming that the last thing is io redirection or nonblocking indicator*/ for (i=0; i < cmd_len && !IS_IO_REDIR (cmd[i]) && !IS_NONBLOCK (cmd[i]); ++i) { if (IS_PIPE (cmd[i])) { raux = pipe_list_push_cmd (cmd_line, cmd + prv_cmd_beg, i-prv_cmd_beg); sysfail (raux<0, -1); prv_cmd_beg = i + 1; } } /*last command (the one that comes after the last pipe)*/ if (prv_cmd_beg < cmd_len) { raux = pipe_list_push_cmd (cmd_line, cmd + prv_cmd_beg, i-prv_cmd_beg); sysfail (raux<0, -1); if (IS_EMPTY_LINE (raux)) { if (cmd_line->pipe_list_head != NULL) ret |= SYNTAX_ERROR; else ret |= EMPTY_LINE; return ret; } prv_cmd_beg = i + 1; } for (;i < cmd_len && !IS_NONBLOCK(cmd[i]); ++i) { /*todo: recognize output redir*/ if (IS_IO_REDIR (cmd[i])) { if (io_redir_seen != -1) cmd_line->io[io_redir_seen] = stringndup (cmd + prv_cmd_beg, i-prv_cmd_beg); prv_cmd_beg = i + 1; io_redir_seen = (IS_INPUT_REDIR (cmd[i])) ? 0 : 1; } else if (IS_PIPE (cmd[i])) ret |= SYNTAX_ERROR; } if (prv_cmd_beg < cmd_len && io_redir_seen != -1) cmd_line->io[io_redir_seen] = stringndup (cmd + prv_cmd_beg, i-prv_cmd_beg); /*if it is non_block it must be the last thing*/ cmd_line->is_nonblock = i < cmd_len && IS_NONBLOCK (cmd[i]); if (i < cmd_len-1) ret |= SYNTAX_ERROR; return ret; }
int try_runcmd (const char* command, int *result, int *io) { int pid, tio[3], tmp_result, nbytes; printf ("\nCommand %s%n", command, &nbytes); printf ("%*s", 20-nbytes, " "); /* Supress output if not redirecting. */ if (!io) { tio[0]=dup(0); tio[1]=dup(1); tio[2]=dup(2); close (0); close(1); close (2); dup(noio[0]); dup(noio[1]); dup(noio[2]); } /* Run the command.*/ runcmd_onexit = finish; pid = runcmd (command, &tmp_result, io); /* Restore output if not redirecting. */ if (!io) { close (0); close (1); close (2); dup(tio[0]); dup(tio[1]); dup(tio[2]); close (tio[0]); close(tio[1]); close (tio[2]); } printf ("(%5d, %8s, %3d, %3sblocking, exec %s)\n\n", pid, IS_NORMTERM(tmp_result) ? "normal" : "abnormal", EXITSTATUS(tmp_result), IS_NONBLOCK(tmp_result) ? "non" : "", IS_EXECOK(tmp_result) ? "success" : "failed"); if (result) *result = tmp_result; return pid; }
int main (int argc, char **argv) { int nbytes; /* Test cases. */ char cmd1[] = "./t1 " strfy(T_MAKEIO); /* Exits EXECFAILSTATUS */ char cmd2[] = "./t1 &"; char cmd3[] = "./t1 " strfy(T_WRITEFIFO) " &"; /* Write to fifo. */ int result, pid, nerrors, rs, redirok, io[3], i, fd, wasnonblock; pthread_t thread; char buffer[4]; struct sigaction act; test_set = "T2"; nerrors = 0; /* Disable standard streams if not redirecting. */ sysfatal ((noio[0] = open ("/dev/null", O_WRONLY)) <0); sysfatal ((noio[1] = open ("/dev/null", O_WRONLY)) <0); sysfatal ((noio[2] = open ("/dev/null", O_WRONLY)) <0); /* Check redirection Redirect subprocess' stdandard input and output to a pipe. Write a predefined watchword and wait for a specific countersign. If subprocess does not read from the pipe or does not write back, assume test failure. */ /* Create the pipe. */ rs = pipe (pipefd); sysfatal (rs<0); /* Use a new thread to write to the pipe so that caller never blocks. This is probably unecessary but we do it anyway. */ pthread_create (&thread, NULL, writepipe, NULL); io[0] = pipefd[0]; /* Redirect standard input. */ io[1] = pipefd[1]; /* Redirect standard output. */ io[2] = 2; /* Dont touch standard error.*/ /* Call runcmd. */ pid = try_runcmd (cmd1, &result, io); /* printf (" Checking...\n"); */ /* Read subprocess reply. If subprocess is not reading from the pipe (redirected standard input) it will block and timeout without having received the watchword, thereby sending an incorrect ack. A correct countersign is sent, otherwise. */ buffer[0]='\0'; nbytes = read (pipefd[0], buffer, T_TOKENSIZE); sysfatal (nbytes<0); buffer[3]=0; /* Check countersign.*/ if (!strcmp(buffer,T_READTHIS)) /* Subprocess answered correctly. */ redirok=1; else /* Subprocess must have timed-out. */ redirok=0; nerrors += check ("whether stdin/stdout are redirected correctly", redirok); /* Check IS_NONBLOCK and runcmd_onexit. Set up a SIGCHLD handler andm ake a nonblocking call. Check IS_NONBLOCK. Check if the handler has been called. */ runcmd_onexit = onexit; pid = try_runcmd (cmd2, &result, NULL); /* printf ("Checking...\n"); */ nerrors += check ("whether nonblock is correctly reported", IS_NONBLOCK(result)); for (i=-0; (i<3) && (!onexit_called); i++) /* Wait a resonable time. */ sleep(1); nerrors += check ("whether runcmd_onexit is called when requested", onexit_called); /* Check parallel execution. Try to communicate with subprocess through a named pipe (FIFO). Send a watchword and checks the countersign. If client is not running in parallel, caller will block. Use timeouts to detect this case. */ /* Create a fifo. */ unlink (T_FIFONAME); rs = mkfifo (T_FIFONAME, 0600); sysfatal (rs<0); buffer[0]='\0'; /* io[0]=0; io[1]=1; io[2]=2; */ /* Set a time out alarm. */ rs = sigaction (SIGALRM, NULL, &act); sysfatal (rs<0); act.sa_handler = giveup; rs = sigaction (SIGALRM, &act, NULL); sysfatal (rs<0); /* Make a nonblocking call. Probe probram will try to open the fifo to read from it. If subprocess is not running in parallel, it will block since caller itself has not opened the fifo as yet, caise the caller to block in a deadlock. Only time out releases caller. */ expired = 0; alarm (TIMEOUT); pid = try_runcmd (cmd3, &result, io); alarm (0); /* Now caller opens the fifo and reads from it. Had a faulty (nonparallel) subprocess been stuck, it would not proceed and write into the fifo. */ fd = open (T_FIFONAME, O_RDONLY); sysfatal (fd<0); /* Caller may read from fifo now. */ nbytes = read (fd, buffer, T_TOKENSIZE); sysfatal (nbytes<0); buffer[nbytes]='\0'; /* Check the countersigh. */ wasnonblock = 0; if (!strcmp(buffer, T_READTHIS)) wasnonblock=1; /* Result is ok if countersigh is correct and caller has not timedout. */ /* printf ("Checking...\n"); */ nerrors += check ("nonblock mode execs in parallel", (!expired) && (wasnonblock)); /* Cleanup. */ unlink (T_FIFONAME); pid = pid; /* Avoid gcc complaints. */ return nerrors; }