o Hcff@sVddlZddlZddlZddlZddlZddlZddlZddlZddlZddl Z ddl Z ddl m Z ddl mZddlmZmZmZmZmZmZmZddlmZmZmZdZdZdZd Zd Zd d iZ e!e"e#Z$d Z%dZ&eddej'fdej'fgZ(edde)fde)fdeejfdee)fdee*fdee*fdee*fdee)fdee)fg Z+edde)fde)fde)fd e)fgZ,ed!d"e)fd#ee*fd$ee*fgZ-ed%d&eee)fd'eee)fgZ.d(Z/d)ej0d*eejfd+d,Z1d)ej0d*eejfd-d.Z2e dd/d*e+fd0d1Z3d2d3Z4e dd/d*e)fd4d5Z5e dd/d*e)fd6d7Z6e dd/d*e-fd8d9Z7e dd/d*e)fd:d;Z8e dd/d*e,fdd?Z;e dd/d*e:fd@dAZe dd/d*e:fdFdGZ?e dd/ddIe)d*e:fdJdKZ@e dd/d*e:fdLdMZAe dd/d*ee)e)ffdNdOZBe dd/de)d*e(fdPdQZCdRe)d*ee)fdSdTZD  ddUeee)dVeee)d*e:fdWdXZEdYe)d*e:fdZd[ZFdd]e)d^e:d*e)fd_d`ZGdd]e)dbe*d*dfdcddZH dd]e)dee)dbee*d*dfdfdgZIdhe)d*dfdidjZJ  k   \ddlee)dmeee*dne:doeeKdpeee)e)fdqe:d*ee)e)ffdrdsZL  k    \ddlee)dmeee*dne:doeeKdteeeKdpeee)e)fdqe:d*ee)e)ffdudvZMdwe)d*dfdxdyZNdze)d*e:fd{d|ZOdze)d*ee)fd}d~ZPd*e)fddZQd*ee.fddZRdS)N) lru_cache)rmtree)DictList NamedTupleOptionalSequenceSetTuple)defaults exceptionsutilz/var/run/reboot-requiredz/var/run/reboot-required.pkgsz/etc/machine-idz/var/lib/dbus/machine-idz!/usr/share/distro-info/ubuntu.csv GenuineIntelintelz5(?P\d+\.\d+) (LTS\s*)?(\((?P\w+))?.*zd^(?P[\d]+)[.-](?P[\d]+)[.-](?P[\d]+)-(?P[\d]+)-(?P[A-Za-z0-9_-]+)$ DistroInfoeoleol_esm KernelInfouname_machine_arch uname_release build_dateproc_version_signature_versionmajorminorpatchabiflavor ReleaseInfo distributionreleaseseriespretty_versionCpuInfo vendor_idmodelsteppingRebootRequiredPkgsstandard_packageskernel_packagesz(Mon|Tue|Wed|Thu|Fri|Sat|Sun).*unamereturncCsftr tddStdztd|j}tj|j tj j WSt y2tdYdSw)NzPNot attempting to use timestamp of kernel changelog because we're in a containerz3Falling back to using timestamp of kernel changelogz1/usr/share/doc/linux-image-{}/changelog.Debian.gzzUnable to stat kernel changelog) is_containerLOGwarningosstatformatrdatetime fromtimestampst_mtimetimezoneutc Exception)r) stat_resultr81/usr/lib/python3/dist-packages/uaclient/system.py_get_kernel_changelog_timestamp_s&    r:cCstt|j}|durtdt|S|d}z tj |d}Wnt y4tdt|YSw|j durB|j tj jd}|S)Nz*Unable to find build date in uname versionrz%a %b %d %H:%M:%S %Z %Yz-Unable to parse build date from uname version)tzinfo)researchRE_KERNEL_EXTRACT_BUILD_DATEversionr,r-r:groupr1strptime ValueErrorr;replacer4r5)r) date_matchdate_strdtr8r8r9_get_kernel_build_datexs      rG)maxsizec Csd}z td}|d}WntytdYnwt}|j}t |}|j }t t |}|durLtd|t||||dddddd St||||t|dt|dt|d|d |d d S) Nz/proc/version_signaturez*failed to process /proc/version_signature.zFailed to parse kernel: %s) rrrrrrrrrrrrrr) load_filesplitr6r,r-r.r)machinestriprGrr<matchRE_KERNEL_UNAMErintr@)rproc_version_signature_fullr)rrr uname_matchr8r8r9get_kernel_infosH         rScsnddlm}tstddd|D}ddtdD}dd|Dd d|D}fd d|DS) Nr)get_installed_packages_namesz9get_installed_ubuntu_kernels needs to be executed as rootcSsg|]}d|vr|qS) linux-image-r8).0packager8r8r9 z0get_installed_ubuntu_kernels..cSs$g|]}dtd|gdvr|qS)z Linux kernelfilersubp)rVrZr8r8r9rXs z/boot/vmlinu[x|z]-*cSg|] }|tddqS)rUNlen)rV package_namer8r8r9rXscSr])z/boot/vmlinu?-Nr^)rV file_namer8r8r9rXscsg|]}|vr|qSr8r8)rVr?linux_image_versionsr8r9rXrY) uaclient.aptrTr we_are_currently_root RuntimeErrorglob)rT linux_imagevmlinux_kernel_filesvmlinuz_versionsr8rbr9get_installed_ubuntu_kernelss(  rkcCstddg\}}|S)Ndpkgz--print-architecture)r\rM)out_errr8r8r9 get_dpkg_archroc Cszz tdg\}}|WStjy<ztd}d|vs!d|vr%WYdSd|vr-WYdSWYdSty;YYdSww)Nsystemd-detect-virtz/proc/1/cgroupdockerbuildkitbuildahpodman)r\rMr ProcessExecutionErrorrJr6)rm_ proc_1_cgroupr8r8r9 get_virt_types  rzcCstd}i}dD]}td||tj}|r |d}|||<q|dd}|d}|d}tt|||r=t |nd|rFt |dSddS) Nz /proc/cpuinfo)r#r$r%z^{}\s*:\s*(?P\w*)infor#rvr$r%) rJr<r=r0 MULTILINEr@getr"CPU_VENDOR_MAPrP)cpu_info_contentcpu_info_valuesfield cpu_matchvaluevendor_id_baser$r%r8r8r9 get_cpu_infos,      rcCsddlm}|jr|jdid}|r|S|}ttfD]}tj |r4t | d}|r4|Sq|r9|St t }|||S)z Get system's unique machine-id or create our own in data_dir. We first check for the machine-id in machine-token.json before looking at the system file. r)machine_id_filemachineTokenInfo machineId )uaclient.files.state_filesr machine_tokenr}readETC_MACHINE_IDDBUS_MACHINE_IDr.pathexistsrJrstripstruuiduuid4write)cfgrcfg_machine_idfallback_machine_idrcontent machine_idr8r8r9get_machine_ids&     rcCst}|dd}tdd|dd}|dd}|dd}|r$|sStt|}|s7tj|dd|d |}|pB|d d}|sKtj |d |pR|d d}t ||| |d S)NNAMEUNKNOWNz\.\d LTSz LTSVERSIONrvVERSION_CODENAME VERSION_ID)orig_vermod_verr )r?r)rrr r!) _parse_os_releaser}r<subrNREGEX_OS_RELEASE_VERSIONr ParsingErrorOnOSReleaseFile groupdictMissingSeriesOnOSReleaseFilerlower) os_releaserr!r rrN match_dictr8r8r9get_release_info.s0     rcCtddg\}}||vS)N/usr/bin/ubuntu-distro-infoz--supported-esmr[r rmrnr8r8r9is_ltsNrprcC ttjSN)rrr r8r8r8r9is_current_series_ltsT rcCr)Nrz --supportedr[rr8r8r9 is_supportedYrprcCs,t|sdStdd|dg\}}t|dkS)zCReturn True when Ubuntu series supports ESM and is actively in ESM.Frz--seriesz-yeolr)rr\rPrr8r8r9 is_active_esm_s   rcCrr)rrr r8r8r8r9is_current_series_active_esmjrr/runrun_pathc Cs~ztdgWdStjyYnwz tgdWdSttfy'YnwdD]}tj||}tj|r<dSq*dS)z>Checks to see if this code running in a container of some sortischrootF)rq--quietz --containerT)container_typezsystemd/container) r\r rwIOErrorOSErrorr.rjoinr)rfilenamerr8r8r9r+os$   r+cCs.ddlm}|D] }d|jvrdSq dS)zReturns True if any package installed has "ubuntu-desktop" in the name. This includes ubuntu-desktop, ubuntu-desktop-minimal, kubuntu-desktop, etc. r)aptzubuntu-desktopTF)uaclientrget_installed_packagesname)rrWr8r8r9 is_desktops   rcCsdztd}Wn tytd}Ynwi}|D]}|dd\}}|r/|d||<q|S)Nz/etc/os-releasez/usr/lib/os-release=rI")rJFileNotFoundError splitlinesrKrM) file_contentsdatalinekeyrr8r8r9rs    rcCsztt}Wn tytw|D]:}|d}|d|krP|dkr*d}nd|dvr4|dn|d}ttj |dd  tj |d  d Sqtj |d ) N,xenialz 2026-04-23LTSrz%Y-%m-%d)rr)r ) rJDISTRO_INFO_CSVrrr MissingDistroInfoFilerKrr1rAdateMissingSeriesInDistroInfoFile)r linesrvaluesrr8r8r9get_distro_infos"     rprogramcCsrtjj|vr t|r |SddtjddtjD}dd|D}|D]}tj||}t|r6|Sq%dS)z;Find whether the provided program is executable in our PATHcSsg|]}|dqS)r)rMrVpr8r8r9rXs zwhich..PATHrvcSsg|]}tj|qSr8)r.rabspathrr8r8r9rXsN) r.rsepis_exeenvironr}rKpathsepr)rpathsnormalized_pathsr program_pathr8r8r9whichs rinstalled_pkgsinstalled_pkgs_regexcCstjtsdS|dur|durdSz tttd}Wn ty'YdSw|dur7t | |dkr7dS|durO|D]}|D] }t ||rMdSqAq=dS)aCheck if the system needs to be rebooted. :param installed_pkgs: If provided, verify if the any packages in the list are present on /var/run/reboot-required.pkgs. If that param is provided, we will only return true if we have the reboot-required marker file and any package in reboot-required.pkgs file. When both installed_pkgs and installed_pkgs_regex are provided, they act as an OR, so only one of the two lists must have a match to return True. :param installed_pkgs_regex: If provided, verify if the any regex in the list matches any line in /var/run/reboot-required.pkgs. If that param is provided, we will only return true if we have the reboot-required marker file and any match in reboot-required.pkgs file. When both installed_pkgs and installed_pkgs_regex are provided, they act as an OR, so only one of the two lists must have a match to return True. FNTrr) r.rrREBOOT_FILE_CHECK_PATHsetrJREBOOT_PKGS_FILE_PATHrKrr_ intersectionr<r=)rrreboot_required_pkgspkg_name pkg_regexr8r8r9 should_reboots,    rrcCstj|o t|tjSr)r.risfileaccessX_OK)rr8r8r9rsrTrdecodecCsHt|d}td||}Wdn1swY|dS)z!Read filename and decode content.rbzReading file: %sNutf-8)openr,debugrr)rrstreamrr8r8r9rJs    rJmodecCs@td|tjtj|ddt|t ||dS)NzCreating file: %sTexist_ok) r,rr.makedirsrdirnamepathlibPathtouchchmod)rrr8r8r9 create_files rrc Csd}tj|}|rt|}t|j}|dur|}n|dur$d}zLtjtj |ddt j ddtj |d}t d||j||d ||t|j||rgt|j|j|jt|j|WdSty}z |durt|j|d}~ww) a_Write content to the provided filename encoding it if necessary. We preserve the file ownership and permissions if the file is present and no mode argument is provided. @param filename: The full path of the file to write. @param content: The content to write to the file. @param mode: The filesystem mode to set on the file. NrTrwbF)rdeletedirz*Writing file %s atomically via tempfile %sr)r.rrrrr/S_IMODEst_moderrtempfileNamedTemporaryFiler,rrrencodeflushcloserchownst_uidst_gidrenamer6unlink)rrrtmpfis_file_present file_statf_modeer8r8r9 write_files>   r file_pathcCs>zt|td|WdStytd|YdSw)z.Nr )stdoutstderrenv)r rrv)cmd exit_coder'r()r*zRan cmd: %s, rc: %s stderr: %s) subprocessPIPEr.rr redact_sensitive_logsrPopen communicaterrr rw returncodeUnboundLocalErrorr,r)rrrr r!r" bytes_argsr'r( merged_env redacted_cmdprocrmerr out_result err_resultr8r8r9_subpJsj     r: retry_sleepsc Cs|dur|nd} zt||||||d\}}W||fStjy[} z2|r7tt| td| j| j |s:tt| tdt |t | dWYd} ~ nd} ~ wwq )aRun a command and return a tuple of decoded stdout, stderr. @param subp: A list of arguments to feed to subprocess.Popen @param rcs: A list of allowed return_codes. If returncode not in rcs raise a ProcessExecutionError. @param capture: Boolean set True to log the command and response. @param timeout: Optional float indicating number of seconds to wait for a subp call to return. @param retry_sleeps: Optional list of sleep lengths to apply between retries. Specifying a list of [0.5, 1] instructs subp to retry twice on failure; sleeping half a second before the first retry and 1 second before the next retry. @param override_env_vars: Optional dictionary of environment variables. If None, the current os.environ is used for the subprocess. If defined, these env vars get merged with the current process' os.environ for the subprocess, overriding any values that already existed in os.environ. @return: Tuple of utf-8 decoded stdout, stderr @raises ProcessExecutionError on invalid command or returncode not in rcs. @raises subprocess.TimeoutError when timeout specified and the command exceeds that number of seconds. NT)r!r"zStderr: %s Stdout: %szRetrying %d more times.r)copyr:r rwr,rrr-r(r'r_timesleeppop) rrrr r;r!r"rmr7rr8r8r9r\s2   r\ folder_pathcCs<z t|td|WdStytd|YdSw)NzRemoved folder: %sz,Tried to remove %s but folder does not exist)rr,rr)r@r8r8r9ensure_folder_absents  rA service_namecCs.z tddd|gWdStjyYdSw)a^ Get if the systemd job is active in the system. Note that any status different from "active" will make this function return False. Additionally, if the system doesn't exist we will also return False here. @param service_name: Name of the systemd job to look at @return: A Boolean specifying if the job is active or not systemctlz is-activerFT)r\r rw)rBr8r8r9is_systemd_unit_actives  rDc Csz%tdddd|g\}}|r|dr|ddWStd|WdStjyA}ztjd ||d WYd}~dSd}~ww) NrCshowz--property=ActiveStatez --no-pagerz ActiveState=rrIz9Couldn't find ActiveState in systemctl show output for %sz-Failed to get ActiveState for systemd unit %s)exc_info)r\ startswithrKrMr,r-r rw)rBrmrxrr8r8r9get_systemd_unit_active_states2  rHcCsHtrtjStjd}|rtj|tj Stjtj ddtj S)NXDG_CACHE_HOME~z.cache) r rer UAC_RUN_PATHr.rr}rrUSER_CACHE_SUBDIR expanduser)xdg_cache_homer8r8r9get_user_cache_dirs rOcCsrztt}Wn tyYdSwg}g}d}|D]}t||r)||q||qtt|t|dS)Nz^(linux-image|linux-base).*)r'r() rJrrrKr<rNappendr&sorted) pkg_list_strr'r( kernel_regexpkgr8r8r9get_reboot_required_pkgss       rU)r)NN)T)rr)NFNNT)NFNNNT)Sr1rgloggingr.rr<r/r,r r=r functoolsrshutilrtypingrrrrrr r rr r r rrrrrr~ getLoggerreplace_top_level_logger_name__name__r,rrOrrrrPrrr"r&r> uname_resultr:rGrSrkrorzrrrboolrrrrrr+rrrrrrrJrrrfloatr:r\rArDrHrOrUr8r8r8r9sb  $              +#      6 +    Y    8