Branch data Line data Source code
1 : : /* C++ modules. Experimental!
2 : : Copyright (C) 2017-2024 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 : : #define INCLUDE_MEMORY
31 : : #include "system.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 writable at white-space. No space-containing args for
56 : : you! */
57 : : // At most every other char could be an argument
58 : 12 : char **argv = new char *[name.size () / 2 + 2];
59 : 12 : unsigned arg_no = 0;
60 : 12 : char *str = new char[name.size ()];
61 : 12 : memcpy (str, name.c_str () + 1, name.size ());
62 : :
63 : 12 : for (auto ptr = str; ; ++ptr)
64 : : {
65 : 24 : while (*ptr == ' ')
66 : 0 : ptr++;
67 : 24 : if (!*ptr)
68 : : break;
69 : :
70 : 24 : if (!arg_no)
71 : : {
72 : : /* @name means look in the compiler's install dir. */
73 : 12 : if (ptr[0] == '@')
74 : 9 : ptr++;
75 : : else
76 : : full_program_name = nullptr;
77 : : }
78 : :
79 : 24 : argv[arg_no++] = ptr;
80 : 753 : while (*ptr && *ptr != ' ')
81 : 729 : ptr++;
82 : 24 : if (!*ptr)
83 : : break;
84 : 12 : *ptr = 0;
85 : : }
86 : 12 : argv[arg_no] = nullptr;
87 : :
88 : 12 : auto *pex = pex_init (PEX_USE_PIPES, progname, NULL);
89 : 12 : FILE *to = pex_input_pipe (pex, false);
90 : 12 : name = argv[0];
91 : 12 : if (!to)
92 : 0 : *errmsg = "connecting input";
93 : : else
94 : : {
95 : 12 : int flags = PEX_SEARCH;
96 : :
97 : 12 : if (full_program_name)
98 : : {
99 : : /* Prepend the invoking path, if the mapper is a simple
100 : : file name. */
101 : 9 : size_t dir_len = progname - full_program_name;
102 : 9 : std::string argv0;
103 : 9 : argv0.reserve (dir_len + name.size ());
104 : 9 : argv0.append (full_program_name, dir_len).append (name);
105 : 9 : name = std::move (argv0);
106 : 9 : argv[0] = const_cast <char *> (name.c_str ());
107 : 9 : flags = 0;
108 : 9 : }
109 : 12 : int err;
110 : 12 : *errmsg = pex_run (pex, flags, argv[0], argv, NULL, NULL, &err);
111 : : }
112 : 12 : delete[] str;
113 : 12 : delete[] argv;
114 : :
115 : 12 : int fd_from = -1, fd_to = -1;
116 : 12 : if (!*errmsg)
117 : : {
118 : 9 : FILE *from = pex_read_output (pex, false);
119 : 9 : if (from && (fd_to = dup (fileno (to))) >= 0)
120 : 9 : fd_from = fileno (from);
121 : : else
122 : 0 : *errmsg = "connecting output";
123 : 9 : fclose (to);
124 : : }
125 : :
126 : 12 : if (*errmsg)
127 : : {
128 : 3 : pex_free (pex);
129 : 3 : return nullptr;
130 : : }
131 : :
132 : 9 : return new module_client (pex, fd_from, fd_to);
133 : : }
134 : :
135 : : module_client *
136 : 3604 : module_client::open_module_client (location_t loc, const char *o,
137 : : class mkdeps *deps,
138 : : void (*set_repo) (const char *),
139 : : char const *full_program_name)
140 : : {
141 : 3604 : module_client *c = nullptr;
142 : 3604 : std::string ident;
143 : 3604 : std::string name;
144 : 3604 : char const *errmsg = nullptr;
145 : 3604 : unsigned line = 0;
146 : :
147 : 3604 : if (o && o[0])
148 : : {
149 : : /* Maybe a local or ipv6 address. */
150 : 42 : name = o;
151 : 42 : auto last = name.find_last_of ('?');
152 : 42 : if (last != name.npos)
153 : : {
154 : 3 : ident = name.substr (last + 1);
155 : 3 : name.erase (last);
156 : : }
157 : :
158 : 42 : if (name.size ())
159 : : {
160 : 42 : switch (name[0])
161 : : {
162 : 0 : case '<':
163 : : // <from>to or <>fromto, or <>
164 : 0 : {
165 : 0 : size_t pos = name.find ('>', 1);
166 : 0 : if (pos == std::string::npos)
167 : 0 : pos = name.size ();
168 : 0 : std::string from (name, 1, pos - 1);
169 : 0 : std::string to;
170 : 0 : if (pos != name.size ())
171 : 0 : to.append (name, pos + 1, std::string::npos);
172 : :
173 : 0 : int fd_from = -1, fd_to = -1;
174 : 0 : if (from.empty () && to.empty ())
175 : : {
176 : 0 : fd_from = fileno (stdin);
177 : 0 : fd_to = fileno (stdout);
178 : : }
179 : : else
180 : : {
181 : 0 : char *ptr;
182 : 0 : if (!from.empty ())
183 : : {
184 : : /* Sadly str::stoul is not portable. */
185 : 0 : const char *cstr = from.c_str ();
186 : 0 : fd_from = strtoul (cstr, &ptr, 10);
187 : 0 : if (*ptr)
188 : : {
189 : : /* Not a number -- a named pipe. */
190 : 0 : int dir = to.empty ()
191 : 0 : ? O_RDWR | O_CLOEXEC : O_RDONLY | O_CLOEXEC;
192 : 0 : fd_from = open (cstr, dir);
193 : : }
194 : 0 : if (to.empty ())
195 : 0 : fd_to = fd_from;
196 : : }
197 : :
198 : 0 : if (!from.empty () && fd_from < 0)
199 : : ;
200 : 0 : else if (to.empty ())
201 : : ;
202 : : else
203 : : {
204 : 0 : const char *cstr = to.c_str ();
205 : 0 : fd_to = strtoul (cstr, &ptr, 10);
206 : 0 : if (*ptr)
207 : : {
208 : : /* Not a number, a named pipe. */
209 : 0 : int dir = from.empty ()
210 : 0 : ? O_RDWR | O_CLOEXEC : O_WRONLY | O_CLOEXEC;
211 : 0 : fd_to = open (cstr, dir);
212 : 0 : if (fd_to < 0)
213 : 0 : close (fd_from);
214 : : }
215 : 0 : if (from.empty ())
216 : 0 : fd_from = fd_to;
217 : : }
218 : : }
219 : :
220 : 0 : if (fd_from < 0 || fd_to < 0)
221 : 0 : errmsg = "opening";
222 : : else
223 : 0 : c = new module_client (fd_from, fd_to);
224 : 0 : }
225 : 0 : break;
226 : :
227 : 0 : case '=':
228 : : // =localsocket
229 : 0 : {
230 : 0 : int fd = -1;
231 : : #if CODY_NETWORKING
232 : 0 : fd = Cody::OpenLocal (&errmsg, name.c_str () + 1);
233 : : #else
234 : : errmsg = "disabled";
235 : : #endif
236 : 0 : if (fd >= 0)
237 : 0 : c = new module_client (fd, fd);
238 : : }
239 : : break;
240 : :
241 : 12 : case '|':
242 : : // |program and args
243 : 12 : c = spawn_mapper_program (&errmsg, name, full_program_name);
244 : 12 : break;
245 : :
246 : 30 : default:
247 : : // file or hostname:port
248 : 30 : {
249 : 30 : auto colon = name.find_last_of (':');
250 : 30 : if (colon != name.npos)
251 : : {
252 : 6 : char const *cptr = name.c_str () + colon;
253 : 6 : char *endp;
254 : 6 : unsigned port = strtoul (cptr + 1, &endp, 10);
255 : :
256 : 6 : if (port && endp != cptr + 1 && !*endp)
257 : : {
258 : 6 : name[colon] = 0;
259 : 6 : int fd = -1;
260 : : #if CODY_NETWORKING
261 : 6 : fd = Cody::OpenInet6 (&errmsg, name.c_str (), port);
262 : : #else
263 : : errmsg = "disabled";
264 : : #endif
265 : 6 : name[colon] = ':';
266 : :
267 : 6 : if (fd >= 0)
268 : 0 : c = new module_client (fd, fd);
269 : : }
270 : : }
271 : :
272 : : }
273 : : break;
274 : : }
275 : : }
276 : : }
277 : :
278 : 18 : if (!c)
279 : : {
280 : : // Make a default in-process client
281 : 3595 : bool file = !errmsg && !name.empty ();
282 : 3595 : auto r = new module_resolver (!file, true);
283 : :
284 : 3595 : if (file)
285 : : {
286 : 24 : int fd = open (name.c_str (), O_RDONLY | O_CLOEXEC);
287 : 24 : if (fd < 0)
288 : 0 : errmsg = "opening";
289 : : else
290 : : {
291 : : /* Add the mapper file to the dependency tracking. */
292 : 24 : if (deps)
293 : 6 : deps_add_dep (deps, name.c_str ());
294 : 48 : if (int l = r->read_tuple_file (fd, ident, false))
295 : : {
296 : 3 : if (l > 0)
297 : : line = l;
298 : 3 : errmsg = "reading";
299 : : }
300 : :
301 : 24 : close (fd);
302 : : }
303 : : }
304 : : else
305 : 3571 : r->set_repo ("gcm.cache");
306 : :
307 : 3595 : auto *s = new Cody::Server (r);
308 : 3595 : c = new module_client (s);
309 : : }
310 : :
311 : : #ifdef SIGPIPE
312 : 3604 : if (!c->IsDirect ())
313 : : /* We need to ignore sig pipe for a while. */
314 : 9 : c->sigpipe = signal (SIGPIPE, SIG_IGN);
315 : : #endif
316 : :
317 : 3604 : if (errmsg)
318 : 21 : error_at (loc, line ? G_("failed %s mapper %qs line %u")
319 : : : G_("failed %s mapper %qs"), errmsg, name.c_str (), line);
320 : :
321 : : // now wave hello!
322 : 3604 : c->Cork ();
323 : 3604 : c->Connect (std::string ("GCC"), ident);
324 : 3604 : c->ModuleRepo ();
325 : 3604 : auto packets = c->Uncork ();
326 : :
327 : 3604 : auto &connect = packets[0];
328 : 3604 : if (connect.GetCode () == Cody::Client::PC_CONNECT)
329 : 3604 : c->flags = Cody::Flags (connect.GetInteger ());
330 : 0 : else if (connect.GetCode () == Cody::Client::PC_ERROR)
331 : 0 : error_at (loc, "failed mapper handshake %s", connect.GetString ().c_str ());
332 : :
333 : 3604 : auto &repo = packets[1];
334 : 3604 : if (repo.GetCode () == Cody::Client::PC_PATHNAME)
335 : 3604 : set_repo (repo.GetString ().c_str ());
336 : :
337 : 7208 : return c;
338 : 3604 : }
339 : :
340 : : void
341 : 3516 : module_client::close_module_client (location_t loc, module_client *mapper)
342 : : {
343 : 3516 : if (mapper->IsDirect ())
344 : : {
345 : 3507 : auto *s = mapper->GetServer ();
346 : 3507 : auto *r = s->GetResolver ();
347 : 3507 : delete s;
348 : 3507 : delete r;
349 : : }
350 : : else
351 : : {
352 : 9 : if (mapper->pex)
353 : : {
354 : 9 : int fd_write = mapper->GetFDWrite ();
355 : 9 : if (fd_write >= 0)
356 : 9 : close (fd_write);
357 : :
358 : 9 : int status;
359 : 9 : pex_get_status (mapper->pex, 1, &status);
360 : :
361 : 9 : pex_free (mapper->pex);
362 : 9 : mapper->pex = NULL;
363 : :
364 : 9 : if (WIFSIGNALED (status))
365 : 0 : error_at (loc, "mapper died by signal %s",
366 : : strsignal (WTERMSIG (status)));
367 : 9 : else if (WIFEXITED (status) && WEXITSTATUS (status) != 0)
368 : 0 : error_at (loc, "mapper exit status %d",
369 : 0 : WEXITSTATUS (status));
370 : : }
371 : : else
372 : : {
373 : 0 : int fd_read = mapper->GetFDRead ();
374 : 0 : close (fd_read);
375 : : }
376 : :
377 : : #ifdef SIGPIPE
378 : : // Restore sigpipe
379 : 9 : if (mapper->sigpipe != SIG_IGN)
380 : 9 : signal (SIGPIPE, mapper->sigpipe);
381 : : #endif
382 : : }
383 : :
384 : 3525 : delete mapper;
385 : 3516 : }
|