Annotation of qemu/roms/ipxe/contrib/vm/serial-console, revision 1.1

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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.