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.




Reply via email to