int ws_raw_writev(struct tcp_connection *c, int fd, const struct iovec *iov, int iovcnt, int tout) { int n; /* we do not have any threosholds for ws struct timeval snd; start_expire_timer(snd,tcpthreshold); */ lock_get(&c->write_lock); /* optimize write for a single chunk */ if (iovcnt == 1) n=tsend_stream(fd, iov[0].iov_base, iov[0].iov_len, tout); else n=tsend_stream_ev(fd, iov, iovcnt, tout); lock_release(&c->write_lock); /* get_time_difference(snd, tcpthreshold, tout); */ return n; }
inline static int _hep_write_on_socket(struct tcp_connection *c, int fd, char *buf, int len){ int n; lock_get(&c->write_lock); if (hep_async) { n=async_tsend_stream(c,fd,buf,len, hep_async_local_write_timeout); } else { n = tsend_stream(fd, buf, len, hep_send_timeout); } lock_release(&c->write_lock); return n; }
void release_tcpconn(struct tcp_connection* c, long state, int unix_sock) { long response[2]; LM_DBG("%s:%d %s releasing\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); LM_DBG("releasing con %p, state %ld, fd=%d, id=%d\n", c, state, c->fd, c->id); LM_DBG("extra_data %p\n", c->extra_data); /* release req & signal the parent */ c->reader_pid=0; /* reset it */ if (c->fd!=-1){ close(c->fd); c->fd=-1; } /* errno==EINTR, EWOULDBLOCK a.s.o todo */ response[0]=(long)c; response[1]=state; if (tsend_stream(unix_sock, (char*)response, sizeof(response), -1)<=0) LM_ERR("tsend_stream failed\n"); }
/*! \brief Finds a tcpconn & sends on it */ int tcp_send(struct socket_info* send_sock, int type, char* buf, unsigned len, union sockaddr_union* to, int id) { struct tcp_connection *c; struct tcp_connection *tmp; struct ip_addr ip; int port; int fd; long response[2]; int n; struct timeval get,rcv,snd; port=0; reset_tcp_vars(tcpthreshold); start_expire_timer(get,tcpthreshold); if (to){ su2ip_addr(&ip, to); port=su_getport(to); c=tcpconn_get(id, &ip, port, tcp_con_lifetime); }else if (id){ c=tcpconn_get(id, 0, 0, tcp_con_lifetime); }else{ LM_CRIT("tcp_send called with null id & to\n"); get_time_difference(get,tcpthreshold,tcp_timeout_con_get); return -1; } if (id){ if (c==0) { if (to){ /* try again w/o id */ c=tcpconn_get(0, &ip, port, tcp_con_lifetime); goto no_id; }else{ LM_ERR("id %d not found, dropping\n", id); get_time_difference(get,tcpthreshold,tcp_timeout_con_get); return -1; } }else goto get_fd; } no_id: if (c==0){ LM_DBG("no open tcp connection found, opening new one\n"); /* create tcp connection */ if ((c=tcpconn_connect(send_sock, to, type))==0){ LM_ERR("connect failed\n"); get_time_difference(get,tcpthreshold,tcp_timeout_con_get); return -1; } c->refcnt++; /* safe to do it w/o locking, it's not yet available to the rest of the world */ fd=c->s; /* send the new tcpconn to "tcp main" */ response[0]=(long)c; response[1]=CONN_NEW; n=send_fd(unix_tcp_sock, response, sizeof(response), c->s); get_time_difference(get,tcpthreshold,tcp_timeout_con_get); if (n<=0){ LM_ERR("failed send_fd: %s (%d)\n", strerror(errno), errno); n=-1; goto end; } goto send_it; } get_fd: get_time_difference(get,tcpthreshold,tcp_timeout_con_get); /* todo: see if this is not the same process holding * c and if so send directly on c->fd */ LM_DBG("tcp connection found (%p), acquiring fd\n", c); /* get the fd */ response[0]=(long)c; response[1]=CONN_GET_FD; start_expire_timer(rcv,tcpthreshold); n=send_all(unix_tcp_sock, response, sizeof(response)); if (n<=0){ LM_ERR("failed to get fd(write):%s (%d)\n", strerror(errno), errno); n=-1; get_time_difference(rcv,tcpthreshold,tcp_timeout_receive_fd); goto release_c; } LM_DBG("c= %p, n=%d\n", c, n); tmp=c; n=receive_fd(unix_tcp_sock, &c, sizeof(c), &fd, MSG_WAITALL); get_time_difference(rcv,tcpthreshold,tcp_timeout_receive_fd); if (n<=0){ LM_ERR("failed to get fd(receive_fd):" " %s (%d)\n", strerror(errno), errno); n=-1; goto release_c; } if (c!=tmp){ LM_CRIT("got different connection:" " %p (id= %d, refcnt=%d state=%d != " " %p (id= %d, refcnt=%d state=%d (n=%d)\n", c, c->id, c->refcnt, c->state, tmp, tmp->id, tmp->refcnt, tmp->state, n ); n=-1; /* fail */ goto end; } LM_DBG("after receive_fd: c= %p n=%d fd=%d\n",c, n, fd); send_it: LM_DBG("sending...\n"); lock_get(&c->write_lock); #ifdef USE_TLS if (c->type==PROTO_TLS) n=tls_blocking_write(c, fd, buf, len); else #endif /* n=tcp_blocking_write(c, fd, buf, len); */ start_expire_timer(snd,tcpthreshold); n=tsend_stream(fd, buf, len, tcp_send_timeout*1000); get_time_difference(snd,tcpthreshold,tcp_timeout_send); stop_expire_timer(get,tcpthreshold,0,buf,(int)len,1); lock_release(&c->write_lock); LM_DBG("after write: c= %p n=%d fd=%d\n",c, n, fd); LM_DBG("buf=\n%.*s\n", (int)len, buf); if (n<0){ LM_ERR("failed to send\n"); /* error on the connection , mark it as bad and set 0 timeout */ c->state=S_CONN_BAD; c->timeout=0; /* tell "main" it should drop this (optional it will t/o anyway?)*/ response[0]=(long)c; response[1]=CONN_ERROR; n=send_all(unix_tcp_sock, response, sizeof(response)); /* CONN_ERROR will auto-dec refcnt => we must not call tcpconn_put !!*/ if (n<=0){ LM_ERR("return failed (write):%s (%d)\n", strerror(errno), errno); } close(fd); return -1; /* error return, no tcpconn_put */ } end: close(fd); release_c: tcpconn_put(c); /* release c (lock; dec refcnt; unlock) */ return n; }
/** * Runs a JSON-RPC command (request or notification) * Params: * dst: destination of the command * cmd: comand to send * id: if present, unique ID of the command, otherwise this is a notification * ret: value returned in case of a request * * Returns: * -2: communication error * -3: reply error * 1: success */ static int jsonrpc_handle_cmd(union sockaddr_union *dst, char *cmd, int *id, pv_value_t *vret) { int r, fd, ret = -2; unsigned int cmd_len; struct timeval begin; struct pollfd pf; int tout_left, total; cJSON *obj = NULL, *aux; char buffer[JSONRPC_DEFAULT_BUFFER_SIZE + 1/* null terminate */]; /* connect to the destination */ fd = jsonrpc_get_fd(dst); if (fd < 0) { LM_ERR("cannot get a connection to %s:%hu\n", JSONRPC_PRINT(dst)); return -2; } /* we have a connection - send the command now */ cmd_len = strlen(cmd); if (tsend_stream(fd, cmd, cmd_len, jrpc_write_timeout) < 0) { LM_ERR("cannot send stream to %s:%hu\n", JSONRPC_PRINT(dst)); goto end; } /* notification - no need to wait for a reply */ if (!id) { ret = 1; goto end; } /* read the reply */ pf.fd = fd; pf.events = POLLIN; total = 0; gettimeofday(&begin, NULL); while (1) { /* compute how long we are allowed to block */ tout_left = jrpc_read_timeout - (get_time_diff(&begin) / 1000); if (tout_left <= 0) { LM_ERR("read timeout reached (%s:%hu)\n", JSONRPC_PRINT(dst)); goto end; } r = poll(&pf, 1, tout_left); if (r < 0) { if (errno == EINTR) continue; LM_ERR("poll failed: %s [%d\n", strerror(errno), errno); goto end; } if (pf.revents & POLLIN) { /* now we can read */ r = read(fd, buffer + total, JSONRPC_DEFAULT_BUFFER_SIZE - total); if (r < 0) { if (errno == EINTR) continue; LM_ERR("cannot read reply from JSON-RPC server %s:%hu\n", JSONRPC_PRINT(dst)); goto end; } total += r; buffer[total] = '\0'; /* everything read - try to parse it now */ obj = cJSON_Parse(buffer); if (!obj) { LM_DBG("could not parse json [%s] - perhapse we did not " "receive all of it, retrying!\n", buffer); continue; } /* yey, we have an object */ break; } else if (pf.revents & POLLERR) { /* someting happened with the connection */ LM_ERR("connection error to %s:%hu - %s:%d\n", JSONRPC_PRINT(dst), strerror(errno), errno); goto end; } } aux = cJSON_GetObjectItem(obj, "error"); if (aux) { /* return the entire error */ vret->rs.s = cJSON_Print(aux); vret->rs.len = strlen(vret->rs.s); vret->flags = PV_VAL_STR; LM_DBG("Error got from JSON-RPC: %s!\n", buffer); ret = -3; goto end; } aux = cJSON_GetObjectItem(obj, "result"); if (!aux) { LM_WARN("Invalid reply from JSON-RPC: %s!\n", buffer); pv_get_null(NULL, NULL, vret); ret = -3; goto end; } if (aux->type == cJSON_Number) pv_get_sintval(NULL, NULL, vret, aux->valueint); else { vret->rs.s = cJSON_Print(aux); vret->rs.len = strlen(vret->rs.s); vret->flags = PV_VAL_STR; } ret = 1; end: shutdown(fd, SHUT_RDWR); close(fd); return ret; }