/* 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; } }
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, ¬ime, ¬ime); } 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; }