Files
strans/ipc.c

246 lines
4.7 KiB
C

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>
#include "ipc.h"
#if !defined(MSG_NOSIGNAL) && !defined(SO_NOSIGPIPE)
#error "ipcsend requires MSG_NOSIGNAL or SO_NOSIGPIPE"
#endif
static void
putlen(unsigned char p[Ipclensz], size_t n)
{
p[0] = n;
p[1] = n >> 8;
}
static size_t
getlen(const unsigned char p[Ipclensz])
{
return p[0] | (p[1] << 8);
}
int
ipcpath(char *dst, size_t cap)
{
const char *dir, *sep;
int n;
if(dst == NULL || cap == 0)
return -1;
dir = getenv("XDG_RUNTIME_DIR");
if(dir != NULL && dir[0] == '/'){
sep = dir[strlen(dir)-1] == '/' ? "" : "/";
n = snprintf(dst, cap, "%s%sstrans.sock", dir, sep);
}else
n = snprintf(dst, cap, "/tmp/strans.%lu",
(unsigned long)getuid());
if(n < 0 || (size_t)n >= cap)
return -1;
return 0;
}
int
ipcconnect(void)
{
struct sockaddr_un addr;
int e, fd;
memset(&addr, 0, sizeof addr);
addr.sun_family = AF_UNIX;
if(ipcpath(addr.sun_path, sizeof addr.sun_path) < 0){
errno = ENAMETOOLONG;
return -1;
}
fd = socket(AF_UNIX, SOCK_STREAM, 0);
if(fd < 0)
return -1;
if(connect(fd, (struct sockaddr*)&addr, sizeof addr) == 0)
return fd;
e = errno;
close(fd);
errno = e;
return -1;
}
void
ipcpackreq(unsigned char req[Ipcreqsz], int want, unsigned int mod,
unsigned int key)
{
req[0] = want ? Ipcreqwant : 0;
req[1] = mod;
req[2] = key;
req[3] = key >> 8;
req[4] = key >> 16;
req[5] = key >> 24;
}
void
ipcpackreset(unsigned char req[Ipcreqsz], int want)
{
memset(req, 0, Ipcreqsz);
req[0] = Ipcreqreset | (want ? Ipcreqwant : 0);
}
void
ipcunpackreq(const unsigned char req[Ipcreqsz], int *want, unsigned int *mod,
unsigned int *key)
{
*want = (req[0] & Ipcreqwant) != 0;
*mod = req[1] & Mmask;
*key = (unsigned int)req[2] |
((unsigned int)req[3] << 8) |
((unsigned int)req[4] << 16) |
((unsigned int)req[5] << 24);
}
int
ipcreqreset(const unsigned char req[Ipcreqsz])
{
return (req[0] & Ipcreqreset) != 0;
}
int
ipcpackresp(unsigned char *dst, size_t cap, int eaten,
const char *commit, size_t ncommit, const char *preedit, size_t npreedit,
int want)
{
size_t n;
if(ncommit > Ipcfieldmax || npreedit > Ipcfieldmax)
return -1;
if((ncommit > 0 && commit == NULL) ||
(want && npreedit > 0 && preedit == NULL))
return -1;
n = Ipcresphdrsz + ncommit + (want ? Ipclensz + npreedit : 0);
if(dst == NULL || cap < n)
return -1;
dst[0] = eaten != 0;
putlen(dst + 1, ncommit);
if(ncommit > 0)
memcpy(dst + Ipcresphdrsz, commit, ncommit);
if(want){
putlen(dst + Ipcresphdrsz + ncommit, npreedit);
if(npreedit > 0)
memcpy(dst + Ipcresphdrsz + ncommit + Ipclensz,
preedit, npreedit);
}
return n;
}
int
ipcreadn(int fd, void *buf, size_t n)
{
unsigned char *p;
ssize_t r;
p = buf;
while(n > 0){
r = read(fd, p, n);
if(r < 0 && errno == EINTR)
continue;
if(r <= 0)
return -1;
p += r;
n -= r;
}
return 0;
}
int
ipcsend(int fd, const void *buf, size_t n)
{
const unsigned char *p;
ssize_t r;
int flags;
#if !defined(MSG_NOSIGNAL) && defined(SO_NOSIGPIPE)
int one, sr;
#endif
p = buf;
#ifdef MSG_NOSIGNAL
flags = MSG_NOSIGNAL;
#else
flags = 0;
#ifdef SO_NOSIGPIPE
one = 1;
do
sr = setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof one);
while(sr < 0 && errno == EINTR);
if(sr < 0)
return -1;
#endif
#endif
while(n > 0){
r = send(fd, p, n, flags);
if(r < 0 && errno == EINTR)
continue;
if(r <= 0)
return -1;
p += r;
n -= r;
}
return 0;
}
static int
readfield(int fd, size_t n, char *dst, size_t cap)
{
unsigned char discard[128];
size_t keep, part;
if(cap > 0 && dst == NULL)
return -1;
keep = 0;
if(cap > 0)
keep = n >= cap ? cap - 1 : n;
if(keep > 0 && ipcreadn(fd, dst, keep) < 0)
return -1;
if(cap > 0)
dst[keep] = '\0';
n -= keep;
while(n > 0){
part = n < sizeof discard ? n : sizeof discard;
if(ipcreadn(fd, discard, part) < 0)
return -1;
n -= part;
}
return 0;
}
int
ipcreadresp(int fd, int want, char *commit, size_t ccap,
char *preedit, size_t pcap, Ipcresp *resp)
{
unsigned char hdr[Ipcresphdrsz], npreedit[Ipclensz];
if(resp == NULL || (ccap > 0 && commit == NULL) ||
(pcap > 0 && preedit == NULL))
return -1;
if(ccap > 0)
commit[0] = '\0';
if(pcap > 0)
preedit[0] = '\0';
memset(resp, 0, sizeof *resp);
if(ipcreadn(fd, hdr, sizeof hdr) < 0)
return -1;
resp->eaten = hdr[0] != 0;
resp->ncommit = getlen(hdr + 1);
if(resp->ncommit > Ipcfieldmax)
return -1;
if(readfield(fd, resp->ncommit, commit, ccap) < 0)
return -1;
if(!want)
return 0;
if(ipcreadn(fd, npreedit, sizeof npreedit) < 0)
return -1;
resp->npreedit = getlen(npreedit);
if(resp->npreedit > Ipcfieldmax)
return -1;
return readfield(fd, resp->npreedit, preedit, pcap);
}