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