Changeset 98068 in vbox for trunk/src/VBox/Main/src-server/linux/HostDnsServiceLinux.cpp
- Timestamp:
- Jan 13, 2023 4:14:20 AM (2 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Main/src-server/linux/HostDnsServiceLinux.cpp
r98067 r98068 26 26 */ 27 27 28 29 /********************************************************************************************************************************* 30 * Header Files * 31 *********************************************************************************************************************************/ 32 #define LOG_GROUP LOG_GROUP_MAIN_HOST 28 33 #include <iprt/assert.h> 29 34 #include <iprt/errcore.h> 30 35 #include <iprt/initterm.h> 31 36 #include <iprt/file.h> 32 #include < iprt/log.h>37 #include <VBox/log.h> 33 38 #include <iprt/stream.h> 34 39 #include <iprt/string.h> … … 53 58 #include <sys/types.h> 54 59 #include <sys/socket.h> 55 56 #include <iprt/sanitized/string> 57 #include <vector> 60 #include <sys/stat.h> 61 58 62 #include "../HostDnsService.h" 59 63 60 64 65 /********************************************************************************************************************************* 66 * Global Variables * 67 *********************************************************************************************************************************/ 61 68 static int g_DnsMonitorStop[2]; 62 69 63 static const std::string g_EtcFolder = "/etc"; 64 static const std::string g_ResolvConf = "resolv.conf"; 65 static const std::string g_ResolvConfFullPath = "/etc/resolv.conf"; 70 static const char g_szEtcFolder[] = "/etc"; 71 static const char g_szResolvConfPath[] = "/etc/resolv.conf"; 72 static const char g_szResolvConfFilename[] = "resolv.conf"; 73 66 74 67 75 class FileDescriptor … … 94 102 }; 95 103 96 struct InotifyEventWithName97 {98 struct inotify_event e;99 char name[NAME_MAX];100 };101 102 104 HostDnsServiceLinux::~HostDnsServiceLinux() 103 105 { … … 119 121 } 120 122 123 #ifdef LOG_ENABLED 124 /** 125 * Format the notifcation event mask into a buffer for logging purposes. 126 */ 127 static const char *InotifyMaskToStr(char *psz, size_t cb, uint32_t fMask) 128 { 129 static struct { const char *pszName; uint32_t cchName, fFlag; } const s_aFlags[] = 130 { 131 # define ENTRY(fFlag) { #fFlag, sizeof(#fFlag) - 1, fFlag } 132 ENTRY(IN_ACCESS), 133 ENTRY(IN_MODIFY), 134 ENTRY(IN_ATTRIB), 135 ENTRY(IN_CLOSE_WRITE), 136 ENTRY(IN_CLOSE_NOWRITE), 137 ENTRY(IN_OPEN), 138 ENTRY(IN_MOVED_FROM), 139 ENTRY(IN_MOVED_TO), 140 ENTRY(IN_CREATE), 141 ENTRY(IN_DELETE), 142 ENTRY(IN_DELETE_SELF), 143 ENTRY(IN_MOVE_SELF), 144 ENTRY(IN_Q_OVERFLOW), 145 ENTRY(IN_IGNORED), 146 ENTRY(IN_UNMOUNT), 147 ENTRY(IN_ISDIR), 148 }; 149 size_t offDst = 0; 150 for (size_t i = 0; i < RT_ELEMENTS(s_aFlags); i++) 151 if (fMask & s_aFlags[i].fFlag) 152 { 153 if (offDst && offDst < cb) 154 psz[offDst++] = ' '; 155 if (offDst < cb) 156 { 157 size_t cbToCopy = RT_MIN(s_aFlags[i].cchName, cb - offDst); 158 memcpy(&psz[offDst], s_aFlags[i].pszName, cbToCopy); 159 offDst += cbToCopy; 160 } 161 162 fMask &= ~s_aFlags[i].fFlag; 163 if (!fMask) 164 break; 165 } 166 if (fMask && offDst < cb) 167 RTStrPrintf(&psz[offDst], cb - offDst, offDst ? " %#x" : "%#x", fMask); 168 else 169 psz[RT_MIN(offDst, cb - 1)] = '\0'; 170 return psz; 171 } 172 #endif 173 174 175 /** 176 * Helper for HostDnsServiceLinux::monitorThreadProc. 177 */ 178 static int monitorSymlinkedDir(int iInotifyFd, char szRealResolvConf[PATH_MAX], size_t *poffFilename) 179 { 180 RT_BZERO(szRealResolvConf, PATH_MAX); 181 182 /* Check that it's a symlink first. */ 183 struct stat st; 184 if ( lstat(g_szResolvConfPath, &st) >= 0 185 && S_ISLNK(st.st_mode)) 186 { 187 /* If realpath fails, the file must've been deleted while we were busy: */ 188 if ( realpath(g_szResolvConfPath, szRealResolvConf) 189 && strchr(szRealResolvConf, '/')) 190 { 191 /* Cut of the filename part. We only need that for deletion checks and such. */ 192 size_t const offFilename = strrchr(szRealResolvConf, '/') - &szRealResolvConf[0]; 193 *poffFilename = offFilename + 1; 194 szRealResolvConf[offFilename] = '\0'; 195 196 /* Try set up directory monitoring. (File monitoring is done via the symlink.) */ 197 return inotify_add_watch(iInotifyFd, szRealResolvConf, IN_MOVE | IN_CREATE | IN_DELETE); 198 } 199 } 200 201 *poffFilename = 0; 202 szRealResolvConf[0] = '\0'; 203 return -1; 204 } 205 121 206 int HostDnsServiceLinux::monitorThreadProc(void) 122 207 { 123 208 /* 124 * inotify initialization 209 * inotify initialization. 210 * 211 * The order here helps keep the descriptor values stable. 125 212 * 126 213 * Note! Ignoring failures here is safe, because poll will ignore entires 127 214 * with negative fd values. 128 215 */ 129 AutoNotify a; 130 int wd[2]; 131 wd[0] = inotify_add_watch(a.fileDescriptor(), 132 g_ResolvConfFullPath.c_str(), IN_CLOSE_WRITE | IN_DELETE_SELF); 133 134 /* If /etc/resolv.conf exists we want to listen for movements: because 135 # mv /etc/resolv.conf ... 136 won't arm IN_DELETE_SELF on wd[0] instead it will fire IN_MOVE_FROM on wd[1]. 137 138 Because on some distributions /etc/resolv.conf is a symlink, wd[0] can't detect 139 deletion, it's recognizible on directory level (wd[1]) only. */ 140 wd[1] = inotify_add_watch(a.fileDescriptor(), g_EtcFolder.c_str(), 141 wd[0] == -1 ? IN_MOVED_TO | IN_CREATE : IN_MOVED_FROM | IN_DELETE); 216 AutoNotify Notify; 217 218 /* Monitor the /etc directory so we can detect moves, unliking and creations 219 involving /etc/resolv.conf: */ 220 int const iWdDir = inotify_add_watch(Notify.fileDescriptor(), g_szEtcFolder, IN_MOVE | IN_CREATE | IN_DELETE); 221 222 /* In case g_szResolvConfPath is a symbolic link, monitor the target directory 223 too for changes to what it links to. */ 224 char szRealResolvConf[PATH_MAX]; 225 size_t offRealResolvConfName = 0; 226 int iWdSymDir = ::monitorSymlinkedDir(Notify.fileDescriptor(), szRealResolvConf, &offRealResolvConfName); 227 228 /* Monitor the resolv.conf itself if it exists, following all symlinks. */ 229 int iWdFile = inotify_add_watch(Notify.fileDescriptor(), g_szResolvConfPath, IN_CLOSE_WRITE | IN_DELETE_SELF); 230 231 Log5Func(("iWdDir=%d iWdSymDir=%d iWdFile=%d\n", iWdDir, iWdSymDir, iWdFile)); 142 232 143 233 /* … … 154 244 * poll initialization: 155 245 */ 156 pollfd polls[2];157 RT_ZERO( polls);158 159 polls[0].fd = a.fileDescriptor();160 polls[0].events = POLLIN;161 162 polls[1].fd = g_DnsMonitorStop[1];163 polls[1].events = POLLIN;246 pollfd aFdPolls[2]; 247 RT_ZERO(aFdPolls); 248 249 aFdPolls[0].fd = Notify.fileDescriptor(); 250 aFdPolls[0].events = POLLIN; 251 252 aFdPolls[1].fd = g_DnsMonitorStop[1]; 253 aFdPolls[1].events = POLLIN; 164 254 165 255 onMonitorThreadInitDone(); … … 170 260 for (;;) 171 261 { 172 rc = poll(polls, RT_ELEMENTS(polls), -1 /*infinite timeout*/); 262 /* 263 * Wait for something to happen. 264 */ 265 rc = poll(aFdPolls, RT_ELEMENTS(aFdPolls), -1 /*infinite timeout*/); 173 266 if (rc == -1) 267 { 268 LogRelMax(32, ("HostDnsServiceLinux::monitorThreadProc: poll failed %d: errno=%d\n", rc, errno)); 269 RTThreadSleep(1); 174 270 continue; 175 176 AssertMsgReturn( (polls[0].revents & (POLLERR | POLLNVAL)) == 0 177 && (polls[1].revents & (POLLERR | POLLNVAL)) == 0, ("Debug Me"), VERR_INTERNAL_ERROR); 178 179 if (polls[1].revents & POLLIN) 180 return VINF_SUCCESS; /* time to shutdown */ 181 182 if (polls[0].revents & POLLIN) 271 } 272 Log5Func(("poll returns %d: [0]=%#x [1]=%#x\n", rc, aFdPolls[1].revents, aFdPolls[0].revents)); 273 274 AssertMsgReturn( (aFdPolls[0].revents & (POLLERR | POLLNVAL)) == 0 275 && (aFdPolls[1].revents & (POLLERR | POLLNVAL)) == 0, ("Debug Me"), VERR_INTERNAL_ERROR); 276 277 278 /* 279 * Check for shutdown first. 280 */ 281 if (aFdPolls[1].revents & POLLIN) 282 return VINF_SUCCESS; 283 284 if (aFdPolls[0].revents & POLLIN) 183 285 { 184 286 /* 185 287 * Read the notification event. 186 288 */ 187 /** @todo r=bird: This is buggy in that it somehow assumes we'll only get 188 * one event here. But since we're waiting on two different DELETE 189 * events for both a specific file and its parent directory, we're likely 190 * to get two DELETE events at the same time. */ 191 struct InotifyEventWithName combo; 192 RT_ZERO(combo); 193 combo.e.wd = -42; /* avoid confusion on the offchance that wd[0] or wd[1] is zero. */ 194 195 ssize_t r = read(polls[0].fd, &combo, sizeof(combo)); 196 RT_NOREF(r); 197 198 if (combo.e.wd == wd[0]) 289 #define INOTIFY_EVENT_SIZE (RT_UOFFSETOF(struct inotify_event, name)) 290 union 199 291 { 200 if (combo.e.mask & IN_CLOSE_WRITE) 201 readResolvConf(); 202 else if (combo.e.mask & IN_DELETE_SELF) 292 uint8_t abBuf[(INOTIFY_EVENT_SIZE * 2 - 1 + NAME_MAX) / INOTIFY_EVENT_SIZE * INOTIFY_EVENT_SIZE * 4]; 293 uint64_t uAlignTrick[2]; 294 } uEvtBuf; 295 296 ssize_t cbEvents = read(Notify.fileDescriptor(), &uEvtBuf, sizeof(uEvtBuf)); 297 Log5Func(("read(inotify) -> %zd\n", cbEvents)); 298 if (cbEvents > 0) 299 Log5(("%.*Rhxd\n", cbEvents, &uEvtBuf)); 300 301 /* 302 * Process the events. 303 * 304 * We'll keep the old watch descriptor number till after we're done 305 * parsing this block of events. Even so, the removal of watches 306 * isn't race free, as they'll get automatically removed when what 307 * is being watched is unliked. 308 */ 309 int iWdFileNew = iWdFile; 310 int iWdSymDirNew = iWdSymDir; 311 bool fTryReRead = false; 312 struct inotify_event const *pCurEvt = (struct inotify_event const *)&uEvtBuf; 313 while (cbEvents >= (ssize_t)INOTIFY_EVENT_SIZE) 314 { 315 #ifdef LOG_ENABLED 316 char szTmp[64]; 317 if (pCurEvt->len == 0) 318 Log5Func(("event: wd=%#x mask=%#x (%s) cookie=%#x\n", pCurEvt->wd, pCurEvt->mask, 319 InotifyMaskToStr(szTmp, sizeof(szTmp), pCurEvt->mask), pCurEvt->cookie)); 320 else 321 Log5Func(("event: wd=%#x mask=%#x (%s) cookie=%#x len=%#x '%s'\n", 322 pCurEvt->wd, pCurEvt->mask, InotifyMaskToStr(szTmp, sizeof(szTmp), pCurEvt->mask), 323 pCurEvt->cookie, pCurEvt->len, pCurEvt->name)); 324 #endif 325 326 /* 327 * The file itself (symlinks followed, remember): 328 */ 329 if (pCurEvt->wd == iWdFile) 203 330 { 204 inotify_rm_watch(a.fileDescriptor(), wd[0]); /* removes file watcher */ 205 int wd2 = inotify_add_watch(a.fileDescriptor(), g_EtcFolder.c_str(), 206 IN_MOVED_TO|IN_CREATE); /* alter folder watcher */ 207 Assert(wd2 == wd[1]); RT_NOREF(wd2); /* ASSUMES wd[1] will be updated */ 331 if (pCurEvt->mask & IN_CLOSE_WRITE) 332 { 333 Log5Func(("file: close-after-write => trigger re-read\n")); 334 fTryReRead = true; 335 } 336 else if (pCurEvt->mask & IN_DELETE_SELF) 337 { 338 Log5Func(("file: deleted self\n")); 339 if (iWdFileNew != -1) 340 { 341 rc = inotify_rm_watch(Notify.fileDescriptor(), iWdFileNew); 342 AssertMsg(rc >= 0, ("%d/%d\n", rc, errno)); 343 iWdFileNew = -1; 344 } 345 } 346 else if (pCurEvt->mask & IN_IGNORED) 347 iWdFileNew = -1; /* file deleted */ 348 else 349 AssertMsgFailed(("file: mask=%#x\n", pCurEvt->mask)); 208 350 } 209 else if (combo.e.mask & IN_IGNORED) 210 wd[0] = -1; /* we want receive any events on this watch */ 351 /* 352 * The /etc directory 353 * 354 * We only care about events relating to the creation, deletion and 355 * renaming of 'resolv.conf'. We'll restablish both the direct file 356 * watching and the watching of any symlinked directory on all of 357 * these events, although for the former we'll delay the re-starting 358 * of the watching till all events have been processed. 359 */ 360 else if (pCurEvt->wd == iWdDir) 361 { 362 if ( pCurEvt->len > 0 363 && strcmp(g_szResolvConfFilename, pCurEvt->name) == 0) 364 { 365 if (pCurEvt->mask & (IN_MOVE | IN_CREATE | IN_DELETE)) 366 { 367 if (iWdFileNew >= 0) 368 { 369 rc = inotify_rm_watch(Notify.fileDescriptor(), iWdFileNew); 370 Log5Func(("dir: moved / created / deleted: dropped file watch (%d - rc=%d/err=%d)\n", 371 iWdFileNew, rc, errno)); 372 iWdFileNew = -1; 373 } 374 if (iWdSymDirNew >= 0) 375 { 376 rc = inotify_rm_watch(Notify.fileDescriptor(), iWdSymDirNew); 377 Log5Func(("dir: moved / created / deleted: dropped symlinked dir watch (%d - %s/%s - rc=%d/err=%d)\n", 378 iWdSymDirNew, szRealResolvConf, &szRealResolvConf[offRealResolvConfName], rc, errno)); 379 iWdSymDirNew = -1; 380 offRealResolvConfName = 0; 381 } 382 if (pCurEvt->mask & (IN_MOVED_TO | IN_CREATE)) 383 { 384 Log5Func(("dir: moved_to / created: trigger re-read\n")); 385 fTryReRead = true; 386 387 iWdSymDirNew = ::monitorSymlinkedDir(Notify.fileDescriptor(), 388 szRealResolvConf, &offRealResolvConfName); 389 if (iWdSymDirNew < 0) 390 Log5Func(("dir: moved_to / created: re-stablished symlinked-directory monitoring: iWdSymDir=%d (%s/%s)\n", 391 iWdSymDirNew, szRealResolvConf, &szRealResolvConf[offRealResolvConfName])); 392 } 393 } 394 else 395 AssertMsgFailed(("dir: %#x\n", pCurEvt->mask)); 396 } 397 } 398 /* 399 * The directory of a symlinked resolv.conf. 400 * 401 * Where we only care when the symlink target is created, moved_to, 402 * deleted or moved_from - i.e. a minimal version of the /etc event 403 * processing above. 404 * 405 * Note! Since we re-statablish monitoring above, szRealResolvConf 406 * might not match the event we're processing. Fortunately, 407 * this shouldn't be important except for debug logging. 408 */ 409 else if (pCurEvt->wd == iWdSymDir) 410 { 411 if ( pCurEvt->len > 0 412 && offRealResolvConfName > 0 413 && strcmp(&szRealResolvConf[offRealResolvConfName], pCurEvt->name) == 0) 414 { 415 if (iWdFileNew >= 0) 416 { 417 rc = inotify_rm_watch(Notify.fileDescriptor(), iWdFileNew); 418 Log5Func(("symdir: moved / created / deleted: drop file watch (%d - rc=%d/err=%d)\n", 419 iWdFileNew, rc, errno)); 420 iWdFileNew = -1; 421 } 422 if (pCurEvt->mask & (IN_MOVED_TO | IN_CREATE)) 423 { 424 Log5Func(("symdir: moved_to / created: trigger re-read\n")); 425 fTryReRead = true; 426 } 427 } 428 } 429 /* We can get here it seems if our inotify_rm_watch calls above takes 430 place after new events relating to the two descriptors happens. */ 211 431 else 432 Log5Func(("Unknown (obsoleted) wd value: %d (mask=%#x cookie=%#x len=%#x)\n", 433 pCurEvt->wd, pCurEvt->mask, pCurEvt->cookie, pCurEvt->len)); 434 435 /* advance to the next event */ 436 Assert(pCurEvt->len / INOTIFY_EVENT_SIZE * INOTIFY_EVENT_SIZE == pCurEvt->len); 437 size_t const cbCurEvt = INOTIFY_EVENT_SIZE + pCurEvt->len; 438 pCurEvt = (struct inotify_event const *)((uintptr_t)pCurEvt + cbCurEvt); 439 cbEvents -= cbCurEvt; 440 } 441 442 /* 443 * Commit the new watch descriptor numbers now that we're 444 * done processing event using the old ones. 445 */ 446 iWdFile = iWdFileNew; 447 iWdSymDir = iWdSymDirNew; 448 449 /* 450 * If the resolv.conf watch descriptor is -1, try restablish it here. 451 */ 452 if (iWdFile == -1) 453 { 454 iWdFile = inotify_add_watch(Notify.fileDescriptor(), g_szResolvConfPath, IN_CLOSE_WRITE | IN_DELETE_SELF); 455 if (iWdFile >= 0) 212 456 { 213 /* 214 * It shouldn't happen, in release we will just ignore in debug 215 * we will have to chance to look at into inotify_event 216 */ 217 AssertMsgFailed(("Debug Me!!!")); 457 Log5Func(("Re-established file watcher: iWdFile=%d\n", iWdFile)); 458 fTryReRead = true; 218 459 } 219 460 } 220 else if (combo.e.wd == wd[1]) 461 462 /* 463 * If any of the events indicate that we should re-read the file, we 464 * do so now. Should reduce number of unnecessary re-reads. 465 */ 466 if (fTryReRead) 221 467 { 222 if (combo.e.mask & (IN_DELETE | IN_MOVED_FROM)) 223 { 224 if (g_ResolvConf == combo.e.name) 225 { 226 /* 227 * Our file has been moved or deleted so we should change watching mode. 228 */ 229 inotify_rm_watch(a.fileDescriptor(), wd[0]); 230 wd[1] = inotify_add_watch(a.fileDescriptor(), g_EtcFolder.c_str(), 231 IN_MOVED_TO | IN_CREATE); 232 AssertMsg(wd[1] != -1, 233 ("It shouldn't happen, further investigation is needed\n")); 234 } 235 } 236 else 237 { 238 AssertMsg(combo.e.mask & (IN_MOVED_TO | IN_CREATE), 239 ("%RX32 event isn't expected, we are waiting for IN_MOVED|IN_CREATE\n", combo.e.mask)); 240 if (g_ResolvConf == combo.e.name) 241 { 242 AssertMsg(wd[0] == -1, ("We haven't removed file watcher first\n")); 243 244 /* alter folder watcher: */ 245 wd[1] = inotify_add_watch(a.fileDescriptor(), 246 g_EtcFolder.c_str(), 247 IN_MOVED_FROM | IN_DELETE); 248 AssertMsg(wd[1] != -1, ("It shouldn't happen.\n")); 249 250 wd[0] = inotify_add_watch(a.fileDescriptor(), 251 g_ResolvConfFullPath.c_str(), 252 IN_CLOSE_WRITE | IN_DELETE_SELF); 253 AssertMsg(wd[0] != -1, ("Adding watcher to file (%s) has been failed!\n", 254 g_ResolvConfFullPath.c_str())); 255 256 /* Notify our listeners */ 257 readResolvConf(); 258 } 259 } 260 } 261 else 262 { 263 /* It shouldn't happen */ 264 AssertMsgFailed(("Shouldn't happen! Please debug me!")); 468 Log5Func(("Calling readResolvConf()...\n")); 469 readResolvConf(); 265 470 } 266 471 }
Note:
See TracChangeset
for help on using the changeset viewer.