Error in Daemon/Service app while compiling



  • Hi Everybody, not sure if it is appropriate to post my code here for a review but i am completely stuck with it.
    Project_Goal: try to learn how to implement daemon/service app. It is intended to be a server which listens to udp/tcp ports and does some magic. All necessary information is being received from conf-file.
    Description: project consist from a few classes:

    • controller - class process commands entered from terminal to start service/daemon.

    • read_config - class process config file which set rules how daemon/service has to work

    • tcp-server - class implement first of two daemons/services ( 2nd will be udp-server ) .

    Main information got from "qt-solutions/qtservice/doc" manual and tcp-server completely and controller partially based on the code found here.

    The code is
    main.cpp

    #include "controller.hpp"
    
    int main( int argc, char **argv ) {
        Controller ctl(argc, argv);
        ctl.processCommand( argc, argv );
    
        return ctl.exec();
    }
    
    

    controller.hpp

    #ifndef CONTROLLER_H
    #define CONTROLLER_H
    
    #include <QTextStream>
    #include <QString>
    #include <QScopedPointer>
    #include "qtservice.h"
    
    #include "read_config.hpp"
    #include "tcp_server.hpp"
    
    class Controller: public QtService< QCoreApplication > {
    public:
        Controller( int argc, char **argv );
        ~Controller() {}
    
        void displayControlOptions();
        void processCommand( int, char **const );
    
        void setCommand( const char *command );
        void setProgName( const char *prName );
        void setAcName( const char *acName );
        void setPswd( const char *pswd );
    
        void start() override;
        void pause() override;
        void resume() override;
    
    private:
        const QString &getCommand() const;
        const QString &getProgName() const;
        const QString &getAcName() const;
        const QString &getPswd() const;
    
        QString name;
        QString command;
        QString account;
        QString password;
    
        QTextStream *pOut; // change to smart ptr later
        Controller *ctl; // change to smart ptr later
        TcpServer *ptrDTcpServer; // change to smart ptr later
        QScopedPointer< ReadConfig > pConfContent;
    };
    
    #endif // CONTROLLER_H
    
    

    controller.cpp

    #include <QTextStream>
    #include "qtservice.h"
    #include <QDebug>
    
    #include "controller.hpp"
    #include "app_setup.hpp"
    
    Controller::Controller( int argc, char **argv )
        : QtService< QCoreApplication >( argc, argv, "Daemon" ) {
        pOut = new QTextStream( stdout );
        setServiceFlags( QtServiceBase::CanBeSuspended );
    }
    
    void Controller::processCommand( int argc, char **const argv ) {
        if ( argc == 1 )
            setProgName( argv[ 0 ] );
        else if ( argc > 1 )
            setCommand( argv[ 1 ] );
        else if ( argc > 2 )
            setAcName( argv[ 2 ] );
        else if ( argc > 3 )
            setPswd( argv[ 3 ] );
    
        ctl = new Controller( argc, argv );
        if ( getCommand().isEmpty() ) {
            *pOut << "No commands were defined.\nDefault settings will be used";
            ctl->start();
        } else {
            if ( 0 == QString::compare( getCommand(), "-i",  Qt::CaseInsensitive ) || 0 == QString::compare( getCommand(), "--init", Qt::CaseInsensitive ) ) {
                  ctl->start();
            } else if ( 0 == QString::compare( getCommand(), "-c",  Qt::CaseInsensitive ) || 0 == QString::compare( getCommand(), "--config",  Qt::CaseInsensitive ) ) {
    
            } else if ( 0 == QString::compare( getCommand(), "-s",  Qt::CaseInsensitive ) || 0 == QString::compare( getCommand(), "--stop",  Qt::CaseInsensitive ) ) {
                    ctl->pause();
                        *pOut << getProgName() + "was stopped sucessfully\n";
            } else if ( 0 == QString::compare( getCommand(), "-u",  Qt::CaseInsensitive ) || 0 == QString::compare( getCommand(),"--uninstall",  Qt::CaseInsensitive ) ) {
                /* not yet implemented */
            } else if ( 0 == QString::compare( getCommand(), "-v",  Qt::CaseInsensitive ) || 0 == QString::compare( getCommand(), "--version",  Qt::CaseInsensitive ) ) {
                /* not yet implemented */
            } else if ( 0 == QString::compare( getCommand(), "-h",  Qt::CaseInsensitive ) || 0 == QString::compare( getCommand(), "--help",  Qt::CaseInsensitive ) ) {
                displayControlOptions();
            }
        }
    }
    
    void Controller::displayControlOptions() {
        *pOut << getProgName() + " - daemon processing udp/tcp data\n\n"
               "SYNOPSIS\n" +
               getProgName() + " [ OPTION ]\n\n"
               "DESCRIPTION\n"
               "-i, --init\n initialize " + getProgName() + " \n\n"
               "-c, --config\n reconfigurate " + getProgName() + " program due to settings in config file\n\n"
               "-s, --stop\n program " + getProgName() + " \n\n"
               "-u, --uninstall\n uninstal " + getProgName() + " \n\n"
               "-v, --version\n display " + getProgName() + " version"
               "-h, --help\n display this help";
        qDebug() << "Change all appearance of the word \"program\""
                    " to the name of this program";
    }
    
    const QString &Controller::getCommand() const {
        return command;
    }
    
    void Controller::setCommand( const char *command ) {
        if ( 0 != QString( command ).length() )
            this->command = command;
        else {
            qDebug() << "Something went wrong";
            qDebug() << "Add writing to log";
        }
    }
    
    const QString &Controller::getProgName() const {
        return name;
    }
    
    void Controller::setProgName( const char *name ) {
        if ( 0 != QString( name ).length() )
            this->name = name;
        else {
            qDebug() << "Something went wrong. program name was not set";
            qDebug() << "Add writing to log";
        }
    }
    
    void Controller::setAcName( const char *accName ) {
        if ( 0 != QString( accName ).length() )
            account = accName;
        else {
            qDebug() << "Something went wrong. account was not set";
            qDebug() << "Add writing to log";
        }
    }
    
    const QString &Controller::getAcName() const {
        return account;
    }
    
    void Controller::setPswd( const char *pswd ) {
        if ( 0 != QString( pswd ).length() )
            password = pswd;
        else {
            qDebug() << "Something went wrong. password was not set";
            qDebug() << "Add writing to log";
        }
    }
    
    const QString &Controller::getPswd() const {
        return password;
    }
    
    void Controller::start() {
        QCoreApplication *app = application();
        quint16 port = pConfContent.data()->getRcvdPortNum();
        QString iAdr = pConfContent.data()->getIpAddress();
        ptrDTcpServer = new TcpServer( iAdr, port, app );
    
        if ( !ptrDTcpServer->isListening() ) {
            logMessage( QString( "Failed to bind to port: %1 adress: %1" ).
                        arg( ptrDTcpServer->serverPort() ).
                        arg( ( ptrDTcpServer->serverAddress() ).toString() ), QtServiceBase::Error );
    
            qDebug() << "Failed to bind to port: " << ptrDTcpServer->serverPort()
                     << "adress: " << ptrDTcpServer->serverAddress();
            app->quit();
        }
    }
    
    void Controller::pause() {
      ptrDTcpServer->pause();
    }
    
    void Controller::resume() {
        ptrDTcpServer->resume();
    }
    

    read_config.hpp

    #ifndef READ_CONFIG_HPP
    #define READ_CONFIG_HPP
    
    #include <QString>
    #include <QtGlobal>
    #include <QList>
    
    class ReadConfig {
    
        public:
            ReadConfig();
            explicit ReadConfig( const ReadConfig & );
            ~ReadConfig(){}
    
            const QString &getSrcFilePath() const;
            const QString &getDstFilePath() const;
            const QString &getProtocolType() const;
            const QString &getIpAddress() const;
            const QString &getMacAddress() const;
            const QList< ReadConfig > &getConfigList() const;
            quint16 getRcvdPortNum() const;
            quint16 getDstPortNum() const;
            bool isFileOrStream() const;
    
        private:
            void readFromFile();
            void parseStr( QString & );
            bool isIpAdrValid( const QString &iAdr );
    
            void setSrcFilePath( const QString &src );
            void setDstFilePath( const QString &dst );
            void setProtocolType( const QString &protocol );
            void setIpAddr( const QString &iAdr );
            void setMacAddr( const QString &mAdr );
            void setRcvdPortNum( const quint16 portN );
            void setDstPortNum( const quint16 portN );
            void setConfigList();
    
            QString srcDirPath;     // directory path from which to read files
            QString dstDirPath;     // directory path where to load files
            QString protocolType;   // protocol type: udp/tcp
            QString ipAdr;          // ip address
            QString macAdr;         // mac address
            quint16 rcvdPortNum;    // port number from which datum are recieved
            quint16 dstPortNum;    // port number from which datum are recieved
            quint16 processNum;    // port number from which datum are recieved
            QList< ReadConfig > setupList;
    };
    
    #endif // READ_CONFIG_HPP
    
    

    read_config.hpp

    #include <cstdint>
    
    #include <QDir>
    #include <QDirIterator>
    #include <QFile>
    #include <QFileInfo>
    #include <QTextStream>
    #include <QStringList>
    #include <QList>
    #include <QChar>
    #include <QHostAddress>
    #include <QDebug>
    
    #include "read_config.hpp"
    
    #define CONFIG_DIR_PATH "://config"
    
    // pre-defined config fields
    #define READ          "read"            // define source of reading file or stream
    #define PROTOCOL      "protocol"        // protocol type: udp
    #define MAC           "mac"             // define field devoted to mac address
    #define IP            "ip"              // define field devoted to ip address
    #define RCVD_PORT     "rcvd_port"       // define field devoted to port address
    #define DST_PORT      "dst_port"        // define field devoted to port address
    #define SRC           "src"             // define field devoted to src dir path
    #define DST           "dst"             // define field devoted to dst dir path
    #define PN            "pn"              // datagram's size
    
    // ========== util functions prototypes ===========
    static bool isPortReserved( const quint16 );
    // static bool isAlgValid( const QString &chkSumAlg );
    
    ReadConfig::ReadConfig()
        : rcvdPortNum( 6700 ),
          dstPortNum( 6600 ),
          processNum( 1 ) {
        readFromFile();
    }
    
    ReadConfig::ReadConfig( const ReadConfig &config )
        : srcDirPath( config.srcDirPath ),
          dstDirPath( config.dstDirPath ),
          ipAdr( config.ipAdr ),
          macAdr ( config.macAdr ),
          rcvdPortNum( config.rcvdPortNum ),
          dstPortNum( config.dstPortNum ),
          processNum( config.processNum ) {
        readFromFile();
    }
    
    void ReadConfig::readFromFile() {
          QFile file( CONFIG_DIR_PATH );
        if ( !file.exists() )
            qDebug() << "File doesn't exist\n";         // throw error here
    
        // set appropriate error messsages for this check
        if ( !file.open( QIODevice::ReadOnly ) )
            qDebug() << "Couldn't open the file\n";     // throw error here
    
        QTextStream out( &file );
        QString tempStr;
        while( out.readLineInto( &tempStr ) ) {
            parseStr( tempStr );
            setupList.append( *this );
         }
    
        file.close();
    }
    
    void ReadConfig::parseStr( QString &str ) {
        if ( 0 == str.indexOf( "//" ) )     // check if the whole line is a comment
            return;
    
        if ( str.indexOf( "//" ) != -1 )    // check if the current line contains comments
            str = str.left( str.indexOf( "//" ) );
    
        QStringList strList = str.split( QRegExp( "\\s+") );
        qint32 index = 0;
        for ( QStringList::const_iterator it = strList.constBegin(); it != strList.constEnd(); ++it ) {
            // look for src field value
            if ( ( *it ).contains( SRC ) ) {
                index = ( *it ).indexOf( ':' );
                if ( index != -1 ) {
                    // count number of characters to be returned by right functions
                    // by this line index == index pos of ':' character
                    index = ( *it ).size() - ( index + 1 );
                    setSrcFilePath( ( *it ).right( index ) );
                }
            }
            // look for dst field value
            if ( ( *it ).contains( DST ) ) {
                index = ( *it ).indexOf( ':' );
                if ( index != -1 ) {
                    // count number of characters to be returned by right functions
                    // by this line index == index pos of ':' character
                    index = ( *it ).size() - ( index + 1 );
                    setDstFilePath( ( *it ).right( index ) );
                }
            }
            // look for protocol type field value
            if ( ( *it ).contains( PROTOCOL ) ) {
                index = ( *it ).indexOf( ':' );
                if ( index != -1 ) {
                    // count number of characters to be returned by right functions
                    // by this line index == index pos of ':' character
                    index = ( *it ).size() - ( index + 1 );
                    setProtocolType( ( *it ).right( index ) );
                }
            }
            // look for ipAdr field value
            if ( ( *it ).contains( IP ) ) {
                index = ( *it ).indexOf( ':' );
                if ( index != -1 ) {
                    // count number of characters to be returned by right functions
                    // by this line index == index pos of ':' character
                    index = ( *it ).size() - ( index + 1 );
                    setIpAddr( ( *it ).right( index ) );
                }
            }
            // look for macAdr field value
            if ( ( *it ).contains( MAC ) ) {
                index = ( *it ).indexOf( ':' );
                if ( index != -1 ) {
                    // count number of characters to be returned by right functions
                    // by this line index == index pos of ':' character
                    index = ( *it ).size() - ( index + 1 );
                    setMacAddr( ( *it ).right( index ) );
                }
            }        
            // look for port field value
            if ( ( *it ).contains( RCVD_PORT ) ) {
                index = ( *it ).indexOf( ':' );
                if ( index != -1 ) {
                    // count number of characters to be returned by right functions
                    // by this line index == index pos of ':' character
                    index = ( *it ).size() - ( index + 1 );
                    bool ok;
                    quint16 tmpPortN = ( ( *it ).right( index ) ).toUInt( &ok, 10 ) ;
                    if ( ok )   // datum were received successfully
                        setRcvdPortNum( tmpPortN );
                }
            }
            // look for port field value
            if ( ( *it ).contains( DST_PORT ) ) {
                index = ( *it ).indexOf( ':' );
                if ( index != -1 ) {
                    // count number of characters to be returned by right functions
                    // by this line index == index pos of ':' character
                    index = ( *it ).size() - ( index + 1 );
                    bool ok;
                    quint16 tmpPortN = ( ( *it ).right( index ) ).toUInt( &ok, 10 ) ;
                    if ( ok )   // datum were received successfully
                        setDstPortNum( tmpPortN );
                }
            }
        }
    }
    
    const QList< ReadConfig > &ReadConfig::getConfigList() const {
        return setupList;
    }
    
    void ReadConfig::setSrcFilePath( const QString &src ) {
        if ( !src.isEmpty() )
            srcDirPath = src;
        else {
            // here should be error check that is not yet implemented
        }
    }
    
    const QString &ReadConfig::getSrcFilePath() const {
        return srcDirPath;
    }
    
    void ReadConfig::setDstFilePath( const QString &dst ) {
        if ( !dst.isEmpty() )
            dstDirPath = dst;
        else {
            // here should be error check that is not yet implemented
        }
    }
    
    const QString &ReadConfig::getDstFilePath() const {
        return dstDirPath;
    }
    
    void ReadConfig::setProtocolType( const QString &protocol ) {
        if ( !protocol.isEmpty() )
            protocolType = protocol;
        else {
            // here should be error check that is not yet implemented
        }
    }
    
    const QString &ReadConfig::getProtocolType() const {
        return protocolType;
    }
    
    void ReadConfig::setIpAddr( const QString &iAdr ) {
        if ( !iAdr.isEmpty() && isIpAdrValid( iAdr ) )
            ipAdr = iAdr;
        else {
            // here should be error check that is not yet implemented
            qDebug() << "IP address is invalid";
        }
    }
    
    const QString &ReadConfig::getIpAddress() const {
        return ipAdr;
    }
    
    void ReadConfig::setMacAddr(const QString &mAdr) {
        if ( !mAdr.isEmpty() )
            macAdr = mAdr;
        else {
            // here should be error check that is not yet implemented
        }
    }
    
    const QString &ReadConfig::getMacAddress() const {
        return macAdr;
    }
    
    void ReadConfig::setRcvdPortNum( const quint16 portN ) {
        if ( isPortReserved( portN ) )
            qDebug() << "Port should be not system port and be in the range of 1024 :: 65535";
            // here should be error check that is not yet implemented
         else
            rcvdPortNum = portN;
    }
    
    quint16 ReadConfig::getRcvdPortNum() const {
       return rcvdPortNum;
    }
    
    void ReadConfig::setDstPortNum( const quint16 portN ) {
        if ( isPortReserved( portN ) )
            dstPortNum = portN;
         else {
            qDebug() << "Port should be not system port and be in the range of 1024 :: 65535";
            // here should be error check that is not yet implemented
        }
    }
    
    quint16 ReadConfig::getDstPortNum() const {
       return dstPortNum;
    }
    
    
    bool ReadConfig::isIpAdrValid( const QString &iAdr ) {
        QHostAddress htAd;
    
        return ( htAd.setAddress( iAdr ) );
    }
    
    // ========== util functions ===========
    static bool isPortReserved( const quint16 portNum ) {
        return ( portNum <= 1023 && portNum > 65535 );
    }
    
    

    destructors are not implemented as i am going to use smart pointers later and will need to re-implement them anyway so for now losing memory is OK for me.
    This project might contain other issues as it is my first project of this type so will be really glad to hear any comment on it and of course i need help with it. When compiling it throws the following error:

    Starting $PROJECT_DIRECTORY shown here
    ASSERT: "!QtServiceBasePrivate::instance" in file ../../../$DIR/qt-solutions/qtservice/src/qtservice.cpp, line 650
    The program has unexpectedly finished.
    The process was ended forcefully.

    Thank you in advance


  • Lifetime Qt Champion

    Hi,

    Out of curiosity, why not use QtServiceController ?



  • @SGaist Hi, thank you for you reply. Actually the reason was that i couldn't completely understand how to use to install and start my particular daemon. I mean i tried to pass path to install method but it didn't work so i decided to try how it was implemented in another example (A simple HTTP Server )



  • The thing is that when i am calling "install" function from QtServiceController Class inside if-statement it returns true but when a few lines later i am checking if the service was installed with isInstalled it returns false. Here the extraction from my test code:
    in header:

    QtServiceController *ptrCtl;
    

    in src file:

            ptrCtl = new QtServiceController( ptrFi->baseName() );
            if ( ptrCtl->install( getProgName() ) )
                ptrCtl->start();
            qDebug() << ptrCtl->serviceName()
                     << ( ( ptrCtl->isRunning() )
                          ? " is" : " is not " ) <<  "running";
            if ( ptrCtl->isInstalled() ) {
                qDebug() << ptrCtl->serviceName()
                         << ( ( ptrCtl->start() )
                              ? " was" : " was not " ) <<  "start";
            } else
                qDebug() << ptrCtl->serviceName() << " was not installed";
    

    ptrCtl->serviceName() has value "client"
    getProgName() contain full path to "client" executable file. ( with file itself )

    output:
    "client" is not running
    "client" was not installed

    will be really grateful for any help or advice


  • Lifetime Qt Champion

    On what OS are you running that ?



  • @SGaist I ran it on Linux ( Ubuntu 16.04 LTS ) but intend to run on Windows either.



  • Actually the situation changed a bit and i managed to run without the error i had in the beginning. In the program there are some possible ways to follow, depending on the set flag ( i set flag: Projects->Run->Command line arguments ). everything seems to work fine with all the flags except the ones that make program to be installed. In this case it falls into infinite-loop. It looks as if it continuously trying to install itself without success ( i mean it was installed on the second iteration, not the first. Which is also weird ). The part of the code i am talking about

    if ( !( ptrCtl->isInstalled() ) ) {
                    qDebug() << ptrCtl->serviceName() << " was not installed ==================== >>>";
                    qDebug() << ptrCtl->serviceName()
                             << ( ( ptrCtl->install( getProgName() ) )
                                  ? " was successfully" : " was not " ) <<  "installed";
    
                    qDebug() << getProgName()
                             << ( ( ptrCtl->start() )
                                  ? " was" : " was not " ) <<  "started";
    }
    

    The program goes into the first if-statement every time but only on the second iteration it installs and all the other times do not do this and certainly never reaches the line with call to "start"-method ( at least not calling to "start"-method ). After the second iteration it looks as if it creates new process branch every time the program stream reaches this line ( with "install"-method ) and started to walk all its way through the main function to "install"-method again.
    I added the line with the phrase "was not installed" just to make the output more readable. The output looks like this

    "d_server" was not installed ==================== >>>
    "d_server" was successfully installed
    "d_server" was not installed ==================== >>>
    "d_server" was not installed ==================== >>>
    "d_server" was not installed ==================== >>>

    There is also another strange thing happens. The first iteration of this infinite-loop contain only 2 parameters for main function ( I mean argc in "int main( int argc, char **argv )" equals 2 ) but all the following - 4. Interesting why is it so?!
    My question here is how to make the program to install itself from the beginning and go to the line with "start-method"? ( well do it without "infinite"-loop ).

    Thank you in advance for any help.



  • maybe important to mention that i did saw the same question on Stack Overflaw and try to use it in my project


  • Lifetime Qt Champion

    Are you trying to install it with root privileges ?



  • @SGaist thank you. it might be the point of course but it also rises a few questions:

    • why my app was installed once ( second iteration ) at least it was shown that it was installed.
    • how to give to the app root privileges? ( i mean of course i tried ( from console ) to use make and then sudo ( super user privileges ) on compiled file but it changed nothing so i might did something wrong. If there are other options how to give root ( or super user privileges ) privileges write me please ).

  • Lifetime Qt Champion

    1. You might have been working as root at some point
    2. What I meant was, as you did, use sudo for the installation.


  • @SGaist I see, it seems there is some other issue then. Regretfully of course, as i still have no clue of what to do.
    Anyway thank you for your help.


  • Lifetime Qt Champion

    On a side note, you might also be interested by @kshegunov QtDaemon module.



  • @SGaist thank you for the link


  • Qt Champions 2016

    I've been silent, because it's been a long time since I had used the QtService and I really don't remember how it's supposed to be set up. As for linux, depending on how the daemon's set up you may need to call insserv to register the init script.



  • @kshegunov Thank you for your suggestion I will try to follow it.



Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.