/*
 *   SPDX-FileCopyrightText: 2001-2010 Klaralvdalens Datakonsult AB.
 *   SPDX-License-Identifier: LGPL-2.0-only
 *
 *   The KD Tools Library is Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB.
 */
#include "kdsingleapplicationguard.h"

#if QT_VERSION >= 0x040400 || defined(DOXYGEN_RUN)
#ifndef QT_NO_SHAREDMEMORY

#include "kdsharedmemorylocker.h"
#include "kdlockedsharedmemorypointer.h"

#include <QVector>
#include <QCoreApplication>
#include <QSharedMemory>
#include <QSharedData>
#include <QBasicTimer>
#include <QElapsedTimer>
#include <QTime>

#include <algorithm>
#include <limits>
#include <cstdlib>
#include <cstring>
#include <cassert>

#ifndef Q_WS_WIN
#include <csignal>
#include <unistd.h>
#endif

#ifdef Q_WS_WIN
#include <windows.h>
#ifndef _SSIZE_T_DEFINED
typedef signed int ssize_t;
#endif
#endif

using namespace kdtools;

#ifndef KDSINGLEAPPLICATIONGUARD_TIMEOUT_SECONDS
#define KDSINGLEAPPLICATIONGUARD_TIMEOUT_SECONDS 10
#endif

#ifndef KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES
#define KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES 10
#endif

#ifndef KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE
#define KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE 32768
#endif

static unsigned int KDSINGLEAPPLICATIONGUARD_SHM_VERSION = 0;

Q_GLOBAL_STATIC_WITH_ARGS( int, registerInstanceType,
                           (qRegisterMetaType<KDSingleApplicationGuard::Instance>()) )

/*!
  \class KDSingleApplicationGuard::Instance
  \relates KDSingleApplicationGuard
  \ingroup core
  \brief Information about instances a KDSingleApplicationGuard knows about

  Instance represents instances of applications under
  KDSingleApplicationGuard protection, and allows access to their
  pid() and the arguments() they were started with.
*/

class KDSingleApplicationGuard::Instance::Private : public QSharedData {
    friend class ::KDSingleApplicationGuard::Instance;
public:
    Private( const QStringList & args, bool truncated, qint64 pid )
        : pid( pid ), arguments( args ), truncated( truncated ) {}

private:
    qint64 pid;
    QStringList arguments;
    bool truncated;
};

struct ProcessInfo;

/*!
 \internal
 */
class KDSingleApplicationGuard::Private
{
    friend class ::KDSingleApplicationGuard;
    friend class ::KDSingleApplicationGuard::Instance;
    friend struct ::ProcessInfo;
    KDSingleApplicationGuard * const q;
public:
    Private( Policy policy, KDSingleApplicationGuard* qq );
    ~Private();

    void create( const QStringList& arguments );

    bool checkOperational( const char * function, const char * act ) const;
    bool checkOperationalPrimary( const char * function, const char * act ) const;

    struct segmentheader
    {
        size_t size : 16;
    };

    static void sharedmem_free( char* );
    static char* sharedmem_malloc( size_t size );

private:
    void shutdownInstance();
    void poll();

private:
    static KDSingleApplicationGuard* primaryInstance;

private:
    QBasicTimer timer;
    QSharedMemory mem;
    int id;
    Policy policy;
    bool operational;
    bool exitRequested;
};

/*!
  \internal
*/
KDSingleApplicationGuard::Instance::Instance( const QStringList & args, bool truncated, qint64 p )
    : d( new Private( args, truncated, p ) )
{
    d->ref.ref();
    (void)registerInstanceType();
}

/*!
  Default constructor. Constructs in Instance that is \link isNull()
  null\endlink.

  \sa isNull()
*/
KDSingleApplicationGuard::Instance::Instance() : d( 0 ) {}

/*!
  Copy constructor.
*/
KDSingleApplicationGuard::Instance::Instance( const Instance & other )
    : d( other.d )
{
    if ( d )
        d->ref.ref();
}

/*!
  Destructor.
*/
KDSingleApplicationGuard::Instance::~Instance()
{
    if ( d && !d->ref.deref() )
        delete d;
}

/*!
  \fn KDSingleApplicationGuard::Instance::swap( Instance & other )

  Swaps the contents of this and \a other.

  This function never throws exceptions.
*/

/*!
  \fn KDSingleApplicationGuard::Instance::operator=( Instance other )

  Assigns the contents of \a other to this.

  This function is strongly exception-safe.
*/

/*!
  \fn std::swap( KDSingleApplicationGuard::Instance & lhs, KDSingleApplicationGuard::Instance & rhs )
  \relates KDSingleApplicationGuard::Instance

  Specialisation of std::swap() for
  KDSingleApplicationGuard::Instance. Calls swap().
*/

/*!
  \fn qSwap( KDSingleApplicationGuard::Instance & lhs, KDSingleApplicationGuard::Instance & rhs )
  \relates KDSingleApplicationGuard::Instance

  Specialisation of qSwap() for
  KDSingleApplicationGuard::Instance. Calls swap().
*/

