summaryrefslogtreecommitdiff
path: root/pkgtools/pkg_install/files/lib/pexec.c
blob: 1358aedb91064f25a4c93df18b194cc83d0f6c00 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
#if HAVE_CONFIG_H
#include "config.h"
#endif
#include <nbcompat.h>
#if HAVE_SYS_WAIT_H
#include <sys/wait.h>
#endif
#if HAVE_ERR_H
#include <err.h>
#endif
#if HAVE_ERRNO_H
#include <errno.h>
#endif
#if HAVE_STDLIB_H
#include <stdlib.h>
#endif
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#if HAVE_STDIO_H
#include <stdio.h>
#endif

#include "lib.h"

/*
 * If the supplied callback is not NULL, then call it.
 */
static void call_callback(void (*callback)(void))
{
	if (callback != NULL) {
		callback();
	}
}

/*
 * create pipe, fork and exec file with arguments in argv
 * child takes stdin from pipe, set up fp for parent to
 * output to pipe, and return this information.
 */
pipe_to_system_t *pipe_to_system_begin(const char *file, char *const argv[],
	void (*cleanup_callback)(void))
{
	pipe_to_system_t *retval;

	retval = malloc(sizeof(pipe_to_system_t));
	if (retval == NULL) {
		call_callback(cleanup_callback);
		errx(2, "can't get pipe space");
	}

	retval->cleanup = cleanup_callback;

	if (pipe(retval->fds) == -1) {
		call_callback(retval->cleanup);
		errx(2, "cannot create pipe");
	}

	retval->pid = fork();
	if (retval->pid == -1) {
		call_callback(retval->cleanup);
		errx(2, "cannot fork process for %s", file);
	}

	if (retval->pid == 0) {		/* The child */
		if (retval->fds[0] != 0) {
			dup2(retval->fds[0], 0);
			close(retval->fds[0]);
		}
		close(retval->fds[1]);
		execvp(file, argv);
		warn("failed to execute %s command", file);
		_exit(2);
	}

	/* Meanwhile, back in the parent process ... */
	close(retval->fds[0]);
	retval->fp = fdopen(retval->fds[1], "w");
	if (retval->fp == NULL) {
		call_callback(retval->cleanup);
		errx(2, "fdopen failed");
	}
	return retval;
}

/*
 * close pipe and wait for child to exit.  on non-zero exit status,
 * call cleanup callback.  return exit status.
 */
int pipe_to_system_end(pipe_to_system_t *to_pipe)
{
	int status;
	int wait_ret;

	fclose(to_pipe->fp);
	do {
		wait_ret = waitpid(to_pipe->pid, &status, 0);
	} while (wait_ret == -1 && errno == EINTR);

	if (wait_ret < 0) {
		call_callback(to_pipe->cleanup);
		errx(2, "waitpid returned failure");
	}
	if (!WIFEXITED(status)) {
		call_callback(to_pipe->cleanup);
		errx(2, "waitpid: process terminated abnormally");
	}
	free(to_pipe);
	return WEXITSTATUS(status);
}