summaryrefslogtreecommitdiff
path: root/process-posix.c
blob: f35017f32be3313e1714760c8cab328d4a4577cd (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
#include "process.h"

#include <fcntl.h>
#include <sys/wait.h>
#include <signal.h>

struct Process {
	pid_t pid;
	int pipe;
	char error[64];
};

bool process_run(Process *proc, char const *command) {
	memset(proc, 0, sizeof *proc);

	bool success = false;
	int pipefd[2];
	if (pipe(pipefd) == 0) {
		pid_t pid = fork();
		if (pid == 0) {
			// child process
			// put child in its own group. it will be in this group with all of its descendents,
			// so by killing everything in the group, we kill all the descendents of this process.
			// if we didn't do this, we would just be killing the sh process in process_kill.
			setpgid(0, 0);
			// send stdout and stderr to pipe
			dup2(pipefd[1], STDOUT_FILENO);
			dup2(pipefd[1], STDERR_FILENO);
			close(pipefd[0]);
			close(pipefd[1]);
			char *program = "/bin/sh";
			char *argv[] = {program, "-c", (char *)command, NULL};
			if (execv(program, argv) == -1) {
				dprintf(STDERR_FILENO, "%s: %s\n", program, strerror(errno));
				exit(127);
			}
		} else if (pid > 0) {
			// parent process
			close(pipefd[1]);
			fcntl(pipefd[0], F_SETFL, fcntl(pipefd[0], F_GETFL) | O_NONBLOCK); // set pipe to non-blocking
			proc->pid = pid;
			proc->pipe = pipefd[0];
			success = true;
		}
	} else {
		strbuf_printf(proc->error, "%s", strerror(errno));
	}
	return success;
}

char const *process_geterr(Process *p) {
	return *p->error ? p->error : NULL;
}

long long process_read(Process *proc, char *data, size_t size) {
	assert(proc->pipe);
	ssize_t bytes_read = read(proc->pipe, data, size);
	if (bytes_read >= 0) {
		return (long long)bytes_read;
	} else if (errno == EAGAIN) {
		return -1;
	} else {
		strbuf_printf(proc->error, "%s", strerror(errno));
		return -2;
	}

}

void process_kill(Process *proc) {
	kill(-proc->pid, SIGKILL); // kill everything in process group
	// get rid of zombie process
	waitpid(proc->pid, NULL, 0);
	close(proc->pipe);
	proc->pid = 0;
	proc->pipe = 0;
}

int process_check_status(Process *proc, char *message, size_t message_size) {
	int wait_status = 0;
	int ret = waitpid(proc->pid, &wait_status, WNOHANG);
	if (ret == 0) {
		// process still running
		return 0;
	} else if (ret > 0) {
		if (WIFEXITED(wait_status)) {
			close(proc->pipe); proc->pipe = 0;
			int code = WEXITSTATUS(wait_status);
			if (code == 0) {
				str_printf(message, message_size, "exited successfully");
				return +1;
			} else {
				str_printf(message, message_size, "exited with code %d", code);
				return -1;
			}
		} else if (WIFSIGNALED(wait_status)) {
			close(proc->pipe);
			str_printf(message, message_size, "terminated by signal %d", WTERMSIG(wait_status));
			return -1;
		}
		return 0;
	} else {
		// this process is gone or something?
		close(proc->pipe); proc->pipe = 0;
		str_printf(message, message_size, "process ended unexpectedly");
		return -1;
	}
}