Moved poll handling to the generic part of the server objects.
Fixed busy waiting on POLLERR events.
Merged struct client into struct thread.
diff --git a/server/request.c b/server/request.c
index 56b394d..62b15e9 100644
--- a/server/request.c
+++ b/server/request.c
@@ -4,13 +4,20 @@
* Copyright (C) 1998 Alexandre Julliard
*/
+#include "config.h"
+
#include <assert.h>
-#include <stdarg.h>
+#include <errno.h>
+#include <fcntl.h>
#include <stdio.h>
-#include <string.h>
#include <stdlib.h>
-#include <signal.h>
+#include <stdarg.h>
+#include <string.h>
+#include <sys/time.h>
#include <sys/types.h>
+#ifdef HAVE_SYS_SOCKET_H
+# include <sys/socket.h>
+#endif
#include <sys/uio.h>
#include <unistd.h>
@@ -22,9 +29,30 @@
#include "server.h"
#define WANT_REQUEST_HANDLERS
#include "request.h"
+
+/* Some versions of glibc don't define this */
+#ifndef SCM_RIGHTS
+#define SCM_RIGHTS 1
+#endif
+
struct thread *current = NULL; /* thread handling the current request */
+
+/* socket communication static structures */
+static struct iovec myiovec;
+static struct msghdr msghdr = { NULL, 0, &myiovec, 1, };
+#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, ... )
{
@@ -34,11 +62,11 @@
fprintf( stderr, "Protocol error:%p: ", thread );
vfprintf( stderr, err, args );
va_end( args );
- remove_client( thread->client, PROTOCOL_ERROR );
+ kill_thread( thread, PROTOCOL_ERROR );
}
/* call a request handler */
-void call_req_handler( struct thread *thread, enum request req, int fd )
+static void call_req_handler( struct thread *thread, enum request req, int fd )
{
current = thread;
clear_error();
@@ -65,31 +93,109 @@
current = NULL;
}
-/* a thread has been killed */
-void call_kill_handler( struct thread *thread, int exit_code )
-{
- /* must be reentrant WRT call_req_handler */
- struct thread *old_current = current;
- current = thread;
- if (current)
- {
- if (debug_level) trace_kill( exit_code );
- thread_killed( current, exit_code );
- }
- current = (old_current != thread) ? old_current : NULL;
-}
-
/* set the fd to pass to the thread */
void set_reply_fd( struct thread *thread, int pass_fd )
{
- client_pass_fd( thread->client, pass_fd );
+ assert( thread->pass_fd == -1 );
+ thread->pass_fd = pass_fd;
}
/* send a reply to a thread */
void send_reply( struct thread *thread )
{
if (thread->state == SLEEPING) thread->state = RUNNING;
- client_reply( thread->client, thread->error );
+ if (debug_level) trace_reply( thread );
+ if (!write_request( thread )) set_select_events( &thread->obj, POLLOUT );
+}
+
+/* read a message from a client that has something to say */
+void read_request( struct thread *thread )
+{
+ int ret;
+ enum request req;
+
+#ifdef HAVE_MSGHDR_ACCRIGHTS
+ msghdr.msg_accrightslen = sizeof(int);
+ msghdr.msg_accrights = (void *)&thread->pass_fd;
+#else /* HAVE_MSGHDR_ACCRIGHTS */
+ msghdr.msg_control = &cmsg;
+ msghdr.msg_controllen = sizeof(cmsg);
+ cmsg.fd = -1;
+#endif /* HAVE_MSGHDR_ACCRIGHTS */
+
+ assert( thread->pass_fd == -1 );
+
+ myiovec.iov_base = (void *)&req;
+ myiovec.iov_len = sizeof(req);
+
+ ret = recvmsg( thread->obj.fd, &msghdr, 0 );
+#ifndef HAVE_MSGHDR_ACCRIGHTS
+ thread->pass_fd = cmsg.fd;
+#endif
+
+ if (ret == sizeof(req))
+ {
+ int pass_fd = thread->pass_fd;
+ thread->pass_fd = -1;
+ call_req_handler( thread, req, pass_fd );
+ if (pass_fd != -1) close( pass_fd );
+ return;
+ }
+ if (ret == -1)
+ {
+ perror("recvmsg");
+ kill_thread( thread, BROKEN_PIPE );
+ return;
+ }
+ if (!ret) /* closed pipe */
+ {
+ kill_thread( thread, BROKEN_PIPE );
+ return;
+ }
+ fatal_protocol_error( thread, "partial message received %d/%d\n", ret, sizeof(req) );
+}
+
+/* send a message to a client that is ready to receive something */
+int write_request( struct thread *thread )
+{
+ int ret;
+
+ if (thread->pass_fd == -1)
+ {
+ ret = write( thread->obj.fd, &thread->error, sizeof(thread->error) );
+ if (ret == sizeof(thread->error)) goto ok;
+ }
+ else /* we have an fd to send */
+ {
+#ifdef HAVE_MSGHDR_ACCRIGHTS
+ msghdr.msg_accrightslen = sizeof(int);
+ msghdr.msg_accrights = (void *)&thread->pass_fd;
+#else /* HAVE_MSGHDR_ACCRIGHTS */
+ msghdr.msg_control = &cmsg;
+ msghdr.msg_controllen = sizeof(cmsg);
+ cmsg.fd = thread->pass_fd;
+#endif /* HAVE_MSGHDR_ACCRIGHTS */
+
+ myiovec.iov_base = (void *)&thread->error;
+ myiovec.iov_len = sizeof(thread->error);
+
+ ret = sendmsg( thread->obj.fd, &msghdr, 0 );
+ close( thread->pass_fd );
+ thread->pass_fd = -1;
+ if (ret == sizeof(thread->error)) goto ok;
+ }
+ if (ret == -1)
+ {
+ if (errno == EWOULDBLOCK) return 0; /* not a fatal error */
+ if (errno != EPIPE) perror("sendmsg");
+ }
+ else fprintf( stderr, "Partial message sent %d/%d\n", ret, sizeof(thread->error) );
+ kill_thread( thread, BROKEN_PIPE );
+ return -1;
+
+ ok:
+ set_select_events( &thread->obj, POLLIN );
+ return 1;
}
/* set the debug level */