static void child_watch_callback( pid_t pid, gint status, gpointer data) { XferFilterProcess *self = XFER_FILTER_PROCESS(data); XferElement *elt = (XferElement *)self; XMsg *msg; char *errmsg = NULL; g_assert(pid == self->child_pid); self->child_pid = -1; /* it's gone now.. */ if (WIFEXITED(status)) { int exitcode = WEXITSTATUS(status); g_debug("%s: process exited with status %d", xfer_element_repr(elt), exitcode); if (exitcode != 0) { errmsg = g_strdup_printf("%s exited with status %d", self->argv[0], exitcode); } } else if (WIFSIGNALED(status)) { int signal = WTERMSIG(status); if (signal != SIGKILL || !self->child_killed) { errmsg = g_strdup_printf("%s died on signal %d", self->argv[0], signal); g_debug("%s: %s", xfer_element_repr(elt), errmsg); } } if (errmsg) { msg = xmsg_new(XFER_ELEMENT(self), XMSG_INFO, 0); msg->message = g_strdup("ERROR"); xfer_queue_message(XFER_ELEMENT(self)->xfer, msg); } else { msg = xmsg_new(XFER_ELEMENT(self), XMSG_INFO, 0); msg->message = g_strdup("SUCCESS"); xfer_queue_message(XFER_ELEMENT(self)->xfer, msg); } /* if this is an error exit, send an XMSG_ERROR and cancel */ if (!elt->cancelled) { if (errmsg) { msg = xmsg_new(XFER_ELEMENT(self), XMSG_ERROR, 0); msg->message = errmsg; xfer_queue_message(XFER_ELEMENT(self)->xfer, msg); xfer_cancel(elt->xfer); } else if (elt->cancel_on_success) { xfer_cancel(elt->xfer); } } /* this element is as good as cancelled already, so fall through to XMSG_DONE */ xfer_queue_message(XFER_ELEMENT(self)->xfer, xmsg_new(XFER_ELEMENT(self), XMSG_DONE, 0)); }
char * xmsg_repr( XMsg *msg) { if (!msg) return "(nil)"; /* better safe than sorry */ /* this just shows the "header" fields for now */ if (!msg->repr) { char *typ = NULL; switch (msg->type) { case XMSG_INFO: typ = "INFO"; break; case XMSG_ERROR: typ = "ERROR"; break; case XMSG_DONE: typ = "DONE"; break; case XMSG_CANCEL: typ = "CANCEL"; break; case XMSG_PART_DONE: typ = "PART_DONE"; break; case XMSG_READY: typ = "READY"; break; default: typ = "**UNKNOWN**"; break; } msg->repr = vstrallocf("<XMsg@%p type=XMSG_%s elt=%s version=%d>", msg, typ, xfer_element_repr(msg->elt), msg->version); } return msg->repr; }
static gboolean cancel_impl( XferElement *elt, gboolean expect_eof) { XferFilterProcess *self = (XferFilterProcess *)elt; /* chain up first */ XFER_ELEMENT_CLASS(parent_class)->cancel(elt, expect_eof); /* if the process is running as root, we can't do anything but wait until * we get an upstream EOF, or downstream does something to trigger a * SIGPIPE */ if (self->need_root) return expect_eof; /* avoid the risk of SIGPIPEs by not killing the process if it is already * expecting an EOF */ if (expect_eof) { return expect_eof; } /* and kill the process, if it's not already dead; this will likely send * SIGPIPE to anything upstream. */ if (self->child_pid != -1) { g_debug("%s: killing child process", xfer_element_repr(elt)); if (kill(self->child_pid, SIGKILL) < 0) { /* log but ignore */ g_debug("while killing child process: %s", strerror(errno)); return FALSE; /* downstream should not expect EOF */ } /* make sure we don't send an XMSG_ERROR about this */ self->child_killed = 1; } return TRUE; /* downstream should expect an EOF */ }
static int do_directtcp_connect( XferElementGlue *self, DirectTCPAddr *addrs) { XferElement *elt = XFER_ELEMENT(self); sockaddr_union addr; int sock; #ifdef WORKING_IPV6 char strsockaddr[INET6_ADDRSTRLEN + 20]; #else char strsockaddr[INET_ADDRSTRLEN + 20]; #endif if (!addrs) { g_debug("element-glue got no directtcp addresses to connect to!"); if (!elt->cancelled) { xfer_cancel_with_error(elt, "%s got no directtcp addresses to connect to", xfer_element_repr(elt)); } goto cancel_wait; } /* set up the sockaddr -- IPv4 only */ copy_sockaddr(&addr, addrs); str_sockaddr_r(&addr, strsockaddr, sizeof(strsockaddr)); if (strncmp(strsockaddr,"255.255.255.255:", 16) == 0) { char buffer[32770]; char *s; int size; char *data_host; int data_port; g_debug("do_directtcp_connect making indirect data connection to %s", strsockaddr); data_port = SU_GET_PORT(&addr); sock = stream_client(NULL, "localhost", data_port, STREAM_BUFSIZE, 0, NULL, 0); if (sock < 0) { xfer_cancel_with_error(elt, "stream_client(): %s", strerror(errno)); goto cancel_wait; } size = full_read(sock, buffer, 32768); if (size < 0 ) { xfer_cancel_with_error(elt, "failed to read from indirecttcp: %s", strerror(errno)); goto cancel_wait; } close(sock); buffer[size++] = ' '; buffer[size] = '\0'; if ((s = strchr(buffer, ':')) == NULL) { xfer_cancel_with_error(elt, "Failed to parse indirect data stream: %s", buffer); goto cancel_wait; } *s++ = '\0'; data_host = buffer; data_port = atoi(s); str_to_sockaddr(data_host, &addr); SU_SET_PORT(&addr, data_port); str_sockaddr_r(&addr, strsockaddr, sizeof(strsockaddr)); } sock = socket(SU_GET_FAMILY(&addr), SOCK_STREAM, 0); g_debug("do_directtcp_connect making data connection to %s", strsockaddr); if (sock < 0) { xfer_cancel_with_error(elt, "socket(): %s", strerror(errno)); goto cancel_wait; } if (connect(sock, (struct sockaddr *)&addr, SS_LEN(&addr)) < 0) { xfer_cancel_with_error(elt, "connect(): %s", strerror(errno)); close(sock); goto cancel_wait; } g_debug("do_directtcp_connect: connected to %s, fd %d", strsockaddr, sock); return sock; cancel_wait: wait_until_xfer_cancelled(elt->xfer); return -1; }
static gboolean start_impl( XferElement *elt) { char *tmpbuf; XferFilterProcess *self = (XferFilterProcess *)elt; char *cmd_str; char **argv; char *errmsg; char **env; int rfd, wfd; /* first build up a log message of what we're going to do, properly shell quoted */ argv = self->argv; cmd_str = g_shell_quote(*(argv++)); while (*argv) { char *qarg = g_shell_quote(*(argv++)); tmpbuf = g_strconcat(cmd_str, " ", qarg, NULL); g_free(cmd_str); cmd_str = tmpbuf; g_free(qarg); } g_debug("%s spawning: %s", xfer_element_repr(elt), cmd_str); rfd = xfer_element_swap_output_fd(elt->upstream, -1); wfd = xfer_element_swap_input_fd(elt->downstream, -1); /* now fork off the child and connect the pipes */ switch (self->child_pid = fork()) { case -1: error("cannot fork: %s", strerror(errno)); /* NOTREACHED */ case 0: /* child */ /* first, copy our fd's out of the stdio range */ while (rfd >= 0 && rfd <= STDERR_FILENO) rfd = dup(rfd); while (wfd >= 0 && wfd <= STDERR_FILENO) wfd = dup(wfd); /* set up stdin, stdout, and stderr, overwriting anything already open * on those fd's */ if (rfd > 0) dup2(rfd, STDIN_FILENO); if (wfd > 0) dup2(wfd, STDOUT_FILENO); dup2(self->pipe_err[1], STDERR_FILENO); /* and close everything else */ safe_fd(-1, 0); env = safe_env(); if (self->need_root && !become_root()) { errmsg = g_strdup_printf("could not become root: %s\n", strerror(errno)); full_write(STDERR_FILENO, errmsg, strlen(errmsg)); exit(1); } execve(self->argv[0], self->argv, env); free_env(env); errmsg = g_strdup_printf("exec of '%s' failed: %s\n", self->argv[0], strerror(errno)); full_write(STDERR_FILENO, errmsg, strlen(errmsg)); exit(1); default: /* parent */ break; } g_free(cmd_str); /* close the pipe fd's */ close(rfd); close(wfd); close(self->pipe_err[1]); /* watch for child death */ self->child_watch = new_child_watch_source(self->child_pid); g_source_set_callback(self->child_watch, (GSourceFunc)child_watch_callback, self, NULL); g_source_attach(self->child_watch, NULL); g_source_unref(self->child_watch); return TRUE; }