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