db.sh 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533
  1. #!/bin/bash
  2. #===========================================================================#
  3. # #
  4. # Hestia Control Panel - Domain Function Library #
  5. # #
  6. #===========================================================================#
  7. # Global
  8. database_set_default_ports() {
  9. # Set default ports for MySQL and PostgreSQL
  10. mysql_default="3306"
  11. pgsql_default="5432"
  12. # Handle missing values for both $PORT and $port
  13. # however don't override both at once or custom ports will be overridden.
  14. if [ -z "$PORT" ]; then
  15. if [ "$type" = 'mysql' ]; then
  16. PORT="$mysql_default"
  17. fi
  18. if [ "$type" = 'pgsql' ]; then
  19. PORT="$pgsql_default"
  20. fi
  21. fi
  22. if [ -z "$port" ]; then
  23. if [ "$type" = 'mysql' ]; then
  24. port="$mysql_default"
  25. fi
  26. if [ "$type" = 'pgsql' ]; then
  27. port="$pgsql_default"
  28. fi
  29. fi
  30. }
  31. # MySQL
  32. mysql_connect() {
  33. unset PORT
  34. host_str=$(grep "HOST='$1'" $HESTIA/conf/mysql.conf)
  35. parse_object_kv_list "$host_str"
  36. if [ -z $PORT ]; then PORT=3306; fi
  37. if [ -z $HOST ] || [ -z $USER ] || [ -z $PASSWORD ]; then
  38. echo "Error: mysql config parsing failed"
  39. log_event "$E_PARSING" "$ARGUMENTS"
  40. exit $E_PARSING
  41. fi
  42. mycnf="$HESTIA/conf/.mysql.$HOST"
  43. if [ ! -e "$mycnf" ]; then
  44. echo "[client]">$mycnf
  45. echo "host='$HOST'" >> $mycnf
  46. echo "user='$USER'" >> $mycnf
  47. echo "password='$PASSWORD'" >> $mycnf
  48. echo "port='$PORT'" >> $mycnf
  49. chmod 600 $mycnf
  50. else
  51. mypw=$(grep password $mycnf|cut -f 2 -d \')
  52. if [ "$mypw" != "$PASSWORD" ]; then
  53. echo "[client]">$mycnf
  54. echo "host='$HOST'" >> $mycnf
  55. echo "user='$USER'" >> $mycnf
  56. echo "password='$PASSWORD'" >> $mycnf
  57. echo "port='$PORT'" >> $mycnf
  58. chmod 660 $mycnf
  59. fi
  60. fi
  61. mysql_out=$(mktemp)
  62. mysql --defaults-file=$mycnf -e 'SELECT VERSION()' > $mysql_out 2>&1
  63. if [ '0' -ne "$?" ]; then
  64. if [ "$notify" != 'no' ]; then
  65. email=$(grep CONTACT $HESTIA/data/users/admin/user.conf |cut -f 2 -d \')
  66. subj="MySQL connection error on $(hostname)"
  67. echo -e "Can't connect to MySQL $HOST\n$(cat $mysql_out)" |\
  68. $SENDMAIL -s "$subj" $email
  69. fi
  70. rm -f $mysql_out
  71. echo "Error: Connection to $HOST failed"
  72. log_event "$E_CONNECT" "$ARGUMENTS"
  73. exit $E_CONNECT
  74. fi
  75. mysql_ver=$(cat $mysql_out |tail -n1 |cut -f 1 -d -)
  76. mysql_fork="mysql"
  77. check_mysql_fork=$(grep "MariaDB" $mysql_out)
  78. if [ "$check_mysql_fork" ]; then
  79. mysql_fork="mariadb"
  80. fi
  81. rm -f $mysql_out
  82. }
  83. mysql_query() {
  84. sql_tmp=$(mktemp)
  85. echo "$1" > $sql_tmp
  86. mysql --defaults-file=$mycnf < "$sql_tmp" 2>/dev/null
  87. rm -f "$sql_tmp"
  88. }
  89. mysql_dump() {
  90. err="/tmp/e.mysql"
  91. mysqldump --defaults-file=$mycnf --single-transaction --routines -r $1 $2 2> $err
  92. if [ '0' -ne "$?" ]; then
  93. rm -rf $tmpdir
  94. if [ "$notify" != 'no' ]; then
  95. email=$(grep CONTACT $HESTIA/data/users/admin/user.conf |cut -f 2 -d \')
  96. subj="MySQL error on $(hostname)"
  97. echo -e "Can't dump database $database\n$(cat $err)" |\
  98. $SENDMAIL -s "$subj" $email
  99. fi
  100. echo "Error: dump $database failed"
  101. log_event "$E_DB" "$ARGUMENTS"
  102. exit "$E_DB"
  103. fi
  104. }
  105. # PostgreSQL
  106. psql_connect() {
  107. unset PORT
  108. host_str=$(grep "HOST='$1'" $HESTIA/conf/pgsql.conf)
  109. parse_object_kv_list "$host_str"
  110. export PGPASSWORD="$PASSWORD"
  111. if [ -z $PORT ]; then PORT=5432; fi
  112. if [ -z $HOST ] || [ -z $USER ] || [ -z $PASSWORD ] || [ -z $TPL ]; then
  113. echo "Error: postgresql config parsing failed"
  114. log_event "$E_PARSING" "$ARGUMENTS"
  115. exit $E_PARSING
  116. fi
  117. psql -h $HOST -U $USER -p $PORT -c "SELECT VERSION()" > /dev/null 2>/tmp/e.psql
  118. if [ '0' -ne "$?" ]; then
  119. if [ "$notify" != 'no' ]; then
  120. email=$(grep CONTACT $HESTIA/data/users/admin/user.conf |cut -f 2 -d \')
  121. subj="PostgreSQL connection error on $(hostname)"
  122. echo -e "Can't connect to PostgreSQL $HOST\n$(cat /tmp/e.psql)" |\
  123. $SENDMAIL -s "$subj" $email
  124. fi
  125. echo "Error: Connection to $HOST failed"
  126. log_event "$E_CONNECT" "$ARGUMENTS"
  127. exit "$E_CONNECT"
  128. fi
  129. }
  130. psql_query() {
  131. sql_tmp=$(mktemp)
  132. echo "$1" > $sql_tmp
  133. psql -h $HOST -U $USER -f "$sql_tmp" 2>/dev/null
  134. rm -f $sql_tmp
  135. }
  136. psql_dump() {
  137. pg_dump -h $HOST -U $USER -c --inserts -O -x -f $1 $2 2>/tmp/e.psql
  138. if [ '0' -ne "$?" ]; then
  139. rm -rf $tmpdir
  140. if [ "$notify" != 'no' ]; then
  141. email=$(grep CONTACT $HESTIA/data/users/admin/user.conf |cut -f 2 -d \')
  142. subj="PostgreSQL error on $(hostname)"
  143. echo -e "Can't dump database $database\n$(cat /tmp/e.psql)" |\
  144. $SENDMAIL -s "$subj" $email
  145. fi
  146. echo "Error: dump $database failed"
  147. log_event "$E_DB" "$ARGUMENTS"
  148. exit "$E_DB"
  149. fi
  150. }
  151. # Get database host
  152. get_next_dbhost() {
  153. if [ -z "$host" ] || [ "$host" == 'default' ]; then
  154. IFS=$'\n'
  155. host='EMPTY_DB_HOST'
  156. config="$HESTIA/conf/$type.conf"
  157. host_str=$(grep "SUSPENDED='no'" $config)
  158. check_row=$(echo "$host_str"|wc -l)
  159. if [ 0 -lt "$check_row" ]; then
  160. if [ 1 -eq "$check_row" ]; then
  161. for db in $host_str; do
  162. parse_object_kv_list "$db"
  163. if [ "$MAX_DB" -gt "$U_DB_BASES" ]; then
  164. host=$HOST
  165. fi
  166. done
  167. else
  168. old_weight='100'
  169. for db in $host_str; do
  170. parse_object_kv_list "$db"
  171. let weight="$U_DB_BASES * 100 / $MAX_DB" >/dev/null 2>&1
  172. if [ "$old_weight" -gt "$weight" ]; then
  173. host="$HOST"
  174. old_weight="$weight"
  175. fi
  176. done
  177. fi
  178. fi
  179. fi
  180. }
  181. # Database charset validation
  182. is_charset_valid() {
  183. host_str=$(grep "HOST='$host'" $HESTIA/conf/$type.conf)
  184. parse_object_kv_list "$host_str"
  185. if [ -z "$(echo $CHARSETS | grep -wi $charset )" ]; then
  186. echo "Error: charset $charset not exist"
  187. log_event "$E_NOTEXIST" "$ARGUMENTS"
  188. exit $E_NOTEXIST
  189. fi
  190. }
  191. # Increase database host value
  192. increase_dbhost_values() {
  193. host_str=$(grep "HOST='$host'" $HESTIA/conf/$type.conf)
  194. parse_object_kv_list "$host_str"
  195. old_dbbases="U_DB_BASES='$U_DB_BASES'"
  196. new_dbbases="U_DB_BASES='$((U_DB_BASES + 1))'"
  197. if [ -z "$U_SYS_USERS" ]; then
  198. old_users="U_SYS_USERS=''"
  199. new_users="U_SYS_USERS='$user'"
  200. else
  201. old_users="U_SYS_USERS='$U_SYS_USERS'"
  202. new_users="U_SYS_USERS='$U_SYS_USERS'"
  203. if [ -z "$(echo $U_SYS_USERS|sed "s/,/\n/g"|grep -w $user)" ]; then
  204. old_users="U_SYS_USERS='$U_SYS_USERS'"
  205. new_users="U_SYS_USERS='$U_SYS_USERS,$user'"
  206. fi
  207. fi
  208. sed -i "s/$old_dbbases/$new_dbbases/g" $HESTIA/conf/$type.conf
  209. sed -i "s/$old_users/$new_users/g" $HESTIA/conf/$type.conf
  210. }
  211. # Decrease database host value
  212. decrease_dbhost_values() {
  213. host_str=$(grep "HOST='$HOST'" $HESTIA/conf/$TYPE.conf)
  214. parse_object_kv_list "$host_str"
  215. old_dbbases="U_DB_BASES='$U_DB_BASES'"
  216. new_dbbases="U_DB_BASES='$((U_DB_BASES - 1))'"
  217. old_users="U_SYS_USERS='$U_SYS_USERS'"
  218. U_SYS_USERS=$(echo "$U_SYS_USERS" |\
  219. sed "s/,/\n/g"|\
  220. sed "s/^$user$//g"|\
  221. sed "/^$/d"|\
  222. sed ':a;N;$!ba;s/\n/,/g')
  223. new_users="U_SYS_USERS='$U_SYS_USERS'"
  224. sed -i "s/$old_dbbases/$new_dbbases/g" $HESTIA/conf/$TYPE.conf
  225. sed -i "s/$old_users/$new_users/g" $HESTIA/conf/$TYPE.conf
  226. }
  227. # Create MySQL database
  228. add_mysql_database() {
  229. mysql_connect $host
  230. query="CREATE DATABASE \`$database\` CHARACTER SET $charset"
  231. mysql_query "$query" > /dev/null
  232. query="GRANT ALL ON \`$database\`.* TO \`$dbuser\`@\`%\`
  233. IDENTIFIED BY '$dbpass'"
  234. mysql_query "$query" > /dev/null
  235. query="GRANT ALL ON \`$database\`.* TO \`$dbuser\`@localhost
  236. IDENTIFIED BY '$dbpass'"
  237. mysql_query "$query" > /dev/null
  238. if [ "$(echo $mysql_ver |cut -d '.' -f2)" -ge 7 ]; then
  239. md5=$(mysql_query "SHOW CREATE USER \`$dbuser\`" 2>/dev/null)
  240. md5=$(echo "$md5" |grep password |cut -f8 -d \')
  241. else
  242. md5=$(mysql_query "SHOW GRANTS FOR \`$dbuser\`" 2>/dev/null)
  243. md5=$(echo "$md5" |grep PASSW|tr ' ' '\n' |tail -n1 |cut -f 2 -d \')
  244. fi
  245. }
  246. # Create PostgreSQL database
  247. add_pgsql_database() {
  248. psql_connect $host
  249. query="CREATE ROLE $dbuser WITH LOGIN PASSWORD '$dbpass'"
  250. psql_query "$query" > /dev/null
  251. query="CREATE DATABASE $database OWNER $dbuser"
  252. if [ "$TPL" = 'template0' ]; then
  253. query="$query ENCODING '$charset' TEMPLATE $TPL"
  254. else
  255. query="$query TEMPLATE $TPL"
  256. fi
  257. psql_query "$query" > /dev/null
  258. query="GRANT ALL PRIVILEGES ON DATABASE $database TO $dbuser"
  259. psql_query "$query" > /dev/null
  260. query="GRANT CONNECT ON DATABASE template1 to $dbuser"
  261. psql_query "$query" > /dev/null
  262. query="SELECT rolpassword FROM pg_authid WHERE rolname='$dbuser'"
  263. md5=$(psql_query "$query" | grep md5 | cut -f 2 -d \ )
  264. }
  265. add_mysql_database_temp_user() {
  266. mysql_connect $host;
  267. query="GRANT ALL ON \`$database\`.* TO \`$dbuser\`@localhost
  268. IDENTIFIED BY '$dbpass'"
  269. mysql_query "$query" > /dev/null
  270. }
  271. delete_mysql_database_temp_user(){
  272. echo $database;
  273. echo $dbuser;
  274. echo $host;
  275. mysql_connect $host;
  276. query="REVOKE ALL ON \`$database\`.* FROM \`$dbuser\`@localhost"
  277. mysql_query "$query" > /dev/null
  278. query="DROP USER '$dbuser'@'localhost'"
  279. mysql_query "$query" > /dev/null
  280. }
  281. # Check if database host do not exist in config
  282. is_dbhost_new() {
  283. if [ -e "$HESTIA/conf/$type.conf" ]; then
  284. check_host=$(grep "HOST='$host'" $HESTIA/conf/$type.conf)
  285. if [ "$check_host" ]; then
  286. echo "Error: db host exist"
  287. log_event "$E_EXISTS" "$ARGUMENTS"
  288. exit $E_EXISTS
  289. fi
  290. fi
  291. }
  292. # Get database values
  293. get_database_values() {
  294. parse_object_kv_list $(grep "DB='$database'" $USER_DATA/db.conf)
  295. }
  296. # Change MySQL database password
  297. change_mysql_password() {
  298. mysql_connect $HOST
  299. query="GRANT ALL ON \`$database\`.* TO \`$DBUSER\`@\`%\`
  300. IDENTIFIED BY '$dbpass'"
  301. mysql_query "$query" > /dev/null
  302. query="GRANT ALL ON \`$database\`.* TO \`$DBUSER\`@localhost
  303. IDENTIFIED BY '$dbpass'"
  304. mysql_query "$query" > /dev/null
  305. if [ "$(echo $mysql_ver |cut -d '.' -f2)" -ge 7 ]; then
  306. md5=$(mysql_query "SHOW CREATE USER \`$DBUSER\`" 2>/dev/null)
  307. md5=$(echo "$md5" |grep password |cut -f8 -d \')
  308. else
  309. md5=$(mysql_query "SHOW GRANTS FOR \`$DBUSER\`" 2>/dev/null)
  310. md5=$(echo "$md5" |grep PASSW|tr ' ' '\n' |tail -n1 |cut -f 2 -d \')
  311. fi
  312. }
  313. # Change PostgreSQL database password
  314. change_pgsql_password() {
  315. psql_connect $HOST
  316. query="ALTER ROLE $DBUSER WITH LOGIN PASSWORD '$dbpass'"
  317. psql_query "$query" > /dev/null
  318. query="SELECT rolpassword FROM pg_authid WHERE rolname='$DBUSER'"
  319. md5=$(psql_query "$query" | grep md5 |cut -f 2 -d \ )
  320. }
  321. # Delete MySQL database
  322. delete_mysql_database() {
  323. mysql_connect $HOST
  324. query="DROP DATABASE \`$database\`"
  325. mysql_query "$query" > /dev/null
  326. query="REVOKE ALL ON \`$database\`.* FROM \`$DBUSER\`@\`%\`"
  327. mysql_query "$query" > /dev/null
  328. query="REVOKE ALL ON \`$database\`.* FROM \`$DBUSER\`@localhost"
  329. mysql_query "$query" > /dev/null
  330. if [ "$(grep "DBUSER='$DBUSER'" $USER_DATA/db.conf |wc -l)" -lt 2 ]; then
  331. query="DROP USER '$DBUSER'@'%'"
  332. mysql_query "$query" > /dev/null
  333. query="DROP USER '$DBUSER'@'localhost'"
  334. mysql_query "$query" > /dev/null
  335. fi
  336. }
  337. # Delete PostgreSQL database
  338. delete_pgsql_database() {
  339. psql_connect $HOST
  340. query="REVOKE ALL PRIVILEGES ON DATABASE $database FROM $DBUSER"
  341. psql_query "$query" > /dev/null
  342. query="DROP DATABASE $database"
  343. psql_query "$query" > /dev/null
  344. if [ "$(grep "DBUSER='$DBUSER'" $USER_DATA/db.conf |wc -l)" -lt 2 ]; then
  345. query="REVOKE CONNECT ON DATABASE template1 FROM $DBUSER"
  346. psql_query "$query" > /dev/null
  347. query="DROP ROLE $DBUSER"
  348. psql_query "$query" > /dev/null
  349. fi
  350. }
  351. # Dump MySQL database
  352. dump_mysql_database() {
  353. mysql_connect $HOST
  354. mysql_dump $dump $database
  355. query="SHOW GRANTS FOR '$DBUSER'@'localhost'"
  356. mysql_query "$query" | grep -v "Grants for" > $grants
  357. query="SHOW GRANTS FOR '$DBUSER'@'%'"
  358. mysql_query "$query" | grep -v "Grants for" > $grants
  359. }
  360. # Dump PostgreSQL database
  361. dump_pgsql_database() {
  362. psql_connect $HOST
  363. psql_dump $dump $database
  364. query="SELECT rolpassword FROM pg_authid WHERE rolname='$DBUSER'"
  365. md5=$(psql_query "$query" | head -n1 | cut -f 2 -d \ )
  366. pw_str="UPDATE pg_authid SET rolpassword='$md5' WHERE rolname='$DBUSER'"
  367. gr_str="GRANT ALL PRIVILEGES ON DATABASE $database to '$DBUSER'"
  368. echo -e "$pw_str\n$gr_str" >> $grants
  369. }
  370. # Check if database server is in use
  371. is_dbhost_free() {
  372. host_str=$(grep "HOST='$host'" $HESTIA/conf/$type.conf)
  373. parse_object_kv_list "$host_str"
  374. if [ 0 -ne "$U_DB_BASES" ]; then
  375. echo "Error: host $HOST is used"
  376. log_event "$E_INUSE" "$ARGUMENTS"
  377. exit $E_INUSE
  378. fi
  379. }
  380. # Suspend MySQL database
  381. suspend_mysql_database() {
  382. mysql_connect $HOST
  383. query="REVOKE ALL ON \`$database\`.* FROM \`$DBUSER\`@\`%\`"
  384. mysql_query "$query" > /dev/null
  385. query="REVOKE ALL ON \`$database\`.* FROM \`$DBUSER\`@localhost"
  386. mysql_query "$query" > /dev/null
  387. }
  388. # Suspend PostgreSQL database
  389. suspend_pgsql_database() {
  390. psql_connect $HOST
  391. query="REVOKE ALL PRIVILEGES ON $database FROM $DBUSER"
  392. psql_query "$query" > /dev/null
  393. }
  394. # Unsuspend MySQL database
  395. unsuspend_mysql_database() {
  396. mysql_connect $HOST
  397. query="GRANT ALL ON \`$database\`.* FROM \`$DBUSER\`@\`%\`"
  398. mysql_query "$query" > /dev/null
  399. query="GRANT ALL ON \`$database\`.* TO \`$DBUSER\`@localhost"
  400. mysql_query "$query" > /dev/null
  401. }
  402. # Unsuspend PostgreSQL database
  403. unsuspend_pgsql_database() {
  404. psql_connect $HOST
  405. query="GRANT ALL PRIVILEGES ON DATABASE $database TO $DBUSER"
  406. psql_query "$query" > /dev/null
  407. }
  408. # Get MySQL disk usage
  409. get_mysql_disk_usage() {
  410. mysql_connect $HOST
  411. query="SELECT SUM( data_length + index_length ) / 1024 / 1024 'Size'
  412. FROM information_schema.TABLES WHERE table_schema='$database'"
  413. usage=$(mysql_query "$query" |tail -n1)
  414. if [ "$usage" == '' ] || [ "$usage" == 'NULL' ] || [ "${usage:0:1}" -eq '0' ]; then
  415. usage=1
  416. fi
  417. export LC_ALL=C
  418. usage=$(printf "%0.f\n" $usage)
  419. }
  420. # Get PostgreSQL disk usage
  421. get_pgsql_disk_usage() {
  422. psql_connect $HOST
  423. query="SELECT pg_database_size('$database');"
  424. usage=$(psql_query "$query")
  425. usage=$(echo "$usage" | grep -v "-" | grep -v 'row' | sed "/^$/d")
  426. usage=$(echo "$usage" | grep -v "pg_database_size" | awk '{print $1}')
  427. if [ -z "$usage" ]; then
  428. usage=0
  429. fi
  430. usage=$(($usage / 1048576))
  431. if [ "$usage" -eq '0' ]; then
  432. usage=1
  433. fi
  434. }
  435. # Delete MySQL user
  436. delete_mysql_user() {
  437. mysql_connect $HOST
  438. query="REVOKE ALL ON \`$database\`.* FROM \`$old_dbuser\`@\`%\`"
  439. mysql_query "$query" > /dev/null
  440. query="REVOKE ALL ON \`$database\`.* FROM \`$old_dbuser\`@localhost"
  441. mysql_query "$query" > /dev/null
  442. query="DROP USER '$old_dbuser'@'%'"
  443. mysql_query "$query" > /dev/null
  444. query="DROP USER '$old_dbuser'@'localhost'"
  445. mysql_query "$query" > /dev/null
  446. }
  447. # Delete PostgreSQL user
  448. delete_pgsql_user() {
  449. psql_connect $HOST
  450. query="REVOKE ALL PRIVILEGES ON DATABASE $database FROM $old_dbuser"
  451. psql_query "$query" > /dev/null
  452. query="REVOKE CONNECT ON DATABASE template1 FROM $old_dbuser"
  453. psql_query "$query" > /dev/null
  454. query="DROP ROLE $old_dbuser"
  455. psql_query "$query" > /dev/null
  456. }