+ if (!g->qemu_help) {
+ if (test_qemu (g) == -1)
+ return -1;
+ }
+
+ if (option == NULL)
+ return 1;
+
+ return strstr (g->qemu_help, option) != NULL;
+}
+
+/* Check if a file can be opened. */
+static int
+is_openable (guestfs_h *g, const char *path, int flags)
+{
+ int fd = open (path, flags);
+ if (fd == -1) {
+ if (g->verbose)
+ perror (path);
+ return 0;
+ }
+ close (fd);
+ return 1;
+}
+
+/* Check the peer effective UID for a TCP socket. Ideally we'd like
+ * SO_PEERCRED for a loopback TCP socket. This isn't possible on
+ * Linux (but it is on Solaris!) so we read /proc/net/tcp instead.
+ */
+static int
+check_peer_euid (guestfs_h *g, int sock, uid_t *rtn)
+{
+#if CAN_CHECK_PEER_EUID
+ struct sockaddr_in peer;
+ socklen_t addrlen = sizeof peer;
+
+ if (getpeername (sock, (struct sockaddr *) &peer, &addrlen) == -1) {
+ perrorf (g, "getpeername");
+ return -1;
+ }
+
+ if (peer.sin_family != AF_INET ||
+ ntohl (peer.sin_addr.s_addr) != INADDR_LOOPBACK) {
+ error (g, "check_peer_euid: unexpected connection from non-IPv4, non-loopback peer (family = %d, addr = %s)",
+ peer.sin_family, inet_ntoa (peer.sin_addr));
+ return -1;
+ }
+
+ struct sockaddr_in our;
+ addrlen = sizeof our;
+ if (getsockname (sock, (struct sockaddr *) &our, &addrlen) == -1) {
+ perrorf (g, "getsockname");
+ return -1;
+ }
+
+ FILE *fp = fopen ("/proc/net/tcp", "r");
+ if (fp == NULL) {
+ perrorf (g, "/proc/net/tcp");
+ return -1;
+ }
+
+ char line[256];
+ if (fgets (line, sizeof line, fp) == NULL) { /* Drop first line. */
+ error (g, "unexpected end of file in /proc/net/tcp");
+ fclose (fp);
+ return -1;
+ }
+
+ while (fgets (line, sizeof line, fp) != NULL) {
+ unsigned line_our_addr, line_our_port, line_peer_addr, line_peer_port;
+ int dummy0, dummy1, dummy2, dummy3, dummy4, dummy5, dummy6;
+ int line_uid;
+
+ if (sscanf (line, "%d:%08X:%04X %08X:%04X %02X %08X:%08X %02X:%08X %08X %d",
+ &dummy0,
+ &line_our_addr, &line_our_port,
+ &line_peer_addr, &line_peer_port,
+ &dummy1, &dummy2, &dummy3, &dummy4, &dummy5, &dummy6,
+ &line_uid) == 12) {
+ /* Note about /proc/net/tcp: local_address and rem_address are
+ * always in network byte order. However the port part is
+ * always in host byte order.
+ *
+ * The sockname and peername that we got above are in network
+ * byte order. So we have to byte swap the port but not the
+ * address part.
+ */
+ if (line_our_addr == our.sin_addr.s_addr &&
+ line_our_port == ntohs (our.sin_port) &&
+ line_peer_addr == peer.sin_addr.s_addr &&
+ line_peer_port == ntohs (peer.sin_port)) {
+ *rtn = line_uid;
+ fclose (fp);
+ return 0;
+ }
+ }
+ }
+
+ error (g, "check_peer_euid: no matching TCP connection found in /proc/net/tcp");
+ fclose (fp);
+ return -1;
+#else /* !CAN_CHECK_PEER_EUID */
+ /* This function exists but should never be called in this
+ * configuration.
+ */
+ abort ();
+#endif /* !CAN_CHECK_PEER_EUID */