Alexandre Julliard | 642d313 | 1998-07-12 19:29:36 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Server-side socket communication functions |
| 3 | * |
| 4 | * Copyright (C) 1998 Alexandre Julliard |
| 5 | */ |
| 6 | |
Patrik Stridvall | 9633632 | 1999-10-24 22:13:47 +0000 | [diff] [blame] | 7 | #include "config.h" |
| 8 | |
Alexandre Julliard | 642d313 | 1998-07-12 19:29:36 +0000 | [diff] [blame] | 9 | #include <assert.h> |
| 10 | #include <errno.h> |
Alexandre Julliard | f2616a2 | 1999-05-20 16:40:23 +0000 | [diff] [blame] | 11 | #include <fcntl.h> |
Alexandre Julliard | 642d313 | 1998-07-12 19:29:36 +0000 | [diff] [blame] | 12 | #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 Stridvall | 9633632 | 1999-10-24 22:13:47 +0000 | [diff] [blame] | 18 | #ifdef HAVE_SYS_SOCKET_H |
| 19 | # include <sys/socket.h> |
| 20 | #endif |
Alexandre Julliard | 642d313 | 1998-07-12 19:29:36 +0000 | [diff] [blame] | 21 | #include <sys/uio.h> |
| 22 | #include <unistd.h> |
| 23 | |
Alexandre Julliard | 829fe32 | 1998-07-26 14:27:39 +0000 | [diff] [blame] | 24 | #include "config.h" |
Alexandre Julliard | 43c190e | 1999-05-15 10:48:19 +0000 | [diff] [blame] | 25 | #include "object.h" |
Alexandre Julliard | 5bc7808 | 1999-06-22 17:26:53 +0000 | [diff] [blame] | 26 | #include "request.h" |
Alexandre Julliard | 642d313 | 1998-07-12 19:29:36 +0000 | [diff] [blame] | 27 | |
Alexandre Julliard | 829fe32 | 1998-07-26 14:27:39 +0000 | [diff] [blame] | 28 | /* Some versions of glibc don't define this */ |
| 29 | #ifndef SCM_RIGHTS |
| 30 | #define SCM_RIGHTS 1 |
| 31 | #endif |
| 32 | |
Alexandre Julliard | 642d313 | 1998-07-12 19:29:36 +0000 | [diff] [blame] | 33 | /* client structure */ |
| 34 | struct client |
| 35 | { |
Alexandre Julliard | 88de35c | 1999-05-16 16:57:49 +0000 | [diff] [blame] | 36 | struct select_user select; /* select user */ |
Alexandre Julliard | ebe29ef | 1999-06-26 08:43:26 +0000 | [diff] [blame] | 37 | unsigned int res; /* current result to send */ |
Alexandre Julliard | 88de35c | 1999-05-16 16:57:49 +0000 | [diff] [blame] | 38 | 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 Julliard | 642d313 | 1998-07-12 19:29:36 +0000 | [diff] [blame] | 41 | }; |
| 42 | |
Alexandre Julliard | 642d313 | 1998-07-12 19:29:36 +0000 | [diff] [blame] | 43 | |
Alexandre Julliard | ebe29ef | 1999-06-26 08:43:26 +0000 | [diff] [blame] | 44 | /* socket communication static structures */ |
Howard Abrams | b135adf | 1999-07-10 13:16:56 +0000 | [diff] [blame] | 45 | static struct iovec myiovec; |
| 46 | static struct msghdr msghdr = { NULL, 0, &myiovec, 1, }; |
Alexandre Julliard | ebe29ef | 1999-06-26 08:43:26 +0000 | [diff] [blame] | 47 | #ifndef HAVE_MSGHDR_ACCRIGHTS |
| 48 | struct cmsg_fd |
Alexandre Julliard | 642d313 | 1998-07-12 19:29:36 +0000 | [diff] [blame] | 49 | { |
Alexandre Julliard | ebe29ef | 1999-06-26 08:43:26 +0000 | [diff] [blame] | 50 | int len; /* sizeof structure */ |
| 51 | int level; /* SOL_SOCKET */ |
| 52 | int type; /* SCM_RIGHTS */ |
| 53 | int fd; /* fd to pass */ |
| 54 | }; |
| 55 | static struct cmsg_fd cmsg = { sizeof(cmsg), SOL_SOCKET, SCM_RIGHTS, -1 }; |
| 56 | #endif /* HAVE_MSGHDR_ACCRIGHTS */ |
Alexandre Julliard | 642d313 | 1998-07-12 19:29:36 +0000 | [diff] [blame] | 57 | |
Alexandre Julliard | 642d313 | 1998-07-12 19:29:36 +0000 | [diff] [blame] | 58 | |
Alexandre Julliard | 642d313 | 1998-07-12 19:29:36 +0000 | [diff] [blame] | 59 | /* send a message to a client that is ready to receive something */ |
Alexandre Julliard | ebe29ef | 1999-06-26 08:43:26 +0000 | [diff] [blame] | 60 | static int do_write( struct client *client ) |
Alexandre Julliard | 642d313 | 1998-07-12 19:29:36 +0000 | [diff] [blame] | 61 | { |
Alexandre Julliard | 642d313 | 1998-07-12 19:29:36 +0000 | [diff] [blame] | 62 | int ret; |
| 63 | |
Alexandre Julliard | 5bc7808 | 1999-06-22 17:26:53 +0000 | [diff] [blame] | 64 | if (client->pass_fd == -1) |
Alexandre Julliard | 642d313 | 1998-07-12 19:29:36 +0000 | [diff] [blame] | 65 | { |
Alexandre Julliard | ebe29ef | 1999-06-26 08:43:26 +0000 | [diff] [blame] | 66 | ret = write( client->select.fd, &client->res, sizeof(client->res) ); |
| 67 | if (ret == sizeof(client->res)) goto ok; |
Alexandre Julliard | 642d313 | 1998-07-12 19:29:36 +0000 | [diff] [blame] | 68 | } |
Alexandre Julliard | 5bc7808 | 1999-06-22 17:26:53 +0000 | [diff] [blame] | 69 | else /* we have an fd to send */ |
Alexandre Julliard | 642d313 | 1998-07-12 19:29:36 +0000 | [diff] [blame] | 70 | { |
Patrik Stridvall | 1bb9403 | 1999-05-08 15:47:44 +0000 | [diff] [blame] | 71 | #ifdef HAVE_MSGHDR_ACCRIGHTS |
Alexandre Julliard | ebe29ef | 1999-06-26 08:43:26 +0000 | [diff] [blame] | 72 | msghdr.msg_accrightslen = sizeof(int); |
Alexandre Julliard | 829fe32 | 1998-07-26 14:27:39 +0000 | [diff] [blame] | 73 | msghdr.msg_accrights = (void *)&client->pass_fd; |
Patrik Stridvall | 1bb9403 | 1999-05-08 15:47:44 +0000 | [diff] [blame] | 74 | #else /* HAVE_MSGHDR_ACCRIGHTS */ |
Alexandre Julliard | 5bc7808 | 1999-06-22 17:26:53 +0000 | [diff] [blame] | 75 | msghdr.msg_control = &cmsg; |
Alexandre Julliard | 642d313 | 1998-07-12 19:29:36 +0000 | [diff] [blame] | 76 | msghdr.msg_controllen = sizeof(cmsg); |
Alexandre Julliard | ebe29ef | 1999-06-26 08:43:26 +0000 | [diff] [blame] | 77 | cmsg.fd = client->pass_fd; |
Patrik Stridvall | 1bb9403 | 1999-05-08 15:47:44 +0000 | [diff] [blame] | 78 | #endif /* HAVE_MSGHDR_ACCRIGHTS */ |
| 79 | |
Howard Abrams | b135adf | 1999-07-10 13:16:56 +0000 | [diff] [blame] | 80 | myiovec.iov_base = (void *)&client->res; |
| 81 | myiovec.iov_len = sizeof(client->res); |
Alexandre Julliard | 5bc7808 | 1999-06-22 17:26:53 +0000 | [diff] [blame] | 82 | |
| 83 | ret = sendmsg( client->select.fd, &msghdr, 0 ); |
| 84 | close( client->pass_fd ); |
| 85 | client->pass_fd = -1; |
Alexandre Julliard | ebe29ef | 1999-06-26 08:43:26 +0000 | [diff] [blame] | 86 | if (ret == sizeof(client->res)) goto ok; |
Alexandre Julliard | 5bc7808 | 1999-06-22 17:26:53 +0000 | [diff] [blame] | 87 | } |
Alexandre Julliard | 642d313 | 1998-07-12 19:29:36 +0000 | [diff] [blame] | 88 | if (ret == -1) |
| 89 | { |
Alexandre Julliard | ebe29ef | 1999-06-26 08:43:26 +0000 | [diff] [blame] | 90 | if (errno == EWOULDBLOCK) return 0; /* not a fatal error */ |
Alexandre Julliard | 642d313 | 1998-07-12 19:29:36 +0000 | [diff] [blame] | 91 | if (errno != EPIPE) perror("sendmsg"); |
Alexandre Julliard | 642d313 | 1998-07-12 19:29:36 +0000 | [diff] [blame] | 92 | } |
Alexandre Julliard | ebe29ef | 1999-06-26 08:43:26 +0000 | [diff] [blame] | 93 | else fprintf( stderr, "Partial message sent %d/%d\n", ret, sizeof(client->res) ); |
Alexandre Julliard | 5bc7808 | 1999-06-22 17:26:53 +0000 | [diff] [blame] | 94 | remove_client( client, BROKEN_PIPE ); |
Alexandre Julliard | ebe29ef | 1999-06-26 08:43:26 +0000 | [diff] [blame] | 95 | return 0; |
| 96 | |
| 97 | ok: |
| 98 | set_select_events( &client->select, READ_EVENT ); |
| 99 | return 1; |
Alexandre Julliard | 642d313 | 1998-07-12 19:29:36 +0000 | [diff] [blame] | 100 | } |
| 101 | |
| 102 | |
| 103 | /* read a message from a client that has something to say */ |
Alexandre Julliard | 5bc7808 | 1999-06-22 17:26:53 +0000 | [diff] [blame] | 104 | static void do_read( struct client *client ) |
Alexandre Julliard | 642d313 | 1998-07-12 19:29:36 +0000 | [diff] [blame] | 105 | { |
Alexandre Julliard | ebe29ef | 1999-06-26 08:43:26 +0000 | [diff] [blame] | 106 | int ret; |
| 107 | enum request req; |
Alexandre Julliard | 642d313 | 1998-07-12 19:29:36 +0000 | [diff] [blame] | 108 | |
Patrik Stridvall | 1bb9403 | 1999-05-08 15:47:44 +0000 | [diff] [blame] | 109 | #ifdef HAVE_MSGHDR_ACCRIGHTS |
Alexandre Julliard | ebe29ef | 1999-06-26 08:43:26 +0000 | [diff] [blame] | 110 | msghdr.msg_accrightslen = sizeof(int); |
| 111 | msghdr.msg_accrights = (void *)&client->pass_fd; |
Patrik Stridvall | 1bb9403 | 1999-05-08 15:47:44 +0000 | [diff] [blame] | 112 | #else /* HAVE_MSGHDR_ACCRIGHTS */ |
Patrik Stridvall | 1bb9403 | 1999-05-08 15:47:44 +0000 | [diff] [blame] | 113 | msghdr.msg_control = &cmsg; |
| 114 | msghdr.msg_controllen = sizeof(cmsg); |
Alexandre Julliard | ebe29ef | 1999-06-26 08:43:26 +0000 | [diff] [blame] | 115 | cmsg.fd = -1; |
Patrik Stridvall | 1bb9403 | 1999-05-08 15:47:44 +0000 | [diff] [blame] | 116 | #endif /* HAVE_MSGHDR_ACCRIGHTS */ |
| 117 | |
Alexandre Julliard | 5bc7808 | 1999-06-22 17:26:53 +0000 | [diff] [blame] | 118 | assert( client->pass_fd == -1 ); |
| 119 | |
Howard Abrams | b135adf | 1999-07-10 13:16:56 +0000 | [diff] [blame] | 120 | myiovec.iov_base = (void *)&req; |
| 121 | myiovec.iov_len = sizeof(req); |
Alexandre Julliard | 5bc7808 | 1999-06-22 17:26:53 +0000 | [diff] [blame] | 122 | |
| 123 | ret = recvmsg( client->select.fd, &msghdr, 0 ); |
| 124 | #ifndef HAVE_MSGHDR_ACCRIGHTS |
| 125 | client->pass_fd = cmsg.fd; |
| 126 | #endif |
| 127 | |
Alexandre Julliard | ebe29ef | 1999-06-26 08:43:26 +0000 | [diff] [blame] | 128 | if (ret == sizeof(req)) |
Alexandre Julliard | 642d313 | 1998-07-12 19:29:36 +0000 | [diff] [blame] | 129 | { |
Alexandre Julliard | 5bc7808 | 1999-06-22 17:26:53 +0000 | [diff] [blame] | 130 | int pass_fd = client->pass_fd; |
Alexandre Julliard | 5bc7808 | 1999-06-22 17:26:53 +0000 | [diff] [blame] | 131 | client->pass_fd = -1; |
Alexandre Julliard | ebe29ef | 1999-06-26 08:43:26 +0000 | [diff] [blame] | 132 | call_req_handler( client->self, req, pass_fd ); |
Alexandre Julliard | 5bc7808 | 1999-06-22 17:26:53 +0000 | [diff] [blame] | 133 | if (pass_fd != -1) close( pass_fd ); |
| 134 | return; |
Alexandre Julliard | 642d313 | 1998-07-12 19:29:36 +0000 | [diff] [blame] | 135 | } |
Alexandre Julliard | 642d313 | 1998-07-12 19:29:36 +0000 | [diff] [blame] | 136 | if (ret == -1) |
| 137 | { |
| 138 | perror("recvmsg"); |
Alexandre Julliard | 88de35c | 1999-05-16 16:57:49 +0000 | [diff] [blame] | 139 | remove_client( client, BROKEN_PIPE ); |
Alexandre Julliard | 767e6f6 | 1998-08-09 12:47:43 +0000 | [diff] [blame] | 140 | return; |
Alexandre Julliard | 642d313 | 1998-07-12 19:29:36 +0000 | [diff] [blame] | 141 | } |
Alexandre Julliard | 5bc7808 | 1999-06-22 17:26:53 +0000 | [diff] [blame] | 142 | if (!ret) /* closed pipe */ |
Alexandre Julliard | 767e6f6 | 1998-08-09 12:47:43 +0000 | [diff] [blame] | 143 | { |
Alexandre Julliard | 88de35c | 1999-05-16 16:57:49 +0000 | [diff] [blame] | 144 | remove_client( client, BROKEN_PIPE ); |
Alexandre Julliard | 767e6f6 | 1998-08-09 12:47:43 +0000 | [diff] [blame] | 145 | return; |
| 146 | } |
Alexandre Julliard | ebe29ef | 1999-06-26 08:43:26 +0000 | [diff] [blame] | 147 | fatal_protocol_error( client->self, "partial message received %d/%d\n", ret, sizeof(req) ); |
Alexandre Julliard | 642d313 | 1998-07-12 19:29:36 +0000 | [diff] [blame] | 148 | } |
| 149 | |
Alexandre Julliard | c6e45ed | 1998-12-27 08:35:39 +0000 | [diff] [blame] | 150 | /* handle a client event */ |
Alexandre Julliard | 88de35c | 1999-05-16 16:57:49 +0000 | [diff] [blame] | 151 | static void client_event( int event, void *private ) |
Alexandre Julliard | 642d313 | 1998-07-12 19:29:36 +0000 | [diff] [blame] | 152 | { |
Alexandre Julliard | c6e45ed | 1998-12-27 08:35:39 +0000 | [diff] [blame] | 153 | struct client *client = (struct client *)private; |
Alexandre Julliard | 5bc7808 | 1999-06-22 17:26:53 +0000 | [diff] [blame] | 154 | if (event & WRITE_EVENT) do_write( client ); |
| 155 | if (event & READ_EVENT) do_read( client ); |
Alexandre Julliard | 642d313 | 1998-07-12 19:29:36 +0000 | [diff] [blame] | 156 | } |
| 157 | |
Alexandre Julliard | 642d313 | 1998-07-12 19:29:36 +0000 | [diff] [blame] | 158 | /*******************************************************************/ |
| 159 | /* server-side exported functions */ |
| 160 | |
| 161 | /* add a client */ |
Alexandre Julliard | 88de35c | 1999-05-16 16:57:49 +0000 | [diff] [blame] | 162 | struct client *add_client( int fd, struct thread *self ) |
Alexandre Julliard | 642d313 | 1998-07-12 19:29:36 +0000 | [diff] [blame] | 163 | { |
Alexandre Julliard | f2616a2 | 1999-05-20 16:40:23 +0000 | [diff] [blame] | 164 | int flags; |
Alexandre Julliard | 88de35c | 1999-05-16 16:57:49 +0000 | [diff] [blame] | 165 | struct client *client = mem_alloc( sizeof(*client) ); |
| 166 | if (!client) return NULL; |
Alexandre Julliard | 642d313 | 1998-07-12 19:29:36 +0000 | [diff] [blame] | 167 | |
Alexandre Julliard | f2616a2 | 1999-05-20 16:40:23 +0000 | [diff] [blame] | 168 | flags = fcntl( fd, F_GETFL, 0 ); |
| 169 | fcntl( fd, F_SETFL, flags | O_NONBLOCK ); |
| 170 | |
Alexandre Julliard | 88de35c | 1999-05-16 16:57:49 +0000 | [diff] [blame] | 171 | client->select.fd = fd; |
| 172 | client->select.func = client_event; |
| 173 | client->select.private = client; |
Alexandre Julliard | 642d313 | 1998-07-12 19:29:36 +0000 | [diff] [blame] | 174 | client->self = self; |
Alexandre Julliard | 88de35c | 1999-05-16 16:57:49 +0000 | [diff] [blame] | 175 | client->timeout = NULL; |
Alexandre Julliard | 642d313 | 1998-07-12 19:29:36 +0000 | [diff] [blame] | 176 | client->pass_fd = -1; |
Alexandre Julliard | 88de35c | 1999-05-16 16:57:49 +0000 | [diff] [blame] | 177 | register_select_user( &client->select ); |
| 178 | set_select_events( &client->select, READ_EVENT ); |
| 179 | return client; |
Alexandre Julliard | 642d313 | 1998-07-12 19:29:36 +0000 | [diff] [blame] | 180 | } |
| 181 | |
| 182 | /* remove a client */ |
Alexandre Julliard | 88de35c | 1999-05-16 16:57:49 +0000 | [diff] [blame] | 183 | void remove_client( struct client *client, int exit_code ) |
Alexandre Julliard | 642d313 | 1998-07-12 19:29:36 +0000 | [diff] [blame] | 184 | { |
Alexandre Julliard | 642d313 | 1998-07-12 19:29:36 +0000 | [diff] [blame] | 185 | assert( client ); |
| 186 | |
Alexandre Julliard | 767e6f6 | 1998-08-09 12:47:43 +0000 | [diff] [blame] | 187 | call_kill_handler( client->self, exit_code ); |
Alexandre Julliard | 642d313 | 1998-07-12 19:29:36 +0000 | [diff] [blame] | 188 | |
Alexandre Julliard | 88de35c | 1999-05-16 16:57:49 +0000 | [diff] [blame] | 189 | if (client->timeout) remove_timeout_user( client->timeout ); |
| 190 | unregister_select_user( &client->select ); |
| 191 | close( client->select.fd ); |
Alexandre Julliard | 642d313 | 1998-07-12 19:29:36 +0000 | [diff] [blame] | 192 | |
| 193 | /* Purge messages */ |
Alexandre Julliard | 642d313 | 1998-07-12 19:29:36 +0000 | [diff] [blame] | 194 | if (client->pass_fd != -1) close( client->pass_fd ); |
| 195 | free( client ); |
| 196 | } |
| 197 | |
Alexandre Julliard | 5bc7808 | 1999-06-22 17:26:53 +0000 | [diff] [blame] | 198 | /* set the fd to pass to the client */ |
| 199 | void client_pass_fd( struct client *client, int pass_fd ) |
Alexandre Julliard | 767e6f6 | 1998-08-09 12:47:43 +0000 | [diff] [blame] | 200 | { |
Alexandre Julliard | 5bc7808 | 1999-06-22 17:26:53 +0000 | [diff] [blame] | 201 | assert( client->pass_fd == -1 ); |
| 202 | client->pass_fd = pass_fd; |
| 203 | } |
Alexandre Julliard | 767e6f6 | 1998-08-09 12:47:43 +0000 | [diff] [blame] | 204 | |
Alexandre Julliard | 5bc7808 | 1999-06-22 17:26:53 +0000 | [diff] [blame] | 205 | /* send a reply to a client */ |
Alexandre Julliard | ebe29ef | 1999-06-26 08:43:26 +0000 | [diff] [blame] | 206 | void client_reply( struct client *client, unsigned int res ) |
Alexandre Julliard | 5bc7808 | 1999-06-22 17:26:53 +0000 | [diff] [blame] | 207 | { |
Alexandre Julliard | ebe29ef | 1999-06-26 08:43:26 +0000 | [diff] [blame] | 208 | 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 Julliard | 642d313 | 1998-07-12 19:29:36 +0000 | [diff] [blame] | 211 | } |