/*
  sends a command to the dropbox server
  returns an hash of the return values

  in theory, this should disconnection errors
  but it doesn't matter right now, any error is a sufficient
  condition to disconnect
*/
static GHashTable *
send_command_to_db(GIOChannel *chan, const gchar *command_name,
		   GHashTable *args, GError **err) {
  GError *tmp_error = NULL;
  GIOStatus iostat;
  gsize bytes_trans;
  gchar *line;

  g_assert(chan != NULL);
  g_assert(command_name != NULL);
  

#define WRITE_OR_DIE_SANI(s,l) {					\
    gchar *sani_s;							\
    sani_s = dropbox_client_util_sanitize(s);				\
    iostat = g_io_channel_write_chars(chan, sani_s,l, &bytes_trans,	\
				      &tmp_error);			\
    g_free(sani_s);							\
    if (iostat == G_IO_STATUS_ERROR ||					\
	iostat == G_IO_STATUS_AGAIN) {					\
      if (tmp_error != NULL) {						\
	g_propagate_error(err, tmp_error);				\
      }									\
      return NULL;							\
    }									\
  }
  
#define WRITE_OR_DIE(s,l) {						\
    iostat = g_io_channel_write_chars(chan, s,l, &bytes_trans,		\
				      &tmp_error);			\
    if (iostat == G_IO_STATUS_ERROR ||					\
	iostat == G_IO_STATUS_AGAIN) {					\
      if (tmp_error != NULL) {						\
	g_propagate_error(err, tmp_error);				\
      }									\
      return NULL;							\
    }									\
  }
  
  /* send command to server */
  WRITE_OR_DIE_SANI(command_name, -1);
  WRITE_OR_DIE("\n", -1);

  if (args != NULL) {
    GList *keys, *li;

    /* oh god */
    keys = glib_check_version(2, 14, 0)
      ? my_g_hash_table_get_keys(args)
      : g_hash_table_get_keys(args);

    for (li = keys; li != NULL; li = g_list_next(li)) {
      int i;
      gchar **value;
      
      WRITE_OR_DIE_SANI((gchar *) li->data, -1);
      
      value = g_hash_table_lookup(args, li->data);
      for (i = 0; value[i] != NULL; i++) {
	WRITE_OR_DIE("\t", -1);
	WRITE_OR_DIE_SANI(value[i], -1);
      }
      WRITE_OR_DIE("\n", -1);
    }

    g_list_free(keys);
  }

  WRITE_OR_DIE("done\n", -1);

