(1) disptach.py: 下面我們看一下Dispatcher類的主要接口。
1)gotoNext & gotoPrev:這兩個接口分別從當前安裝步驟前進(後退)到下一個(上一個)具有用戶界面的安裝步驟,在圖形界面安裝模式下,由InstallControlWindow類調用,在字符模式下,由InstallInterface類(在text.py和cmdline.py中)調用。這兩個函數只是簡單的設置安裝方向,然後調用moveStep函數,其核心操作是moveStep。2)moveStep:我們來重點分析movestep函數,代碼如下:
- def moveStep(self):
- if self.step == None:
- self.step = self.firstStep
- else:
- if self.step >= len(installSteps):
- return None
- log.info("leaving (%d) step %s" %(self._getDir(), installSteps[self.step][0]))
- self.step = self.step + self._getDir()
- if self.step >= len(installSteps):
- return None
- while self.step >= self.firstStep and self.step < len(installSteps) \
- and (self.stepInSkipList(self.step) or self.stepIsDirect(self.step)):
- if self.stepIsDirect(self.step) and not self.stepInSkipList(self.step):
- (stepName, stepFunc) = installSteps[self.step]
- log.info("moving (%d) to step %s" %(self._getDir(), stepName))
- log.debug("%s is a direct step" %(stepName,))
- rc = stepFunc(self.anaconda)
- if rc in [DISPATCH_BACK, DISPATCH_FORWARD]:
- self._setDir(rc)
- log.info("leaving (%d) step %s" %(self._getDir(), stepName))
- # if anything else, leave self.dir alone
- self.step = self.step + self._getDir()
- if self.step == len(installSteps): # 安裝過程完成,退出循環
- return None
- if (self.step < 0):
- # pick the first step not in the skip list
- self.step = 0
- while self.skipSteps.has_key(installSteps[self.step][0]):
- self.step = self.step + 1 # 步數加一,向前
- elif self.step >= len(installSteps):
- self.step = len(installSteps) - 1
- while self.skipSteps.has_key(installSteps[self.step][0]):
- self.step = self.step - 1
- log.info("moving (%d) to step %s" %(self._getDir(), installSteps[self.step][0]))
3)currentStep:Dispatcher類的另一個主要接口,取得當前的安裝步驟及其相關信息返回給調用者。在圖形安裝模式下,該函數主要在InstallControlWindow調度圖形用戶界面類時調用,在字符模式下,主要在InstallInterface調度字符用戶界面時調用,這兩個類通過該接口取得當前安裝步驟的用戶界面對應類及創建該用戶界面類的實例所需的信息。
- def currentStep(self):
- if self.step == None:
- self.gotoNext()
- elif self.step >= len(installSteps):
- return (None, None)
- stepInfo = installSteps[self.step]
- step = stepInfo[0]
- return (step, self.anaconda)
(2)gui.py: 核心是字符安裝模式的InstallInterface類和圖形安裝模式InstallControlWindow類的實現。看InstallControlWindow中的接口。
1)數據結構stepTopClass: 該字典中記錄了安裝過程中所有的具有圖形用戶界面的安裝步驟。
- stepToClass = {
- "language" : ("language_gui", "LanguageWindow"),
- "keyboard" : ("kbd_gui", "KeyboardWindow"),
- "filtertype" : ("filter_type", "FilterTypeWindow"),
- "filter" : ("filter_gui", "FilterWindow"),
- "zfcpconfig" : ("zfcp_gui", "ZFCPWindow"),
- "partition" : ("partition_gui", "PartitionWindow"),
- "parttype" : ("autopart_type", "PartitionTypeWindow"),
- "cleardiskssel": ("cleardisks_gui", "ClearDisksWindow"),
- "findinstall" : ("examine_gui", "UpgradeExamineWindow"),
- "addswap" : ("upgrade_swap_gui", "UpgradeSwapWindow"),
- "upgrademigratefs" : ("upgrade_migratefs_gui", "UpgradeMigrateFSWindow"),
- "bootloader": ("bootloader_main_gui", "MainBootloaderWindow"),
- "upgbootloader": ("upgrade_bootloader_gui", "UpgradeBootloaderWindow"),
- "network" : ("network_gui", "NetworkWindow"),
- "timezone" : ("timezone_gui", "TimezoneWindow"),
- "accounts" : ("account_gui", "AccountWindow"),
- "tasksel": ("task_gui", "TaskWindow"),
- "group-selection": ("package_gui", "GroupSelectionWindow"),
- "install" : ("progress_gui", "InstallProgressWindow"),
- "complete" : ("congrats_gui", "CongratulationWindow"),
- }
2)run: 啓動圖形安裝界面的入口函數。該函數調用了setup_window接口,該接口調用gtk"繪製"圖形安裝界面的主窗體,然後控制權交給了gtk。
- def run (self):
- self.setup_theme()
- self.setup_window(False)
- gtk.main()
- def prevClicked (self, *args):
- try:
- self.currentWindow.getPrev ()
- except StayOnScreen:
- return
- self.anaconda.dispatch.gotoPrev()
- self.setScreen ()
- def nextClicked (self, *args):
- try:
- rc = self.currentWindow.getNext ()
- except StayOnScreen:
- return
- self.anaconda.dispatch.gotoNext()
- self.setScreen ()
- 4)setScreen: 用於設置圖形界面。代碼如下:
- def setScreen (self):
- # 取得當前安裝步驟信息
- (step, anaconda) = self.anaconda.dispatch.currentStep()
- if step is None:
- gtk.main_quit()
- return
- if not stepToClass[step]: # 不在其中,則直接跳到下一步
- if self.anaconda.dispatch.dir == DISPATCH_FORWARD:
- return self.nextClicked()
- else:
- return self.prevClicked()
- (file, className) = stepToClass[step] # 獲得圖形界面類所在模塊及其類名
- newScreenClass = None
- while True:
- try:
- found = imp.find_module(file, iw.__path__)
- moduleName = 'pyanaconda.iw.%s' % file
- loaded = imp.load_module(moduleName, *found) # 載入該圖形界面模塊
- newScreenClass = loaded.__dict__[className]
- break
- except ImportError, e:
- stdout_log.error("loading interface component %s" % className)
- stdout_log.error(traceback.format_exc())
- win = MessageWindow(_("Error!"),
- _("An error occurred when attempting "
- "to load an installer interface "
- "component.\n\nclassName = %s")
- % (className,),
- type="custom", custom_icon="warning",
- custom_buttons=[_("_Exit"),
- _("_Retry")])
- if not win.getrc():
- msg = _("The system will now reboot.")
- buttons = [_("_Reboot")]
- MessageWindow(_("Exiting"),
- msg,
- type="custom",
- custom_icon="warning",
- custom_buttons=buttons)
- sys.exit(0)
- ics = InstallControlState (self)
- # 設置是否是可以返回上一步
- ics.setPrevEnabled(self.anaconda.dispatch.canGoBack())
- self.destroyCurrentWindow() # 銷燬原來的圖形安裝界面
- self.currentWindow = newScreenClass(ics) # 創建新的圖形安裝界面並設爲當前界面
- new_screen = self.currentWindow.getScreen(anaconda) # 生成安裝步驟的界面
- # If the getScreen method returned None, that means the screen did not
- # want to be displayed for some reason and we should skip to the next
- # step. However, we do not want to remove the current step from the
- # list as later events may cause the screen to be displayed.
- if not new_screen:
- if self.anaconda.dispatch.dir == DISPATCH_FORWARD:
- self.anaconda.dispatch.gotoNext()
- else:
- self.anaconda.dispatch.gotoPrev()
- return self.setScreen()
- self.update (ics)
- self.installFrame.add(new_screen)
- self.installFrame.show_all()
- self.currentWindow.focus()
- self.handle = gobject.idle_add(self.handleRenderCallback)
- if self.reloadRcQueued:
- self.window.reset_rc_styles()
- self.reloadRcQueued = 0
至此,InstallControlWindow的主要邏輯已經分析完了,接下來涉及每個具體安裝界面及其安裝操作讀者可以到iw目錄下逐個深入分析。
(3)anaconda主程序: 圖形環境運行是建立在X Server基礎上的,對於圖形模式,anaconda需要先運行X服務器,然後啓動圖形模式安裝過程。而對於字符模式,anaconda的主執行體就作了一件事,啓動字符模式安裝過程。
- if __name__ == "__main__":
- setupPythonPath()
- # ......
- # 解析啓動本腳本時傳入的參數
- (opts, args) = parseOptions()
- from pyanaconda.flags import flags
- if opts.images:
- flags.imageInstall = True
- # 設置log
- import logging
- from pyanaconda import anaconda_log
- anaconda_log.init()
- log = logging.getLogger("anaconda")
- stdoutLog = logging.getLogger("anaconda.stdout")
- # ......
- from pyanaconda import Anaconda
- anaconda = Anaconda() # 創建主執行體實例
- warnings.showwarning = AnacondaShowWarning
- iutil.setup_translations(gettext)
- # ......
- # 檢測內存,現在只用在文本模式下
- check_memory(anaconda, opts, 't')
- if opts.unsupportedMode:
- stdoutLog.error("Running anaconda in %s mode is no longer supported." % opts.unsupportedMode)
- sys.exit(0)
- # ......
- # kickstart文件解析
- if opts.ksfile:
- kickstart.preScriptPass(anaconda, opts.ksfile)
- anaconda.ksdata = kickstart.parseKickstart(anaconda, opts.ksfile)
- opts.rescue = opts.rescue or anaconda.ksdata.rescue.rescue
- # ......
- # 如果沒有X server,使用文本模式
- if not flags.livecdInstall and not iutil.isS390() and not os.access("/usr/bin/Xorg", os.X_OK):
- stdoutLog.warning(_("Graphical installation is not available. "
- "Starting text mode."))
- time.sleep(2)
- anaconda.displayMode = 't'
- # ......
- # 啓動本地的X server
- if anaconda.displayMode == 'g' and not flags.preexisting_x11 and not flags.usevnc:
- try:
- # start X with its USR1 handler set to ignore. this will make it send
- # us SIGUSR1 if it succeeds. if it fails, catch SIGCHLD and bomb out.
- def sigchld_handler(num, frame):
- raise OSError(0, "SIGCHLD caught when trying to start the X server.")
- def sigusr1_handler(num, frame):
- log.debug("X server has signalled a successful start.")
- def preexec_fn():
- signal.signal(signal.SIGUSR1, signal.SIG_IGN)
- old_sigusr1 = signal.signal(signal.SIGUSR1, sigusr1_handler)
- old_sigchld = signal.signal(signal.SIGCHLD, sigchld_handler)
- xout = open("/dev/tty5", "w")
- # 啓動X server
- proc = subprocess.Popen(["Xorg", "-br", "-logfile", "/tmp/X.log",
- ":1", "vt6", "-s", "1440", "-ac",
- "-nolisten", "tcp", "-dpi", "96",
- "-noreset"],
- close_fds=True, stdout=xout, stderr=xout,
- preexec_fn=preexec_fn)
- signal.pause()
- os.environ["DISPLAY"] = ":1"
- doStartupX11Actions()
- except (OSError, RuntimeError) as e:
- stdoutLog.warning(" X startup failed, falling back to text mode")
- anaconda.displayMode = 't'
- graphical_failed = 1
- time.sleep(2)
- finally:
- signal.signal(signal.SIGUSR1, old_sigusr1)
- signal.signal(signal.SIGCHLD, old_sigchld)
- set_x_resolution(opts.runres)
- # ......
- # 初始化UI界面
- anaconda.initInterface()
- anaconda.instClass.configure(anaconda)
- # ......
- # 啓動安裝過程
- try:
- anaconda.intf.run(anaconda)
- except SystemExit, code:
- anaconda.intf.shutdown()
- if anaconda.ksdata and anaconda.ksdata.reboot.eject:
- for drive in anaconda.storage.devicetree.devices:
- if drive.type != "cdrom":
- continue
- log.info("attempting to eject %s" % drive.path)
- drive.eject()
- del anaconda.intf
(4)pyanaconda/__init__.py: 裏面有Anaconda類,負責具體的啓動安裝過程。前面說過,安裝的流程由Dispatcher控制,對於圖形模式,圖形模式的前端顯示及與用戶的交互由InstallControlWindow調度,而字符模式的前端顯示層由InstallInterface調度。因此,啓動安裝過程,實際就是創建主要控制類的實例,調用實例的接口,啓動安裝過程,然後再由這幾個主要的控制類的實例創建具體安裝界面,創建安裝行爲類的實例,調用具體的函數完成具體的安裝過程。
- class Anaconda(object):
- def __init__(self):
- import desktop, dispatch, firewall, security
- import system_config_keyboard.keyboard as keyboard
- from flags import flags
- # ......
- # 創建dispatch實例
- self.dispatch = dispatch.Dispatcher(self)
- # ......
- # ......
- intf = property(_getInterface, _setInterface, _delInterface)
- # ......
- def initInterface(self):
- if self._intf:
- raise RuntimeError, "Second attempt to initialize the InstallInterface"
- # 設置圖形模式需要的鏈接
- if self.displayMode == 'g':
- stdoutLog.info (_("Starting graphical installation."))
- try:
- from gui import InstallInterface
- except Exception, e:
- from flags import flags
- stdoutLog.error("Exception starting GUI installer: %s" %(e,))
- # if we're not going to really go into GUI mode, we need to get
- # back to vc1 where the text install is going to pop up.
- if not flags.livecdInstall:
- isys.vtActivate (1)
- stdoutLog.warning("GUI installer startup failed, falling back to text mode.")
- self.displayMode = 't'
- if 'DISPLAY' in os.environ.keys():
- del os.environ['DISPLAY']
- time.sleep(2)
- if self.displayMode == 't':
- from text import InstallInterface
- if not os.environ.has_key("LANG"):
- os.environ["LANG"] = "en_US.UTF-8"
- if self.displayMode == 'c':
- from cmdline import InstallInterface
- self._intf = InstallInterface() # 創建InstallInterface實例
- return self._intf
整個Anaconda的運行流程如下圖: 圖2 Anaconda運行流程