/*
* Copyright (C) Joerg Lenneis 2003
+ * Copyright (C) Frank Lahm 2010
* All Rights Reserved. See COPYING.
*/
#endif /* ! SOL_TCP */
/* Wait MAX_DELAY seconds before a request to the CNID server times out */
-#define MAX_DELAY 10
+#define MAX_DELAY 20
+#define ONE_DELAY 5
static void RQST_RESET(struct cnid_dbd_rqst *r)
{
int attr;
int err;
struct addrinfo hints, *servinfo, *p;
+ int optval;
+ socklen_t optlen = sizeof(optval);
/* Prepare hint for getaddrinfo */
memset(&hints, 0, sizeof hints);
if ((sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
LOG(log_info, logtype_default, "tsock_getfd: socket CNID server %s:: %s",
host, strerror(errno));
- continue;
+ continue;
}
attr = 1;
}
if (setnonblock(sock, 1) != 0) {
- LOG(log_error, logtype_cnid, "getfd: setnonblock: %s", strerror(err));
+ LOG(log_error, logtype_cnid, "getfd: setnonblock: %s", strerror(errno));
close(sock);
sock = -1;
return -1;
}
-
+
if (connect(sock, p->ai_addr, p->ai_addrlen) == -1) {
if (errno == EINPROGRESS) {
struct timeval tv;
tv.tv_usec = 0;
- tv.tv_sec = 1; /* give it one second ... */
+ tv.tv_sec = 5; /* give it five seconds ... */
fd_set wfds;
FD_ZERO(&wfds);
FD_SET(sock, &wfds);
- if (select(sock + 1, NULL, &wfds, NULL, &tv) < 1) {
+
+ if ((err = select(sock + 1, NULL, &wfds, NULL, &tv)) == 0) {
+ /* timeout */
+ LOG(log_error, logtype_cnid, "getfd: select timed out for CNID server %s",
+ host);
+ close(sock);
+ sock = -1;
+ continue;
+ }
+ if (err == -1) {
+ /* select failed */
+ LOG(log_error, logtype_cnid, "getfd: select failed for CNID server %s",
+ host);
+ close(sock);
+ sock = -1;
+ continue;
+ }
+
+ if ( ! FD_ISSET(sock, &wfds)) {
/* give up */
- LOG(log_error, logtype_cnid, "getfd: select timed out for CNID server %s: %s",
- host, strerror(errno));
+ LOG(log_error, logtype_cnid, "getfd: socket not ready connecting to %s",
+ host);
close(sock);
sock = -1;
continue;
}
- int optval;
- socklen_t optlen;
- if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &optval, &optlen) != 0 || optval != 0) {
- /* somethings still wrong */
- LOG(log_error, logtype_cnid, "getfd: getsockopt error with CNID server %s: %s",
- host, strerror(errno));
+
+ if ((err = getsockopt(sock, SOL_SOCKET, SO_ERROR, &optval, &optlen)) != 0 || optval != 0) {
+ if (err != 0) {
+ /* somethings very wrong */
+ LOG(log_error, logtype_cnid, "getfd: getsockopt error with CNID server %s: %s",
+ host, strerror(errno));
+ } else {
+ errno = optval;
+ LOG(log_error, logtype_cnid, "getfd: getsockopt says: %s",
+ strerror(errno));
+ }
close(sock);
sock = -1;
continue;
continue;
}
}
-
+
/* We've got a socket */
break;
}
freeaddrinfo(servinfo);
if (p == NULL) {
- LOG(log_error, logtype_cnid, "tsock_getfd: no suitable network config from CNID server %s:%s",
- host, port);
+ errno = optval;
+ LOG(log_error, logtype_cnid, "tsock_getfd: no suitable network config from CNID server (%s:%s): %s",
+ host, port, strerror(errno));
return -1;
}
static int write_vec(int fd, struct iovec *iov, ssize_t towrite, int vecs)
{
ssize_t len;
+ int slept = 0;
+ int sleepsecs;
while (1) {
if (((len = writev(fd, iov, vecs)) == -1 && errno == EINTR))
continue;
+ if ((! slept) && len == -1 && errno == EAGAIN) {
+ sleepsecs = 2;
+ while ((sleepsecs = sleep(sleepsecs)));
+ slept = 1;
+ continue;
+ }
+
if (len == towrite) /* wrote everything out */
break;
if (len == -1)
- LOG(log_error, logtype_cnid, "write_vec: short write: %s", strerror(errno));
+ LOG(log_error, logtype_cnid, "write_vec: %s", strerror(errno));
else
LOG(log_error, logtype_cnid, "write_vec: short write: %d", len);
return len;
int len;
struct iovec iov[2];
- LOG(log_debug, logtype_cnid, "init_tsock: BEGIN. Opening volume '%s', CNID Server: %s/%s",
+ LOG(log_debug, logtype_cnid, "init_tsock: BEGIN. Opening volume '%s', CNID Server: %s/%s",
db->db_dir, db->cnidserver, db->cnidport);
if ((fd = tsock_getfd(db->cnidserver, db->cnidport)) < 0)
db->db_dir, strerror(errno));
return -1;
}
-
+
LOG(log_maxdebug, logtype_cnid, "send_packet: {done}");
return 0;
}
return 0;
}
-/*!
- * Non-blocking read "length" bytes within 1 second using select
- *
- * @param socket (r) must be nonblocking !
- * @param data (rw) buffer for the read data
- * @param lenght (r) how many bytes to read
- *
- * @returns number of bytes actually read or -1 on fatal error
- */
-static ssize_t read_packet(int socket, void *data, const size_t length)
-{
- size_t stored;
- ssize_t len;
- struct timeval tv;
- fd_set rfds;
- int ret;
-
- stored = 0;
-
- while (stored < length) {
- len = read(socket, (u_int8_t *) data + stored, length - stored);
- if (len == -1) {
- switch (errno) {
- case EINTR:
- continue;
- case EAGAIN:
- tv.tv_usec = 0;
- tv.tv_sec = 1;
-
- FD_ZERO(&rfds);
- FD_SET(socket, &rfds);
- while ((ret = select(socket + 1, &rfds, NULL, NULL, &tv)) < 1) {
- switch (ret) {
- case 0:
- LOG(log_warning, logtype_cnid, "select timeout 1s");
- return stored;
- default: /* -1 */
- LOG(log_error, logtype_cnid, "select: %s", strerror(errno));
- return -1;
- }
- }
- continue;
- }
- LOG(log_error, logtype_cnid, "read: %s", strerror(errno));
- return -1;
- }
- else if (len > 0)
- stored += len;
- else
- break;
- }
- return stored;
-}
-
/* ---------------------
* send a request and get reply
* assume send is non blocking
len = rply->namelen;
nametmp = rply->name;
- ret = read_packet(db->fd, rply, sizeof(struct cnid_dbd_rply));
+ ret = readt(db->fd, rply, sizeof(struct cnid_dbd_rply), 0, ONE_DELAY);
if (ret != sizeof(struct cnid_dbd_rply)) {
- LOG(log_error, logtype_cnid, "dbd_rpc: Error reading header from fd (db_dir %s): %s",
- db->db_dir, ret == -1?strerror(errno):"closed");
+ LOG(log_debug, logtype_cnid, "dbd_rpc: Error reading header from fd (db_dir %s): %s",
+ db->db_dir, ret == -1 ? strerror(errno) : "closed");
rply->name = nametmp;
return -1;
}
db->db_dir, rply->name, rply->namelen, len);
return -1;
}
- if (rply->namelen && (ret = read_packet(db->fd, rply->name, rply->namelen)) != (ssize_t)rply->namelen) {
+ if (rply->namelen && (ret = readt(db->fd, rply->name, rply->namelen, 0, ONE_DELAY)) != (ssize_t)rply->namelen) {
LOG(log_error, logtype_cnid, "dbd_rpc: Error reading name from fd (db_dir %s): %s",
db->db_dir, ret == -1?strerror(errno):"closed");
return -1;
memcpy(db->client_stamp, stamp, ADEDLEN_PRIVSYN);
memcpy(db->stamp, stamp, ADEDLEN_PRIVSYN);
}
- LOG(log_debug, logtype_cnid, "transmit: attached to '%s', stamp: '%08lx'.",
+ LOG(log_debug, logtype_cnid, "transmit: attached to '%s', stamp: '%08lx'.",
db->db_dir, *(uint64_t *)stamp);
}
if (!dbd_rpc(db, rqst, rply)) {
db->fd = -1; /* FD not valid... will need to reconnect */
}
+ if (errno == ECONNREFUSED) { /* errno carefully injected in tsock_getfd */
+ /* give up */
+ LOG(log_error, logtype_cnid, "transmit: connection refused (db_dir %s)", db->db_dir);
+ return -1;
+ }
+
if (!clean) { /* don't sleep if just got disconnected by cnid server */
time(&t);
if (t - orig > MAX_DELAY) {
cdb->cnid_delete = cnid_dbd_delete;
cdb->cnid_get = cnid_dbd_get;
cdb->cnid_lookup = cnid_dbd_lookup;
+ cdb->cnid_find = cnid_dbd_find;
cdb->cnid_nextid = NULL;
cdb->cnid_resolve = cnid_dbd_resolve;
cdb->cnid_getstamp = cnid_dbd_getstamp;
rqst.op = CNID_DBD_OP_RESOLVE;
rqst.cnid = *id;
- /* This mimicks the behaviour of the "regular" cnid_resolve. So far,
- nobody uses the content of buffer. It only provides space for the
- name in the caller. */
- rply.name = (char *)buffer + CNID_HEADER_LEN;
- rply.namelen = len - CNID_HEADER_LEN;
+ /* Pass buffer to transmit so it can stuff the reply data there */
+ rply.name = (char *)buffer;
+ rply.namelen = len;
if (transmit(db, &rqst, &rply) < 0) {
errno = CNID_ERR_DB;
switch (rply.result) {
case CNID_DBD_RES_OK:
*id = rply.did;
- name = rply.name;
+ name = rply.name + CNID_NAME_OFS;
LOG(log_debug, logtype_cnid, "cnid_dbd_resolve: resolved did: %u, name: '%s'", ntohl(*id), name);
break;
case CNID_DBD_RES_NOTFOUND:
return id;
}
+/* ---------------------- */
+int cnid_dbd_find(struct _cnid_db *cdb, char *name, size_t namelen, void *buffer, size_t buflen)
+{
+ CNID_private *db;
+ struct cnid_dbd_rqst rqst;
+ struct cnid_dbd_rply rply;
+ int count;
+
+ if (!cdb || !(db = cdb->_private) || !name) {
+ LOG(log_error, logtype_cnid, "cnid_find: Parameter error");
+ errno = CNID_ERR_PARAM;
+ return CNID_INVALID;
+ }
+
+ if (namelen > MAXPATHLEN) {
+ LOG(log_error, logtype_cnid, "cnid_find: Path name is too long");
+ errno = CNID_ERR_PATH;
+ return CNID_INVALID;
+ }
+
+ LOG(log_debug, logtype_cnid, "cnid_find(\"%s\")", name);
+
+ RQST_RESET(&rqst);
+ rqst.op = CNID_DBD_OP_SEARCH;
+
+ rqst.name = name;
+ rqst.namelen = namelen;
+
+ rply.name = buffer;
+ rply.namelen = buflen;
+
+ if (transmit(db, &rqst, &rply) < 0) {
+ errno = CNID_ERR_DB;
+ return CNID_INVALID;
+ }
+
+ switch (rply.result) {
+ case CNID_DBD_RES_OK:
+ count = rply.namelen / sizeof(cnid_t);
+ LOG(log_debug, logtype_cnid, "cnid_find: got %d matches", count);
+ break;
+ case CNID_DBD_RES_NOTFOUND:
+ count = 0;
+ break;
+ case CNID_DBD_RES_ERR_DB:
+ errno = CNID_ERR_DB;
+ count = -1;
+ break;
+ default:
+ abort();
+ }
+
+ return count;
+}
+
/* ---------------------- */
int cnid_dbd_update(struct _cnid_db *cdb, const cnid_t id, const struct stat *st,
const cnid_t did, char *name, const size_t len)