Ticket #1714: mediaplayer-atomic-lock.patch

File mediaplayer-atomic-lock.patch, 9.9 KB (added by hedora, 5 years ago)

Fixed case when /proc/?/cmdline has unexpected eof by assuming the other process died.

  • src/main.c

     
    2424 * Main file 
    2525 */ 
    2626 
     27#define _GNU_SOURCE // for getline() 
     28#include <stdio.h> 
     29#include <sys/types.h> 
     30#include <dirent.h> 
     31#include <assert.h> 
     32 
    2733#ifdef HAVE_CONFIG_H 
    2834#  include "config.h" 
    2935#endif 
    3036 
     37#include <string.h> 
     38 
    3139#include <glib.h> 
    3240#include <glib/gi18n.h> 
    3341#include <glib/gprintf.h> 
     
    112120        signal(SIGUSR1, handler_sigusr1); 
    113121} 
    114122 
    115 /** 
    116  * Tests for a lock file and returns the pid of any process already running 
    117  */ 
    118 pid_t 
    119 test_lock(gchar *file_name) 
     123static gint rm_stale_lockdir(const gchar* tmp, const gchar *file_name, pid_t stalepid, gboolean acquired) { 
     124      // shall we delete their lock? 
     125      // if they dont exist, rm /tmp/mediaplayer/theirpid 
     126      // renameo on top of empty dirs rm's the old dir. 
     127      char *theircmdline; 
     128      FILE * fp; 
     129      char * line = NULL; 
     130      size_t len = 0; 
     131      gboolean dellock; 
     132      /** XXX must be better way than parsing proc...*/ 
     133      if(-1 == asprintf(&theircmdline, "%s/%d/cmdline", "/proc", stalepid)) return -1; 
     134 
     135      fp = fopen(theircmdline, "r"); 
     136 
     137      if (fp == NULL) { 
     138        if(errno == ENOENT) { 
     139          g_printf(_("Couldn't find process in dir %s\n"), theircmdline); 
     140          // other process has exited. 
     141          dellock = TRUE; 
     142        } else { 
     143          perror("Error checking to see if old process still exists"); 
     144          return -1; 
     145        } 
     146      } else { 
     147        int found = 0; 
     148        if(-1 == getdelim(&line, &len, 0, fp)) { 
     149          if(feof(fp)) { 
     150            g_printerr(_("Unexpected eof reading from /%s/%d/cmdline.  Assuming other mediaplayer died\n"),"/proc",stalepid); 
     151          } else { 
     152            perror("Error reading from /proc/?/cmdline"); 
     153            return -1; 
     154          } 
     155        } else { 
     156          // XXX gross / slow; looking at cmdline is a hack. 
     157          for(int i = 0; i < strlen(line); i++) { 
     158            if(!strncmp(line+i, "openmoko-mediaplayer", strlen("openmoko-mediaplayer"))) { 
     159              found = 1; 
     160              break; 
     161            } 
     162          } 
     163        } 
     164        if(found) { 
     165          // Their name contains "openmoko-mediaplayer"; the lock is valid. 
     166          g_printf(_("found another openmoko-mediaplayer process\n")); 
     167          dellock = FALSE; 
     168        } else { 
     169          // Some interloper has the pid of a dead mediaplayer. 
     170          g_printf(_("%s doesn't contain with %s\n"),line,"openmoko-mediaplayer"); 
     171          dellock = TRUE; 
     172        } 
     173        assert(fp); 
     174        if(-1 == fclose(fp)) { perror("couldn't close file in proc"); return -1; } 
     175      } 
     176 
     177      if(dellock) { 
     178        char * theirlockfile; 
     179        if(acquired) { 
     180          if(-1 == asprintf(&theirlockfile, "%s/%s/%d",tmp,file_name,stalepid)) return -1; 
     181        } else { 
     182          if(-1 == asprintf(&theirlockfile, "%s/%s.%d/%d",tmp,file_name,stalepid,stalepid)) return -1; 
     183        } 
     184        if(-1 == unlink(theirlockfile)) { 
     185          g_printf(_("removing stale lockfile %s\n"),theirlockfile); 
     186          if(errno != ENOENT) { 
     187            perror("Couldn't remove stale lockfile"); 
     188            return -1; 
     189          } 
     190        } 
     191        free(theirlockfile); 
     192        char * theirlockdir; 
     193        if(acquired) { 
     194          if(-1 == asprintf(&theirlockdir, "%s/%s",tmp,file_name)) return -1; 
     195        } else { 
     196          if(-1 == asprintf(&theirlockdir, "%s/%s.%d",tmp,file_name,stalepid)) return -1; 
     197        } 
     198        if(-1 == rmdir(theirlockfile)) { 
     199          g_printf(_("removing stale lockdir %s\n"),theirlockdir); 
     200          if(errno != ENOENT) { 
     201            perror("Couldn't remove stale lockdir"); 
     202            return -1; 
     203          } 
     204        } 
     205        free(theirlockdir); 
     206      } 
     207 
     208      free(theircmdline); 
     209      return dellock; 
     210} 
     211 
     212static int test_and_set_lockdir(gchar *file_name, pid_t * theirpid) 
    120213{ 
    121         gint fd; 
    122         struct flock fl; 
     214  const char tmp[] = "/tmp"; 
     215  char * dirname = 0, * mydirname = 0, * mylockfilename = 0; 
     216  pid_t mypid = getpid(); 
    123217 
    124         fd = open(file_name, O_WRONLY, S_IWUSR); 
     218  if(-1 == asprintf(&dirname, "%s/%s", tmp, file_name)) return -1; 
     219  if(-1 == asprintf(&mydirname,"%s/%s.%d", tmp, file_name, (int)mypid)) return -1; 
     220  if(-1 == asprintf(&mylockfilename,"%s/%s.%d/%d", tmp, file_name, (int)mypid, (int)mypid)) return -1; 
    125221 
    126         if (fd < 0) 
    127         { 
    128                 if (errno == ENOENT) 
    129                 { 
    130                         return 0; 
    131                 } else { 
    132                         g_printerr("Lock file test failed!\n"); 
    133                         return -1; 
    134                 } 
    135         } 
     222  mypid = getpid(); 
    136223 
    137         fl.l_type = F_WRLCK; 
    138         fl.l_whence = SEEK_SET; 
    139         fl.l_start = 0; 
    140         fl.l_len = 0; 
     224  if(-1 == mkdir(mydirname, 0700)) { 
     225    if(errno == EEXIST) { 
     226      // XXX make sure it's set up properly for us.  If not, return -1. 
     227      if(-1 == unlink(mylockfilename)) { 
     228        if(errno == ENOENT) { 
     229          // The lockfile didn't exist.  Good enough. 
     230        } else { 
     231          return -1; 
     232        } 
     233      } 
     234      // Now the lockfile doesn't exist but the dir does. 
     235    } else { 
     236      g_printerr(_("error creating: %s\n"), mydirname); 
     237      perror("Couldn't create lock directory"); 
     238      return -1; 
     239    } 
     240  } 
    141241 
    142         if (fcntl(fd, F_GETLK, &fl) < 0) 
    143         { 
    144                 close(fd); 
    145                 return -1; 
    146         } 
     242  /* creat a file so linux refueses to silently rename on top of our 
     243     dir. */ 
     244  int fd = creat(mylockfilename, 0700); 
     245  if(-1 == fd) { 
     246    perror("Cdouldn't create lock file"); 
     247    return -1; 
     248  } 
     249  close(fd); 
    147250 
    148         close(fd); 
     251  /* Now we have a directory that only we get to use, since its name 
     252     ends with our pid. */ 
    149253 
    150         return (fl.l_type == F_UNLCK) ? 0 : fl.l_pid; 
    151 } 
     254  *theirpid = 0; /* XXX assumes there is no zero pid process */ 
    152255 
    153 /** 
    154  * Sets a lock file 
    155  */ 
    156 void 
    157 set_lock(gchar *file_name) 
    158 { 
    159         gint fd; 
    160         struct flock fl; 
     256  g_printf(_("rename %s -> %s : "),mydirname, dirname); 
     257  while(-1 == rename(mydirname,dirname)) { 
     258    g_printf(_("failed\n")); 
     259    /* An error occurred while renaming directory. 
    161260 
    162         fd = open(file_name, O_WRONLY|O_CREAT, S_IWUSR); 
    163         if (fd < 0) 
    164         { 
    165                 g_printerr("Failed opening lock file\n"); 
    166                 return; 
    167         } 
     261       In the common case, this is because someone else owns the lock. 
     262     */ 
     263    DIR * olddir = opendir(dirname); 
     264    struct dirent * d; 
     265    if(!olddir) { 
     266      perror("Couldn't open existing lock dir"); 
     267      return -1; 
     268    } 
     269    errno = 0; 
    168270 
    169         fl.l_type = F_WRLCK; 
    170         fl.l_whence = SEEK_SET; 
    171         fl.l_start = 0; 
    172         fl.l_len = 0; 
     271    *theirpid = 1; 
    173272 
    174         if (fcntl(fd, F_SETLK, &fl) < 0) 
    175         { 
    176                 g_printerr("Failed writing lock file\n"); 
    177                 close(fd); 
    178         } 
     273    while((d = readdir(olddir))) { 
     274      if(!((!strcmp(d->d_name,".")) || (!strcmp(d->d_name,"..")))) { 
     275        // parse int 
     276        char * end; 
     277        errno = 0; 
     278        *theirpid = strtoul(d->d_name, &end, 10); 
     279        if(errno || strlen(d->d_name) != end - d->d_name) { 
     280          if(errno) { 
     281            perror("Found junk in lockdir"); 
     282          } else { 
     283            g_printerr(_("Found junk in lockdir %d != %d"), strlen(d->d_name), end - d->d_name); 
     284          } 
     285          return -1; 
     286        } else { 
     287          break; 
     288        } 
     289      } 
     290      errno = 0; 
     291    } 
     292    if(errno) { 
     293      perror("Couldn't read from existing lock dir"); 
     294    } 
     295    if(-1 == closedir(olddir)) { 
     296      perror("Couldn't close existing lock dir"); 
     297      return -1; 
     298    } 
     299 
     300    int rm_ret = rm_stale_lockdir(tmp,file_name, *theirpid, TRUE); 
     301    if(rm_ret == -1) { 
     302      return -1; 
     303    } else if (rm_ret == 1) { 
     304      // deleted stale lock 
     305      *theirpid = 0; 
     306    } else { 
     307      // found valid lock 
     308      assert(*theirpid); 
     309      break; 
     310    } 
     311    // Deleting their lock doesn't imply that we get to obtain the 
     312    // lock; go back up to the top, and retry the rename. 
     313    g_printf(_("rename %s -> %s : "),mydirname, dirname); 
     314  } 
     315  if(*theirpid) { 
     316    // unlink our directory 
     317    if(-1 == unlink(mylockfilename)) { perror("deleting lockfile"); } 
     318    if(-1 == rmdir(mydirname)) {perror("deleting lockdir"); } 
     319    // don't bother with error checking; there's nothing to to about 
     320    // it if we fail. 
     321  } else { 
     322    g_printf(_("obtained lock\n")); 
     323    // delete stale locks 
     324    DIR * tmpdir = opendir("/tmp"); 
     325    struct dirent * d; 
     326    if(!tmpdir) { 
     327      perror("Couldn't open /tmp to scan for stale lockfiles"); 
     328      return -1; 
     329    } 
     330    size_t len = strlen(file_name); 
     331    errno = 0; 
     332 
     333    while((d = readdir(tmpdir))) { 
     334      if(!strncmp(file_name,d->d_name,len) && strlen(d->d_name) > len) { 
     335        g_printf(_("potentially stale lock %s\n"), d->d_name); 
     336        // looks like a incomplete lockfile. 
     337        *theirpid = strtoul(d->d_name+len+1,0,10); 
     338        if(-1 == rm_stale_lockdir(tmp,file_name,*theirpid,FALSE)) { 
     339          return -1; 
     340        } 
     341      } 
     342    } 
     343    if(-1 == closedir(tmpdir)) { 
     344      perror("Failed to close /tmp after scanning for stale lockfiles"); 
     345      return -1; 
     346    } 
     347    *theirpid = 0; 
     348  } 
     349 
     350  free(dirname); 
     351  free(mydirname); 
     352  free(mylockfilename); 
     353 
     354  return *theirpid ? 1 : 0;  // 0 iff we have the lock. 
    179355} 
    180356 
    181357/** 
     
    186362gboolean 
    187363check_lock() 
    188364{ 
    189         gchar *lock_file = "/tmp/mediaplayer.lock"; 
    190         pid_t pid; 
     365        gchar *lock_file = "mediaplayer.lockdir"; 
     366        pid_t pid = getpid(); 
    191367 
    192         pid = test_lock(lock_file); 
    193         if (pid > 0) 
    194         { 
    195                 g_printf(_("Already running an instance of the Media Player, bringing that to the front instead.\n")); 
    196  
    197                 // Was signaling the other process successful? 
    198                 if (kill(pid, SIGUSR1) == 0) 
    199                 { 
    200                         return TRUE; 
    201                 } else { 
    202                         g_printf(_("Previous instance invalid, proceeding with regular startup.\n")); 
    203                 } 
     368        int alreadylocked = test_and_set_lockdir(lock_file, &pid); 
     369        if(alreadylocked == -1) { 
     370          //error 
     371          return TRUE; 
     372        } else if(alreadylocked == 1) { 
     373          g_printf(_("Already running an instance of the Media Player, bringing that to the front instead.\n")); 
     374          // Don't care if signaling the other process was successful, so don't check return value. 
     375          kill(pid, SIGUSR1); 
     376          return TRUE; 
    204377        } 
    205  
    206         set_lock(lock_file); 
    207         return FALSE; 
     378        return FALSE; 
    208379} 
    209380 
    210381/**