Exemplo n.º 1
0
/*
 * Scatter the fds read from the input process to multiple outputs.
 */
STATIC void
scatter_input_fds(struct dgsh_negotiation *mb)
{
	struct dgsh_conc *this_conc = find_conc(mb, pid);
	if (!this_conc) {
		printf("%s(): Concentrator with pid %d not registered",
				__func__, pid);
		exit(1);	// XXX
	}
	int n_to_read = this_conc->input_fds;
	int *read_fds = (int *)malloc(n_to_read * sizeof(int));
	int i, j, write_index = 0;
	bool ignore = false;
	DPRINTF(4, "%s(): fds to read: %d", __func__, n_to_read);

	for (i = 0; i < n_to_read; i++)
		read_fds[i] = read_fd(STDIN_FILENO);

	for (i = STDOUT_FILENO; i != STDIN_FILENO; i = next_fd(i, &ignore)) {
		int n_to_write = get_expected_fds_n(mb, pi[i].pid);
		DPRINTF(4, "%s(): fds to write for p[%d].pid %d: %d",
				__func__, i, pi[i].pid, n_to_write);
		for (j = write_index; j < write_index + n_to_write; j++) {
			write_fd(i, read_fds[j]);
			DPRINTF(4, "%s(): Write fd: %d to output channel: %d",
					__func__, read_fds[j], i);
		}
		write_index += n_to_write;
	}
	assert(write_index == n_to_read);
}
Exemplo n.º 2
0
void do_dup(PCB *pcb) {
    int fd_old = pcb->registers[5],
        fd_new;

    struct File_Descriptor *new_fd_obj,
               *old_fd_obj;

    if(valid_fd(pcb, fd_old, -1)) {

        old_fd_obj = pcb->fd_table[fd_old];

        fd_new = next_fd(pcb);

        if(fd_new > -1) {
            new_fd_obj = new_fd(fd_new, old_fd_obj->permission);

            dup_fd(old_fd_obj,new_fd_obj);

            pcb->fd_table[new_fd_obj->id] = new_fd_obj;

        } else {
            syscall_return(pcb, -1*EMFILE);
        }

        //printf("process %d duping to new fd %d!\n",pcb->pid, fd_new);

        syscall_return(pcb, fd_new);
    } else {
        syscall_return(pcb,-1*EBADF);
    }
}
Exemplo n.º 3
0
/**
 * Register current concentrator to message block's
 * concentrator array
 */