/*!
  \fn KDSingleApplicationGuard::Instance::isNull() const

  Returns whether this instance is null.
*/

/*!
  Returns whether this instance is valid. A valid instance is neither
  null, nor does it have a negative PID.
*/
bool KDSingleApplicationGuard::Instance::isValid() const
{
    return d && d->pid >= 0 ;
}

/*!
  Returns whether the #arguments are complete (\c false) or not (\c
  true), e.g. because they have been truncated due to limited storage
  space.

  \sa arguments()
*/
bool KDSingleApplicationGuard::Instance::areArgumentsTruncated() const
{
    return d && d->truncated;
}

/*!
  Returns the arguments that this instance was started with.

  \sa areArgumentsTruncated()
*/
const QStringList & KDSingleApplicationGuard::Instance::arguments() const
{
    if ( d )
        return d->arguments;
    static const QStringList empty;
    return empty;
}

/*!
  Returns the process-id (PID) of this instance.
*/
qint64 KDSingleApplicationGuard::Instance::pid() const
{
    if ( d )
        return d->pid;
    else
        return -1;
}

/*!
  \class KDSingleApplicationGuard KDSingleApplicationGuard
  \ingroup core
  \brief A guard to protect an application from having several instances.

  KDSingleApplicationGuard can be used to make sure only one instance of an
  application is running at the same time.

  \note As KDSingleApplicationGuard currently uses QSharedMemory, Qt
  4.4 or later is required.
 */

/*!
  \fn void KDSingleApplicationGuard::instanceStarted(const KDSingleApplicationGuard::Instance & instance)

  This signal is emitted by the primary instance whenever another
  instance \a instance started.
 */

/*!
  \fn void KDSingleApplicationGuard::instanceExited(const KDSingleApplicationGuard::Instance & instance)

  This signal is emitted by the primary instance whenever another
  instance \a instance exited.
 */

/*!
  \fn void KDSingleApplicationGuard::raiseRequested()

  This signal is emitted when the current running application is requested
  to raise its main window.
*/

/*!
  \fn void KDSingleApplicationGuard::exitRequested()

  This signal is emitted when the current running application has been asked to exit
  by calling kill on the instance.
*/

/*!
  \fn void KDSingleApplicationGuard::becamePrimaryInstance()

  This signal is emitted when the current running application becomes
  the new primary application. The old primary application has quit.
 */

/*!
  \fn void KDSingleApplicationGuard::becameSecondaryInstance()

  This signal is emmited when the primary instance became secondary instance.
  This happens when the instance doesn't update its status for some (default 10) seconds. Another instance
  got primary instance in that case.
  */

/*!
  \fn void KDSingleApplicationGuard::policyChanged( KDSingleApplicationGuard::Policy policy )

  This signal is emitted when the #policy of the system changes.
*/

enum Command
{
    NoCommand = 0x00,
    ExitedInstance = 0x01,
    NewInstance = 0x02,
    FreeInstance = 0x04,
    ShutDownCommand = 0x08,
    KillCommand = 0x10,
    BecomePrimaryCommand = 0x20,
    RaiseCommand = 0x40
};

static const quint16 PrematureEndOfOptions = -1;
static const quint16 RegularEndOfOptions   = -2;

struct ProcessInfo
{
    static const size_t MarkerSize = sizeof(quint16);

    explicit ProcessInfo( Command c = FreeInstance, const QStringList& arguments = QStringList(), qint64 p = -1 )
        : pid( p ),
          command( c ),
          timestamp( 0 ),
          commandline( 0 )
    {
        setArguments( arguments );
    }

    void setArguments( const QStringList & arguments );
    QStringList arguments( bool * prematureEnd  ) const;

    qint64 pid;
    quint32 command;
    quint32 timestamp;
    char* commandline;
};

static inline bool operator==( const ProcessInfo & lhs, const ProcessInfo & rhs )
{
    return lhs.command == rhs.command &&
           ( lhs.commandline == rhs.commandline || ( lhs.commandline != 0 && rhs.commandline != 0 && ::strcmp( lhs.commandline, rhs.commandline ) == 0 ) );
}

static inline bool operator!=( const ProcessInfo & lhs, const ProcessInfo & rhs )
{
    return !operator==( lhs, rhs );
}

/*!
  This struct contains information about the managed process system.
  \internal
 */
struct InstanceRegister
{
    explicit InstanceRegister( KDSingleApplicationGuard::Policy policy = KDSingleApplicationGuard::NoPolicy )
        : policy( policy ),
          maxInstances( KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES ),
          version( 0 )
    {
        std::fill_n( commandLines, KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE, 0 );
        ::memcpy( magicCookie, "kdsingleapp", 12 );
    }

    /*!
      Returns whether this register was properly initialized by the first instance.
      */
    bool isValid() const
    {
        return ::strcmp( magicCookie, "kdsingleapp" ) == 0;
    }

