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