#undef WRITE_OR_DIE
#undef WRITE_OR_DIE_SANI

  g_io_channel_flush(chan, &tmp_error);
  if (tmp_error != NULL) {
    g_propagate_error(err, tmp_error);
    return NULL;
  }

  /* now we have to read the data */
  iostat = g_io_channel_read_line(chan, &line, NULL,
				  NULL, &tmp_error);
  if (iostat == G_IO_STATUS_ERROR) {
    g_assert(line == NULL);
    g_propagate_error(err, tmp_error);
    return NULL;
  }
  else if (iostat == G_IO_STATUS_AGAIN) {
    g_assert(line == NULL);
    g_set_error(err,
		g_quark_from_static_string("dropbox command connection timed out"),
		0,
		"dropbox command connection timed out");
    return NULL;
  }
  else if (iostat == G_IO_STATUS_EOF) {
    g_assert(line == NULL);
    g_set_error(err,
		g_quark_from_static_string("dropbox command connection closed"),
		0,
		"dropbox command connection closed");
    return NULL;
  }

  /* if the response was okay */
  if (strncmp(line, "ok\n", 3) == 0) {
    GHashTable *return_table = 
      g_hash_table_new_full((GHashFunc) g_str_hash,
			    (GEqualFunc) g_str_equal,
			    (GDestroyNotify) g_free,
			    (GDestroyNotify) g_strfreev);
    
    g_free(line);
    line = NULL;

    receive_args_until_done(chan, return_table, &tmp_error);
    if (tmp_error != NULL) {
      g_hash_table_destroy(return_table);
      g_propagate_error(err, tmp_error);
      return NULL;
    }
      
    return return_table;
  }
  /* otherwise */
  else {
    /* read errors off until we get done */
    do {
      g_free(line);
      line = NULL;
      
      /* clear string */
      iostat = g_io_channel_read_line(chan, &line, NULL,
				      NULL, &tmp_error);
      if (iostat == G_IO_STATUS_ERROR) {
	g_assert(line == NULL);
	g_propagate_error(err, tmp_error);
	return NULL;
      }
      else if (iostat == G_IO_STATUS_AGAIN) {
	g_assert(line == NULL);
	g_set_error(err,
		    g_quark_from_static_string("dropbox command connection timed out"),
		    0,
		    "dropbox command connection timed out");
	return NULL;
	
      }
      else if (iostat == G_IO_STATUS_EOF) {
	g_assert(line == NULL);
	g_set_error(err,
		    g_quark_from_static_string("dropbox command connection closed"),
		    0,
		    "dropbox command connection closed");
	return NULL;
      }

      /* we got our line */
    } while (strncmp(line, "done\n", 5) != 0);

    g_free(line);
    return NULL;
  }
}
Пример #2
0
static pid_t
run_child(struct coll_entry *colle, struct tag_pgrp *active, int quiet_mode,
	  int *failcnt, int fmt_print, FILE * logfile)
{
	ssize_t errlen;
	int cpid;
	int c_stdout = -1;	/* child's stdout, stderr */
	int capturing = 0;	/* output is going to a file instead of stdout */
	char *c_cmdline;
	static long cmdno = 0;
	int errpipe[2];		/* way to communicate to parent that the tag  */
	char errbuf[1024];	/* didn't actually start */

	/* Try to open the file that will be stdout for the test */
	if (test_out_dir) {
		capturing = 1;
		do {
			sprintf(active->output, "%s/%s.%ld",
				test_out_dir, colle->name, cmdno++);
			c_stdout =
			    open(active->output,
				 O_CREAT | O_RDWR | O_EXCL | O_SYNC, 0666);
		} while (c_stdout < 0 && errno == EEXIST);
		if (c_stdout < 0) {
			fprintf(stderr,
				"pan(%s): open of stdout file failed (tag %s).  errno: %d  %s\n  file: %s\n",
				panname, colle->name, errno, strerror(errno),
				active->output);
			return -1;
		}
	}

	/* get the tag's command line arguments ready.  subst_pcnt_f() uses a
	 * static counter, that's why we do it here instead of after we fork.
	 */
	if (colle->pcnt_f) {
		c_cmdline = subst_pcnt_f(colle);
	} else {
		c_cmdline = colle->cmdline;
	}

	if (pipe(errpipe) < 0) {
		fprintf(stderr, "pan(%s): pipe() failed. errno:%d %s\n",
			panname, errno, strerror(errno));
		if (capturing) {
			close(c_stdout);
			unlink(active->output);
		}
		return -1;
	}

	time(&active->mystime);
	active->cmd = colle;

	if (!test_out_dir)
		if (!quiet_mode)
			write_test_start(active);

	if ((cpid = fork()) == -1) {
		fprintf(stderr,
			"pan(%s): fork failed (tag %s).  errno:%d  %s\n",
			panname, colle->name, errno, strerror(errno));
		if (capturing) {
			unlink(active->output);
			close(c_stdout);
		}
		close(errpipe[0]);
		close(errpipe[1]);
		return -1;
	} else if (cpid == 0) {
		/* child */

		fclose(zoofile);
		close(errpipe[0]);
		fcntl(errpipe[1], F_SETFD, 1);	/* close the pipe if we succeed */
		setpgrp();

		umask(0);

#define WRITE_OR_DIE(fd, buf, buflen) do {				\
	if (write((fd), (buf), (buflen)) != (buflen)) {			\
		err(1, "failed to write out %zd bytes at line %d",	\
		    buflen, __LINE__);					\
	}								\
} while(0)

		/* if we're putting output into a buffer file, we need to do the
		 * redirection now.  If we fail
		 */
		if (capturing) {
			if (dup2(c_stdout, fileno(stdout)) == -1) {
				errlen =
				    sprintf(errbuf,
					    "pan(%s): couldn't redirect stdout for tag %s.  errno:%d  %s",
					    panname, colle->name, errno,
					    strerror(errno));
				WRITE_OR_DIE(errpipe[1], &errlen,
					     sizeof(errlen));
				WRITE_OR_DIE(errpipe[1], errbuf, errlen);
				exit(2);
			}
			if (dup2(c_stdout, fileno(stderr)) == -1) {
				errlen =
				    sprintf(errbuf,
					    "pan(%s): couldn't redirect stderr for tag %s.  errno:%d  %s",
					    panname, colle->name, errno,
					    strerror(errno));
				WRITE_OR_DIE(errpipe[1], &errlen,
					     sizeof(errlen));
				WRITE_OR_DIE(errpipe[1], errbuf, errlen);
				exit(2);
			}
		} else {	/* stderr still needs to be redirected */
			if (dup2(fileno(stdout), fileno(stderr)) == -1) {
				errlen =
				    sprintf(errbuf,
					    "pan(%s): couldn't redirect stderr for tag %s.  errno:%d  %s",
					    panname, colle->name, errno,
					    strerror(errno));
				WRITE_OR_DIE(errpipe[1], &errlen,
					     sizeof(errlen));
				WRITE_OR_DIE(errpipe[1], errbuf, errlen);
				exit(2);
			}
		}
		/* If there are any shell-type characters in the cmdline
		 * such as '>', '<', '$', '|', etc, then we exec a shell and
		 * run the cmd under a shell.
		 *
		 * Otherwise, break the cmdline at white space and exec the
		 * cmd directly.
		 */
		if (strpbrk(c_cmdline, "\"';|<>$\\")) {
			execlp("sh", "sh", "-c", c_cmdline, NULL);
			errlen = sprintf(errbuf,
					 "pan(%s): execlp of '%s' (tag %s) failed.  errno:%d %s",
					 panname, c_cmdline, colle->name, errno,
					 strerror(errno));
		} else {
			char **arg_v;

			arg_v = (char **)splitstr(c_cmdline, NULL, NULL);

			execvp(arg_v[0], arg_v);
			errlen = sprintf(errbuf,
					 "pan(%s): execvp of '%s' (tag %s) failed.  errno:%d  %s",
					 panname, arg_v[0], colle->name, errno,
					 strerror(errno));
		}
		WRITE_OR_DIE(errpipe[1], &errlen, sizeof(errlen));
		WRITE_OR_DIE(errpipe[1], errbuf, errlen);
		exit(errno);
	}

	/* parent */

	/* subst_pcnt_f() allocates the command line dynamically
	 * free the malloc to prevent a memory leak
	 */
	if (colle->pcnt_f)
		free(c_cmdline);

	close(errpipe[1]);

	/* if the child couldn't go through with the exec,
	 * clean up the mess, note it, and move on
	 */
	if (read(errpipe[0], &errlen, sizeof(errlen))) {
		int status;
		time_t end_time;
		int termid;
		char *termtype;
		struct tms notime = { 0, 0, 0, 0 };

		if (read(errpipe[0], errbuf, errlen) < 0)
			fprintf(stderr, "Failed to read from errpipe[0]\n");
		close(errpipe[0]);
		errbuf[errlen] = '\0';
		/* fprintf(stderr, "%s", errbuf); */
		waitpid(cpid, &status, 0);
		if (WIFSIGNALED(status)) {
			termid = WTERMSIG(status);
			termtype = "signaled";
		} else if (WIFEXITED(status)) {
			termid = WEXITSTATUS(status);
			termtype = "exited";
		} else if (WIFSTOPPED(status)) {
			termid = WSTOPSIG(status);
			termtype = "stopped";
		} else {
			termid = 0;
			termtype = "unknown";
		}
		time(&end_time);
		if (logfile != NULL) {
			if (!fmt_print) {
				fprintf(logfile,
					"tag=%s stime=%d dur=%d exit=%s "
					"stat=%d core=%s cu=%d cs=%d\n",
					colle->name, (int)(active->mystime),
					(int)(end_time - active->mystime),
					termtype, termid,
					(status & 0200) ? "yes" : "no", 0, 0);
			} else {
				if (termid != 0)
					++ * failcnt;

				fprintf(logfile, "%-30.30s %-10.10s %-5d\n",
					colle->name,
					((termid != 0) ? "FAIL" : "PASS"),
					termid);
			}
			fflush(logfile);
		}

		if (!quiet_mode) {
			//write_test_start(active, errbuf);
			write_test_end(active, errbuf, end_time, termtype,
				       status, termid, &notime, &notime);
		}
		if (capturing) {
			close(c_stdout);
			unlink(active->output);
		}
		return -1;
	}

	close(errpipe[0]);
	if (capturing)
		close(c_stdout);

	active->pgrp = cpid;
	active->stopping = 0;

	if (zoo_mark_cmdline(zoofile, cpid, colle->name, colle->cmdline)) {
		fprintf(stderr, "pan(%s): %s\n", panname, zoo_error);
		exit(1);
	}

	if (Debug & Dstartup)
		fprintf(stderr, "started %s cpid=%d at %s",
			colle->name, cpid, ctime(&active->mystime));

	if (Debug & Dstart) {
		fprintf(stderr, "Executing test = %s as %s", colle->name,
			colle->cmdline);
		if (capturing)
			fprintf(stderr, "with output file = %s\n",
				active->output);
		else
			fprintf(stderr, "\n");
	}

	return cpid;
}