def start_program_reset_handlers_test(self): """Test the reset_handlers parameter of startProgram.""" with tempfile.NamedTemporaryFile() as testscript: testscript.write("""#!/bin/sh # Just hang out and do nothing, forever while true ; do sleep 1 ; done """) testscript.flush() # Start a program with reset_handlers proc = iutil.startProgram(["/bin/sh", testscript.name]) with timer(5): # Kill with SIGPIPE and check that the python's SIG_IGN was not inheritted # The process should die on the signal. proc.send_signal(signal.SIGPIPE) proc.communicate() self.assertEqual(proc.returncode, -(signal.SIGPIPE)) # Start another copy without reset_handlers proc = iutil.startProgram(["/bin/sh", testscript.name], reset_handlers=False) with timer(5): # Kill with SIGPIPE, then SIGTERM, and make sure SIGTERM was the one # that worked. proc.send_signal(signal.SIGPIPE) proc.terminate() proc.communicate() self.assertEqual(proc.returncode, -(signal.SIGTERM))
def start_program_reset_handlers_test(self): """Test the reset_handlers parameter of startProgram.""" with tempfile.NamedTemporaryFile(mode="w+t") as testscript: testscript.write("""#!/bin/sh # Just hang out and do nothing, forever while true ; do sleep 1 ; done """) testscript.flush() # Start a program with reset_handlers proc = iutil.startProgram(["/bin/sh", testscript.name]) with timer(5): # Kill with SIGPIPE and check that the python's SIG_IGN was not inheritted # The process should die on the signal. proc.send_signal(signal.SIGPIPE) proc.communicate() self.assertEqual(proc.returncode, -(signal.SIGPIPE)) # Start another copy without reset_handlers proc = iutil.startProgram(["/bin/sh", testscript.name], reset_handlers=False) with timer(5): # Kill with SIGPIPE, then SIGTERM, and make sure SIGTERM was the one # that worked. proc.send_signal(signal.SIGPIPE) proc.terminate() proc.communicate() self.assertEqual(proc.returncode, -(signal.SIGTERM))
def startVncConfig(self): """Attempt to start vncconfig""" self.log.info(_("Attempting to start vncconfig")) vncconfigcommand = [self.root+"/usr/bin/vncconfig", "-nowin", "-display", ":%s" % constants.X_DISPLAY_NUMBER] # Use startProgram to run vncconfig in the background iutil.startProgram(vncconfigcommand, stdout=self.openlogfile(), stderr=subprocess.STDOUT)
def setUserPassword(self, username, password, isCrypted, lock, algo=None, root="/"): # Only set the password if it is a string, including the empty string. # Otherwise leave it alone (defaults to locked for new users) and reset sp_lstchg if password or password == "": if password == "": log.info("user account %s setup with no password", username) elif not isCrypted: password = cryptPassword(password, algo) if lock: password = "******" + password log.info("user account %s locked", username) proc = iutil.startProgram(["chpasswd", "-R", root, "-e"], stdin=subprocess.PIPE) proc.communicate( ("%s:%s\n" % (username, password)).encode("utf-8")) if proc.returncode != 0: raise OSError( "Unable to set password for new user: status=%s" % proc.returncode) # Reset sp_lstchg to an empty string. On systems with no rtc, this # field can be set to 0, which has a special meaning that the password # must be reset on the next login. iutil.execWithRedirect("chage", ["-R", root, "-d", "", username])
def connectToView(self): """Attempt to connect to self.vncconnecthost""" maxTries = 10 self.log.info(_("Attempting to connect to vnc client on host %s..."), self.vncconnecthost) if self.vncconnectport != "": hostarg = self.vncconnecthost + ":" + self.vncconnectport else: hostarg = self.vncconnecthost vncconfigcommand = [self.root+"/usr/bin/vncconfig", "-display", ":%s" % constants.X_DISPLAY_NUMBER, "-connect", hostarg] for _i in range(maxTries): vncconfp = iutil.startProgram(vncconfigcommand, stdout=subprocess.PIPE, stderr=subprocess.PIPE) # vncconfig process err = vncconfp.communicate()[1].decode("utf-8") if err == '': self.log.info(_("Connected!")) return True elif err.startswith("connecting") and err.endswith("failed\n"): self.log.info(_("Will try to connect again in 15 seconds...")) time.sleep(15) continue else: log.critical(err) iutil.ipmi_report(constants.IPMI_ABORTED) sys.exit(1) self.log.error(P_("Giving up attempting to connect after %d try!\n", "Giving up attempting to connect after %d tries!\n", maxTries), maxTries) return False
def execute(self, storage, ksdata, instClass, users, payload): """ Execute the addon :param storage: Blivet storage object :param ksdata: Kickstart data object :param instClass: Anaconda installclass object :param users: Anaconda users object :param payload: object managing packages and environment groups for the installation """ if not self.enabled: return log.info("Executing docker addon") # This gets called after installation, before initramfs regeneration and kickstart %post scripts. execWithRedirect("mount", ["-o", "bind", getSysroot()+"/var/lib/docker", "/var/lib/docker"]) execWithRedirect("mount", ["-o", "bind", getSysroot()+"/etc/docker", "/etc/docker"]) # Start up the docker daemon log.debug("Starting docker daemon") docker_cmd = ["docker", "daemon"] if ksdata.selinux.selinux: docker_cmd += ["--selinux-enabled"] # Add storage specific arguments to the command docker_cmd += self.storage.docker_cmd(storage, ksdata, instClass, users) docker_cmd += ["--ip-forward=false", "--iptables=false"] docker_cmd += self.extra_args docker_proc = startProgram(docker_cmd, stdout=open("/tmp/docker-daemon.log", "w"), reset_lang=True) log.debug("Running docker commands") script = AnacondaKSScript(self.content, inChroot=False, logfile="/tmp/docker-addon.log") script.run("/") # Kill the docker process log.debug("Shutting down docker daemon") docker_proc.kill() log.debug("Writing docker configs") self.storage.write_configs(storage, ksdata, instClass, users) # Rewrite the OPTIONS entry with the extra args and/or storage specific changes try: docker_cfg = SimpleConfigFile(getSysroot()+"/etc/sysconfig/docker") docker_cfg.read() options = self.storage.options(docker_cfg.get("OPTIONS")) if self.save_args: log.info("Adding extra args to docker OPTIONS") options += " " + " ".join(self.extra_args) docker_cfg.set(("OPTIONS", options)) docker_cfg.write() except IOError as e: log.error("Error updating OPTIONS in /etc/sysconfig/docker: %s", e) # Copy the log files to the system dstdir = "/var/log/anaconda/" os.makedirs(dstdir, exist_ok=True) for l in ["docker-daemon.log", "docker-addon.log"]: shutil.copy2("/tmp/"+l, dstdir+l)
def connectToView(self): """Attempt to connect to self.vncconnecthost""" maxTries = 10 self.log.info(_("Attempting to connect to vnc client on host %s..."), self.vncconnecthost) if self.vncconnectport != "": hostarg = self.vncconnecthost + ":" + self.vncconnectport else: hostarg = self.vncconnecthost vncconfigcommand = [self.root+"/usr/bin/vncconfig", "-display", ":%s" % constants.X_DISPLAY_NUMBER, "-connect", hostarg] for _i in range(maxTries): vncconfp = iutil.startProgram(vncconfigcommand, stdout=subprocess.PIPE, stderr=subprocess.PIPE) # vncconfig process err = vncconfp.communicate()[1].decode("utf-8") if err == '': self.log.info(_("Connected!")) return True elif err.startswith("connecting") and err.endswith("failed\n"): self.log.info(_("Will try to connect again in 15 seconds...")) time.sleep(15) continue else: log.critical(err) iutil.ipmi_abort(scripts=self.anaconda.ksdata.scripts) sys.exit(1) self.log.error(P_("Giving up attempting to connect after %d try!\n", "Giving up attempting to connect after %d tries!\n", maxTries), maxTries) return False
def run_command(self, command, stdin=None, ignore_failure=False): process_error = None try: sys_root = iutil.getSysroot() cmd = iutil.startProgram(command, stderr=subprocess.PIPE, stdin=stdin, root=sys_root) (stdout, stderr) = cmd.communicate() stdout = stdout.decode("utf-8") stderr = stderr.decode("utf-8") if not ignore_failure and cmd.returncode != 0: process_error = "{} failed:\nstdout: \"{}\"\nstderr: \"{}\"".format( command, stdout, stderr) except Exception as e: process_error = str(e) if process_error: self.logger.error(process_error) raise Exception(process_error) return (stdout, stderr)
def test_still_running(): with timer(5): # Run something forever so we can kill it proc = iutil.startProgram(["/bin/sh", "-c", "while true; do sleep 1; done"]) iutil.watchProcess(proc, "test1") proc.kill() # Wait for the SIGCHLD signal.pause()
def execute(self, storage, ksdata, instClass, users): """ Execute the addon :param storage: Blivet storage object :param ksdata: Kickstart data object :param instClass: Anaconda installclass object :param users: Anaconda users object """ log.info("Executing docker addon") # This gets called after installation, before initramfs regeneration and kickstart %post scripts. execWithRedirect("mount", ["-o", "bind", getSysroot()+"/var/lib/docker", "/var/lib/docker"]) execWithRedirect("mount", ["-o", "bind", getSysroot()+"/etc/docker", "/etc/docker"]) # Start up the docker daemon log.debug("Starting docker daemon") dm_fs = "dm.fs=%s" % self.fstype pool_name = "dm.thinpooldev=/dev/mapper/%s-docker--pool" % self.vgname docker_cmd = ["docker", "daemon"] if ksdata.selinux.selinux: docker_cmd += ["--selinux-enabled"] docker_cmd += ["--storage-driver", "devicemapper", "--storage-opt", dm_fs, "--storage-opt", pool_name, "--ip-forward=false", "--iptables=false"] docker_cmd += self.extra_args docker_proc = startProgram(docker_cmd, stdout=open("/tmp/docker-daemon.log", "w"), reset_lang=True) log.debug("Running docker commands") script = AnacondaKSScript(self.content, inChroot=False, logfile="/tmp/docker-addon.log") script.run("/") # Kill the docker process log.debug("Shutting down docker daemon") docker_proc.kill() log.debug("Writing docker configs") with open(getSysroot()+"/etc/sysconfig/docker-storage", "w") as fp: fp.write('DOCKER_STORAGE_OPTIONS="--storage-driver devicemapper ' '--storage-opt %s --storage-opt %s"\n' % (dm_fs, pool_name)) with open(getSysroot()+"/etc/sysconfig/docker-storage-setup", "a") as fp: fp.write("VG=%s\n" % self.vgname) # Rewrite the OPTIONS entry with the extra args, if requested. if self.extra_args and self.save_args: try: docker_cfg = SimpleConfigFile(getSysroot()+"/etc/sysconfig/docker") docker_cfg.read() options = docker_cfg.get("OPTIONS")+" " + " ".join(self.extra_args) docker_cfg.set(("OPTIONS", options)) docker_cfg.write() except IOError as e: log.error("Error updating OPTIONS in /etc/sysconfig/docker: %s", e) # Copy the log files to the system dstdir = "/var/log/anaconda/" os.makedirs(dstdir, exist_ok=True) for l in ["docker-daemon.log", "docker-addon.log"]: shutil.copy2("/tmp/"+l, dstdir+l)
def start_yelp(help_path): """Start a new yelp process and make sure to kill any existing ones :param help_path: path to the help file yelp should load :type help_path: str or NoneType """ kill_yelp() log.debug("starting yelp") global yelp_process # under some extreme circumstances (placeholders missing) # the help path can be None and we need to prevent Popen # receiving None as an argument instead of a string yelp_process = startProgram(["yelp", help_path or ""], reset_lang=False)
def save_layouts_to_udev(self, layouts, variants): """ Sets layouts to udev, so it will also apply to newly connected keyboards (or existing after udevadm trigger). Otherwise Xorg setup them based on xorg.conf with a fallback to hardcoded values. :param layouts: list of layouts :param variants: list of layout variants, matching *layouts* """ udev_rules_dir = '/run/udev/rules.d' udev_rules_path = udev_rules_dir + '/90-keyboard-layout.rules' try: iutil.mkdirChain(udev_rules_dir) except FileExistsError: pass with open(udev_rules_path, 'w') as rules: rules.write('ENV{{ID_INPUT_KEYBOARD}}=="1", ' 'ENV{{xkblayout}}="{layouts}", ' 'ENV{{xkbvariant}}="{variants}"\n'.format( layouts=','.join(layouts), variants=','.join(variants))) iutil.startProgram(['udevadm', 'control', '-R']).communicate()
def start_program_stdout_test(self): """Test redirecting stdout with startProgram.""" marker_text = "yo wassup man" # Create a temporary file that will be written by the program with tempfile.NamedTemporaryFile() as testfile: # Open a new copy of the file so that the child doesn't close and # delete the NamedTemporaryFile stdout = open(testfile.name, 'w') with timer(5): proc = iutil.startProgram(["/bin/echo", marker_text], stdout=stdout) proc.communicate() # Rewind testfile and look for the text testfile.seek(0, os.SEEK_SET) self.assertEqual(testfile.read().strip(), marker_text)
def start_program_stdout_test(self): """Test redirecting stdout with startProgram.""" marker_text = "yo wassup man" # Create a temporary file that will be written by the program with tempfile.NamedTemporaryFile(mode="w+t") as testfile: # Open a new copy of the file so that the child doesn't close and # delete the NamedTemporaryFile stdout = open(testfile.name, 'w') with timer(5): proc = iutil.startProgram(["/bin/echo", marker_text], stdout=stdout) proc.communicate() # Rewind testfile and look for the text testfile.seek(0, os.SEEK_SET) self.assertEqual(testfile.read().strip(), marker_text)
def watch_process_test(self): """Test watchProcess""" def test_still_running(): with timer(5): # Run something forever so we can kill it proc = iutil.startProgram(["/bin/sh", "-c", "while true; do sleep 1; done"]) iutil.watchProcess(proc, "test1") proc.kill() # Wait for the SIGCHLD signal.pause() self.assertRaises(iutil.ExitError, test_still_running) # Make sure watchProcess checks that the process has not already exited with timer(5): proc = iutil.startProgram(["true"]) proc.communicate() self.assertRaises(iutil.ExitError, iutil.watchProcess, proc, "test2")
def do_startup_x11_actions(): """Start the window manager. When metacity actually connects to the X server is unknowable, but fortunately it doesn't matter. metacity does not need to be the first connection to Xorg, and if anaconda starts up before metacity, metacity will just take over and maximize the window and make everything right, fingers crossed. Add XDG_DATA_DIRS to the environment to pull in our overridden schema files. """ datadir = os.environ.get('ANACONDA_DATADIR', '/usr/share/anaconda') if 'XDG_DATA_DIRS' in os.environ: xdg_data_dirs = datadir + '/window-manager:' + os.environ['XDG_DATA_DIRS'] else: xdg_data_dirs = datadir + '/window-manager:/usr/share' childproc = iutil.startProgram(["metacity", "--display", ":1", "--sm-disable"], env_add={'XDG_DATA_DIRS': xdg_data_dirs}) iutil.watchProcess(childproc, "metacity")
def do_startup_x11_actions(): """Start the window manager. When metacity actually connects to the X server is unknowable, but fortunately it doesn't matter. metacity does not need to be the first connection to Xorg, and if anaconda starts up before metacity, metacity will just take over and maximize the window and make everything right, fingers crossed. Add XDG_DATA_DIRS to the environment to pull in our overridden schema files. """ datadir = os.environ.get('ANACONDA_DATADIR', '/usr/share/anaconda') if 'XDG_DATA_DIRS' in os.environ: xdg_data_dirs = datadir + '/window-manager:' + os.environ[ 'XDG_DATA_DIRS'] else: xdg_data_dirs = datadir + '/window-manager:/usr/share' childproc = iutil.startProgram( ["metacity", "--display", ":1", "--sm-disable"], env_add={'XDG_DATA_DIRS': xdg_data_dirs}) iutil.watchProcess(childproc, "metacity")
def setUserPassword(self, username, password, isCrypted, lock, algo=None, root="/"): # Only set the password if it is a string, including the empty string. # Otherwise leave it alone (defaults to locked for new users) and reset sp_lstchg if password or password == "": if password == "": log.info("user account %s setup with no password", username) elif not isCrypted: password = cryptPassword(password, algo) if lock: password = "******" + password log.info("user account %s locked", username) proc = iutil.startProgram(["chpasswd", "-R", root, "-e"], stdin=subprocess.PIPE) proc.communicate(("%s:%s\n" % (username, password)).encode("utf-8")) if proc.returncode != 0: raise OSError("Unable to set password for new user: status=%s" % proc.returncode) # Reset sp_lstchg to an empty string. On systems with no rtc, this # field can be set to 0, which has a special meaning that the password # must be reset on the next login. iutil.execWithRedirect("chage", ["-R", root, "-d", "", username])
def start_program_preexec_fn_test(self): """Test passing preexec_fn to startProgram.""" marker_text = "yo wassup man" # Create a temporary file that will be written before exec with tempfile.NamedTemporaryFile(mode="w+t") as testfile: # Write something to testfile to show this method was run def preexec(): # Open a copy of the file here since close_fds has already closed the descriptor testcopy = open(testfile.name, 'w') testcopy.write(marker_text) testcopy.close() with timer(5): # Start a program that does nothing, with a preexec_fn proc = iutil.startProgram(["/bin/true"], preexec_fn=preexec) proc.communicate() # Rewind testfile and look for the text testfile.seek(0, os.SEEK_SET) self.assertEqual(testfile.read(), marker_text)
def start_program_preexec_fn_test(self): """Test passing preexec_fn to startProgram.""" marker_text = "yo wassup man" # Create a temporary file that will be written before exec with tempfile.NamedTemporaryFile() as testfile: # Write something to testfile to show this method was run def preexec(): # Open a copy of the file here since close_fds has already closed the descriptor testcopy = open(testfile.name, 'w') testcopy.write(marker_text) testcopy.close() with timer(5): # Start a program that does nothing, with a preexec_fn proc = iutil.startProgram(["/bin/true"], preexec_fn=preexec) proc.communicate() # Rewind testfile and look for the text testfile.seek(0, os.SEEK_SET) self.assertEqual(testfile.read(), marker_text)
def run_command(self, command, stdin=None, ignore_failure=False): process_error = None try: sys_root = iutil.getSysroot() cmd = iutil.startProgram(command, stderr=subprocess.PIPE, stdin=stdin, root=sys_root) (stdout, stderr) = cmd.communicate() stdout = stdout.decode("utf-8") stderr = stderr.decode("utf-8") if not ignore_failure and cmd.returncode != 0: process_error = "{} failed:\nstdout: \"{}\"\nstderr: \"{}\"".format(command, stdout, stderr) except Exception as e: process_error = str(e) if process_error: self.logger.error(process_error) raise Exception(process_error) return (stdout, stderr)