Sessions rework
This commit is contained in:
parent
3e85da2afd
commit
074e757028
27 changed files with 1222 additions and 1183 deletions
|
@ -592,6 +592,17 @@ makeOutput(RTSP rtsp)#LTS
|
|||
makeOutput(WAV wav)#LTS
|
||||
makeOutput(SDP sdp http)
|
||||
|
||||
add_executable(MistSession
|
||||
${BINARY_DIR}/mist/.headers
|
||||
src/session.cpp
|
||||
)
|
||||
install(
|
||||
TARGETS MistSession
|
||||
DESTINATION bin
|
||||
)
|
||||
target_link_libraries(MistSession mist)
|
||||
|
||||
|
||||
add_executable(MistProcFFMPEG
|
||||
${BINARY_DIR}/mist/.headers
|
||||
src/process/process_ffmpeg.cpp
|
||||
|
|
486
lib/comms.cpp
486
lib/comms.cpp
|
@ -7,6 +7,7 @@
|
|||
#include "timing.h"
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include "config.h"
|
||||
|
||||
namespace Comms{
|
||||
Comms::Comms(){
|
||||
|
@ -141,200 +142,45 @@ namespace Comms{
|
|||
}
|
||||
}
|
||||
|
||||
Statistics::Statistics() : Comms(){sem.open(SEM_STATISTICS, O_CREAT | O_RDWR, ACCESSPERMS, 1);}
|
||||
Sessions::Sessions() : Connections(){sem.open(SEM_STATISTICS, O_CREAT | O_RDWR, ACCESSPERMS, 1);}
|
||||
|
||||
void Statistics::unload(){
|
||||
if (index != INVALID_RECORD_INDEX){
|
||||
setStatus(COMM_STATUS_DISCONNECT | getStatus());
|
||||
}
|
||||
index = INVALID_RECORD_INDEX;
|
||||
}
|
||||
|
||||
void Statistics::reload(bool _master, bool reIssue){
|
||||
void Sessions::reload(bool _master, bool reIssue){
|
||||
Comms::reload(COMMS_STATISTICS, COMMS_STATISTICS_INITSIZE, _master, reIssue);
|
||||
}
|
||||
|
||||
void Statistics::addFields(){
|
||||
Comms::addFields();
|
||||
dataAccX.addField("sync", RAX_UINT);
|
||||
dataAccX.addField("now", RAX_64UINT);
|
||||
dataAccX.addField("time", RAX_64UINT);
|
||||
dataAccX.addField("lastsecond", RAX_64UINT);
|
||||
dataAccX.addField("down", RAX_64UINT);
|
||||
dataAccX.addField("up", RAX_64UINT);
|
||||
dataAccX.addField("host", RAX_RAW, 16);
|
||||
dataAccX.addField("stream", RAX_STRING, 100);
|
||||
dataAccX.addField("connector", RAX_STRING, 20);
|
||||
dataAccX.addField("crc", RAX_32UINT);
|
||||
dataAccX.addField("pktcount", RAX_64UINT);
|
||||
dataAccX.addField("pktloss", RAX_64UINT);
|
||||
dataAccX.addField("pktretrans", RAX_64UINT);
|
||||
}
|
||||
|
||||
void Statistics::nullFields(){
|
||||
Comms::nullFields();
|
||||
setCRC(0);
|
||||
setConnector("");
|
||||
setStream("");
|
||||
setHost("");
|
||||
setUp(0);
|
||||
setDown(0);
|
||||
setLastSecond(0);
|
||||
setTime(0);
|
||||
setNow(0);
|
||||
setSync(0);
|
||||
setPacketCount(0);
|
||||
setPacketLostCount(0);
|
||||
setPacketRetransmitCount(0);
|
||||
}
|
||||
|
||||
void Statistics::fieldAccess(){
|
||||
Comms::fieldAccess();
|
||||
sync = dataAccX.getFieldAccX("sync");
|
||||
now = dataAccX.getFieldAccX("now");
|
||||
time = dataAccX.getFieldAccX("time");
|
||||
lastSecond = dataAccX.getFieldAccX("lastsecond");
|
||||
down = dataAccX.getFieldAccX("down");
|
||||
up = dataAccX.getFieldAccX("up");
|
||||
host = dataAccX.getFieldAccX("host");
|
||||
stream = dataAccX.getFieldAccX("stream");
|
||||
connector = dataAccX.getFieldAccX("connector");
|
||||
crc = dataAccX.getFieldAccX("crc");
|
||||
pktcount = dataAccX.getFieldAccX("pktcount");
|
||||
pktloss = dataAccX.getFieldAccX("pktloss");
|
||||
pktretrans = dataAccX.getFieldAccX("pktretrans");
|
||||
}
|
||||
|
||||
uint8_t Statistics::getSync() const{return sync.uint(index);}
|
||||
uint8_t Statistics::getSync(size_t idx) const{return (master ? sync.uint(idx) : 0);}
|
||||
void Statistics::setSync(uint8_t _sync){sync.set(_sync, index);}
|
||||
void Statistics::setSync(uint8_t _sync, size_t idx){
|
||||
std::string Sessions::getSessId() const{return sessId.string(index);}
|
||||
std::string Sessions::getSessId(size_t idx) const{return (master ? sessId.string(idx) : 0);}
|
||||
void Sessions::setSessId(std::string _sid){sessId.set(_sid, index);}
|
||||
void Sessions::setSessId(std::string _sid, size_t idx){
|
||||
if (!master){return;}
|
||||
sync.set(_sync, idx);
|
||||
sessId.set(_sid, idx);
|
||||
}
|
||||
|
||||
uint64_t Statistics::getNow() const{return now.uint(index);}
|
||||
uint64_t Statistics::getNow(size_t idx) const{return (master ? now.uint(idx) : 0);}
|
||||
void Statistics::setNow(uint64_t _now){now.set(_now, index);}
|
||||
void Statistics::setNow(uint64_t _now, size_t idx){
|
||||
if (!master){return;}
|
||||
now.set(_now, idx);
|
||||
bool Sessions::sessIdExists(std::string _sid){
|
||||
for (size_t i = 0; i < recordCount(); i++){
|
||||
if (getStatus(i) == COMM_STATUS_INVALID || (getStatus(i) & COMM_STATUS_DISCONNECT)){continue;}
|
||||
if (getSessId(i) == _sid){
|
||||
if (Util::Procs::isRunning(getPid(i))){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t Statistics::getTime() const{return time.uint(index);}
|
||||
uint64_t Statistics::getTime(size_t idx) const{return (master ? time.uint(idx) : 0);}
|
||||
void Statistics::setTime(uint64_t _time){time.set(_time, index);}
|
||||
void Statistics::setTime(uint64_t _time, size_t idx){
|
||||
if (!master){return;}
|
||||
time.set(_time, idx);
|
||||
void Sessions::addFields(){
|
||||
Connections::addFields();
|
||||
dataAccX.addField("sessid", RAX_STRING, 80);
|
||||
}
|
||||
|
||||
uint64_t Statistics::getLastSecond() const{return lastSecond.uint(index);}
|
||||
uint64_t Statistics::getLastSecond(size_t idx) const{
|
||||
return (master ? lastSecond.uint(idx) : 0);
|
||||
}
|
||||
void Statistics::setLastSecond(uint64_t _lastSecond){lastSecond.set(_lastSecond, index);}
|
||||
void Statistics::setLastSecond(uint64_t _lastSecond, size_t idx){
|
||||
if (!master){return;}
|
||||
lastSecond.set(_lastSecond, idx);
|
||||
void Sessions::nullFields(){
|
||||
Connections::nullFields();
|
||||
setSessId("");
|
||||
}
|
||||
|
||||
uint64_t Statistics::getDown() const{return down.uint(index);}
|
||||
uint64_t Statistics::getDown(size_t idx) const{return (master ? down.uint(idx) : 0);}
|
||||
void Statistics::setDown(uint64_t _down){down.set(_down, index);}
|
||||
void Statistics::setDown(uint64_t _down, size_t idx){
|
||||
if (!master){return;}
|
||||
down.set(_down, idx);
|
||||
}
|
||||
|
||||
uint64_t Statistics::getUp() const{return up.uint(index);}
|
||||
uint64_t Statistics::getUp(size_t idx) const{return (master ? up.uint(idx) : 0);}
|
||||
void Statistics::setUp(uint64_t _up){up.set(_up, index);}
|
||||
void Statistics::setUp(uint64_t _up, size_t idx){
|
||||
if (!master){return;}
|
||||
up.set(_up, idx);
|
||||
}
|
||||
|
||||
std::string Statistics::getHost() const{return std::string(host.ptr(index), 16);}
|
||||
std::string Statistics::getHost(size_t idx) const{
|
||||
if (!master){return std::string((size_t)16, (char)'\000');}
|
||||
return std::string(host.ptr(idx), 16);
|
||||
}
|
||||
void Statistics::setHost(std::string _host){host.set(_host, index);}
|
||||
void Statistics::setHost(std::string _host, size_t idx){
|
||||
if (!master){return;}
|
||||
host.set(_host, idx);
|
||||
}
|
||||
|
||||
std::string Statistics::getStream() const{return stream.string(index);}
|
||||
std::string Statistics::getStream(size_t idx) const{return (master ? stream.string(idx) : "");}
|
||||
void Statistics::setStream(std::string _stream){stream.set(_stream, index);}
|
||||
void Statistics::setStream(std::string _stream, size_t idx){
|
||||
if (!master){return;}
|
||||
stream.set(_stream, idx);
|
||||
}
|
||||
|
||||
std::string Statistics::getConnector() const{return connector.string(index);}
|
||||
std::string Statistics::getConnector(size_t idx) const{
|
||||
return (master ? connector.string(idx) : "");
|
||||
}
|
||||
void Statistics::setConnector(std::string _connector){connector.set(_connector, index);}
|
||||
void Statistics::setConnector(std::string _connector, size_t idx){
|
||||
if (!master){return;}
|
||||
connector.set(_connector, idx);
|
||||
}
|
||||
|
||||
uint32_t Statistics::getCRC() const{return crc.uint(index);}
|
||||
uint32_t Statistics::getCRC(size_t idx) const{return (master ? crc.uint(idx) : 0);}
|
||||
void Statistics::setCRC(uint32_t _crc){crc.set(_crc, index);}
|
||||
void Statistics::setCRC(uint32_t _crc, size_t idx){
|
||||
if (!master){return;}
|
||||
crc.set(_crc, idx);
|
||||
}
|
||||
|
||||
uint64_t Statistics::getPacketCount() const{return pktcount.uint(index);}
|
||||
uint64_t Statistics::getPacketCount(size_t idx) const{
|
||||
return (master ? pktcount.uint(idx) : 0);
|
||||
}
|
||||
void Statistics::setPacketCount(uint64_t _count){pktcount.set(_count, index);}
|
||||
void Statistics::setPacketCount(uint64_t _count, size_t idx){
|
||||
if (!master){return;}
|
||||
pktcount.set(_count, idx);
|
||||
}
|
||||
|
||||
uint64_t Statistics::getPacketLostCount() const{return pktloss.uint(index);}
|
||||
uint64_t Statistics::getPacketLostCount(size_t idx) const{
|
||||
return (master ? pktloss.uint(idx) : 0);
|
||||
}
|
||||
void Statistics::setPacketLostCount(uint64_t _lost){pktloss.set(_lost, index);}
|
||||
void Statistics::setPacketLostCount(uint64_t _lost, size_t idx){
|
||||
if (!master){return;}
|
||||
pktloss.set(_lost, idx);
|
||||
}
|
||||
|
||||
uint64_t Statistics::getPacketRetransmitCount() const{return pktretrans.uint(index);}
|
||||
uint64_t Statistics::getPacketRetransmitCount(size_t idx) const{
|
||||
return (master ? pktretrans.uint(idx) : 0);
|
||||
}
|
||||
void Statistics::setPacketRetransmitCount(uint64_t _retrans){pktretrans.set(_retrans, index);}
|
||||
void Statistics::setPacketRetransmitCount(uint64_t _retrans, size_t idx){
|
||||
if (!master){return;}
|
||||
pktretrans.set(_retrans, idx);
|
||||
}
|
||||
|
||||
std::string Statistics::getSessId() const{return getSessId(index);}
|
||||
|
||||
std::string Statistics::getSessId(size_t idx) const{
|
||||
char res[140];
|
||||
memset(res, 0, 140);
|
||||
std::string tmp = host.string(idx);
|
||||
memcpy(res, tmp.c_str(), (tmp.size() > 16 ? 16 : tmp.size()));
|
||||
tmp = stream.string(idx);
|
||||
memcpy(res + 16, tmp.c_str(), (tmp.size() > 100 ? 100 : tmp.size()));
|
||||
tmp = connector.string(idx);
|
||||
memcpy(res + 116, tmp.c_str(), (tmp.size() > 20 ? 20 : tmp.size()));
|
||||
Bit::htobl(res + 136, crc.uint(idx));
|
||||
return Secure::md5(res, 140);
|
||||
void Sessions::fieldAccess(){
|
||||
Connections::fieldAccess();
|
||||
sessId = dataAccX.getFieldAccX("sessid");
|
||||
}
|
||||
|
||||
Users::Users() : Comms(){}
|
||||
|
@ -404,4 +250,282 @@ namespace Comms{
|
|||
if (!master){return;}
|
||||
keyNum.set(_keyNum, idx);
|
||||
}
|
||||
|
||||
/// \brief Claims a spot on the connections page for the input/output which calls this function
|
||||
/// Starts the MistSession binary for each session, which handles the statistics
|
||||
/// and the USER_NEW and USER_END triggers
|
||||
/// \param streamName: Name of the stream the input is providing or an output is making available to viewers
|
||||
/// \param ip: IP address of the viewer which wants to access streamName. For inputs this value can be set to any value
|
||||
/// \param sid: Session ID given by the player or randomly generated
|
||||
/// \param protocol: Protocol currently in use for this connection
|
||||
/// \param sessionMode: Determines how a viewer session is defined:
|
||||
// If set to 0, all connections with the same viewer IP and stream name are bundled.
|
||||
// If set to 1, all connections with the same viewer IP and player ID are bundled.
|
||||
// If set to 2, all connections with the same player ID and stream name are bundled.
|
||||
// If set to 3, all connections with the same viewer IP, player ID and stream name are bundled.
|
||||
/// \param _master: If True, we are reading from this page. If False, we are writing (to our entry) on this page
|
||||
/// \param reIssue: If True, claim a new entry on this page
|
||||
void Connections::reload(std::string streamName, std::string ip, std::string sid, std::string protocol, std::string reqUrl, uint64_t sessionMode, bool _master, bool reIssue){
|
||||
if (sessionMode == 0xFFFFFFFFFFFFFFFFull){
|
||||
FAIL_MSG("The session mode was not initialised properly. Assuming default behaviour of bundling by viewer IP, stream name and player id");
|
||||
sessionMode = SESS_BUNDLE_STREAMNAME_HOSTNAME_SESSIONID;
|
||||
}
|
||||
// Generate a unique session ID for each viewer, input or output
|
||||
sessionId = generateSession(streamName, ip, sid, protocol, sessionMode);
|
||||
if (protocol.size() >= 6 && protocol.substr(0, 6) == "INPUT:"){
|
||||
sessionId = "I" + sessionId;
|
||||
}else if (protocol.size() >= 7 && protocol.substr(0, 7) == "OUTPUT:"){
|
||||
sessionId = "O" + sessionId;
|
||||
}
|
||||
char userPageName[NAME_BUFFER_SIZE];
|
||||
snprintf(userPageName, NAME_BUFFER_SIZE, COMMS_SESSIONS, sessionId.c_str());
|
||||
// Check if the page exists, if not, spawn new session process
|
||||
if (!_master){
|
||||
dataPage.init(userPageName, 0, false, false);
|
||||
if (!dataPage){
|
||||
pid_t thisPid;
|
||||
std::deque<std::string> args;
|
||||
args.push_back(Util::getMyPath() + "MistSession");
|
||||
args.push_back(sessionId);
|
||||
args.push_back("--sessionmode");
|
||||
args.push_back(JSON::Value(sessionMode).asString());
|
||||
args.push_back("--streamname");
|
||||
args.push_back(streamName);
|
||||
args.push_back("--ip");
|
||||
args.push_back(ip);
|
||||
args.push_back("--sid");
|
||||
args.push_back(sid);
|
||||
args.push_back("--protocol");
|
||||
args.push_back(protocol);
|
||||
args.push_back("--requrl");
|
||||
args.push_back(reqUrl);
|
||||
int err = fileno(stderr);
|
||||
thisPid = Util::Procs::StartPiped(args, 0, 0, &err);
|
||||
Util::Procs::forget(thisPid);
|
||||
HIGH_MSG("Spawned new session executeable (pid %u) for sessionId '%s', corresponding to host %s and stream %s", thisPid, sessionId.c_str(), ip.c_str(), streamName.c_str());
|
||||
}
|
||||
}
|
||||
// Open SEM_SESSION
|
||||
if(!sem){
|
||||
char semName[NAME_BUFFER_SIZE];
|
||||
snprintf(semName, NAME_BUFFER_SIZE, SEM_SESSION, sessionId.c_str());
|
||||
sem.open(semName, O_RDWR, ACCESSPERMS, 1);
|
||||
}
|
||||
Comms::reload(userPageName, COMMS_SESSIONS_INITSIZE, _master, reIssue);
|
||||
VERYHIGH_MSG("Reloading connection. Claimed record %lu", index);
|
||||
}
|
||||
|
||||
/// \brief Marks the data page as closed, so that we longer write any new data to is
|
||||
void Connections::setExit(){
|
||||
if (!master){return;}
|
||||
dataAccX.setExit();
|
||||
}
|
||||
|
||||
bool Connections::getExit(){
|
||||
return dataAccX.isExit();
|
||||
}
|
||||
|
||||
void Connections::unload(){
|
||||
if (index != INVALID_RECORD_INDEX){
|
||||
setStatus(COMM_STATUS_DISCONNECT | getStatus());
|
||||
}
|
||||
index = INVALID_RECORD_INDEX;
|
||||
}
|
||||
void Connections::addFields(){
|
||||
Comms::addFields();
|
||||
dataAccX.addField("now", RAX_64UINT);
|
||||
dataAccX.addField("time", RAX_64UINT);
|
||||
dataAccX.addField("lastsecond", RAX_64UINT);
|
||||
dataAccX.addField("down", RAX_64UINT);
|
||||
dataAccX.addField("up", RAX_64UINT);
|
||||
dataAccX.addField("host", RAX_RAW, 16);
|
||||
dataAccX.addField("stream", RAX_STRING, 100);
|
||||
dataAccX.addField("connector", RAX_STRING, 20);
|
||||
dataAccX.addField("tags", RAX_STRING, 512);
|
||||
dataAccX.addField("pktcount", RAX_64UINT);
|
||||
dataAccX.addField("pktloss", RAX_64UINT);
|
||||
dataAccX.addField("pktretrans", RAX_64UINT);
|
||||
}
|
||||
|
||||
void Connections::nullFields(){
|
||||
Comms::nullFields();
|
||||
setTags("");
|
||||
setConnector("");
|
||||
setStream("");
|
||||
setHost("");
|
||||
setUp(0);
|
||||
setDown(0);
|
||||
setLastSecond(0);
|
||||
setTime(0);
|
||||
setNow(0);
|
||||
setPacketCount(0);
|
||||
setPacketLostCount(0);
|
||||
setPacketRetransmitCount(0);
|
||||
}
|
||||
|
||||
void Connections::fieldAccess(){
|
||||
Comms::fieldAccess();
|
||||
now = dataAccX.getFieldAccX("now");
|
||||
time = dataAccX.getFieldAccX("time");
|
||||
lastSecond = dataAccX.getFieldAccX("lastsecond");
|
||||
down = dataAccX.getFieldAccX("down");
|
||||
up = dataAccX.getFieldAccX("up");
|
||||
host = dataAccX.getFieldAccX("host");
|
||||
stream = dataAccX.getFieldAccX("stream");
|
||||
connector = dataAccX.getFieldAccX("connector");
|
||||
tags = dataAccX.getFieldAccX("tags");
|
||||
pktcount = dataAccX.getFieldAccX("pktcount");
|
||||
pktloss = dataAccX.getFieldAccX("pktloss");
|
||||
pktretrans = dataAccX.getFieldAccX("pktretrans");
|
||||
}
|
||||
|
||||
uint64_t Connections::getNow() const{return now.uint(index);}
|
||||
uint64_t Connections::getNow(size_t idx) const{return (master ? now.uint(idx) : 0);}
|
||||
void Connections::setNow(uint64_t _now){now.set(_now, index);}
|
||||
void Connections::setNow(uint64_t _now, size_t idx){
|
||||
if (!master){return;}
|
||||
now.set(_now, idx);
|
||||
}
|
||||
|
||||
uint64_t Connections::getTime() const{return time.uint(index);}
|
||||
uint64_t Connections::getTime(size_t idx) const{return (master ? time.uint(idx) : 0);}
|
||||
void Connections::setTime(uint64_t _time){time.set(_time, index);}
|
||||
void Connections::setTime(uint64_t _time, size_t idx){
|
||||
if (!master){return;}
|
||||
time.set(_time, idx);
|
||||
}
|
||||
|
||||
uint64_t Connections::getLastSecond() const{return lastSecond.uint(index);}
|
||||
uint64_t Connections::getLastSecond(size_t idx) const{
|
||||
return (master ? lastSecond.uint(idx) : 0);
|
||||
}
|
||||
void Connections::setLastSecond(uint64_t _lastSecond){lastSecond.set(_lastSecond, index);}
|
||||
void Connections::setLastSecond(uint64_t _lastSecond, size_t idx){
|
||||
if (!master){return;}
|
||||
lastSecond.set(_lastSecond, idx);
|
||||
}
|
||||
|
||||
uint64_t Connections::getDown() const{return down.uint(index);}
|
||||
uint64_t Connections::getDown(size_t idx) const{return (master ? down.uint(idx) : 0);}
|
||||
void Connections::setDown(uint64_t _down){down.set(_down, index);}
|
||||
void Connections::setDown(uint64_t _down, size_t idx){
|
||||
if (!master){return;}
|
||||
down.set(_down, idx);
|
||||
}
|
||||
|
||||
uint64_t Connections::getUp() const{return up.uint(index);}
|
||||
uint64_t Connections::getUp(size_t idx) const{return (master ? up.uint(idx) : 0);}
|
||||
void Connections::setUp(uint64_t _up){up.set(_up, index);}
|
||||
void Connections::setUp(uint64_t _up, size_t idx){
|
||||
if (!master){return;}
|
||||
up.set(_up, idx);
|
||||
}
|
||||
|
||||
std::string Connections::getHost() const{return std::string(host.ptr(index), 16);}
|
||||
std::string Connections::getHost(size_t idx) const{
|
||||
if (!master){return std::string((size_t)16, (char)'\000');}
|
||||
return std::string(host.ptr(idx), 16);
|
||||
}
|
||||
void Connections::setHost(std::string _host){host.set(_host, index);}
|
||||
void Connections::setHost(std::string _host, size_t idx){
|
||||
if (!master){return;}
|
||||
host.set(_host, idx);
|
||||
}
|
||||
|
||||
std::string Connections::getStream() const{return stream.string(index);}
|
||||
std::string Connections::getStream(size_t idx) const{return (master ? stream.string(idx) : "");}
|
||||
void Connections::setStream(std::string _stream){stream.set(_stream, index);}
|
||||
void Connections::setStream(std::string _stream, size_t idx){
|
||||
if (!master){return;}
|
||||
stream.set(_stream, idx);
|
||||
}
|
||||
|
||||
std::string Connections::getConnector() const{return connector.string(index);}
|
||||
std::string Connections::getConnector(size_t idx) const{
|
||||
return (master ? connector.string(idx) : "");
|
||||
}
|
||||
void Connections::setConnector(std::string _connector){connector.set(_connector, index);}
|
||||
void Connections::setConnector(std::string _connector, size_t idx){
|
||||
if (!master){return;}
|
||||
connector.set(_connector, idx);
|
||||
}
|
||||
|
||||
bool Connections::hasConnector(size_t idx, std::string protocol){
|
||||
std::stringstream sstream(connector.string(idx));
|
||||
std::string _conn;
|
||||
while (std::getline(sstream, _conn, ',')){
|
||||
if (_conn == protocol){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string Connections::getTags() const{return tags.string(index);}
|
||||
std::string Connections::getTags(size_t idx) const{return (master ? tags.string(idx) : 0);}
|
||||
void Connections::setTags(std::string _sid){tags.set(_sid, index);}
|
||||
void Connections::setTags(std::string _sid, size_t idx){
|
||||
if (!master){return;}
|
||||
tags.set(_sid, idx);
|
||||
}
|
||||
|
||||
uint64_t Connections::getPacketCount() const{return pktcount.uint(index);}
|
||||
uint64_t Connections::getPacketCount(size_t idx) const{
|
||||
return (master ? pktcount.uint(idx) : 0);
|
||||
}
|
||||
void Connections::setPacketCount(uint64_t _count){pktcount.set(_count, index);}
|
||||
void Connections::setPacketCount(uint64_t _count, size_t idx){
|
||||
if (!master){return;}
|
||||
pktcount.set(_count, idx);
|
||||
}
|
||||
|
||||
uint64_t Connections::getPacketLostCount() const{return pktloss.uint(index);}
|
||||
uint64_t Connections::getPacketLostCount(size_t idx) const{
|
||||
return (master ? pktloss.uint(idx) : 0);
|
||||
}
|
||||
void Connections::setPacketLostCount(uint64_t _lost){pktloss.set(_lost, index);}
|
||||
void Connections::setPacketLostCount(uint64_t _lost, size_t idx){
|
||||
if (!master){return;}
|
||||
pktloss.set(_lost, idx);
|
||||
}
|
||||
|
||||
uint64_t Connections::getPacketRetransmitCount() const{return pktretrans.uint(index);}
|
||||
uint64_t Connections::getPacketRetransmitCount(size_t idx) const{
|
||||
return (master ? pktretrans.uint(idx) : 0);
|
||||
}
|
||||
void Connections::setPacketRetransmitCount(uint64_t _retrans){pktretrans.set(_retrans, index);}
|
||||
void Connections::setPacketRetransmitCount(uint64_t _retrans, size_t idx){
|
||||
if (!master){return;}
|
||||
pktretrans.set(_retrans, idx);
|
||||
}
|
||||
|
||||
/// \brief Generates a session ID which is unique per viewer
|
||||
/// \return generated session ID as string
|
||||
std::string Connections::generateSession(std::string streamName, std::string ip, std::string sid, std::string connector, uint64_t sessionMode){
|
||||
std::string concat;
|
||||
// First bit defines whether to include stream name
|
||||
if (sessionMode > 7){
|
||||
concat += streamName;
|
||||
sessionMode -= 8;
|
||||
}
|
||||
// Second bit defines whether to include viewer ip
|
||||
if (sessionMode > 3){
|
||||
concat += ip;
|
||||
sessionMode -= 4;
|
||||
}
|
||||
// Third bit defines whether to include player ip
|
||||
if (sessionMode > 1){
|
||||
concat += sid;
|
||||
sessionMode -= 2;
|
||||
}
|
||||
// Fourth bit defines whether to include protocol
|
||||
if (sessionMode == 1){
|
||||
concat += connector;
|
||||
sessionMode = 0;
|
||||
}
|
||||
if (sessionMode > 0){
|
||||
WARN_MSG("Could not resolve session mode of value %lu", sessionMode);
|
||||
}
|
||||
return Secure::sha256(concat.c_str(), concat.length());
|
||||
}
|
||||
}// namespace Comms
|
||||
|
|
50
lib/comms.h
50
lib/comms.h
|
@ -64,21 +64,21 @@ namespace Comms{
|
|||
Util::FieldAccX pid;
|
||||
};
|
||||
|
||||
class Statistics : public Comms{
|
||||
class Connections : public Comms{
|
||||
public:
|
||||
Statistics();
|
||||
operator bool() const{return dataPage.mapped && (master || index != INVALID_RECORD_INDEX);}
|
||||
void reload(std::string streamName, std::string ip, std::string sid, std::string protocol, std::string reqUrl, uint64_t sessionMode, bool _master = false, bool reIssue = false);
|
||||
void unload();
|
||||
void reload(bool _master = false, bool reIssue = false);
|
||||
operator bool() const{return dataPage.mapped && (master || index != INVALID_RECORD_INDEX);}
|
||||
std::string generateSession(std::string streamName, std::string ip, std::string sid, std::string connector, uint64_t sessionMode);
|
||||
std::string sessionId;
|
||||
|
||||
void setExit();
|
||||
bool getExit();
|
||||
|
||||
virtual void addFields();
|
||||
virtual void nullFields();
|
||||
virtual void fieldAccess();
|
||||
|
||||
uint8_t getSync() const;
|
||||
uint8_t getSync(size_t idx) const;
|
||||
void setSync(uint8_t _sync);
|
||||
void setSync(uint8_t _sync, size_t idx);
|
||||
|
||||
uint64_t getNow() const;
|
||||
uint64_t getNow(size_t idx) const;
|
||||
void setNow(uint64_t _now);
|
||||
|
@ -118,11 +118,12 @@ namespace Comms{
|
|||
std::string getConnector(size_t idx) const;
|
||||
void setConnector(std::string _connector);
|
||||
void setConnector(std::string _connector, size_t idx);
|
||||
bool hasConnector(size_t idx, std::string protocol);
|
||||
|
||||
uint32_t getCRC() const;
|
||||
uint32_t getCRC(size_t idx) const;
|
||||
void setCRC(uint32_t _crc);
|
||||
void setCRC(uint32_t _crc, size_t idx);
|
||||
std::string getTags() const;
|
||||
std::string getTags(size_t idx) const;
|
||||
void setTags(std::string _sid);
|
||||
void setTags(std::string _sid, size_t idx);
|
||||
|
||||
uint64_t getPacketCount() const;
|
||||
uint64_t getPacketCount(size_t idx) const;
|
||||
|
@ -139,11 +140,7 @@ namespace Comms{
|
|||
void setPacketRetransmitCount(uint64_t _retransmit);
|
||||
void setPacketRetransmitCount(uint64_t _retransmit, size_t idx);
|
||||
|
||||
std::string getSessId() const;
|
||||
std::string getSessId(size_t index) const;
|
||||
|
||||
private:
|
||||
Util::FieldAccX sync;
|
||||
protected:
|
||||
Util::FieldAccX now;
|
||||
Util::FieldAccX time;
|
||||
Util::FieldAccX lastSecond;
|
||||
|
@ -152,7 +149,8 @@ namespace Comms{
|
|||
Util::FieldAccX host;
|
||||
Util::FieldAccX stream;
|
||||
Util::FieldAccX connector;
|
||||
Util::FieldAccX crc;
|
||||
Util::FieldAccX sessId;
|
||||
Util::FieldAccX tags;
|
||||
Util::FieldAccX pktcount;
|
||||
Util::FieldAccX pktloss;
|
||||
Util::FieldAccX pktretrans;
|
||||
|
@ -186,4 +184,18 @@ namespace Comms{
|
|||
Util::FieldAccX track;
|
||||
Util::FieldAccX keyNum;
|
||||
};
|
||||
|
||||
class Sessions : public Connections{
|
||||
public:
|
||||
Sessions();
|
||||
void reload(bool _master = false, bool reIssue = false);
|
||||
std::string getSessId() const;
|
||||
std::string getSessId(size_t idx) const;
|
||||
void setSessId(std::string _sid);
|
||||
void setSessId(std::string _sid, size_t idx);
|
||||
bool sessIdExists(std::string _sid);
|
||||
virtual void addFields();
|
||||
virtual void nullFields();
|
||||
virtual void fieldAccess();
|
||||
};
|
||||
}// namespace Comms
|
||||
|
|
|
@ -196,11 +196,14 @@ static inline void show_stackframe(){}
|
|||
#define TRACK_PAGE_RECORDSIZE 36
|
||||
|
||||
#define COMMS_STATISTICS "MstStat"
|
||||
#define COMMS_STATISTICS_INITSIZE 8 * 1024 * 1024
|
||||
#define COMMS_STATISTICS_INITSIZE 16 * 1024 * 1024
|
||||
|
||||
#define COMMS_USERS "MstUser%s" //%s stream name
|
||||
#define COMMS_USERS_INITSIZE 512 * 1024
|
||||
|
||||
#define COMMS_SESSIONS "MstSession%s"
|
||||
#define COMMS_SESSIONS_INITSIZE 8 * 1024 * 1024
|
||||
|
||||
#define SEM_STATISTICS "/MstStat"
|
||||
#define SEM_USERS "/MstUser%s" //%s stream name
|
||||
|
||||
|
@ -226,7 +229,9 @@ static inline void show_stackframe(){}
|
|||
#define SEM_LIVE "/MstLIVE%s" //%s stream name
|
||||
#define SEM_INPUT "/MstInpt%s" //%s stream name
|
||||
#define SEM_TRACKLIST "/MstTRKS%s" //%s stream name
|
||||
#define SEM_SESSION "MstSess%s"
|
||||
#define SEM_SESSCACHE "/MstSessCacheLock"
|
||||
#define SESS_BUNDLE_STREAMNAME_HOSTNAME_SESSIONID 14
|
||||
#define SHM_CAPA "MstCapa"
|
||||
#define SHM_PROTO "MstProt"
|
||||
#define SHM_PROXY "MstProx"
|
||||
|
|
|
@ -306,7 +306,6 @@ int main_loop(int argc, char **argv){
|
|||
if (Controller::Storage["config"].isMember("accesslog")){
|
||||
Controller::conf.getOption("accesslog", true)[0u] = Controller::Storage["config"]["accesslog"];
|
||||
}
|
||||
Controller::maxConnsPerIP = Controller::conf.getInteger("maxconnsperip");
|
||||
Controller::Storage["config"]["prometheus"] = Controller::conf.getString("prometheus");
|
||||
Controller::Storage["config"]["accesslog"] = Controller::conf.getString("accesslog");
|
||||
Controller::normalizeTrustedProxies(Controller::Storage["config"]["trustedproxy"]);
|
||||
|
|
|
@ -594,6 +594,7 @@ void Controller::handleAPICommands(JSON::Value &Request, JSON::Value &Response){
|
|||
out["prometheus"] = in["prometheus"];
|
||||
Controller::prometheus = out["prometheus"].asStringRef();
|
||||
}
|
||||
if (in.isMember("sessionMode")){out["sessionMode"] = in["sessionMode"];}
|
||||
if (in.isMember("defaultStream")){out["defaultStream"] = in["defaultStream"];}
|
||||
if (in.isMember("location") && in["location"].isObject()){
|
||||
out["location"]["lat"] = in["location"]["lat"].asDouble();
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -18,8 +18,7 @@
|
|||
namespace Controller{
|
||||
|
||||
extern bool killOnExit;
|
||||
extern unsigned int maxConnsPerIP;
|
||||
|
||||
|
||||
/// This function is ran whenever a stream becomes active.
|
||||
void streamStarted(std::string stream);
|
||||
/// This function is ran whenever a stream becomes inactive.
|
||||
|
@ -35,34 +34,14 @@ namespace Controller{
|
|||
uint64_t pktCount;
|
||||
uint64_t pktLost;
|
||||
uint64_t pktRetransmit;
|
||||
std::string connectors;
|
||||
};
|
||||
|
||||
enum sessType{SESS_UNSET = 0, SESS_INPUT, SESS_OUTPUT, SESS_VIEWER};
|
||||
|
||||
/// This is a comparison and storage class that keeps sessions apart from each other.
|
||||
/// Whenever two of these objects are not equal, it will create a new session.
|
||||
class sessIndex{
|
||||
public:
|
||||
sessIndex();
|
||||
sessIndex(const Comms::Statistics &statComm, size_t id);
|
||||
std::string ID;
|
||||
std::string host;
|
||||
unsigned int crc;
|
||||
std::string streamName;
|
||||
std::string connector;
|
||||
|
||||
bool operator==(const sessIndex &o) const;
|
||||
bool operator!=(const sessIndex &o) const;
|
||||
bool operator>(const sessIndex &o) const;
|
||||
bool operator<=(const sessIndex &o) const;
|
||||
bool operator<(const sessIndex &o) const;
|
||||
bool operator>=(const sessIndex &o) const;
|
||||
std::string toStr();
|
||||
};
|
||||
|
||||
class statStorage{
|
||||
public:
|
||||
void update(Comms::Statistics &statComm, size_t index);
|
||||
void update(Comms::Sessions &statComm, size_t index);
|
||||
bool hasDataFor(unsigned long long);
|
||||
statLog &getDataFor(unsigned long long);
|
||||
std::map<unsigned long long, statLog> log;
|
||||
|
@ -75,36 +54,33 @@ namespace Controller{
|
|||
uint64_t firstActive;
|
||||
uint64_t firstSec;
|
||||
uint64_t lastSec;
|
||||
uint64_t wipedUp;
|
||||
uint64_t wipedDown;
|
||||
uint64_t wipedPktCount;
|
||||
uint64_t wipedPktLost;
|
||||
uint64_t wipedPktRetransmit;
|
||||
std::deque<statStorage> oldConns;
|
||||
sessType sessionType;
|
||||
bool tracked;
|
||||
uint8_t noBWCount; ///< Set to 2 when not to count for external bandwidth
|
||||
std::string streamName;
|
||||
std::string host;
|
||||
std::string curConnector;
|
||||
std::string sessId;
|
||||
|
||||
public:
|
||||
statSession();
|
||||
uint32_t invalidate();
|
||||
uint32_t kill();
|
||||
char sync;
|
||||
std::map<uint64_t, statStorage> curConns;
|
||||
~statSession();
|
||||
statStorage curData;
|
||||
std::set<std::string> tags;
|
||||
sessType getSessType();
|
||||
void wipeOld(uint64_t);
|
||||
void finish(uint64_t index);
|
||||
void switchOverTo(statSession &newSess, uint64_t index);
|
||||
void update(uint64_t index, Comms::Statistics &data);
|
||||
void dropSession(const sessIndex &index);
|
||||
void update(uint64_t index, Comms::Sessions &data);
|
||||
uint64_t getStart();
|
||||
uint64_t getEnd();
|
||||
bool isViewerOn(uint64_t time);
|
||||
bool isConnected();
|
||||
bool isTracked();
|
||||
bool hasDataFor(uint64_t time);
|
||||
bool hasData();
|
||||
std::string getStreamName();
|
||||
std::string getHost();
|
||||
std::string getSessId();
|
||||
std::string getCurrentProtocols();
|
||||
uint64_t newestDataPoint();
|
||||
uint64_t getConnTime(uint64_t time);
|
||||
uint64_t getConnTime();
|
||||
uint64_t getLastSecond(uint64_t time);
|
||||
uint64_t getDown(uint64_t time);
|
||||
uint64_t getUp();
|
||||
|
@ -122,8 +98,6 @@ namespace Controller{
|
|||
uint64_t getBpsUp(uint64_t start, uint64_t end);
|
||||
};
|
||||
|
||||
extern std::map<sessIndex, statSession> sessions;
|
||||
extern std::map<unsigned long, sessIndex> connToSession;
|
||||
extern tthread::mutex statsMutex;
|
||||
extern uint64_t statDropoff;
|
||||
|
||||
|
@ -155,6 +129,7 @@ namespace Controller{
|
|||
void sessions_shutdown(const std::string &streamname, const std::string &protocol = "");
|
||||
bool hasViewers(std::string streamName);
|
||||
void writeSessionCache(); /*LTS*/
|
||||
void killConnections(std::string sessId);
|
||||
|
||||
#define PROMETHEUS_TEXT 0
|
||||
#define PROMETHEUS_JSON 1
|
||||
|
|
|
@ -91,19 +91,6 @@ namespace Controller{
|
|||
rlxAccs->setString("tags", tags, newEndPos);
|
||||
rlxAccs->setEndPos(newEndPos + 1);
|
||||
}
|
||||
if (Triggers::shouldTrigger("USER_END", strm)){
|
||||
std::stringstream plgen;
|
||||
plgen << sessId << "\n"
|
||||
<< strm << "\n"
|
||||
<< conn << "\n"
|
||||
<< host << "\n"
|
||||
<< duration << "\n"
|
||||
<< up << "\n"
|
||||
<< down << "\n"
|
||||
<< tags;
|
||||
std::string payload = plgen.str();
|
||||
Triggers::doTrigger("USER_END", payload, strm);
|
||||
}
|
||||
}
|
||||
|
||||
void normalizeTrustedProxies(JSON::Value &tp){
|
||||
|
@ -450,7 +437,8 @@ namespace Controller{
|
|||
systemBoot = globAccX.getInt("systemBoot");
|
||||
}
|
||||
if(!globAccX.getFieldAccX("defaultStream")
|
||||
|| !globAccX.getFieldAccX("systemBoot")){
|
||||
|| !globAccX.getFieldAccX("systemBoot")
|
||||
|| !globAccX.getFieldAccX("sessionMode")){
|
||||
globAccX.setReload();
|
||||
globCfg.master = true;
|
||||
globCfg.close();
|
||||
|
@ -461,12 +449,16 @@ namespace Controller{
|
|||
if (!globAccX.isReady()){
|
||||
globAccX.addField("defaultStream", RAX_128STRING);
|
||||
globAccX.addField("systemBoot", RAX_64UINT);
|
||||
globAccX.addField("sessionMode", RAX_64UINT);
|
||||
if (!Storage["config"]["sessionMode"]){
|
||||
Storage["config"]["sessionMode"] = SESS_BUNDLE_STREAMNAME_HOSTNAME_SESSIONID;
|
||||
}
|
||||
globAccX.setRCount(1);
|
||||
globAccX.setEndPos(1);
|
||||
globAccX.setReady();
|
||||
}
|
||||
globAccX.setString("defaultStream", Storage["config"]["defaultStream"].asStringRef());
|
||||
globAccX.setInt("systemBoot", systemBoot);
|
||||
globAccX.setInt("sessionMode", Storage["config"]["sessionMode"].asInt());
|
||||
globCfg.master = false; // leave the page after closing
|
||||
}
|
||||
}
|
||||
|
|
|
@ -794,7 +794,7 @@ namespace Mist{
|
|||
void Input::streamMainLoop(){
|
||||
uint64_t statTimer = 0;
|
||||
uint64_t startTime = Util::bootSecs();
|
||||
Comms::Statistics statComm;
|
||||
Comms::Connections statComm;
|
||||
getNext();
|
||||
if (thisPacket && !userSelect.count(thisIdx)){
|
||||
userSelect[thisIdx].reload(streamName, thisIdx, COMM_STATUS_ACTIVE | COMM_STATUS_SOURCE | COMM_STATUS_DONOTTRACK);
|
||||
|
@ -820,7 +820,7 @@ namespace Mist{
|
|||
|
||||
if (Util::bootSecs() - statTimer > 1){
|
||||
// Connect to stats for INPUT detection
|
||||
if (!statComm){statComm.reload();}
|
||||
if (!statComm){statComm.reload(streamName, "", JSON::Value(getpid()).asString(), "INPUT:" + capa["name"].asStringRef(), "", SESS_BUNDLE_STREAMNAME_HOSTNAME_SESSIONID);}
|
||||
if (statComm){
|
||||
if (!statComm){
|
||||
config->is_active = false;
|
||||
|
@ -829,7 +829,6 @@ namespace Mist{
|
|||
}
|
||||
uint64_t now = Util::bootSecs();
|
||||
statComm.setNow(now);
|
||||
statComm.setCRC(getpid());
|
||||
statComm.setStream(streamName);
|
||||
statComm.setConnector("INPUT:" + capa["name"].asStringRef());
|
||||
statComm.setTime(now - startTime);
|
||||
|
@ -842,7 +841,7 @@ namespace Mist{
|
|||
}
|
||||
}
|
||||
|
||||
void Input::connStats(Comms::Statistics &statComm){
|
||||
void Input::connStats(Comms::Connections &statComm){
|
||||
statComm.setUp(0);
|
||||
statComm.setDown(streamByteCount());
|
||||
statComm.setHost(getConnectedBinHost());
|
||||
|
@ -853,7 +852,7 @@ namespace Mist{
|
|||
uint64_t statTimer = 0;
|
||||
uint64_t startTime = Util::bootSecs();
|
||||
size_t idx;
|
||||
Comms::Statistics statComm;
|
||||
Comms::Connections statComm;
|
||||
|
||||
|
||||
DTSC::Meta liveMeta(config->getString("streamname"), false);
|
||||
|
@ -985,7 +984,7 @@ namespace Mist{
|
|||
|
||||
if (Util::bootSecs() - statTimer > 1){
|
||||
// Connect to stats for INPUT detection
|
||||
if (!statComm){statComm.reload();}
|
||||
if (!statComm){statComm.reload(streamName, "", JSON::Value(getpid()).asString(), "INPUT:" + capa["name"].asStringRef(), "", SESS_BUNDLE_STREAMNAME_HOSTNAME_SESSIONID);}
|
||||
if (statComm){
|
||||
if (statComm.getStatus() & COMM_STATUS_REQDISCONNECT){
|
||||
config->is_active = false;
|
||||
|
@ -994,7 +993,6 @@ namespace Mist{
|
|||
}
|
||||
uint64_t now = Util::bootSecs();
|
||||
statComm.setNow(now);
|
||||
statComm.setCRC(getpid());
|
||||
statComm.setStream(streamName);
|
||||
statComm.setConnector("INPUT:" + capa["name"].asStringRef());
|
||||
statComm.setTime(now - startTime);
|
||||
|
|
|
@ -69,7 +69,7 @@ namespace Mist{
|
|||
virtual void userOnActive(size_t id);
|
||||
virtual void userOnDisconnect(size_t id);
|
||||
virtual void userLeadOut();
|
||||
virtual void connStats(Comms::Statistics & statComm);
|
||||
virtual void connStats(Comms::Connections & statComm);
|
||||
virtual void parseHeader();
|
||||
bool bufferFrame(size_t track, uint32_t keyNum);
|
||||
|
||||
|
|
|
@ -196,7 +196,7 @@ namespace Mist{
|
|||
}
|
||||
|
||||
void InputRTSP::streamMainLoop(){
|
||||
Comms::Statistics statComm;
|
||||
Comms::Connections statComm;
|
||||
uint64_t startTime = Util::epoch();
|
||||
uint64_t lastPing = Util::bootSecs();
|
||||
uint64_t lastSecs = 0;
|
||||
|
@ -210,7 +210,7 @@ namespace Mist{
|
|||
if (lastSecs != currSecs){
|
||||
lastSecs = currSecs;
|
||||
// Connect to stats for INPUT detection
|
||||
statComm.reload();
|
||||
statComm.reload(streamName, "", JSON::Value(getpid()).asString(), "INPUT:" + capa["name"].asStringRef(), "", SESS_BUNDLE_STREAMNAME_HOSTNAME_SESSIONID);
|
||||
if (statComm){
|
||||
if (statComm.getStatus() & COMM_STATUS_REQDISCONNECT){
|
||||
config->is_active = false;
|
||||
|
@ -219,7 +219,6 @@ namespace Mist{
|
|||
}
|
||||
uint64_t now = Util::bootSecs();
|
||||
statComm.setNow(now);
|
||||
statComm.setCRC(getpid());
|
||||
statComm.setStream(streamName);
|
||||
statComm.setConnector("INPUT:" + capa["name"].asStringRef());
|
||||
statComm.setUp(tcpCon.dataUp());
|
||||
|
|
|
@ -193,7 +193,7 @@ namespace Mist{
|
|||
|
||||
// Updates stats and quits if parsePacket returns false
|
||||
void InputSDP::streamMainLoop(){
|
||||
Comms::Statistics statComm;
|
||||
Comms::Connections statComm;
|
||||
uint64_t startTime = Util::epoch();
|
||||
uint64_t lastSecs = 0;
|
||||
// Get RTP packets from UDP socket and stop if this fails
|
||||
|
@ -202,7 +202,7 @@ namespace Mist{
|
|||
if (lastSecs != currSecs){
|
||||
lastSecs = currSecs;
|
||||
// Connect to stats for INPUT detection
|
||||
statComm.reload();
|
||||
statComm.reload(streamName, "", JSON::Value(getpid()).asString(), "INPUT:" + capa["name"].asStringRef(), "", SESS_BUNDLE_STREAMNAME_HOSTNAME_SESSIONID);
|
||||
if (statComm){
|
||||
if (statComm.getStatus() == COMM_STATUS_REQDISCONNECT){
|
||||
config->is_active = false;
|
||||
|
@ -211,7 +211,6 @@ namespace Mist{
|
|||
}
|
||||
uint64_t now = Util::bootSecs();
|
||||
statComm.setNow(now);
|
||||
statComm.setCRC(getpid());
|
||||
statComm.setStream(streamName);
|
||||
statComm.setConnector("INPUT:" + capa["name"].asStringRef());
|
||||
statComm.setDown(bytesRead);
|
||||
|
|
|
@ -527,7 +527,7 @@ namespace Mist{
|
|||
void inputTS::streamMainLoop(){
|
||||
meta.removeTrack(tmpIdx);
|
||||
INFO_MSG("Removed temptrack %zu", tmpIdx);
|
||||
Comms::Statistics statComm;
|
||||
Comms::Connections statComm;
|
||||
uint64_t downCounter = 0;
|
||||
uint64_t startTime = Util::bootSecs();
|
||||
uint64_t noDataSince = Util::bootSecs();
|
||||
|
@ -621,7 +621,7 @@ namespace Mist{
|
|||
// Check for and spawn threads here.
|
||||
if (Util::bootSecs() - threadCheckTimer > 1){
|
||||
// Connect to stats for INPUT detection
|
||||
statComm.reload();
|
||||
statComm.reload(streamName, "", JSON::Value(getpid()).asString(), "INPUT:" + capa["name"].asStringRef(), "", SESS_BUNDLE_STREAMNAME_HOSTNAME_SESSIONID);
|
||||
if (statComm){
|
||||
if (statComm.getStatus() & COMM_STATUS_REQDISCONNECT){
|
||||
config->is_active = false;
|
||||
|
@ -630,7 +630,6 @@ namespace Mist{
|
|||
}
|
||||
uint64_t now = Util::bootSecs();
|
||||
statComm.setNow(now);
|
||||
statComm.setCRC(getpid());
|
||||
statComm.setStream(streamName);
|
||||
statComm.setConnector("INPUT:" + capa["name"].asStringRef());
|
||||
statComm.setUp(0);
|
||||
|
|
|
@ -282,7 +282,7 @@ namespace Mist{
|
|||
|
||||
void inputTSSRT::setSingular(bool newSingular){singularFlag = newSingular;}
|
||||
|
||||
void inputTSSRT::connStats(Comms::Statistics &statComm){
|
||||
void inputTSSRT::connStats(Comms::Connections &statComm){
|
||||
statComm.setUp(srtConn.dataUp());
|
||||
statComm.setDown(srtConn.dataDown());
|
||||
statComm.setHost(getConnectedBinHost());
|
||||
|
|
|
@ -40,7 +40,7 @@ namespace Mist{
|
|||
|
||||
Socket::SRTConnection srtConn;
|
||||
bool singularFlag;
|
||||
virtual void connStats(Comms::Statistics &statComm);
|
||||
virtual void connStats(Comms::Connections &statComm);
|
||||
|
||||
Util::ResizeablePointer rawBuffer;
|
||||
size_t rawIdx;
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "output.h"
|
||||
#include "output.h"
|
||||
#include <mist/bitfields.h>
|
||||
#include <mist/defines.h>
|
||||
#include <mist/h264.h>
|
||||
|
@ -92,7 +92,7 @@ namespace Mist{
|
|||
firstTime = 0;
|
||||
firstPacketTime = 0xFFFFFFFFFFFFFFFFull;
|
||||
lastPacketTime = 0;
|
||||
crc = getpid();
|
||||
sid = "";
|
||||
parseData = false;
|
||||
wantRequest = true;
|
||||
sought = false;
|
||||
|
@ -100,7 +100,7 @@ namespace Mist{
|
|||
isBlocking = false;
|
||||
needsLookAhead = 0;
|
||||
extraKeepAway = 0;
|
||||
lastStats = 0;
|
||||
lastStats = 0xFFFFFFFFFFFFFFFFull;
|
||||
maxSkipAhead = 7500;
|
||||
uaDelay = 10;
|
||||
realTime = 1000;
|
||||
|
@ -111,6 +111,7 @@ namespace Mist{
|
|||
lastPushUpdate = 0;
|
||||
previousFile = "";
|
||||
currentFile = "";
|
||||
sessionMode = 0xFFFFFFFFFFFFFFFFull;
|
||||
|
||||
lastRecv = Util::bootSecs();
|
||||
if (myConn){
|
||||
|
@ -211,95 +212,9 @@ namespace Mist{
|
|||
onFail("Not allowed to play (CONN_PLAY)");
|
||||
}
|
||||
}
|
||||
doSync(true);
|
||||
/*LTS-END*/
|
||||
}
|
||||
|
||||
/// If called with force set to true and a USER_NEW trigger enabled, forces a sync immediately.
|
||||
/// Otherwise, does nothing unless the sync byte is set to 2, in which case it forces a sync as well.
|
||||
/// May be called recursively because it calls stats() which calls this function.
|
||||
/// If this happens, the extra calls to the function return instantly.
|
||||
void Output::doSync(bool force){
|
||||
if (!statComm){return;}
|
||||
if (recursingSync){return;}
|
||||
recursingSync = true;
|
||||
if (statComm.getSync() == 2 || force){
|
||||
if (getStatsName() == capa["name"].asStringRef() && Triggers::shouldTrigger("USER_NEW", streamName)){
|
||||
// sync byte 0 = no sync yet, wait for sync from controller...
|
||||
char initialSync = 0;
|
||||
// attempt to load sync status from session cache in shm
|
||||
{
|
||||
IPC::semaphore cacheLock(SEM_SESSCACHE, O_RDWR, ACCESSPERMS, 16);
|
||||
if (cacheLock){cacheLock.wait();}
|
||||
IPC::sharedPage shmSessions(SHM_SESSIONS, SHM_SESSIONS_SIZE, false, false);
|
||||
if (shmSessions.mapped){
|
||||
char shmEmpty[SHM_SESSIONS_ITEM];
|
||||
memset(shmEmpty, 0, SHM_SESSIONS_ITEM);
|
||||
std::string host;
|
||||
Socket::hostBytesToStr(statComm.getHost().data(), 16, host);
|
||||
uint32_t shmOffset = 0;
|
||||
const std::string &cName = capa["name"].asStringRef();
|
||||
while (shmOffset + SHM_SESSIONS_ITEM < SHM_SESSIONS_SIZE){
|
||||
// compare crc
|
||||
if (*(uint32_t*)(shmSessions.mapped + shmOffset) == crc){
|
||||
// compare stream name
|
||||
if (strncmp(shmSessions.mapped + shmOffset + 4, streamName.c_str(), 100) == 0){
|
||||
// compare connector
|
||||
if (strncmp(shmSessions.mapped + shmOffset + 104, cName.c_str(), 20) == 0){
|
||||
// compare host
|
||||
if (strncmp(shmSessions.mapped + shmOffset + 124, host.c_str(), 40) == 0){
|
||||
initialSync = shmSessions.mapped[shmOffset + 164];
|
||||
HIGH_MSG("Instant-sync from session cache to %u", (unsigned int)initialSync);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// stop if we reached the end
|
||||
if (memcmp(shmSessions.mapped + shmOffset, shmEmpty, SHM_SESSIONS_ITEM) == 0){
|
||||
break;
|
||||
}
|
||||
shmOffset += SHM_SESSIONS_ITEM;
|
||||
}
|
||||
}
|
||||
if (cacheLock){cacheLock.post();}
|
||||
}
|
||||
unsigned int i = 0;
|
||||
statComm.setSync(initialSync);
|
||||
// wait max 10 seconds for sync
|
||||
while ((!statComm.getSync() || statComm.getSync() == 2) && i++ < 100){
|
||||
Util::wait(100);
|
||||
stats(true);
|
||||
}
|
||||
HIGH_MSG("USER_NEW sync achieved: %u", statComm.getSync());
|
||||
// 1 = check requested (connection is new)
|
||||
if (statComm.getSync() == 1){
|
||||
std::string payload = streamName + "\n" + getConnectedHost() + "\n" +
|
||||
JSON::Value(crc).asString() + "\n" + capa["name"].asStringRef() +
|
||||
"\n" + reqUrl + "\n" + statComm.getSessId();
|
||||
if (!Triggers::doTrigger("USER_NEW", payload, streamName)){
|
||||
onFail("Not allowed to play (USER_NEW)");
|
||||
statComm.setSync(100); // 100 = denied
|
||||
}else{
|
||||
statComm.setSync(10); // 10 = accepted
|
||||
}
|
||||
}
|
||||
// 100 = denied
|
||||
if (statComm.getSync() == 100){onFail("Not allowed to play (USER_NEW cache)");}
|
||||
if (statComm.getSync() == 0){
|
||||
onFail("Not allowed to play (USER_NEW init timeout)", true);
|
||||
}
|
||||
if (statComm.getSync() == 2){
|
||||
onFail("Not allowed to play (USER_NEW re-init timeout)", true);
|
||||
}
|
||||
// anything else = accepted
|
||||
}else{
|
||||
statComm.setSync(10); // auto-accept if no trigger
|
||||
}
|
||||
}
|
||||
recursingSync = false;
|
||||
}
|
||||
|
||||
std::string Output::getConnectedHost(){return myConn.getHost();}
|
||||
|
||||
std::string Output::getConnectedBinHost(){
|
||||
|
@ -433,10 +348,10 @@ namespace Mist{
|
|||
|
||||
//Connect to stats reporting, if not connected already
|
||||
if (!statComm){
|
||||
statComm.reload();
|
||||
statComm.reload(streamName, getConnectedHost(), sid, capa["name"].asStringRef(), reqUrl, sessionMode);
|
||||
stats(true);
|
||||
}
|
||||
|
||||
|
||||
//push inputs do not need to wait for stream to be ready for playback
|
||||
if (isPushing()){return;}
|
||||
|
||||
|
@ -986,7 +901,7 @@ namespace Mist{
|
|||
INFO_MSG("Will split recording every %lld seconds", atoll(targetParams["split"].c_str()));
|
||||
targetParams["nxt-split"] = JSON::Value((int64_t)(seekPos + endRec)).asString();
|
||||
}
|
||||
// Duration to record in seconds. Overrides recstop.
|
||||
// Duration to record in seconds. Oversides recstop.
|
||||
if (targetParams.count("duration")){
|
||||
long long endRec = atoll(targetParams["duration"].c_str()) * 1000;
|
||||
targetParams["recstop"] = JSON::Value((int64_t)(seekPos + endRec)).asString();
|
||||
|
@ -1301,6 +1216,7 @@ namespace Mist{
|
|||
/// request URL (if any)
|
||||
/// ~~~~~~~~~~~~~~~
|
||||
int Output::run(){
|
||||
sessionMode = Util::getGlobalConfig("sessionMode").asInt();
|
||||
/*LTS-START*/
|
||||
// Connect to file target, if needed
|
||||
if (isFileTarget()){
|
||||
|
@ -1507,6 +1423,8 @@ namespace Mist{
|
|||
/*LTS-END*/
|
||||
|
||||
disconnect();
|
||||
stats(true);
|
||||
userSelect.clear();
|
||||
myConn.close();
|
||||
return 0;
|
||||
}
|
||||
|
@ -1822,7 +1740,7 @@ namespace Mist{
|
|||
// also cancel if it has been less than a second since the last update
|
||||
// unless force is set to true
|
||||
uint64_t now = Util::bootSecs();
|
||||
if (now == lastStats && !force){return;}
|
||||
if (now <= lastStats && !force){return;}
|
||||
|
||||
if (isRecording()){
|
||||
if(lastPushUpdate == 0){
|
||||
|
@ -1861,13 +1779,17 @@ namespace Mist{
|
|||
}
|
||||
}
|
||||
|
||||
if (!statComm){statComm.reload();}
|
||||
if (!statComm){return;}
|
||||
if (!statComm){statComm.reload(streamName, getConnectedHost(), sid, capa["name"].asStringRef(), reqUrl, sessionMode);}
|
||||
if (!statComm){return;}
|
||||
if (statComm.getExit()){
|
||||
onFail("Shutting down since this session is not allowed to view this stream");
|
||||
return;
|
||||
}
|
||||
|
||||
lastStats = now;
|
||||
|
||||
VERYHIGH_MSG("Writing stats: %s, %s, %u, %" PRIu64 ", %" PRIu64, getConnectedHost().c_str(), streamName.c_str(),
|
||||
crc & 0xFFFFFFFFu, myConn.dataUp(), myConn.dataDown());
|
||||
VERYHIGH_MSG("Writing stats: %s, %s, %s, %" PRIu64 ", %" PRIu64, getConnectedHost().c_str(), streamName.c_str(),
|
||||
sid.c_str(), myConn.dataUp(), myConn.dataDown());
|
||||
/*LTS-START*/
|
||||
if (statComm.getStatus() & COMM_STATUS_REQDISCONNECT){
|
||||
onFail("Shutting down on controller request");
|
||||
|
@ -1875,9 +1797,6 @@ namespace Mist{
|
|||
}
|
||||
/*LTS-END*/
|
||||
statComm.setNow(now);
|
||||
statComm.setHost(getConnectedBinHost());
|
||||
statComm.setCRC(crc);
|
||||
statComm.setStream(streamName);
|
||||
statComm.setConnector(getStatsName());
|
||||
connStats(now, statComm);
|
||||
statComm.setLastSecond(thisPacket ? thisPacket.getTime() : 0);
|
||||
|
@ -1887,7 +1806,7 @@ namespace Mist{
|
|||
// Tag the session with the user agent
|
||||
if (newUA && ((now - myConn.connTime()) >= uaDelay || !myConn) && UA.size()){
|
||||
std::string APIcall =
|
||||
"{\"tag_sessid\":{\"" + statComm.getSessId() + "\":" + JSON::string_escape("UA:" + UA) + "}}";
|
||||
"{\"tag_sessid\":{\"" + statComm.sessionId + "\":" + JSON::string_escape("UA:" + UA) + "}}";
|
||||
Socket::UDPConnection uSock;
|
||||
uSock.SetDestination(UDP_API_HOST, UDP_API_PORT);
|
||||
uSock.SendNow(APIcall);
|
||||
|
@ -1895,8 +1814,6 @@ namespace Mist{
|
|||
}
|
||||
/*LTS-END*/
|
||||
|
||||
doSync();
|
||||
|
||||
if (isPushing()){
|
||||
for (std::map<size_t, Comms::Users>::iterator it = userSelect.begin(); it != userSelect.end(); it++){
|
||||
if (it->second.getStatus() & COMM_STATUS_REQDISCONNECT){
|
||||
|
@ -1912,7 +1829,7 @@ namespace Mist{
|
|||
}
|
||||
}
|
||||
|
||||
void Output::connStats(uint64_t now, Comms::Statistics &statComm){
|
||||
void Output::connStats(uint64_t now, Comms::Connections &statComm){
|
||||
statComm.setUp(myConn.dataUp());
|
||||
statComm.setDown(myConn.dataDown());
|
||||
statComm.setTime(now - myConn.connTime());
|
||||
|
|
|
@ -86,7 +86,6 @@ namespace Mist{
|
|||
std::string hostLookup(std::string ip);
|
||||
bool onList(std::string ip, std::string list);
|
||||
std::string getCountry(std::string ip);
|
||||
void doSync(bool force = false);
|
||||
/*LTS-END*/
|
||||
std::map<size_t, uint32_t> currentPage;
|
||||
void loadPageForKey(size_t trackId, size_t keyNum);
|
||||
|
@ -105,6 +104,7 @@ namespace Mist{
|
|||
bool firstData;
|
||||
uint64_t lastPushUpdate;
|
||||
bool newUA;
|
||||
|
||||
protected: // these are to be messed with by child classes
|
||||
virtual bool inlineRestartCapable() const{
|
||||
return false;
|
||||
|
@ -122,15 +122,16 @@ namespace Mist{
|
|||
virtual std::string getStatsName();
|
||||
virtual bool hasSessionIDs(){return false;}
|
||||
|
||||
virtual void connStats(uint64_t now, Comms::Statistics &statComm);
|
||||
virtual void connStats(uint64_t now, Comms::Connections &statComm);
|
||||
|
||||
std::set<size_t> getSupportedTracks(const std::string &type = "") const;
|
||||
|
||||
inline virtual bool keepGoing(){return config->is_active && myConn;}
|
||||
|
||||
Comms::Statistics statComm;
|
||||
Comms::Connections statComm;
|
||||
bool isBlocking; ///< If true, indicates that myConn is blocking.
|
||||
uint32_t crc; ///< Checksum, if any, for usage in the stats.
|
||||
std::string sid; ///< Random identifier used to split connections into sessions
|
||||
uint64_t sessionMode;
|
||||
uint64_t nextKeyTime();
|
||||
|
||||
// stream delaying variables
|
||||
|
|
|
@ -101,7 +101,7 @@ namespace Mist{
|
|||
}
|
||||
}
|
||||
|
||||
void OutCMAF::connStats(uint64_t now, Comms::Statistics &statComm){
|
||||
void OutCMAF::connStats(uint64_t now, Comms::Connections &statComm){
|
||||
// For non-push usage, call usual function.
|
||||
if (!isRecording()){
|
||||
Output::connStats(now, statComm);
|
||||
|
|
|
@ -39,7 +39,7 @@ namespace Mist{
|
|||
bool isReadyForPlay();
|
||||
|
||||
protected:
|
||||
virtual void connStats(uint64_t now, Comms::Statistics &statComm);
|
||||
virtual void connStats(uint64_t now, Comms::Connections &statComm);
|
||||
void onTrackEnd(size_t idx);
|
||||
bool hasSessionIDs(){return !config->getBool("mergesessions");}
|
||||
|
||||
|
@ -72,6 +72,7 @@ namespace Mist{
|
|||
void startPushOut();
|
||||
void pushNext();
|
||||
|
||||
uint32_t crc;
|
||||
HTTP::URL pushUrl;
|
||||
std::map<size_t, CMAFPushTrack> pushTracks;
|
||||
void setupTrackObject(size_t idx);
|
||||
|
|
|
@ -55,7 +55,6 @@ namespace Mist{
|
|||
}
|
||||
|
||||
void HTTPOutput::onFail(const std::string &msg, bool critical){
|
||||
INFO_MSG("Failing '%s': %s", H.url.c_str(), msg.c_str());
|
||||
if (!webSock && !isRecording() && !responded){
|
||||
H.Clean(); // make sure no parts of old requests are left in any buffers
|
||||
H.SetHeader("Server", APPIDENT);
|
||||
|
@ -238,18 +237,6 @@ namespace Mist{
|
|||
}
|
||||
/*LTS-END*/
|
||||
if (H.hasHeader("User-Agent")){UA = H.GetHeader("User-Agent");}
|
||||
if (hasSessionIDs()){
|
||||
if (H.GetVar("sessId").size()){
|
||||
std::string ua = H.GetVar("sessId");
|
||||
crc = checksum::crc32(0, ua.data(), ua.size());
|
||||
}else{
|
||||
std::string ua = JSON::Value(getpid()).asString();
|
||||
crc = checksum::crc32(0, ua.data(), ua.size());
|
||||
}
|
||||
}else{
|
||||
std::string mixed_ua = UA + H.GetHeader("X-Playback-Session-Id");
|
||||
crc = checksum::crc32(0, mixed_ua.data(), mixed_ua.size());
|
||||
}
|
||||
|
||||
if (H.GetVar("audio") != ""){targetParams["audio"] = H.GetVar("audio");}
|
||||
if (H.GetVar("video") != ""){targetParams["video"] = H.GetVar("video");}
|
||||
|
@ -281,6 +268,21 @@ namespace Mist{
|
|||
realTime = 0;
|
||||
}
|
||||
}
|
||||
// Get session ID cookie or generate a random one if it wasn't set
|
||||
if (!sid.size()){
|
||||
std::map<std::string, std::string> storage;
|
||||
const std::string koekjes = H.GetHeader("Cookie");
|
||||
HTTP::parseVars(koekjes, storage);
|
||||
if (storage.count("sid")){
|
||||
// Get sid cookie, which is used to divide connections into sessions
|
||||
sid = storage.at("sid");
|
||||
}else{
|
||||
// Else generate one
|
||||
const std::string newSid = UA + JSON::Value(getpid()).asString();
|
||||
sid = JSON::Value(checksum::crc32(0, newSid.data(), newSid.size())).asString();
|
||||
H.SetHeader("sid", sid.c_str());
|
||||
}
|
||||
}
|
||||
// Handle upgrade to websocket if the output supports it
|
||||
std::string upgradeHeader = H.GetHeader("Upgrade");
|
||||
Util::stringToLower(upgradeHeader);
|
||||
|
|
|
@ -344,7 +344,7 @@ namespace Mist{
|
|||
}
|
||||
}
|
||||
|
||||
void OutTSSRT::connStats(uint64_t now, Comms::Statistics &statComm){
|
||||
void OutTSSRT::connStats(uint64_t now, Comms::Connections &statComm){
|
||||
if (!srtConn){return;}
|
||||
statComm.setUp(srtConn.dataUp());
|
||||
statComm.setDown(srtConn.dataDown());
|
||||
|
|
|
@ -15,7 +15,7 @@ namespace Mist{
|
|||
bool isReadyForPlay(){return true;}
|
||||
virtual void requestHandler();
|
||||
protected:
|
||||
virtual void connStats(uint64_t now, Comms::Statistics &statComm);
|
||||
virtual void connStats(uint64_t now, Comms::Connections &statComm);
|
||||
virtual std::string getConnectedHost(){return srtConn.remotehost;}
|
||||
virtual std::string getConnectedBinHost(){return srtConn.getBinHost();}
|
||||
|
||||
|
|
|
@ -1015,7 +1015,7 @@ namespace Mist{
|
|||
}
|
||||
}
|
||||
|
||||
void OutWebRTC::connStats(uint64_t now, Comms::Statistics &statComm){
|
||||
void OutWebRTC::connStats(uint64_t now, Comms::Connections &statComm){
|
||||
statComm.setUp(myConn.dataUp());
|
||||
statComm.setDown(myConn.dataDown());
|
||||
statComm.setPacketCount(totalPkts);
|
||||
|
|
|
@ -144,7 +144,7 @@ namespace Mist{
|
|||
void onDTSCConverterHasInitData(const size_t trackID, const std::string &initData);
|
||||
void onRTPPacketizerHasRTPPacket(const char *data, size_t nbytes);
|
||||
void onRTPPacketizerHasRTCPPacket(const char *data, uint32_t nbytes);
|
||||
virtual void connStats(uint64_t now, Comms::Statistics &statComm);
|
||||
virtual void connStats(uint64_t now, Comms::Connections &statComm);
|
||||
|
||||
private:
|
||||
uint64_t lastRecv;
|
||||
|
|
|
@ -72,7 +72,7 @@ namespace Mist{
|
|||
}
|
||||
bool needsLock(){return false;}
|
||||
bool isSingular(){return false;}
|
||||
void connStats(Comms::Statistics &statComm){
|
||||
void connStats(Comms::Connections &statComm){
|
||||
for (std::map<size_t, Comms::Users>::iterator it = userSelect.begin(); it != userSelect.end(); it++){
|
||||
if (it->second){it->second.setStatus(COMM_STATUS_DONOTTRACK | it->second.getStatus());}
|
||||
}
|
||||
|
@ -117,7 +117,7 @@ namespace Mist{
|
|||
realTime = 0;
|
||||
OutEBML::sendHeader();
|
||||
};
|
||||
void connStats(uint64_t now, Comms::Statistics &statComm){
|
||||
void connStats(uint64_t now, Comms::Connections &statComm){
|
||||
for (std::map<size_t, Comms::Users>::iterator it = userSelect.begin(); it != userSelect.end(); it++){
|
||||
if (it->second){it->second.setStatus(COMM_STATUS_DONOTTRACK | it->second.getStatus());}
|
||||
}
|
||||
|
|
367
src/session.cpp
Normal file
367
src/session.cpp
Normal file
|
@ -0,0 +1,367 @@
|
|||
#include <mist/defines.h>
|
||||
#include <mist/stream.h>
|
||||
#include <mist/util.h>
|
||||
#include <mist/config.h>
|
||||
#include <mist/auth.h>
|
||||
#include <mist/comms.h>
|
||||
#include <mist/triggers.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
// Stats of connections which have closed are added to these global counters
|
||||
uint64_t globalNow = 0;
|
||||
uint64_t globalTime = 0;
|
||||
uint64_t globalDown = 0;
|
||||
uint64_t globalUp = 0;
|
||||
uint64_t globalPktcount = 0;
|
||||
uint64_t globalPktloss = 0;
|
||||
uint64_t globalPktretrans = 0;
|
||||
// Counts the duration a connector has been active
|
||||
std::map<std::string, uint64_t> connectorCount;
|
||||
std::map<std::string, uint64_t> connectorLastActive;
|
||||
// Set to True when a session gets invalidated, so that we know to run a new USER_NEW trigger
|
||||
bool forceTrigger = false;
|
||||
void handleSignal(int signum){
|
||||
if (signum == SIGUSR1){
|
||||
forceTrigger = true;
|
||||
}
|
||||
}
|
||||
|
||||
void userOnActive(uint64_t &connections){
|
||||
++connections;
|
||||
}
|
||||
|
||||
std::string getEnvWithDefault(const std::string variableName, const std::string defaultValue){
|
||||
const char* value = getenv(variableName.c_str());
|
||||
if (value){
|
||||
unsetenv(variableName.c_str());
|
||||
return value;
|
||||
}else{
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/// \brief Adds stats of closed connections to global counters
|
||||
void userOnDisconnect(Comms::Connections & connections, size_t idx){
|
||||
std::string thisConnector = connections.getConnector(idx);
|
||||
if (thisConnector != ""){
|
||||
connectorCount[thisConnector] += connections.getTime(idx);
|
||||
}
|
||||
globalTime += connections.getTime(idx);
|
||||
globalDown += connections.getDown(idx);
|
||||
globalUp += connections.getUp(idx);
|
||||
globalPktcount += connections.getPacketCount(idx);
|
||||
globalPktloss += connections.getPacketLostCount(idx);
|
||||
globalPktretrans += connections.getPacketRetransmitCount(idx);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv){
|
||||
Comms::Connections connections;
|
||||
Comms::Sessions sessions;
|
||||
uint64_t lastSeen = Util::bootSecs();
|
||||
uint64_t currentConnections = 0;
|
||||
Util::redirectLogsIfNeeded();
|
||||
signal(SIGUSR1, handleSignal);
|
||||
// Init config and parse arguments
|
||||
Util::Config config = Util::Config("MistSession");
|
||||
JSON::Value option;
|
||||
|
||||
option.null();
|
||||
option["arg_num"] = 1;
|
||||
option["arg"] = "string";
|
||||
option["help"] = "Session identifier of the entire session";
|
||||
option["default"] = "";
|
||||
config.addOption("sessionid", option);
|
||||
|
||||
option.null();
|
||||
option["long"] = "sessionmode";
|
||||
option["short"] = "m";
|
||||
option["arg"] = "integer";
|
||||
option["default"] = 0;
|
||||
config.addOption("sessionmode", option);
|
||||
|
||||
option.null();
|
||||
option["long"] = "streamname";
|
||||
option["short"] = "n";
|
||||
option["arg"] = "string";
|
||||
option["default"] = "";
|
||||
config.addOption("streamname", option);
|
||||
|
||||
option.null();
|
||||
option["long"] = "ip";
|
||||
option["short"] = "i";
|
||||
option["arg"] = "string";
|
||||
option["default"] = "";
|
||||
config.addOption("ip", option);
|
||||
|
||||
option.null();
|
||||
option["long"] = "sid";
|
||||
option["short"] = "s";
|
||||
option["arg"] = "string";
|
||||
option["default"] = "";
|
||||
config.addOption("sid", option);
|
||||
|
||||
option.null();
|
||||
option["long"] = "protocol";
|
||||
option["short"] = "p";
|
||||
option["arg"] = "string";
|
||||
option["default"] = "";
|
||||
config.addOption("protocol", option);
|
||||
|
||||
option.null();
|
||||
option["long"] = "requrl";
|
||||
option["short"] = "r";
|
||||
option["arg"] = "string";
|
||||
option["default"] = "";
|
||||
config.addOption("requrl", option);
|
||||
|
||||
config.activate();
|
||||
if (!(config.parseArgs(argc, argv))){
|
||||
FAIL_MSG("Cannot start a new session due to invalid arguments");
|
||||
return 1;
|
||||
}
|
||||
|
||||
const uint64_t bootTime = Util::getMicros();
|
||||
// Get session ID, session mode and other variables used as payload for the USER_NEW and USER_END triggers
|
||||
const std::string thisStreamName = config.getString("streamname");
|
||||
const std::string thisHost = config.getString("ip");
|
||||
const std::string thisSid = config.getString("sid");
|
||||
const std::string thisProtocol = config.getString("protocol");
|
||||
const std::string thisReqUrl = config.getString("requrl");
|
||||
const std::string thisSessionId = config.getString("sessionid");
|
||||
const uint64_t sessionMode = config.getInteger("sessionmode");
|
||||
|
||||
if (thisSessionId == "" || thisProtocol == "" || thisStreamName == ""){
|
||||
FAIL_MSG("Given the following incomplete arguments: SessionId: '%s', protocol: '%s', stream name: '%s'. Aborting opening a new session",
|
||||
thisSessionId.c_str(), thisProtocol.c_str(), thisStreamName.c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
MEDIUM_MSG("Starting a new session for sessionId '%s'", thisSessionId.c_str());
|
||||
if (sessionMode < 1 || sessionMode > 15) {
|
||||
FAIL_MSG("Invalid session mode of value %lu. Should be larger than 0 and smaller than 16", sessionMode);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Try to lock to ensure we are the only process initialising this session
|
||||
IPC::semaphore sessionLock;
|
||||
char semName[NAME_BUFFER_SIZE];
|
||||
snprintf(semName, NAME_BUFFER_SIZE, SEM_SESSION, thisSessionId.c_str());
|
||||
sessionLock.open(semName, O_CREAT | O_RDWR, ACCESSPERMS, 1);
|
||||
// If the lock fails, the previous Session process must've failed in spectacular fashion
|
||||
// It's the Controller's task to clean everything up. When the lock fails, this cleanup hasn't happened yet
|
||||
if (!sessionLock.tryWaitOneSecond()){
|
||||
FAIL_MSG("Session '%s' already locked", thisSessionId.c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Check if a page already exists for this session ID. If so, quit
|
||||
{
|
||||
IPC::sharedPage dataPage;
|
||||
char userPageName[NAME_BUFFER_SIZE];
|
||||
snprintf(userPageName, NAME_BUFFER_SIZE, COMMS_SESSIONS, thisSessionId.c_str());
|
||||
dataPage.init(userPageName, 0, false, false);
|
||||
if (dataPage){
|
||||
INFO_MSG("Session '%s' already has a running process", thisSessionId.c_str());
|
||||
sessionLock.post();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Claim a spot in shared memory for this session on the global statistics page
|
||||
sessions.reload();
|
||||
if (!sessions){
|
||||
FAIL_MSG("Unable to register entry for session '%s' on the stats page", thisSessionId.c_str());
|
||||
sessionLock.post();
|
||||
return 1;
|
||||
}
|
||||
// Open the shared memory page containing statistics for each individual connection in this session
|
||||
connections.reload(thisStreamName, thisHost, thisSid, thisProtocol, thisReqUrl, sessionMode, true, false);
|
||||
// Initialise global session data
|
||||
sessions.setHost(thisHost);
|
||||
sessions.setSessId(thisSessionId);
|
||||
sessions.setStream(thisStreamName);
|
||||
sessionLock.post();
|
||||
|
||||
// Determine session type, since triggers only get run for viewer type sessions
|
||||
uint64_t thisType = 0;
|
||||
if (thisSessionId[0] == 'I'){
|
||||
INFO_MSG("Started new input session %s in %lu microseconds", thisSessionId.c_str(), Util::getMicros(bootTime));
|
||||
thisType = 1;
|
||||
}
|
||||
else if (thisSessionId[0] == 'O'){
|
||||
INFO_MSG("Started new output session %s in %lu microseconds", thisSessionId.c_str(), Util::getMicros(bootTime));
|
||||
thisType = 2;
|
||||
}
|
||||
else{
|
||||
INFO_MSG("Started new viewer session %s in %lu microseconds", thisSessionId.c_str(), Util::getMicros(bootTime));
|
||||
}
|
||||
|
||||
// Do a USER_NEW trigger if it is defined for this stream
|
||||
if (!thisType && Triggers::shouldTrigger("USER_NEW", thisStreamName)){
|
||||
std::string payload = thisStreamName + "\n" + thisHost + "\n" +
|
||||
thisSid + "\n" + thisProtocol +
|
||||
"\n" + thisReqUrl + "\n" + thisSessionId;
|
||||
if (!Triggers::doTrigger("USER_NEW", payload, thisStreamName)){
|
||||
// Mark all connections of this session as finished, since this viewer is not allowed to view this stream
|
||||
connections.setExit();
|
||||
connections.finishAll();
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t lastSecond = 0;
|
||||
uint64_t now = 0;
|
||||
uint64_t time = 0;
|
||||
uint64_t down = 0;
|
||||
uint64_t up = 0;
|
||||
uint64_t pktcount = 0;
|
||||
uint64_t pktloss = 0;
|
||||
uint64_t pktretrans = 0;
|
||||
std::string connector = "";
|
||||
// Stay active until Mist exits or we no longer have an active connection
|
||||
while (config.is_active && (currentConnections || Util::bootSecs() - lastSeen <= 10)){
|
||||
time = 0;
|
||||
connector = "";
|
||||
down = 0;
|
||||
up = 0;
|
||||
pktcount = 0;
|
||||
pktloss = 0;
|
||||
pktretrans = 0;
|
||||
currentConnections = 0;
|
||||
|
||||
// Count active connections
|
||||
COMM_LOOP(connections, userOnActive(currentConnections), userOnDisconnect(connections, id));
|
||||
// Loop through all connection entries to get a summary of statistics
|
||||
for (uint64_t idx = 0; idx < connections.recordCount(); idx++){
|
||||
if (connections.getStatus(idx) == COMM_STATUS_INVALID || connections.getStatus(idx) & COMM_STATUS_DISCONNECT){continue;}
|
||||
uint64_t thisLastSecond = connections.getLastSecond(idx);
|
||||
std::string thisConnector = connections.getConnector(idx);
|
||||
// Save info on the latest active connection separately
|
||||
if (thisLastSecond > lastSecond){
|
||||
lastSecond = thisLastSecond;
|
||||
now = connections.getNow(idx);
|
||||
}
|
||||
connectorLastActive[thisConnector] = thisLastSecond;
|
||||
// Sum all other variables
|
||||
time += connections.getTime(idx);
|
||||
down += connections.getDown(idx);
|
||||
up += connections.getUp(idx);
|
||||
pktcount += connections.getPacketCount(idx);
|
||||
pktloss += connections.getPacketLostCount(idx);
|
||||
pktretrans += connections.getPacketRetransmitCount(idx);
|
||||
}
|
||||
|
||||
// Convert connector duration to string
|
||||
std::stringstream connectorSummary;
|
||||
bool addDelimiter = false;
|
||||
connectorSummary << "{";
|
||||
for (std::map<std::string, uint64_t>::iterator it = connectorLastActive.begin();
|
||||
it != connectorLastActive.end(); ++it){
|
||||
if (lastSecond - it->second < 10000){
|
||||
connectorSummary << (addDelimiter ? "," : "") << it->first;
|
||||
addDelimiter = true;
|
||||
}
|
||||
}
|
||||
connectorSummary << "}";
|
||||
|
||||
// Write summary to global statistics
|
||||
sessions.setTime(time + globalTime);
|
||||
sessions.setDown(down + globalDown);
|
||||
sessions.setUp(up + globalUp);
|
||||
sessions.setPacketCount(pktcount + globalPktcount);
|
||||
sessions.setPacketLostCount(pktloss + globalPktloss);
|
||||
sessions.setPacketRetransmitCount(pktretrans + globalPktretrans);
|
||||
sessions.setLastSecond(lastSecond);
|
||||
sessions.setConnector(connectorSummary.str());
|
||||
sessions.setNow(now);
|
||||
|
||||
// Retrigger USER_NEW if a re-sync was requested
|
||||
if (!thisType && forceTrigger){
|
||||
forceTrigger = false;
|
||||
if (Triggers::shouldTrigger("USER_NEW", thisStreamName)){
|
||||
INFO_MSG("Triggering USER_NEW for stream %s", thisStreamName.c_str());
|
||||
std::string payload = thisStreamName + "\n" + thisHost + "\n" +
|
||||
thisSid + "\n" + thisProtocol +
|
||||
"\n" + thisReqUrl + "\n" + thisSessionId;
|
||||
if (!Triggers::doTrigger("USER_NEW", payload, thisStreamName)){
|
||||
INFO_MSG("USER_NEW rejected stream %s", thisStreamName.c_str());
|
||||
connections.setExit();
|
||||
connections.finishAll();
|
||||
}else{
|
||||
INFO_MSG("USER_NEW accepted stream %s", thisStreamName.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Invalidate connections if the session is marked as invalid
|
||||
if(connections.getExit()){
|
||||
connections.finishAll();
|
||||
break;
|
||||
}
|
||||
// Remember latest activity so we know when this session ends
|
||||
if (currentConnections){
|
||||
lastSeen = Util::bootSecs();
|
||||
}
|
||||
Util::sleep(1000);
|
||||
}
|
||||
|
||||
// Trigger USER_END
|
||||
if (!thisType && Triggers::shouldTrigger("USER_END", thisStreamName)){
|
||||
lastSecond = 0;
|
||||
time = 0;
|
||||
down = 0;
|
||||
up = 0;
|
||||
|
||||
// Get a final summary of this session
|
||||
for (uint64_t idx = 0; idx < connections.recordCount(); idx++){
|
||||
if (connections.getStatus(idx) == COMM_STATUS_INVALID || connections.getStatus(idx) & COMM_STATUS_DISCONNECT){continue;}
|
||||
uint64_t thisLastSecond = connections.getLastSecond(idx);
|
||||
// Set last second to the latest entry
|
||||
if (thisLastSecond > lastSecond){
|
||||
lastSecond = thisLastSecond;
|
||||
}
|
||||
// Count protocol durations across the entire session
|
||||
std::string thisConnector = connections.getConnector(idx);
|
||||
if (thisConnector != ""){
|
||||
connectorCount[thisConnector] += connections.getTime(idx);
|
||||
}
|
||||
// Sum all other variables
|
||||
time += connections.getTime(idx);
|
||||
down += connections.getDown(idx);
|
||||
up += connections.getUp(idx);
|
||||
}
|
||||
|
||||
// Convert connector duration to string
|
||||
std::stringstream connectorSummary;
|
||||
bool addDelimiter = false;
|
||||
connectorSummary << "{";
|
||||
for (std::map<std::string, uint64_t>::iterator it = connectorCount.begin();
|
||||
it != connectorCount.end(); ++it){
|
||||
connectorSummary << (addDelimiter ? "," : "") << it->first << ":" << it->second;
|
||||
addDelimiter = true;
|
||||
}
|
||||
connectorSummary << "}";
|
||||
|
||||
const uint64_t duration = lastSecond - (bootTime / 1000);
|
||||
std::stringstream summary;
|
||||
summary << thisSessionId << "\n"
|
||||
<< thisStreamName << "\n"
|
||||
<< connectorSummary.str() << "\n"
|
||||
<< thisHost << "\n"
|
||||
<< duration << "\n"
|
||||
<< up << "\n"
|
||||
<< down << "\n"
|
||||
<< sessions.getTags();
|
||||
Triggers::doTrigger("USER_END", summary.str(), thisStreamName);
|
||||
}
|
||||
|
||||
if (!thisType && connections.getExit()){
|
||||
WARN_MSG("Session %s has been invalidated since it is not allowed to view stream %s", thisSessionId.c_str(), thisStreamName.c_str());
|
||||
uint64_t sleepStart = Util::bootSecs();
|
||||
// Keep session invalidated for 10 minutes, or until the session stops
|
||||
while (config.is_active && sleepStart - Util::bootSecs() < 600){
|
||||
Util::sleep(1000);
|
||||
}
|
||||
}
|
||||
INFO_MSG("Shutting down session %s", thisSessionId.c_str());
|
||||
return 0;
|
||||
}
|
Loading…
Add table
Reference in a new issue