#include <config.h>
+#define _BSD_SOURCE /* for daemon(3) */
+
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <rpc/xdr.h>
#include <getopt.h>
#include <netdb.h>
+#include <sys/param.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <ctype.h>
+#include <signal.h>
#include "daemon.h"
-void xwrite (int sock, const void *buf, size_t len);
-
static void usage (void);
/* Also in guestfs.c */
#define VMCHANNEL_PORT "6666"
#define VMCHANNEL_ADDR "10.0.2.4"
+int verbose = 0;
+
int
main (int argc, char *argv[])
{
struct addrinfo *res, *rr;
struct addrinfo hints;
XDR xdr;
- unsigned len;
+ uint32_t len;
+ struct sigaction sa;
for (;;) {
c = getopt_long (argc, argv, options, long_options, NULL);
fclose (fp);
buf[n] = '\0';
+ /* Set the verbose flag. Not quite right because this will only
+ * set the flag if host and port aren't set on the command line.
+ * Don't worry about this for now. (XXX)
+ */
+ verbose = strstr (buf, "guestfs_verbose=1") != NULL;
+ if (verbose)
+ printf ("verbose daemon enabled\n");
+
p = strstr (buf, "guestfs=");
if (p) {
port = VMCHANNEL_PORT;
}
+ /* Make sure SIGPIPE doesn't kill us. */
+ memset (&sa, 0, sizeof sa);
+ sa.sa_handler = SIG_IGN;
+ sa.sa_flags = 0;
+ if (sigaction (SIGPIPE, &sa, NULL) == -1)
+ perror ("sigaction SIGPIPE"); /* but try to continue anyway ... */
+
/* Resolve the hostname. */
memset (&hints, 0, sizeof hints);
hints.ai_socktype = SOCK_STREAM;
/* Send the magic length message which indicates that
* userspace is up inside the guest.
*/
- len = 0xf5f55ff5;
+ len = GUESTFS_LAUNCH_FLAG;
xdrmem_create (&xdr, buf, sizeof buf, XDR_ENCODE);
if (!xdr_uint32_t (&xdr, &len)) {
fprintf (stderr, "xdr_uint32_t failed\n");
exit (1);
}
- xwrite (sock, buf, xdr_getpos (&xdr));
+ (void) xwrite (sock, buf, xdr_getpos (&xdr));
xdr_destroy (&xdr);
- /* XXX Fork into the background. */
-
-
-
-
-
-
-
-
-
-
-
+ /* Fork into the background. */
+ if (!dont_fork) {
+ if (daemon (0, 1) == -1) {
+ perror ("daemon");
+ exit (1);
+ }
+ }
+ /* Enter the main loop, reading and performing actions. */
main_loop (sock);
exit (0);
}
-void
+int
xwrite (int sock, const void *buf, size_t len)
{
int r;
r = write (sock, buf, len);
if (r == -1) {
perror ("write");
- exit (1);
+ return -1;
+ }
+ buf += r;
+ len -= r;
+ }
+
+ return 0;
+}
+
+int
+xread (int sock, void *buf, size_t len)
+{
+ int r;
+
+ while (len > 0) {
+ r = read (sock, buf, len);
+ if (r == -1) {
+ perror ("read");
+ return -1;
+ }
+ if (r == 0) {
+ fprintf (stderr, "read: unexpected end of file on fd %d\n", sock);
+ return -1;
}
buf += r;
len -= r;
}
+
+ return 0;
}
static void
fprintf (stderr, "guestfsd [-f] [-h host -p port]\n");
}
-/* Some unimplemented actions. */
int
-do_mount (const char *device, const char *mountpoint)
+add_string (char ***argv, int *size, int *alloc, const char *str)
{
- reply_with_error ("mount not implemented");
- return -1;
+ char **new_argv;
+ char *new_str;
+
+ if (*size >= *alloc) {
+ *alloc += 64;
+ new_argv = realloc (*argv, *alloc * sizeof (char *));
+ if (new_argv == NULL) {
+ reply_with_perror ("realloc");
+ free_strings (*argv);
+ return -1;
+ }
+ *argv = new_argv;
+ }
+
+ if (str) {
+ new_str = strdup (str);
+ if (new_str == NULL) {
+ reply_with_perror ("strdup");
+ free_strings (*argv);
+ }
+ } else
+ new_str = NULL;
+
+ (*argv)[*size] = new_str;
+
+ (*size)++;
+ return 0;
}
int
-do_touch (const char *path)
+count_strings (char * const* const argv)
+{
+ int argc;
+
+ for (argc = 0; argv[argc] != NULL; ++argc)
+ ;
+ return argc;
+}
+
+static int
+compare (const void *vp1, const void *vp2)
{
- reply_with_error ("touch not implemented");
- return -1;
+ char * const *p1 = (char * const *) vp1;
+ char * const *p2 = (char * const *) vp2;
+ return strcmp (*p1, *p2);
+}
+
+void
+sort_strings (char **argv, int len)
+{
+ qsort (argv, len, sizeof (char *), compare);
+}
+
+void
+free_strings (char **argv)
+{
+ int argc;
+
+ for (argc = 0; argv[argc] != NULL; ++argc)
+ free (argv[argc]);
+ free (argv);
+}
+
+void
+free_stringslen (char **argv, int len)
+{
+ int i;
+
+ for (i = 0; i < len; ++i)
+ free (argv[i]);
+ free (argv);
+}
+
+/* This is a more sane version of 'system(3)' for running external
+ * commands. It uses fork/execvp, so we don't need to worry about
+ * quoting of parameters, and it allows us to capture any error
+ * messages in a buffer.
+ */
+int
+command (char **stdoutput, char **stderror, const char *name, ...)
+{
+ va_list args;
+ char **argv, **p;
+ char *s;
+ int i, r;
+
+ /* Collect the command line arguments into an array. */
+ i = 2;
+ argv = malloc (sizeof (char *) * i);
+ if (argv == NULL) {
+ perror ("malloc");
+ return -1;
+ }
+ argv[0] = (char *) name;
+ argv[1] = NULL;
+
+ va_start (args, name);
+
+ while ((s = va_arg (args, char *)) != NULL) {
+ p = realloc (argv, sizeof (char *) * (++i));
+ if (p == NULL) {
+ perror ("realloc");
+ free (argv);
+ va_end (args);
+ return -1;
+ }
+ argv = p;
+ argv[i-2] = s;
+ argv[i-1] = NULL;
+ }
+
+ va_end (args);
+
+ r = commandv (stdoutput, stderror, argv);
+
+ /* NB: Mustn't free the strings which are on the stack. */
+ free (argv);
+
+ return r;
+}
+
+/* Same as 'command', but we allow the status code from the
+ * subcommand to be non-zero, and return that status code.
+ * We still return -1 if there was some other error.
+ */
+int
+commandr (char **stdoutput, char **stderror, const char *name, ...)
+{
+ va_list args;
+ char **argv, **p;
+ char *s;
+ int i, r;
+
+ /* Collect the command line arguments into an array. */
+ i = 2;
+ argv = malloc (sizeof (char *) * i);
+ if (argv == NULL) {
+ perror ("malloc");
+ return -1;
+ }
+ argv[0] = (char *) name;
+ argv[1] = NULL;
+
+ va_start (args, name);
+
+ while ((s = va_arg (args, char *)) != NULL) {
+ p = realloc (argv, sizeof (char *) * (++i));
+ if (p == NULL) {
+ perror ("realloc");
+ free (argv);
+ va_end (args);
+ return -1;
+ }
+ argv = p;
+ argv[i-2] = s;
+ argv[i-1] = NULL;
+ }
+
+ va_end (args);
+
+ r = commandrv (stdoutput, stderror, argv);
+
+ /* NB: Mustn't free the strings which are on the stack. */
+ free (argv);
+
+ return r;
+}
+
+/* Same as 'command', but passing an argv. */
+int
+commandv (char **stdoutput, char **stderror, char * const* const argv)
+{
+ int r;
+
+ r = commandrv (stdoutput, stderror, argv);
+ if (r == 0)
+ return 0;
+ else
+ return -1;
+}
+
+int
+commandrv (char **stdoutput, char **stderror, char * const* const argv)
+{
+ int so_size = 0, se_size = 0;
+ int so_fd[2], se_fd[2];
+ int pid, r, quit, i;
+ fd_set rset, rset2;
+ char buf[256];
+ char *p;
+
+ if (stdoutput) *stdoutput = NULL;
+ if (stderror) *stderror = NULL;
+
+ if (verbose) {
+ printf ("%s", argv[0]);
+ for (i = 1; argv[i] != NULL; ++i)
+ printf (" %s", argv[i]);
+ printf ("\n");
+ }
+
+ if (pipe (so_fd) == -1 || pipe (se_fd) == -1) {
+ perror ("pipe");
+ return -1;
+ }
+
+ pid = fork ();
+ if (pid == -1) {
+ perror ("fork");
+ close (so_fd[0]);
+ close (so_fd[1]);
+ close (se_fd[0]);
+ close (se_fd[1]);
+ return -1;
+ }
+
+ if (pid == 0) { /* Child process. */
+ close (0);
+ close (so_fd[0]);
+ close (se_fd[0]);
+ dup2 (so_fd[1], 1);
+ dup2 (se_fd[1], 2);
+ close (so_fd[1]);
+ close (se_fd[1]);
+
+ execvp (argv[0], argv);
+ perror (argv[0]);
+ _exit (1);
+ }
+
+ /* Parent process. */
+ close (so_fd[1]);
+ close (se_fd[1]);
+
+ FD_ZERO (&rset);
+ FD_SET (so_fd[0], &rset);
+ FD_SET (se_fd[0], &rset);
+
+ quit = 0;
+ while (quit < 2) {
+ rset2 = rset;
+ r = select (MAX (so_fd[0], se_fd[0]) + 1, &rset2, NULL, NULL, NULL);
+ if (r == -1) {
+ perror ("select");
+ quit:
+ if (stdoutput) free (*stdoutput);
+ if (stderror) free (*stderror);
+ close (so_fd[0]);
+ close (se_fd[0]);
+ waitpid (pid, NULL, 0);
+ return -1;
+ }
+
+ if (FD_ISSET (so_fd[0], &rset2)) { /* something on stdout */
+ r = read (so_fd[0], buf, sizeof buf);
+ if (r == -1) {
+ perror ("read");
+ goto quit;
+ }
+ if (r == 0) { FD_CLR (so_fd[0], &rset); quit++; }
+
+ if (r > 0 && stdoutput) {
+ so_size += r;
+ p = realloc (*stdoutput, so_size);
+ if (p == NULL) {
+ perror ("realloc");
+ goto quit;
+ }
+ *stdoutput = p;
+ memcpy (*stdoutput + so_size - r, buf, r);
+ }
+ }
+
+ if (FD_ISSET (se_fd[0], &rset2)) { /* something on stderr */
+ r = read (se_fd[0], buf, sizeof buf);
+ if (r == -1) {
+ perror ("read");
+ goto quit;
+ }
+ if (r == 0) { FD_CLR (se_fd[0], &rset); quit++; }
+
+ if (r > 0 && stderror) {
+ se_size += r;
+ p = realloc (*stderror, se_size);
+ if (p == NULL) {
+ perror ("realloc");
+ goto quit;
+ }
+ *stderror = p;
+ memcpy (*stderror + se_size - r, buf, r);
+ }
+ }
+ }
+
+ close (so_fd[0]);
+ close (se_fd[0]);
+
+ /* Make sure the output buffers are \0-terminated. Also remove any
+ * trailing \n characters from the error buffer (not from stdout).
+ */
+ if (stdoutput) {
+ *stdoutput = realloc (*stdoutput, so_size+1);
+ if (*stdoutput == NULL) {
+ perror ("realloc");
+ *stdoutput = NULL;
+ } else
+ (*stdoutput)[so_size] = '\0';
+ }
+ if (stderror) {
+ *stderror = realloc (*stderror, se_size+1);
+ if (*stderror == NULL) {
+ perror ("realloc");
+ *stderror = NULL;
+ } else {
+ (*stderror)[se_size] = '\0';
+ se_size--;
+ while (se_size >= 0 && (*stderror)[se_size] == '\n')
+ (*stderror)[se_size--] = '\0';
+ }
+ }
+
+ /* Get the exit status of the command. */
+ waitpid (pid, &r, 0);
+
+ if (WIFEXITED (r)) {
+ return WEXITSTATUS (r);
+ } else
+ return -1;
+}
+
+/* Quote 'in' for the shell, and write max len-1 bytes to out. The
+ * result will be NUL-terminated, even if it is truncated.
+ *
+ * Returns number of bytes needed, so if result >= len then the buffer
+ * should have been longer.
+ *
+ * XXX This doesn't quote \n correctly (but is still safe).
+ */
+int
+shell_quote (char *out, int len, const char *in)
+{
+#define SAFE(c) (isalnum((c)) || \
+ (c) == '/' || (c) == '-' || (c) == '_' || (c) == '.')
+ int i, j;
+ int outlen = strlen (in);
+
+ /* Calculate how much output space this really needs. */
+ for (i = 0; in[i]; ++i)
+ if (!SAFE (in[i])) outlen++;
+
+ /* Now copy the string, but only up to len-1 bytes. */
+ for (i = 0, j = 0; in[i]; ++i) {
+ int is_safe = SAFE (in[i]);
+
+ /* Enough space left to write this character? */
+ if (j >= len-1 || (!is_safe && j >= len-2))
+ break;
+
+ if (!is_safe) out[j++] = '\\';
+ out[j++] = in[i];
+ }
+
+ out[j] = '\0';
+
+ return outlen;
}