示例#1
0
/*
 * Call this when proxy negotiation is complete, so that this
 * socket can begin working normally.
 */
void proxy_activate(Proxy_Socket p)
{
  void *data;
  int len;
  long output_before, output_after;

  p->state = PROXY_STATE_ACTIVE;

  /* we want to ignore new receive events until we have sent
   * all of our buffered receive data.
   */
  sk_set_frozen(p->sub_socket, 1);

  /* how many bytes of output have we buffered? */
  output_before = bufchain_size(&p->pending_oob_output_data) +
                  bufchain_size(&p->pending_output_data);
  /* and keep track of how many bytes do not get sent. */
  output_after = 0;

  /* send buffered OOB writes */
  while (bufchain_size(&p->pending_oob_output_data) > 0) {
    bufchain_prefix(&p->pending_oob_output_data, &data, &len);
    output_after += sk_write_oob(p->sub_socket, data, len);
    bufchain_consume(&p->pending_oob_output_data, len);
  }

  /* send buffered normal writes */
  while (bufchain_size(&p->pending_output_data) > 0) {
    bufchain_prefix(&p->pending_output_data, &data, &len);
    output_after += sk_write(p->sub_socket, data, len);
    bufchain_consume(&p->pending_output_data, len);
  }

  /* if we managed to send any data, let the higher levels know. */
  if (output_after < output_before)
    plug_sent(p->plug, output_after);

  /* if we were asked to flush the output during
   * the proxy negotiation process, do so now.
   */
  if (p->pending_flush)
    sk_flush(p->sub_socket);

  /* if we have a pending EOF to send, send it */
  if (p->pending_eof)
    sk_write_eof(p->sub_socket);

  /* if the backend wanted the socket unfrozen, try to unfreeze.
   * our set_frozen handler will flush buffered receive data before
   * unfreezing the actual underlying socket.
   */
  if (!p->freeze)
    sk_set_frozen((Socket)p, 0);
}
示例#2
0
int try_output(int is_stderr)
{
    bufchain *chain = (is_stderr ? &stderr_data : &stdout_data);
    int fd = (is_stderr ? STDERR_FILENO : STDOUT_FILENO);
    void *senddata;
    int sendlen, ret;

    if (bufchain_size(chain) > 0) {
        int prev_nonblock = nonblock(fd);
        do {
            bufchain_prefix(chain, &senddata, &sendlen);
            ret = write(fd, senddata, sendlen);
            if (ret > 0)
                bufchain_consume(chain, ret);
        } while (ret == sendlen && bufchain_size(chain) != 0);
        if (!prev_nonblock)
            no_nonblock(fd);
        if (ret < 0 && errno != EAGAIN) {
            perror(is_stderr ? "stderr: write" : "stdout: write");
            exit(1);
        }
    }
    if (outgoingeof == EOF_PENDING && bufchain_size(&stdout_data) == 0) {
        close(STDOUT_FILENO);
        outgoingeof = EOF_SENT;
    }
    return bufchain_size(&stdout_data) + bufchain_size(&stderr_data);
}
示例#3
0
文件: uxproxy.c 项目: 0x0all/s2putty
static int localproxy_try_send(Local_Proxy_Socket ps)
{
    int sent = 0;

    while (bufchain_size(&ps->pending_output_data) > 0) {
	void *data;
	int len, ret;

	bufchain_prefix(&ps->pending_output_data, &data, &len);
	ret = write(ps->to_cmd, data, len);
	if (ret < 0 && errno != EWOULDBLOCK) {
	    /* We're inside the Unix frontend here, so we know
	     * that the frontend handle is unnecessary. */
	    logevent(NULL, strerror(errno));
	    fatalbox("%s", strerror(errno));
	} else if (ret <= 0) {
	    break;
	} else {
	    bufchain_consume(&ps->pending_output_data, ret);
	    sent += ret;
	}
    }

    if (bufchain_size(&ps->pending_output_data) == 0)
	uxsel_del(ps->to_cmd);
    else
	uxsel_set(ps->to_cmd, 2, localproxy_select_result);

    return sent;
}
示例#4
0
int try_output(int is_stderr)
{
    bufchain *chain = (is_stderr ? &stderr_data : &stdout_data);
    int fd = (is_stderr ? STDERR_FILENO : STDOUT_FILENO);
    void *senddata;
    int sendlen, ret, fl;

    if (bufchain_size(chain) == 0)
        return bufchain_size(&stdout_data) + bufchain_size(&stderr_data);

    fl = fcntl(fd, F_GETFL);
    if (fl != -1 && !(fl & O_NONBLOCK))
	fcntl(fd, F_SETFL, fl | O_NONBLOCK);
    do {
	bufchain_prefix(chain, &senddata, &sendlen);
	ret = write(fd, senddata, sendlen);
	if (ret > 0)
	    bufchain_consume(chain, ret);
    } while (ret == sendlen && bufchain_size(chain) != 0);
    if (fl != -1 && !(fl & O_NONBLOCK))
	fcntl(fd, F_SETFL, fl);
    if (ret < 0 && errno != EAGAIN) {
	perror(is_stderr ? "stderr: write" : "stdout: write");
	exit(1);
    }
    return bufchain_size(&stdout_data) + bufchain_size(&stderr_data);
}
示例#5
0
文件: uxser.c 项目: halcy/PuTTY
static void serial_try_write(Serial serial)
{
    void *data;
    int len, ret;

    assert(serial->fd >= 0);

    while (bufchain_size(&serial->output_data) > 0) {
        bufchain_prefix(&serial->output_data, &data, &len);
	ret = write(serial->fd, data, len);

        if (ret < 0 && (errno == EWOULDBLOCK)) {
            /*
             * We've sent all we can for the moment.
             */
            break;
        }
	if (ret < 0) {
	    perror("write serial port");
	    exit(1);
	}
	bufchain_consume(&serial->output_data, ret);
    }

    serial_uxsel_setup(serial);
}
示例#6
0
static void logfopen_callback(void *handle, int mode)
{
    struct LogContext *ctx = (struct LogContext *)handle;
    char buf[256], *event;
    struct tm tm;
    const char *fmode;

    if (mode == 0) {
        ctx->state = L_ERROR;	       /* disable logging */
    } else {
        fmode = (mode == 1 ? "ab" : "wb");
        ctx->lgfp = f_open(&ctx->currlogfilename, fmode, FALSE);
        if (ctx->lgfp)
            ctx->state = L_OPEN;
        else
            ctx->state = L_ERROR;
    }

    if (ctx->state == L_OPEN) {
        /* Write header line into log file. */
        tm = ltime();
        strftime(buf, 24, "%Y.%m.%d %H:%M:%S", &tm);
        logprintf(ctx, "=~=~=~=~=~=~=~=~=~=~=~= PuTTY log %s"
                  " =~=~=~=~=~=~=~=~=~=~=~=\r\n", buf);
    }

    event = dupprintf("%s session log (%s mode) to file: %s",
                      ctx->state == L_ERROR ?
                      (mode == 0 ? "Disabled writing" : "Error writing") :
                      (mode == 1 ? "Appending" : "Writing new"),
                      (ctx->cfg.logtype == LGTYP_ASCII ? "ASCII" :
                       ctx->cfg.logtype == LGTYP_DEBUG ? "raw" :
                       ctx->cfg.logtype == LGTYP_PACKETS ? "SSH packets" :
                       ctx->cfg.logtype == LGTYP_SSHRAW ? "SSH raw data" :
                       "unknown"),
                      filename_to_str(&ctx->currlogfilename));
    logevent(ctx->frontend, event);
    sfree(event);

    /*
     * Having either succeeded or failed in opening the log file,
     * we should write any queued data out.
     */
    assert(ctx->state != L_OPENING);   /* make _sure_ it won't be requeued */
    while (bufchain_size(&ctx->queue)) {
        void *data;
        int len;
        bufchain_prefix(&ctx->queue, &data, &len);
        logwrite(ctx, data, len);
        bufchain_consume(&ctx->queue, len);
    }
}
示例#7
0
static void handle_try_output(struct handle_output *ctx)
{
    void *senddata;
    int sendlen;

    if (!ctx->busy && bufchain_size(&ctx->queued_data)) {
	bufchain_prefix(&ctx->queued_data, &senddata, &sendlen);
	ctx->buffer = senddata;
	ctx->len = sendlen;
	SetEvent(ctx->ev_from_main);
	ctx->busy = TRUE;
    }
}
示例#8
0
void try_output(int is_stderr)
{
    struct output_data *data = (is_stderr ? &edata : &odata);
    void *senddata;
    int sendlen;

    if (!data->busy) {
        bufchain_prefix(is_stderr ? &stderr_data : &stdout_data,
                        &senddata, &sendlen);
        data->buffer = senddata;
        data->len = sendlen;
        SetEvent(data->eventback);
        data->busy = 1;
    }
}
示例#9
0
static void handle_socket_unfreeze(void *psv)
{
    Handle_Socket ps = (Handle_Socket) psv;
    void *data;
    int len;

    /*
     * If we've been put into a state other than THAWING since the
     * last callback, then we're done.
     */
    if (ps->frozen != THAWING)
        return;

    /*
     * Get some of the data we've buffered.
     */
    bufchain_prefix(&ps->inputdata, &data, &len);
    assert(len > 0);

    /*
     * Hand it off to the plug. Be careful of re-entrance - that might
     * have the effect of trying to close this socket.
     */
    ps->defer_close = TRUE;
    plug_receive(ps->plug, 0, data, len);
    bufchain_consume(&ps->inputdata, len);
    ps->defer_close = FALSE;
    if (ps->deferred_close) {
        sk_handle_close(ps);
        return;
    }

    if (bufchain_size(&ps->inputdata) > 0) {
        /*
         * If there's still data in our buffer, stay in THAWING state,
         * and reschedule ourself.
         */
        queue_toplevel_callback(handle_socket_unfreeze, ps);
    } else {
        /*
         * Otherwise, we've successfully thawed!
         */
        ps->frozen = UNFROZEN;
        handle_unthrottle(ps->recv_h, 0);
    }
}
示例#10
0
static void handle_try_output(struct handle_output *ctx)
{
    void *senddata;
    int sendlen;

    if (!ctx->busy && bufchain_size(&ctx->queued_data)) {
	bufchain_prefix(&ctx->queued_data, &senddata, &sendlen);
	ctx->buffer = senddata;
	ctx->len = sendlen;
	SetEvent(ctx->ev_from_main);
	ctx->busy = TRUE;
    } else if (!ctx->busy && bufchain_size(&ctx->queued_data) == 0 &&
               ctx->outgoingeof == EOF_PENDING) {
        CloseHandle(ctx->h);
        ctx->h = INVALID_HANDLE_VALUE;
        ctx->outgoingeof = EOF_SENT;
    }
}
示例#11
0
文件: uxplink.c 项目: rdebath/sgt
void try_output(int is_stderr)
{
    bufchain *chain = (is_stderr ? &stderr_data : &stdout_data);
    int fd = (is_stderr ? 2 : 1);
    void *senddata;
    int sendlen, ret;

    if (bufchain_size(chain) == 0)
        return;

    bufchain_prefix(chain, &senddata, &sendlen);
    ret = write(fd, senddata, sendlen);
    if (ret > 0)
	bufchain_consume(chain, ret);
    else if (ret < 0) {
	perror(is_stderr ? "stderr: write" : "stdout: write");
	exit(1);
    }
}
示例#12
0
static void sk_proxy_set_frozen(Socket s, int is_frozen)
{
  Proxy_Socket ps = (Proxy_Socket)s;

  if (ps->state != PROXY_STATE_ACTIVE) {
    ps->freeze = is_frozen;
    return;
  }

  /* handle any remaining buffered recv data first */
  if (bufchain_size(&ps->pending_input_data) > 0) {
    ps->freeze = is_frozen;

    /* loop while we still have buffered data, and while we are
     * unfrozen. the plug_receive call in the loop could result
     * in a call back into this function refreezing the socket,
     * so we have to check each time.
     */
    while (!ps->freeze && bufchain_size(&ps->pending_input_data) > 0) {
      void *data;
      char databuf[512];
      int len;
      bufchain_prefix(&ps->pending_input_data, &data, &len);
      if (len > lenof(databuf))
        len = lenof(databuf);
      memcpy(databuf, data, len);
      bufchain_consume(&ps->pending_input_data, len);
      plug_receive(ps->plug, 0, databuf, len);
    }

    /* if we're still frozen, we'll have to wait for another
     * call from the backend to finish unbuffering the data.
     */
    if (ps->freeze)
      return;
  }

  sk_set_frozen(ps->sub_socket, is_frozen);
}
示例#13
0
static void handle_socket_unfreeze(void *psv)
{
    Handle_Socket ps = (Handle_Socket) psv;
    void *data;
    int len, new_backlog;

    /*
     * If we've been put into a state other than THAWING since the
     * last callback, then we're done.
     */
    if (ps->frozen != THAWING)
        return;

    /*
     * Get some of the data we've buffered.
     */
    bufchain_prefix(&ps->inputdata, &data, &len);
    assert(len > 0);

    /*
     * Hand it off to the plug.
     */
    new_backlog = plug_receive(ps->plug, 0, data, len);

    if (bufchain_size(&ps->inputdata) > 0) {
        /*
         * If there's still data in our buffer, stay in THAWING state,
         * and reschedule ourself.
         */
        queue_toplevel_callback(handle_socket_unfreeze, ps);
    } else {
        /*
         * Otherwise, we've successfully thawed!
         */
        ps->frozen = UNFROZEN;
        handle_unthrottle(ps->recv_h, new_backlog);
    }
}
示例#14
0
文件: uxproxy.c 项目: ISTweak/putty
static int localproxy_try_send(Local_Proxy_Socket ps)
{
    int sent = 0;

    while (bufchain_size(&ps->pending_output_data) > 0) {
	void *data;
	int len, ret;

	bufchain_prefix(&ps->pending_output_data, &data, &len);
	ret = write(ps->to_cmd, data, len);
	if (ret < 0 && errno != EWOULDBLOCK) {
            plug_closing(ps->plug, strerror(errno), errno, 0);
            return 0;
	} else if (ret <= 0) {
	    break;
	} else {
	    bufchain_consume(&ps->pending_output_data, ret);
	    sent += ret;
	}
    }

    if (ps->outgoingeof == EOF_PENDING) {
        del234(localproxy_by_tofd, ps);
        close(ps->to_cmd);
        uxsel_del(ps->to_cmd);
        ps->to_cmd = -1;
        ps->outgoingeof = EOF_SENT;
    }

    if (bufchain_size(&ps->pending_output_data) == 0)
	uxsel_del(ps->to_cmd);
    else
	uxsel_set(ps->to_cmd, 2, localproxy_select_result);

    return sent;
}
示例#15
0
static void logfopen_callback(void *handle, int mode)
{
    struct LogContext *ctx = (struct LogContext *)handle;
    char buf[256], *event;
    struct tm tm;
    const char *fmode;
    int shout = FALSE;

    if (mode == 0) {
	ctx->state = L_ERROR;	       /* disable logging */
    } else {
	fmode = (mode == 1 ? "ab" : "wb");
	ctx->lgfp = f_open(ctx->currlogfilename, fmode, FALSE);
	if (ctx->lgfp) {
	    ctx->state = L_OPEN;
        } else {
	    ctx->state = L_ERROR;
            shout = TRUE;
        }
    }

    if (ctx->state == L_OPEN) {
	/* Write header line into log file. */
	tm = ltime();
	strftime(buf, 24, "%Y.%m.%d %H:%M:%S", &tm);
	logprintf(ctx, "=~=~=~=~=~=~=~=~=~=~=~= PuTTY log %s"
		  " =~=~=~=~=~=~=~=~=~=~=~=\r\n", buf);
    }

    event = dupprintf(MPEXT_BOM "%s session log (%s mode) to file: %s",
		      ctx->state == L_ERROR ?
		      (mode == 0 ? "Disabled writing" : "Error writing") :
		      (mode == 1 ? "Appending" : "Writing new"),
		      (ctx->logtype == LGTYP_ASCII ? "ASCII" :
		       ctx->logtype == LGTYP_DEBUG ? "raw" :
		       ctx->logtype == LGTYP_PACKETS ? "SSH packets" :
		       ctx->logtype == LGTYP_SSHRAW ? "SSH raw data" :
		       "unknown"),
		      filename_to_str(ctx->currlogfilename));
    logevent(ctx->frontend, event);
    if (shout) {
        /*
         * If we failed to open the log file due to filesystem error
         * (as opposed to user action such as clicking Cancel in the
         * askappend box), we should log it more prominently. We do
         * this by sending it to the same place that stderr output
         * from the main session goes (so, either a console tool's
         * actual stderr, or a terminal window).
         *
         * Of course this is one case in which that policy won't cause
         * it to turn up embarrassingly in a log file of real server
         * output, because the whole point is that we haven't managed
         * to open any such log file :-)
         */
        from_backend(ctx->frontend, 1, event, strlen(event));
        from_backend(ctx->frontend, 1, "\r\n", 2);
    }
    sfree(event);

    /*
     * Having either succeeded or failed in opening the log file,
     * we should write any queued data out.
     */
    assert(ctx->state != L_OPENING);   /* make _sure_ it won't be requeued */
    while (bufchain_size(&ctx->queue)) {
	void *data;
	int len;
	bufchain_prefix(&ctx->queue, &data, &len);
	logwrite(ctx, data, len);
	bufchain_consume(&ctx->queue, len);
    }
}
示例#16
0
文件: main.c 项目: actility/ong
int main(int argc, char **argv)
{
  int masterr, masterw, slaver, slavew, pid;
  char ptyname[FILENAME_MAX];
  bufchain tochild, tostdout;
  int tochild_active, tostdout_active, fromstdin_active, fromchild_active;
  int exitcode = -1;
  pid_t childpid = -1;

        --argc, ++argv;         /* point at argument after "--" */

  /*
   * Allocate the pipe for transmitting signals back to the
   * top-level select loop.
   */
  if (pipe(signalpipe) < 0) {
    perror("pipe");
    return 1;
  }

  /*
   * Now that pipe exists, we can set up the SIGCHLD handler to
   * write to one end of it. We needn't already know details like
   * which child pid we're waiting for, because we don't need that
   * until we respond to reading the far end of the pipe in the
   * main select loop.
   */
  signal(SIGCHLD, sigchld);

  /*
   * Allocate the pty or pipes.
   */
  masterr = pty_get(ptyname);
  masterw = dup(masterr);
  slaver = open(ptyname, O_RDWR);
  slavew = dup(slaver);
  if (slaver < 0) {
    perror("slave pty: open");
    return 1;
  }

  bufchain_init(&tochild);
  bufchain_init(&tostdout);
  tochild_active = tostdout_active = TRUE;
  fromchild_active = fromstdin_active = TRUE;

  /*
   * Fork and execute the command.
   */
  pid = fork();
  if (pid < 0) {
    perror("fork");
    return 1;
  }

  if (pid == 0) {
    int i;
    /*
     * We are the child.
     */
    close(masterr);
    close(masterw);

    fcntl(slaver, F_SETFD, 0);    /* don't close on exec */
    fcntl(slavew, F_SETFD, 0);    /* don't close on exec */
    close(0);
    dup2(slaver, 0);
    close(1);
    dup2(slavew, 1);
    int fd;
    close(2);
    dup2(slavew, 2);
    setsid();
    setpgid(0, 0);
    tcsetpgrp(0, getpgrp());
    if ((fd = open("/dev/tty", O_RDWR)) >= 0) {
      ioctl(fd, TIOCNOTTY);
      close(fd);
    }
    ioctl(slavew, TIOCSCTTY);
    
    /* Close everything _else_, for tidiness. */
    for (i = 3; i < 1024; i++)
      close(i);
    if (argc > 0) {
      execvp(argv[0], argv);     /* assumes argv has trailing NULL */
    } else {
      execl(getenv("SHELL"), getenv("SHELL"), NULL);
    }
    /*
     * If we're here, exec has gone badly foom.
     */
    perror("exec");
    exit(127);
  }

  /*
   * Now we're the parent. Close the slave fds and start copying
   * stuff back and forth.
   */
  close(slaver);
  close(slavew);
  childpid = pid;

  tcgetattr(0, &oldattrs);
  newattrs = oldattrs;
  newattrs.c_iflag &= ~(IXON | IXOFF | ICRNL | INLCR);
  newattrs.c_oflag &= ~(ONLCR | OCRNL);
  newattrs.c_lflag &= ~(ISIG | ICANON | ECHO);
  atexit(attrsonexit);
  tcsetattr(0, TCSADRAIN, &newattrs);
  

  while (1) {
    fd_set rset, wset;
    char buf[65536];
    int maxfd, ret;

    FD_ZERO(&rset);
    FD_ZERO(&wset);
    maxfd = 0;

    FD_SET(signalpipe[0], &rset);
    maxfd = max(signalpipe[0]+1, maxfd);

    if (tochild_active && bufchain_size(&tochild)) {
      FD_SET(masterw, &wset);
      maxfd = max(masterw+1, maxfd);
    }
    if (tostdout_active && bufchain_size(&tostdout)) {
      FD_SET(1, &wset);
      maxfd = max(1+1, maxfd);
    }
    if (fromstdin_active && bufchain_size(&tochild) < LOCALBUF_LIMIT) {
      FD_SET(0, &rset);
      maxfd = max(0+1, maxfd);
    }
    if (fromchild_active && bufchain_size(&tostdout) < LOCALBUF_LIMIT) {
      FD_SET(masterr, &rset);
      maxfd = max(masterr+1, maxfd);
    }

    do {
      ret = select(maxfd, &rset, &wset, NULL, NULL);
    } while (ret < 0 && (errno == EINTR || errno == EAGAIN));

    if (ret < 0) {
      perror("select");
      return 1;
    }

    if (FD_ISSET(masterr, &rset)) {

      if (FD_ISSET(masterr, &rset)) {
        ret = read(masterr, buf, sizeof(buf));
        if (ret <= 0) {
          /*
           * EIO from a pty master just means end of
           * file, annoyingly. Why can't it report
           * ordinary EOF?
           */
          if (errno == EIO)
            ret = 0;
          if (ret < 0) {
            perror("child process: read");
          }
          close(masterr);
          fromchild_active = FALSE;
          ret = 0;
        }
      } else
        ret = 0;

      if (ret) {
        bufchain_add(&tostdout, buf, ret);
      }
    }
    if (FD_ISSET(0, &rset)) {

      if (FD_ISSET(0, &rset)) {
        ret = read(0, buf, sizeof(buf));
        if (ret <= 0) {
          if (ret < 0) {
            perror("stdin: read");
          }
          close(0);
          fromstdin_active = FALSE;
          ret = 0;
        }
      } else
        ret = 0;

      if (ret) {
        bufchain_add(&tochild, buf, ret);
      }
    }
    if (FD_ISSET(1, &wset)) {
      void *data;
      int len, ret;
      bufchain_prefix(&tostdout, &data, &len);
      if ((ret = write(1, data, len)) < 0) {
        perror("stdout: write");
        close(1);
        close(masterr);
        tostdout_active = fromchild_active = FALSE;
      } else
        bufchain_consume(&tostdout, ret);
    }
    if (FD_ISSET(masterw, &wset)) {
      void *data;
      int len;
      bufchain_prefix(&tochild, &data, &len);
      if ((ret = write(masterw, data, len)) < 0) {
        perror("child process: write");
        close(0);
        close(masterw);
        tochild_active = fromstdin_active = FALSE;
      } else
        bufchain_consume(&tochild, ret);
    }
    if (FD_ISSET(signalpipe[0], &rset)) {
      ret = read(signalpipe[0], buf, 1);
      if (ret == 1 && buf[0] == 'C') {
        int pid, code;
        pid = wait(&code);     /* reap the exit code */
        if (pid == childpid)
          exitcode = code;
      }
    }

    /*
     * If there can be no further data from a direction (the
     * input fd has been closed and the buffered data is used
     * up) but its output fd is still open, close it.
     */
    if (!fromstdin_active && !bufchain_size(&tochild) && tochild_active) {
      tochild_active = FALSE;
      close(masterw);
    }
    if (!fromchild_active && !bufchain_size(&tostdout) && tostdout_active){
      tostdout_active = FALSE;
      close(1);
    }

    /*
     * Termination condition with pipes is that there's still
     * data flowing in at least one direction.
     * 
     * Termination condition for a pty-based run is that the
     * child process hasn't yet terminated and/or there is
     * still buffered data to send.
     */
    if (exitcode < 0)
      /* process is still active */;
    else if (tochild_active && bufchain_size(&tochild))
      /* data still to be sent to child's children */;
    else if (tostdout_active && bufchain_size(&tostdout))
      /* data still to be sent to stdout */;
    else
      break;           /* terminate */
  }

  close(masterw);
  close(masterr);

  if (exitcode < 0) {
    int pid, code;
    pid = wait(&code);
    exitcode = code;
  }

  return (WIFEXITED(exitcode) ? WEXITSTATUS(exitcode) :
      128 | WTERMSIG(exitcode));
}