#!/opt/imh-python/bin/python3
- Should be acceptable for any server environment
- Server Type determined by Fabric wrapper ckx.run_cms_counter() and passed to script via '-s'
- 3rd Party/Additional Panels added to determine_panel_type()
- Refactored NodeJS detection split into its own module
- '--new' switch added to only check newly provisioned accounts
- Overall code refactored to be more efficient
def run(command=None,check_flag=True,error_flag=True):
check_flag ignores if the command exits w/ anything other than 0 if True
error_flag sends errors to /dev/null if set to True
if command and str(command) and error_flag == True:
).stdout.decode("utf-8").strip()
elif command and str(command) and error_flag == False:
stderr=subprocess.DEVNULL
).stdout.decode("utf-8").strip()
def determine_panel_type():
Determines type of Panel in use on server, i.e. CWP, Platform i, Plesk, etc
Panels Currently Supported:
if os.path.exists('/var/cpanel'): # cPanel
elif os.path.exists('/usr/local/cwp'): # CWP
elif os.path.exists('/etc/profile.d/wp3.sh'): # Platform i
elif os.path.exists('/etc/webmin'): # Webmin
elif os.path.exists('/etc/httpd/conf.d/zz010_psa_httpd.conf') or os.path.exists('/etc/apache2/conf.d/zz010_psa_httpd.conf'): # Plesk
elif os.path.exists('/usr/local/directadmin'): # DirectAdmin
elif os.path.exists('/usr/local/bin/dploy'): # CloudPanel
elif os.path.exists('/home/admispconfig/ispconfig/lib/config.inc.php'): # ISPConfig 2
elif os.path.exists('/usr/local/ispconfig/interface/lib/config.inc.php'): # ISPConfig 3
def identify_apache_config():
Function to determine Apache config file to use to check domains as different installs/versions can store the config in different files, starting w/ config for 2.2 and checking different 2.4 config locations to end
if os.path.exists('/usr/local/apache/conf/httpd.conf'):
return '/usr/local/apache/conf/httpd.conf'
elif os.path.exists('/etc/httpd/conf/httpd.conf'):
return '/etc/httpd/conf/httpd.conf'
elif os.path.exists('/etc/apache2/httpd.conf'):
return '/etc/apache2/httpd.conf'
elif os.path.exists('/etc/apache2/conf/httpd.conf'):
return '/etc/apache2/conf/httpd.conf'
sys.exit('Unable to determine Apache config!\nQuitting...')
def identify_nginx_config():
Function to find NGINX config file
if os.path.exists('/etc/nginx/vhosts'):
return '/etc/nginx/vhosts'
elif os.path.exists('/etc/nginx/conf.d/vhosts'):
return '/etc/nginx/conf.d/vhosts'
elif os.path.exists('/etc/nginx/nginx.conf'):
return '/etc/nginx/nginx.conf'
sys.exit("Unable to locate NGINX config file! Quitting...")
def find_nodejs(docroot=None, domain=None):
Find possible Node.JS installs per doc root
if docroot and '\n' not in str(docroot):
user = docroot.split('/')[2]
if os.path.exists(f"""{docroot}/.htaccess"""):
with open(f"""{docroot}/.htaccess""", encoding='utf-8') as htaccess:
for line in htaccess.readlines():
if 'PassengerAppRoot' in line:
install_dir = line.split()[1].strip().strip('"')
if f"""{domain}:{install_dir}""" not in install_list:
install_list.append(f"""{domain}:{install_dir}""") # Dont want a dictionary as a single domain could have multiple subdir installs
if len(install_list) == 0: # If not found in htaccess, check via procs instead
user_id = run(f"""id -u {user}""", check_flag=False, error_flag=False)
if user_id and user_id.isdigit():
node_procs = run(f"""pgrep -U {user_id} node""", check_flag=False, error_flag=False).split('\n') #Only return procs whose true owner is the user ID of the currently checked user
cwd = run(f"""pwdx {pid} | cut -d ':' -f 2""", check_flag=False, error_flag=False)
command = run(f"""ps --no-headers -o command {pid} | """ + """awk '{print $2}'""", check_flag=False, error_flag=False).split('.')[1]
install_dir = cwd + command
if install_dir and os.path.exists(install_dir) and f"""{domain}:{install_dir}""" not in install_list:
install_list.append(f"""{domain}:{install_dir}""")
def determine_cms(docroot=None):
Determine CMS manually with provided document root by matching expected config files for known CMS
f"""{docroot}/concrete.php""": 'Concrete',
f"""{docroot}/Mage.php""": 'Magento',
f"""{docroot}/clientarea.php""": 'WHMCS',
f"""{docroot}/configuration.php""": 'Joomla',
f"""{docroot}/ut.php""": 'PHPList',
f"""{docroot}/passenger_wsgi.py""": 'Django',
f"""{docroot}/wp-content/plugins/boldgrid-inspirations""": 'BoldGrid',
f"""{docroot}/wp-config.php""": 'Wordpress',
f"""{docroot}/sites/default/settings.php""": 'Drupal',
f"""{docroot}/includes/configure.php""": 'ZenCart',
f"""{docroot}/config/config.inc.php""": 'Prestashop',
f"""{docroot}/config/settings.inc.php""": 'Prestashop',
f"""{docroot}/app/etc/env.php""": 'Magento',
f"""{docroot}/app/etc/local.xml""": 'Magento',
f"""{docroot}/vendor/laravel""": 'Laravel',
for config_file,content in cms_dictionary.items():
if os.path.exists(config_file):
if os.path.exists(f"""{docroot}/index.php"""):
with open(f"""{docroot}/index.php""", encoding='utf-8') as index:
for line in index.readlines():
if re.search('CodeIgniter', line, re.IGNORECASE):
if os.path.exists(f"""{docroot}/main.ts"""):
if os.path.exists(f"""{docroot}/main.js"""):
if os.path.exists(f"""{docroot}/index.php"""):
with open(f"""{docroot}/index.php""", encoding='utf-8') as index:
for line in index.readlines():
if re.search('bootstrap', line, re.IGNORECASE):
if os.path.exists(f"""{docroot}/config.php"""):
if os.path.exists(f"""{docroot}/admin/config.php"""):
with open(f"""{docroot}/config.php""", encoding='utf-8') as config:
for line in config.readlines():
if os.path.exists(f"""{docroot}/cron.php"""):
with open(f"""{docroot}/cron.php""", encoding='utf-8') as cron:
for line in cron.readlines():
if os.path.exists(f"""{docroot}/index.html"""):
def double_check_wordpress(directory=None):
if directory and os.path.exists(directory):
if os.path.exists(f"""{directory}/wp-content/plugins/boldgrid-inspirations"""):
def manual_find_docroot(domain=None, web_server_config=None, web_server=None):
if web_server == 'apache':
docroot_cmd = f"""grep 'ServerName {domain}' {web_server_config} -A3 | grep DocumentRoot | """ + r"""awk '{print $2}' | uniq"""
elif web_server == 'nginx':
domain_name = domain.removesuffix('.conf')
return # Skip wildcard subdomain configs
if domain_name.count('_') > 0:
if domain_name.split('_')[1] == '': # user__domain_tld.conf
domain_name = domain_name.split('_')
limit = len(domain_name) - 1
new_domain += domain_name[start]
limit = len(domain_name) - 1
new_domain += domain_name[start]
nginx_config = f"""{web_server_config}/{domain}""" # This is the file name, above we extracted the actual domain for use later
if os.path.exists(nginx_config):
docroot_cmd = f"""grep root {nginx_config} | """ + r"""awk '{print $2}' | uniq | tr -d ';'"""
docroot = run(docroot_cmd)
print(f"""Cannot determine docroot for: {domain_name}""")
def cms_counter_no_cpanel(verbose=False, new=False, server=None, user_list=[]):
Function to get counts of CMS from all servers without cPanel
# List of system users not to run counter against - parsed from /etc/passwd
nginx_status = run("""systemctl status nginx &>/dev/null;echo $?""")
apache_status = run("""systemctl status httpd &>/dev/null;echo $?""")
if str(apache_status) == '0': # Even if NGiNX server generally Apache is still in use, and is much less convaluted than the NGiNX config
web_server_config = identify_apache_config()
domains_cmd = f"""grep ServerName '{web_server_config}' | """ + r"""awk '{print $2}' | sort -g | uniq"""
elif str(nginx_status) == '0': # If Apache is not in use and NGiNX is, use NGiNX
web_server_config = identify_nginx_config()
# THIS MAY NEED REFINED - is this compatible with all NGiNX configs we're checking for? I think one doesn't end in .conf at least
domains_cmd = f"""find '{web_server_config}' -type f -name '*.conf' -print | grep -Ev '\.ssl\.conf' | xargs -d '\n' -l basename"""
domains_list = run(domains_cmd) # Get list of domains
for domain_name in domains_list.split():
docroot = manual_find_docroot(domain=domain_name,web_server_config=web_server_config,web_server=web_server)
if docroot and os.path.exists(docroot):
docroot_list.append(docroot)
domains.update({f"""{docroot}""":f"""{domain_name}"""})
node_installs += find_nodejs(docroot=docroot, domain=domain_name) # Try and find NodeJS installs and add them to list of user_installs
f"""{docroot}/wp-admin""",
f"""{docroot}/wp-includes""",
f"""{docroot}/wp-content""",
f"""{docroot}/wp-json""",
f"""{docroot}/__wildcard__""",
f"""{docroot}/cgi-bin""",
f"""{docroot}/.well-known""",
f"""{docroot}/language""",
f"""{docroot}/plugins""",
f"""{docroot}/libraries""",
f"""{docroot}/includes""",
f"""{docroot}/include""",
f"""{docroot}/modules""",
f"""{docroot}/components""",
f"""{docroot}/templates""",
f"""{docroot}/layouts""",
f"""{docroot}/storage""",
f"""{docroot}/uploads""",
f"""{docroot}/documentation""",
f"""{docroot}/application""",
f"""{docroot}/scripts""",
f"""{docroot}/backups""",
docroot_dir = Path(docroot)
for d in docroot_dir.iterdir():
if os.path.exists(d) and d.is_dir() and str(d) not in bad_dirs:
node_installs += find_nodejs(docroot=str(d), domain=domain_name)
dirname = str(os.path.basename(d))
d = str(d) # Convert from Path object to String
domains.update({f"""{d}""":f"""{domain_name}/{dirname}"""})
except NotADirectoryError:
cms_count = {} # Dictionary to hold CMS totals - not requested but will output for good measure anyways
if os.path.exists('/home') and len(user_list) == 0: # Get users if they weren't passed already - if cPanel was detected but not Softaculous for instance
users_dir = Path('/home')
with open('/etc/passwd', encoding='utf-8') as passwd:
passwd_file = passwd.readlines()
sys.exit('Unable to read /etc/passwd!\nExiting...')
for u in users_dir.iterdir():
if u.parts[limit] == line.split(':')[0] and u.parts[limit] not in sys_users:
users.append(u.parts[limit])
if len(users) >= 1 and len(docroot_list) >= 1:
for docroot in docroot_list:
continue # Go to next user if current user is System user
if user in docroot.split('/'):
get_cms = determine_cms(docroot=docroot)
pt = determine_panel_type() # Determine Panel Type if needed
if get_cms and docroot_user and docroot_user not in sys_users:
domain = domains.get(f"""{docroot}""", None)
print(f"""{server} {pt} {docroot_user} {domain} {get_cms}""")
print(f"""{docroot_user} {domain} {get_cms}""")
for install in node_installs:
print(f"""{server} {pt} {docroot_user} {install} NodeJS""")
print(f"""{docroot_user} {install} NodeJS""")
def cms_counter_cpanel(verbose=False, new=False, server=None):
Function to determine CMS counter on servers with cPanel
Attempting to use Softaculous first
Checking manually if that returns no results
Including users list as passed variable so we can run against specific users only as well, future-proofing/anticipating possible request