2007-09-02 Zoltan Varga <vargaz@gmail.com>
[mono.git] / mono / io-layer / daemon-messages.c
1 /*
2  * daemon-messages.c:  Communications to and from the handle daemon
3  *
4  * Author:
5  *      Dick Porter (dick@ximian.com)
6  *
7  * (C) 2002 Ximian, Inc.
8  */
9
10 #include <config.h>
11 #include <glib.h>
12 #include <pthread.h>
13 #include <errno.h>
14 #include <string.h>
15 #include <sys/types.h>
16 #include <sys/socket.h>
17 /* Freebsd needs this included explicitly, but it doesn't hurt on Linux */
18 #include <sys/uio.h>
19
20 #ifndef HAVE_MSG_NOSIGNAL
21 #include <signal.h>
22 #endif
23
24 #include <mono/io-layer/wapi.h>
25 #include <mono/io-layer/daemon-messages.h>
26
27 /* Solaris doesn't define these */
28 #ifndef CMSG_LEN
29 #define CMSG_LEN(size)    (sizeof (struct cmsghdr) + (size))
30 #endif
31 #ifndef CMSG_SPACE
32 #define CMSG_SPACE(size)  (sizeof (struct cmsghdr) + (size))
33 #endif
34
35 static mono_mutex_t req_mutex;
36 static mono_once_t attr_key_once = MONO_ONCE_INIT;
37 static mono_mutexattr_t attr;
38
39 static void attr_init (void)
40 {
41         int ret;
42
43         ret = mono_mutexattr_init (&attr);
44         g_assert (ret == 0);
45
46         ret = mono_mutexattr_settype (&attr, MONO_MUTEX_RECURSIVE);
47         g_assert (ret == 0);
48
49         ret = mono_mutex_init (&req_mutex, &attr);
50         g_assert (ret == 0);
51 }
52
53 /* Send request on fd, wait for response (called by applications, not
54  * the daemon, indirectly through _wapi_daemon_request_response and
55  * _wapi_daemon_request_response_with_fds)
56  */
57 static void _wapi_daemon_request_response_internal (int fd,
58                                                     struct msghdr *msg,
59                                                     WapiHandleResponse *resp)
60 {
61         int ret;
62 #ifndef HAVE_MSG_NOSIGNAL
63         void (*old_sigpipe)(int);
64 #endif
65
66         mono_once (&attr_key_once, attr_init);
67
68         /* Serialise requests to the daemon from the same process.  We
69          * rely on request turnaround time being minimal anyway, so
70          * performance shouldnt suffer from the mutex.
71          */
72         pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
73                               (void *)&req_mutex);
74         ret = mono_mutex_lock (&req_mutex);
75         g_assert (ret == 0);
76         
77 #ifdef HAVE_MSG_NOSIGNAL
78         do {
79                 ret=sendmsg (fd, msg, MSG_NOSIGNAL);
80         }
81         while (ret==-1 && errno==EINTR);
82 #else
83         old_sigpipe = signal (SIGPIPE, SIG_IGN);
84         do {
85                 ret=sendmsg (fd, msg, 0);
86         }
87         while (ret==-1 && errno==EINTR);
88 #endif
89
90         if(ret!=sizeof(WapiHandleRequest)) {
91                 if(errno==EPIPE) {
92                         g_critical (G_GNUC_PRETTY_FUNCTION ": The handle daemon vanished!");
93                         exit (-1);
94                 } else {
95                         g_warning (G_GNUC_PRETTY_FUNCTION ": Send error: %s",
96                                    strerror (errno));
97                         g_assert_not_reached ();
98                 }
99         }
100
101 #ifdef HAVE_MSG_NOSIGNAL
102         do {
103                 ret=recv (fd, resp, sizeof(WapiHandleResponse), MSG_NOSIGNAL);
104         }
105         while (ret==-1 && errno==EINTR);
106 #else
107         do {
108                 ret=recv (fd, resp, sizeof(WapiHandleResponse), 0);
109         }
110         while (ret==-1 && errno==EINTR);
111         signal (SIGPIPE, old_sigpipe);
112 #endif
113
114         if(ret==-1) {
115                 if(errno==EPIPE) {
116                         g_critical (G_GNUC_PRETTY_FUNCTION ": The handle daemon vanished!");
117                         exit (-1);
118                 } else {
119                         g_warning (G_GNUC_PRETTY_FUNCTION ": Send error: %s",
120                                    strerror (errno));
121                         g_assert_not_reached ();
122                 }
123         }
124                 
125         ret = mono_mutex_unlock (&req_mutex);
126         g_assert (ret == 0);
127         
128         pthread_cleanup_pop (0);
129 }
130
131 /* Send request on fd with filedescriptors, wait for response (called
132  * by applications, not the daemon)
133  */
134 void _wapi_daemon_request_response_with_fds (int fd, WapiHandleRequest *req,
135                                              WapiHandleResponse *resp,
136                                              int in_fd, int out_fd, int err_fd)
137 {
138         struct msghdr msg={0};
139         struct cmsghdr *cmsg;
140         struct iovec iov;
141         char cmsgdata[CMSG_SPACE (sizeof(int)*3)];
142         int *fdptr;
143         
144         msg.msg_name=NULL;
145         msg.msg_namelen=0;
146         msg.msg_iov=&iov;
147         msg.msg_iovlen=1;
148         msg.msg_control=cmsgdata;
149         msg.msg_controllen=sizeof(cmsgdata);
150         msg.msg_flags=0;
151
152         iov.iov_base=req;
153         iov.iov_len=sizeof(WapiHandleRequest);
154
155         cmsg=CMSG_FIRSTHDR (&msg);
156         cmsg->cmsg_len=CMSG_LEN (sizeof(int)*3);
157         cmsg->cmsg_level=SOL_SOCKET;
158         cmsg->cmsg_type=SCM_RIGHTS;
159         fdptr=(int *)CMSG_DATA (cmsg);
160         fdptr[0]=in_fd;
161         fdptr[1]=out_fd;
162         fdptr[2]=err_fd;
163
164         msg.msg_controllen=CMSG_SPACE (sizeof(int)*3);
165
166         _wapi_daemon_request_response_internal (fd, &msg, resp);
167 }
168
169 /* Send request on fd, wait for response (called by applications, not
170  * the daemon)
171  */
172 void _wapi_daemon_request_response (int fd, WapiHandleRequest *req,
173                                     WapiHandleResponse *resp)
174 {
175         struct msghdr msg={0};
176         struct iovec iov;
177         
178         msg.msg_name=NULL;
179         msg.msg_namelen=0;
180         msg.msg_iov=&iov;
181         msg.msg_iovlen=1;
182         msg.msg_control=NULL;
183         msg.msg_controllen=0;
184         msg.msg_flags=0;
185
186         iov.iov_base=req;
187         iov.iov_len=sizeof(WapiHandleRequest);
188
189         _wapi_daemon_request_response_internal (fd, &msg, resp);
190 }
191
192 /* Read request on fd (called by the daemon) */
193 int _wapi_daemon_request (int fd, WapiHandleRequest *req, int *fds,
194                           gboolean *has_fds)
195 {
196         int ret;
197         struct msghdr msg;
198         struct iovec iov;
199         struct cmsghdr *cmsg;
200         guchar cmsgdata[CMSG_SPACE (sizeof(int)*3)];
201         
202         msg.msg_name=NULL;
203         msg.msg_namelen=0;
204         msg.msg_iov=&iov;
205         msg.msg_iovlen=1;
206         msg.msg_control=cmsgdata;
207         msg.msg_controllen=sizeof(cmsgdata);
208         msg.msg_flags=0;
209         iov.iov_base=req;
210         iov.iov_len=sizeof(WapiHandleRequest);
211         
212         do {
213 #ifdef HAVE_MSG_NOSIGNAL
214                 ret=recvmsg (fd, &msg, MSG_NOSIGNAL);
215 #else
216                 ret=recvmsg (fd, &msg, 0);
217 #endif
218         }
219         while (ret==-1 && errno==EINTR);
220
221         if(ret==-1 || ret!= sizeof(WapiHandleRequest)) {
222                 /* Make sure we dont do anything with this response */
223                 req->type=WapiHandleRequestType_Error;
224                 
225 #ifdef DEBUG
226                 g_warning (G_GNUC_PRETTY_FUNCTION ": Recv error: %s",
227                            strerror (errno));
228 #endif
229                 /* The next loop around poll() should tidy up */
230         }
231
232 #ifdef DEBUG
233         if(msg.msg_flags & MSG_OOB) {
234                 g_message (G_GNUC_PRETTY_FUNCTION ": OOB data received");
235         }
236         if(msg.msg_flags & MSG_CTRUNC) {
237                 g_message (G_GNUC_PRETTY_FUNCTION ": ancillary data was truncated");
238         }
239         g_message (G_GNUC_PRETTY_FUNCTION ": msg.msg_controllen=%d",
240                    msg.msg_controllen);
241 #endif
242
243         cmsg=CMSG_FIRSTHDR (&msg);
244         if(cmsg!=NULL && cmsg->cmsg_level==SOL_SOCKET &&
245            cmsg->cmsg_type==SCM_RIGHTS) {
246 #ifdef DEBUG
247                 g_message (G_GNUC_PRETTY_FUNCTION ": cmsg->cmsg_len=%d",
248                            cmsg->cmsg_len);
249                 g_message (G_GNUC_PRETTY_FUNCTION
250                            ": cmsg->level=%d cmsg->type=%d", cmsg->cmsg_level,
251                            cmsg->cmsg_type);
252 #endif
253
254                 memcpy (fds, (int *)CMSG_DATA (cmsg), sizeof(int)*3);
255                 *has_fds=TRUE;
256
257 #ifdef DEBUG
258                 g_message (G_GNUC_PRETTY_FUNCTION
259                            ": fd[0]=%d, fd[1]=%d, fd[2]=%d", fds[0], fds[1],
260                            fds[2]);
261 #endif
262         } else {
263 #ifdef DEBUG
264                 g_message (G_GNUC_PRETTY_FUNCTION ": no ancillary data");
265 #endif
266                 *has_fds=FALSE;
267         }
268
269         return(ret);
270 }
271
272 /* Send response on fd (called by the daemon) */
273 int _wapi_daemon_response (int fd, WapiHandleResponse *resp)
274 {
275         int ret;
276
277         do {
278 #ifdef HAVE_MSG_NOSIGNAL
279                 ret=send (fd, resp, sizeof(WapiHandleResponse), MSG_NOSIGNAL);
280 #else
281                 ret=send (fd, resp, sizeof(WapiHandleResponse), 0);
282 #endif
283         }
284         while (ret==-1 && errno==EINTR);
285
286 #ifdef DEBUG
287
288         if(ret==-1 || ret != sizeof(WapiHandleResponse)) {
289                 g_warning (G_GNUC_PRETTY_FUNCTION ": Send error: %s",
290                            strerror (errno));
291                 /* The next loop around poll() should tidy up */
292         }
293 #endif
294
295         return(ret);
296 }