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