Server IP : 172.67.216.182 / Your IP : 162.158.170.16 Web Server : Apache System : Linux krdc-ubuntu-s-2vcpu-4gb-amd-blr1-01.localdomain 5.15.0-142-generic #152-Ubuntu SMP Mon May 19 10:54:31 UTC 2025 x86_64 User : www ( 1000) PHP Version : 7.4.33 Disable Function : passthru,exec,system,putenv,chroot,chgrp,chown,shell_exec,popen,proc_open,pcntl_exec,ini_alter,ini_restore,dl,openlog,syslog,readlink,symlink,popepassthru,pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,imap_open,apache_setenv MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : ON Directory : /snap/core20/current/usr/lib/python3/dist-packages/cloudinit/config/ |
Upload File : |
# Copyright (C) 2011 Canonical Ltd. # # Author: Scott Moser <[email protected]> # # This file is part of cloud-init. See LICENSE file for license information. """Power State Change: Change power state""" import errno import logging import os import re import subprocess import time from cloudinit import signal_handler, subp, util from cloudinit.cloud import Cloud from cloudinit.config import Config from cloudinit.config.schema import MetaSchema from cloudinit.distros import ALL_DISTROS from cloudinit.settings import PER_INSTANCE frequency = PER_INSTANCE EXIT_FAIL = 254 meta: MetaSchema = { "id": "cc_power_state_change", "distros": [ALL_DISTROS], "frequency": PER_INSTANCE, "activate_by_schema_keys": ["power_state"], } LOG = logging.getLogger(__name__) def givecmdline(pid): # Returns the cmdline for the given process id. In Linux we can use procfs # for this but on BSD there is /usr/bin/procstat. try: # Example output from procstat -c 1 # PID COMM ARGS # 1 init /bin/init -- if util.is_FreeBSD(): (output, _err) = subp.subp(["procstat", "-c", str(pid)]) line = output.splitlines()[1] m = re.search(r"\d+ (\w|\.|-)+\s+(/\w.+)", line) return m.group(2) else: return util.load_text_file("/proc/%s/cmdline" % pid) except IOError: return None def check_condition(cond): if isinstance(cond, bool): LOG.debug("Static Condition: %s", cond) return cond pre = "check_condition command (%s): " % cond try: proc = subprocess.Popen(cond, shell=not isinstance(cond, list)) proc.communicate() ret = proc.returncode if ret == 0: LOG.debug("%sexited 0. condition met.", pre) return True elif ret == 1: LOG.debug("%sexited 1. condition not met.", pre) return False else: LOG.warning("%sunexpected exit %s. do not apply change.", pre, ret) return False except Exception as e: LOG.warning("%sUnexpected error: %s", pre, e) return False def handle(name: str, cfg: Config, cloud: Cloud, args: list) -> None: try: (arg_list, timeout, condition) = load_power_state(cfg, cloud.distro) if arg_list is None: LOG.debug("no power_state provided. doing nothing") return except Exception as e: LOG.warning("%s Not performing power state change!", str(e)) return if condition is False: LOG.debug("Condition was false. Will not perform state change.") return mypid = os.getpid() cmdline = givecmdline(mypid) if not cmdline: LOG.warning("power_state: failed to get cmdline of current process") return devnull_fp = open(os.devnull, "w") LOG.debug("After pid %s ends, will execute: %s", mypid, " ".join(arg_list)) util.fork_cb( run_after_pid_gone, mypid, cmdline, timeout, condition, execmd, [arg_list, devnull_fp], ) def load_power_state(cfg, distro): # returns a tuple of shutdown_command, timeout # shutdown_command is None if no config found pstate = cfg.get("power_state") if pstate is None: return (None, None, None) if not isinstance(pstate, dict): raise TypeError("power_state is not a dict.") modes_ok = ["halt", "poweroff", "reboot"] mode = pstate.get("mode") if mode not in distro.shutdown_options_map: raise TypeError( "power_state[mode] required, must be one of: %s. found: '%s'." % (",".join(modes_ok), mode) ) args = distro.shutdown_command( mode=mode, delay=pstate.get("delay", "now"), message=pstate.get("message"), ) try: timeout = float(pstate.get("timeout", 30.0)) except ValueError as e: raise ValueError( "failed to convert timeout '%s' to float." % pstate["timeout"] ) from e condition = pstate.get("condition", True) if not isinstance(condition, (str, list, bool)): raise TypeError("condition type %s invalid. must be list, bool, str") return (args, timeout, condition) def doexit(sysexit): os._exit(sysexit) def execmd(exe_args, output=None, data_in=None): ret = 1 try: proc = subprocess.Popen( # nosec B603 exe_args, stdin=subprocess.PIPE, stdout=output, stderr=subprocess.STDOUT, ) proc.communicate(data_in) ret = proc.returncode except Exception: doexit(EXIT_FAIL) doexit(ret) def run_after_pid_gone(pid, pidcmdline, timeout, condition, func, args): # wait until pid, with /proc/pid/cmdline contents of pidcmdline # is no longer alive. After it is gone, or timeout has passed # execute func(args) msg = None end_time = time.monotonic() + timeout def fatal(msg): LOG.warning(msg) doexit(EXIT_FAIL) known_errnos = (errno.ENOENT, errno.ESRCH) while True: if time.monotonic() > end_time: msg = "timeout reached before %s ended" % pid break try: cmdline = givecmdline(pid) if cmdline != pidcmdline: msg = "cmdline changed for %s [now: %s]" % (pid, cmdline) break except IOError as ioerr: if ioerr.errno in known_errnos: msg = "pidfile gone [%d]" % ioerr.errno else: fatal("IOError during wait: %s" % ioerr) break except Exception as e: fatal("Unexpected Exception: %s" % e) time.sleep(0.25) if not msg: fatal("Unexpected error in run_after_pid_gone") LOG.debug(msg) try: if not check_condition(condition): return except Exception as e: fatal("Unexpected Exception when checking condition: %s" % e) # systemd could kill this process with a signal before it exits # this is expected, so don't crash with signal_handler.suspend_crash(): func(*args)