Detect whether wineserver is fully started? Signal? File?

Sebastian Lackner sebastian at fds-team.de
Fri Jun 16 16:58:26 CDT 2017


Hi Sebastian,

If you disconnect before the real command is started the wineserver will
assume it is no longer needed and terminate automatically. I would say
this is a bug, because the user definitely does not expect that when
passing "-p". Nevertheless, when you keep the connection open, it should
actually work (and also does work for me, according to a quick test).

A slightly better solution which does not trigger this problem would be
to wait for the wineserver lock (which will also appear in the server dir).
To check a lock from Python you can use the following code snippet:

--- snip ---
import fcntl
import sys
import struct

def getLockPID(file):
    """Return process ID responsible for locking a file descriptor."""

    l_type      = fcntl.F_WRLCK
    l_whence    = 0 # SEEK_SET
    l_start     = 0
    l_len       = 0
    l_pid       = 0

    if sys.platform == 'linux':
        lock = struct.pack('hhlli', l_type, l_whence, l_start, l_len, l_pid)
        lock = fcntl.fcntl(file, fcntl.F_GETLK, lock)
        l_type, l_whence, l_start, l_len, l_pid = struct.unpack('hhlli', lock)

    else:
        raise NotImplementedError("Platform %s not implemented" % sys.platform)

    return l_pid if l_type != fcntl.F_UNLCK else None
--- snip ---

Usage is like:

with open(lock_file, "rb") as fp:
   pid = getLockPID(fp)

There is still a small race-condition (the lock is acquired but the socket
has not been created yet), but Wine will be able to deal with that. It will
retry a couple of times until it finally succeeds.

BTW: As I saw, you replaced the "/tmp" with tempfile.gettempdir(), but this
is actually not correct. Wineserver sockets will always be created in /tmp
(hardcoded in the source) to ensure the correct socket is found, no matter
which environment variables are set.

Best regards,
Sebastian

On 16.06.2017 23:06, Sebastian M. Ernst wrote:
> Hi Sebastian,
> 
> thanks a lot for the idea.
> 
> I tried to convert your idea into a quick & dirty piece of actual code
> for testing it - see below this email. Simplified into pseudo-code I do
> the following:
> - create socket object, type unix / stream (w/o reference to file/path)
> - try: connect to socket file
> - if failed, wait for x seconds and repeat last step (until timeout)
> - if succeeded, close the connection and let the code go ahead
> 
> It will result in a log like the following (assuming that most involved
> files are cached in RAM, otherwise the numbers in the "appeared" message
> will be somewhat higher):
> 
> [wine session] Launching wineserver ...
> [wine session] ... started with PID 28549 ...
> [wine session] ... expecting socket at
> /tmp/.wine-1000/server-803-690008/socket ...
> [wine session] ... appeared (after 0.00 seconds & 1 attempts)!
> 
> So far so good. This code works *sometimes* (rarely, however). It will
> always tell me that the desired socket appeared, which it actually does,
> but usually, most of the time, any subsequent command like "wine
> some.exe" or "winepath -w /path" following the launch of my wineserver
> will crash with this error:
> "wine client error:0: recvmsg: Connection reset by peer"
> 
> It seems that my attempt to connect to the socket (and disconnect from
> it if the connection succeeds) renders it unusable for any wine process
> trying to connect to it afterward. Just leaving my connection open does
> not change the result. What I am missing or doing wrong? Am I using the
> right type of socket (stream)? Is there some sort of a simple "ping"
> message/signal I can send to the wineserver through the socket which
> should result in a deterministic answer I can use for validating the
> connection?
> 
> Regards,
> Sebastian
> 
> 
> 
> # Status log
> self.log.out('[wine session] Launching wineserver ...')
> 
> # Start wine server into prepared environment
> self.proc_wineserver = subprocess.Popen(
> 	['wineserver', '-f', '-p'], # run persistent in foreground
> 	stdin = subprocess.PIPE,
> 	stdout = subprocess.PIPE,
> 	stderr = subprocess.PIPE,
> 	shell = False
> 	)
> 
> # Status log
> self.log.out('[wine session] ... started with PID %d ...' %
> self.proc_wineserver.pid)
> 
> # Get info on WINEPREFIX folder
> info_wineprefix = os.stat(self.dir_wineprefix)
> 
> # Get path of wineserver socket file
> socket_path = os.path.join(
> 	tempfile.gettempdir(),
> 	'.wine-%d' % os.getuid(),
> 	'server-%x-%x' % (info_wineprefix.st_dev, info_wineprefix.st_ino),
> 	'socket'
> 	)
> 
> # Status log
> self.log.out('[wine session] ... expecting socket at %s ...' % socket_path)
> 
> # Create socket client
> wineserver_client = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
> # Set a timeout
> wineserver_client.settimeout(1.0) # seconds
> # Status variable
> got_connection = False
> # Time-step
> wait_for_seconds = 0.01
> # Timeout
> timeout_after_seconds = 30.0
> # Already waited for ...
> started_waiting_at = time.time()
> # Connection trys
> tried_this_many_times = 0
> 
> # Run loop until socket appears
> while True:
> 
> 	# Does socket file exist?
> 	if os.path.exists(socket_path):
> 
> 		# Count attempts
> 		tried_this_many_times += 1
> 
> 		# Can I connect to it?
> 		try:
> 			wineserver_client.connect(socket_path)
> 			got_connection = True
> 			break
> 		except:
> 			pass
> 
> 	# Break to loop after timeout
> 	if time.time() >= (started_waiting_at + timeout_after_seconds):
> 		break
> 
> 	# Wait before trying again
> 	time.sleep(wait_for_seconds)
> 
> # Evaluate the result
> if not got_connection:
> 
> 	self.log.out(
> 		'[wine session] ... did not appear (after %0.2f seconds & %d
> attempts)! Quit.' % (timeout_after_seconds, tried_this_many_times)
> 		)
> 	sys.exit()
> 
> else:
> 
> 	# If it worked, disconnect
> 	wineserver_client.close()
> 
> 	# Log status
> 	self.log.out(
> 		'[wine session] ... appeared (after %0.2f seconds & %d attempts)!' %
> (time.time() - started_waiting_at, tried_this_many_times)
> 		)
> 
> 
> 
> Am 12.06.2017 um 20:43 schrieb Sebastian Lackner:
>>
>> I would suggest to implement the same logic as used by Wine itself.
>> Basically, you would write a loop which periodically tries to connect to the
>> wineserver UNIX socket, until it finally succeeds. The socket will appear
>> at the following location (pseudocode):
>>
>> info = os.stat(WINEPREFIX)
>> socket_path = os.path.join("/tmp", ".wine-%d" % os.getuid(), "server-%x-%x" % (info.st_dev, info.st_ino))
>>
>> Best regards,
>> Sebastian
>>




More information about the wine-devel mailing list