#include "files.hpp"
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/uio.h>
#include <fcntl.h>
#include <unistd.h>
static bool create_path(char* pn) {
char* p = pn;
while (*p == '/') ++p;
while ((p = strchr(p, '/')) != NULL) {
*p = '\0';
if ((mkdir(pn, 0700) == -1) && (errno != EEXIST)) {
log_errno(error, "mkdir(%s)", pn);
*p = '/';
return false;
}
*p = '/';
++p;
}
return true;
}
int file_open(const char* fn, char mode, bool nonblock) {
if (unlikely(!fn)) return -1;
const int flags = nonblock? O_NONBLOCK: 0;
int fd;
if (mode == 'w') {
fd = open(fn, O_WRONLY|O_CREAT|O_EXCL|flags, 0600);
if (fd == -1) {
if (errno == ENOENT) {
if (create_path(strdupa(fn))) {
fd = open(fn, O_WRONLY|O_CREAT|O_EXCL|flags, 0600);
if (fd == -1) {
log_errno(error, "open(%s,%c)", fn, mode);
}
}
} else if (errno == EEXIST) {
if (unlink(fn) != -1) {
fd = open(fn, O_WRONLY|O_CREAT|O_EXCL|flags, 0600);
if (fd == -1) {
log_errno(error, "open(%s,%c)", fn, mode);
}
} else {
log_errno(error, "unlink(%s)", fn);
}
} else {
log_errno(error, "open(%s,%c)", fn, mode);
}
}
} else {
assert(mode == 'r');
fd = open(fn, O_RDONLY|flags);
if (fd == -1) {
log_errno(error, "open(%s,%c)", fn, mode);
}
}
log(io, "open(%s,%c,%d): %d", fn, mode, nonblock, fd);
return fd;
}
off_t file_size(int fd) {
static struct stat ss;
if (fstat(fd, &ss) == -1) {
log_errno(error, "fstat");
return 0;
}
return ss.st_size;
}
bool set_blocking(int fd, bool block) {
int flags = fcntl(fd, F_GETFL, 0);
unless (flags != -1) {
log_errno(error, "fcntl(%d, F_GETFL)", fd);
return false;
}
flags = fcntl(fd, F_SETFL, block? (flags&(~O_NONBLOCK)): (flags|O_NONBLOCK));
unless (flags != -1) {
log_errno(error, "fcntl(%d, F_SETFL, |O_NONBLOCK)", fd);
return false;
}
return true;
}
bool has_data(int fd, msec_t tout) {
struct timeval tv = {
(time_t)tout/1000,
((time_t)tout%1000)*1000
};
fd_set fds;
FD_ZERO(&fds);
FD_SET(fd, &fds);
return (select(fd+1, &fds, NULL, NULL, &tv) != 0);
}
bool readline_r(char* buf, size_t len, char sep, readline_handler handler, intptr_t handler_ctx) {
char* p = buf;
size_t l = len;
char* nl;
while (l) {
if ((nl = strnchr(p, sep, l)) != NULL) {
*nl = '\0';
if (nl != p) {
log(io, "readline buf: '%s', %d", p, (int)(nl-p));
unless (handler(p, nl-p, handler_ctx)) {
log(notice, "readline buf parsing failed");
return false;
}
}
l -= (nl-p)+1;
p = nl+1;
} else {
log(debug, "readline buf w/o ending separator");
unless (handler(p, l, handler_ctx)) {
log(notice, "readline buf parsing failed");
return false;
}
break;
}
}
return true;
}
tristate_t readline_r(int fd, char sep, readline_ctx_t* ctx, readline_handler handler, intptr_t handler_ctx) {
static readline_ctx_t _ctx;
_ctx.len = 0;
char* buf = ctx? ctx->buf: _ctx.buf;
size_t& len = ctx? ctx->len: _ctx.len;
while (true) {
ssize_t rv = read(fd, buf+len, sizeof(readline_ctx_t::buf)-len-1);
if (is_noop_errno(rv)) {
unless (ctx) {
log(error, "readline on non-blocking fd w/o ctx");
return TRI_FALSE;
} else {
}
return TRI_NONE;
} else if (!rv) {
log(io, "readline eof");
if (len) {
log(notice, "won't process last %zu bytes (not terminated?)", len);
}
len = 0;
return TRI_TRUE;
} else if (rv < 0) {
log_errno(notice, "read");
len = 0;
return TRI_FALSE;
}
len += rv;
buf[len] = '\0';
char* p = buf;
size_t l = len;
char* nl;
while (l && ((nl = strnchr(p, sep, l)) != NULL)) {
*nl = '\0';
if (nl != p) {
log(io, "readline: '%s', %d", p, (int)(nl-p));
unless (handler(p, nl-p, handler_ctx)) {
log(notice, "readline parsing failed");
len = 0;
return TRI_FALSE;
}
}
l -= (nl-p)+1;
p = nl+1;
}
if (p == buf) {
if (l >= sizeof(readline_ctx_t::buf)-1) {
log(notice, "truncated line!");
len = 0;
return TRI_FALSE;
} else {
len = l;
}
} else {
if (l) {
log(debug, "moving %zu bytes", l);
memmove(buf, p, l);
}
len = l;
}
}
}
tristate_t separator_write(int fd, const char* b1, size_t l1, const char* b2, size_t l2, const char* b3, size_t l3) {
int ionum;
size_t iolen;
iovec iov[7];
iov[0].iov_base = (void*)">";
iov[0].iov_len = 1;
iov[1].iov_base = (void*)b1;
iov[1].iov_len = l1;
if (b2) {
iov[2].iov_base = (void*)"~";
iov[2].iov_len = 1;
iov[3].iov_base = (void*)b2;
iov[3].iov_len = l2;
if (b3) {
iov[4].iov_base = (void*)"~";
iov[4].iov_len = 1;
iov[5].iov_base = (void*)b3;
iov[5].iov_len = l3;
iov[6].iov_base = (void*)"<";
iov[6].iov_len = 1;
ionum = 7;
iolen = l1 + l2 + l3 + 4;
} else {
iov[4].iov_base = (void*)"<";
iov[4].iov_len = 1;
ionum = 5;
iolen = l1 + l2 + 3;
}
} else {
iov[2].iov_base = (void*)"<";
iov[2].iov_len = 1;
ionum = 3;
iolen = l1 + 2;
}
ssize_t rv = writev(fd, iov, ionum);
if (rv == -1) {
if (is_noop_errno(rv)) {
log_errno(debug, "write(%d)", fd);
return TRI_NONE;
} else {
log_errno(error, "write(%d)", fd);
return TRI_FALSE;
}
} else if ((size_t)rv != iolen) {
log(error, "write(%d): %zd of %zu", fd, rv, iolen);
return TRI_FALSE;
} else {
log(io, "write(%d,%zu+%zu+%zu)", fd, l1, l2, l3);
return TRI_TRUE;
}
}
tristate_t separator_read(int fd, bool (*handler)(int, char*, size_t), msec_t tout) {
while (true) {
if (tout && !has_data(fd, tout)) {
return TRI_NONE;
}
char b[BUF_SIZE];
ssize_t r = read(fd, b, sizeof(b)-1);
if (r == -1) {
if (is_noop_errno(r)) {
log_errno(io, "read(%d)", fd);
return TRI_NONE;
} else {
log_errno(error, "read(%d)", fd);
return TRI_FALSE;
}
} else if (!r) {
log(debug, "read(%d): eof", fd);
return TRI_FALSE;
} else {
log(io, "read(%d): %zd", fd, r);
b[(size_t)r] = '\0';
}
char* p = b;
while (*p) {
char* start = strchr(p, '>');
if (!start) {
log(error, "cannot find start '>'");
break;
}
char* end = strchr(start+1, '<');
if (!end) {
log(error, "cannot find end '<'");
break;
}
*end = '\0';
p = end+1;
log(io, "calling %p(%d, %p, %zu)", handler, fd, start+1, end-start-1);
if (!handler) {
} else if (!handler(fd, start+1, end-start-1)) {
return TRI_FALSE;
}
}
}
assert(false);
return TRI_FALSE;
}