Exemple #1
0
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;
}
Exemple #2
0
// 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());
  }
}
Exemple #3
0
// 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;
}
Exemple #4
0
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
}
Exemple #5
0
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;
}
Exemple #6
0
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;
}
Exemple #7
0
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();
}
Exemple #8
0
// 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;
}
Exemple #9
0
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;
}
Exemple #10
0
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();
}
Exemple #11
0
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;
}
Exemple #12
0
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;
}
Exemple #13
0
// 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();
}
Exemple #14
0
// 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;
	}
}
Exemple #16
0
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;
}
Exemple #17
0
// 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;
}