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