Index: src/main.c
===================================================================
--- src/main.c	(revision 4173)
+++ src/main.c	(working copy)
@@ -24,10 +24,18 @@
  * Main file
  */
 
+#define _GNU_SOURCE // for getline()
+#include <stdio.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <assert.h>
+
 #ifdef HAVE_CONFIG_H
 #  include "config.h"
 #endif
 
+#include <string.h>
+
 #include <glib.h>
 #include <glib/gi18n.h>
 #include <glib/gprintf.h>
@@ -112,70 +120,238 @@
 	signal(SIGUSR1, handler_sigusr1);
 }
 
-/**
- * Tests for a lock file and returns the pid of any process already running
- */
-pid_t
-test_lock(gchar *file_name)
+static gint rm_stale_lockdir(const gchar* tmp, const gchar *file_name, pid_t stalepid, gboolean acquired) {
+      // shall we delete their lock?
+      // if they dont exist, rm /tmp/mediaplayer/theirpid
+      // renameo on top of empty dirs rm's the old dir.
+      char *theircmdline;
+      FILE * fp;
+      char * line = NULL;
+      size_t len = 0;
+      gboolean dellock;
+      /** XXX must be better way than parsing proc...*/
+      if(-1 == asprintf(&theircmdline, "%s/%d/cmdline", "/proc", stalepid)) return -1;
+
+      fp = fopen(theircmdline, "r");
+
+      if (fp == NULL) {
+        if(errno == ENOENT) {
+          g_printf(_("Couldn't find process in dir %s\n"), theircmdline);
+          // other process has exited.
+          dellock = TRUE;
+        } else {
+          perror("Error checking to see if old process still exists");
+          return -1;
+        }
+      } else {
+        int found = 0;
+        if(-1 == getdelim(&line, &len, 0, fp)) {
+          if(feof(fp)) {
+            g_printerr(_("Unexpected eof reading from /%s/%d/cmdline.  Assuming other mediaplayer died\n"),"/proc",stalepid);
+          } else {
+            perror("Error reading from /proc/?/cmdline");
+            return -1;
+          }
+        } else {
+          // XXX gross / slow; looking at cmdline is a hack.
+          for(int i = 0; i < strlen(line); i++) {
+            if(!strncmp(line+i, "openmoko-mediaplayer", strlen("openmoko-mediaplayer"))) {
+              found = 1;
+              break;
+            }
+          }
+        }
+        if(found) {
+          // Their name contains "openmoko-mediaplayer"; the lock is valid.
+          g_printf(_("found another openmoko-mediaplayer process\n"));
+          dellock = FALSE;
+        } else {
+          // Some interloper has the pid of a dead mediaplayer.
+          g_printf(_("%s doesn't contain with %s\n"),line,"openmoko-mediaplayer");
+          dellock = TRUE;
+        }
+        assert(fp);
+        if(-1 == fclose(fp)) { perror("couldn't close file in proc"); return -1; }
+      }
+
+      if(dellock) {
+        char * theirlockfile;
+        if(acquired) {
+          if(-1 == asprintf(&theirlockfile, "%s/%s/%d",tmp,file_name,stalepid)) return -1;
+        } else {
+          if(-1 == asprintf(&theirlockfile, "%s/%s.%d/%d",tmp,file_name,stalepid,stalepid)) return -1;
+        }
+        if(-1 == unlink(theirlockfile)) {
+          g_printf(_("removing stale lockfile %s\n"),theirlockfile);
+          if(errno != ENOENT) {
+            perror("Couldn't remove stale lockfile");
+            return -1;
+          }
+        }
+        free(theirlockfile);
+        char * theirlockdir;
+        if(acquired) {
+          if(-1 == asprintf(&theirlockdir, "%s/%s",tmp,file_name)) return -1;
+        } else {
+          if(-1 == asprintf(&theirlockdir, "%s/%s.%d",tmp,file_name,stalepid)) return -1;
+        }
+        if(-1 == rmdir(theirlockfile)) {
+          g_printf(_("removing stale lockdir %s\n"),theirlockdir);
+          if(errno != ENOENT) {
+            perror("Couldn't remove stale lockdir");
+            return -1;
+          }
+        }
+        free(theirlockdir);
+      }
+
+      free(theircmdline);
+      return dellock;
+}
+
+static int test_and_set_lockdir(gchar *file_name, pid_t * theirpid)
 {
-	gint fd;
-	struct flock fl;
+  const char tmp[] = "/tmp";
+  char * dirname = 0, * mydirname = 0, * mylockfilename = 0;
+  pid_t mypid = getpid();
 
-	fd = open(file_name, O_WRONLY, S_IWUSR);
+  if(-1 == asprintf(&dirname, "%s/%s", tmp, file_name)) return -1;
+  if(-1 == asprintf(&mydirname,"%s/%s.%d", tmp, file_name, (int)mypid)) return -1;
+  if(-1 == asprintf(&mylockfilename,"%s/%s.%d/%d", tmp, file_name, (int)mypid, (int)mypid)) return -1;
 
-	if (fd < 0)
-	{
-		if (errno == ENOENT)
-		{
-			return 0;
-		} else {
-			g_printerr("Lock file test failed!\n");
-			return -1;
-		}
-	}
+  mypid = getpid();
 
-	fl.l_type = F_WRLCK;
-	fl.l_whence = SEEK_SET;
-	fl.l_start = 0;
-	fl.l_len = 0;
+  if(-1 == mkdir(mydirname, 0700)) {
+    if(errno == EEXIST) {
+      // XXX make sure it's set up properly for us.  If not, return -1.
+      if(-1 == unlink(mylockfilename)) {
+        if(errno == ENOENT) {
+          // The lockfile didn't exist.  Good enough.
+        } else {
+          return -1;
+        }
+      }
+      // Now the lockfile doesn't exist but the dir does.
+    } else {
+      g_printerr(_("error creating: %s\n"), mydirname);
+      perror("Couldn't create lock directory");
+      return -1;
+    }
+  }
 
-	if (fcntl(fd, F_GETLK, &fl) < 0)
-	{
-		close(fd);
-		return -1;
-	}
+  /* creat a file so linux refueses to silently rename on top of our
+     dir. */
+  int fd = creat(mylockfilename, 0700);
+  if(-1 == fd) {
+    perror("Cdouldn't create lock file");
+    return -1;
+  }
+  close(fd);
 
-	close(fd);
+  /* Now we have a directory that only we get to use, since its name
+     ends with our pid. */
 
-	return (fl.l_type == F_UNLCK) ? 0 : fl.l_pid;
-}
+  *theirpid = 0; /* XXX assumes there is no zero pid process */
 
