1 /*:ts=8*/
2 /*****************************************************************************
3  * FIDOGATE --- Gateway UNIX Mail/News <-> FIDO NetMail/EchoMail
4  *
5  * $Id: lock.c,v 4.17 2004/08/22 20:19:11 n0ll Exp $
6  *
7  * File locking
8  *
9  *****************************************************************************
10  * Copyright (C) 1990-2004
11  *  _____ _____
12  * |     |___  |   Martin Junius             <mj.at.n0ll.dot.net>
13  * | | | |   | |   Radiumstr. 18
14  * |_|_|_|@home|   D-51069 Koeln, Germany
15  *
16  * This file is part of FIDOGATE.
17  *
18  * FIDOGATE is free software; you can redistribute it and/or modify it
19  * under the terms of the GNU General Public License as published by the
20  * Free Software Foundation; either version 2, or (at your option) any
21  * later version.
22  *
23  * FIDOGATE is distributed in the hope that it will be useful, but
24  * WITHOUT ANY WARRANTY; without even the implied warranty of
25  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
26  * General Public License for more details.
27  *
28  * You should have received a copy of the GNU General Public License
29  * along with FIDOGATE; see the file COPYING.  If not, write to the Free
30  * Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
31  *****************************************************************************/
32 
33 #include "fidogate.h"
34 
35 #include <sys/types.h>
36 #include <fcntl.h>
37 
38 
39 /*
40  * lock_fd() --- lock file using file descriptor, wait
41  */
lock_fd(int fd)42 int lock_fd(int fd)
43 {
44 #ifndef HAS_FCNTL_LOCK
45     return OK;
46 #else
47     struct flock fl;
48     int err;
49 
50     fl.l_type   = F_WRLCK;
51     fl.l_whence = SEEK_SET;
52     fl.l_start  = 0;
53     fl.l_len    = 0;
54 
55     do
56     {
57 	err = fcntl(fd, F_SETLKW, &fl);
58     }
59     while(err == EINTR);
60 
61     return err;
62 #endif
63 }
64 
65 
66 
67 /*
68  * unlock_fd() --- unlock file using file descriptor
69  */
unlock_fd(int fd)70 int unlock_fd(int fd)
71 {
72 #ifndef HAS_FCNTL_LOCK
73     return OK;
74 #else
75     struct flock fl;
76     int err;
77 
78     fl.l_type   = F_UNLCK;
79     fl.l_whence = SEEK_SET;
80     fl.l_start  = 0;
81     fl.l_len    = 0;
82 
83     do
84     {
85 	err = fcntl(fd, F_SETLKW, &fl);
86     }
87     while(err == EINTR);
88 
89     return err;
90 #endif
91 }
92 
93 
94 
95 /*
96  * lock_file() --- lock file using FILE *
97  */
lock_file(FILE * fp)98 int lock_file(FILE *fp)
99 {
100     return lock_fd( fileno(fp) );
101 }
102 
103 
104 
105 /*
106  * unlock_file() --- unlock file using FILE *
107  */
unlock_file(FILE * fp)108 int unlock_file(FILE *fp)
109 {
110     return unlock_fd( fileno(fp) );
111 }
112 
113 
114 
115 #ifdef NFS_SAFE_LOCK_FILES
116 /*
117  * Create lock file, NFS-safe variant
118  */
lock_lockfile_nfs(char * name,int wait,char * id)119 int lock_lockfile_nfs(char *name, int wait, char *id)
120 {
121     char uniq_name[MAXPATH];
122     int uniq_fd;
123     int success;
124     FILE *fp;
125     struct stat st;
126 
127     BUF_COPY(uniq_name, name);
128     str_printf(uniq_name+strlen(uniq_name),
129 	       sizeof(uniq_name)-strlen(uniq_name),
130 	       ".L%d", (int)getpid());
131 
132     /* create unique file */
133     debug(7, "About to create unique %s (for lock %s)", uniq_name, name);
134 
135     uniq_fd = open(uniq_name, O_RDWR | O_CREAT | O_EXCL, BSY_MODE);
136     if(uniq_fd == ERROR)
137     {
138 	if(wait)
139 	{
140 	    logit("$ERROR: creating unique %s (for lock %s) failed",
141 		uniq_name, name);
142 	    exit(EX_OSFILE);
143 	}
144 	else
145 	{
146 	    logit("$WARNING: creating unique %s (for lock %s) failed",
147 		uniq_name, name);
148 	    return ERROR;
149 	}
150     }
151     if((fp = fdopen(uniq_fd, "w")))
152     {
153 	if(id)
154 	    fprintf(fp, "%s\n", id);
155 	else
156 	    fprintf(fp, "%d\n", (int)getpid());
157 	fclose(fp);
158     }
159     close(uniq_fd);
160 
161     /* try to link to actual lock file */
162     do
163     {
164 	success = FALSE;
165 	if( link(uniq_name, name) == ERROR )
166 	{
167 	    /* Other errors than EEXIST are a failure */
168 	    if(errno != EEXIST)
169 	    {
170 		if(wait)
171 		{
172 		    logit("$ERROR: linking unique %s -> lock %s failed",
173 			uniq_name, name);
174 		    unlink(uniq_name);
175 		    exit(EX_OSFILE);
176 		}
177 		else
178 		{
179 		    logit("$WARNING: linking unique %s -> %s failed",
180 			uniq_name, name);
181 		    unlink(uniq_name);
182 		    return ERROR;
183 		}
184 	    }
185 	}
186 	else
187 	{
188 	    /* Link OK, check stat of unique */
189 	    if( stat(uniq_name, &st) == ERROR)
190 	    {
191 		/* Should not fail */
192 		logit("$ERROR: stat unique %s (for lock %s) failed",
193 		    uniq_name, name);
194 		unlink(uniq_name);
195 		exit(EX_OSFILE);
196 	    }
197 	    if(st.st_nlink == 2)
198 		success = TRUE;
199 	}
200 
201 	debug(7, "Linking unique %s -> lock %s %s",
202 	      uniq_name, name, success ? "succeeded" : "failed");
203 
204 	if(wait && !success)
205 	    sleep(5);
206     }
207     while(wait && !success);
208 
209     /* Always remove unique file */
210     unlink(uniq_name);
211 
212     return success ? OK : ERROR;
213 }
214 
215 
216 
217 /*
218  * Delete lock file, NFS-safe variant
219  */
unlock_lockfile_nfs(char * name)220 int unlock_lockfile_nfs(char *name)
221 {
222     int ret = OK;
223 
224     if( unlink(name) == ERROR )
225     {
226 	logit("$WARNING: removing lock %s failed", name);
227 	ret = ERROR;
228     }
229 
230     return ret;
231 }
232 
233 
234 
235 #else /**!NFS_SAFE_LOCKFILES**/
236 /*
237  * Create lock file with PID (id==NULL) or arbitrary string (id!=NULL)
238  */
lock_lockfile_id(char * name,int wait,char * id)239 int lock_lockfile_id(char *name, int wait, char *id)
240 {
241     int fd;
242     FILE *fp;
243 
244     /* Create lock file */
245     debug(7, "Creating lock file %s ...", name);
246     do
247     {
248 	/*
249 	 * Use open() with flag O_EXCL, this will fail if the
250 	 * lock file already exists
251 	 */
252 	fd = open(name, O_RDWR | O_CREAT | O_EXCL, BSY_MODE);
253 	debug(7, "Creating lock file %s %s.",
254 	      name, fd==-1 ? "failed" : "succeeded");
255 	if(fd != -1)
256 	{
257 	    if((fp = fdopen(fd, "w")))
258 	    {
259 		if(id)
260 		    fprintf(fp, "%s\n", id);
261 		else
262 		    fprintf(fp, "%d\n", (int)getpid());
263 		fclose(fp);
264 	    }
265 	    close(fd);
266 	}
267 	else if(wait)
268 	    sleep(5);
269     }
270     while(fd==-1 && wait);
271 
272     return fd==-1 ? ERROR : OK;
273 }
274 
275 
276 
277 /*
278  * Remove lock file
279  */
unlock_lockfile(char * name)280 int unlock_lockfile(char *name)
281 {
282     int ret;
283 
284     ret = unlink(name);
285     debug(7, "Deleting lock file %s %s.",
286 	  name, ret==-1 ? "failed" : "succeeded");
287 
288     return ret==-1 ? ERROR : OK;
289 }
290 #endif /**NFS_SAFE_LOCK_FILES**/
291 
292 
293 
294 /*
295  * Create lock file for program in SPOOLDIR/LOCKS
296  */
lock_program_id(char * name,int wait,char * id)297 int lock_program_id(char *name, int wait, char *id)
298 {
299     char buf[MAXPATH];
300 
301     BUF_COPY3(buf, cf_p_lockdir(), "/", name);
302 
303 #ifdef NFS_SAFE_LOCK_FILES
304     return lock_lockfile_nfs(buf, wait, id);
305 #else
306     return lock_lockfile_id(buf, wait, id);
307 #endif
308 }
309 
310 
lock_program(char * name,int wait)311 int lock_program(char *name, int wait)
312 {
313     char buf[MAXPATH];
314 
315     BUF_COPY3(buf, cf_p_lockdir(), "/", name);
316 
317 #ifdef NFS_SAFE_LOCK_FILES
318     return lock_lockfile_nfs(buf, wait, NULL);
319 #else
320     return lock_lockfile_id(buf, wait, NULL);
321 #endif
322 }
323 
324 
325 
326 /*
327  * Remove lock file for program in SPOOLDIR/LOCKS
328  */
unlock_program(char * name)329 int unlock_program(char *name)
330 {
331     char buf[MAXPATH];
332 
333     BUF_COPY3(buf, cf_p_lockdir(), "/", name);
334 
335 #ifdef NFS_SAFE_LOCK_FILES
336     return unlock_lockfile_nfs(buf);
337 #else
338     return unlock_lockfile(buf);
339 #endif
340 }
341 
342 
343 
344 /***** Test program *********************************************************/
345 #ifdef TEST
346 
main(int argc,char * argv[])347 int main(int argc, char *argv[])
348 {
349     char *file;
350     FILE *fp;
351     int c;
352 
353     if(argc < 2)
354     {
355 	fprintf(stderr, "usage: lock.c-TEST file\n");
356 	exit(1);
357     }
358     file = argv[1];
359 
360     if((fp = fopen(file, "a")) == NULL)
361     {
362 	fprintf(stderr, "lock.c-TEST: can't open %s: ", file);
363 	perror("");
364 	exit(1);
365     }
366 
367     printf("Locking %s ...\n", file);
368     if(lock_file(fp))
369     {
370 	fprintf(stderr, "lock.c-TEST: can't lock %s: ", file);
371 	perror("");
372 	exit(1);
373     }
374     printf("%s locked.\n", file);
375 
376     printf("Press <Return> ..."); fflush(stdout);
377     while((c = getchar()) != '\n') ;
378 
379     printf("Unlocking %s ...\n", file);
380     if(unlock_file(fp))
381     {
382 	fprintf(stderr, "lock.c-TEST: can't unlock %s: ", file);
383 	perror("");
384 	exit(1);
385     }
386     printf("%s unlocked.\n", file);
387 
388     exit(0);
389 }
390 
391 #endif /**TEST**/
392