From 93bfa0d6b1b882828a59a1775001cbcb4ca73faa Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Sun, 2 Jun 2002 21:22:22 +0000 Subject: [PATCH] Implementation for console control events (includes process groups support). --- dlls/ntdll/signal_i386.c | 24 ++++--- dlls/ntdll/signal_sparc.c | 24 ++++--- include/wine/server_protocol.h | 19 +++++- server/console.c | 89 ++++++++++++++++++++++++- server/process.c | 14 ++++ server/process.h | 2 + server/protocol.def | 8 +++ server/request.h | 2 + server/trace.c | 9 +++ win32/console.c | 114 +++++++++++++++++---------------- 10 files changed, 227 insertions(+), 78 deletions(-) diff --git a/dlls/ntdll/signal_i386.c b/dlls/ntdll/signal_i386.c index d93ce8666e5..fbea3e4e466 100644 --- a/dlls/ntdll/signal_i386.c +++ b/dlls/ntdll/signal_i386.c @@ -963,17 +963,21 @@ static HANDLER_DEF(fpe_handler) */ static HANDLER_DEF(int_handler) { - EXCEPTION_RECORD rec; - CONTEXT context; + extern int CONSOLE_HandleCtrlC(void); + if (!CONSOLE_HandleCtrlC()) + { + EXCEPTION_RECORD rec; + CONTEXT context; - save_context( &context, HANDLER_CONTEXT ); - rec.ExceptionCode = CONTROL_C_EXIT; - rec.ExceptionFlags = EXCEPTION_CONTINUABLE; - rec.ExceptionRecord = NULL; - rec.ExceptionAddress = (LPVOID)context.Eip; - rec.NumberParameters = 0; - EXC_RtlRaiseException( &rec, &context ); - restore_context( &context, HANDLER_CONTEXT ); + save_context( &context, HANDLER_CONTEXT ); + rec.ExceptionCode = CONTROL_C_EXIT; + rec.ExceptionFlags = EXCEPTION_CONTINUABLE; + rec.ExceptionRecord = NULL; + rec.ExceptionAddress = (LPVOID)context.Eip; + rec.NumberParameters = 0; + EXC_RtlRaiseException( &rec, &context ); + restore_context( &context, HANDLER_CONTEXT ); + } } diff --git a/dlls/ntdll/signal_sparc.c b/dlls/ntdll/signal_sparc.c index 93871a8584d..5b021783f12 100644 --- a/dlls/ntdll/signal_sparc.c +++ b/dlls/ntdll/signal_sparc.c @@ -310,17 +310,21 @@ static void fpe_handler( int signal, siginfo_t *info, ucontext_t *ucontext ) */ static void int_handler( int signal, siginfo_t *info, ucontext_t *ucontext ) { - EXCEPTION_RECORD rec; - CONTEXT context; + extern int CONSOLE_HandleCtrlC(void); + if (!CONSOLE_HandleCtrlC()) + { + EXCEPTION_RECORD rec; + CONTEXT context; - save_context( &context, ucontext ); - rec.ExceptionCode = CONTROL_C_EXIT; - rec.ExceptionFlags = EXCEPTION_CONTINUABLE; - rec.ExceptionRecord = NULL; - rec.ExceptionAddress = (LPVOID)context.pc; - rec.NumberParameters = 0; - EXC_RtlRaiseException( &rec, &context ); - restore_context( &context, ucontext ); + save_context( &context, ucontext ); + rec.ExceptionCode = CONTROL_C_EXIT; + rec.ExceptionFlags = EXCEPTION_CONTINUABLE; + rec.ExceptionRecord = NULL; + rec.ExceptionAddress = (LPVOID)context.pc; + rec.NumberParameters = 0; + EXC_RtlRaiseException( &rec, &context ); + restore_context( &context, ucontext ); + } } diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index a769789f2dd..c5d991a0de6 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -1366,6 +1366,7 @@ struct read_console_output_reply }; + struct move_console_output_request { struct request_header __header; @@ -1384,6 +1385,19 @@ struct move_console_output_reply +struct send_console_signal_request +{ + struct request_header __header; + int signal; + void* group_id; +}; +struct send_console_signal_reply +{ + struct reply_header __header; +}; + + + struct create_change_notification_request { struct request_header __header; @@ -2784,6 +2798,7 @@ enum request REQ_fill_console_output, REQ_read_console_output, REQ_move_console_output, + REQ_send_console_signal, REQ_create_change_notification, REQ_create_mapping, REQ_open_mapping, @@ -2946,6 +2961,7 @@ union generic_request struct fill_console_output_request fill_console_output_request; struct read_console_output_request read_console_output_request; struct move_console_output_request move_console_output_request; + struct send_console_signal_request send_console_signal_request; struct create_change_notification_request create_change_notification_request; struct create_mapping_request create_mapping_request; struct open_mapping_request open_mapping_request; @@ -3106,6 +3122,7 @@ union generic_reply struct fill_console_output_reply fill_console_output_reply; struct read_console_output_reply read_console_output_reply; struct move_console_output_reply move_console_output_reply; + struct send_console_signal_reply send_console_signal_reply; struct create_change_notification_reply create_change_notification_reply; struct create_mapping_reply create_mapping_reply; struct open_mapping_reply open_mapping_reply; @@ -3192,6 +3209,6 @@ union generic_reply struct get_window_properties_reply get_window_properties_reply; }; -#define SERVER_PROTOCOL_VERSION 80 +#define SERVER_PROTOCOL_VERSION 81 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */ diff --git a/server/console.c b/server/console.c index 6ddd0778892..718d124717e 100644 --- a/server/console.c +++ b/server/console.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "handle.h" #include "process.h" @@ -385,6 +386,54 @@ static int console_input_signaled( struct object *obj, struct thread *thread ) return console->recnum ? 1 : 0; } +struct console_signal_info { + struct console_input *console; + struct process *group; + int signal; +}; + +static int propagate_console_signal_cb(struct process *process, void *user) +{ + struct console_signal_info* csi = (struct console_signal_info*)user; + + if (process->console == csi->console && process->running_threads && + (csi->group == NULL || process->group_id == csi->group)) + { + struct thread *thread = process->thread_list; + + while (thread) + { + struct thread *next = thread->proc_next; + kill( thread->unix_pid, csi->signal ); + thread = next; + } + } + return FALSE; +} + +static void propagate_console_signal( struct console_input *console, + int sig, void* group_id ) +{ + struct console_signal_info csi; + + if (!console) + { + set_error( STATUS_INVALID_PARAMETER ); + return; + } + /* FIXME: should support the other events (like CTRL_BREAK) */ + if (sig != CTRL_C_EVENT) + { + set_error( STATUS_NOT_IMPLEMENTED ); + return; + } + csi.console = console; + csi.signal = SIGINT; + csi.group = group_id; + + enum_processes(propagate_console_signal_cb, &csi); +} + static int get_console_mode( obj_handle_t handle ) { struct object *obj; @@ -443,8 +492,31 @@ static int write_console_input( struct console_input* console, int count, } console->records = new_rec; memcpy( new_rec + console->recnum, records, count * sizeof(INPUT_RECORD) ); - console->recnum += count; + if (console->mode & ENABLE_PROCESSED_INPUT) + { + int i = 0; + while (i < count) + { + if (records[i].EventType == KEY_EVENT && + records[i].Event.KeyEvent.uChar.UnicodeChar == 'C' - 64 && + !(records[i].Event.KeyEvent.dwControlKeyState & ENHANCED_KEY)) + { + if (i != count - 1) + memcpy( &console->records[console->recnum + i], + &console->records[console->recnum + i + 1], + (count - i - 1) * sizeof(INPUT_RECORD) ); + count--; + if (records[i].Event.KeyEvent.bKeyDown) + { + /* send SIGINT to all processes attached to this console */ + propagate_console_signal( console, CTRL_C_EVENT, NULL ); + } + } + else i++; + } + } + console->recnum += count; /* wake up all waiters */ wake_up( &console->obj, 0 ); return count; @@ -1385,3 +1457,18 @@ DECL_HANDLER(move_console_output) scroll_console_output( req->handle, req->x_src, req->y_src, req->x_dst, req->y_dst, req->w, req->h ); } + +/* sends a signal to a console (process, group...) */ +DECL_HANDLER(send_console_signal) +{ + void* group; + + group = req->group_id ? req->group_id : current->process->group_id; + + if (!group) + set_error( STATUS_INVALID_PARAMETER); + else + propagate_console_signal( current->process->console, req->signal, group ); +} + + diff --git a/server/process.c b/server/process.c index 0f89aa341a0..6a60569a7df 100644 --- a/server/process.c +++ b/server/process.c @@ -218,6 +218,7 @@ struct thread *create_process( int fd ) process->exe.dbg_size = 0; process->exe.namelen = 0; process->exe.filename = NULL; + process->group_id = NULL; gettimeofday( &process->start_time, NULL ); if ((process->next = first_process) != NULL) process->next->prev = process; @@ -285,6 +286,7 @@ static struct startup_info *init_process( int ppid, struct init_process_reply *r /* set the process console */ if (!set_process_console( process, parent_thread, info, reply )) return NULL; + process->group_id = process; if (parent) { /* attach to the debugger if requested */ @@ -292,6 +294,8 @@ static struct startup_info *init_process( int ppid, struct init_process_reply *r set_process_debugger( process, parent_thread ); else if (parent->debugger && !(parent->create_flags & DEBUG_ONLY_THIS_PROCESS)) set_process_debugger( process, parent->debugger ); + if (!(process->create_flags & CREATE_NEW_PROCESS_GROUP)) + process->group_id = parent->group_id; } /* thread will be actually suspended in init_done */ @@ -614,6 +618,16 @@ void detach_debugged_processes( struct thread *debugger ) } +void enum_processes( int (*cb)(struct process*, void*), void *user ) +{ + struct process *process; + for (process = first_process; process; process = process->next) + { + if ((cb)(process, user)) break; + } +} + + /* get all information about a process */ static void get_process_info( struct process *process, struct get_process_info_reply *reply ) { diff --git a/server/process.h b/server/process.h index e32049130bc..27ea25f65ff 100644 --- a/server/process.h +++ b/server/process.h @@ -72,6 +72,7 @@ struct process struct process_dll exe; /* main exe file */ void *ldt_copy; /* pointer to LDT copy in client addr space */ void *ldt_flags; /* pointer to LDT flags in client addr space */ + void *group_id; /* group ID of the process */ }; struct process_snapshot @@ -110,6 +111,7 @@ extern void kill_debugged_processes( struct thread *debugger, int exit_code ); extern void detach_debugged_processes( struct thread *debugger ); extern struct process_snapshot *process_snap( int *count ); extern struct module_snapshot *module_snap( struct process *process, int *count ); +extern void enum_processes( int (*cb)(struct process*, void*), void *user); inline static void *get_process_id( struct process *process ) { return process; } inline static int is_process_init_done( struct process *process ) diff --git a/server/protocol.def b/server/protocol.def index 8b68a439fee..9d4f8f37a71 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -1009,6 +1009,7 @@ enum char_info_mode VARARG(data,bytes); @END + /* move a rect (of data) in screen buffer content */ @REQ(move_console_output) obj_handle_t handle; /* handle to the console output */ @@ -1021,6 +1022,13 @@ enum char_info_mode @END +/* Sends a signal to a process group */ +@REQ(send_console_signal) + int signal; /* the signal to send */ + void* group_id; /* the group to send the signal to */ +@END + + /* Create a change notification */ @REQ(create_change_notification) int subtree; /* watch all the subtree */ diff --git a/server/request.h b/server/request.h index 94a659de0fe..5f893abbc3e 100644 --- a/server/request.h +++ b/server/request.h @@ -172,6 +172,7 @@ DECL_HANDLER(write_console_output); DECL_HANDLER(fill_console_output); DECL_HANDLER(read_console_output); DECL_HANDLER(move_console_output); +DECL_HANDLER(send_console_signal); DECL_HANDLER(create_change_notification); DECL_HANDLER(create_mapping); DECL_HANDLER(open_mapping); @@ -333,6 +334,7 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] = (req_handler)req_fill_console_output, (req_handler)req_read_console_output, (req_handler)req_move_console_output, + (req_handler)req_send_console_signal, (req_handler)req_create_change_notification, (req_handler)req_create_mapping, (req_handler)req_open_mapping, diff --git a/server/trace.c b/server/trace.c index b36cfa7de11..c96863d1a75 100644 --- a/server/trace.c +++ b/server/trace.c @@ -1191,6 +1191,12 @@ static void dump_move_console_output_request( const struct move_console_output_r fprintf( stderr, " h=%d", req->h ); } +static void dump_send_console_signal_request( const struct send_console_signal_request *req ) +{ + fprintf( stderr, " signal=%d,", req->signal ); + fprintf( stderr, " group_id=%p", req->group_id ); +} + static void dump_create_change_notification_request( const struct create_change_notification_request *req ) { fprintf( stderr, " subtree=%d,", req->subtree ); @@ -2239,6 +2245,7 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_fill_console_output_request, (dump_func)dump_read_console_output_request, (dump_func)dump_move_console_output_request, + (dump_func)dump_send_console_signal_request, (dump_func)dump_create_change_notification_request, (dump_func)dump_create_mapping_request, (dump_func)dump_open_mapping_request, @@ -2397,6 +2404,7 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_fill_console_output_reply, (dump_func)dump_read_console_output_reply, (dump_func)0, + (dump_func)0, (dump_func)dump_create_change_notification_reply, (dump_func)dump_create_mapping_reply, (dump_func)dump_open_mapping_reply, @@ -2555,6 +2563,7 @@ static const char * const req_names[REQ_NB_REQUESTS] = { "fill_console_output", "read_console_output", "move_console_output", + "send_console_signal", "create_change_notification", "create_mapping", "open_mapping", diff --git a/win32/console.c b/win32/console.c index 1c218894285..a98c971c913 100644 --- a/win32/console.c +++ b/win32/console.c @@ -5,7 +5,7 @@ * Copyright 1997 Karl Garrison * Copyright 1998 John Richardson * Copyright 1998 Marcus Meissner - * Copyright 2001 Eric Pouech + * Copyright 2001,2002 Eric Pouech * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -109,7 +109,7 @@ static BOOL start_console_renderer(void) } /* then try the regular PATH */ - sprintf(buffer, "wineconsole --use-event=%d\n", hEvent); + sprintf(buffer, "wineconsole --use-event=%d", hEvent); if (CreateProcessA(NULL, buffer, NULL, NULL, TRUE, DETACHED_PROCESS, NULL, NULL, &si, &pi)) goto succeed; @@ -210,7 +210,6 @@ static BOOL read_console_input(HANDLE handle, LPINPUT_RECORD buffer, DWORD count { BOOL ret; unsigned read = 0; - DWORD mode; SERVER_START_REQ( read_console_input ) { @@ -220,22 +219,6 @@ static BOOL read_console_input(HANDLE handle, LPINPUT_RECORD buffer, DWORD count if ((ret = !wine_server_call_err( req ))) read = reply->read; } SERVER_END_REQ; - if (count && flush && GetConsoleMode(handle, &mode) && (mode & ENABLE_PROCESSED_INPUT)) - { - int i; - - for (i = 0; i < read; i++) - { - if (buffer[i].EventType == KEY_EVENT && buffer[i].Event.KeyEvent.bKeyDown && - buffer[i].Event.KeyEvent.uChar.UnicodeChar == 'C' - 64 && - !(buffer[i].Event.KeyEvent.dwControlKeyState & ENHANCED_KEY)) - { - GenerateConsoleCtrlEvent(CTRL_C_EVENT, GetCurrentProcessId()); - /* FIXME: this is hackish, but it easily disables IR handling afterwards */ - buffer[i].Event.KeyEvent.uChar.UnicodeChar = 0; - } - } - } if (pRead) *pRead = read; return ret; } @@ -460,6 +443,11 @@ BOOL WINAPI SetConsoleInputExeNameA(LPCSTR name) return ret; } +/****************************************************************** + * CONSOLE_DefaultHandler + * + * Final control event handler + */ static BOOL WINAPI CONSOLE_DefaultHandler(DWORD dwCtrlType) { FIXME("Terminating process %lx on event %lx\n", GetCurrentProcessId(), dwCtrlType); @@ -546,9 +534,6 @@ static WINE_EXCEPTION_FILTER(CONSOLE_CtrlEventHandler) * dwCtrlEvent [I] Type of event * dwProcessGroupID [I] Process group ID to send event to * - * NOTES - * Doesn't yet work...! - * * RETURNS * Success: True * Failure: False (and *should* [but doesn't] set LastError) @@ -556,46 +541,25 @@ static WINE_EXCEPTION_FILTER(CONSOLE_CtrlEventHandler) BOOL WINAPI GenerateConsoleCtrlEvent(DWORD dwCtrlEvent, DWORD dwProcessGroupID) { + BOOL ret; + + TRACE("(%ld, %ld)\n", dwCtrlEvent, dwProcessGroupID); + if (dwCtrlEvent != CTRL_C_EVENT && dwCtrlEvent != CTRL_BREAK_EVENT) { - ERR("invalid event %ld for PGID %ld\n", dwCtrlEvent, dwProcessGroupID); + ERR("Invalid event %ld for PGID %ld\n", dwCtrlEvent, dwProcessGroupID); return FALSE; } - if (dwProcessGroupID == GetCurrentProcessId() || dwProcessGroupID == 0) + SERVER_START_REQ( send_console_signal ) { - int i; - - FIXME("Attempt to send event %ld to self groupID, doing locally only\n", dwCtrlEvent); - - /* this is only meaningfull when done locally, otherwise it will have to be done on - * the 'receive' side of the event generation - */ - if (dwCtrlEvent == CTRL_C_EVENT && console_ignore_ctrl_c) - return TRUE; - - /* try to pass the exception to the debugger - * if it continues, there's nothing more to do - * otherwise, we need to send the ctrl-event to the handlers - */ - __TRY - { - RaiseException( (dwCtrlEvent == CTRL_C_EVENT) ? DBG_CONTROL_C : DBG_CONTROL_BREAK, - 0, 0, NULL); - } - __EXCEPT(CONSOLE_CtrlEventHandler) - { - /* the debugger didn't continue... so, pass to ctrl handlers */ - for (i = 0; i < sizeof(handlers)/sizeof(handlers[0]); i++) - { - if (handlers[i] && (handlers[i])(dwCtrlEvent)) break; - } - } - __ENDTRY; - return TRUE; + req->signal = dwCtrlEvent; + req->group_id = (void*)dwProcessGroupID; + ret = !wine_server_call_err( req ); } - FIXME("event %ld to external PGID %ld - not implemented yet\n", dwCtrlEvent, dwProcessGroupID); - return FALSE; + SERVER_END_REQ; + + return ret; } @@ -1346,6 +1310,7 @@ BOOL WINAPI ScrollConsoleScreenBufferW(HANDLE hConsoleOutput, LPSMALL_RECT lpScr * Console manipulation functions * * ====================================================================*/ + /* some missing functions... * FIXME: those are likely to be defined as undocumented function in kernel32 (or part of them) * should get the right API and implement them @@ -1405,7 +1370,7 @@ BOOL CONSOLE_AppendHistory(const WCHAR* ptr) */ unsigned CONSOLE_GetNumHistoryEntries(void) { - unsigned ret = 0; + unsigned ret = -1; SERVER_START_REQ(get_console_input_info) { req->handle = 0; @@ -1415,3 +1380,40 @@ unsigned CONSOLE_GetNumHistoryEntries(void) return ret; } +/****************************************************************** + * CONSOLE_HandleCtrlC + * + * Check whether the shall manipulate CtrlC events + */ +int CONSOLE_HandleCtrlC(void) +{ + int i; + + /* FIXME: better test whether a console is attached to this process ??? */ + extern unsigned CONSOLE_GetNumHistoryEntries(void); + if (CONSOLE_GetNumHistoryEntries() == (unsigned)-1) return 0; + + /* try to pass the exception to the debugger + * if it continues, there's nothing more to do + * otherwise, we need to send the ctrl-event to the handlers + */ + __TRY + { + RaiseException( DBG_CONTROL_C, 0, 0, NULL ); + } + __EXCEPT(CONSOLE_CtrlEventHandler) + { + /* the debugger didn't continue... so, pass to ctrl handlers */ + /* FIXME: since this routine is called while in a signal handler, + * there are some serious synchronisation issues with + * SetConsoleCtrlHandler (trouble ahead) + */ + for (i = 0; i < sizeof(handlers)/sizeof(handlers[0]); i++) + { + if (handlers[i] && (handlers[i])(CTRL_C_EVENT)) break; + } + } + __ENDTRY; + return 1; +} +