db.sh 16 KB

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