1/*
2 This file is part of libkabc.
3 Copyright (c) 2001,2003 Cornelius Schumacher <schumacher@kde.org>
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public License
16 along with this library; see the file COPYING.LIB. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
19*/
20
21#include "lock.h"
22
23#include <krandom.h>
24#include <kcomponentdata.h>
25#include <kdebug.h>
26#include <kglobal.h>
27#include <klocalizedstring.h>
28#include <kstandarddirs.h>
29
30#include <QtCore/QFile>
31#include <QtCore/QTextStream>
32
33#include <errno.h>
34#include <signal.h>
35#include <sys/types.h>
36#include <sys/stat.h>
37#include <unistd.h>
38
39using namespace KABC;
40
41class Lock::Private
42{
43 public:
44 Private( const QString &identifier )
45 : mIdentifier( identifier ),
46 mOrigIdentifier( identifier )
47 {
48 mIdentifier.replace( QLatin1Char( '/' ), QLatin1Char( '_' ) );
49#ifdef Q_WS_WIN
50 mIdentifier.replace( QLatin1Char( ':' ), QLatin1Char( '_' ) );
51#endif
52 }
53
54 QString mIdentifier;
55 QString mOrigIdentifier;
56 QString mLockUniqueName;
57 QString mError;
58};
59
60Lock::Lock( const QString &identifier )
61 : d( new Private( identifier ) )
62{
63}
64
65Lock::~Lock()
66{
67 unlock();
68
69 delete d;
70}
71
72QString Lock::locksDir()
73{
74 return KStandardDirs::locateLocal( "data", QLatin1String( "kabc/lock/" ) );
75}
76
77bool Lock::readLockFile( const QString &filename, int &pid, QString &app )
78{
79 QFile file( filename );
80 if ( !file.open( QIODevice::ReadOnly ) ) {
81 return false;
82 }
83
84 QTextStream t( &file );
85 t >> pid >> ws >> app;
86
87 return true;
88}
89
90bool Lock::writeLockFile( const QString &filename )
91{
92 QFile file( filename );
93 if ( !file.open( QIODevice::WriteOnly ) ) {
94 return false;
95 }
96
97 QTextStream t( &file );
98 t << ::getpid() << endl << QString( KGlobal::mainComponent().componentName() );
99
100 return true;
101}
102
103QString Lock::lockFileName() const
104{
105 return locksDir() + d->mIdentifier + QLatin1String( ".lock" );
106}
107
108bool Lock::lock()
109{
110 QString lockName = lockFileName();
111 kDebug() << "-- lock name:" << lockName;
112
113 if ( QFile::exists( lockName ) ) { // check if it is a stale lock file
114 int pid;
115 QString app;
116
117 if ( !readLockFile( lockFileName(), pid, app ) ) {
118 d->mError = i18n( "Unable to open lock file." );
119 return false;
120 }
121
122 int retval = ::kill( pid, 0 );
123 if ( retval == -1 && errno == ESRCH ) { // process doesn't exists anymore
124 QFile::remove( lockName );
125 kWarning() << "Removed stale lock file from process '" << app << "'";
126 } else {
127 d->mError = i18n( "The resource '%1' is locked by application '%2'.",
128 d->mOrigIdentifier, app );
129 return false;
130 }
131 }
132
133 QString lockUniqueName;
134 lockUniqueName = d->mIdentifier + KRandom::randomString( 8 );
135 d->mLockUniqueName = KStandardDirs::locateLocal(
136 "data", QLatin1String( "kabc/lock/" ) + lockUniqueName );
137 kDebug() << "-- lock unique name:" << d->mLockUniqueName;
138
139 // Create unique file
140 writeLockFile( d->mLockUniqueName );
141
142 // Create lock file
143 int result = ::link( QFile::encodeName( d->mLockUniqueName ),
144 QFile::encodeName( lockName ) );
145
146 if ( result == 0 ) {
147 d->mError.clear();
148 emit locked();
149 return true;
150 }
151
152 // TODO: check stat
153
154 d->mError = i18n( "Error" );
155 return false;
156}
157
158bool Lock::unlock()
159{
160 int pid;
161 QString app;
162 if ( readLockFile( lockFileName(), pid, app ) ) {
163 if ( pid == getpid() ) {
164 QFile::remove( lockFileName() );
165 QFile::remove( d->mLockUniqueName );
166 emit unlocked();
167 } else {
168 d->mError = i18n( "Unlock failed. Lock file is owned by other process: %1 (%2)", app, pid );
169 kDebug() << d->mError;
170 return false;
171 }
172 }
173
174 d->mError.clear();
175
176 return true;
177}
178
179QString Lock::error() const
180{
181 return d->mError;
182}
183
184