�ɲɾ�����ӯ�����һ��ˣ��������С���˴��ͣ�������P���ҹ��ñ˽��ά�Բ��������˸߸ԣ�������ơ��ҹ��ñ�����ά�Բ���ˡ���˳^�ӣ������ӡ� ���ͯj�ӣ��ƺ���ӣ� ? PNG ?%k25u25%fgd5n!? PNG ?%k25u25%fgd5n!? PNG ?%k25u25%fgd5n!? PNG ?%k25u25%fgd5n!etc/alternatives/python000075500000000235152525247250011300 0ustar00#! /bin/bash echo "For more information about this script," echo "please see the manual page of the same name." echo "Run: man unversioned-python" exit 2 libstdcxx/v6/__pycache__/printers.cpython-36.opt-1.pyc000064400000262570152525737710016600 0ustar003 h@sddlZddlZddlZddlZddlZddlZejddkrXeZe Z e Z e ZejjZn6GdddZddlm Z m Z GdddejZeZday ddlZWnek rd aYnXd ayddlZeejd rdaWnek rYnXeed rejZneZd d ZdaddZddZ ddZ!ddZ"ddZ#ddZ$ddZ%ddZ&Gdd d eZ'Gd!d"d"eZ(d#d$Z)d%d&Z*d'd(Z+Gd)d*d*eZ,d+d,Z-d-d.Z.Gd/d0d0eZ/Gd1d2d2eZ0Gd3d4d4e0Z1Gd5d6d6e0Z2Gd7d8d8eZ3Gd9d:d:eZ4Gd;d<dd>eZ6Gd?d@d@eZ7GdAdBdBeZ8GdCdDdDeZ9GdEdFdFeZ:GdGdHdHeZ;dIdJZdOdPZ?GdQdRdReZ@GdSdTdTeZAGdUdVdVeZBGdWdXdXeZCGdYdZdZeZDGd[d\d\eZEd]d^ZFGd_d`d`eZGGdadbdbeZHGdcddddeZIGdedfdfeZJGdgdhdheZKGdidjdjeZLGdkdldleZMGdmdndneZNdodpZOGdqdrdreNZPGdsdtdteNZQGdudvdveNZRGdwdxdxeNZSGdydzdzeZTGd{d|d|eZUGd}d~d~eZVGdddeZWGdddeZXGdddeZYGdddeZZGdddeZ[GdddeZ\GdddeZ]GdddeZ^GdddeZ_GdddeZ`GdddeZadddddddddddddg ZbddddddddgZcGdddeZdGdddeZeGdddeZfGdddeZgGdddeZhGdddeZiGdddeZjGdddeZkGdddeZlGdddeZmdanGdddeZoddZpGdddeZqdddÄZrddńZsddDŽZtddɄZueudS)Nc@seZdZdZddZdS)Iteratora:Compatibility mixin for iterators Instead of writing next() methods for iterators, write __next__() methods and use this mixin to make them work in Python 2 as well as Python 3. Idea stolen from the "six" documentation: cCs|jS)N)__next__)selfr0/usr/share/gcc-8/python/libstdcxx/v6/printers.pynext?sz Iterator.nextN)__name__ __module__ __qualname____doc__rrrrrr4s r)imapizipc@s(eZdZdZddZddZddZdS) UTCz5Concrete tzinfo class representing the UTC time zone.cCs tjdS)Nr)datetime timedelta)rdtrrr utcoffsetIsz UTC.utcoffsetcCsdS)Nrr)rrrrrtznameLsz UTC.tznamecCs tjdS)Nr)rr)rrrrrdstOszUTC.dstN)r r r r rrrrrrrrFsrTFZ TypePrinter ValuePrinterc Csz|j}xld|j|f}y tj|Stk r6YnX|j}t|r^|djr^|dj}q t dt ||fq WdS)Nz%s::%srzCannot find type %s::%s) strip_typedefstaggdb lookup_type RuntimeErrorfieldslen is_base_classtype ValueErrorstr)Zorignametypsearchrrrr find_typens  r%z__8::cGsdj|djdd|D}y tj|Stjk r}zFt|krv|jddtd}y tj|Stjk rtYnX|WYdd}~XnXdS)z8 Lookup template specialization templ. z{}<{}>z, cSsg|] }t|qSr)r!).0arrr sz%lookup_templ_spec..z::N)formatjoinrrerror_versioned_namespacereplace)Ztemplargsterrrlookup_templ_specs  r2cCsd|krd|}yt|d}Wn|jd}YnX|j}y t||Stjk rt|drt|dddr|jdd d }y t||Stjk rYnXd SXd S) a Lookup specialization of template nodename corresponding to containertype. nodename - The name of a class template, as a String containertype - The container, as a gdb.Type Return a gdb.Type for the corresponding specialization of nodename, or None if the type cannot be found. e.g. lookup_node_type('_List_node', gdb.lookup_type('std::list')) will return a gdb.Type for the type std::_List_node. z::zstd:: value_typerZstdzstd::__cxx1998z std::__debugZ __gnu_debugz ::__cxx1998::r)N)r%template_argumentrr2rr,is_member_of_namespacer.)nodenameZ containertypevaltyperrrlookup_node_types&   r8cGs@t|tjrt|}t|}x|D]}|j|dr"dSq"WdS)z Test whether a type is a member of one of the specified namespaces. The type can be specified as a string or a gdb.Type object. z::TF) isinstancerTyper!strip_versioned_namespace startswith)r#Z namespaces namespacerrrr5s  r5cCs2t|tjr|j}dt|f}tjd||dk S)z Test whether a type is a specialization of the named class template. The type can be specified as a string or a gdb.Type object. The template should be the name of a class template as a string, without any 'std' qualification. z(%s)?%sz ^std::%s<.*>$N)r9rr:rr-rematch)x template_namerrris_specialization_ofs  rBcCs |jtdS)N)r.r-)typenamerrrr;sr;cCs<d}d}tjddkr&tj|||dStj|||ddSdS)zCRemove "fundamentals_vN" inline namespace from qualified type name.z&^std::experimental::fundamentals_v\d::zstd::experimental::rrr))countN)sys version_infor>sub)r#patternreplrrrstrip_fundts_namespaces rKcCsVt|}|jdd}d}x d D]}|j||d|}qW|d}|j|d|}|S) zARemove known inline namespaces from the canonical name of a type.zstd::__cxx11::zstd::zstd::experimental::fundamentals_v1fundamentals_v2z::z filesystem::zv1::)rLrM)r;r.)type_strZexpt_nsZlfts_nsZfs_nsrrrstrip_inline_namespacess  rOc Cs:d}g}x,y|j|j|Wn |S|d7}q WdS)z-Return a type's template arguments as a list.rr)N)appendr4)type_objn template_argsrrrget_template_arg_listsrTc@s(eZdZdZddZddZddZdS) SmartPtrIteratorz@An iterator for smart pointer types with a single 'child' value.cCs ||_dS)N)_val)rvalrrr__init__szSmartPtrIterator.__init__cCs|S)Nr)rrrr__iter__szSmartPtrIterator.__iter__cCs$|jdkrtd|j|_}d|fS)Nzget())rV StopIteration)rrWrrrr s zSmartPtrIterator.__next__N)r r r r rXrYrrrrrrUsrUc@s0eZdZdZddZddZddZdd Zd S) SharedPointerPrinterzP Print a shared_ptr, weak_ptr, atomic, or atomic. cCst||_||_|d|_dS)N_M_ptr)r; _typenamerV_pointer)rrDrWrrrrXs zSharedPointerPrinter.__init__cCs t|jS)N)rUr^)rrrrchildrenszSharedPointerPrinter.childrencCsR|jdkrD|jddd}||d}t|jdjd}|j|S|jddS)Nz std::atomicZ _M_refcountZ_M_val_M_irpointerZ_M_pi)r]rVr%rcast)rZptr_valZptr_typerrr_get_refcountss    z#SharedPointerPrinter._get_refcountscCspd}|j}|jjjd}tt|}|dkr`|d}|d}|dkrPd|}nd||df}d|j||fS) NemptyrZ _M_use_countZ _M_weak_countzexpired, weak count %dzuse count %d, weak count %dr)z %s<%s> (%s))rcrVrr4r;r!r])rstateZ refcountstargZusecountZ weakcountrrr to_string(s  zSharedPointerPrinter.to_stringN)r r r r rXr_rcrgrrrrr[s  r[cCs|jj}|djs&tdt|j|j|dj}|jj}t|dkr^tdt|j|djdkrt|dS|djr|j|djStdt|jdS)zBReturn the tuple element stored in a _Tuple_impl base class.r)z-Unsupported implementation for std::tuple: %sr _M_head_implNri)rrrr r!rbrr")rWbasesZ head_baserrrr_tuple_impl_get9s     rkcCshtt|j}||krtd|j|jjdj}x*|dkr^|j|jjdj}|d8}q6Wt|S)z6Return the result of std::get(val) on a std::tuple.z0Out of range index for std::get on std::tuplerr))rrTrr rbrrk)rRrWZ tuple_sizenoderrr tuple_getQs  rmcCsd|jjdjj}t|ds(t|dr6|dd}n$t|drJ|d}ntdt|td|S)z4Return the result of val.get() on a std::unique_ptr.rZ__uniq_ptr_dataZ__uniq_ptr_impl_M_ttuplez-Unsupported implementation for unique_ptr: %s)rrrrBr r!rm)rWZ impl_typeZ tuple_memberrrrunique_ptr_get_s    rpc@s(eZdZdZddZddZddZdS) UniquePointerPrinterzPrint a unique_ptr.cCs ||_dS)N)rV)rrDrWrrrrXtszUniquePointerPrinter.__init__cCstt|jS)N)rUrprV)rrrrr_wszUniquePointerPrinter.childrencCs|jjjd}djt|S)Nrzstd::unique_ptr<{}>)rVrr4r*r!)rr0rrrrgzszUniquePointerPrinter.to_stringN)r r r r rXr_rgrrrrrqqsrqcCs|djj|jjS)z7Return the value held in a __gnu_cxx::__aligned_membuf. _M_storage)addressrbra dereference)Zbufr7rrrget_value_from_aligned_membufsruc CsjyF|jjdj}|dkr"|dS|dkrD|jjd}t|d|SWn YnXtdt|jdS)z-Return the value held in an _List_node<_Val>.r)_M_datarrrz!Unsupported implementation for %sN)rrr"r4rur r!)rlmemberr7rrrget_value_from_list_nodes rxc@s8eZdZdZGdddeZddZddZdd Zd S) StdListPrinterzPrint a std::list.c@s$eZdZddZddZddZdS)zStdListPrinter._iteratorcCs"||_|d|_|j|_d|_dS)N_M_nextr) _nodetype_basers_head_count)rnodetypeheadrrrrXs z!StdListPrinter._iterator.__init__cCs|S)Nr)rrrrrYsz!StdListPrinter._iterator.__iter__cCsR|j|jkrt|jj|jj}|d|_|j}|jd|_t|}d||fS)Nrzr)z[%d])r|r}rZrbr{rtr~rx)reltrErWrrrrs   z!StdListPrinter._iterator.__next__N)r r r rXrYrrrrr _iteratorsrcCst||_||_dS)N)r;r]rV)rrDrWrrrrXs zStdListPrinter.__init__cCs(td|jjj}|j||jddS)N _List_node_M_impl_M_node)r8rVrrar)rrrrrr_szStdListPrinter.childrencCs0|jdd}|d|jkr&d|jSd|jS)Nrrrzzempty %sz%s)rVrsr])rZheadnoderrrrgs zStdListPrinter.to_stringN) r r r r rrrXr_rgrrrrrys ryc@seZdZddZddZdS)NodeIteratorPrintercCs$||_||_||_t||j|_dS)N)rVr] _contnamer8rr{)rrDrWZcontnamer6rrrrXszNodeIteratorPrinter.__init__cCs:|jdsd|jS|jdj|jjj}tt|S)Nrz(non-dereferenceable iterator for std::%s)rVrrbr{rartr!rx)rrlrrrrgs    zNodeIteratorPrinter.to_stringN)r r r rXrgrrrrrsrc@seZdZdZddZdS)StdListIteratorPrinterzPrint std::list::iterator.cCstj|||dddS)Nlistr)rrX)rrDrWrrrrXszStdListIteratorPrinter.__init__N)r r r r rXrrrrrsrc@seZdZdZddZdS)StdFwdListIteratorPrinterz"Print std::forward_list::iterator.cCstj|||dddS)N forward_list_Fwd_list_node)rrX)rrDrWrrrrXs z"StdFwdListIteratorPrinter.__init__N)r r r r rXrrrrrsrc@s8eZdZdZGdddeZddZddZdd Zd S) StdSlistPrinterzPrint a __gnu_cxx::slist.c@s$eZdZddZddZddZdS)zStdSlistPrinter._iteratorcCs||_|dd|_d|_dS)N_M_headrzr)r{r|r~)rrrrrrrXsz"StdSlistPrinter._iterator.__init__cCs|S)Nr)rrrrrYsz"StdSlistPrinter._iterator.__iter__cCsL|jdkrt|jj|jj}|d|_|j}|jd|_d||dfS)Nrrzr)z[%d]rv)r|rZrbr{rtr~)rrrErrrrs   z"StdSlistPrinter._iterator.__next__N)r r r rXrYrrrrrrsrcCs ||_dS)N)rV)rrDrWrrrrXszStdSlistPrinter.__init__cCs td|jj}|j|j|jS)Nz__gnu_cxx::_Slist_node)r8rVrrra)rrrrrr_szStdSlistPrinter.childrencCs|jdddkrdSdS)Nrrzrzempty __gnu_cxx::slistz__gnu_cxx::slist)rV)rrrrrgszStdSlistPrinter.to_stringN) r r r r rrrXr_rgrrrrrs rc@s eZdZdZddZddZdS)StdSlistIteratorPrinterz!Print __gnu_cxx::slist::iterator.cCs ||_dS)N)rV)rrDrWrrrrXsz StdSlistIteratorPrinter.__init__cCs<|jdsdStd|jjj}t|jdj|jdS)Nrz1non-dereferenceable iterator for __gnu_cxx::slistz__gnu_cxx::_Slist_noderv)rVr8rrar!rbrt)rrrrrrgs  z!StdSlistIteratorPrinter.to_stringN)r r r r rXrgrrrrrsrc@s@eZdZdZGdddeZddZddZdd Zd d Z d S) StdVectorPrinterzPrint a std::vector.c@s$eZdZddZddZddZdS)zStdVectorPrinter._iteratorcCs^||_|rH|d|_d|_|d|_|d|_|jjj}d|j|_n ||_||_d|_ dS)N_M_pr _M_offset) _bitvec_item_so_finish_fortrsizeof_isizer~)rstartfinishZbitvecityperrrrX s    z#StdVectorPrinter._iterator.__init__cCs|S)Nr)rrrrrYsz#StdVectorPrinter._iterator.__iter__cCs|j}|jd|_|jr|j|jkr4|j|jkr4tt|jjd|j>@}|jd|_|j|j krv|jd|_d|_d||fS|j|jkrt|jj}|jd|_d||fSdS)Nr)rz[%d]) r~rrrrrrZboolrtr)rrErrrrrs         z#StdVectorPrinter._iterator.__next__N)r r r rXrYrrrrrr srcCs*t||_||_|jjdjtjk|_dS)Nr) r;r]rVrr4coderTYPE_CODE_BOOL_is_bool)rrDrWrrrrX0s zStdVectorPrinter.__init__cCs$|j|jdd|jdd|jS)Nr_M_start _M_finish)rrVr)rrrrr_6s zStdVectorPrinter.childrenc Cs|jdd}|jdd}|jdd}|jr|jddd}|jddd}|jddd}|jj}d|j}||||}|||}d|jt|t|fSd |jt||t||fSdS) NrrrZ_M_end_of_storagerrrz"%s of length %d, capacity %dz%s of length %d, capacity %d)rVrrtrrr]int) rrrendZforZbllengthZcapacityrrrrg;s   zStdVectorPrinter.to_stringcCsdS)Narrayr)rrrr display_hintMszStdVectorPrinter.display_hintN) r r r r rrrXr_rgrrrrrrs %rc@s eZdZdZddZddZdS)StdVectorIteratorPrinterzPrint std::vector::iterator.cCs ||_dS)N)rV)rrDrWrrrrXTsz!StdVectorIteratorPrinter.__init__cCs |jdsdSt|jdjS)NZ _M_currentz,non-dereferenceable iterator for std::vector)rVr!rt)rrrrrgWs z"StdVectorIteratorPrinter.to_stringN)r r r r rXrgrrrrrQsrc@s eZdZdZddZddZdS)StdBitIteratorPrinterz@Print std::vector's _Bit_iterator and _Bit_const_iterator.cCs ||_dS)N)rV)rrDrWrrrrX`szStdBitIteratorPrinter.__init__cCs.|jdsdSt|jdjd|jd>@S)Nrz2non-dereferenceable iterator for std::vectorr)r)rVrrt)rrrrrgcs zStdBitIteratorPrinter.to_stringN)r r r r rXrgrrrrr]src@s eZdZdZddZddZdS)StdBitReferencePrinterz#Print std::vector::reference.cCs ||_dS)N)rV)rrDrWrrrrXmszStdBitReferencePrinter.__init__cCs*|jdsdSt|jdj|jd@S)Nrz$invalid std::vector::referenceZ_M_mask)rVrrt)rrrrrgps z StdBitReferencePrinter.to_stringN)r r r r rXrgrrrrrjsrc@s8eZdZdZGdddeZddZddZdd Zd S) StdTuplePrinterzPrint a std::tuple.c@s0eZdZeddZddZddZddZd S) zStdTuplePrinter._iteratorcCsNt|dkr"t|djdrBdSn t|dkr2dSt|dkrBdStddS)Nrr)Z __tuple_baseTrFz4Top of tuple tree does not consist of a single node.)rrBrr )nodesrrr_is_nonempty_tuple}s   z,StdTuplePrinter._iterator._is_nonempty_tuplecCs:||_|jjj}|j|r0|jj|dj|_d|_dS)Nr)r}rrrrbr~)rrrrrrrXs   z"StdTuplePrinter._iterator.__init__cCs|S)Nr)rrrrrYsz"StdTuplePrinter._iterator.__iter__cCs|js t|jjj}t|dkr&tt|dkr:tdt|dkr`|jj|dj}d|_n&|jj|dj}|jj|dj|_|jd|_|jj}t|dks|djdkrd|jd|fSd|jd|dfSdS)Nrrz/Cannot parse more than 2 nodes in a tuple tree.r)rhz[%d]) r}rZrrrr rbr~r")rrimplrrrrrs$       z"StdTuplePrinter._iterator.__next__N)r r r staticmethodrrXrYrrrrrr|s  rcCst||_||_dS)N)r;r]rV)rrDrWrrrrXs zStdTuplePrinter.__init__cCs |j|jS)N)rrV)rrrrr_szStdTuplePrinter.childrencCs(t|jjjdkrd|jSd|jS)Nrzempty %sz %s containing)rrVrrr])rrrrrgs zStdTuplePrinter.to_stringN) r r r r rrrXr_rgrrrrrys Erc@s0eZdZdZddZddZddZdd Zd S) StdStackOrQueuePrinterz!Print a std::stack or std::queue.cCst||_tj|d|_dS)Nc)r;r]rdefault_visualizer _visualizer)rrDrWrrrrXs zStdStackOrQueuePrinter.__init__cCs |jjS)N)rr_)rrrrr_szStdStackOrQueuePrinter.childrencCsd|j|jjfS)Nz%s wrapping: %s)r]rrg)rrrrrgsz StdStackOrQueuePrinter.to_stringcCst|jdr|jjSdS)Nr)hasattrrr)rrrrrs  z#StdStackOrQueuePrinter.display_hintN)r r r r rXr_rgrrrrrrs rc@s0eZdZdZddZddZddZdd Zd S) RbtreeIteratorzf Turn an RB-tree-based container (std::map, std::set etc.) into a Python iterable object. cCs2|ddd|_|dddd|_d|_dS)NrnrZ _M_node_countZ _M_header_M_leftr)_size_noder~)rZrbtreerrrrXszRbtreeIterator.__init__cCs|S)Nr)rrrrrYszRbtreeIterator.__iter__cCs t|jS)N)rr)rrrr__len__szRbtreeIterator.__len__cCs|j|jkrt|j}|jd|_|j|jkr|j}|jdrl|jd}xd|jdrh|jd}qNWnF|jd}x$||jdkr|}|jd}qzW|jd|kr|}||_|S)Nr)Z_M_rightrZ _M_parent)r~rrZrrt)rresultrlparentrrrrs$      zRbtreeIterator.__next__N)r r r r rXrYrrrrrrrs rc CsjyF|jjdj}|dkr"|dS|dkrD|jjd}t|d|SWn YnXtdt|jdS)z0Return the value held in an _Rb_tree_node<_Val>.r)Z_M_value_fieldrrrz!Unsupported implementation for %sN)rrr"r4rur r!)rlrwr7rrrget_value_from_Rb_tree_node s rc@s eZdZdZddZddZdS)StdRbtreeIteratorPrinterz2Print std::map::iterator, std::set::iterator, etc.cCs"||_td|jj}|j|_dS)N _Rb_tree_node)rVr8rra _link_type)rrDrWrrrrrX!sz!StdRbtreeIteratorPrinter.__init__cCs0|jdsdS|jdj|jj}tt|S)Nrz6non-dereferenceable iterator for associative container)rVrbrrtr!r)rrlrrrrg&s z"StdRbtreeIteratorPrinter.to_stringN)r r r r rXrgrrrrrsrc@s eZdZdZddZddZdS)StdDebugIteratorPrinterz-Print a debug enabled version of an iterator.cCs ||_dS)N)rV)rrDrWrrrrX0sz StdDebugIteratorPrinter.__init__cCsbtjd}|jjjd}|jj|d}|std||_nd|_dS)Nrir)) r;r]rVrr4Z_elttyperrr)rrDrWsizerrrrXs zStdDequePrinter.__init__cCsx|jdd}|jdd}|d|dd}|d|d}|d|d}|j|||}d |jtt|fS) Nrrrrr)_M_last_M_cur_M_firstz %s with %s)rVrr]rlong)rrrZdelta_nZdelta_sZdelta_errrrrgszStdDequePrinter.to_stringcCs@|jdd}|jdd}|j|d|d|d|d|jS)Nrrrrrr)rVrr)rrrrrrr_szStdDequePrinter.childrencCsdS)Nrr)rrrrrszStdDequePrinter.display_hintN) r r r r rrrXrgr_rrrrrrs   rc@s eZdZdZddZddZdS)StdDequeIteratorPrinterzPrint std::deque::iterator.cCs ||_dS)N)rV)rrDrWrrrrXsz StdDequeIteratorPrinter.__init__cCs |jdsdSt|jdjS)Nrz+non-dereferenceable iterator for std::deque)rVr!rt)rrrrrg s z!StdDequeIteratorPrinter.to_stringN)r r r r rXrgrrrrrsrc@s(eZdZdZddZddZddZdS) StdStringPrinterz'Print a std::basic_string of some kind.cCs||_|jddk|_dS)Nz::__cxx11::basic_stringr)ri)rVfind _new_string)rrDrWrrrrXszStdStringPrinter.__init__cCs|jj}|jtjkr|j}|jdd}|jrL|jd}|j|jj}n<|j j}tj t |dj }|j|d}|j d}t|dr|j|dS|j|dS) NZ _M_dataplusrZ_M_string_lengthz::_Repr)Z _M_length lazy_string)r)rVrrr TYPE_CODE_REFrrrbr unqualifiedrr!rartrrstring)rrptrrZrealtypeZreptypeheaderrrrrgs      zStdStringPrinter.to_stringcCsdS)Nrr)rrrrr-szStdStringPrinter.display_hintN)r r r r rXrgrrrrrrsrcCs"|d}|d}|d}|||fS)z'Access the streambuf put area pointers.Z _M_out_begZ _M_out_curZ _M_in_endr) streambufpbasepptregptrrrraccess_streambuf_ptrs1src@s(eZdZdZddZddZddZdS) StdStringBufPrinterzPrint a std::basic_stringbuf.cCs ||_dS)N)rV)r_rWrrrrX<szStdStringBufPrinter.__init__cCsLt|j\}}}|rB| s"||kr2|j||dS|j||dS|jdS)N)rZ _M_string)rrVr)rrrrrrrrg?s zStdStringBufPrinter.to_stringcCsdS)Nrr)rrrrrIsz StdStringBufPrinter.display_hintN)r r r r rXrgrrrrrr9s rc@s(eZdZdZddZddZddZdS) StdStringStreamPrinterz Print a std::basic_stringstream.cCsZ||_||_dd|jjDd}tjd|j|jtjd|_ |j |djk|_ dS)NcSsg|]}|jr|jqSr)rr)r&frrrr(Xsz3StdStringStreamPrinter.__init__..rZ__streamz$__stream->rdbuf() _M_stringbuf) rVr]rrrZset_convenience_variablerbrsparse_and_eval _streambuf_was_redirected)rrDrWZbasetyperrrrXPs  zStdStringStreamPrinter.__init__cCs$|jrd|j|jjfS|jdS)Nz%s redirected to %sr)rr]rrtrV)rrrrrg]sz StdStringStreamPrinter.to_stringcCs|jr dSdS)Nr)r)rrrrrcsz#StdStringStreamPrinter.display_hintN)r r r r rXrgrrrrrrMs rc@s$eZdZddZddZddZdS)Tr1HashtableIteratorcCsj|d|_d|_|d|_t|jdj|_d|_x2|j|jkrd|j|j|_|jrVP|jd|_q4WdS)NZ _M_bucketsrZ_M_bucket_countZ_Noder))_buckets_bucket _bucket_countr%rra _node_typer)r hashtablerrrrXjs  zTr1HashtableIterator.__init__cCs|S)Nr)rrrrrYvszTr1HashtableIterator.__iter__cCs|jdkrt|jj|j}|jd}|jd|_|jdkr|jd|_x2|j|jkr~|j|j|_|jrpP|jd|_qNW|S)NrZ_M_vrzr))rrZrbrrtrrr)rrlrrrrrys    zTr1HashtableIterator.__next__N)r r r rXrYrrrrrris rc@s$eZdZddZddZddZdS)StdHashtableIteratorcCsR|dd|_|jjd}|jjdjd}tdt||r>dnd}|j|_dS) NZ_M_before_begin_M_nxtr) rzstd::__detail::_Hash_nodetrueZfalse)rrr4r2r!rar)rrr7cachedZ node_typerrrrXs   zStdHashtableIterator.__init__cCs|S)Nr)rrrrrYszStdHashtableIterator.__iter__cCsR|jdkrt|jj|jj}|d|_|dj}|j|jjdj}|jS)Nrrrr) rrZrbrrtrsrr4ra)rrvalptrrrrrs   zStdHashtableIterator.__next__N)r r r rXrYrrrrrrsrc@s<eZdZdZddZddZddZedd Zd d Z d S) Tr1UnorderedSetPrinterz1Print a std::unordered_set or tr1::unordered_set.cCst||_||_dS)N)r;r]rV)rrDrWrrrrXs zTr1UnorderedSetPrinter.__init__cCs|jjdr|jS|jdS)Nzstd::tr1_M_h)r]r<rV)rrrr _hashtables z!Tr1UnorderedSetPrinter._hashtablecCs|jd}d|jt|fS)N_M_element_countz %s with %s)rr]r)rrErrrrgs z Tr1UnorderedSetPrinter.to_stringcCsd|S)Nz[%d]r)irrr _format_countsz$Tr1UnorderedSetPrinter._format_countcCs@t|jtj}|jjdr.t|t|jSt|t |jS)Nzstd::tr1) r r itertoolsrEr]r<rrrr)rcounterrrrr_s zTr1UnorderedSetPrinter.childrenN) r r r r rXrrgrrr_rrrrrs  rc@s\eZdZdZddZddZddZedd Zed d Z ed d Z ddZ ddZ dS)Tr1UnorderedMapPrinterz1Print a std::unordered_map or tr1::unordered_map.cCst||_||_dS)N)r;r]rV)rrDrWrrrrXs zTr1UnorderedMapPrinter.__init__cCs|jjdr|jS|jdS)Nzstd::tr1r)r]r<rV)rrrrrs z!Tr1UnorderedMapPrinter._hashtablecCs|jd}d|jt|fS)Nrz %s with %s)rr]r)rrErrrrgs z Tr1UnorderedMapPrinter.to_stringccs&x |D]}x|D] }|VqWqWdS)Nr)rrrrrr_flattens  zTr1UnorderedMapPrinter._flattencCs|d|dfS)Nrrr)rrrr _format_onesz"Tr1UnorderedMapPrinter._format_onecCsd|S)Nz[%d]r)rrrrrsz$Tr1UnorderedMapPrinter._format_countcCsdt|jtj}|jjdr@|jt|jt|j }t ||S|jt|jt |j }t ||S)Nzstd::tr1) r rrrEr]r<r r rrrr)rr datarrrr_s  zTr1UnorderedMapPrinter.childrencCsdS)Nrr)rrrrrsz#Tr1UnorderedMapPrinter.display_hintN) r r r r rXrrgrr r rr_rrrrrr s    r c@s8eZdZdZGdddeZddZddZdd Zd S) StdForwardListPrinterzPrint a std::forward_list.c@s$eZdZddZddZddZdS)zStdForwardListPrinter._iteratorcCs||_|d|_d|_dS)Nrzr)r{r|r~)rrrrrrrXs z(StdForwardListPrinter._iterator.__init__cCs|S)Nr)rrrrrYsz(StdForwardListPrinter._iterator.__iter__cCsl|jdkrt|jj|jj}|d|_|j}|jd|_|dj}|j|jjdj }d||jfS)Nrrzr)rrz[%d]) r|rZrbr{rtr~rsrr4ra)rrrErrrrrs    z(StdForwardListPrinter._iterator.__next__N)r r r rXrYrrrrrrsrcCs||_t||_dS)N)rVr;r])rrDrWrrrrXszStdForwardListPrinter.__init__cCs(td|jjj}|j||jddS)Nrrr)r8rVrrar)rrrrrr_szStdForwardListPrinter.childrencCs*|jddddkr d|jSd|jS)Nrrrzrzempty %sz%s)rVr])rrrrrgs zStdForwardListPrinter.to_stringN) r r r r rrrXr_rgrrrrrs rc@sBeZdZdZd ddZddZGdddeZd d Zd d Z dS)SingleObjContainerPrinterz8Base class for printers of containers of single objects.NcCs||_||_||_dS)N)_contained_valuer_hint)rrWZvizZhintrrrrXsz"SingleObjContainerPrinter.__init__cCs(ts t|Stjjtjj|p&t|S)z5Return type as a string after applying type printers.)_use_type_printingr!rtypesapply_type_recognizersget_type_recognizers)rrrrr _recognizesz$SingleObjContainerPrinter._recognizec@s$eZdZddZddZddZdS)z$SingleObjContainerPrinter._containedcCs ||_dS)N)rV)rrWrrrrXsz-SingleObjContainerPrinter._contained.__init__cCs|S)Nr)rrrrrY"sz-SingleObjContainerPrinter._contained.__iter__cCs"|jdkrt|j}d|_d|fS)Nz[contained value])rVrZ)rZretvalrrrr%s  z-SingleObjContainerPrinter._contained.__next__N)r r r rXrYrrrrr _containedsrcCs6|jdkr|jdSt|jdr*|jjS|j|jS)Nr_)rrrrr_)rrrrr_,s     z"SingleObjContainerPrinter.childrencCs(t|jdr"t|jdr"|jjS|jS)Nr_r)rrrr)rrrrr3s   z&SingleObjContainerPrinter.display_hint)N) r r r r rXrrrr_rrrrrrs  rc CsH|jj}tjddkr"t|}nt|}ytj|jj SdSdS)z{Find the name of the function referred to by the gdb.Value f, which should contain a function pointer from the program.rrN) rtrsrFrGrrrZ block_for_pcfunctionr")rrrrfunction_pointer_to_name;s  rcs4eZdZdZfddZddZeddZZS)StdExpAnyPrinterz+Print a std::any or std::experimental::any.c st||_t|j|_||_d|_d}d}|jd}|dkrt|}|sXtd|jdj|}tj ||}|std|j|j d} d| kr g} xPt j D]D} y(tj d| |j d} | jtj| Wqtjk rYqXqWt| dkrtd | d} n tj| } | jd|_d}d | kr@|jd d j}n(d | krZ|jd d}ntd|j|j|jjj}tj|}tt |j||dS)NZ _M_managerrzInvalid function pointer in %szi({0}::_Manager_\w+<.*>)::_S_manage\((enum )?{0}::_Op, (const {0}|{0} const) ?\*, (union )?{0}::_Arg ?\*\)zUnknown manager function in %sr)z std::stringzstd::string(?!\w)z;Cannot uniquely determine std::string type used in std::anyz::_Manager_internalrrZ _M_bufferz::_Manager_externalr\)r;r]rKrV_contained_typerr r*r>r?groupr _string_typesrHrPrrr,rr4rsrbrartrsuperrX)rrDrWcontained_value visualizerZmgrfuncZrxmZmgrnameZmgrtypessr@Zmgrtyper) __class__rrrXVsX                 zStdExpAnyPrinter.__init__cCsT|jdkrd|jSd|j}t|jdr8||jjS|j|j}|tt|S)Nz%s [no contained value]z%s containing r_)rr]rrrgrr;r!)rdescr7rrrrgs     zStdExpAnyPrinter.to_stringcs@ttjdjh}ddddtg}|fdd|DO}|S)Nz std::stringzAbasic_string, std::allocator >zstd::zstd::__cxx11::csh|] }|qSrr)r&q)r#rr sz1StdExpAnyPrinter._string_types..)r!rrrr-)ZstringsZqualsr)r#rrs  zStdExpAnyPrinter._string_types) r r r r rXrgrr __classcell__rr)r$rrSs D rcs(eZdZdZfddZddZZS)StdExpOptionalPrinterz5Print a std::optional or std::experimental::optional.c st||_t|j|_|d}|jjdr8|d}|}n*|d}|d}y |d}Wn YnXtj|}|std}tt|j||dS)N _M_payloadzstd::experimental _M_engaged_M_value) r;r]rKr<rrrr)rX)rrDrWZpayloadZengagedrr )r$rrrXs"      zStdExpOptionalPrinter.__init__cCs:|jdkrd|jSt|jdr4d|j|jjfS|jS)Nz%s [no contained value]r_z%s containing %s)rr]rrrg)rrrrrgs    zStdExpOptionalPrinter.to_string)r r r r rXrgr(rr)r$rr)s r)cs(eZdZdZfddZddZZS)StdVariantPrinterzPrint a std::variant.cst|j}t||_|d|_|jt|krz[%s])rrrZrtr~r7r@r=)rrrEr?r0rrrrRs    z$StdExpPathPrinter._iterator.__next__N)r r r rXrYrrrrrrHsrcCs|j|jd|jS)Nr8)rrVr])rrrrr__szStdExpPathPrinter.childrenN) r r r r rXr=rgrrr_rrrrr7.s r7c@s@eZdZdZddZddZddZGdd d eZd d Z d S) StdPathPrinterzPrint a std::filesystem::path.cCsP||_||_t|jdd}|jtjdd@|_|jdkrF||_nd|_dS)Nr8rZ uintptr_tr)rVr]rprbrrr_impl)rrDrWrrrrrXfs zStdPathPrinter.__init__cCsHt|jjtj|jd}|dddkr0dS|d ddkrDdSdS) Nz::_Typerr:zroot-directoryr;r<z root-nameii)r!rrbrrr])rr0rrrr=ps zStdPathPrinter._path_typecCs8d|jd}|jdkr0|j}|r0d||f}d|S)Nz%sr>rz%s [%s]zfilesystem::path %s)rVrr=)rr?r0rrrrgxs   zStdPathPrinter.to_stringc@s$eZdZddZddZddZdS)zStdPathPrinter._iteratorc Cs||_|rtjd}tj|d}tjd}|j|j}|j}ttjdrbtd|j |j }n d|j }|j|j|}|j|j|_ |j ||_ d|_ n d|_ d|_ dS)Nrz::_Cmptcharalignofrr)r@rrrbrartrr:maxrrFrrr~) rrrAZint_typeZ cmpt_type char_typerZ sizeof_ImplbeginrrrrXs      z!StdPathPrinter._iterator.__init__cCs|S)Nr)rrrrrYsz!StdPathPrinter._iterator.__iter__cCsd|j|jkrt|jj}|j}|jd|_|jd|_|d}t|j|j}|sX|}d||fS)Nr)r>z[%s])rrrZrtr~rBr@r=)rrrEr?r0rrrrs    z!StdPathPrinter._iterator.__next__N)r r r rXrYrrrrrrsrcCs|j|j|jS)N)rrDr])rrrrr_szStdPathPrinter.childrenN) r r r r rXr=rgrrr_rrrrrBcs  (rBc@s8eZdZdZddZGdddeZddZdd Zd S) StdPairPrinterz@Print a std::pair object, with 'first' and 'second' as children.cCs ||_dS)N)rV)rrDrWrrrrXszStdPairPrinter.__init__c@s(eZdZdZddZddZddZdS) zStdPairPrinter._iterz?An iterator for std::pair types. Returns 'first' then 'second'.cCs||_d|_dS)Nr)rV_which)rrWrrrrXszStdPairPrinter._iter.__init__cCs|S)Nr)rrrrrYszStdPairPrinter._iter.__iter__cCs8|jdkrt|j}|dkr$d|_nd|_||j|fS)Nrr)rKrZrV)rZwhichrrrrs zStdPairPrinter._iter.__next__N)r r r r rXrYrrrrrrsrcCs |j|jS)N)rrV)rrrrr_szStdPairPrinter.childrencCsdS)Nr)rrrrrgszStdPairPrinter.to_stringN) r r r r rXrrr_rgrrrrrJs rJc@s eZdZdZddZddZdS)StdCmpCatPrinterz#Print a comparison category object.cCs&||jddd|_|d|_dS)N:r)r,)rfindr]rV)rrDrWrrrrXszStdCmpCatPrinter.__init__cCsJ|jdkr|jdkrd}n"ddd ddddd i}|t|j}d j|j|S) Nstrong_orderingrZequalrZ unorderedr)ZlessZ equivalentZgreaterz std::{}::{}ri)r]rVrr*)rr"namesrrrrgs zStdCmpCatPrinter.to_stringN)r r r r rXrgrrrrrLsrLc@s`eZdZdZdZddZeddZeddZ ed d Z ed d Z ed dZ ddZ dS)StdErrorCodePrinterz0Print a std::error_code or std::error_condition.Nc CsL||_t||_tjdkrHyddl}dt_Wntk rFdt_YnXdS)NrTF)rVr;r]rQ_system_is_posixposix ImportError)rrDrWrSrrrrXs   zStdErrorCodePrinter.__init__cCs&tj|}|dk r"|jtjkr"|SdS)N)rrrZTYPE_CODE_ENUM)r"r#rrr_find_errc_enums z#StdErrorCodePrinter._find_errc_enumc CsBxs(     zStdErrorCodePrinter.to_string)r r r r rRrXrrU classmethodrXr]rdrfrgrrrrrQs   + rQc@s eZdZdZddZddZdS)StdRegexStatePrinterz/Print a state node in the NFA for a std::regex.cCs||_||_dS)N)rVr])rrDrWrrrrX[szStdRegexStatePrinter.__init__cCst|jd}|r|dd}|jd}dddddddddddddd }||}d j||}|dk r|jd |dk rd j|||jd |}d |S) NZ _M_opcoderzZaltZsubexprnegZ backref_index) repeat alternativeZ subexpr_beginZ subexpr_endZline_begin_assertionZline_end_assertionZ word_boundaryZsubexpr_lookaheadZbackrefr?ZacceptZdummyunknownzopcode={}, next={}Z_M_z {}, {}={}z{%s})r!rVr*)rZopcodeZnext_idZvariantsvr#rrrrg_s     zStdRegexStatePrinter.to_stringN)r r r r rXrgrrrrrlXsrlc@s@eZdZdZGdddeZddZddZdd Zd d Z d S) StdSpanPrinterzPrint a std::span.c@s$eZdZddZddZddZdS)zStdSpanPrinter._iteratorcCsd|_||_||_dS)Nr)r~_beginr)rrIrrrrrXysz!StdSpanPrinter._iterator.__init__cCs|S)Nr)rrrrrY~sz!StdSpanPrinter._iterator.__iter__cCs8|j|jkrt|j}|jd|_d||j|jfS)Nr)z[%d])r~rrZrtrt)rrErrrrs   z!StdSpanPrinter._iterator.__next__N)r r r rXrYrrrrrrxsrcCsLt||_||_tjd}|jjd|kr:|dd|_n|jjd|_dS)Nzstatic_cast(-1)r)Z _M_extentZ_M_extent_value)r;r]rVrrrr4r)rrDrWZsize_maxrrrrXs   zStdSpanPrinter.__init__cCsd|j|jfS)Nz%s of length %d)r]r)rrrrrgszStdSpanPrinter.to_stringcCs|j|jd|jS)Nr\)rrVr)rrrrr_szStdSpanPrinter.childrencCsdS)Nrr)rrrrrszStdSpanPrinter.display_hintN) r r r r rrrXrgr_rrrrrrsus  rsc@s0eZdZdZddZddZddZdd Zd S) StdInitializerListPrinterzPrint a std::initializer_list.cCs||_||_|d|_dS)Nr6)r]rVr)rrDrWrrrrXsz"StdInitializerListPrinter.__init__cCsd|j|jfS)Nz%s of length %d)r]r)rrrrrgsz#StdInitializerListPrinter.to_stringcCstj|jd|jS)NZ_M_array)rsrrVr)rrrrr_sz"StdInitializerListPrinter.childrencCsdS)Nrr)rrrrrsz&StdInitializerListPrinter.display_hintN)r r r r rXrgr_rrrrrrus ruc@s(eZdZdZddZddZddZdS) StdAtomicPrinterzPrint a std:atomic.cCsrt||_||_d|_|jjjd|_|jjdk rnt|jj}|jdsR|jdrn|d}t |||_|j |_ dS)Nrzstd::shared_ptr = { %s }) rwrgr1rrZ TYPE_CODE_INTrV TYPE_CODE_FLT TYPE_CODE_PTRrr]r!)rrWrrrrgs     zStdAtomicPrinter.to_stringN)r r r r rXrxrgrrrrrvs rvc@s eZdZdZddZddZdS)StdFormatArgsPrinterzPrint a std::basic_format_args.cCst||_||_dS)N)r;r]rV)rrDrWrrrrXs zStdFormatArgsPrinter.__init__cCst|jj}t|dd}|tjdkr0d}n|tjdkrDd}nd}|jd}|dkrbd |S|dkrt|jd }d ||fS) Nrr)rEzstd::format_argswchar_tzstd::wformat_argszstd::basic_format_argsZ_M_packed_sizez%s with 1 argumentZ_M_unpacked_sizez%s with %d arguments)rTrVrrr)rZtargsrHr#rrrrrgs   zStdFormatArgsPrinter.to_stringN)r r r r rXrgrrrrr{sr{c@s0eZdZdZddZddZddZdd Zd S) StdChronoDurationPrinterzPrint a std::chrono::duration.cCst||_||_dS)N)r;r]rV)rrDrWrrrrXs z!StdChronoDurationPrinter.__init__cCs*|jjjd}|jd}|jd}||fS)Nr)r)rVrr4)rZperiodrdenrrr_ratios  zStdChronoDurationPrinter._ratiocCs|j\}}|dkrF|dkr dS|dkr,dS|dkr8dS|dkr|dSn6|dkr||d krZd S|d krfd S|d krrdSdj|Sdj||S)Nr)r#iZmsi@Busiʚ;rW<minihiQdz[{}]sz[{}/{}]s)rr*)rrr~rrr_suffixs&  z StdChronoDurationPrinter._suffixcCs4|jd}|jjjtjkr$d|}dj||jS)N__rz%gz"std::chrono::duration = {{ {}{} }})rVrrrrryr*r)rrrrrrgs z"StdChronoDurationPrinter.to_stringN)r r r r rXrrrgrrrrr}s r}c@s*eZdZdZddZddZd ddZd S) StdChronoTimePointPrinterz Print a std::chrono::time_point.cCst||_||_dS)N)r;r]rV)rrDrWrrrrXs z"StdChronoTimePointPrinter.__init__cCsv|jjjd}t|j}|dks(|dkr,dS|dkr8dS|dkrDdS|d krPdS|d kr\dS|dkrhdSdj|dfS)Nrzstd::chrono::_V2::system_clockzstd::chrono::system_clockstd::chrono::sys_timezstd::chrono::utc_clockstd::chrono::utc_timezstd::chrono::gps_clockstd::chrono::gps_timezstd::chrono::tai_clockstd::chrono::tai_timezstd::filesystem::__file_clockstd::chrono::file_timeumzstd::chrono::local_tstd::chrono::local_timez {} time_point)rr)rN)rN)rN)rr)rr)rVrr4r;r"r*)rclockr"rrr_clocks  z StdChronoTimePointPrinter._clockFc Cs|j\}}|jd}|d}t|jj|}|j}d}|dk r|j\} } || | |} ytjj| t } dj | }Wn YnXd|||f} |r| Sd|| fS)NZ__drrCz [{:%Y-%m-%d %H:%M:%S}]z%d%s%sz %s = { %s }) rrVr}rr"rrrZ fromtimestamp _utc_timezoner*)rZabbrevroffsetrrprintersuffixtimerr~Zsecsrr#rrrrg1s$   z#StdChronoTimePointPrinter.to_stringN)F)r r r r rXrrgrrrrrsrc@s eZdZdZddZddZdS)StdChronoZonedTimePrinterz Print a std::chrono::zoned_time.cCst||_||_dS)N)r;r]rV)rrDrWrrrrXIs z"StdChronoZonedTimePrinter.__init__cCs@|jdjd}|jd}t|jj|}|jd}dj||S)NZ_M_zone_M_nameZ_M_tpTz%std::chrono::zoned_time = {{ {} {} }})rVrtrrr"rgr*)rZzonerrrrrrgMs   z#StdChronoZonedTimePrinter.to_stringN)r r r r rXrgrrrrrFsrZJanuaryZFebruaryZMarchZAprilZMayZJuneZJulyZAugustZ SeptemberZOctoberZNovemberZDecemberZSundayZMondayZTuesdayZ WednesdayZThursdayZFridayZSaturdayc@s eZdZdZddZddZdS)StdChronoCalendarPrinterzDPrint a std::chrono::day, std::chrono::month, std::chrono::year etc.cCst||_||_dS)N)r;r]rV)rrDrWrrrrX_s z!StdChronoCalendarPrinter.__init__c Csv|j}|j}d|kr$|dkr$|d}|jdr6|d}|dkrPdjt|dS|d kr||d ksl|ttkrtd |St|S|dkrd j|S|d kr|d}|dks|ttkrd|Sdjt|S|dkrdj|dt|dS|dkrdj|dS|dkrdj||dS|dkr,dj|S|dkrFdj||dS|dkr`dj||dS|dkrvdj||S|dkrd j|||dS|dkrdj||d!S|d"krd j|||dS|d#krd j|||dS|jd$rrd%}|d&dkr"d'jt|d(d)t|d&}t|d*d+}t|dd+}t|d,d+}|d-rb| }d.j||||SdS)/Nmonthz std::chrono::year_month_day_lastZ_M_mzstd::chrono::yearZ_M_yzstd::chrono::dayz{}Z_M_dzstd::chrono::monthr)z%d is not a valid monthz{}yzstd::chrono::weekdayZ_M_wdrz%d is not a valid weekdayzstd::chrono::weekday_indexedz{}[{}]r.zstd::chrono::weekday_lastz{}[last]zstd::chrono::month_dayz{}/{}zstd::chrono::month_day_lastz{}/lastzstd::chrono::month_weekdayZ_M_wdizstd::chrono::month_weekday_lastZ_M_wdlzstd::chrono::year_monthzstd::chrono::year_month_dayz{}/{}/{}Z_M_mdlzstd::chrono::year_month_weekdayz$std::chrono::year_month_weekday_lastzstd::chrono::hh_mm_ssrCZfractional_widthz.{:0{}d}Z_M_ssZ_M_rrr_M_sZ _M_is_negz{:02}:{:02}:{:02}{})rVr]r<r*rrmonthsweekdays) rrWr#r"yZwdZfractrr#rrrrgcsh               z"StdChronoCalendarPrinter.to_stringN)r r r r rXrgrrrrr\src@s eZdZdZddZddZdS)StdChronoTimeZonePrinterz4Print a chrono::time_zone or chrono::time_zone_link.cCst||_||_dS)N)r;r]rV)rrDrWrrrrXs z!StdChronoTimeZonePrinter.__init__cCs6d|j|jdf}|jjdr2|d|jd7}|S)Nz%s = %srZ_linkz -> %sZ _M_target)r]rVrb)rr!rrrrgs z"StdChronoTimeZonePrinter.to_stringN)r r r r rXrgrrrrrsrc@s eZdZdZddZddZdS)StdChronoLeapSecondPrinterzPrint a chrono::leap_second.cCst||_||_dS)N)r;r]rV)rrDrWrrrrXs z#StdChronoLeapSecondPrinter.__init__cCs.|jdd}d|dk}d|jt||fS)Nrrz+-rz %s %d (%c))rVr]abs)rZdaternrrrrgs z$StdChronoLeapSecondPrinter.to_stringN)r r r r rXrgrrrrrsrc@s eZdZdZddZddZdS)StdChronoTzdbPrinterzPrint a chrono::tzdb.cCst||_||_dS)N)r;r]rV)rrDrWrrrrXs zStdChronoTzdbPrinter.__init__cCsd|j|jdfS)Nz%s %sversion)r]rV)rrrrrgszStdChronoTzdbPrinter.to_stringN)r r r r rXrgrrrrrsrc@s eZdZdZddZddZdS)StdChronoTimeZoneRulePrinterzPrint a chrono::time_zone rule.cCst||_||_dS)N)r;r]rV)rrDrWrrrrXs z%StdChronoTimeZoneRulePrinter.__init__c Cs|jd}|d}t|d}dddddddd}|d}d j||j|d }|d krdd j||}nVt|d }|dkrdj||}n4|dkrd} nd} |d}dj| d || d||}dj|jd|jd|jd|S)NonkindrstZndZrd)r)rrCZ day_of_monthz{}{}Zthrz{} {}Z day_of_weekr)z last {} in {}rr<=r>=z{} {} {} {} {}z.time_zone rule {} from {} to {} starting on {}r"fromZto)rr)rr)rVrr*getr) rrrrsuffixesdayZ ordinal_dayrweekday directionrrrrgs*    z&StdChronoTimeZoneRulePrinter.to_stringN)r r r r rXrgrrrrrsrc@s eZdZdZddZddZdS)StdLocalePrinterzPrint a std::locale.cCs||_||_dS)N)rVr])rrDrWrrrrXszStdLocalePrinter.__init__csx|jdd}d}|ddkr&d}nFtj|jd}tj|jd}|dj}|dj}dj||}||i}d } xN| |kr|| dkr|| j}|| j}d j|||}|||<| d } qxWt|j} t| d kr|}nt| d krl| \} } t |j} d| j | d kr$| }| n| j | d kr<| }| dk rlt fd d |j D}dj|}dj||S)NrZ_M_namesrCr*z::_S_categoriesz::_S_categories_sizez{}={}r)z{};{}={}rc3s|]\}}|kr|VqdS)Nr)r&rrR)otherrr sz-StdLocalePrinter.to_string..z with "{}={}"zstd::locale = "{}"{}) rVrrr]rr*r4valuesrrrEritems)rrPmodr"ZcatsZncatrRr[Z cat_namesrZ uniq_namesZn1Zn2Z name_listr)rrrgsD            zStdLocalePrinter.to_stringN)r r r r rXrgrrrrrsrc@s eZdZdZddZddZdS)StdIntegralConstantPrinterz*Print a std::true_type or std::false_type.cCs||_||_dS)N)rVr])rrDrWrrrrX sz#StdIntegralConstantPrinter.__init__cCsL|jjjd}|jjjd}|jtjkr4|r0dSdSt|j}dj|||S)Nrr)zstd::true_typezstd::false_typez {}<{}, {}>) rVrr4rrrr;r]r*)rr3rZrDrrrrg s  z$StdIntegralConstantPrinter.to_stringN)r r r r rXrgrrrrr src@s eZdZdZddZddZdS)StdTextEncodingPrinterzPrint a std::text_encoding.cCs||_||_dS)N)rVr])rrDrWrrrrX$ szStdTextEncodingPrinter.__init__cCs<|jdj}|ddkr$|jdS|ddkr4dS|dS)NZ_M_repZ_M_idr)rrrq)rVrt)rZreprrrrg( s    z StdTextEncodingPrinter.to_stringN)r r r r rXrgrrrrr! srcs$eZdZfddZddZZS) RxPrintercs$tt|j||_||_d|_dS)NT)rrrXr" _functionenabled)rr"r)r$rrrX3 szRxPrinter.__init__cCs:|js dS|jjtjkr,ttjdr,|j}|j|j |S)Nreferenced_value) rrrrrrValuerrr")rrZrrrinvoke9 s  zRxPrinter.invoke)r r r rXrr(rr)r$rr2 s rcsHeZdZfddZddZddZddZed d Zd d Z Z S) Printercs6tt|j||_g|_i|_d|_tjd|_ dS)NTz^([a-zA-Z0-9_:]+)(<.*>)?$) rrrXr" _subprinters_lookuprr>compile _compiled_rx)rr")r$rrrXH s zPrinter.__init__cCs<|jj|std|t||}|jj|||j|<dS)Nz0libstdc++ programming error: "%s" does not match)rr?r rrrPr)rr"rrrrraddP s     z Printer.addcCs>|j|||d|kr:tjddt|}|j|||dS)N__cxx11z^(std|__gnu_cxx)::z\g<0>%s)rr>rHr-)rbaser"rZvbaserrr add_version[ s  zPrinter.add_versioncCs$|j||||j|d||dS)Nz __cxx1998::)r)rrr"rrrr add_containerc szPrinter.add_containercCs&|jtjkr|j}|jj}|jS)N)rrrrrrr)rrrrget_basic_typeg s  zPrinter.get_basic_typecCsr|j|j}|sdS|jj|}|s(dS|jd}|jjtjkrTttj drT|j }||j krn|j |j |SdS)Nr)r) rrrr?rrrrrrrrr)rrWrDr?basenamerrr__call__r s     zPrinter.__call__) r r r rXrrrrrrr(rr)r$rrG s    rc@s0eZdZdZddZGdddeZddZdS) TemplateTypePrinteral A type printer for class templates with default template arguments. Recognizes specializations of class templates and prints them without any template arguments that use a default template argument. Type printers are recursively applied to the template arguments. e.g. replace 'std::vector >' with 'std::vector'. cCs||_||_d|_dS)NT)r"_defargsr)rr"defargsrrrrX szTemplateTypePrinter.__init__c@s(eZdZdZddZddZddZdS) zTemplateTypePrinter._recognizerz-The recognizer class for TemplateTypePrinter.cCs||_||_dS)N)r"r)rr"rrrrrX sz(TemplateTypePrinter._recognizer.__init__c Cs|jdkrdS|jj|js dSt|}g}d}xtt|D]}||}|jj|}|dk r|j|}y|t j |kr|dSWn$t j k r|j|krdSYnXd}q>|rdS|j |j |q>W|jd|jjd}t|}|ddj|dS)a If type_obj is a specialization of self.name that uses all the default template arguments for the class template, then return a string representation of the type without default arguments. Otherwise, return None. NFTr)rr<r"rTrangerrrr*rrr,rP_recognize_subtyperrOr+) rrQrSZdisplayed_argsZrequire_defaultedrRrfZdefargrArrr recognize s2     z)TemplateTypePrinter._recognizer.recognizecCs|jtjkr|j|jdS|jtjkrj|j|j}t|jjdrR|dSd||j ddfS|jtj kr|j|jdSt tdr|jtj kr|j|jdStj jtj j|}|r|St|S)zxConvert a gdb.Type to a string by applying recognizers, or if that fails then simply converting to a string.rz[]z%s[%d]r)&TYPE_CODE_RVALUE_REFz&&)rrrzrrrr!rrbrrrrrrr)rrQrNrrrr s"     z2TemplateTypePrinter._recognizer._recognize_subtypeN)r r r r rXrrrrrr _recognizer s1rcCs|j|j|jS)z1Return a recognizer object for this type printer.)rr"r)rrrr instantiate szTemplateTypePrinter.instantiateN)r r r r rXobjectrrrrrrr s Prcstd||}tjj||td||}tjj||d|krdttfdd|jD}t||}tjj||td||}tjj||dS)a Add a type printer for a class template with default template arguments. Args: name (str): The template-name of the class template. defargs (dict int:string) The default template arguments. Types in defargs can refer to the Nth template-argument using {N} (with zero-based indices). e.g. 'unordered_map' has these defargs: { 2: 'std::hash<{0}>', 3: 'std::equal_to<{0}>', 4: 'std::allocator >' } zstd::zstd::__debug::rc3s"|]\}}||jdfVqdS)zstd::N)r.)r&rRr)rWrrr sz0add_one_template_type_printer..N)rrrregister_type_printerr-dictr)objr"rrr)rWradd_one_template_type_printer s rc@s2eZdZdZd ddZGdddeZddZdS) FilteringTypePrintera A type printer that uses typedef names for common template specializations. Args: template (str): The class template to recognize. name (str): The typedef-name that will be used instead. targ1 (str, optional): The first template argument. Defaults to None. Checks if a specialization of the class template 'template' is the same type as the typedef 'name', and prints it as 'name' instead. e.g. if an instantiation of std::basic_istream is the same type as std::istream then print it as std::istream. If targ1 is provided (not None), match only template specializations with this type as the first template argument, e.g. if template='basic_string' and targ1='char' then only match 'basic_string' and not 'basic_string'. This rejects non-matching specializations more quickly, without needing to do GDB type lookups. NcCs||_||_||_d|_dS)NT) _templater"_targ1r)rtemplater"targ1rrrrX- szFilteringTypePrinter.__init__c@s eZdZdZddZddZdS)z FilteringTypePrinter._recognizerz.The recognizer class for FilteringTypePrinter.cCs||_||_||_d|_dS)N)rr"r _type_obj)rrr"rrrrrX6 sz)FilteringTypePrinter._recognizer.__init__c Cs|jdkrdS|jdkrz|jdk rDdj|j|j}|jj|sVdSn|jj|jsVdSytj|jj |_Wn YnX|jdkrdStj j |j}tj j |}||krt |jS|jj dddkr|jjjdd}|jjdd}||krt |jSdS) z If type_obj starts with self._template and is the same type as self.name then return self.name, otherwise None. Nz{}<{}z::r) basic_stringz __cxx11::rCri)rrrr*rr<rrr"rrrrOrer.)rrQr#Zt1Zt2s1s2rrrr< s4        z*FilteringTypePrinter._recognizer.recognizeN)r r r r rXrrrrrr3 srcCs|j|j|j|jS)z1Return a recognizer object for this type printer.)rrr"r)rrrrrf sz FilteringTypePrinter.instantiate)N)r r r r rXrrrrrrrr s 3rcCsXtd|d||}tjj||d|krTdt}t|||||}tjj||dS)Nzstd::r)rrrrr-)rrr"rrrWrrradd_one_type_printerk s rc CsdtsdSxxdgD]p}t|d |d d |dt|d|d d |dt|dd|d d |dt|d|d d|dqWxdjD]|}x*dkD]"}t|d||d ||dqWxJdlD]B}t|d||d ||dt|d!||d ||dqWqWxdmD]}x0dpD](}t||d"||d d#|dqWxNdqD]F}t||d(||d)x&drD]}t||||||qlWqJWqWt|d-d.xdsD]}t|d9d:|qWt|d;d<t|d;d=t|d>d?t|d>d@t|dAdBt|dAdCt|dDdEt|dDdFt|dGdHdI}x0dyD](}t||d||d d|dqdequezstd::allocator<{0}>rrz __cxx11::listvectorrzstd::less<{0}>z)std::allocator>)rrCmultimapr4)r)rmultiset unordered_mapzstd::hash<{0}>zstd::equal_to<{0}>)rrCunordered_multimap unordered_set)r)rrCunordered_multisetrCrErr|rrrrrr)rrrrrrCrErr|)rr) rrrrrrrrr)rrrr)rCrrCrErr|)rr )rr#rr)rrr) rrrrr r r r r rrCrErr|rrrrrr)r!r"r#r$r%)rrr)rZchr@abiZdurrWrrrregister_type_printerst s "(     *                   r'cCs8trtjj|tn|dkr t}|jjtt|dS)z4Register libstdc++ pretty-printers with objfile Obj.N) _use_gdb_pprZprintingZregister_pretty_printerlibstdcxx_printerZpretty_printersrPr')rrrrregister_libstdcxx_printers s  r*cCsXtdatjddttjddttjddttjddttjddttjddttjddttjdd ttjdd t tjdd t tjdd t tjdd t tjddt tjddt tjddt tjddt tjddttjddtttjdr.tjddttjddttjdttjdttjdttjdttjdttjdt tjdt tjdttjddttjdd ttjdd!ttjdd"ttjdd#ttjdd$ttjdd%ttjd&dttjd&d ttjd&d!ttjd&d"ttjd&d#ttjd&d$ttjdd'ttjdd(ttjdd)ttjdd)tx2doD]*}tjdd-|ttjdd-|tqWtjd.d/ttjd.d0ttjdd1ttjd2d3t tjd4ttjd5ttjd6ttjd7ttjd8ttjd9d:t!tjd9d;t"tjd9dt$tjd?d>t$tjd@d>t%tjdAd>t%tjdd:t!tjdd;t"tjddrFrirrGrrrr ziprrrZtimezoneZutcrZtzinforr(Z gdb.printingrTrZ gdb.typesrrrZ printer_baser%r-r2r8r5rBr;rKrOrTrUr[rkrmrprqrurxryrrrrrrrrrrrrrrrrrrrrrrrrrrrrr rrrrr)r-r0r5r7rBrJrLrQrlrsrurvr{r}rrrrrrrrrrrrrrr)rrrrr'r*r;rrrrs      %   (&"I  U(*$/A " /%-^!,5I!{'$*/ ?  ",Ce%T oTlibstdcxx/v6/__pycache__/xmethods.cpython-36.pyc000064400000107346152525737710015625 0ustar003 hq@sddlZddlZddlZdZddZddZdZdd ZGd d d ejj Z Gd d d ejj Z Gddde Z Gddde ZGddde ZGddde ZGddde ZGddde ZGdddejjZGdddejj ZGdddeZGd d!d!eZGd"d#d#eZGd$d%d%eZGd&d'd'eZGd(d)d)eZGd*d+d+ejjZGd,d-d-ejjZGd.d/d/eZGd0d1d1eZGd2d3d3ejjZ Gd4d5d5ejj Z!Gd6d7d7e!Z"Gd8d9d9e!Z#Gd:d;d;e!Z$Gdd?d?ejjZ&Gd@dAdAejj Z'GdBdCdCe'Z(GdDdEdEe'Z)GdFdGdGe'Z*GdHdIdIe'Z+GdJdKdKe'Z,GdLdMdMe'Z-GdNdOdOejjZ.GdPdQdQejj Z/GdRdSdSe/Z0GdTdUdUe/Z1GdVdWdWejjZ2GdXdYdYejj Z3GdZd[d[e3Z4Gd\d]d]e3Z5Gd^d_d_ejjZ6Gd`dadaejj Z7Gdbdcdce7Z8Gdddedee7Z9Gdfdgdgejj Z:Gdhdidie:Z;GdjdkdkejjZ$N) isinstancerZTypetag_versioned_namespacerematch)xZ template_namerrris_specialization_of!s  rc@seZdZddZdS)LibStdCxxXMethodcCstjjj||||_dS)N)rxmethodXMethod__init__ worker_class)selfnamerrrrr.szLibStdCxxXMethod.__init__N)__name__ __module__ __qualname__rrrrrr-src@seZdZddZddZdS)ArrayWorkerBasecCs||_||_dS)N) _val_type_size)rval_typesizerrrr6szArrayWorkerBase.__init__cCstjd}|j|jjjS)Nz (void *) 0)rZparse_and_evalcastrpointer dereference)rZnullptrrrr null_value:s zArrayWorkerBase.null_valueN)rrrrr"rrrrr5src@s,eZdZddZddZddZddZd S) ArraySizeWorkercCstj|||dS)N)rr)rrrrrrr@szArraySizeWorker.__init__cCsdS)Nr)rrrr get_arg_typesCszArraySizeWorker.get_arg_typescCstS)N)r)robjrrrget_result_typeFszArraySizeWorker.get_result_typecCs|jS)N)r)rr%rrr__call__IszArraySizeWorker.__call__N)rrrrr$r&r'rrrrr#?sr#c@s,eZdZddZddZddZddZd S) ArrayEmptyWorkercCstj|||dS)N)rr)rrrrrrrNszArrayEmptyWorker.__init__cCsdS)Nr)rrrrr$QszArrayEmptyWorker.get_arg_typescCstS)N)r)rr%rrrr&Tsz ArrayEmptyWorker.get_result_typecCst|jdkS)Nr)intr)rr%rrrr'WszArrayEmptyWorker.__call__N)rrrrr$r&r'rrrrr(Msr(c@s,eZdZddZddZddZddZd S) ArrayFrontWorkercCstj|||dS)N)rr)rrrrrrr\szArrayFrontWorker.__init__cCsdS)Nr)rrrrr$_szArrayFrontWorker.get_arg_typescCs|jS)N)r)rr%rrrr&bsz ArrayFrontWorker.get_result_typecCs&t|jdkr|ddS|jSdS)Nr_M_elems)r)rr")rr%rrrr'es zArrayFrontWorker.__call__N)rrrrr$r&r'rrrrr*[sr*c@s,eZdZddZddZddZddZd S) ArrayBackWorkercCstj|||dS)N)rr)rrrrrrrmszArrayBackWorker.__init__cCsdS)Nr)rrrrr$pszArrayBackWorker.get_arg_typescCs|jS)N)r)rr%rrrr&sszArrayBackWorker.get_result_typecCs,t|jdkr |d|jdS|jSdS)Nrr+)r)rr")rr%rrrr'vszArrayBackWorker.__call__N)rrrrr$r&r'rrrrr,lsr,c@s,eZdZddZddZddZddZd S) ArrayAtWorkercCstj|||dS)N)rr)rrrrrrr~szArrayAtWorker.__init__cCstS)N)r)rrrrr$szArrayAtWorker.get_arg_typescCs|jS)N)r)rr%indexrrrr&szArrayAtWorker.get_result_typecCs4t|t|jkr(tdt||jf|d|S)Nz%Array index "%d" should not be >= %d.r+)r)r IndexError)rr%r/rrrr'szArrayAtWorker.__call__N)rrrrr$r&r'rrrrr.}sr.c@s,eZdZddZddZddZddZd S) ArraySubscriptWorkercCstj|||dS)N)rr)rrrrrrrszArraySubscriptWorker.__init__cCstS)N)r)rrrrr$sz"ArraySubscriptWorker.get_arg_typescCs|jS)N)r)rr%r/rrrr&sz$ArraySubscriptWorker.get_result_typecCs&t|jdkr|d|S|jSdS)Nrr+)r)rr")rr%r/rrrr's zArraySubscriptWorker.__call__N)rrrrr$r&r'rrrrr1sr1c@seZdZddZddZdS)ArrayMethodsMatchercsftjjjtdtdttdttdttdt tdt tdt d_ fd d j D_ dS) Narrayremptyfrontbackatz operator[])rr4r5r6r7z operator[]csg|]}j|qSr) _method_dict).0m)rrr sz0ArrayMethodsMatcher.__init__..)rrXMethodMatcherrmatcher_name_prefixrr#r(r*r,r.r1r8methods)rr)rrrs  zArrayMethodsMatcher.__init__c Cs^t|dsdS|jj|}|dks*|j r.dSy|jd}|jd}Wn dS|j||S)Nr3rr-)rr8getenabledtemplate_argumentr)r class_type method_namemethodZ value_typerrrrr s   zArrayMethodsMatcher.matchN)rrrrr rrrrr2s r2c@s$eZdZddZddZddZdS)DequeWorkerBasecCs||_d|jpd|_dS)Nir-)rsizeof_bufsize)rrrrrrszDequeWorkerBase.__init__cCsf|dd}|dd}|d|dkr,dS|j|d|dd|d|d|d |dS) N_M_impl_M_start _M_finish_M_curr_M_noder-_M_first_M_last)rG)rr%startfinishrrrrs   zDequeWorkerBase.sizecCs`|dd}|d|d}||kr0|d|S||}|ddt||j}|||jS)NrHrIrNrKrLr-)r)rG)rr%idxrOZfirst_node_sizeZ index_noderrrr/s  zDequeWorkerBase.indexN)rrrrrr/rrrrrEs rEc@s$eZdZddZddZddZdS)DequeEmptyWorkercCsdS)Nr)rrrrr$szDequeEmptyWorker.get_arg_typescCstS)N)r)rr%rrrr&sz DequeEmptyWorker.get_result_typecCs |ddd|dddkS)NrHrIrKrJr)rr%rrrr'szDequeEmptyWorker.__call__N)rrrr$r&r'rrrrrRsrRc@s$eZdZddZddZddZdS)DequeSizeWorkercCsdS)Nr)rrrrr$szDequeSizeWorker.get_arg_typescCstS)N)r)rr%rrrr&szDequeSizeWorker.get_result_typecCs |j|S)N)r)rr%rrrr'szDequeSizeWorker.__call__N)rrrr$r&r'rrrrrSsrSc@s$eZdZddZddZddZdS)DequeFrontWorkercCsdS)Nr)rrrrr$szDequeFrontWorker.get_arg_typescCs|jS)N)r)rr%rrrr&sz DequeFrontWorker.get_result_typecCs|ddddS)NrHrIrKrr)rr%rrrr'szDequeFrontWorker.__call__N)rrrr$r&r'rrrrrTsrTc@s$eZdZddZddZddZdS)DequeBackWorkercCsdS)Nr)rrrrr$szDequeBackWorker.get_arg_typescCs|jS)N)r)rr%rrrr&szDequeBackWorker.get_result_typecCs^|ddd|dddkrF|dddd}|d|jdS|ddddSdS) NrHrJrKrMrLr-r)rG)rr% prev_noderrrr's zDequeBackWorker.__call__N)rrrr$r&r'rrrrrUsrUc@s$eZdZddZddZddZdS)DequeSubscriptWorkercCstS)N)r)rrrrr$ sz"DequeSubscriptWorker.get_arg_typescCs|jS)N)r)rr% subscriptrrrr& sz$DequeSubscriptWorker.get_result_typecCs |j||S)N)r/)rr%rYrrrr'szDequeSubscriptWorker.__call__N)rrrr$r&r'rrrrrX srXc@s$eZdZddZddZddZdS) DequeAtWorkercCstS)N)r)rrrrr$szDequeAtWorker.get_arg_typescCs|jS)N)r)rr%r/rrrr&szDequeAtWorker.get_result_typecCs@t|j|}t||kr0tdt||fn |j||SdS)Nz%Deque index "%d" should not be >= %d.)r)rr0r/)rr%r/Z deque_sizerrrr's  zDequeAtWorker.__call__N)rrrr$r&r'rrrrrZsrZc@seZdZddZddZdS)DequeMethodsMatchercsftjjjtdtdttdttdttdt tdt tdt d_ fd d j D_ dS) Ndequer4rr5r6z operator[]r7)r4rr5r6z operator[]r7csg|]}j|qSr)r8)r9r:)rrrr;0sz0DequeMethodsMatcher.__init__..)rrr<rr=rrRrSrTrUrXrZr8r>)rr)rrr%s  zDequeMethodsMatcher.__init__cCs>t|dsdS|jj|}|dks*|j r.dS|j|jdS)Nr\r)rr8r?r@rrA)rrBrCrDrrrr 2s   zDequeMethodsMatcher.matchN)rrrrr rrrrr[$s r[c@seZdZddZddZdS)ForwardListWorkerBasecCs||_||_dS)N)r _node_type)rr node_typerrrr>szForwardListWorkerBase.__init__cCsdS)Nr)rrrrr$Bsz#ForwardListWorkerBase.get_arg_typesN)rrrrr$rrrrr]=sr]c@seZdZddZddZdS)ForwardListEmptyWorkercCstS)N)r)rr%rrrr&Gsz&ForwardListEmptyWorker.get_result_typecCs|ddddkS)NrH_M_head_M_nextrr)rr%rrrr'JszForwardListEmptyWorker.__call__N)rrrr&r'rrrrr`Fsr`c@seZdZddZddZdS)ForwardListFrontWorkercCs|jS)N)r)rr%rrrr&Osz&ForwardListFrontWorker.get_result_typecCs:|dddj|j}|ddj}|j|jjjS)NrHrarb _M_storage)rr^addressrr r!)rr%nodeZ val_addressrrrr'RszForwardListFrontWorker.__call__N)rrrr&r'rrrrrcNsrcc@seZdZddZddZdS)ForwardListMethodsMatchercsJtd}tjjj|tdttdtd_fddjD_ dS)N forward_listr4r5)r4r5csg|]}j|qSr)r8)r9r:)rrrr;`sz6ForwardListMethodsMatcher.__init__..) r=rrr<rrr`rcr8r>)rZ matcher_namer)rrrYs z"ForwardListMethodsMatcher.__init__cCsZt|dsdS|jj|}|dks*|j r.dS|jd}tjt|dj}|j ||S)Nrhrz::_Node) rr8r?r@rArrstrr r)rrBrCrDrr_rrrr bs   zForwardListMethodsMatcher.matchN)rrrrr rrrrrgXs rgc@s$eZdZddZddZddZdS)ListWorkerBasecCs||_||_dS)N)rr^)rrr_rrrrpszListWorkerBase.__init__cCsdS)Nr)rrrrr$tszListWorkerBase.get_arg_typescCsB|j}|jjdjdkr$|dS|dj}|j|jjjS)Nr-Z_M_datard)r!typefieldsrrerrr )rrfZaddrrrrget_value_from_nodews  z"ListWorkerBase.get_value_from_nodeN)rrrrr$rmrrrrrjosrjc@seZdZddZddZdS)ListEmptyWorkercCstS)N)r)rr%rrrr&szListEmptyWorker.get_result_typecCs&|dd}|d|jkrdSdSdS)NrHrLrbTF)re)rr%Z base_noderrrr's zListEmptyWorker.__call__N)rrrr&r'rrrrrnsrnc@seZdZddZddZdS)ListSizeWorkercCstS)N)r)rr%rrrr&szListSizeWorker.get_result_typecCsD|ddd}|ddj}d}x||kr>|d}|d7}q$W|S)NrHrLrbrr-)re)rr%Z begin_nodeZend_noderrrrr's  zListSizeWorker.__call__N)rrrr&r'rrrrrosroc@seZdZddZddZdS)ListFrontWorkercCs|jS)N)r)rr%rrrr&szListFrontWorker.get_result_typecCs"|dddj|j}|j|S)NrHrLrb)rr^rm)rr%rfrrrr'szListFrontWorker.__call__N)rrrr&r'rrrrrpsrpc@seZdZddZddZdS)ListBackWorkercCs|jS)N)r)rr%rrrr&szListBackWorker.get_result_typecCs"|dddj|j}|j|S)NrHrLZ_M_prev)rr^rm)rr%rWrrrr'szListBackWorker.__call__N)rrrr&r'rrrrrqsrqc@seZdZddZddZdS)ListMethodsMatchercsVtjjjtdtdttdttdttdt d_ fddj D_ dS) Nlistr4rr5r6)r4rr5r6csg|]}j|qSr)r8)r9r:)rrrr;sz/ListMethodsMatcher.__init__..) rrr<rr=rrnrorprqr8r>)rr)rrrs  zListMethodsMatcher.__init__cCsZt|dsdS|jj|}|dks*|j r.dS|jd}tjt|dj}|j ||S)Nz(__cxx11::)?listrz::_Node) rr8r?r@rArrrir r)rrBrCrDrr_rrrr s   zListMethodsMatcher.matchN)rrrrr rrrrrrs rrc@s$eZdZddZddZddZdS)VectorWorkerBasecCs ||_dS)N)r)rrrrrrszVectorWorkerBase.__init__cCsz|jjtjkr^|ddd}|ddd}|ddd}|jjjd}||||S|dd|ddSdS)NrHrI_M_prJZ _M_offset)rcoderTYPE_CODE_BOOLr!rkrF)rr%rOrPZ finish_offsetbit_sizerrrrszVectorWorkerBase.sizecCsj|jjtjkrV|ddd}|jjjd}|||}||}|jd|>@dkS|dd|SdS)NrHrIrurvr-r)rrwrrxr!rkrF)rr%r/rOryZvalpoffsetrrrr?s zVectorWorkerBase.getN)rrrrrr?rrrrrts rtc@s$eZdZddZddZddZdS)VectorEmptyWorkercCsdS)Nr)rrrrr$szVectorEmptyWorker.get_arg_typescCstS)N)r)rr%rrrr&sz!VectorEmptyWorker.get_result_typecCst|j|dkS)Nr)r)r)rr%rrrr'szVectorEmptyWorker.__call__N)rrrr$r&r'rrrrr{sr{c@s$eZdZddZddZddZdS)VectorSizeWorkercCsdS)Nr)rrrrr$szVectorSizeWorker.get_arg_typescCstS)N)r)rr%rrrr&sz VectorSizeWorker.get_result_typecCs |j|S)N)r)rr%rrrr'szVectorSizeWorker.__call__N)rrrr$r&r'rrrrr|sr|c@s$eZdZddZddZddZdS)VectorFrontWorkercCsdS)Nr)rrrrr$szVectorFrontWorker.get_arg_typescCs|jS)N)r)rr%rrrr&sz!VectorFrontWorker.get_result_typecCs |j|dS)Nr)r?)rr%rrrr'szVectorFrontWorker.__call__N)rrrr$r&r'rrrrr}sr}c@s$eZdZddZddZddZdS)VectorBackWorkercCsdS)Nr)rrrrr$szVectorBackWorker.get_arg_typescCs|jS)N)r)rr%rrrr&sz VectorBackWorker.get_result_typecCs|j|t|j|dS)Nr-)r?r)r)rr%rrrr'szVectorBackWorker.__call__N)rrrr$r&r'rrrrr~sr~c@s$eZdZddZddZddZdS)VectorAtWorkercCstS)N)r)rrrrr$ szVectorAtWorker.get_arg_typescCs|jS)N)r)rr%r/rrrr&szVectorAtWorker.get_result_typecCs>t|j|}t||kr.tdt||f|j|t|S)Nz&Vector index "%d" should not be >= %d.)r)rr0r?)rr%r/rrrrr's  zVectorAtWorker.__call__N)rrrr$r&r'rrrrr src@s$eZdZddZddZddZdS)VectorSubscriptWorkercCstS)N)r)rrrrr$sz#VectorSubscriptWorker.get_arg_typescCs|jS)N)r)rr%rYrrrr&sz%VectorSubscriptWorker.get_result_typecCs|j|t|S)N)r?r))rr%rYrrrr'!szVectorSubscriptWorker.__call__N)rrrr$r&r'rrrrrsrc@seZdZddZddZdS)VectorMethodsMatchercsftjjjtdtdttdttdttdt tdt tdt d_ fd d j D_ dS) Nvectorrr4r5r6r7z operator[])rr4r5r6r7z operator[]csg|]}j|qSr)r8)r9r:)rrrr;2sz1VectorMethodsMatcher.__init__..)rrr<rr=rr|r{r}r~rrr8r>)rr)rrr&s   zVectorMethodsMatcher.__init__cCs>t|dsdS|jj|}|dks*|j r.dS|j|jdS)Nrr)rr8r?r@rrA)rrBrCrDrrrr 4s   zVectorMethodsMatcher.matchN)rrrrr rrrrr%src@s$eZdZddZddZddZdS)AssociativeContainerWorkerBasecCs ||_dS)N) _unordered)r unorderedrrrr@sz'AssociativeContainerWorkerBase.__init__cCs&|jr|ddS|dddSdS)NZ_M_hZ_M_element_count_M_trHZ _M_node_count)r)rr%rrr node_countCs z)AssociativeContainerWorkerBase.node_countcCsdS)Nr)rrrrr$Isz,AssociativeContainerWorkerBase.get_arg_typesN)rrrrrr$rrrrr?src@seZdZddZddZdS)AssociativeContainerEmptyWorkercCstS)N)r)rr%rrrr&Nsz/AssociativeContainerEmptyWorker.get_result_typecCst|j|dkS)Nr)r)r)rr%rrrr'Qsz(AssociativeContainerEmptyWorker.__call__N)rrrr&r'rrrrrMsrc@seZdZddZddZdS)AssociativeContainerSizeWorkercCstS)N)r)rr%rrrr&Vsz.AssociativeContainerSizeWorker.get_result_typecCs |j|S)N)r)rr%rrrr'Ysz'AssociativeContainerSizeWorker.__call__N)rrrr&r'rrrrrUsrc@seZdZddZddZdS)"AssociativeContainerMethodsMatchercsLtjjjt||_tdttdtd_ fddj D_ dS)Nrr4)rr4csg|]}j|qSr)r8)r9r:)rrrr;gsz?AssociativeContainerMethodsMatcher.__init__..) rrr<rr=_namerrrr8r>)rrr)rrr^s   z+AssociativeContainerMethodsMatcher.__init__cCsDt||jsdS|jj|}|dks,|j r0dSd|jk}|j|S)Nr)rrr8r?r@r)rrBrCrDrrrrr is   z(AssociativeContainerMethodsMatcher.matchN)rrrrr rrrrr]s rc@s8eZdZdZddZddZddZdd Zd d Zd S) UniquePtrGetWorkerzS Implement std::unique_ptr::get() and std::unique_ptr::operator->(). cCs*|jtjk|_|jr |j|_n||_dS)N)rwrTYPE_CODE_ARRAY _is_arraytarget _elem_type)r elem_typerrrrzs zUniquePtrGetWorker.__init__cCsdS)Nr)rrrrr$sz UniquePtrGetWorker.get_arg_typescCs |jjS)N)rr )rr%rrrr&sz"UniquePtrGetWorker.get_result_typecCs|dkp|j S)Nr?)r)rrCrrr _supportsszUniquePtrGetWorker._supportscCs|jjjdjj}t|dr.|dd}nt|drB|d}ndS|jjdj}|jdj}|jd}|jdkr|j|dS|jr|j|jSdSdS)Nrz__uniq_ptr_(data|impl)rtupler-Z _M_head_impl)r!rkrlr rrrZ is_base_class)rr%Z impl_typeZ tuple_memberZtuple_impl_typeZtuple_head_typeZ head_fieldrrrr's      zUniquePtrGetWorker.__call__N) rrr__doc__rr$r&rr'rrrrrus rc@s0eZdZdZddZddZddZdd Zd S) UniquePtrDerefWorkerz*Implement std::unique_ptr::operator*().cCstj||dS)N)rr)rrrrrrszUniquePtrDerefWorker.__init__cCs|jS)N)r)rr%rrrr&sz$UniquePtrDerefWorker.get_result_typecCs|j S)N)r)rrCrrrrszUniquePtrDerefWorker._supportscCstj||jS)N)rr'r!)rr%rrrr'szUniquePtrDerefWorker.__call__N)rrrrrr&rr'rrrrrs rc@s8eZdZdZddZddZddZdd Zd d Zd S) UniquePtrSubscriptWorkerz1Implement std::unique_ptr::operator[](size_t).cCstj||dS)N)rr)rrrrrrsz!UniquePtrSubscriptWorker.__init__cCstS)N)r)rrrrr$sz&UniquePtrSubscriptWorker.get_arg_typescCs|jS)N)r)rr%r/rrrr&sz(UniquePtrSubscriptWorker.get_result_typecCs|jS)N)r)rrCrrrrsz"UniquePtrSubscriptWorker._supportscCstj|||S)N)rr')rr%r/rrrr'sz!UniquePtrSubscriptWorker.__call__N) rrrrrr$r&rr'rrrrrs rc@seZdZddZddZdS)UniquePtrMethodsMatchercsVtjjjtdtdttdttdttdtd_ fddj D_ dS) N unique_ptrr?z operator->z operator*z operator[])r?z operator->z operator*z operator[]csg|]}j|qSr)r8)r9r:)rrrr;sz4UniquePtrMethodsMatcher.__init__..) rrr<rr=rrrrr8r>)rr)rrrs  z UniquePtrMethodsMatcher.__init__cCsPt|dsdS|jj|}|dks*|j r.dS|j|jd}|j|rL|SdS)Nrr)rr8r?r@rrAr)rrBrCrDworkerrrrr s   zUniquePtrMethodsMatcher.matchN)rrrrr rrrrrs rc@s8eZdZdZddZddZddZdd Zd d Zd S) SharedPtrGetWorkerzT Implements std::shared_ptr::get() and std::shared_ptr::operator->(). cCs*|jtjk|_|jr |j|_n||_dS)N)rwrrrrr)rrrrrrs zSharedPtrGetWorker.__init__cCsdS)Nr)rrrrr$sz SharedPtrGetWorker.get_arg_typescCs |jjS)N)rr )rr%rrrr&sz"SharedPtrGetWorker.get_result_typecCs|dkp|j S)Nr?)r)rrCrrrrszSharedPtrGetWorker._supportscCs|dS)NZ_M_ptrr)rr%rrrr'szSharedPtrGetWorker.__call__N) rrrrrr$r&rr'rrrrrs rc@s0eZdZdZddZddZddZdd Zd S) SharedPtrDerefWorkerz*Implement std::shared_ptr::operator*().cCstj||dS)N)rr)rrrrrrszSharedPtrDerefWorker.__init__cCs|jS)N)r)rr%rrrr&sz$SharedPtrDerefWorker.get_result_typecCs|j S)N)r)rrCrrrrszSharedPtrDerefWorker._supportscCstj||jS)N)rr'r!)rr%rrrr'szSharedPtrDerefWorker.__call__N)rrrrrr&rr'rrrrrs rc@s8eZdZdZddZddZddZdd Zd d Zd S) SharedPtrSubscriptWorkerz1Implement std::shared_ptr::operator[](size_t).cCstj||dS)N)rr)rrrrrr sz!SharedPtrSubscriptWorker.__init__cCstS)N)r)rrrrr$sz&SharedPtrSubscriptWorker.get_arg_typescCs|jS)N)r)rr%r/rrrr&sz(SharedPtrSubscriptWorker.get_result_typecCs|jS)N)r)rrCrrrrsz"SharedPtrSubscriptWorker._supportscCsZtjdt|j}|rJ|t|jdkrJtd|jt|t|jdftj|||S)Nz .*\[(\d+)]$r-z.shared_ptr<%s> index "%d" should not be >= %d.) r r rirr)groupr0rr')rr%r/r:rrrr's z!SharedPtrSubscriptWorker.__call__N) rrrrrr$r&rr'rrrrr s rc@s8eZdZdZddZddZddZdd Zd d Zd S) SharedPtrUseCountWorkerz*Implement std::shared_ptr::use_count().cCsdS)Nr)rrrrrr%sz SharedPtrUseCountWorker.__init__cCsdS)Nr)rrrrr$(sz%SharedPtrUseCountWorker.get_arg_typescCs tjdS)NZlong)rr)rr%rrrr&+sz'SharedPtrUseCountWorker.get_result_typecCsdS)NTr)rrCrrrr.sz!SharedPtrUseCountWorker._supportscCs|dd}|r|dSdS)NZ _M_refcountZ_M_piZ _M_use_countrr)rr%Z refcountsrrrr'1s z SharedPtrUseCountWorker.__call__N) rrrrrr$r&rr'rrrrr"s rc@s(eZdZdZddZddZddZdS) SharedPtrUniqueWorkerz'Implement std::shared_ptr::unique().cCstj||dS)N)rr)rrrrrr9szSharedPtrUniqueWorker.__init__cCs tjdS)Nr)rr)rr%rrrr&<sz%SharedPtrUniqueWorker.get_result_typecCstj||dkS)Nr-)rr')rr%rrrr'?szSharedPtrUniqueWorker.__call__N)rrrrrr&r'rrrrr6src@seZdZddZddZdS)SharedPtrMethodsMatchercsftjjjtdtdttdttdttdttdt tdt d_ fd d j D_ dS) N shared_ptrr?z operator->z operator*z operator[] use_countunique)r?z operator->z operator*z operator[]rrcsg|]}j|qSr)r8)r9r:)rrrr;Osz4SharedPtrMethodsMatcher.__init__..) rrr<rr=rrrrrrr8r>)rr)rrrDs  z SharedPtrMethodsMatcher.__init__cCsPt|dsdS|jj|}|dks*|j r.dS|j|jd}|j|rL|SdS)Nrr)rr8r?r@rrAr)rrBrCrDrrrrr Qs   zSharedPtrMethodsMatcher.matchN)rrrrr rrrrrCs rcCstjj|ttjj|ttjj|ttjj|ttjj|ttjj|tdtjj|tdtjj|tdtjj|tdtjj|tdtjj|tdtjj|tdtjj|tdtjj|t tjj|t dS) NsetmapZmultisetZmultimapZ unordered_setZ unordered_mapZunordered_multisetZunordered_multimap) rrZregister_xmethod_matcherr2rgr[rrrrrr)Zlocusrrrregister_libstdcxx_xmethods]s.        r)>rZ gdb.xmethodr r=rrr rrrrZ XMethodWorkerrr#r(r*r,r.r1r<r2rErRrSrTrUrXrZr[r]r`rcrgrjrnrorprqrrrtr{r|r}r~rrrrrrrrrrrrrrrrrrrrrrsr                * libstdcxx/v6/__pycache__/__init__.cpython-36.opt-1.pyc000064400000001066152525737710016460 0ustar003 h}@sddZddZdS)c Cs&y ddl}dStk r dSXdS)NTF)Z gdb.xmethod ImportError)Zgdbr0/usr/share/gcc-8/python/libstdcxx/v6/__init__.pygdb_has_xmethodss rcCs2ddlm}||tr.ddlm}||dS)N)register_libstdcxx_printers)register_libstdcxx_xmethods)ZprintersrrZxmethodsr)objrrrrrrs   rN)rrrrrrslibstdcxx/v6/__pycache__/printers.cpython-36.pyc000064400000262570152525737710015641 0ustar003 h@sddlZddlZddlZddlZddlZddlZejddkrXeZe Z e Z e ZejjZn6GdddZddlm Z m Z GdddejZeZday ddlZWnek rd aYnXd ayddlZeejd rdaWnek rYnXeed rejZneZd d ZdaddZddZ ddZ!ddZ"ddZ#ddZ$ddZ%ddZ&Gdd d eZ'Gd!d"d"eZ(d#d$Z)d%d&Z*d'd(Z+Gd)d*d*eZ,d+d,Z-d-d.Z.Gd/d0d0eZ/Gd1d2d2eZ0Gd3d4d4e0Z1Gd5d6d6e0Z2Gd7d8d8eZ3Gd9d:d:eZ4Gd;d<dd>eZ6Gd?d@d@eZ7GdAdBdBeZ8GdCdDdDeZ9GdEdFdFeZ:GdGdHdHeZ;dIdJZdOdPZ?GdQdRdReZ@GdSdTdTeZAGdUdVdVeZBGdWdXdXeZCGdYdZdZeZDGd[d\d\eZEd]d^ZFGd_d`d`eZGGdadbdbeZHGdcddddeZIGdedfdfeZJGdgdhdheZKGdidjdjeZLGdkdldleZMGdmdndneZNdodpZOGdqdrdreNZPGdsdtdteNZQGdudvdveNZRGdwdxdxeNZSGdydzdzeZTGd{d|d|eZUGd}d~d~eZVGdddeZWGdddeZXGdddeZYGdddeZZGdddeZ[GdddeZ\GdddeZ]GdddeZ^GdddeZ_GdddeZ`GdddeZadddddddddddddg ZbddddddddgZcGdddeZdGdddeZeGdddeZfGdddeZgGdddeZhGdddeZiGdddeZjGdddeZkGdddeZlGdddeZmdanGdddeZoddZpGdddeZqdddÄZrddńZsddDŽZtddɄZueudS)Nc@seZdZdZddZdS)Iteratora:Compatibility mixin for iterators Instead of writing next() methods for iterators, write __next__() methods and use this mixin to make them work in Python 2 as well as Python 3. Idea stolen from the "six" documentation: cCs|jS)N)__next__)selfr0/usr/share/gcc-8/python/libstdcxx/v6/printers.pynext?sz Iterator.nextN)__name__ __module__ __qualname____doc__rrrrrr4s r)imapizipc@s(eZdZdZddZddZddZdS) UTCz5Concrete tzinfo class representing the UTC time zone.cCs tjdS)Nr)datetime timedelta)rdtrrr utcoffsetIsz UTC.utcoffsetcCsdS)Nrr)rrrrrtznameLsz UTC.tznamecCs tjdS)Nr)rr)rrrrrdstOszUTC.dstN)r r r r rrrrrrrrFsrTFZ TypePrinter ValuePrinterc Csz|j}xld|j|f}y tj|Stk r6YnX|j}t|r^|djr^|dj}q t dt ||fq WdS)Nz%s::%srzCannot find type %s::%s) strip_typedefstaggdb lookup_type RuntimeErrorfieldslen is_base_classtype ValueErrorstr)Zorignametypsearchrrrr find_typens  r%z__8::cGsdj|djdd|D}y tj|Stjk r}zFt|krv|jddtd}y tj|Stjk rtYnX|WYdd}~XnXdS)z8 Lookup template specialization templ. z{}<{}>z, cSsg|] }t|qSr)r!).0arrr sz%lookup_templ_spec..z::N)formatjoinrrerror_versioned_namespacereplace)Ztemplargsterrrlookup_templ_specs  r2cCsd|krd|}yt|d}Wn|jd}YnX|j}y t||Stjk rt|drt|dddr|jdd d }y t||Stjk rYnXd SXd S) a Lookup specialization of template nodename corresponding to containertype. nodename - The name of a class template, as a String containertype - The container, as a gdb.Type Return a gdb.Type for the corresponding specialization of nodename, or None if the type cannot be found. e.g. lookup_node_type('_List_node', gdb.lookup_type('std::list')) will return a gdb.Type for the type std::_List_node. z::zstd:: value_typerZstdzstd::__cxx1998z std::__debugZ __gnu_debugz ::__cxx1998::r)N)r%template_argumentrr2rr,is_member_of_namespacer.)nodenameZ containertypevaltyperrrlookup_node_types&   r8cGs@t|tjrt|}t|}x|D]}|j|dr"dSq"WdS)z Test whether a type is a member of one of the specified namespaces. The type can be specified as a string or a gdb.Type object. z::TF) isinstancerTyper!strip_versioned_namespace startswith)r#Z namespaces namespacerrrr5s  r5cCs2t|tjr|j}dt|f}tjd||dk S)z Test whether a type is a specialization of the named class template. The type can be specified as a string or a gdb.Type object. The template should be the name of a class template as a string, without any 'std' qualification. z(%s)?%sz ^std::%s<.*>$N)r9rr:rr-rematch)x template_namerrris_specialization_ofs  rBcCs |jtdS)N)r.r-)typenamerrrr;sr;cCs<d}d}tjddkr&tj|||dStj|||ddSdS)zCRemove "fundamentals_vN" inline namespace from qualified type name.z&^std::experimental::fundamentals_v\d::zstd::experimental::rrr))countN)sys version_infor>sub)r#patternreplrrrstrip_fundts_namespaces rKcCsVt|}|jdd}d}x d D]}|j||d|}qW|d}|j|d|}|S) zARemove known inline namespaces from the canonical name of a type.zstd::__cxx11::zstd::zstd::experimental::fundamentals_v1fundamentals_v2z::z filesystem::zv1::)rLrM)r;r.)type_strZexpt_nsZlfts_nsZfs_nsrrrstrip_inline_namespacess  rOc Cs:d}g}x,y|j|j|Wn |S|d7}q WdS)z-Return a type's template arguments as a list.rr)N)appendr4)type_objn template_argsrrrget_template_arg_listsrTc@s(eZdZdZddZddZddZdS) SmartPtrIteratorz@An iterator for smart pointer types with a single 'child' value.cCs ||_dS)N)_val)rvalrrr__init__szSmartPtrIterator.__init__cCs|S)Nr)rrrr__iter__szSmartPtrIterator.__iter__cCs$|jdkrtd|j|_}d|fS)Nzget())rV StopIteration)rrWrrrr s zSmartPtrIterator.__next__N)r r r r rXrYrrrrrrUsrUc@s0eZdZdZddZddZddZdd Zd S) SharedPointerPrinterzP Print a shared_ptr, weak_ptr, atomic, or atomic. cCst||_||_|d|_dS)N_M_ptr)r; _typenamerV_pointer)rrDrWrrrrXs zSharedPointerPrinter.__init__cCs t|jS)N)rUr^)rrrrchildrenszSharedPointerPrinter.childrencCsR|jdkrD|jddd}||d}t|jdjd}|j|S|jddS)Nz std::atomicZ _M_refcountZ_M_val_M_irpointerZ_M_pi)r]rVr%rcast)rZptr_valZptr_typerrr_get_refcountss    z#SharedPointerPrinter._get_refcountscCspd}|j}|jjjd}tt|}|dkr`|d}|d}|dkrPd|}nd||df}d|j||fS) NemptyrZ _M_use_countZ _M_weak_countzexpired, weak count %dzuse count %d, weak count %dr)z %s<%s> (%s))rcrVrr4r;r!r])rstateZ refcountstargZusecountZ weakcountrrr to_string(s  zSharedPointerPrinter.to_stringN)r r r r rXr_rcrgrrrrr[s  r[cCs|jj}|djs&tdt|j|j|dj}|jj}t|dkr^tdt|j|djdkrt|dS|djr|j|djStdt|jdS)zBReturn the tuple element stored in a _Tuple_impl base class.r)z-Unsupported implementation for std::tuple: %sr _M_head_implNri)rrrr r!rbrr")rWbasesZ head_baserrrr_tuple_impl_get9s     rkcCshtt|j}||krtd|j|jjdj}x*|dkr^|j|jjdj}|d8}q6Wt|S)z6Return the result of std::get(val) on a std::tuple.z0Out of range index for std::get on std::tuplerr))rrTrr rbrrk)rRrWZ tuple_sizenoderrr tuple_getQs  rmcCsd|jjdjj}t|ds(t|dr6|dd}n$t|drJ|d}ntdt|td|S)z4Return the result of val.get() on a std::unique_ptr.rZ__uniq_ptr_dataZ__uniq_ptr_impl_M_ttuplez-Unsupported implementation for unique_ptr: %s)rrrrBr r!rm)rWZ impl_typeZ tuple_memberrrrunique_ptr_get_s    rpc@s(eZdZdZddZddZddZdS) UniquePointerPrinterzPrint a unique_ptr.cCs ||_dS)N)rV)rrDrWrrrrXtszUniquePointerPrinter.__init__cCstt|jS)N)rUrprV)rrrrr_wszUniquePointerPrinter.childrencCs|jjjd}djt|S)Nrzstd::unique_ptr<{}>)rVrr4r*r!)rr0rrrrgzszUniquePointerPrinter.to_stringN)r r r r rXr_rgrrrrrqqsrqcCs|djj|jjS)z7Return the value held in a __gnu_cxx::__aligned_membuf. _M_storage)addressrbra dereference)Zbufr7rrrget_value_from_aligned_membufsruc CsjyF|jjdj}|dkr"|dS|dkrD|jjd}t|d|SWn YnXtdt|jdS)z-Return the value held in an _List_node<_Val>.r)_M_datarrrz!Unsupported implementation for %sN)rrr"r4rur r!)rlmemberr7rrrget_value_from_list_nodes rxc@s8eZdZdZGdddeZddZddZdd Zd S) StdListPrinterzPrint a std::list.c@s$eZdZddZddZddZdS)zStdListPrinter._iteratorcCs"||_|d|_|j|_d|_dS)N_M_nextr) _nodetype_basers_head_count)rnodetypeheadrrrrXs z!StdListPrinter._iterator.__init__cCs|S)Nr)rrrrrYsz!StdListPrinter._iterator.__iter__cCsR|j|jkrt|jj|jj}|d|_|j}|jd|_t|}d||fS)Nrzr)z[%d])r|r}rZrbr{rtr~rx)reltrErWrrrrs   z!StdListPrinter._iterator.__next__N)r r r rXrYrrrrr _iteratorsrcCst||_||_dS)N)r;r]rV)rrDrWrrrrXs zStdListPrinter.__init__cCs(td|jjj}|j||jddS)N _List_node_M_impl_M_node)r8rVrrar)rrrrrr_szStdListPrinter.childrencCs0|jdd}|d|jkr&d|jSd|jS)Nrrrzzempty %sz%s)rVrsr])rZheadnoderrrrgs zStdListPrinter.to_stringN) r r r r rrrXr_rgrrrrrys ryc@seZdZddZddZdS)NodeIteratorPrintercCs$||_||_||_t||j|_dS)N)rVr] _contnamer8rr{)rrDrWZcontnamer6rrrrXszNodeIteratorPrinter.__init__cCs:|jdsd|jS|jdj|jjj}tt|S)Nrz(non-dereferenceable iterator for std::%s)rVrrbr{rartr!rx)rrlrrrrgs    zNodeIteratorPrinter.to_stringN)r r r rXrgrrrrrsrc@seZdZdZddZdS)StdListIteratorPrinterzPrint std::list::iterator.cCstj|||dddS)Nlistr)rrX)rrDrWrrrrXszStdListIteratorPrinter.__init__N)r r r r rXrrrrrsrc@seZdZdZddZdS)StdFwdListIteratorPrinterz"Print std::forward_list::iterator.cCstj|||dddS)N forward_list_Fwd_list_node)rrX)rrDrWrrrrXs z"StdFwdListIteratorPrinter.__init__N)r r r r rXrrrrrsrc@s8eZdZdZGdddeZddZddZdd Zd S) StdSlistPrinterzPrint a __gnu_cxx::slist.c@s$eZdZddZddZddZdS)zStdSlistPrinter._iteratorcCs||_|dd|_d|_dS)N_M_headrzr)r{r|r~)rrrrrrrXsz"StdSlistPrinter._iterator.__init__cCs|S)Nr)rrrrrYsz"StdSlistPrinter._iterator.__iter__cCsL|jdkrt|jj|jj}|d|_|j}|jd|_d||dfS)Nrrzr)z[%d]rv)r|rZrbr{rtr~)rrrErrrrs   z"StdSlistPrinter._iterator.__next__N)r r r rXrYrrrrrrsrcCs ||_dS)N)rV)rrDrWrrrrXszStdSlistPrinter.__init__cCs td|jj}|j|j|jS)Nz__gnu_cxx::_Slist_node)r8rVrrra)rrrrrr_szStdSlistPrinter.childrencCs|jdddkrdSdS)Nrrzrzempty __gnu_cxx::slistz__gnu_cxx::slist)rV)rrrrrgszStdSlistPrinter.to_stringN) r r r r rrrXr_rgrrrrrs rc@s eZdZdZddZddZdS)StdSlistIteratorPrinterz!Print __gnu_cxx::slist::iterator.cCs ||_dS)N)rV)rrDrWrrrrXsz StdSlistIteratorPrinter.__init__cCs<|jdsdStd|jjj}t|jdj|jdS)Nrz1non-dereferenceable iterator for __gnu_cxx::slistz__gnu_cxx::_Slist_noderv)rVr8rrar!rbrt)rrrrrrgs  z!StdSlistIteratorPrinter.to_stringN)r r r r rXrgrrrrrsrc@s@eZdZdZGdddeZddZddZdd Zd d Z d S) StdVectorPrinterzPrint a std::vector.c@s$eZdZddZddZddZdS)zStdVectorPrinter._iteratorcCs^||_|rH|d|_d|_|d|_|d|_|jjj}d|j|_n ||_||_d|_ dS)N_M_pr _M_offset) _bitvec_item_so_finish_fortrsizeof_isizer~)rstartfinishZbitvecityperrrrX s    z#StdVectorPrinter._iterator.__init__cCs|S)Nr)rrrrrYsz#StdVectorPrinter._iterator.__iter__cCs|j}|jd|_|jr|j|jkr4|j|jkr4tt|jjd|j>@}|jd|_|j|j krv|jd|_d|_d||fS|j|jkrt|jj}|jd|_d||fSdS)Nr)rz[%d]) r~rrrrrrZboolrtr)rrErrrrrs         z#StdVectorPrinter._iterator.__next__N)r r r rXrYrrrrrr srcCs*t||_||_|jjdjtjk|_dS)Nr) r;r]rVrr4coderTYPE_CODE_BOOL_is_bool)rrDrWrrrrX0s zStdVectorPrinter.__init__cCs$|j|jdd|jdd|jS)Nr_M_start _M_finish)rrVr)rrrrr_6s zStdVectorPrinter.childrenc Cs|jdd}|jdd}|jdd}|jr|jddd}|jddd}|jddd}|jj}d|j}||||}|||}d|jt|t|fSd |jt||t||fSdS) NrrrZ_M_end_of_storagerrrz"%s of length %d, capacity %dz%s of length %d, capacity %d)rVrrtrrr]int) rrrendZforZbllengthZcapacityrrrrg;s   zStdVectorPrinter.to_stringcCsdS)Narrayr)rrrr display_hintMszStdVectorPrinter.display_hintN) r r r r rrrXr_rgrrrrrrs %rc@s eZdZdZddZddZdS)StdVectorIteratorPrinterzPrint std::vector::iterator.cCs ||_dS)N)rV)rrDrWrrrrXTsz!StdVectorIteratorPrinter.__init__cCs |jdsdSt|jdjS)NZ _M_currentz,non-dereferenceable iterator for std::vector)rVr!rt)rrrrrgWs z"StdVectorIteratorPrinter.to_stringN)r r r r rXrgrrrrrQsrc@s eZdZdZddZddZdS)StdBitIteratorPrinterz@Print std::vector's _Bit_iterator and _Bit_const_iterator.cCs ||_dS)N)rV)rrDrWrrrrX`szStdBitIteratorPrinter.__init__cCs.|jdsdSt|jdjd|jd>@S)Nrz2non-dereferenceable iterator for std::vectorr)r)rVrrt)rrrrrgcs zStdBitIteratorPrinter.to_stringN)r r r r rXrgrrrrr]src@s eZdZdZddZddZdS)StdBitReferencePrinterz#Print std::vector::reference.cCs ||_dS)N)rV)rrDrWrrrrXmszStdBitReferencePrinter.__init__cCs*|jdsdSt|jdj|jd@S)Nrz$invalid std::vector::referenceZ_M_mask)rVrrt)rrrrrgps z StdBitReferencePrinter.to_stringN)r r r r rXrgrrrrrjsrc@s8eZdZdZGdddeZddZddZdd Zd S) StdTuplePrinterzPrint a std::tuple.c@s0eZdZeddZddZddZddZd S) zStdTuplePrinter._iteratorcCsNt|dkr"t|djdrBdSn t|dkr2dSt|dkrBdStddS)Nrr)Z __tuple_baseTrFz4Top of tuple tree does not consist of a single node.)rrBrr )nodesrrr_is_nonempty_tuple}s   z,StdTuplePrinter._iterator._is_nonempty_tuplecCs:||_|jjj}|j|r0|jj|dj|_d|_dS)Nr)r}rrrrbr~)rrrrrrrXs   z"StdTuplePrinter._iterator.__init__cCs|S)Nr)rrrrrYsz"StdTuplePrinter._iterator.__iter__cCs|js t|jjj}t|dkr&tt|dkr:tdt|dkr`|jj|dj}d|_n&|jj|dj}|jj|dj|_|jd|_|jj}t|dks|djdkrd|jd|fSd|jd|dfSdS)Nrrz/Cannot parse more than 2 nodes in a tuple tree.r)rhz[%d]) r}rZrrrr rbr~r")rrimplrrrrrs$       z"StdTuplePrinter._iterator.__next__N)r r r staticmethodrrXrYrrrrrr|s  rcCst||_||_dS)N)r;r]rV)rrDrWrrrrXs zStdTuplePrinter.__init__cCs |j|jS)N)rrV)rrrrr_szStdTuplePrinter.childrencCs(t|jjjdkrd|jSd|jS)Nrzempty %sz %s containing)rrVrrr])rrrrrgs zStdTuplePrinter.to_stringN) r r r r rrrXr_rgrrrrrys Erc@s0eZdZdZddZddZddZdd Zd S) StdStackOrQueuePrinterz!Print a std::stack or std::queue.cCst||_tj|d|_dS)Nc)r;r]rdefault_visualizer _visualizer)rrDrWrrrrXs zStdStackOrQueuePrinter.__init__cCs |jjS)N)rr_)rrrrr_szStdStackOrQueuePrinter.childrencCsd|j|jjfS)Nz%s wrapping: %s)r]rrg)rrrrrgsz StdStackOrQueuePrinter.to_stringcCst|jdr|jjSdS)Nr)hasattrrr)rrrrrs  z#StdStackOrQueuePrinter.display_hintN)r r r r rXr_rgrrrrrrs rc@s0eZdZdZddZddZddZdd Zd S) RbtreeIteratorzf Turn an RB-tree-based container (std::map, std::set etc.) into a Python iterable object. cCs2|ddd|_|dddd|_d|_dS)NrnrZ _M_node_countZ _M_header_M_leftr)_size_noder~)rZrbtreerrrrXszRbtreeIterator.__init__cCs|S)Nr)rrrrrYszRbtreeIterator.__iter__cCs t|jS)N)rr)rrrr__len__szRbtreeIterator.__len__cCs|j|jkrt|j}|jd|_|j|jkr|j}|jdrl|jd}xd|jdrh|jd}qNWnF|jd}x$||jdkr|}|jd}qzW|jd|kr|}||_|S)Nr)Z_M_rightrZ _M_parent)r~rrZrrt)rresultrlparentrrrrs$      zRbtreeIterator.__next__N)r r r r rXrYrrrrrrrs rc CsjyF|jjdj}|dkr"|dS|dkrD|jjd}t|d|SWn YnXtdt|jdS)z0Return the value held in an _Rb_tree_node<_Val>.r)Z_M_value_fieldrrrz!Unsupported implementation for %sN)rrr"r4rur r!)rlrwr7rrrget_value_from_Rb_tree_node s rc@s eZdZdZddZddZdS)StdRbtreeIteratorPrinterz2Print std::map::iterator, std::set::iterator, etc.cCs"||_td|jj}|j|_dS)N _Rb_tree_node)rVr8rra _link_type)rrDrWrrrrrX!sz!StdRbtreeIteratorPrinter.__init__cCs0|jdsdS|jdj|jj}tt|S)Nrz6non-dereferenceable iterator for associative container)rVrbrrtr!r)rrlrrrrg&s z"StdRbtreeIteratorPrinter.to_stringN)r r r r rXrgrrrrrsrc@s eZdZdZddZddZdS)StdDebugIteratorPrinterz-Print a debug enabled version of an iterator.cCs ||_dS)N)rV)rrDrWrrrrX0sz StdDebugIteratorPrinter.__init__cCsbtjd}|jjjd}|jj|d}|std||_nd|_dS)Nrir)) r;r]rVrr4Z_elttyperrr)rrDrWsizerrrrXs zStdDequePrinter.__init__cCsx|jdd}|jdd}|d|dd}|d|d}|d|d}|j|||}d |jtt|fS) Nrrrrr)_M_last_M_cur_M_firstz %s with %s)rVrr]rlong)rrrZdelta_nZdelta_sZdelta_errrrrgszStdDequePrinter.to_stringcCs@|jdd}|jdd}|j|d|d|d|d|jS)Nrrrrrr)rVrr)rrrrrrr_szStdDequePrinter.childrencCsdS)Nrr)rrrrrszStdDequePrinter.display_hintN) r r r r rrrXrgr_rrrrrrs   rc@s eZdZdZddZddZdS)StdDequeIteratorPrinterzPrint std::deque::iterator.cCs ||_dS)N)rV)rrDrWrrrrXsz StdDequeIteratorPrinter.__init__cCs |jdsdSt|jdjS)Nrz+non-dereferenceable iterator for std::deque)rVr!rt)rrrrrg s z!StdDequeIteratorPrinter.to_stringN)r r r r rXrgrrrrrsrc@s(eZdZdZddZddZddZdS) StdStringPrinterz'Print a std::basic_string of some kind.cCs||_|jddk|_dS)Nz::__cxx11::basic_stringr)ri)rVfind _new_string)rrDrWrrrrXszStdStringPrinter.__init__cCs|jj}|jtjkr|j}|jdd}|jrL|jd}|j|jj}n<|j j}tj t |dj }|j|d}|j d}t|dr|j|dS|j|dS) NZ _M_dataplusrZ_M_string_lengthz::_Repr)Z _M_length lazy_string)r)rVrrr TYPE_CODE_REFrrrbr unqualifiedrr!rartrrstring)rrptrrZrealtypeZreptypeheaderrrrrgs      zStdStringPrinter.to_stringcCsdS)Nrr)rrrrr-szStdStringPrinter.display_hintN)r r r r rXrgrrrrrrsrcCs"|d}|d}|d}|||fS)z'Access the streambuf put area pointers.Z _M_out_begZ _M_out_curZ _M_in_endr) streambufpbasepptregptrrrraccess_streambuf_ptrs1src@s(eZdZdZddZddZddZdS) StdStringBufPrinterzPrint a std::basic_stringbuf.cCs ||_dS)N)rV)r_rWrrrrX<szStdStringBufPrinter.__init__cCsLt|j\}}}|rB| s"||kr2|j||dS|j||dS|jdS)N)rZ _M_string)rrVr)rrrrrrrrg?s zStdStringBufPrinter.to_stringcCsdS)Nrr)rrrrrIsz StdStringBufPrinter.display_hintN)r r r r rXrgrrrrrr9s rc@s(eZdZdZddZddZddZdS) StdStringStreamPrinterz Print a std::basic_stringstream.cCsZ||_||_dd|jjDd}tjd|j|jtjd|_ |j |djk|_ dS)NcSsg|]}|jr|jqSr)rr)r&frrrr(Xsz3StdStringStreamPrinter.__init__..rZ__streamz$__stream->rdbuf() _M_stringbuf) rVr]rrrZset_convenience_variablerbrsparse_and_eval _streambuf_was_redirected)rrDrWZbasetyperrrrXPs  zStdStringStreamPrinter.__init__cCs$|jrd|j|jjfS|jdS)Nz%s redirected to %sr)rr]rrtrV)rrrrrg]sz StdStringStreamPrinter.to_stringcCs|jr dSdS)Nr)r)rrrrrcsz#StdStringStreamPrinter.display_hintN)r r r r rXrgrrrrrrMs rc@s$eZdZddZddZddZdS)Tr1HashtableIteratorcCsj|d|_d|_|d|_t|jdj|_d|_x2|j|jkrd|j|j|_|jrVP|jd|_q4WdS)NZ _M_bucketsrZ_M_bucket_countZ_Noder))_buckets_bucket _bucket_countr%rra _node_typer)r hashtablerrrrXjs  zTr1HashtableIterator.__init__cCs|S)Nr)rrrrrYvszTr1HashtableIterator.__iter__cCs|jdkrt|jj|j}|jd}|jd|_|jdkr|jd|_x2|j|jkr~|j|j|_|jrpP|jd|_qNW|S)NrZ_M_vrzr))rrZrbrrtrrr)rrlrrrrrys    zTr1HashtableIterator.__next__N)r r r rXrYrrrrrris rc@s$eZdZddZddZddZdS)StdHashtableIteratorcCsR|dd|_|jjd}|jjdjd}tdt||r>dnd}|j|_dS) NZ_M_before_begin_M_nxtr) rzstd::__detail::_Hash_nodetrueZfalse)rrr4r2r!rar)rrr7cachedZ node_typerrrrXs   zStdHashtableIterator.__init__cCs|S)Nr)rrrrrYszStdHashtableIterator.__iter__cCsR|jdkrt|jj|jj}|d|_|dj}|j|jjdj}|jS)Nrrrr) rrZrbrrtrsrr4ra)rrvalptrrrrrs   zStdHashtableIterator.__next__N)r r r rXrYrrrrrrsrc@s<eZdZdZddZddZddZedd Zd d Z d S) Tr1UnorderedSetPrinterz1Print a std::unordered_set or tr1::unordered_set.cCst||_||_dS)N)r;r]rV)rrDrWrrrrXs zTr1UnorderedSetPrinter.__init__cCs|jjdr|jS|jdS)Nzstd::tr1_M_h)r]r<rV)rrrr _hashtables z!Tr1UnorderedSetPrinter._hashtablecCs|jd}d|jt|fS)N_M_element_countz %s with %s)rr]r)rrErrrrgs z Tr1UnorderedSetPrinter.to_stringcCsd|S)Nz[%d]r)irrr _format_countsz$Tr1UnorderedSetPrinter._format_countcCs@t|jtj}|jjdr.t|t|jSt|t |jS)Nzstd::tr1) r r itertoolsrEr]r<rrrr)rcounterrrrr_s zTr1UnorderedSetPrinter.childrenN) r r r r rXrrgrrr_rrrrrs  rc@s\eZdZdZddZddZddZedd Zed d Z ed d Z ddZ ddZ dS)Tr1UnorderedMapPrinterz1Print a std::unordered_map or tr1::unordered_map.cCst||_||_dS)N)r;r]rV)rrDrWrrrrXs zTr1UnorderedMapPrinter.__init__cCs|jjdr|jS|jdS)Nzstd::tr1r)r]r<rV)rrrrrs z!Tr1UnorderedMapPrinter._hashtablecCs|jd}d|jt|fS)Nrz %s with %s)rr]r)rrErrrrgs z Tr1UnorderedMapPrinter.to_stringccs&x |D]}x|D] }|VqWqWdS)Nr)rrrrrr_flattens  zTr1UnorderedMapPrinter._flattencCs|d|dfS)Nrrr)rrrr _format_onesz"Tr1UnorderedMapPrinter._format_onecCsd|S)Nz[%d]r)rrrrrsz$Tr1UnorderedMapPrinter._format_countcCsdt|jtj}|jjdr@|jt|jt|j }t ||S|jt|jt |j }t ||S)Nzstd::tr1) r rrrEr]r<r r rrrr)rr datarrrr_s  zTr1UnorderedMapPrinter.childrencCsdS)Nrr)rrrrrsz#Tr1UnorderedMapPrinter.display_hintN) r r r r rXrrgrr r rr_rrrrrr s    r c@s8eZdZdZGdddeZddZddZdd Zd S) StdForwardListPrinterzPrint a std::forward_list.c@s$eZdZddZddZddZdS)zStdForwardListPrinter._iteratorcCs||_|d|_d|_dS)Nrzr)r{r|r~)rrrrrrrXs z(StdForwardListPrinter._iterator.__init__cCs|S)Nr)rrrrrYsz(StdForwardListPrinter._iterator.__iter__cCsl|jdkrt|jj|jj}|d|_|j}|jd|_|dj}|j|jjdj }d||jfS)Nrrzr)rrz[%d]) r|rZrbr{rtr~rsrr4ra)rrrErrrrrs    z(StdForwardListPrinter._iterator.__next__N)r r r rXrYrrrrrrsrcCs||_t||_dS)N)rVr;r])rrDrWrrrrXszStdForwardListPrinter.__init__cCs(td|jjj}|j||jddS)Nrrr)r8rVrrar)rrrrrr_szStdForwardListPrinter.childrencCs*|jddddkr d|jSd|jS)Nrrrzrzempty %sz%s)rVr])rrrrrgs zStdForwardListPrinter.to_stringN) r r r r rrrXr_rgrrrrrs rc@sBeZdZdZd ddZddZGdddeZd d Zd d Z dS)SingleObjContainerPrinterz8Base class for printers of containers of single objects.NcCs||_||_||_dS)N)_contained_valuer_hint)rrWZvizZhintrrrrXsz"SingleObjContainerPrinter.__init__cCs(ts t|Stjjtjj|p&t|S)z5Return type as a string after applying type printers.)_use_type_printingr!rtypesapply_type_recognizersget_type_recognizers)rrrrr _recognizesz$SingleObjContainerPrinter._recognizec@s$eZdZddZddZddZdS)z$SingleObjContainerPrinter._containedcCs ||_dS)N)rV)rrWrrrrXsz-SingleObjContainerPrinter._contained.__init__cCs|S)Nr)rrrrrY"sz-SingleObjContainerPrinter._contained.__iter__cCs"|jdkrt|j}d|_d|fS)Nz[contained value])rVrZ)rZretvalrrrr%s  z-SingleObjContainerPrinter._contained.__next__N)r r r rXrYrrrrr _containedsrcCs6|jdkr|jdSt|jdr*|jjS|j|jS)Nr_)rrrrr_)rrrrr_,s     z"SingleObjContainerPrinter.childrencCs(t|jdr"t|jdr"|jjS|jS)Nr_r)rrrr)rrrrr3s   z&SingleObjContainerPrinter.display_hint)N) r r r r rXrrrr_rrrrrrs  rc CsH|jj}tjddkr"t|}nt|}ytj|jj SdSdS)z{Find the name of the function referred to by the gdb.Value f, which should contain a function pointer from the program.rrN) rtrsrFrGrrrZ block_for_pcfunctionr")rrrrfunction_pointer_to_name;s  rcs4eZdZdZfddZddZeddZZS)StdExpAnyPrinterz+Print a std::any or std::experimental::any.c st||_t|j|_||_d|_d}d}|jd}|dkrt|}|sXtd|jdj|}tj ||}|std|j|j d} d| kr g} xPt j D]D} y(tj d| |j d} | jtj| Wqtjk rYqXqWt| dkrtd | d} n tj| } | jd|_d}d | kr@|jd d j}n(d | krZ|jd d}ntd|j|j|jjj}tj|}tt |j||dS)NZ _M_managerrzInvalid function pointer in %szi({0}::_Manager_\w+<.*>)::_S_manage\((enum )?{0}::_Op, (const {0}|{0} const) ?\*, (union )?{0}::_Arg ?\*\)zUnknown manager function in %sr)z std::stringzstd::string(?!\w)z;Cannot uniquely determine std::string type used in std::anyz::_Manager_internalrrZ _M_bufferz::_Manager_externalr\)r;r]rKrV_contained_typerr r*r>r?groupr _string_typesrHrPrrr,rr4rsrbrartrsuperrX)rrDrWcontained_value visualizerZmgrfuncZrxmZmgrnameZmgrtypessr@Zmgrtyper) __class__rrrXVsX                 zStdExpAnyPrinter.__init__cCsT|jdkrd|jSd|j}t|jdr8||jjS|j|j}|tt|S)Nz%s [no contained value]z%s containing r_)rr]rrrgrr;r!)rdescr7rrrrgs     zStdExpAnyPrinter.to_stringcs@ttjdjh}ddddtg}|fdd|DO}|S)Nz std::stringzAbasic_string, std::allocator >zstd::zstd::__cxx11::csh|] }|qSrr)r&q)r#rr sz1StdExpAnyPrinter._string_types..)r!rrrr-)ZstringsZqualsr)r#rrs  zStdExpAnyPrinter._string_types) r r r r rXrgrr __classcell__rr)r$rrSs D rcs(eZdZdZfddZddZZS)StdExpOptionalPrinterz5Print a std::optional or std::experimental::optional.c st||_t|j|_|d}|jjdr8|d}|}n*|d}|d}y |d}Wn YnXtj|}|std}tt|j||dS)N _M_payloadzstd::experimental _M_engaged_M_value) r;r]rKr<rrrr)rX)rrDrWZpayloadZengagedrr )r$rrrXs"      zStdExpOptionalPrinter.__init__cCs:|jdkrd|jSt|jdr4d|j|jjfS|jS)Nz%s [no contained value]r_z%s containing %s)rr]rrrg)rrrrrgs    zStdExpOptionalPrinter.to_string)r r r r rXrgr(rr)r$rr)s r)cs(eZdZdZfddZddZZS)StdVariantPrinterzPrint a std::variant.cst|j}t||_|d|_|jt|krz[%s])rrrZrtr~r7r@r=)rrrEr?r0rrrrRs    z$StdExpPathPrinter._iterator.__next__N)r r r rXrYrrrrrrHsrcCs|j|jd|jS)Nr8)rrVr])rrrrr__szStdExpPathPrinter.childrenN) r r r r rXr=rgrrr_rrrrr7.s r7c@s@eZdZdZddZddZddZGdd d eZd d Z d S) StdPathPrinterzPrint a std::filesystem::path.cCsP||_||_t|jdd}|jtjdd@|_|jdkrF||_nd|_dS)Nr8rZ uintptr_tr)rVr]rprbrrr_impl)rrDrWrrrrrXfs zStdPathPrinter.__init__cCsHt|jjtj|jd}|dddkr0dS|d ddkrDdSdS) Nz::_Typerr:zroot-directoryr;r<z root-nameii)r!rrbrrr])rr0rrrr=ps zStdPathPrinter._path_typecCs8d|jd}|jdkr0|j}|r0d||f}d|S)Nz%sr>rz%s [%s]zfilesystem::path %s)rVrr=)rr?r0rrrrgxs   zStdPathPrinter.to_stringc@s$eZdZddZddZddZdS)zStdPathPrinter._iteratorc Cs||_|rtjd}tj|d}tjd}|j|j}|j}ttjdrbtd|j |j }n d|j }|j|j|}|j|j|_ |j ||_ d|_ n d|_ d|_ dS)Nrz::_Cmptcharalignofrr)r@rrrbrartrr:maxrrFrrr~) rrrAZint_typeZ cmpt_type char_typerZ sizeof_ImplbeginrrrrXs      z!StdPathPrinter._iterator.__init__cCs|S)Nr)rrrrrYsz!StdPathPrinter._iterator.__iter__cCsd|j|jkrt|jj}|j}|jd|_|jd|_|d}t|j|j}|sX|}d||fS)Nr)r>z[%s])rrrZrtr~rBr@r=)rrrEr?r0rrrrs    z!StdPathPrinter._iterator.__next__N)r r r rXrYrrrrrrsrcCs|j|j|jS)N)rrDr])rrrrr_szStdPathPrinter.childrenN) r r r r rXr=rgrrr_rrrrrBcs  (rBc@s8eZdZdZddZGdddeZddZdd Zd S) StdPairPrinterz@Print a std::pair object, with 'first' and 'second' as children.cCs ||_dS)N)rV)rrDrWrrrrXszStdPairPrinter.__init__c@s(eZdZdZddZddZddZdS) zStdPairPrinter._iterz?An iterator for std::pair types. Returns 'first' then 'second'.cCs||_d|_dS)Nr)rV_which)rrWrrrrXszStdPairPrinter._iter.__init__cCs|S)Nr)rrrrrYszStdPairPrinter._iter.__iter__cCs8|jdkrt|j}|dkr$d|_nd|_||j|fS)Nrr)rKrZrV)rZwhichrrrrs zStdPairPrinter._iter.__next__N)r r r r rXrYrrrrrrsrcCs |j|jS)N)rrV)rrrrr_szStdPairPrinter.childrencCsdS)Nr)rrrrrgszStdPairPrinter.to_stringN) r r r r rXrrr_rgrrrrrJs rJc@s eZdZdZddZddZdS)StdCmpCatPrinterz#Print a comparison category object.cCs&||jddd|_|d|_dS)N:r)r,)rfindr]rV)rrDrWrrrrXszStdCmpCatPrinter.__init__cCsJ|jdkr|jdkrd}n"ddd ddddd i}|t|j}d j|j|S) Nstrong_orderingrZequalrZ unorderedr)ZlessZ equivalentZgreaterz std::{}::{}ri)r]rVrr*)rr"namesrrrrgs zStdCmpCatPrinter.to_stringN)r r r r rXrgrrrrrLsrLc@s`eZdZdZdZddZeddZeddZ ed d Z ed d Z ed dZ ddZ dS)StdErrorCodePrinterz0Print a std::error_code or std::error_condition.Nc CsL||_t||_tjdkrHyddl}dt_Wntk rFdt_YnXdS)NrTF)rVr;r]rQ_system_is_posixposix ImportError)rrDrWrSrrrrXs   zStdErrorCodePrinter.__init__cCs&tj|}|dk r"|jtjkr"|SdS)N)rrrZTYPE_CODE_ENUM)r"r#rrr_find_errc_enums z#StdErrorCodePrinter._find_errc_enumc CsBxs(     zStdErrorCodePrinter.to_string)r r r r rRrXrrU classmethodrXr]rdrfrgrrrrrQs   + rQc@s eZdZdZddZddZdS)StdRegexStatePrinterz/Print a state node in the NFA for a std::regex.cCs||_||_dS)N)rVr])rrDrWrrrrX[szStdRegexStatePrinter.__init__cCst|jd}|r|dd}|jd}dddddddddddddd }||}d j||}|dk r|jd |dk rd j|||jd |}d |S) NZ _M_opcoderzZaltZsubexprnegZ backref_index) repeat alternativeZ subexpr_beginZ subexpr_endZline_begin_assertionZline_end_assertionZ word_boundaryZsubexpr_lookaheadZbackrefr?ZacceptZdummyunknownzopcode={}, next={}Z_M_z {}, {}={}z{%s})r!rVr*)rZopcodeZnext_idZvariantsvr#rrrrg_s     zStdRegexStatePrinter.to_stringN)r r r r rXrgrrrrrlXsrlc@s@eZdZdZGdddeZddZddZdd Zd d Z d S) StdSpanPrinterzPrint a std::span.c@s$eZdZddZddZddZdS)zStdSpanPrinter._iteratorcCsd|_||_||_dS)Nr)r~_beginr)rrIrrrrrXysz!StdSpanPrinter._iterator.__init__cCs|S)Nr)rrrrrY~sz!StdSpanPrinter._iterator.__iter__cCs8|j|jkrt|j}|jd|_d||j|jfS)Nr)z[%d])r~rrZrtrt)rrErrrrs   z!StdSpanPrinter._iterator.__next__N)r r r rXrYrrrrrrxsrcCsLt||_||_tjd}|jjd|kr:|dd|_n|jjd|_dS)Nzstatic_cast(-1)r)Z _M_extentZ_M_extent_value)r;r]rVrrrr4r)rrDrWZsize_maxrrrrXs   zStdSpanPrinter.__init__cCsd|j|jfS)Nz%s of length %d)r]r)rrrrrgszStdSpanPrinter.to_stringcCs|j|jd|jS)Nr\)rrVr)rrrrr_szStdSpanPrinter.childrencCsdS)Nrr)rrrrrszStdSpanPrinter.display_hintN) r r r r rrrXrgr_rrrrrrsus  rsc@s0eZdZdZddZddZddZdd Zd S) StdInitializerListPrinterzPrint a std::initializer_list.cCs||_||_|d|_dS)Nr6)r]rVr)rrDrWrrrrXsz"StdInitializerListPrinter.__init__cCsd|j|jfS)Nz%s of length %d)r]r)rrrrrgsz#StdInitializerListPrinter.to_stringcCstj|jd|jS)NZ_M_array)rsrrVr)rrrrr_sz"StdInitializerListPrinter.childrencCsdS)Nrr)rrrrrsz&StdInitializerListPrinter.display_hintN)r r r r rXrgr_rrrrrrus ruc@s(eZdZdZddZddZddZdS) StdAtomicPrinterzPrint a std:atomic.cCsrt||_||_d|_|jjjd|_|jjdk rnt|jj}|jdsR|jdrn|d}t |||_|j |_ dS)Nrzstd::shared_ptr = { %s }) rwrgr1rrZ TYPE_CODE_INTrV TYPE_CODE_FLT TYPE_CODE_PTRrr]r!)rrWrrrrgs     zStdAtomicPrinter.to_stringN)r r r r rXrxrgrrrrrvs rvc@s eZdZdZddZddZdS)StdFormatArgsPrinterzPrint a std::basic_format_args.cCst||_||_dS)N)r;r]rV)rrDrWrrrrXs zStdFormatArgsPrinter.__init__cCst|jj}t|dd}|tjdkr0d}n|tjdkrDd}nd}|jd}|dkrbd |S|dkrt|jd }d ||fS) Nrr)rEzstd::format_argswchar_tzstd::wformat_argszstd::basic_format_argsZ_M_packed_sizez%s with 1 argumentZ_M_unpacked_sizez%s with %d arguments)rTrVrrr)rZtargsrHr#rrrrrgs   zStdFormatArgsPrinter.to_stringN)r r r r rXrgrrrrr{sr{c@s0eZdZdZddZddZddZdd Zd S) StdChronoDurationPrinterzPrint a std::chrono::duration.cCst||_||_dS)N)r;r]rV)rrDrWrrrrXs z!StdChronoDurationPrinter.__init__cCs*|jjjd}|jd}|jd}||fS)Nr)r)rVrr4)rZperiodrdenrrr_ratios  zStdChronoDurationPrinter._ratiocCs|j\}}|dkrF|dkr dS|dkr,dS|dkr8dS|dkr|dSn6|dkr||d krZd S|d krfd S|d krrdSdj|Sdj||S)Nr)r#iZmsi@Busiʚ;rW<minihiQdz[{}]sz[{}/{}]s)rr*)rrr~rrr_suffixs&  z StdChronoDurationPrinter._suffixcCs4|jd}|jjjtjkr$d|}dj||jS)N__rz%gz"std::chrono::duration = {{ {}{} }})rVrrrrryr*r)rrrrrrgs z"StdChronoDurationPrinter.to_stringN)r r r r rXrrrgrrrrr}s r}c@s*eZdZdZddZddZd ddZd S) StdChronoTimePointPrinterz Print a std::chrono::time_point.cCst||_||_dS)N)r;r]rV)rrDrWrrrrXs z"StdChronoTimePointPrinter.__init__cCsv|jjjd}t|j}|dks(|dkr,dS|dkr8dS|dkrDdS|d krPdS|d kr\dS|dkrhdSdj|dfS)Nrzstd::chrono::_V2::system_clockzstd::chrono::system_clockstd::chrono::sys_timezstd::chrono::utc_clockstd::chrono::utc_timezstd::chrono::gps_clockstd::chrono::gps_timezstd::chrono::tai_clockstd::chrono::tai_timezstd::filesystem::__file_clockstd::chrono::file_timeumzstd::chrono::local_tstd::chrono::local_timez {} time_point)rr)rN)rN)rN)rr)rr)rVrr4r;r"r*)rclockr"rrr_clocks  z StdChronoTimePointPrinter._clockFc Cs|j\}}|jd}|d}t|jj|}|j}d}|dk r|j\} } || | |} ytjj| t } dj | }Wn YnXd|||f} |r| Sd|| fS)NZ__drrCz [{:%Y-%m-%d %H:%M:%S}]z%d%s%sz %s = { %s }) rrVr}rr"rrrZ fromtimestamp _utc_timezoner*)rZabbrevroffsetrrprintersuffixtimerr~Zsecsrr#rrrrg1s$   z#StdChronoTimePointPrinter.to_stringN)F)r r r r rXrrgrrrrrsrc@s eZdZdZddZddZdS)StdChronoZonedTimePrinterz Print a std::chrono::zoned_time.cCst||_||_dS)N)r;r]rV)rrDrWrrrrXIs z"StdChronoZonedTimePrinter.__init__cCs@|jdjd}|jd}t|jj|}|jd}dj||S)NZ_M_zone_M_nameZ_M_tpTz%std::chrono::zoned_time = {{ {} {} }})rVrtrrr"rgr*)rZzonerrrrrrgMs   z#StdChronoZonedTimePrinter.to_stringN)r r r r rXrgrrrrrFsrZJanuaryZFebruaryZMarchZAprilZMayZJuneZJulyZAugustZ SeptemberZOctoberZNovemberZDecemberZSundayZMondayZTuesdayZ WednesdayZThursdayZFridayZSaturdayc@s eZdZdZddZddZdS)StdChronoCalendarPrinterzDPrint a std::chrono::day, std::chrono::month, std::chrono::year etc.cCst||_||_dS)N)r;r]rV)rrDrWrrrrX_s z!StdChronoCalendarPrinter.__init__c Csv|j}|j}d|kr$|dkr$|d}|jdr6|d}|dkrPdjt|dS|d kr||d ksl|ttkrtd |St|S|dkrd j|S|d kr|d}|dks|ttkrd|Sdjt|S|dkrdj|dt|dS|dkrdj|dS|dkrdj||dS|dkr,dj|S|dkrFdj||dS|dkr`dj||dS|dkrvdj||S|dkrd j|||dS|dkrdj||d!S|d"krd j|||dS|d#krd j|||dS|jd$rrd%}|d&dkr"d'jt|d(d)t|d&}t|d*d+}t|dd+}t|d,d+}|d-rb| }d.j||||SdS)/Nmonthz std::chrono::year_month_day_lastZ_M_mzstd::chrono::yearZ_M_yzstd::chrono::dayz{}Z_M_dzstd::chrono::monthr)z%d is not a valid monthz{}yzstd::chrono::weekdayZ_M_wdrz%d is not a valid weekdayzstd::chrono::weekday_indexedz{}[{}]r.zstd::chrono::weekday_lastz{}[last]zstd::chrono::month_dayz{}/{}zstd::chrono::month_day_lastz{}/lastzstd::chrono::month_weekdayZ_M_wdizstd::chrono::month_weekday_lastZ_M_wdlzstd::chrono::year_monthzstd::chrono::year_month_dayz{}/{}/{}Z_M_mdlzstd::chrono::year_month_weekdayz$std::chrono::year_month_weekday_lastzstd::chrono::hh_mm_ssrCZfractional_widthz.{:0{}d}Z_M_ssZ_M_rrr_M_sZ _M_is_negz{:02}:{:02}:{:02}{})rVr]r<r*rrmonthsweekdays) rrWr#r"yZwdZfractrr#rrrrgcsh               z"StdChronoCalendarPrinter.to_stringN)r r r r rXrgrrrrr\src@s eZdZdZddZddZdS)StdChronoTimeZonePrinterz4Print a chrono::time_zone or chrono::time_zone_link.cCst||_||_dS)N)r;r]rV)rrDrWrrrrXs z!StdChronoTimeZonePrinter.__init__cCs6d|j|jdf}|jjdr2|d|jd7}|S)Nz%s = %srZ_linkz -> %sZ _M_target)r]rVrb)rr!rrrrgs z"StdChronoTimeZonePrinter.to_stringN)r r r r rXrgrrrrrsrc@s eZdZdZddZddZdS)StdChronoLeapSecondPrinterzPrint a chrono::leap_second.cCst||_||_dS)N)r;r]rV)rrDrWrrrrXs z#StdChronoLeapSecondPrinter.__init__cCs.|jdd}d|dk}d|jt||fS)Nrrz+-rz %s %d (%c))rVr]abs)rZdaternrrrrgs z$StdChronoLeapSecondPrinter.to_stringN)r r r r rXrgrrrrrsrc@s eZdZdZddZddZdS)StdChronoTzdbPrinterzPrint a chrono::tzdb.cCst||_||_dS)N)r;r]rV)rrDrWrrrrXs zStdChronoTzdbPrinter.__init__cCsd|j|jdfS)Nz%s %sversion)r]rV)rrrrrgszStdChronoTzdbPrinter.to_stringN)r r r r rXrgrrrrrsrc@s eZdZdZddZddZdS)StdChronoTimeZoneRulePrinterzPrint a chrono::time_zone rule.cCst||_||_dS)N)r;r]rV)rrDrWrrrrXs z%StdChronoTimeZoneRulePrinter.__init__c Cs|jd}|d}t|d}dddddddd}|d}d j||j|d }|d krdd j||}nVt|d }|dkrdj||}n4|dkrd} nd} |d}dj| d || d||}dj|jd|jd|jd|S)NonkindrstZndZrd)r)rrCZ day_of_monthz{}{}Zthrz{} {}Z day_of_weekr)z last {} in {}rr<=r>=z{} {} {} {} {}z.time_zone rule {} from {} to {} starting on {}r"fromZto)rr)rr)rVrr*getr) rrrrsuffixesdayZ ordinal_dayrweekday directionrrrrgs*    z&StdChronoTimeZoneRulePrinter.to_stringN)r r r r rXrgrrrrrsrc@s eZdZdZddZddZdS)StdLocalePrinterzPrint a std::locale.cCs||_||_dS)N)rVr])rrDrWrrrrXszStdLocalePrinter.__init__csx|jdd}d}|ddkr&d}nFtj|jd}tj|jd}|dj}|dj}dj||}||i}d } xN| |kr|| dkr|| j}|| j}d j|||}|||<| d } qxWt|j} t| d kr|}nt| d krl| \} } t |j} d| j | d kr$| }| n| j | d kr<| }| dk rlt fd d |j D}dj|}dj||S)NrZ_M_namesrCr*z::_S_categoriesz::_S_categories_sizez{}={}r)z{};{}={}rc3s|]\}}|kr|VqdS)Nr)r&rrR)otherrr sz-StdLocalePrinter.to_string..z with "{}={}"zstd::locale = "{}"{}) rVrrr]rr*r4valuesrrrEritems)rrPmodr"ZcatsZncatrRr[Z cat_namesrZ uniq_namesZn1Zn2Z name_listr)rrrgsD            zStdLocalePrinter.to_stringN)r r r r rXrgrrrrrsrc@s eZdZdZddZddZdS)StdIntegralConstantPrinterz*Print a std::true_type or std::false_type.cCs||_||_dS)N)rVr])rrDrWrrrrX sz#StdIntegralConstantPrinter.__init__cCsL|jjjd}|jjjd}|jtjkr4|r0dSdSt|j}dj|||S)Nrr)zstd::true_typezstd::false_typez {}<{}, {}>) rVrr4rrrr;r]r*)rr3rZrDrrrrg s  z$StdIntegralConstantPrinter.to_stringN)r r r r rXrgrrrrr src@s eZdZdZddZddZdS)StdTextEncodingPrinterzPrint a std::text_encoding.cCs||_||_dS)N)rVr])rrDrWrrrrX$ szStdTextEncodingPrinter.__init__cCs<|jdj}|ddkr$|jdS|ddkr4dS|dS)NZ_M_repZ_M_idr)rrrq)rVrt)rZreprrrrg( s    z StdTextEncodingPrinter.to_stringN)r r r r rXrgrrrrr! srcs$eZdZfddZddZZS) RxPrintercs$tt|j||_||_d|_dS)NT)rrrXr" _functionenabled)rr"r)r$rrrX3 szRxPrinter.__init__cCs:|js dS|jjtjkr,ttjdr,|j}|j|j |S)Nreferenced_value) rrrrrrValuerrr")rrZrrrinvoke9 s  zRxPrinter.invoke)r r r rXrr(rr)r$rr2 s rcsHeZdZfddZddZddZddZed d Zd d Z Z S) Printercs6tt|j||_g|_i|_d|_tjd|_ dS)NTz^([a-zA-Z0-9_:]+)(<.*>)?$) rrrXr" _subprinters_lookuprr>compile _compiled_rx)rr")r$rrrXH s zPrinter.__init__cCs<|jj|std|t||}|jj|||j|<dS)Nz0libstdc++ programming error: "%s" does not match)rr?r rrrPr)rr"rrrrraddP s     z Printer.addcCs>|j|||d|kr:tjddt|}|j|||dS)N__cxx11z^(std|__gnu_cxx)::z\g<0>%s)rr>rHr-)rbaser"rZvbaserrr add_version[ s  zPrinter.add_versioncCs$|j||||j|d||dS)Nz __cxx1998::)r)rrr"rrrr add_containerc szPrinter.add_containercCs&|jtjkr|j}|jj}|jS)N)rrrrrrr)rrrrget_basic_typeg s  zPrinter.get_basic_typecCsr|j|j}|sdS|jj|}|s(dS|jd}|jjtjkrTttj drT|j }||j krn|j |j |SdS)Nr)r) rrrr?rrrrrrrrr)rrWrDr?basenamerrr__call__r s     zPrinter.__call__) r r r rXrrrrrrr(rr)r$rrG s    rc@s0eZdZdZddZGdddeZddZdS) TemplateTypePrinteral A type printer for class templates with default template arguments. Recognizes specializations of class templates and prints them without any template arguments that use a default template argument. Type printers are recursively applied to the template arguments. e.g. replace 'std::vector >' with 'std::vector'. cCs||_||_d|_dS)NT)r"_defargsr)rr"defargsrrrrX szTemplateTypePrinter.__init__c@s(eZdZdZddZddZddZdS) zTemplateTypePrinter._recognizerz-The recognizer class for TemplateTypePrinter.cCs||_||_dS)N)r"r)rr"rrrrrX sz(TemplateTypePrinter._recognizer.__init__c Cs|jdkrdS|jj|js dSt|}g}d}xtt|D]}||}|jj|}|dk r|j|}y|t j |kr|dSWn$t j k r|j|krdSYnXd}q>|rdS|j |j |q>W|jd|jjd}t|}|ddj|dS)a If type_obj is a specialization of self.name that uses all the default template arguments for the class template, then return a string representation of the type without default arguments. Otherwise, return None. NFTr)rr<r"rTrangerrrr*rrr,rP_recognize_subtyperrOr+) rrQrSZdisplayed_argsZrequire_defaultedrRrfZdefargrArrr recognize s2     z)TemplateTypePrinter._recognizer.recognizecCs|jtjkr|j|jdS|jtjkrj|j|j}t|jjdrR|dSd||j ddfS|jtj kr|j|jdSt tdr|jtj kr|j|jdStj jtj j|}|r|St|S)zxConvert a gdb.Type to a string by applying recognizers, or if that fails then simply converting to a string.rz[]z%s[%d]r)&TYPE_CODE_RVALUE_REFz&&)rrrzrrrr!rrbrrrrrrr)rrQrNrrrr s"     z2TemplateTypePrinter._recognizer._recognize_subtypeN)r r r r rXrrrrrr _recognizer s1rcCs|j|j|jS)z1Return a recognizer object for this type printer.)rr"r)rrrr instantiate szTemplateTypePrinter.instantiateN)r r r r rXobjectrrrrrrr s Prcstd||}tjj||td||}tjj||d|krdttfdd|jD}t||}tjj||td||}tjj||dS)a Add a type printer for a class template with default template arguments. Args: name (str): The template-name of the class template. defargs (dict int:string) The default template arguments. Types in defargs can refer to the Nth template-argument using {N} (with zero-based indices). e.g. 'unordered_map' has these defargs: { 2: 'std::hash<{0}>', 3: 'std::equal_to<{0}>', 4: 'std::allocator >' } zstd::zstd::__debug::rc3s"|]\}}||jdfVqdS)zstd::N)r.)r&rRr)rWrrr sz0add_one_template_type_printer..N)rrrregister_type_printerr-dictr)objr"rrr)rWradd_one_template_type_printer s rc@s2eZdZdZd ddZGdddeZddZdS) FilteringTypePrintera A type printer that uses typedef names for common template specializations. Args: template (str): The class template to recognize. name (str): The typedef-name that will be used instead. targ1 (str, optional): The first template argument. Defaults to None. Checks if a specialization of the class template 'template' is the same type as the typedef 'name', and prints it as 'name' instead. e.g. if an instantiation of std::basic_istream is the same type as std::istream then print it as std::istream. If targ1 is provided (not None), match only template specializations with this type as the first template argument, e.g. if template='basic_string' and targ1='char' then only match 'basic_string' and not 'basic_string'. This rejects non-matching specializations more quickly, without needing to do GDB type lookups. NcCs||_||_||_d|_dS)NT) _templater"_targ1r)rtemplater"targ1rrrrX- szFilteringTypePrinter.__init__c@s eZdZdZddZddZdS)z FilteringTypePrinter._recognizerz.The recognizer class for FilteringTypePrinter.cCs||_||_||_d|_dS)N)rr"r _type_obj)rrr"rrrrrX6 sz)FilteringTypePrinter._recognizer.__init__c Cs|jdkrdS|jdkrz|jdk rDdj|j|j}|jj|sVdSn|jj|jsVdSytj|jj |_Wn YnX|jdkrdStj j |j}tj j |}||krt |jS|jj dddkr|jjjdd}|jjdd}||krt |jSdS) z If type_obj starts with self._template and is the same type as self.name then return self.name, otherwise None. Nz{}<{}z::r) basic_stringz __cxx11::rCri)rrrr*rr<rrr"rrrrOrer.)rrQr#Zt1Zt2s1s2rrrr< s4        z*FilteringTypePrinter._recognizer.recognizeN)r r r r rXrrrrrr3 srcCs|j|j|j|jS)z1Return a recognizer object for this type printer.)rrr"r)rrrrrf sz FilteringTypePrinter.instantiate)N)r r r r rXrrrrrrrr s 3rcCsXtd|d||}tjj||d|krTdt}t|||||}tjj||dS)Nzstd::r)rrrrr-)rrr"rrrWrrradd_one_type_printerk s rc CsdtsdSxxdgD]p}t|d |d d |dt|d|d d |dt|dd|d d |dt|d|d d|dqWxdjD]|}x*dkD]"}t|d||d ||dqWxJdlD]B}t|d||d ||dt|d!||d ||dqWqWxdmD]}x0dpD](}t||d"||d d#|dqWxNdqD]F}t||d(||d)x&drD]}t||||||qlWqJWqWt|d-d.xdsD]}t|d9d:|qWt|d;d<t|d;d=t|d>d?t|d>d@t|dAdBt|dAdCt|dDdEt|dDdFt|dGdHdI}x0dyD](}t||d||d d|dqdequezstd::allocator<{0}>rrz __cxx11::listvectorrzstd::less<{0}>z)std::allocator>)rrCmultimapr4)r)rmultiset unordered_mapzstd::hash<{0}>zstd::equal_to<{0}>)rrCunordered_multimap unordered_set)r)rrCunordered_multisetrCrErr|rrrrrr)rrrrrrCrErr|)rr) rrrrrrrrr)rrrr)rCrrCrErr|)rr )rr#rr)rrr) rrrrr r r r r rrCrErr|rrrrrr)r!r"r#r$r%)rrr)rZchr@abiZdurrWrrrregister_type_printerst s "(     *                   r'cCs8trtjj|tn|dkr t}|jjtt|dS)z4Register libstdc++ pretty-printers with objfile Obj.N) _use_gdb_pprZprintingZregister_pretty_printerlibstdcxx_printerZpretty_printersrPr')rrrrregister_libstdcxx_printers s  r*cCsXtdatjddttjddttjddttjddttjddttjddttjddttjdd ttjdd t tjdd t tjdd t tjdd t tjddt tjddt tjddt tjddt tjddttjddtttjdr.tjddttjddttjdttjdttjdttjdttjdttjdt tjdt tjdttjddttjdd ttjdd!ttjdd"ttjdd#ttjdd$ttjdd%ttjd&dttjd&d ttjd&d!ttjd&d"ttjd&d#ttjd&d$ttjdd'ttjdd(ttjdd)ttjdd)tx2doD]*}tjdd-|ttjdd-|tqWtjd.d/ttjd.d0ttjdd1ttjd2d3t tjd4ttjd5ttjd6ttjd7ttjd8ttjd9d:t!tjd9d;t"tjd9dt$tjd?d>t$tjd@d>t%tjdAd>t%tjdd:t!tjdd;t"tjddrFrirrGrrrr ziprrrZtimezoneZutcrZtzinforr(Z gdb.printingrTrZ gdb.typesrrrZ printer_baser%r-r2r8r5rBr;rKrOrTrUr[rkrmrprqrurxryrrrrrrrrrrrrrrrrrrrrrrrrrrrrr rrrrr)r-r0r5r7rBrJrLrQrlrsrurvr{r}rrrrrrrrrrrrrrr)rrrrr'r*r;rrrrs      %   (&"I  U(*$/A " /%-^!,5I!{'$*/ ?  ",Ce%T oTlibstdcxx/v6/__pycache__/xmethods.cpython-36.opt-1.pyc000064400000107346152525737710016564 0ustar003 hq@sddlZddlZddlZdZddZddZdZdd ZGd d d ejj Z Gd d d ejj Z Gddde Z Gddde ZGddde ZGddde ZGddde ZGddde ZGdddejjZGdddejj ZGdddeZGd d!d!eZGd"d#d#eZGd$d%d%eZGd&d'd'eZGd(d)d)eZGd*d+d+ejjZGd,d-d-ejjZGd.d/d/eZGd0d1d1eZGd2d3d3ejjZ Gd4d5d5ejj Z!Gd6d7d7e!Z"Gd8d9d9e!Z#Gd:d;d;e!Z$Gdd?d?ejjZ&Gd@dAdAejj Z'GdBdCdCe'Z(GdDdEdEe'Z)GdFdGdGe'Z*GdHdIdIe'Z+GdJdKdKe'Z,GdLdMdMe'Z-GdNdOdOejjZ.GdPdQdQejj Z/GdRdSdSe/Z0GdTdUdUe/Z1GdVdWdWejjZ2GdXdYdYejj Z3GdZd[d[e3Z4Gd\d]d]e3Z5Gd^d_d_ejjZ6Gd`dadaejj Z7Gdbdcdce7Z8Gdddedee7Z9Gdfdgdgejj Z:Gdhdidie:Z;GdjdkdkejjZ$N) isinstancerZTypetag_versioned_namespacerematch)xZ template_namerrris_specialization_of!s  rc@seZdZddZdS)LibStdCxxXMethodcCstjjj||||_dS)N)rxmethodXMethod__init__ worker_class)selfnamerrrrr.szLibStdCxxXMethod.__init__N)__name__ __module__ __qualname__rrrrrr-src@seZdZddZddZdS)ArrayWorkerBasecCs||_||_dS)N) _val_type_size)rval_typesizerrrr6szArrayWorkerBase.__init__cCstjd}|j|jjjS)Nz (void *) 0)rZparse_and_evalcastrpointer dereference)rZnullptrrrr null_value:s zArrayWorkerBase.null_valueN)rrrrr"rrrrr5src@s,eZdZddZddZddZddZd S) ArraySizeWorkercCstj|||dS)N)rr)rrrrrrr@szArraySizeWorker.__init__cCsdS)Nr)rrrr get_arg_typesCszArraySizeWorker.get_arg_typescCstS)N)r)robjrrrget_result_typeFszArraySizeWorker.get_result_typecCs|jS)N)r)rr%rrr__call__IszArraySizeWorker.__call__N)rrrrr$r&r'rrrrr#?sr#c@s,eZdZddZddZddZddZd S) ArrayEmptyWorkercCstj|||dS)N)rr)rrrrrrrNszArrayEmptyWorker.__init__cCsdS)Nr)rrrrr$QszArrayEmptyWorker.get_arg_typescCstS)N)r)rr%rrrr&Tsz ArrayEmptyWorker.get_result_typecCst|jdkS)Nr)intr)rr%rrrr'WszArrayEmptyWorker.__call__N)rrrrr$r&r'rrrrr(Msr(c@s,eZdZddZddZddZddZd S) ArrayFrontWorkercCstj|||dS)N)rr)rrrrrrr\szArrayFrontWorker.__init__cCsdS)Nr)rrrrr$_szArrayFrontWorker.get_arg_typescCs|jS)N)r)rr%rrrr&bsz ArrayFrontWorker.get_result_typecCs&t|jdkr|ddS|jSdS)Nr_M_elems)r)rr")rr%rrrr'es zArrayFrontWorker.__call__N)rrrrr$r&r'rrrrr*[sr*c@s,eZdZddZddZddZddZd S) ArrayBackWorkercCstj|||dS)N)rr)rrrrrrrmszArrayBackWorker.__init__cCsdS)Nr)rrrrr$pszArrayBackWorker.get_arg_typescCs|jS)N)r)rr%rrrr&sszArrayBackWorker.get_result_typecCs,t|jdkr |d|jdS|jSdS)Nrr+)r)rr")rr%rrrr'vszArrayBackWorker.__call__N)rrrrr$r&r'rrrrr,lsr,c@s,eZdZddZddZddZddZd S) ArrayAtWorkercCstj|||dS)N)rr)rrrrrrr~szArrayAtWorker.__init__cCstS)N)r)rrrrr$szArrayAtWorker.get_arg_typescCs|jS)N)r)rr%indexrrrr&szArrayAtWorker.get_result_typecCs4t|t|jkr(tdt||jf|d|S)Nz%Array index "%d" should not be >= %d.r+)r)r IndexError)rr%r/rrrr'szArrayAtWorker.__call__N)rrrrr$r&r'rrrrr.}sr.c@s,eZdZddZddZddZddZd S) ArraySubscriptWorkercCstj|||dS)N)rr)rrrrrrrszArraySubscriptWorker.__init__cCstS)N)r)rrrrr$sz"ArraySubscriptWorker.get_arg_typescCs|jS)N)r)rr%r/rrrr&sz$ArraySubscriptWorker.get_result_typecCs&t|jdkr|d|S|jSdS)Nrr+)r)rr")rr%r/rrrr's zArraySubscriptWorker.__call__N)rrrrr$r&r'rrrrr1sr1c@seZdZddZddZdS)ArrayMethodsMatchercsftjjjtdtdttdttdttdt tdt tdt d_ fd d j D_ dS) Narrayremptyfrontbackatz operator[])rr4r5r6r7z operator[]csg|]}j|qSr) _method_dict).0m)rrr sz0ArrayMethodsMatcher.__init__..)rrXMethodMatcherrmatcher_name_prefixrr#r(r*r,r.r1r8methods)rr)rrrs  zArrayMethodsMatcher.__init__c Cs^t|dsdS|jj|}|dks*|j r.dSy|jd}|jd}Wn dS|j||S)Nr3rr-)rr8getenabledtemplate_argumentr)r class_type method_namemethodZ value_typerrrrr s   zArrayMethodsMatcher.matchN)rrrrr rrrrr2s r2c@s$eZdZddZddZddZdS)DequeWorkerBasecCs||_d|jpd|_dS)Nir-)rsizeof_bufsize)rrrrrrszDequeWorkerBase.__init__cCsf|dd}|dd}|d|dkr,dS|j|d|dd|d|d|d |dS) N_M_impl_M_start _M_finish_M_curr_M_noder-_M_first_M_last)rG)rr%startfinishrrrrs   zDequeWorkerBase.sizecCs`|dd}|d|d}||kr0|d|S||}|ddt||j}|||jS)NrHrIrNrKrLr-)r)rG)rr%idxrOZfirst_node_sizeZ index_noderrrr/s  zDequeWorkerBase.indexN)rrrrrr/rrrrrEs rEc@s$eZdZddZddZddZdS)DequeEmptyWorkercCsdS)Nr)rrrrr$szDequeEmptyWorker.get_arg_typescCstS)N)r)rr%rrrr&sz DequeEmptyWorker.get_result_typecCs |ddd|dddkS)NrHrIrKrJr)rr%rrrr'szDequeEmptyWorker.__call__N)rrrr$r&r'rrrrrRsrRc@s$eZdZddZddZddZdS)DequeSizeWorkercCsdS)Nr)rrrrr$szDequeSizeWorker.get_arg_typescCstS)N)r)rr%rrrr&szDequeSizeWorker.get_result_typecCs |j|S)N)r)rr%rrrr'szDequeSizeWorker.__call__N)rrrr$r&r'rrrrrSsrSc@s$eZdZddZddZddZdS)DequeFrontWorkercCsdS)Nr)rrrrr$szDequeFrontWorker.get_arg_typescCs|jS)N)r)rr%rrrr&sz DequeFrontWorker.get_result_typecCs|ddddS)NrHrIrKrr)rr%rrrr'szDequeFrontWorker.__call__N)rrrr$r&r'rrrrrTsrTc@s$eZdZddZddZddZdS)DequeBackWorkercCsdS)Nr)rrrrr$szDequeBackWorker.get_arg_typescCs|jS)N)r)rr%rrrr&szDequeBackWorker.get_result_typecCs^|ddd|dddkrF|dddd}|d|jdS|ddddSdS) NrHrJrKrMrLr-r)rG)rr% prev_noderrrr's zDequeBackWorker.__call__N)rrrr$r&r'rrrrrUsrUc@s$eZdZddZddZddZdS)DequeSubscriptWorkercCstS)N)r)rrrrr$ sz"DequeSubscriptWorker.get_arg_typescCs|jS)N)r)rr% subscriptrrrr& sz$DequeSubscriptWorker.get_result_typecCs |j||S)N)r/)rr%rYrrrr'szDequeSubscriptWorker.__call__N)rrrr$r&r'rrrrrX srXc@s$eZdZddZddZddZdS) DequeAtWorkercCstS)N)r)rrrrr$szDequeAtWorker.get_arg_typescCs|jS)N)r)rr%r/rrrr&szDequeAtWorker.get_result_typecCs@t|j|}t||kr0tdt||fn |j||SdS)Nz%Deque index "%d" should not be >= %d.)r)rr0r/)rr%r/Z deque_sizerrrr's  zDequeAtWorker.__call__N)rrrr$r&r'rrrrrZsrZc@seZdZddZddZdS)DequeMethodsMatchercsftjjjtdtdttdttdttdt tdt tdt d_ fd d j D_ dS) Ndequer4rr5r6z operator[]r7)r4rr5r6z operator[]r7csg|]}j|qSr)r8)r9r:)rrrr;0sz0DequeMethodsMatcher.__init__..)rrr<rr=rrRrSrTrUrXrZr8r>)rr)rrr%s  zDequeMethodsMatcher.__init__cCs>t|dsdS|jj|}|dks*|j r.dS|j|jdS)Nr\r)rr8r?r@rrA)rrBrCrDrrrr 2s   zDequeMethodsMatcher.matchN)rrrrr rrrrr[$s r[c@seZdZddZddZdS)ForwardListWorkerBasecCs||_||_dS)N)r _node_type)rr node_typerrrr>szForwardListWorkerBase.__init__cCsdS)Nr)rrrrr$Bsz#ForwardListWorkerBase.get_arg_typesN)rrrrr$rrrrr]=sr]c@seZdZddZddZdS)ForwardListEmptyWorkercCstS)N)r)rr%rrrr&Gsz&ForwardListEmptyWorker.get_result_typecCs|ddddkS)NrH_M_head_M_nextrr)rr%rrrr'JszForwardListEmptyWorker.__call__N)rrrr&r'rrrrr`Fsr`c@seZdZddZddZdS)ForwardListFrontWorkercCs|jS)N)r)rr%rrrr&Osz&ForwardListFrontWorker.get_result_typecCs:|dddj|j}|ddj}|j|jjjS)NrHrarb _M_storage)rr^addressrr r!)rr%nodeZ val_addressrrrr'RszForwardListFrontWorker.__call__N)rrrr&r'rrrrrcNsrcc@seZdZddZddZdS)ForwardListMethodsMatchercsJtd}tjjj|tdttdtd_fddjD_ dS)N forward_listr4r5)r4r5csg|]}j|qSr)r8)r9r:)rrrr;`sz6ForwardListMethodsMatcher.__init__..) r=rrr<rrr`rcr8r>)rZ matcher_namer)rrrYs z"ForwardListMethodsMatcher.__init__cCsZt|dsdS|jj|}|dks*|j r.dS|jd}tjt|dj}|j ||S)Nrhrz::_Node) rr8r?r@rArrstrr r)rrBrCrDrr_rrrr bs   zForwardListMethodsMatcher.matchN)rrrrr rrrrrgXs rgc@s$eZdZddZddZddZdS)ListWorkerBasecCs||_||_dS)N)rr^)rrr_rrrrpszListWorkerBase.__init__cCsdS)Nr)rrrrr$tszListWorkerBase.get_arg_typescCsB|j}|jjdjdkr$|dS|dj}|j|jjjS)Nr-Z_M_datard)r!typefieldsrrerrr )rrfZaddrrrrget_value_from_nodews  z"ListWorkerBase.get_value_from_nodeN)rrrrr$rmrrrrrjosrjc@seZdZddZddZdS)ListEmptyWorkercCstS)N)r)rr%rrrr&szListEmptyWorker.get_result_typecCs&|dd}|d|jkrdSdSdS)NrHrLrbTF)re)rr%Z base_noderrrr's zListEmptyWorker.__call__N)rrrr&r'rrrrrnsrnc@seZdZddZddZdS)ListSizeWorkercCstS)N)r)rr%rrrr&szListSizeWorker.get_result_typecCsD|ddd}|ddj}d}x||kr>|d}|d7}q$W|S)NrHrLrbrr-)re)rr%Z begin_nodeZend_noderrrrr's  zListSizeWorker.__call__N)rrrr&r'rrrrrosroc@seZdZddZddZdS)ListFrontWorkercCs|jS)N)r)rr%rrrr&szListFrontWorker.get_result_typecCs"|dddj|j}|j|S)NrHrLrb)rr^rm)rr%rfrrrr'szListFrontWorker.__call__N)rrrr&r'rrrrrpsrpc@seZdZddZddZdS)ListBackWorkercCs|jS)N)r)rr%rrrr&szListBackWorker.get_result_typecCs"|dddj|j}|j|S)NrHrLZ_M_prev)rr^rm)rr%rWrrrr'szListBackWorker.__call__N)rrrr&r'rrrrrqsrqc@seZdZddZddZdS)ListMethodsMatchercsVtjjjtdtdttdttdttdt d_ fddj D_ dS) Nlistr4rr5r6)r4rr5r6csg|]}j|qSr)r8)r9r:)rrrr;sz/ListMethodsMatcher.__init__..) rrr<rr=rrnrorprqr8r>)rr)rrrs  zListMethodsMatcher.__init__cCsZt|dsdS|jj|}|dks*|j r.dS|jd}tjt|dj}|j ||S)Nz(__cxx11::)?listrz::_Node) rr8r?r@rArrrir r)rrBrCrDrr_rrrr s   zListMethodsMatcher.matchN)rrrrr rrrrrrs rrc@s$eZdZddZddZddZdS)VectorWorkerBasecCs ||_dS)N)r)rrrrrrszVectorWorkerBase.__init__cCsz|jjtjkr^|ddd}|ddd}|ddd}|jjjd}||||S|dd|ddSdS)NrHrI_M_prJZ _M_offset)rcoderTYPE_CODE_BOOLr!rkrF)rr%rOrPZ finish_offsetbit_sizerrrrszVectorWorkerBase.sizecCsj|jjtjkrV|ddd}|jjjd}|||}||}|jd|>@dkS|dd|SdS)NrHrIrurvr-r)rrwrrxr!rkrF)rr%r/rOryZvalpoffsetrrrr?s zVectorWorkerBase.getN)rrrrrr?rrrrrts rtc@s$eZdZddZddZddZdS)VectorEmptyWorkercCsdS)Nr)rrrrr$szVectorEmptyWorker.get_arg_typescCstS)N)r)rr%rrrr&sz!VectorEmptyWorker.get_result_typecCst|j|dkS)Nr)r)r)rr%rrrr'szVectorEmptyWorker.__call__N)rrrr$r&r'rrrrr{sr{c@s$eZdZddZddZddZdS)VectorSizeWorkercCsdS)Nr)rrrrr$szVectorSizeWorker.get_arg_typescCstS)N)r)rr%rrrr&sz VectorSizeWorker.get_result_typecCs |j|S)N)r)rr%rrrr'szVectorSizeWorker.__call__N)rrrr$r&r'rrrrr|sr|c@s$eZdZddZddZddZdS)VectorFrontWorkercCsdS)Nr)rrrrr$szVectorFrontWorker.get_arg_typescCs|jS)N)r)rr%rrrr&sz!VectorFrontWorker.get_result_typecCs |j|dS)Nr)r?)rr%rrrr'szVectorFrontWorker.__call__N)rrrr$r&r'rrrrr}sr}c@s$eZdZddZddZddZdS)VectorBackWorkercCsdS)Nr)rrrrr$szVectorBackWorker.get_arg_typescCs|jS)N)r)rr%rrrr&sz VectorBackWorker.get_result_typecCs|j|t|j|dS)Nr-)r?r)r)rr%rrrr'szVectorBackWorker.__call__N)rrrr$r&r'rrrrr~sr~c@s$eZdZddZddZddZdS)VectorAtWorkercCstS)N)r)rrrrr$ szVectorAtWorker.get_arg_typescCs|jS)N)r)rr%r/rrrr&szVectorAtWorker.get_result_typecCs>t|j|}t||kr.tdt||f|j|t|S)Nz&Vector index "%d" should not be >= %d.)r)rr0r?)rr%r/rrrrr's  zVectorAtWorker.__call__N)rrrr$r&r'rrrrr src@s$eZdZddZddZddZdS)VectorSubscriptWorkercCstS)N)r)rrrrr$sz#VectorSubscriptWorker.get_arg_typescCs|jS)N)r)rr%rYrrrr&sz%VectorSubscriptWorker.get_result_typecCs|j|t|S)N)r?r))rr%rYrrrr'!szVectorSubscriptWorker.__call__N)rrrr$r&r'rrrrrsrc@seZdZddZddZdS)VectorMethodsMatchercsftjjjtdtdttdttdttdt tdt tdt d_ fd d j D_ dS) Nvectorrr4r5r6r7z operator[])rr4r5r6r7z operator[]csg|]}j|qSr)r8)r9r:)rrrr;2sz1VectorMethodsMatcher.__init__..)rrr<rr=rr|r{r}r~rrr8r>)rr)rrr&s   zVectorMethodsMatcher.__init__cCs>t|dsdS|jj|}|dks*|j r.dS|j|jdS)Nrr)rr8r?r@rrA)rrBrCrDrrrr 4s   zVectorMethodsMatcher.matchN)rrrrr rrrrr%src@s$eZdZddZddZddZdS)AssociativeContainerWorkerBasecCs ||_dS)N) _unordered)r unorderedrrrr@sz'AssociativeContainerWorkerBase.__init__cCs&|jr|ddS|dddSdS)NZ_M_hZ_M_element_count_M_trHZ _M_node_count)r)rr%rrr node_countCs z)AssociativeContainerWorkerBase.node_countcCsdS)Nr)rrrrr$Isz,AssociativeContainerWorkerBase.get_arg_typesN)rrrrrr$rrrrr?src@seZdZddZddZdS)AssociativeContainerEmptyWorkercCstS)N)r)rr%rrrr&Nsz/AssociativeContainerEmptyWorker.get_result_typecCst|j|dkS)Nr)r)r)rr%rrrr'Qsz(AssociativeContainerEmptyWorker.__call__N)rrrr&r'rrrrrMsrc@seZdZddZddZdS)AssociativeContainerSizeWorkercCstS)N)r)rr%rrrr&Vsz.AssociativeContainerSizeWorker.get_result_typecCs |j|S)N)r)rr%rrrr'Ysz'AssociativeContainerSizeWorker.__call__N)rrrr&r'rrrrrUsrc@seZdZddZddZdS)"AssociativeContainerMethodsMatchercsLtjjjt||_tdttdtd_ fddj D_ dS)Nrr4)rr4csg|]}j|qSr)r8)r9r:)rrrr;gsz?AssociativeContainerMethodsMatcher.__init__..) rrr<rr=_namerrrr8r>)rrr)rrr^s   z+AssociativeContainerMethodsMatcher.__init__cCsDt||jsdS|jj|}|dks,|j r0dSd|jk}|j|S)Nr)rrr8r?r@r)rrBrCrDrrrrr is   z(AssociativeContainerMethodsMatcher.matchN)rrrrr rrrrr]s rc@s8eZdZdZddZddZddZdd Zd d Zd S) UniquePtrGetWorkerzS Implement std::unique_ptr::get() and std::unique_ptr::operator->(). cCs*|jtjk|_|jr |j|_n||_dS)N)rwrTYPE_CODE_ARRAY _is_arraytarget _elem_type)r elem_typerrrrzs zUniquePtrGetWorker.__init__cCsdS)Nr)rrrrr$sz UniquePtrGetWorker.get_arg_typescCs |jjS)N)rr )rr%rrrr&sz"UniquePtrGetWorker.get_result_typecCs|dkp|j S)Nr?)r)rrCrrr _supportsszUniquePtrGetWorker._supportscCs|jjjdjj}t|dr.|dd}nt|drB|d}ndS|jjdj}|jdj}|jd}|jdkr|j|dS|jr|j|jSdSdS)Nrz__uniq_ptr_(data|impl)rtupler-Z _M_head_impl)r!rkrlr rrrZ is_base_class)rr%Z impl_typeZ tuple_memberZtuple_impl_typeZtuple_head_typeZ head_fieldrrrr's      zUniquePtrGetWorker.__call__N) rrr__doc__rr$r&rr'rrrrrus rc@s0eZdZdZddZddZddZdd Zd S) UniquePtrDerefWorkerz*Implement std::unique_ptr::operator*().cCstj||dS)N)rr)rrrrrrszUniquePtrDerefWorker.__init__cCs|jS)N)r)rr%rrrr&sz$UniquePtrDerefWorker.get_result_typecCs|j S)N)r)rrCrrrrszUniquePtrDerefWorker._supportscCstj||jS)N)rr'r!)rr%rrrr'szUniquePtrDerefWorker.__call__N)rrrrrr&rr'rrrrrs rc@s8eZdZdZddZddZddZdd Zd d Zd S) UniquePtrSubscriptWorkerz1Implement std::unique_ptr::operator[](size_t).cCstj||dS)N)rr)rrrrrrsz!UniquePtrSubscriptWorker.__init__cCstS)N)r)rrrrr$sz&UniquePtrSubscriptWorker.get_arg_typescCs|jS)N)r)rr%r/rrrr&sz(UniquePtrSubscriptWorker.get_result_typecCs|jS)N)r)rrCrrrrsz"UniquePtrSubscriptWorker._supportscCstj|||S)N)rr')rr%r/rrrr'sz!UniquePtrSubscriptWorker.__call__N) rrrrrr$r&rr'rrrrrs rc@seZdZddZddZdS)UniquePtrMethodsMatchercsVtjjjtdtdttdttdttdtd_ fddj D_ dS) N unique_ptrr?z operator->z operator*z operator[])r?z operator->z operator*z operator[]csg|]}j|qSr)r8)r9r:)rrrr;sz4UniquePtrMethodsMatcher.__init__..) rrr<rr=rrrrr8r>)rr)rrrs  z UniquePtrMethodsMatcher.__init__cCsPt|dsdS|jj|}|dks*|j r.dS|j|jd}|j|rL|SdS)Nrr)rr8r?r@rrAr)rrBrCrDworkerrrrr s   zUniquePtrMethodsMatcher.matchN)rrrrr rrrrrs rc@s8eZdZdZddZddZddZdd Zd d Zd S) SharedPtrGetWorkerzT Implements std::shared_ptr::get() and std::shared_ptr::operator->(). cCs*|jtjk|_|jr |j|_n||_dS)N)rwrrrrr)rrrrrrs zSharedPtrGetWorker.__init__cCsdS)Nr)rrrrr$sz SharedPtrGetWorker.get_arg_typescCs |jjS)N)rr )rr%rrrr&sz"SharedPtrGetWorker.get_result_typecCs|dkp|j S)Nr?)r)rrCrrrrszSharedPtrGetWorker._supportscCs|dS)NZ_M_ptrr)rr%rrrr'szSharedPtrGetWorker.__call__N) rrrrrr$r&rr'rrrrrs rc@s0eZdZdZddZddZddZdd Zd S) SharedPtrDerefWorkerz*Implement std::shared_ptr::operator*().cCstj||dS)N)rr)rrrrrrszSharedPtrDerefWorker.__init__cCs|jS)N)r)rr%rrrr&sz$SharedPtrDerefWorker.get_result_typecCs|j S)N)r)rrCrrrrszSharedPtrDerefWorker._supportscCstj||jS)N)rr'r!)rr%rrrr'szSharedPtrDerefWorker.__call__N)rrrrrr&rr'rrrrrs rc@s8eZdZdZddZddZddZdd Zd d Zd S) SharedPtrSubscriptWorkerz1Implement std::shared_ptr::operator[](size_t).cCstj||dS)N)rr)rrrrrr sz!SharedPtrSubscriptWorker.__init__cCstS)N)r)rrrrr$sz&SharedPtrSubscriptWorker.get_arg_typescCs|jS)N)r)rr%r/rrrr&sz(SharedPtrSubscriptWorker.get_result_typecCs|jS)N)r)rrCrrrrsz"SharedPtrSubscriptWorker._supportscCsZtjdt|j}|rJ|t|jdkrJtd|jt|t|jdftj|||S)Nz .*\[(\d+)]$r-z.shared_ptr<%s> index "%d" should not be >= %d.) r r rirr)groupr0rr')rr%r/r:rrrr's z!SharedPtrSubscriptWorker.__call__N) rrrrrr$r&rr'rrrrr s rc@s8eZdZdZddZddZddZdd Zd d Zd S) SharedPtrUseCountWorkerz*Implement std::shared_ptr::use_count().cCsdS)Nr)rrrrrr%sz SharedPtrUseCountWorker.__init__cCsdS)Nr)rrrrr$(sz%SharedPtrUseCountWorker.get_arg_typescCs tjdS)NZlong)rr)rr%rrrr&+sz'SharedPtrUseCountWorker.get_result_typecCsdS)NTr)rrCrrrr.sz!SharedPtrUseCountWorker._supportscCs|dd}|r|dSdS)NZ _M_refcountZ_M_piZ _M_use_countrr)rr%Z refcountsrrrr'1s z SharedPtrUseCountWorker.__call__N) rrrrrr$r&rr'rrrrr"s rc@s(eZdZdZddZddZddZdS) SharedPtrUniqueWorkerz'Implement std::shared_ptr::unique().cCstj||dS)N)rr)rrrrrr9szSharedPtrUniqueWorker.__init__cCs tjdS)Nr)rr)rr%rrrr&<sz%SharedPtrUniqueWorker.get_result_typecCstj||dkS)Nr-)rr')rr%rrrr'?szSharedPtrUniqueWorker.__call__N)rrrrrr&r'rrrrr6src@seZdZddZddZdS)SharedPtrMethodsMatchercsftjjjtdtdttdttdttdttdt tdt d_ fd d j D_ dS) N shared_ptrr?z operator->z operator*z operator[] use_countunique)r?z operator->z operator*z operator[]rrcsg|]}j|qSr)r8)r9r:)rrrr;Osz4SharedPtrMethodsMatcher.__init__..) rrr<rr=rrrrrrr8r>)rr)rrrDs  z SharedPtrMethodsMatcher.__init__cCsPt|dsdS|jj|}|dks*|j r.dS|j|jd}|j|rL|SdS)Nrr)rr8r?r@rrAr)rrBrCrDrrrrr Qs   zSharedPtrMethodsMatcher.matchN)rrrrr rrrrrCs rcCstjj|ttjj|ttjj|ttjj|ttjj|ttjj|tdtjj|tdtjj|tdtjj|tdtjj|tdtjj|tdtjj|tdtjj|tdtjj|t tjj|t dS) NsetmapZmultisetZmultimapZ unordered_setZ unordered_mapZunordered_multisetZunordered_multimap) rrZregister_xmethod_matcherr2rgr[rrrrrr)Zlocusrrrregister_libstdcxx_xmethods]s.        r)>rZ gdb.xmethodr r=rrr rrrrZ XMethodWorkerrr#r(r*r,r.r1r<r2rErRrSrTrUrXrZr[r]r`rcrgrjrnrorprqrrrtr{r|r}r~rrrrrrrrrrrrrrrrrrrrrrsr                * libstdcxx/v6/__pycache__/__init__.cpython-36.pyc000064400000001066152525737710015521 0ustar003 h}@sddZddZdS)c Cs&y ddl}dStk r dSXdS)NTF)Z gdb.xmethod ImportError)Zgdbr0/usr/share/gcc-8/python/libstdcxx/v6/__init__.pygdb_has_xmethodss rcCs2ddlm}||tr.ddlm}||dS)N)register_libstdcxx_printers)register_libstdcxx_xmethods)ZprintersrrZxmethodsr)objrrrrrrs   rN)rrrrrrslibstdcxx/v6/xmethods.py000064400000070417152525737710011337 0ustar00# Xmethods for libstdc++. # Copyright (C) 2014-2025 Free Software Foundation, Inc. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . import gdb import gdb.xmethod import re matcher_name_prefix = 'libstdc++::' def get_bool_type(): return gdb.lookup_type('bool') def get_std_size_type(): return gdb.lookup_type('std::size_t') _versioned_namespace = '__8::' def is_specialization_of(x, template_name): """ Test whether a type is a specialization of the named class template. The type can be specified as a string or a gdb.Type object. The template should be the name of a class template as a string, without any 'std' qualification. """ if isinstance(x, gdb.Type): x = x.tag template_name = '(%s)?%s' % (_versioned_namespace, template_name) return re.match(r'^std::(__\d::)?%s<.*>$' % template_name, x) is not None class LibStdCxxXMethod(gdb.xmethod.XMethod): def __init__(self, name, worker_class): gdb.xmethod.XMethod.__init__(self, name) self.worker_class = worker_class # Xmethods for std::array class ArrayWorkerBase(gdb.xmethod.XMethodWorker): def __init__(self, val_type, size): self._val_type = val_type self._size = size def null_value(self): nullptr = gdb.parse_and_eval('(void *) 0') return nullptr.cast(self._val_type.pointer()).dereference() class ArraySizeWorker(ArrayWorkerBase): def __init__(self, val_type, size): ArrayWorkerBase.__init__(self, val_type, size) def get_arg_types(self): return None def get_result_type(self, obj): return get_std_size_type() def __call__(self, obj): return self._size class ArrayEmptyWorker(ArrayWorkerBase): def __init__(self, val_type, size): ArrayWorkerBase.__init__(self, val_type, size) def get_arg_types(self): return None def get_result_type(self, obj): return get_bool_type() def __call__(self, obj): return (int(self._size) == 0) class ArrayFrontWorker(ArrayWorkerBase): def __init__(self, val_type, size): ArrayWorkerBase.__init__(self, val_type, size) def get_arg_types(self): return None def get_result_type(self, obj): return self._val_type def __call__(self, obj): if int(self._size) > 0: return obj['_M_elems'][0] else: return self.null_value() class ArrayBackWorker(ArrayWorkerBase): def __init__(self, val_type, size): ArrayWorkerBase.__init__(self, val_type, size) def get_arg_types(self): return None def get_result_type(self, obj): return self._val_type def __call__(self, obj): if int(self._size) > 0: return obj['_M_elems'][self._size - 1] else: return self.null_value() class ArrayAtWorker(ArrayWorkerBase): def __init__(self, val_type, size): ArrayWorkerBase.__init__(self, val_type, size) def get_arg_types(self): return get_std_size_type() def get_result_type(self, obj, index): return self._val_type def __call__(self, obj, index): if int(index) >= int(self._size): raise IndexError('Array index "%d" should not be >= %d.' % ((int(index), self._size))) return obj['_M_elems'][index] class ArraySubscriptWorker(ArrayWorkerBase): def __init__(self, val_type, size): ArrayWorkerBase.__init__(self, val_type, size) def get_arg_types(self): return get_std_size_type() def get_result_type(self, obj, index): return self._val_type def __call__(self, obj, index): if int(self._size) > 0: return obj['_M_elems'][index] else: return self.null_value() class ArrayMethodsMatcher(gdb.xmethod.XMethodMatcher): def __init__(self): gdb.xmethod.XMethodMatcher.__init__(self, matcher_name_prefix + 'array') self._method_dict = { 'size': LibStdCxxXMethod('size', ArraySizeWorker), 'empty': LibStdCxxXMethod('empty', ArrayEmptyWorker), 'front': LibStdCxxXMethod('front', ArrayFrontWorker), 'back': LibStdCxxXMethod('back', ArrayBackWorker), 'at': LibStdCxxXMethod('at', ArrayAtWorker), 'operator[]': LibStdCxxXMethod('operator[]', ArraySubscriptWorker), } self.methods = [self._method_dict[m] for m in self._method_dict] def match(self, class_type, method_name): if not is_specialization_of(class_type, 'array'): return None method = self._method_dict.get(method_name) if method is None or not method.enabled: return None try: value_type = class_type.template_argument(0) size = class_type.template_argument(1) except: return None return method.worker_class(value_type, size) # Xmethods for std::deque class DequeWorkerBase(gdb.xmethod.XMethodWorker): def __init__(self, val_type): self._val_type = val_type self._bufsize = 512 // val_type.sizeof or 1 def size(self, obj): start = obj['_M_impl']['_M_start'] finish = obj['_M_impl']['_M_finish'] if start['_M_cur'] == finish['_M_cur']: return 0 return (self._bufsize * (finish['_M_node'] - start['_M_node'] - 1) + (finish['_M_cur'] - finish['_M_first']) + (start['_M_last'] - start['_M_cur'])) def index(self, obj, idx): start = obj['_M_impl']['_M_start'] first_node_size = start['_M_last'] - start['_M_cur'] if idx < first_node_size: return start['_M_cur'][idx] idx = idx - first_node_size index_node = start['_M_node'][1 + int(idx) // self._bufsize] return index_node[idx % self._bufsize] class DequeEmptyWorker(DequeWorkerBase): def get_arg_types(self): return None def get_result_type(self, obj): return get_bool_type() def __call__(self, obj): return (obj['_M_impl']['_M_start']['_M_cur'] == obj['_M_impl']['_M_finish']['_M_cur']) class DequeSizeWorker(DequeWorkerBase): def get_arg_types(self): return None def get_result_type(self, obj): return get_std_size_type() def __call__(self, obj): return self.size(obj) class DequeFrontWorker(DequeWorkerBase): def get_arg_types(self): return None def get_result_type(self, obj): return self._val_type def __call__(self, obj): return obj['_M_impl']['_M_start']['_M_cur'][0] class DequeBackWorker(DequeWorkerBase): def get_arg_types(self): return None def get_result_type(self, obj): return self._val_type def __call__(self, obj): if (obj['_M_impl']['_M_finish']['_M_cur'] == obj['_M_impl']['_M_finish']['_M_first']): prev_node = obj['_M_impl']['_M_finish']['_M_node'] - 1 return prev_node[0][self._bufsize - 1] else: return obj['_M_impl']['_M_finish']['_M_cur'][-1] class DequeSubscriptWorker(DequeWorkerBase): def get_arg_types(self): return get_std_size_type() def get_result_type(self, obj, subscript): return self._val_type def __call__(self, obj, subscript): return self.index(obj, subscript) class DequeAtWorker(DequeWorkerBase): def get_arg_types(self): return get_std_size_type() def get_result_type(self, obj, index): return self._val_type def __call__(self, obj, index): deque_size = int(self.size(obj)) if int(index) >= deque_size: raise IndexError('Deque index "%d" should not be >= %d.' % (int(index), deque_size)) else: return self.index(obj, index) class DequeMethodsMatcher(gdb.xmethod.XMethodMatcher): def __init__(self): gdb.xmethod.XMethodMatcher.__init__(self, matcher_name_prefix + 'deque') self._method_dict = { 'empty': LibStdCxxXMethod('empty', DequeEmptyWorker), 'size': LibStdCxxXMethod('size', DequeSizeWorker), 'front': LibStdCxxXMethod('front', DequeFrontWorker), 'back': LibStdCxxXMethod('back', DequeBackWorker), 'operator[]': LibStdCxxXMethod('operator[]', DequeSubscriptWorker), 'at': LibStdCxxXMethod('at', DequeAtWorker) } self.methods = [self._method_dict[m] for m in self._method_dict] def match(self, class_type, method_name): if not is_specialization_of(class_type, 'deque'): return None method = self._method_dict.get(method_name) if method is None or not method.enabled: return None return method.worker_class(class_type.template_argument(0)) # Xmethods for std::forward_list class ForwardListWorkerBase(gdb.xmethod.XMethodMatcher): def __init__(self, val_type, node_type): self._val_type = val_type self._node_type = node_type def get_arg_types(self): return None class ForwardListEmptyWorker(ForwardListWorkerBase): def get_result_type(self, obj): return get_bool_type() def __call__(self, obj): return obj['_M_impl']['_M_head']['_M_next'] == 0 class ForwardListFrontWorker(ForwardListWorkerBase): def get_result_type(self, obj): return self._val_type def __call__(self, obj): node = obj['_M_impl']['_M_head']['_M_next'].cast(self._node_type) val_address = node['_M_storage']['_M_storage'].address return val_address.cast(self._val_type.pointer()).dereference() class ForwardListMethodsMatcher(gdb.xmethod.XMethodMatcher): def __init__(self): matcher_name = matcher_name_prefix + 'forward_list' gdb.xmethod.XMethodMatcher.__init__(self, matcher_name) self._method_dict = { 'empty': LibStdCxxXMethod('empty', ForwardListEmptyWorker), 'front': LibStdCxxXMethod('front', ForwardListFrontWorker) } self.methods = [self._method_dict[m] for m in self._method_dict] def match(self, class_type, method_name): if not is_specialization_of(class_type, 'forward_list'): return None method = self._method_dict.get(method_name) if method is None or not method.enabled: return None val_type = class_type.template_argument(0) node_type = gdb.lookup_type(str(class_type) + '::_Node').pointer() return method.worker_class(val_type, node_type) # Xmethods for std::list class ListWorkerBase(gdb.xmethod.XMethodWorker): def __init__(self, val_type, node_type): self._val_type = val_type self._node_type = node_type def get_arg_types(self): return None def get_value_from_node(self, node): node = node.dereference() if node.type.fields()[1].name == '_M_data': # C++03 implementation, node contains the value as a member return node['_M_data'] # C++11 implementation, node stores value in __aligned_membuf addr = node['_M_storage'].address return addr.cast(self._val_type.pointer()).dereference() class ListEmptyWorker(ListWorkerBase): def get_result_type(self, obj): return get_bool_type() def __call__(self, obj): base_node = obj['_M_impl']['_M_node'] if base_node['_M_next'] == base_node.address: return True else: return False class ListSizeWorker(ListWorkerBase): def get_result_type(self, obj): return get_std_size_type() def __call__(self, obj): begin_node = obj['_M_impl']['_M_node']['_M_next'] end_node = obj['_M_impl']['_M_node'].address size = 0 while begin_node != end_node: begin_node = begin_node['_M_next'] size += 1 return size class ListFrontWorker(ListWorkerBase): def get_result_type(self, obj): return self._val_type def __call__(self, obj): node = obj['_M_impl']['_M_node']['_M_next'].cast(self._node_type) return self.get_value_from_node(node) class ListBackWorker(ListWorkerBase): def get_result_type(self, obj): return self._val_type def __call__(self, obj): prev_node = obj['_M_impl']['_M_node']['_M_prev'].cast(self._node_type) return self.get_value_from_node(prev_node) class ListMethodsMatcher(gdb.xmethod.XMethodMatcher): def __init__(self): gdb.xmethod.XMethodMatcher.__init__(self, matcher_name_prefix + 'list') self._method_dict = { 'empty': LibStdCxxXMethod('empty', ListEmptyWorker), 'size': LibStdCxxXMethod('size', ListSizeWorker), 'front': LibStdCxxXMethod('front', ListFrontWorker), 'back': LibStdCxxXMethod('back', ListBackWorker) } self.methods = [self._method_dict[m] for m in self._method_dict] def match(self, class_type, method_name): if not is_specialization_of(class_type, '(__cxx11::)?list'): return None method = self._method_dict.get(method_name) if method is None or not method.enabled: return None val_type = class_type.template_argument(0) node_type = gdb.lookup_type(str(class_type) + '::_Node').pointer() return method.worker_class(val_type, node_type) # Xmethods for std::vector class VectorWorkerBase(gdb.xmethod.XMethodWorker): def __init__(self, val_type): self._val_type = val_type def size(self, obj): if self._val_type.code == gdb.TYPE_CODE_BOOL: start = obj['_M_impl']['_M_start']['_M_p'] finish = obj['_M_impl']['_M_finish']['_M_p'] finish_offset = obj['_M_impl']['_M_finish']['_M_offset'] bit_size = start.dereference().type.sizeof * 8 return (finish - start) * bit_size + finish_offset else: return obj['_M_impl']['_M_finish'] - obj['_M_impl']['_M_start'] def get(self, obj, index): if self._val_type.code == gdb.TYPE_CODE_BOOL: start = obj['_M_impl']['_M_start']['_M_p'] bit_size = start.dereference().type.sizeof * 8 valp = start + index // bit_size offset = index % bit_size return (valp.dereference() & (1 << offset)) > 0 else: return obj['_M_impl']['_M_start'][index] class VectorEmptyWorker(VectorWorkerBase): def get_arg_types(self): return None def get_result_type(self, obj): return get_bool_type() def __call__(self, obj): return int(self.size(obj)) == 0 class VectorSizeWorker(VectorWorkerBase): def get_arg_types(self): return None def get_result_type(self, obj): return get_std_size_type() def __call__(self, obj): return self.size(obj) class VectorFrontWorker(VectorWorkerBase): def get_arg_types(self): return None def get_result_type(self, obj): return self._val_type def __call__(self, obj): return self.get(obj, 0) class VectorBackWorker(VectorWorkerBase): def get_arg_types(self): return None def get_result_type(self, obj): return self._val_type def __call__(self, obj): return self.get(obj, int(self.size(obj)) - 1) class VectorAtWorker(VectorWorkerBase): def get_arg_types(self): return get_std_size_type() def get_result_type(self, obj, index): return self._val_type def __call__(self, obj, index): size = int(self.size(obj)) if int(index) >= size: raise IndexError('Vector index "%d" should not be >= %d.' % ((int(index), size))) return self.get(obj, int(index)) class VectorSubscriptWorker(VectorWorkerBase): def get_arg_types(self): return get_std_size_type() def get_result_type(self, obj, subscript): return self._val_type def __call__(self, obj, subscript): return self.get(obj, int(subscript)) class VectorMethodsMatcher(gdb.xmethod.XMethodMatcher): def __init__(self): gdb.xmethod.XMethodMatcher.__init__(self, matcher_name_prefix + 'vector') self._method_dict = { 'size': LibStdCxxXMethod('size', VectorSizeWorker), 'empty': LibStdCxxXMethod('empty', VectorEmptyWorker), 'front': LibStdCxxXMethod('front', VectorFrontWorker), 'back': LibStdCxxXMethod('back', VectorBackWorker), 'at': LibStdCxxXMethod('at', VectorAtWorker), 'operator[]': LibStdCxxXMethod('operator[]', VectorSubscriptWorker), } self.methods = [self._method_dict[m] for m in self._method_dict] def match(self, class_type, method_name): if not is_specialization_of(class_type, 'vector'): return None method = self._method_dict.get(method_name) if method is None or not method.enabled: return None return method.worker_class(class_type.template_argument(0)) # Xmethods for associative containers class AssociativeContainerWorkerBase(gdb.xmethod.XMethodWorker): def __init__(self, unordered): self._unordered = unordered def node_count(self, obj): if self._unordered: return obj['_M_h']['_M_element_count'] else: return obj['_M_t']['_M_impl']['_M_node_count'] def get_arg_types(self): return None class AssociativeContainerEmptyWorker(AssociativeContainerWorkerBase): def get_result_type(self, obj): return get_bool_type() def __call__(self, obj): return int(self.node_count(obj)) == 0 class AssociativeContainerSizeWorker(AssociativeContainerWorkerBase): def get_result_type(self, obj): return get_std_size_type() def __call__(self, obj): return self.node_count(obj) class AssociativeContainerMethodsMatcher(gdb.xmethod.XMethodMatcher): def __init__(self, name): gdb.xmethod.XMethodMatcher.__init__(self, matcher_name_prefix + name) self._name = name self._method_dict = { 'size': LibStdCxxXMethod('size', AssociativeContainerSizeWorker), 'empty': LibStdCxxXMethod('empty', AssociativeContainerEmptyWorker), } self.methods = [self._method_dict[m] for m in self._method_dict] def match(self, class_type, method_name): if not is_specialization_of(class_type, self._name): return None method = self._method_dict.get(method_name) if method is None or not method.enabled: return None unordered = 'unordered' in self._name return method.worker_class(unordered) # Xmethods for std::unique_ptr class UniquePtrGetWorker(gdb.xmethod.XMethodWorker): """ Implement std::unique_ptr::get() and std::unique_ptr::operator->(). """ def __init__(self, elem_type): self._is_array = elem_type.code == gdb.TYPE_CODE_ARRAY if self._is_array: self._elem_type = elem_type.target() else: self._elem_type = elem_type def get_arg_types(self): return None def get_result_type(self, obj): return self._elem_type.pointer() def _supports(self, method_name): # operator-> is not supported for unique_ptr return method_name == 'get' or not self._is_array def __call__(self, obj): impl_type = obj.dereference().type.fields()[0].type.tag # Check for new implementations first: if is_specialization_of(impl_type, '__uniq_ptr_(data|impl)'): tuple_member = obj['_M_t']['_M_t'] elif is_specialization_of(impl_type, 'tuple'): tuple_member = obj['_M_t'] else: return None tuple_impl_type = tuple_member.type.fields()[0].type # _Tuple_impl tuple_head_type = tuple_impl_type.fields()[1].type # _Head_base head_field = tuple_head_type.fields()[0] if head_field.name == '_M_head_impl': return tuple_member.cast(tuple_head_type)['_M_head_impl'] elif head_field.is_base_class: return tuple_member.cast(head_field.type) else: return None class UniquePtrDerefWorker(UniquePtrGetWorker): """Implement std::unique_ptr::operator*().""" def __init__(self, elem_type): UniquePtrGetWorker.__init__(self, elem_type) def get_result_type(self, obj): return self._elem_type def _supports(self, method_name): # operator* is not supported for unique_ptr return not self._is_array def __call__(self, obj): return UniquePtrGetWorker.__call__(self, obj).dereference() class UniquePtrSubscriptWorker(UniquePtrGetWorker): """Implement std::unique_ptr::operator[](size_t).""" def __init__(self, elem_type): UniquePtrGetWorker.__init__(self, elem_type) def get_arg_types(self): return get_std_size_type() def get_result_type(self, obj, index): return self._elem_type def _supports(self, method_name): # operator[] is only supported for unique_ptr return self._is_array def __call__(self, obj, index): return UniquePtrGetWorker.__call__(self, obj)[index] class UniquePtrMethodsMatcher(gdb.xmethod.XMethodMatcher): def __init__(self): gdb.xmethod.XMethodMatcher.__init__(self, matcher_name_prefix + 'unique_ptr') self._method_dict = { 'get': LibStdCxxXMethod('get', UniquePtrGetWorker), 'operator->': LibStdCxxXMethod('operator->', UniquePtrGetWorker), 'operator*': LibStdCxxXMethod('operator*', UniquePtrDerefWorker), 'operator[]': LibStdCxxXMethod('operator[]', UniquePtrSubscriptWorker), } self.methods = [self._method_dict[m] for m in self._method_dict] def match(self, class_type, method_name): if not is_specialization_of(class_type, 'unique_ptr'): return None method = self._method_dict.get(method_name) if method is None or not method.enabled: return None worker = method.worker_class(class_type.template_argument(0)) if worker._supports(method_name): return worker return None # Xmethods for std::shared_ptr class SharedPtrGetWorker(gdb.xmethod.XMethodWorker): """ Implements std::shared_ptr::get() and std::shared_ptr::operator->(). """ def __init__(self, elem_type): self._is_array = elem_type.code == gdb.TYPE_CODE_ARRAY if self._is_array: self._elem_type = elem_type.target() else: self._elem_type = elem_type def get_arg_types(self): return None def get_result_type(self, obj): return self._elem_type.pointer() def _supports(self, method_name): # operator-> is not supported for shared_ptr return method_name == 'get' or not self._is_array def __call__(self, obj): return obj['_M_ptr'] class SharedPtrDerefWorker(SharedPtrGetWorker): """Implement std::shared_ptr::operator*().""" def __init__(self, elem_type): SharedPtrGetWorker.__init__(self, elem_type) def get_result_type(self, obj): return self._elem_type def _supports(self, method_name): # operator* is not supported for shared_ptr return not self._is_array def __call__(self, obj): return SharedPtrGetWorker.__call__(self, obj).dereference() class SharedPtrSubscriptWorker(SharedPtrGetWorker): """Implement std::shared_ptr::operator[](size_t).""" def __init__(self, elem_type): SharedPtrGetWorker.__init__(self, elem_type) def get_arg_types(self): return get_std_size_type() def get_result_type(self, obj, index): return self._elem_type def _supports(self, method_name): # operator[] is only supported for shared_ptr return self._is_array def __call__(self, obj, index): # Check bounds if _elem_type is an array of known bound m = re.match(r'.*\[(\d+)]$', str(self._elem_type)) if m and index >= int(m.group(1)): raise IndexError('shared_ptr<%s> index "%d" should not be >= %d.' % (self._elem_type, int(index), int(m.group(1)))) return SharedPtrGetWorker.__call__(self, obj)[index] class SharedPtrUseCountWorker(gdb.xmethod.XMethodWorker): """Implement std::shared_ptr::use_count().""" def __init__(self, elem_type): pass def get_arg_types(self): return None def get_result_type(self, obj): return gdb.lookup_type('long') def _supports(self, method_name): return True def __call__(self, obj): refcounts = obj['_M_refcount']['_M_pi'] return refcounts['_M_use_count'] if refcounts else 0 class SharedPtrUniqueWorker(SharedPtrUseCountWorker): """Implement std::shared_ptr::unique().""" def __init__(self, elem_type): SharedPtrUseCountWorker.__init__(self, elem_type) def get_result_type(self, obj): return gdb.lookup_type('bool') def __call__(self, obj): return SharedPtrUseCountWorker.__call__(self, obj) == 1 class SharedPtrMethodsMatcher(gdb.xmethod.XMethodMatcher): def __init__(self): gdb.xmethod.XMethodMatcher.__init__(self, matcher_name_prefix + 'shared_ptr') self._method_dict = { 'get': LibStdCxxXMethod('get', SharedPtrGetWorker), 'operator->': LibStdCxxXMethod('operator->', SharedPtrGetWorker), 'operator*': LibStdCxxXMethod('operator*', SharedPtrDerefWorker), 'operator[]': LibStdCxxXMethod('operator[]', SharedPtrSubscriptWorker), 'use_count': LibStdCxxXMethod('use_count', SharedPtrUseCountWorker), 'unique': LibStdCxxXMethod('unique', SharedPtrUniqueWorker), } self.methods = [self._method_dict[m] for m in self._method_dict] def match(self, class_type, method_name): if not is_specialization_of(class_type, 'shared_ptr'): return None method = self._method_dict.get(method_name) if method is None or not method.enabled: return None worker = method.worker_class(class_type.template_argument(0)) if worker._supports(method_name): return worker return None def register_libstdcxx_xmethods(locus): gdb.xmethod.register_xmethod_matcher(locus, ArrayMethodsMatcher()) gdb.xmethod.register_xmethod_matcher(locus, ForwardListMethodsMatcher()) gdb.xmethod.register_xmethod_matcher(locus, DequeMethodsMatcher()) gdb.xmethod.register_xmethod_matcher(locus, ListMethodsMatcher()) gdb.xmethod.register_xmethod_matcher(locus, VectorMethodsMatcher()) gdb.xmethod.register_xmethod_matcher( locus, AssociativeContainerMethodsMatcher('set')) gdb.xmethod.register_xmethod_matcher( locus, AssociativeContainerMethodsMatcher('map')) gdb.xmethod.register_xmethod_matcher( locus, AssociativeContainerMethodsMatcher('multiset')) gdb.xmethod.register_xmethod_matcher( locus, AssociativeContainerMethodsMatcher('multimap')) gdb.xmethod.register_xmethod_matcher( locus, AssociativeContainerMethodsMatcher('unordered_set')) gdb.xmethod.register_xmethod_matcher( locus, AssociativeContainerMethodsMatcher('unordered_map')) gdb.xmethod.register_xmethod_matcher( locus, AssociativeContainerMethodsMatcher('unordered_multiset')) gdb.xmethod.register_xmethod_matcher( locus, AssociativeContainerMethodsMatcher('unordered_multimap')) gdb.xmethod.register_xmethod_matcher(locus, UniquePtrMethodsMatcher()) gdb.xmethod.register_xmethod_matcher(locus, SharedPtrMethodsMatcher()) libstdcxx/v6/__init__.py000064400000002175152525737710011237 0ustar00# Copyright (C) 2014-2025 Free Software Foundation, Inc. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # Load the xmethods if GDB supports them. def gdb_has_xmethods(): try: import gdb.xmethod return True except ImportError: return False def register_libstdcxx_printers(obj): # Load the pretty-printers. from .printers import register_libstdcxx_printers register_libstdcxx_printers(obj) if gdb_has_xmethods(): from .xmethods import register_libstdcxx_xmethods register_libstdcxx_xmethods(obj) libstdcxx/v6/printers.py000064400000332765152525737710011361 0ustar00# Pretty-printers for libstdc++. # Copyright (C) 2008-2025 Free Software Foundation, Inc. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . import gdb import itertools import re import sys import errno import datetime # Python 2 + Python 3 compatibility code # Resources about compatibility: # # * : Documentation of the "six" module # FIXME: The handling of e.g. std::basic_string (at least on char) # probably needs updating to work with Python 3's new string rules. # # In particular, Python 3 has a separate type (called byte) for # bytestrings, and a special b"" syntax for the byte literals; the old # str() type has been redefined to always store Unicode text. # # We probably can't do much about this until this GDB PR is addressed: # if sys.version_info[0] > 2: # Python 3 stuff Iterator = object # Python 3 folds these into the normal functions. imap = map izip = zip # Also, int subsumes long long = int _utc_timezone = datetime.timezone.utc else: # Python 2 stuff class Iterator: """Compatibility mixin for iterators Instead of writing next() methods for iterators, write __next__() methods and use this mixin to make them work in Python 2 as well as Python 3. Idea stolen from the "six" documentation: """ def next(self): return self.__next__() # In Python 2, we still need these from itertools from itertools import imap, izip # Python 2 does not provide the datetime.UTC singleton. class UTC(datetime.tzinfo): """Concrete tzinfo class representing the UTC time zone.""" def utcoffset(self, dt): return datetime.timedelta(0) def tzname(self, dt): return "UTC" def dst(self, dt): return datetime.timedelta(0) _utc_timezone = UTC() # Try to use the new-style pretty-printing if available. _use_gdb_pp = True try: import gdb.printing except ImportError: _use_gdb_pp = False # Try to install type-printers. _use_type_printing = False try: import gdb.types if hasattr(gdb.types, 'TypePrinter'): _use_type_printing = True except ImportError: pass # Use the base class if available. if hasattr(gdb, 'ValuePrinter'): printer_base = gdb.ValuePrinter else: printer_base = object # Starting with the type ORIG, search for the member type NAME. This # handles searching upward through superclasses. This is needed to # work around http://sourceware.org/bugzilla/show_bug.cgi?id=13615. def find_type(orig, name): typ = orig.strip_typedefs() while True: # Use Type.tag to ignore cv-qualifiers. PR 67440. search = '%s::%s' % (typ.tag, name) try: return gdb.lookup_type(search) except RuntimeError: pass # The type was not found, so try the superclass. We only need # to check the first superclass, so we don't bother with # anything fancier here. fields = typ.fields() if len(fields) and fields[0].is_base_class: typ = fields[0].type else: raise ValueError("Cannot find type %s::%s" % (str(orig), name)) _versioned_namespace = '__8::' def lookup_templ_spec(templ, *args): """ Lookup template specialization templ. """ t = '{}<{}>'.format(templ, ', '.join([str(a) for a in args])) try: return gdb.lookup_type(t) except gdb.error as e: # Type not found, try again in versioned namespace. global _versioned_namespace if _versioned_namespace not in templ: t = t.replace('::', '::' + _versioned_namespace, 1) try: return gdb.lookup_type(t) except gdb.error: # If that also fails, rethrow the original exception pass raise e # Use this to find container node types instead of find_type, # see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91997 for details. def lookup_node_type(nodename, containertype): """ Lookup specialization of template nodename corresponding to containertype. nodename - The name of a class template, as a String containertype - The container, as a gdb.Type Return a gdb.Type for the corresponding specialization of nodename, or None if the type cannot be found. e.g. lookup_node_type('_List_node', gdb.lookup_type('std::list')) will return a gdb.Type for the type std::_List_node. """ # If nodename is unqualified, assume it's in namespace std. if '::' not in nodename: nodename = 'std::' + nodename # Use either containertype's value_type or its first template argument. try: valtype = find_type(containertype, 'value_type') except: valtype = containertype.template_argument(0) valtype = valtype.strip_typedefs() try: return lookup_templ_spec(nodename, valtype) except gdb.error: # For debug mode containers the node is in std::__cxx1998. if is_member_of_namespace(nodename, 'std'): if is_member_of_namespace(containertype, 'std::__cxx1998', 'std::__debug', '__gnu_debug'): nodename = nodename.replace('::', '::__cxx1998::', 1) try: return lookup_templ_spec(nodename, valtype) except gdb.error: pass return None def is_member_of_namespace(typ, *namespaces): """ Test whether a type is a member of one of the specified namespaces. The type can be specified as a string or a gdb.Type object. """ if isinstance(typ, gdb.Type): typ = str(typ) typ = strip_versioned_namespace(typ) for namespace in namespaces: if typ.startswith(namespace + '::'): return True return False def is_specialization_of(x, template_name): """ Test whether a type is a specialization of the named class template. The type can be specified as a string or a gdb.Type object. The template should be the name of a class template as a string, without any 'std' qualification. """ global _versioned_namespace if isinstance(x, gdb.Type): x = x.tag template_name = '(%s)?%s' % (_versioned_namespace, template_name) return re.match('^std::%s<.*>$' % template_name, x) is not None def strip_versioned_namespace(typename): global _versioned_namespace return typename.replace(_versioned_namespace, '') def strip_fundts_namespace(typ): """Remove "fundamentals_vN" inline namespace from qualified type name.""" pattern = r'^std::experimental::fundamentals_v\d::' repl = 'std::experimental::' if sys.version_info[0] == 2: return re.sub(pattern, repl, typ, 1) else: # Technically this needs Python 3.1 but nobody should be using 3.0 return re.sub(pattern, repl, typ, count=1) def strip_inline_namespaces(type_str): """Remove known inline namespaces from the canonical name of a type.""" type_str = strip_versioned_namespace(type_str) type_str = type_str.replace('std::__cxx11::', 'std::') expt_ns = 'std::experimental::' for lfts_ns in ('fundamentals_v1', 'fundamentals_v2'): type_str = type_str.replace(expt_ns + lfts_ns + '::', expt_ns) fs_ns = expt_ns + 'filesystem::' type_str = type_str.replace(fs_ns + 'v1::', fs_ns) return type_str def get_template_arg_list(type_obj): """Return a type's template arguments as a list.""" n = 0 template_args = [] while True: try: template_args.append(type_obj.template_argument(n)) except: return template_args n += 1 class SmartPtrIterator(Iterator): """An iterator for smart pointer types with a single 'child' value.""" def __init__(self, val): self._val = val def __iter__(self): return self def __next__(self): if self._val is None: raise StopIteration self._val, val = None, self._val return ('get()', val) class SharedPointerPrinter(printer_base): """ Print a shared_ptr, weak_ptr, atomic, or atomic. """ def __init__(self, typename, val): self._typename = strip_versioned_namespace(typename) self._val = val self._pointer = val['_M_ptr'] def children(self): return SmartPtrIterator(self._pointer) # Return the _Sp_counted_base<>* that holds the refcounts. def _get_refcounts(self): if self._typename == 'std::atomic': # A tagged pointer is stored as uintptr_t. ptr_val = self._val['_M_refcount']['_M_val']['_M_i'] ptr_val = ptr_val - (ptr_val % 2) # clear lock bit ptr_type = find_type(self._val['_M_refcount'].type, 'pointer') return ptr_val.cast(ptr_type) return self._val['_M_refcount']['_M_pi'] def to_string(self): state = 'empty' refcounts = self._get_refcounts() targ = self._val.type.template_argument(0) targ = strip_versioned_namespace(str(targ)) if refcounts != 0: usecount = refcounts['_M_use_count'] weakcount = refcounts['_M_weak_count'] if usecount == 0: state = 'expired, weak count %d' % weakcount else: state = 'use count %d, weak count %d' % ( usecount, weakcount - 1) return '%s<%s> (%s)' % (self._typename, targ, state) def _tuple_impl_get(val): """Return the tuple element stored in a _Tuple_impl base class.""" bases = val.type.fields() if not bases[-1].is_base_class: raise ValueError( "Unsupported implementation for std::tuple: %s" % str(val.type)) # Get the _Head_base base class: head_base = val.cast(bases[-1].type) fields = head_base.type.fields() if len(fields) == 0: raise ValueError( "Unsupported implementation for std::tuple: %s" % str(val.type)) if fields[0].name == '_M_head_impl': # The tuple element is the _Head_base::_M_head_impl data member. return head_base['_M_head_impl'] elif fields[0].is_base_class: # The tuple element is an empty base class of _Head_base. # Cast to that empty base class. return head_base.cast(fields[0].type) else: raise ValueError( "Unsupported implementation for std::tuple: %s" % str(val.type)) def tuple_get(n, val): """Return the result of std::get(val) on a std::tuple.""" tuple_size = len(get_template_arg_list(val.type)) if n > tuple_size: raise ValueError("Out of range index for std::get on std::tuple") # Get the first _Tuple_impl<0, T...> base class: node = val.cast(val.type.fields()[0].type) while n > 0: # Descend through the base classes until the Nth one. node = node.cast(node.type.fields()[0].type) n -= 1 return _tuple_impl_get(node) def unique_ptr_get(val): """Return the result of val.get() on a std::unique_ptr.""" # std::unique_ptr contains a std::tuple, # either as a direct data member _M_t (the old implementation) # or within a data member of type __uniq_ptr_data. impl_type = val.type.fields()[0].type.strip_typedefs() # Check for new implementations first: if is_specialization_of(impl_type, '__uniq_ptr_data') \ or is_specialization_of(impl_type, '__uniq_ptr_impl'): tuple_member = val['_M_t']['_M_t'] elif is_specialization_of(impl_type, 'tuple'): tuple_member = val['_M_t'] else: raise ValueError( "Unsupported implementation for unique_ptr: %s" % str(impl_type)) return tuple_get(0, tuple_member) class UniquePointerPrinter(printer_base): """Print a unique_ptr.""" def __init__(self, typename, val): self._val = val def children(self): return SmartPtrIterator(unique_ptr_get(self._val)) def to_string(self): t = self._val.type.template_argument(0) return 'std::unique_ptr<{}>'.format(str(t)) def get_value_from_aligned_membuf(buf, valtype): """Return the value held in a __gnu_cxx::__aligned_membuf.""" return buf['_M_storage'].address.cast(valtype.pointer()).dereference() def get_value_from_list_node(node): """Return the value held in an _List_node<_Val>.""" try: member = node.type.fields()[1].name if member == '_M_data': # C++03 implementation, node contains the value as a member return node['_M_data'] elif member == '_M_storage': # C++11 implementation, node stores value in __aligned_membuf valtype = node.type.template_argument(0) return get_value_from_aligned_membuf(node['_M_storage'], valtype) except: pass raise ValueError("Unsupported implementation for %s" % str(node.type)) class StdListPrinter(printer_base): """Print a std::list.""" class _iterator(Iterator): def __init__(self, nodetype, head): self._nodetype = nodetype self._base = head['_M_next'] self._head = head.address self._count = 0 def __iter__(self): return self def __next__(self): if self._base == self._head: raise StopIteration elt = self._base.cast(self._nodetype).dereference() self._base = elt['_M_next'] count = self._count self._count = self._count + 1 val = get_value_from_list_node(elt) return ('[%d]' % count, val) def __init__(self, typename, val): self._typename = strip_versioned_namespace(typename) self._val = val def children(self): nodetype = lookup_node_type('_List_node', self._val.type).pointer() return self._iterator(nodetype, self._val['_M_impl']['_M_node']) def to_string(self): headnode = self._val['_M_impl']['_M_node'] if headnode['_M_next'] == headnode.address: return 'empty %s' % (self._typename) return '%s' % (self._typename) class NodeIteratorPrinter(printer_base): def __init__(self, typename, val, contname, nodename): self._val = val self._typename = typename self._contname = contname self._nodetype = lookup_node_type(nodename, val.type) def to_string(self): if not self._val['_M_node']: return 'non-dereferenceable iterator for std::%s' % (self._contname) node = self._val['_M_node'].cast( self._nodetype.pointer()).dereference() return str(get_value_from_list_node(node)) class StdListIteratorPrinter(NodeIteratorPrinter): """Print std::list::iterator.""" def __init__(self, typename, val): NodeIteratorPrinter.__init__(self, typename, val, 'list', '_List_node') class StdFwdListIteratorPrinter(NodeIteratorPrinter): """Print std::forward_list::iterator.""" def __init__(self, typename, val): NodeIteratorPrinter.__init__(self, typename, val, 'forward_list', '_Fwd_list_node') class StdSlistPrinter(printer_base): """Print a __gnu_cxx::slist.""" class _iterator(Iterator): def __init__(self, nodetype, head): self._nodetype = nodetype self._base = head['_M_head']['_M_next'] self._count = 0 def __iter__(self): return self def __next__(self): if self._base == 0: raise StopIteration elt = self._base.cast(self._nodetype).dereference() self._base = elt['_M_next'] count = self._count self._count = self._count + 1 return ('[%d]' % count, elt['_M_data']) def __init__(self, typename, val): self._val = val def children(self): nodetype = lookup_node_type('__gnu_cxx::_Slist_node', self._val.type) return self._iterator(nodetype.pointer(), self._val) def to_string(self): if self._val['_M_head']['_M_next'] == 0: return 'empty __gnu_cxx::slist' return '__gnu_cxx::slist' class StdSlistIteratorPrinter(printer_base): """Print __gnu_cxx::slist::iterator.""" def __init__(self, typename, val): self._val = val def to_string(self): if not self._val['_M_node']: return 'non-dereferenceable iterator for __gnu_cxx::slist' nodetype = lookup_node_type( '__gnu_cxx::_Slist_node', self._val.type).pointer() return str(self._val['_M_node'].cast(nodetype).dereference()['_M_data']) class StdVectorPrinter(printer_base): """Print a std::vector.""" class _iterator(Iterator): def __init__(self, start, finish, bitvec): self._bitvec = bitvec if bitvec: self._item = start['_M_p'] self._so = 0 self._finish = finish['_M_p'] self._fo = finish['_M_offset'] itype = self._item.dereference().type self._isize = 8 * itype.sizeof else: self._item = start self._finish = finish self._count = 0 def __iter__(self): return self def __next__(self): count = self._count self._count = self._count + 1 if self._bitvec: if self._item == self._finish and self._so >= self._fo: raise StopIteration elt = bool(self._item.dereference() & (1 << self._so)) self._so = self._so + 1 if self._so >= self._isize: self._item = self._item + 1 self._so = 0 return ('[%d]' % count, elt) else: if self._item == self._finish: raise StopIteration elt = self._item.dereference() self._item = self._item + 1 return ('[%d]' % count, elt) def __init__(self, typename, val): self._typename = strip_versioned_namespace(typename) self._val = val self._is_bool = val.type.template_argument( 0).code == gdb.TYPE_CODE_BOOL def children(self): return self._iterator(self._val['_M_impl']['_M_start'], self._val['_M_impl']['_M_finish'], self._is_bool) def to_string(self): start = self._val['_M_impl']['_M_start'] finish = self._val['_M_impl']['_M_finish'] end = self._val['_M_impl']['_M_end_of_storage'] if self._is_bool: start = self._val['_M_impl']['_M_start']['_M_p'] finish = self._val['_M_impl']['_M_finish']['_M_p'] fo = self._val['_M_impl']['_M_finish']['_M_offset'] itype = start.dereference().type bl = 8 * itype.sizeof length = bl * (finish - start) + fo capacity = bl * (end - start) return ('%s of length %d, capacity %d' % (self._typename, int(length), int(capacity))) else: return ('%s of length %d, capacity %d' % (self._typename, int(finish - start), int(end - start))) def display_hint(self): return 'array' class StdVectorIteratorPrinter(printer_base): """Print std::vector::iterator.""" def __init__(self, typename, val): self._val = val def to_string(self): if not self._val['_M_current']: return 'non-dereferenceable iterator for std::vector' return str(self._val['_M_current'].dereference()) class StdBitIteratorPrinter(printer_base): """Print std::vector's _Bit_iterator and _Bit_const_iterator.""" def __init__(self, typename, val): self._val = val def to_string(self): if not self._val['_M_p']: return 'non-dereferenceable iterator for std::vector' return bool(self._val['_M_p'].dereference() & (1 << self._val['_M_offset'])) class StdBitReferencePrinter(printer_base): """Print std::vector::reference.""" def __init__(self, typename, val): self._val = val def to_string(self): if not self._val['_M_p']: # PR libstdc++/115098 removed the reference default constructor # that this case relates to. New code should never need this, # but we still handle it for compatibility with old binaries. return 'invalid std::vector::reference' return bool(self._val['_M_p'].dereference() & (self._val['_M_mask'])) class StdTuplePrinter(printer_base): """Print a std::tuple.""" class _iterator(Iterator): @staticmethod def _is_nonempty_tuple(nodes): if len(nodes) == 2: if is_specialization_of(nodes[1].type, '__tuple_base'): return True elif len(nodes) == 1: return True elif len(nodes) == 0: return False raise ValueError( "Top of tuple tree does not consist of a single node.") def __init__(self, head): self._head = head # Set the base class as the initial head of the # tuple. nodes = self._head.type.fields() if self._is_nonempty_tuple(nodes): # Set the actual head to the first pair. self._head = self._head.cast(nodes[0].type) self._count = 0 def __iter__(self): return self def __next__(self): # Check for further recursions in the inheritance tree. # For a GCC 5+ tuple self._head is None after visiting all nodes: if not self._head: raise StopIteration nodes = self._head.type.fields() # For a GCC 4.x tuple there is a final node with no fields: if len(nodes) == 0: raise StopIteration # Check that this iteration has an expected structure. if len(nodes) > 2: raise ValueError( "Cannot parse more than 2 nodes in a tuple tree.") if len(nodes) == 1: # This is the last node of a GCC 5+ std::tuple. impl = self._head.cast(nodes[0].type) self._head = None else: # Either a node before the last node, or the last node of # a GCC 4.x tuple (which has an empty parent). # - Left node is the next recursion parent. # - Right node is the actual class contained in the tuple. # Process right node. impl = self._head.cast(nodes[1].type) # Process left node and set it as head. self._head = self._head.cast(nodes[0].type) self._count = self._count + 1 # Finally, check the implementation. If it is # wrapped in _M_head_impl return that, otherwise return # the value "as is". fields = impl.type.fields() if len(fields) < 1 or fields[0].name != "_M_head_impl": return ('[%d]' % (self._count - 1), impl) else: return ('[%d]' % (self._count - 1), impl['_M_head_impl']) def __init__(self, typename, val): self._typename = strip_versioned_namespace(typename) self._val = val def children(self): return self._iterator(self._val) def to_string(self): if len(self._val.type.fields()) == 0: return 'empty %s' % (self._typename) return '%s containing' % (self._typename) class StdStackOrQueuePrinter(printer_base): """Print a std::stack or std::queue.""" def __init__(self, typename, val): self._typename = strip_versioned_namespace(typename) self._visualizer = gdb.default_visualizer(val['c']) def children(self): return self._visualizer.children() def to_string(self): return '%s wrapping: %s' % (self._typename, self._visualizer.to_string()) def display_hint(self): if hasattr(self._visualizer, 'display_hint'): return self._visualizer.display_hint() return None class RbtreeIterator(Iterator): """ Turn an RB-tree-based container (std::map, std::set etc.) into a Python iterable object. """ def __init__(self, rbtree): self._size = rbtree['_M_t']['_M_impl']['_M_node_count'] self._node = rbtree['_M_t']['_M_impl']['_M_header']['_M_left'] self._count = 0 def __iter__(self): return self def __len__(self): return int(self._size) def __next__(self): if self._count == self._size: raise StopIteration result = self._node self._count = self._count + 1 if self._count < self._size: # Compute the next node. node = self._node if node.dereference()['_M_right']: node = node.dereference()['_M_right'] while node.dereference()['_M_left']: node = node.dereference()['_M_left'] else: parent = node.dereference()['_M_parent'] while node == parent.dereference()['_M_right']: node = parent parent = parent.dereference()['_M_parent'] if node.dereference()['_M_right'] != parent: node = parent self._node = node return result def get_value_from_Rb_tree_node(node): """Return the value held in an _Rb_tree_node<_Val>.""" try: member = node.type.fields()[1].name if member == '_M_value_field': # C++03 implementation, node contains the value as a member return node['_M_value_field'] elif member == '_M_storage': # C++11 implementation, node stores value in __aligned_membuf valtype = node.type.template_argument(0) return get_value_from_aligned_membuf(node['_M_storage'], valtype) except: pass raise ValueError("Unsupported implementation for %s" % str(node.type)) # This is a pretty printer for std::_Rb_tree_iterator (which is # std::map::iterator), and has nothing to do with the RbtreeIterator # class above. class StdRbtreeIteratorPrinter(printer_base): """Print std::map::iterator, std::set::iterator, etc.""" def __init__(self, typename, val): self._val = val nodetype = lookup_node_type('_Rb_tree_node', self._val.type) self._link_type = nodetype.pointer() def to_string(self): if not self._val['_M_node']: return 'non-dereferenceable iterator for associative container' node = self._val['_M_node'].cast(self._link_type).dereference() return str(get_value_from_Rb_tree_node(node)) class StdDebugIteratorPrinter(printer_base): """Print a debug enabled version of an iterator.""" def __init__(self, typename, val): self._val = val # Just strip away the encapsulating __gnu_debug::_Safe_iterator # and return the wrapped iterator value. def to_string(self): base_type = gdb.lookup_type('__gnu_debug::_Safe_iterator_base') itype = self._val.type.template_argument(0) safe_seq = self._val.cast(base_type)['_M_sequence'] if not safe_seq: return str(self._val.cast(itype)) if self._val['_M_version'] != safe_seq['_M_version']: return "invalid iterator" return str(self._val.cast(itype)) def num_elements(num): """Return either "1 element" or "N elements" depending on the argument.""" return '1 element' if num == 1 else '%d elements' % num class StdMapPrinter(printer_base): """Print a std::map or std::multimap.""" # Turn an RbtreeIterator into a pretty-print iterator. class _iter(Iterator): def __init__(self, rbiter, type): self._rbiter = rbiter self._count = 0 self._type = type def __iter__(self): return self def __next__(self): if self._count % 2 == 0: n = next(self._rbiter) n = n.cast(self._type).dereference() n = get_value_from_Rb_tree_node(n) self._pair = n item = n['first'] else: item = self._pair['second'] result = ('[%d]' % self._count, item) self._count = self._count + 1 return result def __init__(self, typename, val): self._typename = strip_versioned_namespace(typename) self._val = val def to_string(self): return '%s with %s' % (self._typename, num_elements(len(RbtreeIterator(self._val)))) def children(self): node = lookup_node_type('_Rb_tree_node', self._val.type).pointer() return self._iter(RbtreeIterator(self._val), node) def display_hint(self): return 'map' class StdSetPrinter(printer_base): """Print a std::set or std::multiset.""" # Turn an RbtreeIterator into a pretty-print iterator. class _iter(Iterator): def __init__(self, rbiter, type): self._rbiter = rbiter self._count = 0 self._type = type def __iter__(self): return self def __next__(self): item = next(self._rbiter) item = item.cast(self._type).dereference() item = get_value_from_Rb_tree_node(item) # FIXME: this is weird ... what to do? # Maybe a 'set' display hint? result = ('[%d]' % self._count, item) self._count = self._count + 1 return result def __init__(self, typename, val): self._typename = strip_versioned_namespace(typename) self._val = val def to_string(self): return '%s with %s' % (self._typename, num_elements(len(RbtreeIterator(self._val)))) def children(self): node = lookup_node_type('_Rb_tree_node', self._val.type).pointer() return self._iter(RbtreeIterator(self._val), node) class StdBitsetPrinter(printer_base): """Print a std::bitset.""" def __init__(self, typename, val): self._typename = strip_versioned_namespace(typename) self._val = val def to_string(self): # If template_argument handled values, we could print the # size. Or we could use a regexp on the type. return '%s' % (self._typename) def children(self): try: # An empty bitset may not have any members which will # result in an exception being thrown. words = self._val['_M_w'] except: return [] wtype = words.type # The _M_w member can be either an unsigned long, or an # array. This depends on the template specialization used. # If it is a single long, convert to a single element list. if wtype.code == gdb.TYPE_CODE_ARRAY: tsize = wtype.target().sizeof else: words = [words] tsize = wtype.sizeof nwords = wtype.sizeof / tsize result = [] byte = 0 while byte < nwords: w = words[byte] bit = 0 while w != 0: if (w & 1) != 0: # Another spot where we could use 'set'? result.append(('[%d]' % (byte * tsize * 8 + bit), 1)) bit = bit + 1 w = w >> 1 byte = byte + 1 return result class StdDequePrinter(printer_base): """Print a std::deque.""" class _iter(Iterator): def __init__(self, node, start, end, last, buffer_size): self._node = node self._p = start self._end = end self._last = last self._buffer_size = buffer_size self._count = 0 def __iter__(self): return self def __next__(self): if self._p == self._last: raise StopIteration result = ('[%d]' % self._count, self._p.dereference()) self._count = self._count + 1 # Advance the 'cur' pointer. self._p = self._p + 1 if self._p == self._end: # If we got to the end of this bucket, move to the # next bucket. self._node = self._node + 1 self._p = self._node[0] self._end = self._p + self._buffer_size return result def __init__(self, typename, val): self._typename = strip_versioned_namespace(typename) self._val = val self._elttype = val.type.template_argument(0) size = self._elttype.sizeof if size < 512: self._buffer_size = int(512 / size) else: self._buffer_size = 1 def to_string(self): start = self._val['_M_impl']['_M_start'] end = self._val['_M_impl']['_M_finish'] delta_n = end['_M_node'] - start['_M_node'] - 1 delta_s = start['_M_last'] - start['_M_cur'] delta_e = end['_M_cur'] - end['_M_first'] size = self._buffer_size * delta_n + delta_s + delta_e return '%s with %s' % (self._typename, num_elements(long(size))) def children(self): start = self._val['_M_impl']['_M_start'] end = self._val['_M_impl']['_M_finish'] return self._iter(start['_M_node'], start['_M_cur'], start['_M_last'], end['_M_cur'], self._buffer_size) def display_hint(self): return 'array' class StdDequeIteratorPrinter(printer_base): """Print std::deque::iterator.""" def __init__(self, typename, val): self._val = val def to_string(self): if not self._val['_M_cur']: return 'non-dereferenceable iterator for std::deque' return str(self._val['_M_cur'].dereference()) class StdStringPrinter(printer_base): """Print a std::basic_string of some kind.""" def __init__(self, typename, val): self._val = val self._new_string = typename.find("::__cxx11::basic_string") != -1 def to_string(self): # Make sure &string works, too. type = self._val.type if type.code == gdb.TYPE_CODE_REF: type = type.target() # Calculate the length of the string so that to_string returns # the string according to length, not according to first null # encountered. ptr = self._val['_M_dataplus']['_M_p'] if self._new_string: length = self._val['_M_string_length'] # https://sourceware.org/bugzilla/show_bug.cgi?id=17728 ptr = ptr.cast(ptr.type.strip_typedefs()) else: realtype = type.unqualified().strip_typedefs() reptype = gdb.lookup_type(str(realtype) + '::_Rep').pointer() header = ptr.cast(reptype) - 1 length = header.dereference()['_M_length'] if hasattr(ptr, "lazy_string"): return ptr.lazy_string(length=length) return ptr.string(length=length) def display_hint(self): return 'string' def access_streambuf_ptrs(streambuf): """Access the streambuf put area pointers.""" pbase = streambuf['_M_out_beg'] pptr = streambuf['_M_out_cur'] egptr = streambuf['_M_in_end'] return pbase, pptr, egptr class StdStringBufPrinter(printer_base): """Print a std::basic_stringbuf.""" def __init__(self, _, val): self._val = val def to_string(self): (pbase, pptr, egptr) = access_streambuf_ptrs(self._val) # Logic from basic_stringbuf::_M_high_mark() if pptr: if not egptr or pptr > egptr: return pbase.string(length=pptr - pbase) else: return pbase.string(length=egptr - pbase) return self._val['_M_string'] def display_hint(self): return 'string' class StdStringStreamPrinter(printer_base): """Print a std::basic_stringstream.""" def __init__(self, typename, val): self._val = val self._typename = typename # Check if the stream was redirected. This is essentially: # val['_M_streambuf'] != val['_M_stringbuf'].address # However, GDB can't resolve the virtual inheritance, so we do that # manually. basetype = [f.type for f in val.type.fields() if f.is_base_class][0] gdb.set_convenience_variable('__stream', val.cast(basetype).address) self._streambuf = gdb.parse_and_eval('$__stream->rdbuf()') self._was_redirected = self._streambuf != val['_M_stringbuf'].address def to_string(self): if self._was_redirected: return "%s redirected to %s" % ( self._typename, self._streambuf.dereference()) return self._val['_M_stringbuf'] def display_hint(self): if self._was_redirected: return None return 'string' class Tr1HashtableIterator(Iterator): def __init__(self, hashtable): self._buckets = hashtable['_M_buckets'] self._bucket = 0 self._bucket_count = hashtable['_M_bucket_count'] self._node_type = find_type(hashtable.type, '_Node').pointer() self._node = 0 while self._bucket != self._bucket_count: self._node = self._buckets[self._bucket] if self._node: break self._bucket = self._bucket + 1 def __iter__(self): return self def __next__(self): if self._node == 0: raise StopIteration node = self._node.cast(self._node_type) result = node.dereference()['_M_v'] self._node = node.dereference()['_M_next'] if self._node == 0: self._bucket = self._bucket + 1 while self._bucket != self._bucket_count: self._node = self._buckets[self._bucket] if self._node: break self._bucket = self._bucket + 1 return result class StdHashtableIterator(Iterator): def __init__(self, hashtable): self._node = hashtable['_M_before_begin']['_M_nxt'] valtype = hashtable.type.template_argument(1) cached = hashtable.type.template_argument(9).template_argument(0) node_type = lookup_templ_spec('std::__detail::_Hash_node', str(valtype), 'true' if cached else 'false') self._node_type = node_type.pointer() def __iter__(self): return self def __next__(self): if self._node == 0: raise StopIteration elt = self._node.cast(self._node_type).dereference() self._node = elt['_M_nxt'] valptr = elt['_M_storage'].address valptr = valptr.cast(elt.type.template_argument(0).pointer()) return valptr.dereference() class Tr1UnorderedSetPrinter(printer_base): """Print a std::unordered_set or tr1::unordered_set.""" def __init__(self, typename, val): self._typename = strip_versioned_namespace(typename) self._val = val def _hashtable(self): if self._typename.startswith('std::tr1'): return self._val return self._val['_M_h'] def to_string(self): count = self._hashtable()['_M_element_count'] return '%s with %s' % (self._typename, num_elements(count)) @staticmethod def _format_count(i): return '[%d]' % i def children(self): counter = imap(self._format_count, itertools.count()) if self._typename.startswith('std::tr1'): return izip(counter, Tr1HashtableIterator(self._hashtable())) return izip(counter, StdHashtableIterator(self._hashtable())) class Tr1UnorderedMapPrinter(printer_base): """Print a std::unordered_map or tr1::unordered_map.""" def __init__(self, typename, val): self._typename = strip_versioned_namespace(typename) self._val = val def _hashtable(self): if self._typename.startswith('std::tr1'): return self._val return self._val['_M_h'] def to_string(self): count = self._hashtable()['_M_element_count'] return '%s with %s' % (self._typename, num_elements(count)) @staticmethod def _flatten(list): for elt in list: for i in elt: yield i @staticmethod def _format_one(elt): return (elt['first'], elt['second']) @staticmethod def _format_count(i): return '[%d]' % i def children(self): counter = imap(self._format_count, itertools.count()) # Map over the hash table and flatten the result. if self._typename.startswith('std::tr1'): data = self._flatten( imap(self._format_one, Tr1HashtableIterator(self._hashtable()))) # Zip the two iterators together. return izip(counter, data) data = self._flatten( imap(self._format_one, StdHashtableIterator(self._hashtable()))) # Zip the two iterators together. return izip(counter, data) def display_hint(self): return 'map' class StdForwardListPrinter(printer_base): """Print a std::forward_list.""" class _iterator(Iterator): def __init__(self, nodetype, head): self._nodetype = nodetype self._base = head['_M_next'] self._count = 0 def __iter__(self): return self def __next__(self): if self._base == 0: raise StopIteration elt = self._base.cast(self._nodetype).dereference() self._base = elt['_M_next'] count = self._count self._count = self._count + 1 valptr = elt['_M_storage'].address valptr = valptr.cast(elt.type.template_argument(0).pointer()) return ('[%d]' % count, valptr.dereference()) def __init__(self, typename, val): self._val = val self._typename = strip_versioned_namespace(typename) def children(self): nodetype = lookup_node_type('_Fwd_list_node', self._val.type).pointer() return self._iterator(nodetype, self._val['_M_impl']['_M_head']) def to_string(self): if self._val['_M_impl']['_M_head']['_M_next'] == 0: return 'empty %s' % self._typename return '%s' % self._typename class SingleObjContainerPrinter(printer_base): """Base class for printers of containers of single objects.""" def __init__(self, val, viz, hint=None): self._contained_value = val self._visualizer = viz self._hint = hint def _recognize(self, type): """Return type as a string after applying type printers.""" global _use_type_printing if not _use_type_printing: return str(type) return gdb.types.apply_type_recognizers(gdb.types.get_type_recognizers(), type) or str(type) class _contained(Iterator): def __init__(self, val): self._val = val def __iter__(self): return self def __next__(self): if self._val is None: raise StopIteration retval = self._val self._val = None return ('[contained value]', retval) def children(self): if self._contained_value is None: return self._contained(None) if hasattr(self._visualizer, 'children'): return self._visualizer.children() return self._contained(self._contained_value) def display_hint(self): if (hasattr(self._visualizer, 'children') and hasattr(self._visualizer, 'display_hint')): # If contained value is a map we want to display in the same way. return self._visualizer.display_hint() return self._hint def function_pointer_to_name(f): """Find the name of the function referred to by the gdb.Value f, which should contain a function pointer from the program.""" # Turn the function pointer into an actual address. # This is needed to unpack ppc64 function descriptors. f = f.dereference().address if sys.version_info[0] == 2: # Older versions of GDB need to use long for Python 2, # because int(f) on 64-bit big-endian values raises a # gdb.error saying "Cannot convert value to int." f = long(f) else: f = int(f) try: # If the function can't be found older versions of GDB raise a # RuntimeError saying "Cannot locate object file for block." return gdb.block_for_pc(f).function.name except: return None class StdExpAnyPrinter(SingleObjContainerPrinter): """Print a std::any or std::experimental::any.""" def __init__(self, typename, val): self._typename = strip_versioned_namespace(typename) self._typename = strip_fundts_namespace(self._typename) self._val = val self._contained_type = None contained_value = None visualizer = None mgr = self._val['_M_manager'] if mgr != 0: func = function_pointer_to_name(mgr) if not func: raise ValueError( "Invalid function pointer in %s" % (self._typename)) # We want to use this regular expression: # T::_Manager_xxx<.*>::_S_manage\(T::_Op, const T\*, T::_Arg\*\) # where T is std::any or std::experimental::any. # But we need to account for variances in demangled names # between GDB versions, e.g. 'enum T::_Op' instead of 'T::_Op'. rx = ( r"({0}::_Manager_\w+<.*>)::_S_manage\(" r"(enum )?{0}::_Op, (const {0}|{0} const) ?\*, " r"(union )?{0}::_Arg ?\*\)" ).format(typename) m = re.match(rx, func) if not m: raise ValueError( "Unknown manager function in %s" % self._typename) mgrname = m.group(1) # FIXME need to expand 'std::string' so that gdb.lookup_type works if 'std::string' in mgrname: mgrtypes = [] for s in StdExpAnyPrinter._string_types(): try: x = re.sub(r"std::string(?!\w)", s, m.group(1)) # The following lookup might raise gdb.error if the # manager function was never instantiated for 's' in # the program, because there will be no such type. mgrtypes.append(gdb.lookup_type(x)) except gdb.error: pass if len(mgrtypes) != 1: # FIXME: this is unlikely in practice, but possible for # programs that use both old and new string types with # std::any in a single program. Can we do better? # Maybe find the address of each type's _S_manage and # compare to the address stored in _M_manager? raise ValueError( 'Cannot uniquely determine std::string type ' 'used in std::any' ) mgrtype = mgrtypes[0] else: mgrtype = gdb.lookup_type(mgrname) self._contained_type = mgrtype.template_argument(0) valptr = None if '::_Manager_internal' in mgrname: valptr = self._val['_M_storage']['_M_buffer'].address elif '::_Manager_external' in mgrname: valptr = self._val['_M_storage']['_M_ptr'] else: raise ValueError( "Unknown manager function in %s" % self._typename) contained_value = valptr.cast( self._contained_type.pointer()).dereference() visualizer = gdb.default_visualizer(contained_value) super(StdExpAnyPrinter, self).__init__(contained_value, visualizer) def to_string(self): if self._contained_type is None: return '%s [no contained value]' % self._typename desc = "%s containing " % self._typename if hasattr(self._visualizer, 'children'): return desc + self._visualizer.to_string() valtype = self._recognize(self._contained_type) return desc + strip_versioned_namespace(str(valtype)) @staticmethod def _string_types(): # This lookup for std::string might return the __cxx11 version, # but that's not necessarily the one used by the std::any # manager function we're trying to find. strings = {str(gdb.lookup_type('std::string').strip_typedefs())} # So also consider all the other possible std::string types! s = 'basic_string, std::allocator >' quals = ['std::', 'std::__cxx11::', 'std::' + _versioned_namespace] strings |= {q + s for q in quals} # set of unique strings return strings class StdExpOptionalPrinter(SingleObjContainerPrinter): """Print a std::optional or std::experimental::optional.""" def __init__(self, typename, val): self._typename = strip_versioned_namespace(typename) self._typename = strip_fundts_namespace(self._typename) payload = val['_M_payload'] if self._typename.startswith('std::experimental'): engaged = val['_M_engaged'] contained_value = payload else: engaged = payload['_M_engaged'] contained_value = payload['_M_payload'] try: # Since GCC 9 contained_value = contained_value['_M_value'] except: pass visualizer = gdb.default_visualizer(contained_value) if not engaged: contained_value = None super(StdExpOptionalPrinter, self).__init__( contained_value, visualizer) def to_string(self): if self._contained_value is None: return "%s [no contained value]" % self._typename if hasattr(self._visualizer, 'children'): return "%s containing %s" % (self._typename, self._visualizer.to_string()) return self._typename class StdVariantPrinter(SingleObjContainerPrinter): """Print a std::variant.""" def __init__(self, typename, val): alternatives = get_template_arg_list(val.type) self._typename = strip_versioned_namespace(typename) self._index = val['_M_index'] if self._index >= len(alternatives): self._contained_type = None contained_value = None visualizer = None else: self._contained_type = alternatives[int(self._index)] addr = val['_M_u']['_M_first']['_M_storage'].address contained_value = addr.cast( self._contained_type.pointer()).dereference() visualizer = gdb.default_visualizer(contained_value) super(StdVariantPrinter, self).__init__( contained_value, visualizer, 'array') def to_string(self): if self._contained_value is None: return "%s [no contained value]" % self._typename if hasattr(self._visualizer, 'children'): return "%s [index %d] containing %s" % (self._typename, self._index, self._visualizer.to_string()) return "%s [index %d]" % (self._typename, self._index) class StdNodeHandlePrinter(SingleObjContainerPrinter): """Print a container node handle.""" def __init__(self, typename, val): self._value_type = val.type.template_argument(1) nodetype = val.type.template_argument(2).template_argument(0) self._is_rb_tree_node = is_specialization_of( nodetype.name, '_Rb_tree_node') self._is_map_node = val.type.template_argument(0) != self._value_type nodeptr = val['_M_ptr'] if nodeptr: if self._is_rb_tree_node: contained_value = get_value_from_Rb_tree_node( nodeptr.dereference()) else: contained_value = get_value_from_aligned_membuf(nodeptr['_M_storage'], self._value_type) visualizer = gdb.default_visualizer(contained_value) else: contained_value = None visualizer = None optalloc = val['_M_alloc'] self._alloc = optalloc['_M_payload'] if optalloc['_M_engaged'] else None super(StdNodeHandlePrinter, self).__init__(contained_value, visualizer, 'array') def to_string(self): desc = 'node handle for ' if not self._is_rb_tree_node: desc += 'unordered ' if self._is_map_node: desc += 'map' else: desc += 'set' if self._contained_value: desc += ' with element' if hasattr(self._visualizer, 'children'): return "%s = %s" % (desc, self._visualizer.to_string()) return desc else: return 'empty %s' % desc class StdExpStringViewPrinter(printer_base): """ Print a std::basic_string_view or std::experimental::basic_string_view """ def __init__(self, typename, val): self._val = val def to_string(self): ptr = self._val['_M_str'] len = self._val['_M_len'] if hasattr(ptr, "lazy_string"): return ptr.lazy_string(length=len) return ptr.string(length=len) def display_hint(self): return 'string' class StdExpPathPrinter(printer_base): """Print a std::experimental::filesystem::path.""" def __init__(self, typename, val): self._val = val self._typename = typename start = self._val['_M_cmpts']['_M_impl']['_M_start'] finish = self._val['_M_cmpts']['_M_impl']['_M_finish'] self._num_cmpts = int(finish - start) def _path_type(self): t = str(self._val['_M_type']) if t[-9:] == '_Root_dir': return "root-directory" if t[-10:] == '_Root_name': return "root-name" return None def to_string(self): path = "%s" % self._val['_M_pathname'] if self._num_cmpts == 0: t = self._path_type() if t: path = '%s [%s]' % (path, t) return "experimental::filesystem::path %s" % path class _iterator(Iterator): def __init__(self, cmpts, pathtype): self._pathtype = pathtype self._item = cmpts['_M_impl']['_M_start'] self._finish = cmpts['_M_impl']['_M_finish'] self._count = 0 def __iter__(self): return self def __next__(self): if self._item == self._finish: raise StopIteration item = self._item.dereference() count = self._count self._count = self._count + 1 self._item = self._item + 1 path = item['_M_pathname'] t = StdExpPathPrinter(self._pathtype, item)._path_type() if not t: t = count return ('[%s]' % t, path) def children(self): return self._iterator(self._val['_M_cmpts'], self._typename) class StdPathPrinter(printer_base): """Print a std::filesystem::path.""" def __init__(self, typename, val): self._val = val self._typename = typename impl = unique_ptr_get(self._val['_M_cmpts']['_M_impl']) self._type = impl.cast(gdb.lookup_type('uintptr_t')) & 3 if self._type == 0: self._impl = impl else: self._impl = None def _path_type(self): t = str(self._type.cast(gdb.lookup_type(self._typename + '::_Type'))) if t[-9:] == '_Root_dir': return "root-directory" if t[-10:] == '_Root_name': return "root-name" return None def to_string(self): path = "%s" % self._val['_M_pathname'] if self._type != 0: t = self._path_type() if t: path = '%s [%s]' % (path, t) return "filesystem::path %s" % path class _iterator(Iterator): def __init__(self, impl, pathtype): self._pathtype = pathtype if impl: # We can't access _Impl::_M_size because _Impl is incomplete # so cast to int* to access the _M_size member at offset zero, int_type = gdb.lookup_type('int') cmpt_type = gdb.lookup_type(pathtype + '::_Cmpt') char_type = gdb.lookup_type('char') impl = impl.cast(int_type.pointer()) size = impl.dereference() #self._capacity = (impl + 1).dereference() if hasattr(gdb.Type, 'alignof'): sizeof_Impl = max(2 * int_type.sizeof, cmpt_type.alignof) else: sizeof_Impl = 2 * int_type.sizeof begin = impl.cast(char_type.pointer()) + sizeof_Impl self._item = begin.cast(cmpt_type.pointer()) self._finish = self._item + size self._count = 0 else: self._item = None self._finish = None def __iter__(self): return self def __next__(self): if self._item == self._finish: raise StopIteration item = self._item.dereference() count = self._count self._count = self._count + 1 self._item = self._item + 1 path = item['_M_pathname'] t = StdPathPrinter(self._pathtype, item)._path_type() if not t: t = count return ('[%s]' % t, path) def children(self): return self._iterator(self._impl, self._typename) class StdPairPrinter(printer_base): """Print a std::pair object, with 'first' and 'second' as children.""" def __init__(self, typename, val): self._val = val class _iter(Iterator): """An iterator for std::pair types. Returns 'first' then 'second'.""" def __init__(self, val): self._val = val self._which = 'first' def __iter__(self): return self def __next__(self): if self._which is None: raise StopIteration which = self._which if which == 'first': self._which = 'second' else: self._which = None return (which, self._val[which]) def children(self): return self._iter(self._val) def to_string(self): return None class StdCmpCatPrinter(printer_base): """Print a comparison category object.""" def __init__(self, typename, val): self._typename = typename[typename.rfind(':') + 1:] self._val = val['_M_value'] def to_string(self): if self._typename == 'strong_ordering' and self._val == 0: name = 'equal' else: names = {2: 'unordered', -1: 'less', 0: 'equivalent', 1: 'greater'} name = names[int(self._val)] return 'std::{}::{}'.format(self._typename, name) class StdErrorCodePrinter(printer_base): """Print a std::error_code or std::error_condition.""" _system_is_posix = None # Whether std::system_category() use errno values. def __init__(self, typename, val): self._val = val self._typename = strip_versioned_namespace(typename) # Do this only once ... if StdErrorCodePrinter._system_is_posix is None: try: import posix StdErrorCodePrinter._system_is_posix = True except ImportError: StdErrorCodePrinter._system_is_posix = False @staticmethod def _find_errc_enum(name): typ = gdb.lookup_type(name) if typ is not None and typ.code == gdb.TYPE_CODE_ENUM: return typ return None @classmethod def _find_standard_errc_enum(cls, name): for ns in ['', _versioned_namespace]: try: qname = 'std::{}{}'.format(ns, name) return cls._find_errc_enum(qname) except RuntimeError: pass @classmethod def _match_net_ts_category(cls, cat): net_cats = ['stream', 'socket', 'ip::resolver'] for c in net_cats: func = c + '_category()' for ns in ['', _versioned_namespace]: ns = 'std::{}experimental::net::v1'.format(ns) sym = gdb.lookup_symbol('{}::{}::__c'.format(ns, func))[0] if sym is not None: if cat == sym.value().address: name = 'net::' + func enum = cls._find_errc_enum('{}::{}_errc'.format(ns, c)) return (name, enum) return (None, None) @classmethod def _category_info(cls, cat): """Return details of a std::error_category.""" name = None enum = None is_errno = False # Try these first, or we get "warning: RTTI symbol not found" when # using cat.dynamic_type on the local class types for Net TS # categories. func, enum = cls._match_net_ts_category(cat) if func is not None: return (None, func, enum, is_errno) # This might give a warning for a program-defined category defined as # a local class, but there doesn't seem to be any way to avoid that. typ = cat.dynamic_type.target() # Shortcuts for the known categories defined by libstdc++. if typ.tag.endswith('::generic_error_category'): name = 'generic' is_errno = True if typ.tag.endswith('::system_error_category'): name = 'system' is_errno = cls._system_is_posix if typ.tag.endswith('::future_error_category'): name = 'future' enum = cls._find_standard_errc_enum('future_errc') if typ.tag.endswith('::io_error_category'): name = 'io' enum = cls._find_standard_errc_enum('io_errc') if name is None: try: # Want to call std::error_category::name() override, but it's # unsafe: https://sourceware.org/bugzilla/show_bug.cgi?id=28856 # gdb.set_convenience_variable('__cat', cat) # return '"%s"' % gdb.parse_and_eval('$__cat->name()').string() pass except: pass return (name, typ.tag, enum, is_errno) @staticmethod def _unqualified_name(name): """ Strip any nested-name-specifier from name to give an unqualified name. """ return name.split('::')[-1] def to_string(self): value = self._val['_M_value'] cat = self._val['_M_cat'] name, alt_name, enum, is_errno = self._category_info(cat) if value == 0: default_cats = {'error_code': 'system', 'error_condition': 'generic'} if name == default_cats[self._unqualified_name(self._typename)]: return self._typename + ' = { }' # default-constructed value strval = str(value) if is_errno and value != 0: try: strval = errno.errorcode[int(value)] except: pass elif enum is not None: strval = self._unqualified_name(str(value.cast(enum))) if name is not None: name = '"%s"' % name else: name = alt_name return '%s = {%s: %s}' % (self._typename, name, strval) class StdRegexStatePrinter(printer_base): """Print a state node in the NFA for a std::regex.""" def __init__(self, typename, val): self._val = val self._typename = typename def to_string(self): opcode = str(self._val['_M_opcode']) if opcode: opcode = opcode[25:] next_id = self._val['_M_next'] variants = {'repeat': 'alt', 'alternative': 'alt', 'subexpr_begin': 'subexpr', 'subexpr_end': 'subexpr', 'line_begin_assertion': None, 'line_end_assertion': None, 'word_boundary': 'neg', 'subexpr_lookahead': 'neg', 'backref': 'backref_index', 'match': None, 'accept': None, 'dummy': None, 'unknown': None } v = variants[opcode] s = "opcode={}, next={}".format(opcode, next_id) if v is not None and self._val['_M_' + v] is not None: s = "{}, {}={}".format(s, v, self._val['_M_' + v]) return "{%s}" % (s) class StdSpanPrinter(printer_base): """Print a std::span.""" class _iterator(Iterator): def __init__(self, begin, size): self._count = 0 self._begin = begin self._size = size def __iter__(self): return self def __next__(self): if self._count == self._size: raise StopIteration count = self._count self._count = self._count + 1 return '[%d]' % count, (self._begin + count).dereference() def __init__(self, typename, val): self._typename = strip_versioned_namespace(typename) self._val = val size_max = gdb.parse_and_eval('static_cast(-1)') if val.type.template_argument(1) == size_max: self._size = val['_M_extent']['_M_extent_value'] else: self._size = val.type.template_argument(1) def to_string(self): return '%s of length %d' % (self._typename, self._size) def children(self): return self._iterator(self._val['_M_ptr'], self._size) def display_hint(self): return 'array' class StdInitializerListPrinter(printer_base): """Print a std::initializer_list.""" def __init__(self, typename, val): self._typename = typename self._val = val self._size = val['_M_len'] def to_string(self): return '%s of length %d' % (self._typename, self._size) def children(self): return StdSpanPrinter._iterator(self._val['_M_array'], self._size) def display_hint(self): return 'array' class StdAtomicPrinter(printer_base): """Print a std:atomic.""" def __init__(self, typename, val): self._typename = strip_versioned_namespace(typename) self._val = val self._shptr_printer = None self._value_type = self._val.type.template_argument(0) if self._value_type.tag is not None: typ = strip_versioned_namespace(self._value_type.tag) if (typ.startswith('std::shared_ptr<') or typ.startswith('std::weak_ptr<')): impl = val['_M_impl'] self._shptr_printer = SharedPointerPrinter(typename, impl) self.children = self._shptr_children def _shptr_children(self): return SmartPtrIterator(self._shptr_printer._pointer) def to_string(self): if self._shptr_printer is not None: return self._shptr_printer.to_string() if self._value_type.code == gdb.TYPE_CODE_INT: val = self._val['_M_i'] elif self._value_type.code == gdb.TYPE_CODE_FLT: val = self._val['_M_fp'] elif self._value_type.code == gdb.TYPE_CODE_PTR: val = self._val['_M_b']['_M_p'] elif self._value_type.code == gdb.TYPE_CODE_BOOL: val = self._val['_M_base']['_M_i'] else: val = self._val['_M_i'] return '%s<%s> = { %s }' % (self._typename, str(self._value_type), val) class StdFormatArgsPrinter(printer_base): """Print a std::basic_format_args.""" # TODO: add printer for basic_format_arg and print out children. # TODO: add printer for __format::_ArgStore. def __init__(self, typename, val): self._typename = strip_versioned_namespace(typename) self._val = val def to_string(self): targs = get_template_arg_list(self._val.type) char_type = get_template_arg_list(targs[0])[1] if char_type == gdb.lookup_type('char'): typ = 'std::format_args' elif char_type == gdb.lookup_type('wchar_t'): typ = 'std::wformat_args' else: typ = 'std::basic_format_args' size = self._val['_M_packed_size'] if size == 1: return "%s with 1 argument" % (typ) if size == 0: size = self._val['_M_unpacked_size'] return "%s with %d arguments" % (typ, size) class StdChronoDurationPrinter(printer_base): """Print a std::chrono::duration.""" def __init__(self, typename, val): self._typename = strip_versioned_namespace(typename) self._val = val def _ratio(self): # TODO use reduced period i.e. duration::period period = self._val.type.template_argument(1) num = period.template_argument(0) den = period.template_argument(1) return (num, den) def _suffix(self): num, den = self._ratio() if num == 1: if den == 1: return 's' if den == 1000: return 'ms' if den == 1000000: return 'us' if den == 1000000000: return 'ns' elif den == 1: if num == 60: return 'min' if num == 3600: return 'h' if num == 86400: return 'd' return '[{}]s'.format(num) return "[{}/{}]s".format(num, den) def to_string(self): r = self._val['__r'] if r.type.strip_typedefs().code == gdb.TYPE_CODE_FLT: r = "%g" % r return "std::chrono::duration = {{ {}{} }}".format(r, self._suffix()) class StdChronoTimePointPrinter(printer_base): """Print a std::chrono::time_point.""" def __init__(self, typename, val): self._typename = strip_versioned_namespace(typename) self._val = val def _clock(self): clock = self._val.type.template_argument(0) name = strip_versioned_namespace(clock.name) if name == 'std::chrono::_V2::system_clock' \ or name == 'std::chrono::system_clock': return ('std::chrono::sys_time', 0) # XXX need to remove leap seconds from utc, gps, and tai if name == 'std::chrono::utc_clock': return ('std::chrono::utc_time', None) # XXX if name == 'std::chrono::gps_clock': return ('std::chrono::gps_time', None) # XXX 315964809 if name == 'std::chrono::tai_clock': return ('std::chrono::tai_time', None) # XXX -378691210 if name == 'std::filesystem::__file_clock': return ('std::chrono::file_time', 6437664000) if name == 'std::chrono::local_t': return ('std::chrono::local_time', 0) return ('{} time_point'.format(name), None) def to_string(self, abbrev=False): clock, offset = self._clock() d = self._val['__d'] r = d['__r'] printer = StdChronoDurationPrinter(d.type.name, d) suffix = printer._suffix() time = '' if offset is not None: num, den = printer._ratio() secs = (r * num / den) + offset try: dt = datetime.datetime.fromtimestamp(secs, _utc_timezone) time = ' [{:%Y-%m-%d %H:%M:%S}]'.format(dt) except: pass s = '%d%s%s' % (r, suffix, time) if abbrev: return s return '%s = { %s }' % (clock, s) class StdChronoZonedTimePrinter(printer_base): """Print a std::chrono::zoned_time.""" def __init__(self, typename, val): self._typename = strip_versioned_namespace(typename) self._val = val def to_string(self): zone = self._val['_M_zone'].dereference()['_M_name'] time = self._val['_M_tp'] printer = StdChronoTimePointPrinter(time.type.name, time) time = printer.to_string(True) return 'std::chrono::zoned_time = {{ {} {} }}'.format(zone, time) months = [None, 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'] weekdays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'] class StdChronoCalendarPrinter(printer_base): """Print a std::chrono::day, std::chrono::month, std::chrono::year etc.""" def __init__(self, typename, val): self._typename = strip_versioned_namespace(typename) self._val = val def to_string(self): val = self._val typ = self._typename if 'month' in typ and typ != 'std::chrono::year_month_day_last': m = val['_M_m'] if typ.startswith('std::chrono::year'): y = val['_M_y'] if typ == 'std::chrono::day': return '{}'.format(int(val['_M_d'])) if typ == 'std::chrono::month': if m < 1 or m >= len(months): return "%d is not a valid month" % m return months[m] if typ == 'std::chrono::year': return '{}y'.format(y) if typ == 'std::chrono::weekday': wd = val['_M_wd'] if wd < 0 or wd >= len(weekdays): return "%d is not a valid weekday" % wd return '{}'.format(weekdays[wd]) if typ == 'std::chrono::weekday_indexed': return '{}[{}]'.format(val['_M_wd'], int(val['_M_index'])) if typ == 'std::chrono::weekday_last': return '{}[last]'.format(val['_M_wd']) if typ == 'std::chrono::month_day': return '{}/{}'.format(m, val['_M_d']) if typ == 'std::chrono::month_day_last': return '{}/last'.format(m) if typ == 'std::chrono::month_weekday': return '{}/{}'.format(m, val['_M_wdi']) if typ == 'std::chrono::month_weekday_last': return '{}/{}'.format(m, val['_M_wdl']) if typ == 'std::chrono::year_month': return '{}/{}'.format(y, m) if typ == 'std::chrono::year_month_day': return '{}/{}/{}'.format(y, m, val['_M_d']) if typ == 'std::chrono::year_month_day_last': return '{}/{}'.format(y, val['_M_mdl']) if typ == 'std::chrono::year_month_weekday': return '{}/{}/{}'.format(y, m, val['_M_wdi']) if typ == 'std::chrono::year_month_weekday_last': return '{}/{}/{}'.format(y, m, val['_M_wdl']) if typ.startswith('std::chrono::hh_mm_ss'): fract = '' if val['fractional_width'] != 0: fract = '.{:0{}d}'.format(int(val['_M_ss']['_M_r']), int(val['fractional_width'])) h = int(val['_M_h']['__r']) m = int(val['_M_m']['__r']) s = int(val['_M_s']['__r']) if val['_M_is_neg']: h = -h return '{:02}:{:02}:{:02}{}'.format(h, m, s, fract) class StdChronoTimeZonePrinter(printer_base): """Print a chrono::time_zone or chrono::time_zone_link.""" def __init__(self, typename, val): self._typename = strip_versioned_namespace(typename) self._val = val def to_string(self): str = '%s = %s' % (self._typename, self._val['_M_name']) if self._typename.endswith("_link"): str += ' -> %s' % (self._val['_M_target']) return str class StdChronoLeapSecondPrinter(printer_base): """Print a chrono::leap_second.""" def __init__(self, typename, val): self._typename = strip_versioned_namespace(typename) self._val = val def to_string(self): date = self._val['_M_s']['__r'] neg = '+-'[date < 0] return '%s %d (%c)' % (self._typename, abs(date), neg) class StdChronoTzdbPrinter(printer_base): """Print a chrono::tzdb.""" def __init__(self, typename, val): self._typename = strip_versioned_namespace(typename) self._val = val def to_string(self): return '%s %s' % (self._typename, self._val['version']) class StdChronoTimeZoneRulePrinter(printer_base): """Print a chrono::time_zone rule.""" def __init__(self, typename, val): self._typename = strip_versioned_namespace(typename) self._val = val def to_string(self): on = self._val['on'] kind = on['kind'] month = months[on['month']] suffixes = {1: 'st', 2: 'nd', 3: 'rd', 21: 'st', 22: 'nd', 23: 'rd', 31: 'st'} day = on['day_of_month'] ordinal_day = '{}{}'.format(day, suffixes.get(day, 'th')) if kind == 0: # DayOfMonth start = '{} {}'.format(month, ordinal_day) else: weekday = weekdays[on['day_of_week']] if kind == 1: # LastWeekDay start = 'last {} in {}'.format(weekday, month) else: if kind == 2: # LessEq direction = ('last', '<=') else: direction = ('first', '>=') day = on['day_of_month'] start = '{} {} {} {} {}'.format(direction[0], weekday, direction[1], month, ordinal_day) return 'time_zone rule {} from {} to {} starting on {}'.format( self._val['name'], self._val['from'], self._val['to'], start) class StdLocalePrinter(printer_base): """Print a std::locale.""" def __init__(self, typename, val): self._val = val self._typename = typename def to_string(self): names = self._val['_M_impl']['_M_names'] mod = '' if names[0] == 0: name = '*' else: cats = gdb.parse_and_eval(self._typename + '::_S_categories') ncat = gdb.parse_and_eval(self._typename + '::_S_categories_size') n = names[0].string() cat = cats[0].string() name = '{}={}'.format(cat, n) cat_names = {cat: n} i = 1 while i < ncat and names[i] != 0: n = names[i].string() cat = cats[i].string() name = '{};{}={}'.format(name, cat, n) cat_names[cat] = n i = i + 1 uniq_names = set(cat_names.values()) if len(uniq_names) == 1: name = n elif len(uniq_names) == 2: n1, n2 = (uniq_names) name_list = list(cat_names.values()) other = None if name_list.count(n1) == 1: name = n2 other = n1 elif name_list.count(n2) == 1: name = n1 other = n2 if other is not None: cat = next(c for c, n in cat_names.items() if n == other) mod = ' with "{}={}"'.format(cat, other) return 'std::locale = "{}"{}'.format(name, mod) class StdIntegralConstantPrinter(printer_base): """Print a std::true_type or std::false_type.""" def __init__(self, typename, val): self._val = val self._typename = typename def to_string(self): value_type = self._val.type.template_argument(0) value = self._val.type.template_argument(1) if value_type.code == gdb.TYPE_CODE_BOOL: if value: return "std::true_type" else: return "std::false_type" typename = strip_versioned_namespace(self._typename) return "{}<{}, {}>".format(typename, value_type, value) class StdTextEncodingPrinter(printer_base): """Print a std::text_encoding.""" def __init__(self, typename, val): self._val = val self._typename = typename def to_string(self): rep = self._val['_M_rep'].dereference() if rep['_M_id'] == 1: return self._val['_M_name'] if rep['_M_id'] == 2: return 'unknown' return rep['_M_name'] # A "regular expression" printer which conforms to the # "SubPrettyPrinter" protocol from gdb.printing. class RxPrinter(object): def __init__(self, name, function): super(RxPrinter, self).__init__() self.name = name self._function = function self.enabled = True def invoke(self, value): if not self.enabled: return None if value.type.code == gdb.TYPE_CODE_REF: if hasattr(gdb.Value, "referenced_value"): value = value.referenced_value() return self._function(self.name, value) # A pretty-printer that conforms to the "PrettyPrinter" protocol from # gdb.printing. It can also be used directly as an old-style printer. class Printer(object): def __init__(self, name): super(Printer, self).__init__() self.name = name self._subprinters = [] self._lookup = {} self.enabled = True self._compiled_rx = re.compile('^([a-zA-Z0-9_:]+)(<.*>)?$') def add(self, name, function): # A small sanity check. # FIXME if not self._compiled_rx.match(name): raise ValueError( 'libstdc++ programming error: "%s" does not match' % name) printer = RxPrinter(name, function) self._subprinters.append(printer) self._lookup[name] = printer # Add a name using _GLIBCXX_BEGIN_NAMESPACE_VERSION. def add_version(self, base, name, function): self.add(base + name, function) if '__cxx11' not in base: vbase = re.sub('^(std|__gnu_cxx)::', r'\g<0>%s' % _versioned_namespace, base) self.add(vbase + name, function) # Add a name using _GLIBCXX_BEGIN_NAMESPACE_CONTAINER. def add_container(self, base, name, function): self.add_version(base, name, function) self.add_version(base + '__cxx1998::', name, function) @staticmethod def get_basic_type(type): # If it points to a reference, get the reference. if type.code == gdb.TYPE_CODE_REF: type = type.target() # Get the unqualified type, stripped of typedefs. type = type.unqualified().strip_typedefs() return type.tag def __call__(self, val): typename = self.get_basic_type(val.type) if not typename: return None # All the types we match are template types, so we can use a # dictionary. match = self._compiled_rx.match(typename) if not match: return None basename = match.group(1) if val.type.code == gdb.TYPE_CODE_REF: if hasattr(gdb.Value, "referenced_value"): val = val.referenced_value() if basename in self._lookup: return self._lookup[basename].invoke(val) # Cannot find a pretty printer. Return None. return None libstdcxx_printer = None class TemplateTypePrinter(object): """ A type printer for class templates with default template arguments. Recognizes specializations of class templates and prints them without any template arguments that use a default template argument. Type printers are recursively applied to the template arguments. e.g. replace 'std::vector >' with 'std::vector'. """ def __init__(self, name, defargs): self.name = name self._defargs = defargs self.enabled = True class _recognizer(object): """The recognizer class for TemplateTypePrinter.""" def __init__(self, name, defargs): self.name = name self._defargs = defargs # self._type_obj = None def recognize(self, type_obj): """ If type_obj is a specialization of self.name that uses all the default template arguments for the class template, then return a string representation of the type without default arguments. Otherwise, return None. """ if type_obj.tag is None: return None if not type_obj.tag.startswith(self.name): return None template_args = get_template_arg_list(type_obj) displayed_args = [] require_defaulted = False for n in range(len(template_args)): # The actual template argument in the type: targ = template_args[n] # The default template argument for the class template: defarg = self._defargs.get(n) if defarg is not None: # Substitute other template arguments into the default: defarg = defarg.format(*template_args) # Fail to recognize the type (by returning None) # unless the actual argument is the same as the default. try: if targ != gdb.lookup_type(defarg): return None except gdb.error: # Type lookup failed, just use string comparison: if targ.tag != defarg: return None # All subsequent args must have defaults: require_defaulted = True elif require_defaulted: return None else: # Recursively apply recognizers to the template argument # and add it to the arguments that will be displayed: displayed_args.append(self._recognize_subtype(targ)) # This assumes no class templates in the nested-name-specifier: template_name = type_obj.tag[0:type_obj.tag.find('<')] template_name = strip_inline_namespaces(template_name) return template_name + '<' + ', '.join(displayed_args) + '>' def _recognize_subtype(self, type_obj): """Convert a gdb.Type to a string by applying recognizers, or if that fails then simply converting to a string.""" if type_obj.code == gdb.TYPE_CODE_PTR: return self._recognize_subtype(type_obj.target()) + '*' if type_obj.code == gdb.TYPE_CODE_ARRAY: type_str = self._recognize_subtype(type_obj.target()) if str(type_obj.strip_typedefs()).endswith('[]'): return type_str + '[]' # array of unknown bound return "%s[%d]" % (type_str, type_obj.range()[1] + 1) if type_obj.code == gdb.TYPE_CODE_REF: return self._recognize_subtype(type_obj.target()) + '&' if hasattr(gdb, 'TYPE_CODE_RVALUE_REF'): if type_obj.code == gdb.TYPE_CODE_RVALUE_REF: return self._recognize_subtype(type_obj.target()) + '&&' type_str = gdb.types.apply_type_recognizers( gdb.types.get_type_recognizers(), type_obj) if type_str: return type_str return str(type_obj) def instantiate(self): """Return a recognizer object for this type printer.""" return self._recognizer(self.name, self._defargs) def add_one_template_type_printer(obj, name, defargs): """ Add a type printer for a class template with default template arguments. Args: name (str): The template-name of the class template. defargs (dict int:string) The default template arguments. Types in defargs can refer to the Nth template-argument using {N} (with zero-based indices). e.g. 'unordered_map' has these defargs: { 2: 'std::hash<{0}>', 3: 'std::equal_to<{0}>', 4: 'std::allocator >' } """ printer = TemplateTypePrinter('std::' + name, defargs) gdb.types.register_type_printer(obj, printer) # Add type printer for same type in debug namespace: printer = TemplateTypePrinter('std::__debug::' + name, defargs) gdb.types.register_type_printer(obj, printer) if '__cxx11' not in name: # Add second type printer for same type in versioned namespace: ns = 'std::' + _versioned_namespace # PR 86112 Cannot use dict comprehension here: defargs = dict((n, d.replace('std::', ns)) for (n, d) in defargs.items()) printer = TemplateTypePrinter(ns + name, defargs) gdb.types.register_type_printer(obj, printer) # Add type printer for same type in debug namespace: printer = TemplateTypePrinter('std::__debug::' + name, defargs) gdb.types.register_type_printer(obj, printer) class FilteringTypePrinter(object): """ A type printer that uses typedef names for common template specializations. Args: template (str): The class template to recognize. name (str): The typedef-name that will be used instead. targ1 (str, optional): The first template argument. Defaults to None. Checks if a specialization of the class template 'template' is the same type as the typedef 'name', and prints it as 'name' instead. e.g. if an instantiation of std::basic_istream is the same type as std::istream then print it as std::istream. If targ1 is provided (not None), match only template specializations with this type as the first template argument, e.g. if template='basic_string' and targ1='char' then only match 'basic_string' and not 'basic_string'. This rejects non-matching specializations more quickly, without needing to do GDB type lookups. """ def __init__(self, template, name, targ1=None): self._template = template self.name = name self._targ1 = targ1 self.enabled = True class _recognizer(object): """The recognizer class for FilteringTypePrinter.""" def __init__(self, template, name, targ1): self._template = template self.name = name self._targ1 = targ1 self._type_obj = None def recognize(self, type_obj): """ If type_obj starts with self._template and is the same type as self.name then return self.name, otherwise None. """ if type_obj.tag is None: return None if self._type_obj is None: if self._targ1 is not None: s = '{}<{}'.format(self._template, self._targ1) if not type_obj.tag.startswith(s): # Filter didn't match. return None elif not type_obj.tag.startswith(self._template): # Filter didn't match. return None try: self._type_obj = gdb.lookup_type( self.name).strip_typedefs() except: pass if self._type_obj is None: return None t1 = gdb.types.get_basic_type(self._type_obj) t2 = gdb.types.get_basic_type(type_obj) if t1 == t2: return strip_inline_namespaces(self.name) # Workaround ambiguous typedefs matching both std:: and # std::__cxx11:: symbols. if self._template.split('::')[-1] == 'basic_string': s1 = self._type_obj.tag.replace('__cxx11::', '') s2 = type_obj.tag.replace('__cxx11::', '') if s1 == s2: return strip_inline_namespaces(self.name) return None def instantiate(self): """Return a recognizer object for this type printer.""" return self._recognizer(self._template, self.name, self._targ1) def add_one_type_printer(obj, template, name, targ1=None): printer = FilteringTypePrinter('std::' + template, 'std::' + name, targ1) gdb.types.register_type_printer(obj, printer) if '__cxx11' not in template: ns = 'std::' + _versioned_namespace printer = FilteringTypePrinter(ns + template, ns + name, targ1) gdb.types.register_type_printer(obj, printer) def register_type_printers(obj): global _use_type_printing if not _use_type_printing: return # Add type printers for typedefs std::string, std::wstring etc. for ch in (('', 'char'), ('w', 'wchar_t'), ('u8', 'char8_t'), ('u16', 'char16_t'), ('u32', 'char32_t')): add_one_type_printer(obj, 'basic_string', ch[0] + 'string', ch[1]) add_one_type_printer(obj, '__cxx11::basic_string', ch[0] + 'string', ch[1]) # Typedefs for __cxx11::basic_string used to be in namespace __cxx11: add_one_type_printer(obj, '__cxx11::basic_string', '__cxx11::' + ch[0] + 'string', ch[1]) add_one_type_printer(obj, 'basic_string_view', ch[0] + 'string_view', ch[1]) # Add type printers for typedefs std::istream, std::wistream etc. for ch in (('', 'char'), ('w', 'wchar_t')): for x in ('ios', 'streambuf', 'istream', 'ostream', 'iostream', 'filebuf', 'ifstream', 'ofstream', 'fstream'): add_one_type_printer(obj, 'basic_' + x, ch[0] + x, ch[1]) for x in ('stringbuf', 'istringstream', 'ostringstream', 'stringstream'): add_one_type_printer(obj, 'basic_' + x, ch[0] + x, ch[1]) # types are in __cxx11 namespace, but typedefs aren't: add_one_type_printer(obj, '__cxx11::basic_' + x, ch[0] + x, ch[1]) # Add type printers for typedefs regex, wregex, cmatch, wcmatch etc. for abi in ('', '__cxx11::'): for ch in (('', 'char'), ('w', 'wchar_t')): add_one_type_printer(obj, abi + 'basic_regex', abi + ch[0] + 'regex', ch[1]) for ch in ('c', 's', 'wc', 'ws'): add_one_type_printer( obj, abi + 'match_results', abi + ch + 'match') for x in ('sub_match', 'regex_iterator', 'regex_token_iterator'): add_one_type_printer(obj, abi + x, abi + ch + x) # Note that we can't have a printer for std::wstreampos, because # it is the same type as std::streampos. add_one_type_printer(obj, 'fpos', 'streampos') # Add type printers for typedefs. for dur in ('nanoseconds', 'microseconds', 'milliseconds', 'seconds', 'minutes', 'hours', 'days', 'weeks', 'years', 'months'): add_one_type_printer(obj, 'chrono::duration', 'chrono::' + dur) # Add type printers for typedefs. add_one_type_printer(obj, 'linear_congruential_engine', 'minstd_rand0') add_one_type_printer(obj, 'linear_congruential_engine', 'minstd_rand') add_one_type_printer(obj, 'mersenne_twister_engine', 'mt19937') add_one_type_printer(obj, 'mersenne_twister_engine', 'mt19937_64') add_one_type_printer(obj, 'subtract_with_carry_engine', 'ranlux24_base') add_one_type_printer(obj, 'subtract_with_carry_engine', 'ranlux48_base') add_one_type_printer(obj, 'discard_block_engine', 'ranlux24') add_one_type_printer(obj, 'discard_block_engine', 'ranlux48') add_one_type_printer(obj, 'shuffle_order_engine', 'knuth_b') # Add type printers for experimental::basic_string_view typedefs. ns = 'experimental::fundamentals_v1::' for ch in (('', 'char'), ('w', 'wchar_t'), ('u8', 'char8_t'), ('u16', 'char16_t'), ('u32', 'char32_t')): add_one_type_printer(obj, ns + 'basic_string_view', ns + ch[0] + 'string_view', ch[1]) # Do not show defaulted template arguments in class templates. add_one_template_type_printer(obj, 'unique_ptr', {1: 'std::default_delete<{0}>'}) add_one_template_type_printer(obj, 'deque', {1: 'std::allocator<{0}>'}) add_one_template_type_printer( obj, 'forward_list', {1: 'std::allocator<{0}>'}) add_one_template_type_printer(obj, 'list', {1: 'std::allocator<{0}>'}) add_one_template_type_printer( obj, '__cxx11::list', {1: 'std::allocator<{0}>'}) add_one_template_type_printer(obj, 'vector', {1: 'std::allocator<{0}>'}) add_one_template_type_printer(obj, 'map', {2: 'std::less<{0}>', 3: 'std::allocator>'}) add_one_template_type_printer(obj, 'multimap', {2: 'std::less<{0}>', 3: 'std::allocator>'}) add_one_template_type_printer(obj, 'set', {1: 'std::less<{0}>', 2: 'std::allocator<{0}>'}) add_one_template_type_printer(obj, 'multiset', {1: 'std::less<{0}>', 2: 'std::allocator<{0}>'}) add_one_template_type_printer(obj, 'unordered_map', {2: 'std::hash<{0}>', 3: 'std::equal_to<{0}>', 4: 'std::allocator>'}) add_one_template_type_printer(obj, 'unordered_multimap', {2: 'std::hash<{0}>', 3: 'std::equal_to<{0}>', 4: 'std::allocator>'}) add_one_template_type_printer(obj, 'unordered_set', {1: 'std::hash<{0}>', 2: 'std::equal_to<{0}>', 3: 'std::allocator<{0}>'}) add_one_template_type_printer(obj, 'unordered_multiset', {1: 'std::hash<{0}>', 2: 'std::equal_to<{0}>', 3: 'std::allocator<{0}>'}) def register_libstdcxx_printers(obj): """Register libstdc++ pretty-printers with objfile Obj.""" global _use_gdb_pp global libstdcxx_printer if _use_gdb_pp: gdb.printing.register_pretty_printer(obj, libstdcxx_printer) else: if obj is None: obj = gdb obj.pretty_printers.append(libstdcxx_printer) register_type_printers(obj) def build_libstdcxx_dictionary(): global libstdcxx_printer libstdcxx_printer = Printer("libstdc++-v6") # libstdc++ objects requiring pretty-printing. # In order from: # http://gcc.gnu.org/onlinedocs/libstdc++/latest-doxygen/a01847.html libstdcxx_printer.add_version('std::', 'basic_string', StdStringPrinter) libstdcxx_printer.add_version( 'std::__cxx11::', 'basic_string', StdStringPrinter) libstdcxx_printer.add_container('std::', 'bitset', StdBitsetPrinter) libstdcxx_printer.add_container('std::', 'deque', StdDequePrinter) libstdcxx_printer.add_container('std::', 'list', StdListPrinter) libstdcxx_printer.add_container('std::__cxx11::', 'list', StdListPrinter) libstdcxx_printer.add_container('std::', 'map', StdMapPrinter) libstdcxx_printer.add_container('std::', 'multimap', StdMapPrinter) libstdcxx_printer.add_container('std::', 'multiset', StdSetPrinter) libstdcxx_printer.add_version('std::', 'pair', StdPairPrinter) libstdcxx_printer.add_version('std::', 'priority_queue', StdStackOrQueuePrinter) libstdcxx_printer.add_version('std::', 'queue', StdStackOrQueuePrinter) libstdcxx_printer.add_version('std::', 'tuple', StdTuplePrinter) libstdcxx_printer.add_container('std::', 'set', StdSetPrinter) libstdcxx_printer.add_version('std::', 'stack', StdStackOrQueuePrinter) libstdcxx_printer.add_version('std::', 'unique_ptr', UniquePointerPrinter) libstdcxx_printer.add_container('std::', 'vector', StdVectorPrinter) # vector libstdcxx_printer.add_version('std::', 'locale', StdLocalePrinter) if hasattr(gdb.Value, 'dynamic_type'): libstdcxx_printer.add_version('std::', 'error_code', StdErrorCodePrinter) libstdcxx_printer.add_version('std::', 'error_condition', StdErrorCodePrinter) # Printer registrations for classes compiled with -D_GLIBCXX_DEBUG. libstdcxx_printer.add('std::__debug::bitset', StdBitsetPrinter) libstdcxx_printer.add('std::__debug::deque', StdDequePrinter) libstdcxx_printer.add('std::__debug::list', StdListPrinter) libstdcxx_printer.add('std::__debug::map', StdMapPrinter) libstdcxx_printer.add('std::__debug::multimap', StdMapPrinter) libstdcxx_printer.add('std::__debug::multiset', StdSetPrinter) libstdcxx_printer.add('std::__debug::set', StdSetPrinter) libstdcxx_printer.add('std::__debug::vector', StdVectorPrinter) # These are the TR1 and C++11 printers. # For array - the default GDB pretty-printer seems reasonable. libstdcxx_printer.add_version('std::', 'shared_ptr', SharedPointerPrinter) libstdcxx_printer.add_version('std::', 'weak_ptr', SharedPointerPrinter) libstdcxx_printer.add_container('std::', 'unordered_map', Tr1UnorderedMapPrinter) libstdcxx_printer.add_container('std::', 'unordered_set', Tr1UnorderedSetPrinter) libstdcxx_printer.add_container('std::', 'unordered_multimap', Tr1UnorderedMapPrinter) libstdcxx_printer.add_container('std::', 'unordered_multiset', Tr1UnorderedSetPrinter) libstdcxx_printer.add_container('std::', 'forward_list', StdForwardListPrinter) libstdcxx_printer.add_version( 'std::tr1::', 'shared_ptr', SharedPointerPrinter) libstdcxx_printer.add_version( 'std::tr1::', 'weak_ptr', SharedPointerPrinter) libstdcxx_printer.add_version('std::tr1::', 'unordered_map', Tr1UnorderedMapPrinter) libstdcxx_printer.add_version('std::tr1::', 'unordered_set', Tr1UnorderedSetPrinter) libstdcxx_printer.add_version('std::tr1::', 'unordered_multimap', Tr1UnorderedMapPrinter) libstdcxx_printer.add_version('std::tr1::', 'unordered_multiset', Tr1UnorderedSetPrinter) libstdcxx_printer.add_version('std::', 'initializer_list', StdInitializerListPrinter) libstdcxx_printer.add_version('std::', 'atomic', StdAtomicPrinter) libstdcxx_printer.add_version( 'std::', 'basic_stringbuf', StdStringBufPrinter) libstdcxx_printer.add_version( 'std::__cxx11::', 'basic_stringbuf', StdStringBufPrinter) for sstream in ('istringstream', 'ostringstream', 'stringstream'): libstdcxx_printer.add_version( 'std::', 'basic_' + sstream, StdStringStreamPrinter) libstdcxx_printer.add_version( 'std::__cxx11::', 'basic_' + sstream, StdStringStreamPrinter) libstdcxx_printer.add_version('std::chrono::', 'duration', StdChronoDurationPrinter) libstdcxx_printer.add_version('std::chrono::', 'time_point', StdChronoTimePointPrinter) libstdcxx_printer.add_version('std::', 'integral_constant', StdIntegralConstantPrinter) # std::regex components libstdcxx_printer.add_version('std::__detail::', '_State', StdRegexStatePrinter) # These are the C++11 printer registrations for -D_GLIBCXX_DEBUG cases. # The tr1 namespace containers do not have any debug equivalents, # so do not register printers for them. libstdcxx_printer.add('std::__debug::unordered_map', Tr1UnorderedMapPrinter) libstdcxx_printer.add('std::__debug::unordered_set', Tr1UnorderedSetPrinter) libstdcxx_printer.add('std::__debug::unordered_multimap', Tr1UnorderedMapPrinter) libstdcxx_printer.add('std::__debug::unordered_multiset', Tr1UnorderedSetPrinter) libstdcxx_printer.add('std::__debug::forward_list', StdForwardListPrinter) # Library Fundamentals TS components libstdcxx_printer.add_version('std::experimental::fundamentals_v1::', 'any', StdExpAnyPrinter) libstdcxx_printer.add_version('std::experimental::fundamentals_v1::', 'optional', StdExpOptionalPrinter) libstdcxx_printer.add_version('std::experimental::fundamentals_v1::', 'basic_string_view', StdExpStringViewPrinter) # Filesystem TS components libstdcxx_printer.add_version('std::experimental::filesystem::v1::', 'path', StdExpPathPrinter) libstdcxx_printer.add_version('std::experimental::filesystem::v1::__cxx11::', 'path', StdExpPathPrinter) libstdcxx_printer.add_version('std::filesystem::', 'path', StdPathPrinter) libstdcxx_printer.add_version('std::filesystem::__cxx11::', 'path', StdPathPrinter) # C++17 components libstdcxx_printer.add_version('std::', 'any', StdExpAnyPrinter) libstdcxx_printer.add_version('std::', 'optional', StdExpOptionalPrinter) libstdcxx_printer.add_version('std::', 'basic_string_view', StdExpStringViewPrinter) libstdcxx_printer.add_version('std::', 'variant', StdVariantPrinter) libstdcxx_printer.add_version('std::', '_Node_handle', StdNodeHandlePrinter) # C++20 components libstdcxx_printer.add_version( 'std::', 'partial_ordering', StdCmpCatPrinter) libstdcxx_printer.add_version('std::', 'weak_ordering', StdCmpCatPrinter) libstdcxx_printer.add_version('std::', 'strong_ordering', StdCmpCatPrinter) libstdcxx_printer.add_version('std::', 'span', StdSpanPrinter) libstdcxx_printer.add_version('std::', 'basic_format_args', StdFormatArgsPrinter) for c in ['day', 'month', 'year', 'weekday', 'weekday_indexed', 'weekday_last', 'month_day', 'month_day_last', 'month_weekday', 'month_weekday_last', 'year_month', 'year_month_day', 'year_month_day_last', 'year_month_weekday', 'year_month_weekday_last', 'hh_mm_ss']: libstdcxx_printer.add_version('std::chrono::', c, StdChronoCalendarPrinter) libstdcxx_printer.add_version('std::chrono::', 'time_zone', StdChronoTimeZonePrinter) libstdcxx_printer.add_version('std::chrono::', 'time_zone_link', StdChronoTimeZonePrinter) libstdcxx_printer.add_version('std::chrono::', 'zoned_time', StdChronoZonedTimePrinter) libstdcxx_printer.add_version('std::chrono::', 'leap_second', StdChronoLeapSecondPrinter) libstdcxx_printer.add_version( 'std::chrono::', 'tzdb', StdChronoTzdbPrinter) # libstdcxx_printer.add_version('std::chrono::(anonymous namespace)', 'Rule', # StdChronoTimeZoneRulePrinter) # C++26 components libstdcxx_printer.add_version('std::', 'text_encoding', StdTextEncodingPrinter) # Extensions. libstdcxx_printer.add_version('__gnu_cxx::', 'slist', StdSlistPrinter) if True: # These shouldn't be necessary, if GDB "print *i" worked. # But it often doesn't, so here they are. libstdcxx_printer.add_container('std::', '_List_iterator', StdListIteratorPrinter) libstdcxx_printer.add_container('std::', '_List_const_iterator', StdListIteratorPrinter) libstdcxx_printer.add_version('std::', '_Rb_tree_iterator', StdRbtreeIteratorPrinter) libstdcxx_printer.add_version('std::', '_Rb_tree_const_iterator', StdRbtreeIteratorPrinter) libstdcxx_printer.add_container('std::', '_Deque_iterator', StdDequeIteratorPrinter) libstdcxx_printer.add_container('std::', '_Deque_const_iterator', StdDequeIteratorPrinter) libstdcxx_printer.add_version('__gnu_cxx::', '__normal_iterator', StdVectorIteratorPrinter) libstdcxx_printer.add_container('std::', '_Bit_iterator', StdBitIteratorPrinter) libstdcxx_printer.add_container('std::', '_Bit_const_iterator', StdBitIteratorPrinter) libstdcxx_printer.add_container('std::', '_Bit_reference', StdBitReferencePrinter) libstdcxx_printer.add_version('__gnu_cxx::', '_Slist_iterator', StdSlistIteratorPrinter) libstdcxx_printer.add_container('std::', '_Fwd_list_iterator', StdFwdListIteratorPrinter) libstdcxx_printer.add_container('std::', '_Fwd_list_const_iterator', StdFwdListIteratorPrinter) # Debug (compiled with -D_GLIBCXX_DEBUG) printer # registrations. libstdcxx_printer.add('__gnu_debug::_Safe_iterator', StdDebugIteratorPrinter) build_libstdcxx_dictionary() libstdcxx/__pycache__/__init__.cpython-36.opt-1.pyc000064400000000200152525737710016112 0ustar003 h@sdS)Nrrr-/usr/share/gcc-8/python/libstdcxx/__init__.pyslibstdcxx/__pycache__/__init__.cpython-36.pyc000064400000000200152525737710015153 0ustar003 h@sdS)Nrrr-/usr/share/gcc-8/python/libstdcxx/__init__.pyslibstdcxx/__init__.py000064400000000001152525737710010666 0ustar00 distro.py000064400000140262152527712570006444 0ustar00#!/usr/bin/env python # Copyright 2015,2016,2017 Nir Cohen # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """ The ``distro`` package (``distro`` stands for Linux Distribution) provides information about the Linux distribution it runs on, such as a reliable machine-readable distro ID, or version information. It is the recommended replacement for Python's original :py:func:`platform.linux_distribution` function, but it provides much more functionality. An alternative implementation became necessary because Python 3.5 deprecated this function, and Python 3.8 removed it altogether. Its predecessor function :py:func:`platform.dist` was already deprecated since Python 2.6 and removed in Python 3.8. Still, there are many cases in which access to OS distribution information is needed. See `Python issue 1322 `_ for more information. """ import argparse import json import logging import os import re import shlex import subprocess import sys import warnings from typing import ( Any, Callable, Dict, Iterable, Optional, Sequence, TextIO, Tuple, Type, ) try: from typing import TypedDict except ImportError: # Python 3.7 TypedDict = dict __version__ = "1.8.0" class VersionDict(TypedDict): major: str minor: str build_number: str class InfoDict(TypedDict): id: str version: str version_parts: VersionDict like: str codename: str _UNIXCONFDIR = os.environ.get("UNIXCONFDIR", "/etc") _UNIXUSRLIBDIR = os.environ.get("UNIXUSRLIBDIR", "/usr/lib") _OS_RELEASE_BASENAME = "os-release" #: Translation table for normalizing the "ID" attribute defined in os-release #: files, for use by the :func:`distro.id` method. #: #: * Key: Value as defined in the os-release file, translated to lower case, #: with blanks translated to underscores. #: #: * Value: Normalized value. NORMALIZED_OS_ID = { "ol": "oracle", # Oracle Linux "opensuse-leap": "opensuse", # Newer versions of OpenSuSE report as opensuse-leap } #: Translation table for normalizing the "Distributor ID" attribute returned by #: the lsb_release command, for use by the :func:`distro.id` method. #: #: * Key: Value as returned by the lsb_release command, translated to lower #: case, with blanks translated to underscores. #: #: * Value: Normalized value. NORMALIZED_LSB_ID = { "enterpriseenterpriseas": "oracle", # Oracle Enterprise Linux 4 "enterpriseenterpriseserver": "oracle", # Oracle Linux 5 "redhatenterpriseworkstation": "rhel", # RHEL 6, 7 Workstation "redhatenterpriseserver": "rhel", # RHEL 6, 7 Server "redhatenterprisecomputenode": "rhel", # RHEL 6 ComputeNode } #: Translation table for normalizing the distro ID derived from the file name #: of distro release files, for use by the :func:`distro.id` method. #: #: * Key: Value as derived from the file name of a distro release file, #: translated to lower case, with blanks translated to underscores. #: #: * Value: Normalized value. NORMALIZED_DISTRO_ID = { "redhat": "rhel", # RHEL 6.x, 7.x } # Pattern for content of distro release file (reversed) _DISTRO_RELEASE_CONTENT_REVERSED_PATTERN = re.compile( r"(?:[^)]*\)(.*)\()? *(?:STL )?([\d.+\-a-z]*\d) *(?:esaeler *)?(.+)" ) # Pattern for base file name of distro release file _DISTRO_RELEASE_BASENAME_PATTERN = re.compile(r"(\w+)[-_](release|version)$") # Base file names to be looked up for if _UNIXCONFDIR is not readable. _DISTRO_RELEASE_BASENAMES = [ "SuSE-release", "arch-release", "base-release", "centos-release", "fedora-release", "gentoo-release", "mageia-release", "mandrake-release", "mandriva-release", "mandrivalinux-release", "manjaro-release", "oracle-release", "redhat-release", "rocky-release", "sl-release", "slackware-version", ] # Base file names to be ignored when searching for distro release file _DISTRO_RELEASE_IGNORE_BASENAMES = ( "debian_version", "lsb-release", "oem-release", _OS_RELEASE_BASENAME, "system-release", "plesk-release", "iredmail-release", ) def linux_distribution(full_distribution_name: bool = True) -> Tuple[str, str, str]: """ .. deprecated:: 1.6.0 :func:`distro.linux_distribution()` is deprecated. It should only be used as a compatibility shim with Python's :py:func:`platform.linux_distribution()`. Please use :func:`distro.id`, :func:`distro.version` and :func:`distro.name` instead. Return information about the current OS distribution as a tuple ``(id_name, version, codename)`` with items as follows: * ``id_name``: If *full_distribution_name* is false, the result of :func:`distro.id`. Otherwise, the result of :func:`distro.name`. * ``version``: The result of :func:`distro.version`. * ``codename``: The extra item (usually in parentheses) after the os-release version number, or the result of :func:`distro.codename`. The interface of this function is compatible with the original :py:func:`platform.linux_distribution` function, supporting a subset of its parameters. The data it returns may not exactly be the same, because it uses more data sources than the original function, and that may lead to different data if the OS distribution is not consistent across multiple data sources it provides (there are indeed such distributions ...). Another reason for differences is the fact that the :func:`distro.id` method normalizes the distro ID string to a reliable machine-readable value for a number of popular OS distributions. """ warnings.warn( "distro.linux_distribution() is deprecated. It should only be used as a " "compatibility shim with Python's platform.linux_distribution(). Please use " "distro.id(), distro.version() and distro.name() instead.", DeprecationWarning, stacklevel=2, ) return _distro.linux_distribution(full_distribution_name) def id() -> str: """ Return the distro ID of the current distribution, as a machine-readable string. For a number of OS distributions, the returned distro ID value is *reliable*, in the sense that it is documented and that it does not change across releases of the distribution. This package maintains the following reliable distro ID values: ============== ========================================= Distro ID Distribution ============== ========================================= "ubuntu" Ubuntu "debian" Debian "rhel" RedHat Enterprise Linux "centos" CentOS "fedora" Fedora "sles" SUSE Linux Enterprise Server "opensuse" openSUSE "amzn" Amazon Linux "arch" Arch Linux "buildroot" Buildroot "cloudlinux" CloudLinux OS "exherbo" Exherbo Linux "gentoo" GenToo Linux "ibm_powerkvm" IBM PowerKVM "kvmibm" KVM for IBM z Systems "linuxmint" Linux Mint "mageia" Mageia "mandriva" Mandriva Linux "parallels" Parallels "pidora" Pidora "raspbian" Raspbian "oracle" Oracle Linux (and Oracle Enterprise Linux) "scientific" Scientific Linux "slackware" Slackware "xenserver" XenServer "openbsd" OpenBSD "netbsd" NetBSD "freebsd" FreeBSD "midnightbsd" MidnightBSD "rocky" Rocky Linux "aix" AIX "guix" Guix System ============== ========================================= If you have a need to get distros for reliable IDs added into this set, or if you find that the :func:`distro.id` function returns a different distro ID for one of the listed distros, please create an issue in the `distro issue tracker`_. **Lookup hierarchy and transformations:** First, the ID is obtained from the following sources, in the specified order. The first available and non-empty value is used: * the value of the "ID" attribute of the os-release file, * the value of the "Distributor ID" attribute returned by the lsb_release command, * the first part of the file name of the distro release file, The so determined ID value then passes the following transformations, before it is returned by this method: * it is translated to lower case, * blanks (which should not be there anyway) are translated to underscores, * a normalization of the ID is performed, based upon `normalization tables`_. The purpose of this normalization is to ensure that the ID is as reliable as possible, even across incompatible changes in the OS distributions. A common reason for an incompatible change is the addition of an os-release file, or the addition of the lsb_release command, with ID values that differ from what was previously determined from the distro release file name. """ return _distro.id() def name(pretty: bool = False) -> str: """ Return the name of the current OS distribution, as a human-readable string. If *pretty* is false, the name is returned without version or codename. (e.g. "CentOS Linux") If *pretty* is true, the version and codename are appended. (e.g. "CentOS Linux 7.1.1503 (Core)") **Lookup hierarchy:** The name is obtained from the following sources, in the specified order. The first available and non-empty value is used: * If *pretty* is false: - the value of the "NAME" attribute of the os-release file, - the value of the "Distributor ID" attribute returned by the lsb_release command, - the value of the "" field of the distro release file. * If *pretty* is true: - the value of the "PRETTY_NAME" attribute of the os-release file, - the value of the "Description" attribute returned by the lsb_release command, - the value of the "" field of the distro release file, appended with the value of the pretty version ("" and "" fields) of the distro release file, if available. """ return _distro.name(pretty) def version(pretty: bool = False, best: bool = False) -> str: """ Return the version of the current OS distribution, as a human-readable string. If *pretty* is false, the version is returned without codename (e.g. "7.0"). If *pretty* is true, the codename in parenthesis is appended, if the codename is non-empty (e.g. "7.0 (Maipo)"). Some distributions provide version numbers with different precisions in the different sources of distribution information. Examining the different sources in a fixed priority order does not always yield the most precise version (e.g. for Debian 8.2, or CentOS 7.1). Some other distributions may not provide this kind of information. In these cases, an empty string would be returned. This behavior can be observed with rolling releases distributions (e.g. Arch Linux). The *best* parameter can be used to control the approach for the returned version: If *best* is false, the first non-empty version number in priority order of the examined sources is returned. If *best* is true, the most precise version number out of all examined sources is returned. **Lookup hierarchy:** In all cases, the version number is obtained from the following sources. If *best* is false, this order represents the priority order: * the value of the "VERSION_ID" attribute of the os-release file, * the value of the "Release" attribute returned by the lsb_release command, * the version number parsed from the "" field of the first line of the distro release file, * the version number parsed from the "PRETTY_NAME" attribute of the os-release file, if it follows the format of the distro release files. * the version number parsed from the "Description" attribute returned by the lsb_release command, if it follows the format of the distro release files. """ return _distro.version(pretty, best) def version_parts(best: bool = False) -> Tuple[str, str, str]: """ Return the version of the current OS distribution as a tuple ``(major, minor, build_number)`` with items as follows: * ``major``: The result of :func:`distro.major_version`. * ``minor``: The result of :func:`distro.minor_version`. * ``build_number``: The result of :func:`distro.build_number`. For a description of the *best* parameter, see the :func:`distro.version` method. """ return _distro.version_parts(best) def major_version(best: bool = False) -> str: """ Return the major version of the current OS distribution, as a string, if provided. Otherwise, the empty string is returned. The major version is the first part of the dot-separated version string. For a description of the *best* parameter, see the :func:`distro.version` method. """ return _distro.major_version(best) def minor_version(best: bool = False) -> str: """ Return the minor version of the current OS distribution, as a string, if provided. Otherwise, the empty string is returned. The minor version is the second part of the dot-separated version string. For a description of the *best* parameter, see the :func:`distro.version` method. """ return _distro.minor_version(best) def build_number(best: bool = False) -> str: """ Return the build number of the current OS distribution, as a string, if provided. Otherwise, the empty string is returned. The build number is the third part of the dot-separated version string. For a description of the *best* parameter, see the :func:`distro.version` method. """ return _distro.build_number(best) def like() -> str: """ Return a space-separated list of distro IDs of distributions that are closely related to the current OS distribution in regards to packaging and programming interfaces, for example distributions the current distribution is a derivative from. **Lookup hierarchy:** This information item is only provided by the os-release file. For details, see the description of the "ID_LIKE" attribute in the `os-release man page `_. """ return _distro.like() def codename() -> str: """ Return the codename for the release of the current OS distribution, as a string. If the distribution does not have a codename, an empty string is returned. Note that the returned codename is not always really a codename. For example, openSUSE returns "x86_64". This function does not handle such cases in any special way and just returns the string it finds, if any. **Lookup hierarchy:** * the codename within the "VERSION" attribute of the os-release file, if provided, * the value of the "Codename" attribute returned by the lsb_release command, * the value of the "" field of the distro release file. """ return _distro.codename() def info(pretty: bool = False, best: bool = False) -> InfoDict: """ Return certain machine-readable information items about the current OS distribution in a dictionary, as shown in the following example: .. sourcecode:: python { 'id': 'rhel', 'version': '7.0', 'version_parts': { 'major': '7', 'minor': '0', 'build_number': '' }, 'like': 'fedora', 'codename': 'Maipo' } The dictionary structure and keys are always the same, regardless of which information items are available in the underlying data sources. The values for the various keys are as follows: * ``id``: The result of :func:`distro.id`. * ``version``: The result of :func:`distro.version`. * ``version_parts -> major``: The result of :func:`distro.major_version`. * ``version_parts -> minor``: The result of :func:`distro.minor_version`. * ``version_parts -> build_number``: The result of :func:`distro.build_number`. * ``like``: The result of :func:`distro.like`. * ``codename``: The result of :func:`distro.codename`. For a description of the *pretty* and *best* parameters, see the :func:`distro.version` method. """ return _distro.info(pretty, best) def os_release_info() -> Dict[str, str]: """ Return a dictionary containing key-value pairs for the information items from the os-release file data source of the current OS distribution. See `os-release file`_ for details about these information items. """ return _distro.os_release_info() def lsb_release_info() -> Dict[str, str]: """ Return a dictionary containing key-value pairs for the information items from the lsb_release command data source of the current OS distribution. See `lsb_release command output`_ for details about these information items. """ return _distro.lsb_release_info() def distro_release_info() -> Dict[str, str]: """ Return a dictionary containing key-value pairs for the information items from the distro release file data source of the current OS distribution. See `distro release file`_ for details about these information items. """ return _distro.distro_release_info() def uname_info() -> Dict[str, str]: """ Return a dictionary containing key-value pairs for the information items from the distro release file data source of the current OS distribution. """ return _distro.uname_info() def os_release_attr(attribute: str) -> str: """ Return a single named information item from the os-release file data source of the current OS distribution. Parameters: * ``attribute`` (string): Key of the information item. Returns: * (string): Value of the information item, if the item exists. The empty string, if the item does not exist. See `os-release file`_ for details about these information items. """ return _distro.os_release_attr(attribute) def lsb_release_attr(attribute: str) -> str: """ Return a single named information item from the lsb_release command output data source of the current OS distribution. Parameters: * ``attribute`` (string): Key of the information item. Returns: * (string): Value of the information item, if the item exists. The empty string, if the item does not exist. See `lsb_release command output`_ for details about these information items. """ return _distro.lsb_release_attr(attribute) def distro_release_attr(attribute: str) -> str: """ Return a single named information item from the distro release file data source of the current OS distribution. Parameters: * ``attribute`` (string): Key of the information item. Returns: * (string): Value of the information item, if the item exists. The empty string, if the item does not exist. See `distro release file`_ for details about these information items. """ return _distro.distro_release_attr(attribute) def uname_attr(attribute: str) -> str: """ Return a single named information item from the distro release file data source of the current OS distribution. Parameters: * ``attribute`` (string): Key of the information item. Returns: * (string): Value of the information item, if the item exists. The empty string, if the item does not exist. """ return _distro.uname_attr(attribute) try: from functools import cached_property except ImportError: # Python < 3.8 class cached_property: # type: ignore """A version of @property which caches the value. On access, it calls the underlying function and sets the value in `__dict__` so future accesses will not re-call the property. """ def __init__(self, f: Callable[[Any], Any]) -> None: self._fname = f.__name__ self._f = f def __get__(self, obj: Any, owner: Type[Any]) -> Any: assert obj is not None, f"call {self._fname} on an instance" ret = obj.__dict__[self._fname] = self._f(obj) return ret class LinuxDistribution: """ Provides information about a OS distribution. This package creates a private module-global instance of this class with default initialization arguments, that is used by the `consolidated accessor functions`_ and `single source accessor functions`_. By using default initialization arguments, that module-global instance returns data about the current OS distribution (i.e. the distro this package runs on). Normally, it is not necessary to create additional instances of this class. However, in situations where control is needed over the exact data sources that are used, instances of this class can be created with a specific distro release file, or a specific os-release file, or without invoking the lsb_release command. """ def __init__( self, include_lsb: Optional[bool] = None, os_release_file: str = "", distro_release_file: str = "", include_uname: Optional[bool] = None, root_dir: Optional[str] = None, include_oslevel: Optional[bool] = None, ) -> None: """ The initialization method of this class gathers information from the available data sources, and stores that in private instance attributes. Subsequent access to the information items uses these private instance attributes, so that the data sources are read only once. Parameters: * ``include_lsb`` (bool): Controls whether the `lsb_release command output`_ is included as a data source. If the lsb_release command is not available in the program execution path, the data source for the lsb_release command will be empty. * ``os_release_file`` (string): The path name of the `os-release file`_ that is to be used as a data source. An empty string (the default) will cause the default path name to be used (see `os-release file`_ for details). If the specified or defaulted os-release file does not exist, the data source for the os-release file will be empty. * ``distro_release_file`` (string): The path name of the `distro release file`_ that is to be used as a data source. An empty string (the default) will cause a default search algorithm to be used (see `distro release file`_ for details). If the specified distro release file does not exist, or if no default distro release file can be found, the data source for the distro release file will be empty. * ``include_uname`` (bool): Controls whether uname command output is included as a data source. If the uname command is not available in the program execution path the data source for the uname command will be empty. * ``root_dir`` (string): The absolute path to the root directory to use to find distro-related information files. Note that ``include_*`` parameters must not be enabled in combination with ``root_dir``. * ``include_oslevel`` (bool): Controls whether (AIX) oslevel command output is included as a data source. If the oslevel command is not available in the program execution path the data source will be empty. Public instance attributes: * ``os_release_file`` (string): The path name of the `os-release file`_ that is actually used as a data source. The empty string if no distro release file is used as a data source. * ``distro_release_file`` (string): The path name of the `distro release file`_ that is actually used as a data source. The empty string if no distro release file is used as a data source. * ``include_lsb`` (bool): The result of the ``include_lsb`` parameter. This controls whether the lsb information will be loaded. * ``include_uname`` (bool): The result of the ``include_uname`` parameter. This controls whether the uname information will be loaded. * ``include_oslevel`` (bool): The result of the ``include_oslevel`` parameter. This controls whether (AIX) oslevel information will be loaded. * ``root_dir`` (string): The result of the ``root_dir`` parameter. The absolute path to the root directory to use to find distro-related information files. Raises: * :py:exc:`ValueError`: Initialization parameters combination is not supported. * :py:exc:`OSError`: Some I/O issue with an os-release file or distro release file. * :py:exc:`UnicodeError`: A data source has unexpected characters or uses an unexpected encoding. """ self.root_dir = root_dir self.etc_dir = os.path.join(root_dir, "etc") if root_dir else _UNIXCONFDIR self.usr_lib_dir = ( os.path.join(root_dir, "usr/lib") if root_dir else _UNIXUSRLIBDIR ) if os_release_file: self.os_release_file = os_release_file else: etc_dir_os_release_file = os.path.join(self.etc_dir, _OS_RELEASE_BASENAME) usr_lib_os_release_file = os.path.join( self.usr_lib_dir, _OS_RELEASE_BASENAME ) # NOTE: The idea is to respect order **and** have it set # at all times for API backwards compatibility. if os.path.isfile(etc_dir_os_release_file) or not os.path.isfile( usr_lib_os_release_file ): self.os_release_file = etc_dir_os_release_file else: self.os_release_file = usr_lib_os_release_file self.distro_release_file = distro_release_file or "" # updated later is_root_dir_defined = root_dir is not None if is_root_dir_defined and (include_lsb or include_uname or include_oslevel): raise ValueError( "Including subprocess data sources from specific root_dir is disallowed" " to prevent false information" ) self.include_lsb = ( include_lsb if include_lsb is not None else not is_root_dir_defined ) self.include_uname = ( include_uname if include_uname is not None else not is_root_dir_defined ) self.include_oslevel = ( include_oslevel if include_oslevel is not None else not is_root_dir_defined ) def __repr__(self) -> str: """Return repr of all info""" return ( "LinuxDistribution(" "os_release_file={self.os_release_file!r}, " "distro_release_file={self.distro_release_file!r}, " "include_lsb={self.include_lsb!r}, " "include_uname={self.include_uname!r}, " "include_oslevel={self.include_oslevel!r}, " "root_dir={self.root_dir!r}, " "_os_release_info={self._os_release_info!r}, " "_lsb_release_info={self._lsb_release_info!r}, " "_distro_release_info={self._distro_release_info!r}, " "_uname_info={self._uname_info!r}, " "_oslevel_info={self._oslevel_info!r})".format(self=self) ) def linux_distribution( self, full_distribution_name: bool = True ) -> Tuple[str, str, str]: """ Return information about the OS distribution that is compatible with Python's :func:`platform.linux_distribution`, supporting a subset of its parameters. For details, see :func:`distro.linux_distribution`. """ return ( self.name() if full_distribution_name else self.id(), self.version(), self._os_release_info.get("release_codename") or self.codename(), ) def id(self) -> str: """Return the distro ID of the OS distribution, as a string. For details, see :func:`distro.id`. """ def normalize(distro_id: str, table: Dict[str, str]) -> str: distro_id = distro_id.lower().replace(" ", "_") return table.get(distro_id, distro_id) distro_id = self.os_release_attr("id") if distro_id: return normalize(distro_id, NORMALIZED_OS_ID) distro_id = self.lsb_release_attr("distributor_id") if distro_id: return normalize(distro_id, NORMALIZED_LSB_ID) distro_id = self.distro_release_attr("id") if distro_id: return normalize(distro_id, NORMALIZED_DISTRO_ID) distro_id = self.uname_attr("id") if distro_id: return normalize(distro_id, NORMALIZED_DISTRO_ID) return "" def name(self, pretty: bool = False) -> str: """ Return the name of the OS distribution, as a string. For details, see :func:`distro.name`. """ name = ( self.os_release_attr("name") or self.lsb_release_attr("distributor_id") or self.distro_release_attr("name") or self.uname_attr("name") ) if pretty: name = self.os_release_attr("pretty_name") or self.lsb_release_attr( "description" ) if not name: name = self.distro_release_attr("name") or self.uname_attr("name") version = self.version(pretty=True) if version: name = f"{name} {version}" return name or "" def version(self, pretty: bool = False, best: bool = False) -> str: """ Return the version of the OS distribution, as a string. For details, see :func:`distro.version`. """ versions = [ self.os_release_attr("version_id"), self.lsb_release_attr("release"), self.distro_release_attr("version_id"), self._parse_distro_release_content(self.os_release_attr("pretty_name")).get( "version_id", "" ), self._parse_distro_release_content( self.lsb_release_attr("description") ).get("version_id", ""), self.uname_attr("release"), ] if self.uname_attr("id").startswith("aix"): # On AIX platforms, prefer oslevel command output. versions.insert(0, self.oslevel_info()) elif self.id() == "debian" or "debian" in self.like().split(): # On Debian-like, add debian_version file content to candidates list. versions.append(self._debian_version) version = "" if best: # This algorithm uses the last version in priority order that has # the best precision. If the versions are not in conflict, that # does not matter; otherwise, using the last one instead of the # first one might be considered a surprise. for v in versions: if v.count(".") > version.count(".") or version == "": version = v else: for v in versions: if v != "": version = v break if pretty and version and self.codename(): version = f"{version} ({self.codename()})" return version def version_parts(self, best: bool = False) -> Tuple[str, str, str]: """ Return the version of the OS distribution, as a tuple of version numbers. For details, see :func:`distro.version_parts`. """ version_str = self.version(best=best) if version_str: version_regex = re.compile(r"(\d+)\.?(\d+)?\.?(\d+)?") matches = version_regex.match(version_str) if matches: major, minor, build_number = matches.groups() return major, minor or "", build_number or "" return "", "", "" def major_version(self, best: bool = False) -> str: """ Return the major version number of the current distribution. For details, see :func:`distro.major_version`. """ return self.version_parts(best)[0] def minor_version(self, best: bool = False) -> str: """ Return the minor version number of the current distribution. For details, see :func:`distro.minor_version`. """ return self.version_parts(best)[1] def build_number(self, best: bool = False) -> str: """ Return the build number of the current distribution. For details, see :func:`distro.build_number`. """ return self.version_parts(best)[2] def like(self) -> str: """ Return the IDs of distributions that are like the OS distribution. For details, see :func:`distro.like`. """ return self.os_release_attr("id_like") or "" def codename(self) -> str: """ Return the codename of the OS distribution. For details, see :func:`distro.codename`. """ try: # Handle os_release specially since distros might purposefully set # this to empty string to have no codename return self._os_release_info["codename"] except KeyError: return ( self.lsb_release_attr("codename") or self.distro_release_attr("codename") or "" ) def info(self, pretty: bool = False, best: bool = False) -> InfoDict: """ Return certain machine-readable information about the OS distribution. For details, see :func:`distro.info`. """ return dict( id=self.id(), version=self.version(pretty, best), version_parts=dict( major=self.major_version(best), minor=self.minor_version(best), build_number=self.build_number(best), ), like=self.like(), codename=self.codename(), ) def os_release_info(self) -> Dict[str, str]: """ Return a dictionary containing key-value pairs for the information items from the os-release file data source of the OS distribution. For details, see :func:`distro.os_release_info`. """ return self._os_release_info def lsb_release_info(self) -> Dict[str, str]: """ Return a dictionary containing key-value pairs for the information items from the lsb_release command data source of the OS distribution. For details, see :func:`distro.lsb_release_info`. """ return self._lsb_release_info def distro_release_info(self) -> Dict[str, str]: """ Return a dictionary containing key-value pairs for the information items from the distro release file data source of the OS distribution. For details, see :func:`distro.distro_release_info`. """ return self._distro_release_info def uname_info(self) -> Dict[str, str]: """ Return a dictionary containing key-value pairs for the information items from the uname command data source of the OS distribution. For details, see :func:`distro.uname_info`. """ return self._uname_info def oslevel_info(self) -> str: """ Return AIX' oslevel command output. """ return self._oslevel_info def os_release_attr(self, attribute: str) -> str: """ Return a single named information item from the os-release file data source of the OS distribution. For details, see :func:`distro.os_release_attr`. """ return self._os_release_info.get(attribute, "") def lsb_release_attr(self, attribute: str) -> str: """ Return a single named information item from the lsb_release command output data source of the OS distribution. For details, see :func:`distro.lsb_release_attr`. """ return self._lsb_release_info.get(attribute, "") def distro_release_attr(self, attribute: str) -> str: """ Return a single named information item from the distro release file data source of the OS distribution. For details, see :func:`distro.distro_release_attr`. """ return self._distro_release_info.get(attribute, "") def uname_attr(self, attribute: str) -> str: """ Return a single named information item from the uname command output data source of the OS distribution. For details, see :func:`distro.uname_attr`. """ return self._uname_info.get(attribute, "") @cached_property def _os_release_info(self) -> Dict[str, str]: """ Get the information items from the specified os-release file. Returns: A dictionary containing all information items. """ if os.path.isfile(self.os_release_file): with open(self.os_release_file, encoding="utf-8") as release_file: return self._parse_os_release_content(release_file) return {} @staticmethod def _parse_os_release_content(lines: TextIO) -> Dict[str, str]: """ Parse the lines of an os-release file. Parameters: * lines: Iterable through the lines in the os-release file. Each line must be a unicode string or a UTF-8 encoded byte string. Returns: A dictionary containing all information items. """ props = {} lexer = shlex.shlex(lines, posix=True) lexer.whitespace_split = True tokens = list(lexer) for token in tokens: # At this point, all shell-like parsing has been done (i.e. # comments processed, quotes and backslash escape sequences # processed, multi-line values assembled, trailing newlines # stripped, etc.), so the tokens are now either: # * variable assignments: var=value # * commands or their arguments (not allowed in os-release) # Ignore any tokens that are not variable assignments if "=" in token: k, v = token.split("=", 1) props[k.lower()] = v if "version" in props: # extract release codename (if any) from version attribute match = re.search(r"\((\D+)\)|,\s*(\D+)", props["version"]) if match: release_codename = match.group(1) or match.group(2) props["codename"] = props["release_codename"] = release_codename if "version_codename" in props: # os-release added a version_codename field. Use that in # preference to anything else Note that some distros purposefully # do not have code names. They should be setting # version_codename="" props["codename"] = props["version_codename"] elif "ubuntu_codename" in props: # Same as above but a non-standard field name used on older Ubuntus props["codename"] = props["ubuntu_codename"] return props @cached_property def _lsb_release_info(self) -> Dict[str, str]: """ Get the information items from the lsb_release command output. Returns: A dictionary containing all information items. """ if not self.include_lsb: return {} try: cmd = ("lsb_release", "-a") stdout = subprocess.check_output(cmd, stderr=subprocess.DEVNULL) # Command not found or lsb_release returned error except (OSError, subprocess.CalledProcessError): return {} content = self._to_str(stdout).splitlines() return self._parse_lsb_release_content(content) @staticmethod def _parse_lsb_release_content(lines: Iterable[str]) -> Dict[str, str]: """ Parse the output of the lsb_release command. Parameters: * lines: Iterable through the lines of the lsb_release output. Each line must be a unicode string or a UTF-8 encoded byte string. Returns: A dictionary containing all information items. """ props = {} for line in lines: kv = line.strip("\n").split(":", 1) if len(kv) != 2: # Ignore lines without colon. continue k, v = kv props.update({k.replace(" ", "_").lower(): v.strip()}) return props @cached_property def _uname_info(self) -> Dict[str, str]: if not self.include_uname: return {} try: cmd = ("uname", "-rs") stdout = subprocess.check_output(cmd, stderr=subprocess.DEVNULL) except OSError: return {} content = self._to_str(stdout).splitlines() return self._parse_uname_content(content) @cached_property def _oslevel_info(self) -> str: if not self.include_oslevel: return "" try: stdout = subprocess.check_output("oslevel", stderr=subprocess.DEVNULL) except (OSError, subprocess.CalledProcessError): return "" return self._to_str(stdout).strip() @cached_property def _debian_version(self) -> str: try: with open( os.path.join(self.etc_dir, "debian_version"), encoding="ascii" ) as fp: return fp.readline().rstrip() except FileNotFoundError: return "" @staticmethod def _parse_uname_content(lines: Sequence[str]) -> Dict[str, str]: if not lines: return {} props = {} match = re.search(r"^([^\s]+)\s+([\d\.]+)", lines[0].strip()) if match: name, version = match.groups() # This is to prevent the Linux kernel version from # appearing as the 'best' version on otherwise # identifiable distributions. if name == "Linux": return {} props["id"] = name.lower() props["name"] = name props["release"] = version return props @staticmethod def _to_str(bytestring: bytes) -> str: encoding = sys.getfilesystemencoding() return bytestring.decode(encoding) @cached_property def _distro_release_info(self) -> Dict[str, str]: """ Get the information items from the specified distro release file. Returns: A dictionary containing all information items. """ if self.distro_release_file: # If it was specified, we use it and parse what we can, even if # its file name or content does not match the expected pattern. distro_info = self._parse_distro_release_file(self.distro_release_file) basename = os.path.basename(self.distro_release_file) # The file name pattern for user-specified distro release files # is somewhat more tolerant (compared to when searching for the # file), because we want to use what was specified as best as # possible. match = _DISTRO_RELEASE_BASENAME_PATTERN.match(basename) else: try: basenames = [ basename for basename in os.listdir(self.etc_dir) if basename not in _DISTRO_RELEASE_IGNORE_BASENAMES and os.path.isfile(os.path.join(self.etc_dir, basename)) ] # We sort for repeatability in cases where there are multiple # distro specific files; e.g. CentOS, Oracle, Enterprise all # containing `redhat-release` on top of their own. basenames.sort() except OSError: # This may occur when /etc is not readable but we can't be # sure about the *-release files. Check common entries of # /etc for information. If they turn out to not be there the # error is handled in `_parse_distro_release_file()`. basenames = _DISTRO_RELEASE_BASENAMES for basename in basenames: match = _DISTRO_RELEASE_BASENAME_PATTERN.match(basename) if match is None: continue filepath = os.path.join(self.etc_dir, basename) distro_info = self._parse_distro_release_file(filepath) # The name is always present if the pattern matches. if "name" not in distro_info: continue self.distro_release_file = filepath break else: # the loop didn't "break": no candidate. return {} if match is not None: distro_info["id"] = match.group(1) # CloudLinux < 7: manually enrich info with proper id. if "cloudlinux" in distro_info.get("name", "").lower(): distro_info["id"] = "cloudlinux" return distro_info def _parse_distro_release_file(self, filepath: str) -> Dict[str, str]: """ Parse a distro release file. Parameters: * filepath: Path name of the distro release file. Returns: A dictionary containing all information items. """ try: with open(filepath, encoding="utf-8") as fp: # Only parse the first line. For instance, on SLES there # are multiple lines. We don't want them... return self._parse_distro_release_content(fp.readline()) except OSError: # Ignore not being able to read a specific, seemingly version # related file. # See https://github.com/python-distro/distro/issues/162 return {} @staticmethod def _parse_distro_release_content(line: str) -> Dict[str, str]: """ Parse a line from a distro release file. Parameters: * line: Line from the distro release file. Must be a unicode string or a UTF-8 encoded byte string. Returns: A dictionary containing all information items. """ matches = _DISTRO_RELEASE_CONTENT_REVERSED_PATTERN.match(line.strip()[::-1]) distro_info = {} if matches: # regexp ensures non-None distro_info["name"] = matches.group(3)[::-1] if matches.group(2): distro_info["version_id"] = matches.group(2)[::-1] if matches.group(1): distro_info["codename"] = matches.group(1)[::-1] elif line: distro_info["name"] = line.strip() return distro_info _distro = LinuxDistribution() def main() -> None: logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) logger.addHandler(logging.StreamHandler(sys.stdout)) parser = argparse.ArgumentParser(description="OS distro info tool") parser.add_argument( "--json", "-j", help="Output in machine readable format", action="store_true" ) parser.add_argument( "--root-dir", "-r", type=str, dest="root_dir", help="Path to the root filesystem directory (defaults to /)", ) args = parser.parse_args() if args.root_dir: dist = LinuxDistribution( include_lsb=False, include_uname=False, include_oslevel=False, root_dir=args.root_dir, ) else: dist = _distro if args.json: logger.info(json.dumps(dist.info(), indent=4, sort_keys=True)) else: logger.info("Name: %s", dist.name(pretty=True)) distribution_version = dist.version(pretty=True) logger.info("Version: %s", distribution_version) distribution_codename = dist.codename() logger.info("Codename: %s", distribution_codename) if __name__ == "__main__": main() __pycache__/kcsig_verify.cpython-36.pyc000064400000012234152527712570014105 0ustar003 ;j\@s ddlZddlZddlZddlZddlZddlZddlZdgZdd1iZd Z e d Z e d Z e d Z Gd d d eZd2ddZddZd3ddZddZddZddZddZddZd d!Zejd"d#Zejd$d%Zd&d'Zd(d)Zd4d+d,Zd5d-d.Zd6d/d0Z dS)7NZrsa4096gpgtypekindroleserialkeyz/dev/shmc@s eZdZdS)ErrorN)__name__ __module__ __qualname__rr)/usr/libexec/kcare/python/kcsig_verify.pyr sr latin1cCsNt|}|tkr|S|tkr&|j|S|tkr:tt|Stdt|dS)NzUnsupported pae type )rbtypeutypeencodeintto_bytesstr ValueError)dataencodingdtyperrrrs  rcCs@t|}|tkr|S|tkr&|jdS|tkr8|jdStdS)Nzutf-8)rntyperdecoderrNotImplementedError)rrrrrnstr(s  rwc Cs8|d}t||}|j|WdQRXtj||dS)Nz.tmp)openwriteosrename)fnamecontentmode tmp_fnamefrrr atomic_write3s r)c Cst| }|jSQRXdS)N)r read)r$r(rrr read_file;s r+cCstjt|S)N)jsonloadsr+)r$rrr read_json@sr.cGs:dt|}x(|D] }t|}|dt||f7}qW|S)Ns%ds%d%s)lenr)partsresultpZbprrrpaeDs   r3cstfdd|DS)Ncsg|] }|qSrr).0r()rrr Oszpae_fields..)r3)rfieldsr)rr pae_fieldsMsr7cCst|t|dS)Nr)r7 PAE_FIELDS)rrrrpae_typeRsr9cCs$|dtkr tdt|ddS)Nrzinvalid key type: )r8rr)rrrr check_keyVs r:c csH|r |Vn8tjtdd$}|jt||j|jVWdQRXdS)Nz kcsig-data-)dirprefix)tempfileNamedTemporaryFileTMPDIRr!rflushname)r data_is_filer(rrr temp_datafile[s rCc cs*tj||d}z |VWdtj|XdS)N)r<r;)r=mkdtempshutilrmtree)r<r;Ztemp_dirrrrtemp_directoryfs rGcCs|ttddf}dd|d|dd|g}tj|tjtjtjd}|j|\}}|jd krntd t|d t|WdQRXdS) Nz kcsig-gpgtmp-)r;r<rz --homedirz --keyringz--verify-)stdinstdoutstderrrzVerify error:  ) rGr? subprocessPopenPIPE communicate returncode Exceptionr)keyfiledatafilesigdataZtmp_dircmdr2rJrKrrrrun_gpg_verifyos  rWcCsxt|tjtddX}|jtjt|d|jt ||"}tjt|}t |j ||WdQRXWdQRXdS)Nz kcsig-key-)r;r<r) r:r=r>r?r!base64 b64decoderr@rCrWrA) signaturerrrBkey_filerTrUrrr verify_keyys r\Fc Csd}i}xt|jD]h\}}||kr,d||<qyt|||||Wn.tk rp}zt|||<WYdd}~XqX|d7}qW||fS)Nrzno corresponding root key)itemsr\rRr) signatureskeysrrBcounterrorskeyidsigerrr verify_counts rfcCs|jdd}|jdd}i}|p"t}i}x|djD]\}} yXt| | d|krjdj| d|||<n*| d|krd j| d|||<n| ||<Wq6tk r} zt| ||<w6WYdd} ~ Xq6Xq6Wi} xN|jD]B\}} t| d |dt| d \} } | |kr| ||<q| | |<qWd } xl| jD]`\}} yt |d || |d dWn0tk r} zt| ||<WYdd} ~ Xn X| d7} q.W| st dt j |dS)N thresholdi' min_serialir`rz&invalid kind {0}, accepted list is {1}rz"invalid serial {0}, current is {1}r_)rrT)rrBr]z!Error validating file signature: ) getAVAILABLE_KINDSr^r:formatrRrrfr9r\r r,dumps)rUrT root_keyskindsrgrhrbZapplicable_keysrcrreZ verified_keysraZ root_errorsrrr_verifys>         rocCs$t|}t|}t||||ddS)N)rn)r.ro)ZsigfilerTZrootfilernrUrmrrrverifysrp)rrrrr)r)r)F)N)N)!rX contextlibr,r"rErMr=rjr8r?rrrrrRr rrr)r+r.r3r7r9r:contextmanagerrCrGrWr\rfrorprrrrs:        +__pycache__/distro.cpython-36.pyc000064400000122021152527712570012721 0ustar003 ;j@s(dZddlZddlZddlZddlZddlZddlZddlZddlZddl Z ddl m Z m Z m Z mZmZmZmZmZmZyddl mZWnek reZYnXdZGdddeZGdd d eZejjd d Zejjd d ZdZdddZddddddZ ddiZ!ej"dZ#ej"dZ$dddddddddd d!d"d#d$d%d&gZ%d'd(d)ed*d+d,fZ&dce'ee(e(e(fd.d/d0Z)e(d1d2d3Z*dde'e(d5d6d7Z+dee'e'e(d8d9d:Z,dfe'ee(e(e(fd;dd?Z.dhe'e(d;d@dAZ/die'e(d;dBdCZ0e(d1dDdEZ1e(d1dFdGZ2dje'e'ed8dHdIZ3e e(e(fd1dJdKZ4e e(e(fd1dLdMZ5e e(e(fd1dNdOZ6e e(e(fd1dPdQZ7e(e(dRdSdTZ8e(e(dRdUdVZ9e(e(dRdWdXZ:e(e(dRdYdZZ;ydd[le>Z?dd1d`daZ@eAdbkr$e@dS)ka The ``distro`` package (``distro`` stands for Linux Distribution) provides information about the Linux distribution it runs on, such as a reliable machine-readable distro ID, or version information. It is the recommended replacement for Python's original :py:func:`platform.linux_distribution` function, but it provides much more functionality. An alternative implementation became necessary because Python 3.5 deprecated this function, and Python 3.8 removed it altogether. Its predecessor function :py:func:`platform.dist` was already deprecated since Python 2.6 and removed in Python 3.8. Still, there are many cases in which access to OS distribution information is needed. See `Python issue 1322 `_ for more information. N) AnyCallableDictIterableOptionalSequenceTextIOTupleType) TypedDictz1.8.0c@seZdZUeeedS) VersionDictN)__name__ __module__ __qualname__strmajorminor build_numberrr#/usr/libexec/kcare/python/distro.pyr =s r c@s"eZdZUeeeee dS)InfoDictN) r rrridversionr version_partslikecodenamerrrrrCs rZ UNIXCONFDIRz/etcZ UNIXUSRLIBDIRz/usr/libz os-releaseZoracleZopensuse)Zolz opensuse-leapZrhel)ZenterpriseenterpriseasZenterpriseenterpriseserverZredhatenterpriseworkstationZredhatenterpriseserverZredhatenterprisecomputenoderedhatzA(?:[^)]*\)(.*)\()? *(?:STL )?([\d.+\-a-z]*\d) *(?:esaeler *)?(.+)z(\w+)[-_](release|version)$z SuSE-releasez arch-releasez base-releasezcentos-releasezfedora-releasezgentoo-releasezmageia-releasezmandrake-releasezmandriva-releasezmandrivalinux-releasezmanjaro-releasezoracle-releasezredhat-releasez rocky-releasez sl-releasezslackware-versiondebian_versionz lsb-releasez oem-releasezsystem-releasez plesk-releaseziredmail-releaseT)full_distribution_namereturncCstjdtddtj|S)a .. deprecated:: 1.6.0 :func:`distro.linux_distribution()` is deprecated. It should only be used as a compatibility shim with Python's :py:func:`platform.linux_distribution()`. Please use :func:`distro.id`, :func:`distro.version` and :func:`distro.name` instead. Return information about the current OS distribution as a tuple ``(id_name, version, codename)`` with items as follows: * ``id_name``: If *full_distribution_name* is false, the result of :func:`distro.id`. Otherwise, the result of :func:`distro.name`. * ``version``: The result of :func:`distro.version`. * ``codename``: The extra item (usually in parentheses) after the os-release version number, or the result of :func:`distro.codename`. The interface of this function is compatible with the original :py:func:`platform.linux_distribution` function, supporting a subset of its parameters. The data it returns may not exactly be the same, because it uses more data sources than the original function, and that may lead to different data if the OS distribution is not consistent across multiple data sources it provides (there are indeed such distributions ...). Another reason for differences is the fact that the :func:`distro.id` method normalizes the distro ID string to a reliable machine-readable value for a number of popular OS distributions. zdistro.linux_distribution() is deprecated. It should only be used as a compatibility shim with Python's platform.linux_distribution(). Please use distro.id(), distro.version() and distro.name() instead.) stacklevel)warningswarnDeprecationWarning_distrolinux_distribution)rrrrr&s !r&)rcCstjS)a Return the distro ID of the current distribution, as a machine-readable string. For a number of OS distributions, the returned distro ID value is *reliable*, in the sense that it is documented and that it does not change across releases of the distribution. This package maintains the following reliable distro ID values: ============== ========================================= Distro ID Distribution ============== ========================================= "ubuntu" Ubuntu "debian" Debian "rhel" RedHat Enterprise Linux "centos" CentOS "fedora" Fedora "sles" SUSE Linux Enterprise Server "opensuse" openSUSE "amzn" Amazon Linux "arch" Arch Linux "buildroot" Buildroot "cloudlinux" CloudLinux OS "exherbo" Exherbo Linux "gentoo" GenToo Linux "ibm_powerkvm" IBM PowerKVM "kvmibm" KVM for IBM z Systems "linuxmint" Linux Mint "mageia" Mageia "mandriva" Mandriva Linux "parallels" Parallels "pidora" Pidora "raspbian" Raspbian "oracle" Oracle Linux (and Oracle Enterprise Linux) "scientific" Scientific Linux "slackware" Slackware "xenserver" XenServer "openbsd" OpenBSD "netbsd" NetBSD "freebsd" FreeBSD "midnightbsd" MidnightBSD "rocky" Rocky Linux "aix" AIX "guix" Guix System ============== ========================================= If you have a need to get distros for reliable IDs added into this set, or if you find that the :func:`distro.id` function returns a different distro ID for one of the listed distros, please create an issue in the `distro issue tracker`_. **Lookup hierarchy and transformations:** First, the ID is obtained from the following sources, in the specified order. The first available and non-empty value is used: * the value of the "ID" attribute of the os-release file, * the value of the "Distributor ID" attribute returned by the lsb_release command, * the first part of the file name of the distro release file, The so determined ID value then passes the following transformations, before it is returned by this method: * it is translated to lower case, * blanks (which should not be there anyway) are translated to underscores, * a normalization of the ID is performed, based upon `normalization tables`_. The purpose of this normalization is to ensure that the ID is as reliable as possible, even across incompatible changes in the OS distributions. A common reason for an incompatible change is the addition of an os-release file, or the addition of the lsb_release command, with ID values that differ from what was previously determined from the distro release file name. )r%rrrrrrsPrF)prettyrcCs tj|S)ak Return the name of the current OS distribution, as a human-readable string. If *pretty* is false, the name is returned without version or codename. (e.g. "CentOS Linux") If *pretty* is true, the version and codename are appended. (e.g. "CentOS Linux 7.1.1503 (Core)") **Lookup hierarchy:** The name is obtained from the following sources, in the specified order. The first available and non-empty value is used: * If *pretty* is false: - the value of the "NAME" attribute of the os-release file, - the value of the "Distributor ID" attribute returned by the lsb_release command, - the value of the "" field of the distro release file. * If *pretty* is true: - the value of the "PRETTY_NAME" attribute of the os-release file, - the value of the "Description" attribute returned by the lsb_release command, - the value of the "" field of the distro release file, appended with the value of the pretty version ("" and "" fields) of the distro release file, if available. )r%name)r'rrrr(s$r()r'bestrcCs tj||S)aN Return the version of the current OS distribution, as a human-readable string. If *pretty* is false, the version is returned without codename (e.g. "7.0"). If *pretty* is true, the codename in parenthesis is appended, if the codename is non-empty (e.g. "7.0 (Maipo)"). Some distributions provide version numbers with different precisions in the different sources of distribution information. Examining the different sources in a fixed priority order does not always yield the most precise version (e.g. for Debian 8.2, or CentOS 7.1). Some other distributions may not provide this kind of information. In these cases, an empty string would be returned. This behavior can be observed with rolling releases distributions (e.g. Arch Linux). The *best* parameter can be used to control the approach for the returned version: If *best* is false, the first non-empty version number in priority order of the examined sources is returned. If *best* is true, the most precise version number out of all examined sources is returned. **Lookup hierarchy:** In all cases, the version number is obtained from the following sources. If *best* is false, this order represents the priority order: * the value of the "VERSION_ID" attribute of the os-release file, * the value of the "Release" attribute returned by the lsb_release command, * the version number parsed from the "" field of the first line of the distro release file, * the version number parsed from the "PRETTY_NAME" attribute of the os-release file, if it follows the format of the distro release files. * the version number parsed from the "Description" attribute returned by the lsb_release command, if it follows the format of the distro release files. )r%r)r'r)rrrrBs-r)r)rcCs tj|S)a Return the version of the current OS distribution as a tuple ``(major, minor, build_number)`` with items as follows: * ``major``: The result of :func:`distro.major_version`. * ``minor``: The result of :func:`distro.minor_version`. * ``build_number``: The result of :func:`distro.build_number`. For a description of the *best* parameter, see the :func:`distro.version` method. )r%r)r)rrrrrsrcCs tj|S)a5 Return the major version of the current OS distribution, as a string, if provided. Otherwise, the empty string is returned. The major version is the first part of the dot-separated version string. For a description of the *best* parameter, see the :func:`distro.version` method. )r% major_version)r)rrrr*s r*cCs tj|S)a6 Return the minor version of the current OS distribution, as a string, if provided. Otherwise, the empty string is returned. The minor version is the second part of the dot-separated version string. For a description of the *best* parameter, see the :func:`distro.version` method. )r% minor_version)r)rrrr+s r+cCs tj|S)a3 Return the build number of the current OS distribution, as a string, if provided. Otherwise, the empty string is returned. The build number is the third part of the dot-separated version string. For a description of the *best* parameter, see the :func:`distro.version` method. )r%r)r)rrrrs rcCstjS)a Return a space-separated list of distro IDs of distributions that are closely related to the current OS distribution in regards to packaging and programming interfaces, for example distributions the current distribution is a derivative from. **Lookup hierarchy:** This information item is only provided by the os-release file. For details, see the description of the "ID_LIKE" attribute in the `os-release man page `_. )r%rrrrrrsrcCstjS)a Return the codename for the release of the current OS distribution, as a string. If the distribution does not have a codename, an empty string is returned. Note that the returned codename is not always really a codename. For example, openSUSE returns "x86_64". This function does not handle such cases in any special way and just returns the string it finds, if any. **Lookup hierarchy:** * the codename within the "VERSION" attribute of the os-release file, if provided, * the value of the "Codename" attribute returned by the lsb_release command, * the value of the "" field of the distro release file. )r%rrrrrrsrcCs tj||S)a Return certain machine-readable information items about the current OS distribution in a dictionary, as shown in the following example: .. sourcecode:: python { 'id': 'rhel', 'version': '7.0', 'version_parts': { 'major': '7', 'minor': '0', 'build_number': '' }, 'like': 'fedora', 'codename': 'Maipo' } The dictionary structure and keys are always the same, regardless of which information items are available in the underlying data sources. The values for the various keys are as follows: * ``id``: The result of :func:`distro.id`. * ``version``: The result of :func:`distro.version`. * ``version_parts -> major``: The result of :func:`distro.major_version`. * ``version_parts -> minor``: The result of :func:`distro.minor_version`. * ``version_parts -> build_number``: The result of :func:`distro.build_number`. * ``like``: The result of :func:`distro.like`. * ``codename``: The result of :func:`distro.codename`. For a description of the *pretty* and *best* parameters, see the :func:`distro.version` method. )r%info)r'r)rrrr,s)r,cCstjS)z Return a dictionary containing key-value pairs for the information items from the os-release file data source of the current OS distribution. See `os-release file`_ for details about these information items. )r%os_release_inforrrrr-sr-cCstjS)z Return a dictionary containing key-value pairs for the information items from the lsb_release command data source of the current OS distribution. See `lsb_release command output`_ for details about these information items. )r%lsb_release_inforrrrr. sr.cCstjS)z Return a dictionary containing key-value pairs for the information items from the distro release file data source of the current OS distribution. See `distro release file`_ for details about these information items. )r%distro_release_inforrrrr/sr/cCstjS)z Return a dictionary containing key-value pairs for the information items from the distro release file data source of the current OS distribution. )r% uname_inforrrrr0sr0) attributercCs tj|S)a Return a single named information item from the os-release file data source of the current OS distribution. Parameters: * ``attribute`` (string): Key of the information item. Returns: * (string): Value of the information item, if the item exists. The empty string, if the item does not exist. See `os-release file`_ for details about these information items. )r%os_release_attr)r1rrrr2&sr2cCs tj|S)a Return a single named information item from the lsb_release command output data source of the current OS distribution. Parameters: * ``attribute`` (string): Key of the information item. Returns: * (string): Value of the information item, if the item exists. The empty string, if the item does not exist. See `lsb_release command output`_ for details about these information items. )r%lsb_release_attr)r1rrrr39sr3cCs tj|S)a Return a single named information item from the distro release file data source of the current OS distribution. Parameters: * ``attribute`` (string): Key of the information item. Returns: * (string): Value of the information item, if the item exists. The empty string, if the item does not exist. See `distro release file`_ for details about these information items. )r%distro_release_attr)r1rrrr4Msr4cCs tj|S)aZ Return a single named information item from the distro release file data source of the current OS distribution. Parameters: * ``attribute`` (string): Key of the information item. Returns: * (string): Value of the information item, if the item exists. The empty string, if the item does not exist. )r% uname_attr)r1rrrr5`sr5)cached_propertyc@s@eZdZdZeegefddddZeeeedddZdS) r6zA version of @property which caches the value. On access, it calls the underlying function and sets the value in `__dict__` so future accesses will not re-call the property. N)frcCs|j|_||_dS)N)r _fname_f)selfr7rrr__init__{szcached_property.__init__)objownerrcCs4|dk std|jd|j|}|j|j<|S)Nzcall z on an instance)AssertionErrorr8r9__dict__)r:r<r=retrrr__get__szcached_property.__get__) r rr__doc__rrr;r rArrrrr6usr6c @seZdZdZdUeeeeeeeeeeddddZeddd ZdVee eeefd d d Z edddZ dWeedddZ dXeeedddZ dYee eeefdddZdZeedddZd[eedddZd\eedddZedd d!Zedd"d#Zd]eeedd$d%Zeeefdd&d'Zeeefdd(d)Zeeefdd*d+Zeeefdd,d-Zedd.d/Zeed0d1d2Zeed0d3d4Zeed0d5d6Zeed0d7d8Ze eeefdd9d:Z!e"e#eeefd;dd?Z%e"e&eeeefd;d@dAZ'e eeefddBdCZ(e eddDdEZ)e eddFdGZ*e"e+eeeefd;dHdIZ,e"e-edJdKdLZ.e eeefddMdNZ/eeeefdOdPdQZ0e"eeeefdRdSdTZ1dS)^LinuxDistributiona  Provides information about a OS distribution. This package creates a private module-global instance of this class with default initialization arguments, that is used by the `consolidated accessor functions`_ and `single source accessor functions`_. By using default initialization arguments, that module-global instance returns data about the current OS distribution (i.e. the distro this package runs on). Normally, it is not necessary to create additional instances of this class. However, in situations where control is needed over the exact data sources that are used, instances of this class can be created with a specific distro release file, or a specific os-release file, or without invoking the lsb_release command. N) include_lsbos_release_filedistro_release_file include_unameroot_dirinclude_oslevelrc Cs||_|rtjj|dnt|_|r0tjj|dnt|_|rB||_nHtjj|jt }tjj|jt }tjj |s|tjj | r||_n||_|pd|_ |dk } | r|s|s|rt d|dk r|n| |_ |dk r|n| |_|dk r|n| |_dS)ah The initialization method of this class gathers information from the available data sources, and stores that in private instance attributes. Subsequent access to the information items uses these private instance attributes, so that the data sources are read only once. Parameters: * ``include_lsb`` (bool): Controls whether the `lsb_release command output`_ is included as a data source. If the lsb_release command is not available in the program execution path, the data source for the lsb_release command will be empty. * ``os_release_file`` (string): The path name of the `os-release file`_ that is to be used as a data source. An empty string (the default) will cause the default path name to be used (see `os-release file`_ for details). If the specified or defaulted os-release file does not exist, the data source for the os-release file will be empty. * ``distro_release_file`` (string): The path name of the `distro release file`_ that is to be used as a data source. An empty string (the default) will cause a default search algorithm to be used (see `distro release file`_ for details). If the specified distro release file does not exist, or if no default distro release file can be found, the data source for the distro release file will be empty. * ``include_uname`` (bool): Controls whether uname command output is included as a data source. If the uname command is not available in the program execution path the data source for the uname command will be empty. * ``root_dir`` (string): The absolute path to the root directory to use to find distro-related information files. Note that ``include_*`` parameters must not be enabled in combination with ``root_dir``. * ``include_oslevel`` (bool): Controls whether (AIX) oslevel command output is included as a data source. If the oslevel command is not available in the program execution path the data source will be empty. Public instance attributes: * ``os_release_file`` (string): The path name of the `os-release file`_ that is actually used as a data source. The empty string if no distro release file is used as a data source. * ``distro_release_file`` (string): The path name of the `distro release file`_ that is actually used as a data source. The empty string if no distro release file is used as a data source. * ``include_lsb`` (bool): The result of the ``include_lsb`` parameter. This controls whether the lsb information will be loaded. * ``include_uname`` (bool): The result of the ``include_uname`` parameter. This controls whether the uname information will be loaded. * ``include_oslevel`` (bool): The result of the ``include_oslevel`` parameter. This controls whether (AIX) oslevel information will be loaded. * ``root_dir`` (string): The result of the ``root_dir`` parameter. The absolute path to the root directory to use to find distro-related information files. Raises: * :py:exc:`ValueError`: Initialization parameters combination is not supported. * :py:exc:`OSError`: Some I/O issue with an os-release file or distro release file. * :py:exc:`UnicodeError`: A data source has unexpected characters or uses an unexpected encoding. etczusr/librDNzcIncluding subprocess data sources from specific root_dir is disallowed to prevent false information)rIospathjoin _UNIXCONFDIRetc_dir_UNIXUSRLIBDIRZ usr_lib_dirrF_OS_RELEASE_BASENAMEisfilerG ValueErrorrErHrJ) r:rErFrGrHrIrJZetc_dir_os_release_fileZusr_lib_os_release_fileZis_root_dir_definedrrrr;s(\  zLinuxDistribution.__init__)rcCs dj|dS)zReturn repr of all infoaLinuxDistribution(os_release_file={self.os_release_file!r}, distro_release_file={self.distro_release_file!r}, include_lsb={self.include_lsb!r}, include_uname={self.include_uname!r}, include_oslevel={self.include_oslevel!r}, root_dir={self.root_dir!r}, _os_release_info={self._os_release_info!r}, _lsb_release_info={self._lsb_release_info!r}, _distro_release_info={self._distro_release_info!r}, _uname_info={self._uname_info!r}, _oslevel_info={self._oslevel_info!r}))r:)format)r:rrr__repr__s zLinuxDistribution.__repr__T)rrcCs.|r |jn|j|j|jjdp*|jfS)z Return information about the OS distribution that is compatible with Python's :func:`platform.linux_distribution`, supporting a subset of its parameters. For details, see :func:`distro.linux_distribution`. release_codename)r(rr_os_release_infogetr)r:rrrrr&-s z$LinuxDistribution.linux_distributioncCs~ttttftddd}|jd}|r2||tS|jd}|rJ||tS|jd}|rb||tS|jd}|rz||tSdS)zoReturn the distro ID of the OS distribution, as a string. For details, see :func:`distro.id`. ) distro_idtablercSs|jjdd}|j||S)N _)lowerreplacerY)rZr[rrr normalizeCsz'LinuxDistribution.id..normalizerdistributor_idrD) rrr2NORMALIZED_OS_IDr3NORMALIZED_LSB_IDr4NORMALIZED_DISTRO_IDr5)r:r`rZrrrr=s        zLinuxDistribution.idF)r'rcCs~|jdp&|jdp&|jdp&|jd}|rv|jdp>|jd}|sv|jdpV|jd}|jdd}|rv|d|}|p|dS) zu Return the name of the OS distribution, as a string. For details, see :func:`distro.name`. r(ra pretty_name descriptionT)r'r\rD)r2r3r4r5r)r:r'r(rrrrr(Ys     zLinuxDistribution.name)r'r)rcCs|jd|jd|jd|j|jdjdd|j|jdjdd|jdg}|jdjdrr|jd|jn(|j d ksd |j j kr|j |j d}|rxJ|D]$}|jd |jd ks|dkr|}qWnx|D]}|dkr|}PqW|o|o|jr|d |jd }|S) z{ Return the version of the OS distribution, as a string. For details, see :func:`distro.version`. version_idreleasererDrfraixrdebian.z ())r2r3r4_parse_distro_release_contentrYr5 startswithinsert oslevel_inforrsplitappend_debian_versioncountr)r:r'r)versionsrvrrrrps2      zLinuxDistribution.version)r)rcCsL|j|d}|rHtjd}|j|}|rH|j\}}}||p>d|pDdfSdS)z Return the version of the OS distribution, as a tuple of version numbers. For details, see :func:`distro.version_parts`. )r)z(\d+)\.?(\d+)?\.?(\d+)?rD)rDrDrD)rrecompilematchgroups)r:r)Z version_strZ version_regexmatchesrrrrrrrs   zLinuxDistribution.version_partscCs|j|dS)z Return the major version number of the current distribution. For details, see :func:`distro.major_version`. r)r)r:r)rrrr*szLinuxDistribution.major_versioncCs|j|dS)z Return the minor version number of the current distribution. For details, see :func:`distro.minor_version`. )r)r:r)rrrr+szLinuxDistribution.minor_versioncCs|j|dS)z} Return the build number of the current distribution. For details, see :func:`distro.build_number`. r )r)r:r)rrrrszLinuxDistribution.build_numbercCs|jdp dS)z Return the IDs of distributions that are like the OS distribution. For details, see :func:`distro.like`. Zid_likerD)r2)r:rrrrszLinuxDistribution.likec Cs8y |jdStk r2|jdp0|jdp0dSXdS)zp Return the codename of the OS distribution. For details, see :func:`distro.codename`. rrDN)rXKeyErrorr3r4)r:rrrrs    zLinuxDistribution.codenamecCsBt|j|j||t|j||j||j|d|j|jdS)z Return certain machine-readable information about the OS distribution. For details, see :func:`distro.info`. )rrr)rrrrr)dictrrr*r+rrr)r:r'r)rrrr,s  zLinuxDistribution.infocCs|jS)z Return a dictionary containing key-value pairs for the information items from the os-release file data source of the OS distribution. For details, see :func:`distro.os_release_info`. )rX)r:rrrr-sz!LinuxDistribution.os_release_infocCs|jS)z Return a dictionary containing key-value pairs for the information items from the lsb_release command data source of the OS distribution. For details, see :func:`distro.lsb_release_info`. )_lsb_release_info)r:rrrr.sz"LinuxDistribution.lsb_release_infocCs|jS)z Return a dictionary containing key-value pairs for the information items from the distro release file data source of the OS distribution. For details, see :func:`distro.distro_release_info`. )_distro_release_info)r:rrrr/sz%LinuxDistribution.distro_release_infocCs|jS)z Return a dictionary containing key-value pairs for the information items from the uname command data source of the OS distribution. For details, see :func:`distro.uname_info`. ) _uname_info)r:rrrr0 szLinuxDistribution.uname_infocCs|jS)z5 Return AIX' oslevel command output. ) _oslevel_info)r:rrrrpszLinuxDistribution.oslevel_info)r1rcCs|jj|dS)z Return a single named information item from the os-release file data source of the OS distribution. For details, see :func:`distro.os_release_attr`. rD)rXrY)r:r1rrrr2sz!LinuxDistribution.os_release_attrcCs|jj|dS)z Return a single named information item from the lsb_release command output data source of the OS distribution. For details, see :func:`distro.lsb_release_attr`. rD)rrY)r:r1rrrr3#sz"LinuxDistribution.lsb_release_attrcCs|jj|dS)z Return a single named information item from the distro release file data source of the OS distribution. For details, see :func:`distro.distro_release_attr`. rD)rrY)r:r1rrrr4,sz%LinuxDistribution.distro_release_attrcCs|jj|dS)z Return a single named information item from the uname command output data source of the OS distribution. For details, see :func:`distro.uname_attr`. rD)rrY)r:r1rrrr55szLinuxDistribution.uname_attrc Cs2tjj|jr.t|jdd }|j|SQRXiS)z Get the information items from the specified os-release file. Returns: A dictionary containing all information items. zutf-8)encodingN)rLrMrSrFopen_parse_os_release_content)r:Z release_filerrrrX>sz"LinuxDistribution._os_release_info)linesrc Csi}tj|dd}d|_t|}x0|D](}d|kr&|jdd\}}|||j<q&Wd|krtjd|d}|r|jdp|jd}||d<|d <d |kr|d |d<nd |kr|d |d<|S) aD Parse the lines of an os-release file. Parameters: * lines: Iterable through the lines in the os-release file. Each line must be a unicode string or a UTF-8 encoded byte string. Returns: A dictionary containing all information items. T)posix=r|rz\((\D+)\)|,\s*(\D+)r rrWZversion_codenameZubuntu_codename)shlexwhitespace_splitlistrqr^rwsearchgroup) rpropslexertokenstokenkrvryrWrrrrKs$  z+LinuxDistribution._parse_os_release_contentc CsV|js iSyd}tj|tjd}Wnttjfk r<iSX|j|j}|j|S)z Get the information items from the lsb_release command output. Returns: A dictionary containing all information items. lsb_release-a)stderr)rr) rE subprocess check_outputDEVNULLOSErrorCalledProcessError_to_str splitlines_parse_lsb_release_content)r:cmdstdoutcontentrrrr}sz#LinuxDistribution._lsb_release_infocCs\i}xR|D]J}|jdjdd}t|dkr.q |\}}|j|jddj|jiq W|S)aM Parse the output of the lsb_release command. Parameters: * lines: Iterable through the lines of the lsb_release output. Each line must be a unicode string or a UTF-8 encoded byte string. Returns: A dictionary containing all information items.  :r|r r\r])striprqlenupdater_r^)rrlinekvrrvrrrrs  "z,LinuxDistribution._parse_lsb_release_contentc CsP|js iSyd}tj|tjd}Wntk r6iSX|j|j}|j|S)Nuname-rs)r)rr)rHrrrrrr_parse_uname_content)r:rrrrrrrszLinuxDistribution._uname_infoc CsH|js dSytjdtjd}Wnttjfk r8dSX|j|jS)NrDZoslevel)r)rJrrrrrrr)r:rrrrrszLinuxDistribution._oslevel_infocCsJy0ttjj|jddd}|jjSQRXWntk rDdSXdS)Nrascii)rrD)rrLrMrNrPreadlinerstripFileNotFoundError)r:fprrrrssz!LinuxDistribution._debian_versioncCs\|siSi}tjd|dj}|rX|j\}}|dkrsz:LinuxDistribution._distro_release_info..Nr(r|rZ cloudlinuxrD)rG_parse_distro_release_filerLrMr _DISTRO_RELEASE_BASENAME_PATTERNrylistdirrPsortr_DISTRO_RELEASE_BASENAMESrNrrYr^)r: distro_inforryZ basenamesfilepathr)r:rrs4        z&LinuxDistribution._distro_release_info)rrcCs@y&t|dd}|j|jSQRXWntk r:iSXdS)z Parse a distro release file. Parameters: * filepath: Path name of the distro release file. Returns: A dictionary containing all information items. zutf-8)rN)rrmrr)r:rrrrrrs z,LinuxDistribution._parse_distro_release_file)rrcCstj|jddd}i}|r~|jdddd |d<|jdrZ|jdddd |d<|jdr|jdddd |d<n|r|j|d<|S) a Parse a line from a distro release file. Parameters: * line: Line from the distro release file. Must be a unicode string or a UTF-8 encoded byte string. Returns: A dictionary containing all information items. Nr|r(r rgrrrr)(_DISTRO_RELEASE_CONTENT_REVERSED_PATTERNryrr)rr{rrrrrm1s    z/LinuxDistribution._parse_distro_release_content)NrDrDNNN)T)F)FF)F)F)F)F)FF)2r rrrBrboolrr;rVr r&rr(rrr*r+rrrrr,rr-r.r/r0rpr2r3r4r5r6rX staticmethodrrrrrrrrsrrbytesrrrrmrrrrrCsj}*         1   :rCcCstjt}|jtj|jtjtjt j dd}|j ddddd|j dd t d d d |j }|jr|td d d |jd}nt}|jr|jtj|jdddn@|jd|jdd|jdd}|jd||j}|jd|dS)NzOS distro info tool)rfz--jsonz-jz!Output in machine readable format store_true)helpactionz --root-dirz-rrIz5Path to the root filesystem directory (defaults to /))typedestrF)rErHrJrIT)indent sort_keyszName: %s)r'z Version: %sz Codename: %s)logging getLoggerr setLevelDEBUG addHandler StreamHandlerrrargparseArgumentParser add_argumentr parse_argsrIrCr%jsonr,dumpsr(rr)loggerparserargsdistZdistribution_versionZdistribution_codenamerrrmainNs6      r__main__)T)F)FF)F)F)F)F)FF)BrBrrrrLrwrrrr"Ztypingrrrrrrrr r r ImportErrorr~ __version__r renvironrYrOrQrRrbrcrdrxrrrrrrr&rr(rrr*r+rrrr,r-r.r/r0r2r3r4r5 functoolsr6rCr%rr rrrrs,      +S'0   ,   K( kcarectl/update_utils.py000064400000007577152527712570011445 0ustar00# Copyright (c) Cloud Linux Software, Inc # Licensed under CLOUD LINUX LICENSE AGREEMENT # http://cloudlinux.com/docs/LICENCE.TXT import functools import json import os import time from . import config, constants, log_utils, utils from .py23 import json_loads_nstr if False: # pragma: no cover from typing import Any, Callable, Dict # noqa: F401 STATUS_CHANGE_GAP_DELAY = 5 * 60 # 5 minute def touch_status_gap_file(filename='.kcarestatus'): status_filepath = os.path.join(constants.PATCH_CACHE, filename) utils.atomic_write(status_filepath, utils.timestamp_str()) def status_gap_passed(filename='.kcarestatus'): status_filepath = os.path.join(constants.PATCH_CACHE, filename) if os.path.isfile(status_filepath): with open(status_filepath, 'r') as sfile: try: timestamp = int(sfile.read()) if int(timestamp) + config.STATUS_CHANGE_GAP + STATUS_CHANGE_GAP_DELAY > time.time(): return False except Exception: pass return True def _check_component(component): # type: (str) -> None if component not in ('kernel', 'libcare'): raise ValueError('Unknown update status component: {0}'.format(component)) def _load_update_status(): # type: () -> Dict[str, Any] content = utils.read_file(constants.UPDATE_STATUS_PATH) if content is None: return {} try: result = json_loads_nstr(content) # type: Dict[str, Any] return result except (ValueError, TypeError): log_utils.kcarelog.warning('Failed to parse update status file') return {} def save_update_status(component, error): # type: (str, str) -> None _check_component(component) try: data = _load_update_status() data[component] = { 'error': error, 'timestamp': int(time.time()), } utils.atomic_write(constants.UPDATE_STATUS_PATH, json.dumps(data)) except Exception: log_utils.kcarelog.warning('Failed to save update status', exc_info=True) def _error_status(err): # type: (Exception) -> str # Prefer the short, fixed status label that KcareError subclasses set # (e.g. 'bad signature') for compact, groupable telemetry. When the status # is already echoed in the message (HTTPError's "HTTP Error 414: ...") the # message is the more useful value. When a numeric HTTP status is *not* in # the message, compose a readable "HTTP Error: " rather than a bare # code. str() also keeps an int status subscriptable on read-back. message = str(err) status = str(getattr(err, 'status', '')) if status and status not in message: if status.isdigit(): return 'HTTP Error: {0}'.format(status) return status return message def track_update_status(component): # type: (str) -> Callable[..., Any] _check_component(component) def decorator(fn): # type: (Callable[..., Any]) -> Callable[..., Any] @functools.wraps(fn) def inner(*args, **kwargs): # type: (Any, Any) -> Any try: result = fn(*args, **kwargs) except Exception as err: save_update_status(component, error=_error_status(err)) raise save_update_status(component, error='') return result return inner return decorator def read_update_error(component): # type: (str) -> str _check_component(component) try: data = _load_update_status() # str() guards against status files written by older clients that # persisted a non-string error (e.g. an int HTTP status like 414). error = data.get(component, {}).get('error', '') # type: Any return str(error)[: constants.UPDATE_ERROR_MAX_LENGTH] except Exception: log_utils.kcarelog.warning('Failed to read update status', exc_info=True) return '' kcarectl/serverid.py000064400000003611152527712570010547 0ustar00# Copyright (c) Cloud Linux Software, Inc # Licensed under CLOUD LINUX LICENSE AGREEMENT # http://cloudlinux.com/docs/LICENCE.TXT import os from . import errors, utils from .py23 import json_loads_nstr SYSTEMID = '/etc/sysconfig/kcare/systemid' ALMA_SYSTEMID = '/etc/sysconfig/kcare/systemid.almacare' IM360_LICENSE_FILE = '/var/imunify360/license.json' if False: # pragma: no cover from typing import Optional # noqa: F401 def _systemid(): # type: () -> Optional[str] if not os.path.exists(SYSTEMID): return None with open(SYSTEMID, 'r') as fd: for line in fd: param, _, value = line.partition('=') if param.strip() == 'server_id': return value.strip() raise errors.KcareError('Unable to parse {0}.'.format(SYSTEMID), status='server id parse error') return None def _alma_systemid(): # type: () -> Optional[str] if not os.path.exists(ALMA_SYSTEMID): return None with open(ALMA_SYSTEMID, 'r') as f: return f.readline().strip() def _im360_systemid(): # type: () -> Optional[str] if not os.path.exists(IM360_LICENSE_FILE): return None data = {} with open(IM360_LICENSE_FILE) as f: content = f.read() if content: try: data = json_loads_nstr(content) except Exception: pass # we are not interested why lic file can't be parsed return data.get('id') @utils.cached def get_serverid(): # type: () -> Optional[str] """Get server_id or None if not present. Lookup order: SYSTEMID then IM360_LICENSE_FILE then ALMA_SYSTEMID """ return _systemid() or _im360_systemid() or _alma_systemid() def rm_serverid(): # type: () -> None os.unlink(SYSTEMID) def set_server_id(server_id): # type: (str) -> None utils.atomic_write(SYSTEMID, 'server_id={0}\n'.format(server_id)) kcarectl/capabilities.py000064400000001674152527712570011364 0ustar00# Copyright (c) Cloud Linux Software, Inc # Licensed under CLOUD LINUX LICENSE AGREEMENT # http://cloudlinux.com/docs/LICENCE.TXT if False: # pragma: no cover from typing import List # noqa: F401 def get_kc_capabilites_bits(): # type: () -> int # a stub, will be encoded as int when we have some capabilities return 0 def get_lc_capabilites_bits(): # type: () -> int # a stub, will be encoded as int when we have some capabilities return 0 def has_kc_capabilities(required_capabilities): # type: (List[str]) -> bool # currently agent doesn't have any capabilities # so we fail this check if there are any required capabilities return not required_capabilities def has_lc_capabilities(required_capabilities): # type: (List[str]) -> bool # currently agent doesn't have any capabilities # so we fail this check if there are any required capabilities return not required_capabilities kcarectl/doctor.py000064400000057440152527712570010227 0ustar00# Copyright (c) Cloud Linux Software, Inc # Licensed under CLOUD LINUX LICENSE AGREEMENT # http://cloudlinux.com/docs/LICENCE.TXT """Doctor v2 vitals collector (KPT-5984). Python port of the kcdoctor.sh (1.0-8) vitals collection: gathers the same data set into a DataPackage-based archive processed by the patch server generic uploads pipeline. Scalar and short values are consolidated into a single ``doctor.json`` (the eportal doctor precedent), one field each, instead of a tiny archive entry per value; bulk data (logs, command dumps, package lists, sysfs tunables, crash artifacts) stays as separate archive entries. The 1:1 inventory of collected items lives in docs/features/KPT-5984-doctor-v2/parity-inventory.md. """ import fnmatch import glob import os import re import socket from kcarectl import auth, config, delivery_kit, http_utils, kcare, log_utils, platform_utils, serverid, utils from kcarectl.process_utils import run_command from kcarectl.py23 import HTTPError, URLError, httplib if False: # pragma: no cover from typing import Any, Callable, Dict, List, Optional # noqa: F401 DOCTOR_VERSION = '2.0-1' GRUB2_CFG = '/boot/grub2/grub.cfg' KDUMP_CONF = '/etc/kdump.conf' DPKG_LOG = '/var/log/dpkg.log' APT_SOURCES_GLOB = '/etc/apt/sources.list*' YUM_REPOS_GLOB = '/etc/yum.repos.d/*' LIBCARE_LOGS_GLOB = '/var/log/libcare/*.log' LOG_TAIL_LINES = 10000 SYSFS_ROOT = '/sys' SYSFS_NAME_PATTERNS = ('enable*', 'nr_*', 'max_*', '*cnt*') # plain prefix match, mirroring the kcdoctor.sh get_sysfs_info_detail() # `! -path "*"` find filters SYSFS_EXCLUDE_PREFIXES = ( '/sys/kernel/debug/tracing/events', '/sys/kernel/tracing/events', '/sys/devices', ) CRASH_DUMP_PATTERNS = ('[0-9]*.log', 'kmsg*log') class KcarectlDoctorPackage(delivery_kit.DataPackage): data_type = 'kcarectl-doctor' upload_uri = '/upload/kcarectl-doctor/' @property def max_size(self): # type: () -> int return config.DOCTOR_REPORT_MAX_SIZE_BYTES # --- bulk items: one archive file each --- class CollectionSpec(object): """A single bulk vitals item ported from kcdoctor.sh. Collection failures are recorded in errors.log and never abort the report: an unhandled exception inside the package context manager would remove the whole archive. """ def __init__(self, arcname, collect): # type: (str, Callable[[delivery_kit.DataPackage, str], None]) -> None self.arcname = arcname self._collect = collect def apply(self, data_package): # type: (delivery_kit.DataPackage) -> None try: self._collect(data_package, self.arcname) except Exception as e: data_package.log_error('failed to collect {0}: {1}'.format(self.arcname, e)) def run_spec(arcname, cmd): # type: (str, str) -> CollectionSpec """`run ""` kcdoctor.sh helper (no shell features)""" def collect(data_package, arcname): # type: (delivery_kit.DataPackage, str) -> None data_package.add_stdout(arcname, cmd) return CollectionSpec(arcname, collect) def dump_spec(arcname, path): # type: (str, str) -> CollectionSpec """`dump ""` kcdoctor.sh helper for regular files""" def collect(data_package, arcname): # type: (delivery_kit.DataPackage, str) -> None data_package.add_file(arcname, src_path=path) return CollectionSpec(arcname, collect) def proc_dump_spec(arcname, path): # type: (str, str) -> CollectionSpec """`dump ""` for /proc and /sys files. tar reports them as 0-size so they are first read into memory (see the same approach in anomaly.prepare_kernel_anomaly_report). """ def collect(data_package, arcname): # type: (delivery_kit.DataPackage, str) -> None if not os.path.exists(path): data_package.log_error('file not found: {0}'.format(path)) return with open(path, 'rb') as f: data_package.add_file(arcname, data_bytes=f.read()) return CollectionSpec(arcname, collect) def callable_spec(arcname, fn): # type: (str, Callable[[delivery_kit.DataPackage, str], None]) -> CollectionSpec return CollectionSpec(arcname, fn) # --- scalar/short items: one doctor.json field each --- class FieldSpec(object): """A single doctor.json field ported from kcdoctor.sh. Collection failures are recorded in errors.log and never abort the report; a collector returning None omits the field (e.g. a distro-specific source file absent on this system). """ def __init__(self, key, collect): # type: (str, Callable[[], Any]) -> None self.key = key self._collect = collect def apply(self, data_package, report): # type: (delivery_kit.DataPackage, Dict[str, Any]) -> None try: value = self._collect() except Exception as e: data_package.log_error('failed to collect {0}: {1}'.format(self.key, e)) return if value is not None: report[self.key] = value def read_field(path): # type: (str) -> Callable[[], Optional[str]] """`dump ""` as a doctor.json string field. Read in binary (non-utf8 safe) and decoded leniently; an absent distro-specific source file simply omits the field (None). """ def collect(): # type: () -> Optional[str] if not os.path.exists(path): return None with open(path, 'rb') as f: return f.read().decode('utf-8', 'replace').strip() return collect def grep_field(path, pattern): # type: (str, str) -> Callable[[], Optional[List[str]]] """`grep ` as a doctor.json list-of-lines field. Match on bytes (like collect_apt_sources) so a non-utf8 byte cannot lose a line; the matched lines are decoded leniently for the JSON. """ regex = re.compile(utils.bstr(pattern)) def collect(): # type: () -> Optional[List[str]] if not os.path.exists(path): return None with open(path, 'rb') as f: lines = f.read().splitlines() return [line.decode('utf-8', 'replace') for line in lines if regex.search(line)] return collect def cmd_field(argv, as_lines=False): # type: (List[str], bool) -> Callable[[], Any] """`run ""` captured as a doctor.json field instead of a file.""" def collect(): # type: () -> Any _, stdout, _ = run_command(argv, catch_stdout=True, catch_stderr=True) text = (stdout or '').strip() return text.splitlines() if as_lines else text return collect def collect_doctor_version(): # type: () -> str return DOCTOR_VERSION @utils.cached def get_main_ip(): # type: () -> str """the public IP of the machine as seen by the patch server. Cached: the main_ip and server_id fields share one HTTP roundtrip per run. """ try: response = http_utils.urlopen(utils.get_patch_server_url('myip')) return utils.nstr(response.read()).strip() except Exception: return 'NA' def collect_server_id(): # type: () -> str server_id = serverid.get_serverid() if not server_id: # like kcdoctor.sh: fall back to the main IP with dots replaced server_id = get_main_ip().replace('.', '_') return utils.nstr(server_id) def collect_kernel_id(): # type: () -> str # kcdoctor.sh runs `sha1sum /proc/version`; get_kernel_hash() is the # same digest without the trailing file name return kcare.get_kernel_hash() def collect_uname(): # type: () -> Dict[str, str] result = {} # type: Dict[str, str] for flag in ('a', 'r', 'm', 'p', 'o'): _, stdout, _ = run_command(['uname', '-' + flag], catch_stdout=True, catch_stderr=True) result[flag] = (stdout or '').strip() return result def collect_grub2_entries(): # type: () -> Optional[List[str]] """`grep vmlinuz /boot/grub2/grub.cfg | sed 's/root=.*//'`""" if not os.path.exists(GRUB2_CFG): return None with open(GRUB2_CFG, 'rb') as f: text = f.read().decode('utf-8', 'replace') return [re.sub(r'root=.*', '', line) for line in text.splitlines() if 'vmlinuz' in line] def collect_yum_repos(): # type: () -> List[str] # `echo /etc/yum.repos.d/*`: the repo files present (empty when none) return sorted(glob.glob(YUM_REPOS_GLOB)) def collect_control_panel(): # type: () -> Dict[str, Any] """presence-only port of the kcdoctor.sh detect_cp() probes""" panels = [name for name, probe in platform_utils.CONTROL_PANEL_PROBES if probe()] return {'cp': panels, 'softaculous': platform_utils.has_softaculous()} # --- bulk collectors (one archive file each) --- def collect_ipcs(data_package, arcname): # type: (delivery_kit.DataPackage, str) -> None """`ipcs -m | sed -e s/-/=/g`""" _, stdout, _ = run_command(['ipcs', '-m'], catch_stdout=True, catch_stderr=True) data_package.add_file(arcname, data_bytes=utils.bstr((stdout or '').replace('-', '='), encoding='utf-8')) def collect_dpkg_install_log(data_package, arcname): # type: (delivery_kit.DataPackage, str) -> None """`grep ' install ' /var/log/dpkg.log`""" if not os.path.exists(DPKG_LOG): data_package.log_error('file not found: {0}'.format(DPKG_LOG)) return with open(DPKG_LOG, 'rb') as f: lines = [line for line in f.read().splitlines() if b' install ' in line] data_package.add_file(arcname, data_bytes=b'\n'.join(lines) + b'\n') def collect_packages(data_package, arcname): # type: (delivery_kit.DataPackage, str) -> None # same package list commands as anomaly.prepare_kernel_anomaly_report if os.path.exists('/usr/bin/rpm') or os.path.exists('/bin/rpm'): packages_cmd = r'rpm -q -a --queryformat="%{N}|%{V}-%{R}|%{arch}|%{INSTALLTIME:date}\n"' elif os.path.exists('/usr/bin/dpkg'): packages_cmd = r'/usr/bin/dpkg-query -W -f "${binary:Package}|${Version}|${Architecture}\n"' # an unreadable dpkg.log must not lose the package list below # (partial results, like the other multi-source collectors) try: collect_dpkg_install_log(data_package, 'dpkg.log') except Exception as e: data_package.log_error('failed to collect dpkg.log: {0}'.format(e)) else: packages_cmd = 'echo "unknown package manager"' data_package.add_stdout(arcname, packages_cmd) def collect_apt_sources(data_package, arcname): # type: (delivery_kit.DataPackage, str) -> None """`grep -rE '^(deb|URIs)' /etc/apt/sources.list*`""" regex = re.compile(br'^(deb|URIs)') files = [] # type: List[str] for path in sorted(glob.glob(APT_SOURCES_GLOB)): if os.path.isdir(path): for root, _, names in os.walk(path): files.extend(os.path.join(root, name) for name in sorted(names)) else: files.append(path) # like the shell `grep`: report the no-match case in errors.log # instead of storing an empty entry if not files: data_package.log_error('file not found: {0}'.format(APT_SOURCES_GLOB)) return lines = [] # type: List[bytes] for path in files: # an unreadable file must not lose the lines of the others try: with open(path, 'rb') as f: content = f.read() except Exception as e: data_package.log_error('failed to read {0}: {1}'.format(path, e)) continue prefix = utils.bstr('{0}:'.format(path), encoding='utf-8') lines.extend(prefix + line for line in content.splitlines() if regex.search(line)) data_package.add_file(arcname, data_bytes=b'\n'.join(lines) + b'\n') def collect_libcare_logs(data_package, arcname): # type: (delivery_kit.DataPackage, str) -> None """`tail -n10000 /var/log/libcare/*.log`""" paths = sorted(glob.glob(LIBCARE_LOGS_GLOB)) # like the shell `tail`: report the no-match case in errors.log # instead of storing an empty entry if not paths: data_package.log_error('file not found: {0}'.format(LIBCARE_LOGS_GLOB)) return chunks = [] # type: List[bytes] for path in paths: # an unreadable log must not lose the tails of the others try: with open(path, 'rb') as f: lines = f.read().splitlines()[-LOG_TAIL_LINES:] except Exception as e: data_package.log_error('failed to read {0}: {1}'.format(path, e)) continue header = utils.bstr('==> {0} <==\n'.format(path), encoding='utf-8') chunks.append(header + b'\n'.join(lines)) data_package.add_file(arcname, data_bytes=b'\n'.join(chunks) + b'\n') def collect_crash_dumps(data_package, arcname): # type: (delivery_kit.DataPackage, str) -> None """crashreporter artifacts from /var/cache/kcare/dumps""" if not os.path.isdir(config.KDUMPS_DIR): return data_package.add_stdout(arcname, 'ls -lR {0}'.format(config.KDUMPS_DIR)) for root, _, names in os.walk(config.KDUMPS_DIR): for name in sorted(names): if any(fnmatch.fnmatch(name, pattern) for pattern in CRASH_DUMP_PATTERNS): path = os.path.join(root, name) # keep the path relative to KDUMPS_DIR in the arcname so # same-named dumps from different subdirectories do not # overwrite each other in the archive rel_path = os.path.relpath(path, config.KDUMPS_DIR) data_package.add_file('crashreporter/{0}'.format(rel_path), src_path=path) def collect_kdump(data_package, arcname): # type: (delivery_kit.DataPackage, str) -> None # kcdoctor.sh gates kdump collection on `[ -e /etc/kdump.conf ]`; without # it get_kdump_root() defaults to /var/crash, so an unconfigured host with # a stale crash dir would otherwise be collected. if not os.path.exists(KDUMP_CONF): return kdump_root = kcare.get_kdump_root() if not os.path.isdir(kdump_root): return data_package.add_stdout(arcname, 'ls -lR {0}'.format(kdump_root)) for evt in sorted(glob.glob(os.path.join(kdump_root, '*'))): # only crash event directories can hold a vmcore-dmesg.txt; # skip regular files to keep errors.log free of noise if not os.path.isdir(evt): continue data_package.add_file( 'kdump/{0}/vmcore-dmesg.txt'.format(os.path.basename(evt)), src_path=os.path.join(evt, 'vmcore-dmesg.txt'), ) def collect_sysfs_config(data_package, arcname): # type: (delivery_kit.DataPackage, str) -> None """tunables gathered by the kcdoctor.sh get_sysfs_info() `find /sys` calls""" chunks = [] for root, dirs, names in os.walk(SYSFS_ROOT): if root.startswith(SYSFS_EXCLUDE_PREFIXES): dirs[:] = [] # do not descend into excluded subtrees continue for name in sorted(names): if any(fnmatch.fnmatch(name, pattern) for pattern in SYSFS_NAME_PATTERNS): path = os.path.join(root, name) try: with open(path, 'rb') as f: content = f.read() except Exception: content = b'' chunks.append(utils.bstr('cat {0}\n'.format(path)) + content + b'\n') data_package.add_file(arcname, data_bytes=b''.join(chunks)) # the full kcdoctor.sh (1.0-8) vitals set, in the script order; see # docs/features/KPT-5984-doctor-v2/parity-inventory.md for the mapping # scalar/short items, consolidated into doctor.json (one field each) FIELD_SPECS = [ FieldSpec('doctor_version', collect_doctor_version), FieldSpec('virt_what', cmd_field(['/usr/libexec/kcare/virt-what'], as_lines=True)), FieldSpec('main_ip', get_main_ip), FieldSpec('server_id', collect_server_id), FieldSpec('kernel_id', collect_kernel_id), FieldSpec('date', cmd_field(['date'])), FieldSpec('uname', collect_uname), FieldSpec('redhat_release', read_field('/etc/redhat-release')), FieldSpec('debian_version', read_field('/etc/debian_version')), FieldSpec('os_release', read_field('/etc/os-release')), FieldSpec('issue', read_field('/etc/issue')), FieldSpec('sysconfig_kernel', read_field('/etc/sysconfig/kernel')), FieldSpec('proc_uptime', read_field('/proc/uptime')), FieldSpec('proc_loadavg', read_field('/proc/loadavg')), FieldSpec('proc_cmdline', read_field('/proc/cmdline')), FieldSpec('proc_version', read_field('/proc/version')), FieldSpec('default_grub', grep_field('/etc/default/grub', 'DEFAULT')), FieldSpec('grub2_entries', collect_grub2_entries), FieldSpec('sshd_port', grep_field('/etc/ssh/sshd_config', 'Port')), FieldSpec('yum_repos_d', collect_yum_repos), FieldSpec('control_panel', collect_control_panel), ] # bulk items, one archive file each FILE_SPECS = [ run_spec('syslog', 'tail -n{0} /var/log/syslog'.format(LOG_TAIL_LINES)), run_spec('dmesg', 'dmesg'), run_spec('messages', 'tail -n{0} /var/log/messages'.format(LOG_TAIL_LINES)), run_spec('ls_var_cache_kcare', 'ls -lR /var/cache/kcare/'), dump_spec('kcare.conf', '/etc/sysconfig/kcare/kcare.conf'), run_spec('cpuinfo', 'cat /proc/cpuinfo'), proc_dump_spec('proc_vmstat', '/proc/vmstat'), proc_dump_spec('proc_devices', '/proc/devices'), proc_dump_spec('proc_diskstats', '/proc/diskstats'), proc_dump_spec('proc_mdstat', '/proc/mdstat'), proc_dump_spec('proc_meminfo', '/proc/meminfo'), proc_dump_spec('proc_swaps', '/proc/swaps'), proc_dump_spec('proc_filesystems', '/proc/filesystems'), proc_dump_spec('proc_mounts', '/proc/mounts'), proc_dump_spec('proc_interrupts', '/proc/interrupts'), dump_spec('grub.conf', '/boot/grub/grub.conf'), proc_dump_spec('proc_modules', '/proc/modules'), dump_spec('grub2.cfg', GRUB2_CFG), proc_dump_spec('proc_zoneinfo', '/proc/zoneinfo'), run_spec('ls_boot_configs', 'ls /etc/grub.conf /boot/grub/grub.conf /boot/grub/menu.lst'), run_spec('ls_boot', 'ls -l /boot'), run_spec('printenv', 'printenv'), run_spec('dmidecode', 'dmidecode'), callable_spec('ipcs', collect_ipcs), run_spec('sysctl', 'sysctl -a'), dump_spec('sysctl.conf', '/etc/sysctl.conf'), callable_spec('packages.list', collect_packages), run_spec('lspci', 'lspci -vv'), run_spec('dpkg_l', 'dpkg -l'), callable_spec('apt_sources', collect_apt_sources), dump_spec('yum.conf', '/etc/yum.conf'), run_spec('yum_repolist', 'yum repolist'), callable_spec('libcare_logs', collect_libcare_logs), run_spec('kcarectl.log', 'tail -n{0} /var/log/kcarectl.log'.format(LOG_TAIL_LINES)), dump_spec('kdump.conf', KDUMP_CONF), dump_spec('kcare-cron', '/etc/cron.d/kcare-cron'), callable_spec('crashreporter/ls', collect_crash_dumps), callable_spec('kdump/ls', collect_kdump), run_spec('aa_status', 'aa-status'), run_spec('sestatus', 'sestatus'), run_spec('kprobes_list', 'cat /sys/kernel/debug/kprobes/list'), callable_spec('sysfs_config', collect_sysfs_config), run_spec('lsblk', 'lsblk -f'), run_spec('df', 'df -h'), ] @utils.catch_errors(logger=log_utils.logwarn) def prepare_doctor_report(): # type: () -> Optional[KcarectlDoctorPackage] # None when collection fails (catch_errors): KPT-6064/KPT-6065 # callers must handle it data_package = KcarectlDoctorPackage() with data_package: report = {} # type: Dict[str, Any] for field in FIELD_SPECS: field.apply(data_package, report) data_package.add_json('doctor.json', report) for spec in FILE_SPECS: spec.apply(data_package) return data_package def send_doctor_report(fallback, force_fallback=False): # type: (Callable[[], None], bool) -> None """Collect and upload the doctor report via the generic uploads proxy. PUTs the archive to ``/upload/kcarectl-doctor/`` -- the same modern pipeline as ``kernel-anomaly`` -- which ePortal proxies transparently to the patch server, so no ePortal change is required. Any 2xx reply is success, including the ``REDUCED_REPORT`` ``204`` where the proxy intentionally drops the report (``send()`` returns without raising, so we do not fall back). Falls back to the legacy ``kcdoctor.sh`` flow (``fallback``) when: * ``force_fallback`` (the ``--doctor --fallback`` CLI flag) or ``config.FORCE_DOCTOR_FALLBACK`` is set -- the rollout kill-switch. ``--fallback`` is the one-off, per-invocation form; the config knob is settable per host in kcare.conf or pushed fleet-wide via the ``KC-Flag-Force-Doctor-Fallback`` feature flag. Either forces the legacy flow on every host, registered or not, before the new path is even considered; * the host is not registered, i.e. there is no auth string (OQ-4: the proxy upload requires auth, so the new path is not even attempted); * report collection failed (``prepare_doctor_report`` returned None); * the new route is unavailable on an older patch server / ePortal -- a 404 / 405 / 403, surfaced as ``HTTPError``, or a connection-level failure. ``send()`` drives ``http_utils.upload_file`` (raw ``httplib``, not ``urllib``), so transport failures surface as an ``OSError`` subclass -- ``socket.timeout``, ``ConnectionRefused``, ``socket.gaierror`` (DNS), ``ssl.SSLError`` -- or an ``httplib.HTTPException``, *not* ``URLError``; all of these mean the new path is unusable. ``upload_file`` raises only on status >= 400, so a redirect is not treated as a failure; the proxy and patch server never redirect uploads. :param fallback: legacy ``kcdoctor.sh`` runner, injected by the caller (KPT-6065) to keep this module free of an ``__init__`` import cycle. :param force_fallback: when True (``--doctor --fallback``), skip the v2 path and run ``fallback`` directly; the per-invocation form of ``config.FORCE_DOCTOR_FALLBACK``. """ if force_fallback or config.FORCE_DOCTOR_FALLBACK: src = '--fallback requested' if force_fallback else 'FORCE_DOCTOR_FALLBACK is set' log_utils.loginfo( 'doctor: {0}; using the legacy kcdoctor.sh flow'.format(src), print_msg=False, ) fallback() return if auth.get_http_auth_string() is None: log_utils.loginfo( 'doctor: host is not registered; using the legacy kcdoctor.sh flow', print_msg=False, ) fallback() return data_package = prepare_doctor_report() if data_package is None: log_utils.logwarn('doctor: report collection failed; using the legacy kcdoctor.sh flow') fallback() return try: upload_name = data_package.send() log_utils.loginfo('doctor: report uploaded as {0}'.format(upload_name), print_msg=False) # surface the uploaded filename to the user, the same way the eportal # --doctor does (kc.eportal make_doctor_report -> utils.print_wrapper) # so support can locate the report; the legacy kcdoctor.sh fallback # prints its own "Key:" line instead. print_wrapper (not loginfo) so # the line is shown unconditionally -- loginfo is gated by PRINT_LEVEL # and would be dropped under --quiet/--auto-update, whereas the legacy # kcdoctor.sh echo is not. utils.print_wrapper('Please provide this filename to support: {0}'.format(upload_name)) except (HTTPError, URLError, httplib.HTTPException, socket.error) as err: # upload_file drives httplib directly, so a transport failure # surfaces as socket.error (== OSError on py3: timeout, connection # refused, DNS, TLS) or httplib.HTTPException -- not URLError. An # HTTPError (route absent: 404 / 405 / 403) means the same thing: # the new path is unusable, so fall back to the legacy flow. log_utils.logwarn('doctor: report upload unavailable ({0}); using the legacy kcdoctor.sh flow'.format(err)) fallback() finally: data_package.remove_archive() kcarectl/config.py000064400000004724152527712570010177 0ustar00# Copyright (c) Cloud Linux Software, Inc # Licensed under CLOUD LINUX LICENSE AGREEMENT # http://cloudlinux.com/docs/LICENCE.TXT from . import constants if False: # pragma: no cover from typing import List # noqa: F401 AFTER_UPDATE_COMMAND = None AUTO_STICKY_PATCHSET = None AUTO_UPDATE = True AUTO_UPDATE_DELAY = None BEFORE_UPDATE_COMMAND = None BLACKLIST_FILE = 'kpatch.blacklist' CHECK_CLN_LICENSE_STATUS = True CHECK_SSL_CERTS = True FIXUPS_FILE = 'kpatch.fixups' FLAGS = ['keep-registration', 'manage-libcare'] FORCE_GID = None FORCE_JSON_SIG_V3 = True HTTP_TIMEOUT = 20 HTTP_UPLOAD_TIMEOUT = 240 IGNORE_UNKNOWN_KERNEL = False KPATCH_DEBUG = False # crash reporter ENABLE_CRASHREPORTER = False KDUMPS_DIR = constants.PATCH_CACHE + '/dumps' KMSG_OUTPUT = True KCORE_OUTPUT = True KCORE_OUTPUT_SIZE = 100 # 100Mb IGNORE_FEATURE_FLAGS = False LIBCARE_DISABLED = False LIBCARE_LIBS = ['libc', 'libssl', 'nscd', 'libm', 'libnss_dns'] # type: List[str] LIBCARE_PIDLOGS_MAX_TOTAL_SIZE_MB = 500 LIBCARE_SOCKET_TIMEOUT = 60 LIB_AUTO_UPDATE = True PATCH_BIN = 'kpatch.bin' PATCH_DONE = '.done' PATCH_INFO = 'kpatch.info' PATCH_LEVEL = None # a level to 'stick on' (if 0 then use latest level) PATCH_METHOD = '' PATCH_TYPE = '' PREFIX = '' PREV_PATCH_TYPE = 'default' PRINT_LEVEL = constants.PRINT_INFO REPORT_FQDN = False SEND_PERF_METRICS = False SILENCE_ERRORS = True STATUS_CHANGE_GAP = 4 * 60 * 60 + 5 * 60 # 4 hours STICKY_PATCH = False STICKY_PATCHSET = None UPDATE_DELAY = None UPDATE_FROM_LOCAL = False UPDATE_POLICY = constants.POLICY_REMOTE UPDATE_SYSCTL_CONFIG = True USERSPACE_PATCHES = None USE_CONTENT_FILE_V3 = True USE_SIGNATURE = True KERNEL_VERSION_FILE = '/proc/version' KCARE_UNAME_FILE = '/proc/kcare/effective_version' SUCCESS_TIMEOUT = 5 * 60 # anomaly reports KERNEL_ANOMALY_REPORT_ENABLE = False KERNEL_ANOMALY_REPORT_MAX_SIZE_BYTES = 500 * (1024**2) # 500MB # doctor reports DOCTOR_REPORT_MAX_SIZE_BYTES = 500 * (1024**2) # 500MB # force the legacy kcdoctor.sh flow instead of the v2 /upload proxy path -- a # rollout kill-switch, settable per host in kcare.conf or pushed fleet-wide via # the KC-Flag-Force-Doctor-Fallback feature flag FORCE_DOCTOR_FALLBACK = False # external services communication FORCE_IPV4 = False FORCE_IPV6 = False PATCH_SERVER = 'https://patches.kernelcare.com' PATCH_SERVER_IPV6 = 'https://ipv6.patches.kernelcare.com' REGISTRATION_URL = 'https://cln.cloudlinux.com/api/kcare' REGISTRATION_URL_IPV6 = 'https://ipv6.cln.cloudlinux.com/api/kcare' kcarectl/py23.py000064400000004236152527712570007525 0ustar00# Copyright (c) Cloud Linux Software, Inc # Licensed under CLOUD LINUX LICENSE AGREEMENT # http://cloudlinux.com/docs/LICENCE.TXT import json from . import constants if constants.PY2: # pragma: no py3 cover import httplib # isort: skip - skipped because isort incorrectly treats it as third-party from urllib import quote as urlquote from urllib import urlencode from ConfigParser import ConfigParser from urllib2 import HTTPError from urllib2 import Request as StdRequest from urllib2 import URLError from urllib2 import urlopen as std_urlopen from urlparse import urlparse # type: ignore class Request(StdRequest): def __init__(self, *args, **kwargs): method = kwargs.pop('method', None) StdRequest.__init__(self, *args, **kwargs) if method == 'HEAD': # Older versions of mypy supporting 2.x do not infer type of # the `method` variable correctly here. Cast to str explicitly. self.get_method = lambda: str(method) # type: ignore[assignment] # json.loads returns unicode strings and they can contaminate following # calls. Functions converts all unicode strings into native def _convert(data): dtype = type(data) if dtype is type(u''): return data.encode('utf-8') elif dtype is list: return [_convert(it) for it in data] elif dtype is dict: return dict((_convert(k), _convert(v)) for k, v in data.items()) return data def json_loads_nstr(json_str): return _convert(json.loads(json_str)) else: # pragma: no py2 cover from configparser import ConfigParser from http import client as httplib from urllib.error import HTTPError, URLError from urllib.parse import quote as urlquote from urllib.parse import urlencode, urlparse from urllib.request import Request from urllib.request import urlopen as std_urlopen json_loads_nstr = json.loads __all__ = [ "ConfigParser", "HTTPError", "Request", "URLError", "httplib", "json_loads_nstr", "std_urlopen", "urlencode", "urlparse", "urlquote", ] kcarectl/__main__.py000064400000001443152527712570010445 0ustar00# Copyright (c) Cloud Linux Software, Inc # Licensed under CLOUD LINUX LICENSE AGREEMENT # http://cloudlinux.com/docs/LICENCE.TXT import os import sys from . import errors, log_utils, main, send_exc from .py23 import URLError # The scanner interface should skip us os.environ['KCARE_SCANNER_INTERFACE_DO_NOTHING'] = '1' if __name__ == '__main__': try: sys.exit(main()) except URLError as err: log_utils.logerror('{0}: {1}'.format(err, getattr(err, 'url', 'unknown'))) except errors.KcareError as err: log_utils.logerror(str(err)) sys.exit(1) except Exception as err: if isinstance(err, errors.SafeExceptionWrapper): log_utils.logexc(err.inner) else: log_utils.logexc(err) send_exc() sys.exit(1) kcarectl/fetch.py000064400000011636152527712570010023 0ustar00# Copyright (c) Cloud Linux Software, Inc # Licensed under CLOUD LINUX LICENSE AGREEMENT # http://cloudlinux.com/docs/LICENCE.TXT import hashlib import json import os import kcsig_verify # noqa: E402 from . import auth, config, constants, errors, http_utils, selinux, utils SIG_VERIFY_ORDER = [constants.SIG, constants.SIG_JSON] GPG_BIN = '/usr/bin/gpg' GPG_KEY_DIR = '/var/lib/kcare/gpg' CONTENT_FILE = 'release.content.json' def fetch_signature(url, dst, do_auth=False): urlopen_local = http_utils.urlopen if do_auth: urlopen_local = auth.urlopen_auth if config.FORCE_JSON_SIG_V3: sig_exts = SIG_VERIFY_ORDER[::-1] else: sig_exts = SIG_VERIFY_ORDER for sig_ext in sig_exts: try: signature = urlopen_local(url + sig_ext) break except errors.NotFound as nf: if sig_ext == sig_exts[-1]: raise nf # pragma: no cover sig_dst = dst + sig_ext # pragma: no cover utils.save_to_file(signature, sig_dst) return sig_dst def check_gpg_bin(): if not os.path.isfile(GPG_BIN): raise errors.KcareError('No {0} present. Please install gnupg'.format(GPG_BIN), status='gnupg not found') def check_gpg_signature(file_path, signature): # mocked: tests/unit """ Check a file signature using the gpg tool. If signature is wrong BadSignatureException will be raised. :param file_path: path to file which signature will be checked :param signature: a file with the signature :return: True in case of valid signature :raises: BadSignatureException """ check_gpg_bin() if signature.endswith(constants.SIG_JSON): root_keys = os.path.join(GPG_KEY_DIR, 'root-keys.json') try: kcsig_verify.verify(signature, file_path, root_keys) except kcsig_verify.Error as e: raise errors.BadSignatureException('Bad Signature: {0}: {1}'.format(file_path, str(e))) else: with open(signature, 'rb') as f: sigdata = f.read() keyring = os.path.join(GPG_KEY_DIR, 'kcare_pub.key') try: kcsig_verify.run_gpg_verify(keyring, file_path, sigdata) except Exception as e: raise errors.BadSignatureException('Bad Signature: {0}: {1}'.format(file_path, str(e))) # BadSignatureException is the only side effect of interrupted connection, # should retry file extraction in this case @utils.retry(errors.check_exc(errors.BadSignatureException), count=3, delay=0) def fetch_url(url, dst, check_signature=False, hash_checker=None): response = auth.urlopen_auth(url) tmp = selinux.selinux_safe_tmpname(dst) utils.save_to_file(response, tmp) if hash_checker: hash_checker.check(url, tmp) elif check_signature: signature = fetch_signature(url, tmp, do_auth=True) check_gpg_signature(tmp, signature) os.rename(tmp, dst) return response class HashChecker(object): def __init__(self, baseurl, content_file): self.content_file = content_file self.url_prefix = utils.get_patch_server_url(baseurl).rstrip('/') + '/' self.hashes = json.loads(utils.read_file(content_file))['files'] def check(self, url, fname): cfname = url[len(self.url_prefix) :] if cfname not in self.hashes: raise errors.KcareError( 'Invalid checksum: {0} not found in content file {1}'.format(cfname, self.content_file), status='invalid checksum', ) hsh = hashlib.sha256(utils.read_file_bin(fname)).hexdigest() expected_hsh = self.hashes[cfname]['sha256'] if hsh != expected_hsh: raise errors.BadSignatureException( 'Invalid checksum: {0} has invalid checksum {1}, expected {2}'.format(fname, hsh, expected_hsh) ) @utils.cached def get_hash_checker(level): if not config.USE_CONTENT_FILE_V3: return None if not level.baseurl: return None dst = level.cache_path(CONTENT_FILE) if not os.path.exists(dst): try: # here we also implicitly check content file signature fetch_url(utils.get_patch_server_url(level.baseurl, CONTENT_FILE), dst, config.USE_SIGNATURE) except errors.NotFound: return None return HashChecker(level.baseurl, dst) def wrap_with_cache_key(clbl): """Enrich request with a cache key, and save it if response had.""" def wrapper(*args, **kwargs): cache_key = utils.get_cache_key() if cache_key is not None: if 'headers' not in kwargs: kwargs['headers'] = {} kwargs['headers'][constants.CACHE_KEY_HEADER] = cache_key resp = clbl(*args, **kwargs) new_cache_key = resp.headers.get(constants.CACHE_KEY_HEADER) if new_cache_key is not None and new_cache_key != cache_key: utils.atomic_write(constants.CACHE_KEY_DUMP_PATH, new_cache_key) return resp return wrapper kcarectl/__pycache__/doctor.cpython-36.pyc000064400000047147152527712570014516 0ustar003 ;j _.@s dZddlZddlZddlZddlZddlZddlmZmZm Z m Z m Z m Z m Z mZmZddlmZddlmZmZmZdZdZdZd Zd Zd Zd Zd ZdZdZdZ dZ!Gddde j"Z#Gddde$Z%ddZ&ddZ'd d!Z(d"d#Z)Gd$d%d%e$Z*d&d'Z+d(d)Z,dd+d,Z-d-d.Z.ej/d/d0Z0d1d2Z1d3d4Z2d5d6Z3d7d8Z4d9d:Z5d;d<Z6d=d>Z7d?d@Z8dAdBZ9dCdDZ:dEdFZ;dGdHZe*dMe.e*dNe-dOgdPdQe*dRe0e*dSe1e*dTe2e*dUe-dUge*dVe3e*dWe+dXe*dYe+dZe*d[e+d\e*d]e+d^e*d_e+d`e*dae+dbe*dce+dde*dee+dfe*dge+dhe*die,djdke*dle4e*dme,dndoe*dpe5e*dqe6gZ?e&drdsj@ee&dtdte&dudvj@ee&dwdxe'dydze&d{d|e(d}d~e(dde(dde(dde(dde(dde(dde(dde(dde'dde(dde'dee(dde&dde&dde&dde&dde)de7e&dde'dde)de9e&dde&dde)de:e'dde&dde)de;e&ddj@ee'dee'dde)de<e)de=e&dde&dde&dde)de>e&dde&ddg,ZAejBe jCdddZDdddZEdS)aODoctor v2 vitals collector (KPT-5984). Python port of the kcdoctor.sh (1.0-8) vitals collection: gathers the same data set into a DataPackage-based archive processed by the patch server generic uploads pipeline. Scalar and short values are consolidated into a single ``doctor.json`` (the eportal doctor precedent), one field each, instead of a tiny archive entry per value; bulk data (logs, command dumps, package lists, sysfs tunables, crash artifacts) stays as separate archive entries. The 1:1 inventory of collected items lives in docs/features/KPT-5984-doctor-v2/parity-inventory.md. N) authconfig delivery_kit http_utilskcare log_utilsplatform_utilsserveridutils) run_command) HTTPErrorURLErrorhttplibz2.0-1z/boot/grub2/grub.cfgz/etc/kdump.confz/var/log/dpkg.logz/etc/apt/sources.list*z/etc/yum.repos.d/*z/var/log/libcare/*.logi'z/sysenable*nr_*max_**cnt* /sys/kernel/debug/tracing/events/sys/kernel/tracing/events /sys/devices [0-9]*.logkmsg*logc@s eZdZdZdZeddZdS)KcarectlDoctorPackagezkcarectl-doctorz/upload/kcarectl-doctor/cCstjS)N)rDOCTOR_REPORT_MAX_SIZE_BYTES)selfr,/usr/libexec/kcare/python/kcarectl/doctor.pymax_size;szKcarectlDoctorPackage.max_sizeN)__name__ __module__ __qualname__ data_type upload_uripropertyrrrrrr7src@s eZdZdZddZddZdS)CollectionSpeczA single bulk vitals item ported from kcdoctor.sh. Collection failures are recorded in errors.log and never abort the report: an unhandled exception inside the package context manager would remove the whole archive. cCs||_||_dS)N)arcname_collect)rr%collectrrr__init__LszCollectionSpec.__init__cCsNy|j||jWn6tk rH}z|jdj|j|WYdd}~XnXdS)Nzfailed to collect {0}: {1})r&r% Exception log_errorformat)r data_packageerrrapplyQszCollectionSpec.applyN)rrr __doc__r(r.rrrrr$Dsr$csfdd}t||S)z4`run ""` kcdoctor.sh helper (no shell features)cs|j|dS)N) add_stdout)r,r%)cmdrrr']szrun_spec..collect)r$)r%r1r'r)r1rrun_specYs r2csfdd}t||S)z4`dump ""` kcdoctor.sh helper for regular filescs|j|ddS)N)src_path)add_file)r,r%)pathrrr'hszdump_spec..collect)r$)r%r5r'r)r5r dump_specds r6csfdd}t||S)z`dump ""` for /proc and /sys files. tar reports them as 0-size so they are first read into memory (see the same approach in anomaly.prepare_kernel_anomaly_report). c sLtjjs |jdjdStd}|j||jdWdQRXdS)Nzfile not found: {0}rb) data_bytes)osr5existsr*r+openr4read)r,r%f)r5rrr'ws   zproc_dump_spec..collect)r$)r%r5r'r)r5rproc_dump_specos r>cCs t||S)N)r$)r%fnrrr callable_specsr@c@s eZdZdZddZddZdS) FieldSpeczA single doctor.json field ported from kcdoctor.sh. Collection failures are recorded in errors.log and never abort the report; a collector returning None omits the field (e.g. a distro-specific source file absent on this system). cCs||_||_dS)N)keyr&)rrBr'rrrr(szFieldSpec.__init__cCsXy |j}Wn4tk r@}z|jdj|j|dSd}~XnX|dk rT|||j<dS)Nzfailed to collect {0}: {1})r&r)r*r+rB)rr,reportvaluer-rrrr.s zFieldSpec.applyN)rrr r/r(r.rrrrrAsrAcsfdd}|S)z`dump ""` as a doctor.json string field. Read in binary (non-utf8 safe) and decoded leniently; an absent distro-specific source file simply omits the field (None). c s:tjjsdStd}|jjddjSQRXdS)Nr7zutf-8replace)r9r5r:r;r<decodestrip)r=)r5rrr's  zread_field..collectr)r5r'r)r5r read_fields rHcs"tjtj|fdd}|S)z`grep ` as a doctor.json list-of-lines field. Match on bytes (like collect_apt_sources) so a non-utf8 byte cannot lose a line; the matched lines are decoded leniently for the JSON. c sDtjjsdStd}|jj}WdQRXfdd|DS)Nr7cs"g|]}j|r|jddqS)zutf-8rE)searchrF).0line)regexrr sz/grep_field..collect..)r9r5r:r;r< splitlines)r=lines)r5rLrrr's   zgrep_field..collect)recompiler bstr)r5patternr'r)r5rLr grep_fields rTFcsfdd}|S)z@`run ""` captured as a doctor.json field instead of a file.cs0tddd\}}}|pdj}r,|jS|S)NT) catch_stdout catch_stderr)r rGrN)_stdouttext)argvas_linesrrr's zcmd_field..collectr)r[r\r'r)r[r\r cmd_fieldsr]cCstS)N)DOCTOR_VERSIONrrrrcollect_doctor_versionsr_c Cs<y"tjtjd}tj|jjStk r6dSXdS)zthe public IP of the machine as seen by the patch server. Cached: the main_ip and server_id fields share one HTTP roundtrip per run. ZmyipZNAN)rurlopenr get_patch_server_urlnstrr<rGr))responserrr get_main_ips rdcCs$tj}|stjdd}tj|S)N.rX)r get_serveridrdrEr rb) server_idrrrcollect_server_idsrhcCstjS)N)rget_kernel_hashrrrrcollect_kernel_idsrjcCsBi}x8d D]0}tdd|gddd \}}}|p0d j||<q W|S) Narmpouname-T)rUrVrW)rkrlrmrnro)r rG)resultflagrXrYrrr collect_unames  rtc CsHtjjtsdSttd}|jjdd}WdQRXdd|jDS)z7`grep vmlinuz /boot/grub2/grub.cfg | sed 's/root=.*//'`Nr7zutf-8rEcSs"g|]}d|krtjdd|qS)Zvmlinuzzroot=.*rW)rPsub)rJrKrrrrMsz)collect_grub2_entries..)r9r5r: GRUB2_CFGr;r<rFrN)r=rZrrrcollect_grub2_entries s   rwcCsttjtS)N)sortedglobYUM_REPOS_GLOBrrrrcollect_yum_repossr{cCsddtjD}|tjdS)z8presence-only port of the kcdoctor.sh detect_cp() probescSsg|]\}}|r|qSrr)rJnameproberrrrMsz)collect_control_panel..)cpZ softaculous)rCONTROL_PANEL_PROBEShas_softaculous)Zpanelsrrrcollect_control_panelsrcCs@tddgddd\}}}|j|tj|p(djdddd d d S) z`ipcs -m | sed -e s/-/=/g`ipcsz-mT)rUrVrWrq=zutf-8)encoding)r8N)r r4r rRrE)r,r%rXrYrrr collect_ipcs%src Cshtjjts |jdjtdSttd}dd|jjD}WdQRX|j |dj |dddS)z$`grep ' install ' /var/log/dpkg.log`zfile not found: {0}Nr7cSsg|]}d|kr|qS)s install r)rJrKrrrrM4sz,collect_dpkg_install_log.. )r8) r9r5r:DPKG_LOGr*r+r;r<rNr4join)r,r%r=rOrrrcollect_dpkg_install_log,s    rcCstjjdstjjdrd}nXtjjdrrd}yt|dWqvtk rn}z|jdj|WYdd}~XqvXnd}|j||dS) Nz /usr/bin/rpmz/bin/rpmzFrpm -q -a --queryformat="%{N}|%{V}-%{R}|%{arch}|%{INSTALLTIME:date}\n"z /usr/bin/dpkgzJ/usr/bin/dpkg-query -W -f "${binary:Package}|${Version}|${Architecture}\n"zdpkg.logzfailed to collect dpkg.log: {0}zecho "unknown package manager")r9r5r:rr)r*r+r0)r,r% packages_cmdr-rrrcollect_packages9s $rc sFtjdg}xdttjtD]R}tjj|rfx@tj|D]&\}}|j fddt|Dq:Wq|j |qW|s|j dj tdSg}x|D]}y"t |d}|j}WdQRXWn6tk r} z|j dj || wWYdd} ~ XnXtjdj |d d |j fd d|jDqW|j|d j|d d dS)z/`grep -rE '^(deb|URIs)' /etc/apt/sources.list*`s ^(deb|URIs)c3s|]}tjj|VqdS)N)r9r5r)rJr|)rootrr Usz&collect_apt_sources..zfile not found: {0}Nr7zfailed to read {0}: {1}z{0}:zutf-8)rc3s |]}j|r|VqdS)N)rI)rJrK)prefixrLrrrjsr)r8)rPrQrxryAPT_SOURCES_GLOBr9r5isdirwalkextendappendr*r+r;r<r)r rRrNr4r) r,r%filesr5rXnamesrOr=contentr-r)rrLrrcollect_apt_sourcesLs*  "  "rc Csttjt}|s&|jdjtdSg}x|D]}y0t|d}|jjt d}WdQRXWn6t k r}z|jdj||w0WYdd}~XnXt j dj|dd}|j |dj |q0W|j|dj |dd dS) z%`tail -n10000 /var/log/libcare/*.log`zfile not found: {0}Nr7zfailed to read {0}: {1}z ==> {0} <== zutf-8)rr)r8)rxryLIBCARE_LOGS_GLOBr*r+r;r<rNLOG_TAIL_LINESr)r rRrrr4) r,r%pathschunksr5r=rOr-headerrrrcollect_libcare_logsos  $rcstjjtjsdS|j|djtjxttjtjD]d\}}}xXt|D]Lt fddt DrHtjj |}tjj |tj}|j dj||dqHWq4WdS)z3crashreporter artifacts from /var/cache/kcare/dumpsNz ls -lR {0}c3s|]}tj|VqdS)N)fnmatch)rJrS)r|rrrsz&collect_crash_dumps..zcrashreporter/{0})r3)r9r5rr KDUMPS_DIRr0r+rrxanyCRASH_DUMP_PATTERNSrrelpathr4)r,r%rrXrr5Zrel_pathr)r|rcollect_crash_dumpssrcCstjjtsdStj}tjj|s(dS|j|dj|xTt t j tjj |dD]8}tjj|sfqT|j djtjj |tjj |ddqTWdS)Nz ls -lR {0}*zkdump/{0}/vmcore-dmesg.txtzvmcore-dmesg.txt)r3)r9r5r: KDUMP_CONFrget_kdump_rootrr0r+rxryrr4basename)r,r% kdump_rootZevtrrr collect_kdumps   rc sg}xtjtD]\}}}|jtr2g|dd<qxt|D]tfddtDr.r7zcat {0} r)r8)r9r SYSFS_ROOT startswithSYSFS_EXCLUDE_PREFIXESrxrSYSFS_NAME_PATTERNSr5rr;r<r)rr rRr+r4) r,r%rrdirsrr5r=rr)r|rcollect_sysfs_configs    &rZdoctor_versionZ virt_whatz/usr/libexec/kcare/virt-whatT)r\Zmain_iprgZ kernel_iddaterpZredhat_releasez/etc/redhat-releaseZdebian_versionz/etc/debian_versionZ os_releasez/etc/os-releaseZissuez /etc/issueZsysconfig_kernelz/etc/sysconfig/kernelZ proc_uptimez /proc/uptimeZ proc_loadavgz /proc/loadavgZ proc_cmdlinez /proc/cmdline proc_versionz /proc/versionZ default_grubz/etc/default/grubDEFAULTZ grub2_entriesZ sshd_portz/etc/ssh/sshd_configZPortZ yum_repos_dZ control_panelsyslogztail -n{0} /var/log/syslogdmesgmessagesztail -n{0} /var/log/messagesls_var_cache_kcarezls -lR /var/cache/kcare/z kcare.confz/etc/sysconfig/kcare/kcare.confcpuinfozcat /proc/cpuinfoZ proc_vmstatz /proc/vmstatZ proc_devicesz /proc/devicesZproc_diskstatsz/proc/diskstatsZ proc_mdstatz /proc/mdstatZ proc_meminfoz /proc/meminfoZ proc_swapsz /proc/swapsZproc_filesystemsz/proc/filesystemsZ proc_mountsz /proc/mountsZproc_interruptsz/proc/interruptsz grub.confz/boot/grub/grub.conf proc_modulesz /proc/modulesz grub2.cfgZ proc_zoneinfoz/proc/zoneinfoZls_boot_configsz:ls /etc/grub.conf /boot/grub/grub.conf /boot/grub/menu.lstZls_bootz ls -l /bootZprintenvZ dmidecodersysctlz sysctl -az sysctl.confz/etc/sysctl.confz packages.listZlspciz lspci -vvZdpkg_lzdpkg -lZ apt_sourceszyum.confz /etc/yum.confZ yum_repolistz yum repolistZ libcare_logsz kcarectl.logz tail -n{0} /var/log/kcarectl.logz kdump.confz kcare-cronz/etc/cron.d/kcare-cronzcrashreporter/lszkdump/lsZ aa_statusz aa-statusZsestatusZ kprobes_listz"cat /sys/kernel/debug/kprobes/listZ sysfs_configZlsblkzlsblk -fZdfzdf -h)loggerc Cs\t}|Hi}xtD]}|j||qW|jd|xtD]}|j|q= 400, so a redirect is not treated as a failure; the proxy and patch server never redirect uploads. :param fallback: legacy ``kcdoctor.sh`` runner, injected by the caller (KPT-6065) to keep this module free of an ``__init__`` import cycle. :param force_fallback: when True (``--doctor --fallback``), skip the v2 path and run ``fallback`` directly; the per-invocation form of ``config.FORCE_DOCTOR_FALLBACK``. z--fallback requestedzFORCE_DOCTOR_FALLBACK is setz.doctor: {0}; using the legacy kcdoctor.sh flowF) print_msgNzAdoctor: host is not registered; using the legacy kcdoctor.sh flowzCdoctor: report collection failed; using the legacy kcdoctor.sh flowzdoctor: report uploaded as {0}z,Please provide this filename to support: {0}zJdoctor: report upload unavailable ({0}); using the legacy kcdoctor.sh flow)rFORCE_DOCTOR_FALLBACKrloginfor+rget_http_auth_stringrlogwarnsendr print_wrapperr r r HTTPExceptionsocketerrorremove_archive)fallbackforce_fallbacksrcr, upload_nameerrrrrsend_doctor_report-s4)    r)rrrr)rrr)rr)F)F)Fr/rryr9rPrkcarectlrrrrrrrr r Zkcarectl.process_utilsr Z kcarectl.py23r r rr^rvrrrrzrrrrrr DataPackagerobjectr$r2r6r>r@rArHrTr]r_cachedrdrhrjrtrwr{rrrrrrrrrrr+r catch_errorsrrrrrrrs,          #           kcarectl/__pycache__/constants.cpython-36.pyc000064400000002355152527712570015230 0ustar003 ;j@sddlZddlZdZdZdZdZdZejddkZdZ ej j e dZ ej j e dZ d Zd Zd Zd Zd ZdZdZdZdZdZdZdZdZdZdZdZdZdZdZ dZ!dZ"dZ#dZ$dZ%dS)Nz/var/cache/kcarezfeature_flags.jsonzupdate_status.jsondz Kc-Cache-Keyz/etc/sysconfig/kcare/cache_keyZREMOTEZLOCALZ LOCAL_FIRST3z/var/log/kcarectl.logz.sigz .json-sigz Kc-Auth-Tokenz/etc/sysconfig/kcare/auth_tokenFz/usr/bin/systemctlZmanualautoZsmartzkcare.koz/usr/libexec/kcare/kpatch_ctlz 3.8-1.el8)&ossys PRINT_DEBUG PRINT_INFOZ PRINT_WARN PRINT_ERRORPRINT_CRITICAL version_infoZPY2 PATCH_CACHEpathjoinZFEATURE_FLAGS_CACHEZUPDATE_STATUS_PATHZUPDATE_ERROR_MAX_LENGTHZCACHE_KEY_HEADERZCACHE_KEY_DUMP_PATH POLICY_REMOTE POLICY_LOCALPOLICY_LOCAL_FIRSTZKC_PATCH_VERSIONLOG_FILESIGZSIG_JSONAUTH_TOKEN_HEADERAUTH_TOKEN_DUMP_PATHZ RETRY_DELAYZRETRY_MAX_DELAYZ RETRY_BACKOFFZ RETRY_COUNTZSKIP_SYSTEMCTL_CHECKZ SYSTEMCTLUPDATE_MODE_MANUALUPDATE_MODE_AUTOUPDATE_MODE_SMARTKMOD_BIN KPATCH_CTLVERSIONr!r!//usr/libexec/kcare/python/kcarectl/constants.pysDkcarectl/__pycache__/ipv6_support.cpython-36.pyc000064400000012035152527712570015670 0ustar003 ;j@sddlZddlZddlZddlZddlmZmZmZmZm Z m Z ddl m Z dZ dZdZejjejdZdZGd d d eZeZd d Zd dZddZddZddZdS)N)config constants http_utils log_utilsserveridutils)json_loads_nstrzipv6_preference.json<c@s,eZdZddZeddZeddZdS)IPProtoSelectorcCsbtjrtjddStjjds.tjddStjrBtjddSt}|dk rltjdj|r`dnd |Sd}|j tj stjd d}n2|j tjstjd d}nt j rtjd d}|dk rt ||S|jdd }|jdd }|tkrtjdd}nR|tkrtjdd}n8|tkr8tjdd}n|tkrRtjdd}nd}t ||S)ak Choose ipv6 if it is more suitable. Checks order: - check config values (it is faster) - eportal setup and FORCE_IPVx - then check each proto availability using HEAD requests - then check if we have server_id, it means we don't expect an ip license - and finally we need to check if there is an ip license z,decided to use ipv4 because of config valuesFzkernelcare.comz,decided to use ipv6 because of config valuesTNz%decided to use {0} from on-disk cacheipv6Zipv4z1decided to use ipv4 because ipv6 is not availablez1decided to use ipv6 because ipv4 is not availablez/decided to use ipv4 because server id was found)rz2decided to use ipv4 because ipv4 license was foundz2decided to use ipv6 because ipv6 license was foundz8decided to use ipv4 because ipv4 trial license was foundz8decided to use ipv6 because ipv6 trial license was found)r FORCE_IPV4rlogdebug PATCH_SERVERendswith FORCE_IPV6 _read_cacheformat_is_url_reachablePATCH_SERVER_IPV6r get_serverid _write_cache_get_cln_licenseCLN_VALID_LICENSECLN_TRIAL_ACTIVE_LICENSE)selfcachedresultZ ipv4_licenseZ ipv6_licenser 2/usr/libexec/kcare/python/kcarectl/ipv6_support.pyis_ipv6_preferredsV                    z!IPProtoSelector.is_ipv6_preferredcCsbtj|ddd}ytj|dddddStk r\}ztjdj|t|dSd}~XnXdS) NHEAD)method auth_string F)timeout retry_on_500 retry_countTz%error during HEAD request to {0}: {1})r http_requesturlopen Exceptionrrrstr)urlrequester r r!r`sz!IPProtoSelector._is_url_reachablec Cs|r tjntj}|d}tjtj|ddj}tj|}| sL|j d rbt j j dj |tSy t|dStk rtSXdS)Nz /check.plainF)r)codezUnexpected CLN response: {0})rREGISTRATION_URL_IPV6REGISTRATION_URLrnstrrr,read data_as_dictgetrkcarelogerrorrCLN_NO_LICENSEint ValueError)rZbase_urlr/contentinfor r r!rks  z IPProtoSelector._get_cln_licenseN)__name__ __module__ __qualname__r" staticmethodrrr r r r!r sA r c Cstjt}|sdSy t|}Wn,ttfk rJtjdj|dddSXt |t sntjdj|dddS|j d}t |t stjdj|dddS|j d}t |t j st |t rtjdj|dddStjt|}|d kp|tkr tjd j|tdS|S) Nz+ipv6 preference cache: malformed json {0!r}F) print_msgz/ipv6 preference cache: unexpected payload {0!r} prefer_ipv6z3ipv6 preference cache: unexpected prefer_ipv6 {0!r}tsz)ipv6 preference cache: malformed ts {0!r}rz;ipv6 preference cache: stale entry (age={0:.0f}s, ttl={1}s))r try_to_read CACHE_FILEr r= TypeErrorrlogwarnr isinstancedictr8boolnumbersZIntegraltimer<CACHE_TTL_SECONDSr)r>datarEZ cached_tsZager r r!rs0      rcCsl|ttjd}ytjttj|ddWn:ttfk rf}zt j dj |ddWYdd}~XnXdS)N)rErFT) ensure_dirz*failed to write ipv6 preference cache: {0}F)rD) r<rOr atomic_writerHjsondumpsOSErrorIOErrorrrJr)rrQr1r r r!rs rc Cs(ytjtWntk r"YnXdS)z'Drop the on-disk ipv6 preference cache.N)osunlinkrHrVr r r r! clear_cachesrZcCstjrtjStjS)N)ip_proto_selectorr"rrrr r r r!get_patch_serversr\cCstjrtjStjS)N)r[r"rr3r4r r r r!get_registration_urlsr]iiQ)rTrNrXrOrrrrrrpy23r rrr;pathjoin PATCH_CACHErHrPobjectr r[rrrZr\r]r r r r!s"  b'  kcarectl/__pycache__/anomaly.cpython-36.pyc000064400000007174152527712570014660 0ustar003 ;j@sddlZddlZddlmZmZmZmZejejdddZ ddZ ejejddd Z ejejd d d d Z e dkre idS)N) delivery_kitkcare log_utilsutils)loggercCs|jS)aSend the DataPackage archive to the patch server. Upload errors are logged and swallowed (catch_errors), preserving the historical kernel-anomaly behavior. :param data_package: DataPackage instance to send :return: Upload name (package identifier) )send) data_packager -/usr/libexec/kcare/python/kcarectl/anomaly.pysend_data_packages r cCsTtj}xFtj|D]8\}}||dkr*Pdj||jdd}|j||dqWdS)zZadds recent files for the last hour to the given data package starting from the newest oneiz{0}/{1}/_)src_pathN)timerZsort_files_by_ctimeformatreplaceadd_file)filesrZarchive_prefixnowpathctimeZarcnamer r r copy_recent_filess  rc3Cstj}|tjjdr(|jddtjjdr@|jdd|jdd|jd d |jd d tjjd s|tjjd rd}n$tjjdrd}|jdddnd}|jd|td}|jdtj |j dWdQRXtd}|jdtj |j dWdQRX|jddd|j d||jddd|jd d!j t jytt j|d"Wn4tk r}z|jd#j |WYdd}~XnXytt j|d$Wn4tk r}z|jd%j |WYdd}~XnXWdQRX|S)&Nz/var/log/messagesmessagesztail -n10000 /var/log/messagesz/var/log/syslogZsyslogztail -n10000 /var/log/syslogz kcarectl.logz"tail -n10000 /var/log/kcarectl.logZdmesgZls_var_cache_kcarezls -lR /var/cache/kcare/z /usr/bin/rpmz/bin/rpmzFrpm -q -a --queryformat="%{N}|%{V}-%{R}|%{arch}|%{INSTALLTIME:date}\n"z /usr/bin/dpkgzJ/usr/bin/dpkg-query -W -f "${binary:Package}|${Version}|${Architecture}\n"zdpkg.logz/var/log/dpkg.log)rzecho "unknown package manager"z packages.listz /proc/versionZ proc_version)Z data_bytesz /proc/modulesZ proc_modulesz kcare.confz/etc/sysconfig/kcare/kcare.confzserver_info.jsonz kdump.confz/etc/kdump.confZls_kdumpz ls -lR {0}Zkdumpzfailed to copy kdumps: {0}Z crashreporterz+failed to copy crashreporter artifacts: {0})rZKernelAnomalyPackageosrexistsZ add_stdoutropenrbstrreadZadd_jsonrrZget_kdump_rootrZlist_kdump_txt_files ExceptionZ log_errorZlist_crashreporter_log_files) server_inforZ packages_cmdfer r r prepare_kernel_anomaly_report*sB          " " ",r"F)rZdefault_returnc Cs|d}t|d}t|jdp"d}t|d}|d}yt|dd}Wntttfk rld}YnX||||||g}t|sd Sd }d } d } |d kr|dkrd }|||ko||d knr|dkrd } ||krd } || | g} t| rtjdj|d Sd S)z3taken from eportal - anomalies::detect_agent_rebootreasonuptime patch_levelz-1 last_stoptsstaterFi,TiupdatezAgent anomaly detected: {0}) intgetKeyError TypeError ValueErrorallrloginfor) rr#r$r%r&r'Zstate_tsZfieldsZ first_update_after_reboot_markerZcrash_soon_after_update_markerZno_proper_shutdown_markermarkersr r r detect_anomaly_s8   , r4__main__)rrkcarectlrrrrZ catch_errorslogwarnr rr"r4__name__r r r r s 5-kcarectl/__pycache__/libcare.cpython-36.pyc000064400000044740152527712570014621 0ustar003 ;jb@s ddlZddlZddlZddlZddlZddlmZmZmZm Z m Z m Z m Z m Z mZmZmZmZmZmZddlmZmZmZdZdOZdZd Zd Zd d d d Zd dedgfddedgfgZej ddZ!ddZ"ddZ#ddZ$Gddde%Z&ddZ'dPddZ(dd Z)d!d"Z*d#d$Z+dQd&d'Z,d(d)Z-e$d*d+Z.e$d,d-Z/e$d.d/Z0d0d1Z1d2d3Z2d4d5Z3d6d7Z4d8d9Z5dRd:d;Z6e$e'ej7dd?Z9ej7ej:d@ej;e$e'e jdEdFZ?dGdHZ@dSdIdJZAdKdLZBdMdNZCdS)TN)auth capabilitiesconfigconfig_handlers constantserrorsfetch log_utilsplatform_utils process_utilsselinux server_info update_utilsutils) HTTPErrorjson_loads_nstrurlquotez!/usr/libexec/kcare/libcare-client/run/libcare/libcare.sock/var/run/libcare.sockz /var/cache/kcare/libcare_patchesz /var/cache/kcare/libcare_cvelistz&/etc/sysconfig/kcare/libcare.logrotatedb)mysqldmariadbdpostgresZubuntuz16. libnss_dnsz7.c sttj}ty tj}|dj}|d}Wn0tk r`xtD]\}}}|OqFWYn>X|d}x2tD]*\}}}|dks||krp|j |rp|PqpWfdd|DS)Nrr.csg|]}|kr|qSr).0lib) blacklistedr-/usr/libexec/kcare/python/kcarectl/libcare.py Rsz/_get_effective_libcare_libs..) listr LIBCARE_LIBSsetr get_distrolower ExceptionLIBCARE_LIBS_BLACKLIST startswith) libsZ distro_infodistroversion_Zbl_libsZversion_dottedZ bl_distroZ bl_versionr)rr _get_effective_libcare_libs;s   r.cCsdddgtdS)Nrrr)rr*)r.rrrr get_userspace_mapUsr/cGstjjtjd|f|S)N userspace)ospathjoinr PATCH_CACHE)libnamepartsrrr get_userspace_cache_path\sr7csfdd}|S)NcsVz ||Sy tdWn6tk rN}ztjdj|ddWYdd}~XnXXdS)N clearcachez$Libcare cache clearing failed: '{0}'F) print_msg)libcare_clientr'r logerrorformat)argskwargserr)clblrr wrapperas   z$clear_libcare_cache..wrapperr)r@rAr)r@r clear_libcare_cache`s rBcs0eZdZdfdd Zd ddZddZZS) UserspacePatchLevelNcst||j||S)N)super__new__)clsr5buildidlevelbaseurl) __class__rr rEoszUserspacePatchLevel.__new__cCs||_||_||_||_dS)N)rHr5rGrI)selfr5rGrHrIrrr __init__rszUserspacePatchLevel.__init__cGst|j|jt|f|S)N)r7r5rGstr)rKr6rrr cache_pathxszUserspacePatchLevel.cache_path)N)N)__name__ __module__ __qualname__rErLrN __classcell__rr)rJr rCns rCcsddfdd}|S)NcSsd \}}z|dkrt}i}g}x8t|D],}|jdd||jd<|j|jdgq*Wtj|dd}djd d |jD}djt|}Wdtj t |d d tj t |d d XdS)z(KPT-1543 Save info about applied patchesNzlatest-versionpackagepatchescve) cve_field cSsg|]}dj|qS) )r3)rrecrrr r!szLrefresh_applied_patches_list..save_current_state..T) ensure_dir)rSrS) _libcare_info_get_patches_infogetextendrextract_unique_cvesr3itemssorted atomic_writeLIBCARE_PATCHESLIBCARE_CVE_LIST)infoZversionsZcvesZpackagesZ all_patchesrZZcves_setrrr save_current_state}sz8refresh_applied_patches_list..save_current_statec s"d}z||}|S|XdS)Nr)r=r>rf)r@rgrr rAs  z-refresh_applied_patches_list..wrapperr)r@rAr)r@rgr refresh_applied_patches_list|srhc"Cstjpd}t|}t|j}tjtj|d|||d}|dtj d|7}tj|d}|rlt j j d||}i}|rd|i|d <y t jtj|fd d i|}Wntjk r|rtjd j||d d dStjt||ddYnLtk r4} z.| jdkr"|r"tjdj|ddWYdd} ~ XnXtj|jttj |j!} | jdg} t"j#| s|tj$dj|t%||| d| jd} t&| d} t||| d}t j j'| st j j(|dkr:tj| d}yt j)||tj*t j+| dWn<tk r8} z| jd(kr&tj,dWYdd} ~ XnXt||| }dd |d!|d"g}t-j.|ddd#\}}}|rtjd$j|||d%dt||d&}t j j/| rt j j0|rtj|t j1| |d't j2|d'|| S))Nmainuz latest.v1z?info=updater*tagsZStickyheaders check_licenseFz;No libcare patchset available for {0} at tag {1}, skipping.)r9T) ignore_errorsiz8Invalid sticky patch tag {0}: server rejected the value.zinvalid lib sticky patch)statusrzkLatest LibCare patchset for {0} is incompatible with the current kernecare package version, please upgrade.rHrIz patch.tar.gzrZ patch_url)check_signature hash_checkerzKC+ licence is requiredtarZxfz-Cz--no-same-owner) catch_stdout catch_stderrz(Patches unpacking error: '{0}' '{1}' {2}zpatches unpacking errorlatestz.tmp)rsrt)3rPREFIXrstriprget_patch_server_url LIBNAME_MAPr^rZencoded_server_lib_infor1r2r3r wrap_with_cache_keyr urlopen_authrNotFoundr loginfor<shutilrmtreer7rcode KcareErrorrset_feature_flags_from_headersrmrnstrreadrhas_lc_capabilitiesCapabilitiesMismatchrCrMexistsgetsize fetch_url USE_SIGNATUREget_hash_checkerNoLibcareLicenseExceptionr run_commandislinkisdirsymlinkrename)r5build_id patch_leveltagprefixurl cache_dstZ extra_kwargsresponseexmetarequired_capabilitiesrHplevelZ patch_pathdstcmdrstdoutstderrZ link_namerrr fetch_userspace_patchsr          "     rcCsL| t_|sttj|rdndd|r0ttjjd|r@dnddS)NFALSEYES)LIBCARE_DISABLEDzlibcare service is enableddisabled) rrlibcare_server_stopr update_configlibcare_server_startr kcarelogrf)rrrr set_libcare_statussrc Cstjstjjtjr^tjtjddgtjtjddgtjtjddgtjtjddgn6ytjdd ddg}Wnt k rdSXtj|dS) Nstopzlibcare.socketzlibcare.servicez reset-failedlibcareservice /usr/sbin//sbin/)rr) rSKIP_SYSTEMCTL_CHECKr1r2r SYSTEMCTLr rfind_cmdr')rrrr rsrc Cstjstjjtjrtjtjdddgddd\}}}|dkr@dStjtjddgtjtjddgtjtjd d gtjtjd dgtjtjd dgn@trdSytj d dd d g}Wnt k rdSXtj|dS)Nz is-activez--quietzlibcare.serviceT)rvrwrrzlibcare.socketz reset-failedrstartr /usr/sbin//sbin/)rr) rrr1r2rrr rlibcare_server_startedrr')rr-rrrr rs& rTcsdjddt|pgD}ddg}s6|dd|g7}y t|}Wn6tk rx}ztjdj|d d WYdd}~XnXg}x@|jd D]2}|ry|jt j |Wqt k rYqXqWd d |D}x.|D]&}t fdd|dj D|d<qW|S)N|css|]}djtj|VqdS)z({0})N)r<reescape)rprocrrr $sz _libcare_info..rfz-jz-lz-rz/Gathering userspace libraries info error: '{0}'zuserspace libs info error)rprXcSs$g|]}|jd|jd|dqS)commpid)rrr*)pop)rlinerrr r!:sz!_libcare_info..c3s(|] \}}d|ks r||fVqdS)patchlvlNr)rkv)patchedrr r=sr*)r3rbr:r'rrr<splitappendjsonloads ValueErrordictra)rlimitZregexprlinesr?resultrr)rr r\ s& &  &r\c Cst}x<|D]4}x.|djD]\}}|j|d|dfqWq Wg}t}xb|D]Z}xT|D]L\}} t||t| d} tjj| r^t | d} |j t j | WdQRXq^WqTW|S)Nr*rGrz info.jsonr) r$raaddr/r7rMr1r2isfileopenrrload) rfrUrZr-datarumaprrrZpatch_info_filenamefdrrr r]Bs    "r]cCs ttS)N)r]r\rrrr libcare_patch_info_basicRsrcCs"t}|stjdtjd|iS)NzNo patched processes.r)rr r;rdumps)rrrr libcare_patch_infoWs rcCs"t}|stjdtjd|iS)NzNo patched processes.r)r\r r;rr)rrrr libcare_info_s rcCs.i}x$tD]}|jdd||jd<q W|S)Nzlatest-versionrSrT)rr^)rrZrrr _libcare_versiongs rcCs*x$tjD]\}}|j|r |Sq WdS)NrS)rrar))r5rTr,rrr libcare_versionns rcCsdjdd|DdS)Ncss|]}tj|dVqdS)N)rbstr)rprrr rvsz(libcare_client_format..r)r3)paramsrrr libcare_client_formatusrcCs,xtD]}tjj|r|SqWtjddS)NzLibcare socket is not found.)LIBCARE_SOCKETr1r2rrr)Zlibcare_socketrrr get_available_libcare_socketys  rc Gstjrtjdtjtjtjd}|jdd}z||jt |jtj t |}t j dj|d|j|x|jd}|sP||7}qpW|jdd }t j d j|d |S|jXdS) NzLibcare is disabled.r rzLibcare socket send: {cmd})rizutf-8replacez!Libcare socket recieved: {result})r)rrrrsocketAF_UNIX SOCK_STREAM settimeoutconnectrLIBCARE_SOCKET_TIMEOUTrr logdebugr<sendallrecvdecodeclose)rsockresrrrrrr r:s(        r:cCs|rzxp|D]h}td||}tjj|s(q ytdd|Wq tk rp}ztjdj||ddWYdd}~Xq Xq WdSx|D]}ytdt|Wn6tk r}ztjdj|d dWYdd}~XnXy tdWqtk r}ztjd j|ddWYdd}~XqXqWdS) Nrlrkz --storagez3Userspace patch applying error (storage={0}): '{1}'zuserspace patch apply error)rpZstoragez(Userspace storage switching error: '{0}'zuserspace storage switch errorz%Userspace patch applying error: '{0}') r7r1r2rr:r'rrr<)rrrZ storage_pathr?rrr libcare_patch_applys*     & rcCsHy tdWn6tk rB}ztjdj|ddWYdd}~XnXdS)Nunloadz&Userspace patch unloading error: '{0}'zuserspace patch unload error)rp)r:r'rrr<)r?rrr libcare_unloads rcCsDy tdWn2tk r>}ztjdj|WYdd}~XnXdS)z;Reload libcare-server plugin without restarting the server.ZrepluginzUserspace replugin error: '{0}'N)r:r'rrr<)r?rrr libcare_replugins rrcCst|tjkrtj rdSt}|dkr6t|j}g}x|D]}|j|j |gq@W|srt j dj |dSt ||d\}}}} |rtjd|st j ddStjtjjtjdtyt||dWn>tjk r} zt jt| tjdWYdd} ~ XnXt} t| } ttd d | Ds.zPatched before: {before})beforezPatched after: {after})aftercss|]}|D] }|Vq qdS)Nr)rrarrrr r scss|]}|D] }|Vq qdS)Nr)rrarrrr r scss|]}t|VqdS)N)len)rrrrr rszThe patches have been successfully applied to {count} newly discovered processes. The overall amount of applied patches is {overall}.)countoverallz*Object `{0}` is patched for {1} processes.)"rotate_libcare_logsrUPDATE_MODE_AUTOrLIB_AUTO_UPDATEr/r"keysr_r^r rr<check_userspace_updatesrrr restore_selinux_contextr1r2r3r4rr;rMr\_get_userspace_procsanyrr$valuessumrra)moderrrZprocess_filterZuserspace_patchfailedsomething_foundr-rrZ data_afterrZuniq_procs_afterZuniq_procs_beforeZdiffrrrrrr do_userspace_updatesR    r cCs\z.yt\}}}}Wntjk r*dSXWdtX|r@dS|rHdStjddrXdSdS)Nrz.libcarestatus)filenamer)rrrrrstatus_gap_passed)r r-libs_not_patchedrrr get_userspace_update_statuss rcCsdi}xZ|D]R}xL|djD]<\}}|jdr||kr>g||<||j|d|dfqWq W|S)Nr*rrr)rar^r)rfrrr5rZrrr r.s  "rcCsNt}xB|D]:}x4|djD]$\}}|j||d|jddfqWq W|S)Nr*rGrr)r$rarr^)rfrrr5rZrrr _get_userspace_libs9s  $rcs\s$t}gfdd|jDtdd}t|}d}}d}xt|D]}|\} } } y2t| | | |d} |r|| dkr|wNd}| dkrd}WqNtjk r} zd}tj t | WYdd} ~ XqNtj tj fk rYqNtj k rYqNtjk r>}z,t|ddd krd}tjt |WYdd}~XqNXqNWtjd d ||||fS) Ncsg|]}j|qSr)r_)rr*)rrr r!Esz+check_userspace_updates..F)rrT)rrrpzinvalid lib sticky patchz.libcarestatus)r)r/rr\rrrrrr logwarnrMrrAlreadyTrialedExceptionrgetattrr;rtouch_status_gap_file)rrrZ data_beforerr r rrZr5rrrerr)rr rAs>   $ rc sfd}d}tjddd}|rytj|tgdd\}}}Wn.tk rd}zd}t|}WYdd}~XnX|rtjd j|dd ntj d dd d t j j sdSt jd}yt j}tjdfdd|D}dd|D}|jddd} xD|D]<\}} | t j j| 7} | |krt j| tjjd| qWWn$tk r`tjddd YnXdS)NrrSZ logrotateF) raise_excT)rwrz5failed to run logrotate for libcare logs, stderr: {0})r9zlogrotate utility wasn't foundz/var/log/libcare/irz ^\d+\.log.*cs$g|]}j|rtjj|qSr)matchr1r2r3)rfn)libcare_log_directory pidlog_rerr r!sz'rotate_libcare_logs..cSsg|]}tjj||fqSr)r1r2getctime)rfprrr r!s)reversez%Removed %s because of logs size limitz)Failed to cleanup libcare server logfilesi)r rrLIBCARE_LOGROTATE_CONFIGr'rMr r;r<rr1r2rr!LIBCARE_PIDLOGS_MAX_TOTAL_SIZE_MBlistdirrcompilesortrremoverrflogexc) rcrZlogrotate_pathr-rZmax_total_sizeZ log_filesZ pidlog_filesZpidlog_files_with_ctZ total_sizefilepathr)rrr rrs<       rc CsJytjdd ddg}Wntk r*dSXtj|ddd\}}}|d kS) zKAssume that whenever the service is not running, we did not patch anything.r /usr/sbin//sbin/rrpFT)rvrwr)r+r,)r rr'r)rrr-rrr rs r)rr)NN)TN)N)NN)Drr1rrrrSrrrrrrr r r r r rrrpy23rrrZLIBCARE_CLIENTrrdrer"r|r$r(cachedr.r/r7rBintrCrhrrrrr\r]rrrrrrrr:rlog_all_parent_processesrrtrack_update_statusskip_if_no_selinux_moduleUPDATE_MODE_MANUALr rrrrrrrrrr sh@  L  "    $ H   1*kcarectl/__pycache__/py23.cpython-36.pyc000064400000003704152527712570014010 0ustar003 ;j @s"ddlZddlmZejrddlZddlmZddlmZddl m Z ddl m Z ddl m Z dd l mZdd l mZdd lmZGd d d e Z ddZddZnbddlm Z ddlmZddlm Z mZddlmZddlmZmZddlm Z dd lmZejZddd dddddddg ZdS)N) constants)quote) urlencode) ConfigParser) HTTPError)Request)URLError)urlopen)urlparsec@seZdZddZdS)rcs8|jddtj|f||dkr4fdd|_dS)NmethodHEADcstS)N)str)r r*/usr/libexec/kcare/python/kcarectl/py23.pysz"Request.__init__..)pop StdRequest__init__Z get_method)selfargskwargsr)r rrs zRequest.__init__N)__name__ __module__ __qualname__rrrrrrsrcCsVt|}|tdkr|jdS|tkr4dd|DS|tkrRtdd|jDS|S)Nzutf-8cSsg|] }t|qSr)_convert).0itrrr %sz_convert..css"|]\}}t|t|fVqdS)N)r)rkvrrr 'sz_convert..)typeencodelistdictitems)dataZdtyperrrr s  rcCsttj|S)N)rjsonloads)Zjson_strrrrjson_loads_nstr*sr+)client)rr )rr rrr httplib std_urlopenrr urlquote)r)rrPY2r-Zurllibrr/rrZurllib2rrrr r r.r rr+ configparserZhttpr,Z urllib.errorZ urllib.parseZurllib.requestr*__all__rrrrs@                kcarectl/__pycache__/__main__.cpython-36.pyc000064400000001374152527712570014734 0ustar003 ;j#@s*ddlZddlZddlmZmZmZmZddlmZdej d<e dkr&yej eWnek rZ z ej dje ee d d WYddZ [ Xnejk rZ zej ee ej dWYddZ [ XnXek r$Z z:ee ejreje jn eje eej dWYddZ [ XnXdS) N)errors log_utilsmainsend_exc)URLError1Z"KCARE_SCANNER_INTERFACE_DO_NOTHING__main__z{0}: {1}urlunknown)ossysrrrrpy23renviron__name__exiterrlogerrorformatgetattr KcareErrorstr Exception isinstanceSafeExceptionWrapperlogexcinnerrr./usr/libexec/kcare/python/kcarectl/__main__.pys$   *  kcarectl/__pycache__/serverid.cpython-36.pyc000064400000003446152527712570015041 0ustar003 ;j@sjddlZddlmZmZddlmZdZdZdZdd Z d d Z d d Z ej ddZ ddZddZdS)N)errorsutils)json_loads_nstrz/etc/sysconfig/kcare/systemidz&/etc/sysconfig/kcare/systemid.almacarez/var/imunify360/license.jsonc CsptjjtsdSttdL}xD|D]<}|jd\}}}|jdkrJ|jStjdj tddq"WWdQRXdS)Nr= server_idzUnable to parse {0}.zserver id parse error)status) ospathexistsSYSTEMIDopen partitionstripr KcareErrorformat)fdlineparam_valuer./usr/libexec/kcare/python/kcarectl/serverid.py _systemids    "rc Cs2tjjtsdSttd}|jjSQRXdS)Nr)r r r ALMA_SYSTEMIDrreadliner)frrr_alma_systemid!s  rcCs`tjjtsdSi}tt4}|j}|rLy t|}Wntk rJYnXWdQRX|jdS)Nid) r r r IM360_LICENSE_FILErreadr Exceptionget)datarcontentrrr_im360_systemid)s   r&cCstptptS)zqGet server_id or None if not present. Lookup order: SYSTEMID then IM360_LICENSE_FILE then ALMA_SYSTEMID )rr&rrrrr get_serverid9sr'cCstjtdS)N)r unlinkr rrrr rm_serveridCsr)cCstjtdj|dS)Nzserver_id={0} )r atomic_writer r)rrrr set_server_idHsr+)r rrpy23rr rr rrr&cachedr'r)r+rrrrs  kcarectl/__pycache__/kcare.cpython-36.pyc000064400000025654152527712570014310 0ustar003 ;j)@sddlZddlZddlZddlZddlZddlZddlZddlZddlm Z m Z m Z m Z m Z ddlmZddlmZdZddZd d Zd d Zd dZddZddZddZGdddeZGdddeZGdddeZddZddZdd Z d!d"Z!d#d$Z"d%d&Z#d'd(Z$d)d*Z%e j&d+d,Z'e j&d-d.Z(e j&d/d0Z)d1d2Z*d3d4Z+d5d6Z,d7d8Z-d9d:Z.d;d<Z/e j&d=d>Z0d?d@Z1dAdBZ2dCdDZ3dEdFZ4dS)GN)config constants log_utils process_utilsutils)SafeExceptionWrapper)json_loads_nstrzuname: cCstj|p|dkS)Nz.-_+)strisalnum)cr +/usr/libexec/kcare/python/kcarectl/kcare.py is_uname_charsrcCsft}tt||tjdB}x:|jD].}|jtr&djt t |t tdj Sq&WWdQRXdS)Nr) get_kernel_hashopenget_cache_pathr PATCH_INFO readlines startswith UNAME_LABELjoinfilterrlenstrip) patch_levelkhashfliner r r parse_unames  .r!c CsJtjjtjrFy"ttjd}|j||jdStk rDYnXdS)NwTF) ospathexistsrKCARE_UNAME_FILErwriteclose Exception) new_versionrr r rkcare_update_effective_version%s  r+c Cs.ttjd}ztj|jjS|jXdS)Nrb)rrKERNEL_VERSION_FILEhashlibsha1read hexdigestr()rr r rr1s rcCstjjtjd}tjj|rt|dV}|jj}y t |Wn6t k rdt t tjj |St k rvdSX|SQRXdS)z:Returns timestamp from PATCH_CACHE/stoped.at if its exsitsz stopped.atrerrorNz-1)r#r$rr PATCH_CACHEr%rr0rstripint ValueErrorr getctimer))Zstopped_at_filenameZfhvaluer r r get_last_stop<s     r9cCsPtjpd}tjpd}dj||t||g}tjd|f}|rD||f7}tjj|S)Nnonedefault-patches) rPREFIX PATCH_TYPErr rr3r#r$)rplevelfnameprefixptypeZ patch_dirresultr r rrMs    rcGstjtjf|S)N)rget_patch_server_urlrr>)partsr r rget_kernel_prefixed_urlWsrGc@seZdZddZddZdS)BaseKernelPatchLevelcGst|jt|f|S)N)rrr )selfrFr r r cache_path\szBaseKernelPatchLevel.cache_pathcCs|j|j|j|jdS)N)levelrbaseurlrelease)rKrrLrM)rIr r ras_dict_szBaseKernelPatchLevel.as_dictN)__name__ __module__ __qualname__rJrNr r r rrH[srHcs8eZdZd fdd Zd ddZddZdd ZZS) KernelPatchLevelNcst||j||S)N)super__new__)clsrrKrLrM) __class__r rrTiszKernelPatchLevel.__new__cCs||_||_||_||_dS)N)rKrrLrM)rIrrKrLrMr r r__init__lszKernelPatchLevel.__init__cGstj|j|jf|S)N)rrErLr)rIrFr r rkmod_urlrszKernelPatchLevel.kmod_urlcGstj|j|jt|f|S)N)rrErLrr )rIrFr r rfile_urluszKernelPatchLevel.file_url)N)N)rOrPrQrTrWrXrY __classcell__r r )rVrrRhs rRcs<eZdZfddZddZddZddZd d ZZS) LegacyKernelPatchLevelcsByt||j||Stk r<}zt|WYdd}~XnXdS)N)rSrTr6r)rUrrKexc)rVr rrTzszLegacyKernelPatchLevel.__new__cCs||_||_d|_d|_dS)N)rKrrLrM)rIrrKr r rrWszLegacyKernelPatchLevel.__init__cGs0dtjkr t|jt|f|St|jf|S)Nzpatches.kernelcare.com)r PATCH_SERVERrGrr )rIrFr r rrXs zLegacyKernelPatchLevel.kmod_urlcGst|jt|f|S)N)rGrr )rIrFr r rrYszLegacyKernelPatchLevel.file_urlcCst|jt||S)N)rRrr5)rIrLr r rupgradeszLegacyKernelPatchLevel.upgrade) rOrPrQrTrWrXrYr^rZr r )rVrr[ys  r[cCs^y6ttjjtjdd}tj|j|WdQRXWn"t k rXt j dddYnXdS)Nzkernel_patch_level.jsonr"z!failed to dump kernel patch levelF) print_msg) rr#r$rrr3jsondumprNr)rlogexc)Zkernel_patch_levelrr r rdump_kernel_patch_levels rccCsTy,ttjjtjd}t|jSQRXWn"tk rNt j dddYnXdS)Nzkernel_patch_level.jsonz(failed to read dumped kernel patch levelF)r_) rr#r$rrr3r r0r)rrb)rr r rread_dumped_kernel_patch_levels rdcCstdd|DddddS)NcSsg|]}|tjj|fqSr )r#r$r7).0itr r r sz'sort_files_by_ctime..cSs|dS)Nrr )pairr r rsz%sort_files_by_ctime..T)keyreverse)sorted)Z files_listr r rsort_files_by_ctimes rmc Cs\d}tjjds|Std6}x.|D]&}|j}|jdr$|jdd\}}q$WWdQRX|S)Nz /var/crashz/etc/kdump.confzpath r)r#r$isfilerrrsplit)Z kdump_pathZ kdump_confr _r r rget_kdump_roots    rqcCs*t}tjj|sgStjtjj|dS)Nz*/vmcore)rqr#r$isdirglobr) kdump_rootr r rlist_kdump_vcore_filess rucCs*t}tjj|sgStjtjj|dS)Nz*/*.txt)rqr#r$rrrsr)rtr r rlist_kdump_txt_filess rvcCs(tjjtjsgStjtjjtjdS)Nz*.log)r#r$rrr KDUMPS_DIRrsrr r r rlist_crashreporter_log_filessrxcCs(tjjtjsgSddtjtjDS)NcSsg|]}tjjtj|qSr )r#r$rrrw)rerfr r rrgsz0list_crashreporter_artifacts..)r#r$rrrrwlistdirr r r rlist_crashreporter_artifactssrzcCst}|sdSt|ddS)Nrr)rurm)Zkdumpsr r rkdumps_latest_event_timestampsr{cCs>tjstjjtjr:tjtjddgddd\}}}|jSdS)Nz is-activekdumpT) catch_stdout catch_stderrzsystemd-absent) rSKIP_SYSTEMCTL_CHECKr#r$rn SYSTEMCTLr run_commandr)rpstdoutr r r kdump_statussrcCst}|sdSt|ddS)Nrr)rzrm)Z artifactsr r r$crashreporter_latest_event_timestampsrc Cs:d}tjj|sdSt|d}|jj}WdQRX|S)Nz/sys/module/kcare/versionr)r#r$r%rr0r)Zkmod_version_filerversionr r rget_current_kmod_versions   rcCs6t}|sdStjdddt||tjgj}||kS)NTz /sbin/modinfoz-Fr)rr check_outputrrKMOD_BINr)rr@Z old_versionr*r r ris_kmod_version_changeds rcCst}|stjSt|S)N)loaded_patch_levelplatformrMr!)rr r rkcare_uname_susrcCs,tjjtjr"ttjdjjStSdS)Nr) r#r$r%rr&rr0rrr r r r kcare_unamesrcCs`ttd}|r\y t|Wn0tk rN}zt|dtWYdd}~XnXtt|SdS)Nz patch-levelzUnexpected patch state)parse_patch_descriptionloaded_patch_descriptionr5r6r _patch_infor[r)pler r rrs  rcCsJytjtjdgddStjk rD}z|jtjkr6dSd}~XnXdS)NinfoT)checkr) rrr KPATCH_CTL subprocessCalledProcessError returncodeerrnoZEBUSY)rr r rr!s  rcCsRyddtdDSttfk rL}ztjdt|ddgSd}~XnXdS)NcSsg|]}|jdqS)r)ro)rer r r rrg-sz&get_loaded_modules..z /proc/modulesz#Error getting loaded modules list: F)r_)rOSErrorIOErrorrlogerrorr )exr r rget_loaded_modules*s rcCsdtkrdSttdS)Nkcarezkpatch-description)rget_patch_valuerr r r rr3s rcCstj|j|S)N)r data_as_dictget)rZlabelr r rr;src Csrddddd}|s|S|jd\}}}|jd\}}}|jd\}}}|pLd|d<|pXd|d<||d <||d <|S) Nr;r)z patch-levelz patch-typez last-updatezkernel-version;:r<z patch-levelz patch-typez last-updatezkernel-version) partition) descrDZlevel_type_timestamprpkernelZ level_type timestamprZ patch_typer r rr?s  rcCshtjjtjd}tjj|rdt|d8}y|j}tj |St t t t tfk rXYnXWdQRXdS)Nz kcare.stater)r#r$rrr3r%rr0astZ literal_eval SyntaxErrorrr6 TypeErrorUnicodeDecodeError)Z state_filerstater r r get_stateSs   r)5rrrsr.r`r#rrrrrrrrerrorsrpy23r rrr!r+rr9rrGr5rHrRr[rcrdrmrqrurvrxrzcachedr{rrrrrrrrrrrrrr r r rsV               kcarectl/__pycache__/process_utils.cpython-36.pyc000064400000006253152527712570016113 0ustar003 ;j4@spddlZddlZddlZddlZddlmZmZejdddZddd Z dd d Z d d Z ddZ ddZ dS)N) log_utilsutilsTcCsR|pd}x*|D]"}tjj||}tjj|r|SqW|rJtdj||ndSdS)N /usr/sbin/sbin/usr/bin/binz{0} could not be found at {1})rrrr)ospathjoinisfile Exceptionformat)namepathsZ raise_excitfnamer3/usr/libexec/kcare/python/kcarectl/process_utils.pyfind_cmds  rFc Cs|r tjnd}|rtjnd}tj||||d}|j\}} |j} |dk rRtj|}|dk rdtj| } tjt j dj |j|| dj |d|r| rtj | |} || _| | _| | || fS)N)stdoutstderrshellz Call result for `{cmd}`: exit code {exit_code} === STDOUT === {stdout} === STDERR === {stderr} === END ===  )Z exit_coderrcmd) subprocessPIPEPopen communicate returncodernstrrlogdebugtextwrapdedentrr CalledProcessErroroutputr) command catch_stdout catch_stderrrcheckrrpZstdout_capturedZstderr_capturedcodeexcrrr run_commands&     r-cCst|d|d\}}}|S)NT)r'r))r-)argsr)_rrrr check_outputDsr0c Csydddddt|g}t|dd\}}}|rFtjdj|d d d dS|j}dddd dt|g}t|dd\}}}|rtjd j|d d d dS|j}t||fStk r}ztjdj||dd d dSd}~XnXdS)Npsz --no-headersz-oppidz-pT)r'z3Could not retrieve process parent PID for PID {pid})pidF) print_msgZcommz-Could not retrieve process name for PID {pid}zJCould not retrieve process name and parent PID for PID {pid}, error: {err})r3err)NN)NN)NN)strr-rloginforstripintr ) r3Zcmd_ppidr+rr/r2Zcmd_commrerrr _get_parent_pid_and_process_nameIs$ r;cstjfdd}|S)zMDecorator that logs parent process chain before calling the wrapped function.cst||S)N)_log_all_parent_processes)r.kwargs)funcrrwrapperfsz)log_all_parent_processes..wrapper) functoolswraps)r>r?r)r>rlog_all_parent_processescsrBcCsg}tj}x<|dkrH|dkrHt|\}}|j||f|dkrBP|}qWtjdddxJtt|D]:\}\}}d|d}tjdj||pd |pd d ddqfWdS) NrrzAgent parent processes chain:F)r4-z->z{prefix} "{name}" (pid: {pid})unknown)prefixrr3) r getpidr;appendrr7 enumeratereversedr)Z process_chainZ current_pidr2Z process_namelevelr3rrErrrr<ns  r<)NT)FFFF)F)r@r rr"rrcachedrr-r0r;rBr<rrrrs  %  kcarectl/__pycache__/server_info.cpython-36.pyc000064400000006067152527712570015541 0ustar003 ;j@sddlZddlZddlZddlZddlZddlZddlmZmZm Z m Z m Z m Z m Z mZd ddZdddZdd d Zd d ZdS)N) capabilities constants http_utilskcareplatform_utilsserverid update_utilsutilsFc Cst}t|ptj|d<||d<tj|d<tj|d<tj|d<tj|d<tj|d<t j }|d|d <|d |d <t j |d <t jtj|d <t j|d<t j|d<t j|d<t j|d<tj|d<t jt j}|d|d<|d|d<|d|d<t jpd|d<t j|d<t j|d<tj|d<yt j|d<Wntk rdYnXt j!}|r|||d<t j"}|dk r||d <t#j$d!|d"<|rt j%|d#<|rt j&|d$<|S)%Ntsreasonmachine processorreleasesystemversionrdistrordistro_versionZeuname kcare_version last_stopnodeuptimevirtproxyz last-updateZ ltimestampz patch-level patch_levelz patch-type patch_typekmodZcrashreporter_ts kdump_statusrZkdump_ts server_idstatekernel update_errorZ secure_boot perf_metrics)'dictinttimeplatformr rrrrr get_distror kcare_unamer strip_version_timestamprVERSION get_last_stop get_hostname get_uptimeget_virtr proxy_is_usedparse_patch_descriptionloaded_patch_descriptionget_current_kmod_version$crashreporter_latest_event_timestamprrget_kc_capabilites_bitskdumps_latest_event_timestamp Exceptionr get_serverid get_stater read_update_errorsecure_boot_infoget_performance_metrics) r nowr;r#datar descriptionrr r@1/usr/libexec/kcare/python/kcarectl/server_info.py server_infosT                      rBcCst}t|ptj|d<||d<||d<tj}|d|d<|d|d<tj|d<tjt j |d <tj |d <tj |d <tj |d <tj|d <d}tjjdrtjjd}||d<tjd|d<|S)Nr r rrrrrr rrrrrgz/var/lib/libcare/stopstop_tslibcarer")r$r%r&rr(r'r r r*rr+r-r.r/rget_lc_capabilites_bitsospathexistsgetctimer r:)r rr=r>rrCr@r@rAserver_lib_infoIs&         rJcCst|||d}t|ddS)N)r rr=T) b64_encoding)rJencode_checkin_payload)r rr=infor@r@rAencoded_server_lib_infodsrNcCsP|r2tj|ddd}tjtjtjtj|dStjtj tjt |SdS)NF,:) ensure_ascii separatorszutf-8)rOrP) jsondumpsr nstrbase64urlsafe_b64encodezlibcompressbstr b16encodestr)r>rKZdata_strr@r@rArLisrL)NFF)N)N)rVrSrFr'r&rXrrrrrrrr r rBrJrNrLr@r@r@rAs( 7  kcarectl/__pycache__/utils.cpython-36.pyc000064400000017065152527712570014360 0ustar003 ;j[#@sdddlmZddlZddlZddlZddlZddlZddlZddlZddl Z ddl m Z ddl m Z ej dZdZedZed ZedZdddZd?ddZddZd@ddZdedfddZddZdd Zd!d"Zd#d$Zd%d&Zd'd(Z ed)d*Z!d+d,Z"dAd-d.Z#dBd/d0Z$d1d2Z%dCd4d5Z&d6d7Z'd8d9Z(de)fdfd:d;Z*dS)D)print_functionN)datetime) constantsz^(\d+[.]\d+[-]\d+)Fwc Cstjj|}|r(tjj| r(tj|ytj|j}Wntk rP|}YnXtj ||tjj |ddd<}tj |j ||j ||jtj|j |j}WdQRXtj|tj} ztj| Wdtj| Xtj||dS)N.F)modedirprefixdelete)ospathdirnameexistsmakedirsstatst_mode ExceptiontempfileNamedTemporaryFilebasenamefchmodfilenowriteflushfsyncnameopenO_RDONLYcloserename) fnamecontent ensure_dirr Z create_modeZdnamerfZ tmp_fnameZ folder_fdr)+/usr/libexec/kcare/python/kcarectl/utils.py atomic_write"s$      r+utf-8cCs4t|tkr|St|tkr&|j|S|j|SdS)N)typentypebtypedecodeencode)dataencodingr)r)r*nstr>s    r4latin1cCst|tkr|j|}|S)N)r-utyper1)r2r3r)r)r*bstrHs  r7cCst|tkr|j|}|S)N)r-r/r0)r2r3r)r)r*ustrOs  r8cs4itjfdd}|_j|_|_|S)Nc sH|tt|jf}y|Stk r0YnX||}|<|S)N)tuplesorteditemsKeyError)argskwargsZ cache_keyresult)cachefnr)r*innerZszcached..inner) functoolswrapsr@clearorig)rArBr))r@rAr*cachedVs  rGcsFdkrtjdkrtjdkr*tjifdd}|S)Ncsfdd}|S)Ncs}xltD]`}y ||Stk rJ}z|s:WYdd}~XnXtj|t|tjdtj}qWy ||Stk r}zt |dWYdd}~XnXdS)Nrattempts) rangertimesleepminrandomuniformrRETRY_MAX_DELAYsetattr)r=r>Zldelay_exZfinal_ex)backoff check_retrycountdelayrAstater)r*rBvs     z'retry..decorator..innerr))rArB)rSrTrUrVrW)rAr* decoratoruszretry..decorator)r RETRY_DELAY RETRY_COUNT RETRY_BACKOFF)rTrUrVrSrXr))rSrTrUrVrWr*retryjsr\c Cstjj|sdSg}tj|}|dk r2tj||}xD|D]<}tjj||}||krh|jtj|j |f|j ddq8WxF||dD]6\}} tjj | stjj | rtj | qtj| qWdS)NT)reverse)rrrlistdirfnmatchfilterjoinappendrst_mtimesortisfileislinkremoveshutilrmtree) Z directory exclude_pathkeep_npatternr2r;item full_pathrQentryr)r)r*clean_directorys     rpcCsNttjjtjdddttjjtjdddtjjtjrJtjtjdS)Nmodulesr)rkpatches) rprrrar PATCH_CACHErCACHE_KEY_DUMP_PATHunlinkr)r)r)r*clear_all_cachesrvc Cs^tjj|}tjj|s"tj|t|d(}tj|||jtj |j WdQRXdS)Nwb) rrrrrr!rh copyfileobjrrr)responsedstZ parent_dirr(r)r)r* save_to_files     r{cCstj|}|r|jdp|S)Nr) VERSION_REmatchgroup)versionr}r)r)r*strip_version_timestamps rcCs0|jd\}}}|s$|jd\}}}tj|dS)NT z%Y-%m-%d) partitionrstrptime)Zstr_rawZstr_dateseprQr)r)r*parse_response_datesrcGs0ddlm}djddtd|jf|DS)Nr) ipv6_support/css|]}|jdVqdS)rN)strip).0itr)r)r* sz'get_patch_server_url..)rrrar`get_patch_server)partsrr)r)r*get_patch_server_urls rc Cs0tjj|sdSt|}|jjSQRXdS)N)rrrr!readr)filenamer(r)r)r* try_to_reads  rcCs ttjS)N)rrrtr)r)r)r* get_cache_keysrc Cs.tjj|s|St|| }|jSQRXdS)N)rrrr!r)r%r defaultr(r)r)r* _read_files  rcCst|d|}|S)Nr)r)r%rr?r)r)r* read_files rcCst|d|}|S)Nrb)r)r%rr?r)r)r* read_file_bins rcCsBi}|j}x0|D](}|r|jd\}}}|r|j||<qW|S)N:) splitlinesrr)r2r?Z data_lineslinekey delimitervaluer)r)r* data_as_dicts rcvecCs0t}x$|D]}|j|}|r |j|q W|S)a Extract unique CVEs from a list of patches. Args: patches: List of patch dictionaries cve_field: Field name to extract CVE from (default 'cve' for userspace, 'kpatch-cve' for kernel) Returns: Set of unique CVE strings )setgetadd)rr cve_fieldZ unique_cvespatchrr)r)r*extract_unique_cvess   rcCstttjS)N)strintrJr)r)r)r* timestamp_strsrcGs t|dS)za workaround to fix T201N)print)valuesr)r)r* print_wrappersrcsfdd}|S)Ncs tjfdd}|S)Ncsny ||Sk rh}zBrZdd|Ddd|jD}djjdj||Sd}~XnXdS)NcSsg|] }t|qSr))r)rar)r)r* &szBcatch_errors..decorator..inner..cSsg|]\}}dj||qS)z{0}={1})format)rkvr)r)r*r&sz{0}({1}) failed: {2}z, )r;r__name__ra)r=r>earg_list)default_returnerrorsrAloggerr)r*rBs  z.catch_errors..decorator..inner)rCrD)rArB)rrr)rAr*rXs zcatch_errors..decoratorr))rrrrXr))rrrr* catch_errorssr)Fr r )r,)r5)r5)NNN)N)N)r)+ __future__rr_rCrrMrerhrrJrrrcompiler|Z CACHE_ENTRIESr-r.r/r6r+r4r7r8rGr\rprvr{rrrrrrrrrrrrrrr)r)r)r*sJ        $      kcarectl/__pycache__/errors.cpython-36.pyc000064400000006245152527712570014532 0ustar003 ;j@sddlmZGdddeZGdddeZGdddeZGdd d eZGd d d eZGd d d eZGdddeZ GdddeZ ddZ dS)) HTTPErrorc@seZdZdddZdS)SafeExceptionWrapperNcCs||_||_||_dS)N)inneretypedetails)selfrrrr,/usr/libexec/kcare/python/kcarectl/errors.py__init__ szSafeExceptionWrapper.__init__)NN)__name__ __module__ __qualname__r rrrr rsrcs$eZdZdZdZfddZZS) KcareErrora4Base kernelcare exception which will be considered as expected error and the full traceback will not be shown. Subclasses may set a class-level ``status`` to provide a short, fixed label for error reporting. Individual raise sites can override it per-instance via the ``status`` kwarg. cs2|jdd}|dk rt||_tt|j|dS)Nstatus)popstrrsuperrr )rargskwargsr) __class__rr r s  zKcareError.__init__)r r r __doc__rr __classcell__rr)rr rsrc@s eZdZdS)NotFoundN)r r r rrrr r!src@seZdZdZdS)NoLibcareLicenseExceptionzno libcare licenseN)r r r rrrrr r%src@seZdZdZdS)CapabilitiesMismatchzcapabilities mismatchN)r r r rrrrr r)srcs(eZdZdZfddZddZZS)AlreadyTrialedExceptionzalready trialedcs0tt|j|||d|jd|_||_dS)NT)rrr indexcreatedip)rr!r rr)rrr r 0sz AlreadyTrialedException.__init__cCsdj|j|jS)Nz6The IP {0} was already used for a trial license on {1})formatr!r )rrrr __str__5szAlreadyTrialedException.__str__)r r r rr r#rrr)rr r-s rcs eZdZdZfddZZS)UnableToGetLicenseExceptionzunable to get licensec s tt|jdt|f|dS)Nz6Unknown Issue when getting trial license. Error code: )rr$r r)rcoder)rrr r <s z$UnableToGetLicenseException.__init__)r r r rr rrr)rr r$9sr$c@seZdZdZdS)BadSignatureExceptionz bad signatureN)r r r rrrrr r&Bsr&csfdd}|S)Ncs t|S)N) isinstance)estate)exc_listrr rGszcheck_exc..innerr)r*rr)r*r check_excFs r+N) py23r Exceptionrrrrrrr$r&r+rrrr s   kcarectl/__pycache__/log_utils.cpython-36.pyc000064400000005540152527712570015214 0ustar003 ;j~ @sddlmZddlZddlZddlZddlZddlZddlmZm Z ej dZ ddZ dd d Z dd d Zdd dZdddZdddZddZddZddZd ddZdS)!)print_functionN)config constantskcarecCst|tjtj|dS)N) _printlvlr PRINT_DEBUGkcarelogdebug)messager //usr/libexec/kcare/python/kcarectl/log_utils.pylogdebugs rTcCs|rt|tjtj|dS)N)rr PRINT_INFOr info)r print_msgr r r loginfos rcCs$|rt|tjtjdtj|dS)N)file)rr PRINT_WARNsysstderrr warning)r rr r r logwarnsrcCs$|rt|tjtjdtj|dS)N)r)rr PRINT_ERRORrrr error)r rr r r logerror&srcCs&|rtjtjkrtjtj|dS)N)rrr PRINT_LEVEL traceback print_excr exception)r rr r r logexc,sr cCs|tjkrt||ddS)N)r)rrprint)r levelrr r r r2s rcCs:tjd}tjjdtjjjd}|jtj|j||S)Nz kcare %(levelname)s: %(message)sz/dev/log)addressZfacility)logging FormatterhandlersZ SysLogHandlerZLOG_USERsetLevelINFO setFormatter)Zsyslog_formattersyslog_handlerr r r get_syslog_handler7s    r+cCsntjd}tjdkrJtjjtjddd}|jt |tj |j ||Stj }|j||j ||SdS)Nz&%(asctime)s %(levelname)s: %(message)sri)ZmaxBytesZ backupCounti) r$r%osgetuidr&ZRotatingFileHandlerrLOG_FILEr'minr(r) StreamHandler)r"Zkcare_formatter kcare_handlerr r r get_kcare_handler?s     r3cCsgtjdd<yt|}tj|Wn,tk rP}ztj|WYdd}~XnXtjjdryt }tj|Wn,tk r}ztj|WYdd}~XnXdS)Nz/dev/log) r r&r3 addHandler Exceptionrr-pathexistsr+)r"r2exr*r r r initialize_loggingPs r9cCs"|pd}tdj|t||dS)Nzz8Unable to fetch {0}. Please try again later (error: {1}))rformatstr)r8urlstdoutr r r print_cln_http_errorasr>)T)T)T)T)N)NT) __future__rr$Zlogging.handlersr-rrrr getLoggerr rrrrr rr+r3r9r>r r r r s"       kcarectl/__pycache__/capabilities.cpython-36.pyc000064400000001146152527712570015642 0ustar003 ;j@s$ddZddZddZddZdS) cCsdS)Nrrr2/usr/libexec/kcare/python/kcarectl/capabilities.pyget_kc_capabilites_bits srcCsdS)Nrrrrrrget_lc_capabilites_bitssrcCs| S)Nr)required_capabilitiesrrrhas_kc_capabilitiessrcCs| S)Nr)rrrrhas_lc_capabilities srN)rrrrrrrrs kcarectl/__pycache__/selinux.cpython-36.pyc000064400000003523152527712570014701 0ustar003 ;j@sRddlZddlmZmZmZmZddZddZdd Zd d Z ej d d Z dS)N)errors log_utils process_utilsutilscCs"tjj|\}}tjj|d|S)Nztmp.)ospathsplitjoin)fnameheadtailr-/usr/libexec/kcare/python/kcarectl/selinux.pyselinux_safe_tmpname srcCsNtrJtjdd d|g}tj|ddd\}}}|rJtjdj|||dd dS) NZ restorecon /usr/sbin/sbinz-RT) catch_stdout catch_stderrz8SELinux context restoration for {0} failed with {1}: {2}F) print_msg)rr)is_selinux_enabledrfind_cmd run_commandrlogerrorformat)dnamecmdcode_stderrrrrrestore_selinux_contexts r cCsVtjddgdd\}}}|r2tjdj||ddx|jdD]}||kr>dSq>Wd S) Nz/usr/sbin/semodulez-lT)rz/SELinux modules list gathering error: '{0}' {1}zselinux modules error)status F)rrr KcareErrorrr )Z semodule_namerouterrlinerrris_selinux_module_presentsr'csfdd}|S)Ncs$trtd rtjd||S)Nlibcarez:SELinux is enabled but libcare policy module is not loaded)rr'rr#)argskwargs)clblrrwrapper%s z*skip_if_no_selinux_module..wrapperr)r+r,r)r+rskip_if_no_selinux_module$s r-cCs,tjjdr tjdg\}}}ndS|dkS)Nz/usr/sbin/selinuxenabledFr)rrisfilerr)rrrrrr-s r) rrrrrrr r'r-cachedrrrrrs    kcarectl/__pycache__/config.cpython-36.pyc000064400000004141152527712570014454 0ustar003 ;j @s ddlmZdZdZdZdZdZdZdZdZ dZ ddgZ dZ dZ dZd Zd Zd Zd Zejd ZdZdZd Zd Zd Zd ddddgZdZdZdZdZdZdZ dZ!dZ"dZ#dZ$dZ%ej&Z'd Z(d Z)dZ*d&Z+d Z,dZ-dZ.d Z/ej0Z1dZ2dZ3dZ4dZ5dZ6dZ7d'Z8d Z9d)Z:d+Z;d ZdZ?d Z@d!ZAd"ZBdS),) constantsNTzkpatch.blacklistz kpatch.fixupszkeep-registrationzmanage-libcareFz/dumpsdlibcZlibsslZnscdZlibmZ libnss_dnsi<z kpatch.binz.donez kpatch.infodefaultz /proc/versionz/proc/kcare/effective_versionizhttps://patches.kernelcare.comz#https://ipv6.patches.kernelcare.comz$https://cln.cloudlinux.com/api/kcarez)https://ipv6.cln.cloudlinux.com/api/kcareri@8i,il9i,ii@ii@)CrrAFTER_UPDATE_COMMANDAUTO_STICKY_PATCHSET AUTO_UPDATEAUTO_UPDATE_DELAYBEFORE_UPDATE_COMMANDBLACKLIST_FILECHECK_CLN_LICENSE_STATUSCHECK_SSL_CERTS FIXUPS_FILEFLAGS FORCE_GIDZFORCE_JSON_SIG_V3Z HTTP_TIMEOUTZHTTP_UPLOAD_TIMEOUTIGNORE_UNKNOWN_KERNEL KPATCH_DEBUGENABLE_CRASHREPORTER PATCH_CACHE KDUMPS_DIR KMSG_OUTPUT KCORE_OUTPUTKCORE_OUTPUT_SIZEIGNORE_FEATURE_FLAGSLIBCARE_DISABLEDZ LIBCARE_LIBSZ!LIBCARE_PIDLOGS_MAX_TOTAL_SIZE_MBZLIBCARE_SOCKET_TIMEOUTZLIB_AUTO_UPDATE PATCH_BIN PATCH_DONE PATCH_INFO PATCH_LEVEL PATCH_METHOD PATCH_TYPEPREFIXPREV_PATCH_TYPEZ PRINT_INFO PRINT_LEVELZ REPORT_FQDNSEND_PERF_METRICSSILENCE_ERRORSZSTATUS_CHANGE_GAP STICKY_PATCHSTICKY_PATCHSET UPDATE_DELAYUPDATE_FROM_LOCAL POLICY_REMOTE UPDATE_POLICYUPDATE_SYSCTL_CONFIGUSERSPACE_PATCHESZUSE_CONTENT_FILE_V3 USE_SIGNATUREZKERNEL_VERSION_FILEZKCARE_UNAME_FILESUCCESS_TIMEOUTKERNEL_ANOMALY_REPORT_ENABLE$KERNEL_ANOMALY_REPORT_MAX_SIZE_BYTESZDOCTOR_REPORT_MAX_SIZE_BYTESZFORCE_DOCTOR_FALLBACKZ FORCE_IPV4Z FORCE_IPV6 PATCH_SERVERZPATCH_SERVER_IPV6ZREGISTRATION_URLZREGISTRATION_URL_IPV6r:r:,/usr/libexec/kcare/python/kcarectl/config.pys|  kcarectl/__pycache__/http_utils.cpython-36.pyc000064400000012257152527712570015415 0ustar003 ;jE@sddlZddlZddlZddlZddlmZddlmZmZmZm Z m Z ddl m Z m Z mZmZmZmZddZdd d Zd d Zd dZdddZddZddZeZe jeddddZdS)N)SSLError)config constantserrors log_utilsutils) HTTPErrorRequestURLErrorhttplib std_urlopenurlparsec Ost|dr|j}n |}t|}|jdi}|jtjtjdx |jD]\}}|j ||qJWt j dj ||ydd|krt j|d<t j rttddrtj}d|_tj|_||d<t|f||St|f||Stk r&}z0|jd krtj|j|j|j|j|jWYdd}~Xn~tk r}z`|j r|t|j d d r||j d j!t!j"kr|tj|d t#|ddd j |||_$||_WYdd}~XnXdS) N get_full_urlheaders)z KC-VersionzKC-Patch-Versionz#Requesting url: `{0}`. Headers: {1}timeoutHAS_SNIFcontextirerrnozRequest for `{0}` failed: {1})%hasattrrr popupdaterVERSIONKC_PATCH_VERSIONitems add_headerrlogdebugformatr HTTP_TIMEOUTCHECK_SSL_CERTSgetattrsslcreate_default_contextcheck_hostname CERT_NONE verify_moder r coderNotFoundurlmsghdrsfpr argsrENOENTstrreason) r(r,kwargsZ request_urlrheadervaluectxexr50/usr/libexec/kcare/python/kcarectl/http_utils.py urlopen_bases>      .r7Tcsfdd}|S)Ncsbt|tro|jdkSt|ttjttjfr2dSt |dr^t |j dkr^|j dt j kr^dSdS)NiTr,r) isinstancer r&r r HTTPExceptionrsocketrrlenr,rZ ECONNRESET)estate) retry_on_500r5r6check_functionCs  (z3check_urlopen_retry_factory..check_functionr5)r?r@r5)r?r6check_urlopen_retry_factoryBs rAcCst|dr|j}|jdS)Nrzfile:)rr startswith)r(r5r5r6 is_local_urlOs rCcOsV|jdd}|jdtj}t|r2t|f||Stjt|d|dt|f||S)Nr?T retry_count)r?)count)rr RETRY_COUNTrCr7rretryrA)r(r,r0r?rDr5r5r6urlopenUs  rHcCsHt||d}tj r*|r*|jddj|tj rD|rD|jtj||S)N)method Authorizationz Basic {0})r rUPDATE_FROM_LOCALrrrAUTH_TOKEN_HEADER)r( auth_string auth_tokenrIrequestr5r5r6 http_request]s    rPcCs<|dkrtjdptjdS|dkr8tjdp6tjdSdS)NhttpZ http_proxyZ HTTP_PROXYhttpsZ https_proxyZ HTTPS_PROXY)osgetenv)schemer5r5r6get_proxy_from_envhsrVcCsttdpttdS)NrQrR)boolrVr5r5r5r6 proxy_is_usedosrX)Z check_retrycCstjj|}|stdj|ttj|}|j}|j }|jp@d}|dkrRtd|dkrl|j dkrhdnd}|j dkr~t j }n|j dkrt j }ntd |||tjd } i} |rd j|| d <d | d<t|| d<tj| d<z| jd|x | jD]\} } | j| | qW| jt|d} | j| WdQRX| j}|jdkry |j}Wntk rnd}YnXdj|j}|dk r|dj|7}tj|t||j|ddWd| j XdS)aUpload a file to the given URL using HTTP PUT with chunked streaming. Note: The standard library urllib doesn't support PUT with data We need to use httplib directly for this This function uses streaming upload to support large files up to 1GB without loading the entire file into memory. :param file_path: Path to the file to upload :param upload_url: Full URL to upload the file to. Query params are ignored. :param auth_string: Optional authentication string for Basic Auth :return: None if upload succeeded :raises HTTPError: If upload fails with HTTP status >= 400 :raises ValueError: If URL is invalid z"Refusing to upload empty file: {0}/NzInvalid URL: missing hostnamerRiPrQzInvalid URL: unsupported scheme)rz Basic {0}rJzapplication/octet-streamz Content-TypezContent-Lengthz KC-VersionPUTrbizFailed to upload file: HTTP {0}z - {0})!rSpathgetsize ValueErrorrrrnstrhostnameportrUr HTTPConnectionHTTPSConnectionrHTTP_UPLOAD_TIMEOUTr.rr putrequestr putheader endheadersopensend getresponsestatusread Exceptionrlogerrorr close)Z file_path upload_urlrMZ file_sizeparsedhostrbZurl_pathZconn_clsconnrr1r2fresponseZ error_body error_msgr5r5r6 upload_filevsT              rx)T)NN)N)rrSr;r!rrrrrrpy23r r r r r rr7rArCrHrPrVrXcheck_urlopen_retryrGrxr5r5r5r6s   0   kcarectl/__pycache__/update_utils.cpython-36.pyc000064400000006035152527712570015715 0ustar003 ;j@sddlZddlZddlZddlZddlmZmZmZmZddl m Z dZ ddd Z dd d Z d d ZddZddZddZddZddZdS)N)config constants log_utilsutils)json_loads_nstr< .kcarestatuscCs$tjjtj|}tj|tjdS)N)ospathjoinr PATCH_CACHEr atomic_write timestamp_str)filenamestatus_filepathr2/usr/libexec/kcare/python/kcarectl/update_utils.pytouch_status_gap_filesrcCsztjjtj|}tjj|rvt|dJ}y.t|j}t|t j t t j krTdSWnt k rjYnXWdQRXdS)NrFT)r r r rrisfileopenintreadrSTATUS_CHANGE_GAPSTATUS_CHANGE_GAP_DELAYtime Exception)rrsfile timestamprrrstatus_gap_passeds   r!cCs|dkrtdj|dS)Nkernellibcarez$Unknown update status component: {0})r"r#) ValueErrorformat) componentrrr_check_component%sr'c CsNtjtj}|dkriSy t|}|Sttfk rHtjj diSXdS)Nz"Failed to parse update status file) r read_filerUPDATE_STATUS_PATHrr$ TypeErrorrkcarelogwarning)contentresultrrr_load_update_status+s  r/c Csft|y4t}|ttjd||<tjtjtj |Wn$t k r`t j j dddYnXdS)N)errorr zFailed to save update statusT)exc_info)r'r/rrrrrr)jsondumpsrrr+r,)r&r0datarrrsave_update_status9sr5cCs>t|}tt|dd}|r:||kr:|jr6dj|S|S|S)NstatuszHTTP Error: {0})strgetattrisdigitr%)errmessager6rrr _error_statusGs  r=cstfdd}|S)Ncstjfdd}|S)NcsTy||}Wn4tk rB}ztt|dWYdd}~XnXtdd|S)N)r0r7)rr5r=)argskwargsr.r;)r&fnrrinner_s z5track_update_status..decorator..inner) functoolswraps)r@rA)r&)r@r decorator^s z&track_update_status..decorator)r')r&rDr)r&rtrack_update_statusZs rEc Cs^t|y,t}|j|ijdd}t|dtjStk rXtjj ddddSXdS)Nr0r7zFailed to read update statusT)r1) r'r/getr8rUPDATE_ERROR_MAX_LENGTHrrr+r,)r&r4r0rrrread_update_errorosrHi,)r )r )rBr2r rr7rrrrpy23rrrr!r'r/r5r=rErHrrrrs   kcarectl/__pycache__/config_handlers.cpython-36.pyc000064400000017652152527712570016347 0ustar003 ;j&5@sddlZddlZddlZddlmZmZmZmZmZddl m Z dZ dddd d d d d gZ e ZddZddZejdZddZdddedddeeeedeeeeeeeeeddeeeeeeeeddejddddejdejddddeeeeejddejeeeddeed4Zdd Zd!d"ZGd#d$d$eZd%d&Zd'd(Zd)d*Zd+d,Z ej!ej"d-d.d/Z#ej!ej"d-d0d1Z$dS)2N)config constants http_utils log_utilsutils) ConfigParserz/etc/sysconfig/kcare/kcare.confUSE_CONTENT_FILE_V3FORCE_JSON_SIG_V3ENABLE_CRASHREPORTER KCORE_OUTPUT KMSG_OUTPUTSEND_PERF_METRICSKERNEL_ANOMALY_REPORT_ENABLEFORCE_DOCTOR_FALLBACKcCs |jdkS)N1TRUEYESY)rrrr)upper)valuer5/usr/libexec/kcare/python/kcarectl/config_handlers.pybool_converter#srcCsdd|jdDS)NcSs g|]}|jr|jjqSr)striplower).0itemrrr (szcomma_list..,)split)rrrr comma_list'sr!z^[a-z][a-z0-9_.+\-]*$cCsRgg}}x(t|D]}tj|r&|n|j|qW|rNtjdjdj||S)Nz3LIBCARE_LIBS: ignoring invalid library name(s): {0}z, )r!_LIBCARE_LIB_NAME_REmatchappendrlogwarnformatjoin)rZvalidinvalidtokenrrrlibcare_libs_list0s  r*cCs|jS)N)r)vrrr@sr,cCs|jS)N)r)r+rrrr,DscCs |jdS)N/)rstrip)r+rrrr,SscCs|pdS)Nr)r+rrrr,\scCs |jdS)Nr-)r.)r+rrrr,^scCs |jdS)Nr-)r.)r+rrrr,_scCs |jdS)Nr-)r.)r+rrrr,cscCs |jdS)Nr-)r.)r+rrrr,ds)4AFTER_UPDATE_COMMANDAUTO_STICKY_PATCHSET AUTO_UPDATEAUTO_UPDATE_DELAYBEFORE_UPDATE_COMMANDCHECK_SSL_CERTSDOCTOR_REPORT_MAX_SIZE_BYTESr r FORCE_GID FORCE_IPV4 FORCE_IPV6r HTTP_TIMEOUTHTTP_UPLOAD_TIMEOUTIGNORE_FEATURE_FLAGSIGNORE_UNKNOWN_KERNELr KCORE_OUTPUT_SIZE KDUMPS_DIRr$KERNEL_ANOMALY_REPORT_MAX_SIZE_BYTESr LIBCARE_DISABLED LIBCARE_LIBS!LIBCARE_PIDLOGS_MAX_TOTAL_SIZE_MBLIBCARE_SOCKET_TIMEOUTLIB_AUTO_UPDATE PATCH_LEVEL PATCH_METHOD PATCH_SERVERPATCH_SERVER_IPV6 PATCH_TYPEPREFIXPREV_PATCH_TYPEREGISTRATION_URLREGISTRATION_URL_IPV6 PRINT_LEVEL REPORT_FQDNSILENCE_ERRORSSTATUS_CHANGE_GAP STICKY_PATCHSTICKY_PATCHSET UPDATE_DELAY UPDATE_POLICYUPDATE_SYSCTL_CONFIGUSERSPACE_PATCHESr KERNEL_VERSION_FILEKCARE_UNAME_FILESUCCESS_TIMEOUTrc Kstt}|j}|jx|jD]\}}d}|d}|d}x^tt|D]N}||j|sl||j|rL|dkr|||=n|dt|d||<d}PqLW|s"|j |dt|dq"Wt j tdj |dS)NF= z =  T) openCONFIG readlinescloseitemsrangelen startswithstrr$r atomic_writer') kwargsZcflinesZproprupdatedZprop_eqZprop_spirrr update_configws"rmc Csi}tjd}xD|D]<}|j|}|r<|j\}}|sHd}n td||||<qWt|tt}|r~tddjt|x`|j D]T\}}t|} |dks| dkrqy | |Wqt k rtd||fYqXqWt f|dS)Nz^([^=]+)=([^=]*)$z8Invalid parameter format: %s. Format should be KEY=VALUEzUnknown parameter: %sz, zBad value for %s: %s) recompiler#groups SystemExitsetPOSSIBLE_CONFIG_OPTIONSr'sortedrc Exceptionrm) paramsZparams_for_updatepatternparamr#keyrZunknown_paramsvar_nameconvertrrrupdate_config_from_argss,       r|c@s$eZdZddZddZddZdS) FakeSecHeadcCs||_d|_dS)Nz [asection] )fpsechead)selfr~rrr__init__szFakeSecHead.__init__c Cs&|jrz|jSd|_Xn |jjSdS)N)rr~readline)rrrrrs  zFakeSecHead.readlineccs.|jr|jVd|_x|jD] }|VqWdS)N)rr~)rlinerrr__iter__s  zFakeSecHead.__iter__N)__name__ __module__ __qualname__rrrrrrrr}s r}c sitddddy,ttt}tjr4j|n j|Wntk rTiSXd fdd }x4dD],\}}t j |slj d |}|rl|t j |<qlWx tjD]\}}|||d qWS)Nr^) HTTP_PROXY HTTPS_PROXY)defaultsc sJyjd|}Wntk r(|}YnX|dk rF|r>||}||<dS)Nasection)getru)namedefaultr{r)cpresultrrread_vars z%get_config_settings..read_varhttprhttpsrr)r{)NNrrrr)rr)rr}r_r`rPY2readfp read_filerurget_proxy_from_envrosenvironrsrc)rrschemevariableproxyrzr{r)rrrget_config_settingss$     rcCs(tjt}tjj|tj|dS)N)_CONFIG_OPTIONSclearrr__dict__update)Zsettingsrrrset_settings_from_config_files rc Csi}xv|jD]j\}}|j}|jds*q|jddjdd}ytt|||<Wqtk rvtjj d||YqXqW|S)ak Checking headers for feature flags which start with 'KC-Flag-' and reformat it to dictionary with keys in upper case and without 'KC-Flag-' prefix and dashes replaced with underscores. For unification all header keys are checked in upper case. For example: 'KC-Flag-Some-Value' -> 'SOME_VALUE' :return: dict {'SOME_VALUE': bool, ...} zKC-FLAG-r^-_z(Invalid feature flag header value %s: %s) rcrrfreplaceboolint ValueErrorrkcarelogerror)headersflagsZhdr_nameZ hdr_valueZ upper_name param_namerrr convert_headers_to_feature_flagss  rcCst|tjstdS)N)save_feature_flags_cacherr;set_feature_flags_from_cache)rrrrset_feature_flags_from_headerssr)loggercCs"t|}tjtjtj|ddS)N)content)rrrhrFEATURE_FLAGS_CACHEjsondumps)r feature_flagsrrrr src CsztjjtjsdSttj}tj|}WdQRXxB|jD]6\}}|t krNq<|t krXq<|t j |<t jjd||qs  % kcarectl/__pycache__/delivery_kit.cpython-36.pyc000064400000016616152527712570015713 0ustar003 ;j+@sddlZddlZddlZddlZddlZddlZddlZddlmZddl m Z m Z m Z m Z mZmZddlmZddZGdddeZGd d d eZdS) N)NamedTemporaryFile)authconfig http_utils ipv6_support log_utilsutils) run_commandcCs<t|}x(dD] }|dkr&dj||S|d}qWdj|S) zBRender a byte count as a short human-readable string for the logs.BKiBMiBGiBg@z {0:.1f} {1}z {0:.1f} TiB)r r r r )floatformat)Z num_bytessizeZunitr2/usr/libexec/kcare/python/kcarectl/delivery_kit.py format_sizes    rc@seZdZdZdZdZddZeddZeddZ d d Z d(d dZ ddZ ddZ ddZddZddZddZddZddZdd Zejejd!d"d#Zd$d%Zd&d'Zd S)) DataPackagezGeneric archive package for the patch server uploads pipeline. Based on DataPackage from eportal (delivery_kit.py). Subclasses supply the manifest `data_type`, the `upload_uri` to PUT the archive to and the `max_size` payload limit. cCsd|_g|_d|_dS)Nr)_tar_errors_buffer_total_payload_size)selfrrr__init__-szDataPackage.__init__cCstdS)N)NotImplementedError)rrrrmax_size3szDataPackage.max_sizecCs|j}t|jS)N)_ensure_tar_createdstrname)rtarrrr archive_path8szDataPackage.archive_pathcCsd}d}yttj|ddd\}}}Wn*tk rP}zt|}WYdd}~XnX|rh|jdj|||dk r|j|tj |ddddS)NT) catch_stdout catch_stderrz!failed to dump stdout of {0}: {1}zutf-8)encoding) data_bytes) r shlexsplit Exceptionr log_errorradd_filerbstr)rarcnamecmdstdoutstderr_errr add_stdout>szDataPackage.add_stdoutNFc Csh|dkr|dkrtd|j}d}|dk rZtjj|sL|jdj|dStjj|}nt|}|j |s|jdj|dS| r|j | r|j dj|t |t |j t |jdSyp|r|j||dn"tj|}||_|j|tj||s*|j|7_tjdj|t |t |jdd Wn6tk rb}z|jd j||WYdd}~XnXdS) Nz"No src_path or data_bytes providedrzfile not found: {0}z no available space to store: {0}zPskipping {0}: {1} would exceed the {2} report size limit (already collected {3}))r,z%collected {0}: {1} (report total {2})F) print_msgzfailed to store {0}: {1}) ValueErrorrospathexistsr)rgetsizelen_check_required_space_check_total_payload_limit log_warningrrraddtarfileTarInforaddfileioBytesIOrloginfor() rr,src_pathr%skip_limit_checkr entry_sizeinfor1rrrr*NsJ     zDataPackage.add_filecCsbytjtj|dddd}Wn2tk rN}z|jdj||dSd}~XnX|j||ddS)N)indentzutf-8)r$zfailed to dump {0}: {1})r%)rr+jsondumps TypeErrorr)rr*)rr,datar%r1rrradd_jsons zDataPackage.add_jsoncCstj|j}|j|j|kS)N)r5statvfsr!f_frsizef_bfree)rrFrOrrrr:s z!DataPackage._check_required_spacecCs|j|j|kS)N)rr)rrFrrrr;sz&DataPackage._check_total_payload_limitcCsd|jttjdS)N)Zschema_versiontypeZ time_created) data_typeinttime)rrrr make_manifestszDataPackage.make_manifestcCsNtjtj|jdddd}|j}tjd}t||_ |j |t j |dS)NrH)rIzutf-8)r$z manifest.json) rr+rJrKrWrr>r?r9rr@rArB)rr%r rGrrr _add_manifests   zDataPackage._add_manifestcCs&|j}tj|dd|jj|dS)NF)r3)striprlogerrorrappend)rZ error_msgrrrr)szDataPackage.log_errorcCs&|j}tj|dd|jj|dS)NF)r3)rYrlogwarnrr[)rZ warning_msgrrrr<szDataPackage.log_warningcCsxd D]}tdj|dddd}|jy6tjdj|jdd tj|j|d d |_|j |St k r}zb|jdk ry|jjWnt k rYnXd|_t j j |jrt j|jt|tjsЂWYdd}~XqXqWtjd dS)Nw:xzw:bz2w:gzz.tar.{0}F)suffixdeletezCreating DataPackage: {0})r3T)rmodeZ dereferencez%No supported compression method found)r]r^r_)rrcloserrCrr>openrrXr(r5r6r7unlink isinstanceZCompressionError)rZcompression_modeZtmpfileerrrrr __enter__s(    zDataPackage.__enter__cCsP|jr,dj|jd}|jdtj|dd|jrL|jj|rL|jdSdS)N z errors.logT)r%rEF)rjoinr*rr+rrdremove_archive)rexc_typeZexc_valexc_tberrorsrrr__exit__s zDataPackage.__exit__)loggercCs$|jr tjj|jr tj|jdS)N)rr5r6r7r!rf)rrrrrlszDataPackage.remove_archivecCs|jstd|jS)Nz/DataPackage should be used as a context manager)r RuntimeError)rrrrrszDataPackage._ensure_tar_createdcCst|jjtjj|j}d|kr4||jddnd}ttj |}t j |j |}t j|j|tjd|S)aSend the package archive to the patch server. Upload errors propagate to the caller (see the eportal precedent): wrap with utils.catch_errors where a silent failure is acceptable. :return: Upload name (package identifier) .Nr) upload_urlZ auth_string)rrdr5r6basenamer!findruuidZuuid4rget_patch_server upload_urirZ upload_filerget_http_auth_string)rruext upload_namertrrrsends  zDataPackage.send)NNF)__name__ __module__ __qualname____doc__rTryrpropertyrr!r2r*rNr:r;rWrXr)r<rirpr catch_errorsrr\rlrr}rrrrr"s(   ;    rc@s eZdZdZdZeddZdS)KernelAnomalyPackagezkernel-anomalyz/upload/kernel-anomaly/cCstjS)N)rZ$KERNEL_ANOMALY_REPORT_MAX_SIZE_BYTES)rrrrrszKernelAnomalyPackage.max_sizeN)r~rrrTryrrrrrrrsr)rArJr5r&r>rVrwtempfilerkcarectlrrrrrrZkcarectl.process_utilsr robjectrrrrrrs    ykcarectl/__pycache__/platform_utils.cpython-36.pyc000064400000024247152527712570016264 0ustar003 ;j(@sddlZddlZddlZddlZddlZddlZddlZddlmZm Z m Z m Z m Z m Z dZdZddZe jdd Zd d ZdQd dZdZdddZdRddZddZddZddZe jddZe jddZe jd d!Zd"d#Zd$d%Zd&d'Z d(d)Z!d*d+Z"d,d-Z#d.d/Z$d0d1Z%d2efd3efd4e fd5e!fd6e"fd7e#fd8e$ffZ&d9d:Z'd;d<Z(d=d>Z)e j*e j+d?d@dAZ,e j*e j+d?dBdCZ-e je j*e j+d?dDdEZ.e j*e j+ddFdGdHZ/e j*e j+ddFdIdJZ0e j*e j+d?dKdLZ1e j*e j+dSdFdMdNZ2dOdPZ3dS)TN)config constants log_utils process_utilsselinuxutilsz/usr/libexec/kcare/virt-whatz/proccCs2tjdddkrtjSddl}|jddSdS)NrF)full_distribution_name)r r )sys version_infoplatformlinux_distributiondistro)rr4/usr/libexec/kcare/python/kcarectl/platform_utils.py get_distrosrcCs tjdS)Nr )runamerrrrget_system_unamesrcCsdtjdtjdfS)Nz%s.%srr)r rrrrrget_python_version$srFc Csttjd}tjr^tjddddttj gdd\}}}|sN|j d}nd |}||d <|rlt j |St |}d }x |D]}|d |||f7}q~W|jS) N)python_version agent_versionZpsz-Zz --no-headersz--pidT) catch_stdoutrz error: %sselinux_contextz%s: %s )rrVERSIONrZis_selinux_enabledr run_commandstrosgetpidsplitjsondumpssortedrstrip) is_jsoninforcstdoutstderrrZ info_keysZinfo_strZinfo_keyrrrapp_info(s &  r,z/sys/firmware/efi/efivarsz$8be4df61-93ca-11d2-aa0d-00e098032b8cz$605dab50-e046-4300-abb6-3dd810dd8b23)globalshimc CsFtjjtd||f}tjj|s&dSt|d }|j|SQRXdS)Nz%s-%srb)r pathjoin EFIVARS_PATHexistsopenread)namevendorZ max_bytesZvar_pathvarrrr_read_uefi_varIs   r:c Cs@y&tdtd}|r$|dddkSWntk r:YnXdS)N SecureBootr-rF)r: EFI_VENDORS Exception)Zsecure_boot_varrrris_secure_bootRsr@cCsZyt||}|dkrdSWn.tk rH}zt|j}WYdd}~XnXtjtj|S)N)r:r?rencodernstrbase64urlsafe_b64encode)r7r8Z value_byteserrr_get_uefi_var_encoded]s rFcCstjtjjtd}|r0t|dkr0|dd}|tjjtjjt d}|dsV|Syt dddD|d <t d d tj t D}d |i|d <t ddddg}x<|D]4}|dks|jdr||krt|td |d |<qWWn2tk r}ztjt|WYdd}~XnX|S)Ncmdlinei)rGhas_efirHcss |]}|t|tdfVqdS)r-N)rFr>).0r9rrr rsz#secure_boot_info..r; SetupModer-cSs4g|],}|jtdr|dttd dqS)r.rr)endswithr>len)rIr9rrr tsz$secure_boot_info..varsr.Z MokListRTZ MokListXRTZMokListTrustedRTZ SbatLevelRT HSIStatus MokIgnoreDBZRT)r;rK)rPrQ)r try_to_readr r1r2PROC_DIRrMr4dirnamer3dictr%listdirsetrLrFr>r?rlogwarnr)rGr(Z shim_varsZshim_exclude_varsr9errrrrsecure_boot_infohs$    rZcCsrtjrfy&tjtjddddtjdd}Wqntjk rb}ztj|t j }WYdd}~XqnXnt j }|S)Nrr ) r REPORT_FQDNsocket getaddrinfo gethostname AI_CANONNAMEgaierrorrlogerrorrnode)hostnamegerrr get_hostnames& recCsTtjjtd}tjj|rPt|d}|j}ttt |j d}|j |SdS)Nuptimerrz-1) r r1r2rSisfiler5readlinerintfloatr"close)Z uptime_fileflineresultrrr get_uptimes  rpcCs tjjtrtjtgjSdS)Nz no-virt-what)r r1rhVIRTWHATr check_outputstriprrrrget_virts rtcCs tjjdS)Nz/usr/local/cpanel/cpanel)r r1rhrrrr is_cpanelsrucCs tjjdS)Nz/usr/local/psa/admin/)r r1isdirrrrris_plesksrwcCs tjjdS)Nz/usr/local/interworx/)r r1rvrrrr is_interworxsrxcCs tjjdS)Nz/usr/local/ispmgr/)r r1rvrrrr is_ispmanagersrycCs tjjdS)Nz/usr/local/directadmin/plugins/)r r1rvrrrris_directadminsrzcCs tjjdS)Nz/usr/local/hostingcontroller/)r r1rvrrrris_hosting_controllersr{cCs tjjdS)Nz/hsphere/shared)r r1rvrrrr is_hspheresr|cCstdddDS)z:Softaculous markers checked by the kcdoctor.sh detect_cp()css|]}tjj|VqdS)N)r r1r4)rIr1rrrrJsz"has_softaculous../usr/local/softaculous*/usr/local/cpanel/whostmgr/cgi/softaculous*/usr/local/directadmin/plugins/softaculous)r}r~r)anyrrrrhas_softaculouss rZPleskZcPanelZ InterWorxZ ISPmanagerZ DirectAdminzHosting ControllerzH-SpherecCs2tjjtjjtddo0tjjtjjtdd S)NZvzZveinfoversion)r r1r4r2rSrrrrinside_vz_containersrcCsdttjjtddjkS)Nz/lxc/1Zcgroup)r5r r1r2rSr6rrrrinside_lxc_containersrcCs tjjdS)Nz /.dockerenv)r r1rhrrrrinside_docker_containersr)loggercCsFtjtjjtd}|sdS|jdd\}}}}t|t|t|fS)Nloadavg r )rrRr r1r2rSr"rk)rm1Zm5Zm15_rrrget_load_averages rcsDtjtjjtd}|sdSd tfddd d|jDDS) z!Returns dict of memory info in kBmeminfoNMemTotalMemFree SwapTotalSwapFreec3s&|]\}}|kr|t|fVqdS)N)rj)rIkv) filter_paramsrrrJszget_mem_info..css"|]}tjd|ddVqdS)z[\s:]+Nr )rer")rIrnrrrrJs)rrrr)rrRr r1r2rSrU splitlines)rr)rr get_mem_infos rc Cstjtjjtd}|sdSdd|jdD}t|ttdd|D|dj dt |dj d d|dj d t |dj d dt |dj d d|dj d |dj ddjd S)NcpuinfocSs&g|]}|rtdd|jDqS)css|]}tjd|VqdS)z\s*:\s*N)rr")rIrnrrrrJ sz*get_cpu_info...)rUr)rIZ cpu_linesrrrrN sz get_cpu_info..z css"|]}|jd|jdfVqdS)z physical idzcore idN)get)rIZcpurrrrJszget_cpu_info..r vendor_idmodelz model namez cpu familystepping microcodeflagsr) Z logical_coresZphysical_coresrrZ model_nameZ cpu_familyrrr) rrRr r1r2rSr"rMrWrrj)rZcpusrrr get_cpu_infos   r)rdefault_returncCstddtjtDS)NcSsg|]}|jr|qSr)isdigit)rIdrrrrN sz%get_process_count..)rMr rVrSrrrrget_process_countsrcCs(tjtjjtd}t|pdjdS)Nzsys/fs/file-nr0r)rrRr r1r2rSrjr")Zfd_inforrrget_opened_files_count#srcCs$xtjdD]\}}}t|SWdS)Nz/sys/kernel/debug/kvm)r walkrM)Z_rootdirs_filesrrrget_vm_count_kvm*s rcCs(dd}|d|d|d|dfS)z8Return tuple of total numbers of TCP and UDP connectionscSs4tjtjjtd|}|sdStt|jddS)Nnetrr) rrRr r1r2rSmaxrMr)protorecordsrrrconn_records_count7sz9get_network_connections_count..conn_records_countZtcpZtcp6ZudpZudp6r)rrrrget_network_connections_count2src Cs,t\}}tttttt||dS)N)Z load_averageZmem_infoZcpu_infoZvm_countZ processesZ open_filesZtcp_connectionsZudp_connections)rrrrrrr)Zconn_tcpZconn_udprrrget_performance_metricsAs r)F)r/)rr)4rCr#r rrr\r rrrrrrrrqrSrcachedrrr,r3r>r:r@rFrZrerprtrurwrxryrzr{r|rZCONTROL_PANEL_PROBESrrr catch_errorsrXrrrrrrrrrrrrsd          kcarectl/__pycache__/auth.cpython-36.pyc000064400000021020152527712570014143 0ustar003 ;j/@sddlZddlZddlZddlZddlmZmZmZmZm Z m Z m Z m Z m Z ddlmZmZmZd"ddZdd Zd d Zd d Zd#ddZe jddZddZddZddZddZddZddZd$ddZd d!Z dS)%N) config constantserrors http_utils ipv6_support log_utilsplatform_utilsserveridutils) HTTPErrorURLError urlencodeFcCsd}ytj}|dkr(|s$tjddStjdj|}tj|}t j |j }t j |}|ddkrtj tj|stjdn |stj|tjd|dWn2tk r}z|stj||WYdd}~XnXdS)Nz1Error unregistering server: cannot find server idz&/unregister_server.plain?server_id={0}successtruezServer was unregisteredzError unregistering server: message)r get_serveridrlogerrorrget_registration_urlformatrurlopenr nstrread data_as_dictZ rm_serverid clear_cacheloginfor print_cln_http_error)Zsilenturl server_idresponsecontentreser#*/usr/libexec/kcare/python/kcarectl/auth.py unregisters,      r%cCstjdtj}|dkrdStjtj}ddl}|dkrH|jd|jjt dd}t dd}tj |j |j j tj |j |jj tj |j |j j xLtjd t|\}}}|dkr|rtj|t|tj|jdqWdS) NzHRegister auto-retry has been enabled, the system can be registered laterrz /dev/nullrza+<ii )r print_wrapperosforksetsidsysexitstdoutflushopendup2filenostdinstderrtimesleep _try_registerr set_server_id_set_auth_tokenrr)rpidr-sisocoder auth_tokenr#r#r$_register_retry)s.        r@cCs&|dk r"tjd| r"td||S)Nz ^[\w.-]+$zInvalid value received: %s)rematch ValueError)valuer#r#r$_validate_urlsafe_encodingGs rEcCsyNtj|}|jjtjd}tjtj|j }t |dt |jdt |fSt t fk r~}ztj||dSd}~Xn$tk rtjjd|dSXdS)Nr>rz)Exception while trying to register URL %s)NNN)NNN)rrheadersgetrZAUTH_TOKEN_HEADERr rrrintrEr r rr Exceptionkcarelog exception)rrr?r!r"r#r#r$r8Ms   r8c Cs>y tdWn tk r,tjjdYnXtj}td|fd|fg}djt j |}t |\}}}|dkrt j |t|t jtjddS|dkrtjd nv|d krtjd nb|d krtjd nN|dkrtjdn:|dkrtjdn&|dkrtjdntjdj||r4t|dS|prr?r#r#r$register[s@         rUcCs tjtjS)N)r try_to_readrAUTH_TOKEN_DUMP_PATHr#r#r#r$_get_auth_tokensrXcCs|sdStjtj|dS)N)r atomic_writerrW)r?r#r#r$r:sr:cOsv|jdd}|jddrt}ntj}tj|r@tj|f||Stj|tt|d}t j |ddtj|f||S)Nmethod check_licenseT)rZ)count) pop_check_auth_retryrcheck_urlopen_retryZ is_local_url urlopen_base http_requestget_http_auth_stringrXr rS)rargskwargsrZcheckrequestr#r#r$ urlopen_auths   rhcCs.tj}|r*tjtjtjdj|dSdS)Nz{0}:{1}Z kernelcare)r rr rbase64 b64encodebstrr)rr#r#r$rcsrccCs(t|tr|jdkrt|Stj||S)N)rlrm) isinstancer r>_handle_forbiddenrr`)r"stater#r#r$r_sr_cCsd|kr dStjrtj}tjd}|r8|dj|7}y&tjt j |ddj }tj |}Wn0t k r}ztj||dddSd}~XnX| s|jd  rtjjd j|dS|d dkrd|d<tjd dStdS)aIn case of 403 error we should check what's happen. Case #1. We are trying to register unlicensed machine and should try to register trial. Case #2. We have a valid license but access restrictions on server are not consistent yet and we had to try later. licenseTz /check.plainz?server_id={0}F)Z retry_on_500)r/Nr>zUnexpected CLN response: {0}01z$Unable to access server. Retrying...)rrrs)rCHECK_CLN_LICENSE_STATUSr rrrrr rrrrrr rrrGrJerrorr_register_trial)rprrr infoexr#r#r$ros*   rocCstj}|rtjdj|}ytj|}tj|j }tj |}| sT|j d rhtj dj|dSt |d}|dkrtj ddStdd}|dkrtj d|SWqtk r}ztj||dSd}~XqXntSdS) Nz/check.plain?server_id={0}r>zUnexpected CLN response: {0}rrzKey-based valid license found) key_checkedz No valid key-based license found)r rrrrrrr rrrrGr)rH_get_license_info_by_ipr rr)rrrr r!r>Z license_typer"r#r#r$ license_infos,       r{c Cstjd}y6tj|}tj|j}tj|}|djdkr&t |d}|dkrptj dj |ddS|dkr|d}tj |d j d }tj d j ||d S|d kr|dkr|d}tj |d j d }tj d j |||dko|dkrBd|krtj dj |dn tj dn|jdd}tj dj |Wnhtk rt} ztj| |WYdd} ~ Xn:tk r} ztj dj | |jWYdd} ~ XnXdS)Nz /check.plainrrr>rzValid license found for IP {0}iprZ expire_datez%Y-%m-%dz?You have a trial license for the IP {0} that will expire on {1}r(z0Your trial license for the IP {0} expired on {1}rNzThe IP {0} hasn't been licensedz This server hasn't been licensedrz"Error retrieving license info: {0}z1Unexpected CLN response, cannot find {0} key: {1})rrrrr rrrlowerrHr)rZparse_response_datestrftimerGr rrKeyErrorstrip) ryrrr r!r>r|Z expires_strrr"rMr#r#r$rzs<       (rzcCsFtjjtjd}tjj|r dSytjtj d}t j t j |j }y|djdkrt j|dddtj|ddkrtj|d |d tjd j|d dS|dd krt j|dddtjd n tjdWn.tk r }ztj|WYdd}~XnXWn0tk r@}ztj|jWYdd}~XnXdS)Nztrial-requestedz /trial.plainrrr}T) ensure_dirZexpiredr|createdz3Requesting trial license for IP {0}. Please wait...ZnazInvalid LicenserrR)r*pathjoinr PATCH_CACHEexistsrrrrr rrrr~rYrrZAlreadyTrialedExceptionrrr KcareErrorZUnableToGetLicenseExceptionrr r>)Z trial_markrr!Zker"r#r#r$rvs,     rv)F)F)r)!rir*rAr6r}rrrrrrr r r py23r r rr%r@rEr8rUcachedrXr:rhrcr_ror{rzrvr#r#r#r$s&,  . ) #kcarectl/__pycache__/fetch.cpython-36.pyc000064400000010555152527712570014306 0ustar003 ;j@sddlZddlZddlZddlZddlmZmZmZmZm Z m Z m Z ej ej gZdZdZdZddd Zd d Zd d Ze jejejddddddZGdddeZe jddZddZdS)N)authconfig constantserrors http_utilsselinuxutilsz /usr/bin/gpgz/var/lib/kcare/gpgzrelease.content.jsonFc Cstj}|rtj}tjr&tddd}nt}xT|D]L}y|||}PWq0tjk rz}z||dkrj|WYdd}~Xq0Xq0W||}t j |||S)Nrr ) rurlopenr urlopen_authrFORCE_JSON_SIG_V3SIG_VERIFY_ORDERrNotFoundr save_to_file) urldstdo_authZ urlopen_localZsig_extsZsig_ext signatureZnfZsig_dstr+/usr/libexec/kcare/python/kcarectl/fetch.pyfetch_signatures     rcCs$tjjts tjdjtdddS)Nz$No {0} present. Please install gnupgzgnupg not found)status)ospathisfileGPG_BINr KcareErrorformatrrrr check_gpg_bin)s rcCst|jtjrptjjtd}ytj |||Wqtj k rl}zt j dj |t|WYdd}~XqXnxt|d}|j}WdQRXtjjtd}ytj|||Wn8tk r}zt j dj |t|WYdd}~XnXdS)a8 Check a file signature using the gpg tool. If signature is wrong BadSignatureException will be raised. :param file_path: path to file which signature will be checked :param signature: a file with the signature :return: True in case of valid signature :raises: BadSignatureException zroot-keys.jsonzBad Signature: {0}: {1}Nrbz kcare_pub.key)rendswithrSIG_JSONrrjoin GPG_KEY_DIR kcsig_verifyZverifyErrorrBadSignatureExceptionrstropenreadZrun_gpg_verify Exception) file_pathrZ root_keysefZsigdataZkeyringrrrcheck_gpg_signature.s  * r/)countdelaycCs^tj|}tj|}tj|||r2|j||n|rNt||dd}t||t j |||S)NT)r) rr rselinux_safe_tmpnamer rcheckrr/rrename)rrZcheck_signature hash_checkerresponsetmprrrr fetch_urlMs     r9c@seZdZddZddZdS) HashCheckercCs6||_tj|jdd|_tjtj|d|_dS)N/files) content_filer get_patch_server_urlrstrip url_prefixjsonloads read_filehashes)selfbaseurlr=rrr__init__^szHashChecker.__init__cCsv|t|jd}||jkr4tjdj||jddtjt j |j }|j|d}||krrtj dj|||dS)Nz3Invalid checksum: {0} not found in content file {1}zinvalid checksum)rsha256z USE_SIGNATURErrr:)levelrrrrget_hash_checkerts  rWcsfdd}|S)z=Enrich request with a cache key, and save it if response had.cshtj}|dk r.d|kr i|d<||dtj<||}|jjtj}|dk rd||krdtjtj||S)Nheaders)r get_cache_keyrCACHE_KEY_HEADERrXget atomic_writeCACHE_KEY_DUMP_PATH)argskwargs cache_keyrespZ new_cache_key)clblrrwrappers z$wrap_with_cache_key..wrapperr)rbrcr)rbrwrap_with_cache_keys rd)F)FN)rJrArr%rrrrrrr SIGr"rrr$rSrrr/retry check_excr'r9objectr:cachedrWrdrrrrs $   kcarectl/__pycache__/__init__.cpython-36.pyc000064400000163036152527712570014757 0ustar003 ;j*: @sddlmZddlZddlZddlZddlZddlZddlZddlZddl Z ddl Z ddl Z ddl Z ddl Z ddlZddlZddlZddlmZddlmZddlmZddlmZmZmZmZmZmZmZmZmZm Z m!Z!m"Z"m#Z#m$Z$m%Z%m&Z&m'Z'm(Z(m)Z)m*Z*m+Z+ddlm,Z,m-Z-m.Z.dd l/m0Z0m1Z1m2Z2m3Z3m4Z4d Z5d Z6dZ7dZ8dZ9dZ:dZ;dZZ?ej=dZ@ejAjBdre jAjCddejDdeEde$jFjGejHddZIddZJdd ZKd!d"ZLd#d$ZMdd%d&ZNd'd(ZOd)d*ZPd+d,ZQd-d.ZRd/d0ZSd1d2ZTGd3d4d4eUZVGd5d6d6e,ZWGd7d8d8e,ZXGd9d:d:e,ZYd;d<ZZed=d>Z[dd?d@Z\dAdBZ]dCdDZ^iZ_dEdFZ`e`e2ja_bece dGdsy8ddldZeddlfZgeejhjiegjjeejhjidHkrekdIWnekk rYn8XdJdKZle2jmZnGdLdMdMeoZpGdNdOdOe2jmZqeqe2_mdPdQZrejsfdRdSZtdTdUZudVdWZvGdXdYdYeoZwdZd[Zxd\d]Zydd_d`ZzdadbZ{dcddZ|ddedfZ}dgdhZ~didjZdkdlZdmdnZdodpZdqdrZdsdtZdudvZdwdxZdydzZd{d|Zd}d~ZddZddZddZddZdddZddZddZddZddZdddZddZddZddZddZe&jdddZddZddZGdddZddZddZddZddZejejsfddZddZe&je*jdejfddZddZddZddZdddZddZddZdS))print_functionN)ArgumentParser)contextmanager)datetime)anomalyauth capabilitiesconfigconfig_handlers constantsdoctorerrorsfetch http_utils ipv6_supportkcarelibcare log_utilsplatform_utils process_utilsselinux server_infoserverid update_utilsutils) KcareErrorNotFoundSafeExceptionWrapper) HTTPErrorURLErrorhttplibjson_loads_nstr urlencodecZv312h24h48htestz./etc/sysconfig/kcare/freezer.modules.blacklistz/usr/libexec/kcare/kcdoctor.sh latest.v3 latest.v2z /etc/sysconfig/kcare/sysctl.conf z$==BLACKLIST== (.*)==END BLACKLIST== z'(kpatch.*|ksplice.*|kpatch_livepatch.*)z/usr/libexec/kcare/pythonignore)categorycCsDt}tjjtr@ttd}x|D]}|j|jq"W|j|S)Nr) setospathisfileFREEZER_BLACKLISTopenaddrstripclose)resultfliner;./usr/libexec/kcare/python/kcarectl/__init__.pyget_freezer_blacklistSs   r=cCsB|jd}|r(dj|d||dg}ndj|d|dg}|S)N.rrr?)splitjoin)ptypefilenameZ name_partsr;r;r< _apply_ptype]s  rDcCsJt|tjt_t|tjt_t|tjt_t|tjt_t|tjt_dS)N)rDr PATCH_BIN PATCH_INFOBLACKLIST_FILE FIXUPS_FILE PATCH_DONE)rBr;r;r< apply_ptypefs rJcCstj\}}}d}t|trbt|t rbyd|jtj|j|jf}Wqt t fk r^YqXnPt|t t t frt|t rd|}n*t|t r|jpt|j}|jpd|j}tj}tjtj|d|dt|dt||djtj|dt|ddd S) Nz[Errno %i] %s: '%s'z%srr__name__dattempts)Z agent_versionZpython_versiondistroZdistro_versionerrordetails tracebackrN)sysexc_info isinstanceOSErrorr errnor0strerrorrCAttributeError TypeErrorKeyErrorIOErrorretypetypeinnerrQr get_distror VERSIONget_python_versiongetattrstrrArRZ format_tb)r]valuetbZdetails_sanitizedrOr;r;r< format_exception_without_detailsns*  rgc Csvtjr dStjt}tjtjtj |}tj dd|}t j |t j}yt j|Wntk rpYnXdS)Nz/api/kcarectl-tracez?trace=)r UPDATE_FROM_LOCALjsondumpsrgrnstrbase64Zurlsafe_b64encodeZbstrget_patch_server_urlrZ http_requestrZget_http_auth_stringZ urlopen_base Exception)ZtraceZ encoded_traceurlZrequestr;r;r<send_excs rpcCstj}|dkr tj|ddStjtj}|dkrBtjdtjdttjd&}tj |j dtj |j dWdQRX|rt j |y |Wn*t k rtjjdtjdYnXtjddS)z Run func in a fork in an own process group (will stay alive after kcarectl process death). :param func: function to execute :return: rNarzWait exception)r0forkwaitpidsetsid_exitr7r4r ZLOG_FILEdup2filenotimesleeprnrkcarelog exception)funcrzpidfdr;r;r< nohup_forks(      rcCstjjtjd}tjj|rtt|dH}y,t|j}|t j t j krRt ||Wnt k rhYnXWdQRXtj|tjdS)aCheck the fact that there was a failed patching attempt. If anchor file not exists we should create an anchor with timestamp and schedule its deletion at $timeout. If anchor exists and its timestamp more than $timeout from now we should raise an error. z.kcareprev.lockr.N)r0r1rAr PATCH_CACHEr2r4intreadr SUCCESS_TIMEOUTryPreviousPatchFailedException ValueErrorr atomic_write timestamp_str)Zanchor_filepathZafile timestampr;r;r< touch_anchors   rcCsxytjtjjtjdWntk r.YnXtd|tj j yt ddWn t k rrt jjdYnXdS)z See touch_anchor() for detailed explanation of anchor mechanics. See KPT-730 for details about action registration. :param state_data: dict with current level, kernel_id etc. z.kcareprev.lockdone)reasonzCannot send update info!N)r0remover1rAr rrVregister_actionrget_loaded_modulesclearget_latest_patch_levelrnrr{r|) state_datar;r;r< commit_updates  rcCs(tjtjjtjdtj||dddS)NpatchesrK)Z exclude_path) rclean_directoryr0r1rAr rrget_cache_path)khashZplevelr;r;r< clear_cachesrcCs>tjpd}dj||g}tjd|f}|r2||f7}tjj|S)Nnone-modules)r PREFIXrAr rr0r1)rfnameprefixZ module_dirr8r;r;r<get_current_level_paths    rcCstjt|dt|dddS)NlatestT)Z ensure_dir)rrrrd)r patch_levelr;r;r<save_cache_latestsrc CsVt|d}tjj|rRy"tt|djj}tj ||St t fk rPYnXdS)Nrr.) rr0r1r2rr4rstriprLegacyKernelPatchLevelrrZ)rZpath_with_latestplr;r;r<get_cache_latests   rc@s eZdZdS)CertificateErrorN)rL __module__ __qualname__r;r;r;r<r srcs eZdZdZfddZZS)UnknownKernelExceptionzunknown kernelc s6djtjdtjtj}tt|j |f|dS)NzLNew kernel detected ({0} {1} {2}). There are no updates for this kernel yet.r) formatrr`platformreleaserget_kernel_hashsuperr__init__)selfkwargsmsg) __class__r;r<rszUnknownKernelException.__init__)rLrrstatusr __classcell__r;r;)rr<rsrcs(eZdZdZfddZddZZS)ApplyPatchErrorzpatch apply errorcsFtt|j||||_||_||_||_tjd|_ t j |_ dS)Nr) rrrcode freezer_stylelevel patch_filerr`rOrr)rrrrrargsr)rr;r<rszApplyPatchError.__init__c Cs0dj|j|j|j|j|jdjdd|jDS)Nz0Unable to apply patch ({0} {1} {2} {3} {4}, {5})z, cSsg|] }t|qSr;)rd).0ir;r;r< -sz+ApplyPatchError.__str__..)rrrrrOrrAr)rr;r;r<__str__&szApplyPatchError.__str__)rLrrrrrrr;r;)rr<rs rcs(eZdZdZfddZddZZS)rzprevious patch failedcs"tt|j||||_||_dS)N)rrrranchor)rrrrr)rr;r<r5sz%PreviousPatchFailedException.__init__cCsd}|j|j|jS)NzIt seems, the latest patch, applying at {0}, crashed, and further attempts will be suspended. To force patch applying, remove `{1}` file)rrr)rmessager;r;r<r:sz$PreviousPatchFailedException.__str__)rLrrrrrrr;r;)rr<r2s rcCstjdj|}yztj|}tjtj|j}t |d}|dkrRtj dn8|dkrftj dn$|dkrztj dntj d j||St k r}zt j ||WYdd}~XnXd S) Nz"/nagios/register_key.plain?key={0}rrzKey successfully registeredrzWrong key format or sizerrz!No KernelCare license for that IPzUnknown error {0}r?)rget_registration_urlrrurlopenr data_as_dictrkrr print_wrapperrrprint_cln_http_error)keyroresponseresrer;r;r<!set_monitoring_key_for_ip_licenseCs      rc cs>tjrtjtjddz dVWdtjr8tjtjddXdS)NT)shell)r ZBEFORE_UPDATE_COMMANDr run_commandZAFTER_UPDATE_COMMANDr;r;r;r< execute_hooksWs  rcCst}|j}|j}tj}|dkrht|tjtj t j |t t j|d}tjdtjtj|njtjdtjt|tjdt|tjtjtjtj tjt j tj|tjt jdS)a1 The output will consist of: Ignore output up to the line with "--START--" Line 1: show if update is needed: 0 - updated to latest, 1 - update available, 2 - unknown kernel 3 - kernel doesn't need patches 4 - no license, cannot determine Line 2: licensing message (can be skipped, can be more then one line) Line 3: LICENSE: CODE: 1: license present, 2: trial license present, 0: no license Line 4: Update mode (True - auto-update, False, no auto update) Line 5: Effective kernel version Line 6: Real kernel version Line 7: Patchset Installed # --> If None, no patchset installed Line 8: Uptime (in seconds) If *format* is 'json' return the results in JSON format. Any other output means error retrieving info :return: ri)Z updateCodeZ autoUpdateZeffectiveKernelZ realKernelZloadedPatchLevelZuptimelicensez --START--z LICENSE: N)_patch_level_infor applied_lvlr license_infordr AUTO_UPDATEr kcare_unamerrrrZ get_uptimerrrirj)fmtpliZ update_codeZ loaded_plZlicense_info_resultZresultsr;r;r< plugin_infocs,     rc Cs^tj}ytdd}Wntk r4tjr0dSdSX|dkrBdS||krNdStjrZdSdS)Ninfo)rrrrr)rloaded_patch_levelrrr IGNORE_UNKNOWN_KERNELrZstatus_gap_passed) current_levelZlatest_patch_levelr;r;r<get_update_statussrcCs2tjdd\}}|dkr*|jdr*dSdSdS)NrrZ CloudLinuxz7.extrarK)rr` startswith)rOversionr;r;r<edf_fallback_ptypesrcCsl|j|jf}tj||}tj||j|_|jjtj tj d|tkrZ|jj ddt|<|j rh|j dS)zFunction remembers IP address of host connected to and uses it for later connections. Replaces stdlib version of httplib.HTTPConnection.connect rNrr)hostZportCONNECTION_STICKY_MAPgetsocketZcreate_connectionZtimeoutsockZ setsockoptZ IPPROTO_TCPZ TCP_NODELAYZ getpeername _tunnel_hostZ_tunnel)rZaddrZ resolved_addrr;r;r<sticky_connects  rZHAS_SNIz0.13z%No pyOpenSSL module with SNI ability.cGsdS)NTr;)rr;r;r<dummy_verify_callbacksrc@s,eZdZddZddZddZddZd S) SSLSockcCs||_d|_dS)Nr) _ssl_conn_makefile_refs)rrr;r;r<rszSSLSock.__init__cGs&|jd7_tj|jf|ddiS)Nrr7T)rrZ _fileobjectr)rrr;r;r<makefileszSSLSock.makefilecCs"|j r|jr|jjd|_dS)N)rrr7)rr;r;r<r7s z SSLSock.closecGs |jj|S)N)rsendall)rrr;r;r<rszSSLSock.sendallN)rLrrrrr7rr;r;r;r<rsrc@seZdZddZdS)PyOpenSSLHTTPSConnectioncCstjj|tjjtjj}|jtjjtjj Bt j rJ|j tjj tn|j tjjt|jtjj||j}|j|jp|j}|j|j|jt j rt|j|t||_dS)N)r!HTTPConnectionconnectOpenSSLZSSLZContextZ SSLv23_METHODZ set_optionsZ OP_NO_SSLv2Z OP_NO_SSLv3r CHECK_SSL_CERTSZ set_verifyZ VERIFY_PEERrZ VERIFY_NONEZset_default_verify_pathsZ ConnectionrZset_connect_staterrZset_tlsext_host_nameencodeZ do_handshakematch_hostnameZget_peer_certificater)rZctxZconnZ server_hostr;r;r<rs  z PyOpenSSLHTTPSConnection.connectN)rLrrrr;r;r;r<rsrcCstjr&tj||}tjtj|ddS|dk}tjo6|}xrd|fd|fdgD]Z\}}t j |||d} t j | |d} |rdj | } tj|t ||d | }d } |s|rt || kr|rd nd } tjd j | qNyxtjtj|dd} tjrJtj| rJtj| }tj|}|r.tjdj |ddntjddd|rJ|j| Stk r}z>|sl|r|jdks|jdkrtjdj |wNWYdd}~XqNXqNWdS)NF) check_license latest.v1 latest.v2T)secure_boot_info perf_metrics) b64_encodingzinfo={0}?iXzsecure boot infoz perf metricsz/Check-in URL param is too large, discarding {0}z:Automatic kernel anomaly report uploaded successfully: {0}) print_msgz$Failed to send kernel anomaly reportizCCheck-in request failed with error: {0}, retrying with reduced info)rr)FF)rr)r rhrZget_kernel_prefixed_urlrZwrap_with_cache_keyr urlopen_authZSEND_PERF_METRICSrZencode_checkin_payloadrstickyfylenrlogwarnZKERNEL_ANOMALY_REPORT_ENABLErZdetect_anomalyprepare_kernel_anomaly_reportsend_data_packageloginforemove_archiverr)rrrmoderorZ perf_enabledrrZsinfoZ request_paramZmax_url_lengthZ discard_infor8 data_package upload_nameexr;r;r<_fetch_patch_level_requestsB       $r c Cs8tj}tjdk r$tj|ttjSxtD]}yt||||}tj |j t t j |jj}tjdj||dd|r|jdrt|}|jdg}tj|stjdtj||d|d|d Stj|t|Stk rYq,tk r(}z|jd krtd WYdd}~Xq,Xq,WtdS)Nz;fetch patch level, reason: {0}, kernel latest response: {1}F)r{r zeLatest KernelCare patchset is incompatible with the current kernecare package version, please upgraderbaseurlrzKC licence is required)rr) rrr PATCH_LEVELrr PATCH_LATESTr r Zset_feature_flags_from_headersheadersupdate_all_kmod_paramsrrkrrrrrrr"rr Zhas_kc_capabilitiesrCapabilitiesMismatchZKernelPatchLevelrrrrr) rr rrrrZ latest_infoZrequired_capabilitiesr r;r;r<fetch_patch_level;s2     rcCs<|jt|tj}tjjdj|ytj |ddddSt k r^tjjdj|dSt k r}ztjj dj|t |WYdd}~XnX|jt|tjtj}tjjdj|ytj |ddWnbt k rtjjdj|dStk r6}ztjjd j|t |WYdd}~XnXdS) NzProbing patch URL: {0}FHEAD)rmethodTz{0} is not available: 404zFHEAD request for {0} raised an error, fallback to the GET request: {1})rz{0} is not available: {1})file_urlrDr rErr{rrrrrrndebugrdr ZSIGr )rrBZbin_urlr ror;r;r< probe_patch\s(**rcCsF|tjkr|jtj}n |j|}|j|}tj||tjtj |dS)N)Z hash_checker) r KMOD_BINZkmod_urlr cache_pathrZ fetch_urlr USE_SIGNATUREZget_hash_checker)rnameroZdstr;r;r<fetch_and_verify_kernel_fileus    r!c@s>eZdZdddZddZddZdd Zd d Zd d ZdS) PatchFetcherNcCs ||_dS)N)r)rrr;r;r<rszPatchFetcher.__init__cCs t|j|S)N)r!r)rr r;r;r<_fetchszPatchFetcher._fetchcCsr|jjtj}|jjtj}|jjtj}|jjtj}tdd||||fDopt j j |dkopt j j |dkS)Ncss|]}tjj|VqdS)N)r0r1r2)rr1r;r;r< sz0PatchFetcher.is_patch_fetched..r) rrr rIrErFr rallr0r1getsize)rZpatch_done_pathZpatch_bin_pathZpatch_info_pathZ kmod_bin_pathr;r;r<is_patch_fetchedszPatchFetcher.is_patch_fetchedcCs4|jdkrtd|js|jS|jr6tjd|jStjdt|jtjrytj |jj t j dd}Wnt k r~Yn(X|jjdd}|r|jjtj||_y|jt j Wn0t k rtdj|jt jpdd d YnX|jt j|jtj|jtj|jjt jd d d tjtj |jS)Nz+Cannot fetch patch as no patch level is setzUpdates already downloadedzDownloading updatesr)rz KC-Base-UrlzfThe `{0}` patch level is not found for `{1}` patch type. Please select valid patch type or patch leveldefaultzpatch level not found)rwb)r )!rrr'rrrUrrrrrr rErrrupgraderrkr#rr PATCH_TYPErFr rextract_blacklistrrrIrrestore_selinux_contextr)rresprr;r;r< fetch_patchs:      zPatchFetcher.fetch_patchcCsJt|jjtjdj}|rFtj|}|rFtj |jjtj |j ddS)Nr.r) r4rrr rFr BLACKLIST_REsearchrrrGgroup)rZbufZmor;r;r<r-s  zPatchFetcher.extract_blacklistcCs|dkr dSyt|tj}Wntk r0dSX|jjdd}|rT|jtj|}|j tj}t |d}t dd|j D}WdQRXx|D]}t||qWt jtjdS)z Download fixup files for defined patch level :param level: download fixups for this patch level (usually it's a level of loaded patch) :return: None Nz KC-Base-Urlr.cSsg|] }|jqSr;)r)rfixupr;r;r<rsz-PatchFetcher.fetch_fixups..)r!r rHrrrr+rrkrr4r/ readlinesrr.r r)rrr/rZ fixups_fnamer9fixupsr4r;r;r< fetch_fixupss    zPatchFetcher.fetch_fixups)N) rLrrrr#r'r0r-r7r;r;r;r<r"~s   )r"cCs8t}tj|j|jtjkr*tjdn tjddS)Nrr) rrrrrPLIPATCH_NEED_UPDATErSexit)rr;r;r< kcare_checks    r;c Cs\t}t|}y tj}Wntk r2i}YnXtj}d}|dk r\tj|dj d}tj }|j dg}t t j|dd}t |}dd|D} t t j| d d} td d |D} || } tj} | st jd n t jd t jdj|t jdj||dkr t jdj|| dkr:t jdj| | dkrNt jdt jddS)NZUnknowntsz%Y-%m-%drz kpatch-cve)Z cve_fieldcSs"g|]}|jdgD]}|qqS)r)r)rrecpatchr;r;r<rsz%show_generic_info..Zcvecss|]}t|jdgVqdS)rN)rr)rr=r;r;r<r$sz$show_generic_info..z$KernelCare live patching is disabledz"KernelCare live patching is activez - Last updated on {0}z - Effective kernel version {0}rz* - {0} kernel vulnerabilities live patchedz- - {0} userspace vulnerabilities live patchedz% - This system has no applied patchesz(Type kcarectl --patch-info to learn more)r_kcare_patch_info_jsonrZlibcare_patch_info_basicrrZ get_staterZ fromtimestampZstrftimerrrrZextract_unique_cvessumrrr)r kcare_info libcare_infostateZ latest_updateZeffective_versionZkernel_patchesZkernel_vulnerabilitiesZkernel_patches_countZuserspace_patchesZuserspace_vulnerabilitiesZuserspace_patches_countZtotal_patches_countrr;r;r<show_generic_infos>         rDFc Csytdtjd}|st|jtj}tjt j |j }|rgi}}x>|j dD]0}tj |}|rxd|krx|j|qR|j|qRW||d<tj|}tj|WnJtk r}ztj||jdSd}~Xntk rtjdYnXd S) z Retrieve and output to STDOUT latest patch info, so it is easy to get list of CVEs in use. More info at https://cloudlinux.atlassian.net/browse/KCARE-952 :return: None r)rpolicyz z kpatch-namerrNzNo patches availabler)rr POLICY_REMOTErrr rFrrkrrrr@rappendupdaterirjrrrrro) is_jsonrro patch_inforr8chunkdatarr;r;r<kcare_latest_patch_infos,      rMcCsd|ji}|jdk rt|}g}x>|jdD]0}tj|}|rRd|krR|j|q,|j|q,W||d<tj }|r||dnd|d<|S)Nrz z kpatch-namerrunknown) rr_kcare_patch_infor@rrrGrHrZread_dumped_kernel_patch_level)rr8rJrrKrLZsaved_patch_levelr;r;r<r?4s     r?cCsTtj}tj||jtj}tjj|s2t dddt |dj }|rPt j d|}|S)NzvCan't find information due to the absent patch information file. Please, run /usr/bin/kcarectl --update and try again.zpatch info not found)rr.rK)rrrrr rFr0r1r2rr4rr1sub)rrrrr;r;r<rOHs  rOcCsZt}|s>|jdkr tj|j|jdkr.dStjt|ntjtjt |dddS)NrT)Z sort_keys) rrrrrrrOrirjr?)rIrr;r;r<rJWs   rJcCs:tjd|g}tj|}tj}d}tj||tj||kS)Nz file-infozkpatch-build-time)r KPATCH_CTLr check_outputr _patch_infoZget_patch_value)new_patch_filerZnew_patch_infoZcurrent_patch_infoZbuild_time_labelr;r;r< is_same_patchcs   rUcCsL|dkr dS|r||krdS||kr(dStjtj|tj}t|sHdSdS)NrFT)rrrr rErU) applied_level new_levelrTr;r;r<kcare_need_updateks rXcCsptjrltjjtotjttjs6tj j dj tdSt j dddtgdd\}}}|dkrltj j dj |dS) Nz-File {0} does not exist or has no read accessz /sbin/sysctlz-qz-pT) catch_stdoutrz%Unable to load kcare sysctl.conf: {0})r ZUPDATE_SYSCTL_CONFIGr0r1r2 SYSCTL_CONFIGaccessR_OKrr{warningrrr)r_r;r;r< update_sysctl}sr_c stjjtsttdjtjttjs>tj j dj tdSttdj}|j }|j dx,|D]$tfdd|Dsb|jqbWx|D]}|j|dqW|jWdQRXdS) z*Update SYSCTL_CONFIG accordingly the editsrqzFile {0} has no read accessNzr+rc3s|]}j|VqdS)N)r)rr.)r:r;r<r$sz#edit_sysctl_conf.. )r0r1r2rZr4r7r[r\rr{r]rr5seekanywritetruncate)rrGZsysctllinesrqr;)r:r<edit_sysctl_confs     rfcCs.x(|D] }tj|rtdj|ddqWdS)NzDDetected '{0}' kernel module loaded. Please unload that module firstzconflicting kernel module)r)CONFLICTING_MODULES_REmatchrr)rmoduler;r;r<detect_conflicting_moduless   rjcCsdjtjS)Nz/lib/modules/{0}/extra/kcare.ko)rrZget_system_unamer;r;r;r<get_kcare_kmod_linksrkc CsXtdd}tjtj|tj}tjj|s.dSt |d}|j dddkSQRXdS)Nr)rrbs~Module signature appended~ i) rrrrr rr0r1r2r4r)rZ kmod_fileZvfdr;r;r<kmod_is_signeds    rncs4tjddkrdSddg}tfdd|DS)Nz /proc/keysZ(12ff0613c0f80cfba3b2f8eba71ebc27c5a76170Z(69a6d9eed3f620d5c2e13a1d211c46510a5ad9f5c3s|]}|kVqdS)Nr;)rr) system_keysr;r<r$sz'kcare_certs_enrolled..)rZ try_to_readrb)Z kcare_keysr;)ror<kcare_certs_enrolleds  rpcKsdd|g}x&|jD]\}}|jdj||qWtj|dd\}}}|dkr`tdj||dddS) Nz /sbin/insmodz{0}={1}T)rYrzLUnable to load kmod ({0} {1}). Try to run with `--check-compatibility` flag.zkmod load error)r)itemsrGrrrr)Zkmodrcmdrrerr^r;r;r< load_kmods rscCsTtjr,tdkrtdtdkr,tdtjsDtjsDtjrPtddddS)NFz4Secure boot is enabled. Not supported by KernelCare.z|}YnXtj rbt j j tj  rbt j tj t}t|tfdd|jD}t|f|tdS)Nc3s"|]\}}|kr||fVqdS)Nr;)rkv)available_kmod_paramsr;r<r$!sz"load_kcare_kmod..)rkrrr rshutilcopyrnr rr0r1rrrr}dictrqrs update_depmod)rrr{Z kcare_fileZ kmod_paramsr;)rr<load_kcare_kmods   rcCsXdg}|dk r|jd|gtj|ddd\}}}|rTtjdjdj|||dddS) Nz /sbin/depmodz-aT)rYruz%Running of `{0}` failed with {1}: {2} F)r)extendrrrrwrrA)unamerrrr^stderrr;r;r<r's rcCs8tjd|gdd\}}}|dkr4tdj||dddS)Nz /sbin/rmmodT)rYrzUnable to unload {0} kmod {1}zkmod unload error)r)rrrr)modnamerr^r;r;r< unload_kmod3srcCsTg}xJdg|D]<}tj||dj|}tjj|rt||jdj|qW|S)NZvmlinuxz fixup_{0}.koz fixup_{0})rrrr0r1rrsrG)rrrZloadedmodZmodpathr;r;r< apply_fixups9s rc CsDx>|D]6}y t|Wqtk r:tjjd|YqXqWdS)Nz$Exception while unloading module %s.)rrnrr{r|)r6rr;r;r< remove_fixupsCs   rcCs|r |}n6tjrtj}n(tj|r2d|tjdfSd|tjdfSdddddd}|j}||krj||}ntdj||tjdd d ||tjdfS) NZfreeze_conflictTr(FZ freeze_noneZ freeze_all)ZNONEZNOFREEZEZFULLZFREEZEZSMARTz3Unable to detect freezer style ({0}, {1}, {2}, {3})zfreezer style detection error)r)r Z PATCH_METHODr= intersectionupperrr)freezerrrZpatch_method_mapr;r;r<get_freezer_styleKs&  rrKcs|||dtdtj}tj}t|t||}tj||tj}t ||dj |tj t j tj|} d|k} | otj||} |dk } | ot|otj| } j|| d| rtddS| rtdt|||}tdt|td t|| r"td tdd } | sz5Patch level {0} applied. Effective kernel version {1}waitcstS)N)rr;)rr;r<szkcare_load..)rz)"rrrrrjrrr rErrr,rrZ parse_unameZis_kmod_version_changedrUZkcare_update_effective_versionrHrkpatch_ctl_unpatchrrrrkpatch_ctl_patchr_rrrrZtouch_status_gap_filerr)rrr r use_anchorrrrr descriptionZ kmod_loadedrZ patch_loadedZ same_patchr6r;)rr< kcare_loadmsR              rc Cstjg}tj||tj}tjj|r2|j d|g|j dd|g|j d|dg|j |t j |dd\}}}|dkrt ||||dS)Nz-br>z-dz-mrT)rY)r rQrrr rGr0r1rrrGrrr) rrrrrrZblacklist_filerr^r;r;r<rs  rcCs^tjtjdd|dgddd\}}}|dkrZtjdj||ddtd j|t|d d dS) Nrz-mrT)rYruz4Error unpatching, kpatch_ctl stdout: {0} stderr: {1}F)rzError unpatching [{0}] {1}z unpatch error)r) rrr rQrrwrrrd)rrr|rr;r;r<rs  rcCs8||d<ttj|d<tjtjjtjdt |dS)Nactionr<z kcare.state) rryrrr0r1rAr rrd)rrr;r;r<rsrcCspd}tjj|sdSxVtj|D]H}tjj||dd}tjj|sDq tj|}||kr tj|t|q WdS)Nz/usr/lib/modules/z weak-updateszkcare.ko) r0r1isdirlistdirrAislinkreadlinkunlinkr) kmod_linkZ modules_pathentryZ sym_link_pathZ target_pathr;r;r<update_weak_moduless    rc CsJtj}t}y|j|Wn8tk rT}z|sDtdj|ddWYdd}~XnXtj}t||}t d|kr|dk }|rt tj ||}t j tjdd|dgddd \} } } t|| dkrtjd j| | d d td j| t|ddtjtjtdtdtdt} tjj| r4tj| t| WdQRXdS)NzUnable to retrieve fixups: '{0}'. The unloading of patches has been interrupted. To proceed without fixups, use the --force flag.zfixups retrieval error)rrrz-mrT)rYruz4Error unpatching, kpatch_ctl stdout: {0} stderr: {1}F)rzError unpatching [{0}] {1}z unpatch errorr)countdelay) rrr"r7rnrrrrrrrrrr rQrrrwrdrZretryrZ check_excUNLOAD_RETRY_DELAYrrkr0r1r2rr) rforcerpferrrrZ need_unpatchr6rr|rrr;r;r< kcare_unloads:    rcCs8t}|rt|S|jdkr"|jS|jdk r4tjSdS)Nr)r_kcare_info_jsonrrrrrS)rIrr;r;r<rAs  rAcCsRd|ji}|jdk r>|jtjtj|jtj|jd|j |d<t j |S)Nrzkpatch-descriptionz kpatch-state) rrrHrrrrSZparse_patch_descriptionrrCrirj)rr8r;r;r<rs    rc@s$eZdZdZdZdZdZddZdS)r8rrrrrcCs"||_||_||_||_||_dS)N)rr remote_lvlrrC)rrrrrrCr;r;r<r%s z PLI.__init__N)rLrrrr9PATCH_UNAVALIABLEPATCH_NOT_NEEDEDrr;r;r;r<r8s r8c Cstj}ytdd}|rJt||r6tjdd}}}qxtjdd}}}n.|dkrftjdd}}}ntjd d}}}t|||||}Wnltk rtj }t j rd j t j t jdtj}nd j t jdtjtj}t||ddd }YnX|S) Nr)rz*Update available, run 'kcarectl --update'.ZappliedzThe latest patch is applied.rz(This kernel doesn't require any patches.ZunsetzDNo patches applied, but some are available, run 'kcarectl --update'.zuInvalid sticky patch tag {0} for kernel ({1} {2}). Please check /etc/sysconfig/kcare/kcare.conf STICKY_PATCH settingszLNew kernel detected ({0} {1} {2}). There are no updates for this kernel yet.Z unavailable)rrrrXr8r9rrrrr STICKY_PATCHrrr`rrr)Zcurrent_patch_levelZnew_patch_levelrrrCrr;r;r<r-s8   rc Csd}yXtj}td|fd|fg}tjdj|}tj|}tj tj |j }t |dSt k r}ztj||d Sd}~XnZtk r}ztj||d Sd}~Xn0tk r}ztjdj|d Sd}~XnXdS) z Request to tag server from ePortal. See KCARE-947 for more info :param tag: String used to tag the server :return: 0 on success, -1 on wrong server id, other values otherwise N server_idtagz/tag_server.plain?{0}rrzInternal Error {0})r get_serveridr#rrrrrrrrkrrrrrr rnrw) rrorZqueryrrrZueZeer;r;r< tag_server_s"    rcCstjd}tjdj|t}tj}y:tj ||j }tj t j ||j tj|j ||j }Wn2tk r}ztjdj|WYdd}~XnXtjd|tjgdd\}}}|rtdj||dd WdQRXdS) Nz doctor.shz#Requesting doctor script from `{0}`z3Kcare doctor error: {0}. Fallback to the local one.ZbashT)ruzScript failed with '{0}' {1}zdoctor script failed)r)rrmrZlogdebugrKCDOCTORtempfileZNamedTemporaryFilerZfetch_signaturer Z save_to_filerrZcheck_gpg_signaturernrwrrrZget_patch_serverr)Z doctor_urlZdoctor_filenameZ doctor_dstZ signaturerrr^rr;r;r<kcdoctorzs   "rc CsBtjdjt}ytj|Wntk r2dSXtjddS)Nz{0}-new-versionFzwA new version of the KernelCare package is available. To continue to get kernel updates, please install the new versionT) rrmrEFFECTIVE_LATESTrrr rr)ror;r;r<check_new_kc_versionsrc Cstj}t|}|tjkp*|tjko*|dk}yt||}Wntjk r}z.|dkrXt j t |t j dtj }WYdd}~Xn<t k r}z |rnt jjdj|WYdd}~XnX|tjkr|} n@|} |dkr|tj krtj|d} n|tjkr |} ntd| S)a Get patch level to apply. :param reason: what was the source of request (update, info etc.) :param policy: REMOTE -- get latest patch_level from patchserver, LOCAL -- use cached latest, LOCAL_FIRST -- if cached level is None get latest from patchserver, use cache otherwise :param mode: constants.UPDATE_MODE_MANUAL, constants.UPDATE_MODE_AUTO or constants.UPDATE_MODE_SMART :return: patch_level string Nz#Using previously downloaded patcheszUnable to send data: {0}rz9Unknown policy, choose one of: REMOTE, LOCAL, LOCAL_FIRST)rrrr rFZPOLICY_LOCAL_FIRSTrrrrrrdZ POLICY_LOCALrnr{r]rrr) rrEr rZ cached_levelZconsider_remote_exZ remote_levelrr rr;r;r<rs2  $    rcCs|dkr dS|dkrdn|t_ttddtjrtjtjdtjdkrntjrntjpXt }t dd d j |ft j d j |ntdj |dddS)Nedfr(rKZprobe)r)r,rvrfs.enforce_symlinksifownerfs.symlinkown_gidzfs.enforce_symlinksifowner=1zfs.symlinkown_gid={0}z'{0}' patch type selectedz/'{0}' patch type is unavailable for your kernelzpatch type unavailable)r)rvr)rr)r r,rrr update_configrZ is_cpanelZ FORCE_GID CPANEL_GIDrfrrrr)rBZgidr;r;r<update_patch_types rZkernelc $Cshttj|tjkrtytd||d}WnRtk r~}z6|tjtj fkrltj rlt |}t j j|dSWYdd}~XnXtj}|tjkrtj rdSt|}|jt||dst jddSy(tjtjdddtjtjdd dWn"tk rt j jd YnXtj}t(|j|t|||||tj kd WdQRXtj|t ||dS) ax :param mode: constants.UPDATE_MODE_MANUAL, constants.UPDATE_MODE_AUTO or constants.UPDATE_MODE_SMART :param policy: REMOTE -- download latest and patches from patchserver, LOCAL -- use cached files, LOCAL_FIRST -- download latest and patches if cached level is None, use cache in other cases :param freezer: freezer mode rH)rrEr N)rVrWz%No updates are needed for this kernelrz kcore*.dump)Zkeep_nZpatternz kmsg*.logz#Error during crash reporter cleanup)r)!rxr r,r rFrrrUPDATE_MODE_AUTOUPDATE_MODE_SMARTrrdrr{r]rrrr"r0rXrrrrrnr|rrr7rZdump_kernel_patch_levelr) rr rErrrrrrr;r;r< do_updates<      " rcCstttjttjptjttjp$tjf}|dkr@tdddtjrLtjS|t j krptjp`tj}tjpltj}n tj}tj}|r|S|rd|SdS)NrzInvalid configuration: conflicting settings STICKY_PATCH, [AUTO_]UPDATE_DELAY or [AUTO_]STICKY_PATCHSET. There should be only one of themzconflicting sticky settings)rzrelease-) r@boolr rZ UPDATE_DELAYZAUTO_UPDATE_DELAYZSTICKY_PATCHSETZAUTO_STICKY_PATCHSETrr UPDATE_MODE_MANUAL)r rrZpatchsetr;r;r< get_stickys&  rcCs |d|S)Nr>r;)rrr;r;r< _stickyfy<srcCs t|}|s|S|dkr"t||Stj}|sDtjjdtjdyt j t j dj |}Wn:tk r}ztj||jtjdWYdd}~XnXtjtj|j}t|d}|dkrt|d |S|d kr|S|d krtjjd tjdtjjd |dtjddS)z Used to add sticky prefix to satisfy KCARE-953 :param file: name of the file to stickify :return: stickified file. KEYzHPatch set to STICKY_PATCH=KEY, but server is not registered with the keyrz!/sticky_patch.plain?server_id={0}rNrrrrrrzEServer ID is not recognized. Please check if the server is registeredzError: rrrrr?r)rrrrrr{rrSr:rrrrrrrrorrrkrr)filer srrrrrr;r;r<r@s2       rc Csg}|s dS|jd}|d}|dd}|jd}||krLtdt||s`|j|jkS|dkrt|jdn>|jd s|jd r|jtj|n|jtj|j d d x|D]}|jtj|qWtj d d j |dtj } | j |S)zhMatching according to RFC 6125, section 6.4.3 http://tools.ietf.org/html/rfc6125#section-6.4.3 Fr>rrN*z,too many wildcards in certificate DNS name: z[^.]+zxn--z\*z[^.]*z\Az\.z\Z)r@rrreprlowerrGrreescapereplacecompilerAZ IGNORECASErh) ZdnhostnameZ max_wildcardsZpatspiecesZleftmostZ remainderZ wildcardsZfragZpatr;r;r<_dnsname_matchls(     rc Cs g}xBt|jD]2}|j|}|jdkrddt|jdD}qW|sTtdg}x0|D](\}}|dkr^t||r|dS|j|q^W|s|j j }t||rdS|j|t |dkrt dj |d jtt|n,t |dkrt d j ||d nt d dS) NZsubjectAltNamecSsg|]}|jjddqS)ryr)rr@)ritr;r;r<rsz"match_hostname..,ztempty or no certificate, match_hostname needs a SSL socket or SSL context with either CERT_OPTIONAL or CERT_REQUIREDZDNSrz(hostname {0} doesn't match either of {1}z, zhostname {0} doesn't match {1}rz=no appropriate commonName or subjectAltName fields were found)rangeZget_extension_countZ get_extensionZget_short_namerdr@rrrGZ get_subjectZ commonNamerrrrAmapr) ZcertrZsanrrZdnsnamesrreZcnr;r;r<rs0       rc Cs tddd}|jdddd|jdd d dd|jd d dd|jd dddd|jdddd|jdddd|jdddd|jdddd|jdddd|jdddd|jdd dd|jd!d"dd|jd#d$dd|jd%d&dd|jd'd(d)d|jd*d+dd|jd,d-dd|jd.d/dd|jd0d1dd|jd2d3dd|jd4d5d6d|jd7d8d9d|jd:d;dd|jdd?dd|jd@dAdd|jdBdCdd|jdDdEddFdG|jdHdIdd|jdJdKdd|jdLdMdd|jdNdOdd|jdPdQdd|jdRdSdd|jdTdUdd|jdVdWdd|jdXdYdd|jdZd[d\tdd]d^|jd_d`dd|jdadbdd|j}|jdcddd\d|jdedfdd|jdgdhdd|jdidjd\dd]dk|jdldmdndd]do|jdpdqdr|jdsdtdd|jdudvdwdxdytjs|jdzd{d|d}d]d~|jddd|d}dd~|jdddd|jddddd|jddddd|jdddd|jddddd|jddddd|jddddd|jdddddd|jdddd|jdddd|jddd6dd\dd]d|j}tjtjsFtj dg7_ |j dk rzt t d|j j djtj rvdSdS|js|jrtjrtjt_ntjt_n|jrtjt_|jstjdkrtdtjddStj}|jrtj }n|jrtj!}t"j#|tj$stj%|j&r,t'j(|j)rn|j)dkr\t*|j)t_+tj,tj+dndt_+tj,dd|j-dk rtj,|j-d|j-t_.|j/rd]t_0|j1rd]t_2|j3rdt_4|j5rt5|j6rt7j8dt9n8|j:rtj;dkrtj|j?r&|j?t_@|jAr@t7j8dt9dt_@tj@jBdt_@tj@r~tj@tCkr~t"jDjEdjFtj@djGtC|jHrdt_Id|jHt_J|j=rtK|j=tj;dkrtLt_;t7j8djFtj;pdt9|jMrt'jNtOjM|jPddS|jQr$tQjQdddd}t'jNtPjR|dStStj;|jTrJtTjUtV|jWddS|jXrtYjZtQjQdddd}djF|j[}|j\rt'jN|nRtYj]|}|rt"j^djF|nt"j_ddd|j`rt'jN|n|r|ja|jbr|jPrtbddntbdS|jcrtj,dddS|jdr0tj,dddS|jerHtjf|jedS|jgrZth|jgS|jirjtjji|jkrtj;dkrtj,ddtjjk|jk|jlS|jmrtjjmdkrdSdS|jndk rto|jnS|jprt'jNtjqtr|d|ddk rtsjt|judStj s|jv rd|jvini}|jw r2tsjxS|jy rVtsjzf|dk rVt"j^dă|j{ rvtsjzfdtj|i|n|j} rtsj~t"j^dƃ|j rtsjt"j^dǃ|j rt'jNtsj|j rt'jNtsj|j rtsj rt'jNtsj|j|jdk rj|jdk r,tj p(ttsjj} nddɄ|jj dD} tsjzfdt| i|dk rjt"j^dă|j rtsjzftj|dd˜||j rt'jNt|jPdd} |j rt7j8dt9d} |j r|j} |j rt| tjtjd΍|j> rt| tjdύt"j^dЃ|j rt'jNtj|j r>t| |jdэt"j^d҃|j rld]t_tjtjddӃt| tj|dύ|j rt|jPd|j rtS|j rt|jPd|j rtttjdk rtdS)NZkcarectlz)Manage KernelCare patches for your kernel)Zprogrz--debugrKZ store_true)helprz-iz--infoz]Display information about KernelCare. Use with --json parameter to get result in JSON format.z --app-infozcDisplay information about KernelCare agent. Use with --json parameter to get result in JSON format.z-uz--updatez) into an isolated cache, leaving the default storage untouched. Use together with --lib-update or --userspace-update.)rrrr(rzlibcare-enabledrrrzPlease run as root)r)r)rzTFlag --edf-enabled has been deprecated and will be not available in future releases.rr(zMFlag --test has been deprecated and will be not available in future releases.r(/z(Prefix `{0}` is not in expected one {1}.rzfile:z+edf patches are deprecated. Fallback to {0})rIr)rrr)Zforce_fallbackz)Kernel anomaly report file generated: {0}z0Kernel anomaly report uploaded successfully: {0}z$Failed to send kernel anomaly report)rri)rZYES)rZNOrvr)r,rzUserspace patches are applied.r zUserspace patches are unloaded.zLibcare plugin reloaded.cSsg|]}|jjqSr;)rr)rZptchr;r;r<r5szmain..limit)r rzQFlag --nofreeze has been deprecated and will be not available in future releases.r)r rE)r zKernel is safe)rz=KernelCare protection disabled. Your kernel might not be safe<)rZ add_argumentrZadd_mutually_exclusive_groupr ZLIBCARE_DISABLEDZ parse_argsr Zset_settings_from_config_fileZFLAGSZ has_flagsr/filterr@issubsetquietZ auto_updateZSILENCE_ERRORSr ZPRINT_CRITICALZ PRINT_LEVELZ PRINT_ERRORrZ PRINT_DEBUGrr0getuidprintrSrloggingZINFOZWARNINGDEBUGrZinitialize_loggingZIGNORE_FEATURE_FLAGSZset_feature_flags_from_cacherrZclear_all_cacheZset_patch_levelrdrrZset_sticky_patchrZ nosignaturerZ no_check_certrr~rrtZ edf_enabledwarningswarnDeprecationWarningZ edf_disabledr,ZPREV_PATCH_TYPEZset_patch_typerHrrr(rEXPECTED_PREFIXr{r]rrAZlocalrhZ PATCH_SERVERrrZapp_inforrrirrjrJr Zsend_doctor_reportrZfallbackZkernel_anomaly_reportrrZ archive_pathrrrrZ keep_localrrZenable_auto_updateZdisable_auto_updateZ set_configZupdate_config_from_argsZset_monitoring_keyrZ unregisterrregisterZregister_autoretryrrrrrarcrZset_libcare_statusrZlib_tagZuserspace_statusZget_userspace_update_statusZ lib_updateZdo_userspace_updateZlib_auto_updaterZ lib_unloadZlibcare_unloadZ lib_repluginZlibcare_repluginZlib_inforBZlib_patch_infoZlibcare_patch_infoZ lib_versionZlibcare_server_startedZlibcare_versionZuserspace_updaterlistZget_userspace_mapkeyssortedZuserspace_auto_updaterrAZnofreezerZ smart_updaterrZ UPDATE_POLICYrrrrrrZCHECK_CLN_LICENSE_STATUSryrzrandomZuniformrJrrZlatest_patch_inforMZcheckr;rargvrD) ZparserZexclusive_grouprrrr Zlocal_path_messager Z lib_tag_kwrrr;r;r<mainsr                                               r)r%r&r'r()r)r*)N)N)F)F)N)rKF)rKF)r)Z __future__rrlrirr0rrrrrZsslrSrryrRrZargparser contextlibrrrKrrr r r r r rrrrrrrrrrrrrrrrrZpy23rr r!r"r#rrrr3rrrZrrZDOTALLr1rgr1rinsertfilterwarningsrr{ZsetLevelrr=rDrJrgrprrrrrrrrrrrrrrrrrrrrrrcZdistutils.versionZ distutilsZ OpenSSL.SSLrrZ StrictVersionZ __version__ ImportErrorrZHTTPSConnectionZPureHTTPSConnectionobjectrrr rrrr!r"r;rDrMr?rOrJrUrXr_rfrjrkrnrprsrtrxr}rrrrrrrrrrrrrrZlog_all_parent_processesrrArr8rrrrrFrrZtrack_update_statusrrrrrrrr;r;r;r<s    \    &    4  -! b +             " ?   -  2 .= , 3)kcarectl/config_handlers.py000064400000023222152527712570012051 0ustar00# Copyright (c) Cloud Linux Software, Inc # Licensed under CLOUD LINUX LICENSE AGREEMENT # http://cloudlinux.com/docs/LICENCE.TXT import json import os import re from . import config, constants, http_utils, log_utils, utils from .py23 import ConfigParser if False: # pragma: no cover from typing import Dict, List, Optional, Set # noqa: F401 CONFIG = '/etc/sysconfig/kcare/kcare.conf' FEATURE_FLAGS_WHITELIST = [ # signatures-related things 'USE_CONTENT_FILE_V3', 'FORCE_JSON_SIG_V3', # kernel module - related things 'ENABLE_CRASHREPORTER', 'KCORE_OUTPUT', 'KMSG_OUTPUT', # checkin-related things 'SEND_PERF_METRICS', # anomaly reports 'KERNEL_ANOMALY_REPORT_ENABLE', # doctor reports 'FORCE_DOCTOR_FALLBACK', ] _CONFIG_OPTIONS = set() # type: Set[str] # options were read from config file def bool_converter(value): # type: (str) -> bool return value.upper() in ('1', 'TRUE', 'YES', 'Y') def comma_list(value): # type: (str) -> List[str] return [item.strip().lower() for item in value.split(',') if item.strip()] # A sane library-name shape: starts with a letter, then letters/digits and the # punctuation that occurs in real soname stems (libnss_dns, libstdc++, libc-2.x). _LIBCARE_LIB_NAME_RE = re.compile(r'^[a-z][a-z0-9_.+\-]*$') def libcare_libs_list(value): # type: (str) -> List[str] # Reuse comma_list for splitting/normalising, then drop tokens that cannot # be a library name (typos/garbage would otherwise reach libcare-server as # part of the match regex and silently never match anything). Warns once at # config-load time so a misconfigured LIBCARE_LIBS is visible to the user. valid, invalid = [], [] # type: (List[str], List[str]) for token in comma_list(value): (valid if _LIBCARE_LIB_NAME_RE.match(token) else invalid).append(token) if invalid: log_utils.logwarn('LIBCARE_LIBS: ignoring invalid library name(s): {0}'.format(', '.join(invalid))) return valid POSSIBLE_CONFIG_OPTIONS = { # name: convert function (could be None) 'AFTER_UPDATE_COMMAND': lambda v: v.strip(), 'AUTO_STICKY_PATCHSET': None, 'AUTO_UPDATE': bool_converter, 'AUTO_UPDATE_DELAY': None, 'BEFORE_UPDATE_COMMAND': lambda v: v.strip(), 'CHECK_SSL_CERTS': bool_converter, 'DOCTOR_REPORT_MAX_SIZE_BYTES': int, 'ENABLE_CRASHREPORTER': bool_converter, 'FORCE_DOCTOR_FALLBACK': bool_converter, 'FORCE_GID': None, 'FORCE_IPV4': bool_converter, 'FORCE_IPV6': bool_converter, 'FORCE_JSON_SIG_V3': bool_converter, 'HTTP_TIMEOUT': int, 'HTTP_UPLOAD_TIMEOUT': int, 'IGNORE_FEATURE_FLAGS': bool_converter, 'IGNORE_UNKNOWN_KERNEL': bool_converter, 'KCORE_OUTPUT': bool_converter, 'KCORE_OUTPUT_SIZE': int, 'KDUMPS_DIR': lambda v: v.rstrip('/'), 'KERNEL_ANOMALY_REPORT_ENABLE': bool_converter, 'KERNEL_ANOMALY_REPORT_MAX_SIZE_BYTES': int, 'KMSG_OUTPUT': bool_converter, 'LIBCARE_DISABLED': bool_converter, 'LIBCARE_LIBS': libcare_libs_list, 'LIBCARE_PIDLOGS_MAX_TOTAL_SIZE_MB': int, 'LIBCARE_SOCKET_TIMEOUT': int, 'LIB_AUTO_UPDATE': bool_converter, 'PATCH_LEVEL': lambda v: v or None, 'PATCH_METHOD': str.upper, 'PATCH_SERVER': lambda v: v.rstrip('/'), 'PATCH_SERVER_IPV6': lambda v: v.rstrip('/'), 'PATCH_TYPE': str.lower, 'PREFIX': None, 'PREV_PATCH_TYPE': str.lower, 'REGISTRATION_URL': lambda v: v.rstrip('/'), 'REGISTRATION_URL_IPV6': lambda v: v.rstrip('/'), 'PRINT_LEVEL': int, 'REPORT_FQDN': bool_converter, 'SILENCE_ERRORS': bool_converter, 'STATUS_CHANGE_GAP': int, 'STICKY_PATCH': str.upper, 'STICKY_PATCHSET': None, 'UPDATE_DELAY': None, 'UPDATE_POLICY': str.upper, 'UPDATE_SYSCTL_CONFIG': bool_converter, 'USERSPACE_PATCHES': comma_list, 'USE_CONTENT_FILE_V3': bool_converter, 'KERNEL_VERSION_FILE': None, 'KCARE_UNAME_FILE': None, 'SUCCESS_TIMEOUT': int, 'SEND_PERF_METRICS': bool_converter, } # pragma: no py2 cover def update_config(**kwargs): cf = open(CONFIG) lines = cf.readlines() cf.close() for prop, value in kwargs.items(): updated = False prop_eq = prop + '=' prop_sp = prop + ' ' for i in range(len(lines)): if lines[i].startswith(prop_eq) or lines[i].startswith(prop_sp): if value is None: del lines[i] else: lines[i] = prop + ' = ' + str(value) + '\n' updated = True break if not updated: lines.append(prop + ' = ' + str(value) + '\n') utils.atomic_write(CONFIG, ''.join(lines)) def update_config_from_args(params): # type: (List[str]) -> None params_for_update = {} pattern = re.compile(r'^([^=]+)=([^=]*)$') for param in params: match = pattern.match(param) if match: key, value = match.groups() if not value: value = None else: raise SystemExit('Invalid parameter format: %s. Format should be KEY=VALUE' % param) params_for_update[key] = value unknown_params = set(params_for_update) - set(POSSIBLE_CONFIG_OPTIONS) if unknown_params: raise SystemExit('Unknown parameter: %s' % ', '.join(sorted(unknown_params))) for var_name, value in params_for_update.items(): convert = POSSIBLE_CONFIG_OPTIONS[var_name] if value is None or convert is None: continue try: convert(value) except Exception: raise SystemExit('Bad value for %s: %s' % (var_name, value)) update_config(**params_for_update) class FakeSecHead(object): def __init__(self, fp): self.fp = fp self.sechead = '[asection]\n' # type: Optional[str] def readline(self): # pragma: no py3 cover if self.sechead: try: return self.sechead finally: self.sechead = None else: return self.fp.readline() def __iter__(self): # pragma: no py2 cover if self.sechead: yield self.sechead self.sechead = None for line in self.fp: yield line def get_config_settings(): result = {} cp = ConfigParser(defaults={'HTTP_PROXY': '', 'HTTPS_PROXY': ''}) try: config = FakeSecHead(open(CONFIG)) if constants.PY2: # pragma: no py3 cover cp.readfp(config) else: # pragma: no py2 cover cp.read_file(config) except Exception: return {} def read_var(name, default=None, convert=None): try: value = cp.get('asection', name) except Exception: value = default if value is not None: if convert: value = convert(value) result[name] = value for scheme, variable in [('http', 'HTTP_PROXY'), ('https', 'HTTPS_PROXY')]: # environment settings take precedence over kcare.config ones if not http_utils.get_proxy_from_env(scheme): proxy = cp.get('asection', variable) if proxy: os.environ[variable] = proxy for var_name, convert in POSSIBLE_CONFIG_OPTIONS.items(): read_var(var_name, convert=convert) return result def set_settings_from_config_file(): _CONFIG_OPTIONS.clear() settings = get_config_settings() config.__dict__.update(settings) # memorize keys defined in the config file _CONFIG_OPTIONS.update(settings) def convert_headers_to_feature_flags(headers): # type: (Dict[str, str]) -> Dict[str, bool] """ Checking headers for feature flags which start with 'KC-Flag-' and reformat it to dictionary with keys in upper case and without 'KC-Flag-' prefix and dashes replaced with underscores. For unification all header keys are checked in upper case. For example: 'KC-Flag-Some-Value' -> 'SOME_VALUE' :return: dict {'SOME_VALUE': bool, ...} """ flags = {} for hdr_name, hdr_value in headers.items(): upper_name = hdr_name.upper() if not upper_name.startswith('KC-FLAG-'): continue param_name = upper_name.replace('KC-FLAG-', '').replace('-', '_') try: flags[param_name] = bool(int(hdr_value)) except ValueError: log_utils.kcarelog.error('Invalid feature flag header value %s: %s', upper_name, hdr_value) return flags def set_feature_flags_from_headers(headers): # type: (Dict[str, str]) -> None save_feature_flags_cache(headers) if not config.IGNORE_FEATURE_FLAGS: set_feature_flags_from_cache() @utils.catch_errors(logger=log_utils.logwarn) def save_feature_flags_cache(headers): # type: (Dict[str, str]) -> None feature_flags = convert_headers_to_feature_flags(headers) utils.atomic_write(constants.FEATURE_FLAGS_CACHE, content=json.dumps(feature_flags)) @utils.catch_errors(logger=log_utils.logwarn) def set_feature_flags_from_cache(): # type: () -> None """ Set global variables using feature flag from cached values received with patchserver headers. Checks that option is allowed by whitelist and update global variable using globals() :return: None """ if not os.path.exists(constants.FEATURE_FLAGS_CACHE): return with open(constants.FEATURE_FLAGS_CACHE) as f: feature_flags = json.load(f) for key, value in feature_flags.items(): if key not in FEATURE_FLAGS_WHITELIST: continue if key in _CONFIG_OPTIONS: # param was already set from config file, it has higher priority continue config.__dict__[key] = value log_utils.kcarelog.info('feature flags config override: %s=%s', key, value) kcarectl/libcare.py000064400000061267152527712570010340 0ustar00# Copyright (c) Cloud Linux Software, Inc # Licensed under CLOUD LINUX LICENSE AGREEMENT # http://cloudlinux.com/docs/LICENCE.TXT import json import os import re import shutil import socket from . import ( auth, capabilities, config, config_handlers, constants, errors, fetch, log_utils, platform_utils, process_utils, selinux, server_info, update_utils, utils, ) from .py23 import HTTPError, json_loads_nstr, urlquote if False: # pragma: no cover from typing import Dict, List, Optional, Set, Tuple # noqa: F401 LIBCARE_CLIENT = '/usr/libexec/kcare/libcare-client' LIBCARE_SOCKET = ( "/run/libcare/libcare.sock", "/var/run/libcare.sock", ) LIBCARE_PATCHES = '/var/cache/kcare/libcare_patches' LIBCARE_CVE_LIST = '/var/cache/kcare/libcare_cvelist' LIBCARE_LOGROTATE_CONFIG = '/etc/sysconfig/kcare/libcare.logrotate' LIBNAME_MAP = {'mysqld': 'db', 'mariadbd': 'db', 'postgres': 'db'} # Exclude libs on distros with cross-library patches (libnss_dns + libresolv) already deployed: # - EL7 (all distros): glibc-rh1296031.patch (CVE-2015-7547) # - Ubuntu 16.04: revert-CVE-2015-5180.diff # See LIBCARE-2943 for details. # Entries: (distro_substring | None for any, version_prefix, blacklisted_libs). # version_prefix is matched as a dotted component prefix: '7.' matches '7', # '7.9' and '7.9.2009' but not '70'. CentOS 7's VERSION_ID is bare '7'. # Order matters: more specific rules first; first match wins. # NOTE: distro is matched by substring (`bl_distro in distro`) -- pick a substring # that is unique enough to avoid accidentally matching unrelated distros. LIBCARE_LIBS_BLACKLIST = [ ('ubuntu', '16.', set(['libnss_dns'])), (None, '7.', set(['libnss_dns'])), ] # type: List[Tuple[Optional[str], str, Set[str]]] @utils.cached def _get_effective_libcare_libs(): # type: () -> List[str] libs = list(config.LIBCARE_LIBS) blacklisted = set() # type: Set[str] try: distro_info = platform_utils.get_distro() distro = distro_info[0].lower() version = distro_info[1] except Exception: # Distro unknown: exclude the union of all blacklisted libs so we # never ship a lib that would conflict on *some* affected distro. for _, _, bl_libs in LIBCARE_LIBS_BLACKLIST: blacklisted |= bl_libs else: # Append a trailing dot so a bare major version like '7' matches # a prefix like '7.' without also matching '70'. version_dotted = version + '.' for bl_distro, bl_version, bl_libs in LIBCARE_LIBS_BLACKLIST: if (bl_distro is None or bl_distro in distro) and version_dotted.startswith(bl_version): blacklisted = bl_libs break return [lib for lib in libs if lib not in blacklisted] def get_userspace_map(): # type: () -> Dict[str, List[str]] return { 'db': ['mysqld', 'mariadbd', 'postgres'], 'libs': _get_effective_libcare_libs(), } def get_userspace_cache_path(libname, *parts): return os.path.join(constants.PATCH_CACHE, 'userspace', libname, *parts) def clear_libcare_cache(clbl): def wrapper(*args, **kwargs): try: return clbl(*args, **kwargs) finally: try: libcare_client('clearcache') except Exception as err: # We don't want to show the error to the user but want to see it in logs log_utils.logerror("Libcare cache clearing failed: '{0}'".format(err), print_msg=False) return wrapper class UserspacePatchLevel(int): def __new__(cls, libname, buildid, level, baseurl=None): return super(cls, cls).__new__(cls, level) def __init__(self, libname, buildid, level, baseurl=None): self.level = level self.libname = libname self.buildid = buildid self.baseurl = baseurl def cache_path(self, *parts): return get_userspace_cache_path(self.libname, self.buildid, str(self), *parts) def refresh_applied_patches_list(clbl): def save_current_state(info): """KPT-1543 Save info about applied patches""" versions, cves = '', '' try: if info is None: info = _libcare_info() packages = {} all_patches = [] for rec in _get_patches_info(info): packages[rec.get('package')] = rec.get('latest-version', '') all_patches.extend(rec.get('patches', [])) cves_set = utils.extract_unique_cves(all_patches, cve_field='cve') versions = '\n'.join([' '.join(rec) for rec in packages.items()]) cves = '\n'.join(sorted(cves_set)) # sort for consistent output finally: utils.atomic_write(LIBCARE_PATCHES, versions, ensure_dir=True) utils.atomic_write(LIBCARE_CVE_LIST, cves, ensure_dir=True) def wrapper(*args, **kwargs): info = None try: info = clbl(*args, **kwargs) return info finally: save_current_state(info) return wrapper def fetch_userspace_patch(libname, build_id, patch_level=None, tag=None): prefix = config.PREFIX or 'main' libname = urlquote(libname) build_id = urlquote(build_id.strip()) url = utils.get_patch_server_url(LIBNAME_MAP.get(libname, 'u'), prefix, libname, build_id, 'latest.v1') url += '?info=' + server_info.encoded_server_lib_info('update', patch_level) cache_dst = LIBNAME_MAP.get(libname, 'libs') if tag: cache_dst = os.path.join('tags', tag, cache_dst) extra_kwargs = {} if tag: extra_kwargs['headers'] = {'Sticky': tag} try: response = fetch.wrap_with_cache_key(auth.urlopen_auth)(url, check_license=False, **extra_kwargs) except errors.NotFound: if tag: # In transient tag mode the user explicitly asked for a snapshot; # libraries without a patchset on that date are simply not patched # - skip silently instead of failing the whole update. log_utils.loginfo( 'No libcare patchset available for {0} at tag {1}, skipping.'.format(libname, tag), print_msg=False, ) return None # There is no latest info, so we need to clear cache for corresponding # build_id to prevent updates by "-ctl" utility. shutil.rmtree(get_userspace_cache_path(cache_dst, build_id), ignore_errors=True) raise except HTTPError as ex: if ex.code == 400 and tag: raise errors.KcareError( 'Invalid sticky patch tag {0}: server rejected the value.'.format(tag), status='invalid lib sticky patch', ) raise config_handlers.set_feature_flags_from_headers(response.headers) meta = json_loads_nstr(utils.nstr(response.read())) required_capabilities = meta.get('capabilities', []) if not capabilities.has_lc_capabilities(required_capabilities): raise errors.CapabilitiesMismatch( 'Latest LibCare patchset for {0} is incompatible ' 'with the current kernecare package version, please upgrade.'.format(libname) ) level = UserspacePatchLevel(cache_dst, build_id, meta['level'], meta.get('baseurl')) plevel = str(meta['level']) patch_path = get_userspace_cache_path(cache_dst, build_id, plevel, 'patch.tar.gz') if not os.path.exists(patch_path) or os.path.getsize(patch_path) == 0: url = utils.get_patch_server_url(meta['patch_url']) try: fetch.fetch_url(url, patch_path, check_signature=config.USE_SIGNATURE, hash_checker=fetch.get_hash_checker(level)) except HTTPError as ex: # No license - no access if ex.code in (403, 401): raise errors.NoLibcareLicenseException('KC+ licence is required') raise dst = get_userspace_cache_path(cache_dst, build_id, plevel) cmd = ['tar', 'xf', patch_path, '-C', dst, '--no-same-owner'] code, stdout, stderr = process_utils.run_command(cmd, catch_stdout=True, catch_stderr=True) if code: raise errors.KcareError( "Patches unpacking error: '{0}' '{1}' {2}".format(stderr, stdout, code), status='patches unpacking error' ) link_name = get_userspace_cache_path(cache_dst, build_id, 'latest') if not os.path.islink(link_name) and os.path.isdir(link_name): shutil.rmtree(link_name) os.symlink(plevel, link_name + '.tmp') os.rename(link_name + '.tmp', link_name) return level def set_libcare_status(enabled): config.LIBCARE_DISABLED = not enabled if not enabled: libcare_server_stop() config_handlers.update_config(LIBCARE_DISABLED=('FALSE' if enabled else 'YES')) if enabled: libcare_server_start() log_utils.kcarelog.info('libcare service is ' + ('enabled' if enabled else 'disabled')) def libcare_server_stop(): # Stop the socket first to break cascading restart loops on # systemd 219 (CentOS 7) where a pending connection in the # socket backlog re-activates a failed service. if constants.SKIP_SYSTEMCTL_CHECK or os.path.exists(constants.SYSTEMCTL): process_utils.run_command([constants.SYSTEMCTL, 'stop', 'libcare.socket']) process_utils.run_command([constants.SYSTEMCTL, 'stop', 'libcare.service']) process_utils.run_command([constants.SYSTEMCTL, 'reset-failed', 'libcare']) process_utils.run_command([constants.SYSTEMCTL, 'reset-failed', 'libcare.socket']) else: # pragma: no cover unit try: cmd = [process_utils.find_cmd('service', ('/usr/sbin/', '/sbin/')), 'libcare', 'stop'] except Exception: return process_utils.run_command(cmd) def libcare_server_start(): # KPT-2884: skip restart when libcare.service is already active -- systemd # refuses to activate a socket whose service is already running if constants.SKIP_SYSTEMCTL_CHECK or os.path.exists(constants.SYSTEMCTL): code, _, _ = process_utils.run_command( [constants.SYSTEMCTL, 'is-active', '--quiet', 'libcare.service'], catch_stdout=True, catch_stderr=True, ) if code == 0: return # Break out of a possible cascading restart loop on systemd 219 # (CentOS 7) by stopping socket first, then service, then clearing # failed state. process_utils.run_command([constants.SYSTEMCTL, 'stop', 'libcare.socket']) process_utils.run_command([constants.SYSTEMCTL, 'stop', 'libcare.service']) process_utils.run_command([constants.SYSTEMCTL, 'reset-failed', 'libcare']) process_utils.run_command([constants.SYSTEMCTL, 'reset-failed', 'libcare.socket']) process_utils.run_command([constants.SYSTEMCTL, 'start', 'libcare.socket']) else: if libcare_server_started(): return try: cmd = [process_utils.find_cmd('service', ('/usr/sbin/', '/sbin/')), 'libcare', 'start'] except Exception: # pragma: no cover unit return process_utils.run_command(cmd) def _libcare_info(patched=True, limit=None): # Escape each name: library/process names contain regex metacharacters # (e.g. '.'/'+' in libstdc++ and libc-2.17.so, or '()' in # 'libcrypto.so.1.0.1e (deleted)') that must match literally, not as a regex. regexp = '|'.join("({0})".format(re.escape(proc)) for proc in sorted(limit or [])) cmd = ['info', '-j'] if not patched: cmd += ['-l', '-r', regexp] try: lines = libcare_client(*cmd) except Exception as err: raise errors.KcareError("Gathering userspace libraries info error: '{0}'".format(err), status='userspace libs info error') result = [] for line in lines.split('\n'): if line: try: result.append(json.loads(line)) except ValueError: # We have to do that because socket's output isn't separated to stderr and stdout # so there are chances that will be non-json lines pass # FIXME: remove that libe when library names will be separated to lower # level from process name and pid result = [{'comm': line.pop('comm'), 'pid': line.pop('pid'), 'libs': line} for line in result] for line in result: line['libs'] = dict((k, v) for k, v in line['libs'].items() if ('patchlvl' in v or not patched)) return result def _get_patches_info(info): patches = set() for rec in info: for _, data in rec['libs'].items(): patches.add((data['buildid'], data['patchlvl'])) result = [] umap = get_userspace_map() for cache_dst in umap: for build_id, patchlvl in patches: patch_info_filename = get_userspace_cache_path(cache_dst, build_id, str(patchlvl), 'info.json') if os.path.isfile(patch_info_filename): with open(patch_info_filename, 'r') as fd: result.append(json.load(fd)) return result @clear_libcare_cache def libcare_patch_info_basic(): return _get_patches_info(_libcare_info()) @clear_libcare_cache def libcare_patch_info(): result = libcare_patch_info_basic() if not result: log_utils.logerror("No patched processes.") return json.dumps({'result': result}) @clear_libcare_cache def libcare_info(): result = _libcare_info() if not result: log_utils.logerror("No patched processes.") return json.dumps({'result': result}) def _libcare_version(): result = {} for rec in libcare_patch_info_basic(): result[rec.get('package')] = rec.get('latest-version', '') return result def libcare_version(libname): for package, version in _libcare_version().items(): if libname.startswith(package): return version return '' def libcare_client_format(params): return b''.join(utils.bstr(p) + b'\0' for p in params) + b'\0' def get_available_libcare_socket(): for libcare_socket in LIBCARE_SOCKET: if os.path.exists(libcare_socket): return libcare_socket raise errors.KcareError("Libcare socket is not found.") def libcare_client(*params): if config.LIBCARE_DISABLED: raise errors.KcareError('Libcare is disabled.') sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM, 0) sock.settimeout(10) # connect timeout res = b'' try: sock.connect(get_available_libcare_socket()) sock.settimeout(config.LIBCARE_SOCKET_TIMEOUT) cmd = libcare_client_format(params) log_utils.logdebug("Libcare socket send: {cmd}".format(cmd=params)) sock.sendall(cmd) while True: data = sock.recv(4096) if not data: break res += data result = res.decode('utf-8', 'replace') log_utils.logdebug("Libcare socket recieved: {result}".format(result=result)) return result finally: sock.close() def libcare_patch_apply(limit, tag=None): if tag: # Atomic apply from an isolated, tag-specific storage. Leaves the # default storage untouched, so a transient `--lib-tag` update does # not interfere with the normal `--lib-update` flow. for dst in limit: storage_path = get_userspace_cache_path('tags', tag, dst) # check_userspace_updates only creates the tag-specific storage # directory for groups that had running processes with available # patchsets. If a group (e.g. 'db' on a host without a DB) had # nothing fetched, the directory does not exist - skip the apply # for it explicitly so we don't rely on libcare-server's # behavior of returning 0 on "cannot open storage". if not os.path.isdir(storage_path): continue try: libcare_client('update', '--storage', storage_path) except Exception as err: raise errors.KcareError( "Userspace patch applying error (storage={0}): '{1}'".format(storage_path, err), status='userspace patch apply error', ) return for dst in limit: try: libcare_client('storage', get_userspace_cache_path(dst)) except Exception as err: raise errors.KcareError("Userspace storage switching error: '{0}'".format(err), status='userspace storage switch error') try: libcare_client('update') except Exception as err: raise errors.KcareError("Userspace patch applying error: '{0}'".format(err), status='userspace patch apply error') @clear_libcare_cache @refresh_applied_patches_list @process_utils.log_all_parent_processes def libcare_unload(): try: libcare_client('unload') except Exception as err: raise errors.KcareError("Userspace patch unloading error: '{0}'".format(err), status='userspace patch unload error') def libcare_replugin(): # pragma: no cover unit """Reload libcare-server plugin without restarting the server.""" try: libcare_client('replugin') except Exception as err: raise errors.KcareError("Userspace replugin error: '{0}'".format(err)) # log_all_parent_processes must be outermost to run first @process_utils.log_all_parent_processes @update_utils.track_update_status('libcare') @selinux.skip_if_no_selinux_module @clear_libcare_cache @refresh_applied_patches_list def do_userspace_update(mode=constants.UPDATE_MODE_MANUAL, limit=None, tag=None): """Patch userspace processes to the latest version.""" rotate_libcare_logs() # Auto-update means cron-initiated run and if no # LIB_AUTO_UPDATE flag in the config - nothing will happen. if mode == constants.UPDATE_MODE_AUTO and not config.LIB_AUTO_UPDATE: return None umap = get_userspace_map() if limit is None: limit = list(umap.keys()) process_filter = [] for userspace_patch in limit: process_filter.extend(umap.get(userspace_patch, [])) if not process_filter: # Unknown limits were defined. Do nothing log_utils.loginfo('No such userspace patches: {0}'.format(limit)) return None failed, something_found, _, before = check_userspace_updates(limit=process_filter, tag=tag) if failed: raise errors.KcareError('There were errors while patches downloading (unpacking).') if not something_found: log_utils.loginfo('No patches were found.') return None selinux.restore_selinux_context(os.path.join(constants.PATCH_CACHE, 'userspace')) rotate_libcare_logs() try: # Batch apply for all collected patches libcare_patch_apply(limit, tag=tag) # TODO: clear userspace cache. We need the same logic as for kernel, lets do # it later to reduce this patch size. except errors.KcareError as ex: log_utils.logerror(str(ex)) raise errors.KcareError('There were errors while patches applying.') data_after = _libcare_info() after = _get_userspace_procs(data_after) if not any(list(item['libs'] for item in data_after)): # No patches were applied return None # Info on how many patches were actually patched via before and after diff log_utils.logdebug("Patched before: {before}".format(before=before)) log_utils.logdebug("Patched after: {after}".format(after=after)) uniq_procs_after = set(v for items in after.values() for v in items) uniq_procs_before = set(v for items in before.values() for v in items) diff = uniq_procs_after - uniq_procs_before overall = sum(len(v) for v in after.values()) log_utils.loginfo( "The patches have been successfully applied to {count} newly " "discovered processes. The overall amount of applied patches " "is {overall}.".format(count=len(diff), overall=overall) ) for k, v in after.items(): log_utils.loginfo("Object `{0}` is patched for {1} processes.".format(k, len(v))) return data_after @clear_libcare_cache def get_userspace_update_status(): try: failed, _, libs_not_patched, _ = check_userspace_updates() except errors.KcareError: return 3 finally: rotate_libcare_logs() if failed: return 3 if libs_not_patched: return 1 return 2 if update_utils.status_gap_passed(filename='.libcarestatus') else 0 def _get_userspace_procs(info): result = {} # type: Dict[str, List[Tuple[int, str]]] for item in info: for libname, rec in item['libs'].items(): if rec.get('patchlvl'): if libname not in result: result[libname] = [] result[libname].append((item['pid'], item['comm'])) return result def _get_userspace_libs(info): result = set() for item in info: for libname, rec in item['libs'].items(): result.add((libname, rec['buildid'], rec.get('patchlvl', 0))) return result def check_userspace_updates(limit=None, tag=None): if not limit: umap = get_userspace_map() limit = [] [limit.extend(libs) for libs in umap.values()] data_before = _libcare_info(patched=False, limit=limit) before = _get_userspace_procs(data_before) failed = something_found = False libs_not_patched = True for rec in _get_userspace_libs(data_before): # Download and unpack patches libname, build_id, patchlvl = rec try: result = fetch_userspace_patch(libname, build_id, patchlvl, tag=tag) # In tag mode a None result means the patchset was missing for # this library on the requested date - that's a legitimate skip, # not a "found update". if tag and result is None: continue something_found = True if patchlvl != 0: libs_not_patched = False except errors.CapabilitiesMismatch as e: failed = True log_utils.logwarn(str(e)) except (errors.NotFound, errors.NoLibcareLicenseException): pass except errors.AlreadyTrialedException: raise except errors.KcareError as ex: # An invalid sticky tag is rejected by the server for every # build_id (the 400 is a property of the tag value, not of # the library). Continuing the loop would just hammer the # server with N identical 400s and bury the specific error # under a generic "There were errors while patches downloading" # at the end. Fail fast with the original message instead, # mirroring AlreadyTrialedException handling above. if getattr(ex, 'status', None) == 'invalid lib sticky patch': raise failed = True log_utils.logerror(str(ex)) update_utils.touch_status_gap_file(filename='.libcarestatus') return failed, something_found, libs_not_patched, before def rotate_libcare_logs(): rc = 0 stderr = '' logrotate_path = process_utils.find_cmd('logrotate', raise_exc=False) if logrotate_path: try: rc, _, stderr = process_utils.run_command([logrotate_path, LIBCARE_LOGROTATE_CONFIG], catch_stderr=True) except Exception as e: rc = 1 stderr = str(e) if rc: log_utils.logerror('failed to run logrotate for libcare logs, stderr: {0}'.format(stderr), print_msg=False) else: log_utils.logwarn("logrotate utility wasn't found", print_msg=False) libcare_log_directory = '/var/log/libcare/' if not os.path.isdir(libcare_log_directory): return max_total_size = config.LIBCARE_PIDLOGS_MAX_TOTAL_SIZE_MB * (1024**2) try: log_files = os.listdir(libcare_log_directory) pidlog_re = re.compile(r'^\d+\.log.*') # both .log and .log.x.gz pidlog_files = [os.path.join(libcare_log_directory, fn) for fn in log_files if pidlog_re.match(fn)] pidlog_files_with_ct = [(os.path.getctime(fp), fp) for fp in pidlog_files] pidlog_files_with_ct.sort(reverse=True) # newest files first # delete old files if we have overflow total_size = 0 for _, filepath in pidlog_files_with_ct: total_size += os.path.getsize(filepath) if total_size >= max_total_size: os.remove(filepath) log_utils.kcarelog.info('Removed %s because of logs size limit', filepath) except Exception: # pragma: no cover log_utils.logexc('Failed to cleanup libcare server logfiles', print_msg=False) def libcare_server_started(): """Assume that whenever the service is not running, we did not patch anything.""" try: cmd = [process_utils.find_cmd('service', ('/usr/sbin/', '/sbin/')), 'libcare', 'status'] except Exception: # pragma: no cover unit return False code, _, _ = process_utils.run_command(cmd, catch_stdout=True, catch_stderr=True) return code == 0 kcarectl/auth.py000064400000027732152527712570007677 0ustar00# Copyright (c) Cloud Linux Software, Inc # Licensed under CLOUD LINUX LICENSE AGREEMENT # http://cloudlinux.com/docs/LICENCE.TXT import base64 import os import re import time from . import config, constants, errors, http_utils, ipv6_support, log_utils, platform_utils, serverid, utils from .py23 import HTTPError, URLError, urlencode def unregister(silent=False): url = None try: server_id = serverid.get_serverid() if server_id is None: if not silent: log_utils.logerror('Error unregistering server: cannot find server id') return url = ipv6_support.get_registration_url() + '/unregister_server.plain?server_id={0}'.format(server_id) response = http_utils.urlopen(url) content = utils.nstr(response.read()) res = utils.data_as_dict(content) if res['success'] == 'true': serverid.rm_serverid() # License state transitioned (licensed -> none); the cached ipv6 decision # may have been "ipv4 because server id was found" and no longer applies. ipv6_support.clear_cache() if not silent: log_utils.loginfo('Server was unregistered') elif not silent: log_utils.logerror(content) log_utils.logerror('Error unregistering server: ' + res['message']) except HTTPError as e: if not silent: log_utils.print_cln_http_error(e, url) def _register_retry(url): # pragma: no cover unit utils.print_wrapper('Register auto-retry has been enabled, the system can be registered later') pid = os.fork() if pid > 0: return os.setsid() pid = os.fork() import sys if pid > 0: sys.exit(0) sys.stdout.flush() si = open('/dev/null', 'r') so = open('/dev/null', 'a+') os.dup2(si.fileno(), sys.stdin.fileno()) os.dup2(so.fileno(), sys.stdout.fileno()) os.dup2(so.fileno(), sys.stderr.fileno()) while True: time.sleep(60 * 60 * 2) code, server_id, auth_token = _try_register(url) if code == 0 and server_id: serverid.set_server_id(server_id) _set_auth_token(auth_token) # Daemonized retry: this success runs in a forked child hours after # register() returned, so it can't piggy-back on the synchronous # clear_cache() in register() -- it has to invalidate the cache itself. ipv6_support.clear_cache() sys.exit(0) def _validate_urlsafe_encoding(value): if value is not None and not re.match(r'^[\w.-]+$', value): raise ValueError('Invalid value received: %s' % value) return value def _try_register(url): try: response = http_utils.urlopen(url) auth_token = response.headers.get(constants.AUTH_TOKEN_HEADER, None) res = utils.data_as_dict(utils.nstr(response.read())) return int(res['code']), _validate_urlsafe_encoding(res.get('server_id')), _validate_urlsafe_encoding(auth_token) except (HTTPError, URLError) as e: log_utils.print_cln_http_error(e, url) return None, None, None except Exception: log_utils.kcarelog.exception('Exception while trying to register URL %s' % url) return None, None, None def register(key, retry=False): try: unregister(True) except Exception: log_utils.kcarelog.exception('Exception while trying to unregister URL before register.') hostname = platform_utils.get_hostname() query = urlencode([('hostname', hostname), ('key', key)]) url = '{0}/register_server.plain?{1}'.format(ipv6_support.get_registration_url(), query) code, server_id, auth_token = _try_register(url) if code == 0: serverid.set_server_id(server_id) _set_auth_token(auth_token) # License state transitioned (none -> licensed) and a serverid now exists, # so the next is_ipv6_preferred() will hit a different branch than the # cached "no license yet" decision -- invalidate after the transition, # not before (re-evaluating pre-register would just repeat the probes # and return the same answer since no license exists yet). ipv6_support.clear_cache() log_utils.loginfo('Server Registered') return 0 elif code == 1: log_utils.logerror('Account Locked') elif code == 2: log_utils.logerror('Invalid Key') elif code == 3: log_utils.logerror( 'You have reached maximum registered servers for this key. ' 'Please go to your CLN account, remove unused servers and try again.' ) elif code == 4: log_utils.logerror('IP is not allowed. Please change allowed IP ranges for the key in KernelCare Key tab in CLN') elif code == 5: log_utils.logerror('This IP was already used for trial, you cannot use it for trial again') elif code == 6: log_utils.logerror('This IP was banned. Please contact support for more information at https://www.kernelcare.com/support/') else: log_utils.logerror('Unknown Error {0}'.format(code)) if retry: # pragma: no cover _register_retry(url) return 0 return code or -1 @utils.cached def _get_auth_token(): return utils.try_to_read(constants.AUTH_TOKEN_DUMP_PATH) def _set_auth_token(auth_token): if not auth_token: return utils.atomic_write(constants.AUTH_TOKEN_DUMP_PATH, auth_token) def urlopen_auth(url, *args, **kwargs): method = kwargs.pop('method', None) if kwargs.pop('check_license', True): check = _check_auth_retry else: check = http_utils.check_urlopen_retry if http_utils.is_local_url(url): return http_utils.urlopen_base(url, *args, **kwargs) request = http_utils.http_request(url, get_http_auth_string(), _get_auth_token(), method=method) return utils.retry(check, count=8)(http_utils.urlopen_base)(request, *args, **kwargs) def get_http_auth_string(): server_id = serverid.get_serverid() if server_id: return utils.nstr(base64.b64encode(utils.bstr('{0}:{1}'.format(server_id, 'kernelcare')))) return None def _check_auth_retry(e, state): if isinstance(e, HTTPError) and e.code in (403, 401): return _handle_forbidden(state) return http_utils.check_urlopen_retry(e, state) def _handle_forbidden(state): """In case of 403 error we should check what's happen. Case #1. We are trying to register unlicensed machine and should try to register trial. Case #2. We have a valid license but access restrictions on server are not consistent yet and we had to try later. """ if 'license' in state: # license has already been checked and is valid, no need to ask CLN again return True if config.CHECK_CLN_LICENSE_STATUS: server_id = serverid.get_serverid() url = ipv6_support.get_registration_url() + '/check.plain' if server_id: url += '?server_id={0}'.format(server_id) try: # do not retry in case of 500 from CLN! # otherwise, CLN will die in pain because of too many requests content = utils.nstr(http_utils.urlopen(url, retry_on_500=False).read()) info = utils.data_as_dict(content) except URLError as ex: log_utils.print_cln_http_error(ex, url, stdout=False) return if not info or not info.get('code'): log_utils.kcarelog.error('Unexpected CLN response: {0}'.format(content)) return if info['code'] in ['0', '1']: # license is fine: 0 - valid license, 1 - valid trial license; # looks like htpasswd not updated yet; # mark state as licensed to avoid repeated requests to CLN state['license'] = True log_utils.logerror('Unable to access server. Retrying...') return True else: _register_trial() def license_info(): server_id = serverid.get_serverid() if server_id: url = ipv6_support.get_registration_url() + '/check.plain?server_id={0}'.format(server_id) try: response = http_utils.urlopen(url) content = utils.nstr(response.read()) res = utils.data_as_dict(content) if not res or not res.get('code'): utils.print_wrapper('Unexpected CLN response: {0}'.format(content)) return 1 code = int(res['code']) if code == 0: utils.print_wrapper('Key-based valid license found') return 1 else: license_type = _get_license_info_by_ip(key_checked=1) if license_type == 0: utils.print_wrapper('No valid key-based license found') return license_type except URLError as e: log_utils.print_cln_http_error(e, url) return 0 else: return _get_license_info_by_ip() def _get_license_info_by_ip(key_checked=0): url = ipv6_support.get_registration_url() + '/check.plain' try: response = http_utils.urlopen(url) content = utils.nstr(response.read()) res = utils.data_as_dict(content) if res['success'].lower() == 'true': code = int(res['code']) if code == 0: utils.print_wrapper('Valid license found for IP {0}'.format(res['ip'])) return 1 # valid license if code == 1: ip = res['ip'] expires_str = utils.parse_response_date(res['expire_date']).strftime('%Y-%m-%d') utils.print_wrapper('You have a trial license for the IP {0} that will expire on {1}'.format(ip, expires_str)) return 2 # trial license if code == 2 and key_checked == 0: ip = res['ip'] expires_str = utils.parse_response_date(res['expire_date']).strftime('%Y-%m-%d') utils.print_wrapper('Your trial license for the IP {0} expired on {1}'.format(ip, expires_str)) if code == 3 and key_checked == 0: if 'ip' in res: utils.print_wrapper("The IP {0} hasn't been licensed".format(res['ip'])) else: utils.print_wrapper("This server hasn't been licensed") else: message = res.get('message', '') utils.print_wrapper('Error retrieving license info: {0}'.format(message)) except URLError as e: log_utils.print_cln_http_error(e, url) except KeyError as key: utils.print_wrapper('Unexpected CLN response, cannot find {0} key:\n{1}'.format(key, content.strip())) return 0 # no valid license def _register_trial(): trial_mark = os.path.join(constants.PATCH_CACHE, 'trial-requested') if os.path.exists(trial_mark): return try: response = http_utils.urlopen(ipv6_support.get_registration_url() + '/trial.plain') res = utils.data_as_dict(utils.nstr(response.read())) try: if res['success'].lower() == 'true': utils.atomic_write(trial_mark, '', ensure_dir=True) # Trial just granted: license state flipped (none -> trial), so # the cached ipv4 decision may no longer reflect the right branch # in is_ipv6_preferred(). ipv6_support.clear_cache() if res['expired'] == 'true': raise errors.AlreadyTrialedException(res['ip'], res['created']) log_utils.loginfo('Requesting trial license for IP {0}. Please wait...'.format(res['ip'])) return None elif res['success'] == 'na': utils.atomic_write(trial_mark, '', ensure_dir=True) raise errors.KcareError('Invalid License') else: # TODO: make sane exception messages raise errors.UnableToGetLicenseException(-1) # Invalid response? except KeyError as ke: raise errors.UnableToGetLicenseException(ke) except HTTPError as e: raise errors.UnableToGetLicenseException(e.code) kcarectl/errors.py000064400000003761152527712570010246 0ustar00# Copyright (c) Cloud Linux Software, Inc # Licensed under CLOUD LINUX LICENSE AGREEMENT # http://cloudlinux.com/docs/LICENCE.TXT from .py23 import HTTPError class SafeExceptionWrapper(Exception): def __init__(self, inner, etype=None, details=None): self.inner = inner self.etype = etype self.details = details class KcareError(Exception): """Base kernelcare exception which will be considered as expected error and the full traceback will not be shown. Subclasses may set a class-level ``status`` to provide a short, fixed label for error reporting. Individual raise sites can override it per-instance via the ``status`` kwarg. """ status = '' # type: str def __init__(self, *args, **kwargs): # type: (*object, **object) -> None status = kwargs.pop('status', None) if status is not None: self.status = str(status) super(KcareError, self).__init__(*args) class NotFound(HTTPError): pass class NoLibcareLicenseException(KcareError): status = 'no libcare license' class CapabilitiesMismatch(KcareError): status = 'capabilities mismatch' class AlreadyTrialedException(KcareError): status = 'already trialed' def __init__(self, ip, created, *args, **kwargs): super(AlreadyTrialedException, self).__init__(*args, **kwargs) self.created = created[0 : created.index('T')] self.ip = ip def __str__(self): return 'The IP {0} was already used for a trial license on {1}'.format(self.ip, self.created) class UnableToGetLicenseException(KcareError): status = 'unable to get license' def __init__(self, code, **kwargs): super(UnableToGetLicenseException, self).__init__( 'Unknown Issue when getting trial license. Error code: ' + str(code), **kwargs ) class BadSignatureException(KcareError): status = 'bad signature' def check_exc(*exc_list): def inner(e, state): return isinstance(e, exc_list) return inner kcarectl/platform_utils.py000064400000024006152527712570011771 0ustar00# Copyright (c) Cloud Linux Software, Inc # Licensed under CLOUD LINUX LICENSE AGREEMENT # http://cloudlinux.com/docs/LICENCE.TXT import base64 import json import os import platform import re import socket import sys from . import config, constants, log_utils, process_utils, selinux, utils if False: # pragma: no cover from typing import Any, Dict, Optional, Tuple # noqa: F401 VIRTWHAT = '/usr/libexec/kcare/virt-what' PROC_DIR = '/proc' def get_distro(): if sys.version_info[:2] < (3, 6): # pragma: no py3 cover return platform.linux_distribution() else: # pragma: no distro cover import distro return distro.linux_distribution(full_distribution_name=False) @utils.cached def get_system_uname(): return platform.uname()[2] def get_python_version(): # type: () -> str return '%s.%s' % (sys.version_info[0], sys.version_info[1]) def app_info(is_json=False): # type: (bool) -> str info = { 'python_version': get_python_version(), 'agent_version': constants.VERSION, } if selinux.is_selinux_enabled(): rc, stdout, stderr = process_utils.run_command(['ps', '-Z', '--no-headers', '--pid', str(os.getpid())], catch_stdout=True) if not rc: selinux_context = stdout.split()[0] else: selinux_context = 'error: %s' % stderr info['selinux_context'] = selinux_context if is_json: return json.dumps(info) info_keys = sorted(info) info_str = '' for info_key in info_keys: info_str += '%s: %s\n' % (info_key, info[info_key]) return info_str.rstrip() EFIVARS_PATH = '/sys/firmware/efi/efivars' EFI_VENDORS = { 'global': '8be4df61-93ca-11d2-aa0d-00e098032b8c', 'shim': '605dab50-e046-4300-abb6-3dd810dd8b23', } def _read_uefi_var(name, vendor, max_bytes=256): # type: (str, str, Optional[int]) -> Optional[bytes] var_path = os.path.join(EFIVARS_PATH, '%s-%s' % (name, vendor)) if not os.path.exists(var_path): return None with open(var_path, 'rb') as var: return var.read(max_bytes) def is_secure_boot(): # mocked: tests/unit/test_load_kmod.py # type: () -> bool try: secure_boot_var = _read_uefi_var('SecureBoot', EFI_VENDORS['global']) if secure_boot_var: return secure_boot_var[-1:] == b'\x01' # Get last byte except Exception: # pragma: no cover pass return False def _get_uefi_var_encoded(name, vendor): # type: (str, str) -> Optional[str] try: value_bytes = _read_uefi_var(name, vendor) if value_bytes is None: return None except Exception as e: value_bytes = str(e).encode() return utils.nstr(base64.urlsafe_b64encode(value_bytes)) def secure_boot_info(): # type: () -> dict[str, Any] cmdline = utils.try_to_read(os.path.join(PROC_DIR, 'cmdline')) if cmdline and len(cmdline) > 1024: # pragma: no cover cmdline = cmdline[:1024] info = {'cmdline': cmdline, 'has_efi': os.path.exists(os.path.dirname(EFIVARS_PATH))} # type: dict[str, Any] if not info['has_efi']: return info try: info['global'] = dict((var, _get_uefi_var_encoded(var, EFI_VENDORS['global'])) for var in ('SecureBoot', 'SetupMode')) shim_vars = sorted( [var[0 : -len(EFI_VENDORS['shim']) - 1] for var in os.listdir(EFIVARS_PATH) if var.endswith(EFI_VENDORS['shim'])] ) info['shim'] = {'vars': shim_vars} shim_exclude_vars = set(['MokListRT', 'MokListXRT', 'MokListTrustedRT', 'SbatLevelRT']) for var in shim_vars: if var in ('HSIStatus', 'MokIgnoreDB') or (var.endswith('RT') and var not in shim_exclude_vars): info['shim'][var] = _get_uefi_var_encoded(var, EFI_VENDORS['shim']) except Exception as err: log_utils.logwarn(str(err)) return info @utils.cached def get_hostname(): # type: () -> str # KCARE-1165 If fqdn gathering is forced if config.REPORT_FQDN: try: # getaddrinfo() -> [(family, socktypeget_hostname, proto, canonname, sockaddr), ...] hostname = socket.getaddrinfo(socket.gethostname(), 0, 0, 0, 0, socket.AI_CANONNAME)[0][3] except socket.gaierror as ge: log_utils.logerror(ge) hostname = platform.node() else: hostname = platform.node() return hostname @utils.cached def get_uptime(): # type: () -> str uptime_file = os.path.join(PROC_DIR, 'uptime') if os.path.isfile(uptime_file): f = open(uptime_file, 'r') line = f.readline() result = str(int(float(line.split()[0]))) f.close() return result return '-1' @utils.cached def get_virt(): if os.path.isfile(VIRTWHAT): return process_utils.check_output([VIRTWHAT]).strip() return 'no-virt-what' # pragma: no cover def is_cpanel(): # type: () -> bool return os.path.isfile('/usr/local/cpanel/cpanel') def is_plesk(): # type: () -> bool return os.path.isdir('/usr/local/psa/admin/') def is_interworx(): # type: () -> bool return os.path.isdir('/usr/local/interworx/') def is_ispmanager(): # type: () -> bool return os.path.isdir('/usr/local/ispmgr/') def is_directadmin(): # type: () -> bool return os.path.isdir('/usr/local/directadmin/plugins/') def is_hosting_controller(): # type: () -> bool return os.path.isdir('/usr/local/hostingcontroller/') def is_hsphere(): # type: () -> bool return os.path.isdir('/hsphere/shared') def has_softaculous(): # type: () -> bool """Softaculous markers checked by the kcdoctor.sh detect_cp()""" return any( os.path.exists(path) for path in ( '/usr/local/softaculous', '/usr/local/cpanel/whostmgr/cgi/softaculous', '/usr/local/directadmin/plugins/softaculous', ) ) # control panels reported by the doctor report, ported from the # kcdoctor.sh detect_cp() probes (presence only, no version detection) CONTROL_PANEL_PROBES = ( ('Plesk', is_plesk), ('cPanel', is_cpanel), ('InterWorx', is_interworx), ('ISPmanager', is_ispmanager), ('DirectAdmin', is_directadmin), ('Hosting Controller', is_hosting_controller), ('H-Sphere', is_hsphere), ) def inside_vz_container(): # mocked: tests/unit/test_load_kmod.py return os.path.exists(os.path.join(PROC_DIR, 'vz', 'veinfo')) and not os.path.exists(os.path.join(PROC_DIR, 'vz', 'version')) def inside_lxc_container(): # mocked: tests/unit/test_load_kmod.py return '/lxc/' in open(os.path.join(PROC_DIR, '1', 'cgroup')).read() def inside_docker_container(): # mocked: tests/unit/test_load_kmod.py return os.path.isfile('/.dockerenv') @utils.catch_errors(logger=log_utils.logwarn) def get_load_average(): # type: () -> Optional[Tuple[float, float, float]] loadavg = utils.try_to_read(os.path.join(PROC_DIR, 'loadavg')) if not loadavg: return None m1, m5, m15, _ = loadavg.split(' ', 3) return (float(m1), float(m5), float(m15)) @utils.catch_errors(logger=log_utils.logwarn) def get_mem_info(): # type: () -> Optional[Dict[str, int]] """Returns dict of memory info in kB""" meminfo = utils.try_to_read(os.path.join(PROC_DIR, 'meminfo')) if not meminfo: return None filter_params = ('MemTotal', 'MemFree', 'SwapTotal', 'SwapFree') # optional units are ignored (assumed to be always kB for mem size) return dict((k, int(v)) for k, v in (re.split(r'[\s:]+', line)[:2] for line in meminfo.splitlines()) if k in filter_params) @utils.cached @utils.catch_errors(logger=log_utils.logwarn) def get_cpu_info(): # type: () -> Optional[Dict[str, Any]] cpuinfo = utils.try_to_read(os.path.join(PROC_DIR, 'cpuinfo')) if not cpuinfo: return None cpus = [ dict(re.split(r'\s*:\s*', line) for line in cpu_lines.splitlines()) for cpu_lines in cpuinfo.split('\n\n') # cpu records are separated by an empty line if cpu_lines ] return { 'logical_cores': len(cpus), 'physical_cores': len(set((cpu.get('physical id'), cpu.get('core id')) for cpu in cpus)), 'vendor_id': cpus[0].get('vendor_id'), 'model': int(cpus[0].get('model', 0)), 'model_name': cpus[0].get('model name'), 'cpu_family': int(cpus[0].get('cpu family', 0)), 'stepping': int(cpus[0].get('stepping', 0)), 'microcode': cpus[0].get('microcode'), 'flags': cpus[0].get('flags', '').split(), } @utils.catch_errors(logger=log_utils.logwarn, default_return=0) def get_process_count(): # type: () -> int return len([d for d in os.listdir(PROC_DIR) if d.isdigit()]) @utils.catch_errors(logger=log_utils.logwarn, default_return=0) def get_opened_files_count(): # type: () -> int fd_info = utils.try_to_read(os.path.join(PROC_DIR, 'sys/fs/file-nr')) return int((fd_info or '0').split()[0]) @utils.catch_errors(logger=log_utils.logwarn) def get_vm_count_kvm(): # type: () -> Optional[int] for _root, dirs, _files in os.walk('/sys/kernel/debug/kvm'): return len(dirs) return None @utils.catch_errors(logger=log_utils.logwarn, default_return=(0, 0)) def get_network_connections_count(): # type: () -> Tuple[int, int] """Return tuple of total numbers of TCP and UDP connections""" def conn_records_count(proto): # type: (str) -> int records = utils.try_to_read(os.path.join(PROC_DIR, 'net', proto)) if not records: return 0 return max(len(records.splitlines()) - 1, 0) return conn_records_count('tcp') + conn_records_count('tcp6'), conn_records_count('udp') + conn_records_count('udp6') def get_performance_metrics(): # type: () -> Dict[str, Any] conn_tcp, conn_udp = get_network_connections_count() return { 'load_average': get_load_average(), 'mem_info': get_mem_info(), 'cpu_info': get_cpu_info(), 'vm_count': get_vm_count_kvm(), 'processes': get_process_count(), 'open_files': get_opened_files_count(), 'tcp_connections': conn_tcp, 'udp_connections': conn_udp, } kcarectl/delivery_kit.py000064400000025435152527712570011426 0ustar00# Copyright (c) Cloud Linux Software, Inc # Licensed under CLOUD LINUX LICENSE AGREEMENT # http://cloudlinux.com/docs/LICENCE.TXT import io import json import os import shlex import tarfile import time import uuid from tempfile import NamedTemporaryFile from kcarectl import auth, config, http_utils, ipv6_support, log_utils, utils from kcarectl.process_utils import run_command if False: # pragma: no cover from typing import Any, Dict, List, Optional # noqa: F401 from typing_extensions import Self # noqa: F401 def format_size(num_bytes): # type: (int) -> str """Render a byte count as a short human-readable string for the logs.""" size = float(num_bytes) for unit in ('B', 'KiB', 'MiB', 'GiB'): if size < 1024.0: return '{0:.1f} {1}'.format(size, unit) size /= 1024.0 return '{0:.1f} TiB'.format(size) class DataPackage(object): """Generic archive package for the patch server uploads pipeline. Based on DataPackage from eportal (delivery_kit.py). Subclasses supply the manifest `data_type`, the `upload_uri` to PUT the archive to and the `max_size` payload limit. """ data_type = '' # type: str upload_uri = '' # type: str def __init__(self): # type: () -> None self._tar = None # type: Optional[tarfile.TarFile] self._errors_buffer = [] # type: List[str] self._total_payload_size = 0 @property def max_size(self): # type: () -> int raise NotImplementedError # pragma: no cover @property def archive_path(self): # type: () -> str tar = self._ensure_tar_created() return str(tar.name) def add_stdout(self, arcname, cmd): # type: (str, str) -> None stdout = None stderr = None try: _, stdout, stderr = run_command(shlex.split(cmd), catch_stdout=True, catch_stderr=True) except Exception as e: stderr = str(e) if stderr: self.log_error('failed to dump stdout of {0}:\n{1}'.format(cmd, stderr)) if stdout is not None: self.add_file(arcname, data_bytes=utils.bstr(stdout, encoding='utf-8')) def add_file(self, arcname, src_path=None, data_bytes=None, skip_limit_check=False): # type: (str, Optional[str], Optional[bytes], bool) -> None if src_path is None and data_bytes is None: raise ValueError('No src_path or data_bytes provided') tar = self._ensure_tar_created() entry_size = 0 if src_path is not None: if not os.path.exists(src_path): self.log_error('file not found: {0}'.format(src_path)) return entry_size = os.path.getsize(src_path) else: entry_size = len(data_bytes) # type: ignore[arg-type] if not self._check_required_space(entry_size): self.log_error('no available space to store: {0}'.format(arcname)) return if not skip_limit_check and not self._check_total_payload_limit(entry_size): # a single artifact that would push the report past the size # budget is dropped, not the whole report: warn (so the omission # is visible in kcarectl.log and recorded in errors.log) and skip self.log_warning( 'skipping {0}: {1} would exceed the {2} report size limit (already collected {3})'.format( arcname, format_size(entry_size), format_size(self.max_size), format_size(self._total_payload_size), ) ) return try: if src_path: tar.add(src_path, arcname=arcname) else: info = tarfile.TarInfo(arcname) info.size = entry_size tar.addfile(info, io.BytesIO(data_bytes)) # type: ignore[arg-type] if not skip_limit_check: # entries exempt from the limit (manifest.json, errors.log) # are not counted toward the payload size either self._total_payload_size += entry_size # per-artifact size accounting so an oversized report can be # traced to the item(s) that bloated it (kcarectl.log only) log_utils.loginfo( 'collected {0}: {1} (report total {2})'.format( arcname, format_size(entry_size), format_size(self._total_payload_size), ), print_msg=False, ) except Exception as e: self.log_error('failed to store {0}: {1}'.format(arcname, e)) def add_json(self, arcname, data): # type: (str, Dict[str, Any]) -> None try: data_bytes = utils.bstr(json.dumps(data, indent=4), encoding='utf-8') except TypeError as e: self.log_error('failed to dump {0}:\n{1}'.format(arcname, e)) return self.add_file(arcname, data_bytes=data_bytes) def _check_required_space(self, entry_size): # type: (int) -> bool # here we simplify the check and ignore that the compressed file size will be less statvfs = os.statvfs(self.archive_path) return statvfs.f_frsize * statvfs.f_bfree > entry_size def _check_total_payload_limit(self, entry_size): # type: (int) -> bool # here we simplify the check and ignore that the compressed file size will be less return self.max_size > self._total_payload_size + entry_size def make_manifest(self): # type: () -> Dict[str, Any] return { "schema_version": 1, "type": self.data_type, "time_created": int(time.time()), } def _add_manifest(self): # type: () -> None # manifest.json must always be present (the patch server dispatches # uploads by its `type`), so it bypasses the lenient add_file: write # failures propagate to __enter__ and abort the package creation # instead of being downgraded to errors.log; the entry is exempt # from the payload limit and not counted toward it data_bytes = utils.bstr(json.dumps(self.make_manifest(), indent=4), encoding='utf-8') tar = self._ensure_tar_created() info = tarfile.TarInfo('manifest.json') info.size = len(data_bytes) tar.addfile(info, io.BytesIO(data_bytes)) def log_error(self, error_msg): # type: (str) -> None error_msg = error_msg.strip() log_utils.logerror(error_msg, print_msg=False) self._errors_buffer.append(error_msg) def log_warning(self, warning_msg): # type: (str) -> None # warn to kcarectl.log (print_msg=False: no console noise, like # log_error) and keep the note in the archived errors.log so the # uploaded report records what was dropped warning_msg = warning_msg.strip() log_utils.logwarn(warning_msg, print_msg=False) self._errors_buffer.append(warning_msg) def __enter__(self): # type: () -> Self for compression_mode in ('w:xz', 'w:bz2', 'w:gz'): # pragma: no branch tmpfile = NamedTemporaryFile(suffix='.tar.{0}'.format(compression_mode[2:]), delete=False) tmpfile.close() try: log_utils.loginfo('Creating DataPackage: {0}'.format(tmpfile.name), print_msg=False) # dereference=True mirrors the kcdoctor.sh `dump` (`cat "$1"`): # a symlinked source added via add_file(src_path=...) -- e.g. # /etc/yum.conf -> dnf/dnf.conf on EL8+, /boot/grub2/grub.cfg # on EFI -- is archived by content, not as a dangling link. self._tar = tarfile.open(name=tmpfile.name, mode=compression_mode, dereference=True) self._add_manifest() return self except Exception as err: if self._tar is not None: # the manifest write may fail after a successful open; # don't leak the open handle try: self._tar.close() except Exception: # pragma: no cover pass self._tar = None if os.path.exists(tmpfile.name): # pragma: no branch os.unlink(tmpfile.name) if not isinstance(err, tarfile.CompressionError): raise raise tarfile.CompressionError('No supported compression method found') # pragma: no cover def __exit__(self, exc_type, exc_val, exc_tb): # type: (Optional[type[BaseException]], Optional[BaseException], Any) -> bool if self._errors_buffer: # pragma: no branch errors = '\n'.join(self._errors_buffer) + '\n' self.add_file('errors.log', data_bytes=utils.bstr(errors), skip_limit_check=True) if self._tar: # pragma: no branch self._tar.close() if exc_val: self.remove_archive() return False return True @utils.catch_errors(logger=log_utils.logwarn) def remove_archive(self): # type: () -> None if self._tar and os.path.exists(self.archive_path): # pragma: no branch os.unlink(self.archive_path) def _ensure_tar_created(self): # type: () -> tarfile.TarFile if not self._tar: raise RuntimeError('DataPackage should be used as a context manager') return self._tar def send(self): # type: () -> str """Send the package archive to the patch server. Upload errors propagate to the caller (see the eportal precedent): wrap with utils.catch_errors where a silent failure is acceptable. :return: Upload name (package identifier) """ # flush buffered tar data even when called inside the `with` # block (close() is a no-op on an already closed tar) self._ensure_tar_created().close() # Generate a unique package name # Use find('.') to get extension from first dot to preserve .tar.xz/.tar.bz2/.tar.gz basename = os.path.basename(self.archive_path) ext = basename[basename.find('.') :] if '.' in basename else '' upload_name = str(uuid.uuid4()) + ext upload_url = ipv6_support.get_patch_server() + self.upload_uri + upload_name http_utils.upload_file( self.archive_path, upload_url=upload_url, auth_string=auth.get_http_auth_string(), ) return upload_name class KernelAnomalyPackage(DataPackage): data_type = 'kernel-anomaly' upload_uri = '/upload/kernel-anomaly/' @property def max_size(self): # type: () -> int return config.KERNEL_ANOMALY_REPORT_MAX_SIZE_BYTES kcarectl/http_utils.py000064400000016105152527712570011125 0ustar00# Copyright (c) Cloud Linux Software, Inc # Licensed under CLOUD LINUX LICENSE AGREEMENT # http://cloudlinux.com/docs/LICENCE.TXT import errno import os import socket import ssl from ssl import SSLError if False: # pragma: no cover from typing import Optional, Union # noqa: F401 from . import config, constants, errors, log_utils, utils from .py23 import HTTPError, Request, URLError, httplib, std_urlopen, urlparse def urlopen_base(url, *args, **kwargs): # mocked: tests/unit if hasattr(url, 'get_full_url'): request_url = url.get_full_url() else: request_url = url url = Request(url) headers = kwargs.pop('headers', {}) headers.update( { 'KC-Version': constants.VERSION, 'KC-Patch-Version': constants.KC_PATCH_VERSION, } ) for header, value in headers.items(): url.add_header(header, value) log_utils.logdebug("Requesting url: `{0}`. Headers: {1}".format(request_url, headers)) try: if 'timeout' not in kwargs: kwargs['timeout'] = config.HTTP_TIMEOUT # bandit warns about use of file: in urlopen which can happen here but is secure if not config.CHECK_SSL_CERTS and getattr(ssl, 'HAS_SNI', None): # pragma: no cover unit ctx = ssl.create_default_context() ctx.check_hostname = False ctx.verify_mode = ssl.CERT_NONE kwargs['context'] = ctx return std_urlopen(url, *args, **kwargs) # nosec B310 return std_urlopen(url, *args, **kwargs) # nosec B310 except HTTPError as ex: if ex.code == 404: raise errors.NotFound(ex.url, ex.code, ex.msg, ex.hdrs, ex.fp) # HTTPError is a URLError descendant and contains URL, raise it as is raise except URLError as ex: # Local patches OSError(No such file) should be interpreted as Not found(404) # It was done as a chain because when it implemented with "duck-typing" it will mess # with error context if ex.args and hasattr(ex.args[0], 'errno') and ex.args[0].errno == errno.ENOENT: raise errors.NotFound(url, 404, str(ex), None, None) # type: ignore[arg-type] # there is no information about URL in the base URLError class, add it and raise ex.reason = 'Request for `{0}` failed: {1}'.format(request_url, ex) ex.url = request_url # type: ignore[attr-defined] raise def check_urlopen_retry_factory(retry_on_500=True): def check_function(e, state): if isinstance(e, HTTPError): return retry_on_500 and e.code >= 500 elif isinstance(e, (URLError, httplib.HTTPException, SSLError, socket.timeout)): return True elif hasattr(e, 'args') and len(e.args) == 2 and e.args[0] == errno.ECONNRESET: # pragma: no cover unit # SysCallError "Connection reset by peer" from PyOpenSSL return True return check_function def is_local_url(url): if hasattr(url, 'get_full_url'): url = url.get_full_url() return url.startswith('file:') def urlopen(url, *args, **kwargs): retry_on_500 = kwargs.pop('retry_on_500', True) retry_count = kwargs.pop('retry_count', constants.RETRY_COUNT) if is_local_url(url): return urlopen_base(url, *args, **kwargs) return utils.retry(check_urlopen_retry_factory(retry_on_500=retry_on_500), count=retry_count)(urlopen_base)(url, *args, **kwargs) def http_request(url, auth_string, auth_token=None, method=None): request = Request(url, method=method) if not config.UPDATE_FROM_LOCAL and auth_string: request.add_header('Authorization', 'Basic {0}'.format(auth_string)) if not config.UPDATE_FROM_LOCAL and auth_token: request.add_header(constants.AUTH_TOKEN_HEADER, auth_token) return request def get_proxy_from_env(scheme): if scheme == 'http': return os.getenv('http_proxy') or os.getenv('HTTP_PROXY') elif scheme == 'https': return os.getenv('https_proxy') or os.getenv('HTTPS_PROXY') def proxy_is_used(): return bool(get_proxy_from_env('http')) or bool(get_proxy_from_env('https')) check_urlopen_retry = check_urlopen_retry_factory() @utils.retry(check_retry=check_urlopen_retry) def upload_file(file_path, upload_url, auth_string=None): # type: (str, str, Optional[str]) -> None """Upload a file to the given URL using HTTP PUT with chunked streaming. Note: The standard library urllib doesn't support PUT with data We need to use httplib directly for this This function uses streaming upload to support large files up to 1GB without loading the entire file into memory. :param file_path: Path to the file to upload :param upload_url: Full URL to upload the file to. Query params are ignored. :param auth_string: Optional authentication string for Basic Auth :return: None if upload succeeded :raises HTTPError: If upload fails with HTTP status >= 400 :raises ValueError: If URL is invalid """ file_size = os.path.getsize(file_path) if not file_size: raise ValueError('Refusing to upload empty file: {0}'.format(file_path)) parsed = urlparse(utils.nstr(upload_url)) host = parsed.hostname port = parsed.port url_path = parsed.path or '/' if host is None: raise ValueError('Invalid URL: missing hostname') if port is None: # pragma: no cover port = 443 if parsed.scheme == 'https' else 80 if parsed.scheme == 'http': conn_cls = httplib.HTTPConnection # type: Union[type[httplib.HTTPConnection], type[httplib.HTTPSConnection]] elif parsed.scheme == 'https': # pragma: no cover conn_cls = httplib.HTTPSConnection else: raise ValueError('Invalid URL: unsupported scheme') conn = conn_cls(host, port, timeout=config.HTTP_UPLOAD_TIMEOUT) headers = {} if auth_string: headers['Authorization'] = 'Basic {0}'.format(auth_string) headers['Content-Type'] = 'application/octet-stream' headers['Content-Length'] = str(file_size) headers['KC-Version'] = constants.VERSION try: # Use the lower-level API to support streaming conn.putrequest('PUT', url_path) # Send headers for header, value in headers.items(): conn.putheader(header, value) conn.endheaders() # Stream file data in chunks with open(file_path, 'rb') as f: conn.send(f) response = conn.getresponse() if response.status >= 400: # Read response body for error details try: error_body = response.read() except Exception: # pragma: no cover error_body = None error_msg = 'Failed to upload file: HTTP {0}'.format(response.status) if error_body is not None: # pragma: no branch error_msg += ' - {0}'.format(error_body) # type: ignore[str-bytes-safe] log_utils.logerror(error_msg) # HTTPError compatible with retry mechanism raise HTTPError(upload_url, response.status, error_msg, None, None) # type: ignore[arg-type] finally: conn.close() kcarectl/log_utils.py000064400000005576152527712570010741 0ustar00# Copyright (c) Cloud Linux Software, Inc # Licensed under CLOUD LINUX LICENSE AGREEMENT # http://cloudlinux.com/docs/LICENCE.TXT from __future__ import print_function import logging import logging.handlers import os import sys import traceback from . import config, constants kcarelog = logging.getLogger('kcare') # mocked: tests/unit def logdebug(message): # type: (str) -> None _printlvl(message, constants.PRINT_DEBUG) kcarelog.debug(message) def loginfo(message, print_msg=True): # type: (str, bool) -> None if print_msg: _printlvl(message, constants.PRINT_INFO) kcarelog.info(message) def logwarn(message, print_msg=True): # type: (str, bool) -> None if print_msg: _printlvl(message, constants.PRINT_WARN, file=sys.stderr) # pragma: no cover kcarelog.warning(message) def logerror(message, print_msg=True): if print_msg: _printlvl(message, constants.PRINT_ERROR, file=sys.stderr) kcarelog.error(message) def logexc(message, print_msg=True): if print_msg and constants.PRINT_ERROR >= config.PRINT_LEVEL: traceback.print_exc() kcarelog.exception(message) def _printlvl(message, level, file=None): if level >= config.PRINT_LEVEL: print(message, file=file) # noqa: T201 def get_syslog_handler(): syslog_formatter = logging.Formatter('kcare %(levelname)s: %(message)s') syslog_handler = logging.handlers.SysLogHandler(address='/dev/log', facility=logging.handlers.SysLogHandler.LOG_USER) syslog_handler.setLevel(logging.INFO) syslog_handler.setFormatter(syslog_formatter) return syslog_handler def get_kcare_handler(level): kcare_formatter = logging.Formatter('%(asctime)s %(levelname)s: %(message)s') if os.getuid() == 0: kcare_handler = logging.handlers.RotatingFileHandler( constants.LOG_FILE, maxBytes=1024**2, backupCount=2 ) # type: logging.Handler # We need at least INFO level logs at all times kcare_handler.setLevel(min(level, logging.INFO)) kcare_handler.setFormatter(kcare_formatter) return kcare_handler else: kcare_handler = logging.StreamHandler() kcare_handler.setLevel(level) kcare_handler.setFormatter(kcare_formatter) return kcare_handler def initialize_logging(level): kcarelog.handlers[:] = [] try: kcare_handler = get_kcare_handler(level) kcarelog.addHandler(kcare_handler) except Exception as ex: kcarelog.exception(ex) if os.path.exists('/dev/log'): try: syslog_handler = get_syslog_handler() kcarelog.addHandler(syslog_handler) except Exception as ex: kcarelog.exception(ex) def print_cln_http_error(ex, url=None, stdout=True): url = url or '' logerror('Unable to fetch {0}. Please try again later (error: {1})'.format(url, str(ex)), stdout) kcarectl/selinux.py000064400000003264152527712570010417 0ustar00# Copyright (c) Cloud Linux Software, Inc # Licensed under CLOUD LINUX LICENSE AGREEMENT # http://cloudlinux.com/docs/LICENCE.TXT import os from . import errors, log_utils, process_utils, utils def selinux_safe_tmpname(fname): head, tail = os.path.split(fname) return os.path.join(head, 'tmp.' + tail) def restore_selinux_context(dname): if is_selinux_enabled(): # Try to restore selinux context cmd = [process_utils.find_cmd('restorecon', ('/usr/sbin', '/sbin')), '-R', dname] code, _, stderr = process_utils.run_command(cmd, catch_stdout=True, catch_stderr=True) if code: log_utils.logerror( "SELinux context restoration for {0} failed with {1}: {2}".format(dname, code, stderr), print_msg=False ) def is_selinux_module_present(semodule_name): code, out, err = process_utils.run_command(['/usr/sbin/semodule', '-l'], catch_stdout=True) if code: raise errors.KcareError("SELinux modules list gathering error: '{0}' {1}".format(err, code), status='selinux modules error') for line in out.split('\n'): if semodule_name in line: return True return False def skip_if_no_selinux_module(clbl): def wrapper(*args, **kwargs): if is_selinux_enabled() and not is_selinux_module_present('libcare'): raise errors.KcareError('SELinux is enabled but libcare policy module is not loaded') return clbl(*args, **kwargs) return wrapper @utils.cached def is_selinux_enabled(): if os.path.isfile('/usr/sbin/selinuxenabled'): code, _, _ = process_utils.run_command(['/usr/sbin/selinuxenabled']) else: return False return code == 0 kcarectl/utils.py000064400000021533152527712570010067 0ustar00# Copyright (c) Cloud Linux Software, Inc # Licensed under CLOUD LINUX LICENSE AGREEMENT # http://cloudlinux.com/docs/LICENCE.TXT from __future__ import print_function import fnmatch import functools import os import random import re import shutil import tempfile import time from datetime import datetime from . import constants if False: # pragma: no cover from typing import Any, Callable, Dict, List, Optional, Set, Tuple, TypeVar, Union # noqa: F401 T = TypeVar('T', bound=Callable[..., Any]) VERSION_RE = re.compile(r'^(\d+[.]\d+[-]\d+)') CACHE_ENTRIES = 3 ntype = type('') btype = type(b'') utype = type(u'') def atomic_write(fname, content, ensure_dir=False, mode='w', create_mode=0o644): # type: (str, Union[str, bytes], bool, str, int) -> None dname = os.path.dirname(fname) if ensure_dir and not os.path.exists(dname): os.makedirs(dname) try: st_mode = os.stat(fname).st_mode except Exception: st_mode = create_mode with tempfile.NamedTemporaryFile(mode=mode, dir=dname, prefix=os.path.basename(fname) + '.', delete=False) as f: os.fchmod(f.fileno(), st_mode) f.write(content) f.flush() os.fsync(f.fileno()) tmp_fname = f.name # ensure folder is also updated # https://www.quora.com/When-should-you-fsync-the-containing-directory-in-addition-to-the-file-itself # https://www.reddit.com/r/kernel/comments/1du6ot8/comment/lbgu46i/?utm_source=share&utm_medium=web3x&utm_name=web3xcss&utm_term=1&utm_content=share_button folder_fd = os.open(dname, os.O_RDONLY) try: os.fsync(folder_fd) finally: os.close(folder_fd) os.rename(tmp_fname, fname) def nstr(data, encoding='utf-8'): # pragma: no py2 cover # type: (Union[str, bytes, None], str) -> str if type(data) is ntype: return data elif type(data) is btype: return data.decode(encoding) else: return data.encode(encoding) # type: ignore # pragma: no py3 cover def bstr(data, encoding='latin1'): # pragma: no py2 cover # type: (Union[str, bytes], str) -> bytes if type(data) is utype: data = data.encode(encoding) return data # type: ignore def ustr(data, encoding='latin1'): # pragma: no py2 cover # type: (Union[str, bytes], str) -> str if type(data) is btype: data = data.decode(encoding) return data # type: ignore def cached(fn): # type: (T) -> Any cache = {} # type: dict[tuple[Any, ...], Any] @functools.wraps(fn) def inner(*args, **kwargs): # type: (Any, Any) -> Any cache_key = (args, tuple(sorted(kwargs.items()))) try: return cache[cache_key] except KeyError: pass result = cache[cache_key] = fn(*args, **kwargs) return result inner.cache = cache # type: ignore[attr-defined] inner.clear = cache.clear # type: ignore[attr-defined] inner.orig = fn # type: ignore[attr-defined] return inner def retry(check_retry, count=None, delay=None, backoff=None): # type: (Callable[[Exception, dict[str, Any]], bool], Optional[int], Optional[float], Optional[float]) -> Callable[..., Any] if delay is None: delay = constants.RETRY_DELAY if count is None: count = constants.RETRY_COUNT if backoff is None: backoff = constants.RETRY_BACKOFF state = {} # type: dict[str, Any] def decorator(fn): # type: (Callable[..., Any]) -> Callable[..., Any] def inner(*args, **kwargs): # type: (Any, Any) -> Any ldelay = delay for _ in range(count): try: return fn(*args, **kwargs) except Exception as ex: if not check_retry(ex, state): raise time.sleep(ldelay) # bandit warns about using random.uniform for security which is not the case here ldelay = min(ldelay * random.uniform(1, backoff), constants.RETRY_MAX_DELAY) # nosec B311 # last try try: return fn(*args, **kwargs) except Exception as final_ex: setattr(final_ex, 'attempts', count) raise return inner return decorator def clean_directory(directory, exclude_path=None, keep_n=CACHE_ENTRIES, pattern=None): # type: (str, Optional[str], int, Optional[str]) -> None if not os.path.exists(directory): return data = [] items = os.listdir(directory) if pattern is not None: items = fnmatch.filter(items, pattern) for item in items: full_path = os.path.join(directory, item) if full_path != exclude_path: data.append((os.stat(full_path).st_mtime, full_path)) data.sort(reverse=True) for _, entry in data[keep_n:]: if os.path.isfile(entry) or os.path.islink(entry): os.remove(entry) else: shutil.rmtree(entry) def clear_all_cache(): # type: () -> None clean_directory(os.path.join(constants.PATCH_CACHE, 'modules'), keep_n=0) clean_directory(os.path.join(constants.PATCH_CACHE, 'patches'), keep_n=0) if os.path.exists(constants.CACHE_KEY_DUMP_PATH): os.unlink(constants.CACHE_KEY_DUMP_PATH) def save_to_file(response, dst): # type: (Any, str) -> None parent_dir = os.path.dirname(dst) if not os.path.exists(parent_dir): os.makedirs(parent_dir) with open(dst, 'wb') as f: shutil.copyfileobj(response, f) f.flush() os.fsync(f.fileno()) def strip_version_timestamp(version): # type: (str) -> str match = VERSION_RE.match(version) return match and match.group(1) or version def parse_response_date(str_raw): # type: (str) -> datetime # Try to split it by T str_date, sep, _ = str_raw.partition('T') # No success - split by space if not sep: str_date, _, _ = str_raw.partition(' ') return datetime.strptime(str_date, '%Y-%m-%d') def get_patch_server_url(*parts): # type: (*str) -> str from . import ipv6_support # TODO fix circular import return '/'.join(it.strip('/') for it in filter(None, (ipv6_support.get_patch_server(),) + parts)) def try_to_read(filename): # type: (str) -> Optional[str] if not os.path.exists(filename): return None with open(filename) as f: return f.read().strip() @cached def get_cache_key(): # type: () -> Optional[str] return try_to_read(constants.CACHE_KEY_DUMP_PATH) def _read_file(fname, mode, default): # type: (str, str, Optional[Union[str, bytes]]) -> Union[str, bytes] if not os.path.exists(fname): return default # type: ignore with open(fname, mode) as f: return f.read() # type: ignore def read_file(fname, default=None): # type: (str, Optional[str]) -> str result = _read_file(fname, 'r', default) # type: str # type: ignore[assignment] return result def read_file_bin(fname, default=None): # type: (str, Optional[bytes]) -> bytes result = _read_file(fname, 'rb', default) # type: bytes # type: ignore[assignment] return result def data_as_dict(data): # type: (str) -> dict[str, str] result = {} data_lines = data.splitlines() # type: list[str] for line in data_lines: if line: key, delimiter, value = line.partition(':') if delimiter: result[key] = value.strip() return result def extract_unique_cves(patches, cve_field='cve'): # type: (List[Dict[str, Any]], str) -> Set[str] """ Extract unique CVEs from a list of patches. Args: patches: List of patch dictionaries cve_field: Field name to extract CVE from (default 'cve' for userspace, 'kpatch-cve' for kernel) Returns: Set of unique CVE strings """ unique_cves = set() for patch in patches: cve = patch.get(cve_field) if cve: # only add non-empty CVEs unique_cves.add(cve) return unique_cves def timestamp_str(): # type: () -> str return str(int(time.time())) def print_wrapper(*values): # type: (object) -> None """a workaround to fix T201""" print(*values) # noqa: T201 def catch_errors(logger=None, errors=(Exception,), default_return=None): # type: (Optional[Callable[[str], None]], Tuple[Any, ...], Any) -> Callable[..., Any] def decorator(fn): # type: (T) -> Callable[..., Any] @functools.wraps(fn) def inner(*args, **kwargs): # type: (Any, Any) -> Any try: return fn(*args, **kwargs) except errors as e: if logger: # pragma: no branch arg_list = [str(a) for a in args] + ['{0}={1}'.format(k, v) for k, v in kwargs.items()] logger('{0}({1}) failed: {2}'.format(fn.__name__, ', '.join(arg_list), e)) return default_return return inner return decorator kcarectl/server_info.py000064400000007350152527712570011251 0ustar00# Copyright (c) Cloud Linux Software, Inc # Licensed under CLOUD LINUX LICENSE AGREEMENT # http://cloudlinux.com/docs/LICENCE.TXT import base64 import json import os import platform import time import zlib from . import capabilities, constants, http_utils, kcare, platform_utils, serverid, update_utils, utils if False: # pragma: no cover from typing import Any, Dict # noqa: F401 def server_info(reason, now=None, secure_boot_info=False, perf_metrics=False): data = dict() # type: Dict[str, Any] data['ts'] = int(now or time.time()) data['reason'] = reason data['machine'] = platform.machine() data['processor'] = platform.processor() data['release'] = platform.release() data['system'] = platform.system() data['version'] = platform.version() distro = platform_utils.get_distro() data['distro'] = distro[0] data['distro_version'] = distro[1] data['euname'] = kcare.kcare_uname() data['kcare_version'] = utils.strip_version_timestamp(constants.VERSION) data['last_stop'] = kcare.get_last_stop() data['node'] = platform_utils.get_hostname() data['uptime'] = platform_utils.get_uptime() data['virt'] = platform_utils.get_virt() data['proxy'] = http_utils.proxy_is_used() description = kcare.parse_patch_description(kcare.loaded_patch_description()) data['ltimestamp'] = description['last-update'] data['patch_level'] = description['patch-level'] data['patch_type'] = description['patch-type'] data['kmod'] = kcare.get_current_kmod_version() or '' data['crashreporter_ts'] = kcare.crashreporter_latest_event_timestamp() data['kdump_status'] = kcare.kdump_status() data['capabilities'] = capabilities.get_kc_capabilites_bits() try: data['kdump_ts'] = kcare.kdumps_latest_event_timestamp() except Exception: # Not critical data pass server_id = serverid.get_serverid() if server_id: data['server_id'] = server_id state = kcare.get_state() if state is not None: data['state'] = state data['update_error'] = update_utils.read_update_error('kernel') if secure_boot_info: data['secure_boot'] = platform_utils.secure_boot_info() if perf_metrics: data['perf_metrics'] = platform_utils.get_performance_metrics() return data def server_lib_info(reason, patch_level, now=None): data = dict() # type: Dict[str, Any] data['ts'] = int(now or time.time()) data['reason'] = reason data['patch_level'] = patch_level distro = platform_utils.get_distro() data['distro'] = distro[0] data['distro_version'] = distro[1] data['machine'] = platform.machine() data['kcare_version'] = utils.strip_version_timestamp(constants.VERSION) data['node'] = platform_utils.get_hostname() data['uptime'] = platform_utils.get_uptime() data['virt'] = platform_utils.get_virt() data['capabilities'] = capabilities.get_lc_capabilites_bits() stop_ts = 0.0 if os.path.exists('/var/lib/libcare/stop'): stop_ts = os.path.getctime('/var/lib/libcare/stop') data['stop_ts'] = stop_ts data['update_error'] = update_utils.read_update_error('libcare') return data def encoded_server_lib_info(reason, patch_level, now=None): info = server_lib_info(reason=reason, patch_level=patch_level, now=now) return encode_checkin_payload(info, b64_encoding=True) def encode_checkin_payload(data, b64_encoding): # type: (Dict[str, Any], bool) -> str if b64_encoding: data_str = json.dumps(data, ensure_ascii=False, separators=(',', ':')) return utils.nstr(base64.urlsafe_b64encode(zlib.compress(utils.bstr(data_str, 'utf-8')))) else: # legacy serialization return utils.nstr(base64.b16encode(utils.bstr(str(data)))) kcarectl/kcare.py000064400000024714152527712570010020 0ustar00# Copyright (c) Cloud Linux Software, Inc # Licensed under CLOUD LINUX LICENSE AGREEMENT # http://cloudlinux.com/docs/LICENCE.TXT import ast import errno import glob import hashlib import json import os import platform import subprocess from . import config, constants, log_utils, process_utils, utils from .errors import SafeExceptionWrapper from .py23 import json_loads_nstr if False: # pragma: no cover from typing import List, Optional, Tuple # noqa: F401 UNAME_LABEL = 'uname: ' def is_uname_char(c): # type: (str) -> bool return str.isalnum(c) or c in '.-_+' def parse_uname(patch_level): khash = get_kernel_hash() with open(get_cache_path(khash, patch_level, config.PATCH_INFO), 'r') as f: for line in f.readlines(): if line.startswith(UNAME_LABEL): return ''.join(filter(is_uname_char, line[len(UNAME_LABEL) :].strip())) return '' def kcare_update_effective_version(new_version): if os.path.exists(config.KCARE_UNAME_FILE): try: f = open(config.KCARE_UNAME_FILE, 'w') f.write(new_version) f.close() return True except Exception: pass return False def get_kernel_hash(): # type: () -> str f = open(config.KERNEL_VERSION_FILE, 'rb') try: # sha1 is not used for security, turn off bandit warning # bandit issues a warning that B324 has no test when `nosec B324` is # set here. Using broad `nosec` here to bypass the warning. return hashlib.sha1(f.read()).hexdigest() # nosec B324 finally: f.close() def get_last_stop(): # type: () -> str """Returns timestamp from PATCH_CACHE/stoped.at if its exsits""" stopped_at_filename = os.path.join(constants.PATCH_CACHE, 'stopped.at') if os.path.exists(stopped_at_filename): with open(stopped_at_filename, 'r') as fh: value = fh.read().rstrip() try: int(value) except ValueError: return str(int(os.path.getctime(stopped_at_filename))) except Exception: # pragma: no cover, it should not happen return 'error' return value return '-1' def get_cache_path(khash, plevel, fname): prefix = config.PREFIX or 'none' ptype = config.PATCH_TYPE or 'default' patch_dir = '-'.join([prefix, khash, str(plevel), ptype]) result = (constants.PATCH_CACHE, 'patches', patch_dir) # type: Tuple[str, ...] if fname: result += (fname,) return os.path.join(*result) def get_kernel_prefixed_url(*parts): return utils.get_patch_server_url(config.PREFIX, *parts) class BaseKernelPatchLevel(int): def cache_path(self, *parts): return get_cache_path(self.khash, str(self), *parts) # type: ignore[attr-defined] def as_dict(self): return { 'level': self.level, 'khash': self.khash, 'baseurl': self.baseurl, 'release': self.release, } class KernelPatchLevel(BaseKernelPatchLevel): def __new__(cls, khash, level, baseurl, release=None): return super(cls, cls).__new__(cls, level) def __init__(self, khash, level, baseurl, release=None): self.level = level self.khash = khash self.baseurl = baseurl self.release = release def kmod_url(self, *parts): return utils.get_patch_server_url(self.baseurl, self.khash, *parts) def file_url(self, *parts): return utils.get_patch_server_url(self.baseurl, self.khash, str(self), *parts) class LegacyKernelPatchLevel(BaseKernelPatchLevel): def __new__(cls, khash, level): try: return super(cls, cls).__new__(cls, level) except ValueError as exc: # common error with this class raise SafeExceptionWrapper(exc) def __init__(self, khash, level): self.level = level self.khash = khash self.baseurl = None self.release = None def kmod_url(self, *parts): if 'patches.kernelcare.com' in config.PATCH_SERVER: return get_kernel_prefixed_url(self.khash, str(self), *parts) # ePortal workaround, it doesn't support leveled links to kmod return get_kernel_prefixed_url(self.khash, *parts) def file_url(self, *parts): return get_kernel_prefixed_url(self.khash, str(self), *parts) def upgrade(self, baseurl): return KernelPatchLevel(self.khash, int(self), baseurl) def dump_kernel_patch_level(kernel_patch_level): # type: (BaseKernelPatchLevel) -> None try: with open(os.path.join(constants.PATCH_CACHE, 'kernel_patch_level.json'), 'w') as f: json.dump(kernel_patch_level.as_dict(), f) except Exception: log_utils.logexc('failed to dump kernel patch level', print_msg=False) def read_dumped_kernel_patch_level(): try: with open(os.path.join(constants.PATCH_CACHE, 'kernel_patch_level.json')) as f: return json_loads_nstr(f.read()) except Exception: log_utils.logexc('failed to read dumped kernel patch level', print_msg=False) def sort_files_by_ctime(files_list): # type: (List[str]) -> List[Tuple[str, float]] return sorted( [(it, os.path.getctime(it)) for it in files_list], key=lambda pair: pair[1], reverse=True, ) def get_kdump_root(): # type: () -> str kdump_path = "/var/crash" if not os.path.isfile("/etc/kdump.conf"): return kdump_path with open("/etc/kdump.conf") as kdump_conf: for line in kdump_conf: line = line.strip() if line.startswith('path '): _, kdump_path = line.split(None, 1) return kdump_path def list_kdump_vcore_files(): # type: () -> List[str] kdump_root = get_kdump_root() if not os.path.isdir(kdump_root): return [] return glob.glob(os.path.join(kdump_root, '*/vmcore')) def list_kdump_txt_files(): # type: () -> List[str] kdump_root = get_kdump_root() if not os.path.isdir(kdump_root): return [] return glob.glob(os.path.join(kdump_root, '*/*.txt')) def list_crashreporter_log_files(): # type: () -> List[str] if not os.path.isdir(config.KDUMPS_DIR): return [] return glob.glob(os.path.join(config.KDUMPS_DIR, '*.log')) def list_crashreporter_artifacts(): # type: () -> List[str] if not os.path.isdir(config.KDUMPS_DIR): return [] return [os.path.join(config.KDUMPS_DIR, it) for it in os.listdir(config.KDUMPS_DIR)] @utils.cached def kdumps_latest_event_timestamp(): # type: () -> Optional[float] kdumps = list_kdump_vcore_files() if not kdumps: return None return sort_files_by_ctime(kdumps)[0][1] @utils.cached def kdump_status(): if constants.SKIP_SYSTEMCTL_CHECK or os.path.isfile(constants.SYSTEMCTL): _, stdout, _ = process_utils.run_command([constants.SYSTEMCTL, 'is-active', 'kdump'], catch_stdout=True, catch_stderr=True) return stdout.strip() return 'systemd-absent' @utils.cached def crashreporter_latest_event_timestamp(): # type: () -> Optional[float] artifacts = list_crashreporter_artifacts() if not artifacts: return None return sort_files_by_ctime(artifacts)[0][1] def get_current_kmod_version(): kmod_version_file = '/sys/module/kcare/version' if not os.path.exists(kmod_version_file): return with open(kmod_version_file, 'r') as f: version = f.read().strip() return version def is_kmod_version_changed(khash, plevel): old_version = get_current_kmod_version() if not old_version: return True new_version = process_utils.check_output( ['/sbin/modinfo', '-F', 'version', get_cache_path(khash, plevel, constants.KMOD_BIN)] ).strip() return old_version != new_version def kcare_uname_su(): patch_level = loaded_patch_level() if not patch_level: return platform.release() return parse_uname(patch_level) def kcare_uname(): if os.path.exists(config.KCARE_UNAME_FILE): return open(config.KCARE_UNAME_FILE, 'r').read().strip() else: # TODO: talk to @kolshanov about runtime results from KPATCH_CTL info # (euname from kpatch-description -- not from kpatch.info file) return kcare_uname_su() def loaded_patch_level(): # mocked: tests/unit pl = parse_patch_description(loaded_patch_description())['patch-level'] if pl: try: int(pl) except ValueError as e: raise SafeExceptionWrapper(e, 'Unexpected patch state', _patch_info()) return LegacyKernelPatchLevel(get_kernel_hash(), pl) def _patch_info(): try: return process_utils.check_output([constants.KPATCH_CTL, 'info'], check=True) except subprocess.CalledProcessError as e: if e.returncode == errno.EBUSY: raise return '' @utils.cached def get_loaded_modules(): try: return [line.split()[0] for line in open('/proc/modules')] except (OSError, IOError) as ex: log_utils.logerror('Error getting loaded modules list: ' + str(ex), print_msg=False) return [] def loaded_patch_description(): if 'kcare' not in get_loaded_modules(): return None # example: 28-:1532349972;4.4.0-128.154 # (patch level: number)-(patch type: free/extra/empty):(timestamp);(effective kernel version from kpatch.info) return get_patch_value(_patch_info(), 'kpatch-description') def get_patch_value(info, label): return utils.data_as_dict(info).get(label) def parse_patch_description(desc): result = {'patch-level': None, 'patch-type': 'default', 'last-update': '', 'kernel-version': ''} if not desc: return result level_type_timestamp, _, kernel = desc.partition(';') level_type, _, timestamp = level_type_timestamp.partition(':') patch_level, _, patch_type = level_type.partition('-') # need to return patch_level=None not to break old code # TODO: refactor all loaded_patch_level() usages to work with empty string instead of None result['patch-level'] = patch_level or None result['patch-type'] = patch_type or 'default' result['last-update'] = timestamp result['kernel-version'] = kernel return result def get_state(): state_file = os.path.join(constants.PATCH_CACHE, 'kcare.state') if os.path.exists(state_file): with open(state_file, 'r') as f: try: state = f.read() return ast.literal_eval(state) except (SyntaxError, OSError, ValueError, TypeError, UnicodeDecodeError): pass kcarectl/anomaly.py000064400000011771152527712570010372 0ustar00# Copyright (c) Cloud Linux Software, Inc # Licensed under CLOUD LINUX LICENSE AGREEMENT # http://cloudlinux.com/docs/LICENCE.TXT import os import time from kcarectl import delivery_kit, kcare, log_utils, utils if False: # pragma: no cover from typing import Any, Dict, List # noqa: F401 @utils.catch_errors(logger=log_utils.logwarn) def send_data_package(data_package): # type: (delivery_kit.DataPackage) -> str """Send the DataPackage archive to the patch server. Upload errors are logged and swallowed (catch_errors), preserving the historical kernel-anomaly behavior. :param data_package: DataPackage instance to send :return: Upload name (package identifier) """ return data_package.send() def copy_recent_files(files, data_package, archive_prefix): # type: (List[str], delivery_kit.DataPackage, str) -> None """adds recent files for the last hour to the given data package starting from the newest one""" now = time.time() for path, ctime in kcare.sort_files_by_ctime(files): if now - ctime > 3600: break arcname = '{0}/{1}'.format(archive_prefix, path.replace('/', '_')) data_package.add_file(arcname, src_path=path) @utils.catch_errors(logger=log_utils.logwarn) def prepare_kernel_anomaly_report(server_info): # pragma: no cover # type: (Dict[str, Any]) -> delivery_kit.KernelAnomalyPackage data_package = delivery_kit.KernelAnomalyPackage() with data_package: if os.path.exists('/var/log/messages'): data_package.add_stdout('messages', 'tail -n10000 /var/log/messages') if os.path.exists('/var/log/syslog'): data_package.add_stdout('syslog', 'tail -n10000 /var/log/syslog') data_package.add_stdout('kcarectl.log', 'tail -n10000 /var/log/kcarectl.log') data_package.add_stdout('dmesg', 'dmesg') data_package.add_stdout('ls_var_cache_kcare', 'ls -lR /var/cache/kcare/') if os.path.exists('/usr/bin/rpm') or os.path.exists('/bin/rpm'): packages_cmd = r'rpm -q -a --queryformat="%{N}|%{V}-%{R}|%{arch}|%{INSTALLTIME:date}\n"' elif os.path.exists('/usr/bin/dpkg'): packages_cmd = r'/usr/bin/dpkg-query -W -f "${binary:Package}|${Version}|${Architecture}\n"' data_package.add_file('dpkg.log', src_path='/var/log/dpkg.log') else: packages_cmd = 'echo "unknown package manager"' data_package.add_stdout('packages.list', packages_cmd) # tar fails to add files from /proc directly so we first read them to memory with open('/proc/version') as f: data_package.add_file('proc_version', data_bytes=utils.bstr(f.read())) with open('/proc/modules') as f: data_package.add_file('proc_modules', data_bytes=utils.bstr(f.read())) data_package.add_file('kcare.conf', src_path='/etc/sysconfig/kcare/kcare.conf') data_package.add_json('server_info.json', server_info) # kdump data_package.add_file('kdump.conf', src_path='/etc/kdump.conf') data_package.add_stdout('ls_kdump', 'ls -lR {0}'.format(kcare.get_kdump_root())) try: copy_recent_files(kcare.list_kdump_txt_files(), data_package, 'kdump') except Exception as e: data_package.log_error('failed to copy kdumps:\n{0}'.format(e)) try: copy_recent_files(kcare.list_crashreporter_log_files(), data_package, 'crashreporter') except Exception as e: data_package.log_error('failed to copy crashreporter artifacts:\n{0}'.format(e)) return data_package @utils.catch_errors(logger=log_utils.logwarn, default_return=False) def detect_anomaly(server_info): # type: (Dict[str, Any]) -> bool """taken from eportal - anomalies::detect_agent_reboot""" reason = server_info['reason'] # type: str uptime = int(server_info['uptime']) patch_level = int(server_info.get('patch_level') or '-1') last_stop = int(server_info['last_stop']) ts = server_info['ts'] # type: int try: state_ts = int(server_info['state']['ts']) except (KeyError, TypeError, ValueError): state_ts = 0 fields = [reason, uptime, patch_level, state_ts, last_stop, ts] if not all(fields): return False first_update_after_reboot_marker = False crash_soon_after_update_marker = False no_proper_shutdown_marker = False if uptime < 300 and patch_level == -1: first_update_after_reboot_marker = True if (ts - uptime) > state_ts > (ts - uptime - 1800) and reason == 'update': crash_soon_after_update_marker = True if last_stop < state_ts: # pragma: no branch no_proper_shutdown_marker = True markers = [ first_update_after_reboot_marker, crash_soon_after_update_marker, no_proper_shutdown_marker, ] if all(markers): log_utils.loginfo('Agent anomaly detected: {0}'.format(server_info)) return True return False if __name__ == '__main__': # pragma: no cover prepare_kernel_anomaly_report({}) kcarectl/__init__.py000064400000235052152527712570010471 0ustar00# Copyright (c) Cloud Linux Software, Inc # Licensed under CLOUD LINUX LICENSE AGREEMENT # http://cloudlinux.com/docs/LICENCE.TXT from __future__ import print_function import base64 import json import logging import os import platform import random import re import shutil import socket import ssl import sys import tempfile import time import traceback import warnings from argparse import ArgumentParser from contextlib import contextmanager from datetime import datetime from . import ( anomaly, auth, capabilities, config, config_handlers, constants, doctor, errors, fetch, http_utils, ipv6_support, kcare, libcare, log_utils, platform_utils, process_utils, selinux, server_info, serverid, update_utils, utils, ) from .errors import KcareError, NotFound, SafeExceptionWrapper from .py23 import HTTPError, URLError, httplib, json_loads_nstr, urlencode CPANEL_GID = 99 EFFECTIVE_LATEST = 'v3' EXPECTED_PREFIX = ('12h', '24h', '48h', 'test') FREEZER_BLACKLIST = '/etc/sysconfig/kcare/freezer.modules.blacklist' KCDOCTOR = '/usr/libexec/kcare/kcdoctor.sh' PATCH_LATEST = ( 'latest.v3', 'latest.v2', ) SYSCTL_CONFIG = '/etc/sysconfig/kcare/sysctl.conf' UNLOAD_RETRY_DELAY = 10 BLACKLIST_RE = re.compile('==BLACKLIST==\n(.*)==END BLACKLIST==\n', re.DOTALL) CONFLICTING_MODULES_RE = re.compile('(kpatch.*|ksplice.*|kpatch_livepatch.*)') if os.path.isdir('/usr/libexec/kcare/python'): # pragma: no cover sys.path.insert(0, '/usr/libexec/kcare/python') warnings.filterwarnings('ignore', category=DeprecationWarning) if False: # pragma: no cover from typing import Any, Dict, Optional, Set, Tuple, Union # noqa: F401 log_utils.kcarelog.setLevel(logging.DEBUG) def get_freezer_blacklist(): result = set() if os.path.isfile(FREEZER_BLACKLIST): f = open(FREEZER_BLACKLIST, 'r') for line in f: result.add(line.rstrip()) f.close() return result def _apply_ptype(ptype, filename): name_parts = filename.split('.') if ptype: filename = '.'.join([name_parts[0], ptype, name_parts[-1]]) else: filename = '.'.join([name_parts[0], name_parts[-1]]) return filename def apply_ptype(ptype): config.PATCH_BIN = _apply_ptype(ptype, config.PATCH_BIN) config.PATCH_INFO = _apply_ptype(ptype, config.PATCH_INFO) config.BLACKLIST_FILE = _apply_ptype(ptype, config.BLACKLIST_FILE) config.FIXUPS_FILE = _apply_ptype(ptype, config.FIXUPS_FILE) config.PATCH_DONE = _apply_ptype(ptype, config.PATCH_DONE) def format_exception_without_details(): etype, value, tb = sys.exc_info() details_sanitized = '' if isinstance(value, OSError) and not isinstance(value, URLError): try: # reconstruct for safety, it may be any IO-related subclass details_sanitized = "[Errno %i] %s: '%s'" % (value.errno, os.strerror(value.errno), value.filename) except (AttributeError, TypeError): pass elif isinstance(value, (KeyError, TypeError, IOError)) and not isinstance(value, URLError): details_sanitized = '%s' % value elif isinstance(value, SafeExceptionWrapper): etype = value.etype or type(value.inner) details_sanitized = value.details or ('%s' % value.inner) distro = platform_utils.get_distro() return { 'agent_version': constants.VERSION, 'python_version': platform_utils.get_python_version(), 'distro': distro[0], 'distro_version': distro[1], 'error': getattr(etype, '__name__', str(etype)), 'details': details_sanitized, 'traceback': ''.join(traceback.format_tb(tb, 100)), 'attempts': getattr(value, 'attempts', 0), } def send_exc(): if config.UPDATE_FROM_LOCAL: # pragma: no cover return trace = json.dumps(format_exception_without_details()) encoded_trace = utils.nstr(base64.urlsafe_b64encode(utils.bstr(trace))) # type: str url = utils.get_patch_server_url('/api/kcarectl-trace') + '?trace=' + encoded_trace request = http_utils.http_request(url, auth.get_http_auth_string()) try: http_utils.urlopen_base(request) except Exception: # import traceback # traceback.print_exc() # we really don't interested in exception pass def nohup_fork(func, sleep=None): # pragma: no cover """ Run func in a fork in an own process group (will stay alive after kcarectl process death). :param func: function to execute :return: """ # TODO move to process_utils.py pid = os.fork() if pid != 0: os.waitpid(pid, 0) return os.setsid() pid = os.fork() if pid != 0: os._exit(0) # close standard files to release TTY os.close(0) # redirect stdout/stdin into log file with open(constants.LOG_FILE, 'a') as fd: os.dup2(fd.fileno(), 1) os.dup2(fd.fileno(), 2) if sleep: time.sleep(sleep) try: func() except Exception: log_utils.kcarelog.exception('Wait exception') os._exit(1) os._exit(0) def touch_anchor(): """Check the fact that there was a failed patching attempt. If anchor file not exists we should create an anchor with timestamp and schedule its deletion at $timeout. If anchor exists and its timestamp more than $timeout from now we should raise an error. """ anchor_filepath = os.path.join(constants.PATCH_CACHE, '.kcareprev.lock') if os.path.isfile(anchor_filepath): with open(anchor_filepath, 'r') as afile: try: timestamp = int(afile.read()) # anchor was created quite recently # that means that something went wrong if timestamp + config.SUCCESS_TIMEOUT > time.time(): raise PreviousPatchFailedException(timestamp, anchor_filepath) except ValueError: pass utils.atomic_write(anchor_filepath, utils.timestamp_str()) # write a new timestamp def commit_update(state_data): """ See touch_anchor() for detailed explanation of anchor mechanics. See KPT-730 for details about action registration. :param state_data: dict with current level, kernel_id etc. """ try: os.remove(os.path.join(constants.PATCH_CACHE, '.kcareprev.lock')) except OSError: pass register_action('done', state_data) # reset module cache, to allow server_info get fresh data kcare.get_loaded_modules.clear() try: get_latest_patch_level(reason='done') except Exception: log_utils.kcarelog.exception('Cannot send update info!') def clear_cache(khash, plevel): utils.clean_directory(os.path.join(constants.PATCH_CACHE, 'patches'), exclude_path=kcare.get_cache_path(khash, plevel, '')) def get_current_level_path(khash, fname): prefix = config.PREFIX or 'none' module_dir = '-'.join([prefix, khash]) result = (constants.PATCH_CACHE, 'modules', module_dir) # type: Tuple[str, ...] if fname: result += (fname,) return os.path.join(*result) def save_cache_latest(khash, patch_level): utils.atomic_write(get_current_level_path(khash, 'latest'), str(patch_level), ensure_dir=True) def get_cache_latest(khash): # type: (str) -> Optional[kcare.LegacyKernelPatchLevel] path_with_latest = get_current_level_path(khash, 'latest') if os.path.isfile(path_with_latest): try: pl = int(open(path_with_latest, 'r').read().strip()) return kcare.LegacyKernelPatchLevel(khash, pl) except (ValueError, TypeError): pass return None class CertificateError(ValueError): pass class UnknownKernelException(KcareError): status = 'unknown kernel' def __init__(self, **kwargs): msg = 'New kernel detected ({0} {1} {2}).\nThere are no updates for this kernel yet.'.format( platform_utils.get_distro()[0], platform.release(), kcare.get_kernel_hash() ) super(UnknownKernelException, self).__init__(msg, **kwargs) class ApplyPatchError(KcareError): status = 'patch apply error' def __init__(self, code, freezer_style, level, patch_file, *args, **kwargs): super(ApplyPatchError, self).__init__(*args, **kwargs) self.code = code self.freezer_style = freezer_style self.level = level self.patch_file = patch_file self.distro = platform_utils.get_distro()[0] self.release = platform.release() def __str__(self): return 'Unable to apply patch ({0} {1} {2} {3} {4}, {5})'.format( self.patch_file, self.level, self.code, self.distro, self.release, ', '.join([str(i) for i in self.freezer_style]), ) # KCARE-509 class PreviousPatchFailedException(KcareError): status = 'previous patch failed' def __init__(self, timestamp, anchor, *args, **kwargs): super(PreviousPatchFailedException, self).__init__(*args, **kwargs) self.timestamp = timestamp self.anchor = anchor def __str__(self): message = ( 'It seems, the latest patch, applying at {0}, crashed, ' 'and further attempts will be suspended. ' 'To force patch applying, remove `{1}` file' ) return message.format(self.timestamp, self.anchor) def set_monitoring_key_for_ip_license(key): url = ipv6_support.get_registration_url() + '/nagios/register_key.plain?key={0}'.format(key) try: response = http_utils.urlopen(url) res = utils.data_as_dict(utils.nstr(response.read())) code = int(res['code']) if code == 0: utils.print_wrapper('Key successfully registered') elif code == 1: utils.print_wrapper('Wrong key format or size') elif code == 2: utils.print_wrapper('No KernelCare license for that IP') else: utils.print_wrapper('Unknown error {0}'.format(code)) return code except HTTPError as e: log_utils.print_cln_http_error(e, url) return -1 @contextmanager def execute_hooks(): if config.BEFORE_UPDATE_COMMAND: process_utils.run_command(config.BEFORE_UPDATE_COMMAND, shell=True) try: yield finally: if config.AFTER_UPDATE_COMMAND: process_utils.run_command(config.AFTER_UPDATE_COMMAND, shell=True) def plugin_info(fmt=None): """ The output will consist of: Ignore output up to the line with "--START--" Line 1: show if update is needed: 0 - updated to latest, 1 - update available, 2 - unknown kernel 3 - kernel doesn't need patches 4 - no license, cannot determine Line 2: licensing message (can be skipped, can be more then one line) Line 3: LICENSE: CODE: 1: license present, 2: trial license present, 0: no license Line 4: Update mode (True - auto-update, False, no auto update) Line 5: Effective kernel version Line 6: Real kernel version Line 7: Patchset Installed # --> If None, no patchset installed Line 8: Uptime (in seconds) If *format* is 'json' return the results in JSON format. Any other output means error retrieving info :return: """ pli = _patch_level_info() update_code = pli.code loaded_pl = pli.applied_lvl license_info_result = auth.license_info() if fmt == 'json': results = { 'updateCode': str(update_code), 'autoUpdate': config.AUTO_UPDATE, 'effectiveKernel': kcare.kcare_uname(), 'realKernel': platform.release(), 'loadedPatchLevel': loaded_pl, 'uptime': int(platform_utils.get_uptime()), 'license': license_info_result, } utils.print_wrapper('--START--') utils.print_wrapper(json.dumps(results)) else: utils.print_wrapper('--START--') utils.print_wrapper(str(update_code)) utils.print_wrapper('LICENSE: ' + str(license_info_result)) utils.print_wrapper(config.AUTO_UPDATE) utils.print_wrapper(kcare.kcare_uname()) utils.print_wrapper(platform.release()) utils.print_wrapper(loaded_pl) utils.print_wrapper(platform_utils.get_uptime()) def get_update_status(): current_level = kcare.loaded_patch_level() try: latest_patch_level = get_latest_patch_level(reason='info') except UnknownKernelException: return 0 if config.IGNORE_UNKNOWN_KERNEL else 3 if current_level is None: return 1 if current_level >= latest_patch_level: return 0 return 2 if update_utils.status_gap_passed() else 0 def edf_fallback_ptype(): distro, version = platform_utils.get_distro()[:2] # From talk with @kolshanov if distro == 'CloudLinux' and version.startswith('7.'): return 'extra' else: return '' # addr -> resolved_peer_addr map CONNECTION_STICKY_MAP = {} # type: Dict[Tuple[str, int], Tuple[Optional[str], int]] def sticky_connect(self): """Function remembers IP address of host connected to and uses it for later connections. Replaces stdlib version of httplib.HTTPConnection.connect """ addr = self.host, self.port resolved_addr = CONNECTION_STICKY_MAP.get(addr, addr) # type: Tuple[Optional[str], int] self.sock = socket.create_connection(resolved_addr, self.timeout) self.sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) if addr not in CONNECTION_STICKY_MAP: CONNECTION_STICKY_MAP[addr] = self.sock.getpeername()[:2] if self._tunnel_host: self._tunnel() httplib.HTTPConnection.connect = sticky_connect # type: ignore[method-assign] # python >= 2.7.9 stdlib (with ssl.HAS_SNI) is able to process https request on its own, # for earlier versions manual checks should be done if not getattr(ssl, 'HAS_SNI', None): # pragma: no cover unit # TODO move to http_utils or py23 try: import distutils.version import OpenSSL.SSL if distutils.version.StrictVersion(OpenSSL.__version__) < distutils.version.StrictVersion('0.13'): # type: ignore[attr-defined] raise ImportError('No pyOpenSSL module with SNI ability.') except ImportError: pass else: def dummy_verify_callback(*args): # OpenSSL.SSL.Context.set_verify() requires callback # where additional checks could be done; # here is a dummy callback and a hostname check is made externally # to provide original exception from match_hostname() return True PureHTTPSConnection = httplib.HTTPSConnection class SSLSock(object): def __init__(self, sock): self._ssl_conn = sock self._makefile_refs = 0 def makefile(self, *args): self._makefile_refs += 1 return socket._fileobject(self._ssl_conn, *args, close=True) # type: ignore[attr-defined] def close(self): if not self._makefile_refs and self._ssl_conn: self._ssl_conn.close() self._ssl_conn = None def sendall(self, *args): return self._ssl_conn.sendall(*args) class PyOpenSSLHTTPSConnection(httplib.HTTPSConnection): def connect(self): httplib.HTTPConnection.connect(self) # workaround to force pyopenssl to use TLSv1.2 ctx = OpenSSL.SSL.Context(OpenSSL.SSL.SSLv23_METHOD) ctx.set_options(OpenSSL.SSL.OP_NO_SSLv2 | OpenSSL.SSL.OP_NO_SSLv3) if config.CHECK_SSL_CERTS: ctx.set_verify(OpenSSL.SSL.VERIFY_PEER, dummy_verify_callback) else: ctx.set_verify(OpenSSL.SSL.VERIFY_NONE, dummy_verify_callback) ctx.set_default_verify_paths() conn = OpenSSL.SSL.Connection(ctx, self.sock) conn.set_connect_state() # self._tunnel_host is an original hostname in case of proxy use server_host = self._tunnel_host or self.host # type: ignore[attr-defined] conn.set_tlsext_host_name(server_host.encode()) conn.do_handshake() if config.CHECK_SSL_CERTS: match_hostname(conn.get_peer_certificate(), server_host) self.sock = SSLSock(conn) httplib.HTTPSConnection = PyOpenSSLHTTPSConnection # type: ignore[misc] def _fetch_patch_level_request(khash, latest, reason, mode): if config.UPDATE_FROM_LOCAL: url = kcare.get_kernel_prefixed_url(khash, latest) return fetch.wrap_with_cache_key(auth.urlopen_auth)(url, check_license=False) b64_encoding = latest not in ['latest.v1', 'latest.v2'] perf_enabled = config.SEND_PERF_METRICS and b64_encoding for secure_boot_info, perf_metrics in [(True, perf_enabled), (False, perf_enabled), (False, False)]: # pragma: no branch sinfo = server_info.server_info(reason, secure_boot_info=secure_boot_info, perf_metrics=perf_metrics) request_param = server_info.encode_checkin_payload(sinfo, b64_encoding=b64_encoding) if b64_encoding: request_param = 'info={0}'.format(request_param) url = kcare.get_kernel_prefixed_url(khash, stickyfy(latest, mode)) + '?' + request_param max_url_length = 7000 # the entire request should fit in 8K if (secure_boot_info or perf_metrics) and len(url) > max_url_length: discard_info = 'secure boot info' if secure_boot_info else 'perf metrics' log_utils.logwarn('Check-in URL param is too large, discarding {0}'.format(discard_info)) continue try: result = fetch.wrap_with_cache_key(auth.urlopen_auth)(url, check_license=False) if config.KERNEL_ANOMALY_REPORT_ENABLE and anomaly.detect_anomaly(sinfo): # possible errors in prepare_kernel_anomaly_report and send_data_package are silently ignored data_package = anomaly.prepare_kernel_anomaly_report(sinfo) upload_name = anomaly.send_data_package(data_package) if upload_name: # will be None in case of errors in send_data_package log_utils.loginfo( 'Automatic kernel anomaly report uploaded successfully: {0}'.format(upload_name), print_msg=False ) else: log_utils.logwarn('Failed to send kernel anomaly report', print_msg=False) if data_package: data_package.remove_archive() return result except HTTPError as ex: if (secure_boot_info or perf_metrics) and (ex.code in (413, 414) or ex.code >= 500): log_utils.logwarn('Check-in request failed with error: {0}, retrying with reduced info'.format(ex)) continue raise def fetch_patch_level(reason, mode=constants.UPDATE_MODE_MANUAL): khash = kcare.get_kernel_hash() if config.PATCH_LEVEL is not None: return kcare.LegacyKernelPatchLevel(khash, int(config.PATCH_LEVEL)) for latest in PATCH_LATEST: try: response = _fetch_patch_level_request(khash, latest, reason, mode) config_handlers.set_feature_flags_from_headers(response.headers) update_all_kmod_params() pl = utils.nstr(response.read()).strip() log_utils.loginfo('fetch patch level, reason: {0}, kernel latest response: {1}'.format(reason, pl), print_msg=False) if pl and pl.startswith('{'): latest_info = json_loads_nstr(pl) required_capabilities = latest_info.get('capabilities', []) if not capabilities.has_kc_capabilities(required_capabilities): raise errors.CapabilitiesMismatch( 'Latest KernelCare patchset is incompatible with the current kernecare package version, please upgrade' ) return kcare.KernelPatchLevel(khash, latest_info['level'], latest_info['baseurl'], latest_info['release']) return kcare.LegacyKernelPatchLevel(khash, int(pl)) except NotFound: # old ePortal version < 2.20 returns 404 for latest.v3 requests pass except HTTPError as ex: # No license - no access if ex.code in (403, 401): raise KcareError('KC licence is required') raise raise UnknownKernelException() def probe_patch(level, ptype): bin_url = level.file_url(_apply_ptype(ptype, config.PATCH_BIN)) log_utils.kcarelog.info('Probing patch URL: {0}'.format(bin_url)) try: auth.urlopen_auth(bin_url, check_license=False, method='HEAD') return True except NotFound: log_utils.kcarelog.info('{0} is not available: 404'.format(bin_url)) return False except Exception as ex: # Fallback to GET in case of any error. log_utils.kcarelog.debug('HEAD request for {0} raised an error, fallback to the GET request: {1}'.format(bin_url, str(ex))) url = level.file_url(_apply_ptype(ptype, config.PATCH_BIN) + constants.SIG) log_utils.kcarelog.info('Probing patch URL: {0}'.format(url)) try: auth.urlopen_auth(url, check_license=False) except NotFound: log_utils.kcarelog.info('{0} is not available: 404'.format(url)) return False except URLError as ex: log_utils.kcarelog.info('{0} is not available: {1}'.format(url, str(ex))) return True def fetch_and_verify_kernel_file(level, name): if name == constants.KMOD_BIN: url = level.kmod_url(constants.KMOD_BIN) else: url = level.file_url(name) dst = level.cache_path(name) return fetch.fetch_url(url, dst, config.USE_SIGNATURE, hash_checker=fetch.get_hash_checker(level)) class PatchFetcher(object): # TODO move to fetch.py def __init__(self, patch_level=None): self.patch_level = patch_level # LegacyKernelPatchLevel or KernelPatchLevel def _fetch(self, name): return fetch_and_verify_kernel_file(self.patch_level, name) def is_patch_fetched(self): patch_done_path = self.patch_level.cache_path(config.PATCH_DONE) patch_bin_path = self.patch_level.cache_path(config.PATCH_BIN) patch_info_path = self.patch_level.cache_path(config.PATCH_INFO) kmod_bin_path = self.patch_level.cache_path(constants.KMOD_BIN) return ( all(os.path.isfile(path) for path in (patch_done_path, patch_bin_path, patch_info_path, kmod_bin_path)) and os.path.getsize(patch_bin_path) > 0 and os.path.getsize(kmod_bin_path) > 0 ) def fetch_patch(self): if self.patch_level is None: raise ValueError("Cannot fetch patch as no patch level is set") if not self.patch_level: # level is 0, do nothing return self.patch_level if self.is_patch_fetched(): log_utils.loginfo('Updates already downloaded') return self.patch_level log_utils.loginfo('Downloading updates') # try to upgrade patch level if isinstance(self.patch_level, kcare.LegacyKernelPatchLevel): try: resp = auth.urlopen_auth(self.patch_level.file_url(config.PATCH_BIN), method='HEAD') except NotFound: pass else: baseurl = resp.headers.get('KC-Base-Url', None) if baseurl: self.patch_level = self.patch_level.upgrade(utils.nstr(baseurl)) try: self._fetch(config.PATCH_BIN) except NotFound: raise KcareError( 'The `{0}` patch level is not found for `{1}` patch type. ' 'Please select valid patch type or patch level'.format(self.patch_level, config.PATCH_TYPE or 'default'), status='patch level not found', ) self._fetch(config.PATCH_INFO) self._fetch(constants.KMOD_BIN) self.extract_blacklist() utils.atomic_write(self.patch_level.cache_path(config.PATCH_DONE), b'', mode='wb') selinux.restore_selinux_context(constants.PATCH_CACHE) return self.patch_level def extract_blacklist(self): buf = open(self.patch_level.cache_path(config.PATCH_INFO), 'r').read() if buf: mo = BLACKLIST_RE.search(buf) if mo: utils.atomic_write(self.patch_level.cache_path(config.BLACKLIST_FILE), mo.group(1)) def fetch_fixups(self, level): """ Download fixup files for defined patch level :param level: download fixups for this patch level (usually it's a level of loaded patch) :return: None """ if level is None: return try: # never use cache for fixup files, must be downloaded from scratch resp = fetch_and_verify_kernel_file(level, config.FIXUPS_FILE) except NotFound: return # Upgrade level to a new format with baseurl to fetch fixup files baseurl = resp.headers.get('KC-Base-Url', None) if baseurl: level = level.upgrade(utils.nstr(baseurl)) fixups_fname = level.cache_path(config.FIXUPS_FILE) with open(fixups_fname, 'r') as f: fixups = set([fixup.strip() for fixup in f.readlines()]) for fixup in fixups: fetch_and_verify_kernel_file(level, fixup) selinux.restore_selinux_context(constants.PATCH_CACHE) def kcare_check(): pli = _patch_level_info() utils.print_wrapper(pli.msg) if pli.code == PLI.PATCH_NEED_UPDATE: sys.exit(0) else: sys.exit(1) def show_generic_info(): pli = _patch_level_info() kcare_info = _kcare_patch_info_json(pli) try: libcare_info = libcare.libcare_patch_info_basic() except KcareError: libcare_info = {} state = kcare.get_state() latest_update = "Unknown" if state is not None: latest_update = datetime.fromtimestamp(state['ts']).strftime('%Y-%m-%d') effective_version = kcare.kcare_uname() # count unique CVEs for accurate vulnerability counts kernel_patches = kcare_info.get('patches', []) kernel_vulnerabilities = len(utils.extract_unique_cves(kernel_patches, cve_field='kpatch-cve')) kernel_patches_count = len(kernel_patches) userspace_patches = [patch for rec in libcare_info for patch in rec.get('patches', [])] userspace_vulnerabilities = len(utils.extract_unique_cves(userspace_patches, cve_field='cve')) userspace_patches_count = sum(len(rec.get('patches', [])) for rec in libcare_info) total_patches_count = kernel_patches_count + userspace_patches_count patch_level = kcare.loaded_patch_level() if not patch_level: utils.print_wrapper("KernelCare live patching is disabled") else: utils.print_wrapper("KernelCare live patching is active") utils.print_wrapper(" - Last updated on {0}".format(latest_update)) utils.print_wrapper(" - Effective kernel version {0}".format(effective_version)) if kernel_vulnerabilities > 0: utils.print_wrapper(" - {0} kernel vulnerabilities live patched".format(kernel_vulnerabilities)) if userspace_vulnerabilities > 0: utils.print_wrapper(" - {0} userspace vulnerabilities live patched".format(userspace_vulnerabilities)) if total_patches_count == 0: utils.print_wrapper(" - This system has no applied patches") utils.print_wrapper("Type kcarectl --patch-info to learn more") def kcare_latest_patch_info(is_json=False): """ Retrieve and output to STDOUT latest patch info, so it is easy to get list of CVEs in use. More info at https://cloudlinux.atlassian.net/browse/KCARE-952 :return: None """ try: latest = get_latest_patch_level(reason='info', policy=constants.POLICY_REMOTE) if not latest: raise UnknownKernelException url = latest.file_url(config.PATCH_INFO) patch_info = utils.nstr(auth.urlopen_auth(url).read()) if is_json: patches, result = [], {} for chunk in patch_info.split('\n\n'): data = utils.data_as_dict(chunk) if data and 'kpatch-name' in data: patches.append(data) else: result.update(data) result['patches'] = patches # type: ignore[assignment] patch_info = json.dumps(result) utils.print_wrapper(patch_info) except HTTPError as e: log_utils.print_cln_http_error(e, e.url) return 1 except UnknownKernelException: utils.print_wrapper('No patches available') return 0 def _kcare_patch_info_json(pli): result = {'message': pli.msg} if pli.applied_lvl is not None: patch_info = _kcare_patch_info(pli) patches = [] for chunk in patch_info.split('\n\n'): data = utils.data_as_dict(chunk) if data and 'kpatch-name' in data: patches.append(data) else: result.update(data) result['patches'] = patches saved_patch_level = kcare.read_dumped_kernel_patch_level() result['release'] = saved_patch_level['release'] if saved_patch_level else 'unknown' return result def _kcare_patch_info(pli): khash = kcare.get_kernel_hash() cache_path = kcare.get_cache_path(khash, pli.applied_lvl, config.PATCH_INFO) if not os.path.isfile(cache_path): raise KcareError( "Can't find information due to the absent patch information file." " Please, run /usr/bin/kcarectl --update and try again.", status='patch info not found', ) info = open(cache_path, 'r').read() if info: info = BLACKLIST_RE.sub('', info) return info def patch_info(is_json=False): pli = _patch_level_info() if not is_json: if pli.code != 0: utils.print_wrapper(pli.msg) if pli.applied_lvl is None: return utils.print_wrapper(_kcare_patch_info(pli)) else: utils.print_wrapper(json.dumps(_kcare_patch_info_json(pli), sort_keys=True)) def is_same_patch(new_patch_file): # mocked: tests/unit args = [constants.KPATCH_CTL, 'file-info', new_patch_file] new_patch_info = process_utils.check_output(args) current_patch_info = kcare._patch_info() build_time_label = 'kpatch-build-time' return kcare.get_patch_value(new_patch_info, build_time_label) == kcare.get_patch_value(current_patch_info, build_time_label) def kcare_need_update(applied_level, new_level): if new_level == 0: return False # ignore down-patching if applied_level and new_level < applied_level: return False if applied_level != new_level: return True new_patch_file = kcare.get_cache_path(kcare.get_kernel_hash(), new_level, config.PATCH_BIN) if not is_same_patch(new_patch_file): return True return False def update_sysctl(): # TODO move to platform_utils.py if config.UPDATE_SYSCTL_CONFIG: if not (os.path.isfile(SYSCTL_CONFIG) and os.access(SYSCTL_CONFIG, os.R_OK)): log_utils.kcarelog.warning('File {0} does not exist or has no read access'.format(SYSCTL_CONFIG)) return code, _, _ = process_utils.run_command(['/sbin/sysctl', '-q', '-p', SYSCTL_CONFIG], catch_stdout=True) if code != 0: log_utils.kcarelog.warning('Unable to load kcare sysctl.conf: {0}'.format(code)) def edit_sysctl_conf(remove, append): """Update SYSCTL_CONFIG accordingly the edits""" # TODO move to platform_utils.py # Create if it does not exist if not os.path.isfile(SYSCTL_CONFIG): open(SYSCTL_CONFIG, 'a').close() # Check kcare sysctl path and read access if not os.access(SYSCTL_CONFIG, os.R_OK): log_utils.kcarelog.warning('File {0} has no read access'.format(SYSCTL_CONFIG)) return with open(SYSCTL_CONFIG, 'r+') as sysctl: lines = sysctl.readlines() sysctl.seek(0) for line in lines: # Do not rewrite lines that should be deleted if not any(line.startswith(r) for r in remove): sysctl.write(line) # Write additional lines for a in append: sysctl.write(a + '\n') sysctl.truncate() def detect_conflicting_modules(modules): for module in modules: if CONFLICTING_MODULES_RE.match(module): raise KcareError( "Detected '{0}' kernel module loaded. Please unload that module first".format(module), status='conflicting kernel module', ) def get_kcare_kmod_link(): return '/lib/modules/{0}/extra/kcare.ko'.format(platform_utils.get_system_uname()) def kmod_is_signed(): level = get_latest_patch_level(reason='info') kmod_file = kcare.get_cache_path(kcare.get_kernel_hash(), level, constants.KMOD_BIN) if not os.path.isfile(kmod_file): return None with open(kmod_file, 'rb') as vfd: return vfd.read()[-28:] == b'~Module signature appended~\n' def kcare_certs_enrolled(): # pragma: no cover system_keys = utils.try_to_read('/proc/keys') if system_keys is None: return None kcare_keys = [ '12ff0613c0f80cfba3b2f8eba71ebc27c5a76170', # CloudLinux key '69a6d9eed3f620d5c2e13a1d211c46510a5ad9f5', # AlmaLinux kpatch signing key ] return any(key in system_keys for key in kcare_keys) def load_kmod(kmod, **kwargs): cmd = ['/sbin/insmod', kmod] for key, value in kwargs.items(): cmd.append('{0}={1}'.format(key, value)) code, _, _ = process_utils.run_command(cmd, catch_stdout=True) if code != 0: raise KcareError( 'Unable to load kmod ({0} {1}). Try to run with `--check-compatibility` flag.'.format(kmod, code), status='kmod load error', ) def check_compatibility(): if platform_utils.is_secure_boot(): if kmod_is_signed() is False: raise KcareError('Secure boot is enabled. Not supported by KernelCare.') if kcare_certs_enrolled() is False: raise KcareError('Secure boot is enabled. No KernelCare certificates enrolled.') if platform_utils.inside_vz_container() or platform_utils.inside_lxc_container() or platform_utils.inside_docker_container(): raise KcareError( 'You are running inside a container. Kernelcare should be executed on host side instead.', status='running in container', ) def check_patch_type_compatibility(ptype): # type: (str) -> None # TODO KPT-4474 patch type autodetection cmd = process_utils.find_cmd('modinfo') has_kmodlve = process_utils.run_command([cmd, 'kmodlve'], catch_stdout=True, catch_stderr=True)[0] == 0 if has_kmodlve and ptype in ('free', 'extra'): # KPT-4269 kcarectl: free/extra patches confict with kmodlve log_utils.logerror('{0} patch type conflicts with kmodlve kernel module'.format(ptype)) sys.exit(1) def get_kmod_available_params(kcare_link): stdout = process_utils.check_output(["/sbin/modinfo", "-F", "parm", kcare_link]) available_params = [] for line in stdout.split('\n'): if line.strip(): param_name, _, _ = line.partition(':') available_params.append(param_name) return available_params def make_kmod_new_params(): return { 'kpatch_debug': 1 if config.KPATCH_DEBUG else 0, 'kmsg_output': 1 if config.KMSG_OUTPUT else 0, 'kcore_output': config.KCORE_OUTPUT_SIZE if config.KCORE_OUTPUT else 0, 'kdumps_dir': config.KDUMPS_DIR if isinstance(config.KDUMPS_DIR, str) else "", 'enable_crashreporter': 1 if config.ENABLE_CRASHREPORTER else 0, } def update_all_kmod_params(): if config.KDUMPS_DIR and not os.path.exists(config.KDUMPS_DIR): os.makedirs(config.KDUMPS_DIR) for param, val in make_kmod_new_params().items(): update_kmod_param(param, val) def update_kmod_param(kmod_param_name, param_value): params_root = '/sys/module/kcare/parameters' param_path = os.path.join(params_root, kmod_param_name) if not os.path.exists(param_path): return try: with open(param_path, 'w') as f: f.write(str(param_value)) except Exception: # pragma: no cover log_utils.kcarelog.error('failed to set %s kmod param to %s', kmod_param_name, param_value) def load_kcare_kmod(khash, level): # To make `kdump` service work. We need to copy # `kcare.ko` into `/lib/modules/$(uname -r)/extra/kcare.ko` # and call `/sbin/depmod` kcare_link = get_kcare_kmod_link() kcare_file = kcare.get_cache_path(khash, level, constants.KMOD_BIN) try: shutil.copy(kcare_file, kcare_link) except Exception: kcare_link = kcare_file if config.KDUMPS_DIR and not os.path.exists(config.KDUMPS_DIR): os.makedirs(config.KDUMPS_DIR) kmod_params = make_kmod_new_params() available_kmod_params = get_kmod_available_params(kcare_link) kmod_params = dict((k, v) for k, v in kmod_params.items() if k in available_kmod_params) load_kmod(kcare_link, **kmod_params) update_depmod() def update_depmod(uname=None): cmd = [ '/sbin/depmod', ] if uname is not None: cmd.extend(['-a', uname]) code, _, stderr = process_utils.run_command(cmd, catch_stdout=True, catch_stderr=True) if code: # We don't want to show the error to the user but want to see it in logs log_utils.logerror('Running of `{0}` failed with {1}: {2}'.format(' '.join(cmd), code, stderr), print_msg=False) def unload_kmod(modname): code, _, _ = process_utils.run_command(['/sbin/rmmod', modname], catch_stdout=True) if code != 0: raise KcareError('Unable to unload {0} kmod {1}'.format(modname, code), status='kmod unload error') def apply_fixups(khash, current_level, modules): loaded = [] for mod in ['vmlinux'] + modules: modpath = kcare.get_cache_path(khash, current_level, 'fixup_{0}.ko'.format(mod)) if os.path.exists(modpath): load_kmod(modpath) loaded.append('fixup_{0}'.format(mod)) return loaded def remove_fixups(fixups): for mod in fixups: try: unload_kmod(mod) except Exception: log_utils.kcarelog.exception('Exception while unloading module %s.' % mod) def get_freezer_style(freezer, modules): if freezer: method = freezer elif config.PATCH_METHOD: method = config.PATCH_METHOD elif get_freezer_blacklist().intersection(modules): # blacklist module found, use smart freezer # xxx: this branch could be safely removed when smart would work by default return 'freeze_conflict', freezer, config.PATCH_METHOD, True else: # user doesn't provide patch method and no conflicting modules loaded return 'default', freezer, config.PATCH_METHOD, False # non default patch method, translate it into form accepted by kpatch_ctl patch_method_map = { 'NONE': 'freeze_none', 'NOFREEZE': 'freeze_none', 'FULL': 'freeze_all', 'FREEZE': 'freeze_all', 'SMART': 'freeze_conflict', } method = method.upper() if method in patch_method_map: method = patch_method_map[method] else: raise KcareError( 'Unable to detect freezer style ({0}, {1}, {2}, {3})'.format(method, freezer, config.PATCH_METHOD, False), status='freezer style detection error', ) return method, freezer, config.PATCH_METHOD, False def kcare_load(khash, level, mode, freezer='', use_anchor=False): state_data = {'khash': khash, 'future': level, 'mode': mode} register_action('start', state_data) current_level = kcare.loaded_patch_level() modules = kcare.get_loaded_modules() detect_conflicting_modules(modules) # get freezer in the beginning to prevent any further job in case of exception freezer_style = get_freezer_style(freezer, modules) patch_file = kcare.get_cache_path(khash, level, config.PATCH_BIN) save_cache_latest(khash, level) description = '{0}-{1}:{2};{3}'.format( level, config.PATCH_TYPE, utils.timestamp_str(), kcare.parse_uname(level) # future server_info['ltimestamp'] ) kmod_loaded = 'kcare' in modules kmod_changed = kmod_loaded and kcare.is_kmod_version_changed(khash, level) patch_loaded = current_level is not None same_patch = patch_loaded and is_same_patch(patch_file) and kcare.kcare_update_effective_version(description) state_data.update({'current': current_level, 'kmod_changed': kmod_changed}) if same_patch: register_action('done', state_data) return if patch_loaded: register_action('fxp', state_data) fixups = apply_fixups(khash, current_level, modules) register_action('unpatch', state_data) kpatch_ctl_unpatch(freezer_style) register_action('unfxp', state_data) remove_fixups(fixups) if kmod_changed: register_action('unload', state_data) unload_kmod('kcare') kmod_loaded = False if not kmod_loaded: register_action('load', state_data) load_kcare_kmod(khash, level) if use_anchor: # KCARE-509 touch_anchor() register_action('patch', state_data) kpatch_ctl_patch(patch_file, khash, level, description, freezer_style) update_sysctl() log_utils.loginfo('Patch level {0} applied. Effective kernel version {1}'.format(level, kcare.kcare_uname())) # Update last status check timestamp update_utils.touch_status_gap_file() # do final actions when update is considered as successful register_action('wait', state_data) nohup_fork(lambda: commit_update(state_data), sleep=config.SUCCESS_TIMEOUT) def kpatch_ctl_patch(patch_file, khash, level, description, freezer_style): args = [constants.KPATCH_CTL] blacklist_file = kcare.get_cache_path(khash, level, config.BLACKLIST_FILE) if os.path.exists(blacklist_file): args.extend(['-b', blacklist_file]) args.extend(['patch', '-d', description]) args.extend(['-m', freezer_style[0]]) args.append(patch_file) code, _, _ = process_utils.run_command(args, catch_stdout=True) if code != 0: raise ApplyPatchError(code, freezer_style, level, patch_file) def kpatch_ctl_unpatch(freezer_style): # TODO KPT-4001 refactoring, extract kpatch_ctl run to a class with patch/unpatch/etc methods with logging and error handling code, stdout, stderr = process_utils.run_command( [constants.KPATCH_CTL, 'unpatch', '-m', freezer_style[0]], catch_stdout=True, catch_stderr=True ) if code != 0: log_utils.logerror('Error unpatching, kpatch_ctl stdout:\n{0}\nstderr:\n{1}'.format(stdout, stderr), print_msg=False) raise KcareError('Error unpatching [{0}] {1}'.format(code, str(freezer_style)), status='unpatch error') def register_action(action, state_data): state_data['action'] = action state_data['ts'] = int(time.time()) utils.atomic_write(os.path.join(constants.PATCH_CACHE, 'kcare.state'), str(state_data)) def update_weak_modules(kmod_link): modules_path = '/usr/lib/modules/' if not os.path.isdir(modules_path): return for entry in os.listdir(modules_path): sym_link_path = os.path.join(modules_path, entry, 'weak-updates', 'kcare.ko') if not os.path.islink(sym_link_path): continue target_path = os.readlink(sym_link_path) if target_path == kmod_link: os.unlink(sym_link_path) update_depmod(entry) @process_utils.log_all_parent_processes def kcare_unload(freezer='', force=False): current_level = kcare.loaded_patch_level() pf = PatchFetcher() try: pf.fetch_fixups(current_level) except Exception as err: if not force: raise KcareError( "Unable to retrieve fixups: '{0}'. The unloading of patches has been " "interrupted. To proceed without fixups, use the --force flag.".format(err), status='fixups retrieval error', ) modules = kcare.get_loaded_modules() freezer_style = get_freezer_style(freezer, modules) with execute_hooks(): if 'kcare' in modules: need_unpatch = current_level is not None if need_unpatch: fixups = apply_fixups(kcare.get_kernel_hash(), current_level, modules) code, stdout, stderr = process_utils.run_command( [constants.KPATCH_CTL, 'unpatch', '-m', freezer_style[0]], catch_stdout=True, catch_stderr=True ) remove_fixups(fixups) if code != 0: log_utils.logerror( 'Error unpatching, kpatch_ctl stdout:\n{0}\nstderr:\n{1}'.format(stdout, stderr), print_msg=False ) raise KcareError('Error unpatching [{0}] {1}'.format(code, str(freezer_style)), status='unpatch error') # Unload kcare module and retry once after 10 seconds if failed # Kernel module could be loaded even if patch is not applyed utils.retry(errors.check_exc(KcareError), count=1, delay=UNLOAD_RETRY_DELAY)(unload_kmod)('kcare') kmod_link = get_kcare_kmod_link() if os.path.isfile(kmod_link): os.unlink(kmod_link) # KPT-3469 fix the case with kernel upgrade where third party modules are linked under # /usr/lib/modules/{uname_r}/weak-updates/kcare.ko so that kdump work good update_weak_modules(kmod_link) def kcare_info(is_json): pli = _patch_level_info() if is_json: return _kcare_info_json(pli) else: if pli.code != 0: return pli.msg if pli.applied_lvl is not None: return kcare._patch_info() def _kcare_info_json(pli): result = {'message': pli.msg} if pli.applied_lvl is not None: result.update(utils.data_as_dict(kcare._patch_info())) result.update(kcare.parse_patch_description(result.get('kpatch-description'))) result['kpatch-state'] = pli.state return json.dumps(result) class PLI: PATCH_LATEST = 0 PATCH_NEED_UPDATE = 1 PATCH_UNAVALIABLE = 2 PATCH_NOT_NEEDED = 3 def __init__(self, code, msg, remote_lvl, applied_lvl, state): self.code = code self.msg = msg self.remote_lvl = remote_lvl self.applied_lvl = applied_lvl self.state = state def _patch_level_info(): current_patch_level = kcare.loaded_patch_level() try: # this line raises UnknownKernel from the bottom of this try new_patch_level = get_latest_patch_level(reason='info') if current_patch_level: if kcare_need_update(current_patch_level, new_patch_level): code, msg, state = ( PLI.PATCH_NEED_UPDATE, "Update available, run 'kcarectl --update'.", 'applied', ) else: code, msg, state = ( PLI.PATCH_LATEST, 'The latest patch is applied.', 'applied', ) else: # no patch applied if new_patch_level == 0: code, msg, state = ( PLI.PATCH_NOT_NEEDED, "This kernel doesn't require any patches.", 'unset', ) else: code, msg, state = ( PLI.PATCH_NEED_UPDATE, "No patches applied, but some are available, run 'kcarectl --update'.", 'unset', ) info = PLI(code, msg, new_patch_level, current_patch_level, state) except UnknownKernelException: code = PLI.PATCH_UNAVALIABLE if config.STICKY_PATCH: msg = ( 'Invalid sticky patch tag {0} for kernel ({1} {2}). ' 'Please check /etc/sysconfig/kcare/kcare.conf ' 'STICKY_PATCH settings'.format(config.STICKY_PATCH, platform_utils.get_distro()[0], platform.release()) ) else: msg = 'New kernel detected ({0} {1} {2}).\nThere are no updates for this kernel yet.'.format( platform_utils.get_distro()[0], platform.release(), kcare.get_kernel_hash() ) info = PLI(code, msg, None, None, 'unavailable') return info def tag_server(tag): """ Request to tag server from ePortal. See KCARE-947 for more info :param tag: String used to tag the server :return: 0 on success, -1 on wrong server id, other values otherwise """ url = None try: # TODO: is it ok to send request in case when no server_id found? (machine is not registered in ePortal) server_id = serverid.get_serverid() query = urlencode([('server_id', server_id), ('tag', tag)]) url = ipv6_support.get_registration_url() + '/tag_server.plain?{0}'.format(query) response = http_utils.urlopen(url) res = utils.data_as_dict(utils.nstr(response.read())) return int(res['code']) except HTTPError as e: log_utils.print_cln_http_error(e, url) return -3 except URLError as ue: log_utils.print_cln_http_error(ue, url) return -4 except Exception as ee: log_utils.logerror('Internal Error {0}'.format(ee)) return -5 def kcdoctor(): doctor_url = utils.get_patch_server_url("doctor.sh") log_utils.logdebug("Requesting doctor script from `{0}`".format(doctor_url)) doctor_filename = KCDOCTOR with tempfile.NamedTemporaryFile() as doctor_dst: try: signature = fetch.fetch_signature(doctor_url, doctor_dst.name) utils.save_to_file(http_utils.urlopen(doctor_url), doctor_dst.name) fetch.check_gpg_signature(doctor_dst.name, signature) doctor_filename = doctor_dst.name except Exception as err: log_utils.logerror('Kcare doctor error: {0}. Fallback to the local one.'.format(err)) code, _, stderr = process_utils.run_command(['bash', doctor_filename, ipv6_support.get_patch_server()], catch_stderr=True) if code: raise KcareError("Script failed with '{0}' {1}".format(stderr, code), status='doctor script failed') def check_new_kc_version(): url = utils.get_patch_server_url('{0}-new-version'.format(EFFECTIVE_LATEST)) try: http_utils.urlopen(url) except URLError: return False log_utils.loginfo( 'A new version of the KernelCare package is available. To continue to get kernel updates, please install the new version' ) return True # mocked: tests/unit/test_patch_level_info.py def get_latest_patch_level(reason, policy=constants.POLICY_REMOTE, mode=constants.UPDATE_MODE_MANUAL): """ Get patch level to apply. :param reason: what was the source of request (update, info etc.) :param policy: REMOTE -- get latest patch_level from patchserver, LOCAL -- use cached latest, LOCAL_FIRST -- if cached level is None get latest from patchserver, use cache otherwise :param mode: constants.UPDATE_MODE_MANUAL, constants.UPDATE_MODE_AUTO or constants.UPDATE_MODE_SMART :return: patch_level string """ khash = kcare.get_kernel_hash() cached_level = get_cache_latest(khash) consider_remote_ex = policy == constants.POLICY_REMOTE or (policy == constants.POLICY_LOCAL_FIRST and cached_level is None) try: remote_level = fetch_patch_level(reason, mode) except errors.CapabilitiesMismatch as e: if cached_level is None: # error will be logged in __main__.py raise log_utils.logwarn(str(e)) log_utils.logwarn('Using previously downloaded patches') # force using already downloaded patches policy = constants.POLICY_LOCAL except Exception as ex: if consider_remote_ex: raise else: log_utils.kcarelog.warning('Unable to send data: {0}'.format(ex)) if policy == constants.POLICY_REMOTE: level = remote_level else: level = cached_level if cached_level is None: if policy == constants.POLICY_LOCAL: level = kcare.LegacyKernelPatchLevel(khash, 0) elif policy == constants.POLICY_LOCAL_FIRST: level = remote_level else: raise KcareError('Unknown policy, choose one of: REMOTE, LOCAL, LOCAL_FIRST') return level def update_patch_type(ptype): if ptype == 'edf': # The only way user can get here if call kcarectl --set-patch-type # we don't support this anyway and can silently ignore return config.PATCH_TYPE = '' if ptype == 'default' else ptype if probe_patch(fetch_patch_level(reason='probe'), config.PATCH_TYPE): config_handlers.update_config(PATCH_TYPE=config.PATCH_TYPE) if config.PATCH_TYPE in ('free', 'extra') and platform_utils.is_cpanel(): gid = config.FORCE_GID or CPANEL_GID edit_sysctl_conf( ('fs.enforce_symlinksifowner', 'fs.symlinkown_gid'), ('fs.enforce_symlinksifowner=1', 'fs.symlinkown_gid={0}'.format(gid)), ) log_utils.loginfo("'{0}' patch type selected".format(ptype)) else: raise KcareError("'{0}' patch type is unavailable for your kernel".format(ptype), status='patch type unavailable') # log_all_parent_processes must be outermost to run first @process_utils.log_all_parent_processes @update_utils.track_update_status('kernel') def do_update(freezer, mode, policy=constants.POLICY_REMOTE): """ :param mode: constants.UPDATE_MODE_MANUAL, constants.UPDATE_MODE_AUTO or constants.UPDATE_MODE_SMART :param policy: REMOTE -- download latest and patches from patchserver, LOCAL -- use cached files, LOCAL_FIRST -- download latest and patches if cached level is None, use cache in other cases :param freezer: freezer mode """ check_patch_type_compatibility(config.PATCH_TYPE) if policy == constants.POLICY_REMOTE: check_new_kc_version() try: level = get_latest_patch_level(reason='update', policy=policy, mode=mode) except UnknownKernelException as e: if mode in (constants.UPDATE_MODE_AUTO, constants.UPDATE_MODE_SMART) and config.IGNORE_UNKNOWN_KERNEL: msg = str(e) log_utils.kcarelog.warning(msg) return raise current_level = kcare.loaded_patch_level() # Cron triggers `kcarectl -q --auto-update` regardless of AUTO_UPDATE. # When AUTO_UPDATE=NO, return before touching the on-disk cache so the # currently-applied patch's cache directory is not rotated out by # subsequent fetches (CACHE_ENTRIES rotation would otherwise evict it # and break --patch-info). if mode == constants.UPDATE_MODE_AUTO and not config.AUTO_UPDATE: return pf = PatchFetcher(level) pf.fetch_patch() if not kcare_need_update(applied_level=current_level, new_level=level): log_utils.loginfo('No updates are needed for this kernel') return # Rotate crash report dumps try: utils.clean_directory(config.KDUMPS_DIR, keep_n=3, pattern="kcore*.dump") utils.clean_directory(config.KDUMPS_DIR, keep_n=3, pattern="kmsg*.log") except Exception: log_utils.kcarelog.exception('Error during crash reporter cleanup') khash = kcare.get_kernel_hash() with execute_hooks(): pf.fetch_fixups(current_level) kcare_load(khash, level, mode, freezer, use_anchor=mode == constants.UPDATE_MODE_SMART) kcare.dump_kernel_patch_level(level) clear_cache(khash, level) """ This is needed to support sticky keys as per https://cloudlinux.atlassian.net/browse/KCARE-953 """ def get_sticky(mode): count = sum( ( bool(config.STICKY_PATCH), bool(config.UPDATE_DELAY or config.AUTO_UPDATE_DELAY), bool(config.STICKY_PATCHSET or config.AUTO_STICKY_PATCHSET), ) ) if count > 1: raise KcareError( 'Invalid configuration: conflicting settings STICKY_PATCH,' ' [AUTO_]UPDATE_DELAY or [AUTO_]STICKY_PATCHSET. There should be only one of them', status='conflicting sticky settings', ) if config.STICKY_PATCH: return config.STICKY_PATCH if mode != constants.UPDATE_MODE_MANUAL: delay = config.AUTO_UPDATE_DELAY or config.UPDATE_DELAY patchset = config.AUTO_STICKY_PATCHSET or config.STICKY_PATCHSET else: delay = config.UPDATE_DELAY patchset = config.STICKY_PATCHSET if delay: return delay if patchset: return 'release-' + patchset def _stickyfy(prefix, fname): return prefix + '.' + fname def stickyfy(file, mode): """ Used to add sticky prefix to satisfy KCARE-953 :param file: name of the file to stickify :return: stickified file. """ s = get_sticky(mode) if not s: return file if s != 'KEY': return _stickyfy(s, file) server_id = serverid.get_serverid() if not server_id: log_utils.kcarelog.info('Patch set to STICKY_PATCH=KEY, but server is not registered with the key') sys.exit(-4) try: response = http_utils.urlopen(ipv6_support.get_registration_url() + '/sticky_patch.plain?server_id={0}'.format(server_id)) except HTTPError as e: log_utils.print_cln_http_error(e, e.url) sys.exit(-5) res = utils.data_as_dict(utils.nstr(response.read())) code = int(res['code']) if code == 0: return _stickyfy(res['prefix'], file) elif code == 1: return file elif code == 2: log_utils.kcarelog.info('Server ID is not recognized. Please check if the server is registered') sys.exit(-1) log_utils.kcarelog.info('Error: ' + res['message']) sys.exit(-3) ################################# # from python 2.7.17 ssl stdlib # ################################# def _dnsname_match(dn, hostname, max_wildcards=1): # pragma: no cover """Matching according to RFC 6125, section 6.4.3 http://tools.ietf.org/html/rfc6125#section-6.4.3 """ pats = [] if not dn: return False pieces = dn.split(r'.') leftmost = pieces[0] remainder = pieces[1:] wildcards = leftmost.count('*') if wildcards > max_wildcards: # Issue #17980: avoid denials of service by refusing more # than one wildcard per fragment. A survery of established # policy among SSL implementations showed it to be a # reasonable choice. raise CertificateError('too many wildcards in certificate DNS name: ' + repr(dn)) # speed up common case w/o wildcards if not wildcards: return dn.lower() == hostname.lower() # RFC 6125, section 6.4.3, subitem 1. # The client SHOULD NOT attempt to match a presented identifier in which # the wildcard character comprises a label other than the left-most label. if leftmost == '*': # When '*' is a fragment by itself, it matches a non-empty dotless # fragment. pats.append('[^.]+') elif leftmost.startswith('xn--') or hostname.startswith('xn--'): # RFC 6125, section 6.4.3, subitem 3. # The client SHOULD NOT attempt to match a presented identifier # where the wildcard character is embedded within an A-label or # U-label of an internationalized domain name. pats.append(re.escape(leftmost)) else: # Otherwise, '*' matches any dotless string, e.g. www* pats.append(re.escape(leftmost).replace(r'\*', '[^.]*')) # add the remaining fragments, ignore any wildcards for frag in remainder: pats.append(re.escape(frag)) pat = re.compile(r'\A' + r'\.'.join(pats) + r'\Z', re.IGNORECASE) return pat.match(hostname) # match_hostname tweaked to get dns names from pyopenssl x509 cert object def match_hostname(cert, hostname): # pragma: no cover san = [] for i in range(cert.get_extension_count()): e = cert.get_extension(i) if e.get_short_name() == 'subjectAltName': san = [it.strip().split(':', 1) for it in str(e).split(',')] if not cert: raise ValueError( 'empty or no certificate, match_hostname needs a ' 'SSL socket or SSL context with either ' 'CERT_OPTIONAL or CERT_REQUIRED' ) dnsnames = [] for key, value in san: if key == 'DNS': if _dnsname_match(value, hostname): return dnsnames.append(value) if not dnsnames: # The subject is only checked when there is no dNSName entry # in subjectAltName cn = cert.get_subject().commonName if _dnsname_match(cn, hostname): return dnsnames.append(value) if len(dnsnames) > 1: raise CertificateError("hostname {0} doesn't match either of {1}".format(hostname, ', '.join(map(repr, dnsnames)))) elif len(dnsnames) == 1: raise CertificateError("hostname {0} doesn't match {1}".format(hostname, dnsnames[0])) else: raise CertificateError('no appropriate commonName or subjectAltName fields were found') ##################### # end of ssl stdlib # ##################### def main(): parser = ArgumentParser(prog="kcarectl", description='Manage KernelCare patches for your kernel') parser.add_argument('--debug', help='', action='store_true') parser.add_argument( '-i', '--info', help='Display information about KernelCare. Use with --json parameter to get result in JSON format.', action='store_true', ) parser.add_argument( '--app-info', help='Display information about KernelCare agent. Use with --json parameter to get result in JSON format.', action='store_true', ) parser.add_argument('-u', '--update', help='Download latest patches and apply them to the current kernel', action='store_true') parser.add_argument('--unload', help='Unload patches', action='store_true') parser.add_argument('--smart-update', help='Patch kernel based on UPDATE POLICY settings', action='store_true') parser.add_argument('--auto-update', help='Check if update is available, if so -- update', action='store_true') parser.add_argument( '--local', help='Update from a server local directory; accepts a path where patches are located', metavar='PATH' ) parser.add_argument('--patch-info', help='Return the list of applied patches', action='store_true') parser.add_argument('--freezer', help='Freezer type: full (default), smart, none', metavar='freezer') parser.add_argument('--nofreeze', help="[deprecated] Don't freeze tasks before patching", action='store_true') parser.add_argument('--uname', help='Return safe kernel version', action='store_true') parser.add_argument('--license-info', help='Return current license info', action='store_true') parser.add_argument('--status', help='Return status of updates', action='store_true') parser.add_argument('--register', help='Register using KernelCare Key', metavar='KEY') parser.add_argument( '--register-autoretry', help='Retry registering indefinitely if failed on the first attempt', action='store_true' ) parser.add_argument('--unregister', help='Unregister from KernelCare (for key-based servers only)', action='store_true') parser.add_argument('--check', help='Check if new update available', action='store_true') parser.add_argument( '--latest-patch-info', help='Return patch info for the latest available patch. Use with --json parameter to get result in JSON format.', action='store_true', ) parser.add_argument('--test', help='[deprecated] Use --prefix=test instead', action='store_true') parser.add_argument('--tag', help='Tag server with custom metadata, for ePortal users only', metavar='TAG') parser.add_argument( '--prefix', help='Patch source prefix used to test different builds by downloading builds from different locations based on prefix', metavar='PREFIX', ) parser.add_argument('--nosignature', help='Do not check signature', action='store_true') parser.add_argument( '--set-monitoring-key', help='Set monitoring key for IP based licenses. 16 to 32 characters, alphanumeric only', metavar='KEY' ) parser.add_argument('--doctor', help='Submits a vitals report to CloudLinux for analysis and bug-fixes', action='store_true') parser.add_argument( '--fallback', help='With --doctor, force the legacy kcdoctor.sh flow instead of the v2 upload path', action='store_true', ) parser.add_argument( '--kernel-anomaly-report', help='Submits a kernel anomaly report to CloudLinux for analysis and bug-fixes', action='store_true', ) parser.add_argument('--no-send', help='Skip sending artifacts', action='store_true', dest='save_only') parser.add_argument('--keep-local', help="Don't delete generated kernel anomaly report after sending", action='store_true') parser.add_argument('--enable-auto-update', help='Enable auto updates', action='store_true') parser.add_argument('--disable-auto-update', help='Disable auto updates', action='store_true') parser.add_argument( '--plugin-info', help='Provides the information shown in control panel plugins for KernelCare. ' 'Use with --json parameter to get result in JSON format.', action='store_true', ) parser.add_argument( '--server-info', help='Provides information about the host in JSON format.', action='store_true', ) parser.add_argument( '--json', help="Return '--plugin-info', '--latest-patch-info', '--patch-info', '--app-info' and '--info' results in JSON format", action='store_true', ) parser.add_argument('--version', help='Return the current version of KernelCare', action='store_true') parser.add_argument('--kpatch-debug', help='Enable the debug mode', action='store_true') parser.add_argument('--no-check-cert', help='Disable the patch server SSL certificates checking', action='store_true') parser.add_argument( '--set-patch-level', help='Set patch level to be applied. To select latest patch level set -1', action='store', type=int, default=None, required=False, ) parser.add_argument('--check-compatibility', help='Check compatibility.', action='store_true') parser.add_argument('--clear-cache', help='Clear all cached files', action='store_true') exclusive_group = parser.add_mutually_exclusive_group() exclusive_group.add_argument( '--set-patch-type', help="Set patch type feed. To select default feed use 'default' option", action='store' ) exclusive_group.add_argument('--edf-enabled', help='Enable exploit detection framework', action='store_true') exclusive_group.add_argument('--edf-disabled', help='Disable exploit detection framework', action='store_true') parser.add_argument( '--set-sticky-patch', help='Set patch to stick to date in DDMMYY format, or retrieve it from KEY if set to KEY. Leave empty to unstick', action='store', default=None, required=False, ) parser.add_argument( '-q', '--quiet', help='Suppress messages, provide only errors and warnings to stderr', action='store_true', required=False ) parser.add_argument('--has-flags', help='Check agent features') parser.add_argument('--force', help='Force action and ignore several restristions.', action="store_true") parser.add_argument('--set-config', help='Change configuration option', action='append', metavar='KEY=VALUE') if not config.LIBCARE_DISABLED: parser.add_argument( '--disable-libcare', help='Disable libcare services', dest='enable_libcare', action='store_const', const=False ) parser.add_argument( '--enable-libcare', help='Enable libcare services', dest='enable_libcare', action='store_const', const=True ) parser.add_argument( '--lib-update', help='Download latest patches and apply them to the current userspace libraries', action='store_true' ) parser.add_argument('--lib-unload', '--userspace-unload', help='Unload userspace patches', action='store_true') parser.add_argument('--lib-replugin', '--userspace-replugin', help='Reload libcare-server plugin', action='store_true') parser.add_argument('--lib-auto-update', help='Check if update is available, if so -- update', action='store_true') parser.add_argument('--lib-info', '--userspace-info', help='Display information about KernelCare+.', action='store_true') parser.add_argument( '--lib-patch-info', '--userspace-patch-info', help='Return the list of applied userspace patches', action='store_true' ) parser.add_argument('--lib-version', '--userspace-version', help='Return safe package version', metavar='PACKAGENAME') parser.add_argument( '--userspace-update', metavar='USERSPACE_PATCHES', nargs='?', const="", help='Download latest patches and apply them to the corresponding userspace processes', ) parser.add_argument( '--userspace-auto-update', help='Download latest patches and apply them to the corresponding userspace processes', action='store_true', ) parser.add_argument('--userspace-status', help='Return status of userspace updates', action='store_true') parser.add_argument( '--lib-tag', '--userspace-tag', metavar='TAG', help='Apply userspace patches for a specific tag (DDMMYY, ' 'YYYY-MM-DD, Nd, Nh, release-) into an isolated ' 'cache, leaving the default storage untouched. Use together with ' '--lib-update or --userspace-update.', action='store', default=None, required=False, ) args = parser.parse_args() config_handlers.set_settings_from_config_file() if not config.LIBCARE_DISABLED: config.FLAGS += ['libcare-enabled'] if args.has_flags is not None: if set(filter(None, args.has_flags.split(','))).issubset(config.FLAGS): return 0 else: return 1 # do not remove args.auto_update! # once added to machine, kcare-cron is never changed by package update; # old clients has no -q option in their cron, # so auto_update default silent mode must be saved forever if args.quiet or args.auto_update: if config.SILENCE_ERRORS: config.PRINT_LEVEL = constants.PRINT_CRITICAL else: config.PRINT_LEVEL = constants.PRINT_ERROR elif args.debug: config.PRINT_LEVEL = constants.PRINT_DEBUG if not args.uname: if os.getuid() != 0: print('Please run as root', file=sys.stderr) # noqa: T201 return 1 level = logging.INFO if args.quiet: level = logging.WARNING elif args.debug: level = logging.DEBUG # should be after root role check to create a log file with correct rights log_utils.initialize_logging(level) if not config.IGNORE_FEATURE_FLAGS: # run this after initialize_logging to log settings overrides config_handlers.set_feature_flags_from_cache() if args.clear_cache: utils.clear_all_cache() if args.set_patch_level: if args.set_patch_level >= 0: config.PATCH_LEVEL = str(args.set_patch_level) # type: ignore config_handlers.update_config(PATCH_LEVEL=config.PATCH_LEVEL) else: config.PATCH_LEVEL = None config_handlers.update_config(PATCH_LEVEL='') if args.set_sticky_patch is not None: config_handlers.update_config(STICKY_PATCH=args.set_sticky_patch) config.STICKY_PATCH = args.set_sticky_patch if args.nosignature: config.USE_SIGNATURE = False if args.no_check_cert: config.CHECK_SSL_CERTS = False if args.kpatch_debug: config.KPATCH_DEBUG = True if args.check_compatibility: check_compatibility() # EDF do nothing if args.edf_enabled: warnings.warn('Flag --edf-enabled has been deprecated and will be not available in future releases.', DeprecationWarning) elif args.edf_disabled: if config.PATCH_TYPE == 'edf': args.set_patch_type = ('' if config.PREV_PATCH_TYPE == 'edf' else config.PREV_PATCH_TYPE) or 'default' args.update = True if args.prefix: config.PREFIX = args.prefix if args.test: warnings.warn('Flag --test has been deprecated and will be not available in future releases.', DeprecationWarning) config.PREFIX = 'test' config.PREFIX = config.PREFIX.strip('/') if config.PREFIX and config.PREFIX not in EXPECTED_PREFIX: log_utils.kcarelog.warning('Prefix `{0}` is not in expected one {1}.'.format(config.PREFIX, ' '.join(EXPECTED_PREFIX))) if args.local: config.UPDATE_FROM_LOCAL = True config.PATCH_SERVER = 'file:' + args.local if args.set_patch_type: update_patch_type(args.set_patch_type) if config.PATCH_TYPE == 'edf': config.PATCH_TYPE = edf_fallback_ptype() warnings.warn('edf patches are deprecated. Fallback to {0}'.format(config.PATCH_TYPE or 'default'), DeprecationWarning) if args.app_info: utils.print_wrapper(platform_utils.app_info(is_json=args.json)) return if args.server_info: info = server_info.server_info(reason='debug', secure_boot_info=True, perf_metrics=True) utils.print_wrapper(json.dumps(info)) return apply_ptype(config.PATCH_TYPE) if args.doctor: doctor.send_doctor_report(kcdoctor, force_fallback=args.fallback) return if args.kernel_anomaly_report: data_package = anomaly.prepare_kernel_anomaly_report( server_info.server_info(reason='debug', secure_boot_info=True, perf_metrics=True) ) local_path_message = 'Kernel anomaly report file generated: {0}'.format(data_package.archive_path) if args.save_only: utils.print_wrapper(local_path_message) else: upload_name = anomaly.send_data_package(data_package) if upload_name: log_utils.loginfo('Kernel anomaly report uploaded successfully: {0}'.format(upload_name)) else: # pragma: no cover log_utils.logwarn('Failed to send kernel anomaly report', print_msg=True) if args.keep_local: utils.print_wrapper(local_path_message) elif data_package: # pragma: no branch data_package.remove_archive() if args.plugin_info: if args.json: plugin_info(fmt='json') else: plugin_info() return if args.enable_auto_update: config_handlers.update_config(AUTO_UPDATE='YES') return if args.disable_auto_update: config_handlers.update_config(AUTO_UPDATE='NO') return if args.set_config: config_handlers.update_config_from_args(args.set_config) return if args.set_monitoring_key: return set_monitoring_key_for_ip_license(args.set_monitoring_key) if args.unregister: auth.unregister() if args.register: if config.PATCH_TYPE == 'free': config_handlers.update_config(PATCH_TYPE='extra') return auth.register(args.register, args.register_autoretry) if args.license_info: # license_info returns zero if no valid license found and non-zero otherwise if auth.license_info() != 0: return 0 else: return 1 if args.tag is not None: return tag_server(args.tag) if args.version: utils.print_wrapper(constants.VERSION) if getattr(args, 'enable_libcare', None) is not None: libcare.set_libcare_status(args.enable_libcare) return 0 if not config.LIBCARE_DISABLED: lib_tag_kw = {'tag': args.lib_tag} if args.lib_tag else {} if args.userspace_status: return libcare.get_userspace_update_status() if args.lib_update: if libcare.do_userspace_update(**lib_tag_kw) is not None: log_utils.loginfo('Userspace patches are applied.') if args.lib_auto_update: libcare.do_userspace_update(mode=constants.UPDATE_MODE_AUTO, **lib_tag_kw) elif args.lib_unload: libcare.libcare_unload() log_utils.loginfo('Userspace patches are unloaded.') if args.lib_replugin: # pragma: no cover unit libcare.libcare_replugin() log_utils.loginfo('Libcare plugin reloaded.') if args.lib_info: utils.print_wrapper(libcare.libcare_info()) if args.lib_patch_info: utils.print_wrapper(libcare.libcare_patch_info()) if args.lib_version and libcare.libcare_server_started(): utils.print_wrapper(libcare.libcare_version(args.lib_version)) if args.userspace_update is not None: if args.userspace_update == '': # Get from config or defaults limit = config.USERSPACE_PATCHES or list(libcare.get_userspace_map().keys()) else: limit = [ptch.strip().lower() for ptch in args.userspace_update.split(',')] if libcare.do_userspace_update(limit=sorted(limit), **lib_tag_kw) is not None: log_utils.loginfo('Userspace patches are applied.') if args.userspace_auto_update: libcare.do_userspace_update(mode=constants.UPDATE_MODE_AUTO, limit=None, **lib_tag_kw) if args.info: utils.print_wrapper(kcare_info(is_json=args.json)) freezer = '' if args.nofreeze: warnings.warn('Flag --nofreeze has been deprecated and will be not available in future releases.', DeprecationWarning) freezer = 'none' if args.freezer: freezer = args.freezer if args.smart_update: do_update(freezer, mode=constants.UPDATE_MODE_SMART, policy=config.UPDATE_POLICY) if args.update: do_update(freezer, mode=constants.UPDATE_MODE_MANUAL) log_utils.loginfo('Kernel is safe') if args.uname: utils.print_wrapper(kcare.kcare_uname()) if args.unload: kcare_unload(freezer, force=args.force) log_utils.loginfo('KernelCare protection disabled. Your kernel might not be safe') if args.auto_update: config.CHECK_CLN_LICENSE_STATUS = False # wait to prevent spikes at the beginning of each minute KPT-1874 # bandit warns about using random.uniform for security which is not the case here time.sleep(random.uniform(0, 60)) # nosec B311 do_update(freezer, mode=constants.UPDATE_MODE_AUTO) if args.patch_info: patch_info(is_json=args.json) if args.status: return get_update_status() if args.latest_patch_info: kcare_latest_patch_info(is_json=args.json) if args.check: kcare_check() # No arg were provided if len(sys.argv) == 1: show_generic_info() kcarectl/ipv6_support.py000064400000016303152527712570011406 0ustar00# Copyright (c) Cloud Linux Software, Inc # Licensed under CLOUD LINUX LICENSE AGREEMENT # http://cloudlinux.com/docs/LICENCE.TXT import json import numbers import os import time from . import config, constants, http_utils, log_utils, serverid, utils from .py23 import json_loads_nstr if False: # pragma: no cover from typing import Optional # noqa: F401 # from CLN sources: # code: 0 -> valid license (for key based, or ip based) # code: 1 -> trial license, unexpired # code: 2 -> no valid license, but there is expired trial license # code: 3 -> no valid or trial license CLN_VALID_LICENSE = 0 CLN_TRIAL_ACTIVE_LICENSE = 1 CLN_NO_LICENSE = 3 CACHE_FILE = os.path.join(constants.PATCH_CACHE, 'ipv6_preference.json') CACHE_TTL_SECONDS = 24 * 60 * 60 class IPProtoSelector(object): def is_ipv6_preferred(self): # type: () -> bool """ Choose ipv6 if it is more suitable. Checks order: - check config values (it is faster) - eportal setup and FORCE_IPVx - then check each proto availability using HEAD requests - then check if we have server_id, it means we don't expect an ip license - and finally we need to check if there is an ip license """ # each case is in a separate block for better coverage check if config.FORCE_IPV4: log_utils.logdebug('decided to use ipv4 because of config values') return False elif not config.PATCH_SERVER.endswith('kernelcare.com'): # eportal setup, we don't need to change urls log_utils.logdebug('decided to use ipv4 because of config values') return False elif config.FORCE_IPV6: log_utils.logdebug('decided to use ipv6 because of config values') return True # further checks are more expensive, use cached value if it is set cached = _read_cache() if cached is not None: log_utils.logdebug('decided to use {0} from on-disk cache'.format('ipv6' if cached else 'ipv4')) return cached result = None if not self._is_url_reachable(config.PATCH_SERVER_IPV6): log_utils.logdebug('decided to use ipv4 because ipv6 is not available') result = False elif not self._is_url_reachable(config.PATCH_SERVER): log_utils.logdebug('decided to use ipv6 because ipv4 is not available') result = True elif serverid.get_serverid(): log_utils.logdebug('decided to use ipv4 because server id was found') result = False if result is not None: _write_cache(result) return result ipv4_license = self._get_cln_license(ipv6=False) ipv6_license = self._get_cln_license(ipv6=True) if ipv4_license == CLN_VALID_LICENSE: log_utils.logdebug('decided to use ipv4 because ipv4 license was found') result = False elif ipv6_license == CLN_VALID_LICENSE: log_utils.logdebug('decided to use ipv6 because ipv6 license was found') result = True elif ipv4_license == CLN_TRIAL_ACTIVE_LICENSE: log_utils.logdebug('decided to use ipv4 because ipv4 trial license was found') result = False elif ipv6_license == CLN_TRIAL_ACTIVE_LICENSE: log_utils.logdebug('decided to use ipv6 because ipv6 trial license was found') result = True else: # we don't have any license yet result = False _write_cache(result) return result @staticmethod def _is_url_reachable(url): # type: (str) -> bool request = http_utils.http_request(url, method='HEAD', auth_string=None) # type: ignore[no-untyped-call] try: http_utils.urlopen(request, timeout=10, retry_on_500=False, retry_count=2) # type: ignore[no-untyped-call] return True except Exception as e: log_utils.logdebug('error during HEAD request to {0}: {1}'.format(url, str(e))) return False @staticmethod def _get_cln_license(ipv6): # type: (bool) -> int base_url = config.REGISTRATION_URL_IPV6 if ipv6 else config.REGISTRATION_URL # a comment from auth.py: # do not retry in case of 500 from CLN! # otherwise, CLN will die in pain because of too many requests url = base_url + '/check.plain' content = utils.nstr(http_utils.urlopen(url, retry_on_500=False).read()) # type: ignore[no-untyped-call] info = utils.data_as_dict(content) if not info or not info.get('code'): log_utils.kcarelog.error('Unexpected CLN response: {0}'.format(content)) return CLN_NO_LICENSE try: return int(info['code']) except ValueError: return CLN_NO_LICENSE ip_proto_selector = IPProtoSelector() def _read_cache(): # type: () -> Optional[bool] content = utils.try_to_read(CACHE_FILE) if not content: return None try: data = json_loads_nstr(content) except (ValueError, TypeError): log_utils.logwarn('ipv6 preference cache: malformed json {0!r}'.format(content), print_msg=False) return None if not isinstance(data, dict): log_utils.logwarn('ipv6 preference cache: unexpected payload {0!r}'.format(data), print_msg=False) return None prefer_ipv6 = data.get('prefer_ipv6') if not isinstance(prefer_ipv6, bool): log_utils.logwarn('ipv6 preference cache: unexpected prefer_ipv6 {0!r}'.format(prefer_ipv6), print_msg=False) return None cached_ts = data.get('ts') # numbers.Integral covers int and Python 2 long (the latter matters # once a 32-bit Python 2 host crosses the 2038 sys.maxint boundary); # exclude bool explicitly since it would silently round-trip as 0/1 if not isinstance(cached_ts, numbers.Integral) or isinstance(cached_ts, bool): log_utils.logwarn('ipv6 preference cache: malformed ts {0!r}'.format(cached_ts), print_msg=False) return None # negative age means the system clock jumped backwards (NTP correction etc.) # since we wrote the cache; treat it as a miss rather than as a fresh entry. age = time.time() - int(cached_ts) if age < 0 or age > CACHE_TTL_SECONDS: log_utils.logdebug('ipv6 preference cache: stale entry (age={0:.0f}s, ttl={1}s)'.format(age, CACHE_TTL_SECONDS)) return None return prefer_ipv6 def _write_cache(result): # type: (bool) -> None data = { 'prefer_ipv6': result, 'ts': int(time.time()), } try: utils.atomic_write(CACHE_FILE, json.dumps(data), ensure_dir=True) except (OSError, IOError) as e: log_utils.logwarn('failed to write ipv6 preference cache: {0}'.format(e), print_msg=False) def clear_cache(): # type: () -> None """Drop the on-disk ipv6 preference cache.""" try: os.unlink(CACHE_FILE) except OSError: pass def get_patch_server(): # type: () -> str return config.PATCH_SERVER_IPV6 if ip_proto_selector.is_ipv6_preferred() else config.PATCH_SERVER def get_registration_url(): # type: () -> str return config.REGISTRATION_URL_IPV6 if ip_proto_selector.is_ipv6_preferred() else config.REGISTRATION_URL kcarectl/process_utils.py000064400000010464152527712570011626 0ustar00# Copyright (c) Cloud Linux Software, Inc # Licensed under CLOUD LINUX LICENSE AGREEMENT # http://cloudlinux.com/docs/LICENCE.TXT import functools import os import subprocess import textwrap from . import log_utils, utils if False: # pragma: no cover from typing import Any, List, Optional, Tuple, Union # noqa: F401 @utils.cached def find_cmd(name, paths=None, raise_exc=True): # type: (str, Optional[tuple[str, ...]], bool) -> Optional[str] paths = paths or ('/usr/sbin', '/sbin', '/usr/bin', '/bin') for it in paths: fname = os.path.join(it, name) if os.path.isfile(fname): return fname if raise_exc: raise Exception('{0} could not be found at {1}'.format(name, paths)) else: return None def run_command(command, catch_stdout=False, catch_stderr=False, shell=False, check=False): # mocked: tests/unit/conftest.py stdout = subprocess.PIPE if catch_stdout else None stderr = subprocess.PIPE if catch_stderr else None # We need to eventually keep shell=True as it might break customer's hooks, skip this bandit check. p = subprocess.Popen(command, stdout=stdout, stderr=stderr, shell=shell) # nosec B602 stdout_captured, stderr_captured = p.communicate() # type: Union[bytes, str, None], Union[bytes, str, None] code = p.returncode if stdout_captured is not None: stdout_captured = utils.nstr(stdout_captured) if stderr is not None: stderr_captured = utils.nstr(stderr_captured) log_utils.logdebug( textwrap.dedent( """ Call result for `{cmd}`: exit code {exit_code} === STDOUT === {stdout} === STDERR === {stderr} === END === """ ).format(exit_code=p.returncode, stdout=stdout_captured, stderr=stderr_captured, cmd=' '.join(command)) ) if check and code: exc = subprocess.CalledProcessError(code, command) exc.output = stdout_captured exc.stderr = stderr_captured raise exc return code, stdout_captured, stderr_captured def check_output(args, check=False): # type: (list[str], bool) -> Any _, stdout, _ = run_command(args, catch_stdout=True, check=check) return stdout def _get_parent_pid_and_process_name(pid): # type: (int) -> tuple[Optional[int], Optional[str]] try: # use two subprocesses for ppid and comm to prevent parsing problems (there could be any symbols in comm) cmd_ppid = ['ps', '--no-headers', '-o', 'ppid', '-p', str(pid)] code, stdout, _ = run_command(cmd_ppid, catch_stdout=True) if code: log_utils.loginfo("Could not retrieve process parent PID for PID {pid}".format(pid=pid), print_msg=False) return None, None ppid = stdout.strip() cmd_comm = ['ps', '--no-headers', '-o', 'comm', '-p', str(pid)] code, stdout, _ = run_command(cmd_comm, catch_stdout=True) if code: log_utils.loginfo("Could not retrieve process name for PID {pid}".format(pid=pid), print_msg=False) return None, None name = stdout.strip() return int(ppid), name except Exception as e: log_utils.loginfo( "Could not retrieve process name and parent PID for PID {pid}, error: {err}".format(pid=pid, err=e), print_msg=False ) return None, None def log_all_parent_processes(func): """Decorator that logs parent process chain before calling the wrapped function.""" @functools.wraps(func) def wrapper(*args, **kwargs): _log_all_parent_processes() return func(*args, **kwargs) return wrapper def _log_all_parent_processes(): # type: () -> None process_chain = [] # type: list[tuple[int, Optional[str]]] current_pid = os.getpid() while current_pid != 1 and current_pid != 0: ppid, process_name = _get_parent_pid_and_process_name(current_pid) process_chain.append((current_pid, process_name)) if ppid is None: break current_pid = ppid log_utils.loginfo("Agent parent processes chain:", print_msg=False) for level, (pid, name) in enumerate(reversed(process_chain)): prefix = "-" * level + "->" log_utils.loginfo( '{prefix} "{name}" (pid: {pid})'.format(prefix=prefix, name=name or 'unknown', pid=pid or 'unknown'), print_msg=False ) kcarectl/constants.py000064400000002636152527712570010746 0ustar00# Copyright (c) Cloud Linux Software, Inc # Licensed under CLOUD LINUX LICENSE AGREEMENT # http://cloudlinux.com/docs/LICENCE.TXT import os import sys PRINT_DEBUG = 0 PRINT_INFO = 1 PRINT_WARN = 2 PRINT_ERROR = 3 PRINT_CRITICAL = 4 PY2 = sys.version_info[0] == 2 PATCH_CACHE = '/var/cache/kcare' FEATURE_FLAGS_CACHE = os.path.join(PATCH_CACHE, 'feature_flags.json') UPDATE_STATUS_PATH = os.path.join(PATCH_CACHE, 'update_status.json') UPDATE_ERROR_MAX_LENGTH = 100 CACHE_KEY_HEADER = 'Kc-Cache-Key' CACHE_KEY_DUMP_PATH = '/etc/sysconfig/kcare/cache_key' POLICY_REMOTE = 'REMOTE' POLICY_LOCAL = 'LOCAL' POLICY_LOCAL_FIRST = 'LOCAL_FIRST' KC_PATCH_VERSION = '3' LOG_FILE = '/var/log/kcarectl.log' SIG = '.sig' SIG_JSON = '.json-sig' # false positives by bandit based on the `TOKEN` keyword in name AUTH_TOKEN_HEADER = 'Kc-Auth-Token' # nosec hardcoded_password_string AUTH_TOKEN_DUMP_PATH = '/etc/sysconfig/kcare/auth_token' # nosec hardcoded_password_string # urlopen retry options RETRY_DELAY = 3 RETRY_MAX_DELAY = 30 RETRY_BACKOFF = 2 RETRY_COUNT = 4 # helper vars for tests SKIP_SYSTEMCTL_CHECK = False SYSTEMCTL = '/usr/bin/systemctl' UPDATE_MODE_MANUAL = 'manual' # update is launched manually by `kcarectl -u` UPDATE_MODE_AUTO = 'auto' # update is launched by cron UPDATE_MODE_SMART = 'smart' # update is launched by kcare daemon KMOD_BIN = 'kcare.ko' KPATCH_CTL = '/usr/libexec/kcare/kpatch_ctl' VERSION = '3.8-1.el8' kcsig_verify.py000064400000014134152527712570007622 0ustar00# COPY of scripts/kcsig/kcsig/verify.py from 4fdf3255d5490df60f7dd import base64 import contextlib import json import os import shutil import subprocess import tempfile if False: from typing import Any, Dict, Iterator, List, Mapping, Optional, Sequence, Tuple, Union # noqa: F401 from .types import AnyKey, ReleaseKey, RootKey, RootKeys, Signature, anystr # noqa: F401 AVAILABLE_KINDS = ['rsa4096'] PAE_FIELDS = {'gpg': ('type', 'kind', 'role', 'serial', 'key')} TMPDIR = '/dev/shm' btype = type(b'') utype = type(u'') ntype = type('') class Error(Exception): pass def to_bytes(data, encoding='latin1'): # type: (Union[str, bytes, int], str) -> bytes dtype = type(data) if dtype is btype: return data # type: ignore elif dtype is utype: return data.encode(encoding) # type: ignore elif dtype is int: return to_bytes(str(data)) raise ValueError('Unsupported pae type ' + str(dtype)) def nstr(data): # type: (anystr) -> str dtype = type(data) if dtype is ntype: return data # type: ignore elif dtype is btype: return data.decode('utf-8') # type: ignore elif dtype is utype: # pragma: no cover, used only in py2 return data.encode('utf-8') # type: ignore raise NotImplementedError # pragma: no cover def atomic_write(fname, content, mode='w'): # type: (str, anystr, str) -> None tmp_fname = fname + '.tmp' with open(tmp_fname, mode) as f: f.write(content) os.rename(tmp_fname, fname) def read_file(fname): # type: (str) -> str with open(fname) as f: return f.read() def read_json(fname): # type: (str) -> Any return json.loads(read_file(fname)) def pae(*parts): # type: (Union[str, bytes, int]) -> bytes result = b'%d' % len(parts) for p in parts: bp = to_bytes(p) result += b'%d%s' % (len(bp), bp) return result def pae_fields(data, fields): # type: (Mapping[str, Any], Sequence[str]) -> bytes return pae(*[data[f] for f in fields]) def pae_type(data): # type: (ReleaseKey) -> bytes return pae_fields(data, PAE_FIELDS[data['type']]) def check_key(key): # type: (AnyKey) -> None if key['type'] not in PAE_FIELDS: raise ValueError('invalid key type: ' + nstr(key['type'])) @contextlib.contextmanager def temp_datafile(data, data_is_file): # type: (anystr, bool) -> Iterator[str] if data_is_file: yield data # type: ignore else: with tempfile.NamedTemporaryFile(dir=TMPDIR, prefix='kcsig-data-') as f: f.write(to_bytes(data)) f.flush() yield f.name @contextlib.contextmanager def temp_directory(prefix, dir): # type: (str, str) -> Iterator[str] temp_dir = tempfile.mkdtemp(prefix=prefix, dir=dir) try: yield temp_dir finally: shutil.rmtree(temp_dir) def run_gpg_verify(keyfile, datafile, sigdata): # type: (str, str, bytes) -> None with temp_directory(dir=TMPDIR, prefix='kcsig-gpgtmp-') as tmp_dir: cmd = ['gpg', '--homedir', tmp_dir, '--keyring', keyfile, '--verify', '-', datafile] p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = p.communicate(sigdata) if p.returncode != 0: raise Exception('Verify error: ' + nstr(stderr) + '\n' + nstr(stdout)) def verify_key(signature, key, data, data_is_file): # type: (str, AnyKey, anystr, bool) -> None check_key(key) with tempfile.NamedTemporaryFile(dir=TMPDIR, prefix='kcsig-key-') as key_file: key_file.write(base64.b64decode(to_bytes(key['key']))) key_file.flush() with temp_datafile(data, data_is_file) as datafile: sigdata = base64.b64decode(to_bytes(signature)) run_gpg_verify(key_file.name, datafile, sigdata) def verify_count(signatures, keys, data, data_is_file=False): # type: (Mapping[str, str], Mapping[str, RootKey], anystr, bool) -> Tuple[int, Mapping[str, str]] count = 0 errors = {} for keyid, sig in signatures.items(): if keyid not in keys: errors[keyid] = 'no corresponding root key' continue try: verify_key(sig, keys[keyid], data, data_is_file) except Exception as e: errors[keyid] = str(e) else: count += 1 return count, errors def _verify(sigdata, datafile, root_keys, kinds=None): # type: (Signature, str, RootKeys, Optional[List[str]]) -> None threshold = root_keys.get('threshold', 9999) min_serial = root_keys.get('min_serial', 99999999) errors = {} # type: Dict[str, Union[str, Mapping[str, str]]] kinds = kinds or AVAILABLE_KINDS applicable_keys = {} for keyid, key in sigdata['keys'].items(): try: check_key(key) if key['kind'] not in kinds: errors[keyid] = 'invalid kind {0}, accepted list is {1}'.format(key['kind'], kinds) elif key['serial'] < min_serial: errors[keyid] = 'invalid serial {0}, current is {1}'.format(key['serial'], min_serial) else: applicable_keys[keyid] = key except Exception as e: errors[keyid] = str(e) continue verified_keys = {} for keyid, key in applicable_keys.items(): count, root_errors = verify_count(key['signatures'], root_keys['keys'], data=pae_type(key)) if count < threshold: errors[keyid] = root_errors else: verified_keys[keyid] = key count = 0 for keyid, key in verified_keys.items(): try: verify_key(sigdata['signatures'][keyid], key, data=datafile, data_is_file=True) except Exception as e: errors[keyid] = str(e) else: count += 1 if not count: raise Error('Error validating file signature: ' + json.dumps(errors)) def verify(sigfile, datafile, rootfile, kinds=None): # type: (str, str, str, Optional[List[str]]) -> None sigdata = read_json(sigfile) # type: Signature root_keys = read_json(rootfile) # type: RootKeys _verify(sigdata, datafile, root_keys, kinds=kinds)