|  | /* 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 <parlib/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; | 
|  | } |