diff --git a/src/calamares/main.cpp b/src/calamares/main.cpp index e69de29bb..ca450c85a 100644 --- a/src/calamares/main.cpp +++ b/src/calamares/main.cpp @@ -0,0 +1,44 @@ +/* === This file is part of Calamares - === + * + * Copyright 2014, Teo Mrnjavac + * + * Calamares is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Calamares. If not, see . + */ + + +#include "CalamaresApplication.h" + +#include "kdsingleapplicationguard/kdsingleapplicationguard.h" + +#include + +int +main( int argc, char *argv[] ) +{ + CalamaresApplication a( argc, argv ); + + KDSingleApplicationGuard guard( KDSingleApplicationGuard::AutoKillOtherInstances ); + QObject::connect( &guard, SIGNAL( instanceStarted( KDSingleApplicationGuard::Instance ) ), &a, SLOT( instanceStarted( KDSingleApplicationGuard::Instance ) ) ); + + int returnCode = 0; + if ( guard.isPrimaryInstance() ) + { + a.init(); + returnCode = a.exec(); + } + else + qDebug() << "Calamares is already running, shutting down."; + + return returnCode; +} diff --git a/src/libcalamares/CMakeLists.txt b/src/libcalamares/CMakeLists.txt index 21b43c03c..5736fa752 100644 --- a/src/libcalamares/CMakeLists.txt +++ b/src/libcalamares/CMakeLists.txt @@ -4,13 +4,18 @@ add_definitions( ${QT_DEFINITIONS} ) add_definitions( -DQT_SHARED ) add_definitions( -DQT_SHAREDPOINTER_TRACK_POINTERS ) -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/../calamares/Config.h.in - ${CMAKE_CURRENT_BINARY_DIR}/config.h) -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/../calamares/CalamaresVersion.h.in - ${CMAKE_CURRENT_BINARY_DIR}/CalamaresVersion.h) +configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/../calamares/Config.h.in + ${CMAKE_CURRENT_BINARY_DIR}/config.h ) +configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/../calamares/CalamaresVersion.h.in + ${CMAKE_CURRENT_BINARY_DIR}/CalamaresVersion.h ) set( libSources JobQueue.cpp + + kdsingleapplicationguard/kdsingleapplicationguard.cpp + kdsingleapplicationguard/kdsharedmemorylocker.cpp + kdsingleapplicationguard/kdtoolsglobal.cpp + kdsingleapplicationguard/kdlockedsharedmemorypointer.cpp ) include_directories( @@ -20,14 +25,13 @@ include_directories( ${QT_INCLUDE_DIR} ) -add_library( calamareslib SHARED ${libSources}) -set_target_properties( - calamareslib - PROPERTIES - AUTOMOC TRUE - VERSION ${CALAMARES_VERSION_SHORT} - SOVERSION ${CALAMARES_VERSION_SHORT} - OUTPUT_NAME "calamares" +add_library( calamareslib SHARED ${libSources} ) +set_target_properties( calamareslib + PROPERTIES + AUTOMOC TRUE + VERSION ${CALAMARES_VERSION_SHORT} + SOVERSION ${CALAMARES_VERSION_SHORT} + OUTPUT_NAME "calamares" ) qt5_use_modules( calamareslib Widgets ) @@ -50,6 +54,8 @@ install( TARGETS calamareslib # Install header files file( GLOB rootHeaders "*.h" ) +file( GLOB kdsingleapplicationguardHeaders "kdsingleapplicationguard/*.h" ) install( FILES ${CMAKE_CURRENT_BINARY_DIR}/config.h DESTINATION include/libcalamares ) install( FILES ${rootHeaders} DESTINATION include/libcalamares ) +install( FILES ${kdsingleapplicationguardHeaders} DESTINATION include/libcalamares/kdsingleapplicationguard ) diff --git a/src/libcalamares/DllMacro.h b/src/libcalamares/DllMacro.h new file mode 100644 index 000000000..a3b461b61 --- /dev/null +++ b/src/libcalamares/DllMacro.h @@ -0,0 +1,32 @@ +/* === This file is part of Calamares - === + * + * Copyright 2014, Teo Mrnjavac + * + * Calamares is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Calamares. If not, see . + */ + +#ifndef DLLMACRO_H +#define DLLMACRO_H + +#include + +#ifndef DLLEXPORT +# if defined (DLLEXPORT_PRO) +# define DLLEXPORT Q_DECL_EXPORT +# else +# define DLLEXPORT Q_DECL_IMPORT +# endif +#endif + +#endif diff --git a/src/libcalamares/kdsingleapplicationguard/LICENSE.LGPL.txt b/src/libcalamares/kdsingleapplicationguard/LICENSE.LGPL.txt new file mode 100644 index 000000000..df942b1ff --- /dev/null +++ b/src/libcalamares/kdsingleapplicationguard/LICENSE.LGPL.txt @@ -0,0 +1,488 @@ + + The KD Tools Library is Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. + + You may use, distribute and copy the KD Tools Library under the terms of + GNU Library General Public License version 2, which is displayed below. + +------------------------------------------------------------------------- + GNU LIBRARY GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Library General Public License, applies to some +specially designated Free Software Foundation software, and to any +other libraries whose authors decide to use it. You can use it for +your libraries, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if +you distribute copies of the library, or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link a program with the library, you must provide +complete object files to the recipients so that they can relink them +with the library, after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + Our method of protecting your rights has two steps: (1) copyright +the library, and (2) offer you this license which gives you legal +permission to copy, distribute and/or modify the library. + + Also, for each distributor's protection, we want to make certain +that everyone understands that there is no warranty for this free +library. If the library is modified by someone else and passed on, we +want its recipients to know that what they have is not the original +version, so that any problems introduced by others will not reflect on +the original authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that companies distributing free +software will individually obtain patent licenses, thus in effect +transforming the program into proprietary software. To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + + Most GNU software, including some libraries, is covered by the ordinary +GNU General Public License, which was designed for utility programs. This +license, the GNU Library General Public License, applies to certain +designated libraries. This license is quite different from the ordinary +one; be sure to read it in full, and don't assume that anything in it is +the same as in the ordinary license. + + The reason we have a separate public license for some libraries is that +they blur the distinction we usually make between modifying or adding to a +program and simply using it. Linking a program with a library, without +changing the library, is in some sense simply using the library, and is +analogous to running a utility program or application program. However, in +a textual and legal sense, the linked executable is a combined work, a +derivative of the original library, and the ordinary General Public License +treats it as such. + + Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries. We +concluded that weaker conditions might promote sharing better. + + However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves. This Library General Public License is intended to +permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them. (We have not seen how to achieve +this as regards changes in header files, but we have achieved it as regards +changes in the actual functions of the Library.) The hope is that this +will lead to faster development of free libraries. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, while the latter only +works together with the library. + + Note that it is possible for a library to be covered by the ordinary +General Public License rather than by this special one. + + GNU LIBRARY GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library which +contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Library +General Public License (also called "this License"). Each licensee is +addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also compile or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + c) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + d) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the source code distributed need not include anything that is normally +distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Library General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/src/libcalamares/kdsingleapplicationguard/kdlockedsharedmemorypointer.cpp b/src/libcalamares/kdsingleapplicationguard/kdlockedsharedmemorypointer.cpp new file mode 100644 index 000000000..56ab8ebe4 --- /dev/null +++ b/src/libcalamares/kdsingleapplicationguard/kdlockedsharedmemorypointer.cpp @@ -0,0 +1,475 @@ +#include "kdlockedsharedmemorypointer.h" + +#if QT_VERSION >= 0x040400 || defined( DOXYGEN_RUN ) +#ifndef QT_NO_SHAREDMEMORY + +namespace kdtools +{ +} +using namespace kdtools; + +KDLockedSharedMemoryPointerBase::KDLockedSharedMemoryPointerBase( QSharedMemory * m ) + : locker( m ), + mem( m ) +{ + +} + +KDLockedSharedMemoryPointerBase::KDLockedSharedMemoryPointerBase( QSharedMemory & m ) + : locker( &m ), + mem( &m ) +{ + +} + +KDLockedSharedMemoryPointerBase::~KDLockedSharedMemoryPointerBase() {} + +void * KDLockedSharedMemoryPointerBase::get() { + return mem ? mem->data() : 0 ; +} + +const void * KDLockedSharedMemoryPointerBase::get() const { + return mem ? mem->data() : 0 ; +} + +size_t KDLockedSharedMemoryPointerBase::byteSize() const { + return mem ? mem->size() : 0; +} + +/*! + \class KDLockedSharedMemoryPointer + \ingroup core raii smartptr + \brief Locking pointer for Qt shared memory segments + \since_c 2.1 + + (The exception safety of this class has not been evaluated yet.) + + KDLockedSharedMemoryPointer is a smart immutable pointer, which gives convenient and safe access to a QSharedMemory data segment. + The content of a KDLockedSharedMemoryPointer cannot be changed during it's lifetime. + + You can use this class like a normal pointer to the shared memory segment and be sure it's locked while accessing it. + \note You can only put simple types/structs/classes into it. structs and classes shall not contain any other pointers. See the + documentation of QSharedMemory for details. +*/ + +/*! + \fn KDLockedSharedMemoryPointer::KDLockedSharedMemoryPointer( QSharedMemory * mem ) + + Constructor. Constructs a KDLockedSharedMemory pointer which points to the data segment of \a mem. + The constructor locks \a mem. If the memory segment is already locked by another process, this constructor + blocks until the lock is released. + + \post data() == mem->data() and the memory segment has been locked +*/ + +/*! + \fn KDLockedSharedMemoryPointer::KDLockedSharedMemoryPointer( QSharedMemory & mem ) + + \overload + + \post data() == mem.data() and the memory segment has been locked +*/ + +/*! + \fn KDLockedSharedMemoryPointer::~KDLockedSharedMemoryPointer() + + Destructor. Unlocks the shared memory segment. + + \post The shared memory segment has been unlocked +*/ + +/*! + \fn T * KDLockedSharedMemoryPointer::get() + + \returns a pointer to the contained object. +*/ + +/*! + \fn const T * KDLockedSharedMemoryPointer::get() const + + \returns a const pointer to the contained object + \overload +*/ + +/*! + \fn T * KDLockedSharedMemoryPointer::data() + + Equivalent to get(), provided for consistency with Qt naming conventions. +*/ + +/*! + \fn const T * KDLockedSharedMemoryPointer::data() const + + \overload +*/ + +/*! + \fn T & KDLockedSharedMemoryPointer::operator*() + + Dereference operator. Returns \link get() *get()\endlink. +*/ + +/*! + \fn const T & KDLockedSharedMemoryPointer::operator*() const + + Dereference operator. Returns \link get() *get()\endlink. + \overload +*/ + +/*! + \fn T * KDLockedSharedMemoryPointer::operator->() + + Member-by-pointer operator. Returns get(). +*/ + +/*! + \fn const T * KDLockedSharedMemoryPointer::operator->() const + + Member-by-pointer operator. Returns get(). + \overload +*/ + +/*! + \class KDLockedSharedMemoryArray + \ingroup core raii smartptr + \brief Locking array pointer to Qt shared memory segments + \since_c 2.1 + + (The exception safety of this class has not been evaluated yet.) + + KDLockedSharedMemoryArray is a smart immutable pointer, which gives convenient and safe access to array data stored in a QSharedMemory + data segment. + The content of a KDLockedSharedMemoryArray cannot be changed during it's lifetime. + + You can use this class like a normal pointer to the shared memory segment and be sure it's locked while accessing it. + \note You can only put arrays of simple types/structs/classes into it. structs and classes shall not contain any other pointers. See the + documentation of QSharedMemory for details. + + \sa KDLockedSharedMemoryPointer +*/ + +/*! + \fn KDLockedSharedMemoryArray::KDLockedSharedMemoryArray( QSharedMemory* mem ) + Constructor. Constructs a KDLockedSharedMemoryArray which points to the data segment of \a mem. The constructor locks \a mem. If the memory + segment is already locked by another process, this constructor blocks until the lock is release. + + \post get() == mem->data() and the memory segment has been locked +*/ + +/*! + \fn KDLockedSharedMemoryArray::KDLockedSharedMemoryArray( QSharedMemory& mem ) + \overload + + \post get() == mem->data() and the memory segment has been locked +*/ + + +/*! + \typedef KDLockedSharedMemoryArray::size_type + Typedef for std::size_t. Provided for STL compatibility. +*/ + +/*! + \typedef KDLockedSharedMemoryArray::difference_type + Typedef for std::ptrdiff_t. Provided for STL compatibility. +*/ + +/*! + \typedef KDLockedSharedMemoryArray::iterator + Typedef for T*. Provided for STL compatibility. + \since_t 2.2 +*/ + +/*! + \typedef KDLockedSharedMemoryArray::const_iterator + Typedef for const T*. Provided for STL compatibility. + \since_t 2.2 +*/ + +/*! + \typedef KDLockedSharedMemoryArray::reverse_iterator + Typedef for std::reverse_iterator< \link KDLockedSharedMemoryArray::iterator iterator\endlink >. Provided for STL compatibility. + \since_t 2.2 +*/ + +/*! + \typedef KDLockedSharedMemoryArray::const_reverse_iterator + Typedef for std::reverse_iterator< \link KDLockedSharedMemoryArray::const_iterator const_iterator\endlink >. Provided for STL compatibility. + \since_t 2.2 +*/ + +/*! + \fn KDLockedSharedMemoryArray::iterator KDLockedSharedMemoryArray::begin() + Returns an \link KDLockedSharedMemoryArray::iterator iterator\endlink pointing to the first item of the array. + \since_f 2.2 +*/ + +/*! + \fn KDLockedSharedMemoryArray::const_iterator KDLockedSharedMemoryArray::begin() const + \overload + \since_f 2.2 +*/ + +/*! + \fn KDLockedSharedMemoryArray::iterator KDLockedSharedMemoryArray::end() + Returns an \link KDLockedSharedMemoryArray::iterator iterator\endlink pointing to the item after the last item of the array. + \since_f 2.2 +*/ + +/*! + \fn KDLockedSharedMemoryArray::const_iterator KDLockedSharedMemoryArray::end() const + \overload + \since_f 2.2 +*/ + +/*! + \fn KDLockedSharedMemoryArray::reverse_iterator KDLockedSharedMemoryArray::rbegin() + Returns an \link KDLockedSharedMemoryArray::reverse_iterator reverse_iterator\endlink pointing to the item after the last item of the array. + \since_f 2.2 +*/ + +/*! + \fn KDLockedSharedMemoryArray::const_reverse_iterator KDLockedSharedMemoryArray::rbegin() const + \overload + \since_f 2.2 +*/ + +/*! + \fn KDLockedSharedMemoryArray::reverse_iterator KDLockedSharedMemoryArray::rend() + Returns an \link KDLockedSharedMemoryArray::reverse_iterator reverse_iterator\endlink pointing to the first item of the array. + \since_f 2.2 +*/ + +/*! + \fn KDLockedSharedMemoryArray::const_reverse_iterator KDLockedSharedMemoryArray::rend() const + \overload + \since_f 2.2 +*/ + +/*! + \fn KDLockedSharedMemoryArray::size_type KDLockedSharedMemoryArray::size() const + Returns the size of this array. The size is calculated from the storage size of T and + the size of the shared memory segment. + \since_f 2.2 +*/ + +/*! + \fn T& KDLockedSharedMemoryArray::operator[]( difference_type n ) + Array access operator. Returns a reference to the item at index position \a n. +*/ + +/*! + \fn const T& KDLockedSharedMemoryArray::operator[]( difference_type n ) const + \overload +*/ + +/*! + \fn T& KDLockedSharedMemoryArray::front() + Returns a reference to the first item in the array. This is the same as operator[](0). +*/ + +/*! + \fn const T& KDLockedSharedMemoryArray::front() const + \overload +*/ + +/*! + \fn T& KDLockedSharedMemoryArray::back() + Returns a reference to the last item in the array. This is the same as operator[](size()-1). + \since_f 2.2 +*/ + +/*! + \fn const T& KDLockedSharedMemoryArray::back() const + \overload + \since_f 2.2 +*/ + + +#ifdef eKDTOOLSCORE_UNITTESTS + +#include + +#include +#include + +namespace +{ + struct TestStruct + { + TestStruct( uint nn = 0 ) + : n( nn ), + f( 0.0 ), + c( '\0' ), + b( false ) + { + } + uint n; + double f; + char c; + bool b; + }; + + bool operator==( const TestStruct& lhs, const TestStruct& rhs ) + { + return lhs.n == rhs.n && lhs.f == rhs.f && lhs.c == rhs.c && lhs.b == rhs.b; + } + + class TestThread : public QThread + { + public: + TestThread( const QString& key ) + : mem( key ) + { + mem.attach(); + } + + void run() + { + while( true ) + { + msleep( 100 ); + kdtools::KDLockedSharedMemoryPointer< TestStruct > p( &mem ); + if( !p->b ) + continue; + + p->n = 5; + p->f = 3.14; + p->c = 'A'; + p->b = false; + return; + } + } + + QSharedMemory mem; + }; + + bool isConst( TestStruct* ) + { + return false; + } + + bool isConst( const TestStruct* ) + { + return true; + } +} + + +KDAB_UNITTEST_SIMPLE( KDLockedSharedMemoryPointer, "kdcoretools" ) { + + const QString key = QUuid::createUuid(); + QSharedMemory mem( key ); + const bool created = mem.create( sizeof( TestStruct ) ); + assertTrue( created ); + if ( !created ) + return; // don't execute tests if shm coulnd't be created + + // On Windows, shared mem is only available in increments of page + // size (4k), so don't fail if the segment is larger: + const unsigned long mem_size = mem.size(); + assertGreaterOrEqual( mem_size, sizeof( TestStruct ) ); + + { + kdtools::KDLockedSharedMemoryPointer< TestStruct > p( &mem ); + assertTrue( p ); + *p = TestStruct(); + assertEqual( p->n, 0u ); + assertEqual( p->f, 0.0 ); + assertEqual( p->c, '\0' ); + assertFalse( p->b ); + } + + { + TestThread thread( key ); + assertEqual( thread.mem.key().toStdString(), key.toStdString() ); + assertEqual( static_cast< unsigned long >( thread.mem.size() ), mem_size ); + thread.start(); + + assertTrue( thread.isRunning() ); + thread.wait( 2000 ); + assertTrue( thread.isRunning() ); + + { + kdtools::KDLockedSharedMemoryPointer< TestStruct > p( &mem ); + p->b = true; + } + + thread.wait( 2000 ); + assertFalse( thread.isRunning() ); + } + + { + kdtools::KDLockedSharedMemoryPointer< TestStruct > p( &mem ); + assertEqual( p->n, 5u ); + assertEqual( p->f, 3.14 ); + assertEqual( p->c, 'A' ); + assertFalse( p->b ); + } + + { + kdtools::KDLockedSharedMemoryPointer< TestStruct > p( mem ); + assertEqual( mem.data(), p.get() ); + assertEqual( p.get(), p.operator->() ); + assertEqual( p.get(), &(*p) ); + assertEqual( p.get(), p.data() ); + assertFalse( isConst( p.get() ) ); + } + + { + const kdtools::KDLockedSharedMemoryPointer< TestStruct > p( &mem ); + assertEqual( mem.data(), p.get() ); + assertEqual( p.get(), p.operator->() ); + assertEqual( p.get(), &(*p) ); + assertEqual( p.get(), p.data() ); + assertTrue( isConst( p.get() ) ); + } + + { + QSharedMemory mem2( key + key ); + const bool created2 = mem2.create( 16 * sizeof( TestStruct ) ); + assertTrue( created2 ); + if ( !created2 ) + return; // don't execute tests if shm coulnd't be created + + kdtools::KDLockedSharedMemoryArray a( mem2 ); + assertTrue( a ); + assertEqual( a.get(), mem2.data() ); + assertEqual( &a[0], a.get() ); + + a[1] = a[0]; + assertTrue( a[0] == a[1] ); + + TestStruct ts; + ts.n = 5; + ts.f = 3.14; + a[0] = ts; + assertFalse( a[0] == a[1] ); + assertEqual( a.front().n, ts.n ); + assertEqual( a[0].f, ts.f ); + a[0].n = 10; + assertEqual( a.front().n, 10u ); + ts = a[0]; + assertEqual( ts.n, 10u ); + + std::vector< TestStruct > v; + for( uint i = 0; i < a.size(); ++i ) + v.push_back( TestStruct( i ) ); + + std::copy( v.begin(), v.end(), a.begin() ); + for( uint i = 0; i < a.size(); ++i ) + assertEqual( a[ i ].n, i ); + assertEqual( a.front().n, 0u ); + assertEqual( a.back().n, a.size() - 1 ); + + std::copy( v.begin(), v.end(), a.rbegin() ); + for( uint i = 0; i < a.size(); ++i ) + assertEqual( a[ i ].n, a.size() - 1 - i ); + assertEqual( a.front().n, a.size() - 1 ); + assertEqual( a.back().n, 0u ); + } + +} +#endif // KDTOOLSCORE_UNITTESTS +#endif // QT_NO_SHAREDMEMORY +#endif // QT_VERSION >= 0x040400 || defined( DOXYGEN_RUN ) diff --git a/src/libcalamares/kdsingleapplicationguard/kdlockedsharedmemorypointer.h b/src/libcalamares/kdsingleapplicationguard/kdlockedsharedmemorypointer.h new file mode 100644 index 000000000..df0ea4998 --- /dev/null +++ b/src/libcalamares/kdsingleapplicationguard/kdlockedsharedmemorypointer.h @@ -0,0 +1,115 @@ +#ifndef __KDTOOLS__CORE__KDLOCKEDSHAREDMEMORYPOINTER_H__ +#define __KDTOOLS__CORE__KDLOCKEDSHAREDMEMORYPOINTER_H__ + +#include + +#if QT_VERSION >= 0x040400 || defined( DOXYGEN_RUN ) +#ifndef QT_NO_SHAREDMEMORY + +#include "kdsharedmemorylocker.h" +#include + +#include + +#ifndef DOXYGEN_RUN +namespace kdtools { +#endif + +class KDLockedSharedMemoryPointerBase { +protected: + explicit KDLockedSharedMemoryPointerBase( QSharedMemory * mem ); + explicit KDLockedSharedMemoryPointerBase( QSharedMemory & mem ); + ~KDLockedSharedMemoryPointerBase(); + + // PENDING(marc) do we really want const propagation here? I + // usually declare all my RAII objects const... + void * get(); + const void * get() const; + + KDAB_IMPLEMENT_SAFE_BOOL_OPERATOR( get() ) + + size_t byteSize() const; + +private: + KDSharedMemoryLocker locker; + QSharedMemory * const mem; +}; + +template< typename T> +class MAKEINCLUDES_EXPORT KDLockedSharedMemoryPointer : KDLockedSharedMemoryPointerBase { + KDAB_DISABLE_COPY( KDLockedSharedMemoryPointer ); +public: + explicit KDLockedSharedMemoryPointer( QSharedMemory * m ) + : KDLockedSharedMemoryPointerBase( m ) {} + explicit KDLockedSharedMemoryPointer( QSharedMemory & m ) + : KDLockedSharedMemoryPointerBase( m ) {} + + T * get() { return static_cast( KDLockedSharedMemoryPointerBase::get() ); } + const T * get() const { return static_cast( KDLockedSharedMemoryPointerBase::get() ); } + + T * data() { return static_cast( get() ); } + const T * data() const { return static_cast( get() ); } + + T & operator*() { assert( get() ); return *get(); } + const T & operator*() const { assert( get() ); return *get(); } + + T * operator->() { return get(); } + const T * operator->() const { return get(); } + + KDAB_USING_SAFE_BOOL_OPERATOR( KDLockedSharedMemoryPointerBase ) +}; + +template +class MAKEINCLUDES_EXPORT KDLockedSharedMemoryArray : KDLockedSharedMemoryPointerBase { + KDAB_DISABLE_COPY( KDLockedSharedMemoryArray ); +public: + explicit KDLockedSharedMemoryArray( QSharedMemory * m ) + : KDLockedSharedMemoryPointerBase( m ) {} + explicit KDLockedSharedMemoryArray( QSharedMemory & m ) + : KDLockedSharedMemoryPointerBase( m ) {} + + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; + typedef T* iterator; + typedef const T* const_iterator; + typedef std::reverse_iterator< const_iterator > const_reverse_iterator; + typedef std::reverse_iterator< iterator > reverse_iterator; + + iterator begin() { return get(); } + const_iterator begin() const { return get(); } + + iterator end() { return begin() + size(); } + const_iterator end() const { return begin() + size(); } + + reverse_iterator rbegin() { return reverse_iterator( end() ); } + const_reverse_iterator rbegin() const { return reverse_iterator( end() ); } + + reverse_iterator rend() { return reverse_iterator( begin() ); } + const_reverse_iterator rend() const { return const_reverse_iterator( begin() ); } + + size_type size() const { return byteSize() / sizeof( T ); } + + T * get() { return static_cast( KDLockedSharedMemoryPointerBase::get() ); } + const T * get() const { return static_cast( KDLockedSharedMemoryPointerBase::get() ); } + + T & operator[]( difference_type n ) { assert( get() ); return *(get()+n); } + const T & operator[]( difference_type n ) const { assert( get() ); return *(get()+n); } + + T & front() { assert( get() ); return *get(); } + const T & front() const { assert( get() ); return *get(); } + + T & back() { assert( get() ); return *( get() + size() - 1 ); } + const T & back() const { assert( get() ); return *( get() + size() - 1 ); } + + KDAB_USING_SAFE_BOOL_OPERATOR( KDLockedSharedMemoryPointerBase ) +}; + +#ifndef DOXYGEN_RUN +} +#endif + +#endif /* QT_NO_SHAREDMEMORY */ + +#endif /* QT_VERSION >= 0x040400 || defined( DOXYGEN_RUN ) */ + +#endif /* __KDTOOLS__CORE__KDLOCKEDSHAREDMEMORYPOINTER_H__ */ diff --git a/src/libcalamares/kdsingleapplicationguard/kdsharedmemorylocker.cpp b/src/libcalamares/kdsingleapplicationguard/kdsharedmemorylocker.cpp new file mode 100644 index 000000000..0c99b8fff --- /dev/null +++ b/src/libcalamares/kdsingleapplicationguard/kdsharedmemorylocker.cpp @@ -0,0 +1,40 @@ +#include "kdsharedmemorylocker.h" + +#if QT_VERSION >= 0x040400 || defined( DOXYGEN_RUN ) + +#include + +using namespace kdtools; + +/*! + \class KDSharedMemoryLocker + \ingroup raii core + \brief Exception-safe and convenient wrapper around QSharedMemory::lock() +*/ + +/** + * Constructor. Locks the shared memory segment \a mem. + * If another process has locking the segment, this constructor blocks + * until the lock is released. The memory segments needs to be properly created or attached. + */ +KDSharedMemoryLocker::KDSharedMemoryLocker( QSharedMemory* mem ) + : mem( mem ) +{ + mem->lock(); +} + +/** + * Destructor. Unlocks the shared memory segment associated with this + * KDSharedMemoryLocker. + */ +KDSharedMemoryLocker::~KDSharedMemoryLocker() +{ + mem->unlock(); +} + +#ifdef KDAB_EVAL +#include KDAB_EVAL +static const EvalDialogChecker evalChecker( "KD Tools", false ); +#endif + +#endif diff --git a/src/libcalamares/kdsingleapplicationguard/kdsharedmemorylocker.h b/src/libcalamares/kdsingleapplicationguard/kdsharedmemorylocker.h new file mode 100644 index 000000000..7ae83e771 --- /dev/null +++ b/src/libcalamares/kdsingleapplicationguard/kdsharedmemorylocker.h @@ -0,0 +1,36 @@ +#ifndef __KDTOOLS__CORE__KDSHAREDMEMORYLOCKER_H +#define __KDTOOLS__CORE__KDSHAREDMEMORYLOCKER_H + +#include "kdtoolsglobal.h" + +#if QT_VERSION < 0x040400 && !defined( DOXYGEN_RUN ) +#ifdef Q_CC_GNU +#warning "Can't use KDTools KDSharedMemoryLocker with Qt versions prior to 4.4" +#endif +#else + +class QSharedMemory; + +#ifndef DOXYGEN_RUN +namespace kdtools +{ +#endif + +class KDTOOLSCORE_EXPORT KDSharedMemoryLocker +{ + Q_DISABLE_COPY( KDSharedMemoryLocker ) +public: + KDSharedMemoryLocker( QSharedMemory* mem ); + ~KDSharedMemoryLocker(); + +private: + QSharedMemory* const mem; +}; + +#ifndef DOXYGEN_RUN +} +#endif + +#endif + +#endif diff --git a/src/libcalamares/kdsingleapplicationguard/kdsingleapplicationguard.cpp b/src/libcalamares/kdsingleapplicationguard/kdsingleapplicationguard.cpp new file mode 100644 index 000000000..cd76f8811 --- /dev/null +++ b/src/libcalamares/kdsingleapplicationguard/kdsingleapplicationguard.cpp @@ -0,0 +1,1249 @@ +#include "kdsingleapplicationguard.h" + +#if QT_VERSION >= 0x040400 || defined(DOXYGEN_RUN) +#ifndef QT_NO_SHAREDMEMORY + +#include "kdsharedmemorylocker.h" +#include "kdlockedsharedmemorypointer.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#ifndef Q_WS_WIN +#include +#include +#endif + +#ifdef Q_WS_WIN +#include +#ifndef _SSIZE_T_DEFINED +typedef signed int ssize_t; +#endif +#endif + +using namespace kdtools; + +#ifndef KDSINGLEAPPLICATIONGUARD_TIMEOUT_SECONDS +#define KDSINGLEAPPLICATIONGUARD_TIMEOUT_SECONDS 10 +#endif + +#ifndef KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES +#define KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES 10 +#endif + +#ifndef KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE +#define KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE 32768 +#endif + +static unsigned int KDSINGLEAPPLICATIONGUARD_SHM_VERSION = 0; + +Q_GLOBAL_STATIC_WITH_ARGS( int, registerInstanceType, + (qRegisterMetaType()) ) + +/*! + \class KDSingleApplicationGuard::Instance + \relates KDSingleApplicationGuard + \ingroup core + \brief Information about instances a KDSingleApplicationGuard knows about + + Instance represents instances of applications under + KDSingleApplicationGuard protection, and allows access to their + pid() and the arguments() they were started with. +*/ + +class KDSingleApplicationGuard::Instance::Private : public QSharedData { + friend class ::KDSingleApplicationGuard::Instance; +public: + Private( const QStringList & args, bool truncated, qint64 pid ) + : pid( pid ), arguments( args ), truncated( truncated ) {} + +private: + qint64 pid; + QStringList arguments; + bool truncated; +}; + +struct ProcessInfo; + +/*! + \internal + */ +class KDSingleApplicationGuard::Private +{ + friend class ::KDSingleApplicationGuard; + friend class ::KDSingleApplicationGuard::Instance; + friend struct ::ProcessInfo; + KDSingleApplicationGuard * const q; +public: + Private( Policy policy, KDSingleApplicationGuard* qq ); + ~Private(); + + void create( const QStringList& arguments ); + + bool checkOperational( const char * function, const char * act ) const; + bool checkOperationalPrimary( const char * function, const char * act ) const; + + struct segmentheader + { + size_t size : 16; + }; + + static void sharedmem_free( char* ); + static char* sharedmem_malloc( size_t size ); + +private: + void shutdownInstance(); + void poll(); + +private: + static KDSingleApplicationGuard* primaryInstance; + +private: + QBasicTimer timer; + QSharedMemory mem; + int id; + Policy policy; + bool operational; + bool exitRequested; +}; + +/*! + \internal +*/ +KDSingleApplicationGuard::Instance::Instance( const QStringList & args, bool truncated, qint64 p ) + : d( new Private( args, truncated, p ) ) +{ + d->ref.ref(); + (void)registerInstanceType(); +} + +/*! + Default constructor. Constructs in Instance that is \link isNull() + null\endlink. + + \sa isNull() +*/ +KDSingleApplicationGuard::Instance::Instance() : d( 0 ) {} + +/*! + Copy constructor. +*/ +KDSingleApplicationGuard::Instance::Instance( const Instance & other ) + : d( other.d ) +{ + if ( d ) + d->ref.ref(); +} + +/*! + Destructor. +*/ +KDSingleApplicationGuard::Instance::~Instance() +{ + if ( d && !d->ref.deref() ) + delete d; +} + +/*! + \fn KDSingleApplicationGuard::Instance::swap( Instance & other ) + + Swaps the contents of this and \a other. + + This function never throws exceptions. +*/ + +/*! + \fn KDSingleApplicationGuard::Instance::operator=( Instance other ) + + Assigns the contents of \a other to this. + + This function is strongly exception-safe. +*/ + +/*! + \fn std::swap( KDSingleApplicationGuard::Instance & lhs, KDSingleApplicationGuard::Instance & rhs ) + \relates KDSingleApplicationGuard::Instance + + Specialisation of std::swap() for + KDSingleApplicationGuard::Instance. Calls swap(). +*/ + +/*! + \fn qSwap( KDSingleApplicationGuard::Instance & lhs, KDSingleApplicationGuard::Instance & rhs ) + \relates KDSingleApplicationGuard::Instance + + Specialisation of qSwap() for + KDSingleApplicationGuard::Instance. Calls swap(). +*/ + +/*! + \fn KDSingleApplicationGuard::Instance::isNull() const + + Returns whether this instance is null. +*/ + +/*! + Returns whether this instance is valid. A valid instance is neither + null, nor does it have a negative PID. +*/ +bool KDSingleApplicationGuard::Instance::isValid() const +{ + return d && d->pid >= 0 ; +} + +/*! + Returns whether the #arguments are complete (\c false) or not (\c + true), e.g. because they have been truncated due to limited storage + space. + + \sa arguments() +*/ +bool KDSingleApplicationGuard::Instance::areArgumentsTruncated() const +{ + return d && d->truncated; +} + +/*! + Returns the arguments that this instance was started with. + + \sa areArgumentsTruncated() +*/ +const QStringList & KDSingleApplicationGuard::Instance::arguments() const +{ + if ( d ) + return d->arguments; + static const QStringList empty; + return empty; +} + +/*! + Returns the process-id (PID) of this instance. +*/ +qint64 KDSingleApplicationGuard::Instance::pid() const +{ + if ( d ) + return d->pid; + else + return -1; +} + +/*! + \class KDSingleApplicationGuard KDSingleApplicationGuard + \ingroup core + \brief A guard to protect an application from having several instances. + + KDSingleApplicationGuard can be used to make sure only one instance of an + application is running at the same time. + + \note As KDSingleApplicationGuard currently uses QSharedMemory, Qt + 4.4 or later is required. + */ + +/*! + \fn void KDSingleApplicationGuard::instanceStarted(const KDSingleApplicationGuard::Instance & instance) + + This signal is emitted by the primary instance whenever another + instance \a instance started. + */ + +/*! + \fn void KDSingleApplicationGuard::instanceExited(const KDSingleApplicationGuard::Instance & instance) + + This signal is emitted by the primary instance whenever another + instance \a instance exited. + */ + +/*! + \fn void KDSingleApplicationGuard::raiseRequested() + + This signal is emitted when the current running application is requested + to raise its main window. +*/ + +/*! + \fn void KDSingleApplicationGuard::exitRequested() + + This signal is emitted when the current running application has been asked to exit + by calling kill on the instance. +*/ + +/*! + \fn void KDSingleApplicationGuard::becamePrimaryInstance() + + This signal is emitted when the current running application becomes + the new primary application. The old primary application has quit. + */ + +/*! + \fn void KDSingleApplicationGuard::becameSecondaryInstance() + + This signal is emmited when the primary instance became secondary instance. + This happens when the instance doesn't update its status for some (default 10) seconds. Another instance + got primary instance in that case. + */ + +/*! + \fn void KDSingleApplicationGuard::policyChanged( KDSingleApplicationGuard::Policy policy ) + + This signal is emitted when the #policy of the system changes. +*/ + +enum Command +{ + NoCommand = 0x00, + ExitedInstance = 0x01, + NewInstance = 0x02, + FreeInstance = 0x04, + ShutDownCommand = 0x08, + KillCommand = 0x10, + BecomePrimaryCommand = 0x20, + RaiseCommand = 0x40 +}; + +static const quint16 PrematureEndOfOptions = -1; +static const quint16 RegularEndOfOptions = -2; + +struct ProcessInfo +{ + static const size_t MarkerSize = sizeof(quint16); + + explicit ProcessInfo( Command c = FreeInstance, const QStringList& arguments = QStringList(), qint64 p = -1 ) + : pid( p ), + command( c ), + timestamp( 0 ), + commandline( 0 ) + { + setArguments( arguments ); + } + + void setArguments( const QStringList & arguments ); + QStringList arguments( bool * prematureEnd ) const; + + qint64 pid; + quint32 command; + quint32 timestamp; + char* commandline; +}; + +static inline bool operator==( const ProcessInfo & lhs, const ProcessInfo & rhs ) +{ + return lhs.command == rhs.command && + ( lhs.commandline == rhs.commandline || ( lhs.commandline != 0 && rhs.commandline != 0 && ::strcmp( lhs.commandline, rhs.commandline ) == 0 ) ); +} + +static inline bool operator!=( const ProcessInfo & lhs, const ProcessInfo & rhs ) +{ + return !operator==( lhs, rhs ); +} + +/*! + This struct contains information about the managed process system. + \internal + */ +struct InstanceRegister +{ + explicit InstanceRegister( KDSingleApplicationGuard::Policy policy = KDSingleApplicationGuard::NoPolicy ) + : policy( policy ), + maxInstances( KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES ), + version( 0 ) + { + std::fill_n( commandLines, KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE, 0 ); + ::memcpy( magicCookie, "kdsingleapp", 12 ); + } + + /*! + Returns whether this register was properly initialized by the first instance. + */ + bool isValid() const + { + return ::strcmp( magicCookie, "kdsingleapp" ) == 0; + } + + char magicCookie[ 12 ]; + unsigned int policy : 8; + quint32 maxInstances : 20; + unsigned int version : 4; + ProcessInfo info[ KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES ]; + + char commandLines[ KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE ]; + + Q_DISABLE_COPY( InstanceRegister ) +}; + +void ProcessInfo::setArguments( const QStringList & arguments ) +{ + if( commandline != 0 ) + KDSingleApplicationGuard::Private::sharedmem_free( commandline ); + + commandline = 0; + if( arguments.isEmpty() ) + return; + + size_t totalsize = MarkerSize; + Q_FOREACH( const QString& arg, arguments ) + { + const QByteArray utf8 = arg.toUtf8(); + totalsize += utf8.size() + MarkerSize; + } + InstanceRegister* const reg = reinterpret_cast( KDSingleApplicationGuard::Private::primaryInstance->d->mem.data() ); + this->commandline = KDSingleApplicationGuard::Private::sharedmem_malloc( totalsize ); + if( this->commandline == 0 ) + { + qWarning("KDSingleApplicationguard: out of memory when trying to save arguments.\n"); + return; + } + + char* const commandline = this->commandline + reinterpret_cast(reg->commandLines); + + int argpos = 0; + Q_FOREACH( const QString & arg, arguments ) + { + const QByteArray utf8 = arg.toUtf8(); + const int required = MarkerSize + utf8.size() + MarkerSize ; + const int available = KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE - argpos ; + if ( required > available || utf8.size() > std::numeric_limits::max() ) { + // write a premature-eoo marker, and quit + memcpy( commandline + argpos, &PrematureEndOfOptions, MarkerSize ); + argpos += MarkerSize; + qWarning( "KDSingleApplicationGuard: argument list is too long (bytes required: %d, used: %d, available: %d", + required, argpos - 2, available ); + return; + } else { + const quint16 len16 = utf8.size(); + // write the size of the data... + memcpy( commandline + argpos, &len16, MarkerSize ); + argpos += MarkerSize; + // then the data + memcpy( commandline + argpos, utf8.data(), len16 ); + argpos += len16; + } + } + const ssize_t available = KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE - argpos; + assert( available >= static_cast( MarkerSize ) ); + memcpy( commandline + argpos, &RegularEndOfOptions, MarkerSize ); + argpos += MarkerSize; +} + +QStringList ProcessInfo::arguments( bool * prematureEnd ) const +{ + QStringList result; + if( commandline == 0 ) + { + if( prematureEnd ) + *prematureEnd = true; + return result; + } + + InstanceRegister* const reg = reinterpret_cast( KDSingleApplicationGuard::Private::primaryInstance->d->mem.data() ); + const char* const commandline = this->commandline + reinterpret_cast(reg->commandLines); + + int argpos = 0; + while ( true ) { + const int available = KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE - argpos ; + assert( available >= 2 ); + + quint16 marker; + memcpy( &marker, commandline + argpos, MarkerSize ); + argpos += MarkerSize; + + if ( marker == PrematureEndOfOptions ) { + if ( prematureEnd ) *prematureEnd = true; + break; + } + if ( marker == RegularEndOfOptions ) { + if ( prematureEnd ) *prematureEnd = false; + break; + } + + const int requested = MarkerSize + marker + MarkerSize ; + if ( requested > available ) { + const long long int p = pid; + qWarning( "KDSingleApplicationGuard: inconsistency detected when parsing command-line argument for process %lld", p ); + if ( prematureEnd ) *prematureEnd = true; + break; + } + + result.push_back( QString::fromUtf8( commandline + argpos, marker ) ); + argpos += marker; + } + + return result; +} + +KDSingleApplicationGuard::Private::~Private() +{ + if( primaryInstance == q ) + primaryInstance = 0; +} + +bool KDSingleApplicationGuard::Private::checkOperational( const char * function, const char * act ) const +{ + assert( function ); + assert( act ); + if ( !operational ) + qWarning( "KDSingleApplicationGuard::%s: need to be operational to %s", function, act ); + return operational; +} + +bool KDSingleApplicationGuard::Private::checkOperationalPrimary( const char * function, const char * act ) const +{ + if ( !checkOperational( function, act ) ) + return false; + if ( id != 0 ) + qWarning( "KDSingleApplicationGuard::%s: need to be primary to %s", function, act ); + return id == 0; +} + +struct segmentheader +{ + size_t size : 16; +}; + +void KDSingleApplicationGuard::Private::sharedmem_free( char* pointer ) +{ + InstanceRegister* const reg = reinterpret_cast( KDSingleApplicationGuard::Private::primaryInstance->d->mem.data() ); + char* const heap = reg->commandLines; + char* const heap_ptr = heap + reinterpret_cast(pointer) - sizeof( segmentheader ); + const segmentheader* const header = reinterpret_cast< const segmentheader* >( heap_ptr ); + const size_t size = header->size; + + char* end = heap + KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE; + + std::copy( heap_ptr + size, end, heap_ptr ); + std::fill( end - size, end, 0 ); + + for( uint i = 0; i < reg->maxInstances; ++i ) + { + if( reg->info[ i ].commandline > pointer ) + reg->info[ i ].commandline -= size + sizeof( segmentheader ); + } +} + +char* KDSingleApplicationGuard::Private::sharedmem_malloc( size_t size ) +{ + InstanceRegister* const reg = reinterpret_cast( KDSingleApplicationGuard::Private::primaryInstance->d->mem.data() ); + char* heap = reg->commandLines; + + while( heap + sizeof( segmentheader ) + size < reg->commandLines + KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE ) + { + segmentheader* const header = reinterpret_cast< segmentheader* >( heap ); + if( header->size == 0 ) + { + header->size = size; + return heap + sizeof( segmentheader ) - reinterpret_cast(reg->commandLines); + } + heap += sizeof( header ) + header->size; + } + return 0; +} + +void KDSingleApplicationGuard::Private::shutdownInstance() +{ + KDLockedSharedMemoryPointer< InstanceRegister > instances( &q->d->mem ); + instances->info[ q->d->id ].command |= ExitedInstance; + + if( q->isPrimaryInstance() ) + { + // ohh... we need a new primary instance... + for ( int i = 1, end = instances->maxInstances ; i < end ; ++i ) + { + if( ( instances->info[ i ].command & ( FreeInstance | ExitedInstance | ShutDownCommand | KillCommand ) ) == 0 ) + { + instances->info[ i ].command |= BecomePrimaryCommand; + return; + } + } + // none found? then my species is dead :-( + } +} + +KDSingleApplicationGuard* KDSingleApplicationGuard::Private::primaryInstance = 0; + +/*! + Requests that the instance kills itself (by emitting exitRequested). + + If the instance has since exited, does nothing. + + \sa shutdown(), raise() +*/ +void KDSingleApplicationGuard::Instance::kill() +{ + KDLockedSharedMemoryPointer< InstanceRegister > instances( &KDSingleApplicationGuard::Private::primaryInstance->d->mem ); + for ( int i = 0, end = instances->maxInstances ; i < end ; ++i ) + { + if( instances->info[ i ].pid != d->pid ) + continue; + if( ( instances->info[ i ].command & ( FreeInstance | ExitedInstance ) ) == 0 ) + instances->info[ i ].command = KillCommand; + } +} + +/*! + Requests that the instance shuts itself down (by calling QCoreApplication::quit()). + + If the instance has since exited, does nothing. + + \sa kill(), raise() +*/ +void KDSingleApplicationGuard::Instance::shutdown() +{ + KDLockedSharedMemoryPointer< InstanceRegister > instances( &KDSingleApplicationGuard::Private::primaryInstance->d->mem ); + for ( int i = 0, end = instances->maxInstances ; i < end ; ++i ) + { + if( instances->info[ i ].pid != d->pid ) + continue; + if( ( instances->info[ i ].command & ( FreeInstance | ExitedInstance ) ) == 0 ) + instances->info[ i ].command = ShutDownCommand; + } +} + +/*! + + Requests that the instance raises its main window. + + The effects are implementation-defined: the KDSingleApplicationGuard + corresponding to the instance will emit its \link + KDSingleApplicationGuard::raiseRequested() raiseRequested()\endlink + signal. + + If the instance has since exited, does nothing. + + \sa kill(), shutdown() +*/ +void KDSingleApplicationGuard::Instance::raise() +{ + KDLockedSharedMemoryPointer< InstanceRegister > instances( &KDSingleApplicationGuard::Private::primaryInstance->d->mem ); + for ( int i = 0, end = instances->maxInstances ; i < end ; ++i ) + { + if( instances->info[ i ].pid != d->pid ) + continue; + if( ( instances->info[ i ].command & ( FreeInstance | ExitedInstance ) ) == 0 ) + instances->info[ i ].command = RaiseCommand; + } +} + + +#ifndef Q_WS_WIN +// static +void KDSingleApplicationGuard::SIGINT_handler( int sig ) +{ + if( sig == SIGINT && Private::primaryInstance != 0 ) + Private::primaryInstance->d->shutdownInstance(); + ::exit( 1 ); +} +#endif + +/*! + \enum KDSingleApplicationGuard::Policy + + Defines the policy that a KDSingleApplicationGuard can enforce: +*/ + +/*! + \var KDSingleApplicationGuard::NoPolicy + + instanceStarted() is emitted, and the new instance allowed to continue. +*/ + +/*! + \var KDSingleApplicationGuard::AutoKillOtherInstances + + instanceStarted() is emitted, and the new instance is killed (Instance::kill()). +*/ + +/*! + Creates a new KDSingleApplicationGuard with arguments + QCoreApplication::arguments() and policy AutoKillOtherInstances, + passing \a parent to the base class constructor, as usual. +*/ +KDSingleApplicationGuard::KDSingleApplicationGuard( QObject * parent ) + : QObject( parent ), d( new Private( AutoKillOtherInstances, this ) ) +{ + d->create( QCoreApplication::arguments() ); +} + +/*! + Creates a new KDSingleApplicationGuard with arguments + QCoreApplication::arguments() and policy \a policy, passing \a + parent to the base class constructor, as usual. +*/ +KDSingleApplicationGuard::KDSingleApplicationGuard( Policy policy, QObject * parent ) + : QObject( parent ), d( new Private( policy, this ) ) +{ + d->create( QCoreApplication::arguments() ); +} + +/*! + Creates a new KDSingleApplicationGuard with arguments \a arguments + and policy AutoKillOtherInstances, passing \a parent to the base + class constructor, as usual. +*/ +KDSingleApplicationGuard::KDSingleApplicationGuard( const QStringList & arguments, QObject * parent ) + : QObject( parent ), d( new Private( AutoKillOtherInstances, this ) ) +{ + d->create( arguments ); +} + +/*! + Creates a new KDSingleApplicationGuard with arguments \a arguments + and policy \a policy, passing \a parent to the base class + constructor, as usual. +*/ +KDSingleApplicationGuard::KDSingleApplicationGuard( const QStringList & arguments, Policy policy, QObject * parent ) + : QObject( parent ), d( new Private( policy, this ) ) +{ + d->create( arguments ); +} + +KDSingleApplicationGuard::Private::Private( Policy policy_, KDSingleApplicationGuard * qq ) + : q( qq ), + id( -1 ), + policy( policy_ ), + operational( false ), + exitRequested( false ) +{ +} + +void KDSingleApplicationGuard::Private::create( const QStringList & arguments ) +{ + if ( !QCoreApplication::instance() ) { + qWarning( "KDSingleApplicationGuard: you need to construct a Q(Core)Application before you can construct a KDSingleApplicationGuard" ); + return; + } + + const QString name = QCoreApplication::applicationName(); + if ( name.isEmpty() ) { + qWarning( "KDSingleApplicationGuard: QCoreApplication::applicationName must not be emty" ); + return; + } + + (void)registerInstanceType(); + if ( primaryInstance == 0 ) + primaryInstance = q; + + mem.setKey( name ); + + // if another instance crashed, the shared memory segment is still there on Unix + // the following lines trigger deletion in that case +#ifndef Q_WS_WIN + mem.attach(); + mem.detach(); +#endif + + const bool created = mem.create( sizeof( InstanceRegister ) ); + if( !created ) + { + QString errorMsg; + if( mem.error() != QSharedMemory::NoError && mem.error() != QSharedMemory::AlreadyExists ) + errorMsg += QString::fromLatin1( "QSharedMemomry::create() failed: %1" ).arg( mem.errorString() ); + + if( !mem.attach() ) + { + if( mem.error() != QSharedMemory::NoError ) + errorMsg += QString::fromLatin1( "QSharedMemomry::attach() failed: %1" ).arg( mem.errorString() ); + + qWarning( "KDSingleApplicationGuard: Could neither create nor attach to shared memory segment." ); + qWarning( "%s\n", errorMsg.toLocal8Bit().constData() ); + return; + } + + const int maxWaitMSecs = 1000 * 60; // stop waiting after 60 seconds + QTime waitTimer; + waitTimer.start(); + + // lets wait till the other instance initialized the register + bool initialized = false; + while( !initialized && waitTimer.elapsed() < maxWaitMSecs ) + { + const KDLockedSharedMemoryPointer< InstanceRegister > instances( &mem ); + initialized = instances->isValid(); +#ifdef Q_WS_WIN + ::Sleep(20); +#else + usleep(20000); +#endif + } + + const KDLockedSharedMemoryPointer< InstanceRegister > instances( &mem ); + if ( instances->version != 0 ) { + qWarning( "KDSingleApplicationGuard: Detected version mismatch. " + "Highest supported version: %ud, actual version: %ud", + KDSINGLEAPPLICATIONGUARD_SHM_VERSION, instances->version ); + return; + } + + } + + + KDLockedSharedMemoryPointer< InstanceRegister > instances( &mem ); + + if( !created ) + { + assert( instances->isValid() ); + + // we're _not_ the first instance + // but the + bool killOurSelf = false; + + // find a new slot... + id = std::find( instances->info, instances->info + instances->maxInstances, ProcessInfo() ) - instances->info; + ProcessInfo& info = instances->info[ id ]; + info = ProcessInfo( NewInstance, arguments, QCoreApplication::applicationPid() ); + killOurSelf = instances->policy == AutoKillOtherInstances; + policy = static_cast( instances->policy ); + + // but the signal that we tried to start was sent to the primary application + if( killOurSelf ) + exitRequested = true; + } + else + { + // ok.... we are the first instance + new ( instances.get() ) InstanceRegister( policy ); // create a new list (in shared memory) + id = 0; // our id = 0 + // and we've no command + instances->info[ 0 ] = ProcessInfo( NoCommand, arguments, QCoreApplication::applicationPid() ); + } + +#ifndef Q_WS_WIN + ::signal( SIGINT, SIGINT_handler ); +#endif + + // now listen for commands + timer.start( 750, q ); + + operational = true; +} + +/*! + Destroys this SingleApplicationGuard. + If this instance has been the primary instance and no other instance is existing anymore, + the application is shut down completely. Otherwise the destructor selects another instance to + be the primary instances. + */ +KDSingleApplicationGuard::~KDSingleApplicationGuard() +{ + if( d->id == -1 ) + return; + + d->shutdownInstance(); +} + +/*! + \property KDSingleApplicationGuard::operational + + Contains whether this KDSingleApplicationGuard is operational. + + A non-operational KDSingleApplicationGuard cannot be used in any meaningful way. + + Reasons for a KDSingleApplicationGuard being non-operational include: + \li it was constructed before QApplication (or at least QCoreApplication) was constructed + \li it failed to create or attach to the shared memory segment that is used for communication + + Get this property's value using %isOperational(). +*/ +bool KDSingleApplicationGuard::isOperational() const +{ + return d->operational; +} + +/*! + \property KDSingleApplicationGuard::exitRequested + + Contains wheter this istance has been requested to exit. This will happen when this instance + was just started, but the policy is AutoKillOtherInstances or by explicitely calling kill on + this instance(). + + Get this property's value using %isExitRequested(). +*/ +bool KDSingleApplicationGuard::isExitRequested() const +{ + return d->exitRequested; +}; + +/*! + \property KDSingleApplicationGuard::primaryInstance + + Contains whether this instance is the primary instance. + + The primary instance is the first instance which was started or else the instance which + got selected by KDSingleApplicationGuard's destructor, when the primary instance was + shut down. + + Get this property's value using %isPrimaryInstance(), and monitor changes to it + using becamePrimaryInstance(). + */ +bool KDSingleApplicationGuard::isPrimaryInstance() const +{ + return d->id == 0; +} + +/*! + \property KDSingleApplicationGuard::policy + Specifies the policy KDSingleApplicationGuard is using when new instances are started. + This can only be set in the primary instance. + + Get this property's value using %policy(), set it using %setPolicy(), and monitor changes + to it using policyChanged(). + */ +KDSingleApplicationGuard::Policy KDSingleApplicationGuard::policy() const +{ + return d->policy; +} + +void KDSingleApplicationGuard::setPolicy( Policy policy ) +{ + if ( !d->checkOperationalPrimary( "setPolicy", "change the policy" ) ) + return; + + if( d->policy == policy ) + return; + + d->policy = policy; + emit policyChanged( policy ); + KDLockedSharedMemoryPointer< InstanceRegister > instances( &d->mem ); + instances->policy = policy; +} + +/*! + Returns a list of all currently running instances. + */ +QVector +KDSingleApplicationGuard::instances() const +{ + if ( !d->checkOperational( "instances", "report on other instances" ) ) + return QVector(); + + if ( Private::primaryInstance == 0 ) { + Private::primaryInstance = const_cast( this ); + } + + QVector result; + const KDLockedSharedMemoryPointer< InstanceRegister > instances( const_cast< QSharedMemory* >( &d->mem ) ); + for ( int i = 0, end = instances->maxInstances ; i < end ; ++i ) + { + const ProcessInfo& info = instances->info[ i ]; + if( ( info.command & ( FreeInstance | ExitedInstance ) ) == 0 ) + { + bool truncated; + const QStringList arguments = info.arguments( &truncated ); + result.push_back( Instance( arguments, truncated, info.pid ) ); + } + } + return result; +} + +/*! + Shuts down all other instances. This can only be called from the + the primary instance. + Shut down is done gracefully via QCoreApplication::quit(). + */ +void KDSingleApplicationGuard::shutdownOtherInstances() +{ + if ( !d->checkOperationalPrimary( "shutdownOtherInstances", "shut other instances down" ) ) + return; + + KDLockedSharedMemoryPointer< InstanceRegister > instances( &d->mem ); + for ( int i = 1, end = instances->maxInstances ; i < end ; ++i ) + { + if( ( instances->info[ i ].command & ( FreeInstance | ExitedInstance ) ) == 0 ) + instances->info[ i ].command = ShutDownCommand; + } +} + +/*! + Kills all other instances. This can only be called from the + the primary instance. + Killing is done via emitting exitRequested. It's up to the receiving + instance to react properly. + */ +void KDSingleApplicationGuard::killOtherInstances() +{ + if ( !d->checkOperationalPrimary( "killOtherInstances", "kill other instances" ) ) + return; + + KDLockedSharedMemoryPointer< InstanceRegister > instances( &d->mem ); + for ( int i = 1, end = instances->maxInstances ; i < end ; ++i ) + { + if( ( instances->info[ i ].command & ( FreeInstance | ExitedInstance ) ) == 0 ) + instances->info[ i ].command = KillCommand; + } +} + +bool KDSingleApplicationGuard::event( QEvent * event ) +{ + if ( event->type() == QEvent::Timer ) { + const QTimerEvent * const te = static_cast( event ); + if ( te->timerId() == d->timer.timerId() ) { + d->poll(); + return true; + } + } + return QObject::event( event ); +} + +void KDSingleApplicationGuard::Private::poll() { + + const quint32 now = QDateTime::currentDateTime().toTime_t(); + + if ( primaryInstance == 0 ) { + primaryInstance = q; + } + + if ( q->isPrimaryInstance() ) + { + // only the primary instance will get notified about new instances + QVector< Instance > exitedInstances; + QVector< Instance > startedInstances; + + { + KDLockedSharedMemoryPointer< InstanceRegister > instances( &mem ); + + if( instances->info[ id ].pid != QCoreApplication::applicationPid() ) + { + for ( int i = 1, end = instances->maxInstances ; i < end && id == 0 ; ++i ) + { + if( instances->info[ i ].pid == QCoreApplication::applicationPid() ) + id = i; + } + emit q->becameSecondaryInstance(); + return; + } + + instances->info[ id ].timestamp = now; + + for ( int i = 1, end = instances->maxInstances ; i < end ; ++i ) + { + ProcessInfo& info = instances->info[ i ]; + if( info.command & NewInstance ) + { + bool truncated; + const QStringList arguments = info.arguments( &truncated ); + startedInstances.push_back( Instance( arguments, truncated, info.pid ) ); + info.command &= ~NewInstance; // clear NewInstance flag + } + if( info.command & ExitedInstance ) + { + bool truncated; + const QStringList arguments = info.arguments( &truncated ); + exitedInstances.push_back( Instance( arguments, truncated, info.pid ) ); + info.command = FreeInstance; // set FreeInstance flag + } + } + } + + // one signal for every new instance - _after_ the memory segment was unlocked again + for( QVector< Instance >::const_iterator it = startedInstances.constBegin(); it != startedInstances.constEnd(); ++it ) + emit q->instanceStarted( *it ); + for( QVector< Instance >::const_iterator it = exitedInstances.constBegin(); it != exitedInstances.constEnd(); ++it ) + emit q->instanceExited( *it ); + } + else + { + // do we have a command? + bool killOurSelf = false; + bool shutDownOurSelf = false; + bool policyDidChange = false; + + { + KDLockedSharedMemoryPointer< InstanceRegister > instances( &mem ); + + const Policy oldPolicy = policy; + policy = static_cast( instances->policy ); + policyDidChange = policy != oldPolicy; + + // check for the primary instance health status + if( now - instances->info[ 0 ].timestamp > KDSINGLEAPPLICATIONGUARD_TIMEOUT_SECONDS ) + { + std::swap( instances->info[ 0 ], instances->info[ id ] ); + id = 0; + instances->info[ id ].timestamp = now; + emit q->becamePrimaryInstance(); + instances->info[ id ].command &= ~BecomePrimaryCommand; // afterwards, reset the flag + } + + if( instances->info[ id ].command & BecomePrimaryCommand ) + { + // we became primary! + instances->info[ 0 ] = instances->info[ id ]; + instances->info[ id ] = ProcessInfo(); // change our id to 0 and declare the old slot as free + id = 0; + instances->info[ id ].timestamp = now; + emit q->becamePrimaryInstance(); + } + + if( instances->info[ id ].command & RaiseCommand ) + { + // raise ourself! + emit q->raiseRequested(); + instances->info[ id ].command &= ~RaiseCommand; // afterwards, reset the flag + } + + + killOurSelf = instances->info[ id ].command & KillCommand; // check for kill command + shutDownOurSelf = instances->info[ id ].command & ShutDownCommand; // check for shut down command + instances->info[ id ].command &= ~( KillCommand | ShutDownCommand | BecomePrimaryCommand ); // reset both flags + if( killOurSelf ) + { + instances->info[ id ].command |= ExitedInstance; // upon kill, we have to set the ExitedInstance flag + id = -1; // becauso our d'tor won't be called anymore + } + } + + if( killOurSelf ) // kill our self takes precedence + { + exitRequested = true; + emit q->exitRequested(); + } + else if( shutDownOurSelf ) + qApp->quit(); + else if( policyDidChange ) + emit q->policyChanged( policy ); + } +} + +#include "moc_kdsingleapplicationguard.cpp" + +#ifdef KDTOOLSCORE_UNITTESTS + +#include + +#include "kdautopointer.h" + +#include + +#include +#include +#include + +static void wait( int msec, QSignalSpy * spy=0, int expectedCount=INT_MAX ) +{ + QTime t; + t.start(); + while ( ( !spy || spy->count() < expectedCount ) && t.elapsed() < msec ) + { + qApp->processEvents( QEventLoop::WaitForMoreEvents, qMax( 10, msec - t.elapsed() ) ); + } +} + +static std::ostream& operator<<( std::ostream& stream, const QStringList& list ) +{ + stream << "QStringList("; + for( QStringList::const_iterator it = list.begin(); it != list.end(); ++it ) + { + stream << " " << it->toLocal8Bit().data(); + if( it + 1 != list.end() ) + stream << ","; + } + stream << " )"; + return stream; +} + +namespace { + class ApplicationNameSaver { + Q_DISABLE_COPY( ApplicationNameSaver ) + const QString oldname; + public: + explicit ApplicationNameSaver( const QString & name ) + : oldname( QCoreApplication::applicationName() ) + { + QCoreApplication::setApplicationName( name ); + } + ~ApplicationNameSaver() { + QCoreApplication::setApplicationName( oldname ); + } + }; +} + +KDAB_UNITTEST_SIMPLE( KDSingleApplicationGuard, "kdcoretools" ) { + + // set it to an unique name + const ApplicationNameSaver saver( QUuid::createUuid().toString() ); + + KDAutoPointer guard3; + KDAutoPointer spy3; + KDAutoPointer spy4; + + { + KDSingleApplicationGuard guard1; + assertEqual( guard1.policy(), KDSingleApplicationGuard::AutoKillOtherInstances ); + assertEqual( guard1.instances().count(), 1 ); + assertTrue( guard1.isPrimaryInstance() ); + + guard1.setPolicy( KDSingleApplicationGuard::NoPolicy ); + assertEqual( guard1.policy(), KDSingleApplicationGuard::NoPolicy ); + + QSignalSpy spy1( &guard1, SIGNAL(instanceStarted(KDSingleApplicationGuard::Instance)) ); + + KDSingleApplicationGuard guard2; + assertEqual( guard1.instances().count(), 2 ); + assertEqual( guard2.instances().count(), 2 ); + assertEqual( guard2.policy(), KDSingleApplicationGuard::NoPolicy ); + assertFalse( guard2.isPrimaryInstance() ); + + wait( 1000, &spy1, 1 ); + + assertEqual( spy1.count(), 1 ); + guard3.reset( new KDSingleApplicationGuard ); + spy3.reset( new QSignalSpy( guard3.get(), SIGNAL(becamePrimaryInstance()) ) ); + spy4.reset( new QSignalSpy( guard3.get(), SIGNAL(instanceExited(KDSingleApplicationGuard::Instance) ) ) ); + assertFalse( guard3->isPrimaryInstance() ); + } + + wait( 1000, spy3.get(), 1 ); + wait( 1000, spy4.get(), 1 ); + assertEqual( spy3->count(), 1 ); + assertEqual( guard3->instances().count(), 1 ); + assertTrue( guard3->isPrimaryInstance() ); + guard3.reset( new KDSingleApplicationGuard ); + + assertEqual( guard3->instances().first().arguments(), qApp->arguments() ); + + QSignalSpy spyStarted( guard3.get(), SIGNAL(instanceStarted(KDSingleApplicationGuard::Instance)) ); + QSignalSpy spyExited( guard3.get(), SIGNAL(instanceExited(KDSingleApplicationGuard::Instance)) ); + + { + KDSingleApplicationGuard guard1; + KDSingleApplicationGuard guard2; + + wait( 1000, &spyStarted, 2 ); + + assertEqual( spyStarted.count(), 2 ); + } + + wait( 1000, &spyExited, 2 ); + assertEqual( spyExited.count(), 2 ); + + spyStarted.clear(); + spyExited.clear(); + + { + // check arguments-too-long handling: + QStringList args; + for ( unsigned int i = 0, end = KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE/16 ; i != end ; ++i ) + args.push_back( QLatin1String( "0123456789ABCDEF" ) ); + KDSingleApplicationGuard guard3( args ); + + wait( 1000, &spyStarted, 1 ); + + const QVector instances = guard3.instances(); + assertEqual( instances.size(), 2 ); + + assertTrue( instances[1].areArgumentsTruncated() ); + } +} + +#endif // KDTOOLSCORE_UNITTESTS + +#endif // QT_NO_SHAREDMEMORY +#endif // QT_VERSION >= 0x040400 || defined(DOXYGEN_RUN) diff --git a/src/libcalamares/kdsingleapplicationguard/kdsingleapplicationguard.h b/src/libcalamares/kdsingleapplicationguard/kdsingleapplicationguard.h new file mode 100644 index 000000000..7b744e110 --- /dev/null +++ b/src/libcalamares/kdsingleapplicationguard/kdsingleapplicationguard.h @@ -0,0 +1,141 @@ +#ifndef __KDTOOLSCORE_KDSINGLEAPPLICATIONGUARD_H__ +#define __KDTOOLSCORE_KDSINGLEAPPLICATIONGUARD_H__ + +#include + +#ifndef QT_NO_SHAREDMEMORY + +#include +#include + +#include "pimpl_ptr.h" +#include "DllMacro.h" + +#include + +template class QVector; +class QCoreApplication; + +class DLLEXPORT KDSingleApplicationGuard : public QObject +{ + Q_OBJECT + Q_ENUMS( Policy ) + Q_PROPERTY( bool operational READ isOperational ) + Q_PROPERTY( bool exitRequested READ isExitRequested ) + Q_PROPERTY( bool primaryInstance READ isPrimaryInstance NOTIFY becamePrimaryInstance ) + Q_PROPERTY( Policy policy READ policy WRITE setPolicy NOTIFY policyChanged ) +public: + enum Policy + { + NoPolicy = 0, + AutoKillOtherInstances = 1 + }; + + explicit KDSingleApplicationGuard( QObject * parent=0 ); + explicit KDSingleApplicationGuard( Policy policy, QObject * parent=0 ); + explicit KDSingleApplicationGuard( const QStringList & arguments, QObject * parent=0 ); + explicit KDSingleApplicationGuard( const QStringList & arguments, Policy policy, QObject * parent=0 ); + ~KDSingleApplicationGuard(); + + bool isOperational() const; + + bool isExitRequested() const; + + bool isPrimaryInstance() const; + + Policy policy() const; + void setPolicy( Policy policy ); + + class Instance; + + QVector instances() const; + +Q_SIGNALS: + void instanceStarted( const KDSingleApplicationGuard::Instance & instance ); + void instanceExited( const KDSingleApplicationGuard::Instance & instance ); + void exitRequested(); + void raiseRequested(); + void becamePrimaryInstance(); + void becameSecondaryInstance(); + void policyChanged( KDSingleApplicationGuard::Policy policy ); + +public Q_SLOTS: + void shutdownOtherInstances(); + void killOtherInstances(); + +protected: + /*! \reimp */ bool event( QEvent * event ); + +private: +#ifndef Q_WS_WIN + static void SIGINT_handler( int ); +#endif + +private: + friend struct ProcessInfo; + + class Private; + kdtools::pimpl_ptr< Private > d; +}; + +class DLLEXPORT KDSingleApplicationGuard::Instance { + friend class ::KDSingleApplicationGuard; + friend class ::KDSingleApplicationGuard::Private; + Instance( const QStringList &, bool, qint64 ); +public: + Instance(); + Instance( const Instance & other ); + ~Instance(); + + void swap( Instance & other ) { + std::swap( d, other.d ); + } + + Instance & operator=( Instance other ) { + swap( other ); + return *this; + } + + bool isNull() const { return !d; } + bool isValid() const; + + bool areArgumentsTruncated() const; + + const QStringList & arguments() const; + qint64 pid() const; + + void shutdown(); + void kill(); + void raise(); + +private: + class Private; + Private * d; +}; + +namespace std { + template <> + inline void swap( KDSingleApplicationGuard::Instance & lhs, + KDSingleApplicationGuard::Instance & rhs ) + { + lhs.swap( rhs ); + } +} // namespace std + +QT_BEGIN_NAMESPACE + +template <> +inline void qSwap( KDSingleApplicationGuard::Instance & lhs, + KDSingleApplicationGuard::Instance & rhs ) +{ + lhs.swap( rhs ); +} +Q_DECLARE_METATYPE( KDSingleApplicationGuard::Instance ) +Q_DECLARE_TYPEINFO( KDSingleApplicationGuard::Instance, Q_MOVABLE_TYPE ); + +QT_END_NAMESPACE + + +#endif // QT_NO_SHAREDMEMORY + +#endif /* __KDTOOLSCORE_KDSINGLEAPPLICATIONGUARD_H__ */ diff --git a/src/libcalamares/kdsingleapplicationguard/kdtoolsglobal.cpp b/src/libcalamares/kdsingleapplicationguard/kdtoolsglobal.cpp new file mode 100644 index 000000000..b057614d7 --- /dev/null +++ b/src/libcalamares/kdsingleapplicationguard/kdtoolsglobal.cpp @@ -0,0 +1,32 @@ +#include "kdtoolsglobal.h" + +#include + +#include + +namespace { + struct Version { + unsigned char v[3]; + }; + + static inline bool operator<( const Version & lhs, const Version & rhs ) { + return std::lexicographical_compare( lhs.v, lhs.v + 3, rhs.v, rhs.v + 3 ); + } + static inline bool operator==( const Version & lhs, const Version & rhs ) { + return std::equal( lhs.v, lhs.v + 3, rhs.v ); + } + KDTOOLS_MAKE_RELATION_OPERATORS( Version, static inline ) +} + +static Version kdParseQtVersion( const char * const version ) { + if ( !version || qstrlen( version ) < 5 || version[1] != '.' || version[3] != '.' || ( version[5] != 0 && version[5] != '.' && version[5] != '-' ) ) + return Version(); // parse error + const Version result = { { version[0] - '0', version[2] - '0', version[4] - '0' } }; + return result; +} + +bool _kdCheckQtVersion_impl( int major, int minor, int patchlevel ) { + static const Version actual = kdParseQtVersion( qVersion() ); // do this only once each run... + const Version requested = { { major, minor, patchlevel } }; + return actual >= requested; +} diff --git a/src/libcalamares/kdsingleapplicationguard/kdtoolsglobal.h b/src/libcalamares/kdsingleapplicationguard/kdtoolsglobal.h new file mode 100644 index 000000000..a0f004589 --- /dev/null +++ b/src/libcalamares/kdsingleapplicationguard/kdtoolsglobal.h @@ -0,0 +1,114 @@ +#ifndef __KDTOOLS_KDTOOLSGLOBAL_H__ +#define __KDTOOLS_KDTOOLSGLOBAL_H__ + +#include + +#define KDAB_DISABLE_COPY( x ) private: x( const x & ); x & operator=( const x & ) + +#ifdef KDTOOLS_SHARED +# ifdef BUILD_SHARED_KDTOOLSCORE +# define KDTOOLSCORE_EXPORT Q_DECL_EXPORT +# else +# define KDTOOLSCORE_EXPORT Q_DECL_IMPORT +# endif +# ifdef BUILD_SHARED_KDTOOLSGUI +# define KDTOOLSGUI_EXPORT Q_DECL_EXPORT +# else +# define KDTOOLSGUI_EXPORT Q_DECL_IMPORT +# endif +# ifdef BUILD_SHARED_KDTOOLSXML +# define KDTOOLSXML_EXPORT Q_DECL_EXPORT +# else +# define KDTOOLSXML_EXPORT Q_DECL_IMPORT +# endif +# ifdef BUILD_SHARED_KDUPDATER +# define KDTOOLS_UPDATER_EXPORT Q_DECL_EXPORT +# else +# define KDTOOLS_UPDATER_EXPORT Q_DECL_IMPORT +# endif +#else // KDTOOLS_SHARED +# define KDTOOLSCORE_EXPORT +# define KDTOOLSGUI_EXPORT +# define KDTOOLSXML_EXPORT +# define KDTOOLS_UPDATER_EXPORT +#endif // KDTOOLS_SHARED + +#define MAKEINCLUDES_EXPORT + +#define DOXYGEN_PROPERTY( x ) +#ifdef DOXYGEN_RUN +# define KDAB_IMPLEMENT_SAFE_BOOL_OPERATOR( func ) operator unspecified_bool_type() const { return func; } +# define KDAB_USING_SAFE_BOOL_OPERATOR( Class ) operator unspecified_bool_type() const; +#else +# define KDAB_IMPLEMENT_SAFE_BOOL_OPERATOR( func ) \ + private: \ + struct __safe_bool_dummy__ { void nonnull() {} }; \ + public: \ + typedef void ( __safe_bool_dummy__::*unspecified_bool_type )(); \ + operator unspecified_bool_type() const { \ + return ( func ) ? &__safe_bool_dummy__::nonnull : 0 ; \ + } +#define KDAB_USING_SAFE_BOOL_OPERATOR( Class ) \ + using Class::operator Class::unspecified_bool_type; +#endif + +#define KDTOOLS_MAKE_RELATION_OPERATORS( Class, linkage ) \ + linkage bool operator>( const Class & lhs, const Class & rhs ) { \ + return operator<( rhs, lhs ); \ + } \ + linkage bool operator!=( const Class & lhs, const Class & rhs ) { \ + return !operator==( lhs, rhs ); \ + } \ + linkage bool operator<=( const Class & lhs, const Class & rhs ) { \ + return !operator>( lhs, rhs ); \ + } \ + linkage bool operator>=( const Class & lhs, const Class & rhs ) { \ + return !operator<( lhs, rhs ); \ + } + +template +inline T & __kdtools__dereference_for_methodcall( T & o ) { + return o; +} + +template +inline T & __kdtools__dereference_for_methodcall( T * o ) { + return *o; +} + +#define KDAB_SET_OBJECT_NAME( x ) __kdtools__dereference_for_methodcall( x ).setObjectName( QLatin1String( #x ) ) + +KDTOOLSCORE_EXPORT bool _kdCheckQtVersion_impl( int major, int minor=0, int patchlevel=0 ); +static inline bool kdCheckQtVersion( unsigned int major, unsigned int minor=0, unsigned int patchlevel=0 ) { + return (major<<16|minor<<8|patchlevel) <= static_cast(QT_VERSION) + || _kdCheckQtVersion_impl( major, minor, patchlevel ); +} + +#define KDTOOLS_DECLARE_PRIVATE_BASE( Class ) \ +protected: \ + class Private; \ + Private * d_func() { return _d; } \ + const Private * d_func() const { return _d; } \ + Class( Private * _d_, bool b ) : _d( _d_ ) { init(b); } \ +private: \ + void init(bool); \ +private: \ + Private * _d + +#define KDTOOLS_DECLARE_PRIVATE_DERIVED( Class, Base ) \ +protected: \ + class Private; \ + Private * d_func() { \ + return reinterpret_cast( Base::d_func() ); \ + } \ + const Private * d_func() const { \ + return reinterpret_cast( Base::d_func() ); \ + } \ + Class( Private * _d_, bool b ) \ + : Base( reinterpret_cast(_d_), b ) { init(b); } \ +private: \ + void init(bool) + + +#endif /* __KDTOOLS_KDTOOLSGLOBAL_H__ */ + diff --git a/src/libcalamares/kdsingleapplicationguard/pimpl_ptr.cpp b/src/libcalamares/kdsingleapplicationguard/pimpl_ptr.cpp new file mode 100644 index 000000000..3045ebc2a --- /dev/null +++ b/src/libcalamares/kdsingleapplicationguard/pimpl_ptr.cpp @@ -0,0 +1,203 @@ +#include "pimpl_ptr.h" + +/*! + \class pimpl_ptr: + \ingroup core smartptr + \brief Owning pointer for private implementations + \since_c 2.1 + + (The exception safety of this class has not been evaluated yet.) + + pimpl_ptr is a smart immutable pointer, which owns the contained object. Unlike other smart pointers, + it creates a standard constructed object when instanciated via the + \link pimpl_ptr() standard constructor\endlink. + Additionally, pimpl_ptr respects constness of the pointer object and returns \c const \c T* for + a const pimpl_ptr object. + + The content of a pimpl_ptr cannot be changed during it's lifetime. + + \section general-use General Use + + The general use case of pimpl_ptr is the "Pimpl Idiom", i.e. hiding the private implementation of a class + from the user's compiler which see \c MyClass as + + \code + class MyClass + { + public: + MyClass(); + ~MyClass(); + + // public class API + int value() const; + + private: + class Private; // defined later + kdtools::pimpl_ptr< Private > d; + }; + \endcode + + but not the private parts of it. These can only be seen (and accessed) by the code knowing \c MyClass::Private: + + \code + class MyClass::Private + { + public: + int value; + }; + + MyClass::MyClass() + { + // d was automatically filled with new Private + d->value = 42; + } + + MyClass::~MyClass() + { + // the content of d gets deleted automatically + } + + int MyClass::value() const + { + // access the private part: + // since MyClass::value() is const, the returned pointee is const, too + return d->value; + } + \endcode + +*/ + +/*! + \fn pimpl_ptr::pimpl_ptr() + + Default constructor. Constructs a pimpl_tr that contains (owns) a standard constructed + instance of \c T. + + \post \c *this owns a new object. +*/ + +/*! + \fn pimpl_ptr::pimpl_ptr( T * t ) + + Constructor. Constructs a pimpl_ptr that contains (owns) \a t. + + \post get() == obj +*/ + +/*! + \fn pimpl_ptr::~pimpl_ptr() + + Destructor. + + \post The object previously owned by \c *this has been deleted. +*/ + +/*! + \fn const T * pimpl_ptr::get() const + + \returns a const pointer to the contained (owned) object. + \overload +*/ + +/*! + \fn T * pimpl_ptr::get() + + \returns a pointer to the contained (owned) object. +*/ + +/*! + \fn const T & pimpl_ptr::operator*() const + + Dereference operator. Returns \link get() *get()\endlink. + \overload +*/ + +/*! + \fn T & pimpl_ptr::operator*() + + Dereference operator. Returns \link get() *get()\endlink. +*/ + +/*! + \fn const T * pimpl_ptr::operator->() const + + Member-by-pointer operator. Returns get(). + \overload +*/ + +/*! + \fn T * pimpl_ptr::operator->() + + Member-by-pointer operator. Returns get(). +*/ + +#ifdef KDTOOLSCORE_UNITTESTS + +#include + +#include +#include + +namespace +{ + struct ConstTester + { + bool isConst() + { + return false; + } + + bool isConst() const + { + return true; + } + }; +} + +KDAB_UNITTEST_SIMPLE( pimpl_ptr, "kdcoretools" ) { + + { + kdtools::pimpl_ptr< QObject > p; + assertNotNull( p.get() ); + assertNull( p->parent() ); + } + + + { + QPointer< QObject > o; + { + kdtools::pimpl_ptr< QObject > qobject( new QObject ); + o = qobject.get(); + assertEqual( o, qobject.operator->() ); + assertEqual( o, &(qobject.operator*()) ); + } + assertNull( o ); + } + + { + const kdtools::pimpl_ptr< QObject > qobject( new QObject ); + const QObject* o = qobject.get(); + assertEqual( o, qobject.operator->() ); + assertEqual( o, &(qobject.operator*()) ); + } + + { + kdtools::pimpl_ptr< QObject > o1; + assertTrue( o1 ); + kdtools::pimpl_ptr< QObject > o2( 0 ); + assertFalse( o2 ); + } + + { + const kdtools::pimpl_ptr< ConstTester > o1; + kdtools::pimpl_ptr< ConstTester > o2; + assertTrue( o1->isConst() ); + assertFalse( o2->isConst() ); + assertTrue( (*o1).isConst() ); + assertFalse( (*o2).isConst() ); + assertTrue( o1.get()->isConst() ); + assertFalse( o2.get()->isConst() ); + } +} + +#endif // KDTOOLSCORE_UNITTESTS diff --git a/src/libcalamares/kdsingleapplicationguard/pimpl_ptr.h b/src/libcalamares/kdsingleapplicationguard/pimpl_ptr.h new file mode 100644 index 000000000..7b7f36839 --- /dev/null +++ b/src/libcalamares/kdsingleapplicationguard/pimpl_ptr.h @@ -0,0 +1,44 @@ +#ifndef __KDTOOLSCORE__PIMPL_PTR_H__ +#define __KDTOOLSCORE__PIMPL_PTR_H__ + +#include "kdtoolsglobal.h" + +#ifndef DOXYGEN_RUN +namespace kdtools { +#endif + + template + class pimpl_ptr { + KDAB_DISABLE_COPY( pimpl_ptr ); + T * d; + public: + pimpl_ptr() : d( new T ) {} + explicit pimpl_ptr( T * t ) : d( t ) {} + ~pimpl_ptr() { delete d; d = 0; } + + T * get() { return d; } + const T * get() const { return d; } + + T * operator->() { return get(); } + const T * operator->() const { return get(); } + + T & operator*() { return *get(); } + const T & operator*() const { return *get(); } + + KDAB_IMPLEMENT_SAFE_BOOL_OPERATOR( get() ) + }; + + // these are not implemented, so's we can catch their use at + // link-time. Leaving them undeclared would open up a comparison + // via operator unspecified-bool-type(). + template + void operator==( const pimpl_ptr &, const pimpl_ptr & ); + template + void operator!=( const pimpl_ptr &, const pimpl_ptr & ); + +#ifndef DOXYGEN_RUN +} // namespace kdtools +#endif + +#endif /* __KDTOOLSCORE__PIMPL_PTR_H__ */ +