Version 1.15.7.
[libguestfs.git] / src / events.c
1 /* libguestfs
2  * Copyright (C) 2011 Red Hat Inc.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library 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 GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17  */
18
19 #include <config.h>
20
21 #define _BSD_SOURCE /* for mkdtemp, usleep */
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <stdint.h>
26 #include <inttypes.h>
27 #include <unistd.h>
28 #include <assert.h>
29 #include <string.h>
30
31 #include "c-ctype.h"
32 #include "ignore-value.h"
33
34 #include "guestfs.h"
35 #include "guestfs-internal.h"
36
37 int
38 guestfs_set_event_callback (guestfs_h *g,
39                             guestfs_event_callback cb,
40                             uint64_t event_bitmask,
41                             int flags,
42                             void *opaque)
43 {
44   if (flags != 0) {
45     error (g, "flags parameter should be passed as 0 to this function");
46     return -1;
47   }
48
49   /* We cast size_t to int which is not always safe for large numbers,
50    * and in any case if a program is registering a huge number of
51    * callbacks then we'd want to look at using an alternate data
52    * structure in place of a linear list.
53    */
54   if (g->nr_events >= 1000) {
55     error (g, "too many event callbacks registered");
56     return -1;
57   }
58
59   int event_handle = (int) g->nr_events;
60   g->events =
61     guestfs_safe_realloc (g, g->events,
62                           (g->nr_events+1) * sizeof (struct event));
63   g->nr_events++;
64
65   g->events[event_handle].event_bitmask = event_bitmask;
66   g->events[event_handle].cb = cb;
67   g->events[event_handle].opaque = opaque;
68   g->events[event_handle].opaque2 = NULL;
69
70   return event_handle;
71 }
72
73 void
74 guestfs_delete_event_callback (guestfs_h *g, int event_handle)
75 {
76   if (event_handle < 0 || event_handle >= (int) g->nr_events)
77     return;
78
79   /* Set the event_bitmask to 0, which will ensure that this callback
80    * cannot match any event and therefore cannot be called.
81    */
82   g->events[event_handle].event_bitmask = 0;
83 }
84
85 /* Functions to generate an event with various payloads. */
86
87 void
88 guestfs___call_callbacks_void (guestfs_h *g, uint64_t event)
89 {
90   size_t i;
91
92   for (i = 0; i < g->nr_events; ++i)
93     if ((g->events[i].event_bitmask & event) != 0)
94       g->events[i].cb (g, g->events[i].opaque, event, i, 0, NULL, 0, NULL, 0);
95
96   /* All events with payload type void are discarded if no callback
97    * was registered.
98    */
99 }
100
101 void
102 guestfs___call_callbacks_message (guestfs_h *g, uint64_t event,
103                                   const char *buf, size_t buf_len)
104 {
105   size_t i, count = 0;
106
107   for (i = 0; i < g->nr_events; ++i)
108     if ((g->events[i].event_bitmask & event) != 0) {
109       g->events[i].cb (g, g->events[i].opaque, event, i, 0,
110                        buf, buf_len, NULL, 0);
111       count++;
112     }
113
114   /* Emulate the old-style handlers.  Callers can override
115    * print-on-stderr simply by registering a callback.
116    */
117   if (count == 0 &&
118       (event == GUESTFS_EVENT_APPLIANCE ||
119        event == GUESTFS_EVENT_LIBRARY ||
120        event == GUESTFS_EVENT_TRACE) &&
121       (g->verbose || event == GUESTFS_EVENT_TRACE)) {
122     int from_appliance = event == GUESTFS_EVENT_APPLIANCE;
123     size_t i;
124     char c;
125
126     /* APPLIANCE =>  <buf>
127      * LIBRARY =>    libguestfs: <buf>\n
128      * TRACE =>      libguestfs: trace: <buf>\n  (RHBZ#673479)
129      */
130
131     if (event != GUESTFS_EVENT_APPLIANCE)
132       fputs ("libguestfs: ", stderr);
133
134     if (event == GUESTFS_EVENT_TRACE)
135       fputs ("trace: ", stderr);
136
137     /* Special or non-printing characters in the buffer must be
138      * escaped (RHBZ#731744).  The buffer can contain any 8 bit
139      * character, even \0.
140      *
141      * Handling of \n and \r characters is complex:
142      *
143      * Case 1: Messages from the appliance: These messages already
144      * contain \n and \r characters at logical positions, so we just
145      * echo those out directly.
146      *
147      * Case 2: Messages from other sources: These messages should NOT
148      * contain \n or \r.  If they do, it is escaped.  However we also
149      * need to print a real end of line after these messages.
150      */
151     for (i = 0; i < buf_len; ++i) {
152       c = buf[i];
153       if (c_isprint (c) || (from_appliance && (c == '\n' || c == '\r')))
154         putc (c, stderr);
155       else {
156         switch (c) {
157         case '\0': fputs ("\\0", stderr); break;
158         case '\a': fputs ("\\a", stderr); break;
159         case '\b': fputs ("\\b", stderr); break;
160         case '\f': fputs ("\\f", stderr); break;
161         case '\n': fputs ("\\n", stderr); break;
162         case '\r': fputs ("\\r", stderr); break;
163         case '\t': fputs ("\\t", stderr); break;
164         case '\v': fputs ("\\v", stderr); break;
165         default:
166           fprintf (stderr, "\\x%x", (unsigned) c);
167         }
168       }
169     }
170
171     if (!from_appliance)
172       putc ('\n', stderr);
173   }
174 }
175
176 void
177 guestfs___call_callbacks_array (guestfs_h *g, uint64_t event,
178                                 const uint64_t *array, size_t array_len)
179 {
180   size_t i;
181
182   for (i = 0; i < g->nr_events; ++i)
183     if ((g->events[i].event_bitmask & event) != 0)
184       g->events[i].cb (g, g->events[i].opaque, event, i, 0,
185                        NULL, 0, array, array_len);
186
187   /* All events with payload type array are discarded if no callback
188    * was registered.
189    */
190 }
191
192 /* Emulate old-style callback API.
193  *
194  * There were no event handles, so multiple callbacks per event were
195  * not supported.  Calling the same 'guestfs_set_*_callback' function
196  * would replace the existing event.  Calling it with cb == NULL meant
197  * that the caller wanted to remove the callback.
198  */
199
200 static void
201 replace_old_style_event_callback (guestfs_h *g,
202                                   guestfs_event_callback cb,
203                                   uint64_t event_bitmask,
204                                   void *opaque,
205                                   void *opaque2)
206 {
207   size_t i;
208
209   /* Use 'cb' pointer as a sentinel to replace the existing callback
210    * for this event if one was registered previously.  Else append a
211    * new event.
212    */
213
214   for (i = 0; i < g->nr_events; ++i)
215     if (g->events[i].cb == cb) {
216       if (opaque2 == NULL) {
217         /* opaque2 (the original callback) is NULL, which in the
218          * old-style API meant remove the callback.
219          */
220         guestfs_delete_event_callback (g, i);
221         return;
222       }
223
224       goto replace;
225     }
226
227   if (opaque2 == NULL)
228     return; /* see above */
229
230   /* i == g->nr_events */
231   g->events =
232     guestfs_safe_realloc (g, g->events,
233                           (g->nr_events+1) * sizeof (struct event));
234   g->nr_events++;
235
236  replace:
237   g->events[i].event_bitmask = event_bitmask;
238   g->events[i].cb = cb;
239   g->events[i].opaque = opaque;
240   g->events[i].opaque2 = opaque2;
241 }
242
243 static void
244 log_message_callback_wrapper (guestfs_h *g,
245                               void *opaque,
246                               uint64_t event,
247                               int event_handle,
248                               int flags,
249                               const char *buf, size_t buf_len,
250                               const uint64_t *array, size_t array_len)
251 {
252   guestfs_log_message_cb cb = g->events[event_handle].opaque2;
253   /* Note that the old callback declared the message buffer as
254    * (char *, int).  I sure hope message buffers aren't too large
255    * and that callers aren't writing to them. XXX
256    */
257   cb (g, opaque, (char *) buf, (int) buf_len);
258 }
259
260 void
261 guestfs_set_log_message_callback (guestfs_h *g,
262                                   guestfs_log_message_cb cb, void *opaque)
263 {
264   replace_old_style_event_callback (g, log_message_callback_wrapper,
265                                     GUESTFS_EVENT_APPLIANCE,
266                                     opaque, cb);
267 }
268
269 static void
270 subprocess_quit_callback_wrapper (guestfs_h *g,
271                                   void *opaque,
272                                   uint64_t event,
273                                   int event_handle,
274                                   int flags,
275                                   const char *buf, size_t buf_len,
276                                   const uint64_t *array, size_t array_len)
277 {
278   guestfs_subprocess_quit_cb cb = g->events[event_handle].opaque2;
279   cb (g, opaque);
280 }
281
282 void
283 guestfs_set_subprocess_quit_callback (guestfs_h *g,
284                                       guestfs_subprocess_quit_cb cb, void *opaque)
285 {
286   replace_old_style_event_callback (g, subprocess_quit_callback_wrapper,
287                                     GUESTFS_EVENT_SUBPROCESS_QUIT,
288                                     opaque, cb);
289 }
290
291 static void
292 launch_done_callback_wrapper (guestfs_h *g,
293                               void *opaque,
294                               uint64_t event,
295                               int event_handle,
296                               int flags,
297                               const char *buf, size_t buf_len,
298                               const uint64_t *array, size_t array_len)
299 {
300   guestfs_launch_done_cb cb = g->events[event_handle].opaque2;
301   cb (g, opaque);
302 }
303
304 void
305 guestfs_set_launch_done_callback (guestfs_h *g,
306                                   guestfs_launch_done_cb cb, void *opaque)
307 {
308   replace_old_style_event_callback (g, launch_done_callback_wrapper,
309                                     GUESTFS_EVENT_LAUNCH_DONE,
310                                     opaque, cb);
311 }
312
313 static void
314 close_callback_wrapper (guestfs_h *g,
315                         void *opaque,
316                         uint64_t event,
317                         int event_handle,
318                         int flags,
319                         const char *buf, size_t buf_len,
320                         const uint64_t *array, size_t array_len)
321 {
322   guestfs_close_cb cb = g->events[event_handle].opaque2;
323   cb (g, opaque);
324 }
325
326 void
327 guestfs_set_close_callback (guestfs_h *g,
328                             guestfs_close_cb cb, void *opaque)
329 {
330   replace_old_style_event_callback (g, close_callback_wrapper,
331                                     GUESTFS_EVENT_CLOSE,
332                                     opaque, cb);
333 }
334
335 static void
336 progress_callback_wrapper (guestfs_h *g,
337                            void *opaque,
338                            uint64_t event,
339                            int event_handle,
340                            int flags,
341                            const char *buf, size_t buf_len,
342                            const uint64_t *array, size_t array_len)
343 {
344   guestfs_progress_cb cb = g->events[event_handle].opaque2;
345   assert (array_len >= 4);
346   cb (g, opaque, (int) array[0], (int) array[1], array[2], array[3]);
347 }
348
349 void
350 guestfs_set_progress_callback (guestfs_h *g,
351                                guestfs_progress_cb cb, void *opaque)
352 {
353   replace_old_style_event_callback (g, progress_callback_wrapper,
354                                     GUESTFS_EVENT_PROGRESS,
355                                     opaque, cb);
356 }