blob: 6ccf43905112c96f9691b660895a39588a27c5e3 [file] [log] [blame]
/* **********************************************************
* Copyright (c) 2013 Google, Inc. All rights reserved.
* Copyright (c) 2003 VMware, Inc. All rights reserved.
* **********************************************************/
/*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of VMware, Inc. nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
#define _CRT_SECURE_NO_WARNINGS 1
#include "tools.h"
#ifdef UNIX
# include <sys/wait.h>
# include <stdlib.h>
# include <unistd.h>
# include <string.h>
# include <signal.h>
#endif
#ifdef WINDOWS
static void
fatal_error(const char *function)
{
char message[256];
_snprintf(message, BUFFER_SIZE_ELEMENTS(message),
"Function %s() failed!\nError code 0x%x.\nExiting now.\n",
function, GetLastError());
print(message);
exit(1);
}
#endif
int
main(int argc, char **argv)
{
#ifdef WINDOWS
# define CMDLINE_SIZE (MAX_PATH/*argv[0]*/ + 20/*" %p"*/)
HANDLE event;
char cmdline[CMDLINE_SIZE];
if (argc == 1) {
/* parent process */
STARTUPINFO si = { sizeof(STARTUPINFO) };
PROCESS_INFORMATION pi;
HANDLE job, job2, job3;
JOBOBJECT_EXTENDED_LIMIT_INFORMATION limit = {0,};
DWORD exitcode = (DWORD)-1;
/* For synchronization we create an inherited event */
SECURITY_ATTRIBUTES sa = {sizeof(sa), NULL, TRUE/*inherit*/};
event = CreateEvent(&sa, FALSE/*manual reset*/, FALSE/*start unset*/, NULL);
if (event == NULL)
print("Failed to create event");
_snprintf(cmdline, BUFFER_SIZE_ELEMENTS(cmdline), "%s %p", argv[0], event);
print("creating child #1\n");
if (!CreateProcess(argv[0], cmdline, NULL, NULL, TRUE/*inherit handles*/,
0, NULL, NULL, &si, &pi))
fatal_error("CreateProcess");
if (WaitForSingleObject(event, INFINITE) == WAIT_FAILED)
fatal_error("WaitForSingleObject");
print("terminating child #1 by NtTerminateProcess\n");
if (!TerminateProcess(pi.hProcess, 42))
fatal_error("TerminateProcess");
if (WaitForSingleObject(pi.hProcess, INFINITE) == WAIT_FAILED)
fatal_error("WaitForSingleObject");
GetExitCodeProcess(pi.hProcess, &exitcode);
print("child #1 exit code = %d\n", exitcode);
if (!ResetEvent(event))
fatal_error("ResetEvent");
print("creating child #2\n");
/* In an msys shell, CREATE_BREAKAWAY_FROM_JOB is required because the shell uses
* job objects and by default does not have permission to break away (i#1454).
*/
if (!CreateProcess(argv[0], cmdline, NULL, NULL, TRUE/*inherit handles*/,
CREATE_SUSPENDED | CREATE_BREAKAWAY_FROM_JOB,
NULL, NULL, &si, &pi))
print("CreateProcess failure\n");
job = CreateJobObject(NULL, "drx-test job");
if (!AssignProcessToJobObject(job, pi.hProcess))
fatal_error("AssignProcessToJobObject");
if (!ResumeThread(pi.hThread))
fatal_error("ResumeThread");
if (!CloseHandle(pi.hThread))
fatal_error("CloseHandle");
if (!(WaitForSingleObject(event, INFINITE) != WAIT_FAILED))
fatal_error("WaitForSingleObject");
print("terminating child #2 by NtTerminateJobObject\n");
if (!TerminateJobObject(job, 123456))
fatal_error("TerminateJobObject");
if (!CloseHandle(job))
fatal_error("CloseHandle");
if (WaitForSingleObject(pi.hProcess, INFINITE) == WAIT_FAILED)
fatal_error("WaitForSingleObject");
GetExitCodeProcess(pi.hProcess, &exitcode);
print("child #2 exit code = %d\n", exitcode);
if (!ResetEvent(event))
fatal_error("ResetEvent");
print("creating child #3\n");
if (!CreateProcess(argv[0], cmdline, NULL, NULL, TRUE/*inherit handles*/,
CREATE_SUSPENDED | CREATE_BREAKAWAY_FROM_JOB,
NULL, NULL, &si, &pi))
print("CreateProcess failure\n");
job = CreateJobObject(NULL, "drx-test job");
if (!AssignProcessToJobObject(job, pi.hProcess))
fatal_error("AssignProcessToJobObject");
limit.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
if (!SetInformationJobObject(job, JobObjectExtendedLimitInformation,
&limit, sizeof(limit)))
fatal_error("SetInformationJobObject");
if (!ResumeThread(pi.hThread))
fatal_error("ResumeThread");
if (!CloseHandle(pi.hThread))
fatal_error("CloseHandle");
if (WaitForSingleObject(event, INFINITE) == WAIT_FAILED)
fatal_error("WaitForSingleObject");
print("terminating child #3 by closing job handle\n");
if (!CloseHandle(job))
fatal_error("CloseHandle");
if (WaitForSingleObject(pi.hProcess, INFINITE) == WAIT_FAILED)
fatal_error("WaitForSingleObject");
if (!GetExitCodeProcess(pi.hProcess, &exitcode))
fatal_error("GetExitCodeProcess");
print("child #3 exit code = %d\n", exitcode);
/* Test DuplicateHandle (DrMem i#1401) */
print("creating child #4\n");
if (!CreateProcess(argv[0], cmdline, NULL, NULL, TRUE/*inherit handles*/,
CREATE_SUSPENDED | CREATE_BREAKAWAY_FROM_JOB,
NULL, NULL, &si, &pi))
fatal_error("CreateProcess");
job = CreateJobObject(NULL, "drx-test job");
if (!AssignProcessToJobObject(job, pi.hProcess))
fatal_error("AssignProcessToJobObject");
limit.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
if (!SetInformationJobObject(job, JobObjectExtendedLimitInformation,
&limit, sizeof(limit)))
fatal_error("SetInformationJobObject");
if (!DuplicateHandle(GetCurrentProcess(), job, GetCurrentProcess(), &job2, 0,
FALSE, DUPLICATE_SAME_ACCESS))
fatal_error("DuplicateHandle");
if (!DuplicateHandle(GetCurrentProcess(), job, GetCurrentProcess(), &job3, 0,
FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS))
fatal_error("DuplicateHandle");
if (!ResumeThread(pi.hThread))
fatal_error("ResumeThread");
if (!CloseHandle(pi.hThread))
fatal_error("CloseHandle");
if (WaitForSingleObject(event, INFINITE) == WAIT_FAILED)
fatal_error("WaitForSingleObject");
print("terminating child #4 by closing both job handles\n");
if (!CloseHandle(job2))
fatal_error("CloseHandle");
if (!CloseHandle(job3))
fatal_error("CloseHandle");
if (WaitForSingleObject(pi.hProcess, INFINITE) == WAIT_FAILED)
fatal_error("WaitForSingleObject");
if (!GetExitCodeProcess(pi.hProcess, &exitcode))
fatal_error("GetExitCodeProcess");
print("child #4 exit code = %d\n", exitcode);
}
else { /* child process */
int iter = 0;
if (sscanf(argv[1], "%p", &event) != 1) {
print("Failed to obtain event handle from %s\n", argv[1]);
return -1;
}
if (!SetEvent(event))
print("Failed to set event\n");
/* spin until parent kills us or we time out */
while (iter++ < 12) {
Sleep(5000);
}
}
CloseHandle(event);
#else /* WINDOWS */
int pipefd[2];
pid_t cpid;
char buf = 0;
if (pipe(pipefd) == -1) {
perror("pipe");
exit(1);
}
print("creating child\n");
cpid = fork();
if (cpid == -1) {
perror("fork");
exit(1);
} else if (cpid > 0) {
/* parent */
int status;
close(pipefd[1]); /* close unused write end */
if (read(pipefd[0], &buf, sizeof(buf)) <= 0) {
perror("pipe read failed");
exit(1);
}
print("terminating child by sending SIGKILL\n");
kill(cpid, SIGKILL);
wait(&status); /* wait for child */
close(pipefd[0]);
print("child exit code = %d\n", status);
} else {
/* child */
int iter = 0;
close(pipefd[0]); /* close unused read end */
write(pipefd[1], &buf, sizeof(buf));
close(pipefd[1]);
/* spin until parent kills us or we time out */
while (iter++ < 12) {
sleep(5);
}
}
#endif /* UNIX */
return 0;
}