    char magicCookie[ 12 ];
    unsigned int policy  :  8;
    quint32 maxInstances : 20;
    unsigned int version :  4;
    ProcessInfo info[ KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES ];

    char commandLines[ KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE ];

    Q_DISABLE_COPY( InstanceRegister )
};

void ProcessInfo::setArguments( const QStringList & arguments )
{
    if( commandline != 0 )
        KDSingleApplicationGuard::Private::sharedmem_free( commandline );

    commandline = 0;
    if( arguments.isEmpty() )
        return;

    size_t totalsize = MarkerSize;
    for ( const QString& arg : arguments )
    {
        const QByteArray utf8 = arg.toUtf8();
        totalsize += utf8.size() + MarkerSize;
    }
    InstanceRegister* const reg = reinterpret_cast<InstanceRegister*>( KDSingleApplicationGuard::Private::primaryInstance->d->mem.data() );
    this->commandline = KDSingleApplicationGuard::Private::sharedmem_malloc( totalsize );
    if( this->commandline == 0 )
    {
        qWarning("KDSingleApplicationguard: out of memory when trying to save arguments.\n");
        return;
    }

    char* const commandline = this->commandline + reinterpret_cast<qptrdiff>(reg->commandLines);

    int argpos = 0;
    for ( const QString & arg : arguments )
    {
        const QByteArray utf8 = arg.toUtf8();
        const int required = MarkerSize + utf8.size() + MarkerSize ;
        const int available = KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE - argpos ;
        if ( required > available || utf8.size() > std::numeric_limits<quint16>::max() ) {
            // write a premature-eoo marker, and quit
            memcpy( commandline + argpos, &PrematureEndOfOptions, MarkerSize );
            argpos += MarkerSize;
            qWarning( "KDSingleApplicationGuard: argument list is too long (bytes required: %d, used: %d, available: %d",
                      required, argpos - 2, available );
            return;
        } else {
            const quint16 len16 = utf8.size();
            // write the size of the data...
            memcpy( commandline + argpos, &len16, MarkerSize );
            argpos += MarkerSize;
            // then the data
            memcpy( commandline + argpos, utf8.data(), len16 );
            argpos += len16;
        }
    }
    const ssize_t available = KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE - argpos;
    assert( available >= static_cast<ssize_t>( MarkerSize ) );
    memcpy( commandline + argpos, &RegularEndOfOptions, MarkerSize );
    argpos += MarkerSize;
}

QStringList ProcessInfo::arguments( bool * prematureEnd  ) const
{
    QStringList result;
    if( commandline == 0 )
    {
        if( prematureEnd )
            *prematureEnd = true;
        return result;
    }

    InstanceRegister* const reg = reinterpret_cast<InstanceRegister*>( KDSingleApplicationGuard::Private::primaryInstance->d->mem.data() );
    const char* const commandline = this->commandline + reinterpret_cast<qptrdiff>(reg->commandLines);

    int argpos = 0;
    while ( true ) {
        const int available = KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE - argpos ;
        assert( available >= 2 );

        quint16 marker;
        memcpy( &marker, commandline + argpos, MarkerSize );
        argpos += MarkerSize;

        if ( marker == PrematureEndOfOptions ) {
            if ( prematureEnd ) *prematureEnd = true;
            break;
        }
        if ( marker == RegularEndOfOptions ) {
            if ( prematureEnd ) *prematureEnd = false;
            break;
        }

        const int requested = MarkerSize + marker + MarkerSize ;
        if ( requested > available ) {
            const long long int p = pid;
            qWarning( "KDSingleApplicationGuard: inconsistency detected when parsing command-line argument for process %lld", p );
            if ( prematureEnd ) *prematureEnd = true;
            break;
        }

        result.push_back( QString::fromUtf8( commandline + argpos, marker ) );
        argpos += marker;
    }

    return result;
}

KDSingleApplicationGuard::Private::~Private()
{
    if( primaryInstance == q )
        primaryInstance = 0;
}

bool KDSingleApplicationGuard::Private::checkOperational( const char * function, const char * act ) const
{
    assert( function );
    assert( act );
    if ( !operational )
        qWarning( "KDSingleApplicationGuard::%s: need to be operational to %s", function, act );
    return operational;
}

bool KDSingleApplicationGuard::Private::checkOperationalPrimary( const char * function, const char * act ) const
{
    if ( !checkOperational( function, act ) )
        return false;
    if ( id != 0 )
        qWarning( "KDSingleApplicationGuard::%s: need to be primary to %s", function, act );
    return id == 0;
}

struct segmentheader
{
    size_t size : 16;
};

