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