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 "virtualpidtable.h"
23   	#include <stdlib.h>
24   	#include <string.h>
25   	#include <string>
26   	#include <sstream>
27   	#include <fcntl.h>
28   	#include <sys/syscall.h>
29   	#include "constants.h"
30   	#include "util.h"
31   	#include "syscallwrappers.h"
32   	#include "protectedfds.h"
33   	#include  "../jalib/jconvert.h"
34   	#include  "../jalib/jfilesystem.h"
35   	
36   	#ifdef PID_VIRTUALIZATION
37   	
38   	static pthread_mutex_t tblLock = PTHREAD_MUTEX_INITIALIZER;
39   	
40   	static void _do_lock_tbl()
41   	{
42   	  JASSERT(pthread_mutex_lock(&tblLock) == 0) (JASSERT_ERRNO);
43   	}
44   	
45   	static void _do_unlock_tbl()
46   	{
47   	  JASSERT(pthread_mutex_unlock(&tblLock) == 0) (JASSERT_ERRNO);
48   	}
49   	
50   	dmtcp::VirtualPidTable::VirtualPidTable()
51   	{
52   	  _do_lock_tbl();
53   	  _pid = _real_getpid();
54   	  _ppid = _real_getppid();
55   	  _sid = -1;
56   	  _gid = _real_getpgid(0);
57   	  _isRootOfProcessTree = false;
58   	  _childTable.clear();
59   	  _tidVector.clear();
60   	  _inferiorVector.clear();
61   	  _pidMapTable.clear();
62   	  _pidMapTable[_pid] = _pid;
63   	  _do_unlock_tbl();
Event uninit_member: Non-static class member _fgid is not initialized in this constructor nor in any functions that it calls.
Also see events: [member_decl]
64   	}
65   	
66   	dmtcp::VirtualPidTable& dmtcp::VirtualPidTable::instance()
67   	{
68   	  static VirtualPidTable *inst = new VirtualPidTable(); return *inst;
69   	}
70   	
71   	bool dmtcp::VirtualPidTable::isConflictingPid( pid_t pid)
72   	{
73   	  /*  If pid != originalToCurrentPid(pid), then there is a conflict because
74   	   *    there is an original_pid same as this pid.
75   	   *
76   	   *  If pid == originalToCurrentPid(pid), then there are two cases:
77   	   *    1. there is no mapping from some original_pid to pid ==> no conflict
78   	   *    2. there is a mapping from pid to pid in the table, in which case again
79   	   *       there is not conflict because that mapping essentially is about the
80   	   *       current pid.
81   	   */
82   	  if (pid == instance().originalToCurrentPid( pid ))
83   	    return false;
84   	
85   	  return true;
86   	}
87   	
88   	void dmtcp::VirtualPidTable::preCheckpoint()
89   	{
90   	  // Update Group information before checkpoint
91   	  _ppid = getppid(); // refresh parent PID
92   	  _gid = getpgid(0);
93   	
94   	  _fgid = -1;
95   	  dmtcp::string controllingTerm = jalib::Filesystem::GetControllingTerm();
96   	  if (!controllingTerm.empty()) {
97   	    int tfd = _real_open(controllingTerm.c_str(), O_RDONLY, 0);
98   	    if (tfd >= 0) {
99   	      _fgid = tcgetpgrp(tfd);
100  	      _real_close(tfd);
101  	    }
102  	  }
103  	
104  	  /*
105  	  char s[ L_ctermid ];
106  	  // play around group ID
107  	  _fgid = -1;
108  	  if( ctermid(s) ){
109  	    int tfd = open(s,O_RDONLY);
110  	    if( tfd >= 0 ){
111  	      _fgid = tcgetpgrp(tfd);
112  	      close(tfd);
113  	    }
114  	  }
115  	  */
116  	
117  	  JTRACE("CHECK GROUP PID")(_gid)(_fgid)(_ppid)(pidExists(_gid));
118  	}
119  	
120  	void dmtcp::VirtualPidTable::postRestart()
121  	{
122  	  //getenv ( ENV_VAR_PIDTBLFILE_INITIAL );
123  	  /*
124  	   * PROTECTED_PIDTBL_FD corresponds to the file containing the pids/uniquePids
125  	   *  of all child processes, tids of all threads, and tids of all inferior
126  	   *  process(threads) which are being traced by this process.
127  	   *
128  	   * PROTECTED_PIDMAP_FD corresponds to the file containg computation wide
129  	   *  original_pid -> current_pid map to avoid pid/tid collisions.
130  	   */
131  	  dmtcp::string serialFile = "/proc/self/fd/" + jalib::XToString ( PROTECTED_PIDTBL_FD );
132  	
133  	  serialFile = jalib::Filesystem::ResolveSymlink ( serialFile );
134  	  JASSERT ( serialFile.length() > 0 ) ( serialFile );
135  	  _real_close ( PROTECTED_PIDTBL_FD );
136  	
137  	  JTRACE("Read originals pids from pid-table file") (serialFile);
138  	  jalib::JBinarySerializeReader rd ( serialFile );
139  	  serialize ( rd );
140  	}
141  	
142  	void dmtcp::VirtualPidTable::restoreProcessGroupInfo()
143  	{
144  	  // Restore group assignment
145  	  if( pidExists(_gid) ){
146  	    pid_t cgid = getpgid(0);
147  	    // Group ID is known inside checkpointed processes
148  	    if( _gid != cgid && _pid != _gid ){
149  	      JTRACE("Restore Group Assignment")
150  	        ( _gid ) ( _fgid ) ( cgid ) ( _pid ) ( _ppid ) ( getppid() );
151  	      JWARNING( setpgid(0,_gid) == 0 ) (_gid) (JASSERT_ERRNO)
152  	        .Text("Cannot change group information");
153  	    }else{
154  	      JTRACE("Group is already assigned")(_gid)(cgid);
155  	    }
156  	  }else{
157  	    JTRACE("VirtualPidTable::postRestart SKIP Group information, GID unknown");
158  	  }
159  	}
160  	
161  	void dmtcp::VirtualPidTable::printPidMaps()
162  	{
163  	#ifdef DEBUG
164  	  JASSERT_STDERR << "     originalPid" << "  ->  " << "currentPid" << "\n";
165  	  for ( pid_iterator i = _pidMapTable.begin(); i != _pidMapTable.end(); ++i ) {
166  	    pid_t originalPid = i->first;
167  	    pid_t currentPid  = i->second;
168  	    JASSERT_STDERR << "\t" << originalPid << "\t->\t" << currentPid << "\n";
169  	  }
170  	#endif
171  	}
172  	
173  	void dmtcp::VirtualPidTable::resetOnFork()
174  	{
175  	  _pid = _real_getpid();
176  	  _ppid = currentToOriginalPid ( _real_getppid() );
177  	  _isRootOfProcessTree = false;
178  	  _childTable.clear();
179  	  _tidVector.clear();
180  	  _inferiorVector.clear();
181  	  //_pidMapTable[_pid] = _pid;
182  	  JTRACE("current original to current mappings:") (_pidMapTable.size());
183  	  printPidMaps();
184  	}
185  	
186  	pid_t dmtcp::VirtualPidTable::originalToCurrentPid( pid_t originalPid )
187  	{
188  	  /* This code is called from MTCP while the checkpoint thread is holding
189  	     the JASSERT log lock. Therefore, don't call JTRACE/JASSERT/JINFO/etc. in
190  	     this function. */
191  	  _do_lock_tbl();
192  	  pid_iterator i = _pidMapTable.find(originalPid);
193  	  if ( i == _pidMapTable.end() ) {
194  	    _do_unlock_tbl();
195  	    return originalPid;
196  	  }
197  	
198  	  _do_unlock_tbl();
199  	  return i->second;
200  	}
201  	
202  	pid_t dmtcp::VirtualPidTable::currentToOriginalPid( pid_t currentPid )
203  	{
204  	  /* This code is called from MTCP while the checkpoint thread is holding
205  	     the JASSERT log lock. Therefore, don't call JTRACE/JASSERT/JINFO/etc. in
206  	     this function. */
207  	  _do_lock_tbl();
208  	  for (pid_iterator i = _pidMapTable.begin(); i != _pidMapTable.end(); ++i)
209  	  {
210  	    if ( currentPid == i->second ) {
211  	      _do_unlock_tbl();
212  	      return i->first;
213  	    }
214  	  }
215  	
216  	  _do_unlock_tbl();
217  	  return currentPid;
218  	}
219  	
220  	void dmtcp::VirtualPidTable::insert ( pid_t originalPid, dmtcp::UniquePid uniquePid )
221  	{
222  	  _do_lock_tbl();
223  	  iterator i = _childTable.find( originalPid );
224  	  if ( i != _childTable.end() ) {
225  	    _do_unlock_tbl();
226  	    JTRACE ( "originalPid -> currentPid mapping exists!") ( originalPid ) ( i->second );
227  	  }
228  	
229  	  _childTable[originalPid] = uniquePid;
230  	  _pidMapTable[originalPid] = originalPid;
231  	
232  	  _do_unlock_tbl();
233  	
234  	  JTRACE ( "Creating new originalPid -> currentPid mapping." ) ( originalPid ) ( uniquePid );
235  	}
236  	
237  	void dmtcp::VirtualPidTable::erase( pid_t originalPid )
238  	{
239  	  _do_lock_tbl();
240  	  iterator i = _childTable.find ( originalPid );
241  	  if ( i != _childTable.end() )
242  	    _childTable.erase( originalPid );
243  	
244  	  pid_iterator j = _pidMapTable.find ( originalPid );
245  	  if ( j != _pidMapTable.end() )
246  	    _pidMapTable.erase( originalPid );
247  	  _do_unlock_tbl();
248  	}
249  	
250  	void dmtcp::VirtualPidTable::updateRootOfProcessTree()
251  	{
252  	  if ( _real_getppid() == 1 )
253  	    _isRootOfProcessTree = true;
254  	}
255  	
256  	void dmtcp::VirtualPidTable::updateMapping( pid_t originalPid, pid_t currentPid )
257  	{
258  	  _do_lock_tbl();
259  	  _pidMapTable[originalPid] = currentPid;
260  	  _do_unlock_tbl();
261  	}
262  	
263  	dmtcp::vector< pid_t > dmtcp::VirtualPidTable::getPidVector( )
264  	{
265  	  dmtcp::vector< pid_t > pidVec;
266  	  for ( pid_iterator i = _pidMapTable.begin(); i != _pidMapTable.end(); ++i )
267  	    pidVec.push_back ( i->first );
268  	  return pidVec;
269  	}
270  	
271  	dmtcp::vector< pid_t > dmtcp::VirtualPidTable::getChildPidVector( )
272  	{
273  	  dmtcp::vector< pid_t > childPidVec;
274  	  for ( iterator i = _childTable.begin(); i != _childTable.end(); ++i )
275  	    childPidVec.push_back ( i->first );
276  	  return childPidVec;
277  	}
278  	
279  	dmtcp::vector< pid_t > dmtcp::VirtualPidTable::getTidVector( )
280  	{
281  	  return _tidVector;
282  	}
283  	
284  	dmtcp::vector< pid_t > dmtcp::VirtualPidTable::getInferiorVector( )
285  	{
286  	  return _inferiorVector;
287  	}
288  	
289  	void dmtcp::VirtualPidTable::insertTid( pid_t tid )
290  	{
291  	  eraseTid( tid );
292  	  _do_lock_tbl();
293  	  _tidVector.push_back ( tid );
294  	  _do_unlock_tbl();
295  	  return;
296  	}
297  	
298  	void dmtcp::VirtualPidTable::insertInferior( pid_t tid )
299  	{
300  	  eraseInferior( tid );
301  	  _do_lock_tbl();
302  	  _inferiorVector.push_back ( tid );
303  	  _do_unlock_tbl();
304  	  return;
305  	}
306  	
307  	void dmtcp::VirtualPidTable::eraseTid( pid_t tid )
308  	{
309  	  _do_lock_tbl();
310  	  dmtcp::vector< pid_t >::iterator iter = _tidVector.begin();
311  	  while ( iter != _tidVector.end() ) {
312  	    if ( *iter == tid ) {
313  	      _tidVector.erase( iter );
314  	      _pidMapTable.erase(tid);
315  	      break;
316  	    }
317  	    else
318  	      ++iter;
319  	  }
320  	  _do_unlock_tbl();
321  	  return;
322  	}
323  	
324  	void dmtcp::VirtualPidTable::postExec( )
325  	{
326  	  JTRACE("Post-Exec. Emptying tidVector");
327  	  _do_lock_tbl();
328  	  for (size_t i = 0; i < _tidVector.size(); i++) {
329  	    _pidMapTable.erase( _tidVector[i] );
330  	  }
331  	  _tidVector.clear();
332  	  _do_unlock_tbl();
333  	}
334  	
335  	void dmtcp::VirtualPidTable::eraseInferior( pid_t tid )
336  	{
337  	  _do_lock_tbl();
338  	  dmtcp::vector< pid_t >::iterator iter = _inferiorVector.begin();
339  	  while ( iter != _inferiorVector.end() ) {
340  	    if ( *iter == tid ) {
341  	      _inferiorVector.erase( iter );
342  	      break;
343  	    }
344  	    else
345  	      ++iter;
346  	  }
347  	  _do_unlock_tbl();
348  	  return;
349  	}
350  	
351  	bool dmtcp::VirtualPidTable::pidExists( pid_t pid )
352  	{
353  	  bool retVal = false;
354  	  _do_lock_tbl();
355  	  pid_iterator j = _pidMapTable.find ( pid );
356  	  if ( j != _pidMapTable.end() )
357  	    retVal = true;
358  	
359  	  _do_unlock_tbl();
360  	  return retVal;
361  	}
362  	
363  	void dmtcp::VirtualPidTable::refresh()
364  	{
365  	  updateRootOfProcessTree();//      _isRootOfProcessTree = true;
366  	  refreshChildTable();
367  	  refreshTidVector();
368  	}
369  	
370  	void dmtcp::VirtualPidTable::refreshTidVector()
371  	{
372  	  dmtcp::vector< pid_t >::iterator iter;
373  	  for (iter = _tidVector.begin(); iter != _tidVector.end(); ) {
374  	    int retVal = syscall(SYS_tgkill, _pid, *iter, 0);
375  	    if (retVal == -1 && errno == ESRCH) {
376  	      erase(*iter);
377  	      iter = _tidVector.erase( iter );
378  	    } else {
379  	      iter++;
380  	    }
381  	  }
382  	  return;
383  	}
384  	
385  	void dmtcp::VirtualPidTable::refreshChildTable()
386  	{
387  	  for ( iterator i = _childTable.begin(); i != _childTable.end(); ++i ) {
388  	    pid_t originalPid = i->first;
389  	    int retVal = kill(originalPid, 0);
390  	    /* Check to see if the child process is alive*/
391  	    if (retVal == -1 && errno == ESRCH) {
392  	      erase(originalPid);
393  	    }
394  	  }
395  	}
396  	
397  	void dmtcp::VirtualPidTable::serialize ( jalib::JBinarySerializer& o )
398  	{
399  	  JSERIALIZE_ASSERT_POINT ( "dmtcp::VirtualPidTable:" );
400  	
401  	  if (o.isWriter()){
402  	    updateRootOfProcessTree();//      _isRootOfProcessTree = true;
403  	    //refreshChildTable();
404  	    //refreshTidVector();
405  	  }
406  	
407  	  JTRACE("Save pid information")(_sid)(_ppid)(_gid)(_fgid);
408  	  o & _isRootOfProcessTree & _pid & _sid & _ppid & _gid & _fgid;
409  	
410  	  if ( _isRootOfProcessTree )
411  	    JTRACE ( "This process is Root of Process Tree" );// ( UniquePid::ThisProcess() );
412  	
413  	  serializeChildTable ( o );
414  	
415  	  serializePidMap     ( o );
416  	
417  	  JTRACE ("Serializing tidVector");
418  	  JSERIALIZE_ASSERT_POINT ( "TID Vector:[" );
419  	  o & _tidVector;
420  	  JSERIALIZE_ASSERT_POINT ( "}" );
421  	
422  	  JTRACE ("Serializing inferiorVector");
423  	  JSERIALIZE_ASSERT_POINT ( "Inferior Vector:[" );
424  	  o & _inferiorVector;
425  	  JSERIALIZE_ASSERT_POINT ( "]" );
426  	
427  	  JSERIALIZE_ASSERT_POINT( "EOF" );
428  	}
429  	
430  	
431  	void dmtcp::VirtualPidTable::serializeChildTable ( jalib::JBinarySerializer& o )
432  	{
433  	  size_t numPids = _childTable.size();
434  	  serializeEntryCount(o, numPids);
435  	
436  	  JTRACE ("Serializing ChildPid Table") (numPids) (o.filename());
437  	  pid_t originalPid;
438  	  dmtcp::UniquePid uniquePid;
439  	
440  	  if ( o.isWriter() )
441  	  {
442  	    for ( iterator i = _childTable.begin(); i != _childTable.end(); ++i )
443  	    {
444  	      originalPid = i->first;
445  	      uniquePid   = i->second;
446  	      serializeChildTableEntry ( o, originalPid, uniquePid );
447  	    }
448  	  }
449  	  else
450  	  {
451  	    while ( numPids-- > 0 )
452  	    {
453  	      serializeChildTableEntry ( o, originalPid, uniquePid );
454  	      _childTable[originalPid] = uniquePid;
455  	    }
456  	  }
457  	}
458  	
459  	void  dmtcp::VirtualPidTable::serializeChildTableEntry (
460  	    jalib::JBinarySerializer& o, pid_t& originalPid, dmtcp::UniquePid& uniquePid )
461  	{
462  	  JSERIALIZE_ASSERT_POINT ( "ChildPid:[" );
463  	  o & originalPid & uniquePid;
464  	  JSERIALIZE_ASSERT_POINT ( "]" );
465  	}
466  	
467  	void dmtcp::VirtualPidTable::serializePidMap ( jalib::JBinarySerializer& o )
468  	{
469  	  size_t numMaps = _pidMapTable.size();
470  	  serializeEntryCount(o, numMaps);
471  	
472  	  pid_t originalPid;
473  	  pid_t currentPid;
474  	
475  	  if ( o.isWriter() )
476  	  {
477  	    for ( pid_iterator i = _pidMapTable.begin(); i != _pidMapTable.end(); ++i )
478  	    {
479  	      originalPid = i->first;
480  	      currentPid  = i->second;
481  	      serializePidMapEntry ( o, originalPid, currentPid );
482  	    }
483  	  }
484  	  else
485  	  {
486  	    while ( numMaps-- > 0 )
487  	    {
488  	      serializePidMapEntry ( o, originalPid, currentPid );
489  	      _pidMapTable[originalPid] = currentPid;
490  	    }
491  	  }
492  	
493  	  JTRACE ("Serializing PidMap Table") (numMaps) (o.filename());
494  	  printPidMaps();
495  	}
496  	
497  	void dmtcp::VirtualPidTable::serializePidMapEntry (
498  	    jalib::JBinarySerializer& o, pid_t& originalPid, pid_t& currentPid )
499  	{
500  	  JSERIALIZE_ASSERT_POINT ( "PidMap:[" );
501  	  o & originalPid & currentPid;
502  	  JSERIALIZE_ASSERT_POINT ( "]" );
503  	}
504  	
505  	void dmtcp::VirtualPidTable::serializeEntryCount ( jalib::JBinarySerializer& o,
506  	                                                   size_t& count )
507  	{
508  	  JSERIALIZE_ASSERT_POINT ( "NumEntries:[" );
509  	  o & count;
510  	  JSERIALIZE_ASSERT_POINT ( "]" );
511  	  JTRACE("Num PidMaps:")(count);
512  	}
513  	
514  	void dmtcp::VirtualPidTable::InsertIntoPidMapFile( pid_t originalPid, pid_t currentPid)
515  	{
516  	
517  	  dmtcp::string pidMapFile = "/proc/self/fd/" 
518  	                             + jalib::XToString ( PROTECTED_PIDMAP_FD );
519  	  dmtcp::string pidMapCountFile = "/proc/self/fd/" 
520  	                                  + jalib::XToString ( PROTECTED_PIDMAPCNT_FD );
521  	
522  	  pidMapFile =  jalib::Filesystem::ResolveSymlink ( pidMapFile );
523  	  pidMapCountFile =  jalib::Filesystem::ResolveSymlink ( pidMapCountFile );
524  	
525  	  JASSERT ( pidMapFile.length() > 0 && pidMapCountFile.length() > 0 )
526  	    ( pidMapFile )( pidMapCountFile );
527  	
528  	  // Create Serializers
529  	  jalib::JBinarySerializeWriterRaw mapwr( pidMapFile, PROTECTED_PIDMAP_FD );
530  	  jalib::JBinarySerializeWriterRaw countwr(pidMapCountFile, PROTECTED_PIDMAPCNT_FD );
531  	  jalib::JBinarySerializeReaderRaw countrd(pidMapCountFile, PROTECTED_PIDMAPCNT_FD );
532  	
533  	  // Lock fileset before any operations
534  	  Util::lockFile(PROTECTED_PIDMAP_FD);
535  	  _do_lock_tbl();
536  	
537  	  // Read old number of saved pid maps
538  	  countrd.rewind();
539  	  size_t numMaps;
540  	  serializeEntryCount (countrd,numMaps);
541  	  // Serialize new pair
542  	  serializePidMapEntry (mapwr, originalPid, currentPid );
543  	
544  	  // Commit changes into map count file
545  	  countwr.rewind();
546  	  numMaps++;
547  	  serializeEntryCount (countwr,numMaps);
548  	
549  	  _do_unlock_tbl();
550  	  Util::unlockFile(PROTECTED_PIDMAP_FD);
551  	}
552  	
553  	void dmtcp::VirtualPidTable::readPidMapsFromFile()
554  	{
555  	  dmtcp::string pidMapFile = "/proc/self/fd/" 
556  	                             + jalib::XToString ( PROTECTED_PIDMAP_FD );
557  	  pidMapFile =  jalib::Filesystem::ResolveSymlink ( pidMapFile );
558  	  dmtcp::string pidMapCountFile = "/proc/self/fd/"
559  	                                  + jalib::XToString ( PROTECTED_PIDMAPCNT_FD );
560  	  pidMapCountFile =  jalib::Filesystem::ResolveSymlink ( pidMapCountFile );
561  	  JASSERT ( pidMapFile.length() > 0 && pidMapCountFile.length() > 0 )
562  	    ( pidMapFile )( pidMapCountFile );
563  	
564  	  JTRACE ( "Read PidMaps from file" ) ( pidMapCountFile ) ( pidMapFile );
565  	
566  	  _real_close( PROTECTED_PIDMAP_FD );
567  	  _real_close( PROTECTED_PIDMAPCNT_FD );
568  	
569  	  jalib::JBinarySerializeReader maprd( pidMapFile);
570  	  jalib::JBinarySerializeReader countrd(pidMapCountFile);
571  	
572  	  // Read number of PID mappings
573  	  size_t numMaps;
574  	  serializeEntryCount (countrd,numMaps);
575  	
576  	  // Read pidMapping content
577  	  pid_t originalPid;
578  	  pid_t currentPid;
579  	  while ( numMaps-- > 0 ){
580  	    serializePidMapEntry ( maprd, originalPid, currentPid );
581  	    _pidMapTable[originalPid] = currentPid;
582  	  }
583  	  printPidMaps();
584  	}
585  	
586  	#endif