1 /*
2 lock.c
3
4 $Id: lock.c,v 1.9 2002/11/10 11:32:17 bears Exp $
5 */
6
7 #if HAVE_CONFIG_H
8 #include <config.h>
9 #endif
10
11 #include <stdio.h>
12 #include <errno.h>
13 #include <ctype.h>
14 #include <sys/types.h>
15 #include <sys/stat.h>
16 #include <fcntl.h>
17 #include <signal.h>
18
19 #if TIME_WITH_SYS_TIME
20 #include <sys/time.h>
21 #include <time.h>
22 #else
23 #if HAVE_SYS_TIME_H
24 #include <sys/time.h>
25 #else
26 #include <time.h>
27 #endif
28 #endif
29
30 #include <unistd.h>
31 #include "configfile.h"
32 #include "content.h"
33 #include "lock.h"
34 #include "log.h"
35 #include "database.h"
36 #include "group.h"
37 #include "request.h"
38 #include "portable.h"
39 #include "server.h"
40 #include "util.h"
41
42 struct Lock
43 {
44 const char *name;
45 int lockFd;
46 Str lockFile;
47 Bool doLazyLocking;
48 volatile Bool lazyClose;
49 volatile Bool lazyLockBusy;
50 };
51
52 static struct Lock globalLock = { "global", -1, "", TRUE, FALSE, FALSE };
53 static struct Lock fetchLock = { "fetch", -1, "", FALSE, FALSE, FALSE };
54
55 static SignalHandler oldHandler = NULL;
56
57 /* Block/unblock SIGUSR1. */
58 static Bool
blockSignal(Bool block)59 blockSignal( Bool block )
60 {
61 sigset_t sigs;
62
63 sigemptyset(&sigs);
64 sigaddset(&sigs, SIGUSR1);
65
66 for(;;)
67 {
68 if ( sigprocmask( block ? SIG_BLOCK : SIG_UNBLOCK, &sigs, NULL ) != 0 )
69 {
70 if ( errno != EINTR )
71 {
72 Log_err( "Can't block/unblock signal" );
73 return FALSE;
74 }
75 }
76 else
77 return TRUE;
78 }
79 /* NOTREACHED */
80 }
81
82 /* Check the global lock held. */
83 static Bool
gotLock(struct Lock * lock)84 gotLock( struct Lock *lock )
85 {
86 return ( lock->lockFd != -1 );
87 }
88
89 static void
lockWaitAlarm(int sig)90 lockWaitAlarm( int sig )
91 {
92 UNUSED( sig );
93
94 return;
95 }
96
97 static Bool
waitLock(struct Lock * lock,enum LockRequestWait wait)98 waitLock( struct Lock *lock, enum LockRequestWait wait )
99 {
100 int fd;
101 struct flock l;
102
103 ASSERT( ! gotLock( lock ) );
104 Log_dbg( LOG_DBG_NEWSBASE, "Waiting for lock %s ...", lock->name );
105 if ( lock->lockFile[ 0 ] == '\0' )
106 snprintf( lock->lockFile, MAXCHAR, "%s/lock/%s",
107 Cfg_spoolDir(), lock->name );
108 if ( ( fd = open( lock->lockFile, O_WRONLY | O_CREAT, 0644 ) ) < 0 )
109 {
110 Log_err( "Cannot open %s (%s)", lock->lockFile, strerror( errno ) );
111 return FALSE;
112 }
113 l.l_type = F_WRLCK;
114 l.l_start = 0;
115 l.l_whence = SEEK_SET;
116 l.l_len = 0;
117 if ( wait == LOCK_WAIT )
118 {
119 SignalHandler oldAlarmHandler;
120 unsigned oldAlarm;
121
122 oldAlarmHandler = Utl_installSignalHandler( SIGALRM, lockWaitAlarm );
123 oldAlarm = alarm( 1 );
124 for(;;)
125 {
126 alarm( 1 );
127 if ( fcntl( fd, F_SETLKW, &l ) < 0 )
128 if ( errno != EINTR )
129 {
130 Utl_installSignalHandler( SIGALRM, oldAlarmHandler );
131 alarm( oldAlarm );
132 Log_err( "Cannot lock %s: %s", lock->lockFile,
133 strerror( errno ) );
134 close( lock->lockFd );
135 return FALSE;
136 }
137 else
138 {
139 /* Send SIGUSR1 to the process holding the lock. */
140 if ( fcntl( fd, F_GETLK, &l) == 0 && l.l_type != F_UNLCK )
141 if ( kill( l.l_pid, SIGUSR1 ) < 0 )
142 Log_err( "Can't signal process %d: %s", l.l_pid,
143 strerror( errno ) );
144 }
145 else
146 break;
147 }
148 Utl_installSignalHandler( SIGALRM, oldAlarmHandler );
149 alarm( oldAlarm );
150 }
151 else
152 {
153 if ( fcntl( fd, F_SETLK, &l ) < 0 )
154 {
155 close( lock->lockFd );
156 return FALSE;
157 }
158 }
159
160 lock->lockFd = fd;
161 Log_dbg( LOG_DBG_NEWSBASE, "Lock successful" );
162 return TRUE;
163 }
164
165 static void
releaseLock(struct Lock * lock)166 releaseLock( struct Lock *lock )
167 {
168 struct flock l;
169
170 ASSERT( gotLock( lock ) );
171 l.l_type = F_UNLCK;
172 l.l_start = 0;
173 l.l_whence = SEEK_SET;
174 l.l_len = 0;
175 if ( fcntl( lock->lockFd, F_SETLK, &l ) < 0 )
176 Log_err( "Cannot release %s: %s", lock->lockFile,
177 strerror( errno ) );
178 close( lock->lockFd );
179 lock->lockFd = -1;
180 Log_dbg( LOG_DBG_NEWSBASE, "Releasing lock" );
181 }
182
183 static Bool
openDatabases(void)184 openDatabases( void )
185 {
186 globalLock.lazyClose = FALSE;
187 if ( ! waitLock( &globalLock, LOCK_WAIT ) )
188 {
189 Log_err( "Could not get write lock" );
190 return FALSE;
191 }
192 if ( ! Db_open() )
193 {
194 Log_err( "Could not open database" );
195 releaseLock( &globalLock );
196 return FALSE;
197 }
198 if ( ! Grp_open() )
199 {
200 Log_err( "Could not open groupinfo" );
201 Db_close();
202 releaseLock( &globalLock );
203 return FALSE;
204 }
205 if ( ! Req_open() )
206 {
207 Log_err( "Could not initialize request database" );
208 Grp_close();
209 Db_close();
210 releaseLock( &globalLock );
211 return FALSE;
212 }
213
214 globalLock.lazyClose = globalLock.doLazyLocking;
215 globalLock.lazyLockBusy = TRUE;
216 return TRUE;
217 }
218
219 static void
closeDatabases(void)220 closeDatabases( void )
221 {
222 Grp_close();
223 Db_close();
224 Req_close();
225 Server_flushCache();
226 releaseLock( &globalLock );
227 globalLock.lazyLockBusy = FALSE;
228 globalLock.lazyClose = FALSE;
229 }
230
231 static void
lockSignal(int sig)232 lockSignal( int sig )
233 {
234 UNUSED( sig );
235
236 /*
237 * If we hold the lock right now, flag it to be released next close.
238 * Otherwise, if we are holding it lazily, release it.
239 */
240 if ( globalLock.lazyLockBusy )
241 globalLock.lazyClose = FALSE;
242 else if ( gotLock( &globalLock ) )
243 closeDatabases();
244 return;
245 }
246
247 /* Open all databases and set global lock. */
248 Bool
Lock_openDatabases(void)249 Lock_openDatabases( void )
250 {
251 Bool res;
252
253 /* First time - need to initialise signal handler? */
254 if ( oldHandler == NULL )
255 oldHandler = Utl_installSignalHandler( SIGUSR1, lockSignal );
256
257 if ( ! blockSignal( TRUE ) )
258 return FALSE;
259
260 if ( ! globalLock.lazyClose )
261 res = openDatabases();
262 else
263 res = TRUE;
264
265 globalLock.lazyLockBusy = res;
266
267 if ( ! blockSignal( FALSE ) )
268 return FALSE;
269
270 return res;
271 }
272
273 /* Close all databases and release global lock. */
274 void
Lock_closeDatabases(void)275 Lock_closeDatabases( void )
276 {
277 blockSignal( TRUE );
278
279 if ( ! globalLock.lazyClose )
280 closeDatabases();
281 else
282 globalLock.lazyLockBusy = FALSE;
283
284 blockSignal( FALSE );
285 }
286
287 /* Sync all databases to disc. Maintain global lock status. */
288 void
Lock_syncDatabases(void)289 Lock_syncDatabases( void )
290 {
291 Bool wasOpen;
292
293 if ( gotLock( &globalLock ) )
294 {
295 blockSignal( TRUE );
296
297 wasOpen = globalLock.lazyLockBusy;
298 closeDatabases();
299 if ( wasOpen )
300 openDatabases();
301
302 blockSignal( FALSE );
303 }
304 }
305
306 /* Check the global lock held. */
307 Bool
Lock_gotLock(void)308 Lock_gotLock( void )
309 {
310 return globalLock.lazyLockBusy;
311 }
312
313 /* Get fetch lock. */
314 Bool
Lock_getFetchLock(enum LockRequestWait wait)315 Lock_getFetchLock( enum LockRequestWait wait )
316 {
317 return waitLock( &fetchLock, wait );
318 }
319
320 /* Release fetch lock. */
321 void
Lock_releaseFetchLock(void)322 Lock_releaseFetchLock( void )
323 {
324 releaseLock( &fetchLock );
325 }
326
327 /* Check the fetch lock held. */
328 Bool
Lock_fetchLock(void)329 Lock_fetchLock( void )
330 {
331 return gotLock( &fetchLock );
332 }
333
334