/* * Server-side request handling * * Copyright (C) 1998 Alexandre Julliard * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include "wine/port.h" #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_SYS_SOCKET_H # include #endif #include #include #include #include "winnt.h" #include "winbase.h" #include "wincon.h" #include "thread.h" #include "process.h" #define WANT_REQUEST_HANDLERS #include "request.h" /* Some versions of glibc don't define this */ #ifndef SCM_RIGHTS #define SCM_RIGHTS 1 #endif /* path names for server master Unix socket */ #define CONFDIR "/.wine" /* directory for Wine config relative to $HOME */ #define SERVERDIR "wineserver-" /* server socket directory (hostname appended) */ #define SOCKETNAME "socket" /* name of the socket file */ struct master_socket { struct object obj; /* object header */ struct timeout_user *timeout; /* timeout on last process exit */ }; static void master_socket_dump( struct object *obj, int verbose ); static void master_socket_poll_event( struct object *obj, int event ); static const struct object_ops master_socket_ops = { sizeof(struct master_socket), /* size */ master_socket_dump, /* dump */ no_add_queue, /* add_queue */ NULL, /* remove_queue */ NULL, /* signaled */ NULL, /* satisfied */ NULL, /* get_poll_events */ master_socket_poll_event, /* poll_event */ no_get_fd, /* get_fd */ no_flush, /* flush */ no_get_file_info, /* get_file_info */ NULL, /* queue_async */ no_destroy /* destroy */ }; struct thread *current = NULL; /* thread handling the current request */ unsigned int global_error = 0; /* global error code for when no thread is current */ unsigned int server_start_ticks = 0; /* tick count offset from server startup */ static struct master_socket *master_socket; /* the master socket object */ /* socket communication static structures */ static struct iovec myiovec; static struct msghdr msghdr; #ifndef HAVE_MSGHDR_ACCRIGHTS struct cmsg_fd { int len; /* sizeof structure */ int level; /* SOL_SOCKET */ int type; /* SCM_RIGHTS */ int fd; /* fd to pass */ }; static struct cmsg_fd cmsg = { sizeof(cmsg), SOL_SOCKET, SCM_RIGHTS, -1 }; #endif /* HAVE_MSGHDR_ACCRIGHTS */ /* complain about a protocol error and terminate the client connection */ void fatal_protocol_error( struct thread *thread, const char *err, ... ) { va_list args; va_start( args, err ); fprintf( stderr, "Protocol error:%p: ", thread ); vfprintf( stderr, err, args ); va_end( args ); thread->exit_code = 1; kill_thread( thread, 1 ); } /* complain about a protocol error and terminate the client connection */ void fatal_protocol_perror( struct thread *thread, const char *err, ... ) { va_list args; va_start( args, err ); fprintf( stderr, "Protocol error:%p: ", thread ); vfprintf( stderr, err, args ); perror( " " ); va_end( args ); thread->exit_code = 1; kill_thread( thread, 1 ); } /* die on a fatal error */ void fatal_error( const char *err, ... ) { va_list args; va_start( args, err ); fprintf( stderr, "wineserver: " ); vfprintf( stderr, err, args ); va_end( args ); exit(1); } /* die on a fatal error */ void fatal_perror( const char *err, ... ) { va_list args; va_start( args, err ); fprintf( stderr, "wineserver: " ); vfprintf( stderr, err, args ); perror( " " ); va_end( args ); exit(1); } /* allocate the reply data */ void *set_reply_data_size( size_t size ) { assert( size <= get_reply_max_size() ); if (size && !(current->reply_data = mem_alloc( size ))) size = 0; current->reply_size = size; return current->reply_data; } /* write the remaining part of the reply */ void write_reply( struct thread *thread ) { int ret; if ((ret = write( thread->reply_fd, (char *)thread->reply_data + thread->reply_size - thread->reply_towrite, thread->reply_towrite )) >= 0) { if (!(thread->reply_towrite -= ret)) { free( thread->reply_data ); thread->reply_data = NULL; /* sent everything, can go back to waiting for requests */ change_select_fd( &thread->obj, thread->request_fd, POLLIN ); } return; } if (errno == EPIPE) kill_thread( thread, 0 ); /* normal death */ else if (errno != EWOULDBLOCK && errno != EAGAIN) fatal_protocol_perror( thread, "reply write" ); } /* send a reply to the current thread */ static void send_reply( union generic_reply *reply ) { int ret; if (!current->reply_size) { if ((ret = write( current->reply_fd, reply, sizeof(*reply) )) != sizeof(*reply)) goto error; } else { struct iovec vec[2]; vec[0].iov_base = (void *)reply; vec[0].iov_len = sizeof(*reply); vec[1].iov_base = current->reply_data; vec[1].iov_len = current->reply_size; if ((ret = writev( current->reply_fd, vec, 2 )) < sizeof(*reply)) goto error; if ((current->reply_towrite = current->reply_size - (ret - sizeof(*reply)))) { /* couldn't write it all, wait for POLLOUT */ change_select_fd( ¤t->obj, current->reply_fd, POLLOUT ); return; } } if (current->reply_data) { free( current->reply_data ); current->reply_data = NULL; } return; error: if (ret >= 0) fatal_protocol_error( current, "partial write %d\n", ret ); else if (errno == EPIPE) kill_thread( current, 0 ); /* normal death */ else fatal_protocol_perror( current, "reply write" ); } /* call a request handler */ static void call_req_handler( struct thread *thread ) { union generic_reply reply; enum request req = thread->req.request_header.req; current = thread; current->reply_size = 0; clear_error(); memset( &reply, 0, sizeof(reply) ); if (debug_level) trace_request(); if (req < REQ_NB_REQUESTS) { req_handlers[req]( ¤t->req, &reply ); if (current) { reply.reply_header.error = current->error; reply.reply_header.reply_size = current->reply_size; if (debug_level) trace_reply( req, &reply ); send_reply( &reply ); } current = NULL; return; } fatal_protocol_error( current, "bad request %d\n", req ); } /* read a request from a thread */ void read_request( struct thread *thread ) { int ret; if (!thread->req_toread) /* no pending request */ { if ((ret = read( thread->obj.fd, &thread->req, sizeof(thread->req) )) != sizeof(thread->req)) goto error; if (!(thread->req_toread = thread->req.request_header.request_size)) { /* no data, handle request at once */ call_req_handler( thread ); return; } if (!(thread->req_data = malloc( thread->req_toread ))) fatal_protocol_error( thread, "no memory for %d bytes request\n", thread->req_toread ); } /* read the variable sized data */ for (;;) { ret = read( thread->obj.fd, ((char *)thread->req_data + thread->req.request_header.request_size - thread->req_toread), thread->req_toread ); if (ret <= 0) break; if (!(thread->req_toread -= ret)) { call_req_handler( thread ); free( thread->req_data ); thread->req_data = NULL; return; } } error: if (!ret) /* closed pipe */ kill_thread( thread, 0 ); else if (ret > 0) fatal_protocol_error( thread, "partial read %d\n", ret ); else if (errno != EWOULDBLOCK && errno != EAGAIN) fatal_protocol_perror( thread, "read" ); } /* receive a file descriptor on the process socket */ int receive_fd( struct process *process ) { struct send_fd data; int fd, ret; #ifdef HAVE_MSGHDR_ACCRIGHTS msghdr.msg_accrightslen = sizeof(int); msghdr.msg_accrights = (void *)&fd; #else /* HAVE_MSGHDR_ACCRIGHTS */ msghdr.msg_control = &cmsg; msghdr.msg_controllen = sizeof(cmsg); cmsg.fd = -1; #endif /* HAVE_MSGHDR_ACCRIGHTS */ myiovec.iov_base = (void *)&data; myiovec.iov_len = sizeof(data); ret = recvmsg( process->obj.fd, &msghdr, 0 ); #ifndef HAVE_MSGHDR_ACCRIGHTS fd = cmsg.fd; #endif if (ret == sizeof(data)) { struct thread *thread; if (data.tid) thread = get_thread_from_id( data.tid ); else thread = (struct thread *)grab_object( process->thread_list ); if (!thread || thread->process != process) { if (debug_level) fprintf( stderr, "%08x: *fd* %d <- %d bad thread id\n", (unsigned int)data.tid, data.fd, fd ); close( fd ); } else { if (debug_level) fprintf( stderr, "%08x: *fd* %d <- %d\n", (unsigned int)thread, data.fd, fd ); thread_add_inflight_fd( thread, data.fd, fd ); } if (thread) release_object( thread ); return 0; } if (ret >= 0) { if (ret > 0) fprintf( stderr, "Protocol error: process %p: partial recvmsg %d for fd\n", process, ret ); kill_process( process, NULL, 1 ); } else { if (errno != EWOULDBLOCK && errno != EAGAIN) { fprintf( stderr, "Protocol error: process %p: ", process ); perror( "recvmsg" ); kill_process( process, NULL, 1 ); } } return -1; } /* send an fd to a client */ int send_client_fd( struct process *process, int fd, obj_handle_t handle ) { int ret; if (debug_level) fprintf( stderr, "%08x: *fd* %d -> %d\n", (unsigned int)current, handle, fd ); #ifdef HAVE_MSGHDR_ACCRIGHTS msghdr.msg_accrightslen = sizeof(fd); msghdr.msg_accrights = (void *)&fd; #else /* HAVE_MSGHDR_ACCRIGHTS */ msghdr.msg_control = &cmsg; msghdr.msg_controllen = sizeof(cmsg); cmsg.fd = fd; #endif /* HAVE_MSGHDR_ACCRIGHTS */ myiovec.iov_base = (void *)&handle; myiovec.iov_len = sizeof(handle); ret = sendmsg( process->obj.fd, &msghdr, 0 ); if (ret == sizeof(handle)) return 0; if (ret >= 0) { if (ret > 0) fprintf( stderr, "Protocol error: process %p: partial sendmsg %d\n", process, ret ); kill_process( process, NULL, 1 ); } else { fprintf( stderr, "Protocol error: process %p: ", process ); perror( "sendmsg" ); kill_process( process, NULL, 1 ); } return -1; } /* get current tick count to return to client */ unsigned int get_tick_count(void) { struct timeval t; gettimeofday( &t, NULL ); return (t.tv_sec * 1000) + (t.tv_usec / 1000) - server_start_ticks; } static void master_socket_dump( struct object *obj, int verbose ) { struct master_socket *sock = (struct master_socket *)obj; assert( obj->ops == &master_socket_ops ); fprintf( stderr, "Master socket fd=%d\n", sock->obj.fd ); } /* handle a socket event */ static void master_socket_poll_event( struct object *obj, int event ) { struct master_socket *sock = (struct master_socket *)obj; assert( obj->ops == &master_socket_ops ); assert( sock == master_socket ); /* there is only one master socket */ if (event & (POLLERR | POLLHUP)) { /* this is not supposed to happen */ fprintf( stderr, "wineserver: Error on master socket\n" ); release_object( obj ); } else if (event & POLLIN) { struct sockaddr_un dummy; int len = sizeof(dummy); int client = accept( master_socket->obj.fd, (struct sockaddr *) &dummy, &len ); if (client == -1) return; if (sock->timeout) { remove_timeout_user( sock->timeout ); sock->timeout = NULL; } fcntl( client, F_SETFL, O_NONBLOCK ); create_process( client ); } } /* remove the socket upon exit */ static void socket_cleanup(void) { static int do_it_once; if (!do_it_once++) unlink( SOCKETNAME ); } /* return the configuration directory ($WINEPREFIX or $HOME/.wine) */ const char *get_config_dir(void) { static char *confdir; if (!confdir) { const char *prefix = getenv( "WINEPREFIX" ); if (prefix) { int len = strlen(prefix); if (!(confdir = strdup( prefix ))) fatal_error( "out of memory\n" ); if (len > 1 && confdir[len-1] == '/') confdir[len-1] = 0; } else { const char *home = getenv( "HOME" ); if (!home) { struct passwd *pwd = getpwuid( getuid() ); if (!pwd) fatal_error( "could not find your home directory\n" ); home = pwd->pw_dir; } if (!(confdir = malloc( strlen(home) + strlen(CONFDIR) + 1 ))) fatal_error( "out of memory\n" ); strcpy( confdir, home ); strcat( confdir, CONFDIR ); } } return confdir; } /* create the server directory and chdir to it */ static void create_server_dir(void) { char hostname[64]; char *serverdir; const char *confdir = get_config_dir(); struct stat st; if (gethostname( hostname, sizeof(hostname) ) == -1) fatal_perror( "gethostname" ); if (!(serverdir = malloc( strlen(SERVERDIR) + strlen(hostname) + 1 ))) fatal_error( "out of memory\n" ); if (chdir( confdir ) == -1) fatal_perror( "chdir %s", confdir ); strcpy( serverdir, SERVERDIR ); strcat( serverdir, hostname ); if (chdir( serverdir ) == -1) { if (errno != ENOENT) fatal_perror( "chdir %s", serverdir ); if (mkdir( serverdir, 0700 ) == -1) fatal_perror( "mkdir %s", serverdir ); if (chdir( serverdir ) == -1) fatal_perror( "chdir %s", serverdir ); } if (stat( ".", &st ) == -1) fatal_perror( "stat %s", serverdir ); if (!S_ISDIR(st.st_mode)) fatal_error( "%s is not a directory\n", 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 ); } /* open the master server socket and start waiting for new clients */ void open_master_socket(void) { struct sockaddr_un addr; int fd, slen; /* 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) ); create_server_dir(); if ((fd = socket( AF_UNIX, SOCK_STREAM, 0 )) == -1) fatal_perror( "socket" ); addr.sun_family = AF_UNIX; strcpy( addr.sun_path, SOCKETNAME ); slen = sizeof(addr) - sizeof(addr.sun_path) + strlen(addr.sun_path) + 1; #ifdef HAVE_SOCKADDR_SUN_LEN addr.sun_len = slen; #endif if (bind( fd, (struct sockaddr *)&addr, slen ) == -1) { if ((errno == EEXIST) || (errno == EADDRINUSE)) exit(0); /* pretend we succeeded to start */ else fatal_perror( "bind" ); } atexit( socket_cleanup ); chmod( SOCKETNAME, 0600 ); /* make sure no other user can connect */ if (listen( fd, 5 ) == -1) fatal_perror( "listen" ); if (!(master_socket = alloc_object( &master_socket_ops, fd ))) fatal_error( "out of memory\n" ); master_socket->timeout = NULL; set_select_events( &master_socket->obj, POLLIN ); /* setup msghdr structure constant fields */ msghdr.msg_name = NULL; msghdr.msg_namelen = 0; msghdr.msg_iov = &myiovec; msghdr.msg_iovlen = 1; /* init startup ticks */ server_start_ticks = get_tick_count(); /* go in the background */ switch(fork()) { case -1: fatal_perror( "fork" ); case 0: setsid(); break; default: _exit(0); /* do not call atexit functions */ } } /* master socket timer expiration handler */ static void close_socket_timeout( void *arg ) { /* if a new client is waiting, we keep on running */ if (!check_select_events( master_socket->obj.fd, POLLIN )) release_object( master_socket ); } /* close the master socket and stop waiting for new clients */ void close_master_socket(void) { struct timeval when; if (master_socket_timeout == -1) return; /* just keep running forever */ if (master_socket_timeout) { gettimeofday( &when, 0 ); add_timeout( &when, master_socket_timeout * 1000 ); master_socket->timeout = add_timeout_user( &when, close_socket_timeout, NULL ); } else close_socket_timeout( NULL ); /* close it right away */ } /* lock/unlock the master socket to stop accepting new clients */ void lock_master_socket( int locked ) { set_select_events( &master_socket->obj, locked ? 0 : POLLIN ); }