Home | History | Annotate | Download | only in common
      1 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/common/multi_process_lock.h"
      6 
      7 #include <stdio.h>
      8 #include <sys/socket.h>
      9 #include <sys/un.h>
     10 #include <unistd.h>
     11 
     12 #include "base/eintr_wrapper.h"
     13 #include "base/logging.h"
     14 
     15 class MultiProcessLockLinux : public MultiProcessLock {
     16  public:
     17   explicit MultiProcessLockLinux(const std::string& name)
     18       : name_(name), fd_(-1) { }
     19 
     20   virtual ~MultiProcessLockLinux() {
     21     if (fd_ != -1) {
     22       Unlock();
     23     }
     24   }
     25 
     26   virtual bool TryLock() {
     27     if (fd_ != -1) {
     28       DLOG(ERROR) << "MultiProcessLock is already locked - " << name_;
     29       return true;
     30     }
     31 
     32     if (name_.length() > MULTI_PROCESS_LOCK_NAME_MAX_LEN) {
     33       LOG(ERROR) << "Socket name too long (" << name_.length()
     34                  << " > " << MULTI_PROCESS_LOCK_NAME_MAX_LEN << ") - " << name_;
     35       return false;
     36     }
     37 
     38     struct sockaddr_un address;
     39 
     40     // +1 for terminator, +1 for 0 in position 0 that makes it an
     41     // abstract named socket.
     42     // If this assert fails it is because sockaddr_un.sun_path size has been
     43     // redefined and MULTI_PROCESS_LOCK_NAME_MAX_LEN can change accordingly.
     44     COMPILE_ASSERT(sizeof(address.sun_path)
     45         == MULTI_PROCESS_LOCK_NAME_MAX_LEN + 2, sun_path_size_changed);
     46 
     47     memset(&address, 0, sizeof(address));
     48     int print_length = snprintf(&address.sun_path[1],
     49                                 MULTI_PROCESS_LOCK_NAME_MAX_LEN + 1,
     50                                 "%s", name_.c_str());
     51 
     52     if (print_length < 0 ||
     53         print_length > static_cast<int>(MULTI_PROCESS_LOCK_NAME_MAX_LEN)) {
     54       PLOG(ERROR) << "Couldn't create sun_path - " << name_;
     55       return false;
     56     }
     57 
     58     // Must set the first character of the path to something non-zero
     59     // before we call SUN_LEN which depends on strcpy working.
     60     address.sun_path[0] = '@';
     61     size_t length = SUN_LEN(&address);
     62 
     63     // Reset the first character of the path back to zero so that
     64     // bind returns an abstract name socket.
     65     address.sun_path[0] = 0;
     66     address.sun_family = AF_LOCAL;
     67 
     68     int socket_fd = socket(AF_LOCAL, SOCK_STREAM, 0);
     69     if (socket_fd < 0) {
     70       PLOG(ERROR) << "Couldn't create socket - " << name_;
     71       return false;
     72     }
     73 
     74     if (bind(socket_fd,
     75              reinterpret_cast<sockaddr *>(&address),
     76              length) == 0) {
     77       fd_ = socket_fd;
     78       return true;
     79     } else {
     80       PLOG(ERROR) << "Couldn't bind socket - "
     81                   << &(address.sun_path[1])
     82                   << " Length: " << length;
     83       if (HANDLE_EINTR(close(socket_fd)) < 0) {
     84         PLOG(ERROR) << "close";
     85       }
     86       return false;
     87     }
     88   }
     89 
     90   virtual void Unlock() {
     91     if (fd_ == -1) {
     92       DLOG(ERROR) << "Over-unlocked MultiProcessLock - " << name_;
     93       return;
     94     }
     95     if (HANDLE_EINTR(close(fd_)) < 0) {
     96       PLOG(ERROR) << "close";
     97     }
     98     fd_ = -1;
     99   }
    100 
    101  private:
    102   std::string name_;
    103   int fd_;
    104   DISALLOW_COPY_AND_ASSIGN(MultiProcessLockLinux);
    105 };
    106 
    107 MultiProcessLock* MultiProcessLock::Create(const std::string &name) {
    108   return new MultiProcessLockLinux(name);
    109 }
    110