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