File Coverage

consoles/virtio_terminal.pm
Criterion Covered Total %
statement 150 150 100.0
total 150 150 100.0


line stmt code
1   # Copyright 2016-2020 SUSE LLC
2   # SPDX-License-Identifier: GPL-2.0-or-later
3    
4   use Mojo::Base 'consoles::console', -signatures;
5 6 use autodie;
  6  
  6  
6 6 use Mojo::File 'path';
  6  
  6  
7 6 use Socket qw(SOCK_NONBLOCK PF_UNIX SOCK_STREAM sockaddr_un);
  6  
  6  
8 6 use Errno qw(EAGAIN EWOULDBLOCK);
  6  
  6  
9 6 use English -no_match_vars;
  6  
  6  
10 6 use Carp 'croak';
  6  
  6  
11 6 use Scalar::Util 'blessed';
  6  
  6  
12 6 use Cwd;
  6  
  6  
13 6 use consoles::serial_screen ();
  6  
  6  
14 6 use Fcntl;
  6  
  6  
15 6  
  6  
  6  
16   our $VERSION;
17    
18   =head1 NAME
19    
20   consoles::virtio_terminal
21    
22   =head1 SYNOPSIS
23    
24   Provides functions to allow the testapi to interact with a text only console.
25    
26   =head1 DESCRIPTION
27    
28   This console can be requested when the backend (usually QEMU/KVM) and guest OS
29   support virtio serial and virtio console. The guest also needs to be in a state
30   where it can start a tty on the virtual console. By default openSUSE and SLE
31   automatically start agetty when the kernel finds the virtio console device, but
32   another OS may require some additional configuration.
33    
34   It may also be possible to use a transport other than virtio. This code
35   uses two pipes to communicate with virtio_consoles from qemu.
36    
37   =head1 SUBROUTINES/METHODS
38    
39   =cut
40    
41   my $self = $class->SUPER::new($testapi_console, $args);
42 12 $self->{fd_read} = 0;
  12  
  12  
  12  
  12  
43 12 $self->{fd_write} = 0;
44 12 $self->{pipe_prefix} = $self->{args}->{socked_path} // cwd() . '/virtio_console';
45 12 $self->{snapshots} = {};
46 12 $self->{preload_buffer} = '';
47 12 return $self;
48 12 }
49 12  
50    
51   return undef unless $self->{fd_read} > 0;
52 5 close $self->{fd_read};
  5  
  5  
  5  
53   close $self->{fd_write};
54 1 $self->{fd_read} = 0;
  1  
  1  
55 1 $self->{fd_write} = 0;
56 1 $self->{screen} = undef;
57 1 }
58 1  
59 1 $self->set_snapshot($name, 'activated', $self->{activated});
60 1 $self->set_snapshot($name, 'buffer', $self->{screen} ? $self->{screen}->peak() : $self->{preload_buffer});
61   }
62    
63 4 $self->{activated} = $self->get_snapshot($name, 'activated') // 0;
  4  
  4  
  4  
64 4 my $buffer = $self->get_snapshot($name, 'buffer') // '';
65 4 if (defined($self->{screen})) {
66   $self->{screen}->{carry_buffer} = $buffer;
67   } else {
68 3 $self->{preload_buffer} = $buffer;
  3  
  3  
  3  
69 3 }
70 3 }
71 3  
72 2 my $snapshot = $self->{snapshots}->{$name};
73   return (defined($key) && $snapshot) ? $snapshot->{$key} : $snapshot;
74 1 }
75    
76   $self->{snapshots}->{$name}->{$key} = $value;
77   }
78 15  
  15  
  15  
  15  
  15  
79 15 =head2 F_GETPIPE_SZ
80 15 This is a helper method for system which do not have F_GETPIPE_SZ in
81   there Fcntl bindings. See https://perldoc.perl.org/Fcntl.html
82   =cut
83 8  
  8  
  8  
  8  
  8  
  8  
84 8 =head2 F_SETPIPE_SZ
85   This is a helper method for system which do not have F_SETPIPE_SZ in
86   there Fcntl bindings. See: https://perldoc.perl.org/Fcntl.html
87   =cut
88    
89   no autodie;
90   return fcntl($fd, F_SETPIPE_SZ(), int($newsize));
91 4 }
  4  
  4  
  2  
  2  
  2  
  2  
  2  
  2  
92    
93    
94   =head2 open_pipe
95    
96   open_pipe();
97 4  
  4  
  4  
  3  
  3  
  3  
  1  
  1  
  1  
98   Opens a the read and write pipe based on C<$pipe_prefix>.
99 4  
  4  
  4  
  4  
  4  
100 6 Returns the read and write file descriptors for the open sockets,
  6  
  6  
101 4 otherwise it dies.
102    
103   =cut
104 4 bmwqemu::log_call(pipe_prefix => $self->{pipe_prefix});
  4  
  4  
  4  
  4  
105    
106   sysopen(my $fd_w, $self->{pipe_prefix} . '.in', O_WRONLY)
107   or die "Can't open in pipe for writing $!";
108   sysopen(my $fd_r, $self->{pipe_prefix} . '.out', O_NONBLOCK | O_RDONLY)
109   or die "Can't open out pipe for reading $!";
110    
111   my $newsize = $bmwqemu::vars{VIRTIO_CONSOLE_PIPE_SZ} // path('/proc/sys/fs/pipe-max-size')->slurp();
112   for my $fd (($fd_w, $fd_r)) {
113   my $old = $self->get_pipe_sz($fd) or die("Unable to read PIPE_SZ");
114   {
115   my $new;
116 9 while ($newsize > $old) {
  9  
  9  
117 9 $new = $self->set_pipe_sz($fd, $newsize);
118   last if ($new);
119 9 $newsize /= 2;
120   }
121 8 $new //= $old;
122   bmwqemu::fctinfo("Set PIPE_SZ from $old to $new");
123   }
124 8 }
125 8  
126 15 return ($fd_r, $fd_w);
127   }
128 14  
  14  
129 14 croak 'VIRTIO_CONSOLE is set 0, so no virtio-serial and virtconsole devices will be available to use with this console'
130 21 unless ($bmwqemu::vars{VIRTIO_CONSOLE} // 1);
131 21 ($self->{fd_read}, $self->{fd_write}) = $self->open_pipe() unless ($self->{fd_read});
132 11 $self->{screen} = consoles::serial_screen::->new($self->{fd_read}, $self->{fd_write});
133   $self->{screen}->{carry_buffer} = $self->{preload_buffer};
134 14 $self->{preload_buffer} = '';
135 14 return;
136   }
137    
138    
139 7 1;