Merge branch 'development' into LTS_development
# Conflicts: # CMakeLists.txt # src/analysers/info.cpp
This commit is contained in:
commit
cb298c57fd
13 changed files with 84 additions and 2003 deletions
|
@ -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 #
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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
|
557
lib/ftp.cpp
557
lib/ftp.cpp
|
@ -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;
|
||||
}
|
82
lib/ftp.h
82
lib/ftp.h
|
@ -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
|
400
lib/procs.cpp
400
lib/procs.cpp
|
@ -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;
|
||||
}
|
||||
|
|
21
lib/procs.h
21
lib/procs.h
|
@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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")){
|
||||
|
|
Loading…
Add table
Reference in a new issue