void KDSingleApplicationGuard::Private::sharedmem_free( char* pointer )
{
    InstanceRegister* const reg = reinterpret_cast<InstanceRegister*>( KDSingleApplicationGuard::Private::primaryInstance->d->mem.data() );
    char* const heap = reg->commandLines;
    char* const heap_ptr = heap + reinterpret_cast<qptrdiff>(pointer) - sizeof( segmentheader );
    const segmentheader* const header = reinterpret_cast< const segmentheader* >( heap_ptr );
    const size_t size = header->size;

    char* end = heap + KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE;

    std::copy( heap_ptr + size, end, heap_ptr );
    std::fill( end - size, end, 0 );

    for( uint i = 0; i < reg->maxInstances; ++i )
    {
        if( reg->info[ i ].commandline > pointer )
            reg->info[ i ].commandline -= size + sizeof( segmentheader );
    }
}

char* KDSingleApplicationGuard::Private::sharedmem_malloc( size_t size )
{
    InstanceRegister* const reg = reinterpret_cast<InstanceRegister*>( KDSingleApplicationGuard::Private::primaryInstance->d->mem.data() );
    char* heap = reg->commandLines;

    while( heap + sizeof( segmentheader ) + size < reg->commandLines + KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE )
    {
        segmentheader* const header = reinterpret_cast< segmentheader* >( heap );
        if( header->size == 0 )
        {
            header->size = size;
            return heap + sizeof( segmentheader ) - reinterpret_cast<qptrdiff>(reg->commandLines);
        }
        heap += sizeof( header ) + header->size;
    }
    return 0;
}

void KDSingleApplicationGuard::Private::shutdownInstance()
{
    KDLockedSharedMemoryPointer< InstanceRegister > instances( &q->d->mem );
    instances->info[ q->d->id ].command |= ExitedInstance;

    if( q->isPrimaryInstance() )
    {
        // ohh... we need a new primary instance...
        for ( int i = 1, end = instances->maxInstances ; i  < end ; ++i )
        {
            if( ( instances->info[ i ].command & ( FreeInstance | ExitedInstance | ShutDownCommand | KillCommand ) ) == 0 )
            {
                instances->info[ i ].command |= BecomePrimaryCommand;
                return;
            }
        }
        // none found? then my species is dead :-(
    }
}

KDSingleApplicationGuard* KDSingleApplicationGuard::Private::primaryInstance = 0;

/*!
  Requests that the instance kills itself (by emitting exitRequested).

  If the instance has since exited, does nothing.

  \sa shutdown(), raise()
*/
void KDSingleApplicationGuard::Instance::kill()
{
    KDLockedSharedMemoryPointer< InstanceRegister > instances( &KDSingleApplicationGuard::Private::primaryInstance->d->mem );
    for ( int i = 0, end = instances->maxInstances ; i < end ; ++i )
    {
        if( instances->info[ i ].pid != d->pid )
           continue;
        if( ( instances->info[ i ].command & ( FreeInstance | ExitedInstance ) ) == 0 )
            instances->info[ i ].command = KillCommand;
    }
}

/*!
  Requests that the instance shuts itself down (by calling QCoreApplication::quit()).

  If the instance has since exited, does nothing.

  \sa kill(), raise()
*/
void KDSingleApplicationGuard::Instance::shutdown()
{
    KDLockedSharedMemoryPointer< InstanceRegister > instances( &KDSingleApplicationGuard::Private::primaryInstance->d->mem );
    for ( int i = 0, end = instances->maxInstances ; i < end ; ++i )
    {
        if( instances->info[ i ].pid != d->pid )
           continue;
        if( ( instances->info[ i ].command & ( FreeInstance | ExitedInstance ) ) == 0 )
            instances->info[ i ].command = ShutDownCommand;
    }
}

/*!

  Requests that the instance raises its main window.

  The effects are implementation-defined: the KDSingleApplicationGuard
  corresponding to the instance will emit its \link
  KDSingleApplicationGuard::raiseRequested() raiseRequested()\endlink
  signal.

  If the instance has since exited, does nothing.

  \sa kill(), shutdown()
*/
void KDSingleApplicationGuard::Instance::raise()
{
    KDLockedSharedMemoryPointer< InstanceRegister > instances( &KDSingleApplicationGuard::Private::primaryInstance->d->mem );
    for ( int i = 0, end = instances->maxInstances ; i < end ; ++i )
    {
        if( instances->info[ i ].pid != d->pid )
           continue;
        if( ( instances->info[ i ].command & ( FreeInstance | ExitedInstance ) ) == 0 )
            instances->info[ i ].command = RaiseCommand;
    }
}


#ifndef Q_WS_WIN
// static
void KDSingleApplicationGuard::SIGINT_handler( int sig )
{
    if( sig == SIGINT && Private::primaryInstance != 0 )
        Private::primaryInstance->d->shutdownInstance();
    ::exit( 1 );
}
#endif

/*!
  \enum KDSingleApplicationGuard::Policy

  Defines the policy that a KDSingleApplicationGuard can enforce:
*/

/*!
  \var KDSingleApplicationGuard::NoPolicy

  instanceStarted() is emitted, and the new instance allowed to continue.
*/

/*!
  \var KDSingleApplicationGuard::AutoKillOtherInstances

  instanceStarted() is emitted, and the new instance is killed (Instance::kill()).
*/

