Multithreading & UX update

This commit is contained in:
Brendan LE GLAUNEC
2016-10-28 09:50:37 +02:00
committed by Brendan Le Glaunec
parent de757e848d
commit 509d68f023
39 changed files with 572 additions and 407 deletions
+59 -49
View File
@@ -16,35 +16,39 @@
#include <configuration.h>
#include <memory>
#include <mutex>
#include <stream_model.h>
#include <vector>
namespace etix {
namespace cameradar {
//! The interface a cache_manager should implement to be valid
// The interface a cache_manager should implement to be valid
class cache_manager_iface {
public:
virtual ~cache_manager_iface() {}
//! Launches the manager configuration
//! \return false if failed
// Launches the manager configuration
// \return false if failed
virtual bool configure(std::shared_ptr<etix::cameradar::configuration> configuration) = 0;
//! get the name of the cache manager
// get the name of the cache manager
virtual const std::string& get_name() const = 0;
//! Replaces all cached streams by the content of the vector given as
//! parameter
// Replaces all cached streams by the content of the vector given as
// parameter
virtual void set_streams(std::vector<etix::cameradar::stream_model> model) = 0;
//! Inserts a single stream to the cache
// Inserts a single stream to the cache
virtual void update_stream(const etix::cameradar::stream_model& newmodel) = 0;
//! Gets all cached streams
// Returns true if the stream passed as a parameter has changed in the cache
virtual bool has_changed(const etix::cameradar::stream_model&) = 0;
// Gets all cached streams
virtual std::vector<etix::cameradar::stream_model> get_streams() = 0;
//! Gets all valid streams which have been accessed
// Gets all valid streams which have been accessed
virtual std::vector<etix::cameradar::stream_model> get_valid_streams() = 0;
};
@@ -53,27 +57,30 @@ public:
cache_manager_base() = default;
virtual ~cache_manager_base() = default;
//! Launches the cache manager configuration
//! \return false if failed
// Launches the cache manager configuration
// \return false if failed
virtual bool configure(std::shared_ptr<etix::cameradar::configuration> configuration) = 0;
//! get the name of the cache manager
// get the name of the cache manager
virtual const std::string& get_name() const = 0;
//! Replaces all cached streams by the content of the vector given as
//! parameter
// Replaces all cached streams by the content of the vector given as
// parameter
virtual void set_streams(std::vector<etix::cameradar::stream_model> model) = 0;
//! Updates a single stream to the cache
// Returns true if the stream passed as a parameter has changed in the cache
virtual bool has_changed(const etix::cameradar::stream_model&) = 0;
// Updates a single stream to the cache
virtual void update_stream(const etix::cameradar::stream_model& newmodel) = 0;
//! Gets all cached streams
// Gets all cached streams
virtual std::vector<etix::cameradar::stream_model> get_streams() = 0;
//! Gets all valid streams which have been accessed
// Gets all valid streams which have been accessed
virtual std::vector<etix::cameradar::stream_model> get_valid_streams() = 0;
//! Get the manager's instance
// Get the manager's instance
cache_manager_base& get_instance();
template <typename I, typename T>
@@ -87,59 +94,62 @@ public:
}
};
//! The representation of a cache manager
//!
//! This class loads a shared library, and tries to call an extern "C"
//! function which should instanciate a new instance of the plugin.
// The representation of a cache manager
//
// This class loads a shared library, and tries to call an extern "C"
// function which should instanciate a new instance of the plugin.
class cache_manager {
private:
static const std::string PLUGIN_EXT;
static const std::string default_symbol;
//! the name of the cache manager
// The name of the cache manager
std::string name;
//! The path where the manager is located
//! should be specified in the configuration file
// The write mutex to avoid conflicts when multithreading
std::mutex m;
// The path where the manager is located
// should be specified in the configuration file
std::string path;
//! The symbol entry point of the manager to
//! call to create an instance from the shared library
// The symbol entry point of the manager to
// call to create an instance from the shared library
std::string symbol;
//! The handle to the shared library where is stored the manager
// The handle to the shared library where is stored the manager
void* handle = nullptr;
//! The cache manager instance if it is successfully loaded
// The cache manager instance if it is successfully loaded
cache_manager_iface* ptr = nullptr;
//! Internal function that creates the full path of the cache manager
//!
//! full path is composed of: the path, the name, the string "_cache-manager"
//! and the extension PLUGIN_EXT depending of the platform
// Internal function that creates the full path of the cache manager
//
// full path is composed of: the path, the name, the string "_cache-manager"
// and the extension PLUGIN_EXT depending of the platform
std::string make_full_path();
public:
//! Delete constructor
// Delete constructor
cache_manager() = delete;
//! The manager needs a path and a symbol to be instantiated.
//! The symbol can be changed if the plugin entry point
//! is different than the standard one.
// The manager needs a path and a symbol to be instantiated.
// The symbol can be changed if the plugin entry point
// is different than the standard one.
cache_manager(const std::string& path,
const std::string& name,
const std::string& symbol = default_symbol);
// //! Copy constructor
// // Copy constructor
// cache_manager(cache_manager &other);
//! Move constructor
// Move constructor
cache_manager(cache_manager&& old);
~cache_manager();
//! Creates the instance of the cache_manager
//!
// Creates the instance of the cache_manager
//
// \return false if the cache_manager failed to be instantiated or if
// the cache_manager is not a valid cache manager, true otherwise
bool make_instance();
@@ -152,23 +162,23 @@ public:
return this->get<I, T>();
}
//! Helper to access internal loaded cache_manager
//!
//! Gives access to the methods of the cache_manager using the operator
//! -> (e.g.: cache_manager->get_name());
// Helper to access internal loaded cache_manager
//
// Gives access to the methods of the cache_manager using the operator
// -> (e.g.: cache_manager->get_name());
cache_manager_iface* operator->();
const cache_manager_iface* operator->() const;
//! helper function to check if a cache_manager is instantiated or not
// helper function to check if a cache_manager is instantiated or not
friend bool operator==(std::nullptr_t nullp, const cache_manager& p);
//! helper function to check if a cache_manager is instantiated or not
// helper function to check if a cache_manager is instantiated or not
friend bool operator==(const cache_manager& p, std::nullptr_t nullp);
//! helper function to check if a cache_manager is instantiated or not
// helper function to check if a cache_manager is instantiated or not
friend bool operator!=(std::nullptr_t nullp, const cache_manager& p);
//! helper function to check if a cache_manager is instantiated or not
// helper function to check if a cache_manager is instantiated or not
friend bool operator!=(const cache_manager& p, std::nullptr_t nullp);
};
}
+14 -7
View File
@@ -14,19 +14,26 @@
#pragma once
#include <json/reader.h> // Json::Value
#include <json/value.h> // Json::Value
#include <logger.h> // _LOG_
#include <opt_parse.h> // parsing opt
#include <string> // std::string
#include <utility> // std::pair
#include <logger.h> // _LOG_
#include <json/value.h> // Json::Value
#include <json/reader.h> // Json::Value
namespace etix {
namespace cameradar {
static const std::string default_configuration_path = "conf/cameradar.conf.json";
static const std::string default_ids_file_path_ = "conf/ids.json";
static const std::string default_urls_file_path_ = "conf/url.json";
static const std::string default_ports = "554,8554";
static const std::string default_subnets = "localhost,168.0.0.0/24";
static const std::string default_thumbnail_storage_path = "/tmp";
static const std::string default_rtsp_url_file = "conf/url.json";
static const std::string default_rtsp_ids_file = "conf/ids.json";
static const std::string default_cache_manager_path = "../cache_managers/dumb_cache_manager";
static const std::string default_cache_manager_name = "dumb";
struct configuration {
std::string thumbnail_storage_path;
@@ -49,7 +56,7 @@ struct configuration {
const std::string& rtsp_ids_file,
const std::string& cache_manager_path,
const std::string& cache_manager_name,
const std::string& ports = "1-65535")
const std::string& ports)
: thumbnail_storage_path(thumbnail_storage_path)
, subnets(subnets)
, rtsp_url_file(rtsp_url_file)
@@ -67,6 +74,6 @@ struct configuration {
};
std::pair<bool, std::string> read_file(const std::string& path);
std::pair<bool, configuration> load(const std::string& path);
std::pair<bool, configuration> load(const std::pair<bool, etix::tool::opt_parse>& args);
}
}
+3 -3
View File
@@ -32,8 +32,8 @@ std::string decode64(const std::string& str_to_decode);
std::string base64_encode(unsigned char const*, unsigned int len);
std::string base64_decode(std::string const& s);
} //! encode
} // encode
} //! tool
} // tool
} //! etix
} // etix
+2 -2
View File
@@ -24,8 +24,8 @@ namespace tool {
static std::mutex mutex;
//! Format a string with the given arguments
//! same behavior as sprintf.
// Format a string with the given arguments
// same behavior as sprintf.
template <class... Args>
std::string
fmt(const std::string& base, Args... args) {
+2 -2
View File
@@ -34,8 +34,8 @@ bool create_folder(const std::string& folder);
bool create_recursive_folder(const std::string& folder);
std::string home();
//! this functions take a copy because we need to make some operations on the string
//! for example, we need to apply std::string::pop_back
// this functions take a copy because we need to make some operations on the string
// for example, we need to apply std::string::pop_back
std::string get_file_folder(std::string full_file_path);
bool copy(const std::string& src, const std::string& dst);
+8 -48
View File
@@ -23,20 +23,13 @@ namespace etix {
namespace tool {
//! Parse command line arguments
class opt_parse {
private:
//! An argumetn representation to be passed to the program
struct opt_param {
//! is it required
bool required;
//! Does he needs arguments
bool need_arg;
//! What is its name
std::string name;
//! Description
std::string desc;
//! the argument of the arguments !
std::string argument;
bool is_passed = false;
@@ -44,22 +37,14 @@ private:
: required(required), need_arg(need_arg), name(name), desc(desc) {}
};
//! Map of the different possibles argument as string and their
//! rertpresntation
std::unordered_map<std::string, opt_param> params;
//! The total count of arguments for this program
int argc;
//! The list of arguments as a String
char** argv;
//! The total count of params
int params_cnt = 0;
public:
//! An iterator to iterate over all the arguments of
//! the program
class iterator {
private:
//! The arguments vector and their argumetns
std::vector<std::pair<std::string, std::string>> args;
unsigned int opt_pos = 0;
@@ -71,65 +56,40 @@ public:
return *this;
}
std::pair<std::string, std::string>& operator*() { return this->args.at(this->opt_pos); }
bool operator==(const iterator& rhs) const { return this->opt_pos == rhs.opt_pos; }
bool operator!=(const iterator& rhs) const { return this->opt_pos != rhs.opt_pos; }
bool
operator==(const iterator& rhs) const {
return this->opt_pos == rhs.opt_pos;
}
bool
operator!=(const iterator& rhs) const {
return this->opt_pos != rhs.opt_pos;
}
};
opt_parse() = delete;
//! \param argc Total count of arguements
//! \param argv Cmdline arguments from program startup
opt_parse(int argc, char* argv[]);
~opt_parse();
//! Add a argument required for your program
//!
//! If the specified argument is not given in cmdline, a error will be
//! generated
//! \param name The name of the parameter as a string (e.g "-l")
//! \param desc A description that will be used by the function `print_help`
//! \param need_arg Does the argument require a parameter
void required(const std::string& name, const std::string& desc = "", bool need_arg = true);
//! Add an optional argument for your program
//!
//! If the specified argument is not given in cmdline, a error will be
//! generated
//! \param name The name of the parameter as a string (e.g "-l")
//! \param desc A description that will be used by the function `print_help`
//! \param need_arg Does the argument require a parameter
void optional(const std::string& name, const std::string& desc = "", bool need_arg = true);
//! Process the parsing of the arguments
bool execute();
//! \return an iterator on the begin of the arguments
iterator begin() const;
//! \return the iterator on the end of the arguments
iterator end() const;
//! Print the usage using the parameter setted when referencing the arguments
//! for the program
void print_usage() const;
//! Print an help message generated using all the specified arguments
void print_help() const;
//! Is there on the parameters (missing parameter ? unknows ? missing
//! arguments ?)
//! \return true if there is error, false otherwise
bool has_error() const;
//! Does the option exist or not ?
//! \param opt The name of the option to check
//! \return true if the param exist, false otherwise
bool exist(const std::string& opt) const;
//! Acces to an argument from its name
//! \param opt The name of the option to check
//! \return the the argument of the param as a string
std::string operator[](const std::string& opt) const;
};
@@ -14,9 +14,10 @@
#pragma once
#include <cameradar_task.h> // task interface
#include <cachemanager.h> // cacheManager
#include <cameradar_task.h> // task interface
#include <describe.h> // send DESCRIBE through cURL
#include <future> // std::async & std::future
#include <signal_handler.h> // signals
#include <stream_model.h> // data model
@@ -41,6 +42,7 @@ public:
bool test_ids(const etix::cameradar::stream_model& cit,
const std::string& pit,
const std::string& uit) const;
bool bruteforce_camera(const stream_model& stream) const;
};
}
}
@@ -14,14 +14,15 @@
#pragma once
#include <cachemanager.h> // cacheManager
#include <cameradar_task.h> // task interface
#include <memory> // std::shared_ptr
#include <logger.h> // LOG
#include <curl/curl.h> // cURL client for discovery
#include <describe.h> // send DESCRIBE through cURL
#include <future> // std::async & std::future
#include <logger.h> // LOG
#include <memory> // std::shared_ptr
#include <signal_handler.h> // signals
#include <stream_model.h> // data model
#include <cachemanager.h> // cacheManager
namespace etix {
namespace cameradar {
@@ -42,6 +43,7 @@ public:
virtual bool run() const;
bool test_path(const etix::cameradar::stream_model& cit, const std::string& it) const;
bool bruteforce_camera(const stream_model& stream) const;
};
}
}
@@ -14,13 +14,14 @@
#pragma once
#include <cachemanager.h> // cacheManager
#include <cameradar_task.h> // task interface
#include <fmt.h> // fmt
#include <future> // std::async & std::future
#include <launch_command.h> // launch_command
#include <rtsp_path.h> // make_path
#include <signal_handler.h> // signals
#include <stream_model.h> // data model
#include <cachemanager.h> // cacheManager
#include <fmt.h> // fmt
#include <rtsp_path.h> // make_path
namespace etix {
namespace cameradar {
@@ -41,6 +42,7 @@ public:
virtual bool run() const;
std::string build_output_file_path(const std::string& path) const;
bool generate_thumbnail(const stream_model& stream) const;
};
}
}
+20 -9
View File
@@ -31,11 +31,11 @@ distribution.
#pragma warning(disable : 4786)
#endif
#include <assert.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
// Help out windows:
#if defined(_DEBUG) && !defined(DEBUG)
@@ -43,9 +43,9 @@ distribution.
#endif
#ifdef TIXML_USE_STL
#include <string>
#include <iostream>
#include <sstream>
#include <string>
#define TIXML_STRING std::string
#else
#include "tinystr.h"
@@ -1086,9 +1086,18 @@ public:
return const_cast<TiXmlAttribute*>((const_cast<const TiXmlAttribute*>(this))->Previous());
}
bool operator==(const TiXmlAttribute& rhs) const { return rhs.name == name; }
bool operator<(const TiXmlAttribute& rhs) const { return name < rhs.name; }
bool operator>(const TiXmlAttribute& rhs) const { return name > rhs.name; }
bool
operator==(const TiXmlAttribute& rhs) const {
return rhs.name == name;
}
bool
operator<(const TiXmlAttribute& rhs) const {
return name < rhs.name;
}
bool
operator>(const TiXmlAttribute& rhs) const {
return name > rhs.name;
}
/* Attribute parsing starts: first letter of the name
returns: the next char after the
@@ -1171,7 +1180,6 @@ private:
//*ME: Because of hidden/disabled copy-construktor in TiXmlAttribute
//(sentinel-element),
//*ME: this class must be also use a hidden/disabled copy-constructor
//!!!
TiXmlAttributeSet(const TiXmlAttributeSet&); // not allowed
void operator=(const TiXmlAttributeSet&); // not allowed (as TiXmlAttribute)
@@ -1509,7 +1517,8 @@ public:
#endif
TiXmlText(const TiXmlText& copy) : TiXmlNode(TiXmlNode::TINYXML_TEXT) { copy.CopyTo(this); }
TiXmlText& operator=(const TiXmlText& base) {
TiXmlText&
operator=(const TiXmlText& base) {
base.CopyTo(this);
return *this;
}
@@ -1664,7 +1673,8 @@ public:
TiXmlUnknown(const TiXmlUnknown& copy) : TiXmlNode(TiXmlNode::TINYXML_UNKNOWN) {
copy.CopyTo(this);
}
TiXmlUnknown& operator=(const TiXmlUnknown& copy) {
TiXmlUnknown&
operator=(const TiXmlUnknown& copy) {
copy.CopyTo(this);
return *this;
}
@@ -2039,7 +2049,8 @@ public:
TiXmlHandle(TiXmlNode* _node) { this->node = _node; }
/// Copy constructor
TiXmlHandle(const TiXmlHandle& ref) { this->node = ref.node; }
TiXmlHandle operator=(const TiXmlHandle& ref) {
TiXmlHandle
operator=(const TiXmlHandle& ref) {
if (&ref != this) this->node = ref.node;
return *this;
}
+52 -11
View File
@@ -56,7 +56,7 @@ configuration::load_ids() {
"the default one "
"instead.",
"configuration");
content = read_file(default_ids_file_path_).second;
content = read_file(default_rtsp_ids_file).second;
}
if (content.size()) {
auto root = Json::Value();
@@ -101,7 +101,7 @@ configuration::load_url() {
"the default one "
"instead.",
"configuration");
content = read_file(default_urls_file_path_).second;
content = read_file(default_rtsp_url_file).second;
}
if (content.size()) {
auto root = Json::Value();
@@ -131,18 +131,47 @@ serialize(const Json::Value& root) {
std::pair<bool, configuration> ret;
try {
ret.second.ports = root["ports"].asString();
ret.second.subnets = root["subnets"].asString();
ret.second.rtsp_ids_file = root["rtsp_ids_file"].asString();
ret.second.rtsp_url_file = root["rtsp_url_file"].asString();
ret.second.thumbnail_storage_path = root["thumbnail_storage_path"].asString();
ret.second.cache_manager_path = root["cache_manager_path"].asString();
ret.second.cache_manager_name = root["cache_manager_name"].asString();
if (!root["ports"].isNull())
ret.second.ports = root["ports"].asString();
else
ret.second.ports = default_ports;
if (!root["subnets"].isNull())
ret.second.subnets = root["subnets"].asString();
else
ret.second.subnets = default_subnets;
if (!root["rtsp_ids_file"].isNull())
ret.second.rtsp_ids_file = root["rtsp_ids_file"].asString();
else
ret.second.rtsp_ids_file = default_rtsp_ids_file;
if (!root["rtsp_url_file"].isNull())
ret.second.rtsp_url_file = root["rtsp_url_file"].asString();
else
ret.second.rtsp_url_file = default_rtsp_url_file;
if (!root["thumbnail_storage_path"].isNull())
ret.second.thumbnail_storage_path = root["thumbnail_storage_path"].asString();
else
ret.second.thumbnail_storage_path = default_thumbnail_storage_path;
if (!root["cache_manager_path"].isNull())
ret.second.cache_manager_path = root["cache_manager_path"].asString();
else
ret.second.cache_manager_path = default_cache_manager_path;
if (!root["cache_manager_name"].isNull())
ret.second.cache_manager_name = root["cache_manager_name"].asString();
else
ret.second.cache_manager_name = default_cache_manager_name;
ret.first = true;
} catch (std::exception& e) {
} catch (const std::exception& e) {
LOG_ERR_("Configuration failed : " + std::string(e.what()), "configuration");
ret.first = false;
}
return ret;
}
@@ -156,7 +185,16 @@ configuration::get_raw() const {
// Will return true & valid configuration if success
// Otherwise false & empty configuration
std::pair<bool, configuration>
load(const std::string& path) {
load(const std::pair<bool, etix::tool::opt_parse>& args) {
std::string path;
if (not args.second.exist("-c")) {
path = etix::cameradar::default_configuration_path;
LOG_WARN_("No custom path set, trying to use default path: " + path, "main");
} else {
path = args.second["-c"];
}
// Check if the file exists at the given path
if (access(path.c_str(), F_OK) == -1) {
LOG_ERR_("Can't access: " + path, "configuration");
@@ -191,6 +229,9 @@ load(const std::string& path) {
conf.first &= conf.second.load_url();
conf.first &= conf.second.load_ids();
if (args.second.exist("-s")) conf.second.subnets = args.second["-s"];
if (args.second.exist("-p")) conf.second.ports = args.second["-p"];
return conf;
}
}
+21 -14
View File
@@ -17,9 +17,19 @@
namespace etix {
namespace cameradar {
//! Sends a request to the camera using the OPTION method,
//! then a DESCRIBE to check for valid IDs
//! then another DESCIBE with IDs if an authentication is needed
// Ugly workaround
size_t
write_data(void* buffer, size_t size, size_t nmemb, void* userp) {
// I'm sorry for this
// Forget you ever saw it
(void)buffer;
(void)userp;
return size * nmemb;
}
// Sends a request to the camera using the OPTION method,
// then a DESCRIBE to check for valid IDs
// then another DESCIBE with IDs if an authentication is needed
bool
curl_describe(const std::string& path, bool logs) {
CURL* csession;
@@ -27,23 +37,21 @@ curl_describe(const std::string& path, bool logs) {
struct curl_slist* custom_msg = NULL;
char URL[256];
long rc;
FILE* protofile = NULL;
protofile = fopen("/dev/null", "wb");
csession = curl_easy_init();
if (csession == NULL) return -1;
sprintf(URL, "%s", path.c_str());
// These are the options for all following cURL requests
// Activate verbose if debug is needed
curl_easy_setopt(csession, CURLOPT_NOSIGNAL, 1);
curl_easy_setopt(csession, CURLOPT_TIMEOUT, 1);
curl_easy_setopt(csession, CURLOPT_NOBODY, 1);
curl_easy_setopt(csession, CURLOPT_URL, URL);
curl_easy_setopt(csession, CURLOPT_RTSP_STREAM_URI, URL);
curl_easy_setopt(csession, CURLOPT_FOLLOWLOCATION, 0);
curl_easy_setopt(csession, CURLOPT_HEADER, 0);
curl_easy_setopt(csession, CURLOPT_INTERLEAVEDATA, protofile);
curl_easy_setopt(csession, CURLOPT_VERBOSE, 0);
curl_easy_setopt(csession, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_OPTIONS);
curl_easy_setopt(csession, CURLOPT_WRITEDATA, protofile);
curl_easy_setopt(csession, CURLOPT_WRITEFUNCTION, write_data);
// This request will handshake the stream's server, it should always return 200 OK
curl_easy_perform(csession);
curl_easy_getinfo(csession, CURLINFO_RESPONSE_CODE, &rc);
@@ -51,11 +59,7 @@ curl_describe(const std::string& path, bool logs) {
custom_msg, "Accept: application/x-rtsp-mh, application/rtsl, application/sdp");
curl_easy_setopt(csession, CURLOPT_RTSPHEADER, custom_msg);
curl_easy_setopt(csession, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_DESCRIBE);
curl_easy_setopt(csession, CURLOPT_WRITEDATA, protofile);
// This request will check if the given path is right without the need of encrypted ids
curl_easy_perform(
csession); // will return 404 if no ids and bad route, 401 if ids, 200 is all ok
res = curl_easy_getinfo(csession, CURLINFO_RESPONSE_CODE, &rc);
unsigned long pos = path.find("@");
if (pos != std::string::npos) {
std::string encoded = etix::tool::encode::encode64(path.substr(7, pos - 7));
@@ -63,14 +67,17 @@ curl_describe(const std::string& path, bool logs) {
curl_slist_append(custom_msg, std::string("Authorization: Basic " + encoded).c_str());
curl_easy_setopt(csession, CURLOPT_RTSPHEADER, custom_msg);
curl_easy_setopt(csession, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_DESCRIBE);
curl_easy_setopt(csession, CURLOPT_WRITEDATA, protofile);
// curl_easy_setopt(csession, CURLOPT_WRITEDATA, protofile);
// This request will check if the given ids are good
curl_easy_perform(csession); // will return 404 if good ids, 401 if bad ids
res = curl_easy_getinfo(csession, CURLINFO_RESPONSE_CODE, &rc);
} else {
curl_easy_perform(
csession); // will return 404 if no ids and bad route, 401 if ids, 200 is all ok
res = curl_easy_getinfo(csession, CURLINFO_RESPONSE_CODE, &rc);
}
curl_easy_cleanup(csession);
fclose(protofile);
curl_global_cleanup();
LOG_DEBUG_("Response code : " + std::to_string(rc), "describe");
if (logs) {
// Some cameras return 400 instead of 401, don't know why.
+2 -2
View File
@@ -54,8 +54,8 @@ dispatcher::run() {
worker.join();
}
//! This loop is used to add all the tasks specified in the command line
//! And then run them successively
// This loop is used to add all the tasks specified in the command line
// And then run them successively
void
dispatcher::do_stuff() {
if (opts.second.exist("-d")) {
+2 -2
View File
@@ -88,12 +88,12 @@ create_recursive_folder(const std::string& folder) {
std::string
get_file_folder(std::string full_file_path) {
//! remove ending slash
// remove ending slash
if (full_file_path.back() == '/') full_file_path.pop_back();
size_t last_slash_position = full_file_path.find_last_of('/');
//! it there is no slash, there is no folder to return
// it there is no slash, there is no folder to return
if (last_slash_position == std::string::npos) return "";
return std::string(full_file_path, 0, last_slash_position);
+1 -1
View File
@@ -17,7 +17,7 @@
namespace etix {
namespace cameradar {
//! Launches a command and checks for the return value
// Launches a command and checks for the return value
bool
launch_command(const std::string& cmd) {
int status = system(cmd.c_str());
+5 -12
View File
@@ -24,7 +24,6 @@ void
print_version() {
std::cout << "Cameradar version " << CAMERADAR_VERSION << std::endl;
std::cout << "Build " << CAMERADAR_VERSION_BUILD << std::endl;
std::cout << "Git commit " << CAMERADAR_VERSION_GIT_SHA1 << std::endl;
}
// Command line parsing
@@ -32,6 +31,8 @@ std::pair<bool, etix::tool::opt_parse>
parse_cmdline(int argc, char* argv[]) {
auto opt_parse = etix::tool::opt_parse{ argc, argv };
opt_parse.optional("-s", "Set subnets (e.g.: `172.16.0.0/24`)", true);
opt_parse.optional("-p", "Set ports (e.g.: `554,8554`)", true);
opt_parse.optional("-c", "Path to the configuration file (-c /path/to/conf)", true);
opt_parse.optional("-l", "Set log level (-l 4 will only show warnings and errors)", true);
opt_parse.optional("-d", "Launch the discovery tool on the given subnet", false);
@@ -86,18 +87,10 @@ main(int argc, char* argv[]) {
if (not args.first) return EXIT_FAILURE;
print_version();
// configure file configuration path
auto conf_path = std::string{};
if (not args.second.exist("-c")) {
conf_path = etix::cameradar::default_configuration_path;
LOG_WARN_("No custom path set, trying to use default path: " + conf_path, "main");
} else {
conf_path = args.second["-c"];
}
if (not args.second.exist("-l")) {
etix::tool::logger::get_instance("cameradar").set_level(etix::tool::loglevel::INFO);
LOG_INFO_("No log level set, using log level 2 (ignoring DEBUG)", "main");
etix::tool::logger::get_instance("cameradar").set_level(etix::tool::loglevel::DEBUG);
LOG_INFO_("No log level set, using log level 1", "main");
} else {
try {
int level = std::stoi(args.second["-l"]);
@@ -110,7 +103,7 @@ main(int argc, char* argv[]) {
}
// Try to load the configuration
auto conf = cmrdr::load(conf_path);
auto conf = cmrdr::load(args);
if (not conf.first) { return EXIT_FAILURE; }
LOG_INFO_("Configuration successfully loaded", "main");
+44 -33
View File
@@ -25,9 +25,9 @@ static const std::string no_ids_warning_ =
"default routes. "
"Path bruteforce is impossible without the IDs.";
//! Tries to match the detected combination of Username / Password
//! with the camera stream. Creates a resource in the DB upon
//! valid discovery
// Tries to match the detected combination of Username / Password
// with the camera stream. Creates a resource in the DB upon
// valid discovery
bool
brutelogs::test_ids(const etix::cameradar::stream_model& stream,
const std::string& password,
@@ -36,22 +36,24 @@ brutelogs::test_ids(const etix::cameradar::stream_model& stream,
std::string path = stream.service_name + "://";
if (username != "" || password != "") { path += username + ":" + password + "@"; }
path += stream.address + ":" + std::to_string(stream.port);
LOG_DEBUG_("Testing ids : " + path, "brutelogs");
LOG_INFO_("Testing ids : " + path, "brutelogs");
try {
if (curl_describe(path, true)) {
LOG_DEBUG_("[FOUND IDS] : " + path, "brutelogs");
LOG_INFO_("[FOUND IDS] : " + path, "brutelogs");
found = true;
stream_model newstream{
stream.address, stream.port, username, password,
stream.route, stream.service_name, stream.product, stream.protocol,
stream.state, true, stream.path_found, stream.thumbnail_path
stream.address, stream.port, username, password,
stream.route, stream.service_name, stream.product, stream.protocol,
stream.state, stream.path_found, true, stream.thumbnail_path
};
if ((*cache)->has_changed(stream)) return true;
(*cache)->update_stream(newstream);
} else {
stream_model newstream{ stream.address, stream.port, username,
password, stream.route, stream.service_name,
stream.product, stream.protocol, stream.state,
false, stream.path_found, stream.thumbnail_path };
stream_model newstream{ stream.address, stream.port, username,
password, stream.route, stream.service_name,
stream.product, stream.protocol, stream.state,
stream.path_found, false, stream.thumbnail_path };
if ((*cache)->has_changed(stream)) return true;
(*cache)->update_stream(newstream);
}
} catch (const std::runtime_error& e) {
@@ -63,25 +65,45 @@ brutelogs::test_ids(const etix::cameradar::stream_model& stream,
bool
ids_already_found(std::vector<stream_model> streams, stream_model stream) {
for (const auto& it : streams) {
if ((stream.address == it.address) && (stream.port == it.port) && it.ids_found) return true;
if ((stream.address == it.address) && (stream.port == it.port) && it.ids_found) {
LOG_DEBUG_(">>>>>> PATH " + std::to_string(stream.path_found) + " IDS " +
std::to_string(stream.ids_found),
"wtf");
return true;
}
}
return false;
}
//! Tries to discover the right IDs on all RTSP streams in DB
//! Uses the ids.json file to try different combinations
bool
brutelogs::bruteforce_camera(const stream_model& stream) const {
for (const auto& username : conf.usernames) {
if (signal_handler::instance().should_stop() != etix::cameradar::stop_priority::running)
break;
for (const auto& password : conf.passwords) {
if (signal_handler::instance().should_stop() != etix::cameradar::stop_priority::running)
break;
if ((*cache)->has_changed(stream)) return true;
if (test_ids(stream, password, username)) return true;
}
}
return false;
}
// Tries to discover the right IDs on all RTSP streams in DB
// Uses the ids.json file to try different combinations
bool
brutelogs::run() const {
std::vector<std::future<bool>> futures;
LOG_INFO_(
"Beginning bruteforce of the usernames and passwords task, it may "
"take a while.",
"brutelogs");
std::vector<etix::cameradar::stream_model> streams = (*cache)->get_streams();
LOG_DEBUG_("Found " + std::to_string(streams.size()) + " streams in the cache", "brutelogs");
bool doubleskip;
size_t found = 0;
for (const auto& stream : streams) {
doubleskip = false;
if (signal_handler::instance().should_stop() != etix::cameradar::stop_priority::running)
break;
if ((found < streams.size()) && ids_already_found(streams, stream)) {
@@ -92,24 +114,13 @@ brutelogs::run() const {
"brutelogs");
++found;
} else {
for (const auto& username : conf.usernames) {
if (doubleskip ||
signal_handler::instance().should_stop() !=
etix::cameradar::stop_priority::running)
break;
for (const auto& password : conf.passwords) {
if (doubleskip ||
signal_handler::instance().should_stop() !=
etix::cameradar::stop_priority::running)
break;
if (test_ids(stream, password, username)) {
++found;
doubleskip = true;
}
}
}
futures.push_back(
std::async(std::launch::async, &brutelogs::bruteforce_camera, this, stream));
}
}
for (auto& fit : futures) {
if (fit.get()) { ++found; }
}
if (!found) {
LOG_WARN_(no_ids_warning_, "brutelogs");
return false;
+36 -18
View File
@@ -19,9 +19,9 @@ static const std::string no_route_found_ =
"default "
"routes. Thumbnail generation is impossible without the path.";
//! Tries to match the detected combination of Username / Password
//! with a route for the camera stream. Creates a resource in the DB upon
//! valid discovery
// Tries to match the detected combination of Username / Password
// with a route for the camera stream. Creates a resource in the DB upon
// valid discovery
bool
brutepath::test_path(const stream_model& stream, const std::string& route) const {
bool found = false;
@@ -29,7 +29,7 @@ brutepath::test_path(const stream_model& stream, const std::string& route) const
stream.address + ":" + std::to_string(stream.port);
if (route.front() != '/') { path += "/"; }
path += route;
LOG_DEBUG_("Testing path : " + path, "brutepath");
LOG_INFO_("Testing path : " + path, "brutepath");
try {
if (curl_describe(path, false)) {
// insert in DB and go to the next port, print a cool message
@@ -40,6 +40,7 @@ brutepath::test_path(const stream_model& stream, const std::string& route) const
stream.service_name, stream.product, stream.protocol, stream.state, true,
stream.ids_found, stream.thumbnail_path
};
if ((*cache)->has_changed(stream)) return true;
(*cache)->update_stream(newstream);
} else {
stream_model newstream{
@@ -47,6 +48,7 @@ brutepath::test_path(const stream_model& stream, const std::string& route) const
stream.service_name, stream.product, stream.protocol, stream.state, false,
stream.ids_found, stream.thumbnail_path
};
if ((*cache)->has_changed(stream)) return true;
(*cache)->update_stream(newstream);
}
} catch (const std::runtime_error& e) { LOG_INFO_(e.what(), "brutepath"); }
@@ -56,16 +58,36 @@ brutepath::test_path(const stream_model& stream, const std::string& route) const
bool
path_already_found(std::vector<stream_model> streams, stream_model model) {
for (const auto& stream : streams) {
if ((model.address == stream.address) && (model.port == stream.port) && stream.path_found)
if ((model.address == stream.address) && (model.port == stream.port) && stream.path_found) {
LOG_DEBUG_(">>>>>> PATH " + std::to_string(stream.path_found) + " IDS " +
std::to_string(stream.ids_found),
"wtf");
return true;
}
}
return false;
}
//! Tries to discover a route on all RTSP streams in DB
//! Uses the url.json file to try different routes
bool
brutepath::bruteforce_camera(const stream_model& stream) const {
for (const auto& route : conf.paths) {
if (signal_handler::instance().should_stop() != etix::cameradar::stop_priority::running)
break;
if ((*cache)->has_changed(stream)) return true;
if (test_path(stream, route)) {
return true;
break;
}
}
return false;
}
// Tries to discover a route on all RTSP streams in DB
// Uses the url.json file to try different routes
bool
brutepath::run() const {
std::vector<std::future<bool>> futures;
LOG_INFO_("Beginning bruteforce of the camera paths task, it may take a while.", "bruteforce");
std::vector<stream_model> streams = (*cache)->get_streams();
int found = 0;
@@ -74,22 +96,18 @@ brutepath::run() const {
break;
if (path_already_found(streams, stream)) {
LOG_INFO_(stream.address +
" : This camera's path was already discovered in the database."
"Skipping to the next camera.",
" : This camera's path was already discovered in the database. Skipping "
"to the next camera.",
"brutepath");
++found;
} else {
for (const auto& route : conf.paths) {
if (signal_handler::instance().should_stop() !=
etix::cameradar::stop_priority::running)
break;
if (test_path(stream, route)) {
found++;
break;
}
}
futures.push_back(
std::async(std::launch::async, &brutepath::bruteforce_camera, this, stream));
}
}
for (auto& fit : futures) {
if (fit.get()) { ++found; }
}
if (!found) {
LOG_WARN_(no_route_found_, "brutepath");
+11 -11
View File
@@ -17,15 +17,15 @@
namespace etix {
namespace cameradar {
//! The first command checks if dpkg finds nmap in the system by cutting the
//! result and grepping
//! nmap from it.
//!
//! The second command checks the version of nmap, right now it needs to be the
//! 6.47 but this could
//! be changed to 6 or greater depending on the needs. In a docker container
//! this should not be a
//! problem.
// The first command checks if dpkg finds nmap in the system by cutting the
// result and grepping
// nmap from it.
//
// The second command checks the version of nmap, right now it needs to be the
// 6.47 but this could
// be changed to 6 or greater depending on the needs. In a docker container
// this should not be a
// problem.
bool
nmap_is_ok() {
return (
@@ -33,8 +33,8 @@ nmap_is_ok() {
&& launch_command("mkdir -p /tmp/scans")); // Creates the directory in which the scans will be stored
}
//! Launches and checks the return of the nmap command
//! Uses the subnets specified in the conf file to launch nmap
// Launches and checks the return of the nmap command
// Uses the subnets specified in the conf file to launch nmap
bool
mapping::run() const {
if (nmap_is_ok()) {
+8 -8
View File
@@ -24,7 +24,7 @@ static const std::string no_hosts_found_ =
"were "
"accessible";
//! Avoids segfaults on unknown xml structure
// Avoids segfaults on unknown xml structure
std::string
xml_safe_get(const TiXmlElement* elem, const std::string& attr) {
if (elem == nullptr) return "closed";
@@ -32,8 +32,8 @@ xml_safe_get(const TiXmlElement* elem, const std::string& attr) {
return "closed";
}
//! Parse a single host node (generally containing only one camera)
//! Pushes it back to the data structure
// Parse a single host node (generally containing only one camera)
// Pushes it back to the data structure
void
parsing::parse_camera(TiXmlElement* xml_host, std::vector<stream_model>& data) const {
TiXmlElement* xml_streams = xml_host->FirstChild("ports")->ToElement();
@@ -58,8 +58,8 @@ parsing::parse_camera(TiXmlElement* xml_host, std::vector<stream_model>& data) c
}
}
//! Prints all detected cameras into the data structure and stops the program if
//! no open RTSP streams were found
// Prints all detected cameras into the data structure and stops the program if
// no open RTSP streams were found
bool
parsing::print_detected_cameras(const std::vector<stream_model>& data) const {
int added = 0;
@@ -90,8 +90,8 @@ parsing::print_detected_cameras(const std::vector<stream_model>& data) const {
return true;
}
//! Opens the nmap output file, parses the data of each discovered port
//! Adds the RTSP ports only into the DB
// Opens the nmap output file, parses the data of each discovered port
// Adds the RTSP ports only into the DB
bool
parsing::run() const {
std::vector<stream_model> data;
@@ -113,7 +113,7 @@ parsing::run() const {
LOG_WARN_(no_hosts_found_, "parsing");
if (data.size() == 0) { LOG_WARN_("No cameras were discovered", "parsing"); }
return print_detected_cameras(data);
} catch (std::exception& e) {
} catch (const std::exception& e) {
LOG_ERR_("Error during parsing. brutepath aborted : " + std::string(e.what()), "parsing");
return false;
}
+2 -2
View File
@@ -17,8 +17,8 @@
namespace etix {
namespace cameradar {
//! Launches and checks the return of the nmap command
//! Uses the subnets specified in the conf file to launch nmap
// Launches and checks the return of the nmap command
// Uses the subnets specified in the conf file to launch nmap
bool
print::run() const {
std::vector<stream_model> results = (*cache)->get_valid_streams();
@@ -17,9 +17,9 @@
namespace etix {
namespace cameradar {
//! Gets all the discovered streams with good routes and logs
//! And launches an ffmpeg command to generate a thumbnail
//! In order to check for the stream validity
// Gets all the discovered streams with good routes and logs
// And launches an ffmpeg command to generate a thumbnail
// In order to check for the stream validity
bool
stream_check::run() const {
GstElement* pipeline;
@@ -30,8 +30,8 @@ stream_check::run() const {
std::vector<stream_model> streams = (*cache)->get_valid_streams();
if (not streams.size()) {
LOG_WARN_("There were no valid streams to check. Cameradar will stop.", "stream_check");
return false;
LOG_WARN_("There were no valid streams to check. Cameradar will stop.", "stream_check");
return false;
}
for (const auto& stream : streams) {
GError* error = NULL;
@@ -40,13 +40,17 @@ stream_check::run() const {
gst_parse_launch("rtspsrc name=source ! rtph264depay ! h264parse ! fakesink", &error);
std::string location = "rtsp://";
location += stream.username + ":" + stream.password + "@" + stream.address + ":" + std::to_string(stream.port);
location += stream.username + ":" + stream.password + "@" + stream.address + ":" +
std::to_string(stream.port);
if (pipeline == NULL) {
LOG_ERR_("[" + stream.address + "] Can't configure pipeline", "stream_check");
return false;
} else {
elem = gst_bin_get_by_name(GST_BIN(pipeline), "source");
LOG_DEBUG_("Launching gstreamer check on rtsp://" + stream.username + ":" + stream.password + "@" + stream.address + ":" + std::to_string(stream.port), "gstreamer check");
LOG_DEBUG_("Launching gstreamer check on rtsp://" + stream.username + ":" +
stream.password + "@" + stream.address + ":" +
std::to_string(stream.port),
"gstreamer check");
g_object_set(G_OBJECT(elem), "location", location.c_str(), "latency", 20, NULL);
if (gst_element_set_state(pipeline, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) {
@@ -63,7 +67,9 @@ stream_check::run() const {
(*cache)->update_stream(invalidstream);
return false;
}
LOG_INFO_("[" + stream.address + "] Set pipeline to playing", "stream_check");
LOG_INFO_("[" + stream.address +
"] This stream is accessible and seems to be functional",
"stream_check");
}
}
LOG_INFO_("All streams could be accessed with GStreamer", "stream_check");
+60 -47
View File
@@ -41,61 +41,74 @@ thumbnail::build_output_file_path(const std::string& path) const {
return ss.str();
}
//! Gets all the discovered streams with good routes and logs
//! And launches an ffmpeg command to generate a thumbnail
//! In order to check for the stream validity
bool
thumbnail::generate_thumbnail(const stream_model& stream) const {
LOG_INFO_("Generating thumbnail for " + stream.address, "thumbnail_generation");
if (signal_handler::instance().should_stop() != etix::cameradar::stop_priority::running)
return false;
std::string ffmpeg_cmd =
"mkdir -p %s ; "
"ffmpeg "
"-rtsp_transport tcp "
"-y "
"-nostdin "
"-loglevel quiet " // no logs
"-i '%s' " // input
"-vcodec mjpeg " // jpeg codec
"-vframes 1 " // only take one frame
"-an " // disable audio
"-f image2 " // force image
"-s 240x180 " // force size
"'%s'";
std::string fullpath = make_path(stream);
std::string output = build_output_file_path(stream.address);
ffmpeg_cmd = tool::fmt(ffmpeg_cmd.c_str(),
output.substr(0, output.find_last_of("/")).c_str(),
fullpath.c_str(),
output.c_str());
if (!launch_command(ffmpeg_cmd)) {
LOG_WARN_("The following command [" + ffmpeg_cmd +
"] didn't work. That can either mean that the stream is "
"not valid or "
"that there is a problem with the camera.",
"thumbnail_generation");
return false;
} else {
LOG_DEBUG_("Generated thumbnail : " + ffmpeg_cmd, "thumbnail_generation");
try {
stream_model result{ stream.address, stream.port, stream.username,
stream.password, stream.route, stream.service_name,
stream.product, stream.protocol, stream.state,
stream.path_found, stream.ids_found, output };
(*cache)->update_stream(result);
} catch (const std::exception& e) { LOG_DEBUG_(e.what(), "thumbnail_generation"); }
}
return true;
}
// Gets all the discovered streams with good routes and logs
// And launches an ffmpeg command to generate a thumbnail
// In order to check for the stream validity
bool
thumbnail::run() const {
std::vector<std::future<bool>> futures;
std::vector<stream_model> streams = (*cache)->get_valid_streams();
LOG_INFO_("Started thumbnail generation, it may take a while", "thumbnail");
if (not streams.size()) {
LOG_WARN_("There were no valid streams to generate thumbnails from. Cameradar will stop.", "thumbnail_generation");
return false;
LOG_WARN_("There were no valid streams to generate thumbnails from. Cameradar will stop.",
"thumbnail_generation");
return false;
}
int done = 0;
for (const auto& stream : streams) {
LOG_DEBUG_("Generating thumbnail for " + stream.address, "thumbnail_generation");
if (signal_handler::instance().should_stop() != etix::cameradar::stop_priority::running)
break;
std::string ffmpeg_cmd =
"mkdir -p %s ; "
"ffmpeg "
"-y "
"-nostdin "
"-loglevel quiet "
"-i '%s' "
"-vcodec mjpeg "
"-vframes 1 "
"-an "
"-f image2 "
"-s 320x240 "
"'%s'";
std::string fullpath = make_path(stream);
std::string output = build_output_file_path(stream.address);
ffmpeg_cmd = tool::fmt(ffmpeg_cmd.c_str(),
output.substr(0, output.find_last_of("/")).c_str(),
fullpath.c_str(),
output.c_str());
if (!launch_command(ffmpeg_cmd)) {
LOG_WARN_("The following command [" + ffmpeg_cmd +
"] didn't work. That can either mean that the stream is "
"not valid or "
"that there is a problem with the camera.",
"thumbnail_generation");
} else {
LOG_DEBUG_("Generated thumbnail : " + ffmpeg_cmd, "thumbnail_generation");
try {
stream_model result{ stream.address, stream.port, stream.username,
stream.password, stream.route, stream.service_name,
stream.product, stream.protocol, stream.state,
stream.path_found, stream.ids_found, output };
(*cache)->update_stream(result);
} catch (std::exception& e) { LOG_DEBUG_(e.what(), "thumbnail_generation"); }
}
futures.push_back(
std::async(std::launch::async, &thumbnail::generate_thumbnail, this, stream));
}
for (auto& fit : futures) {
if (fit.get()) { ++done; }
}
LOG_INFO_("All thumbnails have been successfully generated in " +
this->conf.thumbnail_storage_path,
"thumbnail_generation");
return true;
}
}