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  	{
223  	  int fd = _real_open ("/proc/self/fd", O_RDONLY | O_NDELAY |
224  	                                        O_LARGEFILE | O_DIRECTORY, 0);
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  	
233  	  while (true) {
234  	    int nread = _real_syscall(SYS_getdents, fd, buf, allocation);
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  	
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  	
Event secure_coding: [VERY RISKY]. Using "sscanf" can cause a buffer overflow when done incorrectly. sscanf() assumes an arbitrarily large string, so callers must use correct precision specifiers or never use sscanf(). Use correct precision specifiers or do your own parsing.
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