chiark / gitweb /
*** empty log message ***
[sympathy.git] / src / lockfile.c
1 /*
2  * lockfile.c:
3  *
4  * Copyright (c) 2008 James McKenzie <james@fishsoup.dhs.org>,
5  * All rights reserved.
6  *
7  */
8
9 static char rcsid[] = "$Id$";
10
11 /*
12  * $Log$
13  * Revision 1.10  2008/03/02 10:37:56  james
14  * *** empty log message ***
15  *
16  * Revision 1.9  2008/02/15 23:52:12  james
17  * *** empty log message ***
18  *
19  * Revision 1.8  2008/02/15 20:52:36  james
20  * *** empty log message ***
21  *
22  * Revision 1.7  2008/02/15 19:51:30  james
23  * *** empty log message ***
24  *
25  * Revision 1.6  2008/02/15 19:09:00  james
26  * *** empty log message ***
27  *
28  * Revision 1.5  2008/02/15 18:26:49  james
29  * *** empty log message ***
30  *
31  * Revision 1.4  2008/02/15 18:16:48  james
32  * *** empty log message ***
33  *
34  * Revision 1.3  2008/02/15 18:16:35  james
35  * *** empty log message ***
36  *
37  * Revision 1.2  2008/02/15 16:48:56  james
38  * *** empty log message ***
39  *
40  * Revision 1.1  2008/02/15 15:09:17  james
41  * *** empty log message ***
42  *
43  */
44
45 #define LOCK_ASCII
46 #undef LOCK_BINARY
47
48 #define STALE_CHECK_INTERVAL 10
49
50 #include <stdio.h>
51 #include <sys/types.h>
52 #include <sys/stat.h>
53 #include <malloc.h>
54 #include <unistd.h>
55 #include <string.h>
56 #include <strings.h>
57 #include <stdlib.h>
58 #include <pwd.h>
59 #include <fcntl.h>
60 #include <dirent.h>
61 #include <errno.h>
62 #include <time.h>
63 #include <sys/time.h>
64
65 #include "lockfile.h"
66
67 Filelist *
68 filelist_new (void)
69 {
70   Filelist *fl = (Filelist *) malloc (sizeof (Filelist));
71
72   fl->head = NULL;
73
74   return fl;
75 }
76
77 void
78 filelist_remove (Filelist * fl, Filelist_ent * fle)
79 {
80   Filelist_ent **ep;
81
82   for (ep = &fl->head; *ep; ep = &((*ep)->next))
83     if (fle == *ep)
84       break;
85
86
87   if (!*ep)
88     return;
89
90   *ep = fle->next;
91
92   free (fle);
93 }
94
95 void
96 filelist_add (Filelist * fl, char *fn)
97 {
98   Filelist_ent *fle;
99   int i = strlen (fn);
100
101   if (i >= FILE_LIST_MAX_LEN)
102     return;
103
104   for (fle = fl->head; fle; fle = fle->next)
105     if (!strcmp (fle->name, fn))
106       return;
107
108   fle = malloc (sizeof (Filelist_ent));
109
110   strcpy (fle->name, fn);
111
112   fle->next = fl->head;
113   fl->head = fle;
114 }
115
116 void
117 filelist_free (Filelist * fl)
118 {
119   while (fl->head)
120     filelist_remove (fl, fl->head);
121   free (fl);
122 }
123
124 void
125 filelist_print (Filelist * fl, FILE * f)
126 {
127   Filelist_ent *fle;
128   if (!fl) {
129     fprintf (f, "(empty list)\n");
130     return;
131   }
132   for (fle = fl->head; fle; fle = fle->next)
133     fprintf (f, "%s\n", fle->name);
134 }
135
136
137
138
139 static int
140 chown_uucp (fd)
141      int fd;
142 {
143   static int uuid = -1, ugid;
144   struct passwd *pw;
145
146   if (uuid < 0) {
147     if (pw = getpwnam ("uucp")) {
148       uuid = pw->pw_uid;
149       ugid = pw->pw_gid;
150     } else {
151       return -1;
152     }
153   }
154   return fchown (fd, uuid, ugid);
155 }
156
157 int
158 lockfile_make (char *name)
159 {
160   char buf[1024], tmpfn[1024];
161   char *ptr;
162   int fd;
163   int i;
164
165   strcpy (tmpfn, name);
166
167   ptr = rindex (tmpfn, '/');
168   if (!ptr)
169     return -1;
170
171   ptr++;
172
173   ptr += sprintf (ptr, "LTMP.%d", getpid ());
174   *ptr = 0;
175
176   i = sprintf (buf, "%10d\n", getpid ());
177
178   unlink (tmpfn);
179   fd = open (tmpfn, O_WRONLY | O_CREAT | O_TRUNC, 0444);
180   if (fd < 0) {
181     unlink (tmpfn);
182     return -1;
183   }
184
185   write (fd, buf, i);
186   fchmod (fd, 044);
187   if (chown_uucp (fd)) {
188     close (fd);
189     unlink (tmpfn);
190     return -1;
191   }
192
193   close (fd);
194
195   if (link (tmpfn, name) < 0) {
196     unlink (tmpfn);
197     return -1;
198   }
199
200   unlink (tmpfn);
201   return 0;
202 }
203
204
205 void
206 lockfile_add_places (Filelist * fl, char *leaf)
207 {
208   char buf[1024];
209   struct stat stbuf;
210   char *lock_dirs[] =
211     { "/var/lock/uucp", "/var/spool/lock", "/var/spool/uucp", "/etc/locks",
212     "/usr/spool/uucp", "/var/spool/locks", "/usr/spool/lock",
213     "/usr/spool/locks", "/usr/spool/uucp/LCK", "/var/lock"
214   };
215   int i;
216
217   for (i = 0; i < (sizeof (lock_dirs) / sizeof (char *)); ++i) {
218     if (stat (lock_dirs[i], &stbuf))
219       continue;
220     strcpy (buf, lock_dirs[i]);
221     strcat (buf, "/");
222     strcat (buf, leaf);
223     filelist_add (fl, buf);
224   }
225 }
226
227
228 static void
229 do_tedious_mangling (Filelist * fl, char *buf, char *ptr, char inv, int lower)
230 {
231   while (*ptr) {
232     if (lower && (*ptr >= 'A') && (*ptr <= 'Z'))
233       *ptr |= 32;
234     if (*ptr == '/')
235       *ptr = inv;
236     ptr++;
237   }
238
239   lockfile_add_places (fl, buf);
240 }
241
242
243 void
244 lockfile_regularize_and_add (Filelist * fl, char *leaf)
245 {
246   char buf[1024] = "LCK..";
247   char *ptr;
248
249   if (*leaf == '/')
250     leaf++;
251
252   ptr = buf;
253   while (*ptr)
254     ptr++;
255
256   strcpy (ptr, leaf);
257   do_tedious_mangling (fl, buf, ptr, '_', 0);
258   strcpy (ptr, leaf);
259   do_tedious_mangling (fl, buf, ptr, '_', 1);
260   strcpy (ptr, leaf);
261   do_tedious_mangling (fl, buf, ptr, '.', 0);
262   strcpy (ptr, leaf);
263   do_tedious_mangling (fl, buf, ptr, '.', 1);
264 }
265
266 void
267 lockfile_add_name_from_path (Filelist * fl, char *file)
268 {
269   char *ptr = file;
270
271
272   if (*ptr == '/')
273     ptr++;
274   lockfile_regularize_and_add (fl, ptr);
275
276   if (!strncmp (ptr, "dev/", 4)) {
277     ptr += 4;
278     lockfile_regularize_and_add (fl, ptr);
279   }
280
281 }
282
283 void
284 lockfile_add_name_from_dev (Filelist * fl, dev_t dev)
285 {
286   char buf[1024];
287   sprintf (buf, "LCK.%03d.%03d", major (dev), minor (dev));
288   lockfile_add_places (fl, buf);
289 }
290
291 void
292 lockfile_check_dir_for_dev (Filelist * fl, char *dir, dev_t dev)
293 {
294   char buf[1024];
295   struct stat ent_stat;
296   struct dirent *de;
297   DIR *d;
298
299   d = opendir (dir);
300
301   if (!d)
302     return;
303
304   while ((de = readdir (d))) {
305     strcpy (buf, dir);
306     strcat (buf, de->d_name);
307
308     if (stat (buf, &ent_stat))
309       continue;
310     if (!S_ISCHR (ent_stat.st_mode))
311       continue;
312     if (ent_stat.st_rdev != dev)
313       continue;
314
315     lockfile_add_name_from_path (fl, buf);
316
317   }
318   closedir (d);
319 }
320
321 Filelist *
322 lockfile_make_list (char *device)
323 {
324   struct stat dev_stat;
325   Filelist *ret = NULL;
326
327
328   if (stat (device, &dev_stat))
329     return ret;
330   if (!S_ISCHR (dev_stat.st_mode))
331     return ret;
332
333   ret = filelist_new ();
334
335   lockfile_add_name_from_dev (ret, dev_stat.st_rdev);
336
337   lockfile_add_name_from_path (ret, device);
338
339   lockfile_check_dir_for_dev (ret, "/dev/", dev_stat.st_rdev);
340   lockfile_check_dir_for_dev (ret, "/dev/usb/", dev_stat.st_rdev);
341   lockfile_check_dir_for_dev (ret, "/dev/tts/", dev_stat.st_rdev);
342
343   return ret;
344 }
345
346 static void
347 remove_stale_lock (char *path)
348 {
349   int fd;
350   int pid;
351   char apid[20];
352   int length;
353
354   fd = open (path, O_RDONLY);
355   if (fd < 0)
356     return;
357
358   length = read (fd, apid, sizeof (apid) - 1);
359   if (length < 0)
360     length = 0;
361   apid[length] = 0;
362
363   pid = 0;
364   if (length == sizeof (pid) || sscanf (apid, "%d", &pid) != 1 || pid == 0) {
365     pid = *((int *) apid);
366 #ifdef LOCK_ASCII
367     fprintf (stderr,
368              "compiled with ascii locks, found binary lock file (length=%d, pid=%d)!",
369              length, pid);
370 #endif
371   }
372 #ifdef LOCK_BINARY
373   else {
374     fprintf (stderr,
375              "compiled with binary locks, found ascii lock file (length=%d, pid=%d)!",
376              length, pid);
377   }
378 #endif
379
380   close (fd);
381
382   if ((kill (pid, 0) < 0) && (errno == ESRCH)) {
383     fprintf (stderr, "removing stale lock file %s\n", path);
384     unlink (path);
385   }
386
387 }
388
389 void
390 lockfile_remove_stale (Filelist * fl)
391 {
392   Filelist_ent *fle;
393   struct stat buf;
394
395   for (fle = fl->head; fle; fle = fle->next) {
396     if (stat (fle->name, &buf))
397       continue;
398     remove_stale_lock (fle->name);
399   }
400
401
402 }
403
404
405 Filelist *
406 lockfile_lock (Filelist * fl)
407 {
408   Filelist *ret;
409   Filelist_ent *fle;
410
411   ret = filelist_new ();
412
413   lockfile_remove_stale (fl);
414
415   for (fle = fl->head; fle; fle = fle->next) {
416     if (lockfile_make (fle->name)) {
417       fprintf (stderr, "Failed to get lockfile %s\n", fle->name);
418       filelist_free (ret);
419       return NULL;
420     }
421     filelist_add (ret, fle->name);
422   }
423
424   return ret;
425 }
426
427 void
428 lockfile_unlock (Filelist * fl)
429 {
430
431   while (fl->head) {
432     unlink (fl->head->name);
433     filelist_remove (fl, fl->head);
434   }
435 }
436
437
438 /* If we have a passive lock, check noone has an */
439 /* active one, returns 1 if he does, 0 if he doesnt */
440 int
441 serial_lock_check (Serial_lock * l)
442 {
443   Filelist_ent *fle;
444   int locks_found = 0;
445   struct stat buf;
446   struct timeval now, dif;
447
448   if (l->mode == SERIAL_LOCK_ACTIVE)
449     return 0;
450
451   for (fle = l->locks_to_check->head; fle; fle = fle->next) {
452     if (!stat (fle->name, &buf))
453       locks_found++;
454   }
455
456   if (!locks_found)
457     return 0;
458
459   gettimeofday (&now, NULL);
460   timersub (&now, &l->last_stale_purge, &dif);
461
462   if (dif.tv_sec > STALE_CHECK_INTERVAL) {
463     lockfile_remove_stale (l->locks_to_check);
464     l->last_stale_purge = now;
465   }
466
467   return 1;
468 }
469
470 void
471 serial_lock_free (Serial_lock * l)
472 {
473   if (!l)
474     return;
475
476   if (l->locks_held) {
477     lockfile_unlock (l->locks_held);
478     filelist_free (l->locks_held);
479   }
480
481   if (l->locks_to_check) {
482     filelist_free (l->locks_to_check);
483   }
484
485   free (l);
486 }
487
488
489 Serial_lock *
490 serial_lock_new (char *dev, int mode)
491 {
492   Filelist *fl = lockfile_make_list (dev);
493   Serial_lock *l;
494
495   if (!fl)
496     return NULL;
497
498   l = (Serial_lock *) malloc (sizeof (Serial_lock));
499
500   l->mode = mode;
501   l->locks_to_check = fl;
502   l->locks_held = NULL;
503   memset (&l->last_stale_purge, 0, sizeof (l->last_stale_purge));
504
505   if (mode == SERIAL_LOCK_PASSIVE)
506     return l;
507
508   l->locks_held = lockfile_lock (l->locks_to_check);
509   if (!l->locks_held) {
510     serial_lock_free (l);
511     return NULL;
512   }
513
514   return l;
515 }
516
517
518 #if 0
519 int
520 main (int argc, char *argv[])
521 {
522   Filelist *fl = lockfile_make_list ("/dev/ttyS0");
523   Filelist *fll;
524   Filelist_ent *fle;
525
526   filelist_print (fl, stdout);
527
528   fll = lockfile_lock (fl);
529
530   filelist_print (fll, stdout);
531 }
532 #endif