2 # -*- coding: utf-8 -*-
4 # (c) Copyright 2003-2014 Hewlett-Packard Development Company, L.P.
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 # Author: Don Welch, Amarnath Chitumalla
31 from base import utils, tui
32 from core_install import *
35 tui.title("RE-STARTING HP_SYSTRAY")
36 path = utils.which('hp-systray')
38 path = os.path.join(path, 'hp-systray')
40 path = os.path.join(prop.home_dir, 'systray.py')
41 if not os.path.exists(path):
42 log.warn("Unable to start hp-systray")
44 log.debug("Running hp-systray: %s --force-startup" % path)
45 os.spawnlp(os.P_NOWAIT, path, 'hp-systray', '--force-startup')
46 log.debug("Waiting for hp-systray to start...")
50 def progress_callback(cmd="", desc="Working..."):
52 log.info("%s (%s)" % (cmd, desc))
58 return getpass.getpass(log.bold("Please enter the root/superuser password: "))
60 def password_user_entry():
61 return getpass.getpass(log.bold("Please enter the user (%s)'s password: " % os.getenv('USER')))
64 def option_question_callback(opt, desc, default='y'):
65 ok, ans = tui.enter_yes_no("Do you wish to enable '%s'" % desc, default)
66 if not ok: sys.exit(0)
71 def start(language, auto=True, test_depends=False,
72 test_unknown=False, assume_network=False,
73 max_retries=3, enable=None, disable=None):
75 core = CoreInstall(MODE_INSTALLER, INTERACTIVE_MODE)
76 current_version = prop.installed_version_int
77 log.debug("Currently installed version: 0x%06x" % current_version)
79 core.disable = disable
81 if core.running_as_root():
82 log.error("You are running the installer as root. It is highly recommended that you run the installer as")
83 log.error("a regular (non-root) user. Do you still wish to continue?")
85 ok, ans = tui.enter_yes_no(log.bold("Continue with installation"), 'n')
90 log.note("Running in automatic mode. The most common options will be selected.")
93 log.note("Defaults for each question are maked with a '*'. Press <enter> to accept the default.")
95 if not core.distro_name in ("ubuntu","debian","suse","fedora"):
96 log.error("Auto installation is not supported for '%s' distro so all dependencies may not be installed. \nPlease install manually as mentioned in 'http://hplipopensource.com/hplip-web/install/manual/index.html' web-site"% core.distro_name)
97 ok, choice = tui.enter_choice("\nPress 'y' If you still want to continue auto installation. Press 'n' to quit auto instalation(y=yes, n=no*): ",['y','n'],'n')
98 if not ok or choice =='n':
99 log.info("Installation exit")
103 tui.title("INSTALLATION MODE")
104 log.info("Automatic mode will install the full HPLIP solution with the most common options.")
105 log.info("Custom mode allows you to choose installation options to fit specific requirements.")
107 #if os.getenv('DISPLAY') and utils.find_browser() is not None:
109 ok, choice = tui.enter_choice("\nPlease choose the installation mode (a=automatic*, c=custom, w=web installer, q=quit) : ",
110 ['a', 'c', 'w'], 'a')
112 ok, choice = tui.enter_choice("\nPlease choose the installation mode (a=automatic*, c=custom, q=quit) : ",
115 if not ok: sys.exit(0)
122 log.debug("Starting web browser installer...")
123 web_install.start(language)
126 log.info("\nInitializing. Please wait...")
127 prev_hplip_version= sys_conf.get("hplip","version","0.0.0")
131 core.distro_name = 'unknown'
133 core.distro_version = 0
139 core.selected_component = 'hplip'
145 tui.title("INTRODUCTION")
147 if core.selected_component == 'hplip':
148 log.info("This installer will install HPLIP version %s on your computer." % core.version_public)
150 log.info("Please close any running package management systems now (YaST, Adept, Synaptic, Up2date, etc).")
153 # For testing, mark all dependencies missing
155 for d in core.have_dependencies:
156 core.have_dependencies[d] = False
158 num_req_missing = core.count_num_required_missing_dependencies()
159 num_opt_missing = core.count_num_optional_missing_dependencies()
163 # CONFIRM AND SELECT DISTRO NAME AND VERSION
166 tui.title("DISTRO/OS CONFIRMATION")
169 if core.distro_known():
170 log.info("Distro appears to be %s %s.\n" % (core.get_distro_data('display_name', '(unknown)'), core.distro_version))
172 log.debug("Distro = %s Distro Name = %s Display Name= %s Version = %s Supported = %s" % \
173 (core.distro, core.distro_name, core.distros[core.distro_name]['display_name'], \
174 core.distro_version, core.distro_version_supported))
176 distro_ok, ok = False, True
177 if core.distro_known():
178 ok, distro_ok = tui.enter_yes_no('Is "%s %s" your correct distro/OS and version'
179 % (core.get_distro_data('display_name', '(unknown)'), core.distro_version))
184 core.distro_changed()
187 tui.title("DISTRO/OS SELECTION")
188 core.distro, core.distro_version = DISTRO_UNKNOWN, DISTRO_VER_UNKNOWN
190 log.info(log.bold("\nChoose the name of the distro/OS that most closely matches your system:\n"))
193 for d in core.distros_index:
194 dd = core.distros[core.distros_index[d]]
196 max_name = max(max_name, len(dd['display_name']))
198 formatter = utils.TextFormatter(
201 {'width': max_name, 'margin': 2},
205 log.info(formatter.compose(("Num.", "Distro/OS Name")))
206 log.info(formatter.compose(('-'*4, '-'*(max_name))))
210 for d in core.distros_index:
211 dd = core.distros[core.distros_index[d]]
215 log.info(formatter.compose((str(x), dd['display_name'])))
218 ok, y = tui.enter_range("\nEnter number 0...%d (q=quit) ?" % (x-1), 0, x-1)
219 if not ok: sys.exit(0)
221 core.distro = d_temp[y]
222 core.distro_name = core.distros_index[core.distro]
223 distro_display_name = core.distros[core.distro_name]['display_name']
224 log.debug("Distro = %s Distro Name = %s Display Name= %s" %
225 (core.distro, core.distro_name, distro_display_name))
227 if core.distro != DISTRO_UNKNOWN:
228 versions = core.distros[core.distro_name]['versions'].keys()
229 versions.sort(lambda x, y: core.sort_vers(x, y))
231 log.info(log.bold('\nChoose the version of "%s" that most closely matches your system:\n' % distro_display_name))
233 formatter = utils.TextFormatter(
236 {'width': 40, 'margin': 2},
240 log.info(formatter.compose(("Num.", "Distro/OS Version")))
241 log.info(formatter.compose(('-'*4, '-'*40)))
243 log.info(formatter.compose(("0", "Unknown or not listed")))
247 ver_info = core.distros[core.distro_name]['versions'][ver]
249 if ver_info['code_name'] and ver_info['release_date']:
250 text = ver + ' ("' + ver_info['code_name'] + '", Released ' + ver_info['release_date'] + ')'
252 elif ver_info['code_name']:
253 text = ver + ' ("' + ver_info['code_name'] + '")'
255 elif ver_info['release_date']:
256 text = ver + ' (Released ' + ver_info['release_date'] + ')'
261 if not ver_info['supported']:
262 text += " [Unsupported]"
264 log.info(formatter.compose((str(x), text)))
267 ok, core.distro_version_int = tui.enter_range("\nEnter number 0...%d (q=quit) ?" %
269 if not ok: sys.exit(0)
271 if core.distro_version_int == 0:
272 core.distro_version = DISTRO_VER_UNKNOWN
273 core.distro_version_supported = False
275 core.distro_version = versions[core.distro_version_int - 1]
276 core.distro_version_supported = core.get_ver_data('supported', False)
278 log.debug("Distro = %s Distro Name = %s Display Name= %s Version = %s Supported = %s" % \
279 (core.distro, core.distro_name, core.distros[core.distro_name]['display_name'], \
280 core.distro_version, core.distro_version_supported))
282 core.distro_changed()
284 log.info("\nDistro set to: %s %s" %
285 (core.get_distro_data('display_name', '(unknown)'), core.distro_version))
288 if core.distro == DISTRO_UNKNOWN or not core.distro_version_supported:
289 log.error("The distribution/OS that you are running is not supported. This installer\ncannot install an unsupported distribution. Please check your distribution/OS\nand re-run this installer or perform a manual installation.")
291 log.error("The following REQUIRED dependencies are missing and need to be installed:")
293 for d, desc, opt in core.missing_required_dependencies():
294 log.error("Missing REQUIRED dependency: %s (%s)" % (d, desc))
296 for d, desc, req, opt in core.missing_optional_dependencies():
298 log.warning("Missing OPTIONAL dependency: %s (%s) [Required for option '%s']" % (d, desc, opt))
300 log.warning("Missing OPTIONAL dependency: %s (%s) [Optional for option '%s']" % (d, desc, opt))
306 # SELECT OPTIONS TO INSTALL
310 tui.title("SELECT HPLIP OPTIONS")
311 log.info("You can select which HPLIP options to enable. Some options require extra dependencies.")
313 num_opt_missing = core.select_options(option_question_callback)
317 core.selected_options['parallel'] = False
319 log.debug("Req missing=%d Opt missing=%d HPLIP=%s Component=%s" % \
320 (num_req_missing, num_opt_missing, core.hplip_present, core.selected_component))
324 # COLLECT SUPERUSER PASSWORD
326 if not core.running_as_root():
327 su_sudo = core.get_distro_data('su_sudo')
328 if su_sudo == "sudo":
329 tui.title("ENTER USER PASSWORD")
330 ok = core.check_password(password_user_entry, progress_callback)
332 tui.title("ENTER ROOT/SUPERUSER PASSWORD")
333 ok = core.check_password(password_entry, progress_callback)
336 log.error("3 incorrect attempts. (or) Insufficient permissions(i.e. try with sudo user).\nExiting.")
343 if core.distro_supported():
344 distro_notes = core.get_distro_data('notes', '').strip()
345 ver_notes = core.get_ver_data('notes', '').strip()
347 if distro_notes or ver_notes:
348 tui.title("INSTALLATION NOTES")
351 log.info(distro_notes)
358 if not tui.continue_prompt("Please read the installation notes."):
362 # PRE-INSTALL COMMANDS
364 tui.title("RUNNING PRE-INSTALL COMMANDS")
365 if core.run_pre_install(progress_callback): # some cmds were run...
366 num_req_missing = core.count_num_required_missing_dependencies()
367 num_opt_missing = core.count_num_optional_missing_dependencies()
371 # REQUIRED DEPENDENCIES INSTALL
374 depends_to_install = []
376 tui.title("INSTALL MISSING REQUIRED DEPENDENCIES")
378 log.warn("There are %d missing REQUIRED dependencies." % num_req_missing)
379 log.notice("Installation of dependencies requires an active internet connection.")
381 for depend, desc, option in core.missing_required_dependencies():
382 log.warning("Missing REQUIRED dependency: %s (%s)" % (depend, desc))
385 packages, commands = core.get_dependency_data(depend)
386 log.debug("Packages: %s" % ','.join(packages))
387 log.debug("Commands: %s" % ','.join(commands))
389 if core.distro_version_supported and (packages or commands):
393 ok, answer = tui.enter_yes_no("\nWould you like to have this installer install the missing dependency")
394 if not ok: sys.exit(0)
398 log.debug("Adding '%s' to list of dependencies to install." % depend)
399 depends_to_install.append(depend)
402 log.warn("This installer cannot install '%s' for your distro/OS and/or version." % depend)
405 log.error("Installation cannot continue without this dependency. Please manually install this dependency and re-run this installer.")
409 # OPTIONAL dependencies
413 tui.title("INSTALL MISSING OPTIONAL DEPENDENCIES")
414 log.warn("There are %d missing OPTIONAL dependencies." % num_opt_missing)
416 log.notice("Installation of dependencies requires an active internet connection.")
418 for depend, desc, required_for_opt, opt in core.missing_optional_dependencies():
421 log.warning("Missing REQUIRED dependency for option '%s': %s (%s)" % (opt, depend, desc))
424 log.warning("Missing OPTIONAL dependency for option '%s': %s (%s)" % (opt, depend, desc))
427 packages, commands = core.get_dependency_data(depend)
428 log.debug("Packages: %s" % ','.join(packages))
429 log.debug("Commands: %s" % ','.join(commands))
432 if core.distro_version_supported and (packages or commands):
436 ok, answer = tui.enter_yes_no("\nWould you like to have this installer install the missing dependency")
437 if not ok: sys.exit(0)
440 log.debug("Adding '%s' to list of dependencies to install." % depend)
441 depends_to_install.append(depend)
444 log.warning("Missing dependencies may effect the proper functioning of HPLIP. Please manually install this dependency after you exit this installer.")
445 log.warning("Note: Options that have REQUIRED dependencies that are missing will be turned off.")
448 log.warn("Option '%s' has been turned off." % opt)
449 core.selected_options[opt] = False
451 log.warn("This installer cannot install '%s' for your distro/OS and/or version." % depend)
454 log.warn("Option '%s' has been turned off." % opt)
455 core.selected_options[opt] = False
459 log.debug("Dependencies to install: %s hplip_present:%s" % (depends_to_install, core.hplip_present))
461 if core.distro_version_supported and \
462 (depends_to_install or core.hplip_present) and \
463 core.selected_component == 'hplip':
466 # CHECK FOR RUNNING PACKAGE MANAGER
469 pid, cmdline = core.check_pkg_mgr()
471 ok, user_input = tui.enter_choice("A package manager '%s' appears to be running. Please quit the package manager and press enter to continue (i=ignore, r=retry*, f=force, q=quit) :"
472 % cmdline, ['i', 'r', 'q', 'f'], 'r')
474 if not ok: sys.exit(0)
476 if user_input == 'i':
477 log.warn("Ignoring running package manager. Some package operations may fail.")
480 if user_input == 'f':
481 ok, ans = tui.enter_yes_no("\nForce quit of package manager '%s'" % cmdline, 'y')
483 if not ok: sys.exit(0)
486 cmd = core.su_sudo() % ("kill %d" % pid)
487 status, output = core.run(cmd)
490 log.error("Failed to kill process. You may need to manually quit the program.")
492 pid, cmdline = core.check_pkg_mgr()
496 # CHECK FOR ACTIVE NETWORK CONNECTION
498 if not assume_network:
499 tui.title("CHECKING FOR NETWORK CONNECTION")
501 if not core.check_network_connection():
502 log.error("The network appears to be unreachable. Installation may not resolve all dependencies without access to distribution repositories.")
503 ok, choice = tui.enter_choice("Do you want to continue installation without network?. Press 'y' for YES. Press 'n' for NO (y=yes*, n=no) : ",['y', 'n'], 'y')
504 if not ok or choice == 'n':
505 log.info("Please connect network and try again")
508 log.debug("Continuing installation without network")
510 log.info("Network connection present.")
516 tui.title("RUNNING PRE-PACKAGE COMMANDS")
517 core.run_pre_depend(progress_callback)
521 # INSTALL PACKAGES AND RUN COMMANDS
524 tui.title("DEPENDENCY AND CONFLICT RESOLUTION")
528 package_mgr_cmd = core.get_distro_data('package_mgr_cmd')
531 individual_pkgs = True
532 if package_mgr_cmd.startswith('xterm'):
533 individual_pkgs = False
536 log.debug("Preparing to install packages and run commands...")
538 for d in depends_to_install:
539 log.debug("*** Processing dependency: %s" % d)
540 pkgs, commands = core.get_dependency_data(d)
543 log.debug("Package(s) '%s' will be installed to satisfy dependency '%s'." %
546 packages.extend(pkgs)
549 log.debug("Command(s) '%s' will be run to satisfy dependency '%s'." %
550 (','.join(commands), d))
552 commands_to_run.extend(commands)
555 log.error("Invalid package manager")
557 log.debug("Packages: %s" % packages)
558 log.debug("Commands: %s" % commands_to_run)
559 log.debug("Install individual packages: %s" % individual_pkgs)
561 if package_mgr_cmd and packages:
563 for packages_to_install in packages:
566 cmd = utils.cat(package_mgr_cmd)
567 log.debug("Package manager command: %s" % cmd)
569 log.info("Running '%s'\nPlease wait, this may take several minutes..." % cmd)
570 status, output = core.run(cmd)
574 if retries < (max_retries+1):
575 log.error("Command failed. Re-try #%d..." % retries)
578 log.error("Package install command failed with error code %d" % status)
579 ok, ans = tui.enter_yes_no("Would you like to retry installing the missing package(s)")
587 log.warn("Some HPLIP functionality might not function due to missing package(s).")
593 packages_to_install = ' '.join(packages)
595 cmd = utils.cat(package_mgr_cmd)
596 log.debug("Package manager command: %s" % cmd)
598 log.info("Running '%s'\nPlease wait, this may take several minutes..." % cmd)
599 status, output = core.run(cmd)
602 log.error("Package install command failed with error code %d" % status)
603 ok, ans = tui.enter_yes_no("Would you like to retry installing the missing package(s)")
611 log.warn("Some HPLIP functionality might not function due to missing package(s).")
617 for cmd in commands_to_run:
619 log.info("Running '%s'\nPlease wait, this may take several minutes..." % cmd)
620 status, output = core.run(cmd)
623 log.error("Install command failed with error code %d" % status)
632 if core.hplip_present and core.selected_component == 'hplip' and core.distro_version_supported:
633 path = utils.which('hp-uninstall')
634 ok, choice = tui.enter_choice("HPLIP-%s exists, this may conflict with the new one being installed.\nDo you want to ('i'= Remove and Install, 'o'= Overwrite*, 'q'= Quit)? :"%(prev_hplip_version),['i','o','q'],'o')
635 if not ok or choice=='q':
636 log.error("User Exit")
639 # log.info("Uninstalling existing HPLIP-%s"%prev_hplip_version)
640 sts =core.uninstall(NON_INTERACTIVE_MODE)
643 log.warn("Failed to uninstall existing HPLIP-%s. This installation will overwrite on existing HPLIP" %prev_hplip_version)
645 log.debug("HPLIP-%s is uninstalled successfully." %prev_hplip_version)
650 tui.title("RUNNING POST-PACKAGE COMMANDS")
651 core.run_post_depend(progress_callback)
656 # DEPENDENCIES RE-CHECK
658 tui.title("RE-CHECKING DEPENDENCIES")
659 core.check_dependencies()
662 for depend, desc, opt in core.missing_required_dependencies():
664 log.error("A required dependency '%s (%s)' is still missing." % (depend, desc))
667 if num_req_missing > 1:
668 log.error("Installation cannot continue without these dependencies.")
670 log.error("Installation cannot continue without this dependency.")
672 log.error("Please manually install this dependency and re-run this installer.")
675 for depend, desc, required_for_opt, opt in core.missing_optional_dependencies():
677 log.warn("An optional dependency '%s (%s)' is still missing." % (depend, desc))
678 log.warn("Option '%s' has been turned off." % opt)
679 core.selected_options[opt] = False
681 log.warn("An optional dependency '%s (%s)' is still missing." % (depend, desc))
682 log.warn("Some features may not function as expected.")
685 if not num_opt_missing and not num_req_missing:
692 log.debug("Install location = %s" % core.install_location)
700 tui.title("READY TO BUILD AND INSTALL")
701 if not tui.continue_prompt("Ready to perform build and install."):
704 tui.title("PRE-BUILD COMMANDS")
705 core.run_pre_build(progress_callback)
708 tui.title("BUILD AND INSTALL")
711 for cmd in core.build_cmds():
712 log.info("Running '%s'\nPlease wait, this may take several minutes..." % cmd)
713 status, output = core.run(cmd)
716 if 'configure' in cmd:
717 log.error("Configure failed with error: %s" % CONFIGURE_ERRORS.get(status, CONFIGURE_ERRORS[1]))
720 log.error("'%s' command failed with status code %d" % (cmd, status))
724 log.info("Command completed successfully.")
728 log.info("\nBuild complete.")
734 tui.title("POST-BUILD COMMANDS")
735 core.run_post_build(progress_callback)
738 # OPEN MDNS MULTICAST PORT
740 user_conf = UserConfig()
742 if core.selected_options['network']:
743 open_mdns_port = core.get_distro_ver_data('open_mdns_port')
745 tui.title("OPEN MDNS/BONJOUR FIREWALL PORT (MULTICAST PORT 5353)")
747 paragraph = "In order to setup your printer on the network using mDNS/Bonjour, it is required that your internet firewall allows connections on port 5353. If this port is blocked by the firewall, connection to network printers using mDNS/Bonjour will not be possible."
749 for p in tui.format_paragraph(paragraph):
753 ok, ans = tui.enter_yes_no("Do you wish to open this port on your internet firewall")
754 if not ok: sys.exit(0)
757 core.run_open_mdns_port()
759 log.warn("Skipping firewall setup. If this port is blocked on your firewall when setting up network printers, use SLP discovery and device URIs with ?ip=x.x.x.x. When using hp-setup, choose 'SLP' discovery under 'Advanced'.")
763 # Try to close running hp-systray (3.9.2 or later)
766 if current_version >= 0x030902: # 3.9.2
768 from dbus import SessionBus, lowlevel
773 args = ['', '', EVENT_SYSTEMTRAY_EXIT, prop.username, 0, '', '']
774 msg = lowlevel.SignalMessage('/', 'com.hplip.StatusService', 'Event')
775 msg.append(signature='ssisiss', *args)
776 tui.title("CLOSE HP_SYSTRAY")
777 log.info("Sending close message to hp-systray (if it is currently running)...")
778 SessionBus().send_message(msg)
783 tui.title("HPLIP UPDATE NOTIFICATION")
784 ok, choice = tui.enter_choice("Do you want to check for HPLIP updates?. (y=yes*, n=no) : ",['y', 'n'], 'y')
785 if not ok or choice != 'y':
786 user_conf.set('upgrade', 'notify_upgrade', 'false')
788 user_conf.set('upgrade', 'notify_upgrade', 'true')
790 user_conf.set('upgrade','last_upgraded_time',str(int(time.time())))
791 user_conf.set('upgrade','pending_upgrade_time','0')
794 if core.selected_component == 'hplip':
795 tui.title("RESTART OR RE-PLUG IS REQUIRED")
797 paragraph = """If you are installing a USB connected printer, and the printer was plugged in when you started this installer, you will need to either restart your PC or unplug and re-plug in your printer (USB cable only). If you choose to restart, run this command after restarting: %s (Note: If you are using a parallel connection, you will have to restart your PC. If you are using network/wireless, you can ignore and continue).""" % cmd
799 for p in tui.format_paragraph(paragraph):
803 ok, choice = tui.enter_choice("Restart or re-plug in your printer (r=restart, p=re-plug in*, i=ignore/continue, q=quit) : ",
804 ['r', 'p', 'i'], 'p')
812 log.note("IMPORTANT! Make sure to save all work in all open applications before restarting!")
814 ok, ans = tui.enter_yes_no(log.bold("Restart now"), 'n')
821 log.error("Restart failed. Please restart using the system menu.")
826 elif choice == 'p': # 'p'
827 if not tui.continue_prompt("Please unplug and re-plugin your printer now. "):
835 if core.selected_component == 'hplip':
836 tui.title("PRINTER SETUP")
839 install_printer = True
841 ok, install_printer = tui.enter_yes_no("Would you like to setup a printer now")
847 log.info("Please make sure your printer is connected and powered on at this time.")
848 if not core.run_hp_setup():
849 log.error("hp-setup failed. Please run hp-setup manually.")
852 except KeyboardInterrupt:
854 log.error("Aborted.")