Changeset 4644

Show
Ignore:
Timestamp:
10/01/06 22:07:18 (2 years ago)
Author:
erijo
Message:

Redo the "only one instance of Licq" logic.

Instead of checking if the pid in licq.pid is running, use advisory locking. First we grab a write lock on licq.pid. If it fails, this means that licq is already running, otherwise we're alone. By never closing the file, we keep the lock until licq is killed (normally or abnormally) when the kernel will close all open files and remove all locks.

Fixes #1353.

Location:
trunk/licq/src
Files:
2 modified

Legend:

Unmodified
Added
Removed
  • trunk/licq/src/licq.cpp

    r4620 r4644  
    149149/*-----End of OpenSSL code-------------------------------------------------*/ 
    150150 
     151/** 
     152 * Prints the @a error to stderr (by means of gLog), and if the user is running 
     153 * X, tries to show a dialog with the error. 
     154 */ 
     155static void DisplayFatalError(const char* error) 
     156{ 
     157  gLog.Error(error); 
     158 
     159  // Try to show the error if we're running X 
     160  if (getenv("DISPLAY") != NULL) 
     161  { 
     162    pid_t child = fork(); 
     163    if (child == 0) 
     164    { 
     165      // execlp never returns (except on error). 
     166      execlp("kdialog", "kdialog", "--error", error, NULL); 
     167      execlp("Xdialog", "Xdialog", "--title", "Error", "--msgbox", error, "0", "0", NULL); 
     168      execlp("xmessage", "xmessage", "-center", error, NULL); 
     169 
     170      exit(EXIT_FAILURE); 
     171    } 
     172    else if (child != -1) 
     173    { 
     174      int status; 
     175      waitpid(child, &status, 0); 
     176    } 
     177  } 
     178} 
    151179 
    152180/*-----Helper functions for CLicq::UpgradeLicq-----------------------------*/ 
     
    180208  pthread_mutex_init(&mutex_plugins, NULL); 
    181209  pthread_mutex_init(&mutex_protoplugins, NULL); 
    182   m_bDeletePID = true; 
    183210 
    184211  //FIXME ICQ should be put into its own plugin. 
     
    307334 
    308335  // Check pid 
     336  // We do this by acquiring a write lock on the pid file and never closing the file. 
     337  // When Licq is killed (normally or abnormally) the file will be closed by the operating 
     338  // system and the lock released. 
    309339  char szConf[MAX_FILENAME_LEN], szKey[32]; 
    310340  snprintf(szConf, MAX_FILENAME_LEN, "%s/licq.pid", BASE_DIR); 
    311341  szConf[MAX_FILENAME_LEN - 1] = '\0'; 
    312   FILE *fs = fopen(szConf, "r"); 
    313   if (fs != NULL) 
    314   { 
    315     fgets(szKey, 32, fs); 
    316     pid_t pid = atol(szKey); 
    317     if (pid != 0) 
    318     { 
    319       if (kill(pid, 0) == -1) { 
    320         gLog.Warn(tr("%sLicq: Ignoring stale lockfile (pid %d)\n"), L_WARNxSTR, pid); 
     342 
     343  // Never close pidFile! 
     344  int pidFile = open(szConf, O_RDWR | O_CREAT); 
     345  if (pidFile < 0) 
     346  { 
     347    // We couldn't open (or create) the file for writing. 
     348    // If the file doesn't exists we continue without lockfile protection. 
     349    // If it does exist, we bail out. 
     350    struct stat buf; 
     351    if (stat(szConf, &buf) < 0 && errno == ENOENT) 
     352    { 
     353      gLog.Warn(tr("%sLicq: %s cannot be opened for writing.\n" 
     354                   "%s      skipping lockfile protection.\n"), 
     355                L_WARNxSTR, szConf, L_BLANKxSTR); 
     356    } 
     357    else 
     358    { 
     359      const size_t ERR_SIZE = 511; 
     360      char error[ERR_SIZE + 1]; 
     361 
     362      // Try to read the pid of running Licq instance. 
     363      FILE* fs = fopen(szConf, "r"); 
     364      if (fs != NULL) 
     365      { 
     366          fgets(szKey, 32, fs); 
     367          pid_t pid = atol(szKey); 
     368 
     369          snprintf(error, ERR_SIZE, 
     370                   tr("%sLicq: Already running at pid %d.\n" 
     371                      "%s      Kill process or remove %s.\n"), 
     372                   L_ERRORxSTR, pid, L_BLANKxSTR, szConf); 
     373          fclose(fs); 
    321374      } 
    322375      else 
    323376      { 
    324         const size_t ERR_SIZE = 511; 
    325         char error[ERR_SIZE + 1]; 
    326377        snprintf(error, ERR_SIZE, 
    327             tr("%sLicq: Already running at pid %d.\n" 
    328                "%s      Kill process or remove %s.\n"), 
    329             L_ERRORxSTR, pid, L_BLANKxSTR, szConf); 
    330         error[ERR_SIZE] = '\0'; 
    331  
    332         gLog.Error(error); 
    333         m_bDeletePID = false; 
    334  
    335         // Try to show the error if we're running X 
    336         if (getenv("DISPLAY") != NULL) 
    337         { 
    338           pid_t child = fork(); 
    339           if (child == 0) 
    340           { 
    341             // execlp never returns (except on error). 
    342             execlp("kdialog", "kdialog", "--error", error, NULL); 
    343             execlp("Xdialog", "Xdialog", "--title", "Error", "--msgbox", error, "0", "0", NULL); 
    344             execlp("xmessage", "xmessage", "-center", error, NULL); 
    345  
    346             exit(EXIT_FAILURE); 
    347           } 
    348           else if (child != -1) 
    349           { 
    350             int status; 
    351             waitpid(child, &status, 0); 
    352           } 
    353         } 
    354  
    355         return false; 
     378                 tr("%sLicq: Unabled to determine pid of running Licq instance.\n"), 
     379                 L_ERRORxSTR); 
    356380      } 
    357     } 
    358     fclose(fs); 
    359   } 
    360   fs = fopen(szConf, "w"); 
    361   if (fs != NULL) 
    362   { 
    363     chmod(szConf, 00600); 
    364     fprintf(fs, "%d\n", getpid()); 
    365     fclose(fs); 
     381 
     382      error[ERR_SIZE] = '\0'; 
     383      DisplayFatalError(error); 
     384 
     385      return false; 
     386    } 
    366387  } 
    367388  else 
    368     gLog.Warn(tr("%sLicq: %s cannot be opened for writing.\n" 
    369                  "%s      skipping lockfile protection.\n"), 
    370               L_WARNxSTR, szConf, L_BLANKxSTR); 
     389  { 
     390    struct flock lock; 
     391    lock.l_type = F_WRLCK; // Write lock is exclusive lock 
     392    lock.l_whence = SEEK_SET; 
     393    lock.l_start = 0; 
     394    lock.l_len = 0; // Lock file through to the end of file 
     395 
     396    if (fcntl(pidFile, F_SETLK, &lock) != 0) 
     397    { 
     398      // Failed to get the lock => Licq is already running 
     399      const size_t ERR_SIZE = 511; 
     400      char error[ERR_SIZE + 1]; 
     401 
     402      if (fcntl(pidFile, F_GETLK, &lock) != 0) 
     403      { 
     404        snprintf(error, ERR_SIZE, 
     405                tr("%sLicq: Unabled to determine pid of running Licq instance.\n"), 
     406                L_ERRORxSTR); 
     407      } 
     408      else 
     409      { 
     410        snprintf(error, ERR_SIZE, 
     411                tr("%sLicq: Already running at pid %d.\n"), 
     412                L_ERRORxSTR, lock.l_pid); 
     413      } 
     414 
     415      error[ERR_SIZE] = '\0'; 
     416      DisplayFatalError(error); 
     417 
     418      return false; 
     419    } 
     420    else 
     421    { 
     422      // Save our pid in the file 
     423      ftruncate(pidFile, 0); 
     424      int size = snprintf(szKey, 32, "%d\n", getpid()); 
     425      write(pidFile, szKey, (size > 32 ? 32 : size)); 
     426    } 
     427  } 
    371428 
    372429  // Open the config file 
     
    502559  // Kill the daemon 
    503560  if (licqDaemon != NULL) delete licqDaemon; 
    504  
    505   // Remove the lock file 
    506   if (m_bDeletePID) 
    507   { 
    508     char szConf[MAX_FILENAME_LEN]; 
    509     snprintf(szConf, MAX_FILENAME_LEN, "%s/licq.pid", BASE_DIR); 
    510     remove(szConf); 
    511   } 
    512561} 
    513562 
     
    11741223  pthread_join(*t, NULL); 
    11751224 
    1176   // Remove the pid flag 
    1177   char sz[MAX_FILENAME_LEN]; 
    1178   snprintf(sz, MAX_FILENAME_LEN, "%s/licq.pid", BASE_DIR); 
    1179   sz[MAX_FILENAME_LEN - 1] = '\0'; 
    1180   remove(sz); 
    1181  
    11821225  return list_plugins.size(); 
    11831226} 
  • trunk/licq/src/licq.h

    r4526 r4644  
    4646  ProtoPluginsList list_protoplugins; 
    4747  pthread_mutex_t mutex_protoplugins; 
    48   bool m_bDeletePID; 
    4948 
    5049friend class CICQDaemon;