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, |
755 bool kill_unresponsive) { | 770 bool kill_unresponsive) { |
756 DCHECK_GE(timeout_seconds, 0); | 771 DCHECK_GE(retry_attempts, 0); |
| 772 DCHECK_GE(timeout.InMicroseconds(), 0); |
| 773 |
| 774 base::TimeDelta sleep_interval = timeout / 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|. |
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 socket_timeout = TimeDeltaToTimeVal(timeout); |
816 setsockopt(socket.fd(), SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)); | 834 setsockopt(socket.fd(), |
| 835 SOL_SOCKET, |
| 836 SO_SNDTIMEO, |
| 837 &socket_timeout, |
| 838 sizeof(socket_timeout)); |
817 | 839 |
818 // Found another process, prepare our command line | 840 // Found another process, prepare our command line |
819 // format is "START\0<current dir>\0<argv[0]>\0...\0<argv[n]>". | 841 // format is "START\0<current dir>\0<argv[0]>\0...\0<argv[n]>". |
820 std::string to_send(kStartToken); | 842 std::string to_send(kStartToken); |
821 to_send.push_back(kTokenDelimiter); | 843 to_send.push_back(kTokenDelimiter); |
822 | 844 |
823 base::FilePath current_dir; | 845 base::FilePath current_dir; |
824 if (!PathService::Get(base::DIR_CURRENT, ¤t_dir)) | 846 if (!PathService::Get(base::DIR_CURRENT, ¤t_dir)) |
825 return PROCESS_NONE; | 847 return PROCESS_NONE; |
826 to_send.append(current_dir.value()); | 848 to_send.append(current_dir.value()); |
(...skipping 12 matching lines...) Expand all Loading... |
839 return PROFILE_IN_USE; | 861 return PROFILE_IN_USE; |
840 return PROCESS_NONE; | 862 return PROCESS_NONE; |
841 } | 863 } |
842 | 864 |
843 if (shutdown(socket.fd(), SHUT_WR) < 0) | 865 if (shutdown(socket.fd(), SHUT_WR) < 0) |
844 PLOG(ERROR) << "shutdown() failed"; | 866 PLOG(ERROR) << "shutdown() failed"; |
845 | 867 |
846 // Read ACK message from the other process. It might be blocked for a certain | 868 // 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. | 869 // timeout, to make sure the other process has enough time to return ACK. |
848 char buf[kMaxACKMessageLength + 1]; | 870 char buf[kMaxACKMessageLength + 1]; |
849 ssize_t len = | 871 ssize_t len = ReadFromSocket(socket.fd(), buf, kMaxACKMessageLength, timeout); |
850 ReadFromSocket(socket.fd(), buf, kMaxACKMessageLength, timeout_seconds); | |
851 | 872 |
852 // Failed to read ACK, the other process might have been frozen. | 873 // Failed to read ACK, the other process might have been frozen. |
853 if (len <= 0) { | 874 if (len <= 0) { |
854 if (!kill_unresponsive || !KillProcessByLockPath()) | 875 if (!kill_unresponsive || !KillProcessByLockPath()) |
855 return PROFILE_IN_USE; | 876 return PROFILE_IN_USE; |
856 return PROCESS_NONE; | 877 return PROCESS_NONE; |
857 } | 878 } |
858 | 879 |
859 buf[len] = '\0'; | 880 buf[len] = '\0'; |
860 if (strncmp(buf, kShutdownToken, arraysize(kShutdownToken) - 1) == 0) { | 881 if (strncmp(buf, kShutdownToken, arraysize(kShutdownToken) - 1) == 0) { |
(...skipping 11 matching lines...) Expand all Loading... |
872 return PROCESS_NOTIFIED; | 893 return PROCESS_NOTIFIED; |
873 } | 894 } |
874 | 895 |
875 NOTREACHED() << "The other process returned unknown message: " << buf; | 896 NOTREACHED() << "The other process returned unknown message: " << buf; |
876 return PROCESS_NOTIFIED; | 897 return PROCESS_NOTIFIED; |
877 } | 898 } |
878 | 899 |
879 ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessOrCreate() { | 900 ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessOrCreate() { |
880 return NotifyOtherProcessWithTimeoutOrCreate( | 901 return NotifyOtherProcessWithTimeoutOrCreate( |
881 *CommandLine::ForCurrentProcess(), | 902 *CommandLine::ForCurrentProcess(), |
882 kTimeoutInSeconds); | 903 kRetryAttempts, |
| 904 base::TimeDelta::FromSeconds(kTimeoutInSeconds)); |
883 } | 905 } |
884 | 906 |
885 ProcessSingleton::NotifyResult | 907 ProcessSingleton::NotifyResult |
886 ProcessSingleton::NotifyOtherProcessWithTimeoutOrCreate( | 908 ProcessSingleton::NotifyOtherProcessWithTimeoutOrCreate( |
887 const CommandLine& command_line, | 909 const CommandLine& command_line, |
888 int timeout_seconds) { | 910 int retry_attempts, |
889 NotifyResult result = NotifyOtherProcessWithTimeout(command_line, | 911 const base::TimeDelta& timeout) { |
890 timeout_seconds, true); | 912 NotifyResult result = NotifyOtherProcessWithTimeout( |
| 913 command_line, retry_attempts, timeout, true); |
891 if (result != PROCESS_NONE) | 914 if (result != PROCESS_NONE) |
892 return result; | 915 return result; |
893 if (Create()) | 916 if (Create()) |
894 return PROCESS_NONE; | 917 return PROCESS_NONE; |
895 // If the Create() failed, try again to notify. (It could be that another | 918 // 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 | 919 // instance was starting at the same time and managed to grab the lock before |
897 // we did.) | 920 // we did.) |
898 // This time, we don't want to kill anything if we aren't successful, since we | 921 // 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. | 922 // aren't going to try to take over the lock ourselves. |
900 result = NotifyOtherProcessWithTimeout(command_line, timeout_seconds, false); | 923 result = NotifyOtherProcessWithTimeout( |
| 924 command_line, retry_attempts, timeout, false); |
901 if (result != PROCESS_NONE) | 925 if (result != PROCESS_NONE) |
902 return result; | 926 return result; |
903 | 927 |
904 return LOCK_ERROR; | 928 return LOCK_ERROR; |
905 } | 929 } |
906 | 930 |
907 void ProcessSingleton::OverrideCurrentPidForTesting(base::ProcessId pid) { | 931 void ProcessSingleton::OverrideCurrentPidForTesting(base::ProcessId pid) { |
908 current_pid_ = pid; | 932 current_pid_ = pid; |
909 } | 933 } |
910 | 934 |
(...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1045 } | 1069 } |
1046 | 1070 |
1047 void ProcessSingleton::KillProcess(int pid) { | 1071 void ProcessSingleton::KillProcess(int pid) { |
1048 // TODO(james.su@gmail.com): Is SIGKILL ok? | 1072 // TODO(james.su@gmail.com): Is SIGKILL ok? |
1049 int rv = kill(static_cast<base::ProcessHandle>(pid), SIGKILL); | 1073 int rv = kill(static_cast<base::ProcessHandle>(pid), SIGKILL); |
1050 // ESRCH = No Such Process (can happen if the other process is already in | 1074 // 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). | 1075 // progress of shutting down and finishes before we try to kill it). |
1052 DCHECK(rv == 0 || errno == ESRCH) << "Error killing process: " | 1076 DCHECK(rv == 0 || errno == ESRCH) << "Error killing process: " |
1053 << safe_strerror(errno); | 1077 << safe_strerror(errno); |
1054 } | 1078 } |
OLD | NEW |