File zm_database_init of Package ZoneMinder
#!/usr/bin/python
# -*- coding: utf-8 -*-
# zm_database_init version 1.0.2
#
# Author: Monex
# this script is under the same license as the package itself.
#
# Please submit bugfixes or comments to monex@liquid-co.de
import os.path
import re
import sys
import os
import getopt
import posix
import shutil
import getpass
########## important settings ################
VERSION = "1.25.0"# current version of zm
ZM_PATH ="/usr/share/zm" # path to zm shared dir
ZM_CONFIG = "/etc/zm.conf"
########## /important settings ###############
########## globals ###############
version_string = re.compile("ZM_VERSION=(.*)")
sql_string = re.compile("zm_update-(.*)\.sql")
zm_db_string = re.compile("ZM_PATH_BUILD=(.*)")
def updateVersionString(config, oldversion, toversion):
""" replaces the version string in config file with new version string"""
config = config.replace(oldversion, toversion, 1)
writeConfig(config)
def checkZmPath(config):
""" checks for the correct path in zm.conf for db upgrade"""
path = zm_db_string.search(config)
if path:
if path.group(1) != ZM_PATH:
print "found wrong ZM_BUILD Path in config file could not perform db upgrade"
proceed = raw_input("should it be updated? [Y/n]: ")
if proceed.lower() == "y" or proceed == "" or proceed.lower() == "yes":
lines = config.split("\n")
config = ""
for line in lines:
if zm_db_string.search(line):
line = "ZM_PATH_BUILD=" + ZM_PATH
config += line + "\n"
writeConfig(config)
print "ZM_PATH_BUILD successfully updated"
else:
print "WARNING: update may fail when ZM_PATH_BUILD not set to " + ZM_PATH
else:
print "config file wrong"
sys.exit(1)
return config
def checkMyCnf():
""" checks for settings in ~/.my.cnf for client user root entry """
mycnf = ""
try:
file = open(posix.environ["HOME"]+"/.my.cnf", "r")
mycnf = file.read()
file.close()
except:
return False
clientEntriesReg = re.compile("\[client\]([\w\s\d=#]*)")
clientEntries = clientEntriesReg.search(mycnf)
if clientEntries:
lines = clientEntries.group(1).split("\n")
pwOk = False
userOk = False
for line in lines:
commentPos = line.find("#")
if commentPos >= 0:
line = line[:commentPos]
if line == "":
continue
userSearch = re.compile("user\s*=\s*([\S.]*)", re.I)
passSearch = re.compile("password\s*=\s*([\S.]*)", re.I)
user = userSearch.search(line)
password = passSearch.search(line)
if password: # search was successfull
pwOk = True
elif user:
if user.group(1).lower() == "root": # user must be root
userOk = True
if userOk and pwOk:
return True
return False
def generateMyCnf(mysqlPassword):
""" updates/generates a .my.cnf with user root an the given pw (client section) """
mycnf = ""
createBackupCopy = False # create a backup copy when my.cnf eyists
try:
file = open(posix.environ["HOME"]+"/.my.cnf", "r")
mycnf = file.read()
file.close()
createBackupCopy = True # create backup when file exist
except:
pass
clientEntriesReg = re.compile("\[client\]([\w\s\d=#]*)")
clientEntries = clientEntriesReg.search(mycnf)
if clientEntries:
newMycnf = mycnf[:clientEntries.start()]
newMycnf += "[client]\n"
pwReplaced = False
userReplaced = False
lines = clientEntries.group(1).split("\n")
for line in lines:
lowerLine = line.lower()
commentPos = lowerLine.find("#")
if commentPos >= 0:
lowerLine = lowerLine[:commentPos]
if lowerLine.find("user") >= 0: # replace user with root user
newMycnf += "user = root\n"
userReplaced = True
elif lowerLine.find("password") >= 0: #replace password with root password
newMycnf += "password = "+ mysqlPassword +"\n"
pwReplaced = True
else:
if len(line) > 0:
newMycnf += line + "\n"
if not userReplaced: # if entries for user and password do not exist create them
newMycnf += "user = root\n"
if not pwReplaced:
newMycnf += "password = "+ mysqlPassword +"\n"
newMycnf += "\n"
newMycnf += mycnf[clientEntries.end():]
mycnf = newMycnf
else: # mycnf is completly missing generate new one
mycnf = "[client]\n"
mycnf += "user = root\n"
mycnf += "password = "+ mysqlPassword +"\n"
mycnf += "\n"
if createBackupCopy:
try:
shutil.copy(posix.environ["HOME"]+"/.my.cnf", posix.environ["HOME"]+"/.my.cnf.backup")
print "copied old .my.cnf to .my.cnf.backup"
except:
print "ERROR: could not create backup copy of " + posix.environ["HOME"]+"/.my.cnf"
sys.exit(1)
try:
file = open(posix.environ["HOME"]+"/.my.cnf", "w")
file.write(mycnf)
file.close()
os.chmod(posix.environ["HOME"]+"/.my.cnf", 0600) # chmod mycnf
print "generated/updated ~/.my.cnf"
except:
print "ERROR: could not create/write the .my.cnf"
sys.exit(1)
def changeConfigValue(config, option, value):
""" changes the given option in config file to the new given value """
option_s = re.compile(option)
if option[-1] != "=": # add '=' when missing
option += "="
lines = config.split("\n")
config = ""
for line in lines:
if option_s.search(line):
line = option + value
config += line + "\n"
return config
def addNewConfigOption(config, before_option, comment, new_option, value):
""" adds a new option to the config file after the given option """
option_s = re.compile(before_option)
if new_option[-1] != "=": # add '=' when missing
new_option += "="
lines = config.split("\n")
config = ""
for line in lines:
config += line + "\n"
if option_s.search(line):
if comment != "":
config += comment + "\n"
config += new_option + value + "\n"
return config
def getConfigValue(config, option):
""" gets the config value for the given option """
if option[-1] != "=": # add '=' when missing
option += "="
option_s = re.compile(option+ "\s*(\S*)")
lines = config.split("\n")
config = ""
for line in lines:
found = option_s.search(line)
if found:
return found.group(1)
def writeConfig(config):
""" writes the configuration to the config file"""
file = open(ZM_CONFIG, "w")
file.write(config)
file.close()
def enter_mysql_root_pw():
"""dialog for the mysql root password"""
root_passwd = getpass.getpass("enter mysql root password: ")
print # newline
return root_passwd
def run_stuff(db_host = "localhost"):
file = open(ZM_CONFIG, "r")
config = file.read()
file.close()
version = version_string.search(config)
mysql_root_password = ""
myCnfStatus = checkMyCnf()
if version == None:
print "ERROR: could not find out version from config file"
sys.exit(1)
if VERSION == version.group(1):
print "new install create db"
proceed = raw_input("run mysql command to create db as user root? [Y/n]: ")
if proceed.lower() == "y" or proceed == "" or proceed.lower() == "yes":
if not myCnfStatus:
mysql_root_password = enter_mysql_root_pw()
generateMyCnf(mysql_root_password)
myCnfStatus = checkMyCnf()
done = os.system("mysql mysql < "+ZM_PATH+"/db/zm_create.sql -h " + db_host)
if done != 0:
print "SQL ERROR: exiting"
sys.exit(1)
proceed = raw_input("create user zm_admin for zoneminder? [Y/n]: ")
if proceed.lower() == "y" or proceed == "" or proceed.lower() == "yes":
passwd = getpass.getpass("Enter new passwd for user zm_admin: ")
if passwd != "":
print # newline
t_pass = getpass.getpass("retype password: ")
if (t_pass != passwd):
print "ERROR: password do not match. exiting"
sys.exit(1)
print # add one newline
if not myCnfStatus:
mysql_root_password = enter_mysql_root_pw()
generateMyCnf(mysql_root_password)
done = os.system("""echo "GRANT USAGE ON * . * TO 'zm_admin'@'"""+ db_host +"""' IDENTIFIED BY '"""+ passwd + """' WITH MAX_QUERIES_PER_HOUR 0 MAX_CONNECTIONS_PER_HOUR 0 MAX_UPDATES_PER_HOUR 0 ; GRANT SELECT , INSERT , UPDATE , DELETE ON zm . * TO 'zm_admin'@'"""+ db_host +"""';" | mysql mysql -h """ + db_host)
if done != 0:
print "error on mysql. exiting"
sys.exit(1)
else:
print "ERROR: empty passphrase. exiting"
sys.exit(1)
config_update = raw_input("should the config file updated with the new passwd? [Y/n]: ")
if config_update == "Y" or config_update == "y" or config_update == "":
config = changeConfigValue(config, "ZM_DB_PASS=", passwd)
if db_host != "localhost":
config_update = raw_input("should the config file updated with new db host? [Y/n]: ")
if config_update == "Y" or config_update == "y" or config_update == "":
config = changeConfigValue(config, "ZM_DB_HOST=", db_host)
writeConfig(config)
else:
print "when update fails or you are not upgrading from"
print "previous rpm version please ensure that the ZM_PATH_BUILD is set to"
print ZM_PATH + " to find the database update skripts"
print
print "when done upgrade using zmupdate.pl before then answer this with n"
proceed = raw_input("Do you want to perform db update? [Y/n]: ")
if proceed.lower() == "y" or proceed == "" or proceed.lower() == "yes":
config = checkZmPath(config)
print "updating config file version string"
updateVersionString(config, version.group(1), VERSION)
success = True # for tracking all commandos
if not myCnfStatus:
mysql_root_password = enter_mysql_root_pw()
generateMyCnf(mysql_root_password)
zmDbUser = getConfigValue(config, "ZM_DB_USER")
zmDb = getConfigValue(config, "ZM_DB_NAME")
db_host = getConfigValue(config, "ZM_DB_HOST")
print "Giving all priviliges to "+ zmDbUser +" on database "+ zmDb
done = os.system("""echo "GRANT ALL PRIVILEGES ON """+ zmDb +""". * TO '"""+ zmDbUser +"""'@'"""+ db_host +"""';" | mysql mysql -h """ + db_host)
if done != 0:
success = False
print
done = os.system("zmupdate.pl -version="+version.group(1))
if done != 0:
success = False
print "Revoking 'all priviliges' from "+ zmDbUser +" and giving back normal rights"
done = os.system("""echo "REVOKE ALL PRIVILEGES ON """+ zmDb +""" . * FROM '"""+ zmDbUser +"""'@'"""+ db_host +"""'; GRANT SELECT , INSERT , UPDATE , DELETE ON """+ zmDb +""" . * TO '"""+ zmDbUser +"""'@'"""+ db_host +"""';" | mysql mysql -h """ + db_host)
if done != 0:
success = False
if not success:
print "ERROR: upgrade fails"
# undo config update
print "undo config file update version string"
updateVersionString(config, VERSION, version.group(1))
sys.exit(1)
else:
print "updating config file version string"
updateVersionString(config, version.group(1), VERSION)
print "check documentation for new config file settings"
if os.path.isfile(ZM_PATH + "/lock"):
print "removing lock file"
os.remove(ZM_PATH + "/lock")
print "done"
def printUsage():
""" prints out the usage """
print "Usage: zm_database_init [OPTION]...\n"
print "-h, --host=name\tConnect to mysql host (default localhost)"
print " --help\tprints out this help"
def main():
if posix.getuid() != 0:
print "ERROR: you must run zm_database_init as root"
print "exiting ..."
sys.exit(1)
try:
database_host = "localhost" # set localhost as default value
try:
opts, args = getopt.getopt(sys.argv[1:], "h:", ["host=", "help"])
except getopt.GetoptError:
# print help information and exit:
#printUsage()
print "usage"
sys.exit(2)
for o, a in opts:
if o == "--help":
printUsage()
sys.exit()
if o in ("-h", "--host"):
database_host = a
print "INFO: when db is correctly installed and you just reinstalled rpm, then answer all questions with 'n'"
# check for lockfile
if os.path.isfile(ZM_PATH + "/lock"):
run_stuff(database_host)
else:
proceed = raw_input("no lockfile found, proceed anyway? [y/N]: ")
if proceed.lower() == "y" or proceed.lower() == "yes":
run_stuff(database_host)
except KeyboardInterrupt:
print "Interrupted exiting"
sys.exit(0)
if __name__ == "__main__":
main()