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 );
|
Event past_the_end: |
Function "end" creates an iterator. |
|
Event tested_end: |
"i" testing equal to "this->_table.end()". |
| Also see events: |
[deref_iterator] |
|
At conditional (1): "i.operator !=(this->_table.end())": Taking false branch.
|
104 JASSERT ( i != _table.end() ) ( fd ) ( device ) ( _table.size() ).Text ( "failed to find connection for fd" );
|
Event deref_iterator: |
Dereferencing iterator "i" though it is already past the end of its container. |
| Also see events: |
[past_the_end][tested_end] |
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;
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);
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
1001 close_ckpt_to_read(fd);
1002 return rdr.bytes() + strlen(DMTCP_FILE_HEADER);
1003 }