1 /* C++ modules. Experimental! -*- c++ -*- 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 23 #include "resolver.h" 24 // C++ 25 #include <algorithm> 26 #include <memory> 27 // C 28 #include <cstring> 29 // OS 30 #include <fcntl.h> 31 #include <unistd.h> 32 #if 0 // 1 for testing no mmap 33 #define MAPPED_READING 0 34 #else 35 #ifdef IN_GCC 36 #if HAVE_MMAP_FILE && _POSIX_MAPPED_FILES > 0 37 #define MAPPED_READING 1 38 #else 39 #define MAPPED_READING 0 40 #endif 41 #else 42 #ifdef HAVE_SYS_MMAN_H 43 #include <sys/mman.h> 44 #define MAPPED_READING 1 45 #else 46 #define MAPPED_READING 0 47 #endif 48 #endif 49 #endif 50 51 #include <sys/types.h> 52 #include <sys/stat.h> 53 54 #if !defined (IN_GCC) && !MAPPED_READING 55 #define xmalloc(X) malloc(X) 56 #endif 57 58 #if !HOST_HAS_O_CLOEXEC 59 #define O_CLOEXEC 0 60 #endif 61 62 #ifndef DIR_SEPARATOR 63 #define DIR_SEPARATOR '/' 64 #endif 65 66 module_resolver::module_resolver (bool map, bool xlate) 67 : default_map (map), default_translate (xlate) 68 { 69 } 70 71 module_resolver::~module_resolver () 72 { 73 if (fd_repo >= 0) 74 close (fd_repo); 75 } 76 77 bool 78 module_resolver::set_repo (std::string &&r, bool force) 79 { 80 if (force || repo.empty ()) 81 { 82 repo = std::move (r); 83 force = true; 84 } 85 return force; 86 } 87 88 bool 89 module_resolver::add_mapping (std::string &&module, std::string &&file, 90 bool force) 91 { 92 auto res = map.emplace (std::move (module), std::move (file)); 93 if (res.second) 94 force = true; 95 else if (force) 96 res.first->second = std::move (file); 97 98 return force; 99 } 100 101 int 102 module_resolver::read_tuple_file (int fd, char const *prefix, bool force) 103 { 104 struct stat stat; 105 if (fstat (fd, &stat) < 0) 106 return -errno; 107 108 if (!stat.st_size) 109 return 0; 110 111 void *buffer = nullptr; 112 #if MAPPED_READING 113 // Just map the file, we're gonna read all of it, so no need for 114 // line buffering 115 buffer = mmap (nullptr, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0); 116 if (buffer == MAP_FAILED) 117 return -errno; 118 struct Deleter { 119 void operator()(void* p) const { munmap(p, size); } 120 size_t size; 121 }; 122 std::unique_ptr<void, Deleter> guard(buffer, Deleter{(size_t)stat.st_size}); 123 #else 124 buffer = xmalloc (stat.st_size); 125 if (!buffer) 126 return -errno; 127 struct Deleter { void operator()(void* p) const { free(p); } }; 128 std::unique_ptr<void, Deleter> guard(buffer); 129 if (read (fd, buffer, stat.st_size) != stat.st_size) 130 return -errno; 131 #endif 132 133 size_t prefix_len = prefix ? strlen (prefix) : 0; 134 unsigned lineno = 0; 135 136 for (char const *begin = reinterpret_cast <char const *> (buffer), 137 *end = begin + stat.st_size, *eol; 138 begin != end; begin = eol + 1) 139 { 140 lineno++; 141 eol = std::find (begin, end, '\n'); 142 if (eol == end) 143 // last line has no \n, ignore the line, you lose 144 break; 145 146 auto *pos = begin; 147 bool pfx_search = prefix_len != 0; 148 149 pfx_search: 150 while (*pos == ' ' || *pos == '\t') 151 pos++; 152 153 auto *space = pos; 154 while (*space != '\n' && *space != ' ' && *space != '\t') 155 space++; 156 157 if (pos == space) 158 // at end of line, nothing here 159 continue; 160 161 if (pfx_search) 162 { 163 if (size_t (space - pos) == prefix_len 164 && std::equal (pos, space, prefix)) 165 pfx_search = false; 166 pos = space; 167 goto pfx_search; 168 } 169 170 std::string module (pos, space); 171 while (*space == ' ' || *space == '\t') 172 space++; 173 std::string file (space, eol); 174 175 if (module[0] == '$') 176 { 177 if (module == "$root") 178 set_repo (std::move (file)); 179 else 180 return lineno; 181 } 182 else 183 { 184 if (file.empty ()) 185 file = GetCMIName (module); 186 add_mapping (std::move (module), std::move (file), force); 187 } 188 } 189 190 return 0; 191 } 192 193 char const * 194 module_resolver::GetCMISuffix () 195 { 196 return "gcm"; 197 } 198 199 module_resolver * 200 module_resolver::ConnectRequest (Cody::Server *s, unsigned version, 201 std::string &a, std::string &i) 202 { 203 if (!version || version > Cody::Version) 204 s->ErrorResponse ("version mismatch"); 205 else if (a != "GCC") 206 // Refuse anything but GCC 207 ErrorResponse (s, std::string ("only GCC supported")); 208 else if (!ident.empty () && ident != i) 209 // Failed ident check 210 ErrorResponse (s, std::string ("bad ident")); 211 else 212 // Success! 213 s->ConnectResponse ("gcc"); 214 215 return this; 216 } 217 218 int 219 module_resolver::ModuleRepoRequest (Cody::Server *s) 220 { 221 s->PathnameResponse (repo); 222 return 0; 223 } 224 225 int 226 module_resolver::cmi_response (Cody::Server *s, std::string &module) 227 { 228 auto iter = map.find (module); 229 if (iter == map.end ()) 230 { 231 std::string file = default_map ? GetCMIName (module) : std::string (); 232 auto res = map.emplace (module, file); 233 iter = res.first; 234 } 235 236 if (iter->second.empty ()) 237 s->ErrorResponse ("no such module"); 238 else 239 s->PathnameResponse (iter->second); 240 241 return 0; 242 } 243 244 int 245 module_resolver::ModuleExportRequest (Cody::Server *s, Cody::Flags, 246 std::string &module) 247 { 248 return cmi_response (s, module); 249 } 250 251 int 252 module_resolver::ModuleImportRequest (Cody::Server *s, Cody::Flags, 253 std::string &module) 254 { 255 return cmi_response (s, module); 256 } 257 258 int 259 module_resolver::IncludeTranslateRequest (Cody::Server *s, Cody::Flags, 260 std::string &include) 261 { 262 auto iter = map.find (include); 263 if (iter == map.end () && default_translate) 264 { 265 // Not found, look for it 266 auto file = GetCMIName (include); 267 struct stat statbuf; 268 bool ok = true; 269 270 #if HAVE_FSTATAT 271 int fd_dir = AT_FDCWD; 272 if (!repo.empty ()) 273 { 274 if (fd_repo == -1) 275 { 276 fd_repo = open (repo.c_str (), 277 O_RDONLY | O_CLOEXEC | O_DIRECTORY); 278 if (fd_repo < 0) 279 fd_repo = -2; 280 } 281 fd_dir = fd_repo; 282 } 283 284 if (!repo.empty () && fd_repo < 0) 285 ok = false; 286 else if (fstatat (fd_dir, file.c_str (), &statbuf, 0) < 0 287 || !S_ISREG (statbuf.st_mode)) 288 ok = false; 289 #else 290 auto append = repo; 291 append.push_back (DIR_SEPARATOR); 292 append.append (file); 293 if (stat (append.c_str (), &statbuf) < 0 294 || !S_ISREG (statbuf.st_mode)) 295 ok = false; 296 #endif 297 if (!ok) 298 // Mark as not present 299 file.clear (); 300 auto res = map.emplace (include, file); 301 iter = res.first; 302 } 303 304 if (iter == map.end () || iter->second.empty ()) 305 s->BoolResponse (false); 306 else 307 s->PathnameResponse (iter->second); 308 309 return 0; 310 } 311 312 /* This handles a client notification to the server that a CMI has been 313 produced for a module. For this simplified server, we just accept 314 the transaction and respond with "OK". */ 315 316 int 317 module_resolver::ModuleCompiledRequest (Cody::Server *s, Cody::Flags, 318 std::string &) 319 { 320 s->OKResponse(); 321 return 0; 322 } 323