| OLD | NEW |
| 1 // Copyright 2012 The Chromium Authors. All rights reserved. | 1 // Copyright 2012 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 #include "chrome/browser/mac/relauncher.h" | 5 #include "chrome/browser/mac/relauncher.h" |
| 6 | 6 |
| 7 #include <ApplicationServices/ApplicationServices.h> | 7 #include <ApplicationServices/ApplicationServices.h> |
| 8 #include <AvailabilityMacros.h> | 8 #include <AvailabilityMacros.h> |
| 9 #include <crt_externs.h> | 9 #include <crt_externs.h> |
| 10 #include <dlfcn.h> | 10 #include <dlfcn.h> |
| 11 #include <string.h> | 11 #include <string.h> |
| 12 #include <sys/event.h> | 12 #include <sys/event.h> |
| 13 #include <sys/time.h> | 13 #include <sys/time.h> |
| 14 #include <sys/types.h> | 14 #include <sys/types.h> |
| 15 #include <unistd.h> | 15 #include <unistd.h> |
| 16 | 16 |
| 17 #include <string> | 17 #include <string> |
| 18 #include <vector> | 18 #include <vector> |
| 19 | 19 |
| 20 #include "base/basictypes.h" | 20 #include "base/basictypes.h" |
| 21 #include "base/file_util.h" | 21 #include "base/file_util.h" |
| 22 #include "base/files/scoped_file.h" |
| 22 #include "base/logging.h" | 23 #include "base/logging.h" |
| 23 #include "base/mac/mac_logging.h" | 24 #include "base/mac/mac_logging.h" |
| 24 #include "base/mac/mac_util.h" | 25 #include "base/mac/mac_util.h" |
| 25 #include "base/mac/scoped_cftyperef.h" | 26 #include "base/mac/scoped_cftyperef.h" |
| 26 #include "base/path_service.h" | 27 #include "base/path_service.h" |
| 27 #include "base/posix/eintr_wrapper.h" | 28 #include "base/posix/eintr_wrapper.h" |
| 28 #include "base/process/launch.h" | 29 #include "base/process/launch.h" |
| 29 #include "base/strings/stringprintf.h" | 30 #include "base/strings/stringprintf.h" |
| 30 #include "base/strings/sys_string_conversions.h" | 31 #include "base/strings/sys_string_conversions.h" |
| 31 #include "chrome/browser/mac/install_from_dmg.h" | 32 #include "chrome/browser/mac/install_from_dmg.h" |
| (...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 125 PLOG(ERROR) << "pipe"; | 126 PLOG(ERROR) << "pipe"; |
| 126 return false; | 127 return false; |
| 127 } | 128 } |
| 128 | 129 |
| 129 // The parent process will only use pipe_read_fd as the read side of the | 130 // The parent process will only use pipe_read_fd as the read side of the |
| 130 // pipe. It can close the write side as soon as the relauncher process has | 131 // pipe. It can close the write side as soon as the relauncher process has |
| 131 // forked off. The relauncher process will only use pipe_write_fd as the | 132 // forked off. The relauncher process will only use pipe_write_fd as the |
| 132 // write side of the pipe. In that process, the read side will be closed by | 133 // write side of the pipe. In that process, the read side will be closed by |
| 133 // base::LaunchApp because it won't be present in fd_map, and the write side | 134 // base::LaunchApp because it won't be present in fd_map, and the write side |
| 134 // will be remapped to kRelauncherSyncFD by fd_map. | 135 // will be remapped to kRelauncherSyncFD by fd_map. |
| 135 file_util::ScopedFD pipe_read_fd(&pipe_fds[0]); | 136 base::ScopedFD pipe_read_fd(pipe_fds[0]); |
| 136 file_util::ScopedFD pipe_write_fd(&pipe_fds[1]); | 137 base::ScopedFD pipe_write_fd(pipe_fds[1]); |
| 137 | 138 |
| 138 // Make sure kRelauncherSyncFD is a safe value. base::LaunchProcess will | 139 // Make sure kRelauncherSyncFD is a safe value. base::LaunchProcess will |
| 139 // preserve these three FDs in forked processes, so kRelauncherSyncFD should | 140 // preserve these three FDs in forked processes, so kRelauncherSyncFD should |
| 140 // not conflict with them. | 141 // not conflict with them. |
| 141 COMPILE_ASSERT(kRelauncherSyncFD != STDIN_FILENO && | 142 COMPILE_ASSERT(kRelauncherSyncFD != STDIN_FILENO && |
| 142 kRelauncherSyncFD != STDOUT_FILENO && | 143 kRelauncherSyncFD != STDOUT_FILENO && |
| 143 kRelauncherSyncFD != STDERR_FILENO, | 144 kRelauncherSyncFD != STDERR_FILENO, |
| 144 kRelauncherSyncFD_must_not_conflict_with_stdio_fds); | 145 kRelauncherSyncFD_must_not_conflict_with_stdio_fds); |
| 145 | 146 |
| 146 base::FileHandleMappingVector fd_map; | 147 base::FileHandleMappingVector fd_map; |
| 147 fd_map.push_back(std::make_pair(*pipe_write_fd, kRelauncherSyncFD)); | 148 fd_map.push_back(std::make_pair(pipe_write_fd.get(), kRelauncherSyncFD)); |
| 148 | 149 |
| 149 base::LaunchOptions options; | 150 base::LaunchOptions options; |
| 150 options.fds_to_remap = &fd_map; | 151 options.fds_to_remap = &fd_map; |
| 151 if (!base::LaunchProcess(relaunch_args, options, NULL)) { | 152 if (!base::LaunchProcess(relaunch_args, options, NULL)) { |
| 152 LOG(ERROR) << "base::LaunchProcess failed"; | 153 LOG(ERROR) << "base::LaunchProcess failed"; |
| 153 return false; | 154 return false; |
| 154 } | 155 } |
| 155 | 156 |
| 156 // The relauncher process is now starting up, or has started up. The | 157 // The relauncher process is now starting up, or has started up. The |
| 157 // original parent process continues. | 158 // original parent process continues. |
| 158 | 159 |
| 159 pipe_write_fd.reset(); // close(pipe_fds[1]); | 160 pipe_write_fd.reset(); // close(pipe_fds[1]); |
| 160 | 161 |
| 161 // Synchronize with the relauncher process. | 162 // Synchronize with the relauncher process. |
| 162 char read_char; | 163 char read_char; |
| 163 int read_result = HANDLE_EINTR(read(*pipe_read_fd, &read_char, 1)); | 164 int read_result = HANDLE_EINTR(read(pipe_read_fd.get(), &read_char, 1)); |
| 164 if (read_result != 1) { | 165 if (read_result != 1) { |
| 165 if (read_result < 0) { | 166 if (read_result < 0) { |
| 166 PLOG(ERROR) << "read"; | 167 PLOG(ERROR) << "read"; |
| 167 } else { | 168 } else { |
| 168 LOG(ERROR) << "read: unexpected result " << read_result; | 169 LOG(ERROR) << "read: unexpected result " << read_result; |
| 169 } | 170 } |
| 170 return false; | 171 return false; |
| 171 } | 172 } |
| 172 | 173 |
| 173 // Since a byte has been successfully read from the relauncher process, it's | 174 // Since a byte has been successfully read from the relauncher process, it's |
| 174 // guaranteed to have set up its kqueue monitoring this process for exit. | 175 // guaranteed to have set up its kqueue monitoring this process for exit. |
| 175 // It's safe to exit now. | 176 // It's safe to exit now. |
| 176 return true; | 177 return true; |
| 177 } | 178 } |
| 178 | 179 |
| 179 namespace { | 180 namespace { |
| 180 | 181 |
| 181 // In the relauncher process, performs the necessary synchronization steps | 182 // In the relauncher process, performs the necessary synchronization steps |
| 182 // with the parent by setting up a kqueue to watch for it to exit, writing a | 183 // with the parent by setting up a kqueue to watch for it to exit, writing a |
| 183 // byte to the pipe, and then waiting for the exit notification on the kqueue. | 184 // byte to the pipe, and then waiting for the exit notification on the kqueue. |
| 184 // If anything fails, this logs a message and returns immediately. In those | 185 // If anything fails, this logs a message and returns immediately. In those |
| 185 // situations, it can be assumed that something went wrong with the parent | 186 // situations, it can be assumed that something went wrong with the parent |
| 186 // process and the best recovery approach is to attempt relaunch anyway. | 187 // process and the best recovery approach is to attempt relaunch anyway. |
| 187 void RelauncherSynchronizeWithParent() { | 188 void RelauncherSynchronizeWithParent() { |
| 188 // file_util::ScopedFD needs something non-const to operate on. | 189 base::ScopedFD relauncher_sync_fd(kRelauncherSyncFD); |
| 189 int relauncher_sync_fd = kRelauncherSyncFD; | |
| 190 file_util::ScopedFD relauncher_sync_fd_closer(&relauncher_sync_fd); | |
| 191 | 190 |
| 192 int parent_pid = getppid(); | 191 int parent_pid = getppid(); |
| 193 | 192 |
| 194 // PID 1 identifies init. launchd, that is. launchd never starts the | 193 // PID 1 identifies init. launchd, that is. launchd never starts the |
| 195 // relauncher process directly, having this parent_pid means that the parent | 194 // relauncher process directly, having this parent_pid means that the parent |
| 196 // already exited and launchd "inherited" the relauncher as its child. | 195 // already exited and launchd "inherited" the relauncher as its child. |
| 197 // There's no reason to synchronize with launchd. | 196 // There's no reason to synchronize with launchd. |
| 198 if (parent_pid == 1) { | 197 if (parent_pid == 1) { |
| 199 LOG(ERROR) << "unexpected parent_pid"; | 198 LOG(ERROR) << "unexpected parent_pid"; |
| 200 return; | 199 return; |
| 201 } | 200 } |
| 202 | 201 |
| 203 // Set up a kqueue to monitor the parent process for exit. | 202 // Set up a kqueue to monitor the parent process for exit. |
| 204 int kq = kqueue(); | 203 base::ScopedFD kq(kqueue()); |
| 205 if (kq < 0) { | 204 if (!kq.is_valid()) { |
| 206 PLOG(ERROR) << "kqueue"; | 205 PLOG(ERROR) << "kqueue"; |
| 207 return; | 206 return; |
| 208 } | 207 } |
| 209 file_util::ScopedFD kq_closer(&kq); | |
| 210 | 208 |
| 211 struct kevent change = { 0 }; | 209 struct kevent change = { 0 }; |
| 212 EV_SET(&change, parent_pid, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL); | 210 EV_SET(&change, parent_pid, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL); |
| 213 if (kevent(kq, &change, 1, NULL, 0, NULL) == -1) { | 211 if (kevent(kq.get(), &change, 1, NULL, 0, NULL) == -1) { |
| 214 PLOG(ERROR) << "kevent (add)"; | 212 PLOG(ERROR) << "kevent (add)"; |
| 215 return; | 213 return; |
| 216 } | 214 } |
| 217 | 215 |
| 218 // Write a '\0' character to the pipe. | 216 // Write a '\0' character to the pipe. |
| 219 if (HANDLE_EINTR(write(relauncher_sync_fd, "", 1)) != 1) { | 217 if (HANDLE_EINTR(write(relauncher_sync_fd.get(), "", 1)) != 1) { |
| 220 PLOG(ERROR) << "write"; | 218 PLOG(ERROR) << "write"; |
| 221 return; | 219 return; |
| 222 } | 220 } |
| 223 | 221 |
| 224 // Up until now, the parent process was blocked in a read waiting for the | 222 // Up until now, the parent process was blocked in a read waiting for the |
| 225 // write above to complete. The parent process is now free to exit. Wait for | 223 // write above to complete. The parent process is now free to exit. Wait for |
| 226 // that to happen. | 224 // that to happen. |
| 227 struct kevent event; | 225 struct kevent event; |
| 228 int events = kevent(kq, NULL, 0, &event, 1, NULL); | 226 int events = kevent(kq.get(), NULL, 0, &event, 1, NULL); |
| 229 if (events != 1) { | 227 if (events != 1) { |
| 230 if (events < 0) { | 228 if (events < 0) { |
| 231 PLOG(ERROR) << "kevent (monitor)"; | 229 PLOG(ERROR) << "kevent (monitor)"; |
| 232 } else { | 230 } else { |
| 233 LOG(ERROR) << "kevent (monitor): unexpected result " << events; | 231 LOG(ERROR) << "kevent (monitor): unexpected result " << events; |
| 234 } | 232 } |
| 235 return; | 233 return; |
| 236 } | 234 } |
| 237 | 235 |
| 238 if (event.filter != EVFILT_PROC || | 236 if (event.filter != EVFILT_PROC || |
| (...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 372 if (!dmg_bsd_device_name.empty()) { | 370 if (!dmg_bsd_device_name.empty()) { |
| 373 EjectAndTrashDiskImage(dmg_bsd_device_name); | 371 EjectAndTrashDiskImage(dmg_bsd_device_name); |
| 374 } | 372 } |
| 375 | 373 |
| 376 return 0; | 374 return 0; |
| 377 } | 375 } |
| 378 | 376 |
| 379 } // namespace internal | 377 } // namespace internal |
| 380 | 378 |
| 381 } // namespace mac_relauncher | 379 } // namespace mac_relauncher |
| OLD | NEW |