int rtpp_command_pre_parse(struct cfg *cf, struct rtpp_command *cmd) { struct cmd_props cprops; if (fill_cmd_props(cf, cmd, &cprops) != 0) { RTPP_LOG(cf->stable->glog, RTPP_LOG_ERR, "unknown command \"%c\"", cmd->argv[0][0]); reply_error(cmd, ECODE_CMDUNKN); return (-1); } if (cmd->argc < cprops.min_argc || cmd->argc > cprops.max_argc) { RTPP_LOG(cf->stable->glog, RTPP_LOG_ERR, "%s command syntax error" ": invalid number of arguments (%d)", cmd->cca.rname, cmd->argc); reply_error(cmd, ECODE_PARSE_NARGS); return (-1); } if (cprops.has_cmods == 0 && cprops.cmods[0] != '\0') { RTPP_LOG(cf->stable->glog, RTPP_LOG_ERR, "%s command syntax error" ": modifiers are not supported by the command", cmd->cca.rname); reply_error(cmd, ECODE_PARSE_MODS); return (-1); } cmd->cca.call_id = cprops.has_call_id ? cmd->argv[1] : NULL; cmd->cca.from_tag = cprops.fpos > 0 ? cmd->argv[cprops.fpos] : NULL; cmd->cca.to_tag = cprops.tpos > 0 ? cmd->argv[cprops.tpos] : NULL; return (0); }
static int rtpp_stream_handle_play(struct rtpp_stream *self, char *codecs, char *pname, int playcount, struct rtpp_command *cmd, int ptime) { struct rtpp_stream_priv *pvt; int n; char *cp; struct rtpp_server *rsrv; uint16_t seq; uint32_t ssrc; const char *plerror; pvt = PUB2PVT(self); pthread_mutex_lock(&pvt->lock); plerror = "reason unknown"; while (*codecs != '\0') { n = strtol(codecs, &cp, 10); if (cp == codecs) { plerror = "invalid codecs"; break; } codecs = cp; if (*codecs != '\0') codecs++; rsrv = rtpp_server_ctor(pname, n, playcount, cmd->dtime, ptime); if (rsrv == NULL) { RTPP_LOG(pvt->pub.log, RTPP_LOG_DBUG, "rtpp_server_ctor(\"%s\", %d, %d) failed", pname, n, playcount); plerror = "rtpp_server_ctor() failed"; continue; } rsrv->stuid = self->stuid; ssrc = CALL_METHOD(rsrv, get_ssrc); seq = CALL_METHOD(rsrv, get_seq); if (CALL_METHOD(pvt->servers_wrt, reg, rsrv->rcnt, rsrv->sruid) != 0) { CALL_METHOD(rsrv->rcnt, decref); plerror = "servers_wrt->reg() method failed"; break; } assert(pvt->rtps == RTPP_UID_NONE); pvt->rtps = rsrv->sruid; pthread_mutex_unlock(&pvt->lock); cmd->csp->nplrs_created.cnt++; CALL_METHOD(rsrv->rcnt, reg_pd, (rtpp_refcnt_dtor_t)player_predestroy_cb, pvt->rtpp_stats); CALL_METHOD(rsrv->rcnt, decref); RTPP_LOG(pvt->pub.log, RTPP_LOG_INFO, "%d times playing prompt %s codec %d: SSRC=" SSRC_FMT ", seq=%u", playcount, pname, n, ssrc, seq); return 0; } pthread_mutex_unlock(&pvt->lock); RTPP_LOG(pvt->pub.log, RTPP_LOG_ERR, "can't create player: %s", plerror); return -1; }
static int rtpp_proc_ttl_foreach(void *dp, void *ap) { struct foreach_args *fap; struct rtpp_session *sp; fap = (struct foreach_args *)ap; /* * This method does not need us to bump ref, since we are in the * locked context of the rtpp_hash_table, which holds its own ref. */ sp = (struct rtpp_session *)dp; if (CALL_METHOD(sp->rtp, get_ttl) == 0) { RTPP_LOG(sp->log, RTPP_LOG_INFO, "session timeout"); if (sp->timeout_data.notify_target != NULL) { CALL_METHOD(fap->rtpp_notify_cf, schedule, sp->timeout_data.notify_target, sp->timeout_data.notify_tag); } CALL_SMETHOD(fap->rtpp_stats, updatebyname, "nsess_timeout", 1); CALL_METHOD(fap->sessions_wrt, unreg, sp->seuid); return (RTPP_HT_MATCH_DEL); } else { CALL_METHOD(sp->rtp, decr_ttl); } return (RTPP_HT_MATCH_CONT); }
static int rtpp_stream_guess_addr(struct rtpp_stream *self, struct rtp_packet *packet) { int rport; const char *actor, *ptype; struct rtpp_stream_priv *pvt; pvt = PUB2PVT(self); if (self->addr != NULL && ishostseq(self->addr, sstosa(&packet->raddr))) { return (0); } if (self->addr == NULL) { self->addr = malloc(packet->rlen); if (self->addr == NULL) { return (-1); } } actor = rtpp_stream_get_actor(self); ptype = rtpp_stream_get_proto(self); rport = ntohs(satosin(&packet->raddr)->sin_port); if (IS_LAST_PORT(rport)) { return (-1); } memcpy(self->addr, &packet->raddr, packet->rlen); satosin(self->addr)->sin_port = htons(rport + 1); /* Use guessed value as the only true one for asymmetric clients */ self->latch_info.latched = self->asymmetric; RTPP_LOG(pvt->pub.log, RTPP_LOG_INFO, "guessing %s port " "for %s to be %d", ptype, actor, rport + 1); return (0); }
static void do_timeout_notification(struct rtpp_notify_wi *wi, int retries, struct rtpp_log *log) { int result; if (wi->rttp->connected == 0) { reconnect_timeout_handler(log, wi->rttp); /* If connect fails, no notification will be sent */ if (wi->rttp->connected == 0) { RTPP_LOG(log, RTPP_LOG_ERR, "unable to send timeout notification"); return; } } do { result = send(wi->rttp->fd, wi->notify_buf, wi->len - 1, 0); } while (result == -1 && errno == EINTR); if (result < 0) { wi->rttp->connected = 0; RTPP_ELOG(log, RTPP_LOG_ERR, "failed to send timeout notification"); if (retries > 0) do_timeout_notification(wi, retries - 1, log); } }
static int rtpp_stream_check_latch_override(struct rtpp_stream *self, struct rtp_packet *packet) { const char *actor; struct rtpp_stream_priv *pvt; char saddr[MAX_AP_STRBUF]; pvt = PUB2PVT(self); if (self->session_type == SESS_RTCP || self->latch_info.ssrc == 0) return (0); if (rtp_packet_parse(packet) != RTP_PARSER_OK) return (0); if (packet->parsed->ssrc != self->latch_info.ssrc) return (0); if (packet->parsed->seq < self->latch_info.seq && self->latch_info.seq - packet->parsed->seq < 536) return (0); actor = rtpp_stream_get_actor(self); addrport2char_r(sstosa(&packet->raddr), saddr, sizeof(saddr)); RTPP_LOG(pvt->pub.log, RTPP_LOG_INFO, "%s's address re-latched: %s (%s), SSRC=" SSRC_FMT ", Seq=%u->%u", actor, saddr, "RTP", self->latch_info.ssrc, self->latch_info.seq, packet->parsed->seq); self->latch_info.seq = packet->parsed->seq; return (1); }
static void rtpp_mif_dtor(struct rtpp_module_if_priv *pvt) { rtpp_module_if_fin(&(pvt->pub)); /* First, stop the worker thread and wait for it to terminate */ rtpp_queue_put_item(pvt->sigterm, pvt->req_q); pthread_join(pvt->thread_id, NULL); rtpp_queue_destroy(pvt->req_q); /* Then run module destructor (if any) */ if (pvt->mip->dtor != NULL) { pvt->mip->dtor(pvt->mpvt); } #if RTPP_CHECK_LEAKS /* Check if module leaked any mem */ if (rtpp_memdeb_dumpstats(pvt->memdeb_p, 1) != 0) { RTPP_LOG(pvt->log, RTPP_LOG_ERR, "module '%s' leaked memory after " "destruction", pvt->mip->name); } rtpp_memdeb_dtor(pvt->memdeb_p); #endif /* Unload and free everything */ dlclose(pvt->dmp); CALL_METHOD(pvt->log->rcnt, decref); free(pvt); }
static void rtpp_stream_finish_playback(struct rtpp_stream *self, uint64_t sruid) { struct rtpp_stream_priv *pvt; pvt = PUB2PVT(self); pthread_mutex_lock(&pvt->lock); if (pvt->rtps != RTPP_UID_NONE && pvt->rtps == sruid) { pvt->rtps = RTPP_UID_NONE; RTPP_LOG(pvt->pub.log, RTPP_LOG_INFO, "player at port %d has finished", self->port); } pthread_mutex_unlock(&pvt->lock); }
int rtpp_anetio_sendto(struct rtpp_anetio_cf *netio_cf, int sock, const void *msg, \ size_t msg_len, int flags, const struct sockaddr *sendto, socklen_t tolen) { struct rtpp_wi *wi; wi = rtpp_wi_malloc(sock, msg, msg_len, flags, sendto, tolen); if (wi == NULL) { return (-1); } #if RTPP_DEBUG_netio >= 1 wi->debug = 1; wi->log = netio_cf->args[0].glog; CALL_SMETHOD(wi->log->rcnt, incref); #if RTPP_DEBUG_netio >= 2 RTPP_LOG(netio_cf->args[0].glog, RTPP_LOG_DBUG, "malloc(%d, %p, %d, %d, %p, %d) = %p", sock, msg, msg_len, flags, sendto, tolen, wi); RTPP_LOG(netio_cf->args[0].glog, RTPP_LOG_DBUG, "sendto(%d, %p, %d, %d, %p, %d)", wi->sock, wi->msg, wi->msg_len, wi->flags, wi->sendto, wi->tolen); #endif #endif rtpp_queue_put_item(wi, netio_cf->args[0].out_q); return (0); }
static void reconnect_timeout_handler(struct rtpp_log *log, struct rtpp_tnotify_target *rttp) { assert (rttp->connected == 0); if (rttp->fd == -1) { RTPP_LOG(log, RTPP_LOG_DBUG, "connecting timeout socket"); } else { RTPP_LOG(log, RTPP_LOG_DBUG, "reconnecting timeout socket"); close(rttp->fd); } rttp->fd = socket(rttp->socket_type, SOCK_STREAM, 0); if (rttp->fd == -1) { RTPP_ELOG(log, RTPP_LOG_ERR, "can't create timeout socket"); return; } if (rttp->local != NULL) { if (bind(rttp->fd, rttp->local, SA_LEN(rttp->local)) < 0) { RTPP_ELOG(log, RTPP_LOG_ERR, "can't bind timeout socket"); goto e0; } } if (connect(rttp->fd, (struct sockaddr *)&(rttp->remote), rttp->remote_len) == -1) { RTPP_ELOG(log, RTPP_LOG_ERR, "can't connect to timeout socket"); goto e0; } else { rttp->connected = 1; } return; e0: close(rttp->fd); rttp->fd = -1; return; }
static void rtpp_stream_handle_noplay(struct rtpp_stream *self) { struct rtpp_stream_priv *pvt; pvt = PUB2PVT(self); pthread_mutex_lock(&pvt->lock); if (pvt->rtps != RTPP_UID_NONE) { if (CALL_METHOD(pvt->servers_wrt, unreg, pvt->rtps) != NULL) { pvt->rtps = RTPP_UID_NONE; } RTPP_LOG(pvt->pub.log, RTPP_LOG_INFO, "stopping player at port %d", self->port); } pthread_mutex_unlock(&pvt->lock); }
static void rtpp_mif_do_acct(struct rtpp_module_if *self, struct rtpp_acct *acct) { struct rtpp_module_if_priv *pvt; struct rtpp_wi *wi; pvt = PUB2PVT(self); wi = rtpp_wi_malloc_apis(do_acct_aname, &acct, sizeof(acct)); if (wi == NULL) { RTPP_LOG(pvt->log, RTPP_LOG_ERR, "module '%s': cannot allocate " "memory", pvt->mip->name); return; } CALL_METHOD(acct->rcnt, incref); rtpp_queue_put_item(wi, pvt->req_q); }
static int rtpp_stream_latch(struct rtpp_stream *self, double dtime, struct rtp_packet *packet) { const char *actor, *ptype, *ssrc, *seq, *relatch; char ssrc_buf[11], seq_buf[6]; struct rtpp_stream_priv *pvt; char saddr[MAX_AP_STRBUF]; pvt = PUB2PVT(self); if (pvt->last_update != 0 && \ dtime - pvt->last_update < UPDATE_WINDOW) { return (0); } actor = rtpp_stream_get_actor(self); ptype = rtpp_stream_get_proto(self); if (self->session_type == SESS_RTP) { if (rtp_packet_parse(packet) == RTP_PARSER_OK) { self->latch_info.ssrc = packet->parsed->ssrc; self->latch_info.seq = packet->parsed->seq; snprintf(ssrc_buf, sizeof(ssrc_buf), SSRC_FMT, packet->parsed->ssrc); snprintf(seq_buf, sizeof(seq_buf), "%u", packet->parsed->seq); ssrc = ssrc_buf; seq = seq_buf; } else { self->latch_info.ssrc = 0; ssrc = seq = "INVALID"; } } else { self->latch_info.ssrc = 0; ssrc = seq = "UNKNOWN"; } addrport2char_r(sstosa(&packet->raddr), saddr, sizeof(saddr)); if (self->latch_info.latched == 0) { relatch = ""; } else { relatch = "re-"; } RTPP_LOG(pvt->pub.log, RTPP_LOG_INFO, "%s's address %slatched in: %s (%s), SSRC=%s, Seq=%s", actor, relatch, saddr, ptype, ssrc, seq); self->latch_info.latched = 1; return (1); }
static void handle_nomem(struct cfg *cf, struct rtpp_command *cmd, int ecode, struct ul_opts *ulop, struct rtpp_socket **fds, struct rtpp_session *spa) { int i; RTPP_LOG(cf->stable->glog, RTPP_LOG_ERR, "can't allocate memory"); rtpp_command_ul_opts_free(ulop); if (spa != NULL) { CALL_METHOD(spa->rcnt, decref); } if (fds != NULL) { for (i = 0; i < 2; i++) if (fds[i] != NULL) CALL_METHOD(fds[i]->rcnt, decref); } reply_error(cmd, ecode); }
static void rtpp_stream_prefill_addr(struct rtpp_stream *self, struct sockaddr **iapp, double dtime) { struct rtpp_stream_priv *pvt; char saddr[MAX_AP_STRBUF]; const char *actor, *ptype; pvt = PUB2PVT(self); if (self->addr != NULL) pvt->last_update = dtime; /* * Unless the address provided by client historically * cannot be trusted and address is different from one * that we recorded update it. */ if (pvt->untrusted_addr != 0) return; if (self->addr != NULL && isaddrseq(self->addr, *iapp)) { return; } addrport2char_r(*iapp, saddr, sizeof(saddr)); actor = rtpp_stream_get_actor(self); ptype = rtpp_stream_get_proto(self); RTPP_LOG(pvt->pub.log, RTPP_LOG_INFO, "pre-filling %s's %s address " "with %s", actor, ptype, saddr); if (self->addr != NULL) { if (self->latch_info.latched != 0) { if (pvt->prev_addr != NULL) free(pvt->prev_addr); pvt->prev_addr = self->addr; } else { free(self->addr); } } self->addr = *iapp; *iapp = NULL; }
int rtpp_anetio_send_pkt_na(struct sthread_args *sender, int sock, \ struct rtpp_netaddr *sendto, struct rtp_packet *pkt, struct rtpp_refcnt *sock_rcnt, struct rtpp_log *plog) { struct rtpp_wi *wi; int nsend; if (sender->dmode != 0 && pkt->size < LBR_THRS) { nsend = 2; } else { nsend = 1; } wi = rtpp_wi_malloc_pkt_na(sock, pkt, sendto, nsend, sock_rcnt); if (wi == NULL) { rtp_packet_free(pkt); return (-1); } /* * rtpp_wi_malloc_pkt() consumes pkt and returns wi, so no need to * call rtp_packet_free() here. */ #if RTPP_DEBUG_netio >= 2 wi->debug = 1; if (plog == NULL) { plog = sender->glog; } CALL_SMETHOD(plog->rcnt, incref); wi->log = plog; RTPP_LOG(plog, RTPP_LOG_DBUG, "send_pkt(%d, %p, %d, %d, %p, %d)", wi->sock, wi->msg, wi->msg_len, wi->flags, wi->sendto, wi->tolen); #endif rtpp_queue_put_item(wi, sender->out_q); return (0); }
static void rtpp_stream_fill_addr(struct rtpp_stream *self, struct rtp_packet *packet) { const char *actor, *ptype; struct rtpp_stream_priv *pvt; char saddr[MAX_AP_STRBUF]; pvt = PUB2PVT(self); pvt->untrusted_addr = 1; memcpy(self->addr, &packet->raddr, packet->rlen); if (pvt->prev_addr == NULL || memcmp(pvt->prev_addr, &packet->raddr, packet->rlen) != 0) { self->latch_info.latched = 1; } actor = rtpp_stream_get_actor(self); ptype = rtpp_stream_get_proto(self); addrport2char_r(sstosa(&packet->raddr), saddr, sizeof(saddr)); RTPP_LOG(pvt->pub.log, RTPP_LOG_INFO, "%s's address filled in: %s (%s)", actor, saddr, ptype); return; }
static void rtpp_stream_dtor(struct rtpp_stream_priv *pvt) { struct rtpp_stream *pub; pub = &(pvt->pub); rtpp_stream_fin(pub); if (pub->analyzer != NULL) { struct rtpa_stats rst; char ssrc_buf[11]; const char *actor, *ssrc; actor = rtpp_stream_get_actor(pub); CALL_METHOD(pub->analyzer, get_stats, &rst); if (rst.ssrc_changes != 0) { snprintf(ssrc_buf, sizeof(ssrc_buf), SSRC_FMT, rst.last_ssrc); ssrc = ssrc_buf; } else { ssrc = "NONE"; } RTPP_LOG(pvt->pub.log, RTPP_LOG_INFO, "RTP stream from %s: " "SSRC=%s, ssrc_changes=%u, psent=%u, precvd=%u, plost=%d, pdups=%u", actor, ssrc, rst.ssrc_changes, rst.psent, rst.precvd, rst.plost, rst.pdups); if (rst.psent > 0) { CALL_METHOD(pvt->rtpp_stats, updatebyname, "rtpa_nsent", rst.psent); } if (rst.precvd > 0) { CALL_METHOD(pvt->rtpp_stats, updatebyname, "rtpa_nrcvd", rst.precvd); } if (rst.pdups > 0) { CALL_METHOD(pvt->rtpp_stats, updatebyname, "rtpa_ndups", rst.pdups); } if (rst.pecount > 0) { CALL_METHOD(pvt->rtpp_stats, updatebyname, "rtpa_perrs", rst.pecount); } CALL_METHOD(pvt->pub.analyzer->rcnt, decref); } if (pub->fd != NULL) CALL_METHOD(pub->fd->rcnt, decref); if (pub->addr != NULL) free(pub->addr); if (pvt->prev_addr != NULL) free(pvt->prev_addr); if (pub->codecs != NULL) free(pub->codecs); if (pvt->rtps != RTPP_UID_NONE) CALL_METHOD(pvt->servers_wrt, unreg, pvt->rtps); if (pub->resizer != NULL) rtp_resizer_free(pvt->rtpp_stats, pub->resizer); if (pub->rrc != NULL) CALL_METHOD(pub->rrc->rcnt, decref); if (pub->pcount != NULL) CALL_METHOD(pub->pcount->rcnt, decref); CALL_METHOD(pub->ttl->rcnt, decref); CALL_METHOD(pub->pcnt_strm->rcnt, decref); CALL_METHOD(pvt->pub.log->rcnt, decref); pthread_mutex_destroy(&pvt->lock); free(pvt); }
int rtpp_command_ul_handle(struct cfg *cf, struct rtpp_command *cmd, struct ul_opts *ulop, int sidx) { int pidx, lport, sessions_active; struct rtpp_socket *fds[2]; const char *actor; struct rtpp_session *spa, *spb; pidx = 1; lport = 0; spa = spb = NULL; fds[0] = fds[1] = NULL; if (sidx != -1) { RTPP_DBG_ASSERT(cmd->cca.op == UPDATE || cmd->cca.op == LOOKUP); spa = cmd->sp; if (spa->rtp->stream[sidx]->fd == NULL || ulop->new_port != 0) { if (ulop->local_addr != NULL) { spa->rtp->stream[sidx]->laddr = ulop->local_addr; } if (rtpp_create_listener(cf, spa->rtp->stream[sidx]->laddr, &lport, fds) == -1) { RTPP_LOG(spa->log, RTPP_LOG_ERR, "can't create listener"); reply_error(cmd, ECODE_LSTFAIL_1); goto err_undo_0; } if (spa->rtp->stream[sidx]->fd != NULL && ulop->new_port != 0) { RTPP_LOG(spa->log, RTPP_LOG_INFO, "new port requested, releasing %d/%d, replacing with %d/%d", spa->rtp->stream[sidx]->port, spa->rtcp->stream[sidx]->port, lport, lport + 1); CALL_METHOD(cf->stable->sessinfo, update, spa, sidx, fds); } else { RTPP_DBG_ASSERT(spa->rtp->stream[sidx]->fd == NULL); spa->rtp->stream[sidx]->fd = fds[0]; RTPP_DBG_ASSERT(spa->rtcp->stream[sidx]->fd == NULL); spa->rtcp->stream[sidx]->fd = fds[1]; CALL_METHOD(cf->stable->sessinfo, append, spa, sidx); } spa->rtp->stream[sidx]->port = lport; spa->rtcp->stream[sidx]->port = lport + 1; if (spa->complete == 0) { cmd->csp->nsess_complete.cnt++; } spa->complete = 1; } if (ulop->weak) spa->rtp->stream[sidx]->weak = 1; else if (cmd->cca.op == UPDATE) spa->strong = 1; lport = spa->rtp->stream[sidx]->port; ulop->lia[0] = spa->rtp->stream[sidx]->laddr; pidx = (sidx == 0) ? 1 : 0; if (cmd->cca.op == UPDATE) { CALL_METHOD(spa->rtp->stream[0]->ttl, reset_with, cf->stable->max_setup_ttl); CALL_METHOD(spa->rtp->stream[1]->ttl, reset_with, cf->stable->max_setup_ttl); RTPP_LOG(spa->log, RTPP_LOG_INFO, "adding %s flag to existing session, new=%d/%d/%d", ulop->weak ? ( sidx ? "weak[1]" : "weak[0]" ) : "strong", spa->strong, spa->rtp->stream[0]->weak, spa->rtp->stream[1]->weak); } else { CALL_METHOD(spa->rtp->stream[0]->ttl, reset_with, cf->stable->max_ttl); CALL_METHOD(spa->rtp->stream[1]->ttl, reset_with, cf->stable->max_ttl); } RTPP_LOG(spa->log, RTPP_LOG_INFO, "lookup on ports %d/%d, session timer restarted", spa->rtp->stream[0]->port, spa->rtp->stream[1]->port); } else { struct rtpp_hash_table_entry *hte; RTPP_DBG_ASSERT(cmd->cca.op == UPDATE); RTPP_LOG(cf->stable->glog, RTPP_LOG_INFO, "new session %s, tag %s requested, type %s", cmd->cca.call_id, cmd->cca.from_tag, ulop->weak ? "weak" : "strong"); if (cf->stable->slowshutdown != 0) { RTPP_LOG(cf->stable->glog, RTPP_LOG_INFO, "proxy is in the deorbiting-burn mode, new session rejected"); reply_error(cmd, ECODE_SLOWSHTDN); goto err_undo_0; } if (ulop->local_addr != NULL) { ulop->lia[0] = ulop->lia[1] = ulop->local_addr; } if (rtpp_create_listener(cf, ulop->lia[0], &lport, fds) == -1) { RTPP_LOG(cf->stable->glog, RTPP_LOG_ERR, "can't create listener"); reply_error(cmd, ECODE_LSTFAIL_2); goto err_undo_0; } /* * Session creation. If creation is requested with weak flag, * set weak[0]. */ spa = rtpp_session_ctor(cf->stable, &cmd->cca, cmd->dtime, ulop->lia, ulop->weak, lport, fds); if (spa == NULL) { handle_nomem(cf, cmd, ECODE_NOMEM_4, ulop, fds, NULL); return (-1); } hte = CALL_METHOD(cf->stable->sessions_ht, append_refcnt, spa->call_id, spa->rcnt); if (hte == NULL) { handle_nomem(cf, cmd, ECODE_NOMEM_5, ulop, NULL, spa); return (-1); } if (CALL_METHOD(cf->stable->sessions_wrt, reg, spa->rcnt, spa->seuid) != 0) { CALL_METHOD(cf->stable->sessions_ht, remove, spa->call_id, hte); handle_nomem(cf, cmd, ECODE_NOMEM_8, ulop, NULL, spa); return (-1); } cmd->csp->nsess_created.cnt++; /* * Each session can consume up to 5 open file descriptors (2 RTP, * 2 RTCP and 1 logging) so that warn user when he is likely to * exceed 80% mark on hard limit. */ sessions_active = CALL_METHOD(cf->stable->sessions_wrt, get_length); if (sessions_active > (rtpp_rlim_max(cf) * 80 / (100 * 5)) && cf->nofile_limit_warned == 0) { cf->nofile_limit_warned = 1; RTPP_LOG(cf->stable->glog, RTPP_LOG_WARN, "passed 80%% " "threshold on the open file descriptors limit (%d), " "consider increasing the limit using -L command line " "option", (int)rtpp_rlim_max(cf)); } RTPP_LOG(spa->log, RTPP_LOG_INFO, "new session on a port %d created, " "tag %s", lport, cmd->cca.from_tag); if (cf->stable->record_all != 0) { handle_copy(cf, spa, 0, NULL, 0); handle_copy(cf, spa, 1, NULL, 0); } /* Save ref, it will be decref'd by the command disposal code */ RTPP_DBG_ASSERT(cmd->sp == NULL); cmd->sp = spa; } if (cmd->cca.op == UPDATE) { if (!CALL_METHOD(cf->stable->rtpp_tnset_cf, isenabled) && ulop->notify_socket != NULL) RTPP_LOG(spa->log, RTPP_LOG_ERR, "must permit notification socket with -n"); if (spa->timeout_data.notify_tag != NULL) { free(spa->timeout_data.notify_tag); spa->timeout_data.notify_tag = NULL; } if (ulop->notify_socket != NULL) { struct rtpp_tnotify_target *rttp; rttp = CALL_METHOD(cf->stable->rtpp_tnset_cf, lookup, ulop->notify_socket, (cmd->rlen > 0) ? sstosa(&cmd->raddr) : NULL, (cmd->rlen > 0) ? cmd->laddr : NULL); if (rttp == NULL) { RTPP_LOG(spa->log, RTPP_LOG_ERR, "invalid socket name %s", ulop->notify_socket); ulop->notify_socket = NULL; } else { RTPP_LOG(spa->log, RTPP_LOG_INFO, "setting timeout handler"); spa->timeout_data.notify_target = rttp; spa->timeout_data.notify_tag = strdup(ulop->notify_tag); } } else if (spa->timeout_data.notify_target != NULL) { spa->timeout_data.notify_target = NULL; RTPP_LOG(spa->log, RTPP_LOG_INFO, "disabling timeout handler"); } } if (ulop->ia[0] != NULL && ulop->ia[1] != NULL) { CALL_METHOD(spa->rtp->stream[pidx], prefill_addr, &(ulop->ia[0]), cmd->dtime); CALL_METHOD(spa->rtcp->stream[pidx], prefill_addr, &(ulop->ia[1]), cmd->dtime); } spa->rtp->stream[pidx]->asymmetric = spa->rtcp->stream[pidx]->asymmetric = ulop->asymmetric; spa->rtp->stream[pidx]->latch_info.latched = spa->rtcp->stream[pidx]->latch_info.latched = ulop->asymmetric; if (spa->rtp->stream[pidx]->codecs != NULL) { free(spa->rtp->stream[pidx]->codecs); spa->rtp->stream[pidx]->codecs = NULL; } if (ulop->codecs != NULL) { spa->rtp->stream[pidx]->codecs = ulop->codecs; ulop->codecs = NULL; } spa->rtp->stream[NOT(pidx)]->ptime = ulop->requested_ptime; actor = CALL_METHOD(spa->rtp->stream[pidx], get_actor); if (ulop->requested_ptime > 0) { RTPP_LOG(spa->log, RTPP_LOG_INFO, "RTP packets from %s " "will be resized to %d milliseconds", actor, ulop->requested_ptime); } else if (spa->rtp->stream[pidx]->resizer != NULL) { RTPP_LOG(spa->log, RTPP_LOG_INFO, "Resizing of RTP " "packets from %s has been disabled", actor); } if (ulop->requested_ptime > 0) { if (spa->rtp->stream[pidx]->resizer != NULL) { rtp_resizer_set_ptime(spa->rtp->stream[pidx]->resizer, ulop->requested_ptime); } else { spa->rtp->stream[pidx]->resizer = rtp_resizer_new(ulop->requested_ptime); } } else if (spa->rtp->stream[pidx]->resizer != NULL) { rtp_resizer_free(cf->stable->rtpp_stats, spa->rtp->stream[pidx]->resizer); spa->rtp->stream[pidx]->resizer = NULL; } RTPP_DBG_ASSERT(lport != 0); ulop->reply.port = lport; ulop->reply.ia = ulop->lia[0]; if (cf->stable->advaddr[0] != NULL) { if (cf->stable->bmode != 0 && cf->stable->advaddr[1] != NULL && ulop->lia[0] == cf->stable->bindaddr[1]) { ulop->reply.ia_ov = cf->stable->advaddr[1]; } else { ulop->reply.ia_ov = cf->stable->advaddr[0]; } } ul_reply_port(cmd, &ulop->reply); rtpp_command_ul_opts_free(ulop); return (0); err_undo_0: rtpp_command_ul_opts_free(ulop); return (-1); }
struct ul_opts * rtpp_command_ul_opts_parse(struct cfg *cf, struct rtpp_command *cmd) { int len, tpf, n, i; char c; char *cp, *t; const char *errmsg; struct sockaddr_storage tia; struct ul_opts *ulop; ulop = rtpp_zmalloc(sizeof(struct ul_opts)); if (ulop == NULL) { reply_error(cmd, ECODE_NOMEM_1); goto err_undo_0; } ul_opts_init(cf, ulop); if (cmd->cca.op == UPDATE && cmd->argc > 6) { if (cmd->argc == 8) { ulop->notify_socket = cmd->argv[6]; ulop->notify_tag = cmd->argv[7]; } else { ulop->notify_socket = cmd->argv[5]; ulop->notify_tag = cmd->argv[6]; cmd->cca.to_tag = NULL; } len = url_unquote((uint8_t *)ulop->notify_tag, strlen(ulop->notify_tag)); if (len == -1) { RTPP_LOG(cf->stable->glog, RTPP_LOG_ERR, "command syntax error - invalid URL encoding"); reply_error(cmd, ECODE_PARSE_10); goto err_undo_1; } ulop->notify_tag[len] = '\0'; } ulop->addr = cmd->argv[2]; ulop->port = cmd->argv[3]; /* Process additional command modifiers */ for (cp = cmd->argv[0] + 1; *cp != '\0'; cp++) { switch (*cp) { case 'a': case 'A': ulop->asymmetric = 1; break; case 'e': case 'E': if (ulop->lidx < 0 || cf->stable->bindaddr[1] == NULL) { RTPP_LOG(cf->stable->glog, RTPP_LOG_ERR, "command syntax error"); reply_error(cmd, ECODE_PARSE_11); goto err_undo_1; } ulop->lia[ulop->lidx] = cf->stable->bindaddr[1]; ulop->lidx--; break; case 'i': case 'I': if (ulop->lidx < 0 || cf->stable->bindaddr[1] == NULL) { RTPP_LOG(cf->stable->glog, RTPP_LOG_ERR, "command syntax error"); reply_error(cmd, ECODE_PARSE_12); goto err_undo_1; } ulop->lia[ulop->lidx] = cf->stable->bindaddr[0]; ulop->lidx--; break; case '6': ulop->pf = AF_INET6; break; case 's': case 'S': ulop->asymmetric = 0; break; case 'w': case 'W': ulop->weak = 1; break; case 'z': case 'Z': ulop->requested_ptime = strtol(cp + 1, &cp, 10); if (ulop->requested_ptime <= 0) { RTPP_LOG(cf->stable->glog, RTPP_LOG_ERR, "command syntax error"); reply_error(cmd, ECODE_PARSE_13); goto err_undo_1; } cp--; break; case 'c': case 'C': cp += 1; for (t = cp; *cp != '\0'; cp++) { if (!isdigit(*cp) && *cp != ',') break; } if (t == cp) { RTPP_LOG(cf->stable->glog, RTPP_LOG_ERR, "command syntax error"); reply_error(cmd, ECODE_PARSE_14); goto err_undo_1; } ulop->codecs = malloc(cp - t + 1); if (ulop->codecs == NULL) { reply_error(cmd, ECODE_NOMEM_2); goto err_undo_1; } memcpy(ulop->codecs, t, cp - t); ulop->codecs[cp - t] = '\0'; cp--; break; case 'l': case 'L': len = extractaddr(cp + 1, &t, &cp, &tpf); if (len == -1) { RTPP_LOG(cf->stable->glog, RTPP_LOG_ERR, "command syntax error"); reply_error(cmd, ECODE_PARSE_15); goto err_undo_1; } c = t[len]; t[len] = '\0'; ulop->local_addr = host2bindaddr(cf, t, tpf, &errmsg); if (ulop->local_addr == NULL) { RTPP_LOG(cf->stable->glog, RTPP_LOG_ERR, "invalid local address: %s: %s", t, errmsg); reply_error(cmd, ECODE_INVLARG_1); goto err_undo_1; } t[len] = c; cp--; break; case 'r': case 'R': len = extractaddr(cp + 1, &t, &cp, &tpf); if (len == -1) { RTPP_LOG(cf->stable->glog, RTPP_LOG_ERR, "command syntax error"); reply_error(cmd, ECODE_PARSE_16); goto err_undo_1; } c = t[len]; t[len] = '\0'; ulop->local_addr = alloca(sizeof(struct sockaddr_storage)); n = resolve(ulop->local_addr, tpf, t, SERVICE, AI_PASSIVE); if (n != 0) { RTPP_LOG(cf->stable->glog, RTPP_LOG_ERR, "invalid remote address: %s: %s", t, gai_strerror(n)); reply_error(cmd, ECODE_INVLARG_2); goto err_undo_1; } if (local4remote(ulop->local_addr, satoss(ulop->local_addr)) == -1) { RTPP_LOG(cf->stable->glog, RTPP_LOG_ERR, "can't find local address for remote address: %s", t); reply_error(cmd, ECODE_INVLARG_3); goto err_undo_1; } ulop->local_addr = addr2bindaddr(cf, ulop->local_addr, &errmsg); if (ulop->local_addr == NULL) { RTPP_LOG(cf->stable->glog, RTPP_LOG_ERR, "invalid local address: %s", errmsg); reply_error(cmd, ECODE_INVLARG_4); goto err_undo_1; } t[len] = c; cp--; break; case 'n': case 'N': ulop->new_port = 1; break; default: RTPP_LOG(cf->stable->glog, RTPP_LOG_ERR, "unknown command modifier `%c'", *cp); break; } } if (ulop->addr != NULL && ulop->port != NULL && strlen(ulop->addr) >= 7) { n = resolve(sstosa(&tia), ulop->pf, ulop->addr, ulop->port, AI_NUMERICHOST); if (n == 0) { if (!ishostnull(sstosa(&tia))) { for (i = 0; i < 2; i++) { ulop->ia[i] = malloc(SS_LEN(&tia)); if (ulop->ia[i] == NULL) { reply_error(cmd, ECODE_NOMEM_3); goto err_undo_1; } memcpy(ulop->ia[i], &tia, SS_LEN(&tia)); } /* Set port for RTCP, will work both for IPv4 and IPv6 */ n = ntohs(satosin(ulop->ia[1])->sin_port); satosin(ulop->ia[1])->sin_port = htons(n + 1); } } else { RTPP_LOG(cf->stable->glog, RTPP_LOG_ERR, "getaddrinfo(pf=%d, addr=%s, port=%s): %s", ulop->pf, ulop->addr, ulop->port, gai_strerror(n)); } } return (ulop); err_undo_1: rtpp_command_ul_opts_free(ulop); err_undo_0: return (NULL); }
int rtpp_cfile_process(struct rtpp_cfg_stable *csp) { struct ucl_parser *parser; ucl_object_t *conf_root; ucl_object_iter_t it_conf; const ucl_object_t *obj_file; const char *cf_key; int fd, ecode; ecode = 0; fd = open(csp->cfile, O_RDONLY); if (fd < 0) { RTPP_ELOG(csp->glog, RTPP_LOG_ERR, "open failed: %s", csp->cfile); ecode = -1; goto e0; } parser = ucl_parser_new(UCL_PARSER_NO_FILEVARS); if (parser == NULL) { RTPP_LOG(csp->glog, RTPP_LOG_ERR, "ucl_parser_new() failed"); ecode = -1; goto e1; } ucl_parser_add_fd(parser, fd); conf_root = ucl_parser_get_object(parser); if (conf_root == NULL) { RTPP_LOG(csp->glog, RTPP_LOG_ERR, "ucl_parser_get_object() failed"); ecode = -1; goto e2; } if (ucl_parser_get_error(parser)) { RTPP_LOG(csp->glog, RTPP_LOG_ERR, "Parse Error occured: %s", ucl_parser_get_error(parser)); ecode = -1; goto e3; } it_conf = ucl_object_iterate_new(conf_root); if (it_conf == NULL) { RTPP_LOG(csp->glog, RTPP_LOG_ERR, "ucl_object_iterate_new() failed"); ecode = -1; goto e3; } while ((obj_file = ucl_object_iterate_safe(it_conf, true)) != NULL) { cf_key = ucl_object_key(obj_file); RTPP_LOG(csp->glog, RTPP_LOG_DBUG, "Entry: %s", cf_key); if (strcasecmp(cf_key, "modules") == 0) { if (parse_modules(csp, obj_file) < 0) { RTPP_LOG(csp->glog, RTPP_LOG_ERR, "parse_modules() failed"); ecode = -1; goto e4; } } } if (ucl_object_iter_chk_excpn(it_conf)) { ecode = -1; } e4: ucl_object_iterate_free(it_conf); e3: ucl_object_unref(conf_root); e2: ucl_parser_free(parser); e1: close(fd); e0: return (ecode); }
static int parse_modules(struct rtpp_cfg_stable *csp, const ucl_object_t *wop) { ucl_object_iter_t it_conf; const ucl_object_t *obj_file; const char *cf_key; const ucl_object_t *obj_key; int ecode, success; void *confp; const conf_helper_map *fent, *map; struct rtpp_module_conf *mcp; char mpath[PATH_MAX + 1]; const char *cp, *mp; struct rtpp_module_if *mif; it_conf = ucl_object_iterate_new(wop); if (it_conf == NULL) return (-1); ecode = 0; while ((obj_file = ucl_object_iterate_safe(it_conf, true)) != NULL) { cf_key = ucl_object_key(obj_file); RTPP_LOG(csp->glog, RTPP_LOG_DBUG, "\tmodule: %s", cf_key); obj_key = ucl_object_find_key(obj_file, "load"); if (obj_key == NULL) { cp = rtpp_module_dsop_canonic(cf_key, mpath, sizeof(mpath)); if (cp == NULL) { RTPP_LOG(csp->glog, RTPP_LOG_ERR, "Error: Unable to find load parameter in module: %s", cf_key); ecode = -1; goto e0; } } else { if (obj_key->type != UCL_STRING) { RTPP_LOG(csp->glog, RTPP_LOG_ERR, "Error: \"load\" parameter in %s has a wrong type, string is expected", cf_key); ecode = -1; goto e0; } mp = ucl_object_tostring(obj_key); cp = realpath(mp, mpath); if (cp == NULL) { RTPP_ELOG(csp->glog, RTPP_LOG_ERR, "realpath() failed: %s", mp); ecode = -1; goto e0; } } mif = rtpp_module_if_ctor(cp); if (mif == NULL) { RTPP_LOG(csp->glog, RTPP_LOG_ERR, "dymanic module constructor has failed: %s", cp); ecode = -1; goto e0; } if (CALL_METHOD(mif, load, csp, csp->glog) != 0) { RTPP_LOG(csp->glog, RTPP_LOG_ERR, "%p->load() method has failed: %s", mif, cp); goto e1; } if (CALL_METHOD(mif, get_mconf, &mcp) < 0) { RTPP_LOG(csp->glog, RTPP_LOG_ERR, "%p->get_mconf() method has failed: %s", mif, cp); goto e1; } fent = NULL; if (mcp != NULL) { map = mcp->conf_map; confp = mcp->conf_data; } else { map = default_module_map; confp = NULL; } success = conf_helper_mapper(csp->glog, obj_file, map, confp, &fent); if (!success) { RTPP_LOG(csp->glog, RTPP_LOG_ERR, "Config parsing issue in section %s", cf_key); if (fent != NULL && fent->conf_key != NULL) { RTPP_LOG(csp->glog, RTPP_LOG_ERR, "\tparameter %s", fent->conf_key); } goto e1; } if (CALL_METHOD(mif, config) < 0) { RTPP_LOG(csp->glog, RTPP_LOG_ERR, "%p->config() method has failed: %s", mif, cp); goto e1; } rtpp_list_append(csp->modules_cf, mif); continue; e1: ecode = -1; CALL_SMETHOD(mif->rcnt, decref); goto e0; } e0: if (ucl_object_iter_chk_excpn(it_conf)) { RTPP_LOG(csp->glog, RTPP_LOG_ERR, "UCL has failed with an internal error"); ecode = -1; } ucl_object_iterate_free(it_conf); return (ecode); }
struct rtpp_module_if * rtpp_module_if_ctor(struct rtpp_cfg_stable *cfsp, struct rtpp_log *log, const char *mpath) { struct rtpp_refcnt *rcnt; struct rtpp_module_if_priv *pvt; const char *derr; pvt = rtpp_rzmalloc(sizeof(struct rtpp_module_if_priv), &rcnt); if (pvt == NULL) { goto e0; } pvt->pub.rcnt = rcnt; pvt->dmp = dlopen(mpath, RTLD_NOW); if (pvt->dmp == NULL) { derr = dlerror(); if (strstr(derr, mpath) == NULL) { RTPP_LOG(log, RTPP_LOG_ERR, "can't dlopen(%s): %s", mpath, derr); } else { RTPP_LOG(log, RTPP_LOG_ERR, "can't dlopen() module: %s", derr); } goto e1; } pvt->mip = dlsym(pvt->dmp, "rtpp_module"); if (pvt->mip == NULL) { derr = dlerror(); if (strstr(derr, mpath) == NULL) { RTPP_LOG(log, RTPP_LOG_ERR, "can't find 'rtpp_module' symbol in the %s" ": %s", mpath, derr); } else { RTPP_LOG(log, RTPP_LOG_ERR, "can't find 'rtpp_module' symbol: %s", derr); } goto e2; } if (!MI_VER_CHCK(pvt->mip)) { RTPP_LOG(log, RTPP_LOG_ERR, "incompatible API version in the %s, " "consider recompiling the module", mpath); goto e2; } #if RTPP_CHECK_LEAKS pvt->mip->_malloc = &rtpp_memdeb_malloc; pvt->mip->_zmalloc = &rtpp_zmalloc_memdeb; pvt->mip->_free = &rtpp_memdeb_free; pvt->mip->_realloc = &rtpp_memdeb_realloc; pvt->mip->_strdup = &rtpp_memdeb_strdup; pvt->mip->_asprintf = &rtpp_memdeb_asprintf; pvt->mip->_vasprintf = &rtpp_memdeb_vasprintf; pvt->memdeb_p = rtpp_memdeb_init(); rtpp_memdeb_setlog(pvt->memdeb_p, log); #else pvt->mip->_malloc = (rtpp_module_malloc_t)&malloc; pvt->mip->_zmalloc = (rtpp_module_zmalloc_t)&rtpp_zmalloc; pvt->mip->_free = (rtpp_module_free_t)&free; pvt->mip->_realloc = (rtpp_module_realloc_t)&realloc; pvt->mip->_strdup = (rtpp_module_strdup_t)&strdup; pvt->mip->_asprintf = rtpp_module_asprintf; pvt->mip->_vasprintf = rtpp_module_vasprintf; #endif if (pvt->memdeb_p == NULL) { goto e2; } /* We make a copy, so that the module cannot screw us up */ pvt->mip->memdeb_p = pvt->memdeb_p; pvt->sigterm = rtpp_wi_malloc_sgnl(SIGTERM, NULL, 0); if (pvt->sigterm == NULL) { goto e3; } pvt->req_q = rtpp_queue_init(1, "rtpp_module_if(%s)", pvt->mip->name); if (pvt->req_q == NULL) { goto e4; } if (pvt->mip->ctor != NULL) { pvt->mpvt = pvt->mip->ctor(cfsp); if (pvt->mpvt == NULL) { RTPP_LOG(log, RTPP_LOG_ERR, "module '%s' failed to initialize", pvt->mip->name); goto e5; } } if (pvt->mip->on_session_end.argsize != rtpp_acct_OSIZE()) { RTPP_LOG(log, RTPP_LOG_ERR, "incompatible API version in the %s, " "consider recompiling the module", mpath); goto e6; } if (pthread_create(&pvt->thread_id, NULL, (void *(*)(void *))&rtpp_mif_run, pvt) != 0) { goto e6; } CALL_METHOD(log->rcnt, incref); pvt->log = log; pvt->pub.do_acct = &rtpp_mif_do_acct; CALL_METHOD(pvt->pub.rcnt, attach, (rtpp_refcnt_dtor_t)&rtpp_mif_dtor, pvt); return ((&pvt->pub)); e6: if (pvt->mip->dtor != NULL) { pvt->mip->dtor(pvt->mpvt); } e5: rtpp_queue_destroy(pvt->req_q); #if RTPP_CHECK_LEAKS if (rtpp_memdeb_dumpstats(pvt->memdeb_p, 1) != 0) { RTPP_LOG(log, RTPP_LOG_ERR, "module '%s' leaked memory in the failed " "constructor", pvt->mip->name); } #endif e4: rtpp_wi_free(pvt->sigterm); e3: #if RTPP_CHECK_LEAKS rtpp_memdeb_dtor(pvt->memdeb_p); #endif e2: dlclose(pvt->dmp); e1: CALL_METHOD(rcnt, decref); free(pvt); e0: return (NULL); }
static void rtpp_anetio_sthread(struct sthread_args *args) { int n, nsend, i, send_errno, nretry; struct rtpp_wi *wi, *wis[100]; #if RTPP_DEBUG_timers double tp[3], runtime, sleeptime; long run_n; runtime = sleeptime = 0.0; run_n = 0; tp[0] = getdtime(); #endif for (;;) { nsend = rtpp_queue_get_items(args->out_q, wis, 100, 0); #if RTPP_DEBUG_timers tp[1] = getdtime(); #endif for (i = 0; i < nsend; i++) { wi = wis[i]; if (wi->wi_type == RTPP_WI_TYPE_SGNL) { rtpp_wi_free(wi); goto out; } nretry = 0; do { n = sendto(wi->sock, wi->msg, wi->msg_len, wi->flags, wi->sendto, wi->tolen); send_errno = (n < 0) ? errno : 0; #if RTPP_DEBUG_netio >= 1 if (wi->debug != 0) { char daddr[MAX_AP_STRBUF]; addrport2char_r(wi->sendto, daddr, sizeof(daddr), ':'); if (n < 0) { RTPP_ELOG(wi->log, RTPP_LOG_DBUG, "sendto(%d, %p, %lld, %d, %p (%s), %d) = %d", wi->sock, wi->msg, (long long)wi->msg_len, wi->flags, wi->sendto, daddr, wi->tolen, n); } else if (n < wi->msg_len) { RTPP_LOG(wi->log, RTPP_LOG_DBUG, "sendto(%d, %p, %lld, %d, %p (%s), %d) = %d: short write", wi->sock, wi->msg, (long long)wi->msg_len, wi->flags, wi->sendto, daddr, wi->tolen, n); #if RTPP_DEBUG_netio >= 2 } else { RTPP_LOG(wi->log, RTPP_LOG_DBUG, "sendto(%d, %p, %d, %d, %p (%s), %d) = %d", wi->sock, wi->msg, wi->msg_len, wi->flags, wi->sendto, daddr, wi->tolen, n); #endif } } #endif if (n >= 0) { wi->nsend--; } else { /* "EPERM" is Linux thing, yield and retry */ if ((send_errno == EPERM || send_errno == ENOBUFS) && nretry < RTPP_ANETIO_MAX_RETRY) { sched_yield(); nretry++; } else { break; } } } while (wi->nsend > 0); rtpp_wi_free(wi); } #if RTPP_DEBUG_timers sleeptime += tp[1] - tp[0]; tp[0] = getdtime(); runtime += tp[0] - tp[1]; if ((run_n % 10000) == 0) { RTPP_LOG(args->glog, RTPP_LOG_DBUG, "rtpp_anetio_sthread(%p): run %ld aload = %f filtered = %f", \ args, run_n, runtime / (runtime + sleeptime), args->average_load.lastval); } if (runtime + sleeptime > 1.0) { recfilter_apply(&args->average_load, runtime / (runtime + sleeptime)); runtime = sleeptime = 0.0; } run_n += 1; #endif } out: return; }