| /* Copyright (c) 2014 The Regents of the University of California | 
 |  * Barret Rhoden <brho@cs.berkeley.edu> | 
 |  * See LICENSE for details. | 
 |  * | 
 |  * Networking helpers for dealing with the plan 9 interface. */ | 
 |  | 
 | #include <stdlib.h> | 
 | #include <stdio.h> | 
 | #include <string.h> | 
 | #include <unistd.h> | 
 | #include <parlib/assert.h> | 
 | #include <parlib/net.h> | 
 |  | 
 | #include <sys/types.h> | 
 | #include <sys/stat.h> | 
 | #include <fcntl.h> | 
 |  | 
 | /* my cheap dial, assumes either /net/ or a protocol first, with !s btw the | 
 |  * proto, host, and port.  it also will modify addr temporarily.  */ | 
 | int cheap_dial(char *addr, char *local, char *dir, int *cfdp) | 
 | { | 
 | 	int ret, ctlfd, datafd, conv_id; | 
 | 	char *prefix; | 
 | 	char *hostname;						/* including !port */ | 
 | 	size_t buf_len = strlen(addr) + 30;	/* 30 should be enough extra */ | 
 | 	char *buf = malloc(buf_len); | 
 | 	if (!buf) { | 
 | 		perror("Unable to malloc buf!"); | 
 | 		return -1; | 
 | 	} | 
 | 	if (local || dir) { | 
 | 		perror("Cheap dial doesn't support local or dir"); | 
 | 		ret = -1; | 
 | 		goto out_buf; | 
 | 	} | 
 | 	hostname = strchr(addr, '!'); | 
 | 	if (!hostname) { | 
 | 		perror("No first bang"); | 
 | 		ret = -1; | 
 | 		goto out_buf; | 
 | 	} | 
 | 	*hostname++ = '\0'; | 
 | 	prefix = (addr[0] == '/' ? "" : "/net/"); | 
 | 	ret = snprintf(buf, buf_len, "%s%s/clone", prefix, addr); | 
 | 	if (snprintf_error(ret, buf_len)) { | 
 | 		perror("Clone chan path too long"); | 
 | 		ret = -1; | 
 | 		goto out_readdr; | 
 | 	} | 
 | 	ctlfd = open(buf, O_RDWR); | 
 | 	if (ctlfd < 0) { | 
 | 		perror("Can't clone a conversation"); | 
 | 		ret = -1; | 
 | 		goto out_readdr; | 
 | 	} | 
 | 	ret = read(ctlfd, buf, buf_len - 1); | 
 | 	if (ret <= 0) { | 
 | 		if (!ret) | 
 | 			printf("Got early EOF from ctl\n"); | 
 | 		else | 
 | 			perror("Can't read ctl"); | 
 | 		ret = -1; | 
 | 		goto out_ctlfd; | 
 | 	} | 
 | 	buf[ret] = 0; | 
 | 	conv_id = atoi(buf); | 
 | 	ret = snprintf(buf, buf_len, "connect %s", hostname); | 
 | 	if (snprintf_error(ret, buf_len)) { | 
 | 		perror("Connect string too long"); | 
 | 		ret = -1; | 
 | 		goto out_ctlfd; | 
 | 	} | 
 | 	if ((write(ctlfd, buf, strlen(buf)) <= 0)) { | 
 | 		perror("Failed to connect"); | 
 | 		ret = -1; | 
 | 		goto out_ctlfd; | 
 | 	} | 
 | 	ret = snprintf(buf, buf_len, "%s%s/%d/data", prefix, addr, conv_id); | 
 | 	if (snprintf_error(ret, buf_len)) { | 
 | 		perror("Data chan path too long"); | 
 | 		ret = -1; | 
 | 		goto out_ctlfd; | 
 | 	} | 
 | 	datafd = open(buf, O_RDWR); | 
 | 	if (datafd < 0) { | 
 | 		perror("Failed to open data chan"); | 
 | 		ret = -1; | 
 | 		goto out_ctlfd; | 
 | 	} | 
 | 	if (cfdp) | 
 | 		*cfdp = ctlfd; | 
 | 	else | 
 | 		close(ctlfd); | 
 | 	ret = datafd; | 
 | 	/* skip over the ctlfd close */ | 
 | 	goto out_readdr; | 
 |  | 
 | out_ctlfd: | 
 | 	close(ctlfd); | 
 | out_readdr: | 
 | 	/* restore the change we made to addr */ | 
 | 	*--hostname = '!'; | 
 | out_buf: | 
 | 	free(buf); | 
 | 	return ret; | 
 | } |