PageRenderTime 306ms CodeModel.GetById 52ms app.highlight 203ms RepoModel.GetById 38ms app.codeStats 1ms

/src/libtomahawk/thirdparty/kdsingleapplicationguard/kdsingleapplicationguard.cpp

http://github.com/tomahawk-player/tomahawk
C++ | 1249 lines | 777 code | 184 blank | 288 comment | 149 complexity | 4ddd65ed2f1df70ea34258428d7bf2cf MD5 | raw file
   1#include "kdsingleapplicationguard.h"
   2
   3#if QT_VERSION >= 0x040400 || defined(DOXYGEN_RUN)
   4#ifndef QT_NO_SHAREDMEMORY
   5
   6#include "kdsharedmemorylocker.h"
   7#include "kdlockedsharedmemorypointer.h"
   8
   9#include <QVector>
  10#include <QCoreApplication>
  11#include <QSharedMemory>
  12#include <QSharedData>
  13#include <QBasicTimer>
  14#include <QTime>
  15
  16#include <algorithm>
  17#include <limits>
  18#include <cstdlib>
  19#include <cstring>
  20#include <cassert>
  21
  22#ifndef Q_WS_WIN
  23#include <csignal>
  24#include <unistd.h>
  25#endif
  26
  27#ifdef Q_WS_WIN
  28#include <windows.h>
  29#ifndef _SSIZE_T_DEFINED
  30typedef signed int ssize_t;
  31#endif
  32#endif
  33
  34using namespace kdtools;
  35
  36#ifndef KDSINGLEAPPLICATIONGUARD_TIMEOUT_SECONDS
  37#define KDSINGLEAPPLICATIONGUARD_TIMEOUT_SECONDS 10
  38#endif
  39
  40#ifndef KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES
  41#define KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES 10
  42#endif
  43
  44#ifndef KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE
  45#define KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE 32768
  46#endif
  47
  48static unsigned int KDSINGLEAPPLICATIONGUARD_SHM_VERSION = 0;
  49
  50Q_GLOBAL_STATIC_WITH_ARGS( int, registerInstanceType,
  51                           (qRegisterMetaType<KDSingleApplicationGuard::Instance>()) )
  52
  53/*!
  54  \class KDSingleApplicationGuard::Instance
  55  \relates KDSingleApplicationGuard
  56  \ingroup core
  57  \brief Information about instances a KDSingleApplicationGuard knows about
  58
  59  Instance represents instances of applications under
  60  KDSingleApplicationGuard protection, and allows access to their
  61  pid() and the arguments() they were started with.
  62*/
  63
  64class KDSingleApplicationGuard::Instance::Private : public QSharedData {
  65    friend class ::KDSingleApplicationGuard::Instance;
  66public:
  67    Private( const QStringList & args, bool truncated, qint64 pid )
  68        : pid( pid ), arguments( args ), truncated( truncated ) {}
  69
  70private:
  71    qint64 pid;
  72    QStringList arguments;
  73    bool truncated;
  74};
  75
  76struct ProcessInfo;
  77
  78/*!
  79 \internal
  80 */
  81class KDSingleApplicationGuard::Private
  82{
  83    friend class ::KDSingleApplicationGuard;
  84    friend class ::KDSingleApplicationGuard::Instance;
  85    friend struct ::ProcessInfo;
  86    KDSingleApplicationGuard * const q;
  87public:
  88    Private( Policy policy, KDSingleApplicationGuard* qq );
  89    ~Private();
  90
  91    void create( const QStringList& arguments );
  92
  93    bool checkOperational( const char * function, const char * act ) const;
  94    bool checkOperationalPrimary( const char * function, const char * act ) const;
  95
  96    struct segmentheader
  97    {
  98        size_t size : 16;
  99    };
 100
 101    static void sharedmem_free( char* );
 102    static char* sharedmem_malloc( size_t size );
 103
 104private:
 105    void shutdownInstance();
 106    void poll();
 107
 108private:
 109    static KDSingleApplicationGuard* primaryInstance;
 110
 111private:
 112    QBasicTimer timer;
 113    QSharedMemory mem;
 114    int id;
 115    Policy policy;
 116    bool operational;
 117    bool exitRequested;
 118};
 119
 120/*!
 121  \internal
 122*/
 123KDSingleApplicationGuard::Instance::Instance( const QStringList & args, bool truncated, qint64 p )
 124    : d( new Private( args, truncated, p ) )
 125{
 126    d->ref.ref();
 127    (void)registerInstanceType();
 128}
 129
 130/*!
 131  Default constructor. Constructs in Instance that is \link isNull()
 132  null\endlink.
 133
 134  \sa isNull()
 135*/
 136KDSingleApplicationGuard::Instance::Instance() : d( 0 ) {}
 137
 138/*!
 139  Copy constructor.
 140*/
 141KDSingleApplicationGuard::Instance::Instance( const Instance & other )
 142    : d( other.d )
 143{
 144    if ( d )
 145        d->ref.ref();
 146}
 147
 148/*!
 149  Destructor.
 150*/
 151KDSingleApplicationGuard::Instance::~Instance()
 152{
 153    if ( d && !d->ref.deref() )
 154        delete d;
 155}
 156
 157/*!
 158  \fn KDSingleApplicationGuard::Instance::swap( Instance & other )
 159
 160  Swaps the contents of this and \a other.
 161
 162  This function never throws exceptions.
 163*/
 164
 165/*!
 166  \fn KDSingleApplicationGuard::Instance::operator=( Instance other )
 167
 168  Assigns the contents of \a other to this.
 169
 170  This function is strongly exception-safe.
 171*/
 172
 173/*!
 174  \fn std::swap( KDSingleApplicationGuard::Instance & lhs, KDSingleApplicationGuard::Instance & rhs )
 175  \relates KDSingleApplicationGuard::Instance
 176
 177  Specialisation of std::swap() for
 178  KDSingleApplicationGuard::Instance. Calls swap().
 179*/
 180
 181/*!
 182  \fn qSwap( KDSingleApplicationGuard::Instance & lhs, KDSingleApplicationGuard::Instance & rhs )
 183  \relates KDSingleApplicationGuard::Instance
 184
 185  Specialisation of qSwap() for
 186  KDSingleApplicationGuard::Instance. Calls swap().
 187*/
 188
 189/*!
 190  \fn KDSingleApplicationGuard::Instance::isNull() const
 191
 192  Returns whether this instance is null.
 193*/
 194
 195/*!
 196  Returns whether this instance is valid. A valid instance is neither
 197  null, nor does it have a negative PID.
 198*/
 199bool KDSingleApplicationGuard::Instance::isValid() const
 200{
 201    return d && d->pid >= 0 ;
 202}
 203
 204/*!
 205  Returns whether the #arguments are complete (\c false) or not (\c
 206  true), e.g. because they have been truncated due to limited storage
 207  space.
 208
 209  \sa arguments()
 210*/
 211bool KDSingleApplicationGuard::Instance::areArgumentsTruncated() const
 212{
 213    return d && d->truncated;
 214}
 215
 216/*!
 217  Returns the arguments that this instance was started with.
 218
 219  \sa areArgumentsTruncated()
 220*/
 221const QStringList & KDSingleApplicationGuard::Instance::arguments() const
 222{
 223    if ( d )
 224        return d->arguments;
 225    static const QStringList empty;
 226    return empty;
 227}
 228
 229/*!
 230  Returns the process-id (PID) of this instance.
 231*/
 232qint64 KDSingleApplicationGuard::Instance::pid() const
 233{
 234    if ( d )
 235        return d->pid;
 236    else
 237        return -1;
 238}
 239
 240/*!
 241  \class KDSingleApplicationGuard KDSingleApplicationGuard
 242  \ingroup core
 243  \brief A guard to protect an application from having several instances.
 244
 245  KDSingleApplicationGuard can be used to make sure only one instance of an
 246  application is running at the same time.
 247
 248  \note As KDSingleApplicationGuard currently uses QSharedMemory, Qt
 249  4.4 or later is required.
 250 */
 251
 252/*!
 253  \fn void KDSingleApplicationGuard::instanceStarted(const KDSingleApplicationGuard::Instance & instance)
 254
 255  This signal is emitted by the primary instance whenever another
 256  instance \a instance started.
 257 */
 258
 259/*!
 260  \fn void KDSingleApplicationGuard::instanceExited(const KDSingleApplicationGuard::Instance & instance)
 261
 262  This signal is emitted by the primary instance whenever another
 263  instance \a instance exited.
 264 */
 265
 266/*!
 267  \fn void KDSingleApplicationGuard::raiseRequested()
 268
 269  This signal is emitted when the current running application is requested
 270  to raise its main window.
 271*/
 272
 273/*!
 274  \fn void KDSingleApplicationGuard::exitRequested()
 275
 276  This signal is emitted when the current running application has been asked to exit
 277  by calling kill on the instance.
 278*/
 279
 280/*!
 281  \fn void KDSingleApplicationGuard::becamePrimaryInstance()
 282
 283  This signal is emitted when the current running application becomes
 284  the new primary application. The old primary application has quit.
 285 */
 286
 287/*!
 288  \fn void KDSingleApplicationGuard::becameSecondaryInstance()
 289
 290  This signal is emmited when the primary instance became secondary instance.
 291  This happens when the instance doesn't update its status for some (default 10) seconds. Another instance
 292  got primary instance in that case.
 293  */
 294
 295/*!
 296  \fn void KDSingleApplicationGuard::policyChanged( KDSingleApplicationGuard::Policy policy )
 297
 298  This signal is emitted when the #policy of the system changes.
 299*/
 300
 301enum Command
 302{
 303    NoCommand = 0x00,
 304    ExitedInstance = 0x01,
 305    NewInstance = 0x02,
 306    FreeInstance = 0x04,
 307    ShutDownCommand = 0x08,
 308    KillCommand = 0x10,
 309    BecomePrimaryCommand = 0x20,
 310    RaiseCommand = 0x40
 311};
 312
 313static const quint16 PrematureEndOfOptions = -1;
 314static const quint16 RegularEndOfOptions   = -2;
 315
 316struct ProcessInfo
 317{
 318    static const size_t MarkerSize = sizeof(quint16);
 319
 320    explicit ProcessInfo( Command c = FreeInstance, const QStringList& arguments = QStringList(), qint64 p = -1 )
 321        : pid( p ),
 322          command( c ),
 323          timestamp( 0 ),
 324          commandline( 0 )
 325    {
 326        setArguments( arguments );
 327    }
 328
 329    void setArguments( const QStringList & arguments );
 330    QStringList arguments( bool * prematureEnd  ) const;
 331
 332    qint64 pid;
 333    quint32 command;
 334    quint32 timestamp;
 335    char* commandline;
 336};
 337
 338static inline bool operator==( const ProcessInfo & lhs, const ProcessInfo & rhs )
 339{
 340    return lhs.command == rhs.command &&
 341           ( lhs.commandline == rhs.commandline || ( lhs.commandline != 0 && rhs.commandline != 0 && ::strcmp( lhs.commandline, rhs.commandline ) == 0 ) );
 342}
 343
 344static inline bool operator!=( const ProcessInfo & lhs, const ProcessInfo & rhs )
 345{
 346    return !operator==( lhs, rhs );
 347}
 348
 349/*!
 350  This struct contains information about the managed process system.
 351  \internal
 352 */
 353struct InstanceRegister
 354{
 355    explicit InstanceRegister( KDSingleApplicationGuard::Policy policy = KDSingleApplicationGuard::NoPolicy )
 356        : policy( policy ),
 357          maxInstances( KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES ),
 358          version( 0 )
 359    {
 360        std::fill_n( commandLines, KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE, 0 );
 361        ::memcpy( magicCookie, "kdsingleapp", 12 );
 362    }
 363
 364    /*!
 365      Returns whether this register was properly initialized by the first instance.
 366      */
 367    bool isValid() const
 368    {
 369        return ::strcmp( magicCookie, "kdsingleapp" ) == 0;
 370    }
 371
 372    char magicCookie[ 12 ];
 373    unsigned int policy  :  8;
 374    quint32 maxInstances : 20;
 375    unsigned int version :  4;
 376    ProcessInfo info[ KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES ];
 377
 378    char commandLines[ KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE ];
 379
 380    Q_DISABLE_COPY( InstanceRegister )
 381};
 382
 383void ProcessInfo::setArguments( const QStringList & arguments )
 384{
 385    if( commandline != 0 )
 386        KDSingleApplicationGuard::Private::sharedmem_free( commandline );
 387
 388    commandline = 0;
 389    if( arguments.isEmpty() )
 390        return;
 391
 392    size_t totalsize = MarkerSize;
 393    Q_FOREACH( const QString& arg, arguments )
 394    {
 395        const QByteArray utf8 = arg.toUtf8();
 396        totalsize += utf8.size() + MarkerSize;
 397    }
 398    InstanceRegister* const reg = reinterpret_cast<InstanceRegister*>( KDSingleApplicationGuard::Private::primaryInstance->d->mem.data() );
 399    this->commandline = KDSingleApplicationGuard::Private::sharedmem_malloc( totalsize );
 400    if( this->commandline == 0 )
 401    {
 402        qWarning("KDSingleApplicationguard: out of memory when trying to save arguments.\n");
 403        return;
 404    }
 405
 406    char* const commandline = this->commandline + reinterpret_cast<qptrdiff>(reg->commandLines);
 407
 408    int argpos = 0;
 409    Q_FOREACH( const QString & arg, arguments )
 410    {
 411        const QByteArray utf8 = arg.toUtf8();
 412        const int required = MarkerSize + utf8.size() + MarkerSize ;
 413        const int available = KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE - argpos ;
 414        if ( required > available || utf8.size() > std::numeric_limits<quint16>::max() ) {
 415            // write a premature-eoo marker, and quit
 416            memcpy( commandline + argpos, &PrematureEndOfOptions, MarkerSize );
 417            argpos += MarkerSize;
 418            qWarning( "KDSingleApplicationGuard: argument list is too long (bytes required: %d, used: %d, available: %d",
 419                      required, argpos - 2, available );
 420            return;
 421        } else {
 422            const quint16 len16 = utf8.size();
 423            // write the size of the data...
 424            memcpy( commandline + argpos, &len16, MarkerSize );
 425            argpos += MarkerSize;
 426            // then the data
 427            memcpy( commandline + argpos, utf8.data(), len16 );
 428            argpos += len16;
 429        }
 430    }
 431    const ssize_t available = KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE - argpos;
 432    assert( available >= static_cast<ssize_t>( MarkerSize ) );
 433    memcpy( commandline + argpos, &RegularEndOfOptions, MarkerSize );
 434    argpos += MarkerSize;
 435}
 436
 437QStringList ProcessInfo::arguments( bool * prematureEnd  ) const
 438{
 439    QStringList result;
 440    if( commandline == 0 )
 441    {
 442        if( prematureEnd )
 443            *prematureEnd = true;
 444        return result;
 445    }
 446
 447    InstanceRegister* const reg = reinterpret_cast<InstanceRegister*>( KDSingleApplicationGuard::Private::primaryInstance->d->mem.data() );
 448    const char* const commandline = this->commandline + reinterpret_cast<qptrdiff>(reg->commandLines);
 449
 450    int argpos = 0;
 451    while ( true ) {
 452        const int available = KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE - argpos ;
 453        assert( available >= 2 );
 454
 455        quint16 marker;
 456        memcpy( &marker, commandline + argpos, MarkerSize );
 457        argpos += MarkerSize;
 458
 459        if ( marker == PrematureEndOfOptions ) {
 460            if ( prematureEnd ) *prematureEnd = true;
 461            break;
 462        }
 463        if ( marker == RegularEndOfOptions ) {
 464            if ( prematureEnd ) *prematureEnd = false;
 465            break;
 466        }
 467
 468        const int requested = MarkerSize + marker + MarkerSize ;
 469        if ( requested > available ) {
 470            const long long int p = pid;
 471            qWarning( "KDSingleApplicationGuard: inconsistency detected when parsing command-line argument for process %lld", p );
 472            if ( prematureEnd ) *prematureEnd = true;
 473            break;
 474        }
 475
 476        result.push_back( QString::fromUtf8( commandline + argpos, marker ) );
 477        argpos += marker;
 478    }
 479
 480    return result;
 481}
 482
 483KDSingleApplicationGuard::Private::~Private()
 484{
 485    if( primaryInstance == q )
 486        primaryInstance = 0;
 487}
 488
 489bool KDSingleApplicationGuard::Private::checkOperational( const char * function, const char * act ) const
 490{
 491    assert( function );
 492    assert( act );
 493    if ( !operational )
 494        qWarning( "KDSingleApplicationGuard::%s: need to be operational to %s", function, act );
 495    return operational;
 496}
 497
 498bool KDSingleApplicationGuard::Private::checkOperationalPrimary( const char * function, const char * act ) const
 499{
 500    if ( !checkOperational( function, act ) )
 501        return false;
 502    if ( id != 0 )
 503        qWarning( "KDSingleApplicationGuard::%s: need to be primary to %s", function, act );
 504    return id == 0;
 505}
 506
 507struct segmentheader
 508{
 509    size_t size : 16;
 510};
 511
 512void KDSingleApplicationGuard::Private::sharedmem_free( char* pointer )
 513{
 514    InstanceRegister* const reg = reinterpret_cast<InstanceRegister*>( KDSingleApplicationGuard::Private::primaryInstance->d->mem.data() );
 515    char* const heap = reg->commandLines;
 516    char* const heap_ptr = heap + reinterpret_cast<qptrdiff>(pointer) - sizeof( segmentheader );
 517    const segmentheader* const header = reinterpret_cast< const segmentheader* >( heap_ptr );
 518    const size_t size = header->size;
 519
 520    char* end = heap + KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE;
 521
 522    std::copy( heap_ptr + size, end, heap_ptr );
 523    std::fill( end - size, end, 0 );
 524
 525    for( uint i = 0; i < reg->maxInstances; ++i )
 526    {
 527        if( reg->info[ i ].commandline > pointer )
 528            reg->info[ i ].commandline -= size + sizeof( segmentheader );
 529    }
 530}
 531
 532char* KDSingleApplicationGuard::Private::sharedmem_malloc( size_t size )
 533{
 534    InstanceRegister* const reg = reinterpret_cast<InstanceRegister*>( KDSingleApplicationGuard::Private::primaryInstance->d->mem.data() );
 535    char* heap = reg->commandLines;
 536
 537    while( heap + sizeof( segmentheader ) + size < reg->commandLines + KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE )
 538    {
 539        segmentheader* const header = reinterpret_cast< segmentheader* >( heap );
 540        if( header->size == 0 )
 541        {
 542            header->size = size;
 543            return heap + sizeof( segmentheader ) - reinterpret_cast<qptrdiff>(reg->commandLines);
 544        }
 545        heap += sizeof( header ) + header->size;
 546    }
 547    return 0;
 548}
 549
 550void KDSingleApplicationGuard::Private::shutdownInstance()
 551{
 552    KDLockedSharedMemoryPointer< InstanceRegister > instances( &q->d->mem );
 553    instances->info[ q->d->id ].command |= ExitedInstance;
 554
 555    if( q->isPrimaryInstance() )
 556    {
 557        // ohh... we need a new primary instance...
 558        for ( int i = 1, end = instances->maxInstances ; i  < end ; ++i )
 559        {
 560            if( ( instances->info[ i ].command & ( FreeInstance | ExitedInstance | ShutDownCommand | KillCommand ) ) == 0 )
 561            {
 562                instances->info[ i ].command |= BecomePrimaryCommand;
 563                return;
 564            }
 565        }
 566        // none found? then my species is dead :-(
 567    }
 568}
 569
 570KDSingleApplicationGuard* KDSingleApplicationGuard::Private::primaryInstance = 0;
 571
 572/*!
 573  Requests that the instance kills itself (by emitting exitRequested).
 574
 575  If the instance has since exited, does nothing.
 576
 577  \sa shutdown(), raise()
 578*/
 579void KDSingleApplicationGuard::Instance::kill()
 580{
 581    KDLockedSharedMemoryPointer< InstanceRegister > instances( &KDSingleApplicationGuard::Private::primaryInstance->d->mem );
 582    for ( int i = 0, end = instances->maxInstances ; i < end ; ++i )
 583    {
 584        if( instances->info[ i ].pid != d->pid )
 585           continue;
 586        if( ( instances->info[ i ].command & ( FreeInstance | ExitedInstance ) ) == 0 )
 587            instances->info[ i ].command = KillCommand;
 588    }
 589}
 590
 591/*!
 592  Requests that the instance shuts itself down (by calling QCoreApplication::quit()).
 593
 594  If the instance has since exited, does nothing.
 595
 596  \sa kill(), raise()
 597*/
 598void KDSingleApplicationGuard::Instance::shutdown()
 599{
 600    KDLockedSharedMemoryPointer< InstanceRegister > instances( &KDSingleApplicationGuard::Private::primaryInstance->d->mem );
 601    for ( int i = 0, end = instances->maxInstances ; i < end ; ++i )
 602    {
 603        if( instances->info[ i ].pid != d->pid )
 604           continue;
 605        if( ( instances->info[ i ].command & ( FreeInstance | ExitedInstance ) ) == 0 )
 606            instances->info[ i ].command = ShutDownCommand;
 607    }
 608}
 609
 610/*!
 611
 612  Requests that the instance raises its main window.
 613
 614  The effects are implementation-defined: the KDSingleApplicationGuard
 615  corresponding to the instance will emit its \link
 616  KDSingleApplicationGuard::raiseRequested() raiseRequested()\endlink
 617  signal.
 618
 619  If the instance has since exited, does nothing.
 620
 621  \sa kill(), shutdown()
 622*/
 623void KDSingleApplicationGuard::Instance::raise()
 624{
 625    KDLockedSharedMemoryPointer< InstanceRegister > instances( &KDSingleApplicationGuard::Private::primaryInstance->d->mem );
 626    for ( int i = 0, end = instances->maxInstances ; i < end ; ++i )
 627    {
 628        if( instances->info[ i ].pid != d->pid )
 629           continue;
 630        if( ( instances->info[ i ].command & ( FreeInstance | ExitedInstance ) ) == 0 )
 631            instances->info[ i ].command = RaiseCommand;
 632    }
 633}
 634
 635
 636#ifndef Q_WS_WIN
 637// static
 638void KDSingleApplicationGuard::SIGINT_handler( int sig )
 639{
 640    if( sig == SIGINT && Private::primaryInstance != 0 )
 641        Private::primaryInstance->d->shutdownInstance();
 642    ::exit( 1 );
 643}
 644#endif
 645
 646/*!
 647  \enum KDSingleApplicationGuard::Policy
 648
 649  Defines the policy that a KDSingleApplicationGuard can enforce:
 650*/
 651
 652/*!
 653  \var KDSingleApplicationGuard::NoPolicy
 654
 655  instanceStarted() is emitted, and the new instance allowed to continue.
 656*/
 657
 658/*!
 659  \var KDSingleApplicationGuard::AutoKillOtherInstances
 660
 661  instanceStarted() is emitted, and the new instance is killed (Instance::kill()).
 662*/
 663
 664/*!
 665  Creates a new KDSingleApplicationGuard with arguments
 666  QCoreApplication::arguments() and policy AutoKillOtherInstances,
 667  passing \a parent to the base class constructor, as usual.
 668*/
 669KDSingleApplicationGuard::KDSingleApplicationGuard( QObject * parent )
 670    : QObject( parent ), d( new Private( AutoKillOtherInstances, this ) )
 671{
 672    d->create( QCoreApplication::arguments() );
 673}
 674
 675/*!
 676  Creates a new KDSingleApplicationGuard with arguments
 677  QCoreApplication::arguments() and policy \a policy, passing \a
 678  parent to the base class constructor, as usual.
 679*/
 680KDSingleApplicationGuard::KDSingleApplicationGuard( Policy policy, QObject * parent )
 681    : QObject( parent ), d( new Private( policy, this ) )
 682{
 683    d->create( QCoreApplication::arguments() );
 684}
 685
 686/*!
 687  Creates a new KDSingleApplicationGuard with arguments \a arguments
 688  and policy AutoKillOtherInstances, passing \a parent to the base
 689  class constructor, as usual.
 690*/
 691KDSingleApplicationGuard::KDSingleApplicationGuard( const QStringList & arguments, QObject * parent )
 692    : QObject( parent ), d( new Private( AutoKillOtherInstances, this ) )
 693{
 694    d->create( arguments );
 695}
 696
 697/*!
 698  Creates a new KDSingleApplicationGuard with arguments \a arguments
 699  and policy \a policy, passing \a parent to the base class
 700  constructor, as usual.
 701*/
 702KDSingleApplicationGuard::KDSingleApplicationGuard( const QStringList & arguments, Policy policy, QObject * parent )
 703    : QObject( parent ), d( new Private( policy, this ) )
 704{
 705    d->create( arguments );
 706}
 707
 708KDSingleApplicationGuard::Private::Private( Policy policy_, KDSingleApplicationGuard * qq )
 709    : q( qq ),
 710      id( -1 ),
 711      policy( policy_ ),
 712      operational( false ),
 713      exitRequested( false )
 714{
 715}
 716
 717void KDSingleApplicationGuard::Private::create( const QStringList & arguments )
 718{
 719    if ( !QCoreApplication::instance() ) {
 720        qWarning( "KDSingleApplicationGuard: you need to construct a Q(Core)Application before you can construct a KDSingleApplicationGuard" );
 721        return;
 722    }
 723
 724    const QString name = QCoreApplication::applicationName();
 725    if ( name.isEmpty() ) {
 726        qWarning( "KDSingleApplicationGuard: QCoreApplication::applicationName must not be emty" );
 727        return;
 728    }
 729
 730    (void)registerInstanceType();
 731    if ( primaryInstance == 0 )
 732        primaryInstance = q;
 733
 734    mem.setKey( name );
 735
 736    // if another instance crashed, the shared memory segment is still there on Unix
 737    // the following lines trigger deletion in that case
 738#ifndef Q_WS_WIN
 739    mem.attach();
 740    mem.detach();
 741#endif
 742
 743    const bool created = mem.create( sizeof( InstanceRegister ) );
 744    if( !created )
 745    {
 746        QString errorMsg;
 747        if( mem.error() != QSharedMemory::NoError && mem.error() != QSharedMemory::AlreadyExists )
 748            errorMsg += QString::fromLatin1( "QSharedMemomry::create() failed: %1" ).arg( mem.errorString() );
 749
 750        if( !mem.attach() )
 751        {
 752            if( mem.error() != QSharedMemory::NoError )
 753                errorMsg += QString::fromLatin1( "QSharedMemomry::attach() failed: %1" ).arg( mem.errorString() );
 754
 755            qWarning( "KDSingleApplicationGuard: Could neither create nor attach to shared memory segment." );
 756            qWarning( "%s\n",  errorMsg.toLocal8Bit().constData() );
 757            return;
 758        }
 759
 760        const int maxWaitMSecs = 1000 * 60; // stop waiting after 60 seconds
 761        QTime waitTimer;
 762        waitTimer.start();
 763
 764        // lets wait till the other instance initialized the register
 765        bool initialized = false;
 766        while( !initialized && waitTimer.elapsed() < maxWaitMSecs )
 767        {
 768            const KDLockedSharedMemoryPointer< InstanceRegister > instances( &mem );
 769            initialized = instances->isValid();
 770#ifdef Q_WS_WIN
 771            ::Sleep(20);
 772#else
 773            usleep(20000);
 774#endif
 775        }
 776
 777        const KDLockedSharedMemoryPointer< InstanceRegister > instances( &mem );
 778        if ( instances->version != 0 ) {
 779            qWarning( "KDSingleApplicationGuard: Detected version mismatch. "
 780                      "Highest supported version: %ud, actual version: %ud",
 781                      KDSINGLEAPPLICATIONGUARD_SHM_VERSION, instances->version );
 782            return;
 783        }
 784
 785    }
 786
 787
 788    KDLockedSharedMemoryPointer< InstanceRegister > instances( &mem );
 789
 790    if( !created )
 791    {
 792        assert( instances->isValid() );
 793
 794        // we're _not_ the first instance
 795        // but the
 796        bool killOurSelf = false;
 797
 798        // find a new slot...
 799        id = std::find( instances->info, instances->info + instances->maxInstances, ProcessInfo() ) - instances->info;
 800        ProcessInfo& info = instances->info[ id ];
 801        info = ProcessInfo( NewInstance, arguments, QCoreApplication::applicationPid() );
 802        killOurSelf = instances->policy == AutoKillOtherInstances;
 803        policy = static_cast<Policy>( instances->policy );
 804
 805        // but the signal that we tried to start was sent to the primary application
 806        if( killOurSelf )
 807            exitRequested = true;
 808    }
 809    else
 810    {
 811        // ok.... we are the first instance
 812        new ( instances.get() ) InstanceRegister( policy ); // create a new list (in shared memory)
 813        id = 0;                                             // our id = 0
 814        // and we've no command
 815        instances->info[ 0 ] = ProcessInfo( NoCommand, arguments, QCoreApplication::applicationPid() );
 816    }
 817
 818#ifndef Q_WS_WIN
 819    ::signal( SIGINT, SIGINT_handler );
 820#endif
 821
 822    // now listen for commands
 823    timer.start( 750, q );
 824
 825    operational = true;
 826}
 827
 828/*!
 829  Destroys this SingleApplicationGuard.
 830  If this instance has been the primary instance and no other instance is existing anymore,
 831  the application is shut down completely. Otherwise the destructor selects another instance to
 832  be the primary instances.
 833 */
 834KDSingleApplicationGuard::~KDSingleApplicationGuard()
 835{
 836    if( d->id == -1 )
 837        return;
 838
 839    d->shutdownInstance();
 840}
 841
 842/*!
 843  \property KDSingleApplicationGuard::operational
 844
 845  Contains whether this KDSingleApplicationGuard is operational.
 846
 847  A non-operational KDSingleApplicationGuard cannot be used in any meaningful way.
 848
 849  Reasons for a KDSingleApplicationGuard being non-operational include:
 850  \li it was constructed before QApplication (or at least QCoreApplication) was constructed
 851  \li it failed to create or attach to the shared memory segment that is used for communication
 852
 853  Get this property's value using %isOperational().
 854*/
 855bool KDSingleApplicationGuard::isOperational() const
 856{
 857    return d->operational;
 858}
 859
 860/*!
 861  \property KDSingleApplicationGuard::exitRequested
 862
 863  Contains wheter this istance has been requested to exit. This will happen when this instance
 864  was just started, but the policy is AutoKillOtherInstances or by explicitely calling kill on
 865  this instance().
 866
 867  Get this property's value using %isExitRequested().
 868*/
 869bool KDSingleApplicationGuard::isExitRequested() const
 870{
 871    return d->exitRequested;
 872};
 873
 874/*!
 875  \property KDSingleApplicationGuard::primaryInstance
 876
 877   Contains whether this instance is the primary instance.
 878
 879   The primary instance is the first instance which was started or else the instance which
 880   got selected by KDSingleApplicationGuard's destructor, when the primary instance was
 881   shut down.
 882
 883   Get this property's value using %isPrimaryInstance(), and monitor changes to it
 884   using becamePrimaryInstance().
 885 */
 886bool KDSingleApplicationGuard::isPrimaryInstance() const
 887{
 888    return d->id == 0;
 889}
 890
 891/*!
 892 \property KDSingleApplicationGuard::policy
 893 Specifies the policy KDSingleApplicationGuard is using when new instances are started.
 894 This can only be set in the primary instance.
 895
 896 Get this property's value using %policy(), set it using %setPolicy(), and monitor changes
 897 to it using policyChanged().
 898 */
 899KDSingleApplicationGuard::Policy KDSingleApplicationGuard::policy() const
 900{
 901    return d->policy;
 902}
 903
 904void KDSingleApplicationGuard::setPolicy( Policy policy )
 905{
 906    if ( !d->checkOperationalPrimary( "setPolicy", "change the policy" ) )
 907        return;
 908
 909    if( d->policy == policy )
 910        return;
 911
 912    d->policy = policy;
 913    emit policyChanged( policy );
 914    KDLockedSharedMemoryPointer< InstanceRegister > instances( &d->mem );
 915    instances->policy = policy;
 916}
 917
 918/*!
 919 Returns a list of all currently running instances.
 920 */
 921QVector<KDSingleApplicationGuard::Instance>
 922KDSingleApplicationGuard::instances() const
 923{
 924    if ( !d->checkOperational( "instances", "report on other instances" ) )
 925        return QVector<Instance>();
 926
 927    if ( Private::primaryInstance == 0 ) {
 928        Private::primaryInstance = const_cast<KDSingleApplicationGuard*>( this );
 929    }
 930
 931    QVector<Instance> result;
 932    const KDLockedSharedMemoryPointer< InstanceRegister > instances( const_cast< QSharedMemory* >( &d->mem ) );
 933    for ( int i = 0, end = instances->maxInstances ; i < end ; ++i )
 934    {
 935        const ProcessInfo& info = instances->info[ i ];
 936        if( ( info.command & ( FreeInstance | ExitedInstance ) ) == 0 )
 937        {
 938            bool truncated;
 939            const QStringList arguments = info.arguments( &truncated );
 940            result.push_back( Instance( arguments, truncated, info.pid ) );
 941        }
 942    }
 943    return result;
 944}
 945
 946/*!
 947  Shuts down all other instances. This can only be called from the
 948  the primary instance.
 949  Shut down is done gracefully via QCoreApplication::quit().
 950 */
 951void KDSingleApplicationGuard::shutdownOtherInstances()
 952{
 953    if ( !d->checkOperationalPrimary( "shutdownOtherInstances", "shut other instances down" ) )
 954        return;
 955
 956    KDLockedSharedMemoryPointer< InstanceRegister > instances( &d->mem );
 957    for ( int i = 1, end = instances->maxInstances ; i < end ; ++i )
 958    {
 959        if( ( instances->info[ i ].command & ( FreeInstance | ExitedInstance ) ) == 0 )
 960            instances->info[ i ].command = ShutDownCommand;
 961    }
 962}
 963
 964/*!
 965  Kills all other instances. This can only be called from the
 966  the primary instance.
 967  Killing is done via emitting exitRequested. It's up to the receiving
 968  instance to react properly.
 969 */
 970void KDSingleApplicationGuard::killOtherInstances()
 971{
 972    if ( !d->checkOperationalPrimary( "killOtherInstances", "kill other instances" ) )
 973        return;
 974
 975    KDLockedSharedMemoryPointer< InstanceRegister > instances( &d->mem );
 976    for ( int i = 1, end = instances->maxInstances ; i < end ; ++i )
 977    {
 978        if( ( instances->info[ i ].command & ( FreeInstance | ExitedInstance ) ) == 0 )
 979            instances->info[ i ].command = KillCommand;
 980    }
 981}
 982
 983bool KDSingleApplicationGuard::event( QEvent * event )
 984{
 985    if ( event->type() == QEvent::Timer ) {
 986        const QTimerEvent * const te = static_cast<QTimerEvent*>( event );
 987        if ( te->timerId() == d->timer.timerId() ) {
 988            d->poll();
 989            return true;
 990        }
 991    }
 992    return QObject::event( event );
 993}
 994
 995void KDSingleApplicationGuard::Private::poll() {
 996
 997    const quint32 now = QDateTime::currentDateTime().toTime_t();
 998
 999    if ( primaryInstance == 0 ) {
1000        primaryInstance = q;
1001    }
1002
1003    if ( q->isPrimaryInstance() )
1004    {
1005        // only the primary instance will get notified about new instances
1006        QVector< Instance > exitedInstances;
1007        QVector< Instance > startedInstances;
1008
1009        {
1010            KDLockedSharedMemoryPointer< InstanceRegister > instances( &mem );
1011
1012            if( instances->info[ id ].pid != QCoreApplication::applicationPid() )
1013            {
1014                for ( int i = 1, end = instances->maxInstances ; i < end && id == 0 ; ++i )
1015                {
1016                    if( instances->info[ i ].pid == QCoreApplication::applicationPid() )
1017                        id = i;
1018                }
1019                emit q->becameSecondaryInstance();
1020                return;
1021            }
1022
1023            instances->info[ id ].timestamp = now;
1024
1025            for ( int i = 1, end = instances->maxInstances ; i < end ; ++i )
1026            {
1027                ProcessInfo& info = instances->info[ i ];
1028                if( info.command & NewInstance )
1029                {
1030                    bool truncated;
1031                    const QStringList arguments = info.arguments( &truncated );
1032                    startedInstances.push_back( Instance( arguments, truncated, info.pid ) );
1033                    info.command &= ~NewInstance;  // clear NewInstance flag
1034                }
1035                if( info.command & ExitedInstance )
1036                {
1037                    bool truncated;
1038                    const QStringList arguments = info.arguments( &truncated );
1039                    exitedInstances.push_back( Instance( arguments, truncated, info.pid ) );
1040                    info.command = FreeInstance;   // set FreeInstance flag
1041                }
1042            }
1043        }
1044
1045        // one signal for every new instance - _after_ the memory segment was unlocked again
1046        for( QVector< Instance >::const_iterator it = startedInstances.constBegin(); it != startedInstances.constEnd(); ++it )
1047            emit q->instanceStarted( *it );
1048        for( QVector< Instance >::const_iterator it = exitedInstances.constBegin(); it != exitedInstances.constEnd(); ++it )
1049            emit q->instanceExited( *it );
1050    }
1051    else
1052    {
1053        // do we have a command?
1054        bool killOurSelf = false;
1055        bool shutDownOurSelf = false;
1056        bool policyDidChange = false;
1057
1058        {
1059            KDLockedSharedMemoryPointer< InstanceRegister > instances( &mem );
1060
1061            const Policy oldPolicy = policy;
1062            policy = static_cast<Policy>( instances->policy );
1063            policyDidChange = policy != oldPolicy;
1064
1065            // check for the primary instance health status
1066            if( now - instances->info[ 0 ].timestamp > KDSINGLEAPPLICATIONGUARD_TIMEOUT_SECONDS )
1067            {
1068                std::swap( instances->info[ 0 ], instances->info[ id ] );
1069                id = 0;
1070                instances->info[ id ].timestamp = now;
1071                emit q->becamePrimaryInstance();
1072                instances->info[ id ].command &= ~BecomePrimaryCommand;  // afterwards, reset the flag
1073            }
1074
1075            if( instances->info[ id ].command & BecomePrimaryCommand )
1076            {
1077                // we became primary!
1078                instances->info[ 0 ] = instances->info[ id ];
1079                instances->info[ id ] = ProcessInfo();  // change our id to 0 and declare the old slot as free
1080                id = 0;
1081                instances->info[ id ].timestamp = now;
1082                emit q->becamePrimaryInstance();
1083            }
1084
1085            if( instances->info[ id ].command & RaiseCommand )
1086            {
1087               // raise ourself!
1088               emit q->raiseRequested();
1089               instances->info[ id ].command &= ~RaiseCommand;  // afterwards, reset the flag
1090            }
1091
1092
1093            killOurSelf = instances->info[ id ].command & KillCommand;            // check for kill command
1094            shutDownOurSelf = instances->info[ id ].command & ShutDownCommand;    // check for shut down command
1095            instances->info[ id ].command &= ~( KillCommand | ShutDownCommand | BecomePrimaryCommand );  // reset both flags
1096            if( killOurSelf )
1097            {
1098                instances->info[ id ].command |= ExitedInstance;  // upon kill, we have to set the ExitedInstance flag
1099                id = -1;                                          // becauso our d'tor won't be called anymore
1100            }
1101        }
1102
1103        if( killOurSelf )  // kill our self takes precedence
1104        {
1105            exitRequested = true;
1106            emit q->exitRequested();
1107        }
1108        else if( shutDownOurSelf )
1109            qApp->quit();
1110        else if( policyDidChange )
1111            emit q->policyChanged( policy );
1112    }
1113}
1114
1115#include "moc_kdsingleapplicationguard.cpp"
1116
1117#ifdef KDTOOLSCORE_UNITTESTS
1118
1119#include <kdunittest/test.h>
1120
1121#include "kdautopointer.h"
1122
1123#include <iostream>
1124
1125#include <QtCore/QTime>
1126#include <QtCore/QUuid>
1127#include <QtTest/QSignalSpy>
1128
1129static void wait( int msec, QSignalSpy * spy=0, int expectedCount=INT_MAX )
1130{
1131    QTime t;
1132    t.start();
1133    while ( ( !spy || spy->count() < expectedCount ) && t.elapsed() < msec )
1134    {
1135        qApp->processEvents( QEventLoop::WaitForMoreEvents, qMax( 10, msec - t.elapsed() ) );
1136    }
1137}
1138
1139static std::ostream& operator<<( std::ostream& stream, const QStringList& list )
1140{
1141    stream << "QStringList(";
1142    for( QStringList::const_iterator it = list.begin(); it != list.end(); ++it )
1143    {
1144        stream << " " << it->toLocal8Bit().data();
1145        if( it + 1 != list.end() )
1146            stream << ",";
1147    }
1148    stream << " )";
1149    return stream;
1150}
1151
1152namespace {
1153    class ApplicationNameSaver {
1154        Q_DISABLE_COPY( ApplicationNameSaver )
1155        const QString oldname;
1156    public:
1157        explicit ApplicationNameSaver( const QString & name )
1158            : oldname( QCoreApplication::applicationName() )
1159        {
1160            QCoreApplication::setApplicationName( name );
1161        }
1162        ~ApplicationNameSaver() {
1163            QCoreApplication::setApplicationName( oldname );
1164        }
1165    };
1166}
1167
1168KDAB_UNITTEST_SIMPLE( KDSingleApplicationGuard, "kdcoretools" ) {
1169
1170    // set it to an unique name
1171    const ApplicationNameSaver saver( QUuid::createUuid().toString() );
1172
1173    KDAutoPointer<KDSingleApplicationGuard> guard3;
1174    KDAutoPointer<QSignalSpy> spy3;
1175    KDAutoPointer<QSignalSpy> spy4;
1176
1177    {
1178        KDSingleApplicationGuard guard1;
1179        assertEqual( guard1.policy(), KDSingleApplicationGuard::AutoKillOtherInstances );
1180        assertEqual( guard1.instances().count(), 1 );
1181        assertTrue( guard1.isPrimaryInstance() );
1182
1183        guard1.setPolicy( KDSingleApplicationGuard::NoPolicy );
1184        assertEqual( guard1.policy(), KDSingleApplicationGuard::NoPolicy );
1185
1186        QSignalSpy spy1( &guard1, SIGNAL(instanceStarted(KDSingleApplicationGuard::Instance)) );
1187
1188        KDSingleApplicationGuard guard2;
1189        assertEqual( guard1.instances().count(), 2 );
1190        assertEqual( guard2.instances().count(), 2 );
1191        assertEqual( guard2.policy(), KDSingleApplicationGuard::NoPolicy );
1192        assertFalse( guard2.isPrimaryInstance() );
1193
1194        wait( 1000, &spy1, 1 );
1195
1196        assertEqual( spy1.count(), 1 );
1197        guard3.reset( new KDSingleApplicationGuard );
1198        spy3.reset( new QSignalSpy( guard3.get(), SIGNAL(becamePrimaryInstance()) ) );
1199        spy4.reset( new QSignalSpy( guard3.get(), SIGNAL(instanceExited(KDSingleApplicationGuard::Instance) ) ) );
1200        assertFalse( guard3->isPrimaryInstance() );
1201    }
1202
1203    wait( 1000, spy3.get(), 1 );
1204    wait( 1000, spy4.get(), 1 );
1205    assertEqual( spy3->count(), 1 );
1206    assertEqual( guard3->instances().count(), 1 );
1207    assertTrue( guard3->isPrimaryInstance() );
1208    guard3.reset( new KDSingleApplicationGuard );
1209
1210    assertEqual( guard3->instances().first().arguments(), qApp->arguments() );
1211
1212    QSignalSpy spyStarted( guard3.get(), SIGNAL(instanceStarted(KDSingleApplicationGuard::Instance)) );
1213    QSignalSpy spyExited(  guard3.get(), SIGNAL(instanceExited(KDSingleApplicationGuard::Instance)) );
1214
1215    {
1216        KDSingleApplicationGuard guard1;
1217        KDSingleApplicationGuard guard2;
1218
1219        wait( 1000, &spyStarted, 2 );
1220
1221        assertEqual( spyStarted.count(), 2 );
1222    }
1223
1224    wait( 1000, &spyExited, 2 );
1225    assertEqual( spyExited.count(), 2 );
1226
1227    spyStarted.clear();
1228    spyExited.clear();
1229
1230    {
1231        // check arguments-too-long handling:
1232        QStringList args;
1233        for ( unsigned int i = 0, end = KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE/16 ; i != end ; ++i )
1234            args.push_back( QLatin1String( "0123456789ABCDEF" ) );
1235        KDSingleApplicationGuard guard3( args );
1236
1237        wait( 1000, &spyStarted, 1 );
1238
1239        const QVector<KDSingleApplicationGuard::Instance> instances = guard3.instances();
1240        assertEqual( instances.size(), 2 );
1241
1242        assertTrue( instances[1].areArgumentsTruncated() );
1243    }
1244}
1245
1246#endif // KDTOOLSCORE_UNITTESTS
1247
1248#endif // QT_NO_SHAREDMEMORY
1249#endif // QT_VERSION >= 0x040400 || defined(DOXYGEN_RUN)