Beispiel #1
0
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));
}
Beispiel #2
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;
}
Beispiel #3
0
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 */
}
Beispiel #4
0
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;
}
Beispiel #5
0
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;
}