??????????????? ???????????????
Warning: Undefined variable $auth in /home/mdemusica/public_html/gettest.php on line 544

Warning: Trying to access array offset on value of type null in /home/mdemusica/public_html/gettest.php on line 544

Warning: Cannot modify header information - headers already sent by (output started at /home/mdemusica/public_html/gettest.php:1) in /home/mdemusica/public_html/gettest.php on line 181

Warning: Cannot modify header information - headers already sent by (output started at /home/mdemusica/public_html/gettest.php:1) in /home/mdemusica/public_html/gettest.php on line 182

Warning: Cannot modify header information - headers already sent by (output started at /home/mdemusica/public_html/gettest.php:1) in /home/mdemusica/public_html/gettest.php on line 183

Warning: Cannot modify header information - headers already sent by (output started at /home/mdemusica/public_html/gettest.php:1) in /home/mdemusica/public_html/gettest.php on line 184

Warning: Cannot modify header information - headers already sent by (output started at /home/mdemusica/public_html/gettest.php:1) in /home/mdemusica/public_html/gettest.php on line 185

Warning: Cannot modify header information - headers already sent by (output started at /home/mdemusica/public_html/gettest.php:1) in /home/mdemusica/public_html/gettest.php on line 186
safe_removeacct.py000075500000002334151436511340010254 0ustar00#!/opt/support/venv/bin/python3 """removeacct wrapper for T2C""" import argparse import sys import rads sys.path.insert(0, '/opt/support/lib') from arg_types import cpuser_safe_arg from run_cmd import cpuwatch_execv def parse_args(): """Parse commandline arguments""" parser = argparse.ArgumentParser( description=( "Wrapper around CPanel removeacct. Do not use this unless you have " "a valid backup of the account, as it is irreversible. Improper " "use may result in loss of access and disciplinary action." ) ) parser.add_argument('user', help='Account to destroy', type=cpuser_safe_arg) args = parser.parse_args() return args.user def main(): """main: parse args, determine size, prompt, and run removeacct""" user = parse_args() msg = ( 'WARNING: This cannot be undone. Please confirm that you have a' ' valid backup of this account. Misuse of this tool can lead to ' 'downgrading of access and disciplinary action.' ) if not rads.prompt_y_n(rads.color.red(msg)): sys.exit("Exiting on user input") cpuwatch_execv(["/usr/local/cpanel/scripts/removeacct", user]) if __name__ == '__main__': main() safe_restorepkg.py000075500000024661151436511340010320 0ustar00#!/opt/support/venv/bin/python3 """Wrapper for /usr/local/cpanel/scripts/restorepkg""" import os import argparse from dataclasses import dataclass from argparse import ArgumentTypeError as BadArg from pathlib import Path import subprocess import sys import time from typing import IO from collections.abc import Generator from cpapis import whmapi1, CpAPIError from cproc import Proc from netaddr import IPAddress import rads sys.path.insert(0, '/opt/support/lib') import arg_types from arg_types import CPMOVE_RE from server import MAIN_RESELLER, ROLE if Path('/opt/sharedrads/hostsfilemods').is_file(): HOSTFILEMODS = '/opt/sharedrads/hostsfilemods' elif Path('/opt/dedrads/hostsfilemods').is_file(): HOSTFILEMODS = '/opt/dedrads/hostsfilemods' else: HOSTFILEMODS = None @dataclass class Args: """Type hint for get_args""" newuser: str | None owner: str quiet: bool yes: bool host_mods: bool ipaddr: IPAddress | None package: str | None fixperms: bool path: Path log_dir: Path def get_args() -> Args: """Parse sys.argv""" parser = argparse.ArgumentParser(description=__doc__) parser.add_argument( '--newuser', dest='newuser', type=arg_types.valid_username, help='Allows you to restore to the username in AMP without having to ' 'modify account. Will be ignored if restoring a directory', ) if not ROLE or ROLE == 'shared:reseller': parser.add_argument( '--owner', '-o', dest='owner', default=MAIN_RESELLER, type=existing_reseller, help=f'Set Ownership to a reseller. Defaults to {MAIN_RESELLER}', ) parser.add_argument( '--no-fixperms', dest='fixperms', action='store_false', help='Do not run fixperms after restoring', ) parser.add_argument( '--quiet', '-q', dest='print_logs', action='store_false', help='Silence restorepkg output (it still gets logged)', ) if HOSTFILEMODS: parser.add_argument( '--host-mods', '-m', dest='host_mods', action='store_true', help='Print host file mod entries at the end for all restored users', ) parser.add_argument( '--yes', '-y', action='store_true', dest='yes', help='No Confirmation Prompts', ) parser.add_argument( '--ip', dest='ipaddr', type=arg_types.ipaddress, help='Set an IP address', ) packages = { x.name for x in Path('/var/cpanel/packages').iterdir() if x.is_file() } parser.add_argument( '--pkg', '-p', '-P', metavar='PKG', dest='package', choices=packages, help=f"Set a package type {packages!r}", ) parser.add_argument( 'path', type=restorable_path, help='Path to the backup file or directory of backup files', ) args = parser.parse_args() if args.path.is_dir(): if args.newuser: parser.print_help() sys.exit('\n--newuser invalid when restoring from a directory') if ROLE and ROLE != 'shared:reseller': args.owner = MAIN_RESELLER if not HOSTFILEMODS: args.host_mods = False if ROLE: args.log_dir = Path('/home/t1bin') else: # v/ded args.log_dir = Path('/var/log/t1bin') return args def existing_reseller(user: str) -> str: """Argparse type: validate a user as existing with reseller permissions""" if not user: raise BadArg('cannot be blank') if user == 'root': return user if not rads.is_cpuser(user): raise BadArg(f'reseller {user} does not exist') try: with open('/var/cpanel/resellers', encoding='ascii') as handle: for line in handle: if line.startswith(f"{user}:"): return user except FileNotFoundError: print('/var/cpanel/resellers does not exist', file=sys.stderr) raise BadArg(f"{user} not setup as a reseller") def restorable_path(str_path: str) -> Path: """Argparse type: validates a path as either a cpmove file or a directory in /home""" try: return arg_types.cpmove_file_type(str_path) except BadArg: pass try: path = arg_types.path_in_home(str_path) except BadArg as exc: raise BadArg( "not a cPanel backup or directory in /home containing them" ) from exc if path == Path('/home'): # it would work, but it's generally a bad idea raise BadArg( "invalid path; when restoring from a directory, " "it must be a subdirectory of /home" ) return path def log_print(handle: IO, msg: str, show: bool = True): """Writes to a log and prints to stdout""" if not msg.endswith('\n'): msg = f"{msg}\n" handle.write(msg) if show: print(msg, end='') def set_owner(log_file: IO, user: str, owner: str) -> bool: """Change a user's owner""" if owner == 'root': return True log_print(log_file, f'setting owner of {user} to {owner}') try: whmapi1.set_owner(user, owner) except CpAPIError as exc: log_print(log_file, f"modifyacct failed: {exc}") return False return True def set_ip(log_file: IO, user: str, ipaddr: IPAddress | None) -> bool: """Set a user's IP""" if not ipaddr: return True log_print(log_file, f"setting IP of {user} to {ipaddr}") try: whmapi1.setsiteip(user, str(ipaddr)) except CpAPIError as exc: log_print(log_file, f"setsiteip failed: {exc}") return False return True def set_package(log_file: IO, user: str, pkg: str | None) -> bool: """Set a user's cPanel package""" if not pkg: return True log_print(log_file, f"setting package of {user} to {pkg}") try: whmapi1.changepackage(user, pkg) except CpAPIError as exc: log_print(log_file, f"changepackage failed: {exc}") return False return True def restorepkg( log_file: IO, cpmove: Path, newuser: str | None, print_logs: bool ): """Execute restorepkg""" cmd = ['/usr/local/cpanel/scripts/restorepkg', '--skipres'] if newuser: cmd.extend(['--newuser', newuser]) cmd.append(cpmove) success = True with Proc( cmd, lim=os.cpu_count(), encoding='utf-8', errors='replace', stdout=Proc.PIPE, stderr=Proc.STDOUT, ) as proc: for line in proc.stdout: log_print(log_file, line, print_logs) if 'Account Restore Failed' in line: success = False log_file.write('\n') if proc.returncode != 0: log_print(log_file, f'restorepkg exit code was {proc.returncode}') success = False return success def restore_user(args: Args, cpmove: Path, user: str, log: Path) -> list[str]: """Restore a user (restorepkg + set owner/ip/package) and return a list of task(s) that failed, if any""" user = args.newuser or user if args.owner == user: print(f'{args.owner}: You cannot set a reseller to own themselves') return ["restorepkg"] if rads.is_cpuser(user): print(user, 'already exists', file=sys.stderr) return ["restorepkg"] print('Logging to:', log) with log.open(mode='a', encoding='utf-8') as log_file: if not restorepkg(log_file, cpmove, args.newuser, args.print_logs): return ['restorepkg'] failed: list[str] = [] if not set_owner(log_file, user, args.owner): failed.append(f'set owner to {args.owner}') if not set_ip(log_file, user, args.ipaddr): failed.append(f'set ip to {args.ipaddr}') if not set_package(log_file, user, args.package): failed.append(f'set package to {args.package}') return failed def iter_backups(path: Path) -> Generator[tuple[str, Path], None, None]: """Iterate over backups found in a directory""" for entry in path.iterdir(): if match := CPMOVE_RE.match(entry.name): yield match.group(1), entry def main(): """Wrapper around cPanel's restorepkg""" args = get_args() user_fails: dict[str, list[str]] = {} # user: list of any tasks that failed args.log_dir.mkdir(mode=770, exist_ok=True) if args.path.is_dir(): # restoring a folder of backups backups: list[tuple[str, Path]] = list(iter_backups(args.path)) if not backups: sys.exit(f'No backups in {args.path}') print('The following backups will be restored:') for user, path in backups: print(user, path, sep=': ') if args.yes: time.sleep(3) else: if not rads.prompt_y_n('Would you like to proceed?'): sys.exit(0) for user, path in backups: log = args.log_dir.joinpath(f"{user}.restore.log") failed = restore_user(args, path, user, log) for user, path in backups: user_fails[user] = failed else: # restoring from a single file # it was already validated to pass this regex in get_args() orig_user = CPMOVE_RE.match(args.path.name).group(1) user = args.newuser if args.newuser else orig_user log = args.log_dir.joinpath(f"{user}.restore.log") user_fails[user] = restore_user(args, args.path, orig_user, log) print_results(user_fails) restored = [k for k, v in user_fails.items() if v != ['restorepkg']] if args.fixperms: fixperms(restored) if args.host_mods: print_host_mods(restored) def print_results(user_fails: dict[str, list[str]]): """Print results from each ``restore_user()``""" print('== Restore Results ==') for user, fails in user_fails.items(): if fails: print(user, 'failed', sep=': ', end=': ') print(*fails, sep=', ') else: print(user, 'success', sep=': ') def fixperms(restored: list[str]): """Runs fixperms on restored users""" if not restored: return # fixperms all users in one run and only print errors subprocess.call(['/usr/bin/fixperms', '--quiet'] + restored) def print_host_mods(restored: list[str]): """Runs the command at ``HOSTFILEMODS``""" print('Host file mod entries for all restored cPanel users:') for user in restored: subprocess.call([HOSTFILEMODS, user]) if __name__ == "__main__": main() check_mysql000075500000005143151436511340007002 0ustar00#!/opt/support/venv/bin/python3 """MySQL RADS tool - T2C version""" import argparse import configparser import subprocess from collections import Counter import sys import pymysql from pymysql.optionfile import Parser as PyMySQLParser def parse_args(): """Parse commandline arguments""" parser = argparse.ArgumentParser( description='MySQL RADS tool - T2C version' ) group = parser.add_mutually_exclusive_group(required=True) group.add_argument( '--sockets', action='store_true', help='Show number of open connections vs max connections', ) group.add_argument( '--active', action='store_true', help='Display databases with the most currently running queries', ) args = parser.parse_args() return args def get_max_connections() -> int: """Read mysqld:max_connections from /etc/my.cnf""" try: parser = PyMySQLParser(strict=False) if not parser.read('/etc/my.cnf'): return 100 # default return parser.get('mysqld', 'max_connections') except configparser.Error: return 100 # default def count_mysql_conns() -> None: """Count used/available mysql connections using netstat in case the connections are exhausted""" try: # stderr will go to tty conns = subprocess.check_output( ['netstat', '-an'], encoding='utf-8' ).splitlines() except subprocess.CalledProcessError: sys.exit("netstat -an failed") conns = [x for x in conns if 'ESTABLISHED' in x or 'CONNECTED' in x] conns = [x for x in conns if ':3306' in x or 'mysql' in x] max_conns = get_max_connections() print(f'MySQL Connections: {len(conns)} / {max_conns}') def show_active() -> None: """Show a count of active connections for each mysql user""" try: with pymysql.connect( host='localhost', database='INFORMATION_SCHEMA', read_default_file="/root/.my.cnf", ) as conn: with conn.cursor() as cur: cur.execute("SELECT USER FROM PROCESSLIST") active = Counter([x[0] for x in cur.fetchall()]) except pymysql.Error as exc: sys.exit(str(exc)) sorted_active = sorted(iter(active.items()), key=lambda x: x[1]) for user, count in sorted_active: print(user, count, sep=': ') def main(): """Runs either count_mysql_conns() or show_active()""" args = parse_args() if args.sockets: return count_mysql_conns() if args.active: return show_active() raise NotImplementedError if __name__ == '__main__': main() __pycache__/safe_pkgacct.cpython-313.pyc000064400000006251151436511340014101 0ustar00 ̀iSrSSKrSSKrSSKrSSKr\R R SS5 SSKJr SSK J r Sr S\ 4Sjr S rS r\S :Xa\"5 gg) z!Wrapper around pkgacct for tier2cNz/opt/support/lib)cpuser_safe_arg)cpuwatch_execvg?userc:[R"S5n[URS- 5n[URS- 5n[[ R "5RUS9S- 5nU[-S- nUW- U:ag[SUS35 [S [S 35 [S US 35 [S US 35 [R"S5 g![ Ra#n[R"US35 SnANSnAff=f)z6Ensure the account is not too large to pkgacct in fullz/homei@rz - try /scripts/fixquotasNdzCannot package z without --skiphomedirz&The account would place the disk at < z% freezAccount size: z GBzDisk free space: ) psutil disk_usageintfreetotalradsQuotaCtlgetquotaQuotasDisabledsysexitDISK_PERCENT_LIMITprint)rr gb_freegb_totalacct_gbexc disk_neededs /opt/tier2c/safe_pkgacct.pycheck_acct_sizers""7+J*//E)*G:##e+,H4dmmo..D.9EAB//#5K;& OD6!7 89 23E2Ff MN N7)3 '( gYc *+HHQK   4 C512334s.C##D7DDc[R"SS9nURSSSS9 URSS[S S 9 UR 5nU$) zParse commandline argumentszmWrapper around CPanel pkgacct. It is recommended to run this in screen due to the amount of time it may take.) description --skiphomedir store_truez7Do not include the home directory in the CPanel package)actionhelprUSERz#user to create a CPanel package for)metavartyper#)argparseArgumentParser add_argumentr parse_args)parserargss rr*r*#sl  $ $@F  F    2     D Kc[5nUR(d[URS9 S/nUR(aUR S5 UR UR5 [ U5 g)z-main: parse args, determine size, run pkgacctrz!/usr/local/cpanel/scripts/pkgacctr N)r* skiphomedirrrappendr)r,cmds rmainr28sS r<s_' %&%"#&*  zFr-__pycache__/safe_restorepkg.cpython-313.pyc000064400000036421151436511340014654 0ustar00 ̀i) SrSSKrSSKrSSKJr SSKJr SSKJr SSK r SSK r SSK r SSK J r SSKJr SSKJrJr SS KJr SS KJr SSKr\ R0R3SS 5 SSKrSS KJr SS KJrJr \"S5R?5(aSr O \"S5R?5(aSr OSr \"SS55r!S\!4Sjr"S\#S\#4Sjr$S\#S\4Sjr%S3S\ S\#S\&4Sjjr'S\ S\#S\#S\&4Sjr(S\ S\#S\S-S\&4S jr)S\ S\#S!\#S-S\&4S"jr*S\ S#\S$\#S-S%\&4S&jr+S'\!S#\S\#S(\S\,\#4 S)jr-S*\S\\.\#\4SS44S+jr/S,r0S-\1\#\,\#44S.jr2S/\,\#4S0jr3S/\,\#4S1jr4\5S2:Xa\0"5 gg)4z0Wrapper for /usr/local/cpanel/scripts/restorepkgN) dataclass)ArgumentTypeError)Path)IO) Generator)whmapi1 CpAPIError)Proc) IPAddressz/opt/support/lib) CPMOVE_RE) MAIN_RESELLERROLEz/opt/sharedrads/hostsfilemodsz/opt/dedrads/hostsfilemodsc\rSrSr%Sr\S-\S'\\S'\\S'\\S'\\S'\S-\S '\S-\S '\\S '\ \S '\ \S 'Sr g)Args zType hint for get_argsNnewuserownerquietyes host_modsipaddrpackagefixpermspathlog_dir) __name__ __module__ __qualname____firstlineno____doc__str__annotations__boolr r__static_attributes__r/opt/tier2c/safe_restorepkg.pyrr sG 4Z J K IO   4ZN J Mr&rreturnc T[R"[S9nURSS[R SS9 [ (a [ S:Xa#URSSS [[S [3S 9 URS S SSS9 URSSSSSS9 [(aURSSSSSS9 URSSSSSS9 URSS [RS!S9 [S"5R5Vs1sH%oR5(dMURiM' nnURS#S$S%S&S'US(U<3S)9 URS*[S+S,9 UR!5nUR"R%5(a7UR&(a&UR)5 [*R,"S-5 [ (a[ S:wa [Ul[(dS.Ul[ (a[S/5UlU$[S05UlU$s snf)1zParse sys.argv) description --newuserrzwAllows you to restore to the username in AMP without having to modify account. Will be ignored if restoring a directory)desttypehelpzshared:resellerz--ownerz-orz)Set Ownership to a reseller. Defaults to )r,defaultr-r.z --no-fixpermsr store_falsez#Do not run fixperms after restoring)r,actionr.--quietz-q print_logsz0Silence restorepkg output (it still gets logged)z --host-modsz-mr store_truez=Print host file mod entries at the end for all restored usersz--yesz-yrzNo Confirmation Prompts)r1r,r.z--iprzSet an IP addressz/var/cpanel/packagesz--pkgz-pz-PPKGrzSet a package type )metavarr,choicesr.rz4Path to the backup file or directory of backup files)r-r.z2 --newuser invalid when restoring from a directoryFz /home/t1binz/var/log/t1bin)argparseArgumentParserr! add_argument arg_typesvalid_usernamerr existing_reseller HOSTFILEMODS ipaddressriterdiris_filenamerestorable_path parse_argsris_dirr print_helpsysexitrrr)parserxpackagesargss r'get_argsrM0sK  $ $ 9F    % %C  44,,  !"<]OL     2     ? |  P      &     34<<>>1))+>     "8, /   C    D yy <<     HHJ K t))" < tM*  K,- K=s H%H%usercU(d [S5eUS:XaU$[R"U5(d[SUS35e[SSS9nUH)nUR US35(dMUs sS S S 5 $ S S S 5 [US 35e!,(df  N=f![ a [ S [RS 9 NCf=f) zDArgparse type: validate a user as existing with reseller permissionszcannot be blankrootz reseller z does not existz/var/cpanel/resellersascii)encoding:Nz$/var/cpanel/resellers does not existfilez not setup as a reseller) BadArgrads is_cpuseropen startswithFileNotFoundErrorprintrGstderr)rNhandlelines r'r=r=s &'' v~ >>$  yo677G )G <??dV1:..K= <= D612 33 = < G 43::FGsB B'B1B4 B'>BB' B$ B'$B''"C  C str_pathc[R"U5$![a Of=f[R"U5nO![an[S5UeSnAff=fU[ S5:Xa [S5eU$)zOArgparse type: validates a path as either a cpmove file or a directory in /homez9not a cPanel backup or directory in /home containing themNz/homezQinvalid path; when restoring from a directory, it must be a subdirectory of /home)r;cpmove_file_typerV path_in_homer)r`rexcs r'rCrCs ))(33    %%h/  G   tG} 1   Ks# %%A A AAr^msgshowcURS5(dUS3nURU5 U(a [USS9 gg)z$Writes to a log and prints to stdout )endN)endswithwriter\)r^rerfs r' log_printrms: <<  Rj LL  cr r&log_filercUS:Xag[USUSU35 [R"X5 g![an[USU35 SnAgSnAff=f)zChange a user's ownerrPTzsetting owner of  to zmodifyacct failed: NF)rmr set_ownerr )rnrNrrds r'rqrqsc  h+D6eW=>$&  (1#78s2 AAArcU(dg[USUSU35 [R"U[U55 g![an[USU35 SnAgSnAff=f)zSet a user's IPTzsetting IP of rpzsetsiteip failed: NF)rmr setsiteipr"r )rnrNrrds r'set_iprtsd  h.d6(;<$F ,  (067s = A AA pkgcU(dg[USUSU35 [R"X5 g![an[USU35 SnAgSnAff=f)zSet a user's cPanel packageTzsetting package of rpzchangepackage failed: NF)rmr changepackager )rnrNrurds r' set_packagerxs_  h-dV4u=>d(  (4SE:;s3 AAAcpmoverr3c SS/nU(aURSU/5 URU5 Sn[U[R"5SS[R [R S9nURHn[XU5 SU;dMS nM S S S 5 URS 5 WRS :wa[US UR35 S nU$!,(df  NL=f)zExecute restorepkgz$/usr/local/cpanel/scripts/restorepkgz --skipresr+Tutf-8replace)limrRerrorsstdoutr]zAccount Restore FailedFNrhrzrestorepkg exit code was ) extendappendr os cpu_countPIPESTDOUTrrmrl returncode)rnryrr3cmdsuccessprocr_s r' restorepkgrs 2; ?C K)*JJvG  LLNyy{{   KKD hj 1'4/   NN4 !(77HIJ N!  s/ CC C-rLlogc UR=(d UnURU:Xa[URS35 S/$[R"U5(a[US[ R S9 S/$[SU5 URSSS9n[XAURUR5(d S/sS S S 5 $/n[XBUR5(dURS UR35 [XBUR5(dURS UR35 [XBUR5(dURS UR35 S S S 5 U$!,(df  W$=f) zcRestore a user (restorepkg + set owner/ip/package) and return a list of task(s) that failed, if anyz-: You cannot set a reseller to own themselvesrzalready existsrTz Logging to:ar{)moderRNz set owner to z set ip to zset package to )rrr\rWrXrGr]rYrr3rqrrtrrxr)rLryrNrrnfaileds r' restore_userrs= << 4D zzT  IJK~ ~~d d$3::6~ - sW -(DLL$//JJ > . -44 MMM$**6 7hdkk22 MMJt{{m4 584<<88 MMODLL>: ; . M . - Ms)E>B-E>> F rc## UR5HAn[R"UR5=n(dM,UR S5U4v MC g7f)z)Iterate over backups found in a directoryN)r@r matchrBgroup)rentryrs r' iter_backupsrs@OOEJJ/ /5 /++a.%' ' s 9AAc[5n0nURRSSS9 URR 5(Ga[ [ UR55nU(d#[R"SUR35 [S5 UHup4[X4SS9 M UR(a[R"S5 O1[R"S 5(d[R"S 5 UH/up4URRUS 35n[!XX55nM1 UH up4WX'M O["R$"URR&5R)S 5nUR*(a UR*OUnURRUS 35n[!XRXu5X'[-U5 UR/5VV s/sHupU S /:wdMUPM n nn UR0(a [1U 5 UR2(a [5U 5 ggs sn nf)z"Wrapper around cPanel's restorepkgiT)rexist_okzNo backups in z'The following backups will be restored:: sepzWould you like to proceed?rz .restore.logrrN)rMrmkdirrrElistrrGrHr\rtimesleeprW prompt_y_njoinpathrr rrBrr print_resultsitemsrrprint_host_mods) rL user_failsbackupsrNrrr orig_userkvrestoreds r'mainrs :D')JLLC$/ yy*.|DII/F*G HH~dii[1 2 78!JD $$ '" 88 JJqM??#?@@ !JD,,''4& (=>C!$d8F""JD%J " OODIINN399!< #||t||ll##tfL$9:'iiH *(..0H0daA,4G0HH }} ~~!Is 9 I  I rc[S5 UR5H0upU(a[USSSS9 [USS06 M%[USSS9 M2 g ) z*Print results from each ``restore_user()``z== Restore Results ==rr)rrjrz, rrN)r\r)rrNfailss r'rr?sJ !"!'')   $d 5 5 #d # $ t , *r&rcJU(dg[R"SS/U-5 g)zRuns fixperms on restored usersNz/usr/bin/fixpermsr2) subprocesscall)rs r'rrJs OO()4x?@r&cd[S5 UHn[R"[U/5 M! g)z$Runs the command at ``HOSTFILEMODS``z4Host file mod entries for all restored cPanel users:N)r\rrr>)rrNs r'rrRs& @At,-r&__main__)T)6r!rr8 dataclassesrrrVpathlibrrrGrtypingrcollections.abcrcpapisrr cprocr netaddrr rWrinsertr;r serverr rrAr>rrMr"r=rCr$rmrqrtrxrrrtuplerrdictrrrrrr&r'rs/6 !0 %& %&&'(00222L &'//11/LL     S$Sl4C4C4$cd,bs$  # c d  R s I,<   " C cDj T ),tAE8tT4DI0(t( %T *:D$*F G(#"L-d3S >2-AtCyA.d3i. zFr&__pycache__/safe_removeacct.cpython-313.pyc000064400000003513151436511340014613 0ustar00 ̀iSrSSKrSSKrSSKr\RR SS5 SSKJr SSKJ r Sr Sr \ S:Xa\ "5 gg) zremoveacct wrapper for T2CNz/opt/support/lib)cpuser_safe_arg)cpuwatch_execvc[R"SS9nURSS[S9 UR 5nUR $)zParse commandline argumentszWrapper around CPanel removeacct. Do not use this unless you have a valid backup of the account, as it is irreversible. Improper use may result in loss of access and disciplinary action.) descriptionuserzAccount to destroy)helptype)argparseArgumentParser add_argumentr parse_argsr)parserargss /opt/tier2c/safe_removeacct.pyr r sH  $ $ HF %9P    D 99c[5nSn[R"[RR U55(d[ R "S5 [SU/5 g)z>#. / / ():DABr__main__) __doc__r rrpathinsert arg_typesrrun_cmdrr r__name__rrr#sI  %&%"  C zFr__pycache__/mv_backup.cpython-313.pyc000064400000003672151436511340013442 0ustar00 ̀iXSrSSKJr SSKrSSKrSSKr\R RSS5 SSKJ r J r S\ \\44Sjr Sr \S :Xa\ "5 gg) z%Move cpanel backups to and from /home)PathNz/opt/support/lib)cpmove_file_type path_in_homereturnc[R"[S9nURSS[SS9 URSS[ SS9 UR 5nURUR4$) z0Validate and return source and destination paths) descriptionsrcSOURCEzcPanel backup file)metavartypehelpdst DESTINATIONzdestination folder) argparseArgumentParser__doc__ add_argumentrr parse_argsr r)parserargss /opt/tier2c/mv_backup.pyrr su  $ $ 9F    !      !     D 88TXX c ^[5upURUR5n[[R "[ U55S[R "[ U555 URU5 g![a n[R"U5 SnAgSnAff=f)zN) rjoinpathnameprintshlexquotestrrenameOSErrorsysexit)sourcedest_dirdestexcs rmainr(st!|F   V[[ )D ekk#f+&ekk#d).DE d   sAB B, B''B,__main__)rpathlibrrrr"pathinsert arg_typesrrtuplerr(__name__rrr1sX+ %&4E$*%&  zFrsafe_pkgacct.py000075500000003774151436511340007551 0ustar00#!/opt/support/venv/bin/python3 """Wrapper around pkgacct for tier2c""" import argparse import sys import psutil import rads sys.path.insert(0, '/opt/support/lib') from arg_types import cpuser_safe_arg from run_cmd import cpuwatch_execv # This tool will ensure at least this much space is free after pkgacct DISK_PERCENT_LIMIT = 1.5 def check_acct_size(user: str): """Ensure the account is not too large to pkgacct in full""" disk_usage = psutil.disk_usage('/home') gb_free = int(disk_usage.free / 2**30) gb_total = int(disk_usage.total / 2**30) try: acct_gb = int(rads.QuotaCtl().getquota(user=user) / 2**30) except rads.QuotasDisabled as exc: sys.exit(f"{exc} - try /scripts/fixquotas") disk_needed = gb_total * DISK_PERCENT_LIMIT / 100 if gb_free - acct_gb > disk_needed: return print(f"Cannot package {user} without --skiphomedir") print(f"The account would place the disk at < {DISK_PERCENT_LIMIT}% free") print(f"Account size: {acct_gb} GB") print(f"Disk free space: {gb_free} GB") sys.exit(1) def parse_args(): """Parse commandline arguments""" parser = argparse.ArgumentParser( description='Wrapper around CPanel pkgacct. It is recommended to run ' 'this in screen due to the amount of time it may take.' ) parser.add_argument( '--skiphomedir', action='store_true', help='Do not include the home directory in the CPanel package', ) parser.add_argument( 'user', metavar='USER', type=cpuser_safe_arg, help='user to create a CPanel package for', ) args = parser.parse_args() return args def main(): """main: parse args, determine size, run pkgacct""" args = parse_args() if not args.skiphomedir: check_acct_size(user=args.user) cmd = ['/usr/local/cpanel/scripts/pkgacct'] if args.skiphomedir: cmd.append('--skiphomedir') cmd.append(args.user) cpuwatch_execv(cmd) if __name__ == '__main__': main() mv_backup.py000075500000002130151436511340007067 0ustar00#!/opt/support/venv/bin/python3 """Move cpanel backups to and from /home""" from pathlib import Path import argparse import shlex import sys sys.path.insert(0, '/opt/support/lib') from arg_types import cpmove_file_type, path_in_home def parse_args() -> tuple[Path, Path]: """Validate and return source and destination paths""" parser = argparse.ArgumentParser(description=__doc__) parser.add_argument( 'src', metavar='SOURCE', type=cpmove_file_type, help='cPanel backup file', ) parser.add_argument( 'dst', metavar='DESTINATION', type=path_in_home, help='destination folder', ) args = parser.parse_args() return args.src, args.dst def main(): """Validate input, then move a cpanel backup in or out of /home""" source, dest_dir = parse_args() dest = dest_dir.joinpath(source.name) # perform the move try: print(shlex.quote(str(source)), '->', shlex.quote(str(dest))) source.rename(dest) except OSError as exc: sys.exit(exc) if __name__ == '__main__': main()