I have wasted quite a few time last weekend trying to understand how the server and the rest of Wine were talking to each other, so maybe this time could be used productively for people interested in contributing to Wine. It's a map to read the server code and the code interfacing with it. With some help from truly knowledgeable people, in Wine and in english writing (one can always dream ;-)), maybe it could make it to the documentation/ directory. Gerard Introduction to the Wine server and its interface with the rest of Wine Creation date : 01 April 2000 Warnings - this documentation is newbie-oriented (it has been written by someone knowing nothing about the wine server :-)). Don't expect any startling revelation. It's just a description of the existing code, with heavy simplifications. There is no attempt at describing the most advanced features such as debugging or actual Api implementations. - it describes internal functions in a program where the only thing constant is change. Expect the description to become quickly out of date. What is the wine server ? ------------------------- The server is an independant program that insures that processes running Wine work together and talk to each other with some co-ordination. Some Win32 apis that are task, process or thread oriented, usually need synchronisation and/or have security implications. These Apis are often ported to the Wine server, the Wine client part being just a call to the server. For example, the following Apis are currently partly or completely implemented in the server: - file access control - socket access control - thread management - task synchronization (mutexes, events, pipes, semaphores) - atoms - registry - timers - debugger The server assumes responsability in a centralized way for this class of problems. The general approach of the server is that the server should limit itself to control the activity of the tasks; as the server is not multi-threaded, lengthy tasks are done mainly in the client part. The client/server communication is currently done using a networking protocol (sockets) and a per-thread shared memory area. Interface --------- The interface between client and server is completely in the include/server.h file. This file is the only one shared between server and client .c files (all other server include files in the /server subdirectory are internal to the server and display an error message if included in the rest of Wine.) (CLIENT SIDE) Send a request to the wine server ----------------------------------------------- get_req_buffer [include/server.h] -> NTCurrentTeb()->buffer -> preparation of buffer [many files] -> server_call [include/server.h] -> server_call_noerr [scheduler/client.c] -> send_request [scheduler/client.c] -> write request code to NTCurrentTeb()->socket get_req_buffer ------------- The request is initiated by getting a buffer, through this inlined function. It returns a per-thread buffer (NTCurrentTeb()->buffer). (note that NTCurrentTeb boils down to the fs register, used in the x86 Win32 implementation to get at the current thread context; Teb = Thread Environment Block) Technically, the shared memory buffer is mmap()ed in the server and the client. WARNING : in the Wine code, the 'req' symbol is used frequently, with 2 very different meanings : - a *pointer* to the shared memory buffer - an function index; it's an *enum* passed through the socket. buffer preparation ------------------ The buffer is then prepared - this part is entirely query-specific, as there is no standard common part in the buffer such as a result field. In this preparation part that set the fields of the structure in the shared memory buffer, it's important to never use any code that can possibly call the server. The buffer would be changed *after* being initialized but *before* being passed to the server. The structures are declared in [include/server.h] server_call ----------- This inlined function boils down to a call to server_call_noerr. server_call_noerr ---------------- Boils down to a call to send_request. send_request ------------ Does a standard write operation of the request's code to the per-thread socket. Note than the real buffer holding the query is a shared memory area allocated by the server at thread initialisation. (see CLIENT_InitThread() routine in scheduler/client.c) A variant of this routine exists when the Api needs to use a file descriptor (for disk I/O or networking...): send_request_fd (in scheduler/client.c) is similar to send_request but passes also a file descriptor (a structure is used to this end, the syscall sendmsg is used instead of write). (SERVER SIDE) Get a request from the Wine client ------------------------------------------------ The server is waiting on as many sockets are there are threads (the server is responsible for managing threads, and has as such knowledge of the Win32 threads). The wait loop is in [server/select.c] The poll() system call (a variant of select()) is used to call the appropriate event handler. As each socket is owned by one thread, the socket event gives the calling thread. main server loop [server/select.c] -> thread_poll_event [server/thread.c] -> read_request [server/request.c] -> call_req_handler [server/request.c] -> specific handler [server/many files] thread_poll_event ---------------- this routine is an interface to the Unix signals happening on the socket. read_request ------------ calls the system call recvmsg to retrieve the message from the socket. Although 2 different routines are used to send data to the server, this is the only one that retrieves incoming requests. That's the magic of recvmsg. Explanations welcome as I am no expert in U*x programming :-). My guess is that when the sender (the client) uses plain write as in send_request, a message is generated automatically by the socket layer, so that it's only necessary to explicitly build a message when some additional parameter (such as the socket number passed by the Wine client) needs to be passed. call_req_handler ---------------- This inlined function calls the request handler indexed by the request code passed by the socket; call_req_handler calls the actual handlers with the shared memory buffer as parameter. call_req_handler get at the functions pointers in the req_handlers function table [server/request.h]. call_req_handler get the shared buffer through the thread pointer; the thread pointer itself comes from the socket number, as found by the main wait loop. request handlers ---------------- These routines implement the server functions; they are declared by the macro DECL_HANDLER [server/request.h]. The only parameter they get (see the macro) is a pointer to the shared memory buffer, cast to the structure specific to the handler. For example, the delete_key registry handler [server/registry.c] get a struct delete_key_request * pointer. The functions access then the buffer through the members of the structure. Note that often the shared memory buffer is updated (in the handler) to return information to the Wine caller. (SERVER SIDE) Send a reply to the Wine client --------------------------------------------- send_reply [server/request.c] -> write_request [server/request.c] -> set_select_events [server/select.c] send_reply ---------- after the call_req_handler, test if the thread has sent a call that makes it wait for some condition; for example, waiting for a specific event. If not, a reply is sent immediately by calling write_request. write_request ------------- the return sent to the client can be : - either an error code : in this case the error code is just written to the task's socket. - or a file descriptor : in this case the system call sendmsg is used. If this write generates no error, set_select_events is called. set_select_events ----------------- set the bits in the event mask so that the polling loop can receive the next request. (CLIENT SIDE) Get a reply from the Wine server server_call_noerr [scheduler/client.c] -> wait_reply [scheduler/client.c] -> read server_call_fd [scheduler/client.c] -> wait_reply_fd [scheduler/client.c] -> recvmsg -> wait_reply [scheduler/client.c] server_call_noerr ---------------- calls wait_reply, that itself reads the error code on NTCurrentTeb()->socket. server_call_fd -------------- calls wait_reply_fd if it returns a file descriptor else it calls just wait_reply. How to understand more. ---------------------- A way is to use debugmsg +server. This adds messages from the server [server/trace.c] to the normal flow of Wine debug messages. These messages do not begin by trace:server as the standard Wine debugmsg function. Instead, the server Id of the calling thread is displayed. All server operations are displayed (if you do a debugmsg +reg,+server you will not see *only* the registry server operations) Another way is to read the code.