Advertisement
Guest User

Untitled

a guest
Mar 8th, 2019
278
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 17.83 KB | None | 0 0
  1. #!/bin/bash
  2.  
  3. webmaster="$(id -un)" # user who access web files. group is www-data
  4. webgroup="www-data" # apache2 web group, does't need to be webmaster group. SGID set for folder.
  5. maindomain=".localhost" # domain for creating subdomains, leading dot required
  6. virtualhost="*" # ip of virtualhost in case server listen on many interfaces or "*" for all
  7. virtualport="80" # port of virtualhost. apache2 must listen on that ip:port
  8. serveradmin="webmaster@localhost" # admin email
  9. # declare -a webroots=("/home/${webmaster}/Web/${host}"
  10. # "/var/www/${host}"
  11. # "/var/www/${webmaster}/${host}")
  12. # declared below, after a chanse to edit inlined variadles
  13. webroot=0 # folder index in webroots array
  14. a2ensite="050-" # short prefix for virtualhost config file
  15. apachehost="/etc/apache2/sites-available/${a2ensite}" # prefix for virtualhost config file
  16. tmphost=$(mktemp) # temp file for edit config
  17. trap 'rm "$tmphost"' EXIT # rm temp file on exit
  18. host="" # no default subdomain
  19. skipmysql=""
  20. mysqladmin="root"
  21. mysqladminpwd=""
  22.  
  23.  
  24. have_command() {
  25. type -p "$1" >/dev/null
  26. }
  27.  
  28. in_terminal() {
  29. [ -t 0 ]
  30. }
  31. in_terminal && start_in_terminal=1
  32.  
  33. info_user() {
  34. local msg=$1
  35. local windowicon="info" # 'error', 'info', 'question' or 'warning' or path to icon
  36. [ "$2" ] && windowicon="$2"
  37. if in_terminal; then
  38. echo "$msg"
  39. else
  40. zenity --info --text="$msg" --icon-name="${windowicon}"
  41. --no-wrap --title="Virtualhost" 2>/dev/null
  42. fi
  43. }
  44.  
  45. notify_user () {
  46. echo "$1" >&2
  47. in_terminal && return
  48. local windowicon="info" # 'error', 'info', 'question' or 'warning' or path to icon
  49. local msgprefix=""
  50. local prefix="Virtualhost: "
  51. [ "$2" ] && windowicon="$2" && msgprefix="$2: "
  52. if have_command zenity; then
  53. zenity --notification --text="${prefix}$1" --window-icon="$windowicon"
  54. return
  55. fi
  56. if have_command notify-send; then
  57. notify-send "${prefix}${msgprefix}$1"
  58. else
  59. xmessage -buttons Ok:0 -nearmouse "${prefix}${msgprefix}$1" -timeout 10
  60. fi
  61. }
  62.  
  63. # sudo apt hangs when run from script, thats why terminal needed
  64. install_zenity() {
  65. have_command gnome-terminal && gnome-terminal --title="$1" --wait -- $1
  66. have_command zenity && exit 0
  67. exit 1
  68. }
  69. # check if zenity must be installed or text terminal can be used
  70. # if fails = script exit
  71. # to install zenity, run: ./script.sh --gui in X terminal
  72. # ask user to confirm install if able to ask
  73. check_gui() {
  74. local msg="Use terminal or install zenity for gui. '$ sudo apt install zenity'"
  75. local cmd="sudo apt install zenity"
  76. local notfirst=$1
  77. if ! have_command zenity;then
  78. notify_user "$msg" "error"
  79. # --gui set and input/output from terminal possible
  80. if [[ "${gui_set}" && "${start_in_terminal}" ]]; then
  81. read -p "Install zenity for you? sudo required.[y/N]" -r autozenity
  82. reg="^[yY]$"
  83. if [[ "$autozenity" =~ $reg ]]; then
  84. $(install_zenity "$cmd") || exit
  85. exit 0
  86. else
  87. exit 1
  88. fi
  89. else
  90. if [[ "${gui_set}" ]]; then
  91. $(install_zenity "$cmd") || exit
  92. exit 0
  93. else
  94. if ! in_terminal;then
  95. $(install_zenity "$cmd") || exit
  96. exit 0
  97. fi
  98. fi
  99. fi
  100.  
  101. fi
  102. exit 0
  103. }
  104. $(check_gui) || exit
  105.  
  106.  
  107. validate_input() {
  108. local msg="$1"
  109. local var="$2"
  110. local reg=$3
  111. if ! [[ "$var" =~ $reg ]]; then
  112. notify_user "$msg" "error"
  113. exit 1
  114. fi
  115. exit 0
  116. }
  117.  
  118. validate_domain() {
  119. $(LANG=C; validate_input "Bad domain with leading . (dot)" "$1"
  120. "^.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9.])*$") || exit 1
  121. exit 0
  122. }
  123.  
  124. validate_username() {
  125. $(LANG=C; validate_input "Bad username." "$1"
  126. "^[[:lower:]_][[:lower:][:digit:]_-]{2,15}$") || exit 1
  127. if ! id "$1" >/dev/null 2>&1; then
  128. notify_user "User not exists" "error"
  129. exit 1
  130. fi
  131. exit 0
  132. }
  133.  
  134. validate_group() {
  135. getent group "$1" > /dev/null 2&>1
  136. [ $? -ne 0 ] && notify_user "Group $1 not exists" "error" && exit 1
  137. exit 0
  138. }
  139.  
  140. validate_email() {
  141. $(LANG=C; validate_input "Bad admin email." "$1"
  142. "^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@([a-z0-9]([a-z0-9-]*[a-z0-9])?.)*[a-z0-9]([a-z0-9-]*[a-z0-9])?$") || exit 1
  143. exit 0
  144. }
  145.  
  146. validate_subdomain() {
  147. $(LANG=C; validate_input "Bad subdomain" "$1"
  148. "^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$") || exit 1
  149. exit 0
  150. }
  151.  
  152. getopt --test > /dev/null
  153. if [ "$?" -gt 4 ];then
  154. echo 'I’m sorry, `getopt --test` failed in this environment.'
  155. else
  156. OPTIONS="w:d:a:ghi"
  157. LONGOPTS="webmaster:,domain:,adminemail:,gui,help,webroot:,webgroup:,install,subdomain:,skipmysql,mysqlauto"
  158. ! PARSED=$(getopt --options=$OPTIONS --longoptions=$LONGOPTS --name "$0" -- "$@")
  159. if [[ ${PIPESTATUS[0]} -ne 0 ]]; then
  160. # e.g. return value is 1
  161. # then getopt has complained about wrong arguments to stdout
  162. exit 2
  163. fi
  164. # read getopt’s output this way to handle the quoting right:
  165. eval set -- "$PARSED"
  166. while true; do
  167. case "$1" in
  168. "--skipmysql")
  169. skipmysql=1
  170. shift
  171. ;;
  172. "--mysqlauto")
  173. mysqlauto=1
  174. shift
  175. ;;
  176. "-w"|"--webmaster")
  177. webmaster="$2"
  178. webmaster_set=1
  179. shift 2
  180. ;;
  181. "--webgroup")
  182. webgroup="$2"
  183. webgroup_set=1
  184. shift 2
  185. ;;
  186. "--subdomain")
  187. host="$2"
  188. host_set=1
  189. shift 2
  190. ;;
  191. "-d"|"--domain")
  192. maindomain="$2"
  193. maindomain_set=1
  194. shift 2
  195. ;;
  196. "-a"|"--adminemail")
  197. serveradmin="$2"
  198. serveradmin_set=1
  199. shift 2
  200. ;;
  201. "--webroot")
  202. declare -i webroot="$2"
  203. webroot_set=1
  204. shift 2
  205. ;;
  206. "-g"|"--gui")
  207. if [ -z "$DISPLAY" ];then
  208. notify_user "GUI failed. No DISPLAY." "error"
  209. exit 1
  210. else
  211. gui_set=1
  212. # GUI can be enabled at this point, when run from terminal /
  213. # with --gui, so check again
  214. $(check_gui) || exit
  215. in_terminal() {
  216. false
  217. }
  218. fi
  219. shift
  220. ;;
  221. "-i"|"--install")
  222. install_set=1
  223. shift
  224. ;;
  225. "-h"|"--help")
  226. cat << EOF
  227. usage: ./${0} # if some arguments specified in command
  228. # line, script will not ask to input it value
  229. # --install of script makes some values predefined
  230. [--webmaster="userlogin"] # user with permissions to edit www
  231. # files. group is 'www-data'
  232. [--webgroup="www-data"] # group of apache2 service user
  233. [--domain=".localhost"] # leading dot required
  234. [--subdomain="Example"] # Subdomain and webroot folder name
  235. # (folder case censetive)
  236. [--adminemail="admin@localhost"]
  237. [--webroot="0"] # documentroot of virtualhost, zero based index
  238. # webroots[0]="/home/${webmaster}/Web/${subdomain}"
  239. # webroots[1]="/var/www/${subdomain}"
  240. # webroots[2]="/var/www/${webmaster}/${subdomain}"
  241. [--skipmysql] # don't create mysql db and user
  242. [--mysqlauto] # use subdomain as mysql db name, username, empty password
  243. [--gui] # run gui version from terminal else be autodetected without this.
  244. # attemps to install zenity
  245.  
  246. ${0}
  247. --help # this help
  248.  
  249. ${0}
  250. --gui --install # install .desktop shortcut for current user
  251. # and optionally copy script to $home/bin
  252. # --install requires --gui
  253. # shortcut have few options to run, without mysql for example
  254. ${0} "without arguments" # will read values from user
  255. EOF
  256. exit 0
  257. ;;
  258. --)
  259. shift
  260. break
  261. ;;
  262. *)
  263. echo "Error" >&2
  264. exit 3
  265. ;;
  266. esac
  267. done
  268. if [ "$install_set" ]; then
  269. ! [ "$gui_set" ] && notify_user "--gui must be set with --install" && exit 1
  270. homedir=$( getent passwd "$(id -un)" | cut -d: -f6 )
  271.  
  272. source="${BASH_SOURCE[0]}"
  273. while [ -h "$source" ]; do # resolve $SOURCE until the file is no longer a symlink
  274. dir="$( cd -P "$( dirname "$source" )" >/dev/null 2>&1 && pwd )"
  275. source="$(readlink "$source")"
  276. [[ $source != /* ]] && source="$dir/$source"
  277. # ^ if $SOURCE was a relative symlink, we need to resolve it relative to
  278. # the path where the symlink file was located
  279. done
  280. dir="$( cd -P "$( dirname "$source" )" >/dev/null 2>&1 && pwd )"
  281. scriptname=$( basename "$source" )
  282.  
  283. # make a form where script args can be predefined, so not needed every launch
  284. pipestring=$(zenity --forms --add-entry="Domain(leading dot)(Default .localhost)"
  285. --add-entry="Webmaster(default: current username)"
  286. --add-entry="Webgroup(Default: www-data)"
  287. --add-entry="Server admin(Default: webmaster@localhost)"
  288. --text="Predefined values(empty=interactive edit)" --title="Desktop shortcut"
  289. --add-combo="Install path" --combo-values="${homedir}/bin/|${dir}/" 2>/dev/null)
  290.  
  291. # zenity stdout if like value1|value2|etc
  292. IFS='|' read -r -a form <<< "$pipestring"
  293. args=""
  294. if [ "${form[0]}" ]; then $(validate_domain "${form[0]}") || exit 60; fi
  295. if [ "${form[1]}" ]; then $(validate_username "${form[1]}") || exit 61; fi
  296. if [ "${form[2]}" ]; then $(validate_group "${form[2]}") || exit 62; fi
  297. if [ "${form[3]}" ]; then $(validate_email "${form[3]}") || exit 63; fi
  298. if [ "${form[4]}" ]; then mkdir -p "${form[4]}"; fi
  299.  
  300. [ "${form[0]}" ] && args="$args --domain='${form[0]}'"
  301. [ "${form[1]}" ] && args="$args --webmaster='${form[1]}'"
  302. [ "${form[2]}" ] && args="$args --webgroup='${form[2]}'"
  303. [ "${form[3]}" ] && args="$args --adminemail='${form[3]}'"
  304. installpath="${dir}/${scriptname}"
  305. if [ "${form[4]}" != " " ]; then installpath="${form[4]}${scriptname}"; fi
  306.  
  307. cp "${dir}/${scriptname}" "$installpath" >/dev/null 2>&1
  308. chmod u+rx "$installpath"
  309.  
  310. desktop="$homedir/.local/share/applications/virtualhost.desktop"
  311. cat >"$desktop" <<EOF
  312. [Desktop Entry]
  313. Version=1.0
  314. Name=Create Virtualhost
  315. Comment=Easy to create new virtualhost for apache2 server
  316. Keywords=Virtualhost;Apache;Apache2
  317. Exec=/bin/bash ${installpath} ${args}
  318. Terminal=false
  319. X-MultipleArgs=true
  320. Type=Application
  321. Icon=network-wired
  322. Categories=GTK;Development;
  323. StartupNotify=false
  324. Actions=without-arguments;new-install
  325.  
  326. [Desktop Action without-arguments]
  327. Name=Clean launch
  328. Exec=/bin/bash ${installpath}
  329.  
  330. [Desktop Action without-mysql]
  331. Name=Without mysql config
  332. Exec=/bin/bash ${installpath} ${args} --skipmysql
  333.  
  334. [Desktop Action new-install]
  335. Name=Reinstall (define new configuration)
  336. Exec=/bin/bash ${installpath} --install --gui
  337. X-MultipleArgs=true
  338. EOF
  339.  
  340. exit 0
  341. fi
  342.  
  343. # if arguments passed - validate them.
  344. msg=""
  345. if [ "$maindomain_set" ]; then
  346. $(validate_domain "$maindomain")
  347. || msg="Bad value for --domain. Should have leading dot. ".localhost"n"
  348. fi
  349. if [ "$serveradmin_set" ]; then
  350. $(validate_email "$serveradmin") || msg="Bad value for --adminemailn$msg"
  351. fi
  352. if [ "$host_set" ]; then
  353. $(validate_subdomain "$host") || msg="Bad value for --subdomainn$msg"
  354. fi
  355. if [ "$webmaster_set" ]; then
  356. $(validate_username "$webmaster") || msg="Bad value for --webmastern$msg"
  357. fi
  358. if [ "$webgroup_set" ]; then
  359. $(validate_group "$webgroup") || msg="Bad value for --webgroupn$msg"
  360. fi
  361. have_command apache2 || msg="Apache2 not installedn$msg"
  362. if [ "$msg" ];then
  363. [ "$gui_set" ] && info_user "$msg" "error"
  364. exit 1
  365. fi
  366. fi
  367.  
  368. if [ "$(id -un)" == "root" ];then
  369. notify_user "You should not run this script as root but as user with 'sudo' rights." "error"
  370. exit 1
  371. fi
  372.  
  373. get_text_input() {
  374. if in_terminal; then
  375. defaulttext=""
  376. [ "$3" ] && defaulttext="[Default: ${3}]"
  377. read -p "${2}$defaulttext" -r input
  378. # default
  379. [ "$3" ] && [ -z "$input" ] && input="$3"
  380. else
  381. input=$(zenity --entry="$1" --title="Virtualhost" --text="$2" --entry-text="$3" 2>/dev/null)
  382. if [ "$?" == "1" ]; then
  383. echo "[$1]Cancel button" 1>&2
  384. exit 1 # Cancel button pressed
  385. fi
  386. fi
  387. if [ -z "$4" ]; then
  388. case "$input" in
  389. "") notify_user "[$1]Bad input: empty" "error" ; exit 1 ;;
  390. *"*"*) notify_user "[$1]Bad input: wildcard" "error" ; exit 1 ;;
  391. *[[:space:]]*) notify_user "[$1]Bad input: whitespace" "error" ; exit 1 ;;
  392. esac
  393. fi
  394. echo "$input"
  395. }
  396.  
  397. # get input and validate it
  398. if [ -z "$host" ]; then host=$(get_text_input "Subdomain" "Create virtualhost (= Folder name,case sensitive)" "") || exit; fi
  399. $(validate_subdomain "$host") || exit
  400.  
  401. if [ -z "$maindomain_set" ]; then maindomain=$(get_text_input "Domain" "Domain with leading dot." "$maindomain") || exit; fi
  402. $(validate_domain "$maindomain") || exit
  403.  
  404. if [ -z "$webmaster_set" ]; then webmaster=$(get_text_input "Username" "Webmaster username" "$webmaster") || exit; fi
  405. $(validate_username "$webmaster") || exit
  406.  
  407. if [ -z "$serveradmin_set" ]; then serveradmin=$(get_text_input "Admin email" "Server admin email" "$serveradmin") || exit; fi
  408. $(validate_email "$serveradmin") || exit
  409.  
  410. homedir=$( getent passwd "$webmaster" | cut -d: -f6 )
  411. # webroot is a choise of predefined paths array
  412. declare -a webroots=("${homedir}/Web/${host}" "/var/www/${host}" "/var/www/${webmaster}/${host}")
  413. zenitylistcmd=""
  414. # zenily list options is all columns of all rows as a argument, one by one
  415. for (( i=0; i<${#webroots[@]}; i++ ));do
  416. if in_terminal; then
  417. echo "[${i}] ${webroots[$i]}" # reference for text read below
  418. else
  419. zenitylistcmd="${zenitylistcmd}${i} ${i} ${webroots[$i]} "
  420. fi
  421. done
  422. dir=""
  423. [ -z "$webroot_set" ] && if in_terminal; then
  424. webroot=$(get_text_input 'Index' 'Website folder' "$webroot") || exit
  425. else
  426. webroot=$(zenity --list --column=" " --column="Idx" --column="Path" --hide-column=2
  427. --hide-header --radiolist --title="Choose web folder" $zenitylistcmd 2>/dev/null)
  428. fi
  429. if [ -z "${webroots[$webroot]}" ]; then notify_user "Invalid webroot index"; exit 1; fi
  430.  
  431. dir="${webroots[$webroot]}" # folder used as document root for virtualhost
  432. hostfile="${apachehost}${host}.conf" # apache virtualhost config file
  433.  
  434. # virtualhost template
  435. cat >"$tmphost" <<EOF
  436. <VirtualHost ${virtualhost}:${virtualport}>
  437. ServerAdmin $serveradmin
  438. DocumentRoot $dir
  439. ServerName ${host}${maindomain}
  440. ServerAlias ${host}${maindomain}
  441. <Directory "$dir">
  442. AllowOverride All
  443. Require local
  444. </Directory>
  445. # Available loglevels: trace8, ..., trace1, debug, info, notice, warn,
  446. # error, crit, alert, emerg.
  447. LogLevel error
  448. </VirtualHost>
  449. # vim: syntax=apache ts=4 sw=4 sts=4 sr noet
  450. EOF
  451.  
  452. find_editor() {
  453. local editor=${VISUAL:-$EDITOR}
  454. if [ "$editor" ]; then
  455. echo "$editor"
  456. return
  457. fi
  458.  
  459. for cmd in nano vim vi pico; do
  460. if have_command "$cmd"; then
  461. echo "$cmd"
  462. return
  463. fi
  464. done
  465. }
  466.  
  467. if in_terminal; then
  468. # edit virtualhost config
  469. editor=$(find_editor)
  470. if [ -z "$editor" ];then
  471. echo "$tmphost:"
  472. cat $tmphost
  473. echo "edit '$tmphost' to your liking, then hit Enter"
  474. read -p "I'll wait ... "
  475. else
  476. "$editor" "$tmphost"
  477. fi
  478. else
  479. # edit virtualhost config GUI
  480. text=$(zenity --text-info --title="Virtualhost config" --filename="$tmphost" --editable 2>/dev/null)
  481. if [ -z "$text" ];then
  482. # cancel button pressed
  483. exit 0
  484. fi
  485. echo "$text" > "$tmphost"
  486. fi
  487. # probably want some validating here that the user has not broken the config
  488. # apache will not reload config if incorrect
  489.  
  490. mysqlskip="$skipmysql" # skip if --skipmysql set
  491. [ -z "$mysqlskip" ] && if have_command mysqld; then
  492. if [ -z "$mysqlskip" ]; then mysqladminpwd=$(get_text_input "Admin password" "Admin password (${mysqladmin})" "" "skipcheck") || mysqlskip=1; fi
  493. if [ "$mysqlauto" ]; then
  494. mysqldb="$host"
  495. mysqluser="$host"
  496. mysqlpwd=""
  497. else
  498. if [ -z "$mysqlskip" ]; then
  499. mysqldb=$(get_text_input "Database" "Database name(Enter for default)" "$host") || mysqlskip=1
  500. fi
  501. if [ -z "$mysqlskip" ]; then
  502. mysqluser=$(get_text_input "Username" "Mysql user for db:$mysqldb host:localhost(Enter for default)" "$mysqldb") || mysqlskip=1
  503. fi
  504. if [ -z "$mysqlskip" ]; then
  505. mysqlpwd=$(get_text_input "Password" "Mysql password for user:$mysqluser db:$mysqldb host:localhost(Enter for empty)" "" "skipcheck") || mysqlskip=1
  506. fi
  507. fi
  508.  
  509. if [ -z "$mysqlskip" ]; then
  510. tmpmysqlinit=$(mktemp)
  511. trap 'rm "$tmpmysqlinit"' EXIT
  512. cat >"$tmpmysqlinit" <<EOF
  513. CREATE USER '${mysqluser}'@'localhost' IDENTIFIED BY '${mysqlpwd}';
  514. GRANT USAGE ON *.* TO '${mysqluser}'@'localhost';
  515. CREATE DATABASE IF NOT EXISTS `${mysqldb}` CHARACTER SET utf8 COLLATE utf8_general_ci;
  516. GRANT ALL PRIVILEGES ON `${mysqldb}`.* TO '${mysqluser}'@'localhost';
  517. FLUSH PRIVILEGES;
  518. EOF
  519. fi
  520. fi
  521.  
  522. getsuperuser () {
  523. if in_terminal;then
  524. echo "sudo"
  525. else
  526. echo "pkexec"
  527. fi
  528. }
  529.  
  530. notify_user "execute root commands with $(getsuperuser) to create virtualhost" "warning"
  531.  
  532. tmpresult=$(mktemp)
  533. trap 'rm "$tmpresult"' EXIT
  534. tmpmysqlresult=$(mktemp)
  535. trap 'rm "$tmpmysqlresult"' EXIT
  536.  
  537. $(getsuperuser) /bin/bash <<EOF
  538. mkdir -p "$dir"
  539. chown ${webmaster}:${webgroup} "$dir"
  540. chmod u=rwX,g=rXs,o= "$dir"
  541. cp "$tmphost" "$hostfile"
  542. chown root:root "$hostfile"
  543. chmod u=rw,g=r,o=r "$hostfile"
  544. a2ensite "${a2ensite}${host}.conf"
  545. systemctl reload apache2
  546. echo "$?" > "$tmpresult"
  547. if [ -z "${mysqlskip}" ]; then
  548. systemctl start mysql
  549. mysql --user="$mysqladmin" --password="$mysqladminpwd" <"$tmpmysqlinit"
  550. echo "$?" > "$tmpmysqlresult"
  551. fi
  552. EOF
  553.  
  554. if [ $(cat "$tmpmysqlresult") ]; then $mysqlresult="nMysql db,user created"; fi
  555. if [ $(cat "$tmpresult") ]; then notify_user "Virtualhost added. Apache2 reloaded.${mysqlresult}"; fi
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement