Merge branch 'development' into LTS_development

# Conflicts:
#	CMakeLists.txt
#	src/analysers/info.cpp
This commit is contained in:
Thulinma 2015-04-16 12:24:37 +02:00
commit cb298c57fd
13 changed files with 84 additions and 2003 deletions

View file

@ -110,13 +110,10 @@ set(libHeaders
${SOURCE_DIR}/lib/bitstream.h
${SOURCE_DIR}/lib/checksum.h
${SOURCE_DIR}/lib/config.h
${SOURCE_DIR}/lib/converter.h
${SOURCE_DIR}/lib/defines.h
${SOURCE_DIR}/lib/dtsc.h
${SOURCE_DIR}/lib/encryption.h
${SOURCE_DIR}/lib/filesystem.h
${SOURCE_DIR}/lib/flv_tag.h
${SOURCE_DIR}/lib/ftp.h
${SOURCE_DIR}/lib/http_parser.h
${SOURCE_DIR}/lib/json.h
${SOURCE_DIR}/lib/mp4_adobe.h
@ -151,13 +148,10 @@ set(libSources
${SOURCE_DIR}/lib/bitfields.cpp
${SOURCE_DIR}/lib/bitstream.cpp
${SOURCE_DIR}/lib/config.cpp
${SOURCE_DIR}/lib/converter.cpp
${SOURCE_DIR}/lib/dtsc.cpp
${SOURCE_DIR}/lib/dtscmeta.cpp
${SOURCE_DIR}/lib/encryption.cpp
${SOURCE_DIR}/lib/filesystem.cpp
${SOURCE_DIR}/lib/flv_tag.cpp
${SOURCE_DIR}/lib/ftp.cpp
${SOURCE_DIR}/lib/http_parser.cpp
${SOURCE_DIR}/lib/json.cpp
${SOURCE_DIR}/lib/mp4_adobe.cpp
@ -238,16 +232,6 @@ makeAnalyser(OGG ogg)
makeAnalyser(RTP rtp) #LTS
makeAnalyser(RTSP rtsp_rtp) #LTS
makeAnalyser(Stats stats) #LTS
add_executable(MistInfo
src/analysers/info.cpp
)
target_link_libraries(MistInfo
mist
)
install(
TARGETS MistInfo
DESTINATION bin
)
########################################
# MistServer - Inputs #

View file

@ -1,328 +0,0 @@
#include <cstdlib>
#include <cstring>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <sstream>
#include "timing.h"
#include "converter.h"
#include "procs.h"
#include "config.h"
namespace Converter {
///\brief The base constructor
Converter::Converter() {
fillFFMpegEncoders();
}
///\brief A function that fill the internal variables with values provided by examing ffmpeg output
///
///Checks for the following encoders:
/// - AAC
/// - H264
/// - MP3
void Converter::fillFFMpegEncoders() {
std::vector<char *> cmd;
cmd.reserve(3);
cmd.push_back((char *)"ffmpeg");
cmd.push_back((char *)"-encoders");
cmd.push_back(NULL);
int outFD = -1;
Util::Procs::StartPiped("FFMpegInfo", &cmd[0], 0, &outFD, 0);
while (Util::Procs::isActive("FFMpegInfo")) {
Util::sleep(100);
}
FILE * outFile = fdopen(outFD, "r");
char * fileBuf = 0;
size_t fileBufLen = 0;
while (!(feof(outFile) || ferror(outFile)) && (getline(&fileBuf, &fileBufLen, outFile) != -1)) {
if (strstr(fileBuf, "aac") || strstr(fileBuf, "AAC")) {
strtok(fileBuf, " \t");
allCodecs["ffmpeg"][strtok(NULL, " \t")] = "aac";
}
if (strstr(fileBuf, "h264") || strstr(fileBuf, "H264")) {
strtok(fileBuf, " \t");
allCodecs["ffmpeg"][strtok(NULL, " \t")] = "h264";
}
if (strstr(fileBuf, "mp3") || strstr(fileBuf, "MP3")) {
strtok(fileBuf, " \t");
allCodecs["ffmpeg"][strtok(NULL, " \t")] = "mp3";
}
}
fclose(outFile);
}
///\brief A function to obtain all available codecs that have been obtained from the encoders.
///\return A reference to the allCodecs member.
converterInfo & Converter::getCodecs() {
return allCodecs;
}
///\brief A function to obtain the available encoders in JSON format.
///\return A JSON::Value containing all encoder:codec pairs.
JSON::Value Converter::getEncoders() {
JSON::Value result;
for (converterInfo::iterator convIt = allCodecs.begin(); convIt != allCodecs.end(); convIt++) {
for (codecInfo::iterator codIt = convIt->second.begin(); codIt != convIt->second.end(); codIt++) {
if (codIt->second == "h264") {
result[convIt->first]["video"][codIt->first] = codIt->second;
} else {
result[convIt->first]["audio"][codIt->first] = codIt->second;
}
}
}
return result;
}
///\brief Looks in a given path for all files that could be converted
///\param myPath The location to look at, this should be a folder.
///\return A JSON::Value containing all media files in the location, with their corresponding metadata values.
JSON::Value Converter::queryPath(std::string myPath) {
char const * cmd[3] = {0, 0, 0};
std::string mistPath = Util::getMyPath() + "MistInfo";
cmd[0] = mistPath.c_str();
JSON::Value result;
DIR * Dirp = opendir(myPath.c_str());
struct stat StatBuf;
if (Dirp) {
dirent * entry;
while ((entry = readdir(Dirp))) {
if (stat(std::string(myPath + "/" + entry->d_name).c_str(), &StatBuf) == -1) {
continue;
}
if ((StatBuf.st_mode & S_IFREG) == 0) {
continue;
}
std::string fileName = entry->d_name;
std::string mijnPad = std::string(myPath + (myPath[myPath.size() - 1] == '/' ? "" : "/") + entry->d_name);
cmd[1] = mijnPad.c_str();
result[fileName] = JSON::fromString(Util::Procs::getOutputOf((char * const *)cmd));
}
}
return result;
}
///\brief Start a conversion with the given parameters
///\param name The name to use for logging the conversion.
///\param parameters The parameters, accepted are the following:
/// - input The input url
/// - output The output url
/// - encoder The encoder to use
/// - video An object containing video parameters, if not existant no video will be output. Values are:
/// - width The width of the resulting video
/// - height The height of the resulting video
/// - codec The codec to encode video in, or copy to use the current codec
/// - fpks The framerate in fps * 1000
/// - audio An object containing audio parameters, if not existant no audio will be output. Values are:
/// - codec The codec to encode audio in, or copy to use the current codec
/// - samplerate The target samplerate for the audio, in hz
void Converter::startConversion(std::string name, JSON::Value parameters) {
if (!parameters.isMember("input")) {
statusHistory[name] = "No input file supplied";
return;
}
if (!parameters.isMember("output")) {
statusHistory[name] = "No output file supplied";
return;
}
struct stat statBuf;
std::string outPath = parameters["output"].asString();
outPath = outPath.substr(0, outPath.rfind('/'));
int statRes = stat(outPath.c_str(), & statBuf);
if (statRes == -1 || !S_ISDIR(statBuf.st_mode)) {
statusHistory[name] = "Output path is either non-existent, or not a path.";
return;
}
if (!parameters.isMember("encoder")) {
statusHistory[name] = "No encoder specified";
return;
}
if (allCodecs.find(parameters["encoder"]) == allCodecs.end()) {
statusHistory[name] = "Can not find encoder " + parameters["encoder"].asString();
return;
}
if (parameters.isMember("video")) {
if (parameters["video"].isMember("width") && !parameters["video"].isMember("height")) {
statusHistory[name] = "No height parameter given";
return;
}
if (parameters["video"].isMember("height") && !parameters["video"].isMember("width")) {
statusHistory[name] = "No width parameter given";
return;
}
}
std::stringstream encoderCommand;
if (parameters["encoder"] == "ffmpeg") {
encoderCommand << "ffmpeg -i ";
encoderCommand << parameters["input"].asString() << " ";
if (parameters.isMember("video")) {
if (!parameters["video"].isMember("codec") || parameters["video"]["codec"] == "copy") {
encoderCommand << "-vcodec copy ";
} else {
codecInfo::iterator vidCodec = allCodecs["ffmpeg"].find(parameters["video"]["codec"]);
if (vidCodec == allCodecs["ffmpeg"].end()) {
statusHistory[name] = "Can not find video codec " + parameters["video"]["codec"].asString();
return;
}
encoderCommand << "-vcodec " << vidCodec->first << " ";
if (parameters["video"]["codec"].asString() == "h264") {
//Enforce baseline
encoderCommand << "-preset slow -profile:v baseline -level 30 ";
}
if (parameters["video"].isMember("fpks")) {
encoderCommand << "-r " << parameters["video"]["fpks"].asInt() / 1000 << " ";
}
if (parameters["video"].isMember("width")) {
encoderCommand << "-s " << parameters["video"]["width"].asInt() << "x" << parameters["video"]["height"].asInt() << " ";
}
///\todo Keyframe interval (different in older and newer versions of ffmpeg?)
}
} else {
encoderCommand << "-vn ";
}
if (parameters.isMember("audio")) {
if (!parameters["audio"].isMember("codec")) {
encoderCommand << "-acodec copy ";
} else {
codecInfo::iterator audCodec = allCodecs["ffmpeg"].find(parameters["audio"]["codec"]);
if (audCodec == allCodecs["ffmpeg"].end()) {
statusHistory[name] = "Can not find audio codec " + parameters["audio"]["codec"].asString();
return;
}
if (audCodec->second == "aac") {
encoderCommand << "-strict -2 ";
}
encoderCommand << "-acodec " << audCodec->first << " ";
if (parameters["audio"].isMember("samplerate")) {
encoderCommand << "-ar " << parameters["audio"]["samplerate"].asInt() << " ";
}
}
} else {
encoderCommand << "-an ";
}
encoderCommand << "-f flv -";
}
int statusFD = -1;
Util::Procs::StartPiped2(name, encoderCommand.str(), Util::getMyPath() + "MistFLV2DTSC -o " + parameters["output"].asString(), 0, 0, &statusFD, 0);
parameters["statusFD"] = statusFD;
allConversions[name] = parameters;
allConversions[name]["status"]["duration"] = "?";
allConversions[name]["status"]["progress"] = 0;
allConversions[name]["status"]["frame"] = 0;
allConversions[name]["status"]["time"] = 0;
}
///\brief Updates the internal status of the converter class.
///
///Will check for each running conversion whether it is still running, and update its status accordingly
void Converter::updateStatus() {
if (allConversions.size()) {
std::map<std::string, JSON::Value>::iterator cIt;
bool hasChanged = true;
while (hasChanged && allConversions.size()) {
hasChanged = false;
for (cIt = allConversions.begin(); cIt != allConversions.end(); cIt++) {
if (Util::Procs::isActive(cIt->first)) {
int statusFD = dup(cIt->second["statusFD"].asInt());
fsync(statusFD);
FILE * statusFile = fdopen(statusFD, "r");
char * fileBuf = 0;
size_t fileBufLen = 0;
fseek(statusFile, 0, SEEK_END);
std::string line;
int totalTime = 0;
do {
getdelim(&fileBuf, &fileBufLen, '\r', statusFile);
line = fileBuf;
if (line.find("Duration") != std::string::npos) {
int curOffset = line.find("Duration: ") + 10;
totalTime += atoi(line.substr(curOffset, 2).c_str()) * 60 * 60 * 1000;
totalTime += atoi(line.substr(curOffset + 3, 2).c_str()) * 60 * 1000;
totalTime += atoi(line.substr(curOffset + 6, 2).c_str()) * 1000;
totalTime += atoi(line.substr(curOffset + 9, 2).c_str()) * 10;
cIt->second["duration"] = totalTime;
}
} while (!feof(statusFile) && line.find("frame") != 0); //"frame" is the fist word on an actual status line of ffmpeg
if (!feof(statusFile)) {
cIt->second["status"] = parseFFMpegStatus(line);
cIt->second["status"]["duration"] = cIt->second["duration"];
cIt->second["status"]["progress"] = (cIt->second["status"]["time"].asInt() * 100) / cIt->second["duration"].asInt();
} else {
line.erase(line.end() - 1);
line = line.substr(line.rfind("\n") + 1);
cIt->second["status"] = line;
}
free(fileBuf);
fclose(statusFile);
} else {
if (statusHistory.find(cIt->first) == statusHistory.end()) {
statusHistory[cIt->first] = "Conversion successful, running DTSCFix";
Util::Procs::Start(cIt->first + "DTSCFix", Util::getMyPath() + "MistDTSCFix " + cIt->second["output"].asString());
}
allConversions.erase(cIt);
hasChanged = true;
break;
}
}
}
}
if (statusHistory.size()) {
std::map<std::string, std::string>::iterator sIt;
for (sIt = statusHistory.begin(); sIt != statusHistory.end(); sIt++) {
if (statusHistory[sIt->first].find("DTSCFix") != std::string::npos) {
if (Util::Procs::isActive(sIt->first + "DTSCFIX")) {
continue;
}
statusHistory[sIt->first] = "Conversion successful";
}
}
}
}
///\brief Parses a single ffmpeg status line into a JSON format
///\param statusLine The current status of ffmpeg
///\return A JSON::Value with the following values set:
/// - frame The current last encoded frame
/// - time The current last encoded timestamp
JSON::Value Converter::parseFFMpegStatus(std::string statusLine) {
JSON::Value result;
int curOffset = statusLine.find("frame=") + 6;
result["frame"] = atoi(statusLine.substr(curOffset, statusLine.find("fps=") - curOffset).c_str());
curOffset = statusLine.find("time=") + 5;
int myTime = 0;
myTime += atoi(statusLine.substr(curOffset, 2).c_str()) * 60 * 60 * 1000;
myTime += atoi(statusLine.substr(curOffset + 3, 2).c_str()) * 60 * 1000;
myTime += atoi(statusLine.substr(curOffset + 6, 2).c_str()) * 1000;
myTime += atoi(statusLine.substr(curOffset + 9, 2).c_str()) * 10;
result["time"] = myTime;
return result;
}
///\brief Obtain the current internal status of the conversion class
///\return A JSON::Value with the status of each conversion
JSON::Value Converter::getStatus() {
updateStatus();
JSON::Value result;
if (allConversions.size()) {
for (std::map<std::string, JSON::Value>::iterator cIt = allConversions.begin(); cIt != allConversions.end(); cIt++) {
result[cIt->first] = cIt->second["status"];
result[cIt->first]["details"] = cIt->second;
}
}
if (statusHistory.size()) {
std::map<std::string, std::string>::iterator sIt;
for (sIt = statusHistory.begin(); sIt != statusHistory.end(); sIt++) {
result[sIt->first] = sIt->second;
}
}
return result;
}
///\brief Clears the status history of all conversions
void Converter::clearStatus() {
statusHistory.clear();
}
}

View file

@ -1,35 +0,0 @@
#include <map>
#include <string>
#include "json.h"
///\brief A typedef to simplify accessing all codecs
typedef std::map<std::string, std::string> codecInfo;
///\brief A typedef to simplify accessing all encoders
typedef std::map<std::string, codecInfo> converterInfo;
///\brief A namespace containing all functions for handling the conversion API
namespace Converter {
///\brief A class containing the basic conversion API functionality
class Converter {
public:
Converter();
converterInfo & getCodecs();
JSON::Value getEncoders();
JSON::Value queryPath(std::string myPath);
void startConversion(std::string name, JSON::Value parameters);
void updateStatus();
JSON::Value getStatus();
void clearStatus();
JSON::Value parseFFMpegStatus(std::string statusLine);
private:
void fillFFMpegEncoders();
///\brief Holds a list of all current known codecs
converterInfo allCodecs;
///\brief Holds a list of all the current conversions
std::map<std::string, JSON::Value> allConversions;
///\brief Stores the status of all conversions, and the history
std::map<std::string, std::string> statusHistory;
};
}

View file

@ -1,318 +0,0 @@
#include "filesystem.h"
#include "defines.h"
Filesystem::Directory::Directory(std::string PathName, std::string BasePath) {
MyBase = BasePath;
if (PathName[0] == '/') {
PathName.erase(0, 1);
}
if (BasePath[BasePath.size() - 1] != '/') {
BasePath += "/";
}
MyPath = PathName;
FillEntries();
}
Filesystem::Directory::~Directory() {
}
void Filesystem::Directory::FillEntries() {
ValidDir = true;
struct stat StatBuf;
Entries.clear();
DIR * Dirp = opendir((MyBase + MyPath).c_str());
if (!Dirp) {
ValidDir = false;
} else {
dirent * entry;
while ((entry = readdir(Dirp))) {
if (stat((MyBase + MyPath + "/" + entry->d_name).c_str(), &StatBuf) == -1) {
DEBUG_MSG(DLVL_DEVEL, "Skipping %s, reason %s", entry->d_name, strerror(errno));
continue;
}
///Convert stat to string
Entries[std::string(entry->d_name)] = StatBuf;
}
}
}
void Filesystem::Directory::Print() {
/// \todo Remove? Libraries shouldn't print stuff.
if (!ValidDir) {
DEBUG_MSG(DLVL_ERROR, "%s is not a valid directory", (MyBase + MyPath).c_str());
return;
}
printf("%s:\n", (MyBase + MyPath).c_str());
for (std::map<std::string, struct stat>::iterator it = Entries.begin(); it != Entries.end(); it++) {
printf("\t%s\n", (*it).first.c_str());
}
printf("\n");
}
bool Filesystem::Directory::IsDir() {
return ValidDir;
}
std::string Filesystem::Directory::PWD() {
return "/" + MyPath;
}
std::string Filesystem::Directory::LIST(std::vector<std::string> ActiveStreams) {
FillEntries();
int MyPermissions;
std::stringstream Converter;
passwd * pwd; //For Username
group * grp; //For Groupname
tm * tm; //For time localisation
char datestring[256]; //For time localisation
std::string MyLoc = MyBase + MyPath;
if (MyLoc[MyLoc.size() - 1] != '/') {
MyLoc += "/";
}
for (std::map<std::string, struct stat>::iterator it = Entries.begin(); it != Entries.end(); it++) {
bool Active = (std::find(ActiveStreams.begin(), ActiveStreams.end(), (*it).first) != ActiveStreams.end());
if ((Active && (MyVisible[MyPath] & S_ACTIVE)) || ((!Active) && (MyVisible[MyPath] & S_INACTIVE)) || (((*it).second.st_mode / 010000) == 4)) {
if (((*it).second.st_mode / 010000) == 4) {
Converter << 'd';
} else {
Converter << '-';
}
MyPermissions = (((*it).second.st_mode % 010000) / 0100);
if (MyPermissions & 4) {
Converter << 'r';
} else {
Converter << '-';
}
if (MyPermissions & 2) {
Converter << 'w';
} else {
Converter << '-';
}
if (MyPermissions & 1) {
Converter << 'x';
} else {
Converter << '-';
}
MyPermissions = (((*it).second.st_mode % 0100) / 010);
if (MyPermissions & 4) {
Converter << 'r';
} else {
Converter << '-';
}
if (MyPermissions & 2) {
Converter << 'w';
} else {
Converter << '-';
}
if (MyPermissions & 1) {
Converter << 'x';
} else {
Converter << '-';
}
MyPermissions = ((*it).second.st_mode % 010);
if (MyPermissions & 4) {
Converter << 'r';
} else {
Converter << '-';
}
if (MyPermissions & 2) {
Converter << 'w';
} else {
Converter << '-';
}
if (MyPermissions & 1) {
Converter << 'x';
} else {
Converter << '-';
}
Converter << ' ';
Converter << (*it).second.st_nlink;
Converter << ' ';
if ((pwd = getpwuid((*it).second.st_uid))) {
Converter << pwd->pw_name;
} else {
Converter << (*it).second.st_uid;
}
Converter << ' ';
if ((grp = getgrgid((*it).second.st_gid))) {
Converter << grp->gr_name;
} else {
Converter << (*it).second.st_gid;
}
Converter << ' ';
Converter << (*it).second.st_size;
Converter << ' ';
tm = localtime(&((*it).second.st_mtime));
strftime(datestring, sizeof(datestring), "%b %d %H:%M", tm);
Converter << datestring;
Converter << ' ';
Converter << (*it).first;
Converter << '\n';
}
}
return Converter.str();
}
bool Filesystem::Directory::CWD(std::string Path) {
if (Path[0] == '/') {
Path.erase(0, 1);
MyPath = Path;
} else {
if (MyPath != "") {
MyPath += "/";
}
MyPath += Path;
}
FillEntries();
printf("New Path: %s\n", MyPath.c_str());
if (MyPermissions.find(MyPath) != MyPermissions.end()) {
printf("\tPermissions: %d\n", MyPermissions[MyPath]);
}
return SimplifyPath();
}
bool Filesystem::Directory::CDUP() {
return CWD("..");
}
std::string Filesystem::Directory::RETR(std::string Path) {
std::string Result;
std::string FileName;
if (Path[0] == '/') {
Path.erase(0, 1);
FileName = MyBase + Path;
} else {
FileName = MyBase + MyPath + "/" + Path;
}
std::ifstream File;
File.open(FileName.c_str());
while (File.good()) {
Result += File.get();
}
File.close();
return Result;
}
void Filesystem::Directory::STOR(std::string Path, std::string Data) {
if (MyPermissions.find(MyPath) == MyPermissions.end() || (MyPermissions[MyPath] & P_STOR)) {
std::string FileName;
if (Path[0] == '/') {
Path.erase(0, 1);
FileName = MyBase + Path;
} else {
FileName = MyBase + MyPath + "/" + Path;
}
std::ofstream File;
File.open(FileName.c_str());
File << Data;
File.close();
}
}
bool Filesystem::Directory::SimplifyPath() {
MyPath += "/";
std::vector<std::string> TempPath;
std::string TempString;
for (std::string::iterator it = MyPath.begin(); it != MyPath.end(); it++) {
if ((*it) == '/') {
if (TempString == "..") {
if (!TempPath.size()) {
return false;
}
TempPath.erase((TempPath.end() - 1));
} else if (TempString != "." && TempString != "") {
TempPath.push_back(TempString);
}
TempString = "";
} else {
TempString += (*it);
}
}
MyPath = "";
for (std::vector<std::string>::iterator it = TempPath.begin(); it != TempPath.end(); it++) {
MyPath += (*it);
if (it != (TempPath.end() - 1)) {
MyPath += "/";
}
}
if (MyVisible.find(MyPath) == MyVisible.end()) {
MyVisible[MyPath] = S_ALL;
}
return true;
}
bool Filesystem::Directory::DELE(std::string Path) {
if (MyPermissions.find(MyPath) == MyPermissions.end() || (MyPermissions[MyPath] & P_DELE)) {
std::string FileName;
if (Path[0] == '/') {
Path.erase(0, 1);
FileName = MyBase + Path;
} else {
FileName = MyBase + MyPath + "/" + Path;
}
if (std::remove(FileName.c_str())) {
DEBUG_MSG(DLVL_ERROR, "Removing file %s failed", FileName.c_str());
return false;
}
return true;
}
return false;
}
bool Filesystem::Directory::MKD(std::string Path) {
std::string FileName;
if (Path[0] == '/') {
Path.erase(0, 1);
FileName = MyBase + Path;
} else {
FileName = MyBase + MyPath + "/" + Path;
}
if (mkdir(FileName.c_str(), S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)) {
DEBUG_MSG(DLVL_ERROR, "Creating directory %s failed", FileName.c_str());
return false;
}
MyVisible[FileName] = S_ALL;
return true;
}
bool Filesystem::Directory::Rename(std::string From, std::string To) {
if (MyPermissions.find(MyPath) == MyPermissions.end() || (MyPermissions[MyPath] & P_RNFT)) {
std::string FileFrom;
if (From[0] == '/') {
From.erase(0, 1);
FileFrom = MyBase + From;
} else {
FileFrom = MyBase + MyPath + "/" + From;
}
std::string FileTo;
if (To[0] == '/') {
FileTo = MyBase + To;
} else {
FileTo = MyBase + MyPath + "/" + To;
}
if (std::rename(FileFrom.c_str(), FileTo.c_str())) {
DEBUG_MSG(DLVL_ERROR, "Renaming %s to %s failed", FileFrom.c_str(), FileTo.c_str());
return false;
}
return true;
}
return false;
}
void Filesystem::Directory::SetPermissions(std::string Path, char Permissions) {
MyPermissions[Path] = Permissions;
}
bool Filesystem::Directory::HasPermission(char Permission) {
if (MyPermissions.find(MyPath) == MyPermissions.end() || (MyPermissions[MyPath] & Permission)) {
return true;
}
return false;
}
void Filesystem::Directory::SetVisibility(std::string Pathname, char Visible) {
MyVisible[Pathname] = Visible;
}

View file

@ -1,67 +0,0 @@
#pragma once
#include <cstdio>
#include <string>
#include <cstring>
#include <vector>
#include <algorithm>
#include <map>
#include <sstream>
#include <fstream>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>
#include <locale.h>
#include <langinfo.h>
#include <stdint.h>
#include <errno.h>
namespace Filesystem {
enum DIR_Permissions {
P_LIST = 0x01, //List
P_RETR = 0x02, //Retrieve
P_STOR = 0x04, //Store
P_RNFT = 0x08, //Rename From/To
P_DELE = 0x10, //Delete
P_MKD = 0x20, //Make directory
P_RMD = 0x40, //Remove directory
};
enum DIR_Show {
S_NONE = 0x00, S_ACTIVE = 0x01, S_INACTIVE = 0x02, S_ALL = 0x03,
};
class Directory {
public:
Directory(std::string PathName = "", std::string BasePath = ".");
~Directory();
void Print();
bool IsDir();
std::string PWD();
std::string LIST(std::vector<std::string> ActiveStreams = std::vector<std::string>());
bool CWD(std::string Path);
bool CDUP();
bool DELE(std::string Path);
bool MKD(std::string Path);
std::string RETR(std::string Path);
void STOR(std::string Path, std::string Data);
bool Rename(std::string From, std::string To);
void SetPermissions(std::string PathName, char Permissions);
bool HasPermission(char Permission);
void SetVisibility(std::string Pathname, char Visible);
private:
bool ValidDir;
bool SimplifyPath();
void FillEntries();
std::string MyBase;
std::string MyPath;
std::map<std::string, struct stat> Entries;
std::map<std::string, char> MyPermissions;
std::map<std::string, char> MyVisible;
};
//Directory Class
}//Filesystem namespace

View file

@ -1,557 +0,0 @@
#include "ftp.h"
FTP::User::User(Socket::Connection NewConnection, std::map<std::string, std::string> Credentials) {
Conn = NewConnection;
MyPassivePort = 0;
USER = "";
PASS = "";
MODE = MODE_STREAM;
STRU = STRU_FILE;
TYPE = TYPE_ASCII_NONPRINT;
PORT = 20;
RNFR = "";
AllCredentials = Credentials;
MyDir = Filesystem::Directory("", FTPBasePath);
MyDir.SetPermissions("", Filesystem::P_LIST);
MyDir.SetPermissions("Unconverted", Filesystem::P_LIST | Filesystem::P_DELE | Filesystem::P_RNFT | Filesystem::P_STOR | Filesystem::P_RETR);
MyDir.SetPermissions("Converted", Filesystem::P_LIST | Filesystem::P_DELE | Filesystem::P_RNFT | Filesystem::P_RETR);
MyDir.SetPermissions("OnDemand", Filesystem::P_LIST | Filesystem::P_RETR);
MyDir.SetPermissions("Live", Filesystem::P_LIST);
MyDir.SetVisibility("Converted", Filesystem::S_INACTIVE);
MyDir.SetVisibility("OnDemand", Filesystem::S_ACTIVE);
}
FTP::User::~User() {
}
int FTP::User::ParseCommand(std::string Command) {
Commands ThisCmd = CMD_NOCMD;
if (Command.substr(0, 4) == "NOOP") {
ThisCmd = CMD_NOOP;
Command.erase(0, 5);
}
if (Command.substr(0, 4) == "USER") {
ThisCmd = CMD_USER;
Command.erase(0, 5);
}
if (Command.substr(0, 4) == "PASS") {
ThisCmd = CMD_PASS;
Command.erase(0, 5);
}
if (Command.substr(0, 4) == "QUIT") {
ThisCmd = CMD_QUIT;
Command.erase(0, 5);
}
if (Command.substr(0, 4) == "PORT") {
ThisCmd = CMD_PORT;
Command.erase(0, 5);
}
if (Command.substr(0, 4) == "RETR") {
ThisCmd = CMD_RETR;
Command.erase(0, 5);
}
if (Command.substr(0, 4) == "STOR") {
ThisCmd = CMD_STOR;
Command.erase(0, 5);
}
if (Command.substr(0, 4) == "TYPE") {
ThisCmd = CMD_TYPE;
Command.erase(0, 5);
}
if (Command.substr(0, 4) == "MODE") {
ThisCmd = CMD_MODE;
Command.erase(0, 5);
}
if (Command.substr(0, 4) == "STRU") {
ThisCmd = CMD_STRU;
Command.erase(0, 5);
}
if (Command.substr(0, 4) == "EPSV") {
ThisCmd = CMD_EPSV;
Command.erase(0, 5);
}
if (Command.substr(0, 4) == "PASV") {
ThisCmd = CMD_PASV;
Command.erase(0, 5);
}
if (Command.substr(0, 4) == "LIST") {
ThisCmd = CMD_LIST;
Command.erase(0, 5);
}
if (Command.substr(0, 4) == "CDUP") {
ThisCmd = CMD_CDUP;
Command.erase(0, 5);
}
if (Command.substr(0, 4) == "DELE") {
ThisCmd = CMD_DELE;
Command.erase(0, 5);
}
if (Command.substr(0, 4) == "RNFR") {
ThisCmd = CMD_RNFR;
Command.erase(0, 5);
}
if (Command.substr(0, 4) == "RNTO") {
ThisCmd = CMD_RNTO;
Command.erase(0, 5);
}
if (Command.substr(0, 3) == "PWD") {
ThisCmd = CMD_PWD;
Command.erase(0, 4);
}
if (Command.substr(0, 3) == "CWD") {
ThisCmd = CMD_CWD;
Command.erase(0, 4);
}
if (Command.substr(0, 3) == "RMD") {
ThisCmd = CMD_RMD;
Command.erase(0, 4);
}
if (Command.substr(0, 3) == "MKD") {
ThisCmd = CMD_MKD;
Command.erase(0, 4);
}
if (ThisCmd != CMD_RNTO) {
RNFR = "";
}
switch (ThisCmd) {
case CMD_NOOP: {
return 200; //Command okay.
break;
}
case CMD_USER: {
USER = "";
PASS = "";
if (Command == "") {
return 501;
} //Syntax error in parameters or arguments.
USER = Command;
return 331; //User name okay, need password.
break;
}
case CMD_PASS: {
if (USER == "") {
return 503;
} //Bad sequence of commands
if (Command == "") {
return 501;
} //Syntax error in parameters or arguments.
PASS = Command;
if (!LoggedIn()) {
USER = "";
PASS = "";
return 530; //Not logged in.
}
return 230;
break;
}
case CMD_LIST: {
Socket::Connection Connected = Passive.accept();
if (Connected.connected()) {
Conn.SendNow("125 Data connection already open; transfer starting.\n");
} else {
Conn.SendNow("150 File status okay; about to open data connection.\n");
}
while (!Connected.connected()) {
Connected = Passive.accept();
}
std::string tmpstr = MyDir.LIST(ActiveStreams);
Connected.SendNow(tmpstr);
Connected.close();
return 226;
break;
}
case CMD_QUIT: {
return 221; //Service closing control connection. Logged out if appropriate.
break;
}
case CMD_PORT: {
if (!LoggedIn()) {
return 530;
} //Not logged in.
if (Command == "") {
return 501;
} //Syntax error in parameters or arguments.
PORT = atoi(Command.c_str());
return 200; //Command okay.
break;
}
case CMD_EPSV: {
if (!LoggedIn()) {
return 530;
} //Not logged in.
MyPassivePort = (rand() % 9999);
Passive = Socket::Server(MyPassivePort, "0.0.0.0", true);
return 229;
break;
}
case CMD_PASV: {
if (!LoggedIn()) {
return 530;
} //Not logged in.
MyPassivePort = (rand() % 9999) + 49152;
Passive = Socket::Server(MyPassivePort, "0.0.0.0", true);
return 227;
break;
}
case CMD_RETR: {
if (!LoggedIn()) {
return 530;
} //Not logged in.
if (Command == "") {
return 501;
} //Syntax error in parameters or arguments.
if (!MyDir.HasPermission(Filesystem::P_RETR)) {
return 550;
} //Access denied.
Socket::Connection Connected = Passive.accept();
if (Connected.connected()) {
Conn.SendNow("125 Data connection already open; transfer starting.\n");
} else {
Conn.SendNow("150 File status okay; about to open data connection.\n");
}
while (!Connected.connected()) {
Connected = Passive.accept();
}
std::string tmpstr = MyDir.RETR(Command);
Connected.SendNow(tmpstr);
Connected.close();
return 226;
break;
}
case CMD_STOR: {
if (!LoggedIn()) {
return 530;
} //Not logged in.
if (Command == "") {
return 501;
} //Syntax error in parameters or arguments.
if (!MyDir.HasPermission(Filesystem::P_STOR)) {
return 550;
} //Access denied.
Socket::Connection Connected = Passive.accept();
if (Connected.connected()) {
Conn.SendNow("125 Data connection already open; transfer starting.\n");
} else {
Conn.SendNow("150 File status okay; about to open data connection.\n");
}
while (!Connected.connected()) {
Connected = Passive.accept();
}
std::string Buffer;
while (Connected.spool()) {
}
/// \todo Comment me back in. ^_^
//Buffer = Connected.Received();
MyDir.STOR(Command, Buffer);
return 250;
break;
}
case CMD_TYPE: {
if (!LoggedIn()) {
return 530;
} //Not logged in.
if (Command == "") {
return 501;
} //Syntax error in parameters or arguments.
if (Command.size() != 1 && Command.size() != 3) {
return 501;
} //Syntax error in parameters or arguments.
switch (Command[0]) {
case 'A': {
if (Command.size() > 1) {
if (Command[1] != ' ') {
return 501;
} //Syntax error in parameters or arguments.
if (Command[2] != 'N') {
return 504;
} //Command not implemented for that parameter.
}
TYPE = TYPE_ASCII_NONPRINT;
break;
}
case 'I': {
if (Command.size() > 1) {
if (Command[1] != ' ') {
return 501;
} //Syntax error in parameters or arguments.
if (Command[2] != 'N') {
return 504;
} //Command not implemented for that parameter.
}
TYPE = TYPE_IMAGE_NONPRINT;
break;
}
default: {
return 504; //Command not implemented for that parameter.
break;
}
}
return 200; //Command okay.
break;
}
case CMD_MODE: {
if (!LoggedIn()) {
return 530;
} //Not logged in.
if (Command == "") {
return 501;
} //Syntax error in parameters or arguments.
if (Command.size() != 1) {
return 501;
} //Syntax error in parameters or arguments.
if (Command[0] != 'S') {
return 504;
} //Command not implemented for that parameter.
MODE = MODE_STREAM;
return 200; //Command okay.
break;
}
case CMD_STRU: {
if (!LoggedIn()) {
return 530;
} //Not logged in.
if (Command == "") {
return 501;
} //Syntax error in parameters or arguments.
if (Command.size() != 1) {
return 501;
} //Syntax error in parameters or arguments.
switch (Command[0]) {
case 'F': {
STRU = STRU_FILE;
break;
}
case 'R': {
STRU = STRU_RECORD;
break;
}
default: {
return 504; //Command not implemented for that parameter.
break;
}
}
return 200; //Command okay.
break;
}
case CMD_PWD: {
if (!LoggedIn()) {
return 550;
} //Not logged in.
if (Command != "") {
return 501;
} //Syntax error in parameters or arguments.
return 2570; //257 -- 0 to indicate PWD over MKD
break;
}
case CMD_CWD: {
if (!LoggedIn()) {
return 530;
} //Not logged in.
Filesystem::Directory TmpDir = MyDir;
if (TmpDir.CWD(Command)) {
if (TmpDir.IsDir()) {
MyDir = TmpDir;
return 250;
}
}
return 550;
break;
}
case CMD_CDUP: {
if (!LoggedIn()) {
return 530;
} //Not logged in.
if (Command != "") {
return 501;
} //Syntax error in parameters or arguments.
Filesystem::Directory TmpDir = MyDir;
if (TmpDir.CDUP()) {
if (TmpDir.IsDir()) {
MyDir = TmpDir;
return 250;
}
}
return 550;
break;
}
case CMD_DELE: {
if (!LoggedIn()) {
return 530;
} //Not logged in.
if (Command == "") {
return 501;
} //Syntax error in parameters or arguments.
if (!MyDir.DELE(Command)) {
return 550;
}
return 250;
break;
}
case CMD_RMD: {
if (!LoggedIn()) {
return 530;
} //Not logged in.
if (Command == "") {
return 501;
} //Syntax error in parameters or arguments.
if (!MyDir.HasPermission(Filesystem::P_RMD)) {
return 550;
}
if (!MyDir.DELE(Command)) {
return 550;
}
return 250;
break;
}
case CMD_MKD: {
if (!LoggedIn()) {
return 530;
} //Not logged in.
if (Command == "") {
return 501;
} //Syntax error in parameters or arguments.
if (!MyDir.HasPermission(Filesystem::P_MKD)) {
return 550;
}
if (!MyDir.MKD(Command)) {
return 550;
}
return 2571;
break;
}
case CMD_RNFR: {
if (!LoggedIn()) {
return 530;
} //Not logged in.
if (Command == "") {
return 501;
} //Syntax error in parameters or arguments.
RNFR = Command;
return 350; //Awaiting further information
}
case CMD_RNTO: {
if (!LoggedIn()) {
return 530;
} //Not logged in.
if (Command == "") {
return 501;
} //Syntax error in parameters or arguments.
if (RNFR == "") {
return 503;
} //Bad sequence of commands
if (!MyDir.Rename(RNFR, Command)) {
return 550;
}
return 250;
}
default: {
return 502; //Command not implemented.
break;
}
}
}
bool FTP::User::LoggedIn() {
if (USER == "" || PASS == "") {
return false;
}
if (!AllCredentials.size()) {
return true;
}
if ((AllCredentials.find(USER) != AllCredentials.end()) && AllCredentials[USER] == PASS) {
return true;
}
return false;
}
std::string FTP::User::NumToMsg(int MsgNum) {
std::string Result;
switch (MsgNum) {
case 200: {
Result = "200 Message okay.\n";
break;
}
case 221: {
Result = "221 Service closing control connection. Logged out if appropriate.\n";
break;
}
case 226: {
Result = "226 Closing data connection.\n";
break;
}
case 227: {
std::stringstream sstr;
sstr << "227 Entering passive mode (0,0,0,0,";
sstr << (MyPassivePort >> 8) % 256;
sstr << ",";
sstr << MyPassivePort % 256;
sstr << ").\n";
Result = sstr.str();
break;
}
case 229: {
std::stringstream sstr;
sstr << "229 Entering extended passive mode (|||";
sstr << MyPassivePort;
sstr << "|).\n";
Result = sstr.str();
break;
}
case 230: {
Result = "230 User logged in, proceed.\n";
break;
}
case 250: {
Result = "250 Requested file action okay, completed.\n";
break;
}
case 2570: { //PWD
Result = "257 \"" + MyDir.PWD() + "\" selected as PWD\n";
break;
}
case 2571: { //MKD
Result = "257 \"" + MyDir.PWD() + "\" created\n";
break;
}
case 331: {
Result = "331 User name okay, need password.\n";
break;
}
case 350: {
Result = "350 Requested file action pending further information\n";
break;
}
case 501: {
Result = "501 Syntax error in parameters or arguments.\n";
break;
}
case 502: {
Result = "502 Command not implemented.\n";
break;
}
case 503: {
Result = "503 Bad sequence of commands.\n";
break;
}
case 504: {
Result = "504 Command not implemented for that parameter.\n";
break;
}
case 530: {
Result = "530 Not logged in.\n";
break;
}
case 550: {
Result = "550 Requested action not taken.\n";
break;
}
default: {
Result = "Error msg not implemented?\n";
break;
}
}
return Result;
}

View file

@ -1,82 +0,0 @@
#include <map>
#include <string>
#include <cstdlib>
#include <cstdio>
#include <iostream>
#include <sstream>
#include <string>
#include "./socket.h"
#include "./filesystem.h"
#include <unistd.h>
#include "./json.h"
namespace FTP {
static std::string FTPBasePath = "/tmp/mist/OnDemand/";
enum Mode {
MODE_STREAM,
};
//FTP::Mode enumeration
enum Structure {
STRU_FILE, STRU_RECORD,
};
//FTP::Structure enumeration
enum Type {
TYPE_ASCII_NONPRINT, TYPE_IMAGE_NONPRINT,
};
//FTP::Type enumeration
enum Commands {
CMD_NOCMD,
CMD_NOOP,
CMD_USER,
CMD_PASS,
CMD_QUIT,
CMD_PORT,
CMD_RETR,
CMD_STOR,
CMD_TYPE,
CMD_MODE,
CMD_STRU,
CMD_EPSV,
CMD_PASV,
CMD_LIST,
CMD_PWD,
CMD_CWD,
CMD_CDUP,
CMD_DELE,
CMD_RMD,
CMD_MKD,
CMD_RNFR,
CMD_RNTO,
};
//FTP::Commands enumeration
class User {
public:
User(Socket::Connection NewConnection, std::map<std::string, std::string> Credentials);
~User();
int ParseCommand(std::string Command);
bool LoggedIn();
std::string NumToMsg(int MsgNum);
Socket::Connection Conn;
private:
std::map<std::string, std::string> AllCredentials;
std::string USER;
std::string PASS;
Mode MODE;
Structure STRU;
Type TYPE;
int PORT;
Socket::Server Passive;
int MyPassivePort;
Filesystem::Directory MyDir;
std::string RNFR;
std::vector<std::string> ActiveStreams;
};
//FTP::User class
}//FTP Namespace

View file

@ -21,8 +21,7 @@
#include <stdio.h>
#include "timing.h"
std::map<pid_t, std::string> Util::Procs::plist;
std::map<pid_t, Util::TerminationNotifier> Util::Procs::exitHandlers;
std::set<pid_t> Util::Procs::plist;
bool Util::Procs::handler_set = false;
@ -39,7 +38,7 @@ static bool childRunning(pid_t p) {
}
/// sends sig 0 to process (pid). returns true if process is running
bool Util::Procs::isRunnning(pid_t pid){
bool Util::Procs::isRunning(pid_t pid){
return !kill(pid, 0);
}
@ -49,8 +48,8 @@ bool Util::Procs::isRunnning(pid_t pid){
/// all remaining children. Waits one more second for cleanup to finish, then exits.
void Util::Procs::exit_handler() {
int waiting = 0;
std::map<pid_t, std::string> listcopy = plist;
std::map<pid_t, std::string>::iterator it;
std::set<pid_t> listcopy = plist;
std::set<pid_t>::iterator it;
if (listcopy.empty()) {
return;
}
@ -58,7 +57,7 @@ void Util::Procs::exit_handler() {
//wait up to 0.5 second for applications to shut down
while (!listcopy.empty() && waiting <= 25) {
for (it = listcopy.begin(); it != listcopy.end(); it++) {
if (!childRunning((*it).first)) {
if (!childRunning(*it)) {
listcopy.erase(it);
break;
}
@ -76,8 +75,8 @@ void Util::Procs::exit_handler() {
//send sigint to all remaining
if (!listcopy.empty()) {
for (it = listcopy.begin(); it != listcopy.end(); it++) {
DEBUG_MSG(DLVL_DEVEL, "SIGINT %d: %s", (*it).first, (*it).second.c_str());
kill((*it).first, SIGINT);
DEBUG_MSG(DLVL_DEVEL, "SIGINT %d", *it);
kill(*it, SIGINT);
}
}
@ -86,7 +85,7 @@ void Util::Procs::exit_handler() {
//wait up to 5 seconds for applications to shut down
while (!listcopy.empty() && waiting <= 250) {
for (it = listcopy.begin(); it != listcopy.end(); it++) {
if (!childRunning((*it).first)) {
if (!childRunning(*it)) {
listcopy.erase(it);
break;
}
@ -104,8 +103,8 @@ void Util::Procs::exit_handler() {
//send sigkill to all remaining
if (!listcopy.empty()) {
for (it = listcopy.begin(); it != listcopy.end(); it++) {
DEBUG_MSG(DLVL_DEVEL, "SIGKILL %d: %s", (*it).first, (*it).second.c_str());
kill((*it).first, SIGKILL);
DEBUG_MSG(DLVL_DEVEL, "SIGKILL %d", *it);
kill(*it, SIGKILL);
}
}
@ -114,7 +113,7 @@ void Util::Procs::exit_handler() {
//wait up to 1 second for applications to shut down
while (!listcopy.empty() && waiting <= 50) {
for (it = listcopy.begin(); it != listcopy.end(); it++) {
if (!childRunning((*it).first)) {
if (!childRunning(*it)) {
listcopy.erase(it);
break;
}
@ -170,23 +169,15 @@ void Util::Procs::childsig_handler(int signum) {
return;
}
#if DEBUG >= DLVL_HIGH
std::string pname = plist[ret];
#endif
plist.erase(ret);
#if DEBUG >= DLVL_HIGH
if (!isActive(pname)) {
DEBUG_MSG(DLVL_HIGH, "Process %s fully terminated", pname.c_str());
DEBUG_MSG(DLVL_HIGH, "Process %d fully terminated", ret);
} else {
DEBUG_MSG(DLVL_HIGH, "Child process %d exited", ret);
}
#endif
if (exitHandlers.count(ret) > 0) {
TerminationNotifier tn = exitHandlers[ret];
exitHandlers.erase(ret);
tn(ret, exitcode);
}
}
}
@ -195,8 +186,8 @@ void Util::Procs::childsig_handler(int signum) {
std::string Util::Procs::getOutputOf(char * const * argv) {
std::string ret;
int fin = 0, fout = -1, ferr = 0;
StartPiped("output_getter", argv, &fin, &fout, &ferr);
while (isActive("output_getter")) {
pid_t myProc = StartPiped(argv, &fin, &fout, &ferr);
while (isActive(myProc)) {
Util::sleep(100);
}
FILE * outFile = fdopen(fout, "r");
@ -210,258 +201,12 @@ std::string Util::Procs::getOutputOf(char * const * argv) {
return ret;
}
/// Runs the given command and returns the stdout output as a string.
std::string Util::Procs::getOutputOf(std::string cmd) {
std::string ret;
int fin = 0, fout = -1, ferr = 0;
StartPiped("output_getter", cmd, &fin, &fout, &ferr);
while (isActive("output_getter")) {
Util::sleep(100);
}
FILE * outFile = fdopen(fout, "r");
char * fileBuf = 0;
size_t fileBufLen = 0;
while (!(feof(outFile) || ferror(outFile)) && (getline(&fileBuf, &fileBufLen, outFile) != -1)) {
ret += fileBuf;
}
free(fileBuf);
fclose(outFile);
return ret;
}
/// Attempts to run the command cmd.
/// Replaces the current process - use after forking first!
/// This function will never return - it will either run the given
/// command or kill itself with return code 42.
void Util::Procs::runCmd(std::string & cmd) {
//split cmd into arguments
//supports a maximum of 20 arguments
char * tmp = (char *)cmd.c_str();
char * tmp2 = 0;
char * args[21];
int i = 0;
tmp2 = strtok(tmp, " ");
args[0] = tmp2;
while (tmp2 != 0 && (i < 20)) {
tmp2 = strtok(0, " ");
++i;
args[i] = tmp2;
}
if (i == 20) {
args[20] = 0;
}
//execute the command
execvp(args[0], args);
DEBUG_MSG(DLVL_ERROR, "Error running %s: %s", cmd.c_str(), strerror(errno));
_exit(42);
}
/// Starts a new process if the name is not already active.
/// \return 0 if process was not started, process PID otherwise.
/// \arg name Name for this process - only used internally.
/// \arg cmd Commandline for this process.
pid_t Util::Procs::Start(std::string name, std::string cmd) {
if (isActive(name)) {
return getPid(name);
}
setHandler();
pid_t ret = fork();
if (ret == 0) {
runCmd(cmd);
} else {
if (ret > 0) {
DEBUG_MSG(DLVL_HIGH, "Process %s started, PID %d: %s", name.c_str(), ret, cmd.c_str());
plist.insert(std::pair<pid_t, std::string>(ret, name));
} else {
DEBUG_MSG(DLVL_ERROR, "Process %s could not be started: fork() failed", name.c_str());
return 0;
}
}
return ret;
}
/// Starts two piped processes if the name is not already active.
/// \return 0 if process was not started, sub (sending) process PID otherwise.
/// \arg name Name for this process - only used internally.
/// \arg cmd Commandline for sub (sending) process.
/// \arg cmd2 Commandline for main (receiving) process.
pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2) {
if (isActive(name)) {
return getPid(name);
}
setHandler();
int pfildes[2];
if (pipe(pfildes) == -1) {
DEBUG_MSG(DLVL_ERROR, "Process %s could not be started. Pipe creation failed.", name.c_str());
return 0;
}
int devnull = open("/dev/null", O_RDWR);
pid_t ret = fork();
if (ret == 0) {
close(pfildes[0]);
dup2(pfildes[1], STDOUT_FILENO);
close(pfildes[1]);
dup2(devnull, STDIN_FILENO);
dup2(devnull, STDERR_FILENO);
runCmd(cmd);
} else {
if (ret > 0) {
plist.insert(std::pair<pid_t, std::string>(ret, name));
} else {
DEBUG_MSG(DLVL_ERROR, "Process %s could not be started. fork() failed.", name.c_str());
close(pfildes[1]);
close(pfildes[0]);
return 0;
}
}
pid_t ret2 = fork();
if (ret2 == 0) {
close(pfildes[1]);
dup2(pfildes[0], STDIN_FILENO);
close(pfildes[0]);
dup2(devnull, STDOUT_FILENO);
dup2(devnull, STDERR_FILENO);
runCmd(cmd2);
} else {
if (ret2 > 0) {
DEBUG_MSG(DLVL_HIGH, "Process %s started, PIDs (%d, %d): %s | %s", name.c_str(), ret, ret2, cmd.c_str(), cmd2.c_str());
plist.insert(std::pair<pid_t, std::string>(ret2, name));
} else {
DEBUG_MSG(DLVL_ERROR, "Process %s could not be started. fork() failed.", name.c_str());
Stop(name);
close(pfildes[1]);
close(pfildes[0]);
return 0;
}
}
close(pfildes[1]);
close(pfildes[0]);
return ret;
}
/// Starts three piped processes if the name is not already active.
/// \return 0 if process was not started, sub (sending) process PID otherwise.
/// \arg name Name for this process - only used internally.
/// \arg cmd Commandline for sub (sending) process.
/// \arg cmd2 Commandline for sub (middle) process.
/// \arg cmd3 Commandline for main (receiving) process.
pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2, std::string cmd3) {
if (isActive(name)) {
return getPid(name);
}
setHandler();
int pfildes[2];
int pfildes2[2];
if (pipe(pfildes) == -1) {
DEBUG_MSG(DLVL_ERROR, "Process %s could not be started. Pipe creation failed.", name.c_str());
return 0;
}
if (pipe(pfildes2) == -1) {
DEBUG_MSG(DLVL_ERROR, "Process %s could not be started. Pipe creation failed.", name.c_str());
return 0;
}
int devnull = open("/dev/null", O_RDWR);
pid_t ret = fork();
if (ret == 0) {
close(pfildes[0]);
dup2(pfildes[1], STDOUT_FILENO);
close(pfildes[1]);
dup2(devnull, STDIN_FILENO);
dup2(devnull, STDERR_FILENO);
close(pfildes2[1]);
close(pfildes2[0]);
runCmd(cmd);
} else {
if (ret > 0) {
plist.insert(std::pair<pid_t, std::string>(ret, name));
} else {
DEBUG_MSG(DLVL_ERROR, "Process %s could not be started. fork() failed.", name.c_str());
close(pfildes[1]);
close(pfildes[0]);
close(pfildes2[1]);
close(pfildes2[0]);
return 0;
}
}
pid_t ret2 = fork();
if (ret2 == 0) {
close(pfildes[1]);
close(pfildes2[0]);
dup2(pfildes[0], STDIN_FILENO);
close(pfildes[0]);
dup2(pfildes2[1], STDOUT_FILENO);
close(pfildes2[1]);
dup2(devnull, STDERR_FILENO);
runCmd(cmd2);
} else {
if (ret2 > 0) {
DEBUG_MSG(DLVL_HIGH, "Process %s started, PIDs (%d, %d): %s | %s", name.c_str(), ret, ret2, cmd.c_str(), cmd2.c_str());
plist.insert(std::pair<pid_t, std::string>(ret2, name));
} else {
DEBUG_MSG(DLVL_ERROR, "Process %s could not be started. fork() failed.", name.c_str());
Stop(name);
close(pfildes[1]);
close(pfildes[0]);
close(pfildes2[1]);
close(pfildes2[0]);
return 0;
}
}
close(pfildes[1]);
close(pfildes[0]);
pid_t ret3 = fork();
if (ret3 == 0) {
close(pfildes[1]);
close(pfildes[0]);
close(pfildes2[1]);
dup2(pfildes2[0], STDIN_FILENO);
close(pfildes2[0]);
dup2(devnull, STDOUT_FILENO);
dup2(devnull, STDERR_FILENO);
runCmd(cmd3);
} else {
if (ret3 > 0) {
DEBUG_MSG(DLVL_HIGH, "Process %s started, PIDs (%d, %d, %d): %s | %s | %s", name.c_str(), ret, ret2, ret3, cmd.c_str(), cmd2.c_str(), cmd3.c_str());
plist.insert(std::pair<pid_t, std::string>(ret3, name));
} else {
DEBUG_MSG(DLVL_ERROR, "Process %s could not be started. fork() failed.", name.c_str());
Stop(name);
close(pfildes[1]);
close(pfildes[0]);
close(pfildes2[1]);
close(pfildes2[0]);
return 0;
}
}
return ret3;
}
/// Starts a new process with given fds if the name is not already active.
/// \return 0 if process was not started, process PID otherwise.
/// \arg name Name for this process - only used internally.
/// \arg argv Command for this process.
/// \arg fdin Standard input file descriptor. If null, /dev/null is assumed. Otherwise, if arg contains -1, a new fd is automatically allocated and written into this arg. Then the arg will be used as fd.
/// \arg fdout Same as fdin, but for stdout.
/// \arg fdout Same as fdin, but for stderr.
pid_t Util::Procs::StartPiped(std::string name, char * const * argv, int * fdin, int * fdout, int * fderr) {
if (isActive(name)) {
DEBUG_MSG(DLVL_WARN, "Process %s already active - skipping start", name.c_str());
return getPid(name);
}
int pidtemp = StartPiped(argv, fdin, fdout, fderr);
if (pidtemp > 0) {
plist.insert(std::pair<pid_t, std::string>(pidtemp, name));
}
return pidtemp;
}
pid_t Util::Procs::StartPiped(char * const * argv, int * fdin, int * fdout, int * fderr) {
pid_t pid;
int pipein[2], pipeout[2], pipeerr[2];
@ -574,6 +319,7 @@ pid_t Util::Procs::StartPiped(char * const * argv, int * fdin, int * fdout, int
}
return 0;
} else { //parent
plist.insert(pid);
DEBUG_MSG(DLVL_HIGH, "Piped process %s started, PID %d", argv[0], pid);
if (devnull != -1) {
close(devnull);
@ -594,70 +340,6 @@ pid_t Util::Procs::StartPiped(char * const * argv, int * fdin, int * fdout, int
return pid;
}
/// Starts a new process with given fds if the name is not already active.
/// \return 0 if process was not started, process PID otherwise.
/// \arg name Name for this process - only used internally.
/// \arg cmd Command for this process.
/// \arg fdin Standard input file descriptor. If null, /dev/null is assumed. Otherwise, if arg contains -1, a new fd is automatically allocated and written into this arg. Then the arg will be used as fd.
/// \arg fdout Same as fdin, but for stdout.
/// \arg fdout Same as fdin, but for stderr.
pid_t Util::Procs::StartPiped(std::string name, std::string cmd, int * fdin, int * fdout, int * fderr) {
//Convert the given command to a char * []
char * tmp = (char *)cmd.c_str();
char * tmp2 = 0;
char * args[21];
int i = 0;
tmp2 = strtok(tmp, " ");
args[0] = tmp2;
while (tmp2 != 0 && (i < 20)) {
tmp2 = strtok(0, " ");
++i;
args[i] = tmp2;
}
if (i == 20) {
args[20] = 0;
}
return StartPiped(name, args, fdin, fdout, fderr);
}
pid_t Util::Procs::StartPiped2(std::string name, std::string cmd1, std::string cmd2, int * fdin, int * fdout, int * fderr1, int * fderr2) {
int pfildes[2];
if (pipe(pfildes) == -1) {
DEBUG_MSG(DLVL_ERROR, "Pipe creation failed for process %s", name.c_str());
return 0;
}
pid_t res1 = StartPiped(name, cmd1, fdin, &pfildes[1], fderr1);
if (!res1) {
close(pfildes[1]);
close(pfildes[0]);
return 0;
}
pid_t res2 = StartPiped(name + "receiving", cmd2, &pfildes[0], fdout, fderr2);
if (!res2) {
Stop(res1);
close(pfildes[1]);
close(pfildes[0]);
return 0;
}
//we can close these because the fork in StartPiped() copies them.
close(pfildes[1]);
close(pfildes[0]);
return res1;
}
/// Stops the named process, if running.
/// \arg name (Internal) name of process to stop
void Util::Procs::Stop(std::string name) {
int max = 5;
while (isActive(name)) {
Stop(getPid(name));
max--;
if (max <= 0) {
return;
}
}
}
/// Stops the process with this pid, if running.
/// \arg name The PID of the process to stop.
void Util::Procs::Stop(pid_t name) {
@ -672,10 +354,10 @@ void Util::Procs::Murder(pid_t name) {
/// (Attempts to) stop all running child processes.
void Util::Procs::StopAll() {
std::map<pid_t, std::string> listcopy = plist;
std::map<pid_t, std::string>::iterator it;
std::set<pid_t> listcopy = plist;
std::set<pid_t>::iterator it;
for (it = listcopy.begin(); it != listcopy.end(); it++) {
Stop((*it).first);
Stop(*it);
}
}
@ -684,54 +366,8 @@ int Util::Procs::Count() {
return plist.size();
}
/// Returns true if a process by this name is currently active.
bool Util::Procs::isActive(std::string name) {
std::map<pid_t, std::string> listcopy = plist;
std::map<pid_t, std::string>::iterator it;
for (it = listcopy.begin(); it != listcopy.end(); it++) {
if ((*it).second == name) {
if (childRunning((*it).first)) {
return true;
} else {
plist.erase((*it).first);
}
}
}
return false;
}
/// Returns true if a process with this PID is currently active.
bool Util::Procs::isActive(pid_t name) {
return (plist.count(name) == 1) && (kill(name, 0) == 0);
}
/// Gets PID for this named process, if active.
/// \return NULL if not active, process PID otherwise.
pid_t Util::Procs::getPid(std::string name) {
std::map<pid_t, std::string>::iterator it;
for (it = plist.begin(); it != plist.end(); it++) {
if ((*it).second == name) {
return (*it).first;
}
}
return 0;
}
/// Gets name for this process PID, if active.
/// \return Empty string if not active, name otherwise.
std::string Util::Procs::getName(pid_t name) {
if (plist.count(name) == 1) {
return plist[name];
}
return "";
}
/// Registers one notifier function for when a process indentified by PID terminates.
/// \return true if the notifier could be registered, false otherwise.
bool Util::Procs::SetTerminationNotifier(pid_t pid, TerminationNotifier notifier) {
if (plist.find(pid) != plist.end()) {
exitHandlers[pid] = notifier;
return true;
}
return false;
}

View file

@ -4,19 +4,16 @@
#pragma once
#include <unistd.h>
#include <string>
#include <map>
#include <set>
#include <vector>
/// Contains utility code, not directly related to streaming media
namespace Util {
typedef void (*TerminationNotifier)(pid_t pid, int exitCode);
/// Deals with spawning, monitoring and stopping child processes
class Procs {
private:
static std::map<pid_t, std::string> plist; ///< Holds active processes
static std::map<pid_t, TerminationNotifier> exitHandlers; ///< termination function, if any
static std::set<pid_t> plist; ///< Holds active processes
static bool handler_set; ///< If true, the sigchld handler has been setup.
static void childsig_handler(int signum);
static void exit_handler();
@ -24,25 +21,13 @@ namespace Util {
static void setHandler();
public:
static std::string getOutputOf(char * const * argv);
static std::string getOutputOf(std::string cmd);
static pid_t Start(std::string name, std::string cmd);
static pid_t Start(std::string name, std::string cmd, std::string cmd2);
static pid_t Start(std::string name, std::string cmd, std::string cmd2, std::string cmd3);
static pid_t StartPiped(char * const * argv, int * fdin, int * fdout, int * fderr);
static pid_t StartPiped(std::string name, char * const * argv, int * fdin, int * fdout, int * fderr);
static pid_t StartPiped(std::string name, std::string cmd, int * fdin, int * fdout, int * fderr);
static pid_t StartPiped2(std::string name, std::string cmd1, std::string cmd2, int * fdin, int * fdout, int * fderr1, int * fderr2);
static void Stop(std::string name);
static void Stop(pid_t name);
static void Murder(pid_t name);
static void StopAll();
static int Count();
static bool isActive(std::string name);
static bool isActive(pid_t name);
static bool isRunnning(pid_t pid);
static pid_t getPid(std::string name);
static std::string getName(pid_t name);
static bool SetTerminationNotifier(pid_t pid, TerminationNotifier notifier);
static bool isRunning(pid_t pid);
};
}

View file

@ -305,12 +305,12 @@ namespace IPC {
} while (i < 10 && !handle && autoBackoff);
}
if (!handle) {
FAIL_MSG("%s for page %s failed: %s", (master ? "CreateFileMapping" : "OpenFileMapping"), name.c_str(), strerror(errno));
FAIL_MSG("%s for page %s failed with error code %u", (master ? "CreateFileMapping" : "OpenFileMapping"), name.c_str(), GetLastError());
return;
}
mapped = (char *)MapViewOfFile(handle, FILE_MAP_ALL_ACCESS, 0, 0, 0);
if (!mapped) {
FAIL_MSG("MapViewOfFile for page %s failed: %s", name.c_str(), strerror(errno));
FAIL_MSG("MapViewOfFile for page %s failed with error code %u", name.c_str(), GetLastError());
return;
}
//Under cygwin, the extra 4 bytes contain the real size of the page.
@ -740,7 +740,7 @@ namespace IPC {
DEBUG_MSG(DLVL_VERYHIGH, "Shared memory %s is now at count %u", baseName.c_str(), amount);
}
unsigned short tmpPID = *((unsigned short *)(it->mapped+1+offset+payLen-2));
if(!Util::Procs::isRunnning(tmpPID) && !(*counter == 126 || *counter == 127 || *counter == 254 || *counter == 255)){
if(!Util::Procs::isRunning(tmpPID) && !(*counter == 126 || *counter == 127 || *counter == 254 || *counter == 255)){
WARN_MSG("process disappeared, timing out. (pid %d)", tmpPID);
*counter = 126; //if process is already dead, instant timeout.
}

View file

@ -1,109 +0,0 @@
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <string>
#include <mist/json.h>
#include <mist/dtsc.h>
#include <mist/procs.h>
#include <mist/timing.h>
namespace Info {
int getInfo(int argc, char* argv[]) {
if (argc < 2){
fprintf( stderr, "Usage: %s <filename>\n", argv[0] );
return 1;
}
DTSC::File F(argv[1]);
JSON::Value fileSpecs = F.getMeta().toJSON();
if( !fileSpecs ) {
char ** cmd = (char**)malloc(3*sizeof(char*));
cmd[0] = (char*)"ffprobe";
cmd[1] = argv[1];
cmd[2] = NULL;
int outFD = -1;
Util::Procs::StartPiped("FFProbe", cmd, 0, 0, &outFD);
while( Util::Procs::isActive("FFProbe")){ Util::sleep(100); }
FILE * outFile = fdopen( outFD, "r" );
char * fileBuf = 0;
size_t fileBufLen = 0;
while ( !(feof(outFile) || ferror(outFile)) && (getline(&fileBuf, &fileBufLen, outFile) != -1)){
std::string line = fileBuf;
if (line.find("Input") != std::string::npos){
std::string tmp = line.substr(line.find(", ") + 2);
fileSpecs["format"] = tmp.substr(0, tmp.find(","));
}
if (line.find("Duration") != std::string::npos){
std::string tmp = line.substr(line.find(": ", line.find("Duration")) + 2);
tmp = tmp.substr(0, tmp.find(","));
int length = (((atoi(tmp.substr(0,2).c_str()) * 60) + atoi(tmp.substr(3,2).c_str())) * 60) + atoi(tmp.substr(6,2).c_str());
fileSpecs["length"] = length;
length *= 100;
length += atoi(tmp.substr(9,2).c_str());
fileSpecs["lastms"] = length * 10;
}
if (line.find("bitrate") != std::string::npos ){
std::string tmp = line.substr(line.find(": ", line.find("bitrate")) + 2);
fileSpecs["bps"] = atoi(tmp.substr(0, tmp.find(" ")).c_str()) * 128;
}
if (line.find("Stream") != std::string::npos ){
std::string tmp = line.substr(line.find(" ", line.find("Stream")) + 1);
int strmIdx = fileSpecs["streams"].size();
int curPos = 0;
curPos = tmp.find(": ", curPos) + 2;
fileSpecs["streams"][strmIdx]["type"] = tmp.substr(curPos, tmp.find(":", curPos) - curPos);
curPos = tmp.find(":", curPos) + 2;
fileSpecs["streams"][strmIdx]["codec"] = tmp.substr(curPos, tmp.find_first_of(", ", curPos) - curPos);
curPos = tmp.find(",", curPos) + 2;
if (fileSpecs["streams"][strmIdx]["type"] == "Video"){
fileSpecs["streams"][strmIdx]["encoding"] = tmp.substr(curPos, tmp.find(",", curPos) - curPos);
curPos = tmp.find(",", curPos) + 2;
fileSpecs["streams"][strmIdx]["width"] = atoi(tmp.substr(curPos, tmp.find("x", curPos) - curPos).c_str());
curPos = tmp.find("x", curPos) + 1;
fileSpecs["streams"][strmIdx]["height"] = atoi(tmp.substr(curPos, tmp.find(",", curPos) - curPos).c_str());
curPos = tmp.find(",", curPos) + 2;
fileSpecs["streams"][strmIdx]["bps"] = atoi(tmp.substr(curPos, tmp.find(" ", curPos) - curPos).c_str()) * 128;
curPos = tmp.find(",", curPos) + 2;
fileSpecs["streams"][strmIdx]["fpks"] = (int)(atof(tmp.substr(curPos, tmp.find(" ", curPos) - curPos).c_str()) * 1000);
fileSpecs["streams"][strmIdx].removeMember( "type" );
fileSpecs["video"] = fileSpecs["streams"][strmIdx];
}else if (fileSpecs["streams"][strmIdx]["type"] == "Audio"){
fileSpecs["streams"][strmIdx]["samplerate"] = atoi(tmp.substr(curPos, tmp.find(" ", curPos) - curPos).c_str());
curPos = tmp.find(",", curPos) + 2;
if (tmp.substr(curPos, tmp.find(",", curPos) - curPos) == "stereo"){
fileSpecs["streams"][strmIdx]["channels"] = 2;
}else if (tmp.substr(curPos, tmp.find(",", curPos) - curPos) == "mono"){
fileSpecs["streams"][strmIdx]["channels"] = 1;
}else{
fileSpecs["streams"][strmIdx]["channels"] = tmp.substr(curPos, tmp.find(",", curPos) - curPos);
}
curPos = tmp.find(",", curPos) + 2;
fileSpecs["streams"][strmIdx]["samplewidth"] = tmp.substr(curPos, tmp.find(",", curPos) - curPos);
curPos = tmp.find(",", curPos) + 2;
fileSpecs["streams"][strmIdx]["bps"] = atoi(tmp.substr(curPos, tmp.find(" ", curPos) - curPos).c_str()) * 128;
fileSpecs["streams"][strmIdx].removeMember( "type" );
fileSpecs["audio"] = fileSpecs["streams"][strmIdx];
}
}
}
fclose( outFile );
fileSpecs.removeMember( "streams" );
} else {
fileSpecs["format"] = "dtsc";
JSON::Value tracks = fileSpecs["tracks"];
for(JSON::ObjIter trackIt = tracks.ObjBegin(); trackIt != tracks.ObjEnd(); trackIt++){
fileSpecs["tracks"][trackIt->first].removeMember("fragments");
fileSpecs["tracks"][trackIt->first].removeMember("keys");
fileSpecs["tracks"][trackIt->first].removeMember("keysizes");
fileSpecs["tracks"][trackIt->first].removeMember("parts");
fileSpecs["tracks"][trackIt->first].removeMember("ivecs");/*LTS*/
}
}
printf( "%s", fileSpecs.toString().c_str() );
return 0;
}
}
int main(int argc, char* argv[]) {
return Info::getInfo(argc, argv);
}

View file

@ -18,21 +18,16 @@
///\brief Holds everything unique to the controller.
namespace Controller {
static std::map<long long, std::string> currentConnectors; ///<The currently running connectors.
static inline std::string toConn(long long i){
return std::string("Conn") + JSON::Value(i).asString();
}
static std::map<std::string, pid_t> currentConnectors; ///<The currently running connectors.
///\brief Checks if the binary mentioned in the protocol argument is currently active, if so, restarts it.
///\param protocol The protocol to check.
void UpdateProtocol(std::string protocol){
std::map<long long, std::string>::iterator iter;
std::map<std::string, pid_t>::iterator iter;
for (iter = currentConnectors.begin(); iter != currentConnectors.end(); iter++){
if (iter->second.substr(0, protocol.size()) == protocol){
Log("CONF", "Killing connector for update: " + iter->second);
Util::Procs::Stop(toConn(iter->first));
if (iter->first.substr(0, protocol.size()) == protocol){
Log("CONF", "Killing connector for update: " + iter->first);
Util::Procs::Stop(iter->second);
}
}
}
@ -65,7 +60,8 @@ namespace Controller {
}
}
static inline void buildPipedArguments(JSON::Value & p, char * argarr[], JSON::Value & capabilities){
static inline void buildPipedArguments(const std::string & proto, char * argarr[], JSON::Value & capabilities){
JSON::Value p = JSON::fromString(proto);
int argnum = 0;
static std::string tmparg;
tmparg = Util::getMyPath() + std::string("MistOut") + p["connector"].asStringRef();
@ -82,12 +78,11 @@ namespace Controller {
if (pipedCapa.isMember("optional")){builPipedPart(p, argarr, argnum, pipedCapa["optional"]);}
}
///\brief Checks current protocol coguration, updates state of enabled connectors if neccesary.
///\brief Checks current protocol configuration, updates state of enabled connectors if neccessary.
///\param p An object containing all protocols.
///\param capabilities An object containing the detected capabilities.
void CheckProtocols(JSON::Value & p, JSON::Value & capabilities){
std::map<long long, std::string> new_connectors;
std::map<long long, std::string>::iterator iter;
std::set<std::string> runningConns;
// used for building args
int zero = 0;
@ -102,12 +97,13 @@ namespace Controller {
for (JSON::ArrIter ait = p.ArrBegin(); ait != p.ArrEnd(); ait++){
counter = ait - p.ArrBegin();
std::string prevOnline = ( *ait)["online"].asString();
#define connName (*ait)["connector"].asStringRef()
const std::string & connName = (*ait)["connector"].asStringRef();
//do not further parse if there's no connector name
if ( !(*ait).isMember("connector") || connName == ""){
( *ait)["online"] = "Missing connector name";
continue;
}
//ignore connectors that are not installed
if ( !capabilities["connectors"].isMember(connName)){
( *ait)["online"] = "Not installed";
if (( *ait)["online"].asString() != prevOnline){
@ -115,14 +111,13 @@ namespace Controller {
}
continue;
}
#define connCapa capabilities["connectors"][connName]
//list connectors that go through HTTP as 'enabled' without actually running them.
JSON::Value & connCapa = capabilities["connectors"][connName];
if (connCapa.isMember("socket") || (connCapa.isMember("deps") && connCapa["deps"].asStringRef() == "HTTP")){
( *ait)["online"] = "Enabled";
continue;
}
//check required parameters, skip if anything is missing
if (connCapa.isMember("required")){
bool gotAll = true;
for (JSON::ObjIter it = connCapa["required"].ObjBegin(); it != connCapa["required"].ObjEnd(); ++it){
@ -137,12 +132,13 @@ namespace Controller {
}
if (!gotAll){continue;}
}
//remove current online status
( *ait).removeMember("online");
/// \todo Check dependencies?
new_connectors[counter] = (*ait).toString();
if (Util::Procs::isActive(toConn(counter))){
//set current online status
std::string myCmd = (*ait).toString();
runningConns.insert(myCmd);
if (currentConnectors.count(myCmd) && Util::Procs::isActive(currentConnectors[myCmd])){
( *ait)["online"] = 1;
}else{
( *ait)["online"] = 0;
@ -150,28 +146,28 @@ namespace Controller {
}
//shut down deleted/changed connectors
for (iter = currentConnectors.begin(); iter != currentConnectors.end(); iter++){
if (new_connectors.count(iter->first) != 1 || new_connectors[iter->first] != iter->second){
Log("CONF", "Stopping connector " + iter->second);
Util::Procs::Stop(toConn(iter->first));
std::map<std::string, pid_t>::iterator it;
for (it = currentConnectors.begin(); it != currentConnectors.end(); it++){
if (!runningConns.count(it->first)){
Log("CONF", "Stopping connector " + it->first);
Util::Procs::Stop(it->second);
}
}
//start up new/changed connectors
for (iter = new_connectors.begin(); iter != new_connectors.end(); iter++){
if (currentConnectors.count(iter->first) != 1 || currentConnectors[iter->first] != iter->second || !Util::Procs::isActive(toConn(iter->first))){
Log("CONF", "Starting connector: " + iter->second);
while (runningConns.size() && conf.is_active){
if (!currentConnectors.count(*runningConns.begin()) || !Util::Procs::isActive(currentConnectors[*runningConns.begin()])){
Log("CONF", "Starting connector: " + *runningConns.begin());
// clear out old args
for (i=0; i<15; i++){argarr[i] = 0;}
// get args for this connector
buildPipedArguments(p[(long long unsigned)iter->first], (char **)&argarr, capabilities);
buildPipedArguments(*runningConns.begin(), (char **)&argarr, capabilities);
// start piped w/ generated args
Util::Procs::StartPiped(toConn(iter->first), argarr, &zero, &out, &err);//redirects output to out. Must make a new pipe, redirect std err
currentConnectors[*runningConns.begin()] = Util::Procs::StartPiped(argarr, &zero, &out, &err);
}
runningConns.erase(runningConns.begin());
}
//store new state
currentConnectors = new_connectors;
}
}

View file

@ -130,60 +130,35 @@ namespace Controller {
// if the file isn't dtsc and there's no dtsh file, run getStream on it
// this guarantees that if the stream is playable, it now has a valid header.
DEBUG_MSG(DLVL_INSANE, "(re)loading metadata for stream %s", name.c_str());
if ((URL.substr(URL.size() - 5) != ".dtsc") && (stat((URL+".dtsh").c_str(), &fileinfo) != 0)){
DEBUG_MSG(DLVL_INSANE, "Stream %s is non-DTSC file without DTSH. Opening stream to generate DTSH...", name.c_str());
Util::startInput(name);
DEBUG_MSG(DLVL_INSANE, "Waiting for stream %s to open...", name.c_str());
//wait for the stream
{
char streamPageName[NAME_BUFFER_SIZE];
snprintf(streamPageName, NAME_BUFFER_SIZE, SHM_STREAM_INDEX, name.c_str());
IPC::sharedPage streamIndex(streamPageName, DEFAULT_META_PAGE_SIZE, false, false);
if (!streamIndex.mapped){
DEBUG_MSG(DLVL_INSANE, "Stream %s opening failed! Cancelling and marking as corrupt.", name.c_str());
data["meta"].null();
data["meta"]["tracks"].null();
data["error"] = "Stream offline: Corrupt file?";
if (data["error"].asStringRef() != prevState){
Log("WARN", "Source file " + URL + " seems to be corrupt.");
}
data["online"] = 0;
return;
Util::startInput(name);
DEBUG_MSG(DLVL_INSANE, "Waiting for stream %s to open...", name.c_str());
//wait for the stream
{
char streamPageName[NAME_BUFFER_SIZE];
snprintf(streamPageName, NAME_BUFFER_SIZE, SHM_STREAM_INDEX, name.c_str());
IPC::sharedPage streamIndex(streamPageName, DEFAULT_META_PAGE_SIZE, false, false);
if (!streamIndex.mapped){
DEBUG_MSG(DLVL_INSANE, "Stream %s opening failed! Cancelling and marking as corrupt.", name.c_str());
data["meta"].null();
data["meta"]["tracks"].null();
data["error"] = "Stream offline: Corrupt file?";
if (data["error"].asStringRef() != prevState){
Log("WARN", "Source file " + URL + " seems to be corrupt.");
}
unsigned int i = 0;
JSON::fromDTMI((const unsigned char*)streamIndex.mapped + 8, streamIndex.len - 8, i, data["meta"]);
if (data["meta"].isMember("tracks") && data["meta"]["tracks"].size()){
for(JSON::ObjIter trackIt = data["meta"]["tracks"].ObjBegin(); trackIt != data["meta"]["tracks"].ObjEnd(); trackIt++){
trackIt->second.removeMember("fragments");
trackIt->second.removeMember("keys");
trackIt->second.removeMember("keysizes");
trackIt->second.removeMember("parts");
trackIt->second.removeMember("ivecs");/*LTS*/
}
}
if ( !data["meta"] || !data["meta"].isMember("tracks") || !data["meta"]["tracks"]){
data["error"] = "Stream offline: Corrupt file?";
if (data["error"].asStringRef() != prevState){
Log("WARN", "Source file " + URL + " seems to be corrupt.");
}
data["online"] = 0;
return;
}
DEBUG_MSG(DLVL_INSANE, "Metadata for stream %s (re)loaded", name.c_str());
data["online"] = 0;
return;
}
DEBUG_MSG(DLVL_INSANE, "Stream %s opened", name.c_str());
}else{
//now, run mistinfo on the source - or on the accompanying dtsh file, if it exists
if (stat((URL+".dtsh").c_str(), &fileinfo) == 0){
DEBUG_MSG(DLVL_INSANE, "Stream %s has a DTSH - opening DTSH instead of main stream file", name.c_str());
URL += ".dtsh";
unsigned int i = 0;
JSON::fromDTMI((const unsigned char*)streamIndex.mapped + 8, streamIndex.len - 8, i, data["meta"]);
if (data["meta"].isMember("tracks") && data["meta"]["tracks"].size()){
for(JSON::ObjIter trackIt = data["meta"]["tracks"].ObjBegin(); trackIt != data["meta"]["tracks"].ObjEnd(); trackIt++){
trackIt->second.removeMember("fragments");
trackIt->second.removeMember("keys");
trackIt->second.removeMember("keysizes");
trackIt->second.removeMember("parts");
trackIt->second.removeMember("ivecs");
}
}
char * tmp_cmd[3] = {0, 0, 0};
std::string mistinfo = Util::getMyPath() + "MistInfo";
tmp_cmd[0] = (char*)mistinfo.c_str();
tmp_cmd[1] = (char*)URL.c_str();
DEBUG_MSG(DLVL_INSANE, "Running MistInfo for stream %s on file %s", name.c_str(), tmp_cmd[1]);
data["meta"] = JSON::fromString(Util::Procs::getOutputOf(tmp_cmd));
if ( !data["meta"] || !data["meta"].isMember("tracks") || !data["meta"]["tracks"]){
data["error"] = "Stream offline: Corrupt file?";
if (data["error"].asStringRef() != prevState){
@ -192,8 +167,9 @@ namespace Controller {
data["online"] = 0;
return;
}
DEBUG_MSG(DLVL_INSANE, "Metadata for stream %s succesfully (re)loaded", name.c_str());
DEBUG_MSG(DLVL_INSANE, "Metadata for stream %s (re)loaded", name.c_str());
}
DEBUG_MSG(DLVL_INSANE, "Stream %s opened", name.c_str());
}
if (!hasViewers(name)){
if ( !data.isMember("error")){