/* Guestfs.set_event_callback */ CAMLprim value ocaml_guestfs_set_event_callback (value gv, value closure, value events) { CAMLparam3 (gv, closure, events); char key[64]; int eh; uint64_t event_bitmask; guestfs_h *g = Guestfs_val (gv); event_bitmask = event_bitmask_of_event_list (events); value *root = guestfs_safe_malloc (g, sizeof *root); *root = closure; eh = guestfs_set_event_callback (g, event_callback_wrapper, event_bitmask, 0, root); if (eh == -1) { free (root); ocaml_guestfs_raise_error (g, "set_event_callback"); } /* XXX This global root is generational, but we cannot rely on every * user having the OCaml 3.11 version which supports this. */ caml_register_global_root (root); snprintf (key, sizeof key, "_ocaml_event_%d", eh); guestfs_set_private (g, key, root); CAMLreturn (Val_int (eh)); }
/* Guestfs.set_event_callback */ value ocaml_guestfs_set_event_callback (value gv, value closure, value events) { CAMLparam3 (gv, closure, events); char key[64]; int eh; uint64_t event_bitmask; guestfs_h *g = Guestfs_val (gv); event_bitmask = event_bitmask_of_event_list (events); value *root = guestfs_int_safe_malloc (g, sizeof *root); *root = closure; eh = guestfs_set_event_callback (g, event_callback_wrapper, event_bitmask, 0, root); if (eh == -1) { free (root); ocaml_guestfs_raise_error (g, "set_event_callback"); } caml_register_generational_global_root (root); snprintf (key, sizeof key, "_ocaml_event_%d", eh); guestfs_set_private (g, key, root); CAMLreturn (Val_int (eh)); }
int main (int argc, char *argv[]) { guestfs_h *g; const char *key; void *data; size_t count; g = guestfs_create (); if (g == NULL) error (EXIT_FAILURE, errno, "guestfs_create"); if (guestfs_set_event_callback (g, close_callback, GUESTFS_EVENT_CLOSE, 0, NULL) == -1) exit (EXIT_FAILURE); guestfs_set_private (g, PREFIX "a", (void *) 1); guestfs_set_private (g, PREFIX "b", (void *) 2); guestfs_set_private (g, PREFIX "c", (void *) 3); guestfs_set_private (g, PREFIX "a", (void *) 4); /* overwrites previous */ /* Check we can fetch keys. */ assert (guestfs_get_private (g, PREFIX "a") == (void *) 4); assert (guestfs_get_private (g, PREFIX "b") == (void *) 2); assert (guestfs_get_private (g, PREFIX "c") == (void *) 3); assert (guestfs_get_private (g, PREFIX "d") == NULL); /* Check we can count keys by iterating. */ count = 0; data = guestfs_first_private (g, &key); while (data != NULL) { if (strncmp (key, PREFIX, strlen (PREFIX)) == 0) count++; data = guestfs_next_private (g, &key); } assert (count == 3); /* Delete some keys. */ guestfs_set_private (g, PREFIX "a", NULL); guestfs_set_private (g, PREFIX "b", NULL); /* Count them again. */ count = 0; data = guestfs_first_private (g, &key); while (data != NULL) { if (strncmp (key, PREFIX, strlen (PREFIX)) == 0) count++; data = guestfs_next_private (g, &key); } assert (count == 1); /* Closing should implicitly call the close_callback function. */ guestfs_close (g); assert (close_callback_called == 1); exit (EXIT_SUCCESS); }
/* Common function to set up event callbacks and record data in memory * for a particular pass (0 <= pass < NR_TEST_PASSES). */ static void set_up_event_handlers (guestfs_h *g, size_t pass) { struct pass_data *data; assert (/* 0 <= pass && */ pass < NR_TEST_PASSES); data = &pass_data[pass]; data->pass = pass; data->nr_events = 0; data->events = NULL; get_time (&data->start_t); data->incomplete_log_message = -1; data->seen_launch = 0; guestfs_set_event_callback (g, close_callback, GUESTFS_EVENT_CLOSE, 0, data); guestfs_set_event_callback (g, subprocess_quit_callback, GUESTFS_EVENT_SUBPROCESS_QUIT, 0, data); guestfs_set_event_callback (g, launch_done_callback, GUESTFS_EVENT_LAUNCH_DONE, 0, data); guestfs_set_event_callback (g, appliance_callback, GUESTFS_EVENT_APPLIANCE, 0, data); guestfs_set_event_callback (g, library_callback, GUESTFS_EVENT_LIBRARY, 0, data); guestfs_set_event_callback (g, trace_callback, GUESTFS_EVENT_TRACE, 0, data); guestfs_set_verbose (g, 1); guestfs_set_trace (g, 1); }
int main (int argc, char *argv[]) { guestfs_h *g; const char *filename = "test.log"; FILE *debugfp; debugfp = fopen (filename, "w"); if (debugfp == NULL) { perror (filename); exit (EXIT_FAILURE); } g = guestfs_create (); if (g == NULL) { fprintf (stderr, "failed to create handle\n"); exit (EXIT_FAILURE); } if (guestfs_set_event_callback (g, debug_to_file, GUESTFS_EVENT_LIBRARY | GUESTFS_EVENT_APPLIANCE | GUESTFS_EVENT_WARNING | GUESTFS_EVENT_TRACE, 0, debugfp) == -1) exit (EXIT_FAILURE); if (guestfs_set_verbose (g, 1) == -1) exit (EXIT_FAILURE); if (guestfs_set_trace (g, 1) == -1) exit (EXIT_FAILURE); if (guestfs_add_drive_opts (g, "/dev/null", GUESTFS_ADD_DRIVE_OPTS_FORMAT, "raw", GUESTFS_ADD_DRIVE_OPTS_READONLY, 1, -1) == -1) exit (EXIT_FAILURE); if (guestfs_launch (g) == -1) exit (EXIT_FAILURE); guestfs_close (g); exit (EXIT_SUCCESS); }
PyObject * py_guestfs_set_event_callback (PyObject *self, PyObject *args) { PyObject *py_g; guestfs_h *g; PyObject *py_callback; unsigned PY_LONG_LONG events; int eh; PyObject *py_eh; char key[64]; if (!PyArg_ParseTuple (args, (char *) "OOK:guestfs_set_event_callback", &py_g, &py_callback, &events)) return NULL; if (!PyCallable_Check (py_callback)) { PyErr_SetString (PyExc_TypeError, "callback parameter is not callable " "(eg. lambda or function)"); return NULL; } g = get_handle (py_g); eh = guestfs_set_event_callback (g, py_guestfs_event_callback_wrapper, events, 0, py_callback); if (eh == -1) { PyErr_SetString (PyExc_RuntimeError, guestfs_last_error (g)); return NULL; } /* Increase the refcount for this callback since we are storing it * in the opaque C libguestfs handle. We need to remember that we * did this, so we can decrease the refcount for all undeleted * callbacks left around at close time (see py_guestfs_close). */ Py_XINCREF (py_callback); snprintf (key, sizeof key, "_python_event_%d", eh); guestfs_set_private (g, key, py_callback); py_eh = PyLong_FromLong ((long) eh); return py_eh; }
JNIEXPORT jint JNICALL Java_com_redhat_et_libguestfs_GuestFS__1set_1event_1callback (JNIEnv *env, jobject obj, jlong jg, jobject jcallback, jlong jevents) { guestfs_h *g = (guestfs_h *) (long) jg; int r; struct callback_data *data; jclass callback_class; jmethodID method; char key[64]; callback_class = (*env)->GetObjectClass (env, jcallback); method = (*env)->GetMethodID (env, callback_class, METHOD_NAME, METHOD_SIGNATURE); if (method == 0) { throw_exception (env, "GuestFS.set_event_callback: callback class does not implement the EventCallback interface"); return -1; } data = malloc (sizeof *data); if (data == NULL) { throw_out_of_memory (env, "malloc"); return -1; } (*env)->GetJavaVM (env, &data->jvm); data->method = method; r = guestfs_set_event_callback (g, java_callback, (uint64_t) jevents, 0, data); if (r == -1) { free (data); throw_exception (env, guestfs_last_error (g)); return -1; } /* Register jcallback as a global reference so the GC won't free it. */ data->callback = (*env)->NewGlobalRef (env, jcallback); /* Store 'data' in the handle, so we can free it at some point. */ snprintf (key, sizeof key, "_java_event_%d", r); guestfs_set_private (g, key, data); return (jint) r; }
int run_event (const char *cmd, size_t argc, char *argv[]) { int r; struct entry *entry = NULL, *old_entry; if (argc != 3) { fprintf (stderr, _("use 'event <name> <eventset> <script>' to register an event handler\n")); goto error; } entry = calloc (1, sizeof *entry); if (entry == NULL) { perror ("calloc"); goto error; } entry->eh = -1; r = event_bitmask_of_event_set (argv[1], &entry->event_bitmask); if (r == -1) goto error; entry->name = strdup (argv[0]); if (entry->name == NULL) { perror ("strdup"); goto error; } entry->command = strdup (argv[2]); if (entry->command == NULL) { perror ("strdup"); goto error; } entry->eh = guestfs_set_event_callback (g, do_event_handler, entry->event_bitmask, 0, entry); if (entry->eh == -1) goto error; r = hash_insert_if_absent (event_handlers, entry, (const void **) &old_entry); if (r == -1) goto error; if (r == 0) { /* old_entry set to existing entry */ entry->next = old_entry->next; /* XXX are we allowed to update the old entry? */ old_entry->next = entry; } return 0; error: if (entry) { if (entry->eh >= 0) guestfs_delete_event_callback (g, entry->eh); free (entry->name); free (entry->command); free (entry); } return -1; }
static void test_virtio_serial (void) { int fd, r, eh; char tmpfile[] = "/tmp/speedtestXXXXXX"; struct sigaction sa, old_sa; if (!virtio_serial_upload && !virtio_serial_download) return; /* Create a sparse file. We could upload from /dev/zero, but we * won't get progress messages because libguestfs tests if the * source file is a regular file. */ fd = mkstemp (tmpfile); if (fd == -1) error (EXIT_FAILURE, errno, "mkstemp: %s", tmpfile); if (ftruncate (fd, TEST_SERIAL_MAX_SIZE) == -1) error (EXIT_FAILURE, errno, "ftruncate"); if (close (fd) == -1) error (EXIT_FAILURE, errno, "close"); g = guestfs_create (); if (!g) error (EXIT_FAILURE, errno, "guestfs_create"); if (guestfs_add_drive_scratch (g, INT64_C (100*1024*1024), -1) == -1) exit (EXIT_FAILURE); if (guestfs_launch (g) == -1) exit (EXIT_FAILURE); /* Make and mount a filesystem which will be used by the download test. */ if (guestfs_mkfs (g, "ext4", "/dev/sda") == -1) exit (EXIT_FAILURE); if (guestfs_mount (g, "/dev/sda", "/") == -1) exit (EXIT_FAILURE); /* Time out the upload after TEST_SERIAL_MAX_TIME seconds have passed. */ memset (&sa, 0, sizeof sa); sa.sa_handler = stop_transfer; sa.sa_flags = SA_RESTART; sigaction (SIGALRM, &sa, &old_sa); /* Get progress messages, which will tell us how much data has been * transferred. */ eh = guestfs_set_event_callback (g, progress_cb, GUESTFS_EVENT_PROGRESS, 0, NULL); if (eh == -1) exit (EXIT_FAILURE); if (virtio_serial_upload) { gettimeofday (&start, NULL); rate = -1; operation = "upload"; alarm (max_time_override > 0 ? max_time_override : TEST_SERIAL_MAX_TIME); /* For the upload test, upload the sparse file to /dev/null in the * appliance. Hopefully this is mostly testing just virtio-serial. */ guestfs_push_error_handler (g, NULL, NULL); r = guestfs_upload (g, tmpfile, "/dev/null"); alarm (0); unlink (tmpfile); guestfs_pop_error_handler (g); /* It's possible that the upload will finish before the alarm fires, * or that the upload will be stopped by the alarm. */ if (r == -1 && guestfs_last_errno (g) != EINTR) { fprintf (stderr, "%s: expecting upload command to return EINTR\n%s\n", guestfs_int_program_name, guestfs_last_error (g)); exit (EXIT_FAILURE); } if (rate == -1) { rate_error: fprintf (stderr, "%s: internal error: progress callback was not called! (r=%d, errno=%d)\n", guestfs_int_program_name, r, guestfs_last_errno (g)); exit (EXIT_FAILURE); } print_rate ("virtio-serial upload rate:", rate); } if (virtio_serial_download) { /* For the download test, download a sparse file within the * appliance to /dev/null on the host. */ if (guestfs_touch (g, "/sparse") == -1) exit (EXIT_FAILURE); if (guestfs_truncate_size (g, "/sparse", TEST_SERIAL_MAX_SIZE) == -1) exit (EXIT_FAILURE); gettimeofday (&start, NULL); rate = -1; operation = "download"; alarm (max_time_override > 0 ? max_time_override : TEST_SERIAL_MAX_TIME); guestfs_push_error_handler (g, NULL, NULL); r = guestfs_download (g, "/sparse", "/dev/null"); alarm (0); guestfs_pop_error_handler (g); if (r == -1 && guestfs_last_errno (g) != EINTR) { fprintf (stderr, "%s: expecting download command to return EINTR\n%s\n", guestfs_int_program_name, guestfs_last_error (g)); exit (EXIT_FAILURE); } if (rate == -1) goto rate_error; print_rate ("virtio-serial download rate:", rate); } if (guestfs_shutdown (g) == -1) exit (EXIT_FAILURE); guestfs_close (g); /* Restore SIGALRM signal handler. */ sigaction (SIGALRM, &old_sa, NULL); }
/* Worker thread. */ static void * start_thread (void *thread_data_vp) { struct thread_data *thread_data = thread_data_vp; int quit = 0; int err; size_t i; guestfs_h *g; unsigned errors = 0; char id[64]; for (;;) { CLEANUP_FREE char *log_file = NULL; CLEANUP_FCLOSE FILE *log_fp = NULL; /* Take the next process. */ err = pthread_mutex_lock (&mutex); if (err != 0) { fprintf (stderr, "%s: pthread_mutex_lock: %s", guestfs_int_program_name, strerror (err)); goto error; } i = n; if (i > 0) { printf ("%zu to go ... \r", n); fflush (stdout); n--; } else quit = 1; err = pthread_mutex_unlock (&mutex); if (err != 0) { fprintf (stderr, "%s: pthread_mutex_unlock: %s", guestfs_int_program_name, strerror (err)); goto error; } if (quit) /* Work finished. */ break; g = guestfs_create (); if (g == NULL) { perror ("guestfs_create"); errors++; if (!ignore_errors) goto error; } /* Only if using --log, set up a callback. See examples/debug-logging.c */ if (log_template != NULL) { size_t j, k; log_file = malloc (log_file_size + 1); if (log_file == NULL) abort (); for (j = 0, k = 0; j < strlen (log_template); ++j) { if (log_template[j] == '%') { snprintf (&log_file[k], log_file_size - k, "%zu", i); k += strlen (&log_file[k]); } else log_file[k++] = log_template[j]; } log_file[k] = '\0'; log_fp = fopen (log_file, "w"); if (log_fp == NULL) { perror (log_file); abort (); } guestfs_set_event_callback (g, message_callback, event_bitmask, 0, log_fp); } snprintf (id, sizeof id, "%zu", i); guestfs_set_identifier (g, id); guestfs_set_trace (g, trace); guestfs_set_verbose (g, verbose); if (guestfs_add_drive_ro (g, "/dev/null") == -1) { errors++; if (!ignore_errors) goto error; } if (guestfs_launch (g) == -1) { errors++; if (!ignore_errors) goto error; } if (guestfs_shutdown (g) == -1) { errors++; if (!ignore_errors) goto error; } guestfs_close (g); } if (errors > 0) { fprintf (stderr, "%s: thread %d: %u errors were ignored\n", guestfs_int_program_name, thread_data->thread_num, errors); goto error; } thread_data->r = 0; return &thread_data->r; error: thread_data->r = -1; return &thread_data->r; }
int run_reopen (const char *cmd, size_t argc, char *argv[]) { guestfs_h *g2; int r; const char *p; guestfs_error_handler_cb cb; void *cb_data; if (argc > 0) { fprintf (stderr, _("'reopen' command takes no parameters\n")); return -1; } if (guestfs_shutdown (g) == -1) return -1; /* Open the new handle first, so we can copy the settings from the * old one to the new one, and also so if it fails we still have an * open handle. */ g2 = guestfs_create (); if (g2 == NULL) { fprintf (stderr, _("reopen: guestfs_create: failed to create handle\n")); return -1; } /* Now copy some of the settings from the old handle. The settings * we copy are those which are set by guestfish itself. */ cb = guestfs_get_error_handler (g, &cb_data); guestfs_set_error_handler (g2, cb, cb_data); r = guestfs_get_verbose (g); if (r >= 0) guestfs_set_verbose (g2, r); r = guestfs_get_trace (g); if (r >= 0) guestfs_set_trace (g2, r); r = guestfs_get_autosync (g); if (r >= 0) guestfs_set_autosync (g2, r); p = guestfs_get_path (g); if (p) guestfs_set_path (g2, p); r = guestfs_get_pgroup (g); if (r >= 0) guestfs_set_pgroup (g2, r); if (progress_bars) guestfs_set_event_callback (g2, progress_callback, GUESTFS_EVENT_PROGRESS, 0, NULL); /* Close the original handle. */ guestfs_close (g); g = g2; /* We don't bother copying event handlers over to the new handle, * but we have to reset the list because they were registered * against the old handle. */ free_event_handlers (); init_event_handlers (); return 0; }