mirror of
https://github.com/ntop/ntopng.git
synced 2026-05-06 03:45:26 +00:00
445 lines
13 KiB
C++
445 lines
13 KiB
C++
/*
|
|
*
|
|
* (C) 2013-18 - ntop.org
|
|
*
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*
|
|
*/
|
|
|
|
#include "ntop_includes.h"
|
|
|
|
extern "C" {
|
|
extern char* rrd_strversion(void);
|
|
};
|
|
|
|
AfterShutdownAction afterShutdownAction = after_shutdown_nop;
|
|
|
|
/* ******************************** */
|
|
|
|
void sighup(int sig) {
|
|
;
|
|
}
|
|
|
|
/* ******************************** */
|
|
|
|
#ifdef __linux__
|
|
static void nohup_after_shutdown_command(const char * const after_shutdown_command) {
|
|
char command_buf[256];
|
|
int res;
|
|
|
|
if(!after_shutdown_command || after_shutdown_command[0] == '\0')
|
|
return;
|
|
|
|
/* Self-restarting service does not restart with systemd:
|
|
This is a hard limitation imposed by systemd.
|
|
The best suggestions so far are to use at, cron, or systemd timer units.
|
|
|
|
https://unix.stackexchange.com/questions/202048/self-restarting-service-does-not-restart-with-systemd
|
|
*/
|
|
if((res = snprintf(command_buf, sizeof(command_buf),
|
|
"echo \"sleep 1 && %s\" | at now",
|
|
after_shutdown_command)) < 0
|
|
|| res >= (int)sizeof(command_buf))
|
|
return;
|
|
|
|
printf("%s\n", command_buf);
|
|
fflush(stdout);
|
|
system(command_buf);
|
|
}
|
|
#endif
|
|
|
|
/* ******************************** */
|
|
|
|
void sigproc(int sig) {
|
|
static int called = 0;
|
|
|
|
if(called) {
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "Ok I am leaving now");
|
|
_exit(0);
|
|
} else {
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "Shutting down...");
|
|
called = 1;
|
|
ntop->getGlobals()->requestShutdown();
|
|
}
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
#ifdef WIN32
|
|
|
|
void initWinsock32() {
|
|
WORD wVersionRequested;
|
|
WSADATA wsaData;
|
|
int err;
|
|
|
|
wVersionRequested = MAKEWORD(2, 0);
|
|
err = WSAStartup( wVersionRequested, &wsaData );
|
|
if( err != 0 ) {
|
|
/* Tell the user that we could not find a usable */
|
|
/* WinSock DLL. */
|
|
printf("FATAL ERROR: unable to initialize Winsock 2.x.\n");
|
|
exit(-1);
|
|
}
|
|
}
|
|
|
|
/* ******************************** */
|
|
|
|
extern "C" {
|
|
int ntop_main(int argc, char *argv[])
|
|
#else
|
|
int main(int argc, char *argv[])
|
|
#endif
|
|
{
|
|
Prefs *prefs = NULL;
|
|
char *ifName;
|
|
int rc;
|
|
char *affinity;
|
|
int indexAffinity = 0;
|
|
char *core_id_s = NULL;
|
|
int core_id;
|
|
char path[MAX_PATH];
|
|
FILE *fd;
|
|
ThreadedActivity *boot_activity;
|
|
|
|
#ifdef WIN32
|
|
initWinsock32();
|
|
#endif
|
|
|
|
if((ntop = new(std::nothrow) Ntop(argv[0])) == NULL) _exit(0);
|
|
if((prefs = new(std::nothrow) Prefs(ntop)) == NULL) _exit(0);
|
|
|
|
#ifndef WIN32
|
|
if((argc >= 2) && (argv[1][0] != '-')) {
|
|
rc = prefs->loadFromFile(argv[1]);
|
|
if(argc > 2)
|
|
rc = prefs->loadFromCLI(argc, argv);
|
|
} else
|
|
#endif
|
|
rc = prefs->loadFromCLI(argc, argv);
|
|
|
|
if(rc < 0) return(-1);
|
|
|
|
ntop->registerPrefs(prefs, false);
|
|
|
|
if((boot_activity = new ThreadedActivity(BOOT_SCRIPT_PATH))) {
|
|
/* Don't call run() as by the time the script will be run the delete below will free the memory */
|
|
/* NOTE: preferences restore from file is handled here */
|
|
boot_activity->runScript();
|
|
delete boot_activity;
|
|
}
|
|
|
|
prefs->registerNetworkInterfaces();
|
|
|
|
if(prefs->get_num_user_specified_interfaces() == 0) {
|
|
/* We add all interfaces available on this host */
|
|
prefs->add_default_interfaces();
|
|
}
|
|
|
|
#ifdef NTOPNG_PRO
|
|
ntop->registerNagios();
|
|
#endif
|
|
|
|
prefs->reloadPrefsFromRedis();
|
|
prefs->validate();
|
|
|
|
if(prefs->daemonize_ntopng())
|
|
ntop->daemonize();
|
|
|
|
#ifdef __linux__
|
|
/* Store number of CPUs before dropping privileges */
|
|
ntop->setNumCPUs(sysconf(_SC_NPROCESSORS_ONLN));
|
|
ntop->getTrace()->traceEvent(TRACE_INFO, "System has %d CPU cores", ntop->getNumCPUs());
|
|
#endif
|
|
|
|
affinity = prefs->get_cpu_affinity();
|
|
|
|
for(int i = 0; i < MAX_NUM_INTERFACE_IDS; i++) {
|
|
NetworkInterface *iface = NULL;
|
|
|
|
if((ifName = ntop->get_if_name(i)) == NULL
|
|
|| !strncmp(ifName, "view:", 5) /* Defer view interfaces init */)
|
|
continue;
|
|
|
|
try {
|
|
/* [ zmq-collector.lua@tcp://127.0.0.1:5556 ] */
|
|
#ifndef HAVE_NEDGE
|
|
if(!strcmp(ifName, "dummy")) {
|
|
iface = new DummyInterface();
|
|
} else if((strstr(ifName, "tcp://") || strstr(ifName, "ipc://"))) {
|
|
char *at = strchr(ifName, '@');
|
|
char *endpoint;
|
|
|
|
if(at != NULL)
|
|
endpoint = &at[1];
|
|
else
|
|
endpoint = ifName;
|
|
|
|
iface = new CollectorInterface(endpoint);
|
|
#if defined(HAVE_PF_RING) && (!defined(NTOPNG_EMBEDDED_EDITION)) && (!defined(__i686__)) && (!defined(__ARM_ARCH))
|
|
} else if(strstr(ifName, "zcflow:")) {
|
|
iface = new ZCCollectorInterface(ifName);
|
|
#endif
|
|
} else
|
|
#endif
|
|
{
|
|
iface = NULL;
|
|
|
|
#if defined(NTOPNG_PRO) && !defined(WIN32)
|
|
if(strncmp(ifName, "bridge:", 7) == 0) {
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "\n");
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Inline/bridge capabilities have now been moved in ntopng Edge (nEdge)");
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "For more information and free migration see:");
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "https://www.ntop.org/support/faq/migration-of-ntopng-inline-pro-enterprises-licenses-to-ntopng-edge-nedge/");
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "\n");
|
|
}
|
|
#endif
|
|
|
|
#if defined(HAVE_NEDGE)
|
|
if(iface == NULL && strncmp(ifName, "nf:", 3) == 0)
|
|
iface = new NetfilterInterface(ifName);
|
|
#endif
|
|
|
|
#ifdef HAVE_PF_RING
|
|
if((iface == NULL) && (!strstr(ifName, ".pcap"))) {
|
|
errno = 0;
|
|
iface = new PF_RINGInterface(ifName);
|
|
}
|
|
#endif
|
|
}
|
|
} catch(int err) {
|
|
ntop->getTrace()->traceEvent(TRACE_INFO, "An exception occurred during %s interface creation[%d]: %s. Falling back to pcap",
|
|
ifName, err, strerror(err));
|
|
if(iface) delete iface;
|
|
iface = NULL;
|
|
} catch(...) {
|
|
ntop->getTrace()->traceEvent(TRACE_INFO, "An exception occurred during %s interface creation. Falling back to pcap", ifName);
|
|
if(iface) delete iface;
|
|
iface = NULL;
|
|
}
|
|
|
|
#ifndef HAVE_NEDGE
|
|
if(iface == NULL) {
|
|
try {
|
|
errno = 0;
|
|
iface = new PcapInterface(ifName);
|
|
} catch(int err) {
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR, "An exception occurred during %s interface creation[%d]: %s",
|
|
ifName, err, strerror(err));
|
|
if(iface) delete iface;
|
|
iface = NULL;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if(iface) {
|
|
/* NOTE: allocate the PacketDumper only after setting the pcap_datalink_type */
|
|
iface->loadDumpPrefs();
|
|
|
|
if(affinity != NULL) {
|
|
char *tmp;
|
|
|
|
if(indexAffinity == 0)
|
|
core_id_s = strtok_r(affinity, ",", &tmp);
|
|
else
|
|
core_id_s = strtok_r(NULL, ",", &tmp);
|
|
|
|
if(core_id_s != NULL)
|
|
core_id = atoi(core_id_s);
|
|
else
|
|
core_id = indexAffinity;
|
|
|
|
indexAffinity++;
|
|
iface->setCPUAffinity(core_id);
|
|
}
|
|
|
|
if(prefs->get_packet_filter())
|
|
iface->set_packet_filter(prefs->get_packet_filter());
|
|
|
|
ntop->registerInterface(iface);
|
|
}
|
|
} /* for */
|
|
|
|
/* Instantiated deferred view interfaces */
|
|
for(int i = 0; i < MAX_NUM_INTERFACE_IDS; i++) {
|
|
NetworkInterface *iface = NULL;
|
|
|
|
if((ifName = ntop->get_if_name(i)) == NULL || strncmp(ifName, "view:", 5))
|
|
continue;
|
|
|
|
if((iface = new ViewInterface(ifName)))
|
|
ntop->registerInterface(iface);
|
|
}
|
|
|
|
#ifndef HAVE_NEDGE
|
|
ntop->createExportInterface();
|
|
ntop->getElasticSearch()->startFlowDump();
|
|
ntop->getLogstash()->startFlowDump();
|
|
#endif
|
|
|
|
if(ntop->getFirstInterface() == NULL) {
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR, "Startup error: missing super-user privileges ?");
|
|
exit(0);
|
|
}
|
|
|
|
#ifndef WIN32
|
|
if(prefs->get_pid_path() != NULL) {
|
|
FILE *fd;
|
|
|
|
fd = fopen(prefs->get_pid_path(), "w");
|
|
if(fd != NULL) {
|
|
int n;
|
|
|
|
chmod(prefs->get_pid_path(), CONST_DEFAULT_FILE_MODE);
|
|
n = fprintf(fd, "%u\n", getpid());
|
|
fclose(fd);
|
|
|
|
if(n > 0) {
|
|
chmod(prefs->get_pid_path(), 0644);
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "PID stored in file %s",
|
|
prefs->get_pid_path());
|
|
} else
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR, "The PID file %s is empty: is your disk full perhaps ?",
|
|
prefs->get_pid_path());
|
|
} else
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR, "Unable to store PID in file %s: %s",
|
|
prefs->get_pid_path(), strerror(errno));
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
It's safe to drop privileges now if http and https ports
|
|
are non-privileged. Otherwise it is necessary to delay
|
|
the privilege drop after the web server bind()
|
|
*/
|
|
if(prefs->do_change_user()
|
|
&& (prefs->get_http_port() >= 1024)
|
|
&& (prefs->get_https_port() >= 1024)) {
|
|
Utils::dropPrivileges();
|
|
}
|
|
|
|
ntop->loadGeolocation(prefs->get_docs_dir());
|
|
ntop->loadMacManufacturers(prefs->get_docs_dir());
|
|
ntop->loadTrackers();
|
|
ntop->registerHTTPserver(new HTTPserver(prefs->get_docs_dir(), prefs->get_scripts_dir()));
|
|
|
|
/*
|
|
If mysql flows dump is enabled, then it is necessary to create
|
|
and update the database schema
|
|
*/
|
|
if(prefs->do_dump_flows_on_mysql()) {
|
|
/* create the schema only one time, no need to call it for every interface */
|
|
if(!ntop->getFirstInterface()->createDBSchema()){
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR,
|
|
"Unable to create database schema, quitting.");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
} else if(prefs->do_read_flows_from_nprobe_mysql()) {
|
|
/* Create a view only one time for the first interface */
|
|
if(!ntop->getFirstInterface()->createNprobeDBView()){
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR,
|
|
"Unable to create a view on the nProbe database.");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
/*
|
|
We have created the network interface and thus changed user. Let's now check
|
|
if we can write on the data directory
|
|
*/
|
|
Utils::mkdir_tree(ntop->get_working_dir());
|
|
|
|
snprintf(path, sizeof(path), "%s/.test", ntop->get_working_dir());
|
|
ntop->fixPath(path);
|
|
|
|
if((fd = fopen(path, "w")) == NULL) {
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR,
|
|
"Unable to write on %s as '%s' [%s]: %s. Please specify a different directory (-d)",
|
|
ntop->get_working_dir(), prefs->get_user(), path, strerror(errno));
|
|
exit(EXIT_FAILURE);
|
|
} else {
|
|
chmod(path, CONST_DEFAULT_FILE_MODE);
|
|
fclose(fd); /* All right */
|
|
unlink(path);
|
|
}
|
|
|
|
if(prefs->is_log_to_file_enabled()
|
|
#ifndef WIN32
|
|
|| prefs->daemonize_ntopng()
|
|
#endif
|
|
) {
|
|
char path[MAX_PATH];
|
|
|
|
Utils::mkdir_tree(ntop->get_data_dir());
|
|
Utils::mkdir_tree(ntop->get_working_dir());
|
|
snprintf(path, sizeof(path), "%s/ntopng.log", ntop->get_working_dir() /* "C:\\Windows\\Temp" */);
|
|
ntop->fixPath(path);
|
|
ntop->registerLogFile(path);
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "Logging onto %s", path);
|
|
}
|
|
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "Working directory: %s",
|
|
ntop->get_working_dir());
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "Scripts/HTML pages directory: %s",
|
|
ntop->get_install_dir());
|
|
|
|
#ifndef WIN32
|
|
signal(SIGHUP, sighup);
|
|
signal(SIGINT, sigproc);
|
|
signal(SIGTERM, sigproc);
|
|
#endif
|
|
|
|
#if defined(WIN32) && defined(DEMO_WIN32)
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "-----------------------------------------------------------");
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "This is a demo version of ntopng limited to %d packets", MAX_NUM_PACKETS);
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Please go to http://shop.ntop.org for getting the full version");
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "-----------------------------------------------------------");
|
|
#endif
|
|
|
|
/* this returns after a shutdown has been requested */
|
|
ntop->start();
|
|
|
|
/* perform all the necessary cleanup and wait for other threads termination */
|
|
ntop->shutdownAll();
|
|
|
|
delete ntop;
|
|
ntop = NULL;
|
|
|
|
#ifdef __linux__
|
|
switch(afterShutdownAction) {
|
|
case after_shutdown_reboot:
|
|
nohup_after_shutdown_command("systemctl start systemd-reboot");
|
|
break;
|
|
case after_shutdown_poweroff:
|
|
nohup_after_shutdown_command("systemctl start systemd-poweroff");
|
|
break;
|
|
case after_shutdown_restart_self:
|
|
/* Necessary to check if the service has been activated via systemd before actually
|
|
restarting it using systemd. When the service hasn't been started with systemd (e.g., during the development)
|
|
it is desirable to skip the use of systemd */
|
|
nohup_after_shutdown_command("systemctl restart ntopng");
|
|
break;
|
|
case after_shutdown_nop:
|
|
default:
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
return(0);
|
|
}
|
|
|
|
#ifdef WIN32
|
|
}
|
|
#endif
|