daemon: debug segv correct use of dereferencing NULL.
[libguestfs.git] / erlang / erl-guestfs-proto.c
1 /* libguestfs Erlang bindings.
2  * Copyright (C) 2011 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 along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  */
18
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <errno.h>
23
24 #include <erl_interface.h>
25 #include <ei.h>
26
27 #include "error.h"
28 #include "full-read.h"
29 #include "full-write.h"
30
31 #include "guestfs.h"
32
33 guestfs_h *g;
34
35 extern ETERM *dispatch (ETERM *message);
36 extern int atom_equals (ETERM *atom, const char *name);
37 extern ETERM *make_error (const char *funname);
38 extern ETERM *unknown_optarg (const char *funname, ETERM *optargname);
39 extern ETERM *unknown_function (ETERM *fun);
40 extern ETERM *make_string_list (char **r);
41 extern ETERM *make_table (char **r);
42 extern ETERM *make_bool (int r);
43 extern char **get_string_list (ETERM *term);
44 extern int get_bool (ETERM *term);
45 extern void free_strings (char **r);
46
47 /* This stops things getting out of hand, but also lets us detect
48  * protocol problems quickly.
49  */
50 #define MAX_MESSAGE_SIZE (32*1024*1024)
51
52 static unsigned char *read_message (void);
53 static void write_reply (ETERM *);
54
55 int
56 main (void)
57 {
58   unsigned char *buf;
59   ETERM *ret, *message;
60
61   erl_init (NULL, 0);
62
63   /* This process has a single libguestfs handle.  If the Erlang
64    * system creates more than one handle, then more than one of these
65    * processes will be running.
66    */
67   g = guestfs_create ();
68   if (g == NULL)
69     error (EXIT_FAILURE, 0, "could not create guestfs handle");
70
71   guestfs_set_error_handler (g, NULL, NULL);
72
73   while ((buf = read_message ()) != NULL) {
74     message = erl_decode (buf);
75     free (buf);
76
77     ret = dispatch (message);
78     erl_free_term (message);
79
80     write_reply (ret);
81     erl_free_term (ret);
82   }
83
84   guestfs_close (g);
85
86   exit (EXIT_SUCCESS);
87 }
88
89 /* The Erlang port always sends the length of the buffer as 4
90  * bytes in network byte order, followed by the message buffer.
91  */
92 static unsigned char *
93 read_message (void)
94 {
95   unsigned char buf[4];
96   size_t size;
97   unsigned char *r;
98
99   errno = 0;
100   if (full_read (0, buf, 4) != 4) {
101     if (errno == 0) /* ok - closed connection normally */
102       return NULL;
103     else
104       error (EXIT_FAILURE, errno, "read message size");
105   }
106
107   size = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3];
108
109   if (size > MAX_MESSAGE_SIZE)
110     error (EXIT_FAILURE, 0, "message larger than MAX_MESSAGE_SIZE");
111
112   r = malloc (size);
113   if (r == NULL)
114     error (EXIT_FAILURE, errno, "malloc");
115
116   if (full_read (0, r, size) != size)
117     error (EXIT_FAILURE, errno, "read message content");
118
119   return r;
120 }
121
122 static void
123 write_reply (ETERM *term)
124 {
125   size_t size;
126   unsigned char sbuf[4];
127   unsigned char *buf;
128
129   size = erl_term_len (term);
130
131   buf = malloc (size);
132   if (buf == NULL)
133     error (EXIT_FAILURE, errno, "malloc");
134
135   erl_encode (term, buf);
136
137   sbuf[0] = (size >> 24) & 0xff;
138   sbuf[1] = (size >> 16) & 0xff;
139   sbuf[2] = (size >> 8) & 0xff;
140   sbuf[3] = size & 0xff;
141
142   if (full_write (1, sbuf, 4) != 4)
143     error (EXIT_FAILURE, errno, "write message size");
144
145   if (full_write (1, buf, size) != size)
146     error (EXIT_FAILURE, errno, "write message content");
147
148   free (buf);
149 }
150
151 /* Note that all published Erlang code/examples etc uses strncmp in
152  * a buggy way.  This is the right way to do it.
153  */
154 int
155 atom_equals (ETERM *atom, const char *name)
156 {
157   size_t namelen = strlen (name);
158   size_t atomlen = ERL_ATOM_SIZE (atom);
159   if (namelen != atomlen) return 0;
160   return strncmp (ERL_ATOM_PTR (atom), name, atomlen) == 0;
161 }
162
163 ETERM *
164 make_error (const char *funname)
165 {
166   ETERM *error = erl_mk_atom ("error");
167   ETERM *msg = erl_mk_string (guestfs_last_error (g));
168   ETERM *num = erl_mk_int (guestfs_last_errno (g));
169   ETERM *t[3] = { error, msg, num };
170   return erl_mk_tuple (t, 3);
171 }
172
173 ETERM *
174 unknown_function (ETERM *fun)
175 {
176   ETERM *unknown = erl_mk_atom ("unknown");
177   ETERM *funcopy = erl_copy_term (fun);
178   ETERM *t[2] = { unknown, funcopy };
179   return erl_mk_tuple (t, 2);
180 }
181
182 ETERM *
183 unknown_optarg (const char *funname, ETERM *optargname)
184 {
185   ETERM *unknownarg = erl_mk_atom ("unknownarg");
186   ETERM *copy = erl_copy_term (optargname);
187   ETERM *t[2] = { unknownarg, copy };
188   return erl_mk_tuple (t, 2);
189 }
190
191 ETERM *
192 make_string_list (char **r)
193 {
194   size_t i, size;
195
196   for (size = 0; r[size] != NULL; ++size)
197     ;
198
199   ETERM *t[size];
200
201   for (i = 0; r[i] != NULL; ++i)
202     t[i] = erl_mk_string (r[i]);
203
204   return erl_mk_list (t, size);
205 }
206
207 /* Make a hash table.  The number of elements returned by the C
208  * function is always even.
209  */
210 ETERM *
211 make_table (char **r)
212 {
213   size_t i, size;
214
215   for (size = 0; r[size] != NULL; ++size)
216     ;
217
218   ETERM *t[size/2];
219   ETERM *a[2];
220
221   for (i = 0; r[i] != NULL; i += 2) {
222     a[0] = erl_mk_string (r[i]);
223     a[1] = erl_mk_string (r[i+1]);
224     t[i/2] = erl_mk_tuple (a, 2);
225   }
226
227   return erl_mk_list (t, size/2);
228 }
229
230 ETERM *
231 make_bool (int r)
232 {
233   if (r)
234     return erl_mk_atom ("true");
235   else
236     return erl_mk_atom ("false");
237 }
238
239 char **
240 get_string_list (ETERM *term)
241 {
242   ETERM *t;
243   size_t i, size;
244   char **r;
245
246   for (size = 0, t = term; !ERL_IS_EMPTY_LIST (t);
247        size++, t = ERL_CONS_TAIL (t))
248     ;
249
250   r = malloc ((size+1) * sizeof (char *));
251   if (r == NULL)
252     error (EXIT_FAILURE, errno, "malloc");
253
254   for (i = 0, t = term; !ERL_IS_EMPTY_LIST (t); i++, t = ERL_CONS_TAIL (t))
255     r[i] = erl_iolist_to_string (ERL_CONS_HEAD (t));
256   r[size] = NULL;
257
258   return r;
259 }
260
261 int
262 get_bool (ETERM *term)
263 {
264   if (atom_equals (term, "true"))
265     return 1;
266   else
267     return 0;
268 }
269
270 void
271 free_strings (char **r)
272 {
273   size_t i;
274
275   for (i = 0; r[i] != NULL; ++i)
276     free (r[i]);
277   free (r);
278 }