| /******************************************************************************* |
| * Copyright (c) 2009, 2010 Wind River Systems, Inc. and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * and Eclipse Distribution License v1.0 which accompany this distribution. |
| * The Eclipse Public License is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * and the Eclipse Distribution License is available at |
| * http://www.eclipse.org/org/documents/edl-v10.php. |
| * |
| * Contributors: |
| * Wind River Systems - initial API and implementation |
| *******************************************************************************/ |
| |
| /* |
| * This module provides notifications of process/thread exited or stopped. |
| */ |
| |
| #include <config.h> |
| |
| #if (ENABLE_DebugContext && !ENABLE_ContextProxy) || SERVICE_Processes || SERVICE_Terminals |
| |
| #include <assert.h> |
| #include <errno.h> |
| #include <framework/errors.h> |
| #include <framework/myalloc.h> |
| #include <framework/events.h> |
| #include <framework/trace.h> |
| #include <framework/asyncreq.h> |
| #include <framework/waitpid.h> |
| |
| typedef struct WaitPIDListenerInfo { |
| WaitPIDListener * listener; |
| void * args; |
| } WaitPIDListenerInfo; |
| |
| #define MAX_LISTENERS 8 |
| |
| static WaitPIDListenerInfo listeners[MAX_LISTENERS]; |
| static int listener_cnt = 0; |
| |
| static void init(void); |
| |
| void add_waitpid_listener(WaitPIDListener * listener, void * args) { |
| assert(listener_cnt < MAX_LISTENERS); |
| if (listener_cnt == 0) init(); |
| listeners[listener_cnt].listener = listener; |
| listeners[listener_cnt].args = args; |
| listener_cnt++; |
| } |
| |
| #if defined(WIN32) |
| |
| #define MAX_HANDLES 64 |
| |
| typedef struct WaitPIDThread { |
| DWORD thread; |
| HANDLE handles[MAX_HANDLES]; |
| DWORD handle_cnt; |
| int shutdown; |
| struct WaitPIDThread * next; |
| } WaitPIDThread; |
| |
| static WaitPIDThread * threads = NULL; |
| static HANDLE semaphore = NULL; |
| |
| #define check_error_win32(ok) { if (!(ok)) check_error(set_win32_errno(GetLastError())); } |
| |
| static void waitpid_event(void * args) { |
| int i; |
| HANDLE prs = args; |
| DWORD pid = GetProcessId(prs); |
| DWORD exit_code = 0; |
| check_error_win32(GetExitCodeProcess(prs, &exit_code)); |
| for (i = 0; i < listener_cnt; i++) { |
| listeners[i].listener(pid, 1, exit_code, 0, 0, 0, listeners[i].args); |
| } |
| check_error_win32(CloseHandle(prs)); |
| } |
| |
| static DWORD WINAPI waitpid_thread_func(LPVOID x) { |
| WaitPIDThread * thread = (WaitPIDThread *)x; |
| check_error_win32(WaitForSingleObject(semaphore, INFINITE) != WAIT_FAILED); |
| while (!thread->shutdown) { |
| DWORD n = 0; |
| HANDLE arr[MAX_HANDLES]; |
| DWORD cnt = thread->handle_cnt; |
| memcpy(arr, thread->handles, cnt * sizeof(HANDLE)); |
| check_error_win32(ReleaseSemaphore(semaphore, 1, 0)); |
| n = WaitForMultipleObjects(cnt, arr, FALSE, INFINITE); |
| check_error_win32(n != WAIT_FAILED); |
| check_error_win32(WaitForSingleObject(semaphore, INFINITE) != WAIT_FAILED); |
| if (n > 0) { |
| assert(thread->handles[n] == arr[n]); |
| post_event(waitpid_event, thread->handles[n]); |
| memmove(thread->handles + n, thread->handles + n + 1, (thread->handle_cnt - n - 1) * sizeof(HANDLE)); |
| thread->handle_cnt--; |
| } |
| } |
| return 0; |
| } |
| |
| static void init(void) { |
| assert(threads == NULL); |
| semaphore = CreateSemaphore(NULL, 1, 1, NULL); |
| } |
| |
| void add_waitpid_process(int pid) { |
| HANDLE prs = NULL; |
| WaitPIDThread * thread = threads; |
| check_error_win32(WaitForSingleObject(semaphore, INFINITE) != WAIT_FAILED); |
| while (thread != NULL && thread->handle_cnt >= MAX_HANDLES) thread = thread->next; |
| if (thread == NULL) { |
| thread = (WaitPIDThread *)loc_alloc_zero(sizeof(WaitPIDThread)); |
| thread->next = threads; |
| threads = thread; |
| check_error_win32((thread->handles[thread->handle_cnt++] = CreateEvent(NULL, 0, 0, NULL)) != NULL); |
| check_error_win32(CreateThread(NULL, 0, waitpid_thread_func, thread, 0, &thread->thread) != NULL); |
| } |
| check_error_win32((prs = OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE, FALSE, pid)) != NULL); |
| thread->handles[thread->handle_cnt++] = prs; |
| check_error_win32(SetEvent(thread->handles[0])); |
| check_error_win32(ReleaseSemaphore(semaphore, 1, 0)); |
| } |
| |
| #elif defined(_WRS_KERNEL) |
| |
| #include <taskHookLib.h> |
| |
| typedef struct EventInfo { |
| UINT32 pid; |
| SEM_ID signal; |
| } EventInfo; |
| |
| static WIND_TCB * main_thread; |
| |
| static void task_delete_event(void * args) { |
| int i; |
| EventInfo * info = args; |
| for (i = 0; i < listener_cnt; i++) { |
| listeners[i].listener(info->pid, 1, 0, 0, 0, 0, listeners[i].args); |
| } |
| semGive(info->signal); |
| } |
| |
| static void task_delete_hook(WIND_TCB * tcb) { |
| if (tcb != main_thread && taskIdCurrent != main_thread) { |
| EventInfo info; |
| VX_COUNTING_SEMAPHORE(signal_mem); |
| info.signal = semCInitialize(signal_mem, SEM_Q_FIFO, 0); |
| info.pid = (UINT32)tcb; |
| post_event(task_delete_event, &info); |
| semTake(info.signal, WAIT_FOREVER); |
| semTerminate(info.signal); |
| } |
| } |
| |
| static void init(void) { |
| main_thread = taskIdCurrent; |
| taskDeleteHookAdd((FUNCPTR)task_delete_hook); |
| } |
| |
| void add_waitpid_process(int pid) { |
| } |
| |
| #else |
| |
| #include <sys/wait.h> |
| |
| static void waitpid_done(void * arg) { |
| int i; |
| AsyncReqInfo * req = (AsyncReqInfo *)arg; |
| pid_t pid = req->u.wpid.pid; |
| int status = req->u.wpid.status; |
| int error = req->error; |
| int exited = 0; |
| int exit_code = 0; |
| int signal = 0; |
| int event_code = 0; |
| int syscall = 0; |
| |
| trace(LOG_WAITPID, "waitpid: pid %d status %#x, error %d", pid, status, error); |
| assert(req->u.wpid.rval == -1 || req->u.wpid.rval == pid); |
| |
| if (req->u.wpid.rval == -1) { |
| assert(error); |
| trace(LOG_ALWAYS, "waitpid error (pid %d): %d %d", pid, error, errno_to_str(error)); |
| exited = 1; |
| exit_code = error; |
| } |
| else if (WIFEXITED(status)) { |
| exited = 1; |
| exit_code = WEXITSTATUS(status); |
| trace(LOG_WAITPID, "waitpid: pid %d exited, exit code %d", pid, exit_code); |
| } |
| else if (WIFSIGNALED(status)) { |
| exited = 1; |
| signal = WTERMSIG(status); |
| trace(LOG_WAITPID, "waitpid: pid %d terminated, signal %d", pid, signal); |
| } |
| else if (WIFSTOPPED(status)) { |
| signal = WSTOPSIG(status) & 0x7f; |
| event_code = status >> 16; |
| syscall = (WSTOPSIG(status) & 0x80) != 0; |
| trace(LOG_WAITPID, "waitpid: pid %d suspended, signal %d, event code %d", pid, signal, event_code); |
| } |
| else { |
| trace(LOG_ALWAYS, "unexpected status (0x%x) from waitpid (pid %d)", status, pid); |
| exited = 1; |
| } |
| for (i = 0; i < listener_cnt; i++) { |
| listeners[i].listener(pid, exited, exit_code, signal, event_code, syscall, listeners[i].args); |
| } |
| if (exited) { |
| loc_free(req); |
| } |
| else { |
| req->error = 0; |
| req->u.wpid.status = 0; |
| async_req_post(req); |
| } |
| } |
| |
| void add_waitpid_process(int pid) { |
| AsyncReqInfo * req = (AsyncReqInfo *)loc_alloc_zero(sizeof(AsyncReqInfo)); |
| assert(listener_cnt > 0); |
| req->done = waitpid_done; |
| req->type = AsyncReqWaitpid; |
| req->u.wpid.pid = pid; |
| #if defined(__linux__) |
| req->u.wpid.options |= __WALL; |
| #endif |
| async_req_post(req); |
| } |
| |
| static void init(void) { |
| } |
| |
| #endif |
| #endif |