STATIC int
set_io_channels(struct dgsh_negotiation *mb)
{
	if (find_conc(mb, pid))
		return 0;

	struct dgsh_conc c;
	c.pid = pid;
	c.input_fds = -1;
	c.output_fds = -1;
	c.n_proc_pids = (nfd > 2 ? nfd - 2 : 1);
	c.multiple_inputs = multiple_inputs;
	c.proc_pids = (int *)malloc(sizeof(int) * c.n_proc_pids);
	int j = 0, i;

	DPRINTF(4, "%s: n_proc_pids: %d", __func__, c.n_proc_pids);
	if (multiple_inputs) {
		c.endpoint_pid = pi[STDOUT_FILENO].pid;
		if (c.endpoint_pid == 0)
			return 1;
		for (i = STDIN_FILENO; i < nfd; i == STDIN_FILENO ? i = FREE_FILENO : i++)
			if (pi[i].pid == 0) {
				free(c.proc_pids);
				return 1;
			} else
				c.proc_pids[j++] = pi[i].pid;
	} else {
		bool ignore;
		c.endpoint_pid = pi[STDIN_FILENO].pid;
		if (c.endpoint_pid == 0)
			return 1;
		for (i = STDOUT_FILENO; i != STDIN_FILENO; i = next_fd(i, &ignore))
			if (pi[i].pid == 0) {
				free(c.proc_pids);
				return 1;
			} else
				c.proc_pids[j++] = pi[i].pid;
	}

	if (!mb->conc_array) {
		mb->conc_array = (struct dgsh_conc *)malloc(sizeof(struct dgsh_conc));
		mb->n_concs = 1;
	} else {
		mb->n_concs++;
		mb->conc_array = (struct dgsh_conc *)realloc(mb->conc_array,
					sizeof(struct dgsh_conc) * mb->n_concs);
	}
	memcpy(&mb->conc_array[mb->n_concs - 1], &c, sizeof(struct dgsh_conc));

	DPRINTF(4, "%s(): Added conc with pid: %d, now n_concs: %d",
			__func__, mb->conc_array[mb->n_concs - 1].pid, mb->n_concs);

	return 0;
}
Exemplo n.º 4
0
int first_fd(int *state, int *rwx)
{
    *state = 0;
    return next_fd(state, rwx);
}
Exemplo n.º 5
0
int main(int argc, char **argv)
{
    bool sending;
    int *fdlist;
    int fd;
    int i, fdstate;
    size_t fdsize;
    int exitcode;
    bool errors;
    enum TriState sanitise_stdout = AUTO, sanitise_stderr = AUTO;
    bool use_subsystem = false;
    bool just_test_share_exists = false;
    unsigned long now;
    struct winsize size;
    const struct BackendVtable *backvt;

    fdlist = NULL;
    fdsize = 0;
    /*
     * Initialise port and protocol to sensible defaults. (These
     * will be overridden by more or less anything.)
     */
    default_protocol = PROT_SSH;
    default_port = 22;

    bufchain_init(&stdout_data);
    bufchain_init(&stderr_data);
    bufchain_sink_init(&stdout_bcs, &stdout_data);
    bufchain_sink_init(&stderr_bcs, &stderr_data);
    stdout_bs = BinarySink_UPCAST(&stdout_bcs);
    stderr_bs = BinarySink_UPCAST(&stderr_bcs);
    outgoingeof = EOF_NO;

    flags = FLAG_STDERR_TTY;
    cmdline_tooltype |=
        (TOOLTYPE_HOST_ARG |
         TOOLTYPE_HOST_ARG_CAN_BE_SESSION |
         TOOLTYPE_HOST_ARG_PROTOCOL_PREFIX |
         TOOLTYPE_HOST_ARG_FROM_LAUNCHABLE_LOAD);

    stderr_tty_init();
    /*
     * Process the command line.
     */
    conf = conf_new();
    do_defaults(NULL, conf);
    loaded_session = false;
    default_protocol = conf_get_int(conf, CONF_protocol);
    default_port = conf_get_int(conf, CONF_port);
    errors = false;
    {
	/*
	 * Override the default protocol if PLINK_PROTOCOL is set.
	 */
	char *p = getenv("PLINK_PROTOCOL");
	if (p) {
            const struct BackendVtable *vt = backend_vt_from_name(p);
            if (vt) {
                default_protocol = vt->protocol;
                default_port = vt->default_port;
		conf_set_int(conf, CONF_protocol, default_protocol);
		conf_set_int(conf, CONF_port, default_port);
	    }
	}
    }
    while (--argc) {
	char *p = *++argv;
        int ret = cmdline_process_param(p, (argc > 1 ? argv[1] : NULL),
                                        1, conf);
        if (ret == -2) {
            fprintf(stderr,
                    "plink: option \"%s\" requires an argument\n", p);
            errors = true;
        } else if (ret == 2) {
            --argc, ++argv;
        } else if (ret == 1) {
            continue;
        } else if (!strcmp(p, "-batch")) {
            console_batch_mode = true;
        } else if (!strcmp(p, "-s")) {
            /* Save status to write to conf later. */
            use_subsystem = true;
        } else if (!strcmp(p, "-V") || !strcmp(p, "--version")) {
            version();
        } else if (!strcmp(p, "--help")) {
            usage();
            exit(0);
        } else if (!strcmp(p, "-pgpfp")) {
            pgp_fingerprints();
            exit(1);
        } else if (!strcmp(p, "-o")) {
            if (argc <= 1) {
                fprintf(stderr,
                        "plink: option \"-o\" requires an argument\n");
                errors = true;
            } else {
                --argc;
                /* Explicitly pass "plink" in place of appname for
                 * error reporting purposes. appname will have been
                 * set by be_foo.c to something more generic, probably
                 * "PuTTY". */
                provide_xrm_string(*++argv, "plink");
            }
        } else if (!strcmp(p, "-shareexists")) {
            just_test_share_exists = true;
        } else if (!strcmp(p, "-fuzznet")) {
            conf_set_int(conf, CONF_proxy_type, PROXY_FUZZ);
            conf_set_str(conf, CONF_proxy_telnet_command, "%host");
        } else if (!strcmp(p, "-sanitise-stdout") ||
                   !strcmp(p, "-sanitize-stdout")) {
            sanitise_stdout = FORCE_ON;
        } else if (!strcmp(p, "-no-sanitise-stdout") ||
                   !strcmp(p, "-no-sanitize-stdout")) {
            sanitise_stdout = FORCE_OFF;
        } else if (!strcmp(p, "-sanitise-stderr") ||
                   !strcmp(p, "-sanitize-stderr")) {
            sanitise_stderr = FORCE_ON;
        } else if (!strcmp(p, "-no-sanitise-stderr") ||
                   !strcmp(p, "-no-sanitize-stderr")) {
            sanitise_stderr = FORCE_OFF;
        } else if (!strcmp(p, "-no-antispoof")) {
            console_antispoof_prompt = false;
	} else if (*p != '-') {
            strbuf *cmdbuf = strbuf_new();

            while (argc > 0) {
                if (cmdbuf->len > 0)
                    put_byte(cmdbuf, ' '); /* add space separator */
                put_datapl(cmdbuf, ptrlen_from_asciz(p));
                if (--argc > 0)
                    p = *++argv;
            }

            conf_set_str(conf, CONF_remote_cmd, cmdbuf->s);
            conf_set_str(conf, CONF_remote_cmd2, "");
            conf_set_bool(conf, CONF_nopty, true);  /* command => no tty */

            strbuf_free(cmdbuf);
            break;		       /* done with cmdline */
        } else {
            fprintf(stderr, "plink: unknown option \"%s\"\n", p);
            errors = true;
	}
    }

    if (errors)
	return 1;

    if (!cmdline_host_ok(conf)) {
	usage();
    }

    prepare_session(conf);

    /*
     * Perform command-line overrides on session configuration.
     */
    cmdline_run_saved(conf);

    /*
     * If we have no better ideas for the remote username, use the local
     * one, as 'ssh' does.
     */
    if (conf_get_str(conf, CONF_username)[0] == '\0') {
	char *user = get_username();
	if (user) {
	    conf_set_str(conf, CONF_username, user);
	    sfree(user);
	}
    }

    /*
     * Apply subsystem status.
     */
    if (use_subsystem)
        conf_set_bool(conf, CONF_ssh_subsys, true);

    if (!*conf_get_str(conf, CONF_remote_cmd) &&
	!*conf_get_str(conf, CONF_remote_cmd2) &&
	!*conf_get_str(conf, CONF_ssh_nc_host))
	flags |= FLAG_INTERACTIVE;

    /*
     * Select protocol. This is farmed out into a table in a
     * separate file to enable an ssh-free variant.
     */
    backvt = backend_vt_from_proto(conf_get_int(conf, CONF_protocol));
    if (!backvt) {
	fprintf(stderr,
		"Internal fault: Unsupported protocol found\n");
	return 1;
    }

    /*
     * Block SIGPIPE, so that we'll get EPIPE individually on
     * particular network connections that go wrong.
     */
    putty_signal(SIGPIPE, SIG_IGN);

    /*
     * Set up the pipe we'll use to tell us about SIGWINCH.
     */
    if (pipe(signalpipe) < 0) {
	perror("pipe");
	exit(1);
    }
    /* We don't want the signal handler to block if the pipe's full. */
    nonblock(signalpipe[0]);
    nonblock(signalpipe[1]);
    cloexec(signalpipe[0]);
    cloexec(signalpipe[1]);
    putty_signal(SIGWINCH, sigwinch);

    /*
     * Now that we've got the SIGWINCH handler installed, try to find
     * out the initial terminal size.
     */
    if (ioctl(STDIN_FILENO, TIOCGWINSZ, &size) >= 0) {
	conf_set_int(conf, CONF_width, size.ws_col);
	conf_set_int(conf, CONF_height, size.ws_row);
    }

    /*
     * Decide whether to sanitise control sequences out of standard
     * output and standard error.
     *
     * If we weren't given a command-line override, we do this if (a)
     * the fd in question is pointing at a terminal, and (b) we aren't
     * trying to allocate a terminal as part of the session.
     *
     * (Rationale: the risk of control sequences is that they cause
     * confusion when sent to a local terminal, so if there isn't one,
     * no problem. Also, if we allocate a remote terminal, then we
     * sent a terminal type, i.e. we told it what kind of escape
     * sequences we _like_, i.e. we were expecting to receive some.)
     */
    if (sanitise_stdout == FORCE_ON ||
        (sanitise_stdout == AUTO && isatty(STDOUT_FILENO) &&
         conf_get_bool(conf, CONF_nopty))) {
        stdout_scc = stripctrl_new(stdout_bs, true, L'\0');
        stdout_bs = BinarySink_UPCAST(stdout_scc);
    }
    if (sanitise_stderr == FORCE_ON ||
        (sanitise_stderr == AUTO && isatty(STDERR_FILENO) &&
         conf_get_bool(conf, CONF_nopty))) {
        stderr_scc = stripctrl_new(stderr_bs, true, L'\0');
        stderr_bs = BinarySink_UPCAST(stderr_scc);
    }

    sk_init();
    uxsel_init();

    /*
     * Plink doesn't provide any way to add forwardings after the
     * connection is set up, so if there are none now, we can safely set
     * the "simple" flag.
     */
    if (conf_get_int(conf, CONF_protocol) == PROT_SSH &&
	!conf_get_bool(conf, CONF_x11_forward) &&
	!conf_get_bool(conf, CONF_agentfwd) &&
	!conf_get_str_nthstrkey(conf, CONF_portfwd, 0))
	conf_set_bool(conf, CONF_ssh_simple, true);

    if (just_test_share_exists) {
        if (!backvt->test_for_upstream) {
            fprintf(stderr, "Connection sharing not supported for connection "
                    "type '%s'\n", backvt->name);
            return 1;
        }
        if (backvt->test_for_upstream(conf_get_str(conf, CONF_host),
                                      conf_get_int(conf, CONF_port), conf))
            return 0;
        else
            return 1;
    }

    /*
     * Start up the connection.
     */
    logctx = log_init(default_logpolicy, conf);
    {
	const char *error;
	char *realhost;
	/* nodelay is only useful if stdin is a terminal device */
	bool nodelay = conf_get_bool(conf, CONF_tcp_nodelay) && isatty(0);

	/* This is a good place for a fuzzer to fork us. */
#ifdef __AFL_HAVE_MANUAL_CONTROL
	__AFL_INIT();
#endif

        error = backend_init(backvt, plink_seat, &backend, logctx, conf,
                             conf_get_str(conf, CONF_host),
                             conf_get_int(conf, CONF_port),
                             &realhost, nodelay,
                             conf_get_bool(conf, CONF_tcp_keepalives));
	if (error) {
	    fprintf(stderr, "Unable to open connection:\n%s\n", error);
	    return 1;
	}
        ldisc_create(conf, NULL, backend, plink_seat);
	sfree(realhost);
    }

    /*
     * Set up the initial console mode. We don't care if this call
     * fails, because we know we aren't necessarily running in a
     * console.
     */
    local_tty = (tcgetattr(STDIN_FILENO, &orig_termios) == 0);
    atexit(cleanup_termios);
    seat_echoedit_update(plink_seat, 1, 1);
    sending = false;
    now = GETTICKCOUNT();

    pollwrapper *pw = pollwrap_new();

    while (1) {
	int rwx;
	int ret;
        unsigned long next;

        pollwrap_clear(pw);

	pollwrap_add_fd_rwx(pw, signalpipe[0], SELECT_R);

	if (!sending &&
            backend_connected(backend) &&
            backend_sendok(backend) &&
            backend_sendbuffer(backend) < MAX_STDIN_BACKLOG) {
	    /* If we're OK to send, then try to read from stdin. */
            pollwrap_add_fd_rwx(pw, STDIN_FILENO, SELECT_R);
	}

	if (bufchain_size(&stdout_data) > 0) {
	    /* If we have data for stdout, try to write to stdout. */
            pollwrap_add_fd_rwx(pw, STDOUT_FILENO, SELECT_W);
	}

	if (bufchain_size(&stderr_data) > 0) {
	    /* If we have data for stderr, try to write to stderr. */
            pollwrap_add_fd_rwx(pw, STDERR_FILENO, SELECT_W);
	}

	/* Count the currently active fds. */
	i = 0;
	for (fd = first_fd(&fdstate, &rwx); fd >= 0;
	     fd = next_fd(&fdstate, &rwx)) i++;

	/* Expand the fdlist buffer if necessary. */
        sgrowarray(fdlist, fdsize, i);

	/*
	 * Add all currently open fds to pw, and store them in fdlist
	 * as well.
	 */
	int fdcount = 0;
	for (fd = first_fd(&fdstate, &rwx); fd >= 0;
	     fd = next_fd(&fdstate, &rwx)) {
	    fdlist[fdcount++] = fd;
            pollwrap_add_fd_rwx(pw, fd, rwx);
	}

        if (toplevel_callback_pending()) {
            ret = pollwrap_poll_instant(pw);
        } else if (run_timers(now, &next)) {
            do {
                unsigned long then;
                long ticks;

		then = now;
		now = GETTICKCOUNT();
		if (now - then > next - then)
		    ticks = 0;
		else
		    ticks = next - now;

                bool overflow = false;
                if (ticks > INT_MAX) {
                    ticks = INT_MAX;
                    overflow = true;
                }

                ret = pollwrap_poll_timeout(pw, ticks);
                if (ret == 0 && !overflow)
                    now = next;
                else
                    now = GETTICKCOUNT();
            } while (ret < 0 && errno == EINTR);
        } else {
            ret = pollwrap_poll_endless(pw);
        }

        if (ret < 0 && errno == EINTR)
            continue;

	if (ret < 0) {
	    perror("poll");
	    exit(1);
	}

	for (i = 0; i < fdcount; i++) {
	    fd = fdlist[i];
            int rwx = pollwrap_get_fd_rwx(pw, fd);
            /*
             * We must process exceptional notifications before
             * ordinary readability ones, or we may go straight
             * past the urgent marker.
             */
	    if (rwx & SELECT_X)
		select_result(fd, SELECT_X);
	    if (rwx & SELECT_R)
		select_result(fd, SELECT_R);
	    if (rwx & SELECT_W)
		select_result(fd, SELECT_W);
	}

	if (pollwrap_check_fd_rwx(pw, signalpipe[0], SELECT_R)) {
	    char c[1];
	    struct winsize size;
	    if (read(signalpipe[0], c, 1) <= 0)
		/* ignore error */;
	    /* ignore its value; it'll be `x' */
	    if (ioctl(STDIN_FILENO, TIOCGWINSZ, (void *)&size) >= 0)
                backend_size(backend, size.ws_col, size.ws_row);
	}

	if (pollwrap_check_fd_rwx(pw, STDIN_FILENO, SELECT_R)) {
	    char buf[4096];
	    int ret;

            if (backend_connected(backend)) {
		ret = read(STDIN_FILENO, buf, sizeof(buf));
                noise_ultralight(NOISE_SOURCE_IOLEN, ret);
		if (ret < 0) {
		    perror("stdin: read");
		    exit(1);
		} else if (ret == 0) {
                    backend_special(backend, SS_EOF, 0);
		    sending = false;   /* send nothing further after this */
		} else {
		    if (local_tty)
			from_tty(buf, ret);
		    else
                        backend_send(backend, buf, ret);
		}
	    }
	}

	if (pollwrap_check_fd_rwx(pw, STDOUT_FILENO, SELECT_W)) {
            backend_unthrottle(backend, try_output(false));
	}

	if (pollwrap_check_fd_rwx(pw, STDERR_FILENO, SELECT_W)) {
            backend_unthrottle(backend, try_output(true));
	}

        run_toplevel_callbacks();

        if (!backend_connected(backend) &&
	    bufchain_size(&stdout_data) == 0 &&
	    bufchain_size(&stderr_data) == 0)
	    break;		       /* we closed the connection */
    }
    exitcode = backend_exitcode(backend);
    if (exitcode < 0) {
	fprintf(stderr, "Remote process exit code unavailable\n");
	exitcode = 1;		       /* this is an error condition */
    }
    cleanup_exit(exitcode);
    return exitcode;		       /* shouldn't happen, but placates gcc */
}
Exemplo n.º 6
0
/*
 * Pass around the message blocks so that they reach all processes
 * connected through the concentrator.
 */
