| OLD | NEW |
| (Empty) | |
| 1 // Copyright (c) 2006-2009 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 "base/zygote_manager.h" |
| 6 |
| 7 #if defined(OS_LINUX) |
| 8 |
| 9 #include <errno.h> |
| 10 #include <fcntl.h> |
| 11 #include <poll.h> |
| 12 #include <stdlib.h> |
| 13 #include <sys/file.h> // for flock() |
| 14 #include <sys/stat.h> |
| 15 #include <sys/uio.h> // for struct iovec |
| 16 #include <sys/wait.h> |
| 17 #include <unistd.h> // for ssize_t |
| 18 |
| 19 #include <string> |
| 20 |
| 21 #include "base/eintr_wrapper.h" |
| 22 #include "base/file_descriptor_shuffle.h" |
| 23 #include "base/file_util.h" |
| 24 #include "base/logging.h" |
| 25 #include "base/process_util.h" |
| 26 #include "base/reserved_file_descriptors.h" |
| 27 #include "base/singleton.h" |
| 28 |
| 29 using file_util::Delete; |
| 30 |
| 31 // See comment below, where sigaction is called. |
| 32 static void SIGCHLDHandler(int signal) { |
| 33 } |
| 34 |
| 35 namespace base { |
| 36 |
| 37 const char ZygoteManager::kZMagic[] = "zygo"; |
| 38 |
| 39 ZygoteManager::~ZygoteManager() { |
| 40 if (server_fd_ != -1) { |
| 41 close(server_fd_); |
| 42 server_fd_ = -1; |
| 43 } |
| 44 if (client_fd_ != -1) { |
| 45 close(client_fd_); |
| 46 client_fd_ = -1; |
| 47 } |
| 48 if (lockfd_ != -1) { |
| 49 close(lockfd_); |
| 50 lockfd_ = -1; |
| 51 } |
| 52 if (canary_fd_ != -1) { |
| 53 // Wake up the poll() in ReadAndHandleMessage() |
| 54 close(canary_fd_); |
| 55 canary_fd_ = -1; |
| 56 } |
| 57 } |
| 58 |
| 59 // Runs in client process |
| 60 ZygoteManager* ZygoteManager::Get() { |
| 61 static bool checked = false; |
| 62 static bool enabled = false; |
| 63 if (!checked) { |
| 64 enabled = (getenv("ENABLE_ZYGOTE_MANAGER") != NULL); |
| 65 // sanity check - make sure all the places that relaunch chrome |
| 66 // have been zygotified. |
| 67 if (enabled) |
| 68 DCHECK(getenv("ZYGOTE_MANAGER_STARTED") == NULL) |
| 69 << "fork/exec used instead of LongFork"; |
| 70 (void) setenv("ZYGOTE_MANAGER_STARTED", "1", 1); |
| 71 checked = true; |
| 72 } |
| 73 if (!enabled) |
| 74 return NULL; |
| 75 return Singleton<ZygoteManager>::get(); |
| 76 } |
| 77 |
| 78 // Runs in zygote manager process |
| 79 int ZygoteManager::UnpickleHeader(const Pickle& reply, void** iter) { |
| 80 std::string magic; |
| 81 if (!reply.ReadString(iter, &magic) || magic != std::string(kZMagic)) { |
| 82 LOG(ERROR) << "reply didn't start with " << kZMagic; |
| 83 return ZMBAD; |
| 84 } |
| 85 pid_t clientpid = (pid_t) -1; |
| 86 if (!reply.ReadInt(iter, &clientpid)) { |
| 87 LOG(ERROR) << "Can't read client pid"; |
| 88 return ZMBAD; |
| 89 } |
| 90 if (clientpid != getpid()) { |
| 91 LOG(ERROR) << "got client pid " << clientpid << ", expected " << getpid(); |
| 92 DCHECK(clientpid == getpid()); |
| 93 return ZMBAD; |
| 94 } |
| 95 int kind; |
| 96 if (!reply.ReadInt(iter, &kind)) { |
| 97 LOG(ERROR) << "can't read kind"; |
| 98 return ZMBAD; |
| 99 } |
| 100 return kind; |
| 101 } |
| 102 |
| 103 // Runs in client process (only used in unit test) |
| 104 bool ZygoteManager::Ping(base::TimeDelta* delta) { |
| 105 if (client_fd_ == -1) |
| 106 return false; |
| 107 |
| 108 Pickle pickle; |
| 109 pickle.WriteString(kZMagic); |
| 110 pickle.WriteInt(getpid()); |
| 111 pickle.WriteInt(ZMPING); |
| 112 |
| 113 int bytes_sent; |
| 114 int bytes_read = -1; |
| 115 |
| 116 TimeTicks time_sent = TimeTicks::HighResNow(); |
| 117 |
| 118 // Lock fork server, send the pickle, wait for the reply, unlock |
| 119 if (flock(lockfd_, LOCK_EX)) |
| 120 LOG(ERROR) << "flock failed, errno " << errno; |
| 121 bytes_sent = HANDLE_EINTR(write(client_fd_, |
| 122 const_cast<void *>(pickle.data()), pickle.size())); |
| 123 if (bytes_sent > 0) { |
| 124 bytes_read = HANDLE_EINTR(read(client_fd_, msg_buf_, kMAX_MSG_LEN)); |
| 125 } |
| 126 if (flock(lockfd_, LOCK_UN)) |
| 127 LOG(ERROR) << "flock failed, errno " << errno; |
| 128 |
| 129 TimeTicks time_received = TimeTicks::HighResNow(); |
| 130 |
| 131 if (bytes_sent < 1) { |
| 132 LOG(ERROR) << "Can't send to zm, errno " << errno; |
| 133 return false; |
| 134 } |
| 135 if (bytes_read < 1) { |
| 136 LOG(ERROR) << "Can't get from zm, errno " << errno; |
| 137 return false; |
| 138 } |
| 139 |
| 140 // Unpickle the reply |
| 141 Pickle reply(msg_buf_, bytes_read); |
| 142 void* iter = NULL; |
| 143 int kind = UnpickleHeader(reply, &iter); |
| 144 if (kind != ZMPINGED) { |
| 145 LOG(ERROR) << "reply wrong kind " << kind; |
| 146 return false; |
| 147 } |
| 148 *delta = TimeTicks::HighResNow() - time_sent; |
| 149 LOG(INFO) << "Round trip time in microseconds: " << delta->InMicroseconds(); |
| 150 return true; |
| 151 } |
| 152 |
| 153 // Runs in zygote manager process |
| 154 void ZygoteManager::PingHandler(const Pickle& request, void* iter, |
| 155 Pickle* reply, std::vector<std::string>** newargv) { |
| 156 reply->WriteInt(ZMPINGED); |
| 157 } |
| 158 |
| 159 // Runs in browser process, called only by base::ForkApp() |
| 160 pid_t ZygoteManager::LongFork(const std::vector<std::string>& argv, |
| 161 const file_handle_mapping_vector& fds_to_remap) { |
| 162 if (client_fd_ == -1) |
| 163 return -1; |
| 164 |
| 165 Pickle pickle; |
| 166 |
| 167 // Encode the arguments and the desired remote fd numbers in the pickle, |
| 168 // and the fds in a separate buffer |
| 169 pickle.WriteString(kZMagic); |
| 170 pickle.WriteInt(getpid()); |
| 171 pickle.WriteInt(ZMFORK); |
| 172 pickle.WriteInt(argv.size()); |
| 173 std::vector<std::string>::const_iterator argi; |
| 174 for (argi = argv.begin(); argi != argv.end(); ++argi) |
| 175 pickle.WriteString(*argi); |
| 176 pickle.WriteInt(fds_to_remap.size()); |
| 177 |
| 178 // Wrap the pickle and the fds together in a msghdr |
| 179 ::msghdr msg; |
| 180 memset(&msg, 0, sizeof(msg)); |
| 181 struct iovec iov; |
| 182 msg.msg_iov = &iov; |
| 183 msg.msg_iovlen = 1; |
| 184 msg.msg_control = cmsg_buf_; |
| 185 msg.msg_controllen = CMSG_LEN(sizeof(int) * fds_to_remap.size()); |
| 186 struct cmsghdr* cmsg; |
| 187 cmsg = CMSG_FIRSTHDR(&msg); |
| 188 cmsg->cmsg_level = SOL_SOCKET; |
| 189 cmsg->cmsg_type = SCM_RIGHTS; |
| 190 cmsg->cmsg_len = msg.msg_controllen; |
| 191 int* wire_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg)); |
| 192 for (size_t i = 0; i < fds_to_remap.size(); i++) { |
| 193 pickle.WriteInt(fds_to_remap[i].second); |
| 194 wire_fds[i] = fds_to_remap[i].first; |
| 195 } |
| 196 iov.iov_base = const_cast<void *>(pickle.data()); |
| 197 iov.iov_len = pickle.size(); |
| 198 |
| 199 int bytes_sent; |
| 200 int bytes_read = -1; |
| 201 |
| 202 // Lock fork server, send the pickle, wait for the reply, unlock |
| 203 if (flock(lockfd_, LOCK_EX)) |
| 204 LOG(ERROR) << "flock failed, errno " << errno; |
| 205 bytes_sent = HANDLE_EINTR(sendmsg(client_fd_, &msg, MSG_WAITALL)); |
| 206 if (bytes_sent > 0) { |
| 207 bytes_read = HANDLE_EINTR(read(client_fd_, msg_buf_, kMAX_MSG_LEN)); |
| 208 } |
| 209 if (flock(lockfd_, LOCK_UN)) |
| 210 LOG(ERROR) << "flock failed, errno " << errno; |
| 211 |
| 212 if (bytes_sent < 1) { |
| 213 LOG(ERROR) << "Can't send to zm, errno " << errno << ", fd " << client_fd_; |
| 214 return (pid_t) -1; |
| 215 } |
| 216 if (bytes_read < 1) { |
| 217 LOG(ERROR) << "Can't get from zm, errno " << errno; |
| 218 return (pid_t) -1; |
| 219 } |
| 220 |
| 221 // Unpickle the reply |
| 222 Pickle reply(msg_buf_, bytes_read); |
| 223 void* iter = NULL; |
| 224 int kind = UnpickleHeader(reply, &iter); |
| 225 if (kind != ZMFORKED) { |
| 226 LOG(ERROR) << "reply wrong kind " << kind; |
| 227 return (pid_t) -1; |
| 228 } |
| 229 pid_t newpid = (pid_t) -1; |
| 230 int pid_errno; |
| 231 if (!reply.ReadInt(&iter, &newpid) || !reply.ReadInt(&iter, &pid_errno)) { |
| 232 LOG(ERROR) << "fork failed, can't read pid/errno"; |
| 233 return (pid_t) -1; |
| 234 } |
| 235 if ((newpid == (pid_t) -1) || pid_errno != 0) { |
| 236 LOG(ERROR) << "fork failed, pid " << newpid << ", errno " << pid_errno; |
| 237 return (pid_t) -1; |
| 238 } |
| 239 return newpid; |
| 240 } |
| 241 |
| 242 // Runs in zygote manager process |
| 243 bool ZygoteManager::LongForkHandler(const Pickle& request, void* iter, |
| 244 Pickle* reply, std::vector<std::string>** newargv, |
| 245 const int wire_fds[], int num_wire_fds) { |
| 246 file_handle_mapping_vector fds_to_remap; |
| 247 pid_t childpid; |
| 248 |
| 249 reply->WriteInt(ZMFORKED); |
| 250 |
| 251 // Unpickle commandline for new child |
| 252 std::vector<std::string>* argv = new std::vector<std::string>; |
| 253 int argc; |
| 254 request.ReadInt(&iter, &argc); |
| 255 for (int i = 0; i < argc; i++) { |
| 256 std::string arg; |
| 257 if (!request.ReadString(&iter, &arg)) { |
| 258 LOG(ERROR) << "can't read arg?"; |
| 259 goto error_reply; |
| 260 } |
| 261 argv->push_back(arg); |
| 262 } |
| 263 // Unpickle file descriptor map for new child |
| 264 int numfds; |
| 265 request.ReadInt(&iter, &numfds); |
| 266 DCHECK(numfds == num_wire_fds); |
| 267 if (numfds != num_wire_fds) { |
| 268 LOG(ERROR) << "numfds " << numfds << " != num_wire_fds " << num_wire_fds; |
| 269 goto error_reply; |
| 270 } |
| 271 for (int i = 0; i < numfds; i++) { |
| 272 int fd; |
| 273 if (!request.ReadInt(&iter, &fd)) { |
| 274 LOG(ERROR) << "can't read fd?"; |
| 275 goto error_reply; |
| 276 } |
| 277 fds_to_remap.push_back(std::pair<int, int>(wire_fds[i], fd)); |
| 278 } |
| 279 |
| 280 // Mitosis! |
| 281 childpid = fork(); |
| 282 |
| 283 if (childpid != 0) { |
| 284 // parent |
| 285 // first off, close our copy of the child's file descriptors |
| 286 for (file_handle_mapping_vector::const_iterator |
| 287 it = fds_to_remap.begin(); it != fds_to_remap.end(); ++it) { |
| 288 close(it->first); |
| 289 } |
| 290 |
| 291 // Finish formatting the reply |
| 292 reply->WriteInt(childpid); |
| 293 if (childpid == (pid_t) -1) { |
| 294 reply->WriteInt(errno); |
| 295 return false; |
| 296 } else { |
| 297 reply->WriteInt(0); |
| 298 } |
| 299 } else { |
| 300 // child |
| 301 // Apply file descriptor map |
| 302 InjectiveMultimap fd_shuffle; |
| 303 for (file_handle_mapping_vector::const_iterator |
| 304 it = fds_to_remap.begin(); it != fds_to_remap.end(); ++it) { |
| 305 fd_shuffle.push_back(InjectionArc(it->first, it->second, false)); |
| 306 } |
| 307 |
| 308 // Avoid closing descriptor children will need to contact fork server. |
| 309 fd_shuffle.push_back(InjectionArc(client_fd_, client_fd_, false)); |
| 310 // Avoid closing log descriptor we're using |
| 311 int logfd = logging::GetLoggingFileDescriptor(); |
| 312 if (logfd != -1) |
| 313 fd_shuffle.push_back(InjectionArc(logfd, logfd, false)); |
| 314 // And of course avoid closing the cached fds. |
| 315 std::map<std::string, int>::iterator i; |
| 316 for (i = cached_fds_.begin(); i != cached_fds_.end(); ++i) { |
| 317 fd_shuffle.push_back(InjectionArc(i->second, i->second, false)); |
| 318 } |
| 319 |
| 320 // If there is any clash in the mapping, this function will DCHECK. |
| 321 if (!ShuffleFileDescriptors(fd_shuffle)) |
| 322 exit(127); |
| 323 |
| 324 // Open this after shuffle to avoid using reserved slots. |
| 325 lockfd_ = open(lockfile_.c_str(), O_RDWR, 0); |
| 326 if (lockfd_ == -1) { |
| 327 // TODO(dkegel): real error handling |
| 328 exit(126); |
| 329 } |
| 330 // Mark it as not to be closed. |
| 331 fd_shuffle.push_back(InjectionArc(lockfd_, lockfd_, false)); |
| 332 |
| 333 // Also closes reserved fds. |
| 334 CloseSuperfluousFds(fd_shuffle); |
| 335 |
| 336 *newargv = argv; |
| 337 // Because *newargv is set, we will return to main instead of looping |
| 338 } |
| 339 return true; |
| 340 |
| 341 error_reply: |
| 342 reply->WriteInt(-1); |
| 343 reply->WriteInt(-1); |
| 344 for (int i=0; i<num_wire_fds; i++) |
| 345 close(wire_fds[i]); |
| 346 return false; |
| 347 } |
| 348 |
| 349 // Runs in browser process, called by ProcessWatcher::EnsureProcessTerminated(). |
| 350 void ZygoteManager::EnsureProcessTerminated(pid_t childpid) { |
| 351 if (client_fd_ == -1) |
| 352 return; |
| 353 |
| 354 Pickle pickle; |
| 355 |
| 356 pickle.WriteString(kZMagic); |
| 357 pickle.WriteInt(getpid()); |
| 358 pickle.WriteInt(ZMREAP); |
| 359 pickle.WriteInt(childpid); |
| 360 |
| 361 int bytes_sent = HANDLE_EINTR( |
| 362 write(client_fd_, const_cast<void*>(pickle.data()), pickle.size())); |
| 363 |
| 364 if (bytes_sent < 1) { |
| 365 LOG(ERROR) << "Can't send to zm, errno " << errno << ", fd " << client_fd_; |
| 366 } |
| 367 } |
| 368 |
| 369 // Runs in zygote manager process |
| 370 void ZygoteManager::EnsureProcessTerminatedHandler(const Pickle& request, |
| 371 void* iter) { |
| 372 pid_t childpid; |
| 373 request.ReadInt(&iter, &childpid); |
| 374 NOTIMPLEMENTED(); |
| 375 // TODO(dkegel): put childpid on a watch list, and terminate it |
| 376 // after a while as chrome/common/process_watcher does. |
| 377 } |
| 378 |
| 379 static bool ValidateFilename(const std::string& filename) { |
| 380 // We only have to open one kind of file, but we don't know |
| 381 // the directory it's in, so be as restrictive as we can within |
| 382 // those bounds. |
| 383 |
| 384 static const char* allowed_prefix = "/"; |
| 385 if (filename.compare(0, strlen(allowed_prefix), allowed_prefix) != 0) { |
| 386 LOG(ERROR) << "filename did not start with " << allowed_prefix; |
| 387 return false; |
| 388 } |
| 389 static const char* allowed_suffix = ".pak"; |
| 390 if ((filename.length() <= strlen(allowed_suffix)) || |
| 391 (filename.compare(filename.length() - strlen(allowed_suffix), |
| 392 strlen(allowed_suffix), allowed_suffix) != 0)) { |
| 393 LOG(ERROR) << "filename did not end in " << allowed_suffix; |
| 394 return false; |
| 395 } |
| 396 if (filename.find("../") != std::string::npos) { |
| 397 LOG(ERROR) << "filename contained relative component"; |
| 398 return false; |
| 399 } |
| 400 static const char* forbidden_prefixes[] = { |
| 401 "/var/", "/tmp/", "/etc/", "/dev/", "/proc/", 0 }; |
| 402 for (const char** p = forbidden_prefixes; |
| 403 *p; p++) { |
| 404 if (filename.compare(0, strlen(*p), *p) == 0) { |
| 405 LOG(ERROR) << "filename began with " << *p; |
| 406 return false; |
| 407 } |
| 408 } |
| 409 return true; |
| 410 } |
| 411 |
| 412 // Runs in browser process |
| 413 int ZygoteManager::OpenFile(const std::string& filename) { |
| 414 // For security reasons, we only support .pak files, |
| 415 // and only in certain locations. |
| 416 if (!ValidateFilename(filename)) { |
| 417 LOG(INFO) << "ZygoteManager: filename " << filename << " disallowed."; |
| 418 return -1; |
| 419 } |
| 420 |
| 421 std::map<std::string, int>::iterator mapiter = cached_fds_.find(filename); |
| 422 if (mapiter != cached_fds_.end()) |
| 423 return mapiter->second; |
| 424 |
| 425 if (client_fd_ == -1) |
| 426 return -1; |
| 427 |
| 428 Pickle pickle; |
| 429 |
| 430 pickle.WriteString(kZMagic); |
| 431 pickle.WriteInt(getpid()); |
| 432 pickle.WriteInt(ZMOPEN); |
| 433 pickle.WriteString(filename); |
| 434 |
| 435 // Get ready to receive fds |
| 436 ::msghdr msg = {0}; |
| 437 struct iovec iov = {msg_buf_, kMAX_MSG_LEN}; |
| 438 msg.msg_iov = &iov; |
| 439 msg.msg_iovlen = 1; |
| 440 msg.msg_control = cmsg_buf_; |
| 441 msg.msg_controllen = kMAX_CMSG_LEN; |
| 442 |
| 443 ssize_t bytes_sent; |
| 444 ssize_t bytes_read = 0; |
| 445 |
| 446 if (flock(lockfd_, LOCK_EX)) |
| 447 LOG(ERROR) << "flock failed, errno " << errno; |
| 448 bytes_sent = HANDLE_EINTR( |
| 449 write(client_fd_, const_cast<void *>(pickle.data()), pickle.size())); |
| 450 if (bytes_sent > 0) { |
| 451 bytes_read = HANDLE_EINTR(recvmsg(client_fd_, &msg, MSG_WAITALL)); |
| 452 } |
| 453 if (flock(lockfd_, LOCK_UN)) |
| 454 LOG(ERROR) << "flock failed, errno " << errno; |
| 455 |
| 456 if (bytes_sent < 1) { |
| 457 LOG(ERROR) << "Can't send to zm, errno " << errno << ", fd " << client_fd_; |
| 458 return -1; |
| 459 } |
| 460 if (bytes_read < 1) { |
| 461 LOG(ERROR) << "Can't get from zm, errno " << errno; |
| 462 return -1; |
| 463 } |
| 464 |
| 465 // Locate the sole block of sent file descriptors within the list of |
| 466 // control messages |
| 467 const int* wire_fds = NULL; |
| 468 unsigned num_wire_fds = 0; |
| 469 if (msg.msg_controllen > 0) { |
| 470 struct cmsghdr* cmsg; |
| 471 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; |
| 472 cmsg = CMSG_NXTHDR(&msg, cmsg)) { |
| 473 if (cmsg->cmsg_level == SOL_SOCKET && |
| 474 cmsg->cmsg_type == SCM_RIGHTS) { |
| 475 const unsigned payload_len = cmsg->cmsg_len - CMSG_LEN(0); |
| 476 assert(payload_len % sizeof(int) == 0); |
| 477 wire_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg)); |
| 478 num_wire_fds = payload_len / sizeof(int); |
| 479 break; |
| 480 } |
| 481 } |
| 482 } |
| 483 DCHECK(!(msg.msg_flags & MSG_CTRUNC)); |
| 484 |
| 485 // Unpickle the reply |
| 486 Pickle reply(msg_buf_, bytes_read); |
| 487 void* iter = NULL; |
| 488 int kind = UnpickleHeader(reply, &iter); |
| 489 if (kind != ZMOPENED) { |
| 490 LOG(ERROR) << "reply wrong kind " << kind; |
| 491 goto error_close; |
| 492 } |
| 493 int newfd_errno; |
| 494 if (!reply.ReadInt(&iter, &newfd_errno)) { |
| 495 LOG(ERROR) << "open failed, can't read errno"; |
| 496 goto error_close; |
| 497 } |
| 498 if (newfd_errno != 0) { |
| 499 LOG(ERROR) << "open failed, errno " << newfd_errno; |
| 500 goto error_close; |
| 501 } |
| 502 if (num_wire_fds != 1) { |
| 503 LOG(ERROR) << "open failed, reply wrong number fds " << num_wire_fds; |
| 504 goto error_close; |
| 505 } |
| 506 if (wire_fds[0] == -1) { |
| 507 LOG(ERROR) << "open failed, fd -1"; |
| 508 NOTREACHED(); |
| 509 return -1; |
| 510 } |
| 511 return wire_fds[0]; |
| 512 |
| 513 error_close: |
| 514 for (unsigned i=0; i<num_wire_fds; i++) |
| 515 close(wire_fds[i]); |
| 516 return -1; |
| 517 } |
| 518 |
| 519 // Runs in zygote manager process |
| 520 bool ZygoteManager::OpenFileHandler(const Pickle& request, void* iter, |
| 521 Pickle* reply, ::msghdr* msg) { |
| 522 reply->WriteInt(ZMOPENED); |
| 523 |
| 524 std::string filename; |
| 525 if (!request.ReadString(&iter, &filename)) { |
| 526 LOG(ERROR) << "no filename?"; |
| 527 return false; |
| 528 } |
| 529 if (!ValidateFilename(filename)) { |
| 530 // Fake a unix error code |
| 531 reply->WriteInt(EPERM); |
| 532 return false; |
| 533 } |
| 534 |
| 535 std::map<std::string, int>::iterator i; |
| 536 int newfd; |
| 537 std::map<std::string, int>::iterator mapiter = cached_fds_.find(filename); |
| 538 if (mapiter == cached_fds_.end()) { |
| 539 // Verify that file is a plain file |
| 540 struct stat statbuf; |
| 541 if (lstat(filename.c_str(), &statbuf) != 0) { |
| 542 LOG(ERROR) << "can't stat " << filename << ", errno " << errno; |
| 543 return false; |
| 544 } |
| 545 if (!S_ISREG(statbuf.st_mode)) { |
| 546 LOG(ERROR) << "not regular file " << filename; |
| 547 // Fake a unix error code |
| 548 reply->WriteInt(EISDIR); |
| 549 return false; |
| 550 } |
| 551 newfd = open(filename.c_str(), O_RDONLY, 0); |
| 552 if (newfd != -1) { |
| 553 cached_fds_[filename] = newfd; |
| 554 } else { |
| 555 LOG(ERROR) << "can't open " << filename << ", errno " << errno; |
| 556 } |
| 557 } else { |
| 558 newfd = mapiter->second; |
| 559 } |
| 560 if (newfd == -1) { |
| 561 reply->WriteInt(errno); |
| 562 } else { |
| 563 reply->WriteInt(0); |
| 564 msg->msg_control = cmsg_buf_; |
| 565 msg->msg_controllen = CMSG_LEN(sizeof(int)); |
| 566 struct cmsghdr* cmsg; |
| 567 cmsg = CMSG_FIRSTHDR(msg); |
| 568 cmsg->cmsg_level = SOL_SOCKET; |
| 569 cmsg->cmsg_type = SCM_RIGHTS; |
| 570 cmsg->cmsg_len = msg->msg_controllen; |
| 571 int* wire_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg)); |
| 572 wire_fds[0] = newfd; |
| 573 } |
| 574 |
| 575 return true; |
| 576 } |
| 577 |
| 578 // Runs in zygote manager process |
| 579 bool ZygoteManager::ReadAndHandleMessage(std::vector<std::string>** newargv) { |
| 580 // Wait for activity either on canary fd or main fd. |
| 581 struct pollfd watcher[2]; |
| 582 memset(watcher, 0, sizeof(watcher)); |
| 583 watcher[0].fd = canary_fd_; |
| 584 watcher[0].events = POLLIN|POLLHUP; |
| 585 watcher[1].fd = server_fd_; |
| 586 watcher[1].events = POLLIN; |
| 587 // Wait at most one minute. This lets us detect case where |
| 588 // canary socket is closed abruptly because the main client aborted. |
| 589 // Also lets us reap dead children once a minute even if we don't get SIGCHLD. |
| 590 // We'd like to wait less time, but that's hard on battery life. |
| 591 // Note: handle EINTR manually here, not with wrapper, as we need |
| 592 // to return when we're interrupted so caller can reap promptly. |
| 593 int nactive = poll(watcher, 2, 60*1000); |
| 594 |
| 595 if (nactive == -1) { |
| 596 if (errno == EINTR) { |
| 597 LOG(INFO) << "poll interrupted"; |
| 598 // Probably SIGCHLD. Return to main loop so it can reap. |
| 599 return true; |
| 600 } |
| 601 LOG(ERROR) << "poll failed, errno " << errno << ", aborting"; |
| 602 return false; |
| 603 } |
| 604 |
| 605 // If it was the canary, exit |
| 606 if (watcher[0].revents != 0) { |
| 607 LOG(INFO) << "notified of peer destruction, exiting"; |
| 608 return false; |
| 609 } |
| 610 if ((watcher[1].revents & POLLIN) != POLLIN) { |
| 611 // spurious wakeup? |
| 612 return true; |
| 613 } |
| 614 |
| 615 ssize_t bytes_read = 0; |
| 616 struct msghdr msg = {0}; |
| 617 struct iovec iov = {msg_buf_, kMAX_MSG_LEN}; |
| 618 msg.msg_iov = &iov; |
| 619 msg.msg_iovlen = 1; |
| 620 msg.msg_control = cmsg_buf_; |
| 621 msg.msg_controllen = kMAX_CMSG_LEN; |
| 622 bytes_read = HANDLE_EINTR(recvmsg(server_fd_, &msg, MSG_WAITALL)); |
| 623 if (bytes_read == 0) { |
| 624 LOG(ERROR) << "got EOF, aborting"; |
| 625 return false; |
| 626 } |
| 627 if (bytes_read == -1) { |
| 628 LOG(ERROR) << "got errno " << errno << ", aborting"; |
| 629 return false; |
| 630 } |
| 631 |
| 632 // Locate the sole block of sent file descriptors within the list of |
| 633 // control messages |
| 634 const int* wire_fds = NULL; |
| 635 unsigned num_wire_fds = 0; |
| 636 if (msg.msg_controllen > 0) { |
| 637 struct cmsghdr* cmsg; |
| 638 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; |
| 639 cmsg = CMSG_NXTHDR(&msg, cmsg)) { |
| 640 if (cmsg->cmsg_level == SOL_SOCKET && |
| 641 cmsg->cmsg_type == SCM_RIGHTS) { |
| 642 const unsigned payload_len = cmsg->cmsg_len - CMSG_LEN(0); |
| 643 assert(payload_len % sizeof(int) == 0); |
| 644 wire_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg)); |
| 645 num_wire_fds = payload_len / sizeof(int); |
| 646 break; |
| 647 } |
| 648 } |
| 649 } |
| 650 DCHECK(!(msg.msg_flags & MSG_CTRUNC)); |
| 651 |
| 652 // Unpickle/parse message |
| 653 Pickle pickle(msg_buf_, bytes_read); |
| 654 void* iter = NULL; |
| 655 std::string magic; |
| 656 if (!pickle.ReadString(&iter, &magic) || magic != std::string(kZMagic)) { |
| 657 LOG(ERROR) << "msg didn't start with " << kZMagic << ", got " << magic; |
| 658 for (unsigned i=0; i<num_wire_fds; i++) |
| 659 close(wire_fds[i]); |
| 660 return true; |
| 661 } |
| 662 pid_t clientpid = (pid_t) -1; |
| 663 pickle.ReadInt(&iter, &clientpid); |
| 664 int kind; |
| 665 pickle.ReadInt(&iter, &kind); |
| 666 |
| 667 Pickle reply; |
| 668 reply.WriteString(kZMagic); |
| 669 reply.WriteInt(clientpid); |
| 670 |
| 671 struct msghdr replymsg = {0}; |
| 672 memset(&replymsg, 0, sizeof(replymsg)); |
| 673 |
| 674 switch (kind) { |
| 675 case ZMPING: |
| 676 DCHECK_EQ(0U, num_wire_fds); |
| 677 PingHandler(pickle, iter, &reply, newargv); |
| 678 break; |
| 679 case ZMFORK: |
| 680 // TODO(dkegel): real error handling |
| 681 (void) LongForkHandler(pickle, iter, &reply, newargv, wire_fds, |
| 682 num_wire_fds); |
| 683 if (*newargv != NULL) { |
| 684 // Child. Just return to caller, who will return from SetLongFork. |
| 685 return true; |
| 686 } |
| 687 break; |
| 688 case ZMREAP: |
| 689 DCHECK_EQ(0U, num_wire_fds); |
| 690 EnsureProcessTerminatedHandler(pickle, iter); |
| 691 // no reply to this message |
| 692 return true; |
| 693 case ZMOPEN: |
| 694 DCHECK_EQ(0U, num_wire_fds); |
| 695 // TODO(dkegel): real error handling |
| 696 (void) OpenFileHandler(pickle, iter, &reply, &replymsg); |
| 697 break; |
| 698 default: |
| 699 // TODO(dkegel): real error handling |
| 700 LOG(ERROR) << "Unknown message kind " << kind; |
| 701 DCHECK_EQ(0U, num_wire_fds); |
| 702 break; |
| 703 } |
| 704 |
| 705 struct iovec riov = {const_cast<void *>(reply.data()), reply.size()}; |
| 706 replymsg.msg_iov = &riov; |
| 707 replymsg.msg_iovlen = 1; |
| 708 |
| 709 int bytes_sent; |
| 710 bytes_sent = HANDLE_EINTR(sendmsg(server_fd_, &replymsg, MSG_WAITALL)); |
| 711 if (bytes_sent != static_cast<int>(riov.iov_len)) { |
| 712 // TODO(dkegel): real error handling |
| 713 LOG(ERROR) << "Can't send reply."; |
| 714 return false; |
| 715 } |
| 716 return true; |
| 717 } |
| 718 |
| 719 // Called only by ChromeMain(), forks the zygote manager process. |
| 720 std::vector<std::string>* ZygoteManager::Start() { |
| 721 DCHECK(lockfd_ == -1); |
| 722 DCHECK(canary_fd_ == -1); |
| 723 DCHECK(server_fd_ == -1); |
| 724 DCHECK(client_fd_ == -1); |
| 725 |
| 726 int pipe_fds[2]; |
| 727 |
| 728 // Avoid using the reserved fd slots. |
| 729 int reserved_fds[kReservedFds]; |
| 730 for (int i=0; i < kReservedFds; i++) |
| 731 reserved_fds[i] = open("/dev/null", O_RDONLY, 0); |
| 732 |
| 733 // Create the main communications pipe. |
| 734 int err = HANDLE_EINTR(socketpair(AF_UNIX, SOCK_DGRAM, 0, pipe_fds)); |
| 735 if (err != 0) { |
| 736 // TODO(dkegel): real error handling |
| 737 exit(99); |
| 738 } |
| 739 server_fd_ = pipe_fds[1]; |
| 740 client_fd_ = pipe_fds[0]; |
| 741 |
| 742 // Create the pipe used only to relay destruction event server. |
| 743 // Must be SOCK_STREAM so close() is sensed by poll(). |
| 744 err = HANDLE_EINTR(socketpair(AF_UNIX, SOCK_STREAM, 0, pipe_fds)); |
| 745 if (err != 0) { |
| 746 // TODO(dkegel): real error handling |
| 747 exit(99); |
| 748 } |
| 749 |
| 750 // Create lock file. |
| 751 // TODO(dkegel): get rid of this |
| 752 char lockfile[256]; |
| 753 strcpy(lockfile, "/tmp/zygote_manager_lock.XXXXXX"); |
| 754 lockfd_ = mkstemp(lockfile); |
| 755 if (lockfd_ == -1) { |
| 756 // TODO(dkegel): real error handling |
| 757 exit(99); |
| 758 } |
| 759 lockfile_.assign(lockfile); |
| 760 |
| 761 // Fork a fork server. |
| 762 pid_t childpid = fork(); |
| 763 |
| 764 if (childpid) { |
| 765 for (int i=0; i < kReservedFds; i++) |
| 766 close(reserved_fds[i]); |
| 767 |
| 768 // Original parent. Continues on with the main program |
| 769 // and becomes the first client. |
| 770 close(server_fd_); |
| 771 server_fd_ = -1; |
| 772 |
| 773 close(pipe_fds[1]); |
| 774 canary_fd_ = pipe_fds[0]; |
| 775 |
| 776 // Return now to indicate this is the original process. |
| 777 return NULL; |
| 778 } else { |
| 779 close(lockfd_); |
| 780 |
| 781 close(pipe_fds[0]); |
| 782 canary_fd_ = pipe_fds[1]; |
| 783 |
| 784 // We need to accept SIGCHLD, even though our handler is a no-op because |
| 785 // otherwise we cannot wait on children. (According to POSIX 2001.) |
| 786 // (And otherwise poll() might not wake up on SIGCHLD.) |
| 787 struct sigaction action; |
| 788 memset(&action, 0, sizeof(action)); |
| 789 action.sa_handler = SIGCHLDHandler; |
| 790 CHECK(sigaction(SIGCHLD, &action, NULL) == 0); |
| 791 |
| 792 // Original child. Acts as the server. |
| 793 while (true) { |
| 794 std::vector<std::string>* newargv = NULL; |
| 795 if (!ReadAndHandleMessage(&newargv)) |
| 796 break; |
| 797 if (newargv) { |
| 798 // Return new commandline to show caller this is a new child process. |
| 799 return newargv; |
| 800 } |
| 801 // Server process continues around loop. |
| 802 |
| 803 // Reap children. |
| 804 while (true) { |
| 805 int status = -1; |
| 806 pid_t reaped = waitpid(-1, &status, WNOHANG); |
| 807 if (reaped != -1 && reaped != 0) { |
| 808 LOG(INFO) << "Reaped pid " << reaped; |
| 809 continue; |
| 810 } |
| 811 break; |
| 812 } |
| 813 } |
| 814 // Server cleanup after EOF or error reading from the socket. |
| 815 // Chrome doesn't seem to get here in practice. |
| 816 Delete(FilePath(lockfile_), false); |
| 817 // TODO(dkegel): real error handling |
| 818 LOG(INFO) << "exiting. " << cached_fds_.size() << " cached fds."; |
| 819 std::map<std::string, int>::iterator i; |
| 820 for (i = cached_fds_.begin(); i != cached_fds_.end(); ++i) { |
| 821 LOG(INFO) << "Closing fd " << i->second << " filename " << i->first; |
| 822 close(i->second); |
| 823 } |
| 824 exit(-1); |
| 825 } |
| 826 } |
| 827 } |
| 828 #endif // defined(OS_LINUX) |
| OLD | NEW |