2011-04-04 20:16:00 +00:00
#
# The Qubes OS Project, http://www.qubes-os.org
#
# Copyright (C) 2011 Tomasz Sterna <tomek@xiaoka.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
#
import gtk
2011-07-20 14:55:43 +00:00
import libuser
2011-10-03 21:02:55 +00:00
import os , string , sys , time , re
2011-04-04 20:16:00 +00:00
import threading , subprocess , grp
from firstboot . config import *
from firstboot . constants import *
from firstboot . functions import *
from firstboot . module import *
import gettext
_ = lambda x : gettext . ldgettext ( " firstboot " , x )
N_ = lambda x : x
class moduleClass ( Module ) :
2015-03-10 20:46:22 +00:00
netvm_name = " sys-net "
fwvm_name = " sys-firewall "
2011-04-04 20:16:00 +00:00
def __init__ ( self ) :
Module . __init__ ( self )
self . priority = 10000
2011-04-05 17:14:35 +00:00
self . sidebarTitle = N_ ( " Create Service VMs " )
self . title = N_ ( " Create Service VMs " )
2011-04-04 20:16:00 +00:00
self . icon = " qubes.png "
2011-07-20 14:55:43 +00:00
self . admin = libuser . admin ( )
2015-04-14 21:40:49 +00:00
self . default_template = ' fedora-21 '
2011-04-04 20:16:00 +00:00
2014-04-08 03:27:18 +00:00
def _showErrorMessage ( self , text ) :
dlg = gtk . MessageDialog ( None , 0 , gtk . MESSAGE_ERROR , gtk . BUTTONS_OK , text )
dlg . set_position ( gtk . WIN_POS_CENTER )
dlg . set_modal ( True )
rc = dlg . run ( )
dlg . destroy ( )
return None
2011-04-04 20:16:00 +00:00
def apply ( self , interface , testing = False ) :
try :
2011-07-20 14:55:43 +00:00
qubes_users = self . admin . enumerateUsersByGroup ( ' qubes ' )
2014-04-08 03:27:18 +00:00
if not self . radio_dontdoanything . get_active ( ) :
if len ( qubes_users ) < 1 :
self . _showErrorMessage ( _ ( " You must create a user account to create default VMs. " ) )
return RESULT_FAILURE
else :
self . qubes_user = qubes_users [ 0 ]
2011-07-20 14:55:43 +00:00
2011-04-06 10:43:38 +00:00
self . radio_servicevms_and_appvms . set_sensitive ( False )
self . radio_onlyservicevms . set_sensitive ( False )
self . radio_dontdoanything . set_sensitive ( False )
2011-04-04 20:16:00 +00:00
if self . progress is None :
self . progress = gtk . ProgressBar ( )
self . progress . set_pulse_step ( 0.06 )
self . vbox . pack_start ( self . progress , True , False )
self . progress . show ( )
if testing :
return RESULT_SUCCESS
2015-04-07 13:06:24 +00:00
self . set_default_template ( )
2011-04-06 10:43:38 +00:00
if self . radio_dontdoanything . get_active ( ) :
return RESULT_SUCCESS
2014-03-21 01:49:59 +00:00
interface . nextButton . set_sensitive ( False )
2015-04-14 21:40:49 +00:00
errors = [ ]
try :
self . configure_template ( )
except Exception as e :
errors . append ( ( self . stage , str ( e ) ) )
try :
self . create_default_netvm ( )
self . create_default_fwvm ( )
self . set_networking_type ( netvm = True )
self . start_qubes_networking ( )
except Exception as e :
errors . append ( ( self . stage , str ( e ) ) )
try :
self . create_default_dvm ( )
except Exception as e :
errors . append ( ( self . stage , str ( e ) ) )
2011-04-04 20:16:00 +00:00
2011-04-06 10:43:38 +00:00
if self . radio_servicevms_and_appvms . get_active ( ) :
2015-04-14 21:40:49 +00:00
try :
self . create_appvms ( )
except Exception as e :
errors . append ( ( self . stage , str ( e ) ) )
if errors :
msg = " "
for ( stage , error ) in errors :
msg + = " {} failed: \n {} \n \n " . format ( stage , error )
self . stage = " firstboot "
raise Exception ( msg )
2011-04-06 10:43:38 +00:00
2014-03-21 01:49:59 +00:00
interface . nextButton . set_sensitive ( True )
2011-04-04 20:16:00 +00:00
return RESULT_SUCCESS
except Exception as e :
md = gtk . MessageDialog ( interface . win , gtk . DIALOG_DESTROY_WITH_PARENT ,
gtk . MESSAGE_ERROR , gtk . BUTTONS_CLOSE ,
self . stage + " failure! \n \n " + str ( e ) )
md . run ( )
md . destroy ( )
self . show_stage ( " Failure... " )
self . progress . hide ( )
2011-04-06 11:15:20 +00:00
2012-03-10 21:44:08 +00:00
self . radio_dontdoanything . set_active ( True )
2014-03-21 01:49:59 +00:00
interface . nextButton . set_sensitive ( True )
2011-04-06 11:15:20 +00:00
2011-04-04 20:16:00 +00:00
return RESULT_FAILURE
def show_stage ( self , stage ) :
self . stage = stage
self . progress . set_text ( stage )
2015-04-07 13:06:24 +00:00
def set_default_template ( self ) :
2015-04-14 21:40:49 +00:00
subprocess . call ( [ ' /usr/bin/qubes-prefs ' , ' --set ' , ' default-template ' , self . default_template ] )
2015-04-07 13:06:24 +00:00
2011-04-04 20:16:00 +00:00
def configure_template ( self ) :
2011-04-06 08:20:38 +00:00
self . show_stage ( _ ( " Configuring default TemplateVM " ) )
2011-04-05 17:14:35 +00:00
self . run_in_thread ( self . do_configure_template )
2011-04-04 20:16:00 +00:00
def run_in_thread ( self , method ) :
thread = threading . Thread ( target = method )
thread . start ( )
count = 0
while thread . is_alive ( ) :
self . progress . pulse ( )
while gtk . events_pending ( ) :
gtk . main_iteration ( False )
time . sleep ( 0.1 )
if self . process_error is not None :
raise Exception ( self . process_error )
def create_default_netvm ( self ) :
self . show_stage ( _ ( " Creating default NetworkVM " ) )
self . run_in_thread ( self . do_create_netvm )
def create_default_fwvm ( self ) :
self . show_stage ( _ ( " Creating default FirewallVM " ) )
self . run_in_thread ( self . do_create_fwvm )
def create_default_dvm ( self ) :
self . show_stage ( _ ( " Creating default DisposableVM " ) )
self . run_in_thread ( self . do_create_dvm )
def set_networking_type ( self , netvm ) :
if netvm :
self . show_stage ( _ ( " Setting FirewallVM + NetworkVM networking " ) )
self . run_in_thread ( self . do_set_netvm_networking )
else :
self . show_stage ( _ ( " Setting Dom0 networking " ) )
self . run_in_thread ( self . do_set_dom0_networking )
2011-04-05 17:14:35 +00:00
def start_qubes_networking ( self ) :
2011-04-05 21:11:45 +00:00
self . show_stage ( _ ( " Starting Qubes networking " ) )
2011-04-05 17:14:35 +00:00
self . run_in_thread ( self . do_start_networking )
2011-04-06 10:43:38 +00:00
def create_appvms ( self ) :
self . show_stage ( _ ( " Creating handy AppVMs " ) )
self . run_in_thread ( self . do_create_appvms )
2015-04-14 21:40:49 +00:00
def run_command ( self , command , stdin = None ) :
2011-04-04 20:16:00 +00:00
try :
os . setgid ( self . qubes_gid )
os . umask ( 0007 )
2015-04-14 21:40:49 +00:00
cmd = subprocess . Popen ( command , stdout = subprocess . PIPE , stderr = subprocess . STDOUT , stdin = stdin )
2011-04-04 20:16:00 +00:00
out = cmd . communicate ( ) [ 0 ]
if cmd . returncode == 0 :
self . process_error = None
else :
2015-04-14 21:40:49 +00:00
self . process_error = " {} failed: \n {} " . format ( command , out )
# Actually only self.process_error will be visible to the user
raise Exception ( " {} failed " . format ( command ) )
2011-04-04 20:16:00 +00:00
except Exception as e :
self . process_error = str ( e )
2015-03-19 10:09:16 +00:00
def get_timezone ( self ) :
localtime = " /etc/localtime "
zoneinfo = " /usr/share/zoneinfo/ " # must end with "/"
if os . path . exists ( localtime ) and os . path . islink ( localtime ) :
tzfile = os . path . realpath ( localtime )
if tzfile . startswith ( zoneinfo ) :
return tzfile [ len ( zoneinfo ) : ]
return None
2011-12-23 16:25:02 +00:00
def find_net_devices ( self ) :
2011-12-19 15:54:55 +00:00
p = subprocess . Popen ( [ " /sbin/lspci " , " -mm " , " -n " ] , stdout = subprocess . PIPE )
2011-10-03 21:02:55 +00:00
result = p . communicate ( )
retcode = p . returncode
if ( retcode != 0 ) :
print " ERROR when executing lspci! "
raise IOError
net_devices = set ( )
rx_netdev = re . compile ( r " ^([0-9][0-9]:[0-9][0-9].[0-9]) \" 02 " )
for dev in str ( result [ 0 ] ) . splitlines ( ) :
match = rx_netdev . match ( dev )
if match is not None :
dev_bdf = match . group ( 1 )
assert dev_bdf is not None
net_devices . add ( dev_bdf )
return net_devices
2011-04-04 20:16:00 +00:00
def do_create_netvm ( self ) :
2013-01-26 21:39:54 +00:00
self . run_command ( [ ' su ' , ' -c ' , ' /usr/bin/qvm-create --net --label red %s ' % self . netvm_name , self . qubes_user ] )
2011-12-12 03:49:23 +00:00
for dev in self . find_net_devices ( ) :
2011-10-03 21:02:55 +00:00
self . run_command ( [ ' /usr/bin/qvm-pci ' , ' -a ' , self . netvm_name , dev ] )
2011-04-04 20:16:00 +00:00
def do_create_fwvm ( self ) :
2013-01-26 21:39:54 +00:00
self . run_command ( [ ' su ' , ' -c ' , ' /usr/bin/qvm-create --proxy --label green %s ' % self . fwvm_name , ' - ' , self . qubes_user ] )
2011-04-04 20:16:00 +00:00
def do_create_dvm ( self ) :
2015-04-14 21:40:49 +00:00
try :
self . run_command ( [ ' su ' , ' -c ' , ' /usr/bin/qvm-create-default-dvm --default-template --default-script ' , self . qubes_user ] )
except :
# Kill DispVM template if still running
# Do not use self.run_command to not clobber process output
subprocess . call ( [ ' qvm-kill ' , ' {} -dvm ' . format ( self . default_template ) ] )
raise
2011-04-04 20:16:00 +00:00
def do_set_netvm_networking ( self ) :
2013-01-26 21:39:54 +00:00
self . run_command ( [ ' /usr/bin/qvm-prefs ' , ' --force-root ' , ' --set ' , self . fwvm_name , ' netvm ' , self . netvm_name ] )
2012-02-07 02:11:16 +00:00
self . run_command ( [ ' /usr/bin/qubes-prefs ' , ' --set ' , ' default-netvm ' , self . fwvm_name ] )
2011-04-04 20:16:00 +00:00
def do_set_dom0_networking ( self ) :
2012-02-07 02:11:16 +00:00
self . run_command ( [ ' /usr/bin/qubes-prefs ' , ' --set ' , ' default-netvm ' , ' dom0 ' ] )
2011-04-04 20:16:00 +00:00
2011-04-05 17:14:35 +00:00
def do_start_networking ( self ) :
2015-04-14 21:40:49 +00:00
self . run_command ( [ ' /usr/sbin/service ' , ' qubes-netvm ' , ' start ' ] )
2011-04-05 17:14:35 +00:00
def do_configure_template ( self ) :
2012-07-20 11:02:19 +00:00
for template in os . listdir ( ' /var/lib/qubes/vm-templates ' ) :
2015-04-14 21:40:49 +00:00
self . run_command ( [ ' qvm-start ' , ' --no-guid ' , template ] )
2012-07-20 11:02:19 +00:00
# Copy timezone setting from Dom0 to template
2015-04-14 21:40:49 +00:00
self . run_command ( [ ' qvm-run ' , ' --nogui ' , ' --pass-io ' ,
2015-03-19 10:09:16 +00:00
' -u ' , ' root ' , template , ' cat > /etc/locale.conf ' ] ,
stdin = open ( ' /etc/locale.conf ' , ' r ' ) )
2015-04-14 21:40:49 +00:00
self . run_command ( [ ' su ' , ' -c ' ,
2015-03-19 10:09:16 +00:00
' qvm-sync-appmenus {} ' . format ( template ) ,
' - ' , self . qubes_user ] )
2015-04-14 21:40:49 +00:00
self . run_command ( [ ' qvm-shutdown ' , ' --wait ' , template ] )
2011-04-05 17:14:35 +00:00
2011-04-06 10:43:38 +00:00
def do_create_appvms ( self ) :
2011-07-20 14:55:43 +00:00
self . run_command ( [ ' su ' , ' -c ' , ' /usr/bin/qvm-create work --label green ' , ' - ' , self . qubes_user ] )
self . run_command ( [ ' su ' , ' -c ' , ' /usr/bin/qvm-create banking --label green ' , ' - ' , self . qubes_user ] )
self . run_command ( [ ' su ' , ' -c ' , ' /usr/bin/qvm-create personal --label yellow ' , ' - ' , self . qubes_user ] )
self . run_command ( [ ' su ' , ' -c ' , ' /usr/bin/qvm-create untrusted --label red ' , ' - ' , self . qubes_user ] )
2011-04-06 10:43:38 +00:00
2011-04-04 20:16:00 +00:00
def createScreen ( self ) :
self . vbox = gtk . VBox ( spacing = 5 )
2011-04-06 17:06:00 +00:00
label = gtk . Label ( _ ( " Almost there! We just need to create a few system service VM. \n \n "
2011-07-02 12:56:56 +00:00
" We can also create a few AppVMs that might be useful for most users, "
" or you might prefer to do it yourself later. \n \n "
2011-04-06 10:43:38 +00:00
" Choose an option below and click ' Finish ' ... " ) )
2011-04-04 20:16:00 +00:00
label . set_line_wrap ( True )
label . set_alignment ( 0.0 , 0.5 )
label . set_size_request ( 500 , - 1 )
self . vbox . pack_start ( label , False , True , padding = 20 )
2011-07-02 12:56:56 +00:00
self . radio_servicevms_and_appvms = gtk . RadioButton ( None , _ ( " Create default service VMs, and pre-defined AppVMs (work, banking, personal, untrusted) " ) )
2011-04-06 10:43:38 +00:00
self . vbox . pack_start ( self . radio_servicevms_and_appvms , False , True )
2011-04-06 11:15:20 +00:00
self . radio_onlyservicevms = gtk . RadioButton ( self . radio_servicevms_and_appvms , _ ( " Just create default service VMs " ) )
2011-04-06 10:43:38 +00:00
self . vbox . pack_start ( self . radio_onlyservicevms , False , True )
2011-04-06 11:15:20 +00:00
self . radio_dontdoanything = gtk . RadioButton ( self . radio_servicevms_and_appvms , _ ( " Do not create any VMs right now (not recommended, for advanced users only) " ) )
2011-04-06 10:43:38 +00:00
self . vbox . pack_start ( self . radio_dontdoanything , False , True )
2011-04-04 20:16:00 +00:00
self . progress = None
def initializeUI ( self ) :
2011-04-06 10:43:38 +00:00
self . radio_servicevms_and_appvms . set_active ( True )
2011-04-04 20:16:00 +00:00
self . qubes_gid = grp . getgrnam ( ' qubes ' ) . gr_gid
self . stage = " Initialization "
self . process_error = None