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