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