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