Try<bool> setMTU(const string& _link, unsigned int mtu) { Result<Netlink<struct rtnl_link> > link = internal::get(_link); if (link.isError()) { return Error(link.error()); } else if (link.isNone()) { return false; } // TODO(jieyu): We use ioctl to set the MTU because libnl has some // issues with rtnl_link_change. struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, _link.c_str(), IFNAMSIZ); ifr.ifr_mtu = mtu; int fd = ::socket(AF_INET, SOCK_STREAM, 0); if (fd == -1) { return ErrnoError(); } if (ioctl(fd, SIOCSIFMTU, &ifr) == -1) { if (errno == ENODEV) { os::close(fd); return false; } // Save the error string as os::close may overwrite errno. const string message = strerror(errno); os::close(fd); return Error(message); } return true; }
// By default, recursively deletes a directory akin to: 'rm -r'. If the // programmer sets recursive to false, it deletes a directory akin to: 'rmdir'. // Note that this function expects an absolute path. inline Try<Nothing> rmdir(const std::string& directory, bool recursive = true) { // Canonicalize the path to Windows style for the call to // `recursive_remove_directory`. Result<std::string> root = os::realpath(directory); if (root.isError()) { return Error(root.error()); } else if (root.isNone()) { return Error( "Argument to `os::rmdir` is not a valid directory or file: '" + directory + "'"); } if (!recursive) { if (::_rmdir(directory.c_str()) < 0) { return ErrnoError(); } else { return Nothing(); } } else { return os::internal::recursive_remove_directory(root.get()); } }
// Suspends execution of the calling process until a child specified by `pid` // has changed state. Unlike the POSIX standard function `::waitpid`, this // function does not use -1 and 0 to signify errors and nonblocking return. // Instead, we return `Result<pid_t>`: // * In case of error, we return `Error` rather than -1. For example, we // would return an `Error` in case of `EINVAL`. // * In case of nonblocking return, we return `None` rather than 0. For // example, if we pass `WNOHANG` in the `options`, we would expect 0 to be // returned in the case that children specified by `pid` exist, but have // not changed state yet. In this case we return `None` instead. // // NOTE: There are important differences between the POSIX and Windows // implementations of this function: // * On POSIX, `pid_t` is a signed number, but on Windows, PIDs are `DWORD`, // which is `unsigned long`. Thus, if we use `DWORD` to represent the `pid` // argument, passing -1 as the `pid` would (on most modern servers) // silently convert to a really large `pid`. This is undesirable. // * Since it is important to be able to detect -1 has been passed to // `os::waitpid`, as a matter of practicality, we choose to: // (1) Use `long` to represent the `pid` argument. // (2) Disable using any value <= 0 for `pid` on Windows. // * This decision is pragmatic. The reasoning is: // (1) The Windows code paths call `os::waitpid` in only a handful of // places, and in none of these conditions do we need `-1` as a value. // (2) Since PIDs virtually never take on values outside the range of // vanilla signed `long` it is likely that an accidental conversion // will never happen. // (3) Even though it is not formalized in the C specification, the // implementation of `long` on the vast majority of production servers // is 2's complement, so we expect that when we accidentally do // implicitly convert from `unsigned long` to `long`, we will "wrap // around" to negative values. And since we've disabled the negative // `pid` in the Windows implementation, we should error out. // * Finally, on Windows, we currently do not check that the process we are // attempting to await is a child process. inline Result<pid_t> waitpid(long pid, int* status, int options) { const bool wait_for_child = (options & WNOHANG) == 0; // NOTE: Windows does not implement pids <= 0. if (pid <= 0) { errno = ENOSYS; return ErrnoError( "os::waitpid: Value of pid is '" + stringify(pid) + "'; the Windows implementation currently does not allow values <= 0"); } else if (options != 0 && options != WNOHANG) { // NOTE: We only support `options == 0` or `options == WNOHANG`. On Windows // no flags other than `WNOHANG` are supported. errno = ENOSYS; return ErrnoError( "os::waitpid: Only flag `WNOHANG` is implemented on Windows"); } // TODO(hausdorff): Check that `pid` is one of the child processes. If not, // set `errno` to `ECHILD` and return -1. // Open the child process as a safe `SharedHandle`. const HANDLE process = ::OpenProcess( PROCESS_QUERY_INFORMATION | SYNCHRONIZE, FALSE, static_cast<DWORD>(pid)); if (process == NULL) { return WindowsError("os::waitpid: Failed to open process for pid '" + stringify(pid) + "'"); } SharedHandle scoped_process(process, ::CloseHandle); // If `WNOHANG` flag is set, don't wait. Otherwise, wait for child to // terminate. const DWORD wait_time = wait_for_child ? INFINITE : 0; const DWORD wait_results = ::WaitForSingleObject( scoped_process.get(), wait_time); // Verify our wait exited correctly. const bool state_signaled = wait_results == WAIT_OBJECT_0; if (options == 0 && !state_signaled) { // If `WNOHANG` is not set, then we should have stopped waiting only for a // state change in `scoped_process`. errno = ECHILD; return WindowsError( "os::waitpid: Failed to wait for pid '" + stringify(pid) + "'. `::WaitForSingleObject` should have waited for child process to " + "exit, but returned code '" + stringify(wait_results) + "' instead"); } else if (wait_for_child && !state_signaled && wait_results != WAIT_TIMEOUT) { // If `WNOHANG` is set, then a successful wait should report either a // timeout (since we set the time to wait to `0`), or a successful state // change of `scoped_process`. Anything else is an error. errno = ECHILD; return WindowsError( "os::waitpid: Failed to wait for pid '" + stringify(pid) + "'. `ENOHANG` flag was passed in, so `::WaitForSingleObject` should " + "have either returned `WAIT_OBJECT_0` or `WAIT_TIMEOUT` (the " + "timeout was set to 0, because we are not waiting for the child), " + "but instead returned code '" + stringify(wait_results) + "'"); } if (!wait_for_child && wait_results == WAIT_TIMEOUT) { // Success. `ENOHANG` was set and we got a timeout, so return `None` (POSIX // `::waitpid` would return 0 here). return None(); } // Attempt to retrieve exit code from child process. Store that exit code in // the `status` variable if it's `NULL`. DWORD child_exit_code = 0; if (!::GetExitCodeProcess(scoped_process.get(), &child_exit_code)) { errno = ECHILD; return WindowsError( "os::waitpid: Successfully waited on child process with pid '" + std::to_string(pid) + "', but could not retrieve exit code"); } if (status != NULL) { *status = child_exit_code; } // Success. Return pid of the child process for which the status is reported. return pid; }
inline Result<IPNetwork> IPNetwork::fromLinkDevice( const std::string& name, int family) { #if !defined(__linux__) && !defined(__APPLE__) return Error("Not implemented"); #else if (family != AF_INET) { return Error("Unsupported family type: " + stringify(family)); } struct ifaddrs* ifaddr = NULL; if (getifaddrs(&ifaddr) == -1) { return ErrnoError(); } // Indicates whether the link device is found or not. bool found = false; for (struct ifaddrs* ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { if (ifa->ifa_name != NULL && !strcmp(ifa->ifa_name, name.c_str())) { found = true; if (ifa->ifa_addr != NULL && ifa->ifa_addr->sa_family == family) { IP address = IP::create(*ifa->ifa_addr).get(); if (ifa->ifa_netmask != NULL && ifa->ifa_netmask->sa_family == family) { IP netmask = IP::create(*ifa->ifa_netmask).get(); freeifaddrs(ifaddr); Try<IPNetwork> network = IPNetwork::create(address, netmask); if (network.isError()) { return Error(network.error()); } return network.get(); } freeifaddrs(ifaddr); // Note that this is the case where netmask is not specified. // We've seen such cases when VPN is used. In that case, a // default /32 prefix is used. Try<IPNetwork> network = IPNetwork::create(address, 32); if (network.isError()) { return Error(network.error()); } return network.get(); } } } freeifaddrs(ifaddr); if (!found) { return Error("Cannot find the link device"); } return None(); #endif }
std::vector<uint8_t> readIntelHexFile(const std::string& path) { std::vector<uint8_t> result; std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(path.c_str(), "r"), fclose); if (!fp) throw ErrnoError("fopen", errno); int expNextAddr = 0; while (true) { char header[9]; if (fread(header, 1, sizeof(header), fp.get()) != sizeof(header)) { if (feof(fp.get())) break; throw ErrnoError("fread", errno); } if (header[0] != ':') { throw std::runtime_error("failed to find start code"); } char byteCountStr[3]; memcpy(byteCountStr, &header[1], 2); byteCountStr[2] = '\0'; int byteCount = strtol(byteCountStr, nullptr, 16); if (g_verbose) printf("byteCount: %d\n", byteCount); char addrStr[5]; memcpy(addrStr, &header[3], 4); addrStr[4] = '\0'; int addr = strtol(addrStr, nullptr, 16); if (g_verbose) printf("addr: %04x\n", addr); if (header[7] != '0') { throw std::runtime_error("record type doesn't start with 0"); } if (header[8] == '1') { // EOF record break; } else if (header[8] != '0') { throw std::runtime_error( stringPrintF("encountered unsupported record type 0%c!", header[8])); } if (addr != expNextAddr) throw std::runtime_error(stringPrintF( "Expected addr record %04x, but read %04x", expNextAddr, addr)); char byteStr[3]; byteStr[2] = '\0'; for (int i = 0; i < byteCount; ++i) { if (fread(byteStr, 1, 2, fp.get()) != 2) throw ErrnoError("fread byteStr", errno); int byte = strtol(byteStr, nullptr, 16); result.push_back(static_cast<uint8_t>(byte)); } expNextAddr = addr + byteCount; if (fread(byteStr, 1, 2, fp.get()) != 2) throw ErrnoError("fread checksum", errno); while (true) { int c = fgetc(fp.get()); if (c == EOF) break; else if (!isspace(c)) { ungetc(c, fp.get()); break; } } } return result; }
Try<ResourcesState> ResourcesState::recover( const std::string& rootDir, bool strict) { ResourcesState state; const string& path = paths::getResourcesInfoPath(rootDir); if (!os::exists(path)) { LOG(INFO) << "No checkpointed resources found at '" << path << "'"; return state; } Try<int> fd = os::open(path, O_RDWR | O_CLOEXEC); if (fd.isError()) { string message = "Failed to open resources file '" + path + "': " + fd.error(); if (strict) { return Error(message); } else { LOG(WARNING) << message; state.errors++; return state; } } Result<Resource> resource = None(); while (true) { // Ignore errors due to partial protobuf read and enable undoing // failed reads by reverting to the previous seek position. resource = ::protobuf::read<Resource>(fd.get(), true, true); if (!resource.isSome()) { break; } state.resources += resource.get(); } off_t offset = lseek(fd.get(), 0, SEEK_CUR); if (offset < 0) { os::close(fd.get()); return ErrnoError("Failed to lseek resources file '" + path + "'"); } // Always truncate the file to contain only valid resources. // NOTE: This is safe even though we ignore partial protobuf read // errors above, because the 'fd' is properly set to the end of the // last valid resource by 'protobuf::read()'. if (ftruncate(fd.get(), offset) != 0) { os::close(fd.get()); return ErrnoError("Failed to truncate resources file '" + path + "'"); } // After reading a non-corrupted resources file, 'record' should be // 'none'. if (resource.isError()) { string message = "Failed to read resources file '" + path + "': " + resource.error(); os::close(fd.get()); if (strict) { return Error(message); } else { LOG(WARNING) << message; state.errors++; return state; } } os::close(fd.get()); return state; }
inline Try<Nothing> rmdir( const std::string& directory, bool recursive = true, bool removeRoot = true, bool continueOnError = false) { unsigned int errorCount = 0; if (!recursive) { if (::rmdir(directory.c_str()) < 0) { return ErrnoError(); } } else { // NOTE: `fts_open` will not always return `nullptr` if the path does not // exist. We manually induce an error here to indicate that we can't remove // a directory that does not exist. if (!os::exists(directory)) { return ErrnoError(ENOENT); } char* paths[] = {const_cast<char*>(directory.c_str()), nullptr}; // Using `FTS_PHYSICAL` here because we need `FTSENT` for the // symbolic link in the directory and not the target it links to. FTS* tree = fts_open(paths, (FTS_NOCHDIR | FTS_PHYSICAL), nullptr); if (tree == nullptr) { return ErrnoError(); } FTSENT* node; while ((node = fts_read(tree)) != nullptr) { switch (node->fts_info) { case FTS_DP: // Don't remove the root of the traversal of `removeRoot` // is false. if (!removeRoot && node->fts_level == FTS_ROOTLEVEL) { continue; } if (::rmdir(node->fts_path) < 0 && errno != ENOENT) { if (continueOnError) { LOG(ERROR) << "Failed to delete directory '" << node->fts_path << "': " << os::strerror(errno); ++errorCount; } else { Error error = ErrnoError(); fts_close(tree); return error; } } break; // `FTS_DEFAULT` would include any file type which is not // explicitly described by any of the other `fts_info` values. case FTS_DEFAULT: case FTS_F: case FTS_SL: // `FTS_SLNONE` should never be the case as we don't set // `FTS_COMFOLLOW` or `FTS_LOGICAL`. Adding here for completion. case FTS_SLNONE: if (::unlink(node->fts_path) < 0 && errno != ENOENT) { if (continueOnError) { LOG(ERROR) << "Failed to delete path '" << node->fts_path << "': " << os::strerror(errno); ++errorCount; } else { Error error = ErrnoError(); fts_close(tree); return error; } } break; default: break; } } if (errno != 0) { Error error = ErrnoError("fts_read failed"); fts_close(tree); return error; } if (fts_close(tree) < 0) { return ErrnoError(); } } if (errorCount > 0) { return Error("Failed to delete " + stringify(errorCount) + " paths"); } return Nothing(); }
// Runs the provided command in a subprocess. inline Try<Subprocess> subprocess(const std::string& command) { // Create pipes for stdin, stdout, stderr. // Index 0 is for reading, and index 1 is for writing. int stdinPipe[2]; int stdoutPipe[2]; int stderrPipe[2]; if (pipe(stdinPipe) == -1) { return ErrnoError("Failed to create pipe"); } else if (pipe(stdoutPipe) == -1) { os::close(stdinPipe[0]); os::close(stdinPipe[1]); return ErrnoError("Failed to create pipe"); } else if (pipe(stderrPipe) == -1) { os::close(stdinPipe[0]); os::close(stdinPipe[1]); os::close(stdoutPipe[0]); os::close(stdoutPipe[1]); return ErrnoError("Failed to create pipe"); } pid_t pid; if ((pid = fork()) == -1) { os::close(stdinPipe[0]); os::close(stdinPipe[1]); os::close(stdoutPipe[0]); os::close(stdoutPipe[1]); os::close(stderrPipe[0]); os::close(stderrPipe[1]); return ErrnoError("Failed to fork"); } Subprocess process; process.data->pid = pid; if (process.data->pid == 0) { // Child. // Close parent's end of the pipes. os::close(stdinPipe[1]); os::close(stdoutPipe[0]); os::close(stderrPipe[0]); // Make our pipes look like stdin, stderr, stdout before we exec. while (dup2(stdinPipe[0], STDIN_FILENO) == -1 && errno == EINTR); while (dup2(stdoutPipe[1], STDOUT_FILENO) == -1 && errno == EINTR); while (dup2(stderrPipe[1], STDERR_FILENO) == -1 && errno == EINTR); // Close the copies. os::close(stdinPipe[0]); os::close(stdoutPipe[1]); os::close(stderrPipe[1]); execl("/bin/sh", "sh", "-c", command.c_str(), (char *) NULL); ABORT("Failed to execl '/bin sh -c ", command.c_str(), "'\n"); } // Parent. // Close the child's end of the pipes. os::close(stdinPipe[0]); os::close(stdoutPipe[1]); os::close(stderrPipe[1]); process.data->in = stdinPipe[1]; process.data->out = stdoutPipe[0]; process.data->err = stderrPipe[0]; // Rather than directly exposing the future from process::reap, we // must use an explicit promise so that we can ensure we can receive // the termination signal. Otherwise, the caller can discard the // reap future, and we will not know when it is safe to close the // file descriptors. Promise<Option<int> >* promise = new Promise<Option<int> >(); process.data->status = promise->future(); // We need to bind a copy of this Subprocess into the onAny callback // below to ensure that we don't close the file descriptors before // the subprocess has terminated (i.e., because the caller doesn't // keep a copy of this Subprocess around themselves). process::reap(process.data->pid) .onAny(lambda::bind(internal::cleanup, lambda::_1, promise, process)); return process; }
Try<TaskState> TaskState::recover( const string& rootDir, const SlaveID& slaveId, const FrameworkID& frameworkId, const ExecutorID& executorId, const UUID& uuid, const TaskID& taskId, bool strict) { TaskState state; state.id = taskId; string message; // Read the task info. string path = paths::getTaskInfoPath( rootDir, slaveId, frameworkId, executorId, uuid, taskId); const Result<Task>& task = ::protobuf::read<Task>(path); if (!task.isSome()) { message = "Failed to read task info from '" + path + "': " + (task.isError() ? task.error() : " none"); if (strict) { return Error(message); } else { LOG(WARNING) << message; return state; } } state.info = task.get(); // Read the status updates. path = paths::getTaskUpdatesPath( rootDir, slaveId, frameworkId, executorId, uuid, taskId); // Open the status updates file for reading and writing (for truncating). const Try<int>& fd = os::open(path, O_RDWR); if (fd.isError()) { message = "Failed to open status updates file '" + path + "': " + fd.error(); if (strict) { return Error(message); } else { LOG(WARNING) << message; return state; } } // Now, read the updates. Result<StatusUpdateRecord> record = None(); while (true) { record = ::protobuf::read<StatusUpdateRecord>(fd.get()); if (!record.isSome()) { break; } if (record.get().type() == StatusUpdateRecord::UPDATE) { state.updates.push_back(record.get().update()); } else { state.acks.insert(UUID::fromBytes(record.get().uuid())); } } // After reading a non-corrupted updates file, 'record' should be 'none'. if (record.isError()) { message = "Failed to read status updates file '" + path + "': " + record.error(); if (strict) { return Error(message); } else { LOG(WARNING) << message; // Truncate the file to contain only valid updates. if (ftruncate(fd.get(), lseek(fd.get(), 0, SEEK_CUR)) != 0) { return ErrnoError( "Failed to truncate status updates file '" + path + "'"); } return state; } } // Close the updates file. const Try<Nothing>& close = os::close(fd.get()); if (close.isError()) { message = "Failed to close status updates file '" + path + "': " + close.error(); if (strict) { return Error(message); } else { LOG(WARNING) << message; return state; } } return state; }
inline Try<Nothing> rmdir(const std::string& directory, bool recursive = true) { if (!recursive) { if (::rmdir(directory.c_str()) < 0) { return ErrnoError(); } } else { // NOTE: `fts_open` will not always return `NULL` if the path does not // exist. We manually induce an error here to indicate that we can't remove // a directory that does not exist. if (!os::exists(directory)) { errno = ENOENT; return ErrnoError(); } char* paths[] = {const_cast<char*>(directory.c_str()), NULL}; // Using `FTS_PHYSICAL` here because we need `FTSENT` for the // symbolic link in the directory and not the target it links to. FTS* tree = fts_open(paths, (FTS_NOCHDIR | FTS_PHYSICAL), NULL); if (tree == NULL) { return ErrnoError(); } FTSENT* node; while ((node = fts_read(tree)) != NULL) { switch (node->fts_info) { case FTS_DP: if (::rmdir(node->fts_path) < 0 && errno != ENOENT) { Error error = ErrnoError(); fts_close(tree); return error; } break; // `FTS_DEFAULT` would include any file type which is not // explicitly described by any of the other `fts_info` values. case FTS_DEFAULT: case FTS_F: case FTS_SL: // `FTS_SLNONE` should never be the case as we don't set // `FTS_COMFOLLOW` or `FTS_LOGICAL`. Adding here for completion. case FTS_SLNONE: if (::unlink(node->fts_path) < 0 && errno != ENOENT) { Error error = ErrnoError(); fts_close(tree); return error; } break; default: break; } } if (errno != 0) { Error error = ErrnoError(); fts_close(tree); return error; } if (fts_close(tree) < 0) { return ErrnoError(); } } return Nothing(); }
inline Result<T> read( int fd, bool ignorePartial = false, bool undoFailed = false) { off_t offset = 0; if (undoFailed) { // Save the offset so we can re-adjust if something goes wrong. offset = lseek(fd, 0, SEEK_CUR); if (offset == -1) { return ErrnoError("Failed to lseek to SEEK_CUR"); } } uint32_t size; Result<std::string> result = os::read(fd, sizeof(size)); if (result.isError()) { if (undoFailed) { lseek(fd, offset, SEEK_SET); } return Error("Failed to read size: " + result.error()); } else if (result.isNone()) { return None(); // No more protobufs to read. } else if (result.get().size() < sizeof(size)) { // Hit EOF unexpectedly. if (undoFailed) { // Restore the offset to before the size read. lseek(fd, offset, SEEK_SET); } if (ignorePartial) { return None(); } return Error( "Failed to read size: hit EOF unexpectedly, possible corruption"); } // Parse the size from the bytes. memcpy((void*) &size, (void*) result.get().data(), sizeof(size)); // NOTE: Instead of specifically checking for corruption in 'size', we simply // try to read 'size' bytes. If we hit EOF early, it is an indication of // corruption. result = os::read(fd, size); if (result.isError()) { if (undoFailed) { // Restore the offset to before the size read. lseek(fd, offset, SEEK_SET); } return Error("Failed to read message: " + result.error()); } else if (result.isNone() || result.get().size() < size) { // Hit EOF unexpectedly. if (undoFailed) { // Restore the offset to before the size read. lseek(fd, offset, SEEK_SET); } if (ignorePartial) { return None(); } return Error("Failed to read message of size " + stringify(size) + " bytes: hit EOF unexpectedly, possible corruption"); } // Parse the protobuf from the string. // NOTE: We need to capture a const reference to the data because it // must outlive the creation of ArrayInputStream. const std::string& data = result.get(); T message; google::protobuf::io::ArrayInputStream stream(data.data(), data.size()); if (!message.ParseFromZeroCopyStream(&stream)) { if (undoFailed) { // Restore the offset to before the size read. lseek(fd, offset, SEEK_SET); } return Error("Failed to deserialize message"); } return message; }
Try<Resources> ResourcesState::recoverResources( const string& path, bool strict, unsigned int& errors) { Resources resources; Try<int> fd = os::open(path, O_RDWR | O_CLOEXEC); if (fd.isError()) { string message = "Failed to open resources file '" + path + "': " + fd.error(); if (strict) { return Error(message); } else { LOG(WARNING) << message; errors++; return resources; } } Result<Resource> resource = None(); while (true) { // Ignore errors due to partial protobuf read and enable undoing // failed reads by reverting to the previous seek position. resource = ::protobuf::read<Resource>(fd.get(), true, true); if (!resource.isSome()) { break; } resources += resource.get(); } off_t offset = lseek(fd.get(), 0, SEEK_CUR); if (offset < 0) { os::close(fd.get()); return ErrnoError("Failed to lseek resources file '" + path + "'"); } // Always truncate the file to contain only valid resources. // NOTE: This is safe even though we ignore partial protobuf read // errors above, because the 'fd' is properly set to the end of the // last valid resource by 'protobuf::read()'. Try<Nothing> truncated = os::ftruncate(fd.get(), offset); if (truncated.isError()) { os::close(fd.get()); return Error( "Failed to truncate resources file '" + path + "': " + truncated.error()); } // After reading a non-corrupted resources file, 'record' should be // 'none'. if (resource.isError()) { string message = "Failed to read resources file '" + path + "': " + resource.error(); os::close(fd.get()); if (strict) { return Error(message); } else { LOG(WARNING) << message; errors++; return resources; } } os::close(fd.get()); return resources; }
// Recursive version of `RemoveDirectory`. Two things are notable about this // implementation: // // 1. Unlike `rmdir`, this requires Windows-formatted paths, and therefore // should be in the `internal` namespace. // 2. To match the semantics of the POSIX implementation, this function // implements the semantics of `rm -r`, rather than `rmdir`. In particular, // if `path` points at a file, this function will delete it, while a call to // `rmdir` will not. inline Try<Nothing> recursive_remove_directory( const std::string& path, bool removeRoot, bool continueOnError) { // NOTE: Special case required to match the semantics of POSIX. See comment // above. As below, this also handles symlinks correctly, i.e., given a path // to a symlink, we delete the symlink rather than the target. if (os::stat::isfile(path)) { return os::rm(path); } // Appending a slash here if the path doesn't already have one simplifies // path join logic later, because (unlike Unix) Windows doesn't like double // slashes in paths. std::string current_path; if (!strings::endsWith(path, "\\")) { current_path = path + "\\"; } else { current_path = path; } // Get first file matching pattern `X:\path\to\wherever\*`. WIN32_FIND_DATA found; const std::string search_pattern = current_path + "*"; const SharedHandle search_handle( FindFirstFile(search_pattern.c_str(), &found), FindClose); if (search_handle.get() == INVALID_HANDLE_VALUE) { return WindowsError( "`os::internal::recursive_remove_directory` failed when searching " "for files with pattern '" + search_pattern + "'"); } do { // NOTE: do-while is appropriate here because folder is guaranteed to have // at least a file called `.` (and probably also one called `..`). const std::string current_file(found.cFileName); const bool is_current_directory = current_file.compare(".") == 0; const bool is_parent_directory = current_file.compare("..") == 0; // Don't try to delete `.` and `..` files in directory. if (is_current_directory || is_parent_directory) { continue; } // Path to remove. const std::string current_absolute_path = current_path + current_file; const bool is_directory = os::stat::isdir(current_absolute_path); // Delete current path, whether it's a directory, file, or symlink. if (is_directory) { Try<Nothing> removed = recursive_remove_directory( current_absolute_path, true, continueOnError); if (removed.isError()) { if (continueOnError) { LOG(WARNING) << "Failed to delete directory " << current_absolute_path << " with error " << removed.error(); } else { return Error(removed.error()); } } } else { // NOTE: this also handles symbolic links. if (::remove(current_absolute_path.c_str()) != 0) { if (continueOnError) { LOG(WARNING) << "`os::internal::recursive_remove_directory`" << " attempted to delete file '" << current_absolute_path << "', but failed"; } else { return WindowsError( "`os::internal::recursive_remove_directory` attempted to delete " "file '" + current_absolute_path + "', but failed"); } } } } while (FindNextFile(search_handle.get(), &found)); // Finally, remove current directory unless `removeRoot` is disabled. if (removeRoot && ::_rmdir(current_path.c_str()) == -1) { if (continueOnError) { LOG(WARNING) << "`os::internal::recursive_remove_directory`" << " attempted to delete directory '" << current_path << "', but failed"; return ErrnoError("rmdir failed in 'continueOnError' mode"); } else { return ErrnoError( "`os::internal::recursive_remove_directory` attempted to delete " "directory '" + current_path + "', but failed"); } } return Nothing(); }
// Recursive version of `RemoveDirectory`. NOTE: unlike `rmdir`, this requires // Windows-formatted paths, and therefore should be in the `internal` namespace. inline Try<Nothing> recursive_remove_directory(const std::string& path) { // Appending a slash here if the path doesn't already have one simplifies // path join logic later, because (unlike Unix) Windows doesn't like double // slashes in paths. std::string current_path; if (!strings::endsWith(path, "\\")) { current_path = path + "\\"; } else { current_path = path; } // Get first file matching pattern `X:\path\to\wherever\*`. WIN32_FIND_DATA found; const std::string search_pattern = current_path + "*"; const SharedHandle search_handle( FindFirstFile(search_pattern.c_str(), &found), FindClose); if (search_handle.get() == INVALID_HANDLE_VALUE) { return WindowsError( "`os::internal::recursive_remove_directory` failed when searching " "for files with pattern '" + search_pattern + "'"); } do { // NOTE: do-while is appropriate here because folder is guaranteed to have // at least a file called `.` (and probably also one called `..`). const std::string current_file(found.cFileName); const bool is_current_directory = current_file.compare(".") == 0; const bool is_parent_directory = current_file.compare("..") == 0; // Don't try to delete `.` and `..` files in directory. if (is_current_directory || is_parent_directory) { continue; } // Path to remove. const std::string current_absolute_path = current_path + current_file; const bool is_directory = os::stat::isdir(current_absolute_path); // Delete current path, whether it's a directory, file, or symlink. if (is_directory) { Try<Nothing> removed = recursive_remove_directory(current_absolute_path); if (removed.isError()) { return Error(removed.error()); } } else { // NOTE: this also handles symbolic links. if (::remove(current_absolute_path.c_str()) != 0) { return WindowsError( "`os::internal::recursive_remove_directory` attempted to delete " "file '" + current_absolute_path + "', but failed"); } } } while (FindNextFile(search_handle.get(), &found)); // Finally, remove current directory. if (::_rmdir(current_path.c_str()) == -1) { return ErrnoError( "`os::internal::recursive_remove_directory` attempted to delete file " "'" + current_path + "', but failed"); } return Nothing(); }
void VideoInput::InitDevice(void) { struct v4l2_capability cap; struct v4l2_cropcap cropcap; struct v4l2_crop crop; struct v4l2_format fmt; unsigned int min; if (-1 == Xioctl(VIDIOC_QUERYCAP, &cap)) { if (EINVAL == errno) { cout << "VideoInput: " << dev_name; cout << " is not a V4L2 device\n"; errored = true; } else { ErrnoError("VIDIOC_QUERYCAP"); } } if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) { cout << "VideoInput: " << dev_name; cout << " is not a capture device\n"; errored = true; } switch (io) { case IO_METHOD_READ: if (!(cap.capabilities & V4L2_CAP_READWRITE)) { cout << "VideoInput: " << dev_name; cout << " does not support read i/o\n"; errored = true; } break; case IO_METHOD_MMAP: if (!(cap.capabilities & V4L2_CAP_STREAMING)) { cout << "VideoInput: " << dev_name; cout << " does not support streaming i/o\n"; errored = true; } break; } int input = 1; if (-1 == Xioctl(VIDIOC_S_INPUT, &input)) cout << "VideoInput: Input select error\n"; cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if (-1 == Xioctl(VIDIOC_CROPCAP, &cropcap)) cout << "VideoInput: Cropcap failure\n"; crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; crop.c = cropcap.defrect; // reset to default if (-1 == Xioctl(VIDIOC_S_CROP, &crop)) cout << "VideoInput: Cropping not supported\n"; CLEAR(fmt); fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; fmt.fmt.pix.width = w; fmt.fmt.pix.height = h; fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_GREY; fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; if (-1 == Xioctl(VIDIOC_S_FMT, &fmt)) ErrnoError("VIDIOC_S_FMT"); // Note VIDIOC_S_FMT may change width and height // Buggy driver paranoia min = fmt.fmt.pix.width * 2; if (fmt.fmt.pix.bytesperline < min) fmt.fmt.pix.bytesperline = min; min = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height; if (fmt.fmt.pix.sizeimage < min) fmt.fmt.pix.sizeimage = min; w = fmt.fmt.pix.width; h = fmt.fmt.pix.height; cout << "VideoInput: New image size: "; cout << w << "x" << h << endl; videobuffer = new vidbuffertype; videobuffer->buffer = new uint8_t [w*h]; videobuffer->w = w; videobuffer->h = h; videobuffer->bufferlen = w*h; switch (io) { case IO_METHOD_READ: InitRead(fmt.fmt.pix.sizeimage); break; case IO_METHOD_MMAP: InitMmap(); break; } }
Try<TaskState> TaskState::recover( const string& rootDir, const SlaveID& slaveId, const FrameworkID& frameworkId, const ExecutorID& executorId, const UUID& uuid, const TaskID& taskId, bool strict) { TaskState state; state.id = taskId; string message; // Read the task info. string path = paths::getTaskInfoPath( rootDir, slaveId, frameworkId, executorId, uuid, taskId); if (!os::exists(path)) { // This could happen if the slave died after creating the task // directory but before it checkpointed the task info. LOG(WARNING) << "Failed to find task info file '" << path << "'"; return state; } const Result<Task>& task = ::protobuf::read<Task>(path); if (task.isError()) { message = "Failed to read task info from '" + path + "': " + task.error(); if (strict) { return Error(message); } else { LOG(WARNING) << message; state.errors++; return state; } } if (task.isNone()) { // This could happen if the slave died after opening the file for // writing but before it checkpointed anything. LOG(WARNING) << "Found empty task info file '" << path << "'"; return state; } state.info = task.get(); // Read the status updates. path = paths::getTaskUpdatesPath( rootDir, slaveId, frameworkId, executorId, uuid, taskId); if (!os::exists(path)) { // This could happen if the slave died before it checkpointed // any status updates for this task. LOG(WARNING) << "Failed to find status updates file '" << path << "'"; return state; } // Open the status updates file for reading and writing (for truncating). const Try<int>& fd = os::open(path, O_RDWR); if (fd.isError()) { message = "Failed to open status updates file '" + path + "': " + fd.error(); if (strict) { return Error(message); } else { LOG(WARNING) << message; state.errors++; return state; } } // Now, read the updates. Result<StatusUpdateRecord> record = None(); while (true) { // Ignore errors due to partial protobuf read. record = ::protobuf::read<StatusUpdateRecord>(fd.get(), true); if (!record.isSome()) { break; } if (record.get().type() == StatusUpdateRecord::UPDATE) { state.updates.push_back(record.get().update()); } else { state.acks.insert(UUID::fromBytes(record.get().uuid())); } } // Always truncate the file to contain only valid updates. // NOTE: This is safe even though we ignore partial protobuf // read errors above, because the 'fd' is properly set to the // end of the last valid update by 'protobuf::read()'. if (ftruncate(fd.get(), lseek(fd.get(), 0, SEEK_CUR)) != 0) { return ErrnoError( "Failed to truncate status updates file '" + path + "'"); } // After reading a non-corrupted updates file, 'record' should be 'none'. if (record.isError()) { message = "Failed to read status updates file '" + path + "': " + record.error(); if (strict) { return Error(message); } else { LOG(WARNING) << message; state.errors++; return state; } } // Close the updates file. const Try<Nothing>& close = os::close(fd.get()); if (close.isError()) { message = "Failed to close status updates file '" + path + "': " + close.error(); if (strict) { return Error(message); } else { LOG(WARNING) << message; state.errors++; return state; } } return state; }
// Runs the provided command in a subprocess. Try<Subprocess> subprocess( const string& command, const Option<map<string, string> >& environment, const Option<lambda::function<int()> >& setup) { // Create pipes for stdin, stdout, stderr. // Index 0 is for reading, and index 1 is for writing. int stdinPipe[2]; int stdoutPipe[2]; int stderrPipe[2]; if (pipe(stdinPipe) == -1) { return ErrnoError("Failed to create pipe"); } else if (pipe(stdoutPipe) == -1) { os::close(stdinPipe[0]); os::close(stdinPipe[1]); return ErrnoError("Failed to create pipe"); } else if (pipe(stderrPipe) == -1) { os::close(stdinPipe[0]); os::close(stdinPipe[1]); os::close(stdoutPipe[0]); os::close(stdoutPipe[1]); return ErrnoError("Failed to create pipe"); } // We need to do this construction before doing the fork as it // might not be async-safe. // TODO(tillt): Consider optimizing this to not pass an empty map // into the constructor or even further to use execl instead of // execle once we have no user supplied environment. os::ExecEnv envp(environment.get(map<string, string>())); pid_t pid; if ((pid = fork()) == -1) { os::close(stdinPipe[0]); os::close(stdinPipe[1]); os::close(stdoutPipe[0]); os::close(stdoutPipe[1]); os::close(stderrPipe[0]); os::close(stderrPipe[1]); return ErrnoError("Failed to fork"); } Subprocess process; process.data->pid = pid; if (process.data->pid == 0) { // Child. // Close parent's end of the pipes. os::close(stdinPipe[1]); os::close(stdoutPipe[0]); os::close(stderrPipe[0]); // Make our pipes look like stdin, stderr, stdout before we exec. while (dup2(stdinPipe[0], STDIN_FILENO) == -1 && errno == EINTR); while (dup2(stdoutPipe[1], STDOUT_FILENO) == -1 && errno == EINTR); while (dup2(stderrPipe[1], STDERR_FILENO) == -1 && errno == EINTR); // Close the copies. os::close(stdinPipe[0]); os::close(stdoutPipe[1]); os::close(stderrPipe[1]); if (setup.isSome()) { int status = setup.get()(); if (status != 0) { _exit(status); } } execle("/bin/sh", "sh", "-c", command.c_str(), (char*) NULL, envp()); ABORT("Failed to execle '/bin sh -c ", command.c_str(), "'\n"); } // Parent. // Close the child's end of the pipes. os::close(stdinPipe[0]); os::close(stdoutPipe[1]); os::close(stderrPipe[1]); process.data->in = stdinPipe[1]; process.data->out = stdoutPipe[0]; process.data->err = stderrPipe[0]; // Rather than directly exposing the future from process::reap, we // must use an explicit promise so that we can ensure we can receive // the termination signal. Otherwise, the caller can discard the // reap future, and we will not know when it is safe to close the // file descriptors. Promise<Option<int> >* promise = new Promise<Option<int> >(); process.data->status = promise->future(); // We need to bind a copy of this Subprocess into the onAny callback // below to ensure that we don't close the file descriptors before // the subprocess has terminated (i.e., because the caller doesn't // keep a copy of this Subprocess around themselves). process::reap(process.data->pid) .onAny(lambda::bind(internal::cleanup, lambda::_1, promise, process)); return process; }