|
|
1.1 ! root 1: #!/usr/bin/perl -w ! 2: ! 3: =head1 NAME ! 4: ! 5: serial-console ! 6: ! 7: =head1 SYNOPSIS ! 8: ! 9: serial-console [options] ! 10: ! 11: Options: ! 12: ! 13: -h,--help Display brief help message ! 14: -v,--verbose Increase verbosity ! 15: -q,--quiet Decrease verbosity ! 16: -l,--log FILE Log output to file ! 17: -r,--rcfile FILE Modify specified bochsrc file ! 18: ! 19: =head1 DESCRIPTION ! 20: ! 21: C<serial-console> provides a virtual serial console for use with ! 22: Bochs. Running C<serial-console> creates a pseudo-tty. The master ! 23: side of this pty is made available to the user for interaction; the ! 24: slave device is written to the Bochs configuration file ! 25: (C<bochsrc.txt>) for use by a subsequent Bochs session. ! 26: ! 27: =head1 EXAMPLES ! 28: ! 29: =over 4 ! 30: ! 31: =item C<serial-console> ! 32: ! 33: Create a virtual serial console for Bochs, modify C<bochsrc.txt> ! 34: appropriately. ! 35: ! 36: =item C<serial-console -r ../.bochsrc -l serial.log> ! 37: ! 38: Create a virtual serial console for Bochs, modify C<../.bochsrc> ! 39: appropriately, log output to C<serial.log>. ! 40: ! 41: =back ! 42: ! 43: =head1 INVOCATION ! 44: ! 45: Before starting Bochs, run C<serial-console> in a different session ! 46: (e.g. a different xterm window). When you subsequently start Bochs, ! 47: anything that the emulated machine writes to its serial port will ! 48: appear in the window running C<serial-console>, and anything typed in ! 49: the C<serial-console> window will arrive on the emulated machine's ! 50: serial port. ! 51: ! 52: You do B<not> need to rerun C<serial-console> afresh for each Bochs ! 53: session. ! 54: ! 55: =head1 OPTIONS ! 56: ! 57: =over 4 ! 58: ! 59: =item B<-l,--log FILE> ! 60: ! 61: Log all output (i.e. everything that is printed in the ! 62: C<serial-console> window) to the specified file. ! 63: ! 64: =item B<-r,--rcfile FILE> ! 65: ! 66: Modify the specified bochsrc file. The file will be updated to ! 67: contain the path to the slave side of the psuedo tty that we create. ! 68: The original file will be restored when C<serial-console> exits. The ! 69: default is to modify the file C<bochsrc.txt> in the current directory. ! 70: ! 71: To avoid modifying any bochsrc file, use C<--norcfile>. ! 72: ! 73: =back ! 74: ! 75: =cut ! 76: ! 77: use IO::Pty; ! 78: use IO::Select; ! 79: use File::Spec::Functions qw ( :ALL ); ! 80: use Getopt::Long; ! 81: use Pod::Usage; ! 82: use POSIX qw ( :termios_h ); ! 83: use strict; ! 84: use warnings; ! 85: ! 86: my $o; ! 87: my $restore_file = {}; ! 88: my $restore_termios; ! 89: use constant BLOCKSIZE => 8192; ! 90: ! 91: ############################################################################## ! 92: # ! 93: # Parse command line options into options hash ($o) ! 94: # ! 95: # $o = parse_opts(); ! 96: ! 97: sub parse_opts { ! 98: # $o is the hash that will hold the options ! 99: my $o = { ! 100: verbosity => 1, ! 101: rcfile => 'bochsrc.txt', ! 102: }; ! 103: # Special handlers for some options ! 104: my $opt_handlers = { ! 105: verbose => sub { $o->{verbosity}++; }, ! 106: quiet => sub { $o->{verbosity}--; }, ! 107: help => sub { pod2usage(1); }, ! 108: norcfile => sub { delete $o->{rcfile}; }, ! 109: }; ! 110: # Merge handlers into main options hash (so that Getopt::Long can find them) ! 111: $o->{$_} = $opt_handlers->{$_} foreach keys %$opt_handlers; ! 112: # Option specifiers for Getopt::Long ! 113: my @optspec = ( 'help|h|?', ! 114: 'quiet|q+', ! 115: 'verbose|v+', ! 116: 'log|l=s', ! 117: 'rcfile|r=s', ! 118: 'norcfile', ! 119: ); ! 120: # Do option parsing ! 121: Getopt::Long::Configure ( 'bundling' ); ! 122: pod2usage("Error parsing command-line options") unless GetOptions ( ! 123: $o, @optspec ); ! 124: # Clean up $o by removing the handlers ! 125: delete $o->{$_} foreach keys %$opt_handlers; ! 126: return $o; ! 127: } ! 128: ! 129: ############################################################################## ! 130: # ! 131: # Modify bochsrc file ! 132: ! 133: sub patch_bochsrc { ! 134: my $active = shift; ! 135: my $pty = shift; ! 136: ! 137: # Rename active file to backup file ! 138: ( my $vol, my $dir, my $file ) = splitpath ( $active ); ! 139: $file = '.'.$file.".serial-console"; ! 140: my $backup = catpath ( $vol, $dir, $file ); ! 141: rename $active, $backup ! 142: or die "Could not back up $active to $backup: $!\n"; ! 143: ! 144: # Derive line to be inserted ! 145: my $patch = "com1: enabled=1, mode=term, dev=$pty\n"; ! 146: ! 147: # Modify file ! 148: open my $old, "<$backup" or die "Could not open $backup: $!\n"; ! 149: open my $new, ">$active" or die "Could not open $active: $!\n"; ! 150: print $new <<"EOF"; ! 151: ################################################## ! 152: # ! 153: # This file has been modified by serial-console. ! 154: # ! 155: # Do not modify this file; it will be erased when ! 156: # serial-console (pid $$) exits and will be ! 157: # replaced with the backup copy held in ! 158: # $backup. ! 159: # ! 160: ################################################## ! 161: ! 162: ! 163: EOF ! 164: my $patched; ! 165: while ( my $line = <$old> ) { ! 166: if ( $line =~ /^\s*\#?\s*com1:\s*\S/ ) { ! 167: if ( ! $patched ) { ! 168: $line = $patch; ! 169: $patched = 1; ! 170: } else { ! 171: $line = '# '.$line unless $line =~ /^\s*\#/; ! 172: } ! 173: } ! 174: print $new $line; ! 175: } ! 176: print $new $patch unless $patched; ! 177: close $old; ! 178: close $new; ! 179: ! 180: return $backup; ! 181: } ! 182: ! 183: ############################################################################## ! 184: # ! 185: # Attach/detach message printing and terminal settings ! 186: ! 187: sub bochs_attached { ! 188: print STDERR "Bochs attached.\n\n\n" ! 189: if $o->{verbosity} >= 1; ! 190: } ! 191: ! 192: sub bochs_detached { ! 193: print STDERR "\n\nWaiting for bochs to attach...\n" ! 194: if $o->{verbosity} >= 1; ! 195: } ! 196: ! 197: ############################################################################## ! 198: # ! 199: # Main program ! 200: ! 201: $o = parse_opts(); ! 202: pod2usage(1) if @ARGV; ! 203: ! 204: # Catch signals ! 205: my $sigdie = sub { die "Exiting via signal\n"; }; ! 206: $SIG{INT} = $sigdie; ! 207: ! 208: # Create Pty, close slave side ! 209: my $pty = IO::Pty->new(); ! 210: $pty->close_slave(); ! 211: $pty->set_raw(); ! 212: print STDERR "Slave pty is ".$pty->ttyname."\n" if $o->{verbosity} >= 1; ! 213: ! 214: # Open logfile ! 215: my $log; ! 216: if ( $o->{log} ) { ! 217: open $log, ">$o->{log}" or die "Could not open $o->{log}: $!\n"; ! 218: } ! 219: ! 220: # Set up terminal ! 221: my $termios; ! 222: if ( -t STDIN ) { ! 223: $termios = POSIX::Termios->new; ! 224: $restore_termios = POSIX::Termios->new; ! 225: $termios->getattr ( fileno(STDIN) ); ! 226: $restore_termios->getattr ( fileno(STDIN) ); ! 227: $termios->setlflag ( $termios->getlflag & ~(ICANON) & ~(ECHO) ); ! 228: $termios->setiflag ( $termios->getiflag & ~(ICRNL) ); ! 229: $termios->setattr ( fileno(STDIN), TCSANOW ); ! 230: } ! 231: ! 232: # Modify bochsrc file ! 233: $restore_file = { $o->{rcfile} => ! 234: patch_bochsrc ( $o->{rcfile}, $pty->ttyname ) } ! 235: if $o->{rcfile}; ! 236: ! 237: # Start character shunt ! 238: my $attached = 1; ! 239: my $select = IO::Select->new ( \*STDIN, $pty ); ! 240: while ( 1 ) { ! 241: my %can_read = map { $_ => 1 } ! 242: $select->can_read ( $attached ? undef : 1 ); ! 243: if ( $can_read{\*STDIN} ) { ! 244: sysread ( STDIN, my $data, BLOCKSIZE ) ! 245: or die "Cannot read from STDIN: $!\n"; ! 246: $pty->syswrite ( $data ); ! 247: } ! 248: if ( $can_read{$pty} ) { ! 249: if ( $pty->sysread ( my $data, BLOCKSIZE ) ) { ! 250: # Actual data available ! 251: bochs_attached() if $attached == 0; ! 252: $attached = 1; ! 253: syswrite ( STDOUT, $data ); ! 254: $log->syswrite ( $data ) if $log; ! 255: } else { ! 256: # No data available but select() says we can read. This almost ! 257: # certainly indicates that nothing is attached to the slave. ! 258: bochs_detached() if $attached == 1; ! 259: $attached = 0; ! 260: sleep ( 1 ); ! 261: } ! 262: } else { ! 263: bochs_attached() if $attached == 0; ! 264: $attached = 1; ! 265: } ! 266: } ! 267: ! 268: END { ! 269: # Restore bochsrc file if applicable ! 270: if ( ( my $orig_file, my $backup_file ) = %$restore_file ) { ! 271: unlink $orig_file; ! 272: rename $backup_file, $orig_file; ! 273: } ! 274: # Restore terminal settings if applicable ! 275: if ( $restore_termios ) { ! 276: $restore_termios->setattr ( fileno(STDIN), TCSANOW ); ! 277: } ! 278: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.