6fc8b19fbd25f629063e9fc89dc44a4a5ac9dc27
[libguestfs.git] / daemon / guestfsd.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 <string.h>
24 #include <unistd.h>
25 #include <rpc/types.h>
26 #include <rpc/xdr.h>
27 #include <getopt.h>
28 #include <netdb.h>
29
30 #include "daemon.h"
31
32 void xwrite (int sock, const void *buf, size_t len);
33
34 static void usage (void);
35
36 /* Also in guestfs.c */
37 #define VMCHANNEL_PORT "6666"
38 #define VMCHANNEL_ADDR "10.0.2.4"
39
40 int
41 main (int argc, char *argv[])
42 {
43   static const char *options = "fh:p:?";
44   static struct option long_options[] = {
45     { "foreground", 0, 0, 'f' },
46     { "help", 0, 0, '?' },
47     { "host", 1, 0, 'h' },
48     { "port", 1, 0, 'p' },
49     { 0, 0, 0, 0 }
50   };
51   int c, n, r;
52   int dont_fork = 0;
53   const char *host = NULL;
54   const char *port = NULL;
55   FILE *fp;
56   char buf[4096];
57   char *p, *p2;
58   int sock;
59   struct addrinfo *res, *rr;
60   struct addrinfo hints;
61   XDR xdr;
62   unsigned len;
63
64   for (;;) {
65     c = getopt_long (argc, argv, options, long_options, NULL);
66     if (c == -1) break;
67
68     switch (c) {
69     case 'f':
70       dont_fork = 1;
71       break;
72
73     case 'h':
74       host = optarg;
75       break;
76
77     case 'p':
78       port = optarg;
79       break;
80
81     case '?':
82       usage ();
83       exit (0);
84
85     default:
86       fprintf (stderr, "guestfsd: unexpected command line option 0x%x\n", c);
87       exit (1);
88     }
89   }
90
91   if (optind < argc) {
92     usage ();
93     exit (1);
94   }
95
96   /* If host and port aren't set yet, try /proc/cmdline. */
97   if (!host || !port) {
98     fp = fopen ("/proc/cmdline", "r");
99     if (fp == NULL) {
100       perror ("/proc/cmdline");
101       goto next;
102     }
103     n = fread (buf, 1, sizeof buf - 1, fp);
104     fclose (fp);
105     buf[n] = '\0';
106
107     p = strstr (buf, "guestfs=");
108
109     if (p) {
110       p += 8;
111       p2 = strchr (p, ':');
112       if (p2) {
113         *p2++ = '\0';
114         host = p;
115         r = strcspn (p2, " \n");
116         p2[r] = '\0';
117         port = p2;
118       }
119     }
120   }
121
122  next:
123   /* Can't parse /proc/cmdline, so use built-in defaults. */
124   if (!host || !port) {
125     host = VMCHANNEL_ADDR;
126     port = VMCHANNEL_PORT;
127   }
128
129   /* Resolve the hostname. */
130   memset (&hints, 0, sizeof hints);
131   hints.ai_socktype = SOCK_STREAM;
132   hints.ai_flags = AI_ADDRCONFIG;
133   r = getaddrinfo (host, port, &hints, &res);
134   if (r != 0) {
135     fprintf (stderr, "%s:%s: %s\n", host, port, gai_strerror (r));
136     exit (1);
137   }
138
139   /* Connect to the given TCP socket. */
140   sock = -1;
141   for (rr = res; rr != NULL; rr = rr->ai_next) {
142     sock = socket (rr->ai_family, rr->ai_socktype, rr->ai_protocol);
143     if (sock != -1) {
144       if (connect (sock, rr->ai_addr, rr->ai_addrlen) == 0)
145         break;
146       perror ("connect");
147
148       close (sock);
149       sock = -1;
150     }
151   }
152   freeaddrinfo (res);
153
154   if (sock == -1) {
155     fprintf (stderr, "connection to %s:%s failed\n", host, port);
156     exit (1);
157   }
158
159   /* Send the magic length message which indicates that
160    * userspace is up inside the guest.
161    */
162   len = 0xf5f55ff5;
163   xdrmem_create (&xdr, buf, sizeof buf, XDR_ENCODE);
164   if (!xdr_uint32_t (&xdr, &len)) {
165     fprintf (stderr, "xdr_uint32_t failed\n");
166     exit (1);
167   }
168
169   xwrite (sock, buf, xdr_getpos (&xdr));
170
171   xdr_destroy (&xdr);
172
173   /* XXX Fork into the background. */
174
175
176
177
178
179
180
181
182
183
184
185
186   main_loop (sock);
187
188   exit (0);
189 }
190
191 void
192 xwrite (int sock, const void *buf, size_t len)
193 {
194   int r;
195
196   while (len > 0) {
197     r = write (sock, buf, len);
198     if (r == -1) {
199       perror ("write");
200       exit (1);
201     }
202     buf += r;
203     len -= r;
204   }
205 }
206
207 void
208 xread (int sock, void *buf, size_t len)
209 {
210   int r;
211
212   while (len > 0) {
213     r = read (sock, buf, len);
214     if (r == -1) {
215       perror ("read");
216       exit (1);
217     }
218     if (r == 0) {
219       fprintf (stderr, "read: unexpected end of file on comms socket\n");
220       exit (1);
221     }
222     buf += r;
223     len -= r;
224   }
225 }
226
227 static void
228 usage (void)
229 {
230   fprintf (stderr, "guestfsd [-f] [-h host -p port]\n");
231 }
232
233 /* Some unimplemented actions. */
234 int
235 do_mount (const char *device, const char *mountpoint)
236 {
237   reply_with_error ("mount not implemented");
238   return -1;
239 }
240
241 int
242 do_touch (const char *path)
243 {
244   reply_with_error ("touch not implemented");
245   return -1;
246 }