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 "connectionmanager.h"
23   	
24   	#include  "../jalib/jfilesystem.h"
25   	#include  "../jalib/jconvert.h"
26   	#include  "../jalib/jassert.h"
27   	#include "protectedfds.h"
28   	#include "syscallwrappers.h"
29   	#include "util.h"
30   	
31   	#include <sys/types.h>
32   	#include <sys/stat.h>
33   	#include <fcntl.h>
34   	#include <sys/types.h>
35   	#include <unistd.h>
36   	#include <errno.h>
37   	#include <sys/types.h>
38   	#include <sys/wait.h>
39   	
40   	static dmtcp::string _procFDPath ( int fd )
41   	{
42   	  return "/proc/self/fd/" + jalib::XToString ( fd );
43   	}
44   	
45   	static bool _isBadFd ( int fd )
46   	{
47   	  dmtcp::string device = jalib::Filesystem::ResolveSymlink ( _procFDPath ( fd ) );
48   	  return ( device == "" );
49   	}
50   	
51   	dmtcp::ConnectionList& dmtcp::ConnectionList::instance()
52   	{
53   	  static ConnectionList inst; return inst;
54   	}
55   	
56   	dmtcp::KernelDeviceToConnection& dmtcp::KernelDeviceToConnection::instance()
57   	{
58   	  static KernelDeviceToConnection inst; return inst;
59   	}
60   	
61   	dmtcp::UniquePtsNameToPtmxConId& dmtcp::UniquePtsNameToPtmxConId::instance()
62   	{
63   	  static UniquePtsNameToPtmxConId inst; return inst;
64   	}
65   	
66   	dmtcp::ConnectionList::ConnectionList() {}
67   	
68   	dmtcp::KernelDeviceToConnection::KernelDeviceToConnection() {}
69   	
70   	dmtcp::ConnectionToFds::ConnectionToFds ( KernelDeviceToConnection& source )
71   	{
72   	  dmtcp::vector<int> fds = jalib::Filesystem::ListOpenFds();
73   	  JTRACE("Creating Connection->FD mapping")(fds.size());
74   	  KernelDeviceToConnection::instance().dbgSpamFds();
75   	  _procname = jalib::Filesystem::GetProgramName();
76   	  _hostname = jalib::Filesystem::GetCurrentHostname();
77   	  _inhostname = jalib::Filesystem::GetCurrentHostname();
78   	  _pid = UniquePid::ThisProcess();
79   	  _ppid = UniquePid::ParentProcess();
80   	
81   	  for ( size_t i=0; i<fds.size(); ++i )
82   	  {
83   	    if ( _isBadFd ( fds[i] ) ) continue;
84   	    if ( ProtectedFDs::isProtected ( fds[i] ) ) continue;
85   	    Connection* con = &source.retrieve ( fds[i] );
86   	    _table[con->id() ].push_back ( fds[i] );
87   	  }
88   	}
89   	
90   	void dmtcp::ConnectionToFds::erase ( const ConnectionIdentifier& conId )
91   	{
92   	  JTRACE("erasing connection from ConnectionToFds") (conId);
93   	  // Find returns iterator 'it' w/ 0 or more elts, with first elt matching key.
94   	  iterator it = _table.find(conId);
95   	  JASSERT( it != _table.end() );
96   	  _table.erase(it);
97   	}
98   	
99   	dmtcp::Connection& dmtcp::KernelDeviceToConnection::retrieve ( int fd )
100  	{
101  	  dmtcp::string device = fdToDevice ( fd );
102  	  JASSERT ( device.length() > 0 ) ( fd ).Text ( "invalid fd" );
103  	  iterator i = _table.find ( device );
104  	  JASSERT ( i != _table.end() ) ( fd ) ( device ) ( _table.size() ).Text ( "failed to find connection for fd" );
105  	  return ConnectionList::instance() [i->second];
106  	}
107  	
108  	void dmtcp::KernelDeviceToConnection::create ( int fd, Connection* c )
109  	{
110  	  ConnectionList::instance().add ( c );
111  	
112  	  dmtcp::string device = fdToDevice ( fd, true );
113  	
114  	  JTRACE ( "device created" ) ( fd ) ( device ) ( c->id() );
115  	
116  	  JASSERT ( device.length() > 0 ) ( fd ).Text ( "invalid fd" );
117  	  iterator i = _table.find ( device );
118  	  JASSERT ( i == _table.end() ) ( fd ) ( device ).Text ( "connection already exists" );
119  	  _table[device] = c->id();
120  	}
121  	
122  	
123  	void dmtcp::KernelDeviceToConnection::createPtyDevice ( int fd, dmtcp::string device, Connection* c )
124  	{
125  	  ConnectionList::instance().add ( c );
126  	
127  	  JTRACE ( "device created" ) ( fd ) ( device ) ( c->id() );
128  	
129  	  JASSERT ( device.length() > 0 ) ( fd ).Text ( "invalid fd" );
130  	
131  	  /* FIXME: The following JWARNING should be re-enabled */
132  	  //iterator i = _table.find ( device );
133  	  //JWARNING ( i == _table.end() ) ( fd ) ( device ).Text ( "connection already exists" );
134  	
135  	  _table[device] = c->id();
136  	}
137  	
138  	dmtcp::string dmtcp::KernelDeviceToConnection::fdToDevice ( int fd, bool noOnDemandConnection )
139  	{
140  	  //gather evidence
141  	  errno = 0;
142  	  dmtcp::string device = jalib::Filesystem::ResolveSymlink ( _procFDPath ( fd ) );
143  	  bool isBadFd = ( device == "" );
144  	
145  	  if ( isBadFd )
146  	  {
147  	    JTRACE ( "bad fd (we expect one of these lines)" ) ( fd );
148  	    JASSERT ( device == "" ) ( fd ) ( _procFDPath ( fd ) ) ( device ) ( JASSERT_ERRNO )
149  	      .Text ( "expected badFd not to have a proc entry..." );
150  	
151  	    return "";
152  	  }
153  	
154  	  bool isFile  = ( device[0] == '/' );
155  	
156  	  bool isTty = (device.compare("/dev/tty") == 0);
157  	
158  	  bool isPtmx  = (device.compare("/dev/ptmx") == 0);
159  	  bool isPts   = Util::strStartsWith(device, "/dev/pts/");
160  	
161  	  bool isBSDMaster  = (Util::strStartsWith(device, "/dev/pty") && 
162  	                       device.compare("/dev/pty") != 0);
163  	  bool isBSDSlave   = (Util::strStartsWith(device, "/dev/tty") &&
164  	                       device.compare("/dev/tty")) != 0;
165  	
166  	  if ( isTty ) {
167  	    dmtcp::string deviceName = "tty:" + device;
168  	
169  	    if(noOnDemandConnection)
170  	      return deviceName;
171  	
172  	    iterator i = _table.find ( deviceName );
173  	
174  	    if ( i == _table.end() )
175  	    {
176  	      JTRACE("Creating /dev/tty connection [on-demand]");
177  	      int type = PtyConnection::PTY_DEV_TTY;
178  	
179  	      Connection * c = new PtyConnection ( device, device, type );
180  	      createPtyDevice ( fd, deviceName, c );
181  	    }
182  	
183  	    return deviceName;
184  	
185  	  } else if ( isPtmx ) {
186  	    char ptsName[21];
187  	    JASSERT(_real_ptsname_r(fd, ptsName, 21) == 0) (JASSERT_ERRNO);
188  	
189  	    string ptsNameStr = ptsName;
190  	
191  	    dmtcp::string deviceName = "ptmx[" + ptsNameStr + "]:" + device;
192  	
193  	    if(noOnDemandConnection)
194  	      return deviceName;
195  	
196  	    iterator i = _table.find ( deviceName );
197  	    JASSERT ( i != _table.end() ) ( fd ) ( device ) ( deviceName ) ( ptsNameStr )
198  	      .Text ("Device not found in connection list");
199  	
200  	    return deviceName;
201  	
202  	  } else if ( isPts ) {
203  	    dmtcp::string deviceName = "pts:" + device;
204  	
205  	    if(noOnDemandConnection)
206  	      return deviceName;
207  	
208  	    iterator i = _table.find ( deviceName );
209  	
210  	    if ( i == _table.end() )
211  	    {
212  	      JWARNING(false) .Text("PTS Device not found");
213  	      int type;
214  	      dmtcp::string currentTty = jalib::Filesystem::GetControllingTerm();
215  	
216  	      JTRACE( "Controlling Terminal") (currentTty);
217  	
218  	      if ( currentTty.compare(device) == 0 ) {
219  	        type = dmtcp::PtyConnection::PTY_CTTY;
220  	        JTRACE ( "creating TTY connection [on-demand]" )
221  	          ( deviceName );
222  	
223  	        Connection * c = new PtyConnection ( device, device, type );
224  	        createPtyDevice ( fd, deviceName, c );
225  	      } else {
226  	        JASSERT ( false ) ( fd ) ( device )
227  	          .Text ("PTS Device not found in connection list");
228  	      }
229  	    }
230  	
231  	    return deviceName;
232  	
233  	  } else if ( isBSDMaster ) {
234  	    dmtcp::string deviceName = "BSDMasterPty:" + device;
235  	    dmtcp::string slaveDeviceName = device.replace(0, strlen("/dev/pty"), "/dev/tty");
236  	
237  	    if(noOnDemandConnection)
238  	      return deviceName;
239  	
240  	    iterator i = _table.find ( deviceName );
241  	    if ( i == _table.end() )
242  	    {
243  	      JTRACE ( "creating BSD Master Pty connection [on-demand]" )
244  	        ( deviceName ) ( slaveDeviceName );
245  	
246  	      int type = dmtcp::PtyConnection::PTY_BSD_MASTER;
247  	      Connection * c = new dmtcp::PtyConnection ( device, type );
248  	
249  	      ConnectionList::instance().add ( c );
250  	      _table[deviceName] = c->id();
251  	    }
252  	
253  	    return deviceName;
254  	
255  	  } else if ( isBSDSlave ) {
256  	    dmtcp::string deviceName = "BSDSlave:" + device;
257  	    dmtcp::string masterDeviceName = device.replace(0, strlen("/dev/tty"), "/dev/pty");
258  	
259  	
260  	    if(noOnDemandConnection)
261  	      return deviceName;
262  	
263  	    iterator i = _table.find ( deviceName );
264  	    if ( i == _table.end() )
265  	    {
266  	      JTRACE ( "creating BSD Slave Pty connection [on-demand]" )
267  	        ( deviceName ) ( masterDeviceName );
268  	
269  	      int type = dmtcp::PtyConnection::PTY_BSD_SLAVE;
270  	      Connection * c = new dmtcp::PtyConnection ( device, type );
271  	
272  	      ConnectionList::instance().add ( c );
273  	      _table[deviceName] = c->id();
274  	    }
275  	
276  	    return deviceName;
277  	
278  	  } else if ( isFile ) {
279  	    // Can be file or FIFO channel
280  	    struct stat buf;
281  	    stat(device.c_str(),&buf);
282  	
283  	    if (!jalib::Filesystem::FileExists(device)) {
284  	
285  	      // Make sure _path ends with DELETED_FILE_SUFFIX
286  	      JASSERT(Util::strEndsWith(device, DELETED_FILE_SUFFIX));
287  	
288  	      dmtcp::string deviceName = "file["+jalib::XToString ( fd ) +"]:" + device;
289  	
290  	      if(noOnDemandConnection)
291  	        return deviceName;
292  	
293  	      iterator i = _table.find ( deviceName );
294  	      if ( i == _table.end() )
295  	      {
296  	        JTRACE ( "creating file connection [on-demand]" ) ( deviceName );
297  	        off_t offset = lseek ( fd, 0, SEEK_CUR );
298  	        Connection * c = new FileConnection ( device, offset, FileConnection::FILE_DELETED );
299  	        ConnectionList::instance().add ( c );
300  	        _table[deviceName] = c->id();
301  	      }
302  	
303  	      return deviceName;
304  	    } else if (S_ISREG(buf.st_mode) || S_ISCHR(buf.st_mode) || 
305  	               S_ISDIR(buf.st_mode) || S_ISBLK(buf.st_mode)) {
306  	      /* /dev/null is a character special file (non-regular file) */
307  	      dmtcp::string deviceName = "file["+jalib::XToString ( fd ) +"]:" + device;
308  	
309  	      if(noOnDemandConnection)
310  	        return deviceName;
311  	
312  	      iterator i = _table.find ( deviceName );
313  	      if ( i == _table.end() )
314  	      {
315  	        JTRACE ( "creating file connection [on-demand]" ) ( deviceName );
316  	        off_t offset = lseek ( fd, 0, SEEK_CUR );
317  	        Connection * c = new FileConnection ( device, offset );
318  	        ConnectionList::instance().add ( c );
319  	        _table[deviceName] = c->id();
320  	      }
321  	
322  	      return deviceName;
323  	
324  	    } else if (S_ISFIFO(buf.st_mode)){
325  	      dmtcp::string deviceName = "fifo["+jalib::XToString ( fd ) +"]:" + device;
326  	
327  	      if(noOnDemandConnection)
328  	        return deviceName;
329  	
330  	      iterator i = _table.find ( deviceName );
331  	      if (i == _table.end())
332  	      {
333  	        JTRACE ( "creating fifo connection [on-demand]" ) ( deviceName );
334  	        Connection * c = new FifoConnection( device );
335  	        ConnectionList::instance().add( c );
336  	        _table[deviceName] = c->id();
337  	        return deviceName;
338  	      } else {
339  	        return deviceName;
340  	      }
341  	    } else {
342  	      JASSERT(false) (device) .Text("Unimplemented file type.");
343  	    }
344  	  }
345  	  //JWARNING(false) (device) .Text("UnImplemented Connection Type.");
346  	  return device;
347  	}
348  	
349  	// TODO: To properly implement STL erase(), it should return the next iterator.
350  	void dmtcp::ConnectionList::erase ( iterator i )
351  	{
352  	  Connection * con = i->second;
353  	  JTRACE ( "deleting stale connection..." ) ( con->id() );
354  	  KernelDeviceToConnection::instance().erase( i->first );
355  	  _connections.erase ( i );
356  	  delete con;
357  	}
358  	
359  	void dmtcp::ConnectionList::erase ( dmtcp::ConnectionIdentifier& key )
360  	{
361  	  iterator i = _connections.find(key);
362  	  JASSERT(i != _connections.end());
363  	  erase(i);
364  	}
365  	
366  	// TODO: To properly implement STL erase(), it should return the next iterator.
367  	void dmtcp::KernelDeviceToConnection::erase( const ConnectionIdentifier& con )
368  	{
369  	  for(iterator i = _table.begin(); i!=_table.end(); ++i){
370  	    if(i->second == con){
371  	      dmtcp::string k = i->first;
372  	      JTRACE("removing device->con mapping")(k)(con);
373  	      _table.erase(k);
374  	      return;
375  	    }
376  	  }
377  	  JTRACE("WARNING:: failed to find connection in table to erase it")(con);
378  	}
379  	
380  	//called when a device name changes
381  	void dmtcp::KernelDeviceToConnection::redirect( int fd, const ConnectionIdentifier& id ){
382  	  //first delete the old one
383  	  erase(id);
384  	
385  	  //now add the new fd
386  	  dmtcp::string device = fdToDevice ( fd, true );
387  	  JTRACE ( "redirecting device" )(fd)(device) (id);
388  	  JASSERT ( device.length() > 0 ) ( fd ).Text ( "invalid fd" );
389  	  iterator i = _table.find ( device );
390  	  JASSERT ( i == _table.end() ) ( fd ) ( device ).Text ( "connection already exists" );
391  	  _table[device] = id;
392  	}
393  	
394  	void dmtcp::KernelDeviceToConnection::dbgSpamFds()
395  	{
396  	#ifdef DEBUG
397  	  JASSERT_STDERR << "Listing FDs...\n";
398  	  dmtcp::vector<int> fds = jalib::Filesystem::ListOpenFds();
399  	  for ( size_t i=0; i<fds.size(); ++i )
400  	  {
401  	    if ( _isBadFd ( fds[i] ) ) continue;
402  	    if(ProtectedFDs::isProtected( fds[i] )) continue;
403  	    dmtcp::string device = fdToDevice ( fds[i], true );
404  	    bool exists = ( _table.find ( device ) != _table.end() );
405  	    JASSERT_STDERR << fds[i]
406  	                   << " -> "  << device
407  	                   << " inTable=" << exists << "\n";
408  	  }
409  	#endif
410  	}
411  	
412  	
413  	//fix things up post-restart (all or KernelDevices have changed)
414  	dmtcp::KernelDeviceToConnection::KernelDeviceToConnection ( const ConnectionToFds& source )
415  	{
416  	  JTRACE ( "reconstructing table..." );
417  	  for ( ConnectionToFds::const_iterator i = source.begin()
418  	        ; i!=source.end()
419  	        ; ++i )
420  	  {
421  	    ConnectionIdentifier con = i->first;
422  	    const dmtcp::vector<int>& fds = i->second;
423  	    JWARNING(fds.size() > 0)(con);
424  	    if(fds.size()>0){
425  	      dmtcp::string device = fdToDevice ( fds[0], true );
426  	      _table[device] = con;
427  	#ifdef DEBUG
428  	      //double check to make sure all fds have same device
429  	      Connection *c = &(ConnectionList::instance()[con]);
430  	      for ( size_t i=1; i<fds.size(); ++i ) {
431  	        if ( c->conType() != Connection::FILE ) {
432  	          JASSERT ( device == fdToDevice ( fds[i] ) )
433  	            ( device ) ( fdToDevice ( fds[i] ) ) ( fds[i] ) ( fds[0] );
434  	        } else {
435  	          dmtcp::string filePath =
436  	            jalib::Filesystem::ResolveSymlink ( _procFDPath ( fds[i] ) );
437  	          JASSERT ( filePath == ((FileConnection *)c)->filePath() )
438  	            ( fds[i] ) ( filePath ) ( fds[0] ) ( ((FileConnection *)c)->filePath() );
439  	        }
440  	      }
441  	#endif
442  	    }
443  	  }
444  	#ifdef DEBUG
445  	  JTRACE ( "new fd table..." );
446  	  dbgSpamFds();
447  	#endif
448  	
449  	}
450  	
451  	void dmtcp::ConnectionList::serialize ( jalib::JBinarySerializer& o )
452  	{
453  	  JSERIALIZE_ASSERT_POINT ( "dmtcp::ConnectionList:" );
454  	
455  	  size_t numCons = _connections.size();
456  	  o & numCons;
457  	
458  	  if ( o.isWriter() )
459  	  {
460  	    for ( iterator i=_connections.begin(); i!=_connections.end(); ++i )
461  	    {
462  	
463  	      ConnectionIdentifier key = i->first;
464  	      Connection& con = *i->second;
465  	      int type = con.conType();
466  	
467  	      JSERIALIZE_ASSERT_POINT ( "[StartConnection]" );
468  	      o & key & type;
469  	      con.serialize ( o );
470  	      JSERIALIZE_ASSERT_POINT ( "[EndConnection]" );
471  	    }
472  	  }
473  	  else
474  	  {
475  	    while ( numCons-- > 0 )
476  	    {
477  	
478  	      ConnectionIdentifier key;
479  	      int type = -1;
480  	      Connection* con = NULL;
481  	
482  	      JSERIALIZE_ASSERT_POINT ( "[StartConnection]" );
483  	      o & key & type;
484  	
485  	      switch ( type )
486  	      {
487  	      case Connection::TCP:
488  	        con = new TcpConnection ( -1,-1,-1 );
489  	        break;
490  	      case Connection::FILE:
491  	        con = new FileConnection ( "?", -1 );
492  	        break;
493  	        //             case Connection::PIPE:
494  	        //                 con = new PipeConnection();
495  	        //                 break;
496  	      case Connection::FIFO:
497  	        // sleep(15);
498  	        con = new FifoConnection ( "?" );
499  	        break;
500  	      case Connection::PTY:
501  	        con = new PtyConnection();
502  	        break;
503  	      case Connection::STDIO:
504  	        con = new StdioConnection();
505  	        break;
506  	      default:
507  	        JASSERT ( false ) ( key ) ( o.filename() ).Text ( "unknown connection type" );
508  	      }
509  	
510  	      JASSERT( con != NULL )(key);
511  	
512  	      con->serialize ( o );
513  	
514  	      ConnectionMapT::const_iterator i = _connections.find(key);
515  	      if(i != _connections.end())
516  	      {
517  	        JTRACE("merging connections from two restore targets")(key)(type);
518  	        con->mergeWith(*(i->second));
519  	      }
520  	
521  	      _connections[key] = con;
522  	
523  	      JSERIALIZE_ASSERT_POINT ( "[EndConnection]" );
524  	    }
525  	
526  	  }
527  	
528  	  JSERIALIZE_ASSERT_POINT ( "EndConnectionList" );
529  	}
530  	
531  	//examine /proc/self/fd for unknown connections
532  	void dmtcp::ConnectionList::scanForPreExisting()
533  	{
534  	  dmtcp::vector<int> fds = jalib::Filesystem::ListOpenFds();
535  	  for ( size_t i=0; i<fds.size(); ++i )
536  	  {
537  	    if ( _isBadFd ( fds[i] ) ) continue;
538  	    if ( ProtectedFDs::isProtected ( fds[i] ) ) continue;
539  	    KernelDeviceToConnection::instance().handlePreExistingFd ( fds[i] );
540  	  }
541  	}
542  	
543  	void dmtcp::KernelDeviceToConnection::handlePreExistingFd ( int fd )
544  	{
545  	  //this has the side effect of on-demand creating everything except sockets
546  	  dmtcp::string device = KernelDeviceToConnection::instance().fdToDevice ( fd, true );
547  	
548  	  JTRACE ( "scanning pre-existing device" ) ( fd ) ( device );
549  	
550  	  //so if it doesn't exist it must be a socket
551  	  if ( _table.find ( device ) == _table.end() )
552  	  {
553  	    if ( fd <= 2 )
554  	    {
555  	      create(fd, new StdioConnection(fd));
556  	    }
557  	    else if ( device.compare("/dev/tty") == 0 )
558  	    {
559  	      dmtcp::string deviceName = "tty:" + device;
560  	      JTRACE ( "Found pre-existing /dev/tty" )
561  	        ( fd ) ( deviceName );
562  	
563  	      int type = dmtcp::PtyConnection::PTY_DEV_TTY;
564  	
565  	      PtyConnection *con = new PtyConnection ( device, device, type );
566  	      create ( fd, con );
567  	    }
568  	    else if ( Util::strStartsWith(device, "/dev/pts/")) 
569  	    {
570  	      dmtcp::string deviceName = "pts["+jalib::XToString ( fd ) +"]:" + device;
571  	      JNOTE ( "Found pre-existing PTY connection, will be restored as current TTY" )
572  	        ( fd ) ( deviceName );
573  	
574  	      int type = dmtcp::PtyConnection::PTY_CTTY;
575  	
576  	      PtyConnection *con = new PtyConnection ( device, device, type );
577  	      create ( fd, con );
578  	    }
579  	    else
580  	    {
581  	      JNOTE ( "found pre-existing socket... will not be restored" ) ( fd ) ( device );
582  	      TcpConnection* con = new TcpConnection ( 0, 0, 0 );
583  	      con->markPreExisting();
584  	      create ( fd, con );
585  	    }
586  	  }
587  	}
588  	
589  	void dmtcp::KernelDeviceToConnection::prepareForFork ( )
590  	{
591  	  dmtcp::vector<int> fds = jalib::Filesystem::ListOpenFds();
592  	  JTRACE("Scanning /proc/self/fd for new connections. New connections will be created");
593  	  for ( size_t i=0; i<fds.size(); ++i )
594  	  {
595  	    if ( _isBadFd ( fds[i] ) ) continue;
596  	    if ( ProtectedFDs::isProtected ( fds[i] ) ) continue;
597  	    dmtcp::string device = fdToDevice ( fds[i] );
598  	  }
599  	}
600  	
601  	void dmtcp::ConnectionToFds::serialize ( jalib::JBinarySerializer& o )
602  	{
603  	  JSERIALIZE_ASSERT_POINT ( "dmtcp-serialized-connection-table!v0.07" );
604  	  ConnectionList::instance().serialize ( o );
605  	  JSERIALIZE_ASSERT_POINT ( "dmtcp::ConnectionToFds:" );
606  	
607  	  // Current process information
608  	  o & _procname & _inhostname & _pid & _ppid;
609  	
610  	  size_t numCons = _table.size();
611  	  o & numCons;
612  	
613  	  if ( o.isWriter() )
614  	  {
615  	    _hostname = jalib::Filesystem::GetCurrentHostname();
616  	    o & _hostname;
617  	    JTRACE("Writing hostname to checkpoint file")(_hostname)(_inhostname)(_procname)(_ppid);
618  	
619  	    // Save connections
620  	    for ( iterator i=_table.begin(); i!=_table.end(); ++i )
621  	    {
622  	      JSERIALIZE_ASSERT_POINT ( "CFdEntry:" );
623  	      ConnectionIdentifier key = i->first;
624  	      dmtcp::vector<int>& val = i->second;
625  	      o & key & val;
626  	      JASSERT ( val.size() >0 ) (key) ( o.filename() ).Text ( "would write empty fd list" );
627  	    }
628  	  }
629  	  else
630  	  {
631  	    o & _hostname;
632  	    // Save connections
633  	    while ( numCons-- > 0 )
634  	    {
635  	      JSERIALIZE_ASSERT_POINT ( "CFdEntry:" );
636  	      ConnectionIdentifier key;
637  	      dmtcp::vector<int> val;
638  	      o & key & val;
639  	      JWARNING ( val.size() >0 ) (key) ( o.filename() ).Text ( "reading empty fd list" );
640  	      _table[key]=val;
641  	    }
642  	  }
643  	  JSERIALIZE_ASSERT_POINT ( "EOF" );
644  	}
645  	
646  	void dmtcp::KernelDeviceToConnection::serialize ( jalib::JBinarySerializer& o )
647  	{
648  	  JSERIALIZE_ASSERT_POINT ( "dmtcp-serialized-exec-lifeboat!v0.07" );
649  	  ConnectionList::instance().serialize ( o );
650  	  JSERIALIZE_ASSERT_POINT ( "dmtcp::KernelDeviceToConnection:" );
651  	
652  	  size_t numCons = _table.size();
653  	  o & numCons;
654  	
655  	  // Save/Restore parent process UniquePid
656  	  // parentProcess() is for inspection tools
657  	  o & UniquePid::ParentProcess();
658  	
659  	  if ( o.isWriter() )
660  	  {
661  	    for ( iterator i=_table.begin(); i!=_table.end(); ++i )
662  	    {
663  	      JSERIALIZE_ASSERT_POINT ( "KDEntry:" );
664  	      dmtcp::string key = i->first;
665  	      ConnectionIdentifier val = i->second;
666  	      o & key & val;
667  	    }
668  	  }
669  	  else
670  	  {
671  	    while ( numCons-- > 0 )
672  	    {
673  	      JSERIALIZE_ASSERT_POINT ( "KDEntry:" );
674  	      dmtcp::string key = "?";
675  	      ConnectionIdentifier val;
676  	      o & key & val;
677  	      _table[key] = val;
678  	    }
679  	
680  	  }
681  	
682  	  JSERIALIZE_ASSERT_POINT ( "EOF" );
683  	}
684  	
685  	
686  	dmtcp::Connection& dmtcp::ConnectionList::operator[] ( const ConnectionIdentifier& id )
687  	{
688  	  //  dmtcp::cout << "Operator [], conId=" << id << "\n";
689  	  JASSERT ( _connections.find ( id ) != _connections.end() ) ( id )
690  	  .Text ( "Unknown connection" );
691  	  //  dmtcp::cout << "Operator [], found: " << (_connections.find ( id ) != _connections.end())
692  	  //            << "\n";
693  	  //  dmtcp::cout << "Operator [], Result: conId=" << _connections[id]->id() << "\n";
694  	  return *_connections[id];
695  	}
696  	
697  	void dmtcp::ConnectionList::add ( Connection* c )
698  	{
699  	  JWARNING ( _connections.find ( c->id() ) == _connections.end() ) ( c->id() )
700  	  .Text ( "duplicate connection" );
701  	  _connections[c->id() ] = c;
702  	}
703  	
704  	int dmtcp::SlidingFdTable::getFdFor ( const ConnectionIdentifier& con )
705  	{
706  	  //is our work already done?
707  	  if ( _conToFd.find ( con ) != _conToFd.end() )
708  	    return _conToFd[con];
709  	
710  	  //find a free fd
711  	  int newfd;
712  	  while ( isInUse ( newfd = _nextFd++ ) ) {}
713  	
714  	  //ad it
715  	  _conToFd[con] = newfd;
716  	  _fdToCon[newfd]  = con;
717  	
718  	  JTRACE ( "allocated fd for connection" ) ( newfd ) ( con );
719  	
720  	  return newfd;
721  	}
722  	void dmtcp::SlidingFdTable::freeUpFd ( int fd )
723  	{
724  	  //is that FD in use?
725  	  if ( _fdToCon.find ( fd ) == _fdToCon.end() )
726  	    return;
727  	
728  	  ConnectionIdentifier con = _fdToCon[fd];
729  	
730  	  if ( con == ConnectionIdentifier::Null() )
731  	    return;
732  	
733  	  //find a free fd
734  	  int newfd;
735  	  while ( isInUse ( newfd = _nextFd++ ) ) {}
736  	
737  	  JTRACE ( "sliding fd for connection" ) ( fd ) ( newfd ) ( con );
738  	
739  	  //do the change
740  	  changeFd ( fd, newfd );
741  	
742  	  //update table
743  	  _conToFd[con] = newfd;
744  	  _fdToCon[newfd]  = con;
745  	  _fdToCon[fd] = ConnectionIdentifier::Null();
746  	}
747  	bool dmtcp::SlidingFdTable::isInUse ( int fd ) const
748  	{
749  	  if ( _fdToCon.find ( fd ) != _fdToCon.end() )
750  	    return true;
751  	  //double check with the filesystem
752  	  dmtcp::string device = jalib::Filesystem::ResolveSymlink ( _procFDPath ( fd ) );
753  	  return device != "";
754  	}
755  	void dmtcp::SlidingFdTable::changeFd ( int oldfd, int newfd )
756  	{
757  	  if ( oldfd == newfd ) return;
758  	  JASSERT ( _real_dup2 ( oldfd,newfd ) == newfd ) ( oldfd ) ( newfd ).Text ( "dup2() failed" );
759  	  JASSERT ( _real_close ( oldfd ) == 0 ) ( oldfd ).Text ( "close() failed" );
760  	}
761  	
762  	void dmtcp::SlidingFdTable::closeAll()
763  	{
764  	  for ( dmtcp::map< ConnectionIdentifier, int >::iterator i=_conToFd.begin()
765  	        ; i!=_conToFd.end()
766  	        ; ++i )
767  	  {
768  	    JWARNING ( _real_close ( i->second ) ==0 ) ( i->second ) ( JASSERT_ERRNO );
769  	  }
770  	  _conToFd.clear();
771  	}
772  	
773  	dmtcp::Connection& dmtcp::UniquePtsNameToPtmxConId::retrieve ( dmtcp::string str )
774  	{
775  	  iterator i = _table.find ( str );
776  	  JASSERT ( i != _table.end() ) ( str ) ( _table.size() ).Text ( "failed to find connection for fd" );
777  	  return ConnectionList::instance() [i->second];
778  	}
779  	
780  	
781  	dmtcp::string dmtcp::UniquePtsNameToPtmxConId::retrieveCurrentPtsDeviceName ( dmtcp::string str )
782  	{
783  	  iterator i = _table.find ( str );
784  	  JASSERT ( i != _table.end() ) ( str ) ( _table.size() ).Text ( "failed to find connection for fd" );
785  	  Connection* c = &(ConnectionList::instance() [i->second]);
786  	
787  	  PtyConnection* ptmxConnection = (PtyConnection *)c;
788  	
789  	  JASSERT( ptmxConnection->ptyType() == dmtcp::PtyConnection::PTY_MASTER );
790  	
791  	  return ptmxConnection->ptsName();
792  	}
793  	
794  	
795  	/*
796  	dmtcp::PtsToSymlink::PtsToSymlink() { }
797  	
798  	dmtcp::PtsToSymlink& dmtcp::PtsToSymlink::instance()
799  	{
800  	  static PtsToSymlink inst; return inst;
801  	}
802  	
803  	void dmtcp::PtsToSymlink::add ( dmtcp::string device, dmtcp::string filename )
804  	{
805  	  //    JWARNING(_table.find(device) == _table.end())(device)
806  	  //            .Text("duplicate connection");
807  	  _table[device] = filename;
808  	}
809  	
810  	void dmtcp::PtsToSymlink::replace ( dmtcp::string oldDevice, dmtcp::string newDevice )
811  	{
812  	  iterator i = _table.find ( oldDevice );
813  	  JASSERT ( i != _table.end() )( oldDevice ).Text ( "old device not found" );
814  	  dmtcp::string filename = _table[oldDevice];
815  	  _table.erase ( i );
816  	  _table[newDevice] = filename;
817  	}
818  	
819  	dmtcp::string dmtcp::PtsToSymlink::getFilename ( dmtcp::string device )
820  	{
821  	  dmtcp::string filename = "?";
822  	  iterator i = _table.find ( device );
823  	  if ( i != _table.end() )
824  	  {
825  	    filename = _table[device];
826  	  }
827  	  return filename;
828  	}
829  	
830  	bool dmtcp::PtsToSymlink::exists( dmtcp::string device )
831  	{
832  	  dmtcp::string filename = getFilename(device);
833  	  if (filename.compare("?") == 0){
834  	    return false;
835  	  }
836  	  return true;
837  	}
838  	*/
839  	
840  	pid_t dmtcp::ConnectionToFds::gzip_child_pid = -1;
841  	static void close_ckpt_to_read(const int fd)
842  	{
843  	    int status;
844  	    int rc;
Event neg_sink_parm_call: Passing "fd" to "close", which cannot accept a negative.
845  	    while (-1 == (rc = close(fd)) && errno == EINTR) ;
846  	    JASSERT (rc != -1) ("close:") (JASSERT_ERRNO);
847  	    if (dmtcp::ConnectionToFds::gzip_child_pid != -1) {
848  	      while (-1 == (rc = waitpid(dmtcp::ConnectionToFds::gzip_child_pid,
849  				         &status, 0)) && errno == EINTR) ;
850  	      JASSERT (rc != -1) ("waitpid:") (JASSERT_ERRNO);
851  	      dmtcp::ConnectionToFds::gzip_child_pid = -1;
852  	    }
853  	}
854  	
855  	// Define DMTCP_OLD_PCLOSE to get back the old buggy version.
856  	// Remove the old version when satisfied this is better.
857  	#ifndef DMTCP_OLD_PCLOSE
858  	// Copied from mtcp/mtcp_restart.c.
859  	#define DMTCP_MAGIC_FIRST 'D'
860  	#define GZIP_FIRST 037
861  	char *mtcp_executable_path(char *filename);
862  	static char first_char(const char *filename)
863  	{
864  	    int fd, rc;
865  	    char c;
866  	
867  	    fd = open(filename, O_RDONLY);
868  	    JASSERT(fd >= 0)(filename).Text("ERROR: Cannot open filename");
869  	
870  	    rc = read(fd, &c, 1);
871  	    JASSERT(rc == 1)(filename).Text("ERROR: Error reading from filename");
872  	
873  	    close(fd);
874  	    return c;
875  	}
876  	
877  	// Copied from mtcp/mtcp_restart.c.
878  	// Let's keep this code close to MTCP code to avoid maintenance problems.
879  	// MTCP code in:  mtcp/mtcp_restart.c:open_ckpt_to_read()
880  	// A previous version tried to replace this with popen, causing a regression:
881  	//   (no call to pclose, and possibility of using a wrong fd).
882  	// Returns fd; sets dmtcp::gzip_child_pid::ConnectionToFds, if gzip compression.
883  	static int open_ckpt_to_read(const char *filename)
884  	{
885  	    int fd;
886  	    int fds[2];
887  	    char fc;
888  	    const char *gzip_path = "gzip";
889  	    static const char * gzip_args[] = { "gzip", "-d", "-", NULL };
890  	    pid_t cpid;
891  	
892  	    fc = first_char(filename);
893  	    fd = open(filename, O_RDONLY);
894  	    JASSERT(fd>=0)(filename).Text("Failed to open file.");
895  	
896  	    if(fc == DMTCP_MAGIC_FIRST) /* no compression */
897  	        return fd;
898  	    else if(fc == GZIP_FIRST) /* gzip */
899  	    {
900  	        JASSERT(pipe(fds) != -1)(filename).Text("Cannote create pipe to execute gunzip to decompress checkpoint file!");
901  	
902  	        cpid = _real_fork();
903  	
904  	        JASSERT(cpid != -1).Text("ERROR: Cannot fork to execute gunzip to decompress checkpoint file!");
905  	        if(cpid > 0) /* parent process */
906  	        {
907  	           JTRACE ( "created gzip child process to uncompress checkpoint file")(cpid);
908  	            dmtcp::ConnectionToFds::gzip_child_pid = cpid;
909  	            close(fd);
910  	            close(fds[1]);
911  	            return fds[0];
912  	        }
913  	        else /* child process */
914  	        {
915  	           JTRACE ( "child process, will exec into gzip");
916  	            fd = dup(dup(dup(fd)));
917  	            fds[1] = dup(fds[1]);
918  	            close(fds[0]);
919  	            JASSERT(fd != -1);
920  	            JASSERT(dup2(fd, STDIN_FILENO) == STDIN_FILENO);
921  	            close(fd);
922  	            JASSERT(dup2(fds[1], STDOUT_FILENO) == STDOUT_FILENO);
923  	            close(fds[1]);
924  	            _real_execvp(gzip_path, (char **)gzip_args);
925  	            JASSERT(gzip_path!=NULL)(gzip_path).Text("Failed to launch gzip.");
926  	            /* should not get here */
927  	            JASSERT(false)("ERROR: Decompression failed!  No restoration will be performed!  Cancelling now!");
928  	            abort();
929  	        }
930  	    }
931  	    else /* invalid magic number */
932  	        JASSERT(false).Text("ERROR: Invalid magic number in this checkpoint file!");
933  	    // NOT_REACHED
934  	    return -1;
935  	}
936  	
937  	// See comments above for open_ckpt_to_read()
938  	int dmtcp::ConnectionToFds::openDmtcpCheckpointFile(const dmtcp::string& path){
939  	  // Function also sets dmtcp::gzip_child_pid::ConnectionToFds
940  	  int fd = open_ckpt_to_read( path.c_str() );
941  	  // The rest of this function is for compatibility with original definition.
942  	  JASSERT(fd>=0)(path).Text("Failed to open file.");
943  	  char buf[512];
944  	  const int len = strlen(DMTCP_FILE_HEADER);
945  	  JASSERT(read(fd, buf, len)==len)(path).Text("read() failed");
946  	  if(strncmp(buf, DMTCP_FILE_HEADER, len)==0){
947  	    JTRACE("opened checkpoint file [uncompressed]")(path);
948  	  }else{
949  	    close_ckpt_to_read(fd);
950  	    fd = open_ckpt_to_read( path.c_str() ); /* Re-open from beginning */
951  	  }
952  	  return fd;
953  	}
954  	#else
955  	int dmtcp::ConnectionToFds::openDmtcpCheckpointFile(const dmtcp::string& path){
956  	  int fd = open( path.c_str(), O_RDONLY);
957  	  JASSERT(fd>=0)(path).Text("Failed to open file.");
958  	  char buf[512];
959  	  const int len = strlen(DMTCP_FILE_HEADER);
960  	  JASSERT(read(fd, buf, len)==len)(path).Text("read() failed");
961  	  if(strncmp(buf, DMTCP_FILE_HEADER, len)==0){
962  	    JTRACE("opened checkpoint file [uncompressed]")(path);
963  	    return fd;
964  	  }else{
965  	    close(fd);
966  	    dmtcp::string cmd = dmtcp::string()+"exec gzip -d - < '"+path+"'";
967  	    FILE* t = popen(cmd.c_str(),"r");
968  	    JASSERT(t!=NULL)(path)(cmd).Text("Failed to launch gzip.");
969  	    JTRACE ( "created gzip child process to uncompress checkpoint file");
970  	    fd = fileno(t);
971  	    JASSERT(read(fd, buf, len)==len)(cmd)(path).Text("Invalid checkpoint file");
972  	    JASSERT(strncmp(buf, DMTCP_FILE_HEADER, len)==0)(path).Text("Invalid checkpoint file");
973  	    JTRACE("opened checkpoint file [compressed]")(path);
974  	    return fd;
975  	  }
976  	}
977  	#endif
978  	
979  	int dmtcp::ConnectionToFds::openMtcpCheckpointFile(const dmtcp::string& path){
980  	  int fd = openDmtcpCheckpointFile(path);
981  	  jalib::JBinarySerializeReaderRaw rdr(path, fd);
982  	  static ConnectionToFds trash;
983  	  trash.serialize(rdr);
984  	  return fd;
985  	}
986  	
987  	#ifdef PID_VIRTUALIZATION
988  	int dmtcp::ConnectionToFds::loadFromFile(const dmtcp::string& path, UniquePid &compGroup, int &numPeers, dmtcp::VirtualPidTable& virtualPidTable){
989  	#else
990  	int dmtcp::ConnectionToFds::loadFromFile(const dmtcp::string& path,UniquePid &compGroup,  int &numPeers){
991  	#endif
992  	  int fd = openDmtcpCheckpointFile(path);
Event var_tested_neg: Variable "fd" tests negative.
Also see events: [negative_returns]
At conditional (1): "fd != -1": Taking false branch.
993  	  JASSERT(fd != -1);
994  	  jalib::JBinarySerializeReaderRaw rdr(path, fd);
995  	  rdr & compGroup;
996  	  rdr & numPeers;
997  	  serialize(rdr);
998  	#ifdef PID_VIRTUALIZATION
999  	  virtualPidTable.serialize(rdr);
1000 	#endif
Event negative_returns: "fd" is passed to a parameter that cannot be negative. [details]
Also see events: [var_tested_neg]
1001 	  close_ckpt_to_read(fd);
1002 	  return rdr.bytes() + strlen(DMTCP_FILE_HEADER);
1003 	}