/* * lockfile.c: * * Copyright (c) 2008 James McKenzie , * All rights reserved. * */ static char rcsid[] = "$Id$"; /* * $Log$ * Revision 1.18 2012/06/22 10:22:24 james * *** empty log message *** * * Revision 1.17 2010/07/16 11:04:10 james * ignore tedious return values * * Revision 1.16 2008/05/09 12:56:11 james * *** empty log message *** * * Revision 1.15 2008/03/07 14:13:40 james * *** empty log message *** * * Revision 1.14 2008/03/07 13:16:02 james * *** empty log message *** * * Revision 1.13 2008/03/07 12:37:04 james * *** empty log message *** * * Revision 1.12 2008/03/03 06:04:42 james * *** empty log message *** * * Revision 1.11 2008/03/02 10:38:18 james * *** empty log message *** * * Revision 1.10 2008/03/02 10:37:56 james * *** empty log message *** * * Revision 1.9 2008/02/15 23:52:12 james * *** empty log message *** * * Revision 1.8 2008/02/15 20:52:36 james * *** empty log message *** * * Revision 1.7 2008/02/15 19:51:30 james * *** empty log message *** * * Revision 1.6 2008/02/15 19:09:00 james * *** empty log message *** * * Revision 1.5 2008/02/15 18:26:49 james * *** empty log message *** * * Revision 1.4 2008/02/15 18:16:48 james * *** empty log message *** * * Revision 1.3 2008/02/15 18:16:35 james * *** empty log message *** * * Revision 1.2 2008/02/15 16:48:56 james * *** empty log message *** * * Revision 1.1 2008/02/15 15:09:17 james * *** empty log message *** * */ static inline char * stop_wno_unused_on_rcsid (void) { return rcsid; } #define LOCK_ASCII #undef LOCK_BINARY #define STALE_CHECK_INTERVAL 10 #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined (HAVE_SYS_SYSMACROS_H) #include #endif #include "lockfile.h" extern void *xmalloc (size_t); Filelist * filelist_new (void) { Filelist *fl = (Filelist *) xmalloc (sizeof (Filelist)); fl->head = NULL; return fl; } void filelist_remove (Filelist * fl, Filelist_ent * fle) { Filelist_ent **ep; for (ep = &fl->head; *ep; ep = &((*ep)->next)) if (fle == *ep) break; if (!*ep) return; *ep = fle->next; free (fle); } void filelist_add (Filelist * fl, char *fn) { Filelist_ent *fle; int i = strlen (fn); if (i >= FILE_LIST_MAX_LEN) return; for (fle = fl->head; fle; fle = fle->next) if (!strcmp (fle->name, fn)) return; fle = xmalloc (sizeof (Filelist_ent)); strcpy (fle->name, fn); fle->next = fl->head; fl->head = fle; } void filelist_free (Filelist * fl) { while (fl->head) filelist_remove (fl, fl->head); free (fl); } void filelist_print (Filelist * fl, FILE * f) { Filelist_ent *fle; if (!fl) { fprintf (f, "(empty list)\n"); return; } for (fle = fl->head; fle; fle = fle->next) fprintf (f, "%s\n", fle->name); } static int chown_uucp (fd) int fd; { static int uuid = -1, ugid; struct passwd *pw; if (uuid < 0) { if ((pw = getpwnam ("uucp"))) { uuid = pw->pw_uid; ugid = pw->pw_gid; } else { return -1; } } return fchown (fd, uuid, ugid); } int lockfile_make (char *name) { char buf[1024], tmpfn[1024]; char *ptr; int fd; int i; strcpy (tmpfn, name); ptr = rindex (tmpfn, '/'); if (!ptr) return -1; ptr++; ptr += sprintf (ptr, "LTMP.%d", getpid ()); *ptr = 0; i = sprintf (buf, "%10d\n", getpid ()); unlink (tmpfn); fd = open (tmpfn, O_WRONLY | O_CREAT | O_TRUNC, 0444); if (fd < 0) { unlink (tmpfn); return -1; } int result; result = write (fd, buf, i); fchmod (fd, 044); #if 0 if (chown_uucp (fd)) { close (fd); unlink (tmpfn); return -1; } #else chown_uucp (fd); #endif close (fd); if (link (tmpfn, name) < 0) { unlink (tmpfn); return -1; } unlink (tmpfn); return 0; } void lockfile_add_places (Filelist * fl, char *leaf) { char buf[1024]; struct stat stbuf; char *lock_dirs[] = { "/var/lock/uucp", "/var/spool/lock", "/var/spool/uucp", "/etc/locks", "/usr/spool/uucp", "/var/spool/locks", "/usr/spool/lock", "/usr/spool/locks", "/usr/spool/uucp/LCK", "/var/lock" }; int i; for (i = 0; i < (sizeof (lock_dirs) / sizeof (char *)); ++i) { if (stat (lock_dirs[i], &stbuf)) continue; strcpy (buf, lock_dirs[i]); strcat (buf, "/"); strcat (buf, leaf); filelist_add (fl, buf); } } static void do_tedious_mangling (Filelist * fl, char *buf, char *ptr, char inv, int lower) { while (*ptr) { if (lower && (*ptr >= 'A') && (*ptr <= 'Z')) *ptr |= 32; if (*ptr == '/') *ptr = inv; ptr++; } lockfile_add_places (fl, buf); } void lockfile_regularize_and_add (Filelist * fl, char *leaf) { char buf[1024] = "LCK.."; char *ptr; if (*leaf == '/') leaf++; ptr = buf; while (*ptr) ptr++; strcpy (ptr, leaf); do_tedious_mangling (fl, buf, ptr, '_', 0); strcpy (ptr, leaf); do_tedious_mangling (fl, buf, ptr, '_', 1); strcpy (ptr, leaf); do_tedious_mangling (fl, buf, ptr, '.', 0); strcpy (ptr, leaf); do_tedious_mangling (fl, buf, ptr, '.', 1); } void lockfile_add_name_from_path (Filelist * fl, char *file) { char *ptr = file; if (*ptr == '/') ptr++; lockfile_regularize_and_add (fl, ptr); if (!strncmp (ptr, "dev/", 4)) { ptr += 4; lockfile_regularize_and_add (fl, ptr); } } void lockfile_add_name_from_dev (Filelist * fl, dev_t dev) { char buf[1024]; sprintf (buf, "LCK.%03d.%03d", major (dev), minor (dev)); lockfile_add_places (fl, buf); } void lockfile_check_dir_for_dev (Filelist * fl, char *dir, dev_t dev) { char buf[1024]; struct stat ent_stat; struct dirent *de; DIR *d; d = opendir (dir); if (!d) return; while ((de = readdir (d))) { strcpy (buf, dir); strcat (buf, de->d_name); if (stat (buf, &ent_stat)) continue; if (!S_ISCHR (ent_stat.st_mode)) continue; if (ent_stat.st_rdev != dev) continue; lockfile_add_name_from_path (fl, buf); } closedir (d); } Filelist * lockfile_make_list (char *device) { struct stat dev_stat; Filelist *ret = NULL; if (stat (device, &dev_stat)) return ret; if (!S_ISCHR (dev_stat.st_mode)) return ret; ret = filelist_new (); if (ret) { lockfile_add_name_from_dev (ret, dev_stat.st_rdev); lockfile_add_name_from_path (ret, device); lockfile_check_dir_for_dev (ret, "/dev/", dev_stat.st_rdev); lockfile_check_dir_for_dev (ret, "/dev/usb/", dev_stat.st_rdev); lockfile_check_dir_for_dev (ret, "/dev/tts/", dev_stat.st_rdev); } return ret; } static void remove_stale_lock (char *path) { int fd; int pid; char apid[20]; int length; fd = open (path, O_RDONLY); if (fd < 0) return; length = read (fd, apid, sizeof (apid) - 1); if (length < 0) length = 0; apid[length] = 0; pid = 0; if (length == sizeof (pid) || sscanf (apid, "%d", &pid) != 1 || pid == 0) { //pid=*(int *)(void *)(char *) apid; memcpy (&pid, apid, sizeof (pid)); #ifdef LOCK_ASCII fprintf (stderr, "compiled with ascii locks, found binary lock file (length=%d, pid=%d)!", length, pid); #endif } #ifdef LOCK_BINARY else { fprintf (stderr, "compiled with binary locks, found ascii lock file (length=%d, pid=%d)!", length, pid); } #endif close (fd); if ((kill (pid, 0) < 0) && (errno == ESRCH)) { fprintf (stderr, "removing stale lock file %s\n", path); unlink (path); } } void lockfile_remove_stale (Filelist * fl) { Filelist_ent *fle; struct stat buf; for (fle = fl->head; fle; fle = fle->next) { if (stat (fle->name, &buf)) continue; remove_stale_lock (fle->name); } } Filelist * lockfile_lock (Filelist * fl) { Filelist *ret; Filelist_ent *fle; ret = filelist_new (); if (ret) { lockfile_remove_stale (fl); for (fle = fl->head; fle; fle = fle->next) { if (lockfile_make (fle->name)) { fprintf (stderr, "Failed to get lockfile %s\n", fle->name); filelist_free (ret); return NULL; } filelist_add (ret, fle->name); } } return ret; } void lockfile_unlock (Filelist * fl) { while (fl->head) { unlink (fl->head->name); filelist_remove (fl, fl->head); } } /* If we have a passive lock, check noone has an */ /* active one, returns 1 if he does, 0 if he doesnt */ int serial_lock_check (Serial_lock * l) { Filelist_ent *fle; int locks_found = 0; struct stat buf; struct timeval now, dif; if (l->mode == SERIAL_LOCK_ACTIVE) return 0; for (fle = l->locks_to_check->head; fle; fle = fle->next) { if (!stat (fle->name, &buf)) locks_found++; } if (!locks_found) return 0; gettimeofday (&now, NULL); timersub (&now, &l->last_stale_purge, &dif); if (dif.tv_sec > STALE_CHECK_INTERVAL) { lockfile_remove_stale (l->locks_to_check); l->last_stale_purge = now; } return 1; } void serial_lock_free (Serial_lock * l) { if (!l) return; if (l->locks_held) { lockfile_unlock (l->locks_held); filelist_free (l->locks_held); } if (l->locks_to_check) { filelist_free (l->locks_to_check); } free (l); } Serial_lock * serial_lock_new (char *dev, int mode) { Filelist *fl = lockfile_make_list (dev); Serial_lock *l; if (!fl) return NULL; l = (Serial_lock *) xmalloc (sizeof (Serial_lock)); l->mode = mode; l->locks_to_check = fl; l->locks_held = NULL; memset (&l->last_stale_purge, 0, sizeof (l->last_stale_purge)); if (mode == SERIAL_LOCK_PASSIVE) return l; l->locks_held = lockfile_lock (l->locks_to_check); if (!l->locks_held) { serial_lock_free (l); return NULL; } return l; } #if 0 int main (int argc, char *argv[]) { Filelist *fl = lockfile_make_list ("/dev/ttyS0"); Filelist *fll; Filelist_ent *fle; filelist_print (fl, stdout); fll = lockfile_lock (fl); filelist_print (fll, stdout); } #endif