Fixed a couple of file descriptor leaks.
[wine/multimedia.git] / scheduler / client.c
blobf148f53300fe6ffd2b5c24610ddcf9deb6409757
1 /*
2 * Client part of the client/server communication
4 * Copyright (C) 1998 Alexandre Julliard
5 */
7 #include "config.h"
9 #include <assert.h>
10 #include <ctype.h>
11 #include <errno.h>
12 #include <fcntl.h>
13 #include <stdio.h>
14 #include <string.h>
15 #include <sys/types.h>
16 #ifdef HAVE_SYS_SOCKET_H
17 # include <sys/socket.h>
18 #endif
19 #include <sys/un.h>
20 #ifdef HAVE_SYS_MMAN_H
21 #include <sys/mman.h>
22 #endif
23 #include <sys/stat.h>
24 #include <sys/uio.h>
25 #include <unistd.h>
26 #include <stdarg.h>
28 #include "process.h"
29 #include "thread.h"
30 #include "server.h"
31 #include "winerror.h"
32 #include "options.h"
34 /* Some versions of glibc don't define this */
35 #ifndef SCM_RIGHTS
36 #define SCM_RIGHTS 1
37 #endif
39 #define SERVERDIR "/wineserver-" /* server socket directory (hostname appended) */
40 #define SOCKETNAME "socket" /* name of the socket file */
42 /* data structure used to pass an fd with sendmsg/recvmsg */
43 struct cmsg_fd
45 int len; /* sizeof structure */
46 int level; /* SOL_SOCKET */
47 int type; /* SCM_RIGHTS */
48 int fd; /* fd to pass */
51 static void *boot_thread_id;
54 /* die on a fatal error; use only during initialization */
55 static void fatal_error( const char *err, ... )
57 va_list args;
59 va_start( args, err );
60 fprintf( stderr, "wine: " );
61 vfprintf( stderr, err, args );
62 va_end( args );
63 exit(1);
66 /* die on a fatal error; use only during initialization */
67 static void fatal_perror( const char *err, ... )
69 va_list args;
71 va_start( args, err );
72 fprintf( stderr, "wine: " );
73 vfprintf( stderr, err, args );
74 perror( " " );
75 va_end( args );
76 exit(1);
79 /***********************************************************************
80 * server_protocol_error
82 void server_protocol_error( const char *err, ... )
84 va_list args;
86 va_start( args, err );
87 fprintf( stderr, "Client protocol error:%p: ", NtCurrentTeb()->tid );
88 vfprintf( stderr, err, args );
89 va_end( args );
90 SYSDEPS_ExitThread(1);
94 /***********************************************************************
95 * server_perror
97 static void server_perror( const char *err )
99 fprintf( stderr, "Client protocol error:%p: ", NtCurrentTeb()->tid );
100 perror( err );
101 SYSDEPS_ExitThread(1);
105 /***********************************************************************
106 * send_request
108 * Send a request to the server.
110 static void send_request( enum request req )
112 int ret;
113 if ((ret = write( NtCurrentTeb()->socket, &req, sizeof(req) )) == sizeof(req))
114 return;
115 if (ret == -1)
117 if (errno == EPIPE) SYSDEPS_ExitThread(0);
118 server_perror( "sendmsg" );
120 server_protocol_error( "partial msg sent %d/%d\n", ret, sizeof(req) );
123 /***********************************************************************
124 * send_request_fd
126 * Send a request to the server, passing a file descriptor.
128 static void send_request_fd( enum request req, int fd )
130 int ret;
131 #ifndef HAVE_MSGHDR_ACCRIGHTS
132 struct cmsg_fd cmsg;
133 #endif
134 struct msghdr msghdr;
135 struct iovec vec;
137 vec.iov_base = (void *)&req;
138 vec.iov_len = sizeof(req);
140 msghdr.msg_name = NULL;
141 msghdr.msg_namelen = 0;
142 msghdr.msg_iov = &vec;
143 msghdr.msg_iovlen = 1;
145 #ifdef HAVE_MSGHDR_ACCRIGHTS
146 msghdr.msg_accrights = (void *)&fd;
147 msghdr.msg_accrightslen = sizeof(fd);
148 #else /* HAVE_MSGHDR_ACCRIGHTS */
149 cmsg.len = sizeof(cmsg);
150 cmsg.level = SOL_SOCKET;
151 cmsg.type = SCM_RIGHTS;
152 cmsg.fd = fd;
153 msghdr.msg_control = &cmsg;
154 msghdr.msg_controllen = sizeof(cmsg);
155 msghdr.msg_flags = 0;
156 #endif /* HAVE_MSGHDR_ACCRIGHTS */
158 if ((ret = sendmsg( NtCurrentTeb()->socket, &msghdr, 0 )) == sizeof(req)) return;
159 if (ret == -1)
161 if (errno == EPIPE) SYSDEPS_ExitThread(0);
162 server_perror( "sendmsg" );
164 server_protocol_error( "partial msg sent %d/%d\n", ret, sizeof(req) );
167 /***********************************************************************
168 * wait_reply
170 * Wait for a reply from the server.
172 static unsigned int wait_reply(void)
174 int ret;
175 unsigned int res;
177 for (;;)
179 if ((ret = read( NtCurrentTeb()->socket, &res, sizeof(res) )) == sizeof(res))
180 return res;
181 if (!ret) break;
182 if (ret == -1)
184 if (errno == EINTR) continue;
185 if (errno == EPIPE) break;
186 server_perror("read");
188 server_protocol_error( "partial msg received %d/%d\n", ret, sizeof(res) );
190 /* the server closed the connection; time to die... */
191 SYSDEPS_ExitThread(0);
195 /***********************************************************************
196 * wait_reply_fd
198 * Wait for a reply from the server, when a file descriptor is passed.
200 static unsigned int wait_reply_fd( int *fd )
202 struct iovec vec;
203 int ret;
204 unsigned int res;
206 #ifdef HAVE_MSGHDR_ACCRIGHTS
207 struct msghdr msghdr;
209 *fd = -1;
210 msghdr.msg_accrights = (void *)fd;
211 msghdr.msg_accrightslen = sizeof(*fd);
212 #else /* HAVE_MSGHDR_ACCRIGHTS */
213 struct msghdr msghdr;
214 struct cmsg_fd cmsg;
216 cmsg.len = sizeof(cmsg);
217 cmsg.level = SOL_SOCKET;
218 cmsg.type = SCM_RIGHTS;
219 cmsg.fd = -1;
220 msghdr.msg_control = &cmsg;
221 msghdr.msg_controllen = sizeof(cmsg);
222 msghdr.msg_flags = 0;
223 #endif /* HAVE_MSGHDR_ACCRIGHTS */
225 msghdr.msg_name = NULL;
226 msghdr.msg_namelen = 0;
227 msghdr.msg_iov = &vec;
228 msghdr.msg_iovlen = 1;
229 vec.iov_base = (void *)&res;
230 vec.iov_len = sizeof(res);
232 for (;;)
234 if ((ret = recvmsg( NtCurrentTeb()->socket, &msghdr, 0 )) == sizeof(res))
236 #ifndef HAVE_MSGHDR_ACCRIGHTS
237 *fd = cmsg.fd;
238 #endif
239 return res;
241 if (!ret) break;
242 if (ret == -1)
244 if (errno == EINTR) continue;
245 if (errno == EPIPE) break;
246 server_perror("recvmsg");
248 server_protocol_error( "partial seq received %d/%d\n", ret, sizeof(res) );
250 /* the server closed the connection; time to die... */
251 SYSDEPS_ExitThread(0);
255 /***********************************************************************
256 * server_call_noerr
258 * Perform a server call.
260 unsigned int server_call_noerr( enum request req )
262 send_request( req );
263 return wait_reply();
267 /***********************************************************************
268 * server_call_fd
270 * Perform a server call, passing a file descriptor.
271 * If *fd is != -1, it will be passed to the server.
272 * If the server passes an fd, it will be stored into *fd.
274 unsigned int server_call_fd( enum request req, int fd_out, int *fd_in )
276 unsigned int res;
278 if (fd_out == -1) send_request( req );
279 else send_request_fd( req, fd_out );
281 if (fd_in) res = wait_reply_fd( fd_in );
282 else res = wait_reply();
283 if (res) SetLastError( RtlNtStatusToDosError(res) );
284 return res; /* error code */
288 /***********************************************************************
289 * start_server
291 * Start a new wine server.
293 static void start_server( const char *oldcwd )
295 static int started; /* we only try once */
296 if (!started)
298 int pid = fork();
299 if (pid == -1) fatal_perror( "fork" );
300 if (!pid)
302 char *path, *p;
303 /* first try the installation dir */
304 execl( BINDIR "/wineserver", "wineserver", NULL );
305 if (oldcwd) chdir( oldcwd );
306 /* now try the dir we were launched from */
307 if (!(path = malloc( strlen(argv0) + 20 )))
308 fatal_error( "out of memory\n" );
309 if ((p = strrchr( strcpy( path, argv0 ), '/' )))
311 strcpy( p, "/wineserver" );
312 execl( path, "wineserver", NULL );
313 strcpy( p, "/server/wineserver" );
314 execl( path, "wineserver", NULL );
316 /* now try the path */
317 execlp( "wineserver", "wineserver", NULL );
318 /* and finally the current dir */
319 execl( "./server/wineserver", "wineserver", NULL );
320 fatal_error( "could not exec wineserver\n" );
322 started = 1;
326 /***********************************************************************
327 * server_connect
329 * Attempt to connect to an existing server socket.
330 * We need to be in the server directory already.
332 static int server_connect( const char *oldcwd, const char *serverdir )
334 struct sockaddr_un addr;
335 struct stat st;
336 int s, slen;
338 if (chdir( serverdir ) == -1)
340 if (errno != ENOENT) fatal_perror( "chdir to %s", serverdir );
341 start_server( NULL );
342 return -1;
344 if (stat( ".", &st ) == -1) fatal_perror( "stat %s", serverdir );
345 if (st.st_uid != getuid()) fatal_error( "'%s' is not owned by you\n", serverdir );
346 if (st.st_mode & 077) fatal_error( "'%s' must not be accessible by other users\n", serverdir );
348 if (lstat( SOCKETNAME, &st ) == -1)
350 if (errno != ENOENT) fatal_perror( "lstat %s/%s", serverdir, SOCKETNAME );
351 start_server( oldcwd );
352 return -1;
354 if (!S_ISSOCK(st.st_mode))
355 fatal_error( "'%s/%s' is not a socket\n", serverdir, SOCKETNAME );
356 if (st.st_uid != getuid())
357 fatal_error( "'%s/%s' is not owned by you\n", serverdir, SOCKETNAME );
359 if ((s = socket( AF_UNIX, SOCK_STREAM, 0 )) == -1) fatal_perror( "socket" );
360 addr.sun_family = AF_UNIX;
361 strcpy( addr.sun_path, SOCKETNAME );
362 slen = sizeof(addr) - sizeof(addr.sun_path) + strlen(addr.sun_path) + 1;
363 #ifdef HAVE_SOCKADDR_SUN_LEN
364 addr.sun_len = slen;
365 #endif
366 if (connect( s, (struct sockaddr *)&addr, slen ) == -1)
368 close( s );
369 return -2;
371 fcntl( s, F_SETFD, 1 ); /* set close on exec flag */
372 return s;
376 /***********************************************************************
377 * CLIENT_InitServer
379 * Start the server and create the initial socket pair.
381 int CLIENT_InitServer(void)
383 int delay, fd, size;
384 const char *env_fd;
385 char hostname[64];
386 char *oldcwd, *serverdir;
387 const char *configdir;
389 /* first check if we inherited the socket fd */
390 if ((env_fd = getenv( "__WINE_FD" )) && isdigit(env_fd[0]))
392 fd = atoi( env_fd );
393 if (fcntl( fd, F_GETFL, 0 ) != -1) return fd;
396 /* retrieve the current directory */
397 for (size = 512; ; size *= 2)
399 if (!(oldcwd = malloc( size ))) break;
400 if (getcwd( oldcwd, size )) break;
401 free( oldcwd );
402 if (errno == ERANGE) continue;
403 oldcwd = NULL;
404 break;
407 /* get the server directory name */
408 if (gethostname( hostname, sizeof(hostname) ) == -1) fatal_perror( "gethostname" );
409 configdir = PROFILE_GetConfigDir();
410 serverdir = malloc( strlen(configdir) + strlen(SERVERDIR) + strlen(hostname) + 1 );
411 if (!serverdir) fatal_error( "out of memory\n" );
412 strcpy( serverdir, configdir );
413 strcat( serverdir, SERVERDIR );
414 strcat( serverdir, hostname );
416 /* try to connect, leaving some time for the server to start up */
417 for (delay = 10000; delay < 500000; delay += delay / 2)
419 if ((fd = server_connect( oldcwd, serverdir )) >= 0) goto done;
420 usleep( delay );
423 if (fd == -2)
425 fatal_error( "'%s/%s' exists,\n"
426 " but I cannot connect to it; maybe the server has crashed?\n"
427 " If this is the case, you should remove the socket file and try again.\n",
428 serverdir, SOCKETNAME );
430 fatal_error( "could not start wineserver, giving up\n" );
432 done:
433 /* switch back to the starting directory */
434 if (oldcwd)
436 chdir( oldcwd );
437 free( oldcwd );
439 return fd;
443 /***********************************************************************
444 * CLIENT_InitThread
446 * Send an init thread request. Return 0 if OK.
448 int CLIENT_InitThread(void)
450 struct get_thread_buffer_request *first_req;
451 struct init_thread_request *req;
452 TEB *teb = NtCurrentTeb();
453 int fd;
455 if (wait_reply_fd( &fd ) || (fd == -1))
456 server_protocol_error( "no fd passed on first request\n" );
457 if ((teb->buffer_size = lseek( fd, 0, SEEK_END )) == -1) server_perror( "lseek" );
458 teb->buffer = mmap( 0, teb->buffer_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0 );
459 close( fd );
460 if (teb->buffer == (void*)-1) server_perror( "mmap" );
461 first_req = teb->buffer;
462 teb->process->server_pid = first_req->pid;
463 teb->tid = first_req->tid;
464 if (first_req->version != SERVER_PROTOCOL_VERSION)
465 server_protocol_error( "version mismatch %d/%d.\n"
466 "Your %s binary was not upgraded correctly,\n"
467 "or you have an older one somewhere in your PATH.\n",
468 first_req->version, SERVER_PROTOCOL_VERSION,
469 (first_req->version > SERVER_PROTOCOL_VERSION) ? "wine" : "wineserver" );
470 if (first_req->boot) boot_thread_id = teb->tid;
471 else if (boot_thread_id == teb->tid) boot_thread_id = 0;
473 req = teb->buffer;
474 req->unix_pid = getpid();
475 req->teb = teb;
476 req->entry = teb->entry_point;
477 return server_call_noerr( REQ_INIT_THREAD );
480 /***********************************************************************
481 * CLIENT_BootDone
483 * Signal that we have finished booting, and set debug level.
485 int CLIENT_BootDone( int debug_level )
487 struct boot_done_request *req = get_req_buffer();
488 req->debug_level = debug_level;
489 return server_call( REQ_BOOT_DONE );
493 /***********************************************************************
494 * CLIENT_IsBootThread
496 * Return TRUE if current thread is the boot thread.
498 int CLIENT_IsBootThread(void)
500 return (GetCurrentThreadId() == (DWORD)boot_thread_id);