Exemple #1
0
bool process::launch(const std::string &cmd,
                     const std::vector<std::string> &args) {

  PROCESS_INFORMATION proc_info;
  STARTUPINFO startup_info;
  ZeroMemory(&proc_info, sizeof(PROCESS_INFORMATION));
  ZeroMemory(&startup_info, sizeof(STARTUPINFO));

  char *c_arglist = convert_args(cmd, args);

  BOOL ret;
  // For Windows, we include the cmd with the arguments so that a search path
  // is used for any executable without a full path
  ret = CreateProcess(NULL,
      c_arglist,
      NULL,
      NULL,
      FALSE,
      CREATE_NO_WINDOW,
      NULL,
      NULL,
      &startup_info,
      &proc_info);

  if(!ret) {
    auto err = GetLastError();
    logstream(LOG_ERROR) << "Failed to launch process: " <<
      get_last_err_str(err) << std::endl;
    delete[] c_arglist;
    return false;
  } else {
    // Don't need to have a thread handle. We'll just cancel the process if
    // need be
    CloseHandle(proc_info.hThread);
    m_launched = true;
  }

  // Used for killing the process
  m_proc_handle = proc_info.hProcess;
  m_pid = proc_info.dwProcessId;

  logstream(LOG_INFO) << "Launched process with pid: " << m_pid << std::endl;

  return true;
}
ssize_t process::read_from_child(void *buf, size_t count) {
  if(!m_launched)
    log_and_throw("No process launched!");
  if(!m_launched_with_popen || m_read_handle == NULL)
    log_and_throw("Cannot read from child, no pipe initialized. "
        "Launch with popen to do this.");

  // Keep from overflowing
  if(count > std::numeric_limits<DWORD>::max()) {
    count = std::numeric_limits<DWORD>::max();
  }

  DWORD bytes_read;
  BOOL ret = ReadFile(m_read_handle, (LPWORD)buf, count, &bytes_read, NULL);
  if(!ret) {
    logstream(LOG_ERROR) << "ReadFile failed: " <<
      get_last_err_str(GetLastError()) << std::endl;
  }

  return ret ? ssize_t(bytes_read) : ssize_t(-1);
}
bool process::kill(bool async) {
  if(!m_launched)
    log_and_throw("No process launched!");

  if(m_proc_handle != NULL) {
    BOOL ret = TerminateProcess(m_proc_handle, 1);
    auto err_code = GetLastError();
    if(!async)
      WaitForSingleObject(m_proc_handle, 10000);
    CloseHandle(m_proc_handle);
    m_proc_handle = NULL;

    if(!ret) {
      logstream(LOG_INFO) << get_last_err_str(err_code);
      return false;
    }

    return true;
  }

  return false;
}
bool process::launch(const std::string &cmd,
                     const std::vector<std::string> &args) {

  PROCESS_INFORMATION proc_info;
  STARTUPINFO startup_info;
  ZeroMemory(&proc_info, sizeof(PROCESS_INFORMATION));
  ZeroMemory(&startup_info, sizeof(STARTUPINFO));
  startup_info.cb = sizeof(startup_info);

  char *c_arglist = convert_args(cmd, args);

  logstream(LOG_INFO) << "Launching process using command: >>> " << c_arglist << " <<< " << std::endl;

  // Set up the proper handlers.  We are duplicating the handlers as
  // the given handles may or may not be inheritable.  DuplicateHandle
  // is (supposedly) the safest way to do this.
  startup_info.dwFlags |= STARTF_USESTDHANDLES;

  BOOL ret;

  // First, set up redirection for stdout.
  ret = DuplicateHandle( GetCurrentProcess(), GetStdHandle(STD_OUTPUT_HANDLE),
                         GetCurrentProcess(), &m_stdout_handle, 0, TRUE, DUPLICATE_SAME_ACCESS);

  if(!ret) {
    auto err = GetLastError();
    logstream(LOG_WARNING) << "Failed to duplicate stdout file handle: " << get_last_err_str(err)
                           << "; continuing with default handle." << std::endl;
    m_stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE);
  }

  startup_info.hStdOutput = m_stdout_handle;

  // Second, set up redirection for stderr.
  ret = DuplicateHandle( GetCurrentProcess(), GetStdHandle(STD_ERROR_HANDLE),
                         GetCurrentProcess(), &m_stderr_handle, 0, TRUE, DUPLICATE_SAME_ACCESS);

  if(!ret) {
    auto err = GetLastError();
    logstream(LOG_WARNING) << "Failed to duplicate stderr file handle: " << get_last_err_str(err)
                           << "; continuing with default handle." << std::endl;
    m_stderr_handle = GetStdHandle(STD_ERROR_HANDLE);
  }

  startup_info.hStdError = m_stderr_handle;

  // For Windows, we include the cmd with the arguments so that a search path
  // is used for any executable without a full path
  ret = CreateProcess(NULL,
      c_arglist, // command line
      NULL,      // process security attributes
      NULL,      // primary thread security attributes
      TRUE,      // handles are inherited
      CREATE_NO_WINDOW, // creation flags
      NULL,      // use parent's environment
      NULL,      // use parent's current directory
      &startup_info, // use parent's current directory
      &proc_info); // use parent's current directory

  if(!ret) {
    auto err = GetLastError();
    logstream(LOG_ERROR) << "Failed to launch process: " << get_last_err_str(err) << std::endl;
    delete[] c_arglist;
    return false;
  } else {

    // Don't need to have a thread handle. We'll just cancel the process if
    // need be.
    CloseHandle(proc_info.hThread);
    m_launched = true;
  }

  // Used for killing the process
  m_proc_handle = proc_info.hProcess;
  m_pid = proc_info.dwProcessId;

  logstream(LOG_INFO) << "Launched process with pid: " << m_pid << std::endl;

  // Wait up to 100 milliseconds before querying the process for it's status.
  DWORD dwMillisec = 100;
  DWORD dwWaitStatus = WaitForSingleObject(m_proc_handle, dwMillisec );

  if(dwWaitStatus == WAIT_FAILED) {
    auto err = GetLastError();
    logstream(LOG_WARNING) << "Error in WaitForSingleObject after CreateProcess: "
                           << get_last_err_str(err) << std::endl;
  }

  // Query the status of the process.  Will return STILL_ACTIVE if all
  // is good.
  DWORD potential_exit_code;
  ret = GetExitCodeProcess(m_proc_handle, &potential_exit_code);

  if(!ret) {
    auto err = GetLastError();
    logstream(LOG_WARNING) << "Error querying process status code: " << get_last_err_str(err) << std::endl;
  }

  logstream(LOG_INFO) << "Process status of " << m_pid << " = " << potential_exit_code << std::endl;

  if(potential_exit_code != STILL_ACTIVE) {
    logstream(LOG_ERROR) << "Launched process " << m_pid
                         << " exited immediately with error code " << potential_exit_code << std::endl;
    return false;
  }

  return true;
}
bool process::popen(const std::string &cmd,
                     const std::vector<std::string> &args,
                     int child_write_fd) {
  // We will only support stdout and stderr in Windows
  if(child_write_fd != STDOUT_FILENO && child_write_fd != STDERR_FILENO) {
    logstream(LOG_ERROR) << "Cannot read anything other than stdout or stderr "
      "from child on Windows." << std::endl;
    return false;
  }
  
  SECURITY_ATTRIBUTES sa_attr;
  sa_attr.nLength=sizeof(SECURITY_ATTRIBUTES);
  // Allow handles to be inherited when process created
  sa_attr.bInheritHandle = TRUE;
  sa_attr.lpSecurityDescriptor = NULL;


  if(!CreatePipe(&m_read_handle, &m_write_handle, &sa_attr, 0)) {
    //TODO: Figure out how to get error string on Windows
    logstream(LOG_ERROR) << "Failed to create pipe: " <<
      get_last_err_str(GetLastError()) << std::endl;

    return false;
  }

  // Make sure the parent end of the pipe is NOT inherited
  if(!SetHandleInformation(m_read_handle, HANDLE_FLAG_INHERIT, 0)) {
    logstream(LOG_ERROR) << "Failed to set handle information: " <<
      get_last_err_str(GetLastError()) << std::endl;
    return false;
  }

  PROCESS_INFORMATION proc_info;
  STARTUPINFO startup_info;
  ZeroMemory(&proc_info, sizeof(PROCESS_INFORMATION));
  ZeroMemory(&startup_info, sizeof(STARTUPINFO));

  if(m_read_handle != NULL) {
    startup_info.cb = sizeof(STARTUPINFO);
    if(child_write_fd == STDOUT_FILENO) {
      startup_info.hStdOutput = m_write_handle;
    } else if(child_write_fd == STDERR_FILENO) {
      startup_info.hStdError = m_write_handle;
    }
    startup_info.dwFlags |= STARTF_USESTDHANDLES;
  } else {
    logstream(LOG_ERROR) << "Read handle NULL after pipe created." << std::endl;
    return false;
  }

  char *c_arglist = convert_args(cmd, args);

  BOOL ret;
  // For Windows, we include the cmd with the arguments so that a search path
  // is used for any executable without a full path
  ret = CreateProcess(NULL,
      c_arglist,
      NULL,
      NULL,
      TRUE,
      0,
      NULL,
      NULL,
      &startup_info,
      &proc_info);


  if(!ret) {
    auto err = GetLastError();
    logstream(LOG_ERROR) << "Failed to launch process: " <<
      get_last_err_str(err) << std::endl;
    delete[] c_arglist;
    return false;

  } else {
    // Don't need to have a thread handle. We'll just cancel the process if
    // need be
    CloseHandle(proc_info.hThread);

    // Now that the process has been created, close the handle that was
    // inherited by the child.  Apparently if you DON'T do this, reading from
    // the child will never report an error when the child is done writing, and
    // you'll hang forever waiting for an EOF. There goes a few hours of my
    // life.
    CloseHandle(m_write_handle);
    m_write_handle = NULL;

    m_launched = TRUE;
    m_launched_with_popen = TRUE;
  }

  // Used for killing the process
  m_proc_handle = proc_info.hProcess;
  m_pid = proc_info.dwProcessId;

  logstream(LOG_INFO) << "Launched process with pid: " << m_pid << std::endl;

  return true;
}
Exemple #6
0
  std::string unity_global::load_toolkit(std::string soname,
                                         std::string module_subpath) {
    // rewrite "local" protocol
    std::string protocol = fileio::get_protocol(soname);
    if (protocol == "local") {
      soname = fileio::remove_protocol(soname);
    }

    so_registration_list regentry;
    regentry.original_soname = soname;
    logstream(LOG_INFO) << "Attempt loading of " << sanitize_url(soname) << std::endl;

    // see if the file exists and whether we need to donwnload it
    if (fileio::try_to_open_file(soname) == false) {
      return "Unable to open file " + sanitize_url(soname);
    }

    if (protocol != "") {
      // there is a protocol associated. We need to copy this file to local
      // issue a copy to copy it to the local temp directory
      std::string tempname = get_temp_name();
      fileio::copy(soname, tempname);
      soname = tempname;
    }
    if (!file_contains_substring(soname, "get_toolkit_function_registration") &&
        !file_contains_substring(soname, "get_toolkit_class_registration")) {
      return soname + " is not a valid extension";
    }



    // get the base name of the shared library (without the .so)
    std::string modulename = fileio::get_filename(regentry.original_soname);
    std::vector<std::string> split_names;
    boost::algorithm::split(split_names, modulename, boost::is_any_of("."));
    if (split_names.size() == 0) return "Invalid filename";
    if (module_subpath.empty()) {
      regentry.modulename = split_names[0];
    } else if (module_subpath == "..") {
      regentry.modulename = "";
    } else {
      regentry.modulename = module_subpath + "." + split_names[0];
    }

    // goody. now for the dl loading
#ifndef _WIN32
    void* dl = dlopen(soname.c_str(), RTLD_NOW | RTLD_LOCAL);
#else
    void *dl = (void *)LoadLibrary(soname.c_str());
#endif
    logstream(LOG_INFO) << "Library load of " << sanitize_url(soname) << std::endl;
    regentry.effective_soname = soname;
    regentry.dl = dl;
    // check for failure
    if (dl == NULL) {
#ifndef _WIN32
      char* err = dlerror();
      // I think we need to copy this out early
      std::string ret = err;
      logstream(LOG_ERROR) << "Unable to load " << sanitize_url(soname) << ": " << ret << std::endl;
      if (err) return ret;
      else return "dlopen failed due to an unknown error";
#else
      std::string ret = get_last_err_str(GetLastError());
      logstream(LOG_ERROR) << "Unable to load " << sanitize_url(soname) << ": " << ret << std::endl;
      if (!ret.empty()) return ret;
      else return "LoadLibrary failed due to an unknown error";
#endif
    }

  /**************************************************************************/
  /*                                                                        */
  /*                         Function Registration                          */
  /*                                                                        */
  /**************************************************************************/
    // get the registration symbols
    std::vector<std::string> toolkit_function_reg_names
                {"get_toolkit_function_registration",
                  "_Z33get_toolkit_function_registrationv",
                  "__Z33get_toolkit_function_registrationv"};

    get_toolkit_function_registration_type get_toolkit_function_registration = nullptr;
    for (auto reg_name : toolkit_function_reg_names) {
      get_toolkit_function_registration =
          reinterpret_cast<get_toolkit_function_registration_type>
          (
#ifndef _WIN32
           dlsym(dl, reg_name.c_str())
#else
           (void *)GetProcAddress((HMODULE)dl, reg_name.c_str())
#endif
           );
      if (get_toolkit_function_registration != nullptr) break;
    }

    // register functions
    if (get_toolkit_function_registration) {
      auto functions = (*get_toolkit_function_registration)();
      for (auto& fn: functions) {
        if (!regentry.modulename.empty()) {
          fn.name = regentry.modulename + "." + fn.name;
        }
        fn.description["file"] = regentry.original_soname;
        logstream(LOG_INFO) << "Adding function: " << fn.name << std::endl;
        regentry.functions.push_back(fn.name);
      }
      toolkit_functions->register_toolkit_function(functions);
    }

/**************************************************************************/
/*                                                                        */
/*                           Class Registration                           */
/*                                                                        */
/**************************************************************************/

    std::vector<std::string> toolkit_class_reg_names
                {"get_toolkit_class_registration",
                 "_Z30get_toolkit_class_registrationv",
                 "__Z30get_toolkit_class_registrationv"};
    get_toolkit_class_registration_type get_toolkit_class_registration = nullptr;
    for (auto reg_name : toolkit_class_reg_names) {
      get_toolkit_class_registration =
          reinterpret_cast<get_toolkit_class_registration_type>
          (
#ifndef _WIN32
           dlsym(dl, reg_name.c_str())
#else
           (void *)GetProcAddress((HMODULE)dl, reg_name.c_str())
#endif
           );
      if (get_toolkit_class_registration != nullptr) break;
    }

    // register classes
    if (get_toolkit_class_registration) {
      auto class_reg = (*get_toolkit_class_registration)();
      for (auto& cl: class_reg) {
        if (!regentry.modulename.empty()) {
          cl.name = regentry.modulename + "." + cl.name;
        }
        cl.description["file"] = regentry.original_soname;
        logstream(LOG_INFO) << "Adding class : " << cl.name << std::endl;
        regentry.functions.push_back(cl.name);
      }
      classes->register_toolkit_class(class_reg);
    }


    if (regentry.functions.empty() && regentry.classes.empty()) {
      // nothing has been registered! unload the dl
#ifndef _WIN32
      dlclose(dl);
#else
      FreeLibrary((HMODULE)dl);
#endif
      return "No functions or classes registered by " + sanitize_url(soname);
    }
    // note that it is possible to load a toolkit multiple times.
    // It is not safe to unload previously loaded toolkits since I may have
    // a reference to it (for instance a class). We just keep loading over
    // and hope for the best.

    // store and remember the dlhandle and what was registered;
    dynamic_loaded_toolkits[regentry.original_soname] = regentry;
    return std::string();
  }