c++ - After forking bash with forkpty and execvp, bash does not respond to SIGINT -


background

i'm writing terminal emulator text editor written in node.js (javascript). application uses c++ fork shell , communicate in backend. code forks shell written developer not seem maintaining project anymore. left me no other choice try , fix problem myself. i've gone through code line line point understand of doing.

problem

i can't figure out why bash won't send sigint signal it's sub processes. else works perfectly. can communicate shell instance in node.js , run commands. if write signal interrupt escape code ('\x03') fd of pty, sigint signal isn't sent. see ^c in terminal display. commands ping interpret , stop. commands cat, python, or java not stop , show ^c.

example:

user:example user$ cat ^c^c^c^c^c^c^c^c 

code

first forkpty used pty instance. then, pty instance calls execvp replace process shell process. in instance, execvp launches execvp('/bin/bash', {'/bin/bash', '--login'}).

nan_method(ptyfork) {   nan::handlescope scope;    if (info.length() != 9       || !info[0]->isstring() // file       || !info[1]->isarray() // args       || !info[2]->isarray() // env       || !info[3]->isstring() // cwd       || !info[4]->isnumber() // cols       || !info[5]->isnumber() // rows       || !info[6]->isnumber() // uid       || !info[7]->isnumber() // gid       || !info[8]->isfunction() // onexit   ) {     return nan::throwerror(       "usage: pty.fork(file, args, env, cwd, cols, rows, uid, gid, onexit)");   }    // file   string::utf8value file(info[0]->tostring());    // args   int = 0;   local<array> argv_ = local<array>::cast(info[1]);   int argc = argv_->length();   int argl = argc + 1 + 1;   char **argv = new char*[argl];   argv[0] = strdup(*file);   argv[argl-1] = null;   (; < argc; i++) {     string::utf8value arg(argv_->get(nan::new<integer>(i))->tostring());     argv[i+1] = strdup(*arg);   }    // env   = 0;   local<array> env_ = local<array>::cast(info[2]);   int envc = env_->length();   char **env = new char*[envc+1];   env[envc] = null;   (; < envc; i++) {     string::utf8value pair(env_->get(nan::new<integer>(i))->tostring());     env[i] = strdup(*pair);   }    // cwd   string::utf8value cwd_(info[3]->tostring());   char *cwd = strdup(*cwd_);    // size   struct winsize winp;   winp.ws_col = info[4]->integervalue();   winp.ws_row = info[5]->integervalue();   winp.ws_xpixel = 0;   winp.ws_ypixel = 0;    // uid / gid   int uid = info[6]->integervalue();   int gid = info[7]->integervalue();    // fork pty   int master = -1;   char name[40];   pid_t pid = pty_forkpty(&master, name, null, &winp);    if (pid) {     (i = 0; < argl; i++) free(argv[i]);     delete[] argv;     (i = 0; < envc; i++) free(env[i]);     delete[] env;     free(cwd);   }    switch (pid) {     case -1:       return nan::throwerror("forkpty(3) failed.");     case 0:       if (strlen(cwd)) chdir(cwd);        if (uid != -1 && gid != -1) {         if (setgid(gid) == -1) {           perror("setgid(2) failed.");           _exit(1);         }         if (setuid(uid) == -1) {           perror("setuid(2) failed.");           _exit(1);         }       }        pty_execvpe(argv[0], argv, env);        perror("execvp(3) failed.");       _exit(1);     default:       if (pty_nonblock(master) == -1) {         return nan::throwerror("could not set master fd nonblocking.");       }        local<object> obj = nan::new<object>();       nan::set(obj,         nan::new<string>("fd").tolocalchecked(),         nan::new<number>(master));       nan::set(obj,         nan::new<string>("pid").tolocalchecked(),         nan::new<number>(pid));       nan::set(obj,         nan::new<string>("pty").tolocalchecked(),         nan::new<string>(name).tolocalchecked());        pty_baton *baton = new pty_baton();       baton->exit_code = 0;       baton->signal_code = 0;       baton->cb.reset(local<function>::cast(info[8]));       baton->pid = pid;       baton->async.data = baton;        uv_async_init(uv_default_loop(), &baton->async, pty_after_waitpid);        uv_thread_create(&baton->tid, pty_waitpid, static_cast<void*>(baton));        return info.getreturnvalue().set(obj);   }    return info.getreturnvalue().setundefined(); }  /**  * execvpe  */  // execvpe(3) not portable. // http://www.gnu.org/software/gnulib/manual/html_node/execvpe.html static int pty_execvpe(const char *file, char **argv, char **envp) {   char **old = environ;   environ = envp;   int ret = execvp(file, argv);   environ = old;   return ret; }  /**  * nonblocking fd  */  static int pty_nonblock(int fd) {   int flags = fcntl(fd, f_getfl, 0);   if (flags == -1) return -1;   return fcntl(fd, f_setfl, flags | o_nonblock); }  /**  * pty_waitpid  * wait sigchld read exit status.  */  static void pty_waitpid(void *data) {   int ret;   int stat_loc;    pty_baton *baton = static_cast<pty_baton*>(data);    errno = 0;    if ((ret = waitpid(baton->pid, &stat_loc, 0)) != baton->pid) {     if (ret == -1 && errno == eintr) {       return pty_waitpid(baton);     }     if (ret == -1 && errno == echild) {       // xxx node v0.8.x seems have problem.       // waitpid handled elsewhere.       ;     } else {       assert(false);     }   }    if (wifexited(stat_loc)) {     baton->exit_code = wexitstatus(stat_loc); // errno?   }    if (wifsignaled(stat_loc)) {     baton->signal_code = wtermsig(stat_loc);   }    uv_async_send(&baton->async); }  /**  * pty_after_waitpid  * callback after exit status has been read.  */  static void #if node_version_at_least(0, 11, 0) pty_after_waitpid(uv_async_t *async) { #else pty_after_waitpid(uv_async_t *async, int unhelpful) { #endif   nan::handlescope scope;   pty_baton *baton = static_cast<pty_baton*>(async->data);    local<value> argv[] = {     nan::new<integer>(baton->exit_code),     nan::new<integer>(baton->signal_code),   };    local<function> cb = nan::new<function>(baton->cb);   baton->cb.reset();   memset(&baton->cb, -1, sizeof(baton->cb));   nan::callback(cb).call(nan::getcurrentcontext()->global(), 2, argv);    uv_close((uv_handle_t *)async, pty_after_close); }  /**  * pty_after_close  * uv_close() callback - free handle data  */  static void pty_after_close(uv_handle_t *handle) {   uv_async_t *async = (uv_async_t *)handle;   pty_baton *baton = static_cast<pty_baton*>(async->data);   delete baton; }  static pid_t pty_forkpty(int *amaster, char *name,             const struct termios *termp,             const struct winsize *winp) {   return forkpty(amaster, name, (termios *)termp, (winsize *)winp); } 

what i've tried

i have tried using execvp call exec -l bash instead. launches bash not solve issue.
have tried writing fd directly within c++ incase node.js. same issue of seeing ^c not registering sigint.

the thing work use shell such zsh. when using zsh, sigint sent , subprocesses stop. else odd bash shells launched zsh don't experience issue. in other words, if fork zsh , call command exec bash (or exec -l bash), new bash instance send sigint signal supposed to.

i know not me because application public , multiple users have reported issue. on linux, on os x. major issue, appreciated if point out going on here.

additional information

print out of stty -a:

user:example user$ stty -a speed 9600 baud; 15 rows; 159 columns; lflags: icanon isig iexten echo echoe -echok echoke -echonl echoctl         -echoprt -altwerase -noflsh -tostop -flusho pendin -nokerninfo         -extproc iflags: -istrip icrnl -inlcr -igncr ixon -ixoff ixany imaxbel -iutf8         -ignbrk brkint -inpck -ignpar -parmrk oflags: opost onlcr -oxtabs -onocr -onlret cflags: cread cs8 -parenb -parodd hupcl -clocal -cstopb -crtscts -dsrflow         -dtrflow -mdmbuf cchars: discard = ^o; dsusp = ^y; eof = ^d; eol = <undef>;         eol2 = <undef>; erase = ^?; intr = ^c; kill = ^u; lnext = ^v;         min = 1; quit = ^\; reprint = ^r; start = ^q; status = ^t;         stop = ^s; susp = ^z; time = 0; werase = ^w; 

short answer: call setsid before exec


signals sent active process group of session associate slave pty. calling setsid, session created associate pty, otherwise no session created, no signal emitted.


Comments

Popular posts from this blog

javascript - Chart.js (Radar Chart) different scaleLineColor for each scaleLine -

apache - Error with PHP mail(): Multiple or malformed newlines found in additional_header -

java - Android – MapFragment overlay button shadow, just like MyLocation button -