STATIC int
pass_message_blocks(void)
{
	fd_set readfds, writefds;
	int nfds = 0;
	int i;
	int oi = -1;		/* scatter/gather block's origin index */
	int ofd = -1;		/* ... origin fd direction */
	bool ro = false;	/* Whether the read block's origin should
				 * be restored
				 */
	bool iswrite = false;

	if (noinput) {
#ifdef TIME
		clock_gettime(CLOCK_MONOTONIC, &tstart);
#endif
		construct_message_block("dgsh-conc", pid);
		chosen_mb->origin_fd_direction = STDOUT_FILENO;
		chosen_mb->is_origin_conc = true;
		chosen_mb->conc_pid = pid;
		pi[STDOUT_FILENO].to_write = chosen_mb;
	}

	for (;;) {
		// Create select(2) masks
		FD_ZERO(&readfds);
		FD_ZERO(&writefds);
		for (i = 0; i < nfd; i++) {
			if (noinput && i == STDIN_FILENO)
				continue;
			if (i == STDERR_FILENO)
				continue;
			if (!pi[i].seen) {
				FD_SET(i, &readfds);
				nfds = max(i + 1, nfds);
			}
			if (pi[i].to_write && !pi[i].written) {
				FD_SET(i, &writefds);
				nfds = max(i + 1, nfds);
				pi[i].to_write->is_origin_conc = true;
				pi[i].to_write->conc_pid = pid;
				DPRINTF(4, "Actual origin: conc with pid %d", pid);
			}
		}

	again:
		if (select(nfds, &readfds, &writefds, NULL, NULL) < 0) {
			if (errno == EINTR)
				goto again;
			/* All other cases are internal errors. */
			err(1, "select");
		}

		// Read/write what we can
		for (i = 0; i < nfd; i++) {
			if (FD_ISSET(i, &writefds)) {
				iswrite = true;
				assert(pi[i].to_write);
				chosen_mb = pi[i].to_write;
				DPRINTF(4, "**fd i: %d set for writing to tool with pid %d", i, pi[i].pid);
				write_message_block(i); // XXX check return

				if (pi[i].to_write->state == PS_RUN ||
					pi[i].to_write->state == PS_DRAW_EXIT ||
					(pi[i].to_write->state == PS_ERROR &&
						pi[i].to_write->is_error_confirmed))
					pi[i].written = true;

				// Write side exit
				if (is_ready(i, pi[i].to_write)) {
					pi[i].run_ready = true;
					DPRINTF(4, "**%s(): pi[%d] is run ready",
							__func__, i);
				}
				pi[i].to_write = NULL;
			}
			if (FD_ISSET(i, &readfds)) {
				struct dgsh_negotiation *rb;
				ro = false;
				int next = next_fd(i, &ro);

				assert(!pi[i].run_ready);
				assert(pi[next].to_write == NULL);
				if (read_message_block(i, &pi[next].to_write) ==
								OP_ERROR) {
					chosen_mb->state = PS_ERROR;
					if (noinput)
						chosen_mb->is_error_confirmed = true;
					pi[next].to_write = chosen_mb;
					continue;
				}
				rb = pi[next].to_write;

				DPRINTF(4, "%s(): next write via fd %d to pid %d",
						__func__, next, pi[next].pid);

				if (oi == -1) {
					if ((multiple_inputs && i == 1) ||
							(!multiple_inputs &&
							 i == 0)) {
						oi = rb->origin_index;
						ofd = rb->origin_fd_direction;
						DPRINTF(4, "**Store origin: %d, fd: %s",
							oi, ofd ? "stdout" : "stdin");
					}
				}

				/* If conc talks to conc, set conc's pid
				 * Required in order to allocate fds correctly
				 * in the end
				 */
				if (rb->is_origin_conc)
					pi[i].pid = rb->conc_pid;
				else
					pi[i].pid = get_origin_pid(rb);

				/* If needed, re-set origin.
				 * Don't move this block before get_origin_pid()
				 */
				if (ro) {
					DPRINTF(4, "**Restore origin: %d, fd: %s",
							oi, ofd ? "stdout" : "stdin");
					pi[next].to_write->origin_index = oi;
					pi[next].to_write->origin_fd_direction = ofd;
				} else if (noinput) {
					pi[next].to_write->origin_index = -1;
					pi[next].to_write->origin_fd_direction = STDOUT_FILENO;
				}

				/* Set a conc's required/provided IO in mb */
				if (!noinput)
					set_io_channels(pi[next].to_write);

				if (rb->state == PS_NEGOTIATION &&
						noinput) {
					int j, seen = 0;
					pi[i].seen = true;
					for (j = 1; j < nfd; j++)
						if (pi[j].seen)
							seen++;
					if ((nfd > 2 && seen == nfd - 2) ||
							seen == nfd - 1) {
						chosen_mb = rb;
						DPRINTF(1, "%s(): Gathered I/O requirements.", __func__);
						int state = solve_graph();
						if (state == OP_ERROR) {
							pi[next].to_write->state = PS_ERROR;
							pi[next].to_write->is_error_confirmed = true;
						} else if (state == OP_DRAW_EXIT)
							pi[next].to_write->state = PS_DRAW_EXIT;
						else {
							DPRINTF(1, "%s(): Computed solution", __func__);
							pi[next].to_write->state = PS_RUN;
						}
						for (j = 1; j < nfd; j++)
							pi[j].seen = false;
						// Don't free
						chosen_mb = NULL;
					}
				} else if (rb->state == PS_RUN ||
						rb->state == PS_DRAW_EXIT ||
						(rb->state == PS_ERROR &&
						rb->is_error_confirmed))
					pi[i].seen = true;
				else if (rb->state == PS_ERROR)
					rb->is_error_confirmed = true;

				print_state(i, (int)rb->initiator_pid, 1);
				if (pi[i].seen && pi[i].written) {
					chosen_mb = pi[next].to_write;
					pi[i].run_ready = true;
					DPRINTF(4, "**%s(): pi[%d] is run ready",
							__func__, i);
				}
			}
		}

		// See if all processes are run-ready
		nfds = 0;
		for (i = 0; i < nfd; i++) {
			if (pi[i].run_ready)
				nfds++;
			print_state(i, nfds, 2);
		}
		if ((nfd > 2 && (nfds == nfd - 1 ||
					(noinput && nfds == nfd - 2))) ||
		    (nfds == nfd || (noinput && nfds == nfd - 1))) {
			assert(chosen_mb != NULL);
			DPRINTF(4, "%s(): conc leaves negotiation", __func__);
			return chosen_mb->state;
		} else if (chosen_mb != NULL &&	iswrite) { // Free if we have written
			DPRINTF(4, "chosen_mb: %lx, i: %d, next: %d, pi[next].to_write: %lx\n",
				(long)chosen_mb, i, next_fd(i, &ro), (long)pi[next_fd(i, &ro)].to_write);
			free_mb(chosen_mb);
			chosen_mb = NULL;
			iswrite = false;
		}
	}
}