Plan 9 from Bell Labs’s /usr/web/sources/contrib/cinap_lenrek/dial.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


#include <u.h>
#include <libc.h>

enum
{
	Maxconcurr	= 4,
	Maxstring	= 128,
};

typedef struct DS DS;
typedef struct Conn Conn;

struct DS {
	/* dist string */
	char	*netdir;
	char	*proto;
	char	*rem;

	/* other args */
	char	*local;
	char	*dir;
	int	*cfdp;
};

struct Conn {
	Conn	*next;

	int	pid;

	int	cfd;
	int	dfd;

	char	dest[Maxstring];
	char	dir[NETPATHLEN];
};

static Conn*
openconn(char *clone, char *dest, char *netdir)
{
	char *x, *p, *e;
	Conn *c;
	int n;

	if((c = malloc(sizeof(Conn))) == nil)
		return nil;

	c->next = nil;
	c->pid = 0;
	c->cfd = -1;
	c->dfd = -1;

	snprint(c->dest, sizeof c->dest, "%s", dest);

	if(netdir){
		if(*clone == '/' && (p = strchr(clone+1, '/')))
			clone = ++p;
		snprint(c->dir, sizeof c->dir, "%s/%s", netdir, clone);
	} else
		snprint(c->dir, sizeof c->dir, "%s", clone);

	e = c->dir + sizeof c->dir;
	if((p = strrchr(c->dir, '/')) == nil)
		goto err;
	if((c->cfd = open(c->dir, ORDWR)) < 0)
		goto err;
	if((n = (e - p)-1) <= 0)
		goto err;
	if((n = read(c->cfd, p, n)) <= 0)
		goto err;
	p[n] = 0;
	for(x = p; *x == ' '; x++)
		;
	snprint(p, e - p, "/%ld/data", strtoul(x, 0, 0));

	if((c->dfd = open(c->dir, ORDWR)) < 0)
		goto err;
	if(p = strrchr(c->dir, '/'))
		*p = 0;
	return c;

err:
	if(c->cfd >= 0)
		close(c->cfd);
	if(c->dfd >= 0)
		close(c->dfd);
	free(c);
	return nil;
}

static char*
readcs(int cs, char *buf, int nbuf, char **destp)
{
	char *p;
	int n;

	if((n = read(cs, buf, nbuf-1)) <= 0)
		return nil;
	if(buf[n-1] == '\n')
		n--;
	buf[n] = 0;
	if((p = strchr(buf, ' ')) == nil)
		return nil;
	*p++ = 0;
	if(destp)
		*destp = p;
	return buf;
}

static Conn*
getconn(int cs, char *buf, int nbuf, char *netdir)
{
	char *clone, *dest;

	if(clone = readcs(cs, buf, nbuf, &dest))
		return openconn(clone, dest, netdir);
	return nil;
}


static int
connect(Conn *c, char *local)
{
	if(local)
		return fprint(c->cfd, "connect %s %s", c->dest, local) > 0;
	else
		return fprint(c->cfd, "connect %s", c->dest) > 0;
}

static int
aconnect(Conn *c, char *local)
{
	if((c->pid = rfork(RFPROC)) < 0)
		return 0;
	else if(c->pid > 0)
		return 1;

	notify(nil);
	if(connect(c, local))
		_exits(nil);
	_exits("%r");
	return -1;
}

static int
canfork(char *buf, int nbuf)
{
	int fd;

	snprint(buf, nbuf, "/proc/%d/note", getpid());
	if((fd = open(buf, OWRITE)) >= 0)
		close(fd);
	return fd >= 0;
}