/*!
  Creates a new KDSingleApplicationGuard with arguments
  QCoreApplication::arguments() and policy AutoKillOtherInstances,
  passing \a parent to the base class constructor, as usual.
*/
KDSingleApplicationGuard::KDSingleApplicationGuard( QObject * parent )
    : QObject( parent ), d( new Private( AutoKillOtherInstances, this ) )
{
    d->create( QCoreApplication::arguments() );
}

/*!
  Creates a new KDSingleApplicationGuard with arguments
  QCoreApplication::arguments() and policy \a policy, passing \a
  parent to the base class constructor, as usual.
*/
KDSingleApplicationGuard::KDSingleApplicationGuard( Policy policy, QObject * parent )
    : QObject( parent ), d( new Private( policy, this ) )
{
    d->create( QCoreApplication::arguments() );
}

/*!
  Creates a new KDSingleApplicationGuard with arguments \a arguments
  and policy AutoKillOtherInstances, passing \a parent to the base
  class constructor, as usual.
*/
KDSingleApplicationGuard::KDSingleApplicationGuard( const QStringList & arguments, QObject * parent )
    : QObject( parent ), d( new Private( AutoKillOtherInstances, this ) )
{
    d->create( arguments );
}

/*!
  Creates a new KDSingleApplicationGuard with arguments \a arguments
  and policy \a policy, passing \a parent to the base class
  constructor, as usual.
*/
KDSingleApplicationGuard::KDSingleApplicationGuard( const QStringList & arguments, Policy policy, QObject * parent )
    : QObject( parent ), d( new Private( policy, this ) )
{
    d->create( arguments );
}

KDSingleApplicationGuard::Private::Private( Policy policy_, KDSingleApplicationGuard * qq )
    : q( qq ),
      id( -1 ),
      policy( policy_ ),
      operational( false ),
      exitRequested( false )
{
}

void KDSingleApplicationGuard::Private::create( const QStringList & arguments )
{
    if ( !QCoreApplication::instance() ) {
        qWarning( "KDSingleApplicationGuard: you need to construct a Q(Core)Application before you can construct a KDSingleApplicationGuard" );
        return;
    }

    const QString name = QCoreApplication::applicationName();
    if ( name.isEmpty() ) {
        qWarning( "KDSingleApplicationGuard: QCoreApplication::applicationName must not be empty" );
        return;
    }

    (void)registerInstanceType();
    if ( primaryInstance == 0 )
        primaryInstance = q;

    mem.setKey( name );

    // if another instance crashed, the shared memory segment is still there on Unix
    // the following lines trigger deletion in that case
#ifndef Q_WS_WIN
    mem.attach();
    mem.detach();
#endif

    const bool created = mem.create( sizeof( InstanceRegister ) );
    if( !created )
    {
        QString errorMsg;
        if( mem.error() != QSharedMemory::NoError && mem.error() != QSharedMemory::AlreadyExists )
            errorMsg += QString::fromLatin1( "QSharedMemomry::create() failed: %1" ).arg( mem.errorString() );

        if( !mem.attach() )
        {
            if( mem.error() != QSharedMemory::NoError )
                errorMsg += QString::fromLatin1( "QSharedMemomry::attach() failed: %1" ).arg( mem.errorString() );

            qWarning( "KDSingleApplicationGuard: Could neither create nor attach to shared memory segment." );
            qWarning( "%s\n",  errorMsg.toLocal8Bit().constData() );
            return;
        }

        const int maxWaitMSecs = 1000 * 60; // stop waiting after 60 seconds
        QElapsedTimer waitTimer;
        waitTimer.start();

        // lets wait till the other instance initialized the register
        bool initialized = false;
        while( !initialized && waitTimer.elapsed() < maxWaitMSecs )
        {
            const KDLockedSharedMemoryPointer< InstanceRegister > instances( &mem );
            initialized = instances->isValid();
#ifdef Q_WS_WIN
            ::Sleep(20);
#else
            usleep(20000);
#endif
        }

        const KDLockedSharedMemoryPointer< InstanceRegister > instances( &mem );
        if ( instances->version != 0 ) {
            qWarning( "KDSingleApplicationGuard: Detected version mismatch. "
                      "Highest supported version: %ud, actual version: %ud",
                      KDSINGLEAPPLICATIONGUARD_SHM_VERSION, instances->version );
            return;
        }

    }


    KDLockedSharedMemoryPointer< InstanceRegister > instances( &mem );

    if( !created )
    {
        assert( instances->isValid() );

        // we're _not_ the first instance
        // but the
        bool killOurSelf = false;

        // find a new slot...
        id = std::find( instances->info, instances->info + instances->maxInstances, ProcessInfo() ) - instances->info;
        ProcessInfo& info = instances->info[ id ];
        info = ProcessInfo( NewInstance, arguments, QCoreApplication::applicationPid() );
        killOurSelf = instances->policy == AutoKillOtherInstances;
        policy = static_cast<Policy>( instances->policy );

        // but the signal that we tried to start was sent to the primary application
        if( killOurSelf )
            exitRequested = true;
    }
    else
    {
        // ok.... we are the first instance
        new ( instances.get() ) InstanceRegister( policy ); // create a new list (in shared memory)
        id = 0;                                             // our id = 0
        // and we've no command
        instances->info[ 0 ] = ProcessInfo( NoCommand, arguments, QCoreApplication::applicationPid() );
    }

#ifndef Q_WS_WIN
    ::signal( SIGINT, SIGINT_handler );
#endif

    // now listen for commands
    timer.start( 750, q );

    operational = true;
}

