2016-06-17 01:25:40 +02:00
# include <cassert>
# include <csignal>
2017-03-21 17:55:46 +01:00
# include <stdlib.h>
2019-01-07 18:13:49 +01:00
# include <stdio.h>
2016-08-07 18:39:45 +02:00
2020-05-12 19:51:19 +02:00
# if !defined(__APPLE__) && !defined(_WIN32)
2016-08-07 18:39:45 +02:00
/* prctl is Linux only */
# include <sys/prctl.h>
# endif
2020-05-12 19:51:19 +02:00
// getpid()
# ifdef _WIN32
2020-07-12 09:18:40 +02:00
# include "console.h"
2020-05-12 19:51:19 +02:00
# include <process.h>
# else
# include <unistd.h>
# endif
2016-08-07 18:39:45 +02:00
2016-07-10 12:18:40 +02:00
# include <exception>
2016-06-17 01:25:40 +02:00
# include <QCoreApplication>
2017-08-01 17:29:47 +02:00
# include <QApplication>
2016-06-17 01:25:40 +02:00
# include <QLocale>
# include <QFile>
2016-08-06 08:28:42 +02:00
# include <QString>
2016-09-17 00:40:29 +02:00
# include <QResource>
# include <QDir>
# include <QStringList>
2017-08-01 17:29:47 +02:00
# include <QSystemTrayIcon>
2016-06-17 01:25:40 +02:00
# include "HyperionConfig.h"
# include <utils/Logger.h>
2016-10-13 21:59:10 +02:00
# include <utils/FileUtils.h>
2016-08-28 15:10:43 +02:00
# include <commandline/Parser.h>
# include <commandline/IntOption.h>
2019-09-17 21:33:46 +02:00
# include <../../include/db/AuthTable.h>
2016-06-17 01:25:40 +02:00
2020-05-12 19:51:19 +02:00
# include "detectProcess.h"
2017-08-01 17:29:47 +02:00
# ifdef ENABLE_X11
# include <X11/Xlib.h>
# endif
2016-06-17 01:25:40 +02:00
# include "hyperiond.h"
2017-08-01 17:29:47 +02:00
# include "systray.h"
2016-06-17 01:25:40 +02:00
2016-08-28 15:10:43 +02:00
using namespace commandline ;
2016-06-17 01:25:40 +02:00
2016-12-03 21:11:52 +01:00
# define PERM0664 QFileDevice::ReadOwner | QFileDevice::ReadGroup | QFileDevice::ReadOther | QFileDevice::WriteOwner | QFileDevice::WriteGroup
2020-05-12 19:51:19 +02:00
# ifndef _WIN32
2016-06-17 01:25:40 +02:00
void signal_handler ( const int signum )
{
2019-07-14 22:43:22 +02:00
// Hyperion Managment instance
2020-05-12 19:51:19 +02:00
HyperionIManager * _hyperion = HyperionIManager : : getInstance ( ) ;
2019-04-19 08:22:23 +02:00
2020-05-12 19:51:19 +02:00
if ( signum = = SIGCHLD )
2017-01-29 21:20:12 +01:00
{
// only quit when a registered child process is gone
// currently this feature is not active ...
return ;
}
2019-04-19 08:22:23 +02:00
else if ( signum = = SIGUSR1 )
{
if ( _hyperion ! = nullptr )
{
2020-03-26 19:37:39 +01:00
_hyperion - > toggleStateAllInstances ( false ) ;
2019-04-19 08:22:23 +02:00
}
return ;
}
else if ( signum = = SIGUSR2 )
{
if ( _hyperion ! = nullptr )
{
2020-03-26 19:37:39 +01:00
_hyperion - > toggleStateAllInstances ( true ) ;
2019-04-19 08:22:23 +02:00
}
return ;
}
2019-07-14 22:43:22 +02:00
2016-06-17 01:25:40 +02:00
QCoreApplication : : quit ( ) ;
// reset signal handler to default (in case this handler is not capable of stopping)
signal ( signum , SIG_DFL ) ;
}
2020-05-12 19:51:19 +02:00
# endif
2016-06-17 01:25:40 +02:00
2017-08-01 17:29:47 +02:00
QCoreApplication * createApplication ( int & argc , char * argv [ ] )
{
bool isGuiApp = false ;
bool forceNoGui = false ;
// command line
for ( int i = 1 ; i < argc ; + + i )
{
if ( qstrcmp ( argv [ i ] , " --desktop " ) = = 0 )
{
isGuiApp = true ;
}
else if ( qstrcmp ( argv [ i ] , " --service " ) = = 0 )
{
isGuiApp = false ;
forceNoGui = true ;
}
}
// on osx/windows gui always available
2020-05-12 19:51:19 +02:00
# if defined(__APPLE__) || defined(_WIN32)
2017-08-01 17:29:47 +02:00
isGuiApp = true & & ! forceNoGui ;
# else
if ( ! forceNoGui )
{
// if x11, then test if xserver is available
# ifdef ENABLE_X11
Display * dpy = XOpenDisplay ( NULL ) ;
2017-10-12 11:55:03 +02:00
if ( dpy ! = NULL )
2017-08-01 17:29:47 +02:00
{
XCloseDisplay ( dpy ) ;
isGuiApp = true ;
}
2017-08-04 19:44:52 +02:00
# endif
2017-08-01 17:29:47 +02:00
}
# endif
if ( isGuiApp )
{
QApplication * app = new QApplication ( argc , argv ) ;
2020-02-16 16:24:33 +01:00
// add optional library path
app - > addLibraryPath ( QApplication : : applicationDirPath ( ) + " /../lib " ) ;
2017-08-01 17:29:47 +02:00
app - > setApplicationDisplayName ( " Hyperion " ) ;
2018-12-31 15:48:29 +01:00
app - > setWindowIcon ( QIcon ( " :/hyperion-icon-32px.png " ) ) ;
2019-07-14 22:43:22 +02:00
return app ;
2017-08-01 17:29:47 +02:00
}
QCoreApplication * app = new QCoreApplication ( argc , argv ) ;
app - > setApplicationName ( " Hyperion " ) ;
app - > setApplicationVersion ( HYPERION_VERSION ) ;
2020-02-16 16:24:33 +01:00
// add optional library path
app - > addLibraryPath ( QApplication : : applicationDirPath ( ) + " /../lib " ) ;
2017-08-01 17:29:47 +02:00
return app ;
}
2016-06-17 01:25:40 +02:00
int main ( int argc , char * * argv )
{
2020-05-12 19:51:19 +02:00
# ifndef _WIN32
2017-03-21 17:55:46 +01:00
setenv ( " AVAHI_COMPAT_NOWARN " , " 1 " , 1 ) ;
2020-05-12 19:51:19 +02:00
# endif
2016-06-21 21:41:26 +02:00
// initialize main logger and set global log level
2020-05-12 19:51:19 +02:00
Logger * log = Logger : : getInstance ( " MAIN " ) ;
2016-06-23 13:48:49 +02:00
Logger : : setLogLevel ( Logger : : WARNING ) ;
2016-06-17 01:25:40 +02:00
2019-07-14 22:43:22 +02:00
// check if we are running already an instance
// TODO Allow one session per user
// http://www.qtcentre.org/threads/44489-Get-Process-ID-for-a-running-application
QStringList listOfPids ;
2020-05-12 19:51:19 +02:00
# ifdef _WIN32
const char * processName = " hyperiond.exe " ;
# else
const char * processName = " hyperiond " ;
# endif
if ( getProcessIdsByProcessName ( processName , listOfPids ) > 1 )
2019-07-14 22:43:22 +02:00
{
Error ( log , " The Hyperion Daemon is already running, abort start " ) ;
return 0 ;
}
2016-06-17 01:25:40 +02:00
// Initialising QCoreApplication
2019-07-14 22:43:22 +02:00
QScopedPointer < QCoreApplication > app ( createApplication ( argc , argv ) ) ;
2017-08-01 17:29:47 +02:00
bool isGuiApp = ( qobject_cast < QApplication * > ( app . data ( ) ) ! = 0 & & QSystemTrayIcon : : isSystemTrayAvailable ( ) ) ;
2016-06-17 01:25:40 +02:00
2020-05-12 19:51:19 +02:00
# ifndef _WIN32
2016-06-17 01:25:40 +02:00
signal ( SIGINT , signal_handler ) ;
signal ( SIGTERM , signal_handler ) ;
2017-01-29 21:20:12 +01:00
signal ( SIGABRT , signal_handler ) ;
2016-06-17 01:25:40 +02:00
signal ( SIGCHLD , signal_handler ) ;
2016-07-18 09:23:55 +02:00
signal ( SIGPIPE , signal_handler ) ;
2019-04-19 08:22:23 +02:00
signal ( SIGUSR1 , signal_handler ) ;
signal ( SIGUSR2 , signal_handler ) ;
2020-05-12 19:51:19 +02:00
# endif
2016-06-17 01:25:40 +02:00
// force the locale
setlocale ( LC_ALL , " C " ) ;
QLocale : : setDefault ( QLocale : : c ( ) ) ;
2016-08-28 15:10:43 +02:00
Parser parser ( " Hyperion Daemon " ) ;
parser . addHelpOption ( ) ;
2016-06-17 01:25:40 +02:00
2019-09-17 21:33:46 +02:00
BooleanOption & versionOption = parser . add < BooleanOption > ( 0x0 , " version " , " Show version information " ) ;
Option & userDataOption = parser . add < Option > ( ' u ' , " userdata " , " Overwrite user data path, defaults to home directory of current user (%1) " , QDir : : homePath ( ) + " /.hyperion " ) ;
BooleanOption & resetPassword = parser . add < BooleanOption > ( 0x0 , " resetPassword " , " Lost your password? Reset it with this option back to 'hyperion' " ) ;
2020-06-17 20:59:26 +02:00
BooleanOption & deleteDB = parser . add < BooleanOption > ( 0x0 , " deleteDatabase " , " Start all over? This Option will delete the database " ) ;
2019-09-17 21:33:46 +02:00
BooleanOption & silentOption = parser . add < BooleanOption > ( ' s ' , " silent " , " do not print any outputs " ) ;
BooleanOption & verboseOption = parser . add < BooleanOption > ( ' v ' , " verbose " , " Increase verbosity " ) ;
BooleanOption & debugOption = parser . add < BooleanOption > ( ' d ' , " debug " , " Show debug messages " ) ;
2020-07-12 09:18:40 +02:00
# ifdef WIN32
BooleanOption & consoleOption = parser . add < BooleanOption > ( ' c ' , " console " , " Open a console window to view log output " ) ;
# endif
parser . add < BooleanOption > ( 0x0 , " desktop " , " show systray on desktop " ) ;
parser . add < BooleanOption > ( 0x0 , " service " , " force hyperion to start as console service " ) ;
2019-09-17 21:33:46 +02:00
Option & exportEfxOption = parser . add < Option > ( 0x0 , " export-effects " , " export effects to given path " ) ;
2016-09-17 00:40:29 +02:00
2019-07-14 22:43:22 +02:00
parser . process ( * qApp ) ;
2016-06-17 01:25:40 +02:00
2020-07-12 09:18:40 +02:00
# ifdef WIN32
if ( parser . isSet ( consoleOption ) )
{
CreateConsole ( ) ;
}
# endif
2016-06-23 13:48:49 +02:00
int logLevelCheck = 0 ;
2016-08-28 15:10:43 +02:00
if ( parser . isSet ( silentOption ) )
2016-06-23 13:48:49 +02:00
{
Logger : : setLogLevel ( Logger : : OFF ) ;
logLevelCheck + + ;
}
2016-08-28 15:10:43 +02:00
if ( parser . isSet ( verboseOption ) )
2016-06-23 13:48:49 +02:00
{
Logger : : setLogLevel ( Logger : : INFO ) ;
logLevelCheck + + ;
}
2016-08-28 15:10:43 +02:00
if ( parser . isSet ( debugOption ) )
2016-06-23 13:48:49 +02:00
{
Logger : : setLogLevel ( Logger : : DEBUG ) ;
logLevelCheck + + ;
}
if ( logLevelCheck > 1 )
{
2019-01-07 18:13:49 +01:00
Error ( log , " aborting, because options --silent --verbose --debug can't be used together " ) ;
2016-06-23 13:48:49 +02:00
return 0 ;
}
2016-06-17 01:25:40 +02:00
2016-08-28 15:10:43 +02:00
if ( parser . isSet ( versionOption ) )
2016-06-17 01:25:40 +02:00
{
2019-07-14 22:43:22 +02:00
std : : cout
2020-06-28 23:05:32 +02:00
< < " Hyperion Ambilight Deamon " < < std : : endl
2019-07-14 22:43:22 +02:00
< < " \t Version : " < < HYPERION_VERSION < < " ( " < < HYPERION_BUILD_ID < < " ) " < < std : : endl
< < " \t Build Time: " < < __DATE__ < < " " < < __TIME__ < < std : : endl ;
2016-06-17 01:25:40 +02:00
return 0 ;
}
2016-09-17 00:40:29 +02:00
if ( parser . isSet ( exportEfxOption ) )
{
Q_INIT_RESOURCE ( EffectEngine ) ;
QDir directory ( " :/effects/ " ) ;
QDir destDir ( exportEfxOption . value ( parser ) ) ;
if ( directory . exists ( ) & & destDir . exists ( ) )
{
std : : cout < < " extract to folder: " < < std : : endl ;
2016-10-13 22:58:16 +02:00
QStringList filenames = directory . entryList ( QStringList ( ) < < " * " , QDir : : Files , QDir : : Name | QDir : : IgnoreCase ) ;
2016-12-03 21:11:52 +01:00
QString destFileName ;
2020-07-12 09:19:59 +02:00
for ( const QString & filename : filenames )
2016-09-17 00:40:29 +02:00
{
2016-12-03 21:11:52 +01:00
destFileName = destDir . dirName ( ) + " / " + filename ;
if ( QFile : : exists ( destFileName ) )
QFile : : remove ( destFileName ) ;
2017-10-12 11:55:03 +02:00
2016-09-17 00:40:29 +02:00
std : : cout < < " Extract: " < < filename . toStdString ( ) < < " ... " ;
2016-12-03 21:11:52 +01:00
if ( QFile : : copy ( QString ( " :/effects/ " ) + filename , destFileName ) )
2016-09-17 00:40:29 +02:00
{
2016-12-03 21:11:52 +01:00
QFile : : setPermissions ( destFileName , PERM0664 ) ;
2016-09-17 00:40:29 +02:00
std : : cout < < " ok " < < std : : endl ;
}
else
{
std : : cout < < " error, aborting " < < std : : endl ;
return 1 ;
}
}
return 0 ;
}
Error ( log , " can not export to %s " , exportEfxOption . getCString ( parser ) ) ;
return 1 ;
}
2016-10-13 21:59:10 +02:00
2016-07-10 12:18:40 +02:00
int rc = 1 ;
2019-08-15 23:49:32 +02:00
2016-07-10 12:18:40 +02:00
try
{
2019-08-17 09:44:57 +02:00
// handle and create userDataPath for user data, default path is home directory + /.hyperion
2019-08-15 23:49:32 +02:00
// NOTE: No further checks inside Hyperion. FileUtils::writeFile() will resolve permission errors and others that occur during runtime
2019-08-17 09:44:57 +02:00
QString userDataPath ( userDataOption . value ( parser ) ) ;
QDir mDir ( userDataPath ) ;
QFileInfo mFi ( userDataPath ) ;
if ( ! mDir . mkpath ( userDataPath ) | | ! mFi . isWritable ( ) | | ! mDir . isReadable ( ) )
throw std : : runtime_error ( " The user data path ' " + mDir . absolutePath ( ) . toStdString ( ) + " ' can't be created or isn't read/writeable. Please setup permissions correctly! " ) ;
Info ( log , " Set user data path to '%s' " , QSTRING_CSTR ( mDir . absolutePath ( ) ) ) ;
2019-08-15 23:49:32 +02:00
2019-09-17 21:33:46 +02:00
// reset Password without spawning daemon
if ( parser . isSet ( resetPassword ) )
{
AuthTable * table = new AuthTable ( userDataPath ) ;
if ( table - > resetHyperionUser ( ) ) {
Info ( log , " Password reset successfull " ) ;
delete table ;
exit ( 0 ) ;
} else {
Error ( log , " Failed to reset password! " ) ;
delete table ;
exit ( 1 ) ;
}
}
2020-06-17 20:59:26 +02:00
// delete database before start
if ( parser . isSet ( deleteDB ) )
{
QString dbFile = mDir . absolutePath ( ) + " /db/hyperion.db " ;
if ( QFile : : exists ( dbFile ) )
{
if ( ! QFile : : remove ( dbFile ) )
{
Info ( log , " Failed to delete Database! " ) ;
exit ( 1 ) ;
}
}
}
2019-08-15 23:49:32 +02:00
HyperionDaemon * hyperiond = nullptr ;
try
{
2019-08-17 09:44:57 +02:00
hyperiond = new HyperionDaemon ( userDataPath , qApp , bool ( logLevelCheck ) ) ;
2019-08-15 23:49:32 +02:00
}
catch ( std : : exception & e )
{
Error ( log , " Hyperion Daemon aborted: %s " , e . what ( ) ) ;
throw ;
}
2016-07-10 12:18:40 +02:00
// run the application
2017-08-01 17:29:47 +02:00
if ( isGuiApp )
{
Info ( log , " start systray " ) ;
QApplication : : setQuitOnLastWindowClosed ( false ) ;
2018-12-30 22:07:53 +01:00
SysTray tray ( hyperiond ) ;
2017-08-01 17:29:47 +02:00
tray . hide ( ) ;
rc = ( qobject_cast < QApplication * > ( app . data ( ) ) ) - > exec ( ) ;
}
else
{
rc = app - > exec ( ) ;
}
Info ( log , " Application closed with code %d " , rc ) ;
2019-08-15 23:49:32 +02:00
delete hyperiond ;
2016-07-10 12:18:40 +02:00
}
catch ( std : : exception & e )
{
2019-08-15 23:49:32 +02:00
Error ( log , " Hyperion aborted: %s " , e . what ( ) ) ;
2016-07-10 12:18:40 +02:00
}
2016-06-17 01:25:40 +02:00
2016-06-21 21:41:26 +02:00
// delete components
Logger : : deleteInstance ( ) ;
2016-06-17 01:25:40 +02:00
return rc ;
}