root/util_misc.cpp

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. lockFile
  2. unlockFile
  3. strStartsWith
  4. strEndsWith
  5. strStartsWith
  6. strEndsWith
  7. joinStrings
  8. removeSuffix
  9. tokenizeString
  10. split
  11. createDirectoryTree
  12. writeAll
  13. readAll
  14. skipBytes
  15. changeFd
  16. dupFds
  17. readLine
  18. readDec
  19. readHex
  20. readChar
  21. readProcMapsLine
  22. memProtToOpenFlags
  23. getTracerPid
  24. isPtraced
  25. isValidFd
  26. pageSize
  27. pageMask
  28. areZeroPages
  29. findExecutable
  30. isNscdArea
  31. isSysVShmArea
  32. isIBShmArea

   1 /****************************************************************************
   2  *   Copyright (C) 2006-2013 by Jason Ansel, Kapil Arya, and Gene Cooperman *
   3  *   jansel@csail.mit.edu, kapil@ccs.neu.edu, gene@ccs.neu.edu              *
   4  *                                                                          *
   5  *  This file is part of DMTCP.                                             *
   6  *                                                                          *
   7  *  DMTCP is free software: you can redistribute it and/or                  *
   8  *  modify it under the terms of the GNU Lesser General Public License as   *
   9  *  published by the Free Software Foundation, either version 3 of the      *
  10  *  License, or (at your option) any later version.                         *
  11  *                                                                          *
  12  *  DMTCP is distributed in the hope that it will be useful,                *
  13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of          *
  14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the           *
  15  *  GNU Lesser General Public License for more details.                     *
  16  *                                                                          *
  17  *  You should have received a copy of the GNU Lesser General Public        *
  18  *  License along with DMTCP:dmtcp/src.  If not, see                        *
  19  *  <http://www.gnu.org/licenses/>.                                         *
  20  ****************************************************************************/
  21 
  22 #include <stdlib.h>
  23 #include <string.h>
  24 #include <fcntl.h>
  25 #include  "util.h"
  26 #include  "membarrier.h"
  27 #include  "syscallwrappers.h"
  28 #include  "dmtcp.h"
  29 #include  "../jalib/jassert.h"
  30 #include  "../jalib/jfilesystem.h"
  31 
  32 using namespace dmtcp;
  33 
  34 void Util::lockFile(int fd)
  35 {
  36   struct flock fl;
  37 
  38   fl.l_type   = F_WRLCK;  // F_RDLCK, F_WRLCK, F_UNLCK
  39   fl.l_whence = SEEK_SET; // SEEK_SET, SEEK_CUR, SEEK_END
  40   fl.l_start  = 0;        // Offset from l_whence
  41   fl.l_len    = 0;        // length, 0 = to EOF
  42   //fl.l_pid    = _real_getpid(); // our PID
  43 
  44   int result = -1;
  45   errno = 0;
  46   do {
  47     result = _real_fcntl(fd, F_SETLKW, &fl);  /* F_GETLK, F_SETLK, F_SETLKW */
  48   } while (result == -1 && errno == EINTR);
  49 
  50   JASSERT (result != -1) (JASSERT_ERRNO)
  51     .Text("Unable to lock the PID MAP file");
  52 #if (__arm__ || __aarch64__)
  53   WMB;  // DMB, ensure writes by others to memory have completed before we
  54         //      we enter protected region.
  55 #endif
  56 }
  57 
  58 void Util::unlockFile(int fd)
  59 {
  60   struct flock fl;
  61   int result;
  62 
  63 #if (__arm__ || __aarch64__)
  64   RMB; WMB; // DMB, ensure accesses to protected memory have completed
  65             //      before releasing lock
  66 #endif
  67   fl.l_type   = F_UNLCK;  // tell it to unlock the region
  68   fl.l_whence = SEEK_SET; // SEEK_SET, SEEK_CUR, SEEK_END
  69   fl.l_start  = 0;        // Offset from l_whence
  70   fl.l_len    = 0;        // length, 0 = to EOF
  71 
  72 #if (__arm__ || __aarch64__)
  73   WMB;  // DSB, ensure update of fl before seen by other CPUs
  74 #endif
  75 
  76   result = _real_fcntl(fd, F_SETLK, &fl); /* set the region to unlocked */
  77 
  78   JASSERT (result != -1 || errno == ENOLCK) (JASSERT_ERRNO)
  79     .Text("Unlock Failed");
  80 }
  81 
  82 bool Util::strStartsWith(const char *str, const char *pattern)
  83 {
  84   if (str == NULL || pattern == NULL) {
  85     return false;
  86   }
  87   int len1 = strlen(str);
  88   int len2 = strlen(pattern);
  89   if (len1 >= len2) {
  90     return strncmp(str, pattern, len2) == 0;
  91   }
  92   return false;
  93 }
  94 
  95 bool Util::strEndsWith(const char *str, const char *pattern)
  96 {
  97   if (str == NULL || pattern == NULL) {
  98     return false;
  99   }
 100   int len1 = strlen(str);
 101   int len2 = strlen(pattern);
 102   if (len1 >= len2) {
 103     size_t idx = len1 - len2;
 104     return strncmp(str+idx, pattern, len2) == 0;
 105   }
 106   return false;
 107 }
 108 
 109 bool Util::strStartsWith(const string& str, const char *pattern)
 110 {
 111   return strStartsWith(str.c_str(), pattern);
 112 }
 113 
 114 bool Util::strEndsWith(const string& str, const char *pattern)
 115 {
 116   return strEndsWith(str.c_str(), pattern);
 117 }
 118 
 119 string Util::joinStrings(vector<string> v, const string& delim)
 120 {
 121   string result;
 122   if (v.size() > 0) {
 123     result = v[0];
 124     for (size_t i = 1; i < v.size(); i++) {
 125       result += delim + v[i];
 126     }
 127   }
 128   return result;
 129 }
 130 
 131 
 132 string Util::removeSuffix(const string& s, const string& suffix)
 133 {
 134   if (strEndsWith(s, suffix.c_str())) {
 135     string result(s, s.length() - suffix.length());
 136     return result;
 137   }
 138   return s;
 139 }
 140 
 141 
 142 // Tokenizes the string using the delimiters.
 143 // Empty tokens will not be included in the result.
 144 vector<string> Util::tokenizeString(const string& s, const string& delims)
 145 {
 146   size_t offset = 0;
 147   vector<string> tokens;
 148 
 149   while (true) {
 150     size_t i = s.find_first_not_of(delims, offset);
 151     if (i == string::npos) {
 152       break;
 153     }
 154 
 155     size_t j = s.find_first_of(delims, i);
 156     if (j == string::npos) {
 157       tokens.push_back(s.substr(i));
 158       offset = s.length();
 159       continue;
 160     }
 161 
 162     tokens.push_back(s.substr(i, j - i));
 163     offset = j;
 164   }
 165   return tokens;
 166 }
 167 
 168 // Add it back if needed.
 169 #if 0
 170 // Splits the string using the provided delimiters.
 171 // The string is split each time at the first character
 172 // that matches any of the characters specified in delims.
 173 // Empty tokens are allowed in the result.
 174 // Optionally, maximum number of tokens to be returned
 175 // can be specified.
 176 inline vector<string> split(
 177     const string& s,
 178     const string& delims,
 179     const Option<unsigned int>& n = None())
 180 {
 181   vector<string> tokens;
 182   size_t offset = 0;
 183   size_t next = 0;
 184 
 185   while (n.isNone() || n.get() > 0) {
 186     next = s.find_first_of(delims, offset);
 187     if (next == string::npos) {
 188       tokens.push_back(s.substr(offset));
 189       break;
 190     }
 191 
 192     tokens.push_back(s.substr(offset, next - offset));
 193     offset = next + 1;
 194 
 195     // Finish splitting if we've found enough tokens.
 196     if (n.isSome() && tokens.size() == n.get() - 1) {
 197       tokens.push_back(s.substr(offset));
 198       break;
 199     }
 200   }
 201   return tokens;
 202 }
 203 #endif
 204 
 205 bool Util::createDirectoryTree(const string& path)
 206 {
 207   size_t index = path.rfind('/');
 208 
 209   if (index == string::npos)
 210     return true;
 211 
 212   string dir = path.substr(0, index);
 213 
 214   index = path.find('/');
 215   while (index != string::npos) {
 216     if (index > 1) {
 217       string dirName = path.substr(0, index);
 218 
 219       errno = 0;
 220       int res = mkdir(dirName.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
 221 #ifdef STAMPEDE_LUSTRE_FIX
 222       if (res < 0) {
 223         if (errno == EACCES) {
 224           struct stat buff;
 225           int ret = stat(dirName.c_str(), &buff);
 226           if (ret != 0) {
 227             return false;
 228           };
 229         } else if (errno == EEXIST) {
 230           /* do nothing */
 231         } else {
 232           return false;
 233         }
 234       }
 235 #else
 236       if (res == -1 && errno != EEXIST) {
 237         return false;
 238       }
 239 #endif
 240     }
 241     index = path.find('/', index+1);
 242   }
 243   return true;
 244 }
 245 
 246 // Fails or does entire write (returns count)
 247 ssize_t Util::writeAll(int fd, const void *buf, size_t count)
 248 {
 249   const char *ptr = (const char *) buf;
 250   size_t num_written = 0;
 251 
 252   do {
 253     ssize_t rc = _real_write (fd, ptr + num_written, count - num_written);
 254     if (rc == -1) {
 255       if (errno == EINTR || errno == EAGAIN)
 256         continue;
 257       else
 258         return rc;
 259     }
 260     else if (rc == 0)
 261       break;
 262     else // else rc > 0
 263       num_written += rc;
 264   } while (num_written < count);
 265   JASSERT (num_written == count) (num_written) (count);
 266   return num_written;
 267 }
 268 
 269 // Fails, succeeds, or partial read due to EOF (returns num read)
 270 // return value:
 271 //    -1: unrecoverable error
 272 //   <n>: number of bytes read
 273 ssize_t Util::readAll(int fd, void *buf, size_t count)
 274 {
 275   ssize_t rc;
 276   char *ptr = (char *) buf;
 277   size_t num_read = 0;
 278   for (num_read = 0; num_read < count;) {
 279     rc = _real_read (fd, ptr + num_read, count - num_read);
 280     if (rc == -1) {
 281       if (errno == EINTR || errno == EAGAIN)
 282         continue;
 283       else
 284         return -1;
 285     }
 286     else if (rc == 0)
 287       break;
 288     else // else rc > 0
 289       num_read += rc;
 290   }
 291   return num_read;
 292 }
 293 
 294 ssize_t Util::skipBytes(int fd, size_t count)
 295 {
 296   char buf[1024];
 297   ssize_t rc;
 298   ssize_t totalSkipped = 0;
 299   while (count > 0) {
 300     rc = Util::readAll(fd, buf, MIN(count, sizeof(buf)));
 301 
 302     if (rc == -1) {
 303       break;
 304     }
 305     count -= rc;
 306     totalSkipped += rc;
 307   }
 308   return totalSkipped;
 309 }
 310 
 311 void Util::changeFd(int oldfd, int newfd)
 312 {
 313   if (oldfd != newfd) {
 314     JASSERT(_real_dup2(oldfd, newfd) == newfd);
 315     _real_close(oldfd);
 316   }
 317 }
 318 
 319 void Util::dupFds(int oldfd, const vector<int>& newfds)
 320 {
 321   changeFd(oldfd, newfds[0]);
 322   for (size_t i = 1; i < newfds.size(); i++) {
 323     JASSERT(_real_dup2(newfds[0], newfds[i]) == newfds[i]);
 324   }
 325 }
 326 
 327 
 328 /* Begin miscellaneous/helper functions. */
 329 
 330 /* Reads from fd until count bytes are read, or
 331  * newline encountered.
 332  *
 333  * Side effects: Copies the characters, including
 334  * the newline, read from the fd into the buf.
 335  *
 336  * Returns num of chars read on success;
 337  *         -1 on read failure or invalid args; and
 338  *         -2 if the buffer is too small
 339  */
 340 int Util::readLine(int fd, char *buf, int count)
 341 {
 342   int i = 0;
 343   char c;
 344   JASSERT(fd >= 0 && buf != NULL) (fd) ((void*)buf);
 345 #define NEWLINE '\n' // Linux, OSX
 346   while (i < count) {
 347     ssize_t rc = read(fd, &c, 1);
 348     if (rc == 0) {
 349       break;
 350     } else if (rc < 0) {
 351       buf[i] = '\0';
 352       return -1;
 353     } else {
 354       buf[i++] = c;
 355       if (c == NEWLINE) break;
 356     }
 357   }
 358   buf[i] = '\0';
 359   if (i >= count)
 360     return -2;
 361   else
 362     return i;
 363 }
 364 
 365 /* Read decimal number, return value and terminating character */
 366 
 367 char Util::readDec (int fd, VA *value)
 368 {
 369   char c;
 370   unsigned long int v;
 371 
 372   v = 0;
 373   while (1) {
 374     c = readChar (fd);
 375     if ((c >= '0') && (c <= '9')) c -= '0';
 376     else break;
 377     v = v * 10 + c;
 378   }
 379   *value = (VA)v;
 380   return (c);
 381 }
 382 
 383 /* Read decimal number, return value and terminating character */
 384 
 385 char Util::readHex (int fd, VA *value)
 386 {
 387   char c;
 388   unsigned long int v;
 389 
 390   v = 0;
 391   while (1) {
 392     c = readChar (fd);
 393          if ((c >= '0') && (c <= '9')) c -= '0';
 394     else if ((c >= 'a') && (c <= 'f')) c -= 'a' - 10;
 395     else if ((c >= 'A') && (c <= 'F')) c -= 'A' - 10;
 396     else break;
 397     v = v * 16 + c;
 398   }
 399   *value = (VA)v;
 400   return (c);
 401 }
 402 
 403 /* Read non-null character, return null if EOF */
 404 
 405 char Util::readChar (int fd)
 406 {
 407   char c;
 408   int rc;
 409 
 410   do {
 411     rc = _real_read (fd, &c, 1);
 412   } while ( rc == -1 && errno == EINTR );
 413   if (rc <= 0) return (0);
 414   return (c);
 415 }
 416 
 417 
 418 int Util::readProcMapsLine(int mapsfd, ProcMapsArea *area)
 419 {
 420   char c, rflag, sflag, wflag, xflag;
 421   int i;
 422   off_t offset;
 423   unsigned int long devmajor, devminor, inodenum;
 424   VA startaddr, endaddr;
 425 
 426   c = readHex (mapsfd, &startaddr);
 427   if ((c == 0) && (startaddr == 0)) return (0);
 428   if (c != '-') {
 429     goto skipeol;
 430   }
 431   c = readHex (mapsfd, &endaddr);
 432   if (c != ' ') goto skipeol;
 433   if (endaddr < startaddr) goto skipeol;
 434 
 435   rflag = c = readChar (mapsfd);
 436   if ((c != 'r') && (c != '-')) goto skipeol;
 437   wflag = c = readChar (mapsfd);
 438   if ((c != 'w') && (c != '-')) goto skipeol;
 439   xflag = c = readChar (mapsfd);
 440   if ((c != 'x') && (c != '-')) goto skipeol;
 441   sflag = c = readChar (mapsfd);
 442   if ((c != 's') && (c != 'p')) goto skipeol;
 443 
 444   c = readChar (mapsfd);
 445   if (c != ' ') goto skipeol;
 446 
 447   c = readHex (mapsfd, (VA *)&offset);
 448   if (c != ' ') goto skipeol;
 449   area -> offset = offset;
 450 
 451   c = readHex (mapsfd, (VA *)&devmajor);
 452   if (c != ':') goto skipeol;
 453   c = readHex (mapsfd, (VA *)&devminor);
 454   if (c != ' ') goto skipeol;
 455   c = readDec (mapsfd, (VA *)&inodenum);
 456   area -> name[0] = '\0';
 457   while (c == ' ') c = readChar (mapsfd);
 458   if (c == '/' || c == '[' || c == '(') {
 459     // absolute pathname, or [stack], [vdso], etc.
 460     // On some machines, deleted files have a " (deleted)" prefix to the
 461     // filename.
 462     i = 0;
 463     do {
 464       area -> name[i++] = c;
 465       if (i == sizeof area -> name) goto skipeol;
 466       c = readChar (mapsfd);
 467     } while (c != '\n');
 468     area -> name[i] = '\0';
 469   }
 470 
 471   if (c != '\n') goto skipeol;
 472 
 473   area -> addr = startaddr;
 474   area -> size = endaddr - startaddr;
 475   area -> endAddr = endaddr;
 476   area -> prot = 0;
 477   if (rflag == 'r') area -> prot |= PROT_READ;
 478   if (wflag == 'w') area -> prot |= PROT_WRITE;
 479   if (xflag == 'x') area -> prot |= PROT_EXEC;
 480   area -> flags = MAP_FIXED;
 481   if (sflag == 's') area -> flags |= MAP_SHARED;
 482   if (sflag == 'p') area -> flags |= MAP_PRIVATE;
 483   if (area -> name[0] == '\0') area -> flags |= MAP_ANONYMOUS;
 484 
 485   area->devmajor = devmajor;
 486   area->devminor = devminor;
 487   area->inodenum = inodenum;
 488   return (1);
 489 
 490 skipeol:
 491   JASSERT(false) .Text("Not Reached");
 492   return (0);  /* NOTREACHED : stop compiler warning */
 493 }
 494 
 495 int Util::memProtToOpenFlags(int prot)
 496 {
 497   if (prot & (PROT_READ | PROT_WRITE)) return O_RDWR;
 498   if (prot & PROT_READ) return O_RDONLY;
 499   if (prot & PROT_WRITE) return O_WRONLY;
 500   return 0;
 501 }
 502 
 503 #define TRACER_PID_STR "TracerPid:"
 504 pid_t Util::getTracerPid(pid_t tid)
 505 {
 506   if (!dmtcp_real_to_virtual_pid) {
 507     return 0;
 508   }
 509 
 510   char buf[512];
 511   char *str;
 512   static int tracerStrLen = strlen(TRACER_PID_STR);
 513   int fd;
 514 
 515   if (tid == -1) {
 516     tid = dmtcp_gettid();
 517   }
 518   sprintf(buf, "/proc/%d/status", tid);
 519   fd = _real_open(buf, O_RDONLY, 0);
 520   JASSERT(fd != -1) (buf) (JASSERT_ERRNO);
 521   readAll(fd, buf, sizeof buf);
 522   _real_close(fd);
 523   str = strstr(buf, TRACER_PID_STR);
 524   JASSERT(str != NULL);
 525   str += tracerStrLen;
 526 
 527   while (*str == ' ' || *str == '\t') {
 528     str++;
 529   }
 530 
 531   pid_t tracerPid = (pid_t) strtol(str, NULL, 10);
 532   return tracerPid == 0 ? tracerPid : dmtcp_real_to_virtual_pid(tracerPid);
 533 }
 534 
 535 bool Util::isPtraced()
 536 {
 537   return getTracerPid() != 0;
 538 }
 539 
 540 bool Util::isValidFd(int fd)
 541 {
 542   return _real_fcntl(fd, F_GETFL, 0) != -1;
 543 }
 544 
 545 size_t Util::pageSize()
 546 {
 547   static size_t page_size = sysconf(_SC_PAGESIZE);
 548   return page_size;
 549 }
 550 
 551 size_t Util::pageMask()
 552 {
 553   static size_t page_mask = ~(pageSize() - 1);
 554   return page_mask;
 555 }
 556 
 557 /* This function detects if the given pages are zero pages or not. There is
 558  * scope of improving this function using some optimizations.
 559  *
 560  * TODO: One can use /proc/self/pagemap to detect if the page is backed by a
 561  * shared zero page.
 562  */
 563 bool Util::areZeroPages(void *addr, size_t numPages)
 564 {
 565   static size_t page_size = pageSize();
 566   long long *buf = (long long*) addr;
 567   size_t i;
 568   size_t end = numPages * page_size / sizeof (*buf);
 569   long long res = 0;
 570   for (i = 0; i + 7 < end; i += 8) {
 571     res = buf[i+0] | buf[i+1] | buf[i+2] | buf[i+3] |
 572           buf[i+4] | buf[i+5] | buf[i+6] | buf[i+7];
 573     if (res != 0) {
 574       break;
 575     }
 576   }
 577   return res == 0;
 578 }
 579 
 580 /* Caller must allocate exec_path of size at least MTCP_MAX_PATH */
 581 char *Util::findExecutable(char *executable, const char* path_env,
 582                                   char *exec_path)
 583 {
 584   char *path;
 585   const char *tmp_env;
 586   int len;
 587 
 588   JASSERT(exec_path != NULL);
 589   if (path_env == NULL) {
 590     path_env = ""; // Will try stdpath later in this function
 591   }
 592   tmp_env = path_env;
 593 
 594   while (*tmp_env != '\0') {
 595     path = exec_path;
 596     len = 0;
 597     while (*tmp_env != ':' && *tmp_env != '\0' && ++len < PATH_MAX - 1)
 598       *path++ = *tmp_env++;
 599     if (*tmp_env == ':') /* but if *tmp_env == '\0', will exit while loop */
 600       tmp_env++;
 601     *path++ = '/'; /* '...//... is same as .../... in POSIX */
 602     len++;
 603     *path++ = '\0';
 604     strncat(exec_path, executable, PATH_MAX - len - 1);
 605     if (access(exec_path, X_OK) == 0){
 606       // Artem: Additionally check that this is regular file.
 607       // From access point of view directories are executables too :)
 608       // I ran into problem on the system where user home dir was in the PATH
 609       // and I create a directory named "hbict" in it.
 610       // Eventually home path was before my sandbox path and DMTCP was
 611       // trying to call a directory :)
 612       struct stat buf;
 613       if( stat(exec_path, &buf) ){
 614         continue;
 615       }
 616       if( S_ISREG(buf.st_mode) )
 617         return exec_path;
 618     }
 619   }
 620 
 621   // In case we're running with PATH environment variable unset:
 622   const char * stdpath = "/usr/local/bin:/usr/bin:/bin";
 623   if (strcmp(path_env, stdpath) == 0) {
 624     return NULL;  // Already tried stdpath
 625   } else {
 626     return findExecutable(executable, stdpath, exec_path);
 627   }
 628 }
 629 
 630 // Check for NSCD area.
 631 bool Util::isNscdArea(const ProcMapsArea& area)
 632 {
 633   if (strStartsWith(area.name, "/run/nscd") || // OpenSUSE (newer)
 634       strStartsWith(area.name, "/var/run/nscd") || // OpenSUSE (older)
 635       strStartsWith(area.name, "/var/cache/nscd") || // Debian/Ubuntu
 636       strStartsWith(area.name, "/var/db/nscd")) { // RedHat/Fedora
 637     return true;
 638   }
 639   return false;
 640 }
 641 
 642 // Check for Sys V shared memory area.
 643 bool Util::isSysVShmArea(const ProcMapsArea& area)
 644 {
 645   return strStartsWith(area.name, "/SYSV");
 646 }
 647 
 648 // Check for Sys V shared memory area.
 649 bool Util::isIBShmArea(const ProcMapsArea& area)
 650 {
 651   return strStartsWith(area.name, "/dev/infiniband/uverbs");
 652 }

/* [<][>][^][v][top][bottom][index][help] */