Update clang format
[c2run.git] / c2run.c
blob89a26196335ab7d927cbcb1d06e8e3ae584d3473
1 /* SPDX-License-Identifier: GPL-2.0
3 * Copyright (C) 2021 Julien Rische <jrische@laposte.net>.
4 * Copyright (C) 2018-2021 Jason A. Donenfeld <Jason@zx2c4.com>.
5 * All Rights Reserved.
6 */
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <unistd.h>
12 #include <errno.h>
13 #include <sys/types.h>
14 #include <sys/wait.h>
15 #include <sys/mman.h>
16 #include <fcntl.h>
17 #include <stdbool.h>
19 typedef const char *err_t;
21 const char *header = "#include <stddef.h>\n"
22 "#include <stdint.h>\n"
23 "#include <inttypes.h>\n"
24 "#include <stdbool.h>\n"
25 "#include <string.h>\n"
26 "#include <assert.h>\n"
27 "#include <errno.h>\n"
28 "#include <unistd.h>\n"
29 "#include <pthread.h>\n"
30 "#include <limits.h>\n"
31 "#include <time.h>\n"
32 "#include <fcntl.h>\n"
33 "#include <stdio.h>\n"
34 "#include <stdlib.h>\n"
35 "int main(int argc, char *argv[], char *envp[]) {\n"
36 "(void) argc;\n"
37 "(void) argv;\n"
38 "(void) envp;\n";
40 void exec_cc(int pipe, char *output_path, size_t argc, char *argv[])
42 size_t cc_argc;
43 char **cc_argv;
44 char *cc = getenv("CC") ?: "gcc";
46 if (dup2(pipe, 0) < 0) {
47 perror("Error: unable to duplicate pipe fd");
48 _exit(1);
51 cc_argc = argc + 6;
52 cc_argv = malloc(cc_argc * sizeof(*cc_argv));
53 if (!cc_argv) {
54 perror("Error: cannot allocation compiler argument list");
55 _exit(1);
58 memcpy(cc_argv, (char *[]){cc, "-xc", "-o", output_path}, 4 * sizeof(char *));
59 memcpy(cc_argv + 4, argv, argc * sizeof(*argv));
60 memcpy(cc_argv + (cc_argc - 2), (char *[]){"-", NULL}, 2 * sizeof(char *));
62 execvp(cc, cc_argv);
63 free(cc_argv);
64 _exit(1);
67 err_t pipe_source(int pipe, const char *src_path, int input)
69 ssize_t len;
71 if (src_path) {
72 char beginning[2];
74 len = read(input, beginning, 2);
75 if (len < 0)
76 return "Error: unable to read from input file";
77 else if (len == 2 && beginning[0] == '#' && beginning[1] == '!')
78 len = write(pipe, "//", 2);
79 else if (len > 0)
80 len = write(pipe, beginning, (size_t)len);
81 if (len < 0)
82 return "Error: unable to write input preamble";
83 } else {
84 len = write(pipe, header, strlen(header));
85 if (len < 0)
86 return "Error: unable to write input header";
88 if (splice(input, NULL, pipe, NULL, 0x7fffffff, 0) < 0)
89 return "Error: unable to splice input to compiler child";
90 if (!src_path) {
91 len = write(pipe, "}", 1);
92 if (len < 0)
93 return "Error: unable to write input footer";
95 close(pipe);
97 return 0;
100 int main(int argc, char *argv[], char *envp[])
102 err_t err;
103 int ret = 0;
104 size_t cc_argc = (size_t)argc - 1;
105 char **cc_argv, **esc_argv = (char *[]){argv[0], NULL};
107 int fd, input = STDIN_FILENO, pipes[2] = {0};
108 pid_t cc_pid;
109 char *arg, *output_path, *src_path = NULL;
111 cc_argv = argv + 1;
112 for (size_t i = 1; arg = argv[i], i < (size_t)argc; ++i) {
113 bool cc_param = arg[0] == '-';
114 if (!cc_param)
115 src_path = arg;
116 if (!cc_param || strncmp("-", arg, 2) == 0) {
117 cc_argc = i - 1;
118 esc_argv = argv + i;
119 break;
123 if (src_path) {
124 input = open(src_path, O_RDONLY);
125 if (input < 0) {
126 perror("Error: unable to open input file");
127 ret = 1;
128 goto end;
132 if (pipe(pipes) < 0) {
133 perror("Error: unable to open filter pipe");
134 ret = 1;
135 goto end;
138 fd = memfd_create("c2run", 0);
139 if (fd < 0) {
140 perror("Error: unable to create memfd");
141 ret = 1;
142 goto end;
144 if (asprintf(&output_path, "/proc/self/fd/%d", fd) < 0) {
145 perror("Error: unable to allocate memory for fd string");
146 ret = 1;
147 goto end;
150 cc_pid = fork();
151 if (cc_pid < 0) {
152 perror("Error: unable to fork for compiler");
153 ret = 1;
154 goto end;
157 if (cc_pid == 0) {
158 close(input);
159 close(pipes[1]);
160 exec_cc(pipes[0], output_path, cc_argc, cc_argv);
163 close(pipes[0]);
164 err = pipe_source(pipes[1], src_path, input);
165 if (err) {
166 perror(err);
167 ret = 1;
168 goto end;
172 int status;
173 if (waitpid(cc_pid, &status, 0) != cc_pid || (!WIFEXITED(status) || WEXITSTATUS(status))) {
174 fprintf(stderr, "Error: compiler process did not complete successfully\n");
175 ret = 1;
176 goto end;
180 if (fexecve(fd, esc_argv, envp) < 0) {
181 perror("Error: could not execute compiled program");
182 ret = 1;
183 goto end;
186 end:
187 close(input);
188 close(pipes[0]);
189 close(pipes[1]);
190 return ret;