GCC Code Coverage Report


Directory: src/
File: src/process.c
Date: 2024-12-26 11:11:59
Exec Total Coverage
Lines: 41 84 48.8%
Functions: 5 8 62.5%
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 process_execute(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 process_execute_and_capture(const char *args[], const char *envp[],
97 struct process_output *result)
98 {
99 static const int flags = O_CLOEXEC;
100 int pipe_fds[2];
101 9180 int ret = 0;
102
103 9180 ret = pipe2(pipe_fds, flags);
104
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9180 times.
9180 if (ret < 0) {
105 log_errno("pipe2");
106 return -1;
107 }
108
109 9180 pid_t child_pid = fork();
110
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9180 times.
9180 if (child_pid < 0) {
111 log_errno("fork");
112 goto close_pipe;
113 }
114
115
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9180 times.
9180 if (!child_pid)
116 exit(redirect_and_exec_child(pipe_fds, args, envp));
117
118 9180 file_close(pipe_fds[1]);
119
120 9180 ret = file_read(pipe_fds[0], &result->data, &result->data_size);
121
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9180 times.
9180 if (ret < 0)
122 goto close_pipe;
123
124 9180 ret = wait_for_child(child_pid, &result->ec);
125
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9180 times.
9180 if (ret < 0)
126 goto free_data;
127
128 9180 goto close_pipe;
129
130 free_data:
131 free(result->data);
132
133 9180 close_pipe:
134 9180 file_close(pipe_fds[0]);
135 /* No errno checking here, we might've already closed the write end. */
136 9180 close(pipe_fds[1]);
137
138 9180 return ret;
139 }
140
141 18360 int process_output_create(struct process_output **_output)
142 {
143 18360 struct process_output *output = calloc(1, sizeof(struct process_output));
144
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18360 times.
18360 if (!output) {
145 log_errno("calloc");
146 return -1;
147 }
148
149 18360 output->ec = 0;
150 18360 output->data = NULL;
151 18360 output->data_size = 0;
152
153 18360 *_output = output;
154 18360 return 0;
155 }
156
157 18360 void process_output_destroy(struct process_output *output)
158 {
159 18360 free(output->data);
160 18360 free(output);
161 18360 }
162
163 9180 void process_output_dump(const struct process_output *output)
164 {
165
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 9180 times.
9180 log("Process exit code: %d\n", output->ec);
166
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 9180 times.
9180 log("Process output: %zu bytes\n", output->data_size);
167 9180 }
168