/*!
  Destroys this SingleApplicationGuard.
  If this instance has been the primary instance and no other instance is existing anymore,
  the application is shut down completely. Otherwise the destructor selects another instance to
  be the primary instances.
 */
KDSingleApplicationGuard::~KDSingleApplicationGuard()
{
    if( d->id == -1 )
        return;

    d->shutdownInstance();
}

/*!
  \property KDSingleApplicationGuard::operational

  Contains whether this KDSingleApplicationGuard is operational.

  A non-operational KDSingleApplicationGuard cannot be used in any meaningful way.

  Reasons for a KDSingleApplicationGuard being non-operational include:
  \li it was constructed before QApplication (or at least QCoreApplication) was constructed
  \li it failed to create or attach to the shared memory segment that is used for communication

  Get this property's value using %isOperational().
*/
bool KDSingleApplicationGuard::isOperational() const
{
    return d->operational;
}

/*!
  \property KDSingleApplicationGuard::exitRequested

  Contains wheter this istance has been requested to exit. This will happen when this instance
  was just started, but the policy is AutoKillOtherInstances or by explicitely calling kill on
  this instance().

  Get this property's value using %isExitRequested().
*/
bool KDSingleApplicationGuard::isExitRequested() const
{
    return d->exitRequested;
};

/*!
  \property KDSingleApplicationGuard::primaryInstance

   Contains whether this instance is the primary instance.

   The primary instance is the first instance which was started or else the instance which
   got selected by KDSingleApplicationGuard's destructor, when the primary instance was
   shut down.

   Get this property's value using %isPrimaryInstance(), and monitor changes to it
   using becamePrimaryInstance().
 */
bool KDSingleApplicationGuard::isPrimaryInstance() const
{
    return d->id == 0;
}

/*!
 \property KDSingleApplicationGuard::policy
 Specifies the policy KDSingleApplicationGuard is using when new instances are started.
 This can only be set in the primary instance.

 Get this property's value using %policy(), set it using %setPolicy(), and monitor changes
 to it using policyChanged().
 */
KDSingleApplicationGuard::Policy KDSingleApplicationGuard::policy() const
{
    return d->policy;
}

void KDSingleApplicationGuard::setPolicy( Policy policy )
{
    if ( !d->checkOperationalPrimary( "setPolicy", "change the policy" ) )
        return;

    if( d->policy == policy )
        return;

    d->policy = policy;
    emit policyChanged( policy );
    KDLockedSharedMemoryPointer< InstanceRegister > instances( &d->mem );
    instances->policy = policy;
}

/*!
 Returns a list of all currently running instances.
 */
QVector<KDSingleApplicationGuard::Instance>
KDSingleApplicationGuard::instances() const
{
    if ( !d->checkOperational( "instances", "report on other instances" ) )
        return QVector<Instance>();

    if ( Private::primaryInstance == 0 ) {
        Private::primaryInstance = const_cast<KDSingleApplicationGuard*>( this );
    }

    QVector<Instance> result;
    const KDLockedSharedMemoryPointer< InstanceRegister > instances( const_cast< QSharedMemory* >( &d->mem ) );
    for ( int i = 0, end = instances->maxInstances ; i < end ; ++i )
    {
        const ProcessInfo& info = instances->info[ i ];
        if( ( info.command & ( FreeInstance | ExitedInstance ) ) == 0 )
        {
            bool truncated;
            const QStringList arguments = info.arguments( &truncated );
            result.push_back( Instance( arguments, truncated, info.pid ) );
        }
    }
    return result;
}

/*!
  Shuts down all other instances. This can only be called from the
  the primary instance.
  Shut down is done gracefully via QCoreApplication::quit().
 */
void KDSingleApplicationGuard::shutdownOtherInstances()
{
    if ( !d->checkOperationalPrimary( "shutdownOtherInstances", "shut other instances down" ) )
        return;

    KDLockedSharedMemoryPointer< InstanceRegister > instances( &d->mem );
    for ( int i = 1, end = instances->maxInstances ; i < end ; ++i )
    {
        if( ( instances->info[ i ].command & ( FreeInstance | ExitedInstance ) ) == 0 )
            instances->info[ i ].command = ShutDownCommand;
    }
}

/*!
  Kills all other instances. This can only be called from the
  the primary instance.
  Killing is done via emitting exitRequested. It's up to the receiving
  instance to react properly.
 */
