/*********************************************************************** * server_init_process_done */ NTSTATUS server_init_process_done(void) { PEB *peb = NtCurrentTeb()->Peb; IMAGE_NT_HEADERS *nt = RtlImageNtHeader( peb->ImageBaseAddress ); NTSTATUS status; /* Install signal handlers; this cannot be done earlier, since we cannot * send exceptions to the debugger before the create process event that * is sent by REQ_INIT_PROCESS_DONE. * We do need the handlers in place by the time the request is over, so * we set them up here. If we segfault between here and the server call * something is very wrong... */ signal_init_process(); /* Signal the parent process to continue */ SERVER_START_REQ( init_process_done ) { req->module = wine_server_client_ptr( peb->ImageBaseAddress ); #ifdef __i386__ req->ldt_copy = wine_server_client_ptr( &wine_ldt_copy ); #endif req->entry = wine_server_client_ptr( (char *)peb->ImageBaseAddress + nt->OptionalHeader.AddressOfEntryPoint ); req->gui = (nt->OptionalHeader.Subsystem != IMAGE_SUBSYSTEM_WINDOWS_CUI); status = wine_server_call( req ); } SERVER_END_REQ; return status; }
/*********************************************************************** * SetWinEventHook [USER32.@] * * Set up an event hook for a set of events. * * PARAMS * event_min [I] Lowest event handled by pfnProc * event_max [I] Highest event handled by pfnProc * inst [I] DLL containing pfnProc * proc [I] Callback event hook function * pid [I] Process to get events from, or 0 for all processes * tid [I] Thread to get events from, or 0 for all threads * flags [I] Flags indicating the status of pfnProc * * RETURNS * Success: A handle representing the hook. * Failure: A NULL handle. */ HWINEVENTHOOK WINAPI SetWinEventHook(DWORD event_min, DWORD event_max, HMODULE inst, WINEVENTPROC proc, DWORD pid, DWORD tid, DWORD flags) { HWINEVENTHOOK handle = 0; WCHAR module[MAX_PATH]; DWORD len; TRACE("%d,%d,%p,%p,%08x,%04x,%08x\n", event_min, event_max, inst, proc, pid, tid, flags); if (inst) { if (!(len = GetModuleFileNameW(inst, module, MAX_PATH)) || len >= MAX_PATH) inst = 0; } if ((flags & WINEVENT_INCONTEXT) && !inst) { SetLastError(ERROR_HOOK_NEEDS_HMOD); return 0; } if (event_min > event_max) { SetLastError(ERROR_INVALID_HOOK_FILTER); return 0; } /* FIXME: what if the tid or pid belongs to another process? */ if (tid) /* thread-local hook */ inst = 0; SERVER_START_REQ( set_hook ) { req->id = WH_WINEVENT; req->pid = pid; req->tid = tid; req->event_min = event_min; req->event_max = event_max; req->flags = flags; req->unicode = 1; if (inst) /* make proc relative to the module base */ { req->proc = wine_server_client_ptr( (void *)((char *)proc - (char *)inst) ); wine_server_add_data( req, module, strlenW(module) * sizeof(WCHAR) ); } else req->proc = wine_server_client_ptr( proc ); if (!wine_server_call_err( req )) { handle = wine_server_ptr_handle( reply->handle ); get_user_thread_info()->active_hooks = reply->active_hooks; } } SERVER_END_REQ; TRACE("-> %p\n", handle); return handle; }
/*********************************************************************** * server_init_thread * * Send an init thread request. Return 0 if OK. */ size_t server_init_thread( int unix_pid, int unix_tid, void *entry_point ) { int ret; int reply_pipe[2]; struct sigaction sig_act; size_t info_size; sig_act.sa_handler = SIG_IGN; sig_act.sa_flags = 0; sigemptyset( &sig_act.sa_mask ); /* ignore SIGPIPE so that we get an EPIPE error instead */ sigaction( SIGPIPE, &sig_act, NULL ); /* automatic child reaping to avoid zombies */ #ifdef SA_NOCLDWAIT sig_act.sa_flags |= SA_NOCLDWAIT; #endif sigaction( SIGCHLD, &sig_act, NULL ); /* create the server->client communication pipes */ if (pipe( reply_pipe ) == -1) server_protocol_perror( "pipe" ); if (pipe( ntdll_get_thread_data()->wait_fd ) == -1) server_protocol_perror( "pipe" ); wine_server_send_fd( reply_pipe[1] ); wine_server_send_fd( ntdll_get_thread_data()->wait_fd[1] ); ntdll_get_thread_data()->reply_fd = reply_pipe[0]; close( reply_pipe[1] ); /* set close on exec flag */ fcntl( ntdll_get_thread_data()->reply_fd, F_SETFD, 1 ); fcntl( ntdll_get_thread_data()->wait_fd[0], F_SETFD, 1 ); fcntl( ntdll_get_thread_data()->wait_fd[1], F_SETFD, 1 ); SERVER_START_REQ( init_thread ) { req->unix_pid = unix_pid; req->unix_tid = unix_tid; req->teb = wine_server_client_ptr( NtCurrentTeb() ); req->peb = wine_server_client_ptr( NtCurrentTeb()->Peb ); req->entry = wine_server_client_ptr( entry_point ); req->reply_fd = reply_pipe[1]; req->wait_fd = ntdll_get_thread_data()->wait_fd[1]; req->debug_level = (TRACE_ON(server) != 0); ret = wine_server_call( req ); NtCurrentTeb()->ClientId.UniqueProcess = ULongToHandle(reply->pid); NtCurrentTeb()->ClientId.UniqueThread = ULongToHandle(reply->tid); info_size = reply->info_size; server_start_time = reply->server_start; } SERVER_END_REQ; if (ret) server_protocol_error( "init_thread failed with status %x\n", ret ); return info_size; }
/*********************************************************************** * UnhookWindowsHook (USER32.@) */ BOOL WINAPI UnhookWindowsHook( INT id, HOOKPROC proc ) { BOOL ret; TRACE( "%s %p\n", hook_names[id-WH_MINHOOK], proc ); SERVER_START_REQ( remove_hook ) { req->handle = 0; req->id = id; req->proc = wine_server_client_ptr( proc ); ret = !wine_server_call_err( req ); if (ret) get_user_thread_info()->active_hooks = reply->active_hooks; } SERVER_END_REQ; if (!ret && GetLastError() == ERROR_INVALID_HANDLE) SetLastError( ERROR_INVALID_HOOK_HANDLE ); return ret; }
/****************************************************************************** * NtQueueApcThread (NTDLL.@) */ NTSTATUS WINAPI NtQueueApcThread( HANDLE handle, PNTAPCFUNC func, ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3 ) { NTSTATUS ret; SERVER_START_REQ( queue_apc ) { req->handle = wine_server_obj_handle( handle ); if (func) { req->call.type = APC_USER; req->call.user.func = wine_server_client_ptr( func ); req->call.user.args[0] = arg1; req->call.user.args[1] = arg2; req->call.user.args[2] = arg3; } else req->call.type = APC_NONE; /* wake up only */ ret = wine_server_call( req ); } SERVER_END_REQ; return ret; }
/*********************************************************************** * set_windows_hook * * Implementation of SetWindowsHookExA and SetWindowsHookExW. */ static HHOOK set_windows_hook( INT id, HOOKPROC proc, HINSTANCE inst, DWORD tid, BOOL unicode ) { HHOOK handle = 0; WCHAR module[MAX_PATH]; DWORD len; if (!proc) { SetLastError( ERROR_INVALID_FILTER_PROC ); return 0; } if (tid) /* thread-local hook */ { if (id == WH_JOURNALRECORD || id == WH_JOURNALPLAYBACK || id == WH_KEYBOARD_LL || id == WH_MOUSE_LL || id == WH_SYSMSGFILTER) { /* these can only be global */ SetLastError( ERROR_INVALID_PARAMETER ); return 0; } } else /* system-global hook */ { if (id == WH_KEYBOARD_LL || id == WH_MOUSE_LL) inst = 0; else if (!inst) { SetLastError( ERROR_HOOK_NEEDS_HMOD ); return 0; } } if (inst && (!(len = GetModuleFileNameW( inst, module, MAX_PATH )) || len >= MAX_PATH)) { SetLastError( ERROR_INVALID_PARAMETER ); return 0; } SERVER_START_REQ( set_hook ) { req->id = id; req->pid = 0; req->tid = tid; req->event_min = EVENT_MIN; req->event_max = EVENT_MAX; req->flags = WINEVENT_INCONTEXT; req->unicode = unicode; if (inst) /* make proc relative to the module base */ { req->proc = wine_server_client_ptr( (void *)((char *)proc - (char *)inst) ); wine_server_add_data( req, module, strlenW(module) * sizeof(WCHAR) ); } else req->proc = wine_server_client_ptr( proc ); if (!wine_server_call_err( req )) { handle = wine_server_ptr_handle( reply->handle ); get_user_thread_info()->active_hooks = reply->active_hooks; } } SERVER_END_REQ; TRACE( "%s %p %x -> %p\n", hook_names[id-WH_MINHOOK], proc, tid, handle ); return handle; }
/*********************************************************************** * RtlCreateUserThread (NTDLL.@) */ NTSTATUS WINAPI RtlCreateUserThread( HANDLE process, const SECURITY_DESCRIPTOR *descr, BOOLEAN suspended, PVOID stack_addr, SIZE_T stack_reserve, SIZE_T stack_commit, PRTL_THREAD_START_ROUTINE start, void *param, HANDLE *handle_ptr, CLIENT_ID *id ) { sigset_t sigset; pthread_t pthread_id; pthread_attr_t attr; struct ntdll_thread_data *thread_data; struct startup_info *info = NULL; HANDLE handle = 0, actctx = 0; TEB *teb = NULL; DWORD tid = 0; int request_pipe[2]; NTSTATUS status; if (process != NtCurrentProcess()) { apc_call_t call; apc_result_t result; memset( &call, 0, sizeof(call) ); call.create_thread.type = APC_CREATE_THREAD; call.create_thread.func = wine_server_client_ptr( start ); call.create_thread.arg = wine_server_client_ptr( param ); call.create_thread.reserve = stack_reserve; call.create_thread.commit = stack_commit; call.create_thread.suspend = suspended; status = server_queue_process_apc( process, &call, &result ); if (status != STATUS_SUCCESS) return status; if (result.create_thread.status == STATUS_SUCCESS) { if (id) id->UniqueThread = ULongToHandle(result.create_thread.tid); if (handle_ptr) *handle_ptr = wine_server_ptr_handle( result.create_thread.handle ); else NtClose( wine_server_ptr_handle( result.create_thread.handle )); } return result.create_thread.status; } if (server_pipe( request_pipe ) == -1) return STATUS_TOO_MANY_OPENED_FILES; wine_server_send_fd( request_pipe[0] ); SERVER_START_REQ( new_thread ) { req->access = THREAD_ALL_ACCESS; req->attributes = 0; /* FIXME */ req->suspend = suspended; req->request_fd = request_pipe[0]; if (!(status = wine_server_call( req ))) { handle = wine_server_ptr_handle( reply->handle ); tid = reply->tid; } close( request_pipe[0] ); } SERVER_END_REQ; if (status) { close( request_pipe[1] ); return status; } pthread_sigmask( SIG_BLOCK, &server_block_set, &sigset ); if ((status = signal_alloc_thread( &teb ))) goto error; teb->Peb = NtCurrentTeb()->Peb; teb->ClientId.UniqueProcess = ULongToHandle(GetCurrentProcessId()); teb->ClientId.UniqueThread = ULongToHandle(tid); teb->StaticUnicodeString.Buffer = teb->StaticUnicodeBuffer; teb->StaticUnicodeString.MaximumLength = sizeof(teb->StaticUnicodeBuffer); /* create default activation context frame for new thread */ RtlGetActiveActivationContext(&actctx); if (actctx) { RTL_ACTIVATION_CONTEXT_STACK_FRAME *frame; frame = RtlAllocateHeap(GetProcessHeap(), 0, sizeof(*frame)); frame->Previous = NULL; frame->ActivationContext = actctx; frame->Flags = 0; teb->ActivationContextStack.ActiveFrame = frame; RtlAddRefActivationContext(actctx); } info = (struct startup_info *)(teb + 1); info->teb = teb; info->entry_point = start; info->entry_arg = param; thread_data = (struct ntdll_thread_data *)teb->SpareBytes1; thread_data->request_fd = request_pipe[1]; thread_data->reply_fd = -1; thread_data->wait_fd[0] = -1; thread_data->wait_fd[1] = -1; if ((status = virtual_alloc_thread_stack( teb, stack_reserve, stack_commit ))) goto error; pthread_attr_init( &attr ); pthread_attr_setstack( &attr, teb->DeallocationStack, (char *)teb->Tib.StackBase - (char *)teb->DeallocationStack ); pthread_attr_setscope( &attr, PTHREAD_SCOPE_SYSTEM ); /* force creating a kernel thread */ interlocked_xchg_add( &nb_threads, 1 ); if (pthread_create( &pthread_id, &attr, (void * (*)(void *))start_thread, info )) { interlocked_xchg_add( &nb_threads, -1 ); pthread_attr_destroy( &attr ); status = STATUS_NO_MEMORY; goto error; } pthread_attr_destroy( &attr ); pthread_sigmask( SIG_SETMASK, &sigset, NULL ); if (id) id->UniqueThread = ULongToHandle(tid); if (handle_ptr) *handle_ptr = handle; else NtClose( handle ); return STATUS_SUCCESS; error: if (teb) signal_free_thread( teb ); if (handle) NtClose( handle ); pthread_sigmask( SIG_SETMASK, &sigset, NULL ); close( request_pipe[1] ); return status; }
/*********************************************************************** * server_init_thread * * Send an init thread request. Return 0 if OK. */ size_t server_init_thread( void *entry_point ) { static const int is_win64 = (sizeof(void *) > sizeof(int)); const char *arch = getenv( "WINEARCH" ); int ret; int reply_pipe[2]; struct sigaction sig_act; size_t info_size; sig_act.sa_handler = SIG_IGN; sig_act.sa_flags = 0; sigemptyset( &sig_act.sa_mask ); /* ignore SIGPIPE so that we get an EPIPE error instead */ sigaction( SIGPIPE, &sig_act, NULL ); /* create the server->client communication pipes */ if (server_pipe( reply_pipe ) == -1) server_protocol_perror( "pipe" ); if (server_pipe( ntdll_get_thread_data()->wait_fd ) == -1) server_protocol_perror( "pipe" ); wine_server_send_fd( reply_pipe[1] ); wine_server_send_fd( ntdll_get_thread_data()->wait_fd[1] ); ntdll_get_thread_data()->reply_fd = reply_pipe[0]; close( reply_pipe[1] ); SERVER_START_REQ( init_thread ) { req->unix_pid = getpid(); req->unix_tid = get_unix_tid(); req->teb = wine_server_client_ptr( NtCurrentTeb() ); req->entry = wine_server_client_ptr( entry_point ); req->reply_fd = reply_pipe[1]; req->wait_fd = ntdll_get_thread_data()->wait_fd[1]; req->debug_level = (TRACE_ON(server) != 0); req->cpu = client_cpu; ret = wine_server_call( req ); NtCurrentTeb()->ClientId.UniqueProcess = ULongToHandle(reply->pid); NtCurrentTeb()->ClientId.UniqueThread = ULongToHandle(reply->tid); info_size = reply->info_size; server_start_time = reply->server_start; server_cpus = reply->all_cpus; } SERVER_END_REQ; is_wow64 = !is_win64 && (server_cpus & (1 << CPU_x86_64)) != 0; ntdll_get_thread_data()->wow64_redir = is_wow64; switch (ret) { case STATUS_SUCCESS: if (arch) { if (!strcmp( arch, "win32" ) && (is_win64 || is_wow64)) fatal_error( "WINEARCH set to win32 but '%s' is a 64-bit installation.\n", wine_get_config_dir() ); if (!strcmp( arch, "win64" ) && !is_win64 && !is_wow64) fatal_error( "WINEARCH set to win64 but '%s' is a 32-bit installation.\n", wine_get_config_dir() ); } return info_size; case STATUS_NOT_REGISTRY_FILE: fatal_error( "'%s' is a 32-bit installation, it cannot support 64-bit applications.\n", wine_get_config_dir() ); case STATUS_NOT_SUPPORTED: if (is_win64) fatal_error( "wineserver is 32-bit, it cannot support 64-bit applications.\n" ); else fatal_error( "'%s' is a 64-bit installation, it cannot be used with a 32-bit wineserver.\n", wine_get_config_dir() ); default: server_protocol_error( "init_thread failed with status %x\n", ret ); } }
/*********************************************************************** * OutputDebugStringA (KERNEL32.@) * * Output by an application of an ascii string to a debugger (if attached) * and program log. * * PARAMS * str [I] The message to be logged and given to the debugger. * * RETURNS * * Nothing. */ void WINAPI OutputDebugStringA( LPCSTR str ) { static HANDLE DBWinMutex = NULL; static BOOL mutex_inited = FALSE; if (!str) str = ""; /* send string to attached debugger */ SERVER_START_REQ( output_debug_string ) { req->string = wine_server_client_ptr( str ); req->length = strlen(str) + 1; wine_server_call( req ); } SERVER_END_REQ; WARN("%s\n", debugstr_a(str)); /* send string to a system-wide monitor */ /* FIXME should only send to monitor if no debuggers are attached */ if (!mutex_inited) { /* first call to OutputDebugString, initialize mutex handle */ static const WCHAR mutexname[] = {'D','B','W','i','n','M','u','t','e','x',0}; HANDLE mutex = CreateMutexExW( NULL, mutexname, 0, SYNCHRONIZE ); if (mutex) { if (InterlockedCompareExchangePointer( &DBWinMutex, mutex, 0 ) != 0) { /* someone beat us here... */ CloseHandle( mutex ); } } mutex_inited = TRUE; } if (DBWinMutex) { static const WCHAR shmname[] = {'D','B','W','I','N','_','B','U','F','F','E','R',0}; static const WCHAR eventbuffername[] = {'D','B','W','I','N','_','B','U','F','F','E','R','_','R','E','A','D','Y',0}; static const WCHAR eventdataname[] = {'D','B','W','I','N','_','D','A','T','A','_','R','E','A','D','Y',0}; HANDLE mapping; mapping = OpenFileMappingW( FILE_MAP_WRITE, FALSE, shmname ); if (mapping) { LPVOID buffer; HANDLE eventbuffer, eventdata; buffer = MapViewOfFile( mapping, FILE_MAP_WRITE, 0, 0, 0 ); eventbuffer = OpenEventW( SYNCHRONIZE, FALSE, eventbuffername ); eventdata = OpenEventW( EVENT_MODIFY_STATE, FALSE, eventdataname ); if (buffer && eventbuffer && eventdata) { /* monitor is present, synchronize with other OutputDebugString invocations */ WaitForSingleObject( DBWinMutex, INFINITE ); /* acquire control over the buffer */ if (WaitForSingleObject( eventbuffer, 10000 ) == WAIT_OBJECT_0) { int str_len; struct _mon_buffer_t { DWORD pid; char buffer[1]; } *mon_buffer = (struct _mon_buffer_t*) buffer; str_len = strlen( str ); if (str_len > (4096 - sizeof(DWORD) - 1)) str_len = 4096 - sizeof(DWORD) - 1; mon_buffer->pid = GetCurrentProcessId(); memcpy( mon_buffer->buffer, str, str_len ); mon_buffer->buffer[str_len] = 0; /* signal data ready */ SetEvent( eventdata ); } ReleaseMutex( DBWinMutex ); } if (buffer) UnmapViewOfFile( buffer ); if (eventbuffer) CloseHandle( eventbuffer ); if (eventdata) CloseHandle( eventdata ); CloseHandle( mapping ); } } }