/* send our task port to the server */ static void send_server_task_port(void) { mach_port_t bootstrap_port, wineserver_port; kern_return_t kret; struct { mach_msg_header_t header; mach_msg_body_t body; mach_msg_port_descriptor_t task_port; } msg; if (task_get_bootstrap_port(mach_task_self(), &bootstrap_port) != KERN_SUCCESS) return; kret = bootstrap_look_up(bootstrap_port, (char*)wine_get_server_dir(), &wineserver_port); if (kret != KERN_SUCCESS) fatal_error( "cannot find the server port: 0x%08x\n", kret ); mach_port_deallocate(mach_task_self(), bootstrap_port); msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0) | MACH_MSGH_BITS_COMPLEX; msg.header.msgh_size = sizeof(msg); msg.header.msgh_remote_port = wineserver_port; msg.header.msgh_local_port = MACH_PORT_NULL; msg.body.msgh_descriptor_count = 1; msg.task_port.name = mach_task_self(); msg.task_port.disposition = MACH_MSG_TYPE_COPY_SEND; msg.task_port.type = MACH_MSG_PORT_DESCRIPTOR; kret = mach_msg_send(&msg.header); if (kret != KERN_SUCCESS) server_protocol_error( "mach_msg_send failed: 0x%08x\n", kret ); mach_port_deallocate(mach_task_self(), wineserver_port); }
/* create the lock file and return its file descriptor */ static int create_server_lock(void) { struct stat st; int fd; if (lstat( server_lock_name, &st ) == -1) { if (errno != ENOENT) fatal_perror( "lstat %s/%s", wine_get_server_dir(), server_lock_name ); } else { if (!S_ISREG(st.st_mode)) fatal_error( "%s/%s is not a regular file\n", wine_get_server_dir(), server_lock_name ); } if ((fd = open( server_lock_name, O_CREAT|O_TRUNC|O_WRONLY, 0600 )) == -1) fatal_perror( "error creating %s/%s", wine_get_server_dir(), server_lock_name ); return fd; }
/* kill the wine server holding the lock */ int kill_lock_owner( int sig ) { const char *server_dir = wine_get_server_dir(); int fd, i, ret = 0; pid_t pid = 0; struct flock fl; if (!server_dir) return 0; /* no server dir, nothing to do */ create_server_dir( server_dir ); fd = create_server_lock(); for (i = 1; i <= 20; i++) { fl.l_type = F_WRLCK; fl.l_whence = SEEK_SET; fl.l_start = 0; fl.l_len = 1; if (fcntl( fd, F_GETLK, &fl ) == -1) goto done; if (fl.l_type != F_WRLCK) goto done; /* the file is not locked */ if (!pid) /* first time around */ { if (!(pid = fl.l_pid)) goto done; /* shouldn't happen */ if (sig == -1) { if (kill( pid, SIGINT ) == -1) goto done; kill( pid, SIGCONT ); ret = 1; } else /* just send the specified signal and return */ { ret = (kill( pid, sig ) != -1); goto done; } } else if (fl.l_pid != pid) goto done; /* no longer the same process */ usleep( 50000 * i ); } /* waited long enough, now kill it */ kill( pid, SIGKILL ); done: close( fd ); return ret; }
/* create a file handle to represent a VxD, by opening a dummy file in the wineserver directory */ static HANDLE open_vxd_handle( LPCWSTR name ) { static const WCHAR prefixW[] = {'\\','?','?','\\','u','n','i','x'}; const char *dir = wine_get_server_dir(); int len; HANDLE ret; NTSTATUS status; OBJECT_ATTRIBUTES attr; UNICODE_STRING nameW; IO_STATUS_BLOCK io; len = MultiByteToWideChar( CP_UNIXCP, 0, dir, -1, NULL, 0 ); nameW.Length = sizeof(prefixW) + (len + strlenW( name )) * sizeof(WCHAR); nameW.MaximumLength = nameW.Length + sizeof(WCHAR); if (!(nameW.Buffer = HeapAlloc( GetProcessHeap(), 0, nameW.MaximumLength ))) { SetLastError( ERROR_NOT_ENOUGH_MEMORY ); return 0; } memcpy( nameW.Buffer, prefixW, sizeof(prefixW) ); MultiByteToWideChar( CP_UNIXCP, 0, dir, -1, nameW.Buffer + sizeof(prefixW)/sizeof(WCHAR), len ); len += sizeof(prefixW) / sizeof(WCHAR); nameW.Buffer[len-1] = '/'; strcpyW( nameW.Buffer + len, name ); attr.Length = sizeof(attr); attr.RootDirectory = 0; attr.Attributes = 0; attr.ObjectName = &nameW; attr.SecurityDescriptor = NULL; attr.SecurityQualityOfService = NULL; status = NtCreateFile( &ret, 0, &attr, &io, NULL, 0, FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN_IF, FILE_SYNCHRONOUS_IO_ALERT, NULL, 0 ); if (status) { ret = 0; SetLastError( RtlNtStatusToDosError(status) ); } RtlFreeUnicodeString( &nameW ); return ret; }
/* wait for the server lock */ int wait_for_lock(void) { const char *server_dir = wine_get_server_dir(); int fd, r; struct flock fl; if (!server_dir) return 0; /* no server dir, so no lock to wait on */ create_server_dir( server_dir ); fd = create_server_lock(); fl.l_type = F_WRLCK; fl.l_whence = SEEK_SET; fl.l_start = 0; fl.l_len = 1; r = fcntl( fd, F_SETLKW, &fl ); close(fd); return r; }
/*********************************************************************** * server_connect * * Attempt to connect to an existing server socket. * We need to be in the server directory already. */ static int server_connect(void) { const char *serverdir; struct sockaddr_un addr; struct stat st; int s, slen, retry, fd_cwd; /* retrieve the current directory */ fd_cwd = open( ".", O_RDONLY ); if (fd_cwd != -1) fcntl( fd_cwd, F_SETFD, 1 ); /* set close on exec flag */ setup_config_dir(); serverdir = wine_get_server_dir(); /* chdir to the server directory */ if (chdir( serverdir ) == -1) { if (errno != ENOENT) fatal_perror( "chdir to %s", serverdir ); start_server(); if (chdir( serverdir ) == -1) fatal_perror( "chdir to %s", serverdir ); } /* make sure we are at the right place */ if (stat( ".", &st ) == -1) fatal_perror( "stat %s", serverdir ); if (st.st_uid != getuid()) fatal_error( "'%s' is not owned by you\n", serverdir ); if (st.st_mode & 077) fatal_error( "'%s' must not be accessible by other users\n", serverdir ); for (retry = 0; retry < 6; retry++) { /* if not the first try, wait a bit to leave the previous server time to exit */ if (retry) { usleep( 100000 * retry * retry ); start_server(); if (lstat( SOCKETNAME, &st ) == -1) continue; /* still no socket, wait a bit more */ } else if (lstat( SOCKETNAME, &st ) == -1) /* check for an already existing socket */ { if (errno != ENOENT) fatal_perror( "lstat %s/%s", serverdir, SOCKETNAME ); start_server(); if (lstat( SOCKETNAME, &st ) == -1) continue; /* still no socket, wait a bit more */ } /* make sure the socket is sane (ISFIFO needed for Solaris) */ if (!S_ISSOCK(st.st_mode) && !S_ISFIFO(st.st_mode)) fatal_error( "'%s/%s' is not a socket\n", serverdir, SOCKETNAME ); if (st.st_uid != getuid()) fatal_error( "'%s/%s' is not owned by you\n", serverdir, SOCKETNAME ); /* try to connect to it */ addr.sun_family = AF_UNIX; strcpy( addr.sun_path, SOCKETNAME ); slen = sizeof(addr) - sizeof(addr.sun_path) + strlen(addr.sun_path) + 1; #ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN addr.sun_len = slen; #endif if ((s = socket( AF_UNIX, SOCK_STREAM, 0 )) == -1) fatal_perror( "socket" ); if (connect( s, (struct sockaddr *)&addr, slen ) != -1) { /* switch back to the starting directory */ if (fd_cwd != -1) { fchdir( fd_cwd ); close( fd_cwd ); } fcntl( s, F_SETFD, 1 ); /* set close on exec flag */ return s; } close( s ); } server_connect_error( serverdir ); }
/* open the master server socket and start waiting for new clients */ void open_master_socket(void) { const char *server_dir = wine_get_server_dir(); const char *config_dir = wine_get_config_dir(); int fd, pid, status, sync_pipe[2]; char dummy; /* make sure no request is larger than the maximum size */ assert( sizeof(union generic_request) == sizeof(struct request_max_size) ); assert( sizeof(union generic_reply) == sizeof(struct request_max_size) ); /* make sure the stdio fds are open */ fd = open( "/dev/null", O_RDWR ); while (fd >= 0 && fd <= 2) fd = dup( fd ); if (!server_dir) fatal_error( "directory %s cannot be accessed\n", config_dir ); if (chdir( config_dir ) == -1) fatal_perror( "chdir to %s", config_dir ); if ((config_dir_fd = open( ".", O_RDONLY )) == -1) fatal_perror( "open %s", config_dir ); create_server_dir( server_dir ); if (!foreground) { if (pipe( sync_pipe ) == -1) fatal_perror( "pipe" ); pid = fork(); switch( pid ) { case 0: /* child */ setsid(); close( sync_pipe[0] ); acquire_lock(); /* close stdin and stdout */ dup2( fd, 0 ); dup2( fd, 1 ); /* signal parent */ dummy = 0; write( sync_pipe[1], &dummy, 1 ); close( sync_pipe[1] ); break; case -1: fatal_perror( "fork" ); break; default: /* parent */ close( sync_pipe[1] ); /* wait for child to signal us and then exit */ if (read( sync_pipe[0], &dummy, 1 ) == 1) _exit(0); /* child terminated, propagate exit status */ waitpid( pid, &status, 0 ); if (WIFEXITED(status)) _exit( WEXITSTATUS(status) ); _exit(1); } } else /* remain in the foreground */ { acquire_lock(); } /* init the process tracing mechanism */ init_tracing_mechanism(); close( fd ); }
/* acquire the main server lock */ static void acquire_lock(void) { struct sockaddr_un addr; struct stat st; struct flock fl; int fd, slen, got_lock = 0; fd = create_server_lock(); fl.l_type = F_WRLCK; fl.l_whence = SEEK_SET; fl.l_start = 0; fl.l_len = 1; if (fcntl( fd, F_SETLK, &fl ) != -1) { /* check for crashed server */ if (stat( server_socket_name, &st ) != -1 && /* there is a leftover socket */ stat( "core", &st ) != -1 && st.st_size) /* and there is a non-empty core file */ { fprintf( stderr, "Warning: a previous instance of the wine server seems to have crashed.\n" "Please run 'gdb %s %s/core',\n" "type 'backtrace' at the gdb prompt and report the results. Thanks.\n\n", server_argv0, wine_get_server_dir() ); } unlink( server_socket_name ); /* we got the lock, we can safely remove the socket */ got_lock = 1; /* in that case we reuse fd without closing it, this ensures * that we hold the lock until the process exits */ } else { switch(errno) { case ENOLCK: break; case EACCES: /* check whether locks work at all on this file system */ if (fcntl( fd, F_GETLK, &fl ) == -1) break; /* fall through */ case EAGAIN: exit(2); /* we didn't get the lock, exit with special status */ default: fatal_perror( "fcntl %s/%s", wine_get_server_dir(), server_lock_name ); } /* it seems we can't use locks on this fs, so we will use the socket existence as lock */ close( fd ); } if ((fd = socket( AF_UNIX, SOCK_STREAM, 0 )) == -1) fatal_perror( "socket" ); addr.sun_family = AF_UNIX; strcpy( addr.sun_path, server_socket_name ); slen = sizeof(addr) - sizeof(addr.sun_path) + strlen(addr.sun_path) + 1; #ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN addr.sun_len = slen; #endif if (bind( fd, (struct sockaddr *)&addr, slen ) == -1) { if ((errno == EEXIST) || (errno == EADDRINUSE)) { if (got_lock) fatal_error( "couldn't bind to the socket even though we hold the lock\n" ); exit(2); /* we didn't get the lock, exit with special status */ } fatal_perror( "bind" ); } atexit( socket_cleanup ); chmod( server_socket_name, 0600 ); /* make sure no other user can connect */ if (listen( fd, 5 ) == -1) fatal_perror( "listen" ); if (!(master_socket = alloc_object( &master_socket_ops )) || !(master_socket->fd = create_anonymous_fd( &master_socket_fd_ops, fd, &master_socket->obj, 0 ))) fatal_error( "out of memory\n" ); set_fd_events( master_socket->fd, POLLIN ); make_object_static( &master_socket->obj ); }
/* open the master server socket and start waiting for new clients */ void open_master_socket(void) { const char *server_dir = wine_get_server_dir(); int fd, pid, status, sync_pipe[2]; char dummy; /* make sure no request is larger than the maximum size */ assert( sizeof(union generic_request) == sizeof(struct request_max_size) ); assert( sizeof(union generic_reply) == sizeof(struct request_max_size) ); if (!server_dir) fatal_error( "directory %s cannot be accessed\n", wine_get_config_dir() ); create_server_dir( server_dir ); if (!foreground) { if (pipe( sync_pipe ) == -1) fatal_perror( "pipe" ); pid = fork(); switch( pid ) { case 0: /* child */ setsid(); close( sync_pipe[0] ); acquire_lock(); /* close stdin and stdout */ if ((fd = open( "/dev/null", O_RDWR )) != -1) { dup2( fd, 0 ); dup2( fd, 1 ); close( fd ); } /* signal parent */ dummy = 0; write( sync_pipe[1], &dummy, 1 ); close( sync_pipe[1] ); break; case -1: fatal_perror( "fork" ); break; default: /* parent */ close( sync_pipe[1] ); /* wait for child to signal us and then exit */ if (read( sync_pipe[0], &dummy, 1 ) == 1) _exit(0); /* child terminated, propagate exit status */ wait4( pid, &status, 0, NULL ); if (WIFEXITED(status)) _exit( WEXITSTATUS(status) ); _exit(1); } } else /* remain in the foreground */ { acquire_lock(); } /* setup msghdr structure constant fields */ msghdr.msg_name = NULL; msghdr.msg_namelen = 0; msghdr.msg_iov = &myiovec; msghdr.msg_iovlen = 1; /* init startup time */ gettimeofday( &server_start_time, NULL ); }