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 17You should have received a copy of the GNU General Public License 18along 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 44module_client::module_client (pex_obj *p, int fd_from, int fd_to) 45 : Client (fd_from, fd_to), pex (p) 46{ 47} 48 49static module_client * 50spawn_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 133module_client * 134module_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 330void 331module_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