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