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