Fix code which looked for leaked FDs between each command.
[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 "ignore-value.h"
33
34 #include "daemon.h"
35 #include "../src/guestfs_protocol.h"
36
37 /* The message currently being processed. */
38 int proc_nr;
39 int serial;
40
41 /* The daemon communications socket. */
42 static int sock;
43
44 void
45 main_loop (int _sock)
46 {
47   XDR xdr;
48   char *buf;
49   char lenbuf[4];
50   uint32_t len;
51   struct guestfs_message_header hdr;
52   struct timeval start_t, end_t;
53   int64_t start_us, end_us, elapsed_us;
54
55   sock = _sock;
56
57   for (;;) {
58     /* Most common errors are leaked memory and leaked file descriptors,
59      * so run this between each command:
60      */
61     if (verbose && 0)
62       ignore_value (system ("ls -l /proc/self/fd"));
63
64     /* Read the length word. */
65     if (xread (sock, lenbuf, 4) == -1)
66       exit (1);
67
68     xdrmem_create (&xdr, lenbuf, 4, XDR_DECODE);
69     xdr_uint32_t (&xdr, &len);
70     xdr_destroy (&xdr);
71
72     if (len > GUESTFS_MESSAGE_MAX) {
73       fprintf (stderr, "guestfsd: incoming message is too long (%u bytes)\n",
74                len);
75       exit (1);
76     }
77
78     buf = malloc (len);
79     if (!buf) {
80       reply_with_perror ("malloc");
81       continue;
82     }
83
84     if (xread (sock, buf, len) == -1)
85       exit (1);
86
87 #ifdef ENABLE_PACKET_DUMP
88     if (verbose) {
89       size_t i, j;
90
91       for (i = 0; i < len; i += 16) {
92         printf ("%04zx: ", i);
93         for (j = i; j < MIN (i+16, len); ++j)
94           printf ("%02x ", (unsigned char) buf[j]);
95         for (; j < i+16; ++j)
96           printf ("   ");
97         printf ("|");
98         for (j = i; j < MIN (i+16, len); ++j)
99           if (isprint (buf[j]))
100             printf ("%c", buf[j]);
101           else
102             printf (".");
103         for (; j < i+16; ++j)
104           printf (" ");
105         printf ("|\n");
106       }
107     }
108 #endif
109
110     /* In verbose mode, display the time taken to run each command. */
111     if (verbose)
112       gettimeofday (&start_t, NULL);
113
114     /* Decode the message header. */
115     xdrmem_create (&xdr, buf, len, XDR_DECODE);
116     if (!xdr_guestfs_message_header (&xdr, &hdr)) {
117       fprintf (stderr, "guestfsd: could not decode message header\n");
118       exit (1);
119     }
120
121     /* Check the version etc. */
122     if (hdr.prog != GUESTFS_PROGRAM) {
123       reply_with_error ("wrong program (%d)", hdr.prog);
124       goto cont;
125     }
126     if (hdr.vers != GUESTFS_PROTOCOL_VERSION) {
127       reply_with_error ("wrong protocol version (%d)", hdr.vers);
128       goto cont;
129     }
130     if (hdr.direction != GUESTFS_DIRECTION_CALL) {
131       reply_with_error ("unexpected message direction (%d)", hdr.direction);
132       goto cont;
133     }
134     if (hdr.status != GUESTFS_STATUS_OK) {
135       reply_with_error ("unexpected message status (%d)", hdr.status);
136       goto cont;
137     }
138
139     /* Now start to process this message. */
140     proc_nr = hdr.proc;
141     serial = hdr.serial;
142     dispatch_incoming_message (&xdr);
143     /* Note that dispatch_incoming_message will also send a reply. */
144
145     /* In verbose mode, display the time taken to run each command. */
146     if (verbose) {
147       gettimeofday (&end_t, NULL);
148
149       start_us = (int64_t) start_t.tv_sec * 1000000 + start_t.tv_usec;
150       end_us = (int64_t) end_t.tv_sec * 1000000 + end_t.tv_usec;
151       elapsed_us = end_us - start_us;
152       fprintf (stderr, "proc %d (%s) took %d.%02d seconds\n",
153                proc_nr,
154                proc_nr >= 0 && proc_nr < GUESTFS_PROC_NR_PROCS
155                ? function_names[proc_nr] : "UNKNOWN PROCEDURE",
156                (int) (elapsed_us / 1000000),
157                (int) ((elapsed_us / 10000) % 100));
158     }
159
160   cont:
161     xdr_destroy (&xdr);
162     free (buf);
163   }
164 }
165
166 static void send_error (const char *msg);
167
168 void
169 reply_with_error (const char *fs, ...)
170 {
171   char err[GUESTFS_ERROR_LEN];
172   va_list args;
173
174   va_start (args, fs);
175   vsnprintf (err, sizeof err, fs, args);
176   va_end (args);
177
178   send_error (err);
179 }
180
181 void
182 reply_with_perror (const char *fs, ...)
183 {
184   char buf1[GUESTFS_ERROR_LEN];
185   char buf2[GUESTFS_ERROR_LEN];
186   va_list args;
187   int err = errno;
188
189   va_start (args, fs);
190   vsnprintf (buf1, sizeof buf1, fs, args);
191   va_end (args);
192
193   snprintf (buf2, sizeof buf2, "%s: %s", buf1, strerror (err));
194
195   send_error (buf2);
196 }
197
198 static void
199 send_error (const char *msg)
200 {
201   XDR xdr;
202   char buf[GUESTFS_ERROR_LEN + 200];
203   char lenbuf[4];
204   struct guestfs_message_header hdr;
205   struct guestfs_message_error err;
206   unsigned len;
207
208   fprintf (stderr, "guestfsd: error: %s\n", msg);
209
210   xdrmem_create (&xdr, buf, sizeof buf, XDR_ENCODE);
211
212   hdr.prog = GUESTFS_PROGRAM;
213   hdr.vers = GUESTFS_PROTOCOL_VERSION;
214   hdr.direction = GUESTFS_DIRECTION_REPLY;
215   hdr.status = GUESTFS_STATUS_ERROR;
216   hdr.proc = proc_nr;
217   hdr.serial = serial;
218
219   if (!xdr_guestfs_message_header (&xdr, &hdr)) {
220     fprintf (stderr, "guestfsd: failed to encode error message header\n");
221     exit (1);
222   }
223
224   err.error_message = (char *) msg;
225
226   if (!xdr_guestfs_message_error (&xdr, &err)) {
227     fprintf (stderr, "guestfsd: failed to encode error message body\n");
228     exit (1);
229   }
230
231   len = xdr_getpos (&xdr);
232   xdr_destroy (&xdr);
233
234   xdrmem_create (&xdr, lenbuf, 4, XDR_ENCODE);
235   xdr_uint32_t (&xdr, &len);
236   xdr_destroy (&xdr);
237
238   if (xwrite (sock, lenbuf, 4) == -1) {
239     fprintf (stderr, "xwrite failed\n");
240     exit (1);
241   }
242   if (xwrite (sock, buf, len) == -1) {
243     fprintf (stderr, "xwrite failed\n");
244     exit (1);
245   }
246 }
247
248 void
249 reply (xdrproc_t xdrp, char *ret)
250 {
251   XDR xdr;
252   char buf[GUESTFS_MESSAGE_MAX];
253   char lenbuf[4];
254   struct guestfs_message_header hdr;
255   unsigned len;
256
257   xdrmem_create (&xdr, buf, sizeof buf, XDR_ENCODE);
258
259   hdr.prog = GUESTFS_PROGRAM;
260   hdr.vers = GUESTFS_PROTOCOL_VERSION;
261   hdr.direction = GUESTFS_DIRECTION_REPLY;
262   hdr.status = GUESTFS_STATUS_OK;
263   hdr.proc = proc_nr;
264   hdr.serial = serial;
265
266   if (!xdr_guestfs_message_header (&xdr, &hdr)) {
267     fprintf (stderr, "guestfsd: failed to encode reply header\n");
268     exit (1);
269   }
270
271   if (xdrp) {
272     /* This can fail if the reply body is too large, for example
273      * if it exceeds the maximum message size.  In that case
274      * we want to return an error message instead. (RHBZ#509597).
275      */
276     if (!(*xdrp) (&xdr, ret)) {
277       reply_with_perror ("guestfsd: failed to encode reply body\n");
278       xdr_destroy (&xdr);
279       return;
280     }
281   }
282
283   len = xdr_getpos (&xdr);
284   xdr_destroy (&xdr);
285
286   xdrmem_create (&xdr, lenbuf, 4, XDR_ENCODE);
287   xdr_uint32_t (&xdr, &len);
288   xdr_destroy (&xdr);
289
290   if (xwrite (sock, lenbuf, 4) == -1) {
291     fprintf (stderr, "xwrite failed\n");
292     exit (1);
293   }
294   if (xwrite (sock, buf, len) == -1) {
295     fprintf (stderr, "xwrite failed\n");
296     exit (1);
297   }
298 }
299
300 /* Receive file chunks, repeatedly calling 'cb'. */
301 int
302 receive_file (receive_cb cb, void *opaque)
303 {
304   guestfs_chunk chunk;
305   char lenbuf[4];
306   char *buf;
307   XDR xdr;
308   int r;
309   uint32_t len;
310
311   for (;;) {
312     /* Read the length word. */
313     if (xread (sock, lenbuf, 4) == -1)
314       exit (1);
315
316     xdrmem_create (&xdr, lenbuf, 4, XDR_DECODE);
317     xdr_uint32_t (&xdr, &len);
318     xdr_destroy (&xdr);
319
320     if (len == GUESTFS_CANCEL_FLAG)
321       continue;                 /* Just ignore it. */
322
323     if (len > GUESTFS_MESSAGE_MAX) {
324       fprintf (stderr, "guestfsd: incoming message is too long (%u bytes)\n",
325                len);
326       exit (1);
327     }
328
329     buf = malloc (len);
330     if (!buf) {
331       perror ("malloc");
332       return -1;
333     }
334
335     if (xread (sock, buf, len) == -1)
336       exit (1);
337
338     xdrmem_create (&xdr, buf, len, XDR_DECODE);
339     memset (&chunk, 0, sizeof chunk);
340     if (!xdr_guestfs_chunk (&xdr, &chunk)) {
341       xdr_destroy (&xdr);
342       free (buf);
343       return -1;
344     }
345     xdr_destroy (&xdr);
346     free (buf);
347
348     if (verbose)
349       printf ("receive_file: got chunk: cancel = %d, len = %d, buf = %p\n",
350               chunk.cancel, chunk.data.data_len, chunk.data.data_val);
351
352     if (chunk.cancel) {
353       fprintf (stderr, "receive_file: received cancellation from library\n");
354       xdr_free ((xdrproc_t) xdr_guestfs_chunk, (char *) &chunk);
355       return -2;
356     }
357     if (chunk.data.data_len == 0) {
358       xdr_free ((xdrproc_t) xdr_guestfs_chunk, (char *) &chunk);
359       return 0;                 /* end of file */
360     }
361
362     if (cb)
363       r = cb (opaque, chunk.data.data_val, chunk.data.data_len);
364     else
365       r = 0;
366
367     xdr_free ((xdrproc_t) xdr_guestfs_chunk, (char *) &chunk);
368     if (r == -1)                /* write error */
369       return -1;
370   }
371 }
372
373 /* Send a cancellation flag back to the library. */
374 void
375 cancel_receive (void)
376 {
377   XDR xdr;
378   char fbuf[4];
379   uint32_t flag = GUESTFS_CANCEL_FLAG;
380
381   xdrmem_create (&xdr, fbuf, sizeof fbuf, XDR_ENCODE);
382   xdr_uint32_t (&xdr, &flag);
383   xdr_destroy (&xdr);
384
385   if (xwrite (sock, fbuf, sizeof fbuf) == -1) {
386     perror ("write to socket");
387     return;
388   }
389
390   /* Keep receiving chunks and discarding, until library sees cancel. */
391   (void) receive_file (NULL, NULL);
392 }
393
394 static int check_for_library_cancellation (void);
395 static int send_chunk (const guestfs_chunk *);
396
397 /* Also check if the library sends us a cancellation message. */
398 int
399 send_file_write (const void *buf, int len)
400 {
401   guestfs_chunk chunk;
402   int cancel;
403
404   if (len > GUESTFS_MAX_CHUNK_SIZE) {
405     fprintf (stderr, "send_file_write: len (%d) > GUESTFS_MAX_CHUNK_SIZE (%d)\n",
406              len, GUESTFS_MAX_CHUNK_SIZE);
407     return -1;
408   }
409
410   cancel = check_for_library_cancellation ();
411
412   if (cancel) {
413     chunk.cancel = 1;
414     chunk.data.data_len = 0;
415     chunk.data.data_val = NULL;
416   } else {
417     chunk.cancel = 0;
418     chunk.data.data_len = len;
419     chunk.data.data_val = (char *) buf;
420   }
421
422   if (send_chunk (&chunk) == -1)
423     return -1;
424
425   if (cancel) return -2;
426   return 0;
427 }
428
429 static int
430 check_for_library_cancellation (void)
431 {
432   fd_set rset;
433   struct timeval tv;
434   int r;
435   char buf[4];
436   uint32_t flag;
437   XDR xdr;
438
439   FD_ZERO (&rset);
440   FD_SET (sock, &rset);
441   tv.tv_sec = 0;
442   tv.tv_usec = 0;
443   r = select (sock+1, &rset, NULL, NULL, &tv);
444   if (r == -1) {
445     perror ("select");
446     return 0;
447   }
448   if (r == 0)
449     return 0;
450
451   /* Read the message from the daemon. */
452   r = xread (sock, buf, sizeof buf);
453   if (r == -1)
454     return 0;
455
456   xdrmem_create (&xdr, buf, sizeof buf, XDR_DECODE);
457   xdr_uint32_t (&xdr, &flag);
458   xdr_destroy (&xdr);
459
460   if (flag != GUESTFS_CANCEL_FLAG) {
461     fprintf (stderr, "check_for_library_cancellation: read 0x%x from library, expected 0x%x\n",
462              flag, GUESTFS_CANCEL_FLAG);
463     return 0;
464   }
465
466   return 1;
467 }
468
469 int
470 send_file_end (int cancel)
471 {
472   guestfs_chunk chunk;
473
474   chunk.cancel = cancel;
475   chunk.data.data_len = 0;
476   chunk.data.data_val = NULL;
477   return send_chunk (&chunk);
478 }
479
480 static int
481 send_chunk (const guestfs_chunk *chunk)
482 {
483   char buf[GUESTFS_MAX_CHUNK_SIZE + 48];
484   char lenbuf[4];
485   XDR xdr;
486   uint32_t len;
487
488   xdrmem_create (&xdr, buf, sizeof buf, XDR_ENCODE);
489   if (!xdr_guestfs_chunk (&xdr, (guestfs_chunk *) chunk)) {
490     fprintf (stderr, "send_chunk: failed to encode chunk\n");
491     xdr_destroy (&xdr);
492     return -1;
493   }
494
495   len = xdr_getpos (&xdr);
496   xdr_destroy (&xdr);
497
498   xdrmem_create (&xdr, lenbuf, 4, XDR_ENCODE);
499   xdr_uint32_t (&xdr, &len);
500   xdr_destroy (&xdr);
501
502   int err = (xwrite (sock, lenbuf, 4) == 0
503              && xwrite (sock, buf, len) == 0 ? 0 : -1);
504   if (err) {
505     fprintf (stderr, "send_chunk: write failed\n");
506     exit (1);
507   }
508
509   return err;
510 }