1 /*
2  Domoticz, Open Source Home Automation System
3 
4  Copyright (C) 2012,2015 Rob Peters (GizMoCuz)
5 
6  Domoticz is free software: you can redistribute it and/or modify it
7  under the terms of the GNU General Public License as published
8  by the Free Software Foundation, either version 3 of the License,
9  or (at your option) any later version.
10 
11  This program is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty
13  of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14  See the GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with Domoticz. If not, see <http://www.gnu.org/licenses/>.
18 */
19 
20 #include "stdafx.h"
21 #include "mainworker.h"
22 #include <stdio.h>
23 #include <sys/types.h>
24 #include <signal.h>
25 #include <iostream>
26 #include "CmdLine.h"
27 #include "Logger.h"
28 #include "Helper.h"
29 #include "WebServerHelper.h"
30 #include "SQLHelper.h"
31 #include "../notifications/NotificationHelper.h"
32 #include "appversion.h"
33 #include "localtime_r.h"
34 #include "SignalHandler.h"
35 
36 #if defined WIN32
37 	#include "../msbuild/WindowsHelper.h"
38 	#include <Shlobj.h>
39 #else
40 	#include <sys/stat.h>
41 	#include <unistd.h>
42 	#include <syslog.h>
43 	#include <errno.h>
44 	#include <fcntl.h>
45 	#include <string.h>
46 #endif
47 
48 const char *szHelp =
49 "Usage: Domoticz -www port\n"
50 "\t-version display version number\n"
51 "\t-www port (for example -www 8080, or -www 0 to disable http)\n"
52 "\t-wwwbind address (for example -wwwbind 0.0.0.0 or -wwwbind 192.168.0.20)\n"
53 #ifdef WWW_ENABLE_SSL
54 "\t-sslwww port (for example -sslwww 443, or -sslwww 0 to disable https)\n"
55 "\t-sslcert file_path (for example /opt/domoticz/server_cert.pem)\n"
56 "\t-sslkey file_path (if different from certificate file)\n"
57 "\t-sslpass passphrase (to access to server private key in certificate)\n"
58 "\t-sslmethod method (supported methods: tlsv1, tlsv1_server, sslv23, sslv23_server, tlsv11, tlsv11_server, tlsv12, tlsv12_server)\n"
59 "\t-ssloptions options (for SSL options, default is 'default_workarounds,no_sslv2,no_sslv3,no_tlsv1,no_tlsv1_1,single_dh_use')\n"
60 "\t-ssldhparam file_path (for SSL DH parameters)\n"
61 #endif
62 #if defined WIN32
63 "\t-wwwroot file_path (for example D:\\www)\n"
64 "\t-dbase file_path (for example D:\\domoticz.db)\n"
65 "\t-userdata file_path (for example D:\\domoticzdata)\n"
66 #else
67 "\t-wwwroot file_path (for example /opt/domoticz/www)\n"
68 "\t-dbase file_path (for example /opt/domoticz/domoticz.db)\n"
69 "\t-userdata file_path (for example /opt/domoticz)\n"
70 #endif
71 "\t-webroot additional web root, useful with proxy servers (for example domoticz)\n"
72 "\t-startupdelay seconds (default=0)\n"
73 "\t-nowwwpwd (in case you forgot the web server username/password)\n"
74 "\t-nocache (do not return appcache, use only when developing the web pages)\n"
75 "\t-wwwcompress mode (on = always compress [default], off = always decompress, static = no processing but try precompressed first)\n"
76 #if defined WIN32
77 "\t-nobrowser (do not start web browser (Windows Only)\n"
78 #endif
79 "\t-noupdates do not use the internal update functionality\n"
80 #if defined WIN32
81 "\t-log file_path (for example D:\\domoticz.log)\n"
82 #else
83 "\t-log file_path (for example /var/log/domoticz.log)\n"
84 #endif
85 "\t-loglevel (combination of: normal,status,error,debug)\n"
86 "\t-debuglevel (combination of: normal,hardware,received,webserver,eventsystem,python,thread_id)\n"
87 "\t-notimestamps (do not prepend timestamps to logs; useful with syslog, etc.)\n"
88 "\t-php_cgi_path (for example /usr/bin/php-cgi)\n"
89 #ifndef WIN32
90 "\t-daemon (run as background daemon)\n"
91 "\t-pidfile pid file location (for example /var/run/domoticz.pid)\n"
92 "\t-syslog [user|daemon|local0 .. local7] (use syslog as log output, defaults to facility 'user')\n"
93 "\t-f config_file (for example /etc/domoticz.conf)\n"
94 #endif
95 "";
96 
97 #ifndef WIN32
98 struct _facilities {
99 	const char* facname;
100 	const int   facvalue;
101 };
102 
103 static const _facilities facilities[] =
104 {
105 	{ "daemon", LOG_DAEMON },
106 	{ "user",   LOG_USER },
107 	{ "local0", LOG_LOCAL0 },
108 	{ "local1", LOG_LOCAL1 },
109 	{ "local2", LOG_LOCAL2 },
110 	{ "local3", LOG_LOCAL3 },
111 	{ "local4", LOG_LOCAL4 },
112 	{ "local5", LOG_LOCAL5 },
113 	{ "local6", LOG_LOCAL6 },
114 	{ "local7", LOG_LOCAL7 }
115 };
116 std::string logfacname = "user";
117 #endif
118 std::string szStartupFolder;
119 std::string szUserDataFolder;
120 std::string szWWWFolder;
121 std::string szWebRoot;
122 std::string dbasefile;
123 
124 bool bHasInternalTemperature=false;
125 std::string szInternalTemperatureCommand = "/opt/vc/bin/vcgencmd measure_temp";
126 
127 bool bHasInternalVoltage=false;
128 std::string szInternalVoltageCommand = "";
129 
130 bool bHasInternalCurrent=false;
131 std::string szInternalCurrentCommand = "";
132 
133 
134 std::string szAppVersion="???";
135 int iAppRevision=0;
136 std::string szAppHash="???";
137 std::string szAppDate="???";
138 std::string szPyVersion="None";
139 int ActYear;
140 time_t m_StartTime=time(NULL);
141 std::string szRandomUUID = "???";
142 
143 MainWorker m_mainworker;
144 CLogger _log;
145 http::server::CWebServerHelper m_webservers;
146 CSQLHelper m_sql;
147 CNotificationHelper m_notifications;
148 
149 std::string logfile;
150 bool g_bStopApplication = false;
151 bool g_bUseSyslog = false;
152 bool g_bRunAsDaemon = false;
153 bool g_bDontCacheWWW = false;
154 http::server::_eWebCompressionMode g_wwwCompressMode = http::server::WWW_USE_GZIP;
155 bool g_bUseUpdater = true;
156 http::server::server_settings webserver_settings;
157 #ifdef WWW_ENABLE_SSL
158 http::server::ssl_server_settings secure_webserver_settings;
159 #endif
160 bool bStartWebBrowser = true;
161 bool g_bUseWatchdog = true;
162 bool g_bIsWSL = false;
163 
164 #define DAEMON_NAME "domoticz"
165 #define PID_FILE "/var/run/domoticz.pid"
166 
167 std::string daemonname = DAEMON_NAME;
168 std::string pidfile = PID_FILE;
169 int pidFilehandle = 0;
170 
171 #ifndef WIN32
daemonShutdown()172 void daemonShutdown()
173 {
174 	if (pidFilehandle != 0) {
175 		close(pidFilehandle);
176 		pidFilehandle = 0;
177 	}
178 }
179 
daemonize(const char * rundir,const char * pidfile)180 void daemonize(const char *rundir, const char *pidfile)
181 {
182 	int pid, sid, i;
183 	char str[10];
184 	struct sigaction newSigAction;
185 	sigset_t newSigSet;
186 
187 	/* Set signal mask - signals we want to block */
188 	sigemptyset(&newSigSet);
189 	sigaddset(&newSigSet, SIGCHLD);  /* ignore child - i.e. we don't need to wait for it */
190 	sigaddset(&newSigSet, SIGTSTP);  /* ignore Tty stop signals */
191 	sigaddset(&newSigSet, SIGTTOU);  /* ignore Tty background writes */
192 	sigaddset(&newSigSet, SIGTTIN);  /* ignore Tty background reads */
193 	sigprocmask(SIG_BLOCK, &newSigSet, NULL);   /* Block the above specified signals */
194 
195 	/* Set up a signal handler */
196 	newSigAction.sa_sigaction = signal_handler;
197 	sigemptyset(&newSigAction.sa_mask);
198 	newSigAction.sa_flags = SA_SIGINFO;
199 
200 	/* Signals to handle */
201 	sigaction(SIGTERM, &newSigAction, NULL);    // catch term signal
202 	sigaction(SIGINT,  &newSigAction, NULL);    // catch interrupt signal
203 	sigaction(SIGSEGV, &newSigAction, NULL);    // catch segmentation fault signal
204 	sigaction(SIGABRT, &newSigAction, NULL);    // catch abnormal termination signal
205 	sigaction(SIGILL,  &newSigAction, NULL);    // catch invalid program image
206 	sigaction(SIGUSR1, &newSigAction, NULL);    // catch SIGUSR1 (used by watchdog)
207 #ifndef WIN32
208 	sigaction(SIGHUP,  &newSigAction, NULL);    // catch HUP, for log rotation
209 #endif
210 
211 	/* Fork*/
212 	pid = fork();
213 
214 	if (pid < 0)
215 	{
216 		/* Could not fork */
217 		exit(EXIT_FAILURE);
218 	}
219 
220 	if (pid > 0)
221 	{
222 		/* Child created ok, so exit parent process */
223 		exit(EXIT_SUCCESS);
224 	}
225 
226 	/* Ensure only one copy */
227 	pidFilehandle = open(pidfile, O_RDWR | O_CREAT, 0600);
228 
229 	if (pidFilehandle == -1)
230 	{
231 		/* Couldn't open lock file */
232 		syslog(LOG_INFO, "Could not open PID lock file %s, exiting", pidfile);
233 		exit(EXIT_FAILURE);
234 	}
235 
236 	/* Try to lock file */
237 	if (lockf(pidFilehandle, F_TLOCK, 0) == -1)
238 	{
239 		/* Couldn't get lock on lock file */
240 		syslog(LOG_INFO, "Could not lock PID lock file %s, exiting", pidfile);
241 		exit(EXIT_FAILURE);
242 	}
243 
244 	/* Get and format PID */
245 	sprintf(str, "%d\n", getpid());
246 
247 	/* write pid to lockfile */
248 	int twrite=write(pidFilehandle, str, strlen(str));
249 	if (twrite != int(strlen(str)))
250 	{
251 		syslog(LOG_INFO, "Could not write to lock file %s, exiting", pidfile);
252 		exit(EXIT_FAILURE);
253 	}
254 
255 
256 	/* Child continues */
257 
258 	umask(027); /* Set file permissions 750 */
259 
260 	if (!logfile.empty())
261 		_log.SetOutputFile(logfile.c_str());
262 
263 	/* Get a new process group */
264 	sid = setsid();
265 
266 	if (sid < 0)
267 	{
268 		exit(EXIT_FAILURE);
269 	}
270 
271 	/* Close out the standard file descriptors */
272 	close(STDIN_FILENO);
273 	close(STDOUT_FILENO);
274 	close(STDERR_FILENO);
275 
276 	/* Route I/O connections */
277 
278 	/* Open STDIN */
279 	i = open("/dev/null", O_RDWR);
280 
281 	/* STDOUT */
282 	int dret = dup(i);
283 	if (dret == -1)
284 	{
285 		_log.Log(LOG_ERROR, "Could not set STDOUT descriptor !");
286 	}
287 
288 	/* STDERR */
289 	dret = dup(i);
290 	if (dret == -1)
291 	{
292 		_log.Log(LOG_ERROR, "Could not set STDERR descriptor !");
293 	}
294 
295 	int cdret = chdir(rundir); /* change running directory */
296 	if (cdret == -1)
297 	{
298 		_log.Log(LOG_ERROR, "Could not change running directory !");
299 	}
300 }
301 #endif
302 
303 #if defined(_WIN32)
getExecutablePathName(char * pathName,size_t pathNameCapacity)304 	static size_t getExecutablePathName(char* pathName, size_t pathNameCapacity)
305 	{
306 		return GetModuleFileNameA(NULL, pathName, (DWORD)pathNameCapacity);
307 	}
308 #elif defined(__linux__) || defined(__CYGWIN32__) /* elif of: #if defined(_WIN32) */
309 	#include <unistd.h>
getExecutablePathName(char * pathName,size_t pathNameCapacity)310 	static size_t getExecutablePathName(char* pathName, size_t pathNameCapacity)
311 	{
312 		size_t pathNameSize = readlink("/proc/self/exe", pathName, pathNameCapacity - 1);
313 		pathName[pathNameSize] = '\0';
314 		return pathNameSize;
315 	}
316 #elif defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__)
317 	#include <sys/sysctl.h>
getExecutablePathName(char * pathName,size_t pathNameCapacity)318 	static size_t getExecutablePathName(char* pathName, size_t pathNameCapacity)
319 	{
320 		int mib[4];
321 #if defined(__DragonFly__) || defined(__FreeBSD__)
322 		mib[0] = CTL_KERN;
323 		mib[1] = KERN_PROC;
324 		mib[2] = KERN_PROC_PATHNAME;
325 		mib[3] = -1;
326 #else // __NetBSD__
327 		mib[0] = CTL_KERN;
328 		mib[1] = KERN_PROC_ARGS;
329 		mib[2] = getpid();
330 		mib[3] = KERN_PROC_PATHNAME;
331 #endif
332 		size_t cb = pathNameCapacity-1;
333 		sysctl(mib, 4, pathName, &cb, NULL, 0);
334 		return cb;
335 	}
336 #elif defined(__OpenBSD__)
337 #include <sys/sysctl.h>
getExecutablePathName(char * pathName,size_t pathNameCapacity)338 static size_t getExecutablePathName(char* pathName, size_t pathNameCapacity)
339 {
340         int mib[4];
341         char **argv;
342         size_t len = 0;
343         const char *comm;
344 
345         mib[0] = CTL_KERN;
346         mib[1] = KERN_PROC_ARGS;
347         mib[2] = getpid();
348         mib[3] = KERN_PROC_ARGV;
349         pathName[0] = '\0';
350         if (sysctl(mib, 4, NULL, &len, NULL, 0) < 0)
351         {
352                 return 0;
353         }
354 
355         if (!(argv = (char**)malloc(len)))
356         {
357                 return 0;
358         }
359 
360         if (sysctl(mib, 4, argv, &len, NULL, 0) < 0)
361         {
362                 len = 0;
363                 goto finally;
364         }
365 
366 	        len = 0;
367         comm = argv[0];
368 
369         if (*comm == '/' || *comm == '.')
370         {
371                 // In OpenBSD PATH_MAX is 1024
372                 char * fullPath = (char*) malloc(PATH_MAX);
373                 if(!fullPath)
374                 {
375                         goto finally;
376                 }
377 
378                 if (realpath(comm, fullPath))
379                 {
380                         if(pathNameCapacity > strnlen(fullPath, PATH_MAX))
381                         {
382                                 strlcpy(pathName, fullPath, pathNameCapacity);
383                                 len = strnlen(pathName, pathNameCapacity);
384                                 free(fullPath);
385                         }
386                 }
387         }
388         else
389         {
390                 char *sp;
391                 char *xpath = strdup(getenv("PATH"));
392                 char *path = strtok_r(xpath, ":", &sp);
393                 struct stat st;
394 
395 		if (!xpath)
396                 {
397                         goto finally;
398                 }
399 
400                 while (path)
401                 {
402                         snprintf(pathName, pathNameCapacity, "%s/%s", path, comm);
403                         if (!stat(pathName, &st) && (st.st_mode & S_IXUSR))
404                         {
405                                 break;
406                         }
407                         pathName[0] = '\0';
408                         path = strtok_r(NULL, ":", &sp);
409                 }
410                 free(xpath);
411         }
412 
413   finally:
414         free(argv);
415         return len;
416 }
417 #elif defined(__APPLE__) /* elif of: #elif defined(__linux__) */
418 	#include <mach-o/dyld.h>
getExecutablePathName(char * pathName,size_t pathNameCapacity)419 	static size_t getExecutablePathName(char* pathName, size_t pathNameCapacity)
420 	{
421 		uint32_t pathNameSize = 0;
422 
423 		_NSGetExecutablePath(NULL, &pathNameSize);
424 
425 		if (pathNameSize > pathNameCapacity)
426 			pathNameSize = pathNameCapacity;
427 
428 		if (!_NSGetExecutablePath(pathName, &pathNameSize))
429 		{
430 			char real[PATH_MAX];
431 
432 			if (realpath(pathName, real) != NULL)
433 			{
434 				pathNameSize = strlen(real);
435 				strncpy(pathName, real, pathNameSize);
436 			}
437 
438 			return pathNameSize;
439 		}
440 
441 		return 0;
442 	}
443 #else /* else of: #elif defined(__APPLE__) */
444 	#error provide your own getExecutablePathName implementation
445 #endif /* end of: #if defined(_WIN32) */
446 
GetAppVersion()447 void GetAppVersion()
448 {
449 	szAppVersion = VERSION_STRING;
450 	iAppRevision = APPVERSION;
451 	szAppHash = APPHASH;
452 	char szTmp[200];
453 	struct tm ltime;
454 	time_t btime = APPDATE;
455 	localtime_r(&btime, &ltime);
456 	ActYear = ltime.tm_year + 1900;
457 	strftime(szTmp, 200, "%Y-%m-%d %H:%M:%S", &ltime);
458 	szAppDate = szTmp;
459 }
460 
461 #if !defined WIN32
CheckForOnboardSensors()462 void CheckForOnboardSensors()
463 {
464 	//Check if we are running on a RaspberryPi
465 	std::string sLine = "";
466 	std::ifstream infile;
467 
468 #if defined(__FreeBSD__)
469 	infile.open("/compat/linux/proc/cpuinfo");
470 #else
471 	infile.open("/proc/cpuinfo");
472 #endif
473 	if (infile.is_open())
474 	{
475 		while (!infile.eof())
476 		{
477 			getline(infile, sLine);
478 			if (
479 				(sLine.find("BCM2708") != std::string::npos) ||
480 				(sLine.find("BCM2709") != std::string::npos)
481 				)
482 			{
483 				//Core temperature of BCM2835 SoC
484 				_log.Log(LOG_STATUS, "System: Raspberry Pi");
485 				szInternalTemperatureCommand = "/opt/vc/bin/vcgencmd measure_temp";
486 				bHasInternalTemperature = true;
487 				break;
488 			}
489 		}
490 		infile.close();
491 	}
492 	if (!bHasInternalTemperature)
493 	{
494 		if (file_exist("/sys/devices/platform/sunxi-i2c.0/i2c-0/0-0034/temp1_input"))
495 		{
496 			_log.Log(LOG_STATUS, "System: Cubieboard/Cubietruck");
497 			szInternalTemperatureCommand = "cat /sys/devices/platform/sunxi-i2c.0/i2c-0/0-0034/temp1_input | awk '{ printf (\"temp=%0.2f\\n\",$1/1000); }'";
498 			bHasInternalTemperature = true;
499 		}
500 		else if (file_exist("/sys/devices/virtual/thermal/thermal_zone0/temp"))
501 		{
502 			//_log.Log(LOG_STATUS,"System: ODroid");
503 			szInternalTemperatureCommand = "cat /sys/devices/virtual/thermal/thermal_zone0/temp | awk '{ if ($1 < 100) printf(\"temp=%d\\n\",$1); else printf (\"temp=%0.2f\\n\",$1/1000); }'";
504 			bHasInternalTemperature = true;
505 		}
506 	}
507 	if (file_exist("/sys/class/power_supply/ac/voltage_now"))
508 	{
509 		szInternalVoltageCommand = "cat /sys/class/power_supply/ac/voltage_now | awk '{ printf (\"volt=%0.2f\\n\",$1/1000000); }'";
510 		bHasInternalVoltage = true;
511 	}
512 	if (file_exist("/sys/class/power_supply/ac/current_now"))
513 	{
514 		szInternalCurrentCommand = "cat /sys/class/power_supply/ac/current_now | awk '{ printf (\"curr=%0.2f\\n\",$1/1000000); }'";
515 		bHasInternalCurrent = true;
516 	}
517 	//New Armbian Kernal 4.14+
518 	if (file_exist("/sys/class/power_supply/axp20x-ac/voltage_now"))
519 	{
520 		szInternalVoltageCommand = "cat /sys/class/power_supply/axp20x-ac/voltage_now | awk '{ printf (\"volt=%0.2f\\n\",$1/1000000); }'";
521 		bHasInternalVoltage = true;
522 	}
523 	if (file_exist("/sys/class/power_supply/axp20x-ac/current_now"))
524 	{
525 		szInternalCurrentCommand = "cat /sys/class/power_supply/axp20x-ac/current_now | awk '{ printf (\"curr=%0.2f\\n\",$1/1000000); }'";
526 		bHasInternalCurrent = true;
527 	}
528 
529 #if defined (__OpenBSD__)
530 	szInternalTemperatureCommand = "sysctl hw.sensors.acpitz0.temp0|sed -e 's/.*temp0/temp/'|cut -d ' ' -f 1";
531 	bHasInternalTemperature = true;
532 	szInternalVoltageCommand = "sysctl hw.sensors.acpibat0.volt1|sed -e 's/.*volt1/volt/'|cut -d ' ' -f 1";
533 	bHasInternalVoltage = true;
534 	//bHasInternalCurrent = true;
535 
536 #endif
537 }
538 #endif
539 
GetConfigBool(std::string szValue)540 bool GetConfigBool(std::string szValue)
541 {
542 	stdlower(szValue);
543 	return (szValue == "yes");
544 }
545 
ParseConfigFile(const std::string & szConfigFile)546 bool ParseConfigFile(const std::string &szConfigFile)
547 {
548 	std::ifstream infile;
549 	std::string sLine;
550 	infile.open(szConfigFile.c_str());
551 	if (!infile.is_open())
552 	{
553 		_log.Log(LOG_ERROR, "Could not open Configuration file '%s'",szConfigFile.c_str());
554 		return false;
555 	}
556 	while (!infile.eof())
557 	{
558 		getline(infile, sLine);
559 		sLine.erase(std::remove(sLine.begin(), sLine.end(), '\r'), sLine.end());
560 		sLine = stdstring_trim(sLine);
561 		if (sLine.empty())
562 			continue;
563 		if (sLine.find('#') == 0)
564 			continue; //Skip lines starting with # (Comments)
565 
566 		size_t pos = sLine.find('=');
567 		if (pos == std::string::npos)
568 			continue; //invalid config line, should always contain xx=yy
569 		std::string szFlag = sLine.substr(0, pos);
570 		sLine = sLine.substr(pos + 1);
571 
572 		szFlag = stdstring_trim(szFlag);
573 		sLine = stdstring_trim(sLine);
574 
575 		if (szFlag == "http_port") {
576 			webserver_settings.listening_port = sLine;
577 		}
578 #ifdef WWW_ENABLE_SSL
579 		else if (szFlag == "ssl_port") {
580 			secure_webserver_settings.listening_port = sLine;
581 		}
582 		else if (szFlag == "ssl_cert") {
583 			secure_webserver_settings.cert_file_path = sLine;
584 		}
585 		else if (szFlag == "ssl_key") {
586 			secure_webserver_settings.private_key_file_path = sLine;
587 		}
588 		else if (szFlag == "ssl_dhparam") {
589 			secure_webserver_settings.tmp_dh_file_path = sLine;
590 		}
591 		else if (szFlag == "ssl_pass") {
592 			secure_webserver_settings.private_key_pass_phrase = sLine;
593 		}
594 		else if (szFlag == "ssl_method") {
595 			secure_webserver_settings.ssl_method = sLine;
596 		}
597 		else if (szFlag == "ssl_options") {
598 			secure_webserver_settings.ssl_options = sLine;
599 		}
600 #endif
601 		else if (szFlag == "http_root") {
602 			szWWWFolder = sLine;
603 		}
604 		else if (szFlag == "web_root") {
605 			szWebRoot = sLine;
606 		}
607 		else if (szFlag == "www_compress_mode") {
608 			if (sLine == "on")
609 				g_wwwCompressMode = http::server::WWW_USE_GZIP;
610 			else if (sLine == "off")
611 				g_wwwCompressMode = http::server::WWW_FORCE_NO_GZIP_SUPPORT;
612 			else if (sLine == "static")
613 				g_wwwCompressMode = http::server::WWW_USE_STATIC_GZ_FILES;
614 			else {
615 				_log.Log(LOG_ERROR, "Invalid www_compress_mode value in Configuration file '%s'", szConfigFile.c_str());
616 				return false;
617 			}
618 		}
619 		else if (szFlag == "cache") {
620 			g_bDontCacheWWW = !GetConfigBool(sLine);
621 		}
622 		else if (szFlag == "reset_password") {
623 			m_mainworker.m_bIgnoreUsernamePassword = GetConfigBool(sLine);
624 		}
625 		else if (szFlag == "log_file") {
626 			logfile = sLine;
627 		}
628 		else if (szFlag == "loglevel") {
629 			_log.SetLogFlags(sLine);
630 		}
631 		else if (szFlag == "debuglevel") {
632 			_log.SetDebugFlags(sLine);
633 		}
634 		else if (szFlag == "notimestamps") {
635 			_log.EnableLogTimestamps(!GetConfigBool(sLine));
636 		}
637 #ifndef WIN32
638 		else if (szFlag == "syslog") {
639 			g_bUseSyslog = true;
640 			logfacname = sLine;
641 		}
642 #endif
643 		else if (szFlag == "dbase_file") {
644 			dbasefile = sLine;
645 		}
646 		else if (szFlag == "startup_delay") {
647 			int DelaySeconds = atoi(sLine.c_str());
648 			_log.Log(LOG_STATUS, "Startup delay... waiting %d seconds...", DelaySeconds);
649 			sleep_seconds(DelaySeconds);
650 		}
651 		else if (szFlag == "updates") {
652 			g_bUseUpdater = GetConfigBool(sLine);
653 		}
654 		else if (szFlag == "php_cgi_path") {
655 			webserver_settings.php_cgi_path = sLine;
656 #ifdef WWW_ENABLE_SSL
657 			secure_webserver_settings.php_cgi_path = sLine;
658 #endif
659 		}
660 		else if (szFlag == "app_path") {
661 			szStartupFolder = sLine;
662 			FixFolderEnding(szStartupFolder);
663 		}
664 		else if (szFlag == "userdata_path") {
665 			szUserDataFolder = sLine;
666 			FixFolderEnding(szUserDataFolder);
667 		}
668 		else if (szFlag == "daemon_name") {
669 			daemonname = sLine;
670 		}
671 		else if (szFlag == "daemon") {
672 			g_bRunAsDaemon = GetConfigBool(sLine);
673 		}
674 		else if (szFlag == "pidfile") {
675 			pidfile = sLine;
676 		}
677 		else if (szFlag == "launch_browser") {
678 			bStartWebBrowser = GetConfigBool(sLine);
679 		}
680 	}
681 	infile.close();
682 
683 	return true;
684 }
685 
DisplayAppVersion()686 void DisplayAppVersion()
687 {
688 	_log.Log(LOG_STATUS, "Domoticz V%s (c)2012-%d GizMoCuz", szAppVersion.c_str(), ActYear);
689 	_log.Log(LOG_STATUS, "Build Hash: %s, Date: %s", szAppHash.c_str(), szAppDate.c_str());
690 }
691 
692 time_t m_LastHeartbeat = 0;
693 
694 #if defined WIN32
WinMain(_In_ HINSTANCE hInstance,_In_opt_ HINSTANCE hPrevInstance,_In_ LPSTR lpCmdLine,_In_ int nShowCmd)695 int WINAPI WinMain(_In_ HINSTANCE hInstance,_In_opt_ HINSTANCE hPrevInstance,_In_ LPSTR lpCmdLine,_In_ int nShowCmd)
696 #else
697 int main(int argc, char**argv)
698 #endif
699 {
700 #if defined WIN32
701 #ifndef _DEBUG
702 	CreateMutexA(0, FALSE, "Local\\Domoticz");
703 	if(GetLastError() == ERROR_ALREADY_EXISTS) {
704 		MessageBox(HWND_DESKTOP,"Another instance of Domoticz is already running!","Domoticz",MB_OK);
705 		return 1;
706 	}
707 #endif //_DEBUG
708 	RedirectIOToConsole();
709 #endif //WIN32
710 
711 	CCmdLine cmdLine;
712 
713 	// parse argc,argv
714 #if defined WIN32
715 	cmdLine.SplitLine(__argc, __argv);
716 #else
717 	cmdLine.SplitLine(argc, argv);
718 	//ignore pipe errors
719 	signal(SIGPIPE, SIG_IGN);
720 #endif
721 	bool bUseConfigFile = cmdLine.HasSwitch("-f");
722 	if (bUseConfigFile) {
723 		if (cmdLine.GetArgumentCount("-f") != 1)
724 		{
725 			_log.Log(LOG_ERROR, "Please specify the configuration file.");
726 			return 1;
727 		}
728 		std::string szConfigFile = cmdLine.GetSafeArgument("-f", 0, "");
729 		if (!ParseConfigFile(szConfigFile))
730 			return 1;
731 	}
732 	else {
733 		if (cmdLine.HasSwitch("-loglevel"))
734 		{
735 			std::string szLevel = cmdLine.GetSafeArgument("-loglevel", 0, "");
736 			_log.SetLogFlags(szLevel);
737 		}
738 		if (cmdLine.HasSwitch("-debuglevel"))
739 		{
740 			std::string szLevel = cmdLine.GetSafeArgument("-debuglevel", 0, "");
741 			_log.SetDebugFlags(szLevel);
742 		}
743 		if (cmdLine.HasSwitch("-notimestamps"))
744 		{
745 			_log.EnableLogTimestamps(false);
746 		}
747 		if (cmdLine.HasSwitch("-log"))
748 		{
749 			if (cmdLine.GetArgumentCount("-log") != 1)
750 			{
751 				_log.Log(LOG_ERROR, "Please specify an output log file");
752 				return 1;
753 			}
754 			logfile = cmdLine.GetSafeArgument("-log", 0, "domoticz.log");
755 		}
756 		if (cmdLine.HasSwitch("-approot"))
757 		{
758 			if (cmdLine.GetArgumentCount("-approot") != 1)
759 			{
760 				_log.Log(LOG_ERROR, "Please specify a APP root path");
761 				return 1;
762 			}
763 			std::string szroot = cmdLine.GetSafeArgument("-approot", 0, "");
764 			if (szroot.size() != 0) {
765 				szStartupFolder = szroot;
766 				FixFolderEnding(szStartupFolder);
767 			}
768 		}
769 	}
770 
771 	if (!logfile.empty())
772 		_log.SetOutputFile(logfile.c_str());
773 
774 	if (szStartupFolder.empty())
775 	{
776 #if !defined WIN32
777 		char szStartupPath[255];
778 		getExecutablePathName((char*)&szStartupPath,255);
779 		szStartupFolder=szStartupPath;
780 		if (szStartupFolder.find_last_of('/')!=std::string::npos)
781 			szStartupFolder=szStartupFolder.substr(0,szStartupFolder.find_last_of('/')+1);
782 #else
783 #ifndef _DEBUG
784 		char szStartupPath[255];
785 		char * p;
786 		GetModuleFileName(NULL, szStartupPath, sizeof(szStartupPath));
787 		p = szStartupPath + strlen(szStartupPath);
788 
789 		while (p >= szStartupPath && *p != '\\')
790 			p--;
791 
792 		if (++p >= szStartupPath)
793 			*p = 0;
794 		szStartupFolder=szStartupPath;
795 		size_t start_pos = szStartupFolder.find("\\Release\\");
796 		if(start_pos != std::string::npos) {
797 			szStartupFolder.replace(start_pos, 9, "\\domoticz\\");
798 			_log.Log(LOG_STATUS,"%s",szStartupFolder.c_str());
799 		}
800 #endif
801 #endif
802 	}
803 #if defined(__linux__)
804 	g_bIsWSL = IsWSL();
805 #endif
806 
807 	/* call srand once for the entire app */
808 	std::srand((unsigned int)std::time(nullptr));
809 	szRandomUUID = GenerateUUID();
810 
811 	GetAppVersion();
812 	DisplayAppVersion();
813 
814 #if !defined WIN32
815 	CheckForOnboardSensors();
816 #endif
817 	if (!szStartupFolder.empty())
818 		_log.Log(LOG_STATUS, "Startup Path: %s", szStartupFolder.c_str());
819 
820 	if (szWWWFolder.empty())
821 		szWWWFolder = szStartupFolder + "www";
822 	if (szUserDataFolder.empty())
823 		szUserDataFolder = szStartupFolder;
824 
825 	if (!bUseConfigFile) {
826 		if ((cmdLine.HasSwitch("-h")) || (cmdLine.HasSwitch("--help")) || (cmdLine.HasSwitch("/?")))
827 		{
828 			_log.Log(LOG_NORM, "%s", szHelp);
829 			return 0;
830 		}
831 		if ((cmdLine.HasSwitch("-version")) || (cmdLine.HasSwitch("--version")))
832 		{
833 			//Application version is already displayed
834 			//DisplayAppVersion();
835 			return 0;
836 		}
837 		if (cmdLine.HasSwitch("-userdata"))
838 		{
839 			if (cmdLine.GetArgumentCount("-userdata") != 1)
840 			{
841 				_log.Log(LOG_ERROR, "Please specify a path for user data to be stored");
842 				return 1;
843 			}
844 			std::string szroot = cmdLine.GetSafeArgument("-userdata", 0, "");
845 			if (szroot.size() != 0)
846 			{
847 				szUserDataFolder = szroot;
848 				FixFolderEnding(szUserDataFolder);
849 			}
850 		}
851 		if (cmdLine.HasSwitch("-startupdelay"))
852 		{
853 			if (cmdLine.GetArgumentCount("-startupdelay") != 1)
854 			{
855 				_log.Log(LOG_ERROR, "Please specify a startupdelay");
856 				return 1;
857 			}
858 			int DelaySeconds = atoi(cmdLine.GetSafeArgument("-startupdelay", 0, "").c_str());
859 			_log.Log(LOG_STATUS, "Startup delay... waiting %d seconds...", DelaySeconds);
860 			sleep_seconds(DelaySeconds);
861 		}
862 		if (cmdLine.HasSwitch("-wwwbind"))
863 		{
864 			if (cmdLine.GetArgumentCount("-wwwbind") != 1)
865 			{
866 				_log.Log(LOG_ERROR, "Please specify an address");
867 				return 1;
868 			}
869 			webserver_settings.listening_address = cmdLine.GetSafeArgument("-wwwbind", 0, "0.0.0.0");
870 		}
871 		if (cmdLine.HasSwitch("-www"))
872 		{
873 			if (cmdLine.GetArgumentCount("-www") != 1)
874 			{
875 				_log.Log(LOG_ERROR, "Please specify a port");
876 				return 1;
877 			}
878 			std::string wwwport = cmdLine.GetSafeArgument("-www", 0, "");
879 			int iPort = (int)atoi(wwwport.c_str());
880 			if ((iPort < 0) || (iPort > 32767))
881 			{
882 				_log.Log(LOG_ERROR, "Please specify a valid www port");
883 				return 1;
884 			}
885 			webserver_settings.listening_port = wwwport;
886 		}
887 		if (cmdLine.HasSwitch("-php_cgi_path"))
888 		{
889 			if (cmdLine.GetArgumentCount("-php_cgi_path") != 1)
890 			{
891 				_log.Log(LOG_ERROR, "Please specify the path to the php-cgi command");
892 				return 1;
893 			}
894 			webserver_settings.php_cgi_path = cmdLine.GetSafeArgument("-php_cgi_path", 0, "");
895 		}
896 		if (cmdLine.HasSwitch("-wwwroot"))
897 		{
898 			if (cmdLine.GetArgumentCount("-wwwroot") != 1)
899 			{
900 				_log.Log(LOG_ERROR, "Please specify a WWW root path");
901 				return 1;
902 			}
903 			std::string szroot = cmdLine.GetSafeArgument("-wwwroot", 0, "");
904 			if (szroot.size() != 0)
905 				szWWWFolder = szroot;
906 		}
907 	}
908 	webserver_settings.www_root = szWWWFolder;
909 	m_mainworker.SetWebserverSettings(webserver_settings);
910 #ifdef WWW_ENABLE_SSL
911 	if (!bUseConfigFile) {
912 		if (cmdLine.HasSwitch("-sslwww"))
913 		{
914 			if (cmdLine.GetArgumentCount("-sslwww") != 1)
915 			{
916 				_log.Log(LOG_ERROR, "Please specify a port");
917 				return 1;
918 			}
919 			std::string wwwport = cmdLine.GetSafeArgument("-sslwww", 0, "");
920 			int iPort = (int)atoi(wwwport.c_str());
921 			if ((iPort < 0) || (iPort > 32767))
922 			{
923 				_log.Log(LOG_ERROR, "Please specify a valid sslwww port");
924 				return 1;
925 			}
926 			secure_webserver_settings.listening_port = wwwport;
927 		}
928 		if (!webserver_settings.listening_address.empty()) {
929 			// Secure listening address has to be equal
930 			secure_webserver_settings.listening_address = webserver_settings.listening_address;
931 		}
932 		if (cmdLine.HasSwitch("-sslcert"))
933 		{
934 			if (cmdLine.GetArgumentCount("-sslcert") != 1)
935 			{
936 				_log.Log(LOG_ERROR, "Please specify a file path for your server certificate file");
937 				return 1;
938 			}
939 			secure_webserver_settings.cert_file_path = cmdLine.GetSafeArgument("-sslcert", 0, "");
940 			secure_webserver_settings.private_key_file_path = secure_webserver_settings.cert_file_path;
941 		}
942 		if (cmdLine.HasSwitch("-sslkey"))
943 		{
944 			if (cmdLine.GetArgumentCount("-sslkey") != 1)
945 			{
946 				_log.Log(LOG_ERROR, "Please specify a file path for your server SSL key file");
947 				return 1;
948 			}
949 			secure_webserver_settings.private_key_file_path = cmdLine.GetSafeArgument("-sslkey", 0, "");
950 		}
951 		if (cmdLine.HasSwitch("-sslpass"))
952 		{
953 			if (cmdLine.GetArgumentCount("-sslpass") != 1)
954 			{
955 				_log.Log(LOG_ERROR, "Please specify a passphrase to access to your server private key in certificate file");
956 				return 1;
957 			}
958 			secure_webserver_settings.private_key_pass_phrase = cmdLine.GetSafeArgument("-sslpass", 0, "");
959 		}
960 		if (cmdLine.HasSwitch("-sslmethod"))
961 		{
962 			if (cmdLine.GetArgumentCount("-sslmethod") != 1)
963 			{
964 				_log.Log(LOG_ERROR, "Please specify a SSL method");
965 				return 1;
966 			}
967 			secure_webserver_settings.ssl_method = cmdLine.GetSafeArgument("-sslmethod", 0, "");
968 		}
969 		if (cmdLine.HasSwitch("-ssloptions"))
970 		{
971 			if (cmdLine.GetArgumentCount("-ssloptions") != 1)
972 			{
973 				_log.Log(LOG_ERROR, "Please specify SSL options");
974 				return 1;
975 			}
976 			secure_webserver_settings.ssl_options = cmdLine.GetSafeArgument("-ssloptions", 0, "");
977 		}
978 		if (cmdLine.HasSwitch("-ssldhparam"))
979 		{
980 			if (cmdLine.GetArgumentCount("-ssldhparam") != 1)
981 			{
982 				_log.Log(LOG_ERROR, "Please specify a file path for the SSL DH parameters file");
983 				return 1;
984 			}
985 			secure_webserver_settings.tmp_dh_file_path = cmdLine.GetSafeArgument("-ssldhparam", 0, "");
986 		}
987 		if (cmdLine.HasSwitch("-php_cgi_path"))
988 		{
989 			if (cmdLine.GetArgumentCount("-php_cgi_path") != 1)
990 			{
991 				_log.Log(LOG_ERROR, "Please specify the path to the php-cgi command");
992 				return 1;
993 			}
994 			secure_webserver_settings.php_cgi_path = cmdLine.GetSafeArgument("-php_cgi_path", 0, "");
995 		}
996 	}
997 	secure_webserver_settings.www_root = szWWWFolder;
998 	m_mainworker.SetSecureWebserverSettings(secure_webserver_settings);
999 #endif
1000 	if (!bUseConfigFile) {
1001 		if (cmdLine.HasSwitch("-nowwwpwd"))
1002 		{
1003 			m_mainworker.m_bIgnoreUsernamePassword = true;
1004 		}
1005 		if (cmdLine.HasSwitch("-nocache"))
1006 		{
1007 			g_bDontCacheWWW = true;
1008 		}
1009 		if (cmdLine.HasSwitch("-wwwcompress"))
1010 		{
1011 			if (cmdLine.GetArgumentCount("-wwwcompress") != 1)
1012 			{
1013 				_log.Log(LOG_ERROR, "Please specify a compress mode");
1014 				return 1;
1015 			}
1016 			std::string szmode = cmdLine.GetSafeArgument("-wwwcompress", 0, "on");
1017 			if (szmode == "off")
1018 				g_wwwCompressMode = http::server::WWW_FORCE_NO_GZIP_SUPPORT;
1019 			else if (szmode == "static")
1020 				g_wwwCompressMode = http::server::WWW_USE_STATIC_GZ_FILES;
1021 		}
1022 	}
1023 	if (dbasefile.empty()) {
1024 		dbasefile = szUserDataFolder + "domoticz.db";
1025 #ifdef WIN32
1026 #ifndef _DEBUG
1027 		if (!IsUserAnAdmin())
1028 		{
1029 			char szPath[MAX_PATH];
1030 			HRESULT hr = SHGetFolderPath(NULL, CSIDL_COMMON_APPDATA, NULL, 0, szPath);
1031 			if (SUCCEEDED(hr))
1032 			{
1033 				std::string sPath = szPath;
1034 				sPath += "\\Domoticz";
1035 
1036 				DWORD dwAttr = GetFileAttributes(sPath.c_str());
1037 				BOOL bDirExists = (dwAttr != 0xffffffff && (dwAttr & FILE_ATTRIBUTE_DIRECTORY));
1038 				if (!bDirExists)
1039 				{
1040 					BOOL bRet = CreateDirectory(sPath.c_str(), NULL);
1041 					if (bRet == FALSE) {
1042 						MessageBox(0, "Error creating Domoticz directory in program data folder (%ProgramData%)!!", "Error:", MB_OK);
1043 					}
1044 				}
1045 				sPath += "\\domoticz.db";
1046 				dbasefile = sPath;
1047 			}
1048 		}
1049 #endif
1050 #endif
1051 	}
1052 	if (!bUseConfigFile) {
1053 		if (cmdLine.HasSwitch("-dbase"))
1054 		{
1055 			if (cmdLine.GetArgumentCount("-dbase") != 1)
1056 			{
1057 				_log.Log(LOG_ERROR, "Please specify a Database Name");
1058 				return 1;
1059 			}
1060 			dbasefile = cmdLine.GetSafeArgument("-dbase", 0, "domoticz.db");
1061 		}
1062 	}
1063 	m_sql.SetDatabaseName(dbasefile);
1064 
1065 	if (!bUseConfigFile) {
1066 		if (cmdLine.HasSwitch("-webroot"))
1067 		{
1068 			if (cmdLine.GetArgumentCount("-webroot") != 1)
1069 			{
1070 				_log.Log(LOG_ERROR, "Please specify a web root path");
1071 				return 1;
1072 			}
1073 			std::string szroot = cmdLine.GetSafeArgument("-webroot", 0, "");
1074 			if (szroot.size() != 0)
1075 				szWebRoot = szroot;
1076 		}
1077 		if (cmdLine.HasSwitch("-noupdates"))
1078 		{
1079 			g_bUseUpdater = false;
1080 		}
1081 	}
1082 
1083 #if defined WIN32
1084 	if (!bUseConfigFile) {
1085 		if (cmdLine.HasSwitch("-nobrowser"))
1086 		{
1087 			bStartWebBrowser = false;
1088 		}
1089 	}
1090 	//Init WinSock
1091 	WSADATA data;
1092 	WORD version;
1093 
1094 	version = (MAKEWORD(2, 2));
1095 	int ret = WSAStartup(version, &data);
1096 	if (ret != 0)
1097 	{
1098 		ret = WSAGetLastError();
1099 
1100 		if (ret == WSANOTINITIALISED)
1101 		{
1102 			_log.Log(LOG_ERROR, "Error: Winsock could not be initialized!");
1103 		}
1104 	}
1105 	CoInitializeEx(0, COINIT_MULTITHREADED);
1106 	CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL);
1107 #endif
1108 
1109 #ifndef WIN32
1110 	if (!bUseConfigFile) {
1111 		if (cmdLine.HasSwitch("-daemon"))
1112 		{
1113 			g_bRunAsDaemon = true;
1114 		}
1115 		if (cmdLine.HasSwitch("-daemonname"))
1116 		{
1117 			daemonname = cmdLine.GetSafeArgument("-daemonname", 0, DAEMON_NAME);
1118 		}
1119 
1120 		if (cmdLine.HasSwitch("-pidfile"))
1121 		{
1122 			pidfile = cmdLine.GetSafeArgument("-pidfile", 0, PID_FILE);
1123 		}
1124 
1125 		if (cmdLine.HasSwitch("-syslog"))
1126 		{
1127 			g_bUseSyslog = true;
1128 			logfacname = cmdLine.GetSafeArgument("-syslog", 0, "");
1129 			if (logfacname.length() == 0)
1130 			{
1131 				logfacname = "user";
1132 			}
1133 		}
1134 	}
1135 
1136 	if ((g_bRunAsDaemon)||(g_bUseSyslog))
1137 	{
1138 		int logfacility = 0;
1139 
1140 		for ( size_t idx = 0; idx < sizeof(facilities)/sizeof(facilities[0]); idx++ )
1141 		{
1142 			if (strcmp(facilities[idx].facname, logfacname.c_str()) == 0)
1143 			{
1144 				logfacility = facilities[idx].facvalue;
1145 				break;
1146 			}
1147 		}
1148 		if ( logfacility == 0 )
1149 		{
1150 			_log.Log(LOG_ERROR, "%s is an unknown syslog facility", logfacname.c_str());
1151 			return 1;
1152 		}
1153 
1154 		// _log.Log(LOG_STATUS, "syslog to %s (%x)", logfacname.c_str(), logfacility);
1155 		setlogmask(LOG_UPTO(LOG_INFO));
1156 		openlog(daemonname.c_str(), LOG_CONS | LOG_PERROR, logfacility);
1157 
1158 		syslog(LOG_INFO, "Domoticz is starting up....");
1159 	}
1160 
1161 	if (g_bRunAsDaemon)
1162 	{
1163 		/* Deamonize */
1164 		daemonize(szStartupFolder.c_str(), pidfile.c_str());
1165 	}
1166 	if ((g_bRunAsDaemon) || (g_bUseSyslog))
1167 	{
1168 		syslog(LOG_INFO, "Domoticz running...");
1169 	}
1170 #endif
1171 
1172 	if (!g_bRunAsDaemon)
1173 	{
1174 #ifndef WIN32
1175 		struct sigaction newSigAction;
1176 
1177 		/* Set up a signal handler */
1178 		newSigAction.sa_sigaction = signal_handler;
1179 		sigemptyset(&newSigAction.sa_mask);
1180 		newSigAction.sa_flags = SA_SIGINFO;
1181 
1182 		/* Signals to handle */
1183 		sigaction(SIGTERM, &newSigAction, NULL);    // catch term signal
1184 		sigaction(SIGINT,  &newSigAction, NULL);    // catch interrupt signal
1185 		sigaction(SIGSEGV, &newSigAction, NULL);    // catch segmentation fault signal
1186 		sigaction(SIGABRT, &newSigAction, NULL);    // catch abnormal termination signal
1187 		sigaction(SIGILL,  &newSigAction, NULL);    // catch invalid program image
1188 		sigaction(SIGFPE,  &newSigAction, NULL);    // catch floating point error
1189 		sigaction(SIGUSR1, &newSigAction, NULL);    // catch SIGUSR1 (used by watchdog)
1190 #else
1191 		signal(SIGINT, signal_handler);
1192 		signal(SIGTERM, signal_handler);
1193 #endif
1194 	}
1195 
1196 	// start Watchdog thread after daemonization
1197 	m_LastHeartbeat = mytime(NULL);
1198 	std::thread thread_watchdog(Do_Watchdog_Work);
1199 	SetThreadName(thread_watchdog.native_handle(), "Watchdog");
1200 
1201 	if (!m_mainworker.Start())
1202 	{
1203 		return 1;
1204 	}
1205 	m_StartTime = time(NULL);
1206 
1207 
1208 	/* now, lets get into an infinite loop of doing nothing. */
1209 #if defined WIN32
1210 #ifndef _DEBUG
1211 	RedirectIOToConsole();	//hide console
1212 #endif
1213 	InitWindowsHelper(hInstance, hPrevInstance, nShowCmd, m_mainworker.GetWebserverAddress(), atoi(m_mainworker.GetWebserverPort().c_str()), bStartWebBrowser);
1214 	MSG Msg;
1215 	while (!g_bStopApplication)
1216 	{
1217 		if (PeekMessage(&Msg, NULL, 0, 0, PM_NOREMOVE))
1218 		{
1219 			if (GetMessage(&Msg, NULL, 0, 0) > 0)
1220 			{
1221 				TranslateMessage(&Msg);
1222 				DispatchMessage(&Msg);
1223 			}
1224 		}
1225 		else
1226 			sleep_milliseconds(100);
1227 		m_LastHeartbeat = mytime(NULL);
1228 	}
1229 	TrayMessage(NIM_DELETE, NULL);
1230 #else
1231 	while ( !g_bStopApplication )
1232 	{
1233 		sleep_seconds(1);
1234 		m_LastHeartbeat = mytime(NULL);
1235 	}
1236 #endif
1237 	_log.Log(LOG_STATUS, "Closing application!...");
1238 	fflush(stdout);
1239 	_log.Log(LOG_STATUS, "Stopping worker...");
1240 	try
1241 	{
1242 		m_mainworker.Stop();
1243 	}
1244 	catch (...)
1245 	{
1246 
1247 	}
1248 #ifndef WIN32
1249 	if (g_bRunAsDaemon)
1250 	{
1251 		syslog(LOG_INFO, "Domoticz stopped...");
1252 		daemonShutdown();
1253 
1254 		// Delete PID file
1255 		remove(pidfile.c_str());
1256 	}
1257 #else
1258 	// Release WinSock
1259 	WSACleanup();
1260 	CoUninitialize();
1261 #endif
1262 	g_stop_watchdog = true;
1263 	thread_watchdog.join();
1264 	return 0;
1265 }
1266 
1267