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