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, <ime);
456 ActYear = ltime.tm_year + 1900;
457 strftime(szTmp, 200, "%Y-%m-%d %H:%M:%S", <ime);
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