159862a4690b91b059b9bff011c958bf3aa4b171
[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
30 #include "ignore-value.h"
31
32 #include "guestfs.h"
33 #include "guestfs-internal.h"
34
35 int
36 guestfs_set_event_callback (guestfs_h *g,
37                             guestfs_event_callback cb,
38                             uint64_t event_bitmask,
39                             int flags,
40                             void *opaque)
41 {
42   if (flags != 0) {
43     error (g, "flags parameter should be passed as 0 to this function");
44     return -1;
45   }
46
47   /* We cast size_t to int which is not always safe for large numbers,
48    * and in any case if a program is registering a huge number of
49    * callbacks then we'd want to look at using an alternate data
50    * structure in place of a linear list.
51    */
52   if (g->nr_events >= 1000) {
53     error (g, "too many event callbacks registered");
54     return -1;
55   }
56
57   int event_handle = (int) g->nr_events;
58   g->events =
59     guestfs_safe_realloc (g, g->events,
60                           (g->nr_events+1) * sizeof (struct event));
61   g->nr_events++;
62
63   g->events[event_handle].event_bitmask = event_bitmask;
64   g->events[event_handle].cb = cb;
65   g->events[event_handle].opaque = opaque;
66   g->events[event_handle].opaque2 = NULL;
67
68   return event_handle;
69 }
70
71 void
72 guestfs_delete_event_callback (guestfs_h *g, int event_handle)
73 {
74   if (event_handle < 0 || event_handle >= (int) g->nr_events)
75     return;
76
77   /* Set the event_bitmask to 0, which will ensure that this callback
78    * cannot match any event and therefore cannot be called.
79    */
80   g->events[event_handle].event_bitmask = 0;
81 }
82
83 /* Functions to generate an event with various payloads. */
84
85 void
86 guestfs___call_callbacks_void (guestfs_h *g, uint64_t event)
87 {
88   size_t i;
89
90   for (i = 0; i < g->nr_events; ++i)
91     if ((g->events[i].event_bitmask & event) != 0)
92       g->events[i].cb (g, g->events[i].opaque, event, i, 0, NULL, 0, NULL, 0);
93
94   /* All events with payload type void are discarded if no callback
95    * was registered.
96    */
97 }
98
99 void
100 guestfs___call_callbacks_message (guestfs_h *g, uint64_t event,
101                                   const char *buf, size_t buf_len)
102 {
103   size_t i, count = 0;
104
105   for (i = 0; i < g->nr_events; ++i)
106     if ((g->events[i].event_bitmask & event) != 0) {
107       g->events[i].cb (g, g->events[i].opaque, event, i, 0,
108                        buf, buf_len, NULL, 0);
109       count++;
110     }
111
112   /* If nothing was registered and we're verbose or tracing, then we
113    * print the message on stderr.  This essentially emulates the
114    * behaviour of the old-style handlers, while allowing callers to
115    * override print-on-stderr simply by registering a callback.
116    */
117   if (count == 0 && (g->verbose || event == GUESTFS_EVENT_TRACE)) {
118     const char *prefix = "libguestfs: ";
119     const char *trace = "trace: ";
120     const char *nl = "\n";
121
122     /* APPLIANCE =>  <buf>
123      * LIBRARY =>    libguestfs: <buf>\n
124      * TRACE =>      libguestfs: trace: <buf>\n  (RHBZ#673479)
125      */
126
127     if (event != GUESTFS_EVENT_APPLIANCE)
128       ignore_value (write (STDERR_FILENO, prefix, strlen (prefix)));
129
130     if (event == GUESTFS_EVENT_TRACE)
131       ignore_value (write (STDERR_FILENO, trace, strlen (trace)));
132
133     ignore_value (write (STDERR_FILENO, buf, buf_len));
134
135     /* Messages from the appliance already contain \n characters, others
136      * need this to be appended.
137      */
138     if (event != GUESTFS_EVENT_APPLIANCE)
139       ignore_value (write (STDERR_FILENO, nl, strlen (nl)));
140   }
141 }
142
143 void
144 guestfs___call_callbacks_array (guestfs_h *g, uint64_t event,
145                                 const uint64_t *array, size_t array_len)
146 {
147   size_t i;
148
149   for (i = 0; i < g->nr_events; ++i)
150     if ((g->events[i].event_bitmask & event) != 0)
151       g->events[i].cb (g, g->events[i].opaque, event, i, 0,
152                        NULL, 0, array, array_len);
153
154   /* All events with payload type array are discarded if no callback
155    * was registered.
156    */
157 }
158
159 /* Emulate old-style callback API.
160  *
161  * There were no event handles, so multiple callbacks per event were
162  * not supported.  Calling the same 'guestfs_set_*_callback' function
163  * would replace the existing event.  Calling it with cb == NULL meant
164  * that the caller wanted to remove the callback.
165  */
166
167 static void
168 replace_old_style_event_callback (guestfs_h *g,
169                                   guestfs_event_callback cb,
170                                   uint64_t event_bitmask,
171                                   void *opaque,
172                                   void *opaque2)
173 {
174   size_t i;
175
176   /* Use 'cb' pointer as a sentinel to replace the existing callback
177    * for this event if one was registered previously.  Else append a
178    * new event.
179    */
180
181   for (i = 0; i < g->nr_events; ++i)
182     if (g->events[i].cb == cb) {
183       if (opaque2 == NULL) {
184         /* opaque2 (the original callback) is NULL, which in the
185          * old-style API meant remove the callback.
186          */
187         guestfs_delete_event_callback (g, i);
188         return;
189       }
190
191       goto replace;
192     }
193
194   if (opaque2 == NULL)
195     return; /* see above */
196
197   /* i == g->nr_events */
198   g->events =
199     guestfs_safe_realloc (g, g->events,
200                           (g->nr_events+1) * sizeof (struct event));
201   g->nr_events++;
202
203  replace:
204   g->events[i].event_bitmask = event_bitmask;
205   g->events[i].cb = cb;
206   g->events[i].opaque = opaque;
207   g->events[i].opaque2 = opaque2;
208 }
209
210 static void
211 log_message_callback_wrapper (guestfs_h *g,
212                               void *opaque,
213                               uint64_t event,
214                               int event_handle,
215                               int flags,
216                               const char *buf, size_t buf_len,
217                               const uint64_t *array, size_t array_len)
218 {
219   guestfs_log_message_cb cb = g->events[event_handle].opaque2;
220   /* Note that the old callback declared the message buffer as
221    * (char *, int).  I sure hope message buffers aren't too large
222    * and that callers aren't writing to them. XXX
223    */
224   cb (g, opaque, (char *) buf, (int) buf_len);
225 }
226
227 void
228 guestfs_set_log_message_callback (guestfs_h *g,
229                                   guestfs_log_message_cb cb, void *opaque)
230 {
231   replace_old_style_event_callback (g, log_message_callback_wrapper,
232                                     GUESTFS_EVENT_APPLIANCE,
233                                     opaque, cb);
234 }
235
236 static void
237 subprocess_quit_callback_wrapper (guestfs_h *g,
238                                   void *opaque,
239                                   uint64_t event,
240                                   int event_handle,
241                                   int flags,
242                                   const char *buf, size_t buf_len,
243                                   const uint64_t *array, size_t array_len)
244 {
245   guestfs_subprocess_quit_cb cb = g->events[event_handle].opaque2;
246   cb (g, opaque);
247 }
248
249 void
250 guestfs_set_subprocess_quit_callback (guestfs_h *g,
251                                       guestfs_subprocess_quit_cb cb, void *opaque)
252 {
253   replace_old_style_event_callback (g, subprocess_quit_callback_wrapper,
254                                     GUESTFS_EVENT_SUBPROCESS_QUIT,
255                                     opaque, cb);
256 }
257
258 static void
259 launch_done_callback_wrapper (guestfs_h *g,
260                               void *opaque,
261                               uint64_t event,
262                               int event_handle,
263                               int flags,
264                               const char *buf, size_t buf_len,
265                               const uint64_t *array, size_t array_len)
266 {
267   guestfs_launch_done_cb cb = g->events[event_handle].opaque2;
268   cb (g, opaque);
269 }
270
271 void
272 guestfs_set_launch_done_callback (guestfs_h *g,
273                                   guestfs_launch_done_cb cb, void *opaque)
274 {
275   replace_old_style_event_callback (g, launch_done_callback_wrapper,
276                                     GUESTFS_EVENT_LAUNCH_DONE,
277                                     opaque, cb);
278 }
279
280 static void
281 close_callback_wrapper (guestfs_h *g,
282                         void *opaque,
283                         uint64_t event,
284                         int event_handle,
285                         int flags,
286                         const char *buf, size_t buf_len,
287                         const uint64_t *array, size_t array_len)
288 {
289   guestfs_close_cb cb = g->events[event_handle].opaque2;
290   cb (g, opaque);
291 }
292
293 void
294 guestfs_set_close_callback (guestfs_h *g,
295                             guestfs_close_cb cb, void *opaque)
296 {
297   replace_old_style_event_callback (g, close_callback_wrapper,
298                                     GUESTFS_EVENT_CLOSE,
299                                     opaque, cb);
300 }
301
302 static void
303 progress_callback_wrapper (guestfs_h *g,
304                            void *opaque,
305                            uint64_t event,
306                            int event_handle,
307                            int flags,
308                            const char *buf, size_t buf_len,
309                            const uint64_t *array, size_t array_len)
310 {
311   guestfs_progress_cb cb = g->events[event_handle].opaque2;
312   assert (array_len >= 4);
313   cb (g, opaque, (int) array[0], (int) array[1], array[2], array[3]);
314 }
315
316 void
317 guestfs_set_progress_callback (guestfs_h *g,
318                                guestfs_progress_cb cb, void *opaque)
319 {
320   replace_old_style_event_callback (g, progress_callback_wrapper,
321                                     GUESTFS_EVENT_PROGRESS,
322                                     opaque, cb);
323 }