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;
}