root/util_misc.cpp
/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following definitions.
- lockFile
- unlockFile
- strStartsWith
- strEndsWith
- strStartsWith
- strEndsWith
- joinStrings
- removeSuffix
- tokenizeString
- split
- createDirectoryTree
- writeAll
- readAll
- skipBytes
- changeFd
- dupFds
- readLine
- readDec
- readHex
- readChar
- readProcMapsLine
- memProtToOpenFlags
- getTracerPid
- isPtraced
- isValidFd
- pageSize
- pageMask
- areZeroPages
- findExecutable
- isNscdArea
- isSysVShmArea
- 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 }