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.5  2008/02/15 18:26:49  james
14  * *** empty log message ***
15  *
16  * Revision 1.4  2008/02/15 18:16:48  james
17  * *** empty log message ***
18  *
19  * Revision 1.3  2008/02/15 18:16:35  james
20  * *** empty log message ***
21  *
22  * Revision 1.2  2008/02/15 16:48:56  james
23  * *** empty log message ***
24  *
25  * Revision 1.1  2008/02/15 15:09:17  james
26  * *** empty log message ***
27  *
28  */
29
30 #define LOCK_ASCII
31 #undef LOCK_BINARY
32
33 #include <stdio.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <malloc.h>
37 #include <unistd.h>
38 #include <string.h>
39 #include <strings.h>
40 #include <stdlib.h>
41 #include <pwd.h>
42 #include <fcntl.h>
43 #include <dirent.h>
44 #include <errno.h>
45
46 #include "lockfile.h"
47
48 Filelist *
49 filelist_new (void)
50 {
51   Filelist *fl = (Filelist *) malloc (sizeof (Filelist));
52
53   fl->head = NULL;
54
55   return fl;
56 }
57
58 void
59 filelist_remove (Filelist * fl, Filelist_ent * fle)
60 {
61   Filelist_ent **ep;
62
63   for (ep = &fl->head; *ep; ep = &((*ep)->next))
64     if (fle == *ep)
65       break;
66
67
68   if (!*ep)
69     return;
70
71   *ep = fle->next;
72
73   free (fle);
74 }
75
76 void
77 filelist_add (Filelist * fl, char *fn)
78 {
79   Filelist_ent *fle;
80   int i = strlen (fn);
81
82   if (i >= FILE_LIST_MAX_LEN)
83     return;
84
85   for (fle = fl->head; fle; fle = fle->next)
86     if (!strcmp (fle->name, fn))
87       return;
88
89   fle = malloc (sizeof (Filelist_ent));
90
91   strcpy (fle->name, fn);
92
93   fle->next = fl->head;
94   fl->head = fle;
95 }
96
97 void
98 filelist_free (Filelist * fl)
99 {
100   while (fl->head)
101     filelist_remove (fl, fl->head);
102   free (fl);
103 }
104
105 void
106 filelist_print (Filelist * fl, FILE * f)
107 {
108   Filelist_ent *fle;
109   if (!fl)
110     {
111       fprintf (f, "(empty list)\n");
112       return;
113     }
114   for (fle = fl->head; fle; fle = fle->next)
115     fprintf (f, "%s\n", fle->name);
116 }
117
118
119
120
121 static int
122 chown_uucp (fd)
123      int fd;
124 {
125   static int uuid = -1, ugid;
126   struct passwd *pw;
127
128   if (uuid < 0)
129     {
130       if (pw = getpwnam ("uucp"))
131         {
132           uuid = pw->pw_uid;
133           ugid = pw->pw_gid;
134         }
135       else
136         {
137           return -1;
138         }
139     }
140   return fchown (fd, uuid, ugid);
141 }
142
143 int
144 lockfile_make (char *name)
145 {
146   char buf[1024], tmpfn[1024];
147   char *ptr;
148   int fd;
149   int i;
150
151   strcpy (tmpfn, name);
152
153   ptr = rindex (tmpfn, '/');
154   if (!ptr)
155     return -1;
156
157   ptr++;
158
159   ptr += sprintf (ptr, "LTMP.%d", getpid ());
160   *ptr = 0;
161
162   i = sprintf (buf, "%10d\n", getpid ());
163
164   unlink (tmpfn);
165   fd = open (tmpfn, O_WRONLY | O_CREAT | O_TRUNC, 0444);
166   if (fd < 0)
167     {
168       unlink (tmpfn);
169       return -1;
170     }
171
172   write (fd, buf, i);
173   fchmod (fd, 044);
174   if (chown_uucp (fd))
175     {
176       close (fd);
177       unlink (tmpfn);
178       return -1;
179     }
180
181   close (fd);
182
183   if (link (tmpfn, name) < 0)
184     {
185       unlink (tmpfn);
186       return -1;
187     }
188
189   unlink (tmpfn);
190   return 0;
191 }
192
193
194 void
195 lockfile_add_places (Filelist * fl, char *leaf)
196 {
197   char buf[1024];
198   struct stat stbuf;
199   char *lock_dirs[] =
200     { "/var/lock/uucp", "/var/spool/lock", "/var/spool/uucp", "/etc/locks",
201     "/usr/spool/uucp", "/var/spool/locks", "/usr/spool/lock",
202     "/usr/spool/locks",
203     "/usr/spool/uucp/LCK"
204   };
205   int i;
206
207   for (i = 0; i < (sizeof (lock_dirs) / sizeof (char *)); ++i)
208     {
209       if (stat (lock_dirs[i], &stbuf))
210         continue;
211       strcpy (buf, lock_dirs[i]);
212       strcat (buf, "/");
213       strcat (buf, leaf);
214       filelist_add (fl, buf);
215     }
216 }
217
218
219 static void
220 do_tedious_mangling (Filelist * fl, char *buf, char *ptr, char inv, int lower)
221 {
222   while (*ptr)
223     {
224       if (lower && (*ptr >= 'A') && (*ptr <= 'Z'))
225         *ptr |= 32;
226       if (*ptr == '/')
227         *ptr = inv;
228       ptr++;
229     }
230
231   lockfile_add_places (fl, buf);
232 }
233
234
235 void
236 lockfile_regularize_and_add (Filelist * fl, char *leaf)
237 {
238   char buf[1024] = "LCK..";
239   char *ptr;
240
241   if (*leaf == '/')
242     leaf++;
243
244   ptr = buf;
245   while (*ptr)
246     ptr++;
247
248   strcpy (ptr, leaf);
249   do_tedious_mangling (fl, buf, ptr, '_', 0);
250   strcpy (ptr, leaf);
251   do_tedious_mangling (fl, buf, ptr, '_', 1);
252   strcpy (ptr, leaf);
253   do_tedious_mangling (fl, buf, ptr, '.', 0);
254   strcpy (ptr, leaf);
255   do_tedious_mangling (fl, buf, ptr, '.', 1);
256 }
257
258 void
259 lockfile_add_name_from_path (Filelist * fl, char *file)
260 {
261   char *ptr = file;
262
263
264   if (*ptr == '/')
265     ptr++;
266   lockfile_regularize_and_add (fl, ptr);
267
268   if (!strncmp (ptr, "dev/", 4))
269     {
270       ptr += 4;
271       lockfile_regularize_and_add (fl, ptr);
272     }
273
274 }
275
276 void
277 lockfile_add_name_from_dev (Filelist * fl, dev_t dev)
278 {
279   char buf[1024];
280   sprintf (buf, "LCK.%03d.%03d", major (dev), minor (dev));
281   lockfile_add_places (fl, buf);
282 }
283
284 void
285 lockfile_check_dir_for_dev (Filelist * fl, char *dir, dev_t dev)
286 {
287   char buf[1024];
288   struct stat ent_stat;
289   struct dirent *de;
290   DIR *d;
291
292   d = opendir (dir);
293
294   if (!d)
295     return;
296
297   while ((de = readdir (d)))
298     {
299       strcpy (buf, dir);
300       strcat (buf, de->d_name);
301
302       if (stat (buf, &ent_stat))
303         continue;
304       if (!S_ISCHR (ent_stat.st_mode))
305         continue;
306       if (ent_stat.st_rdev != dev)
307         continue;
308
309       lockfile_add_name_from_path (fl, buf);
310
311     }
312   closedir (d);
313 }
314
315 Filelist *
316 lockfile_make_list (char *device)
317 {
318   struct stat dev_stat;
319   Filelist *ret = NULL;
320
321
322   if (stat (device, &dev_stat))
323     return ret;
324   if (!S_ISCHR (dev_stat.st_mode))
325     return ret;
326
327   ret = filelist_new ();
328
329   lockfile_add_name_from_dev (ret, dev_stat.st_rdev);
330
331   lockfile_add_name_from_path (ret, device);
332
333   lockfile_check_dir_for_dev (ret, "/dev/", dev_stat.st_rdev);
334   lockfile_check_dir_for_dev (ret, "/dev/usb/", dev_stat.st_rdev);
335   lockfile_check_dir_for_dev (ret, "/dev/tts/", dev_stat.st_rdev);
336
337   return ret;
338 }
339
340 static void
341 remove_stale_lock (char *path)
342 {
343   int fd;
344   int pid;
345   char apid[20];
346   int length;
347
348   fd = open (path, O_RDONLY);
349   if (fd < 0)
350     return;
351
352   length = read (fd, apid, sizeof (apid) - 1);
353   if (length < 0)
354     length = 0;
355   apid[length] = 0;
356
357   pid = 0;
358   if (length == sizeof (pid) || sscanf (apid, "%d", &pid) != 1 || pid == 0)
359     {
360       pid = *((int *) apid);
361 #ifdef LOCK_ASCII
362       fprintf (stderr,
363                "compiled with ascii locks, found binary lock file (length=%d, pid=%d)!",
364                length, pid);
365 #endif
366     }
367 #ifdef LOCK_BINARY
368   else
369     {
370       fprintf (stderr,
371                "compiled with binary locks, found ascii lock file (length=%d, pid=%d)!",
372                length, pid);
373     }
374 #endif
375
376   close (fd);
377
378   if ((kill (pid, 0) < 0) && (errno == ESRCH))
379     {
380       fprintf (stderr, "removing stale lock file %s\n", path);
381       unlink (path);
382     }
383
384 }
385
386 void
387 lockfile_remove_stale (Filelist * fl)
388 {
389   Filelist_ent *fle;
390   struct stat buf;
391
392   for (fle = fl->head; fle; fle = fle->next)
393     {
394       if (stat (fle->name, &buf))
395         continue;
396       remove_stale_lock (fle->name);
397     }
398
399
400 }
401
402
403 Filelist *
404 lockfile_lock (Filelist * fl)
405 {
406   Filelist *ret;
407   Filelist_ent *fle;
408
409   ret = filelist_new ();
410
411   lockfile_remove_stale (fl);
412
413   for (fle = fl->head; fle; fle = fle->next)
414     {
415       if (lockfile_make (fle->name))
416         {
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 #if 1
428 int
429 main (int argc, char *argv[])
430 {
431   Filelist *fl = lockfile_make_list ("/dev/ttyS0");
432   Filelist *fll;
433   Filelist_ent *fle;
434
435   filelist_print (fl, stdout);
436
437   fll = lockfile_lock (fl);
438
439   filelist_print (fll, stdout);
440 }
441 #endif