Line data Source code
1 : /* C++ modules. Experimental!
2 : Copyright (C) 2017-2026 Free Software Foundation, Inc.
3 : Written by Nathan Sidwell <nathan@acm.org> while at FaceBook
4 :
5 : This file is part of GCC.
6 :
7 : GCC is free software; you can redistribute it and/or modify it
8 : under the terms of the GNU General Public License as published by
9 : the Free Software Foundation; either version 3, or (at your option)
10 : any later version.
11 :
12 : GCC is distributed in the hope that it will be useful, but
13 : WITHOUT ANY WARRANTY; without even the implied warranty of
14 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 : General Public License for more details.
16 :
17 : You should have received a copy of the GNU General Public License
18 : along with GCC; see the file COPYING3. If not see
19 : <http://www.gnu.org/licenses/>. */
20 :
21 : #include "config.h"
22 : #if defined (__unix__)
23 : // Solaris11's socket header used bcopy, which we poison. cody.hh
24 : // will include it later under the above check
25 : #include <sys/socket.h>
26 : #endif
27 : #define INCLUDE_STRING
28 : #define INCLUDE_VECTOR
29 : #define INCLUDE_MAP
30 : #include "system.h"
31 : #include "libiberty.h"
32 :
33 : #include "line-map.h"
34 : #include "rich-location.h"
35 : #include "diagnostic-core.h"
36 : #include "mapper-client.h"
37 : #include "intl.h"
38 : #include "mkdeps.h"
39 :
40 : #include "../../c++tools/resolver.h"
41 :
42 : #if !HOST_HAS_O_CLOEXEC
43 : #define O_CLOEXEC 0
44 : #endif
45 :
46 9 : module_client::module_client (pex_obj *p, int fd_from, int fd_to)
47 9 : : Client (fd_from, fd_to), pex (p)
48 : {
49 9 : }
50 :
51 : static module_client *
52 12 : spawn_mapper_program (char const **errmsg, std::string &name,
53 : char const *full_program_name)
54 : {
55 : // Split mapper argument into parameters.
56 12 : char** original_argv = buildargv (name.c_str () + 1);
57 12 : int arg_no = countargv (original_argv);
58 12 : char **argv = new char *[arg_no + 1];
59 36 : for (int i = 0; i < arg_no; i++)
60 24 : argv[i] = original_argv[i];
61 :
62 : /* @name means look in the compiler's install dir. */
63 12 : if (arg_no && argv[0][0] == '@')
64 9 : argv[0] = argv[0] + 1;
65 : else
66 : full_program_name = nullptr;
67 12 : argv[arg_no] = nullptr;
68 :
69 12 : auto *pex = pex_init (PEX_USE_PIPES, progname, NULL);
70 12 : FILE *to = pex_input_pipe (pex, false);
71 12 : name = argv[0];
72 12 : if (!to)
73 0 : *errmsg = "connecting input";
74 : else
75 : {
76 12 : int flags = PEX_SEARCH;
77 :
78 12 : if (full_program_name)
79 : {
80 : /* Prepend the invoking path, if the mapper is a simple
81 : file name. */
82 9 : size_t dir_len = progname - full_program_name;
83 9 : std::string argv0;
84 9 : argv0.reserve (dir_len + name.size ());
85 9 : argv0.append (full_program_name, dir_len).append (name);
86 9 : name = std::move (argv0);
87 9 : argv[0] = const_cast <char *> (name.c_str ());
88 9 : flags = 0;
89 9 : }
90 12 : int err;
91 12 : *errmsg = pex_run (pex, flags, argv[0], argv, NULL, NULL, &err);
92 : }
93 12 : delete[] argv;
94 12 : freeargv (original_argv);
95 :
96 12 : int fd_from = -1, fd_to = -1;
97 12 : if (!*errmsg)
98 : {
99 9 : FILE *from = pex_read_output (pex, false);
100 9 : if (from && (fd_to = dup (fileno (to))) >= 0)
101 9 : fd_from = fileno (from);
102 : else
103 0 : *errmsg = "connecting output";
104 9 : fclose (to);
105 : }
106 :
107 12 : if (*errmsg)
108 : {
109 3 : pex_free (pex);
110 3 : return nullptr;
111 : }
112 :
113 9 : return new module_client (pex, fd_from, fd_to);
114 : }
115 :
116 : module_client *
117 4709 : module_client::open_module_client (location_t loc, const char *o,
118 : class mkdeps *deps,
119 : void (*set_repo) (const char *),
120 : char const *full_program_name)
121 : {
122 4709 : module_client *c = nullptr;
123 4709 : std::string ident;
124 4709 : std::string name;
125 4709 : char const *errmsg = nullptr;
126 4709 : unsigned line = 0;
127 :
128 4709 : if (o && o[0])
129 : {
130 : /* Maybe a local or ipv6 address. */
131 42 : name = o;
132 42 : auto last = name.find_last_of ('?');
133 42 : if (last != name.npos)
134 : {
135 3 : ident = name.substr (last + 1);
136 3 : name.erase (last);
137 : }
138 :
139 42 : if (name.size ())
140 : {
141 42 : switch (name[0])
142 : {
143 0 : case '<':
144 : // <from>to or <>fromto, or <>
145 0 : {
146 0 : size_t pos = name.find ('>', 1);
147 0 : if (pos == std::string::npos)
148 0 : pos = name.size ();
149 0 : std::string from (name, 1, pos - 1);
150 0 : std::string to;
151 0 : if (pos != name.size ())
152 0 : to.append (name, pos + 1, std::string::npos);
153 :
154 0 : int fd_from = -1, fd_to = -1;
155 0 : if (from.empty () && to.empty ())
156 : {
157 0 : fd_from = fileno (stdin);
158 0 : fd_to = fileno (stdout);
159 : }
160 : else
161 : {
162 0 : char *ptr;
163 0 : if (!from.empty ())
164 : {
165 : /* Sadly str::stoul is not portable. */
166 0 : const char *cstr = from.c_str ();
167 0 : fd_from = strtoul (cstr, &ptr, 10);
168 0 : if (*ptr)
169 : {
170 : /* Not a number -- a named pipe. */
171 0 : int dir = to.empty ()
172 0 : ? O_RDWR | O_CLOEXEC : O_RDONLY | O_CLOEXEC;
173 0 : fd_from = open (cstr, dir);
174 : }
175 0 : if (to.empty ())
176 0 : fd_to = fd_from;
177 : }
178 :
179 0 : if (!from.empty () && fd_from < 0)
180 : ;
181 0 : else if (to.empty ())
182 : ;
183 : else
184 : {
185 0 : const char *cstr = to.c_str ();
186 0 : fd_to = strtoul (cstr, &ptr, 10);
187 0 : if (*ptr)
188 : {
189 : /* Not a number, a named pipe. */
190 0 : int dir = from.empty ()
191 0 : ? O_RDWR | O_CLOEXEC : O_WRONLY | O_CLOEXEC;
192 0 : fd_to = open (cstr, dir);
193 0 : if (fd_to < 0)
194 0 : close (fd_from);
195 : }
196 0 : if (from.empty ())
197 0 : fd_from = fd_to;
198 : }
199 : }
200 :
201 0 : if (fd_from < 0 || fd_to < 0)
202 0 : errmsg = "opening";
203 : else
204 0 : c = new module_client (fd_from, fd_to);
205 0 : }
206 0 : break;
207 :
208 0 : case '=':
209 : // =localsocket
210 0 : {
211 0 : int fd = -1;
212 : #if CODY_NETWORKING
213 0 : fd = Cody::OpenLocal (&errmsg, name.c_str () + 1);
214 : #else
215 : errmsg = "disabled";
216 : #endif
217 0 : if (fd >= 0)
218 0 : c = new module_client (fd, fd);
219 : }
220 : break;
221 :
222 12 : case '|':
223 : // |program and args
224 12 : c = spawn_mapper_program (&errmsg, name, full_program_name);
225 12 : break;
226 :
227 30 : default:
228 : // file or hostname:port
229 30 : {
230 30 : auto colon = name.find_last_of (':');
231 30 : if (colon != name.npos)
232 : {
233 6 : char const *cptr = name.c_str () + colon;
234 6 : char *endp;
235 6 : unsigned port = strtoul (cptr + 1, &endp, 10);
236 :
237 6 : if (port && endp != cptr + 1 && !*endp)
238 : {
239 6 : name[colon] = 0;
240 6 : int fd = -1;
241 : #if CODY_NETWORKING
242 6 : fd = Cody::OpenInet6 (&errmsg, name.c_str (), port);
243 : #else
244 : errmsg = "disabled";
245 : #endif
246 6 : name[colon] = ':';
247 :
248 6 : if (fd >= 0)
249 0 : c = new module_client (fd, fd);
250 : }
251 : }
252 :
253 : }
254 : break;
255 : }
256 : }
257 : }
258 :
259 18 : if (!c)
260 : {
261 : // Make a default in-process client
262 4700 : bool file = !errmsg && !name.empty ();
263 4700 : auto r = new module_resolver (!file, true);
264 :
265 4700 : if (file)
266 : {
267 24 : int fd = open (name.c_str (), O_RDONLY | O_CLOEXEC);
268 24 : if (fd < 0)
269 0 : errmsg = "opening";
270 : else
271 : {
272 : /* Add the mapper file to the dependency tracking. */
273 24 : if (deps)
274 6 : deps_add_dep (deps, name.c_str ());
275 48 : if (int l = r->read_tuple_file (fd, ident, false))
276 : {
277 3 : if (l > 0)
278 : line = l;
279 3 : errmsg = "reading";
280 : }
281 :
282 24 : close (fd);
283 : }
284 : }
285 : else
286 4676 : r->set_repo ("gcm.cache");
287 :
288 4700 : auto *s = new Cody::Server (r);
289 4700 : c = new module_client (s);
290 : }
291 :
292 : #ifdef SIGPIPE
293 4709 : if (!c->IsDirect ())
294 : /* We need to ignore sig pipe for a while. */
295 9 : c->sigpipe = signal (SIGPIPE, SIG_IGN);
296 : #endif
297 :
298 4709 : if (errmsg)
299 21 : error_at (loc, line ? G_("failed %s mapper %qs line %u")
300 : : G_("failed %s mapper %qs"), errmsg, name.c_str (), line);
301 :
302 : // now wave hello!
303 4709 : c->Cork ();
304 4709 : c->Connect (std::string ("GCC"), ident);
305 4709 : c->ModuleRepo ();
306 4709 : auto packets = c->Uncork ();
307 :
308 4709 : auto &connect = packets[0];
309 4709 : if (connect.GetCode () == Cody::Client::PC_CONNECT)
310 4709 : c->flags = Cody::Flags (connect.GetInteger ());
311 0 : else if (connect.GetCode () == Cody::Client::PC_ERROR)
312 0 : error_at (loc, "failed mapper handshake %s", connect.GetString ().c_str ());
313 :
314 4709 : auto &repo = packets[1];
315 4709 : if (repo.GetCode () == Cody::Client::PC_PATHNAME)
316 4709 : set_repo (repo.GetString ().c_str ());
317 :
318 9418 : return c;
319 4709 : }
320 :
321 : void
322 4528 : module_client::close_module_client (location_t loc, module_client *mapper)
323 : {
324 4528 : if (mapper->IsDirect ())
325 : {
326 4519 : auto *s = mapper->GetServer ();
327 4519 : auto *r = s->GetResolver ();
328 4519 : delete s;
329 4519 : delete r;
330 : }
331 : else
332 : {
333 9 : if (mapper->pex)
334 : {
335 9 : int fd_write = mapper->GetFDWrite ();
336 9 : if (fd_write >= 0)
337 9 : close (fd_write);
338 :
339 9 : int status;
340 9 : pex_get_status (mapper->pex, 1, &status);
341 :
342 9 : pex_free (mapper->pex);
343 9 : mapper->pex = NULL;
344 :
345 9 : if (WIFSIGNALED (status))
346 0 : error_at (loc, "mapper died by signal %s",
347 : strsignal (WTERMSIG (status)));
348 9 : else if (WIFEXITED (status) && WEXITSTATUS (status) != 0)
349 0 : error_at (loc, "mapper exit status %d",
350 0 : WEXITSTATUS (status));
351 : }
352 : else
353 : {
354 0 : int fd_read = mapper->GetFDRead ();
355 0 : close (fd_read);
356 : }
357 :
358 : #ifdef SIGPIPE
359 : // Restore sigpipe
360 9 : if (mapper->sigpipe != SIG_IGN)
361 9 : signal (SIGPIPE, mapper->sigpipe);
362 : #endif
363 : }
364 :
365 4537 : delete mapper;
366 4528 : }
|