void KDSingleApplicationGuard::killOtherInstances()
{
    if ( !d->checkOperationalPrimary( "killOtherInstances", "kill other instances" ) )
        return;

    KDLockedSharedMemoryPointer< InstanceRegister > instances( &d->mem );
    for ( int i = 1, end = instances->maxInstances ; i < end ; ++i )
    {
        if( ( instances->info[ i ].command & ( FreeInstance | ExitedInstance ) ) == 0 )
            instances->info[ i ].command = KillCommand;
    }
}

bool KDSingleApplicationGuard::event( QEvent * event )
{
    if ( event->type() == QEvent::Timer ) {
        const QTimerEvent * const te = static_cast<QTimerEvent*>( event );
        if ( te->timerId() == d->timer.timerId() ) {
            d->poll();
            return true;
        }
    }
    return QObject::event( event );
}

void KDSingleApplicationGuard::Private::poll() {

    const quint32 now = QDateTime::currentDateTime().toTime_t();

    if ( primaryInstance == 0 ) {
        primaryInstance = q;
    }

    if ( q->isPrimaryInstance() )
    {
        // only the primary instance will get notified about new instances
        QVector< Instance > exitedInstances;
        QVector< Instance > startedInstances;

        {
            KDLockedSharedMemoryPointer< InstanceRegister > instances( &mem );

            if( instances->info[ id ].pid != QCoreApplication::applicationPid() )
            {
                for ( int i = 1, end = instances->maxInstances ; i < end && id == 0 ; ++i )
                {
                    if( instances->info[ i ].pid == QCoreApplication::applicationPid() )
                        id = i;
                }
                emit q->becameSecondaryInstance();
                return;
            }

            instances->info[ id ].timestamp = now;

            for ( int i = 1, end = instances->maxInstances ; i < end ; ++i )
            {
                ProcessInfo& info = instances->info[ i ];
                if( info.command & NewInstance )
                {
                    bool truncated;
                    const QStringList arguments = info.arguments( &truncated );
                    startedInstances.push_back( Instance( arguments, truncated, info.pid ) );
                    info.command &= ~NewInstance;  // clear NewInstance flag
                }
                if( info.command & ExitedInstance )
                {
                    bool truncated;
                    const QStringList arguments = info.arguments( &truncated );
                    exitedInstances.push_back( Instance( arguments, truncated, info.pid ) );
                    info.command = FreeInstance;   // set FreeInstance flag
                }
            }
        }

        // one signal for every new instance - _after_ the memory segment was unlocked again
        for( QVector< Instance >::const_iterator it = startedInstances.constBegin(); it != startedInstances.constEnd(); ++it )
            emit q->instanceStarted( *it );
        for( QVector< Instance >::const_iterator it = exitedInstances.constBegin(); it != exitedInstances.constEnd(); ++it )
            emit q->instanceExited( *it );
    }
    else
    {
        // do we have a command?
        bool killOurSelf = false;
        bool shutDownOurSelf = false;
        bool policyDidChange = false;

        {
            KDLockedSharedMemoryPointer< InstanceRegister > instances( &mem );

            const Policy oldPolicy = policy;
            policy = static_cast<Policy>( instances->policy );
            policyDidChange = policy != oldPolicy;

            // check for the primary instance health status
            if( now - instances->info[ 0 ].timestamp > KDSINGLEAPPLICATIONGUARD_TIMEOUT_SECONDS )
            {
                std::swap( instances->info[ 0 ], instances->info[ id ] );
                id = 0;
                instances->info[ id ].timestamp = now;
                emit q->becamePrimaryInstance();
                instances->info[ id ].command &= ~BecomePrimaryCommand;  // afterwards, reset the flag
            }

            if( instances->info[ id ].command & BecomePrimaryCommand )
            {
                // we became primary!
                instances->info[ 0 ] = instances->info[ id ];
                instances->info[ id ] = ProcessInfo();  // change our id to 0 and declare the old slot as free
                id = 0;
                instances->info[ id ].timestamp = now;
                emit q->becamePrimaryInstance();
            }

            if( instances->info[ id ].command & RaiseCommand )
            {
               // raise ourself!
               emit q->raiseRequested();
               instances->info[ id ].command &= ~RaiseCommand;  // afterwards, reset the flag
            }


            killOurSelf = instances->info[ id ].command & KillCommand;            // check for kill command
            shutDownOurSelf = instances->info[ id ].command & ShutDownCommand;    // check for shut down command
            instances->info[ id ].command &= ~( KillCommand | ShutDownCommand | BecomePrimaryCommand );  // reset both flags
            if( killOurSelf )
            {
                instances->info[ id ].command |= ExitedInstance;  // upon kill, we have to set the ExitedInstance flag
                id = -1;                                          // becauso our d'tor won't be called anymore
            }
        }

        if( killOurSelf )  // kill our self takes precedence
        {
            exitRequested = true;
            emit q->exitRequested();
        }
        else if( shutDownOurSelf )
            qApp->quit();
        else if( policyDidChange )
            emit q->policyChanged( policy );
    }
}

