Fix file descriptor leak in daemon.
[libguestfs.git] / daemon / proto.c
1 /* libguestfs - the guestfsd daemon
2  * Copyright (C) 2009 Red Hat Inc. 
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18
19 #include <config.h>
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <stdarg.h>
24 #include <string.h>
25 #include <unistd.h>
26 #include <errno.h>
27 #include <ctype.h>
28 #include <sys/param.h>          /* defines MIN */
29 #include <rpc/types.h>
30 #include <rpc/xdr.h>
31
32 #include "daemon.h"
33 #include "../src/guestfs_protocol.h"
34
35 /* The message currently being processed. */
36 int proc_nr;
37 int serial;
38
39 /* The daemon communications socket. */
40 static int sock;
41
42 void
43 main_loop (int _sock)
44 {
45   XDR xdr;
46   char *buf;
47   char lenbuf[4];
48   unsigned len;
49   struct guestfs_message_header hdr;
50
51   sock = _sock;
52
53   for (;;) {
54 #if 0
55     /* Most common errors are leaked memory and leaked file descriptors,
56      * so run this between each command:
57      */
58     if (verbose)
59       system ("ls -l /proc/self/fd");
60 #endif
61
62     /* Read the length word. */
63     xread (sock, lenbuf, 4);
64     xdrmem_create (&xdr, lenbuf, 4, XDR_DECODE);
65     xdr_uint32_t (&xdr, &len);
66     xdr_destroy (&xdr);
67
68     if (len > GUESTFS_MESSAGE_MAX) {
69       fprintf (stderr, "guestfsd: incoming message is too long (%u bytes)\n",
70                len);
71       exit (1);
72     }
73
74     buf = malloc (len);
75     if (!buf) {
76       reply_with_perror ("malloc");
77       continue;
78     }
79
80     xread (sock, buf, len);
81
82     if (verbose) {
83       int i, j;
84
85       for (i = 0; i < len; i += 16) {
86         printf ("%04x: ", i);
87         for (j = i; j < MIN (i+16, len); ++j)
88           printf ("%02x ", (unsigned char) buf[j]);
89         for (; j < i+16; ++j)
90           printf ("   ");
91         printf ("|");
92         for (j = i; j < MIN (i+16, len); ++j)
93           if (isprint (buf[j]))
94             printf ("%c", buf[j]);
95           else
96             printf (".");
97         for (; j < i+16; ++j)
98           printf (" ");
99         printf ("|\n");
100       }
101     }
102
103     /* Decode the message header. */
104     xdrmem_create (&xdr, buf, len, XDR_DECODE);
105     if (!xdr_guestfs_message_header (&xdr, &hdr)) {
106       fprintf (stderr, "guestfsd: could not decode message header\n");
107       exit (1);
108     }
109
110     /* Check the version etc. */
111     if (hdr.prog != GUESTFS_PROGRAM) {
112       reply_with_error ("wrong program (%d)", hdr.prog);
113       goto cont;
114     }
115     if (hdr.vers != GUESTFS_PROTOCOL_VERSION) {
116       reply_with_error ("wrong protocol version (%d)", hdr.vers);
117       goto cont;
118     }
119     if (hdr.direction != GUESTFS_DIRECTION_CALL) {
120       reply_with_error ("unexpected message direction (%d)", hdr.direction);
121       goto cont;
122     }
123     if (hdr.status != GUESTFS_STATUS_OK) {
124       reply_with_error ("unexpected message status (%d)", hdr.status);
125       goto cont;
126     }
127
128     /* Now start to process this message. */
129     proc_nr = hdr.proc;
130     serial = hdr.serial;
131     dispatch_incoming_message (&xdr);
132     /* Note that dispatch_incoming_message will also send a reply. */
133
134   cont:
135     xdr_destroy (&xdr);
136     free (buf);
137   }
138 }
139
140 static void send_error (const char *msg);
141
142 void
143 reply_with_error (const char *fs, ...)
144 {
145   char err[GUESTFS_ERROR_LEN];
146   va_list args;
147
148   va_start (args, fs);
149   vsnprintf (err, sizeof err, fs, args);
150   va_end (args);
151
152   send_error (err);
153 }
154
155 void
156 reply_with_perror (const char *fs, ...)
157 {
158   char buf1[GUESTFS_ERROR_LEN];
159   char buf2[GUESTFS_ERROR_LEN];
160   va_list args;
161   int err = errno;
162
163   va_start (args, fs);
164   vsnprintf (buf1, sizeof buf1, fs, args);
165   va_end (args);
166
167   snprintf (buf2, sizeof buf2, "%s: %s", buf1, strerror (err));
168
169   send_error (buf2);
170 }
171
172 static void
173 send_error (const char *msg)
174 {
175   XDR xdr;
176   char buf[GUESTFS_ERROR_LEN + 200];
177   char lenbuf[4];
178   struct guestfs_message_header hdr;
179   struct guestfs_message_error err;
180   unsigned len;
181
182   fprintf (stderr, "guestfsd: error: %s\n", msg);
183
184   xdrmem_create (&xdr, buf, sizeof buf, XDR_ENCODE);
185
186   hdr.prog = GUESTFS_PROGRAM;
187   hdr.vers = GUESTFS_PROTOCOL_VERSION;
188   hdr.direction = GUESTFS_DIRECTION_REPLY;
189   hdr.status = GUESTFS_STATUS_ERROR;
190   hdr.proc = proc_nr;
191   hdr.serial = serial;
192
193   if (!xdr_guestfs_message_header (&xdr, &hdr)) {
194     fprintf (stderr, "guestfsd: failed to encode error message header\n");
195     exit (1);
196   }
197
198   err.error_message = (char *) msg;
199
200   if (!xdr_guestfs_message_error (&xdr, &err)) {
201     fprintf (stderr, "guestfsd: failed to encode error message body\n");
202     exit (1);
203   }
204
205   len = xdr_getpos (&xdr);
206   xdr_destroy (&xdr);
207
208   xdrmem_create (&xdr, lenbuf, 4, XDR_ENCODE);
209   xdr_uint32_t (&xdr, &len);
210   xdr_destroy (&xdr);
211
212   (void) xwrite (sock, lenbuf, 4);
213   (void) xwrite (sock, buf, len);
214 }
215
216 void
217 reply (xdrproc_t xdrp, char *ret)
218 {
219   XDR xdr;
220   char buf[GUESTFS_MESSAGE_MAX];
221   char lenbuf[4];
222   struct guestfs_message_header hdr;
223   unsigned len;
224
225   xdrmem_create (&xdr, buf, sizeof buf, XDR_ENCODE);
226
227   hdr.prog = GUESTFS_PROGRAM;
228   hdr.vers = GUESTFS_PROTOCOL_VERSION;
229   hdr.direction = GUESTFS_DIRECTION_REPLY;
230   hdr.status = GUESTFS_STATUS_OK;
231   hdr.proc = proc_nr;
232   hdr.serial = serial;
233
234   if (!xdr_guestfs_message_header (&xdr, &hdr)) {
235     fprintf (stderr, "guestfsd: failed to encode reply header\n");
236     exit (1);
237   }
238
239   if (xdrp) {
240     if (!(*xdrp) (&xdr, ret)) {
241       fprintf (stderr, "guestfsd: failed to encode reply body\n");
242       exit (1);
243     }
244   }
245
246   len = xdr_getpos (&xdr);
247   xdr_destroy (&xdr);
248
249   xdrmem_create (&xdr, lenbuf, 4, XDR_ENCODE);
250   xdr_uint32_t (&xdr, &len);
251   xdr_destroy (&xdr);
252
253   (void) xwrite (sock, lenbuf, 4);
254   (void) xwrite (sock, buf, len);
255 }
256
257 /* Receive file chunks, repeatedly calling 'cb'. */
258 int
259 receive_file (receive_cb cb, void *opaque)
260 {
261   guestfs_chunk chunk;
262   char lenbuf[4];
263   char *buf;
264   XDR xdr;
265   int r;
266   uint32_t len;
267
268   for (;;) {
269     /* Read the length word. */
270     xread (sock, lenbuf, 4);
271     xdrmem_create (&xdr, lenbuf, 4, XDR_DECODE);
272     xdr_uint32_t (&xdr, &len);
273     xdr_destroy (&xdr);
274
275     if (len == GUESTFS_CANCEL_FLAG)
276       continue;                 /* Just ignore it. */
277
278     if (len > GUESTFS_MESSAGE_MAX) {
279       fprintf (stderr, "guestfsd: incoming message is too long (%u bytes)\n",
280                len);
281       exit (1);
282     }
283
284     buf = malloc (len);
285     if (!buf) {
286       perror ("malloc");
287       return -1;
288     }
289
290     xread (sock, buf, len);
291
292     xdrmem_create (&xdr, buf, len, XDR_DECODE);
293     memset (&chunk, 0, sizeof chunk);
294     if (!xdr_guestfs_chunk (&xdr, &chunk)) {
295       xdr_destroy (&xdr);
296       free (buf);
297       return -1;
298     }
299     xdr_destroy (&xdr);
300     free (buf);
301
302     if (verbose)
303       printf ("receive_file: got chunk: cancel = %d, len = %d, buf = %p\n",
304               chunk.cancel, chunk.data.data_len, chunk.data.data_val);
305
306     if (chunk.cancel) {
307       fprintf (stderr, "receive_file: received cancellation from library\n");
308       xdr_free ((xdrproc_t) xdr_guestfs_chunk, (char *) &chunk);
309       return -2;
310     }
311     if (chunk.data.data_len == 0) {
312       xdr_free ((xdrproc_t) xdr_guestfs_chunk, (char *) &chunk);
313       return 0;                 /* end of file */
314     }
315
316     if (cb)
317       r = cb (opaque, chunk.data.data_val, chunk.data.data_len);
318     else
319       r = 0;
320
321     xdr_free ((xdrproc_t) xdr_guestfs_chunk, (char *) &chunk);
322     if (r == -1)                /* write error */
323       return -1;
324   }
325 }
326
327 /* Send a cancellation flag back to the library. */
328 void
329 cancel_receive (void)
330 {
331   XDR xdr;
332   char fbuf[4];
333   uint32_t flag = GUESTFS_CANCEL_FLAG;
334
335   xdrmem_create (&xdr, fbuf, sizeof fbuf, XDR_ENCODE);
336   xdr_uint32_t (&xdr, &flag);
337   xdr_destroy (&xdr);
338
339   if (xwrite (sock, fbuf, sizeof fbuf) == -1) {
340     perror ("write to socket");
341     return;
342   }
343
344   /* Keep receiving chunks and discarding, until library sees cancel. */
345   (void) receive_file (NULL, NULL);
346 }
347
348 static int check_for_library_cancellation (void);
349 static int send_chunk (const guestfs_chunk *);
350
351 /* Also check if the library sends us a cancellation message. */
352 int
353 send_file_write (const void *buf, int len)
354 {
355   guestfs_chunk chunk;
356   int cancel;
357
358   if (len > GUESTFS_MAX_CHUNK_SIZE) {
359     fprintf (stderr, "send_file_write: len (%d) > GUESTFS_MAX_CHUNK_SIZE (%d)\n",
360              len, GUESTFS_MAX_CHUNK_SIZE);
361     return -1;
362   }
363
364   cancel = check_for_library_cancellation ();
365
366   if (cancel) {
367     chunk.cancel = 1;
368     chunk.data.data_len = 0;
369     chunk.data.data_val = NULL;
370   } else {
371     chunk.cancel = 0;
372     chunk.data.data_len = len;
373     chunk.data.data_val = (char *) buf;
374   }
375
376   if (send_chunk (&chunk) == -1)
377     return -1;
378
379   if (cancel) return -2;
380   return 0;
381 }
382
383 static int
384 check_for_library_cancellation (void)
385 {
386   fd_set rset;
387   struct timeval tv;
388   int r;
389   char buf[4];
390   uint32_t flag;
391   XDR xdr;
392
393   FD_ZERO (&rset);
394   FD_SET (sock, &rset);
395   tv.tv_sec = 0;
396   tv.tv_usec = 0;
397   r = select (sock+1, &rset, NULL, NULL, &tv);
398   if (r == -1) {
399     perror ("select");
400     return 0;
401   }
402   if (r == 0)
403     return 0;
404
405   /* Read the message from the daemon. */
406   r = xread (sock, buf, sizeof buf);
407   if (r == -1) {
408     perror ("read");
409     return 0;
410   }
411
412   xdrmem_create (&xdr, buf, sizeof buf, XDR_DECODE);
413   xdr_uint32_t (&xdr, &flag);
414   xdr_destroy (&xdr);
415
416   if (flag != GUESTFS_CANCEL_FLAG) {
417     fprintf (stderr, "check_for_library_cancellation: read 0x%x from library, expected 0x%x\n",
418              flag, GUESTFS_CANCEL_FLAG);
419     return 0;
420   }
421
422   return 1;
423 }
424
425 void
426 send_file_end (int cancel)
427 {
428   guestfs_chunk chunk;
429
430   chunk.cancel = cancel;
431   chunk.data.data_len = 0;
432   chunk.data.data_val = NULL;
433   send_chunk (&chunk);
434 }
435
436 static int
437 send_chunk (const guestfs_chunk *chunk)
438 {
439   char buf[GUESTFS_MAX_CHUNK_SIZE + 48];
440   char lenbuf[4];
441   XDR xdr;
442   uint32_t len;
443
444   xdrmem_create (&xdr, buf, sizeof buf, XDR_ENCODE);
445   if (!xdr_guestfs_chunk (&xdr, (guestfs_chunk *) chunk)) {
446     fprintf (stderr, "send_chunk: failed to encode chunk\n");
447     xdr_destroy (&xdr);
448     return -1;
449   }
450
451   len = xdr_getpos (&xdr);
452   xdr_destroy (&xdr);
453
454   xdrmem_create (&xdr, lenbuf, 4, XDR_ENCODE);
455   xdr_uint32_t (&xdr, &len);
456   xdr_destroy (&xdr);
457
458   (void) xwrite (sock, lenbuf, 4);
459   (void) xwrite (sock, buf, len);
460
461   return 0;
462 }