error_status_t WinutilsCreateProcessAsUser()

in hadoop-common-project/hadoop-common/src/main/winutils/service.c [958:1221]


error_status_t WinutilsCreateProcessAsUser( 
    /* [in] */ handle_t IDL_handle,
    /* [in] */ int nmPid,
    /* [in] */ CREATE_PROCESS_REQUEST *request,
    /* [out] */ CREATE_PROCESS_RESPONSE **response) {

  DWORD dwError = ERROR_SUCCESS;
  LPCWSTR inserts[] = {request->cwd, request->jobName, request->user, request->pidFile, request->cmdLine, NULL};
  WCHAR winutilsPath[MAX_PATH];
  WCHAR fullCmdLine[32768];
  HANDLE taskStdInRd = INVALID_HANDLE_VALUE, taskStdInWr = INVALID_HANDLE_VALUE,
    taskStdOutRd = INVALID_HANDLE_VALUE, taskStdOutWr = INVALID_HANDLE_VALUE,
    taskStdErrRd = INVALID_HANDLE_VALUE, taskStdErrWr = INVALID_HANDLE_VALUE,
    hNmProcess = INVALID_HANDLE_VALUE,
    hDuplicateProcess = INVALID_HANDLE_VALUE,
    hDuplicateThread = INVALID_HANDLE_VALUE,
    hDuplicateStdIn  = INVALID_HANDLE_VALUE,
    hDuplicateStdOut = INVALID_HANDLE_VALUE,
    hDuplicateStdErr = INVALID_HANDLE_VALUE,
    hSelfProcess = INVALID_HANDLE_VALUE,
    hJob = INVALID_HANDLE_VALUE;
  BOOL fMustCleanupProcess = FALSE;
  
  HRESULT hr;
  STARTUPINFO si;
  PROCESS_INFORMATION pi;
  SECURITY_ATTRIBUTES saTaskStdInOutErr;

  ZeroMemory( &si, sizeof(si) );
  si.cb = sizeof(si);
  ZeroMemory( &pi, sizeof(pi) );
  pi.hProcess = INVALID_HANDLE_VALUE;
  pi.hThread = INVALID_HANDLE_VALUE;
  ZeroMemory( &saTaskStdInOutErr, sizeof(saTaskStdInOutErr));
  

  if (gJobName) {
    hJob = OpenJobObject(JOB_OBJECT_ASSIGN_PROCESS, FALSE, gJobName);
    if (!hJob) {
      dwError = GetLastError();
      ReportSvcCheckError(EVENTLOG_ERROR_TYPE, SERVICE_CATEGORY, 
        dwError, L"OpenJobObject");
      goto done;
    }
  }


  // NB: GetCurrentProcess returns a pseudo-handle that just so happens 
  // has the value -1, ie. INVALID_HANDLE_VALUE. It cannot fail.
  // 
  hSelfProcess = GetCurrentProcess();

  hNmProcess = OpenProcess(PROCESS_DUP_HANDLE, FALSE, nmPid);
  if (NULL == hNmProcess) {
    dwError = GetLastError();
    ReportSvcCheckError(EVENTLOG_ERROR_TYPE, SERVICE_CATEGORY, 
      dwError, L"OpenProcess");
    goto done;
  }

  GetModuleFileName(NULL, winutilsPath, sizeof(winutilsPath)/sizeof(WCHAR));
  dwError = GetLastError(); // Always check after GetModuleFileName for ERROR_INSSUFICIENT_BUFFER
  if (dwError) {
    ReportSvcCheckError(EVENTLOG_ERROR_TYPE, SERVICE_CATEGORY, 
      dwError, L"GetModuleFileName");
    goto done;
  }

  // NB. We can call CreateProcess("wintuls","task create ...") or we can call
  // CreateProcess(NULL, "winutils task create"). Only the second form passes "task" as
  // argv[1], as expected by main. First form passes "task" as argv[0] and main fails.
  
  hr = StringCbPrintf(fullCmdLine, sizeof(fullCmdLine), L"\"%s\" task createAsUser %ls %ls %ls %ls",
    winutilsPath,
    request->jobName, request->user, request->pidFile, request->cmdLine);
  if (FAILED(hr)) {
    ReportSvcCheckError(EVENTLOG_ERROR_TYPE, SERVICE_CATEGORY, 
      hr, L"StringCbPrintf:fullCmdLine");
    goto done;
  }

  LogDebugMessage(L"[%ls]: %ls %ls\n", request->cwd, winutilsPath, fullCmdLine);

  // stdin/stdout/stderr redirection is handled here
  // We create 3 anonymous named pipes. 
  // Security attributes are required so that the handles can be inherited.
  // We assign one end of the pipe to the process (stdin gets a read end, stdout gets a write end)
  // We then duplicate the other end in the NM process, and we close our own handle
  // Finally we return the duplicate handle values to the NM
  // The NM will attach Java file dscriptors to the duplicated handles and 
  // read/write them as ordinary Java InputStream/OutputStream objects

  si.dwFlags |= STARTF_USESTDHANDLES;

  saTaskStdInOutErr.nLength = sizeof(SECURITY_ATTRIBUTES); 
  saTaskStdInOutErr.bInheritHandle = TRUE; 
  saTaskStdInOutErr.lpSecurityDescriptor = NULL; 

  if (!CreatePipe(&taskStdInRd, &taskStdInWr, &saTaskStdInOutErr, 0)) {
    dwError = GetLastError();
    goto done;
  }
  if (!SetHandleInformation(taskStdInWr, HANDLE_FLAG_INHERIT, FALSE)) {
    dwError = GetLastError();
    goto done;
  }
  si.hStdInput  = taskStdInRd;

  if (!CreatePipe(&taskStdOutRd, &taskStdOutWr, &saTaskStdInOutErr, 0)) {
    dwError = GetLastError();
    goto done;
  }
  if (!SetHandleInformation(taskStdOutRd, HANDLE_FLAG_INHERIT, FALSE)) {
    dwError = GetLastError();
    goto done;
  }
  si.hStdOutput  = taskStdOutWr;

  if (!CreatePipe(&taskStdErrRd, &taskStdErrWr, &saTaskStdInOutErr, 0)) {
    dwError = GetLastError();
    goto done;
  }
  if (!SetHandleInformation(taskStdErrRd, HANDLE_FLAG_INHERIT, FALSE)) {
    dwError = GetLastError();
    goto done;
  }
  si.hStdError  = taskStdErrWr;

  if (!CreateProcess(
    NULL,                     // lpApplicationName,
    fullCmdLine,              // lpCommandLine,
    NULL,                     // lpProcessAttributes,
    NULL,                     // lpThreadAttributes,
    TRUE,                     // bInheritHandles,
    CREATE_SUSPENDED,         // dwCreationFlags,
    NULL,                     // lpEnvironment,
    request->cwd,             // lpCurrentDirectory,
    &si,                      // lpStartupInfo
    &pi)) {                   // lpProcessInformation
    
    dwError = GetLastError();
    ReportSvcCheckError(EVENTLOG_ERROR_TYPE, SERVICE_CATEGORY, 
      dwError, L"CreateProcess");
    goto done;
  }

  fMustCleanupProcess = TRUE;

  LogDebugMessage(L"CreateProcess: pid:%x\n", pi.dwProcessId);

  if (INVALID_HANDLE_VALUE != hJob) {
    if (!AssignProcessToJobObject(hJob, pi.hProcess)) {
      dwError = GetLastError();
      goto done;
    }
  }

  // Grant full access to the container user on the 'winutils task createAsUser ...' helper process
  dwError = AddNodeManagerAndUserACEsToObject(pi.hProcess, request->user, PROCESS_ALL_ACCESS);
  if (dwError) {
    LogDebugMessage(L"failed: AddNodeManagerAndUserACEsToObject\n");
    goto done;
  }

  if (!DuplicateHandle(hSelfProcess, pi.hProcess, hNmProcess,
    &hDuplicateProcess, 0, FALSE, DUPLICATE_SAME_ACCESS)) {
    dwError = GetLastError();
    LogDebugMessage(L"failed: pi.hProcess\n");
    goto done;
  }
  
  if (!DuplicateHandle(hSelfProcess, pi.hThread, hNmProcess,
    &hDuplicateThread, 0, FALSE, DUPLICATE_SAME_ACCESS)) {
    dwError = GetLastError();
    LogDebugMessage(L"failed: pi.hThread\n");
    goto done;
  }

  if (!DuplicateHandle(hSelfProcess, taskStdInWr, hNmProcess,
    &hDuplicateStdIn, 0, FALSE, DUPLICATE_SAME_ACCESS)) {
    dwError = GetLastError();
    LogDebugMessage(L"failed: taskStdInWr\n");
    goto done;
  }

  if (!DuplicateHandle(hSelfProcess, taskStdOutRd, hNmProcess,
    &hDuplicateStdOut, 0, FALSE, DUPLICATE_SAME_ACCESS)) {
    dwError = GetLastError();
    LogDebugMessage(L"failed: taskStdOutRd\n");
    goto done;
  }

  if (!DuplicateHandle(hSelfProcess, taskStdErrRd, hNmProcess,
    &hDuplicateStdErr, 0, FALSE, DUPLICATE_SAME_ACCESS)) {
    dwError = GetLastError();
    LogDebugMessage(L"failed: taskStdErrRd\n");
    goto done;
  }

  *response = (CREATE_PROCESS_RESPONSE*) MIDL_user_allocate(sizeof(CREATE_PROCESS_RESPONSE));
  if (NULL == *response) {
    dwError = ERROR_OUTOFMEMORY;
    LogDebugMessage(L"Failed to allocate CREATE_PROCESS_RESPONSE* response\n");
    goto done;
  }

  // We're now transfering ownership of the duplicated handles to the caller
  // If the RPC call fails *after* this point the handles are leaked inside the NM process
  // Note that there are no more API calls, only assignments. A failure could occur only if
  // foced (process kill) or hardware error (faulty memory, processort bit flip etc).

  // as MIDL has no 'HANDLE' type, the (LONG_PTR) is used instead
  (*response)->hProcess = (LONG_PTR)hDuplicateProcess;
  (*response)->hThread = (LONG_PTR)hDuplicateThread;
  (*response)->hStdIn = (LONG_PTR)hDuplicateStdIn;
  (*response)->hStdOut = (LONG_PTR)hDuplicateStdOut;
  (*response)->hStdErr = (LONG_PTR)hDuplicateStdErr;

  fMustCleanupProcess = FALSE;
  
done:

  if (fMustCleanupProcess) {
    LogDebugMessage(L"Cleaning process: %d due to error:%d\n", pi.dwProcessId, dwError);
    TerminateProcess(pi.hProcess, EXIT_FAILURE);

    // cleanup the duplicate handles inside the NM.

    if (INVALID_HANDLE_VALUE != hDuplicateProcess) {
      DuplicateHandle(hNmProcess, hDuplicateProcess, NULL, NULL, 0, FALSE, DUPLICATE_CLOSE_SOURCE);
    }
    if (INVALID_HANDLE_VALUE != hDuplicateThread) {
      DuplicateHandle(hNmProcess, hDuplicateThread, NULL, NULL, 0, FALSE, DUPLICATE_CLOSE_SOURCE);
    }
    if (INVALID_HANDLE_VALUE != hDuplicateStdIn) {
      DuplicateHandle(hNmProcess, hDuplicateStdIn, NULL, NULL, 0, FALSE, DUPLICATE_CLOSE_SOURCE);
    }
    if (INVALID_HANDLE_VALUE != hDuplicateStdOut) {
      DuplicateHandle(hNmProcess, hDuplicateStdOut, NULL, NULL, 0, FALSE, DUPLICATE_CLOSE_SOURCE);
    }
    if (INVALID_HANDLE_VALUE != hDuplicateStdErr) {
      DuplicateHandle(hNmProcess, hDuplicateStdErr, NULL, NULL, 0, FALSE, DUPLICATE_CLOSE_SOURCE);
    }
  }

  if (INVALID_HANDLE_VALUE != hSelfProcess) CloseHandle(hSelfProcess);
  if (INVALID_HANDLE_VALUE != hNmProcess) CloseHandle(hNmProcess);
  if (INVALID_HANDLE_VALUE != taskStdInRd) CloseHandle(taskStdInRd);
  if (INVALID_HANDLE_VALUE != taskStdInWr) CloseHandle(taskStdInWr);
  if (INVALID_HANDLE_VALUE != taskStdOutRd) CloseHandle(taskStdOutRd);
  if (INVALID_HANDLE_VALUE != taskStdOutWr) CloseHandle(taskStdOutWr);
  if (INVALID_HANDLE_VALUE != taskStdErrRd) CloseHandle(taskStdErrRd);
  if (INVALID_HANDLE_VALUE != taskStdErrWr) CloseHandle(taskStdErrWr);


  // This is closing our own process/thread handles. 
  // If the transfer was succesfull the NM has its own duplicates (if any)
  if (INVALID_HANDLE_VALUE != pi.hThread) CloseHandle(pi.hThread);
  if (INVALID_HANDLE_VALUE != pi.hProcess) CloseHandle(pi.hProcess);

  if (hJob) CloseHandle(hJob);

  return dwError;
}