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

http://github.com/tomahawk-player/tomahawk · C++ · 1249 lines · 777 code · 184 blank · 288 comment · 151 complexity · 4ddd65ed2f1df70ea34258428d7bf2cf MD5 · raw file

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