static void g_cancellable_open_pipe (GCancellable *cancellable) { GCancellablePrivate *priv; priv = cancellable->priv; if (pipe (priv->cancel_pipe) == 0) { /* Make them nonblocking, just to be sure we don't block * on errors and stuff */ set_fd_nonblocking (priv->cancel_pipe[0]); set_fd_nonblocking (priv->cancel_pipe[1]); set_fd_close_exec (priv->cancel_pipe[0]); set_fd_close_exec (priv->cancel_pipe[1]); if (priv->cancelled) { const char ch = 'x'; gssize c; do c = write (priv->cancel_pipe[1], &ch, 1); while (c == -1 && errno == EINTR); } } }
int main(int argc, char **argv) { struct sigaction sigact; sigset_t sigmask; pid_t pid; int status; int exitcode, myexitcode; int opt; char *arg; int i, r, fd_out, newcmd, argsize = 0; int pipe_fd[MAX_CMDS][2]; int progout_pipe_fd[MAX_CMDS][2]; progname = argv[0]; /* Parse command-line options */ be_verbose = show_help = show_version = 0; opterr = 0; while ( (opt = getopt_long(argc,argv,"+o:M:v",long_opts,(int *) 0))!=-1 ) { switch ( opt ) { case 0: /* long-only option */ break; case 'v': /* verbose option */ be_verbose = 1; verb("verbose mode enabled"); break; case 'o': /* outprog option */ write_progout = 1; progoutfilename = strdup(optarg); verb("writing program #2 output to '%s'", progoutfilename); break; case 'M': /* outmeta option */ outputmeta = 1; metafilename = strdup(optarg); verb("writing metadata to '%s'", metafilename); break; case ':': /* getopt error */ case '?': error(0,"unknown option or missing argument `%c'",optopt); break; default: error(0,"getopt returned character code `%c' ??",(char)opt); } } if ( show_help ) usage(); if ( show_version ) version(PROGRAM,VERSION); if ( argc<=optind ) error(0,"no command specified"); /* Parse commands to be executed */ ncmds = 0; /* Zero-based index to current command in loop, contains #commands specified after loop */ newcmd = 1; /* Is current command newly started? */ for(i=optind; i<argc; i++) { /* Check for commands separator */ if ( strcmp(argv[i],"=")==0 ) { ncmds++; if ( newcmd ) error(0,"empty command #%d specified", ncmds); newcmd = 1; if ( ncmds+1>MAX_CMDS ) { error(0,"too many commands specified: %d > %d", ncmds+1, MAX_CMDS); } continue; } /* Un-escape multiple = at start of argument */ arg = argv[i]; if ( strncmp(arg,"==",2)==0 ) arg++; if ( newcmd ) { newcmd = 0; cmd_name[ncmds] = arg; cmd_nargs[ncmds] = 0; argsize = 5; cmd_args[ncmds] = malloc(argsize*sizeof(void *)); if ( cmd_args[ncmds]==NULL ) error(0,"cannot allocate memory"); } else { if ( cmd_nargs[ncmds]+1>argsize ) { argsize += 10; cmd_args[ncmds] = realloc(cmd_args[ncmds],argsize*sizeof(void *)); if ( cmd_args[ncmds]==NULL ) error(0,"cannot allocate memory"); } cmd_args[ncmds][cmd_nargs[ncmds]++] = arg; } } ncmds++; if ( newcmd ) error(0,"empty command #%d specified", ncmds); if ( ncmds!=2 ) { error(0,"%d commands specified, 2 required", ncmds); } /* Install TERM signal handler */ if ( sigemptyset(&sigmask)!=0 ) error(errno,"creating signal mask"); if ( sigprocmask(SIG_SETMASK, &sigmask, NULL)!=0 ) { error(errno,"unmasking signals"); } if ( sigaddset(&sigmask,SIGTERM)!=0 ) error(errno,"setting signal mask"); sigact.sa_handler = terminate; sigact.sa_flags = SA_RESETHAND | SA_RESTART; sigact.sa_mask = sigmask; if ( sigaction(SIGTERM,&sigact,NULL)!=0 ) { error(errno,"installing signal handler"); } validator_exited_first = 0; submission_still_alive = 1; /* Create pipes and by default close all file descriptors when executing a forked subcommand, required ones are reset below. */ for(i=0; i<ncmds; i++) { if ( pipe(pipe_fd[i])!=0 ) error(errno,"creating pipes"); verb("command #%d: read fd=%d, write fd=%d", i, pipe_fd[i][0], pipe_fd[i][1]); set_fd_close_exec(pipe_fd[i][0], 1); set_fd_close_exec(pipe_fd[i][1], 1); #ifdef PROC_MAX_PIPE_SIZE resize_pipe(pipe_fd[i][1]); #endif } /* Setup file and extra pipe for writing program output. */ if ( write_progout ) { if ( (progoutfile = fopen(progoutfilename,"w"))==NULL ) { error(errno,"cannot open `%s'",progoutfilename); } for(i=0; i<ncmds; i++) { if ( pipe(progout_pipe_fd[i])!=0 ) error(errno,"creating pipes"); set_fd_close_exec(progout_pipe_fd[i][0], 1); set_fd_close_exec(progout_pipe_fd[i][1], 1); #ifdef PROC_MAX_PIPE_SIZE resize_pipe(progout_pipe_fd[i][1]); #endif verb("writing program #%d output via pipe %d -> %d", i, progout_pipe_fd[i][1], progout_pipe_fd[i][0]); } } /* Execute commands as subprocesses and connect pipes as required. */ for(i=0; i<ncmds; i++) { fd_out = write_progout ? progout_pipe_fd[i][1] : pipe_fd[1-i][1]; cmd_fds[i][0] = pipe_fd[i][0]; cmd_fds[i][1] = fd_out; cmd_fds[i][2] = FDREDIR_NONE; verb("pipes for command #%d are %d and %d", i, cmd_fds[i][0], cmd_fds[i][1]); set_fd_close_exec(pipe_fd[i][0], 0); set_fd_close_exec(fd_out, 0); cmd_exit[i] = -1; cmd_pid[i] = execute(cmd_name[i], (const char **)cmd_args[i], cmd_nargs[i], cmd_fds[i], 0); if ( cmd_pid[i]==-1 ) error(errno,"failed to execute command #%d",i+1); verb("started #%d, pid %d: %s",i+1,cmd_pid[i],cmd_name[i]); set_fd_close_exec(pipe_fd[i][0], 1); set_fd_close_exec(fd_out, 1); } gettimeofday(&start_time, NULL); if ( write_progout ) { if ( close(pipe_fd[1][0])!=0 ) error(errno,"closing pipe read end"); if ( close(pipe_fd[0][0])!=0 ) error(errno,"closing pipe read end"); if ( close(progout_pipe_fd[0][1])!=0 ) error(errno,"closing pipe write end"); if ( close(progout_pipe_fd[1][1])!=0 ) error(errno,"closing pipe write end"); } else { for(i=0; i<ncmds; i++) { if ( close(pipe_fd[i][0])!=0 ) error(errno,"closing pipe read end"); if ( close(pipe_fd[i][1])!=0 ) error(errno,"closing pipe write end"); } } /* Wait for running child commands and check exit status. */ while ( 1 ) { if ( write_progout ) { for(i=0; i<ncmds; i++) { pump_pipes(&progout_pipe_fd[i][0], &pipe_fd[1-i][1], 1-i); } pid = 0; for(i=0; i<ncmds; i++) { if ( cmd_exit[i]==-1 ) { pid = waitpid(cmd_pid[i], &status, WNOHANG); if ( pid != 0 ) break; } } if ( pid==0 ) continue; } else { pid = waitpid(-1, &status, 0); } if ( pid<0 ) { /* No more child processes, we're done. */ if ( errno==ECHILD ) break; error(errno,"waiting for children"); } /* Pump pipes one more time to improve detection which program exited first. */ if ( write_progout ) { for(i=0; i<ncmds; i++) { pump_pipes(&progout_pipe_fd[i][0], &pipe_fd[1-i][1], 1-i); } } for(i=0; i<ncmds; i++) if ( cmd_pid[i]==pid ) { if (i == 1) { submission_still_alive = 0; } warning(0, "command #%d, pid %d has exited (with status %d)",i+1,pid,status); break; } if ( i>=ncmds ) error(0, "waited for unknown child"); cmd_exit[i] = status; verb("command #%d, pid %d has exited (with status %d)",i+1,pid,status); if (cmd_exit[0] != -1 && cmd_exit[1] != -1) { /* Both child processes are done. */ if (validator_exited_first && WIFEXITED(cmd_exit[0])) { exitcode = WEXITSTATUS(cmd_exit[0]); if ( outputmeta && (metafile = fopen(metafilename,"w"))==NULL ) { error(errno,"cannot open `%s'",metafilename); } write_meta("exitcode","%d", exitcode); /* TODO: add more meta data like run time. */ if ( outputmeta && fclose(metafile)!=0 ) { error(errno,"closing file `%s'",metafilename); } } break; } }; /* Reset pipe filedescriptors to use blocking I/O. */ for(i=0; i<ncmds; i++) { if ( write_progout && progout_pipe_fd[i][0]>=0 ) { r = fcntl(progout_pipe_fd[i][0], F_GETFL); if (r == -1) error(errno, "fcntl, getting flags"); r = fcntl(progout_pipe_fd[i][0], F_SETFL, r ^ O_NONBLOCK); if (r == -1) error(errno, "fcntl, setting flags"); do { pump_pipes(&progout_pipe_fd[i][0], &pipe_fd[1-i][1], 1-i); } while ( progout_pipe_fd[i][0]>=0 ); } } /* Check exit status of commands and report back the exit code of the first. */ myexitcode = exitcode = 0; for(i=0; i<ncmds; i++) { if ( cmd_exit[i]!=0 ) { status = cmd_exit[i]; /* Test whether command has finished abnormally */ if ( ! WIFEXITED(status) ) { if ( WIFSIGNALED(status) ) { warning(0,"command #%d terminated with signal %d",i+1,WTERMSIG(status)); exitcode = 128+WTERMSIG(status); } else { error(0,"command #%d exit status unknown: %d",i+1,status); } } else { /* Log the exitstatus of the failed commands */ exitcode = WEXITSTATUS(status); if ( exitcode!=0 ) { warning(0,"command #%d exited with exitcode %d",i+1,exitcode); } } /* Only report it for the first command. */ if ( i==0 ) myexitcode = exitcode; } } return myexitcode; }