1 /*
2 * binkleyforce -- unix FTN mailer project
3 *
4 * Copyright (c) 1998-2000 Alexander Belkin, 2:5020/1398.11
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * $Id: io_unix_lock.c,v 1.1.1.1 2004/09/09 09:52:38 kstepanenkov Exp $
12 */
13
14 #include "includes.h"
15 #include "confread.h"
16 #include "logger.h"
17 #include "util.h"
18 #include "io.h"
19
20 /*
21 * Set lock files type: Ascii, binary or SVR4? (not implemented yet)
22 */
23 #if (BFORCE_LOCK_TYPE == 1)
24 # define LOCK_ASCII
25 #elif (BFORCE_LOCK_TYPE == 2)
26 # define LOCK_BIN
27 #else
28 # error "Not implemented lock files type (BFORCE_LOCK_TYPE)"
29 #endif
30
31 /*
32 * Set lock files directory
33 */
34 #ifndef BFORCE_LOCK_DIR
35 # error "Not defined lock files directory (BFORCE_LOCK_DIR)"
36 #endif
37
38 /*
39 * Set prefix for lock file names
40 */
41 #ifndef BFORCE_LOCK_PREFIX
42 # define BFORCE_LOCK_PREFIX "LCK.."
43 #endif
44
45 /*
46 * How much times we will try to lock (if device
47 * is allready locked by another process)
48 */
49 #ifndef BFORCE_LOCK_TRIES
50 # define BFORCE_LOCK_TRIES 2
51 #endif
52
53 /*
54 * Delay in seconds between tries
55 */
56 #ifndef BFORCE_LOCK_DELAY
57 # define BFORCE_LOCK_DELAY 1
58 #endif
59
60 /* ========================================================================= */
61 /* "Low level" interface for tty lock files access (private) */
62 /* */
63 /* char *lock_getname(const char *lockdir, const s_modemport *modemport) */
64 /* pid_t lock_read_pid(const char *lckname) */
65 /* int lock_check(const char *lckname) */
66 /* int lock_create(const char *lckname, const char *tmpname) */
67 /* */
68 /* ========================================================================= */
69
70 /* ------------------------------------------------------------------------- */
71 /* Get lock file name with full path (this value must be free'ed) */
72 /* ------------------------------------------------------------------------- */
lock_getname(const char * lockdir,const s_modemport * modemport)73 static char *lock_getname(const char *lockdir, const s_modemport *modemport)
74 {
75 char *lckname;
76 char *p = NULL;
77
78 ASSERT(modemport != NULL);
79
80 if( lockdir && *lockdir )
81 lckname = (char *)xstrcpy(lockdir);
82 else
83 lckname = (char *)xstrcpy(BFORCE_LOCK_DIR);
84
85 lckname = (char *)xstrcat(lckname, BFORCE_LOCK_PREFIX);
86 lckname = (char *)xstrcat(lckname, (p = port_get_name(modemport->name)));
87
88 if( p )
89 free(p);
90
91 return lckname;
92 }
93
94 /* ------------------------------------------------------------------------- */
95 /* Get lock file's PID. Return zero on error and leave errno with last error */
96 /* ------------------------------------------------------------------------- */
lock_read_pid(const char * lckname)97 static pid_t lock_read_pid(const char *lckname)
98 {
99 int len, fd;
100 pid_t pid;
101 char buf[32];
102
103 ASSERT(lckname != NULL);
104
105 if( (fd = open(lckname, O_RDONLY)) < 0 )
106 return 0;
107
108 if( (len = read(fd, buf, sizeof(buf)-1)) < 0 )
109 {
110 close(fd);
111 return 0;
112 }
113
114 buf[len] = '\0';
115
116 if( len == sizeof(pid) || sscanf(buf, "%d", &pid) != 1 || pid == 0 )
117 {
118 /* We found binary lock file? */
119 pid = *((int *)buf);
120 #ifndef LOCK_BINARY
121 bf_log("warning: found binary lock file %s", lckname);
122 #endif
123 }
124 #ifdef LOCK_BINARY
125 else
126 {
127 /* Found ASCII lock file */
128 bf_log("warning: found ascii lock file %s", lckname);
129 }
130 #endif
131 close(fd);
132
133 return pid;
134 }
135
136 /* ------------------------------------------------------------------------- */
137 /* Validate lock file, check it's process still lives. */
138 /* Return: */
139 /* CHECKLOCK_NOLOCK - no lock file found */
140 /* CHECKLOCK_LOCKED - valid lock file */
141 /* CHECKLOCK_ERROR - invalid lock file name, access error, etc. */
142 /* CHECKLOCK_OURLOCK - if it is our lock file (with our PID) */
143 /* ------------------------------------------------------------------------- */
lock_check(const char * lckname)144 static int lock_check(const char *lckname)
145 {
146 pid_t pid = 0;
147 struct stat st;
148
149 ASSERT(lckname != NULL);
150
151 if( stat(lckname, &st) && errno == ENOENT )
152 return LOCKCHECK_NOLOCK;
153
154 if( (pid = lock_read_pid(lckname)) == 0 )
155 {
156 if( errno == ENOENT )
157 return LOCKCHECK_NOLOCK;
158 return LOCKCHECK_LOCKED;
159 }
160
161 DEB((D_MODEM, "lock_check: lock PID = %d", pid));
162
163 if( pid == getpid() )
164 { return LOCKCHECK_OURLOCK; }
165
166 if( kill(pid, 0) == -1 )
167 {
168 if( errno == ESRCH )
169 {
170 /*
171 * That process no longer exists, remove lock file
172 */
173 bf_log("found stale lock file with PID %d", pid);
174
175 if( unlink(lckname) == -1 )
176 {
177 logerr("cannot remove lockfile \"%s\", lckname");
178 return LOCKCHECK_ERROR;
179 } else
180 return LOCKCHECK_NOLOCK;
181 }
182 else
183 {
184 return LOCKCHECK_ERROR;
185 }
186 }
187
188 /* locking process still lives */
189 return LOCKCHECK_LOCKED;
190 }
191
192 /* ------------------------------------------------------------------------- */
193 /* Create lock file using link(tmpname, lckname). Return zero on success. */
194 /* ------------------------------------------------------------------------- */
lock_create(const char * lckname,const char * tmpname)195 static int lock_create(const char *lckname, const char *tmpname)
196 {
197 int rc, fd;
198 int tries;
199
200 #ifdef LOCK_BINARY
201 pid_t pid;
202 #else
203 char buf[32];
204 #endif
205
206 ASSERT(lckname != NULL && tmpname != NULL);
207
208 if( (fd = open(tmpname, O_CREAT | O_RDWR, 0644)) < 0 )
209 {
210 logerr("can't open temp file \"%s\"", tmpname);
211 return 1;
212 }
213
214 chmod(tmpname, 0644);
215
216 #ifdef LOCK_BINARY
217 pid = getpid();
218 rc = ( write(fd, (char *)&pid, sizeof(pid)) != sizeof(pid) );
219 #else
220 sprintf(buf, "%10d\n", (int)getpid());
221 rc = ( write(fd, (char *)buf, strlen(buf)) != strlen(buf) );
222 #endif
223 close(fd);
224
225 if( rc )
226 {
227 logerr("can't write PID to temp. lock file \"%s\"", tmpname);
228 unlink(tmpname);
229 return 1;
230 }
231
232 for( tries = 0; tries < BFORCE_LOCK_TRIES; tries++ )
233 {
234 if( link(tmpname, lckname) == -1 )
235 {
236 if( errno == EEXIST )
237 {
238 rc = lock_check(lckname);
239 if( rc == LOCKCHECK_ERROR )
240 {
241 break;
242 }
243 else if( rc == LOCKCHECK_OURLOCK )
244 {
245 /* There is our lock! :) */
246 unlink(tmpname);
247 return 0;
248 }
249 else if( rc == LOCKCHECK_LOCKED )
250 {
251 /* sleep some time */
252 sleep(BFORCE_LOCK_DELAY);
253 }
254 }
255 else
256 {
257 bf_log("can't create link to temp. lock file \"%s\"", lckname);
258 break;
259 }
260 }
261 else
262 {
263 /* Successful lock */
264 unlink(tmpname);
265 return 0;
266 }
267 }
268
269 /* Can't create lock file */
270 unlink(tmpname);
271 return 1;
272 }
273
274 /* ========================================================================= */
275 /* PUBLIC functions for tty lock files access */
276 /* */
277 /* int port_checklock(const char *lockdir, const char *dev) */
278 /* int port_lock(char *lockdir, char *dev) */
279 /* int port_unlock(const char *lockdir, const char *dev) */
280 /* */
281 /* ========================================================================= */
282
283 /* ------------------------------------------------------------------------- */
284 /* Check, is tty locked? Return one of return values of lock_check() */
285 /* ------------------------------------------------------------------------- */
port_checklock(const char * lockdir,const s_modemport * modemport)286 int port_checklock(const char *lockdir, const s_modemport *modemport)
287 {
288 int rc;
289 char *lckname;
290
291 if( (lckname = lock_getname(lockdir, modemport)) == NULL )
292 {
293 bf_log("can't get lock file name");
294 return LOCKCHECK_ERROR;
295 }
296
297 DEB((D_MODEM, "checkttylock: lock device = \"%s\", lockfile = \"%s\"",
298 modemport->name, lckname));
299
300 rc = lock_check(lckname);
301
302 if( lckname )
303 free(lckname);
304
305 return rc;
306 }
307
308 /* ------------------------------------------------------------------------- */
309 /* Lock serial port. On success return zero value */
310 /* ------------------------------------------------------------------------- */
port_lock(const char * lockdir,const s_modemport * modemport)311 int port_lock(const char *lockdir, const s_modemport *modemport)
312 {
313 int rc;
314 char *lckname;
315 char *tmpname, *p_tmpname;
316
317 if( lockdir && *lockdir )
318 tmpname = xstrcpy(lockdir);
319 else
320 tmpname = xstrcpy(BFORCE_LOCK_DIR);
321
322 tmpname = xstrcat(tmpname, "bfXXXXXX");
323
324 if( (p_tmpname = mktemp(tmpname)) == NULL )
325 {
326 logerr("can't generate unique file name from \"%s\"", tmpname);
327 free(tmpname); return 1;
328 }
329
330 if( (lckname = lock_getname(lockdir, modemport)) == NULL )
331 {
332 bf_log("can't get lock file name");
333 return(1);
334 }
335
336 DEB((D_MODEM, "locktty: lock device = \"%s\", tmpfile = \"%s\", lockfile = \"%s\"",
337 modemport->name, p_tmpname, lckname));
338
339 rc = lock_create(lckname, p_tmpname);
340
341 if( tmpname )
342 free(tmpname);
343 if( lckname )
344 free(lckname);
345
346 return rc;
347 }
348
349 /* ------------------------------------------------------------------------- */
350 /* Unlock serial port. Return zero on success. */
351 /* ------------------------------------------------------------------------- */
port_unlock(const char * lockdir,const s_modemport * modemport)352 int port_unlock(const char *lockdir, const s_modemport *modemport)
353 {
354 pid_t pid = 0;
355 char *lckname;
356
357 if( (lckname = lock_getname(lockdir, modemport)) == NULL )
358 return 1;
359
360 DEB((D_MODEM, "unlocktty: unlock device = \"%s\", lockfile = \"%s\"",
361 modemport->name, lckname));
362
363 if( (pid = lock_read_pid(lckname)) == 0 )
364 {
365 free(lckname);
366 return 1;
367 }
368
369 if( pid == getpid() && unlink(lckname) == -1 )
370 {
371 free(lckname);
372 return 1;
373 }
374
375 free(lckname);
376
377 return 0;
378 }
379
380