source: calamares/trunk/fuentes/src/libcalamares/kdsingleapplicationguard/kdsingleapplicationguard.cpp @ 7538

Last change on this file since 7538 was 7538, checked in by kbut, 13 months ago

sync with github

File size: 38.1 KB
Line 
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    for ( 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    for ( 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 empty" );
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)
Note: See TracBrowser for help on using the repository browser.