Console: control-C handling

Eric Pouech eric.pouech at wanadoo.fr
Sat Jun 1 14:26:25 CDT 2002


this is a first attempt at implementing the control event handling in
console
this requires:
- adding support for process group in server (as of today it's done
directly in the process structure, it might be investigated if we need
to map Win32 process groups on top of Unix process groups)
- passing control events from processes to wine server
- signalling back the processes with the event (this today only work for
the Ctrl-C event, other events are needed, like break (we could map it
to SIGQUIT), but it would be harder for the other events (close, logoff,
shutdown). it may require putting in place a more sophisticated scheme.

I also don't like the ntdll/kernel mix up here. Even if part of console
code (win32/console.c and editline.c) are still in ntdll (as of today),
they'll move to kernel32 at some point, and the patch adds another
dependancy that we'll need to take care of.

A+
-------------- next part --------------
Name:          con_ctrl_c
ChangeLog:     implementation for console control events (includes process groups support)
License:       X11
GenDate:       2002/06/01 19:11:57 UTC
ModifiedFiles: dlls/ntdll/signal_i386.c dlls/ntdll/signal_sparc.c server/console.c server/process.c server/process.h server/protocol.def win32/console.c
AddedFiles:    
===================================================================
RCS file: /home/cvs/cvsroot/wine/wine/dlls/ntdll/signal_i386.c,v
retrieving revision 1.36
diff -u -u -r1.36 signal_i386.c
--- dlls/ntdll/signal_i386.c	31 May 2002 23:25:49 -0000	1.36
+++ dlls/ntdll/signal_i386.c	1 Jun 2002 12:27:32 -0000
@@ -963,17 +974,21 @@
  */
 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 );
+    }
 }
 
 
Index: dlls/ntdll/signal_sparc.c
===================================================================
RCS file: /home/cvs/cvsroot/wine/wine/dlls/ntdll/signal_sparc.c,v
retrieving revision 1.12
diff -u -u -r1.12 signal_sparc.c
--- dlls/ntdll/signal_sparc.c	31 May 2002 23:25:49 -0000	1.12
+++ dlls/ntdll/signal_sparc.c	1 Jun 2002 12:47:49 -0000
@@ -310,17 +310,21 @@
  */
 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 );
+    }
 }
 
 
Index: server/console.c
===================================================================
RCS file: /home/cvs/cvsroot/wine/wine/server/console.c,v
retrieving revision 1.39
diff -u -u -r1.39 console.c
--- server/console.c	31 May 2002 23:06:53 -0000	1.39
+++ server/console.c	1 Jun 2002 13:37:14 -0000
@@ -27,6 +27,7 @@
 #include <string.h>
 #include <stdio.h>
 #include <unistd.h>
+#include <signal.h>
 
 #include "handle.h"
 #include "process.h"
@@ -385,6 +386,54 @@
     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 @@
     }
     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 @@
     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 );
+}
+
+
Index: server/process.c
===================================================================
RCS file: /home/cvs/cvsroot/wine/wine/server/process.c,v
retrieving revision 1.86
diff -u -u -r1.86 process.c
--- server/process.c	30 May 2002 20:12:58 -0000	1.86
+++ server/process.c	1 Jun 2002 07:44:33 -0000
@@ -218,7 +218,8 @@
     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;
     first_process = process;
@@ -285,6 +286,7 @@
     /* 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 @@
             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 */
@@ -613,11 +617,19 @@
     }
 }
 
+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 )
 {
     reply->pid              = get_process_id( process );
     reply->debugged         = (process->debugger != 0);
     reply->exit_code        = process->exit_code;
     reply->priority         = process->priority;
Index: server/process.h
===================================================================
RCS file: /home/cvs/cvsroot/wine/wine/server/process.h,v
retrieving revision 1.30
diff -u -u -r1.30 process.h
--- server/process.h	30 May 2002 20:12:58 -0000	1.30
+++ server/process.h	1 Jun 2002 05:26:26 -0000
@@ -72,6 +72,7 @@
     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 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 )
Index: server/protocol.def
===================================================================
RCS file: /home/cvs/cvsroot/wine/wine/server/protocol.def,v
retrieving revision 1.38
diff -u -u -r1.38 protocol.def
--- server/protocol.def	30 May 2002 20:12:58 -0000	1.38
+++ server/protocol.def	1 Jun 2002 05:35:58 -0000
@@ -1009,6 +1010,7 @@
     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 */
@@ -1018,6 +1020,13 @@
     short int    y_dst;
     short int    w;             /* size of the rect (width, height) to move */
     short int    h;
+ at END
+
+
+/* Sends a signal to a process group */
+ at REQ(send_console_signal)
+    int          signal;        /* the signal to send */
+    void*        group_id;      /* the group to send the signal to */
 @END
 
 
Index: win32/console.c
===================================================================
RCS file: /home/cvs/cvsroot/wine/wine/win32/console.c,v
retrieving revision 1.94
diff -u -u -r1.94 console.c
--- win32/console.c	31 May 2002 23:06:54 -0000	1.94
+++ win32/console.c	1 Jun 2002 16:21:30 -0000
@@ -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 @@
     }
 
     /* 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 @@
 {
     BOOL	ret;
     unsigned	read = 0;
-    DWORD	mode;
 
     SERVER_START_REQ( read_console_input )
     {
@@ -220,22 +219,6 @@
         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 @@
     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 @@
  *    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 @@
 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 @@
  * 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 @@
  */
 unsigned CONSOLE_GetNumHistoryEntries(void)
 {
-    unsigned ret = 0;
+    unsigned ret = -1;
     SERVER_START_REQ(get_console_input_info)
     {
         req->handle = 0;
@@ -1413,5 +1378,42 @@
     }
     SERVER_END_REQ;
     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;
 }
 


More information about the wine-patches mailing list