static int
csdial(DS *ds)
{
	char buf[Maxstring+NETPATHLEN+4];
	Conn *conns, *winner, *c;
	int cs, ret, kids, more;

	conns = winner = nil;
	snprint(buf, sizeof buf, "%s/cs", ds->netdir);
	if((cs = open(buf, ORDWR)) < 0){
		snprint(buf, sizeof buf, "%s/%s/clone", ds->netdir, ds->proto);
		if((conns = openconn(buf, ds->rem, nil)) == nil)
			goto out;
		if(connect(conns, ds->local))
			winner = conns;
		goto out;
	}

	if(fprint(cs, "%s!%s", ds->proto, ds->rem) < 0)
		goto out;

	seek(cs, 0, 0);
	if((conns = getconn(cs, buf, sizeof buf, ds->netdir)) == nil){
		werrstr("no address to dial");
		goto out;
	}
	conns->next = getconn(cs, buf, sizeof buf, ds->netdir);
	if(conns->next == nil || !canfork(buf, sizeof buf)){
		if(connect(c = conns, ds->local)){
			winner = c;
			goto out;
		}
		if((c = c->next) == nil)
			goto out;
		if(connect(c, ds->local)){
			winner = c;
			goto out;
		}
		while(c->next = getconn(cs, buf, sizeof buf, ds->netdir)){
			if(connect(c = c->next, ds->local)){
				winner = c;
				goto out;
			}
		}
		goto out;
	}

	more = 1;
	kids = 0;
	if(aconnect(conns, ds->local))
		kids++;
	if(aconnect(conns->next, ds->local))
		kids++;
	for(;;){
		Waitmsg *m;

		while(more && kids < Maxconcurr){
			if((c = getconn(cs, buf, sizeof buf, ds->netdir)) == nil){
				more = 0;
				break;
			}
			c->next = conns;
			conns = c;
			if(aconnect(c, ds->local))
				kids++;
		}

		if(kids == 0)
			break;

		if(m = wait()){
			for(c = conns; c; c = c->next){
				if(c->pid != m->pid)
					continue;
				c->pid = 0;
				--kids;
				if(m->msg[0]){
					char *p;

					if(p = strchr(m->msg, ':'))
						p++;
					else
						p = m->msg;
					while(*p == ' ')
						p++;
					werrstr("%s", p);
				} else if(winner)
					fprint(c->cfd, "hangup");
				else
					winner = c;
				break;
			}
			free(m);
		}

		if(winner || m == nil){
			more = 0;
			for(c = conns; c; c = c->next)
				if(c->pid)
					postnote(PNPROC, c->pid, "die");
		}
	}

out:
	if(cs >= 0)
		close(cs);
	if(c = winner){
		if(ds->dir)
			strncpy(ds->dir, c->dir, NETPATHLEN);
		if(ds->cfdp)
			*ds->cfdp = c->cfd;
		else
			close(c->cfd);
		ret = c->dfd;
	} else
		ret = -1;
	while(c = conns){
		conns = c->next;
		if(c != winner){
			close(c->cfd);
			close(c->dfd);
		}
		free(c);
	}
	return ret;
}

int
dial(char *dest, char *local, char *dir, int *cfdp)
{
	char buf[Maxstring], *p, *x;
	int ret;
	DS ds;

	ds.local = local;
	ds.dir = dir;
	ds.cfdp = cfdp;

	snprint(buf, sizeof buf, "%s", dest);

	if((p = strchr(buf, '!')) == 0) {
		ds.netdir = 0;
		ds.proto = "net";
		ds.rem = buf;
	} else {
		if(buf[0] != '/' && buf[0] != '#'){
			ds.netdir = 0;
			ds.proto = buf;
		} else {
			for(x = p; *x != '/'; x--)
				;
			*x++ = 0;
			ds.netdir = buf;
			ds.proto = x;
		}
		*p++ = 0;
		ds.rem = p;
	}

	if(ds.netdir)
		return csdial(&ds);

	ds.netdir = "/net";
	if((ret = csdial(&ds)) < 0){
		char err[ERRMAX];

		rerrstr(err, sizeof err);
		if(strstr(err, "refused"))
			return ret;

		ds.netdir = "/net.alt";
		if((ret = csdial(&ds)) < 0){
			char alterr[ERRMAX];

			/* use previous error if /net.alt was not available */
			rerrstr(alterr, sizeof alterr);
			if(strstr(alterr, "translate") || strstr(alterr, "does not exist"))
				werrstr("%s", err);
		}
	}
	return ret;
}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.