-/**
- * Sets a lock file
- */
-void
-set_lock(gchar *file_name)
-{
-	gint fd;
-	struct flock fl;
+  g_printf(_("rename %s -> %s : "),mydirname, dirname);
+  while(-1 == rename(mydirname,dirname)) {
+    g_printf(_("failed\n"));
+    /* An error occurred while renaming directory.
 
-	fd = open(file_name, O_WRONLY|O_CREAT, S_IWUSR);
-	if (fd < 0)
-	{
-		g_printerr("Failed opening lock file\n");
-		return;
-	}
+       In the common case, this is because someone else owns the lock.
+     */
+    DIR * olddir = opendir(dirname);
+    struct dirent * d;
+    if(!olddir) {
+      perror("Couldn't open existing lock dir");
+      return -1;
+    }
+    errno = 0;
 
-	fl.l_type = F_WRLCK;
-	fl.l_whence = SEEK_SET;
-	fl.l_start = 0;
-	fl.l_len = 0;
+    *theirpid = 1;
 
-	if (fcntl(fd, F_SETLK, &fl) < 0)
-	{
-		g_printerr("Failed writing lock file\n");
-		close(fd);
-	}
+    while((d = readdir(olddir))) {
+      if(!((!strcmp(d->d_name,".")) || (!strcmp(d->d_name,"..")))) {
+        // parse int
+        char * end;
+        errno = 0;
+        *theirpid = strtoul(d->d_name, &end, 10);
+        if(errno || strlen(d->d_name) != end - d->d_name) {
+          if(errno) {
+            perror("Found junk in lockdir");
+          } else {
+            g_printerr(_("Found junk in lockdir %d != %d"), strlen(d->d_name), end - d->d_name);
+          }
+          return -1;
+        } else {
+          break;
+        }
+      }
+      errno = 0;
+    }
+    if(errno) {
+      perror("Couldn't read from existing lock dir");
+    }
+    if(-1 == closedir(olddir)) {
+      perror("Couldn't close existing lock dir");
+      return -1;
+    }
+
+    int rm_ret = rm_stale_lockdir(tmp,file_name, *theirpid, TRUE);
+    if(rm_ret == -1) {
+      return -1;
+    } else if (rm_ret == 1) {
+      // deleted stale lock
+      *theirpid = 0;
+    } else {
+      // found valid lock
+      assert(*theirpid);
+      break;
+    }
+    // Deleting their lock doesn't imply that we get to obtain the
+    // lock; go back up to the top, and retry the rename.
+    g_printf(_("rename %s -> %s : "),mydirname, dirname);
+  }
+  if(*theirpid) {
+    // unlink our directory
+    if(-1 == unlink(mylockfilename)) { perror("deleting lockfile"); }
+    if(-1 == rmdir(mydirname)) {perror("deleting lockdir"); }
+    // don't bother with error checking; there's nothing to to about
+    // it if we fail.
+  } else {
+    g_printf(_("obtained lock\n"));
+    // delete stale locks
+    DIR * tmpdir = opendir("/tmp");
+    struct dirent * d;
+    if(!tmpdir) {
+      perror("Couldn't open /tmp to scan for stale lockfiles");
+      return -1;
+    }
+    size_t len = strlen(file_name);
+    errno = 0;
+
+    while((d = readdir(tmpdir))) {
+      if(!strncmp(file_name,d->d_name,len) && strlen(d->d_name) > len) {
+        g_printf(_("potentially stale lock %s\n"), d->d_name);
+        // looks like a incomplete lockfile.
+        *theirpid = strtoul(d->d_name+len+1,0,10);
+        if(-1 == rm_stale_lockdir(tmp,file_name,*theirpid,FALSE)) {
+          return -1;
+        }
+      }
+    }
+    if(-1 == closedir(tmpdir)) {
+      perror("Failed to close /tmp after scanning for stale lockfiles");
+      return -1;
+    }
+    *theirpid = 0;
+  }
+
+  free(dirname);
+  free(mydirname);
+  free(mylockfilename);
+
+  return *theirpid ? 1 : 0;  // 0 iff we have the lock.
 }
 
 /**
@@ -186,25 +362,20 @@
 gboolean
 check_lock()
 {
-	gchar *lock_file = "/tmp/mediaplayer.lock";
-	pid_t pid;
+	gchar *lock_file = "mediaplayer.lockdir";
+	pid_t pid = getpid();
 
-	pid = test_lock(lock_file);
-	if (pid > 0)
-	{
-		g_printf(_("Already running an instance of the Media Player, bringing that to the front instead.\n"));
-
-		// Was signaling the other process successful?
-		if (kill(pid, SIGUSR1) == 0)
-		{
-			return TRUE;
-		} else {
-			g_printf(_("Previous instance invalid, proceeding with regular startup.\n"));
-		}
+        int alreadylocked = test_and_set_lockdir(lock_file, &pid);
+        if(alreadylocked == -1) {
+          //error
+          return TRUE;
+        } else if(alreadylocked == 1) {
+          g_printf(_("Already running an instance of the Media Player, bringing that to the front instead.\n"));
+          // Don't care if signaling the other process was successful, so don't check return value.
+          kill(pid, SIGUSR1);
+          return TRUE;
 	}
-
-	set_lock(lock_file);
-	return FALSE;
+        return FALSE;
 }
 
 /**
