| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 // On Linux, when the user tries to launch a second copy of chrome, we check | 5 // On Linux, when the user tries to launch a second copy of chrome, we check |
| 6 // for a socket in the user's profile directory. If the socket file is open we | 6 // for a socket in the user's profile directory. If the socket file is open we |
| 7 // send a message to the first chrome browser process with the current | 7 // send a message to the first chrome browser process with the current |
| 8 // directory and second process command line flags. The second process then | 8 // directory and second process command line flags. The second process then |
| 9 // exits. | 9 // exits. |
| 10 // | 10 // |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 85 #if defined(OS_LINUX) | 85 #if defined(OS_LINUX) |
| 86 #include "chrome/browser/ui/process_singleton_dialog_linux.h" | 86 #include "chrome/browser/ui/process_singleton_dialog_linux.h" |
| 87 #endif | 87 #endif |
| 88 | 88 |
| 89 #if defined(TOOLKIT_VIEWS) && defined(OS_LINUX) && !defined(OS_CHROMEOS) | 89 #if defined(TOOLKIT_VIEWS) && defined(OS_LINUX) && !defined(OS_CHROMEOS) |
| 90 #include "ui/views/linux_ui/linux_ui.h" | 90 #include "ui/views/linux_ui/linux_ui.h" |
| 91 #endif | 91 #endif |
| 92 | 92 |
| 93 using content::BrowserThread; | 93 using content::BrowserThread; |
| 94 | 94 |
| 95 const int ProcessSingleton::kTimeoutInSeconds; | |
| 96 | |
| 97 namespace { | 95 namespace { |
| 98 | 96 |
| 97 // Timeout for the current browser process to respond. 20 seconds should be |
| 98 // enough. |
| 99 const int kTimeoutInSeconds = 20; |
| 100 // Number of retries to notify the browser. 20 retries over 20 seconds = 1 try |
| 101 // per second. |
| 102 const int kRetryAttempts = 20; |
| 99 static bool g_disable_prompt; | 103 static bool g_disable_prompt; |
| 100 const char kStartToken[] = "START"; | 104 const char kStartToken[] = "START"; |
| 101 const char kACKToken[] = "ACK"; | 105 const char kACKToken[] = "ACK"; |
| 102 const char kShutdownToken[] = "SHUTDOWN"; | 106 const char kShutdownToken[] = "SHUTDOWN"; |
| 103 const char kTokenDelimiter = '\0'; | 107 const char kTokenDelimiter = '\0'; |
| 104 const int kMaxMessageLength = 32 * 1024; | 108 const int kMaxMessageLength = 32 * 1024; |
| 105 const int kMaxACKMessageLength = arraysize(kShutdownToken) - 1; | 109 const int kMaxACKMessageLength = arraysize(kShutdownToken) - 1; |
| 106 | 110 |
| 107 const char kLockDelimiter = '-'; | 111 const char kLockDelimiter = '-'; |
| 108 | 112 |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 151 } | 155 } |
| 152 PLOG(ERROR) << "write() failed"; | 156 PLOG(ERROR) << "write() failed"; |
| 153 return false; | 157 return false; |
| 154 } | 158 } |
| 155 bytes_written += rv; | 159 bytes_written += rv; |
| 156 } while (bytes_written < length); | 160 } while (bytes_written < length); |
| 157 | 161 |
| 158 return true; | 162 return true; |
| 159 } | 163 } |
| 160 | 164 |
| 161 // Wait a socket for read for a certain timeout in seconds. | 165 struct timeval TimeDeltaToTimeVal(const base::TimeDelta& delta) { |
| 166 struct timeval result; |
| 167 result.tv_sec = delta.InSeconds(); |
| 168 result.tv_usec = delta.InMicroseconds() % base::Time::kMicrosecondsPerSecond; |
| 169 return result; |
| 170 } |
| 171 |
| 172 // Wait a socket for read for a certain timeout. |
| 162 // Returns -1 if error occurred, 0 if timeout reached, > 0 if the socket is | 173 // Returns -1 if error occurred, 0 if timeout reached, > 0 if the socket is |
| 163 // ready for read. | 174 // ready for read. |
| 164 int WaitSocketForRead(int fd, int timeout) { | 175 int WaitSocketForRead(int fd, const base::TimeDelta& timeout) { |
| 165 fd_set read_fds; | 176 fd_set read_fds; |
| 166 struct timeval tv; | 177 struct timeval tv = TimeDeltaToTimeVal(timeout); |
| 167 | 178 |
| 168 FD_ZERO(&read_fds); | 179 FD_ZERO(&read_fds); |
| 169 FD_SET(fd, &read_fds); | 180 FD_SET(fd, &read_fds); |
| 170 tv.tv_sec = timeout; | |
| 171 tv.tv_usec = 0; | |
| 172 | 181 |
| 173 return HANDLE_EINTR(select(fd + 1, &read_fds, NULL, NULL, &tv)); | 182 return HANDLE_EINTR(select(fd + 1, &read_fds, NULL, NULL, &tv)); |
| 174 } | 183 } |
| 175 | 184 |
| 176 // Read a message from a socket fd, with an optional timeout in seconds. | 185 // Read a message from a socket fd, with an optional timeout. |
| 177 // If |timeout| <= 0 then read immediately. | 186 // If |timeout| <= 0 then read immediately. |
| 178 // Return number of bytes actually read, or -1 on error. | 187 // Return number of bytes actually read, or -1 on error. |
| 179 ssize_t ReadFromSocket(int fd, char *buf, size_t bufsize, int timeout) { | 188 ssize_t ReadFromSocket(int fd, |
| 180 if (timeout > 0) { | 189 char* buf, |
| 190 size_t bufsize, |
| 191 const base::TimeDelta& timeout) { |
| 192 if (timeout > base::TimeDelta()) { |
| 181 int rv = WaitSocketForRead(fd, timeout); | 193 int rv = WaitSocketForRead(fd, timeout); |
| 182 if (rv <= 0) | 194 if (rv <= 0) |
| 183 return rv; | 195 return rv; |
| 184 } | 196 } |
| 185 | 197 |
| 186 size_t bytes_read = 0; | 198 size_t bytes_read = 0; |
| 187 do { | 199 do { |
| 188 ssize_t rv = HANDLE_EINTR(read(fd, buf + bytes_read, bufsize - bytes_read)); | 200 ssize_t rv = HANDLE_EINTR(read(fd, buf + bytes_read, bufsize - bytes_read)); |
| 189 if (rv < 0) { | 201 if (rv < 0) { |
| 190 if (errno != EAGAIN && errno != EWOULDBLOCK) { | 202 if (errno != EAGAIN && errno != EWOULDBLOCK) { |
| (...skipping 546 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 737 cookie_path_ = user_data_dir.Append(chrome::kSingletonCookieFilename); | 749 cookie_path_ = user_data_dir.Append(chrome::kSingletonCookieFilename); |
| 738 | 750 |
| 739 kill_callback_ = base::Bind(&ProcessSingleton::KillProcess, | 751 kill_callback_ = base::Bind(&ProcessSingleton::KillProcess, |
| 740 base::Unretained(this)); | 752 base::Unretained(this)); |
| 741 } | 753 } |
| 742 | 754 |
| 743 ProcessSingleton::~ProcessSingleton() { | 755 ProcessSingleton::~ProcessSingleton() { |
| 744 } | 756 } |
| 745 | 757 |
| 746 ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcess() { | 758 ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcess() { |
| 747 return NotifyOtherProcessWithTimeout(*CommandLine::ForCurrentProcess(), | 759 return NotifyOtherProcessWithTimeout( |
| 748 kTimeoutInSeconds, | 760 *CommandLine::ForCurrentProcess(), |
| 749 true); | 761 kRetryAttempts, |
| 762 base::TimeDelta::FromSeconds(kTimeoutInSeconds), |
| 763 true); |
| 750 } | 764 } |
| 751 | 765 |
| 752 ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessWithTimeout( | 766 ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessWithTimeout( |
| 753 const CommandLine& cmd_line, | 767 const CommandLine& cmd_line, |
| 754 int timeout_seconds, | 768 int retry_attempts, |
| 769 const base::TimeDelta& timeout_interval, |
| 755 bool kill_unresponsive) { | 770 bool kill_unresponsive) { |
| 756 DCHECK_GE(timeout_seconds, 0); | 771 DCHECK_GE(retry_attempts, 0); |
| 772 DCHECK_GE(timeout_interval.InMicroseconds(), 0); |
| 773 |
| 774 base::TimeDelta sleep_interval = timeout_interval / retry_attempts; |
| 757 | 775 |
| 758 ScopedSocket socket; | 776 ScopedSocket socket; |
| 759 for (int retries = 0; retries <= timeout_seconds; ++retries) { | 777 for (int retries = 0; retries <= retry_attempts; ++retries) { |
| 760 // Try to connect to the socket. | 778 // Try to connect to the socket. |
| 761 if (ConnectSocket(&socket, socket_path_, cookie_path_)) | 779 if (ConnectSocket(&socket, socket_path_, cookie_path_)) |
| 762 break; | 780 break; |
| 763 | 781 |
| 764 // If we're in a race with another process, they may be in Create() and have | 782 // If we're in a race with another process, they may be in Create() and have |
| 765 // created the lock but not attached to the socket. So we check if the | 783 // created the lock but not attached to the socket. So we check if the |
| 766 // process with the pid from the lockfile is currently running and is a | 784 // process with the pid from the lockfile is currently running and is a |
| 767 // chrome browser. If so, we loop and try again for |timeout_seconds|. | 785 // chrome browser. If so, we loop and try again for |timeout_interval|. |
| 768 | 786 |
| 769 std::string hostname; | 787 std::string hostname; |
| 770 int pid; | 788 int pid; |
| 771 if (!ParseLockPath(lock_path_, &hostname, &pid)) { | 789 if (!ParseLockPath(lock_path_, &hostname, &pid)) { |
| 772 // No lockfile exists. | 790 // No lockfile exists. |
| 773 return PROCESS_NONE; | 791 return PROCESS_NONE; |
| 774 } | 792 } |
| 775 | 793 |
| 776 if (hostname.empty()) { | 794 if (hostname.empty()) { |
| 777 // Invalid lockfile. | 795 // Invalid lockfile. |
| (...skipping 17 matching lines...) Expand all Loading... |
| 795 return PROCESS_NONE; | 813 return PROCESS_NONE; |
| 796 } | 814 } |
| 797 | 815 |
| 798 if (IsSameChromeInstance(pid)) { | 816 if (IsSameChromeInstance(pid)) { |
| 799 // Orphaned lockfile (pid is part of same chrome instance we are, even | 817 // Orphaned lockfile (pid is part of same chrome instance we are, even |
| 800 // though we haven't tried to create a lockfile yet). | 818 // though we haven't tried to create a lockfile yet). |
| 801 UnlinkPath(lock_path_); | 819 UnlinkPath(lock_path_); |
| 802 return PROCESS_NONE; | 820 return PROCESS_NONE; |
| 803 } | 821 } |
| 804 | 822 |
| 805 if (retries == timeout_seconds) { | 823 if (retries == retry_attempts) { |
| 806 // Retries failed. Kill the unresponsive chrome process and continue. | 824 // Retries failed. Kill the unresponsive chrome process and continue. |
| 807 if (!kill_unresponsive || !KillProcessByLockPath()) | 825 if (!kill_unresponsive || !KillProcessByLockPath()) |
| 808 return PROFILE_IN_USE; | 826 return PROFILE_IN_USE; |
| 809 return PROCESS_NONE; | 827 return PROCESS_NONE; |
| 810 } | 828 } |
| 811 | 829 |
| 812 base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(1)); | 830 base::PlatformThread::Sleep(sleep_interval); |
| 813 } | 831 } |
| 814 | 832 |
| 815 timeval timeout = {timeout_seconds, 0}; | 833 timeval timeout = TimeDeltaToTimeVal(timeout_interval); |
| 816 setsockopt(socket.fd(), SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)); | 834 setsockopt(socket.fd(), SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)); |
| 817 | 835 |
| 818 // Found another process, prepare our command line | 836 // Found another process, prepare our command line |
| 819 // format is "START\0<current dir>\0<argv[0]>\0...\0<argv[n]>". | 837 // format is "START\0<current dir>\0<argv[0]>\0...\0<argv[n]>". |
| 820 std::string to_send(kStartToken); | 838 std::string to_send(kStartToken); |
| 821 to_send.push_back(kTokenDelimiter); | 839 to_send.push_back(kTokenDelimiter); |
| 822 | 840 |
| 823 base::FilePath current_dir; | 841 base::FilePath current_dir; |
| 824 if (!PathService::Get(base::DIR_CURRENT, ¤t_dir)) | 842 if (!PathService::Get(base::DIR_CURRENT, ¤t_dir)) |
| 825 return PROCESS_NONE; | 843 return PROCESS_NONE; |
| (...skipping 14 matching lines...) Expand all Loading... |
| 840 return PROCESS_NONE; | 858 return PROCESS_NONE; |
| 841 } | 859 } |
| 842 | 860 |
| 843 if (shutdown(socket.fd(), SHUT_WR) < 0) | 861 if (shutdown(socket.fd(), SHUT_WR) < 0) |
| 844 PLOG(ERROR) << "shutdown() failed"; | 862 PLOG(ERROR) << "shutdown() failed"; |
| 845 | 863 |
| 846 // Read ACK message from the other process. It might be blocked for a certain | 864 // Read ACK message from the other process. It might be blocked for a certain |
| 847 // timeout, to make sure the other process has enough time to return ACK. | 865 // timeout, to make sure the other process has enough time to return ACK. |
| 848 char buf[kMaxACKMessageLength + 1]; | 866 char buf[kMaxACKMessageLength + 1]; |
| 849 ssize_t len = | 867 ssize_t len = |
| 850 ReadFromSocket(socket.fd(), buf, kMaxACKMessageLength, timeout_seconds); | 868 ReadFromSocket(socket.fd(), buf, kMaxACKMessageLength, timeout_interval); |
| 851 | 869 |
| 852 // Failed to read ACK, the other process might have been frozen. | 870 // Failed to read ACK, the other process might have been frozen. |
| 853 if (len <= 0) { | 871 if (len <= 0) { |
| 854 if (!kill_unresponsive || !KillProcessByLockPath()) | 872 if (!kill_unresponsive || !KillProcessByLockPath()) |
| 855 return PROFILE_IN_USE; | 873 return PROFILE_IN_USE; |
| 856 return PROCESS_NONE; | 874 return PROCESS_NONE; |
| 857 } | 875 } |
| 858 | 876 |
| 859 buf[len] = '\0'; | 877 buf[len] = '\0'; |
| 860 if (strncmp(buf, kShutdownToken, arraysize(kShutdownToken) - 1) == 0) { | 878 if (strncmp(buf, kShutdownToken, arraysize(kShutdownToken) - 1) == 0) { |
| (...skipping 11 matching lines...) Expand all Loading... |
| 872 return PROCESS_NOTIFIED; | 890 return PROCESS_NOTIFIED; |
| 873 } | 891 } |
| 874 | 892 |
| 875 NOTREACHED() << "The other process returned unknown message: " << buf; | 893 NOTREACHED() << "The other process returned unknown message: " << buf; |
| 876 return PROCESS_NOTIFIED; | 894 return PROCESS_NOTIFIED; |
| 877 } | 895 } |
| 878 | 896 |
| 879 ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessOrCreate() { | 897 ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessOrCreate() { |
| 880 return NotifyOtherProcessWithTimeoutOrCreate( | 898 return NotifyOtherProcessWithTimeoutOrCreate( |
| 881 *CommandLine::ForCurrentProcess(), | 899 *CommandLine::ForCurrentProcess(), |
| 882 kTimeoutInSeconds); | 900 kRetryAttempts, |
| 901 base::TimeDelta::FromSeconds(kTimeoutInSeconds)); |
| 883 } | 902 } |
| 884 | 903 |
| 885 ProcessSingleton::NotifyResult | 904 ProcessSingleton::NotifyResult |
| 886 ProcessSingleton::NotifyOtherProcessWithTimeoutOrCreate( | 905 ProcessSingleton::NotifyOtherProcessWithTimeoutOrCreate( |
| 887 const CommandLine& command_line, | 906 const CommandLine& command_line, |
| 888 int timeout_seconds) { | 907 int retry_attempts, |
| 889 NotifyResult result = NotifyOtherProcessWithTimeout(command_line, | 908 const base::TimeDelta& timeout_interval) { |
| 890 timeout_seconds, true); | 909 NotifyResult result = NotifyOtherProcessWithTimeout( |
| 910 command_line, retry_attempts, timeout_interval, true); |
| 891 if (result != PROCESS_NONE) | 911 if (result != PROCESS_NONE) |
| 892 return result; | 912 return result; |
| 893 if (Create()) | 913 if (Create()) |
| 894 return PROCESS_NONE; | 914 return PROCESS_NONE; |
| 895 // If the Create() failed, try again to notify. (It could be that another | 915 // If the Create() failed, try again to notify. (It could be that another |
| 896 // instance was starting at the same time and managed to grab the lock before | 916 // instance was starting at the same time and managed to grab the lock before |
| 897 // we did.) | 917 // we did.) |
| 898 // This time, we don't want to kill anything if we aren't successful, since we | 918 // This time, we don't want to kill anything if we aren't successful, since we |
| 899 // aren't going to try to take over the lock ourselves. | 919 // aren't going to try to take over the lock ourselves. |
| 900 result = NotifyOtherProcessWithTimeout(command_line, timeout_seconds, false); | 920 result = NotifyOtherProcessWithTimeout( |
| 921 command_line, retry_attempts, timeout_interval, false); |
| 901 if (result != PROCESS_NONE) | 922 if (result != PROCESS_NONE) |
| 902 return result; | 923 return result; |
| 903 | 924 |
| 904 return LOCK_ERROR; | 925 return LOCK_ERROR; |
| 905 } | 926 } |
| 906 | 927 |
| 907 void ProcessSingleton::OverrideCurrentPidForTesting(base::ProcessId pid) { | 928 void ProcessSingleton::OverrideCurrentPidForTesting(base::ProcessId pid) { |
| 908 current_pid_ = pid; | 929 current_pid_ = pid; |
| 909 } | 930 } |
| 910 | 931 |
| (...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1045 } | 1066 } |
| 1046 | 1067 |
| 1047 void ProcessSingleton::KillProcess(int pid) { | 1068 void ProcessSingleton::KillProcess(int pid) { |
| 1048 // TODO(james.su@gmail.com): Is SIGKILL ok? | 1069 // TODO(james.su@gmail.com): Is SIGKILL ok? |
| 1049 int rv = kill(static_cast<base::ProcessHandle>(pid), SIGKILL); | 1070 int rv = kill(static_cast<base::ProcessHandle>(pid), SIGKILL); |
| 1050 // ESRCH = No Such Process (can happen if the other process is already in | 1071 // ESRCH = No Such Process (can happen if the other process is already in |
| 1051 // progress of shutting down and finishes before we try to kill it). | 1072 // progress of shutting down and finishes before we try to kill it). |
| 1052 DCHECK(rv == 0 || errno == ESRCH) << "Error killing process: " | 1073 DCHECK(rv == 0 || errno == ESRCH) << "Error killing process: " |
| 1053 << safe_strerror(errno); | 1074 << safe_strerror(errno); |
| 1054 } | 1075 } |
| OLD | NEW |