// #include "moc_kdsingleapplicationguard.cpp"

#ifdef KDTOOLSCORE_UNITTESTS

#include <kdunittest/test.h>

#include "kdautopointer.h"

#include <iostream>

#include <QtCore/QTime>
#include <QtCore/QUuid>
#include <QtTest/QSignalSpy>

static void wait( int msec, QSignalSpy * spy=0, int expectedCount=INT_MAX )
{
    QTime t;
    t.start();
    while ( ( !spy || spy->count() < expectedCount ) && t.elapsed() < msec )
    {
        qApp->processEvents( QEventLoop::WaitForMoreEvents, qMax( 10, msec - t.elapsed() ) );
    }
}

static std::ostream& operator<<( std::ostream& stream, const QStringList& list )
{
    stream << "QStringList(";
    for( QStringList::const_iterator it = list.begin(); it != list.end(); ++it )
    {
        stream << " " << it->toLocal8Bit().data();
        if( it + 1 != list.end() )
            stream << ",";
    }
    stream << " )";
    return stream;
}

namespace {
    class ApplicationNameSaver {
        Q_DISABLE_COPY( ApplicationNameSaver )
        const QString oldname;
    public:
        explicit ApplicationNameSaver( const QString & name )
            : oldname( QCoreApplication::applicationName() )
        {
            QCoreApplication::setApplicationName( name );
        }
        ~ApplicationNameSaver() {
            QCoreApplication::setApplicationName( oldname );
        }
    };
}

KDAB_UNITTEST_SIMPLE( KDSingleApplicationGuard, "kdcoretools" ) {

    // set it to an unique name
    const ApplicationNameSaver saver( QUuid::createUuid().toString() );

    KDAutoPointer<KDSingleApplicationGuard> guard3;
    KDAutoPointer<QSignalSpy> spy3;
    KDAutoPointer<QSignalSpy> spy4;

    {
        KDSingleApplicationGuard guard1;
        assertEqual( guard1.policy(), KDSingleApplicationGuard::AutoKillOtherInstances );
        assertEqual( guard1.instances().count(), 1 );
        assertTrue( guard1.isPrimaryInstance() );

        guard1.setPolicy( KDSingleApplicationGuard::NoPolicy );
        assertEqual( guard1.policy(), KDSingleApplicationGuard::NoPolicy );

        QSignalSpy spy1( &guard1, SIGNAL(instanceStarted(KDSingleApplicationGuard::Instance)) );

        KDSingleApplicationGuard guard2;
        assertEqual( guard1.instances().count(), 2 );
        assertEqual( guard2.instances().count(), 2 );
        assertEqual( guard2.policy(), KDSingleApplicationGuard::NoPolicy );
        assertFalse( guard2.isPrimaryInstance() );

        wait( 1000, &spy1, 1 );

        assertEqual( spy1.count(), 1 );
        guard3.reset( new KDSingleApplicationGuard );
        spy3.reset( new QSignalSpy( guard3.get(), SIGNAL(becamePrimaryInstance()) ) );
        spy4.reset( new QSignalSpy( guard3.get(), SIGNAL(instanceExited(KDSingleApplicationGuard::Instance) ) ) );
        assertFalse( guard3->isPrimaryInstance() );
    }

    wait( 1000, spy3.get(), 1 );
    wait( 1000, spy4.get(), 1 );
    assertEqual( spy3->count(), 1 );
    assertEqual( guard3->instances().count(), 1 );
    assertTrue( guard3->isPrimaryInstance() );
    guard3.reset( new KDSingleApplicationGuard );

    assertEqual( guard3->instances().first().arguments(), qApp->arguments() );

    QSignalSpy spyStarted( guard3.get(), SIGNAL(instanceStarted(KDSingleApplicationGuard::Instance)) );
    QSignalSpy spyExited(  guard3.get(), SIGNAL(instanceExited(KDSingleApplicationGuard::Instance)) );

    {
        KDSingleApplicationGuard guard1;
        KDSingleApplicationGuard guard2;

        wait( 1000, &spyStarted, 2 );

        assertEqual( spyStarted.count(), 2 );
    }

    wait( 1000, &spyExited, 2 );
    assertEqual( spyExited.count(), 2 );

    spyStarted.clear();
    spyExited.clear();

    {
        // check arguments-too-long handling:
        QStringList args;
        for ( unsigned int i = 0, end = KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE/16 ; i != end ; ++i )
            args.push_back( QLatin1String( "0123456789ABCDEF" ) );
        KDSingleApplicationGuard guard3( args );

        wait( 1000, &spyStarted, 1 );

        const QVector<KDSingleApplicationGuard::Instance> instances = guard3.instances();
        assertEqual( instances.size(), 2 );

        assertTrue( instances[1].areArgumentsTruncated() );
    }
}

#endif // KDTOOLSCORE_UNITTESTS

#endif // QT_NO_SHAREDMEMORY
#endif // QT_VERSION >= 0x040400 || defined(DOXYGEN_RUN)
