1    	/****************************************************************************
2    	 *   Copyright (C) 2006-2010 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 the dmtcp/src module of DMTCP (DMTCP:dmtcp/src).  *
6    	 *                                                                          *
7    	 *  DMTCP:dmtcp/src 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 "uniquepid.h"
23   	#include <stdlib.h>
24   	#include <string.h>
25   	#include <string>
26   	#include <pwd.h>
27   	#include <sstream>
28   	#include <fcntl.h>
29   	#include <sys/stat.h>
30   	#include "constants.h"
31   	#include "../jalib/jconvert.h"
32   	#include "../jalib/jfilesystem.h"
33   	#include "../jalib/jserialize.h"
34   	#include "syscallwrappers.h"
35   	#include "protectedfds.h"
36   	
37   	static dmtcp::string checkpointFilename_str;
38   	static dmtcp::string ckptFilesDirName_str;
39   	
40   	inline static long theUniqueHostId(){
41   	#ifdef USE_GETHOSTID
42   	  return ::gethostid()
43   	#else
44   	  //gethostid() calls socket() on some systems, which we don't want
45   	  char buf[512];
46   	  JASSERT(::gethostname(buf, sizeof(buf))==0)(JASSERT_ERRNO);
47   	  //so return a bad hash of our hostname
48   	  long h = 0;
49   	  for(char* i=buf; *i!='\0'; ++i)
50   	    h = (*i) + (331*h);
51   	  //make it positive for good measure
52   	  return h>0 ? h : -1*h;
53   	#endif
54   	}
55   	
56   	
57   	static dmtcp::UniquePid& nullProcess()
58   	{
59   	  static char buf[sizeof(dmtcp::UniquePid)];
60   	  static dmtcp::UniquePid* t=NULL;
61   	  if(t==NULL) t = new (buf) dmtcp::UniquePid(0,0,0);
62   	  return *t;
63   	}
64   	static dmtcp::UniquePid& theProcess()
65   	{
66   	  static char buf[sizeof(dmtcp::UniquePid)];
67   	  static dmtcp::UniquePid* t=NULL;
68   	  if(t==NULL) t = new (buf) dmtcp::UniquePid(0,0,0);
69   	  return *t;
70   	}
71   	static dmtcp::UniquePid& parentProcess()
72   	{
73   	  static char buf[sizeof(dmtcp::UniquePid)];
74   	  static dmtcp::UniquePid* t=NULL;
75   	  if(t==NULL) t = new (buf) dmtcp::UniquePid(0,0,0);
76   	  return *t;
77   	}
78   	
79   	// _generation field of return value may later have to be modified.
80   	// So, it can't return a const dmtcp::UniquePid
81   	dmtcp::UniquePid& dmtcp::UniquePid::ThisProcess(bool disableJTrace /*=false*/)
82   	{
83   	  if ( theProcess() == nullProcess() )
84   	  {
85   	    theProcess() = dmtcp::UniquePid ( theUniqueHostId() ,
86   	                                      ::_real_getpid(),
87   	                                      ::time(NULL) );
88   	    if (disableJTrace == false)
89   	      JTRACE ( "recalculated process UniquePid..." ) ( theProcess() );
90   	  }
91   	
92   	  return theProcess();
93   	}
94   	
95   	dmtcp::UniquePid& dmtcp::UniquePid::ParentProcess()
96   	{
97   	  return parentProcess();
98   	}
99   	
100  	/*!
101  	    \fn dmtcp::UniquePid::UniquePid()
102  	 */
103  	dmtcp::UniquePid::UniquePid()
104  	    :_pid ( 0 )
105  	    ,_hostid ( 0 )
106  	{
107  	  memset ( &_time,0,sizeof ( _time ) );
108  	}
109  	
110  	dmtcp::UniquePid::UniquePid(pid_t pid)
111  	    :_pid ( pid )
112  	{
113  	  _hostid = theUniqueHostId();
114  	  memset ( &_time,0,sizeof ( _time ) );
115  	}
116  	
117  	
118  	long  dmtcp::UniquePid::hostid() const
119  	{
120  	  return _hostid;
121  	}
122  	
123  	
124  	pid_t  dmtcp::UniquePid::pid() const
125  	{
126  	  return _pid;
127  	}
128  	
129  	
130  	time_t  dmtcp::UniquePid::time() const
131  	{
132  	  return _time;
133  	}
134  	
135  	int  dmtcp::UniquePid::generation() const
136  	{
137  	  return _generation;
138  	}
139  	void  dmtcp::UniquePid::incrementGeneration()
140  	{
141  	  _generation++;
142  	}
143  	
144  	
145  	const char* dmtcp::UniquePid::checkpointFilename()
146  	{
147  	  if ( checkpointFilename_str.empty() )
148  	  {
149  	    dmtcp::ostringstream os;
150  	
151  	    const char* dir = getenv ( ENV_VAR_CHECKPOINT_DIR );
152  	    if ( dir != NULL ){
153  	      os << dir << '/';
154  	    }
155  	
156  	    os << CKPT_FILE_PREFIX
157  	       << jalib::Filesystem::GetProgramName()
158  	       << '_' << ThisProcess()
159  	#ifdef UNIQUE_CHECKPOINT_FILENAMES
160  	       << "_XXXXX"
161  	#endif
162  	       << CKPT_FILE_SUFFIX;
163  	
164  	    checkpointFilename_str = os.str();
165  	  }
166  	
167  	#ifdef UNIQUE_CHECKPOINT_FILENAMES
168  	  // Include 5-digit generation number in filename, which changes
169  	  //   after each checkpoint, during same process
170  	  JASSERT( Util::strEndsWith(checkpointFilename_str, CKPT_FILE_SUFFIX) )
171  		 ( checkpointFilename_str )
172  		 .Text ( "checkpointFilename_str doesn't end in .dmtcp" );
173  	  sprintf((char *)checkpointFilename_str.c_str()
174  		  + checkpointFilename_str.length() - strlen("XXXXX" CKPT_FILE_SUFFIX),
175  		  "%5.5d%s", ThisProcess().generation(), CKPT_FILE_SUFFIX);
176  	#endif
177  	  return checkpointFilename_str.c_str();
178  	}
179  	
180  	dmtcp::string dmtcp::UniquePid::checkpointFilesDirName()
181  	{
182  	  if ( ckptFilesDirName_str.empty() ) {
183  	    ckptFilesDirName_str = jalib::Filesystem::FileBaseName(checkpointFilename());
184  	    ckptFilesDirName_str.erase(ckptFilesDirName_str.length() - 
185  	                                   strlen(CKPT_FILE_SUFFIX));
186  	    ckptFilesDirName_str += CKPT_FILES_SUBDIR_SUFFIX;
187  	  }
188  	  return ckptFilesDirName_str;
189  	}
190  	
191  	dmtcp::string dmtcp::UniquePid::dmtcpTableFilename()
192  	{
193  	  static int count = 0;
194  	  dmtcp::ostringstream os;
195  	
196  	  os << getTmpDir() << "/dmtcpConTable." << ThisProcess()
197  	     << '_' << jalib::XToString ( count++ );
198  	  return os.str();
199  	}
200  	
201  	#ifdef PID_VIRTUALIZATION
202  	dmtcp::string dmtcp::UniquePid::pidTableFilename()
203  	{
204  	  static int count = 0;
205  	  dmtcp::ostringstream os;
206  	
207  	  os << getTmpDir() << "/dmtcpPidTable." << ThisProcess()
208  	     << '_' << jalib::XToString ( count++ );
209  	  return os.str();
210  	}
211  	#endif
212  	
213  	const char* dmtcp::UniquePid::ptsSymlinkFilename ( char *ptsname )
214  	{
215  	  char *devicename = ptsname + strlen ( "/dev/pts/" );
216  	
217  	  //this must be static so dmtcp::string isn't destructed
218  	  static dmtcp::string ptsSymlinkFilename_str;
219  	
220  	  ptsSymlinkFilename_str = getTmpDir();
221  	  ptsSymlinkFilename_str += "/pts_" + ThisProcess().toString() + '_';
222  	  ptsSymlinkFilename_str += devicename;
223  	
224  	  return ptsSymlinkFilename_str.c_str();
225  	}
226  	
227  	dmtcp::string dmtcp::UniquePid::getTmpDir()
228  	{
229  	  dmtcp::string device = jalib::Filesystem::ResolveSymlink ( "/proc/self/fd/"
230  	                           + jalib::XToString ( PROTECTED_TMPDIR_FD ) );
231  	  if ( device.empty() ) {
232  	    JWARNING ( false ) .Text ("Unable to determine DMTCP TMPDIR, retrying.");
233  	    setTmpDir(getenv(ENV_VAR_TMPDIR));
234  	    device = jalib::Filesystem::ResolveSymlink ( "/proc/self/fd/"
235  	               + jalib::XToString ( PROTECTED_TMPDIR_FD ) );
236  	    JASSERT ( !device.empty() )
237  	      .Text ( "Still unable to determine DMTCP_TMPDIR" );
238  	  }
239  	  return device;
240  	}
241  	
242  	/*
243  	 * setTmpDir() computes the TmpDir to be used by DMTCP. It does so by using
244  	 * DMTCP_TMPDIR env, current username, and hostname. Once computed, we open the
245  	 * directory on file descriptor PROTECTED_TMPDIR_FD. The getTmpDir() routine
246  	 * finds the TmpDir from looking at PROTECTED_TMPDIR_FD in proc file system.
247  	 *
248  	 * This mechanism was introduced to avoid calls to gethostname(), getpwuid()
249  	 * etc. while DmtcpWorker was still initializing (in constructor) or the
250  	 * process was restarting. gethostname(), getpwuid() will create a socket
251  	 * connect to some DNS server to find out hostname and username. The socket is
252  	 * closed only at next exec() and thus it leaves a dangling socket in the
253  	 * worker process. To resolve this issue, we make sure to call setTmpDir() only
254  	 * from dmtcp_checkpoint and dmtcp_restart process and once the user process
255  	 * has been exec()ed, we use getTmpDir() only.
256  	 */
257  	void dmtcp::UniquePid::setTmpDir(const char* envVarTmpDir) {
258  	  dmtcp::string tmpDir;
259  	#define HOSTNAME_MAX_CHARS 255
260  	
261  	  char hostname[HOSTNAME_MAX_CHARS + 1];
262  	  bzero(hostname, HOSTNAME_MAX_CHARS + 1);
263  	
264  	  JASSERT ( gethostname(hostname, HOSTNAME_MAX_CHARS) == 0 || errno == ENAMETOOLONG)
265  	    .Text ( "gethostname() failed" );
266  	
267  	  dmtcp::ostringstream o;
268  	
269  	  char *userName = const_cast<char *>("");
270  	  if ( getpwuid ( getuid() ) != NULL ) {
271  	    userName = getpwuid ( getuid() ) -> pw_name;
272  	  } else if ( getenv("USER") != NULL ) {
273  	    userName = getenv("USER");
274  	  }
275  	
276  	  if (envVarTmpDir) {
277  	    o << envVarTmpDir;
278  	  } else if (getenv("TMPDIR")) {
279  	    o << getenv("TMPDIR") << "/dmtcp-" << userName << "@" << hostname;
280  	  } else {
281  	    o << "/tmp/dmtcp-" << userName << "@" << hostname;
282  	  }
283  	
284  	  JASSERT(mkdir(o.str().c_str(), S_IRWXU) == 0 || errno == EEXIST)
285  	    (JASSERT_ERRNO) (o.str())
286  	    .Text("Error creating tmp directory");
287  	
288  	  JASSERT(0 == access(o.str().c_str(), X_OK|W_OK)) (o.str())
289  	    .Text("ERROR: Missing execute- or write-access to tmp dir");
290  	
291  	  int tmpFd = open ( o.str().c_str(), O_RDONLY  );
Event var_tested_neg: Variable "tmpFd" tests negative.
Also see events: [negative_returns]
At conditional (1): "tmpFd != -1": Taking false branch.
292  	  JASSERT(tmpFd != -1);
Event negative_returns: "tmpFd" is passed to a parameter that cannot be negative.
Also see events: [var_tested_neg]
293  	  JASSERT(dup2(tmpFd, PROTECTED_TMPDIR_FD)==PROTECTED_TMPDIR_FD);
294  	  close ( tmpFd );
295  	}
296  	
297  	/*!
298  	    \fn dmtcp::UniquePid::operator<() const
299  	 */
300  	bool dmtcp::UniquePid::operator< ( const UniquePid& that ) const
301  	{
302  	#define TRY_LEQ(param) if(this->param != that.param) return this->param < that.param;
303  	  TRY_LEQ ( _hostid );
304  	  TRY_LEQ ( _pid );
305  	  TRY_LEQ ( _time );
306  	  return false;
307  	}
308  	
309  	bool dmtcp::UniquePid::operator== ( const UniquePid& that ) const
310  	{
311  	  return _hostid==that.hostid()
312  	         && _pid==that.pid()
313  	         && _time==that.time();
314  	}
315  	
316  	dmtcp::ostream& dmtcp::operator<< ( dmtcp::ostream& o,const dmtcp::UniquePid& id )
317  	{
318  	  o << std::hex << id.hostid() << '-' << std::dec << id.pid() << '-' << std::hex << id.time() << std::dec;
319  	  return o;
320  	}
321  	
322  	dmtcp::string dmtcp::UniquePid::toString() const{
323  	  dmtcp::ostringstream o;
324  	  o << *this;
325  	  return o.str();
326  	}
327  	
328  	
329  	void dmtcp::UniquePid::resetOnFork ( const dmtcp::UniquePid& newId )
330  	{
331  	  // parentProcess() is for inspection tools
332  	  parentProcess() = ThisProcess();
333  	  JTRACE ( "Explicitly setting process UniquePid" ) ( newId );
334  	  theProcess() = newId;
335  	  checkpointFilename_str.clear();
336  	  ckptFilesDirName_str.clear();
337  	}
338  	
339  	bool dmtcp::UniquePid::isNull() const
340  	{
341  	  return (*this == nullProcess());
342  	}
343  	
344  	void dmtcp::UniquePid::serialize ( jalib::JBinarySerializer& o )
345  	{
346  	  UniquePid theCurrentProcess, theParentProcess;
347  	
348  	  if ( o.isWriter() )
349  	  {
350  	    theCurrentProcess = ThisProcess();
351  	    theParentProcess = ParentProcess();
352  	  }
353  	
354  	  o & theCurrentProcess & theParentProcess;
355  	
356  	  if ( o.isReader() )
357  	  {
358  	    theProcess() = theCurrentProcess;
359  	    parentProcess() = theParentProcess;
360  	  }
361  	}
362