/* This is called if we detect EOF, ie. qemu died. */ static void child_cleanup (guestfs_h *g) { debug (g, "child_cleanup: %p: child process died", g); g->attach_ops->shutdown (g); if (g->fd[0] >= 0) close (g->fd[0]); if (g->fd[1] >= 0) close (g->fd[1]); close (g->sock); g->fd[0] = -1; g->fd[1] = -1; g->sock = -1; memset (&g->launch_t, 0, sizeof g->launch_t); g->state = CONFIG; guestfs___call_callbacks_void (g, GUESTFS_EVENT_SUBPROCESS_QUIT); }
void guestfs_close (guestfs_h *g) { if (g->state == NO_HANDLE) { /* Not safe to call ANY callbacks here, so ... */ fprintf (stderr, _("guestfs_close: called twice on the same handle\n")); return; } if (g->trace) { const char trace_msg[] = "close"; guestfs___call_callbacks_message (g, GUESTFS_EVENT_TRACE, trace_msg, strlen (trace_msg)); } debug (g, "closing guestfs handle %p (state %d)", g, g->state); /* Try to sync if autosync flag is set. */ if (g->autosync && g->state == READY) guestfs_internal_autosync (g); /* If we are valgrinding the daemon, then we *don't* want to kill * the subprocess because we want the final valgrind messages sent * when we close sockets below. However for normal production use, * killing the subprocess is the right thing to do (in case the * daemon or qemu is not responding). */ #ifndef VALGRIND_DAEMON /* Kill the qemu subprocess. */ if (g->state != CONFIG) guestfs_kill_subprocess (g); #endif /* Run user close callbacks. */ guestfs___call_callbacks_void (g, GUESTFS_EVENT_CLOSE); /* Remove all other registered callbacks. Since we've already * called the close callbacks, we shouldn't call any others. */ free (g->events); g->nr_events = 0; g->events = NULL; guestfs___free_inspect_info (g); guestfs___free_drives (&g->drives); /* Close sockets. */ if (g->fd[0] >= 0) close (g->fd[0]); if (g->fd[1] >= 0) close (g->fd[1]); if (g->sock >= 0) close (g->sock); g->fd[0] = -1; g->fd[1] = -1; g->sock = -1; /* Wait for subprocess(es) to exit. */ if (g->pid > 0) waitpid (g->pid, NULL, 0); if (g->recoverypid > 0) waitpid (g->recoverypid, NULL, 0); /* Remove whole temporary directory. */ guestfs___remove_tmpdir (g->tmpdir); free (g->tmpdir); if (g->cmdline) { size_t i; for (i = 0; i < g->cmdline_size; ++i) free (g->cmdline[i]); free (g->cmdline); } /* Mark the handle as dead before freeing it. */ g->state = NO_HANDLE; gl_lock_lock (handles_lock); if (handles == g) handles = g->next; else { guestfs_h *gg; for (gg = handles; gg->next != g; gg = gg->next) ; gg->next = g->next; } gl_lock_unlock (handles_lock); if (g->pda) hash_free (g->pda); free (g->last_error); free (g->path); free (g->qemu); free (g->append); free (g->qemu_help); free (g->qemu_version); free (g); }
int guestfs___recv_from_daemon (guestfs_h *g, uint32_t *size_rtn, void **buf_rtn) { char summary[MAX_MESSAGE_SUMMARY]; fd_set rset, rset2; FD_ZERO (&rset); if (g->fd[1] >= 0) /* Read qemu stdout for log messages & EOF. */ FD_SET (g->fd[1], &rset); FD_SET (g->sock, &rset); /* Read socket for data & EOF. */ int max_fd = MAX (g->sock, g->fd[1]); *size_rtn = 0; *buf_rtn = NULL; char lenbuf[4]; /* nr is the size of the message, but we prime it as -4 because we * have to read the message length word first. */ ssize_t nr = -4; for (;;) { ssize_t message_size = *size_rtn != GUESTFS_PROGRESS_FLAG ? *size_rtn : PROGRESS_MESSAGE_SIZE; if (nr >= message_size) break; rset2 = rset; int r = select (max_fd+1, &rset2, NULL, NULL, NULL); if (r == -1) { if (errno == EINTR || errno == EAGAIN) continue; perrorf (g, "select"); free (*buf_rtn); *buf_rtn = NULL; return -1; } if (g->fd[1] >= 0 && FD_ISSET (g->fd[1], &rset2)) { if (read_log_message_or_eof (g, g->fd[1], 0) == -1) { free (*buf_rtn); *buf_rtn = NULL; return -1; } } if (FD_ISSET (g->sock, &rset2)) { if (nr < 0) { /* Have we read the message length word yet? */ r = read (g->sock, lenbuf+nr+4, -nr); if (r == -1) { if (errno == EINTR || errno == EAGAIN) continue; int err = errno; perrorf (g, "read"); /* Under some circumstances we see "Connection reset by peer" * here when the child dies suddenly. Catch this and call * the cleanup function, same as for EOF. */ if (err == ECONNRESET) child_cleanup (g); return -1; } if (r == 0) { unexpected_end_of_file_from_daemon_error (g); child_cleanup (g); return -1; } nr += r; if (nr < 0) /* Still not got the whole length word. */ continue; XDR xdr; xdrmem_create (&xdr, lenbuf, 4, XDR_DECODE); xdr_uint32_t (&xdr, size_rtn); xdr_destroy (&xdr); /* *size_rtn changed, recalculate message_size */ message_size = *size_rtn != GUESTFS_PROGRESS_FLAG ? *size_rtn : PROGRESS_MESSAGE_SIZE; if (*size_rtn == GUESTFS_LAUNCH_FLAG) { if (g->state != LAUNCHING) error (g, _("received magic signature from guestfsd, but in state %d"), g->state); else { g->state = READY; guestfs___call_callbacks_void (g, GUESTFS_EVENT_LAUNCH_DONE); } debug (g, "recv_from_daemon: received GUESTFS_LAUNCH_FLAG"); return 0; } else if (*size_rtn == GUESTFS_CANCEL_FLAG) { debug (g, "recv_from_daemon: received GUESTFS_CANCEL_FLAG"); return 0; } else if (*size_rtn == GUESTFS_PROGRESS_FLAG) /*FALLTHROUGH*/; /* If this happens, it's pretty bad and we've probably lost * synchronization. */ else if (*size_rtn > GUESTFS_MESSAGE_MAX) { error (g, _("message length (%u) > maximum possible size (%d)"), (unsigned) *size_rtn, GUESTFS_MESSAGE_MAX); return -1; } /* Allocate the complete buffer, size now known. */ *buf_rtn = safe_malloc (g, message_size); /*FALLTHROUGH*/ } size_t sizetoread = message_size - nr; if (sizetoread > BUFSIZ) sizetoread = BUFSIZ; r = read (g->sock, (char *) (*buf_rtn) + nr, sizetoread); if (r == -1) { if (errno == EINTR || errno == EAGAIN) continue; perrorf (g, "read"); free (*buf_rtn); *buf_rtn = NULL; return -1; } if (r == 0) { unexpected_end_of_file_from_daemon_error (g); child_cleanup (g); free (*buf_rtn); *buf_rtn = NULL; return -1; } nr += r; } } /* Got the full message, caller can start processing it. */ #ifdef ENABLE_PACKET_DUMP if (g->verbose) { ssize_t i, j; for (i = 0; i < nr; i += 16) { printf ("%04zx: ", i); for (j = i; j < MIN (i+16, nr); ++j) printf ("%02x ", (*(unsigned char **)buf_rtn)[j]); for (; j < i+16; ++j) printf (" "); printf ("|"); for (j = i; j < MIN (i+16, nr); ++j) if (c_isprint ((*(char **)buf_rtn)[j])) printf ("%c", (*(char **)buf_rtn)[j]); else printf ("."); for (; j < i+16; ++j) printf (" "); printf ("|\n"); } } #endif if (*size_rtn == GUESTFS_PROGRESS_FLAG) { guestfs_progress message; XDR xdr; xdrmem_create (&xdr, *buf_rtn, PROGRESS_MESSAGE_SIZE, XDR_DECODE); xdr_guestfs_progress (&xdr, &message); xdr_destroy (&xdr); guestfs___progress_message_callback (g, &message); free (*buf_rtn); *buf_rtn = NULL; /* Process next message. */ return guestfs___recv_from_daemon (g, size_rtn, buf_rtn); } debug (g, "recv_from_daemon: %" PRIu32 " bytes: %s", *size_rtn, message_summary (*buf_rtn, *size_rtn, summary)); return 0; }
void guestfs_close (guestfs_h *g) { struct qemu_param *qp, *qp_next; if (g->state == NO_HANDLE) { /* Not safe to call ANY callbacks here, so ... */ fprintf (stderr, _("guestfs_close: called twice on the same handle\n")); return; } /* Remove the handle from the handles list. */ gl_lock_lock (handles_lock); if (handles == g) handles = g->next; else { guestfs_h *gg; for (gg = handles; gg->next != g; gg = gg->next) ; gg->next = g->next; } gl_lock_unlock (handles_lock); if (g->trace) { const char trace_msg[] = "close"; guestfs___call_callbacks_message (g, GUESTFS_EVENT_TRACE, trace_msg, strlen (trace_msg)); } debug (g, "closing guestfs handle %p (state %d)", g, g->state); /* If we are valgrinding the daemon, then we *don't* want to kill * the subprocess because we want the final valgrind messages sent * when we close sockets below. However for normal production use, * killing the subprocess is the right thing to do (in case the * daemon or qemu is not responding). */ #ifndef VALGRIND_DAEMON if (g->state != CONFIG) ignore_value (guestfs_shutdown (g)); #endif /* Run user close callbacks. */ guestfs___call_callbacks_void (g, GUESTFS_EVENT_CLOSE); /* Remove whole temporary directory. */ guestfs___remove_tmpdir (g->tmpdir); /* Mark the handle as dead and then free up all memory. */ g->state = NO_HANDLE; free (g->events); g->nr_events = 0; g->events = NULL; #if HAVE_FUSE guestfs___free_fuse (g); #endif guestfs___free_inspect_info (g); guestfs___free_drives (&g->drives); for (qp = g->qemu_params; qp; qp = qp_next) { free (qp->qemu_param); free (qp->qemu_value); qp_next = qp->next; free (qp); } if (g->pda) hash_free (g->pda); free (g->tmpdir); free (g->last_error); free (g->path); free (g->qemu); free (g->append); free (g); }