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