blob: c540aac042c08301f8c348d16728eaa01dc85582 [file] [log] [blame]
Alexandre Julliard642d3131998-07-12 19:29:36 +00001/*
2 * Server-side socket communication functions
3 *
4 * Copyright (C) 1998 Alexandre Julliard
5 */
6
Patrik Stridvall96336321999-10-24 22:13:47 +00007#include "config.h"
8
Alexandre Julliard642d3131998-07-12 19:29:36 +00009#include <assert.h>
10#include <errno.h>
Alexandre Julliardf2616a21999-05-20 16:40:23 +000011#include <fcntl.h>
Alexandre Julliard642d3131998-07-12 19:29:36 +000012#include <stdio.h>
13#include <stdlib.h>
14#include <stdarg.h>
15#include <string.h>
16#include <sys/time.h>
17#include <sys/types.h>
Patrik Stridvall96336321999-10-24 22:13:47 +000018#ifdef HAVE_SYS_SOCKET_H
19# include <sys/socket.h>
20#endif
Alexandre Julliard642d3131998-07-12 19:29:36 +000021#include <sys/uio.h>
22#include <unistd.h>
23
Alexandre Julliard829fe321998-07-26 14:27:39 +000024#include "config.h"
Alexandre Julliard43c190e1999-05-15 10:48:19 +000025#include "object.h"
Alexandre Julliard5bc78081999-06-22 17:26:53 +000026#include "request.h"
Alexandre Julliard642d3131998-07-12 19:29:36 +000027
Alexandre Julliard829fe321998-07-26 14:27:39 +000028/* Some versions of glibc don't define this */
29#ifndef SCM_RIGHTS
30#define SCM_RIGHTS 1
31#endif
32
Alexandre Julliard642d3131998-07-12 19:29:36 +000033/* client structure */
34struct client
35{
Alexandre Julliard88de35c1999-05-16 16:57:49 +000036 struct select_user select; /* select user */
Alexandre Julliardebe29ef1999-06-26 08:43:26 +000037 unsigned int res; /* current result to send */
Alexandre Julliard88de35c1999-05-16 16:57:49 +000038 int pass_fd; /* fd to pass to and from the client */
39 struct thread *self; /* client thread (opaque pointer) */
40 struct timeout_user *timeout; /* current timeout (opaque pointer) */
Alexandre Julliard642d3131998-07-12 19:29:36 +000041};
42
Alexandre Julliard642d3131998-07-12 19:29:36 +000043
Alexandre Julliardebe29ef1999-06-26 08:43:26 +000044/* socket communication static structures */
Howard Abramsb135adf1999-07-10 13:16:56 +000045static struct iovec myiovec;
46static struct msghdr msghdr = { NULL, 0, &myiovec, 1, };
Alexandre Julliardebe29ef1999-06-26 08:43:26 +000047#ifndef HAVE_MSGHDR_ACCRIGHTS
48struct cmsg_fd
Alexandre Julliard642d3131998-07-12 19:29:36 +000049{
Alexandre Julliardebe29ef1999-06-26 08:43:26 +000050 int len; /* sizeof structure */
51 int level; /* SOL_SOCKET */
52 int type; /* SCM_RIGHTS */
53 int fd; /* fd to pass */
54};
55static struct cmsg_fd cmsg = { sizeof(cmsg), SOL_SOCKET, SCM_RIGHTS, -1 };
56#endif /* HAVE_MSGHDR_ACCRIGHTS */
Alexandre Julliard642d3131998-07-12 19:29:36 +000057
Alexandre Julliard642d3131998-07-12 19:29:36 +000058
Alexandre Julliard642d3131998-07-12 19:29:36 +000059/* send a message to a client that is ready to receive something */
Alexandre Julliardebe29ef1999-06-26 08:43:26 +000060static int do_write( struct client *client )
Alexandre Julliard642d3131998-07-12 19:29:36 +000061{
Alexandre Julliard642d3131998-07-12 19:29:36 +000062 int ret;
63
Alexandre Julliard5bc78081999-06-22 17:26:53 +000064 if (client->pass_fd == -1)
Alexandre Julliard642d3131998-07-12 19:29:36 +000065 {
Alexandre Julliardebe29ef1999-06-26 08:43:26 +000066 ret = write( client->select.fd, &client->res, sizeof(client->res) );
67 if (ret == sizeof(client->res)) goto ok;
Alexandre Julliard642d3131998-07-12 19:29:36 +000068 }
Alexandre Julliard5bc78081999-06-22 17:26:53 +000069 else /* we have an fd to send */
Alexandre Julliard642d3131998-07-12 19:29:36 +000070 {
Patrik Stridvall1bb94031999-05-08 15:47:44 +000071#ifdef HAVE_MSGHDR_ACCRIGHTS
Alexandre Julliardebe29ef1999-06-26 08:43:26 +000072 msghdr.msg_accrightslen = sizeof(int);
Alexandre Julliard829fe321998-07-26 14:27:39 +000073 msghdr.msg_accrights = (void *)&client->pass_fd;
Patrik Stridvall1bb94031999-05-08 15:47:44 +000074#else /* HAVE_MSGHDR_ACCRIGHTS */
Alexandre Julliard5bc78081999-06-22 17:26:53 +000075 msghdr.msg_control = &cmsg;
Alexandre Julliard642d3131998-07-12 19:29:36 +000076 msghdr.msg_controllen = sizeof(cmsg);
Alexandre Julliardebe29ef1999-06-26 08:43:26 +000077 cmsg.fd = client->pass_fd;
Patrik Stridvall1bb94031999-05-08 15:47:44 +000078#endif /* HAVE_MSGHDR_ACCRIGHTS */
79
Howard Abramsb135adf1999-07-10 13:16:56 +000080 myiovec.iov_base = (void *)&client->res;
81 myiovec.iov_len = sizeof(client->res);
Alexandre Julliard5bc78081999-06-22 17:26:53 +000082
83 ret = sendmsg( client->select.fd, &msghdr, 0 );
84 close( client->pass_fd );
85 client->pass_fd = -1;
Alexandre Julliardebe29ef1999-06-26 08:43:26 +000086 if (ret == sizeof(client->res)) goto ok;
Alexandre Julliard5bc78081999-06-22 17:26:53 +000087 }
Alexandre Julliard642d3131998-07-12 19:29:36 +000088 if (ret == -1)
89 {
Alexandre Julliardebe29ef1999-06-26 08:43:26 +000090 if (errno == EWOULDBLOCK) return 0; /* not a fatal error */
Alexandre Julliard642d3131998-07-12 19:29:36 +000091 if (errno != EPIPE) perror("sendmsg");
Alexandre Julliard642d3131998-07-12 19:29:36 +000092 }
Alexandre Julliardebe29ef1999-06-26 08:43:26 +000093 else fprintf( stderr, "Partial message sent %d/%d\n", ret, sizeof(client->res) );
Alexandre Julliard5bc78081999-06-22 17:26:53 +000094 remove_client( client, BROKEN_PIPE );
Alexandre Julliardebe29ef1999-06-26 08:43:26 +000095 return 0;
96
97 ok:
98 set_select_events( &client->select, READ_EVENT );
99 return 1;
Alexandre Julliard642d3131998-07-12 19:29:36 +0000100}
101
102
103/* read a message from a client that has something to say */
Alexandre Julliard5bc78081999-06-22 17:26:53 +0000104static void do_read( struct client *client )
Alexandre Julliard642d3131998-07-12 19:29:36 +0000105{
Alexandre Julliardebe29ef1999-06-26 08:43:26 +0000106 int ret;
107 enum request req;
Alexandre Julliard642d3131998-07-12 19:29:36 +0000108
Patrik Stridvall1bb94031999-05-08 15:47:44 +0000109#ifdef HAVE_MSGHDR_ACCRIGHTS
Alexandre Julliardebe29ef1999-06-26 08:43:26 +0000110 msghdr.msg_accrightslen = sizeof(int);
111 msghdr.msg_accrights = (void *)&client->pass_fd;
Patrik Stridvall1bb94031999-05-08 15:47:44 +0000112#else /* HAVE_MSGHDR_ACCRIGHTS */
Patrik Stridvall1bb94031999-05-08 15:47:44 +0000113 msghdr.msg_control = &cmsg;
114 msghdr.msg_controllen = sizeof(cmsg);
Alexandre Julliardebe29ef1999-06-26 08:43:26 +0000115 cmsg.fd = -1;
Patrik Stridvall1bb94031999-05-08 15:47:44 +0000116#endif /* HAVE_MSGHDR_ACCRIGHTS */
117
Alexandre Julliard5bc78081999-06-22 17:26:53 +0000118 assert( client->pass_fd == -1 );
119
Howard Abramsb135adf1999-07-10 13:16:56 +0000120 myiovec.iov_base = (void *)&req;
121 myiovec.iov_len = sizeof(req);
Alexandre Julliard5bc78081999-06-22 17:26:53 +0000122
123 ret = recvmsg( client->select.fd, &msghdr, 0 );
124#ifndef HAVE_MSGHDR_ACCRIGHTS
125 client->pass_fd = cmsg.fd;
126#endif
127
Alexandre Julliardebe29ef1999-06-26 08:43:26 +0000128 if (ret == sizeof(req))
Alexandre Julliard642d3131998-07-12 19:29:36 +0000129 {
Alexandre Julliard5bc78081999-06-22 17:26:53 +0000130 int pass_fd = client->pass_fd;
Alexandre Julliard5bc78081999-06-22 17:26:53 +0000131 client->pass_fd = -1;
Alexandre Julliardebe29ef1999-06-26 08:43:26 +0000132 call_req_handler( client->self, req, pass_fd );
Alexandre Julliard5bc78081999-06-22 17:26:53 +0000133 if (pass_fd != -1) close( pass_fd );
134 return;
Alexandre Julliard642d3131998-07-12 19:29:36 +0000135 }
Alexandre Julliard642d3131998-07-12 19:29:36 +0000136 if (ret == -1)
137 {
138 perror("recvmsg");
Alexandre Julliard88de35c1999-05-16 16:57:49 +0000139 remove_client( client, BROKEN_PIPE );
Alexandre Julliard767e6f61998-08-09 12:47:43 +0000140 return;
Alexandre Julliard642d3131998-07-12 19:29:36 +0000141 }
Alexandre Julliard5bc78081999-06-22 17:26:53 +0000142 if (!ret) /* closed pipe */
Alexandre Julliard767e6f61998-08-09 12:47:43 +0000143 {
Alexandre Julliard88de35c1999-05-16 16:57:49 +0000144 remove_client( client, BROKEN_PIPE );
Alexandre Julliard767e6f61998-08-09 12:47:43 +0000145 return;
146 }
Alexandre Julliardebe29ef1999-06-26 08:43:26 +0000147 fatal_protocol_error( client->self, "partial message received %d/%d\n", ret, sizeof(req) );
Alexandre Julliard642d3131998-07-12 19:29:36 +0000148}
149
Alexandre Julliardc6e45ed1998-12-27 08:35:39 +0000150/* handle a client event */
Alexandre Julliard88de35c1999-05-16 16:57:49 +0000151static void client_event( int event, void *private )
Alexandre Julliard642d3131998-07-12 19:29:36 +0000152{
Alexandre Julliardc6e45ed1998-12-27 08:35:39 +0000153 struct client *client = (struct client *)private;
Alexandre Julliard5bc78081999-06-22 17:26:53 +0000154 if (event & WRITE_EVENT) do_write( client );
155 if (event & READ_EVENT) do_read( client );
Alexandre Julliard642d3131998-07-12 19:29:36 +0000156}
157
Alexandre Julliard642d3131998-07-12 19:29:36 +0000158/*******************************************************************/
159/* server-side exported functions */
160
161/* add a client */
Alexandre Julliard88de35c1999-05-16 16:57:49 +0000162struct client *add_client( int fd, struct thread *self )
Alexandre Julliard642d3131998-07-12 19:29:36 +0000163{
Alexandre Julliardf2616a21999-05-20 16:40:23 +0000164 int flags;
Alexandre Julliard88de35c1999-05-16 16:57:49 +0000165 struct client *client = mem_alloc( sizeof(*client) );
166 if (!client) return NULL;
Alexandre Julliard642d3131998-07-12 19:29:36 +0000167
Alexandre Julliardf2616a21999-05-20 16:40:23 +0000168 flags = fcntl( fd, F_GETFL, 0 );
169 fcntl( fd, F_SETFL, flags | O_NONBLOCK );
170
Alexandre Julliard88de35c1999-05-16 16:57:49 +0000171 client->select.fd = fd;
172 client->select.func = client_event;
173 client->select.private = client;
Alexandre Julliard642d3131998-07-12 19:29:36 +0000174 client->self = self;
Alexandre Julliard88de35c1999-05-16 16:57:49 +0000175 client->timeout = NULL;
Alexandre Julliard642d3131998-07-12 19:29:36 +0000176 client->pass_fd = -1;
Alexandre Julliard88de35c1999-05-16 16:57:49 +0000177 register_select_user( &client->select );
178 set_select_events( &client->select, READ_EVENT );
179 return client;
Alexandre Julliard642d3131998-07-12 19:29:36 +0000180}
181
182/* remove a client */
Alexandre Julliard88de35c1999-05-16 16:57:49 +0000183void remove_client( struct client *client, int exit_code )
Alexandre Julliard642d3131998-07-12 19:29:36 +0000184{
Alexandre Julliard642d3131998-07-12 19:29:36 +0000185 assert( client );
186
Alexandre Julliard767e6f61998-08-09 12:47:43 +0000187 call_kill_handler( client->self, exit_code );
Alexandre Julliard642d3131998-07-12 19:29:36 +0000188
Alexandre Julliard88de35c1999-05-16 16:57:49 +0000189 if (client->timeout) remove_timeout_user( client->timeout );
190 unregister_select_user( &client->select );
191 close( client->select.fd );
Alexandre Julliard642d3131998-07-12 19:29:36 +0000192
193 /* Purge messages */
Alexandre Julliard642d3131998-07-12 19:29:36 +0000194 if (client->pass_fd != -1) close( client->pass_fd );
195 free( client );
196}
197
Alexandre Julliard5bc78081999-06-22 17:26:53 +0000198/* set the fd to pass to the client */
199void client_pass_fd( struct client *client, int pass_fd )
Alexandre Julliard767e6f61998-08-09 12:47:43 +0000200{
Alexandre Julliard5bc78081999-06-22 17:26:53 +0000201 assert( client->pass_fd == -1 );
202 client->pass_fd = pass_fd;
203}
Alexandre Julliard767e6f61998-08-09 12:47:43 +0000204
Alexandre Julliard5bc78081999-06-22 17:26:53 +0000205/* send a reply to a client */
Alexandre Julliardebe29ef1999-06-26 08:43:26 +0000206void client_reply( struct client *client, unsigned int res )
Alexandre Julliard5bc78081999-06-22 17:26:53 +0000207{
Alexandre Julliardebe29ef1999-06-26 08:43:26 +0000208 if (debug_level) trace_reply( client->self, res, client->pass_fd );
209 client->res = res;
210 if (!do_write( client )) set_select_events( &client->select, WRITE_EVENT );
Alexandre Julliard642d3131998-07-12 19:29:36 +0000211}