GCC Code Coverage Report


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