1    	/****************************************************************************
2    	 *   Copyright (C) 2006-2008 by Jason Ansel                                 *
3    	 *   jansel@csail.mit.edu                                                   *
4    	 *                                                                          *
5    	 *   This file is part of the JALIB module of DMTCP (DMTCP:dmtcp/jalib).    *
6    	 *                                                                          *
7    	 *  DMTCP:dmtcp/jalib 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:dmtcp/src 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 <sys/types.h>
23   	#include <sys/stat.h>
24   	#include <fcntl.h>
25   	#include <unistd.h>
26   	#include <dirent.h>
27   	#include <algorithm>
28   	#include <errno.h>
29   	#include <sys/utsname.h>
30   	#include <sys/syscall.h>
31   	#include "jfilesystem.h"
32   	#include "jconvert.h"
33   	#include "syscallwrappers.h"
34   	#include "util.h"
35   	
36   	namespace
37   	{
38   	  jalib::string _GetProgramExe()
39   	  {
40   	    jalib::string exe = "/proc/self/exe";
41   	    jalib::string exeRes = jalib::Filesystem::ResolveSymlink ( exe );
42   	    JASSERT ( exe != exeRes ) ( exe ).Text ( "problem with /proc/self/exe" );
43   	    return exeRes;
44   	  }
45   	
46   	  // Set buf, and return length read (including all null characters)
47   	  int _GetProgramCmdline(char *buf, int size)
48   	  {
49   	    int fd = open("/proc/self/cmdline", O_RDONLY);
50   	    int rc;
51   	    int count = 0;
52   	    JASSERT(fd >= 0);
53   	    // rc == 0 means EOF, or else it means buf is full (size chars read)
54   	    rc = dmtcp::Util::readAll(fd, buf, size);
55   	    return rc;
56   	  }
57   	
58   	}
59   	
60   	jalib::string jalib::Filesystem::GetCWD()
61   	{
62   	  jalib::string cwd;
63   	  char buf[PATH_MAX];
64   	  JASSERT(getcwd(buf, PATH_MAX) == buf)
65   	    .Text("Pathname too long");
66   	  cwd = buf;
67   	  return cwd;
68   	}
69   	
70   	jalib::string jalib::Filesystem::FileBaseName ( const jalib::string& str )
71   	{
72   	  int lastSlash = 0;
73   	  for ( size_t i = 0; i<str.length(); ++i )
74   	    if ( str[i] == '/' )
75   	      lastSlash = i;
76   	  return str.substr ( lastSlash+1 );
77   	}
78   	
79   	jalib::string jalib::Filesystem::DirBaseName ( const jalib::string& str )
80   	{
81   	  int lastSlash = 0;
82   	  for ( size_t i = 0; i<str.length(); ++i )
83   	    if ( str[i] == '/' )
84   	      lastSlash = i;
85   	  return str.substr ( 0,lastSlash );
86   	}
87   	
88   	jalib::string jalib::Filesystem::GetProgramDir()
89   	{
90   	  static jalib::string value = DirBaseName ( GetProgramPath() );
91   	  return value;
92   	}
93   	
94   	jalib::string jalib::Filesystem::GetProgramName()
95   	{
96   	  static jalib::string value = "";
97   	  if (value == "") {
98   	    int len;
99   	    char cmdline[1024];
100  	    value = FileBaseName ( GetProgramPath() ); // uses /proc/self/exe
101  	    // We may rewrite "a.out" to "/lib/ld-linux.so.2 a.out".  If so, find cmd.
102  	    if (!value.empty()
103  	        && ( value == ResolveSymlink("/lib/ld-linux.so.2")
104  	            || value == ResolveSymlink("/lib64/ld-linux-x86-64.so.2") )
105  		&& (len = _GetProgramCmdline(cmdline, sizeof(cmdline))) > 0
106  		&& len > strlen(cmdline) + 1 // more than one word in cmdline
107  		&& *(cmdline + strlen(cmdline) + 1) != '-') // second word not a flag
108  	      value = FileBaseName(cmdline + strlen(cmdline) + 1); // find second word
109  	  }
110  	  return value;
111  	}
112  	
113  	jalib::string jalib::Filesystem::GetProgramPath()
114  	{
115  	  static jalib::string value = _GetProgramExe();
116  	  return value;
117  	}
118  	
119  	
120  	jalib::string jalib::Filesystem::ResolveSymlink ( const jalib::string& path )
121  	{
122  	  char buf [1024]; // This could be passed on via call to readlink()
123  	  // If path is not a symbolic link, just return it.
124  	  if (lstat(path.c_str(), (struct stat *)buf) == 0
125  	      && ! S_ISLNK(((struct stat *)buf)->st_mode))
126  	    return path;
127  	  memset ( buf,0,sizeof ( buf ) );
128  	  int len = readlink ( path.c_str(), buf, sizeof ( buf )-1 );
129  	  if ( len <= 0 )
130  	    return "";
131  	  return buf;
132  	}
133  	
134  	bool jalib::Filesystem::FileExists ( const jalib::string& str )
135  	{
136  	  struct stat st;
137  	  
138  	  if( !stat(str.c_str(),&st) ){
139  	    return true;
140  	  }else {
141  	    return false;
142  	  } 
143  	  /* Old variant. If file is write-only we fail but this is wrong 
144  	  FILE* fp = fopen ( str.c_str(),"r" );
145  	  if ( fp != NULL ) fclose ( fp );
146  	  return fp != NULL;
147  	   */
148  	}
149  	
150  	#define FHU_TRY_DIR(expr) {\
151  	    jalib::string pth = expr; \
152  	    if(FileExists(pth)) \
153  	        return pth;}
154  	
155  	
156  	jalib::string jalib::Filesystem::FindHelperUtility ( const jalib::string& file, bool dieOnError /*= true*/ )
157  	{
158  	  const char* d = NULL;
159  	  if ( ( d=getenv ( "JALIB_UTILITY_DIR" ) ) != NULL )
160  	  {
161  	    jalib::string udir = d;
162  	    FHU_TRY_DIR ( udir + "/" + file );
163  	    FHU_TRY_DIR ( udir + "/mtcp/" + file );
164  	    FHU_TRY_DIR ( udir + "/../mtcp/" + file );
165  	    FHU_TRY_DIR ( udir + "/../../mtcp/" + file );
166  	    FHU_TRY_DIR ( udir + "/../../../mtcp/" + file );
167  	    FHU_TRY_DIR ( udir + "/../" + file );
168  	    FHU_TRY_DIR ( udir + "/../../" + file );
169  	    FHU_TRY_DIR ( udir + "/../../../" + file );
170  	    FHU_TRY_DIR ( udir + "/../lib/dmtcp/" + file );
171  	  }
172  	  FHU_TRY_DIR ( GetProgramDir() + "/" + file );
173  	  FHU_TRY_DIR ( GetProgramDir() + "/mtcp/" + file );
174  	  FHU_TRY_DIR ( GetProgramDir() + "/../mtcp/" + file );
175  	  FHU_TRY_DIR ( GetProgramDir() + "/../../mtcp/" + file );
176  	  FHU_TRY_DIR ( GetProgramDir() + "/../../../mtcp/" + file );
177  	  FHU_TRY_DIR ( GetProgramDir() + "/../" + file );
178  	  FHU_TRY_DIR ( GetProgramDir() + "/../../" + file );
179  	  FHU_TRY_DIR ( GetProgramDir() + "/../../../" + file );
180  	  FHU_TRY_DIR ( GetProgramDir() + "/../lib/dmtcp/" + file );
181  	  FHU_TRY_DIR ( "./" + file );
182  	  FHU_TRY_DIR ( "../" + file );
183  	  FHU_TRY_DIR ( "../../" + file );
184  	  FHU_TRY_DIR ( "../../../" + file );
185  	  FHU_TRY_DIR ( "/bin/" + file );
186  	  FHU_TRY_DIR ( "/usr/bin/" + file );
187  	  FHU_TRY_DIR ( "/lib/" + file );
188  	  FHU_TRY_DIR ( "/lib64/" + file );
189  	  FHU_TRY_DIR ( "/usr/lib/" + file );
190  	  FHU_TRY_DIR ( "/usr/lib64/" + file );
191  	  JASSERT ( !dieOnError ) ( file ) ( GetProgramDir() ) ( d )
192  	    .Text ( "failed to find needed file" );
193  	  return file;
194  	}
195  	
196  	
197  	jalib::StringVector jalib::Filesystem::GetProgramArgs()
198  	{
199  	  StringVector rv;
200  	
201  	  jalib::string path = "/proc/self/cmdline";
202  	  FILE* args = fopen ( path.c_str(),"r" );
203  	
204  	  JASSERT ( args != NULL ) ( path ).Text ( "failed to open command line" );
205  	
206  	  char * lineptr = ( char* ) malloc ( 512 ); //getdelim will auto-grow this buffer
207  	  size_t len = 511;
208  	
209  	  while ( getdelim ( &lineptr, &len, '\0', args ) >= 0 )
210  	  {
211  	    rv.push_back ( lineptr );
212  	  }
213  	
214  	  free ( lineptr );
215  	
216  	  return rv;
217  	}
218  	
219  	#define MALLOC_SAFE_LISTOPENFDS
220  	#ifdef MALLOC_SAFE_LISTOPENFDS
221  	jalib::IntVector jalib::Filesystem::ListOpenFds()
222  	{
Event negative_return_fn: Function "_real_open("/proc/self/fd", 67584, 0U)" returns a negative number. [details]
Event var_assign: Assigning: signed variable "fd" = "_real_open".
Also see events: [negative_returns]
223  	  int fd = _real_open ("/proc/self/fd", O_RDONLY | O_NDELAY |
224  	                                        O_LARGEFILE | O_DIRECTORY, 0);
At conditional (1): "fd >= 0": Taking false branch.
225  	  JASSERT(fd>=0);
226  	
227  	  const size_t allocation = (4 * BUFSIZ < sizeof (struct dirent64)
228  	                             ? sizeof (struct dirent64) : 4 * BUFSIZ);
229  	  char *buf = (char*) JALLOC_HELPER_MALLOC(allocation);
230  	
231  	  IntVector fdVec;
232  	
At conditional (2): "true": Taking true branch.
233  	  while (true) {
234  	    int nread = _real_syscall(SYS_getdents, fd, buf, allocation);
At conditional (3): "nread == 0": Taking true branch.
235  	    if (nread == 0) {
236  	      break;
237  	    }
238  	    JASSERT(nread > 0);
239  	    for (int pos = 0; pos < nread;) {
240  	      struct linux_dirent *d = (struct linux_dirent *) (&buf[pos]);
241  	      if (d->d_ino > 0) {
242  	        char *ch;
243  	        int fdnum = strtol ( d->d_name, &ch, 10 );
244  	        if ( *ch == 0 && fdnum >= 0 ) {
245  	          fdVec.push_back ( fdnum );
246  	        }
247  	      }
248  	      pos += d->d_reclen;
249  	    }
250  	  }
251  	
Event negative_returns: "fd" is passed to a parameter that cannot be negative. [details]
Also see events: [negative_return_fn][var_assign]
252  	  _real_close(fd);
253  	
254  	  std::sort(fdVec.begin(), fdVec.end());
255  	  JALLOC_HELPER_FREE(buf);
256  	  return fdVec;
257  	}
258  	#else
259  	jalib::IntVector jalib::Filesystem::ListOpenFds()
260  	{
261  	  jalib::string dir = "/proc/self/fd";
262  	  IntVector rv;
263  	  struct dirent **namelist;
264  	  char* p;
265  	  int nents = scandir ( dir.c_str(), &namelist, NULL, versionsort );
266  	  JASSERT ( nents >= 0 ) ( dir ) ( JASSERT_ERRNO ).Text ( "failed to open directory" );
267  	
268  	  for ( int i = 0; i < nents; i ++ )
269  	  {
270  	    struct dirent * de = namelist[i];
271  	    int fdnum = strtol ( de -> d_name, &p, 10 );
272  	    if ( *p == 0 && fdnum >= 0 )
273  	    {
274  	      rv.push_back ( fdnum );
275  	    }
276  	    free ( de );
277  	  }
278  	  free ( namelist );
279  	
280  	  return rv;
281  	}
282  	#endif
283  	
284  	jalib::string jalib::Filesystem::GetCurrentHostname()
285  	{
286  	  struct utsname tmp;
287  	  memset ( &tmp,0,sizeof ( tmp ) );
288  	  JASSERT(uname ( &tmp ) != -1) (JASSERT_ERRNO);
289  	  jalib::string name = "unknown";
290  	  if ( strlen(tmp.nodename) != 0 )
291  	    name = tmp.nodename;
292  	//   #ifdef _GNU_SOURCE
293  	//   if(tmp.domainname != 0)
294  	//     name += jalib::string(".") + tmp.domainname;
295  	//   #endif
296  	  return name;
297  	}
298  	
299  	jalib::string jalib::Filesystem::GetControllingTerm()
300  	{
301  	  char sbuf[1024];
302  	  jalib::ostringstream ttyName;
303  	  char *tmp;
304  	  char *S;
305  	  char state;
306  	  int ppid, pgrp, session, tty, tpgid;
307  	
308  	  int fd, num_read;
309  	
310  	  fd = open("/proc/self/stat", O_RDONLY, 0);
311  	  JASSERT( fd >= 0 ) (strerror(errno))
312  	    .Text ("Unable to open /proc/self/stat\n");
313  	
314  	  num_read = read(fd, sbuf, sizeof sbuf - 1);
315  	  close(fd);
316  	  if(num_read<=0) return NULL;
317  	  sbuf[num_read] = '\0';
318  	
319  	  S = strchr(sbuf, '(') + 1;
320  	  tmp = strrchr(S, ')');
321  	  S = tmp + 2;                 // skip ") "
322  	
323  	  sscanf(S,
324  	      "%c "
325  	      "%d %d %d %d %d ",
326  	      &state,
327  	      &ppid, &pgrp, &session, &tty, &tpgid
328  	      );
329  	
330  	  int maj =  ((unsigned)(tty)>>8u) & 0xfffu;
331  	  int min =  ((unsigned)(tty)&0xffu) | (((unsigned)(tty)&0xfff00000u)>>12u);
332  	
333  	  /* /dev/pts/ * has major numbers in the range 136 - 143 */
334  	  if ( maj >= 136 && maj <= 143) 
335  	    ttyName << "/dev/pts/" << min+(maj-136)*256;
336  	  else
337  	    ttyName << "";
338  	
339  	  return ttyName.str();
340  	}
341