Directory: | src/ |
---|---|
File: | src/process.c |
Date: | 2024-04-25 03:45:42 |
Exec | Total | Coverage | |
---|---|---|---|
Lines: | 41 | 84 | 48.8% |
Branches: | 12 | 52 | 23.1% |
Line | Branch | Exec | Source |
---|---|---|---|
1 | /* | ||
2 | * Copyright (c) 2022 Egor Tensin <egor@tensin.name> | ||
3 | * This file is part of the "cimple" project. | ||
4 | * For details, see https://github.com/egor-tensin/cimple. | ||
5 | * Distributed under the MIT License. | ||
6 | */ | ||
7 | |||
8 | #include "process.h" | ||
9 | #include "file.h" | ||
10 | #include "log.h" | ||
11 | |||
12 | #include <fcntl.h> | ||
13 | #include <stdlib.h> | ||
14 | #include <sys/wait.h> | ||
15 | #include <unistd.h> | ||
16 | |||
17 | ✗ | static int exec_child(const char *args[], const char *envp[]) | |
18 | { | ||
19 | static const char *default_envp[] = {NULL}; | ||
20 | |||
21 | ✗ | if (!envp) | |
22 | ✗ | envp = default_envp; | |
23 | |||
24 | ✗ | int ret = execvpe(args[0], (char *const *)args, (char *const *)envp); | |
25 | ✗ | if (ret < 0) { | |
26 | ✗ | log_errno("execvpe"); | |
27 | ✗ | return ret; | |
28 | } | ||
29 | |||
30 | ✗ | return ret; | |
31 | } | ||
32 | |||
33 | 9180 | static int wait_for_child(pid_t pid, int *ec) | |
34 | { | ||
35 | int status; | ||
36 | |||
37 | 9180 | pid_t ret = waitpid(pid, &status, __WNOTHREAD); | |
38 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9180 times.
|
9180 | if (ret < 0) { |
39 | ✗ | log_errno("waitpid"); | |
40 | ✗ | return ret; | |
41 | } | ||
42 | |||
43 | /* The child process reports the lowest 8 bits of its exit code, which | ||
44 | * are treated as an unsigned integer on Linux. | ||
45 | * | ||
46 | * If it was killed by a signal, indicate that by negating the signal | ||
47 | * number. */ | ||
48 | |||
49 |
2/2✓ Branch 0 taken 9144 times.
✓ Branch 1 taken 36 times.
|
9180 | if (WIFEXITED(status)) { |
50 | 9144 | *ec = WEXITSTATUS(status); | |
51 |
1/2✓ Branch 0 taken 36 times.
✗ Branch 1 not taken.
|
36 | } else if (WIFSIGNALED(status)) { |
52 | 36 | *ec = -WTERMSIG(status); | |
53 | } else { | ||
54 | ✗ | log_err("This shouldn't happen: %d\n", status); | |
55 | ✗ | *ec = 1; | |
56 | } | ||
57 | |||
58 | 9180 | return 0; | |
59 | } | ||
60 | |||
61 | ✗ | int proc_spawn(const char *args[], const char *envp[], int *ec) | |
62 | { | ||
63 | ✗ | pid_t child_pid = fork(); | |
64 | ✗ | if (child_pid < 0) { | |
65 | ✗ | log_errno("fork"); | |
66 | ✗ | return child_pid; | |
67 | } | ||
68 | |||
69 | ✗ | if (!child_pid) | |
70 | ✗ | exit(exec_child(args, envp)); | |
71 | |||
72 | ✗ | return wait_for_child(child_pid, ec); | |
73 | } | ||
74 | |||
75 | ✗ | static int redirect_and_exec_child(int pipe_fds[2], const char *args[], const char *envp[]) | |
76 | { | ||
77 | ✗ | int ret = 0; | |
78 | |||
79 | ✗ | file_close(pipe_fds[0]); | |
80 | |||
81 | ✗ | ret = dup2(pipe_fds[1], STDOUT_FILENO); | |
82 | ✗ | if (ret < 0) { | |
83 | ✗ | log_errno("dup2"); | |
84 | ✗ | return ret; | |
85 | } | ||
86 | |||
87 | ✗ | ret = dup2(pipe_fds[1], STDERR_FILENO); | |
88 | ✗ | if (ret < 0) { | |
89 | ✗ | log_errno("dup2"); | |
90 | ✗ | return ret; | |
91 | } | ||
92 | |||
93 | ✗ | return exec_child(args, envp); | |
94 | } | ||
95 | |||
96 | 9180 | int proc_capture(const char *args[], const char *envp[], struct proc_output *result) | |
97 | { | ||
98 | static const int flags = O_CLOEXEC; | ||
99 | int pipe_fds[2]; | ||
100 | 9180 | int ret = 0; | |
101 | |||
102 | 9180 | ret = pipe2(pipe_fds, flags); | |
103 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9180 times.
|
9180 | if (ret < 0) { |
104 | ✗ | log_errno("pipe2"); | |
105 | ✗ | return -1; | |
106 | } | ||
107 | |||
108 | 9180 | pid_t child_pid = fork(); | |
109 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9180 times.
|
9180 | if (child_pid < 0) { |
110 | ✗ | log_errno("fork"); | |
111 | ✗ | goto close_pipe; | |
112 | } | ||
113 | |||
114 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9180 times.
|
9180 | if (!child_pid) |
115 | ✗ | exit(redirect_and_exec_child(pipe_fds, args, envp)); | |
116 | |||
117 | 9180 | file_close(pipe_fds[1]); | |
118 | |||
119 | 9180 | ret = file_read(pipe_fds[0], &result->data, &result->data_size); | |
120 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9180 times.
|
9180 | if (ret < 0) |
121 | ✗ | goto close_pipe; | |
122 | |||
123 | 9180 | ret = wait_for_child(child_pid, &result->ec); | |
124 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9180 times.
|
9180 | if (ret < 0) |
125 | ✗ | goto free_data; | |
126 | |||
127 | 9180 | goto close_pipe; | |
128 | |||
129 | ✗ | free_data: | |
130 | ✗ | free(result->data); | |
131 | |||
132 | 9180 | close_pipe: | |
133 | 9180 | file_close(pipe_fds[0]); | |
134 | /* No errno checking here, we might've already closed the write end. */ | ||
135 | 9180 | close(pipe_fds[1]); | |
136 | |||
137 | 9180 | return ret; | |
138 | } | ||
139 | |||
140 | 18360 | int proc_output_create(struct proc_output **_output) | |
141 | { | ||
142 | 18360 | struct proc_output *output = calloc(1, sizeof(struct proc_output)); | |
143 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 18360 times.
|
18360 | if (!output) { |
144 | ✗ | log_errno("calloc"); | |
145 | ✗ | return -1; | |
146 | } | ||
147 | |||
148 | 18360 | output->ec = 0; | |
149 | 18360 | output->data = NULL; | |
150 | 18360 | output->data_size = 0; | |
151 | |||
152 | 18360 | *_output = output; | |
153 | 18360 | return 0; | |
154 | } | ||
155 | |||
156 | 18360 | void proc_output_destroy(struct proc_output *output) | |
157 | { | ||
158 | 18360 | free(output->data); | |
159 | 18360 | free(output); | |
160 | 18360 | } | |
161 | |||
162 | 9180 | void proc_output_dump(const struct proc_output *output) | |
163 | { | ||
164 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 9180 times.
|
9180 | log("Process exit code: %d\n", output->ec); |
165 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 9180 times.
|
9180 | log("Process output: %zu bytes\n", output->data_size); |
166 | 9180 | } | |
167 |