Detect PHP flexibly, assert wland configuration
[infodrom/musiikki-web.git] / install-system
1 #! /bin/bash
2
3 ESC=$'\e'
4 col_red="${ESC}[1;31m"
5 col_green_bold="${ESC}[1;32m"
6 col_green="${ESC}[32m"
7 col_yellow="${ESC}[1;33m"
8 #col_cyan="${ESC}[1;36m"
9 col_reset="${ESC}[0m"
10
11 opt_destdir=/srv/musiikki
12 opt_adminpwfile=${opt_destdir}/htpasswd
13 opt_verbose=1
14 opt_uid=
15 opt_gid=
16 opt_apt=
17 opt_hostname=
18 opt_essid=
19 opt_wpakey=
20 opt_subnet=
21
22 # default name of Raspian
23 default_hostname=raspberrypi
24
25 echo_ok()
26 {
27     echo "${col_green}$*${col_reset}"
28 }
29
30 echo_ok_bold()
31 {
32     echo "${col_green_bold}$*${col_reset}"
33 }
34
35 echo_error()
36 {
37     echo "${col_red}$*${col_reset}"
38 }
39
40 echo_verbose()
41 {
42     test $opt_verbose -eq 1 || return
43
44     echo -n "$*"
45 }
46
47 echo_newline_verbose()
48 {
49     test $opt_verbose -eq 1 || return
50
51     echo ""
52 }
53
54 # ------------------------------------------------------------
55
56 assert_system_uidgid()
57 {
58     test -z "$opt_uid" || return
59
60     if [ -d ${opt_destdir} ]
61     then
62         opt_uid=$(stat --format=%u ${opt_destdir})
63         opt_gid=$(stat --format=%g ${opt_destdir})
64     else
65         echo -n "${col_yellow}Set file owner to${col_reset}: "
66         read opt_uid
67
68         echo -n "${col_yellow}Set file group to${col_reset}: "
69         read opt_gid
70     fi
71 }
72
73 assert_system_hostname()
74 {
75     test -z "$opt_hostname" || return
76
77     echo -n "${col_yellow}Set hostname to${col_reset}: "
78     read opt_hostname
79 }
80
81 assert_system_essid()
82 {
83     test -z "$opt_essid" || return
84
85     if [ -f /etc/musiikki.conf ]
86     then
87         opt_essid=$(grep '^title=' /etc/musiikki.conf)
88         opt_essid=${opt_essid#title=}
89     else
90         if [ -f /etc/hostapd/hostapd.conf ]
91         then
92             opt_essid=$(grep '^ssid=' /etc/hostapd/hostapd.conf)
93             opt_essid=${opt_essid#ssid=}
94         else
95             echo -n "${col_yellow}Set Wireless ESSID to${col_reset}: "
96             read opt_essid
97         fi
98     fi
99 }
100
101 assert_system_wpakey()
102 {
103     test -z "$opt_wpakey" || return
104
105     if [ -f /etc/hostapd/hostapd.conf ]
106     then
107         opt_wpakey=$(grep '^wpa_passphrase=' /etc/hostapd/hostapd.conf)
108         opt_wpakey=${opt_wpakey#wpa_passphrase=}
109     else
110         echo -n "${col_yellow}Set Wireless WPA key to${col_reset}: "
111         read opt_wpakey
112     fi
113 }
114
115 assert_system_subnet()
116 {
117     test -z "$opt_subnet" || return
118
119
120     if grep -q '^server\.bind' /etc/lighttpd/lighttpd.conf
121     then
122         opt_subnet=$(grep '^server\.bind\s' /etc/lighttpd/lighttpd.conf)
123         opt_subnet=${opt_subnet#*\"}
124         opt_subnet=${opt_subnet%.1\"*}
125     else
126         echo -n "${col_yellow}Set subnet (3 octets only) to${col_reset}: "
127         read opt_subnet
128     fi
129 }
130
131 # ------------------------------------------------------------
132
133 welcome()
134 {
135     echo
136     echo_ok_bold "Installing Infodrom MediaServer"
137     echo
138 }
139
140 assert_root()
141 {
142     if [ "$(/usr/bin/whoami)" != "root" ]
143     then
144         echo_error "The installation needs to be done as root"
145         return 1
146     fi
147
148     return 0
149 }
150
151 assert_directory()
152 {
153     if [ ! -x install-system ]
154     then
155         echo_error "Current directory lacks executable install-system"
156         return 1
157     fi
158
159     if [ ! -d bin ]
160     then
161         echo_error "Current directory lacks bin directory"
162         return 1
163     fi
164
165     if [ ! -d config-templates ]
166     then
167         echo_error "Current directory lacks config-templates directory"
168         return 1
169     fi
170
171     if [ "$(pwd)" != "${opt_destdir}" -a -x /usr/bin/rsync ]
172     then
173         assert_system_uidgid
174
175         # Relocate ourself
176         rsync -arH ./ ${opt_destdir}/
177         chown -R ${opt_uid}.${opt_gid} ${opt_destdir}
178     fi
179
180     return 0
181 }
182
183 assert_hostname()
184 {
185     if [ "$(cat /etc/hostname)" = "$default_hostname" ]
186     then
187         echo_verbose "Default hostname found"; echo_newline_verbose
188         assert_system_hostname
189         echo $opt_hostname > /etc/hostname
190         printf "192.168.1.1\t%s\n" "$opt_hostname" >> /etc/hosts
191     fi
192 }
193
194 assert_programs()
195 {
196
197     echo_verbose "Looking for programs: "
198
199     echo_verbose "sed... "
200     if [ ! -x /bin/sed ]
201     then
202         echo_newline_verbose
203         echo_error "Program sed missing"
204         return 1
205     fi
206
207     echo_verbose "dpkg... "
208     if [ ! -x /usr/bin/dpkg ]
209     then
210         echo_newline_verbose
211         echo_error "Program dpkg missing"
212         return 1
213     fi
214
215     echo_verbose "apt-get... "
216     if [ -x /usr/bin/apt-get ]
217     then
218         opt_apt=/usr/bin/apt-get
219     else
220         echo_verbose "apt... "
221         if [ -x /usr/bin/apt ]
222         then
223             opt_apt=/usr/bin/apt
224         else
225             echo_verbose "aptitude... "
226             if [ -x /usr/bin/aptitude ]
227             then
228                 opt_apt=/usr/bin/aptitude
229             else
230                 echo_newline_verbose
231                 echo_error "High-level package manager missing (apt-get/apt/aptitude)"
232                 return 1
233             fi
234         fi
235     fi
236
237     echo_newline_verbose
238
239     return 0
240 }
241
242 assert_no_recommends()
243 {
244     if ! grep -q 'APT::Install-Recommends False;' /etc/apt/apt.conf.d/*
245     then
246         echo_ok "Configure APT not to install recommended packages"
247         echo 'APT::Install-Recommends False;' > /etc/apt/apt.conf.d/40recommends
248     fi
249 }
250
251 install_packages()
252 {
253     if [ -z "$opt_apt" ]
254     then
255         echo_error "No high-level package manager available"
256         return 1
257     fi
258
259     set -e
260     $opt_apt update
261     $opt_apt install -y $*
262     set +e
263
264     return 0
265 }
266
267 remove_packages()
268 {
269     if [ -z "$opt_apt" ]
270     then
271         echo_error "No high-level package manager available"
272         return 1
273     fi
274
275     set -e
276     $opt_apt purge -y $*
277     set +e
278
279     return 0
280 }
281
282 package_exists()
283 {
284     local pkg=$1
285
286     if [ -f "/var/lib/dpkg/info/${pkg}.list" ]
287     then
288         return 0
289     else
290         return 1
291     fi
292 }
293
294 assert_packages()
295 {
296     missing=
297
298     echo_verbose "Looking for installed packages: "
299
300     for pkg in sudo super rsync hostapd isc-dhcp-server minidlna lighttpd samba
301     do
302         echo_verbose "${pkg}... "
303         if ! package_exists ${pkg}
304         then
305             missing="$missing $pkg"
306         fi
307     done
308
309     echo_verbose "php... "
310     php_cgi=$(apt-cache search php7*-cgi|cut -d' ' -f 1|grep php7.*-cgi|sort|tail -n 1)
311     php=${php_cgi%-cgi}
312
313     for pkg in ${php}-cgi ${php}-json ${php}-sqlite3
314     do
315         echo_verbose "${pkg}... "
316         if ! package_exists ${pkg}
317         then
318             missing="$missing $pkg"
319         fi
320     done
321
322     echo_newline_verbose
323
324     if [ -n "$missing" ]
325     then
326         echo_ok "Installing$missing"
327         install_packages $missing || return 1
328         echo_ok "Successfully installed$missing"
329     fi
330
331     toomuch=
332
333     echo_verbose "Looking for installed packages: "
334
335     for pkg in triggerhappy avahi-daemon
336     do
337         echo_verbose "${pkg}... "
338         if package_exists ${pkg}
339         then
340             toomuch="$toomuch $pkg"
341         fi
342     done
343
344     echo_newline_verbose
345
346     if [ -n "$toomuch" ]
347     then
348         echo_ok "Removing$toomuch"
349         remove_packages $toomuch || return 1
350         echo_ok "Successfully removed$toomuch"
351     fi
352 }
353
354 assert_musiikki()
355 {
356     if [ ! -f /etc/musiikki.conf ]
357     then
358         assert_system_essid
359
360         echo_ok "Installing main configuration"
361         sed "s/{essid}/${opt_essid}/g;s!{basedir}!${opt_destdir}!g;s!{htpasswd}!${opt_adminpwfile}!g" config-templates/musiikki.conf.template > /root/.template
362         install -o root -g root -m 644 /root/.template /etc/musiikki.conf
363         rm -f /root/.template
364     fi
365 }
366
367 assert_hostapd()
368 {
369     if [ ! -f /etc/hostapd/hostapd.conf ]
370     then
371         assert_system_essid
372         assert_system_wpakey
373
374         echo_ok "Create hostapd configuration"
375         sed "s/{essid}/${opt_essid}/g;s/{wpakey}/${opt_wpakey}/g" config-templates/hostapd.conf.template > /root/.template
376         install -o root -g root -m 644 /root/.template /etc/hostapd/hostapd.conf
377         rm -f /root/.template
378     fi
379
380     if grep -q '#DAEMON_CONF=""' /etc/default/hostapd
381     then
382         echo_ok "Activate hostapd"
383         sed -i 's!#DAEMON_CONF=""!DAEMON_CONF="/etc/hostapd/hostapd.conf"!' /etc/default/hostapd
384     fi
385 }
386
387 assert_network()
388 {
389     if ! grep -q '^source /etc/network/interfaces.d/wlan1.conf' /etc/network/interfaces
390     then
391         echo >> /etc/network/interfaces
392         echo 'source /etc/network/interfaces.d/wlan1.conf' >> /etc/network/interfaces
393         echo >> /etc/network/interfaces
394     fi
395
396     sed -i 's!#net.ipv4.ip_forward=1!net.ipv4.ip_forward=1!' /etc/sysctl.conf
397     if ! grep -q '^net.ipv4.ip_forward=1' /etc/sysctl.conf
398     then
399         echo >> /etc/sysctl.conf
400         echo 'net.ipv4.ip_forward=1' >> /etc/sysctl.conf
401     fi
402
403     # iptables -t nat -A POSTROUTING -o wlan0 -j MASQUERADE
404     # iptables -A FORWARD -i wlan0 -o wlan1 -m state --state RELATED,ESTABLISHED -j ACCEPT
405     # iptables -A FORWARD -i wlan1 -o wlan0 -j ACCEPT
406     # iptables-save > /srv/musiikki/iptables_nat.conf
407 }
408
409 assert_dhcpd()
410 {
411     assert_system_subnet
412
413     if [ ! -f /etc/network/interfaces.d/wlan0.conf ]
414     then
415        cat <<EOT > /etc/network/interfaces.d/wlan0.conf
416 auto wlan0
417 iface wlan0 inet static
418         address ${opt_subnet}.1
419         netmask 255.255.255.0
420         broadcast ${opt_subnet}.255
421         network ${opt_subnet}.0
422         #gateway ${opt_subnet}.1
423 EOT
424        echo "source /etc/network/interfaces.d/wlan0.conf" >> /etc/network/interfaces
425     fi
426
427     if ! grep -q "^subnet ${opt_subnet}.0 " /etc/dhcp/dhcpd.conf
428     then
429         echo_ok "Add DHCP configuration"
430         sed "s/{subnet}/${opt_subnet}/g" config-templates/dhcpd.conf.template >> /etc/dhcp/dhcpd.conf
431     fi
432
433     if grep -q 'INTERFACESv4=""' /etc/default/isc-dhcp-server
434     then
435         echo_ok "Add WLAN interface to DCHP config"
436         sed -i 's/INTERFACESv4=".*"/INTERFACESv4="wlan0"/' /etc/default/isc-dhcp-server
437     fi
438 }
439
440 assert_super()
441 {
442     echo_verbose "Asserting permissions "
443     for prg in $(cd bin && echo *)
444     do
445         test $prg != musiikki-rescan || continue
446
447         echo_verbose "${prg}... "
448         if ! grep -q "^${prg}" /etc/super.tab
449         then
450             printf "%s\t%s/bin/%s www-data u+g=root password=n\n" ${prg} ${opt_destdir} ${prg} >> /etc/super.tab
451         fi
452     done
453
454     echo_newline_verbose
455 }
456
457 assert_minidlna()
458 {
459     assert_system_essid
460     assert_system_subnet
461
462     if ! grep -q "^friendly_name=${opt_essid}" /etc/minidlna.conf
463     then
464         sed -i "s!#friendly_name=!friendly_name=${opt_essid}!;s!^media_dir=.*!!" /etc/minidlna.conf
465         sed -i "s!#listening_ip=!listening_ip=${opt_subnet}.1!" /etc/minidlna.conf
466         sed -i "s!#db_dir=.*!db_dir=/media/music/cache!" /etc/minidlna.conf
467
468         echo "" >>  /etc/minidlna.conf
469         echo "model_name=${opt_essid}" >>  /etc/minidlna.conf
470         echo "media_dir=A,/media/music/music" >>  /etc/minidlna.conf
471         echo "media_dir=P,/media/music/pictures" >>  /etc/minidlna.conf
472         echo "media_dir=V,/media/music/video" >>  /etc/minidlna.conf
473     fi
474
475     test -d /media/music/cache || install -d -m 755 /media/music/cache
476     chown -R minidlna:minidlna /media/music/cache
477
478     test -d /media/music/music/playlists || install -d -m 755 /media/music/music/playlists
479     chown -R www-data:www-data /media/music/music/playlists
480 }
481
482 assert_lighttpd()
483 {
484     echo_verbose "Asserting lighttpd modules "
485
486
487     echo_verbose "05-auth... "
488     if [ ! -f /etc/lighttpd/conf-enabled/05-auth.conf ]
489     then
490        assert_system_essid
491
492        cat <<EOT > /etc/lighttpd/conf-enabled/05-auth.conf
493 # /usr/share/doc/lighttpd/authentication.txt.gz
494
495 server.modules                += ( "mod_auth" )
496
497 auth.backend                 = "plain"
498 auth.backend.plain.userfile  = "${opt_adminpwfile}"
499 # auth.backend.plain.groupfile = "lighttpd.group"
500
501 auth.require                   = ( "/admin/" =>
502                                    (
503                                      "method"  => "basic",
504                                      "realm"   => "${opt_essid}",
505                                      "require" => "user=admin",
506                                    )
507                                  )
508 EOT
509     fi
510
511     for name in 10-fastcgi 15-fastcgi-php
512     do
513         echo_verbose "${name}... "
514         if [ ! -f /etc/lighttpd/conf-enabled/${name}.conf ]
515         then
516             ln -s ../conf-available/${name}.conf /etc/lighttpd/conf-enabled/${name}.conf
517         fi
518     done
519
520     echo_newline_verbose
521
522     if [ ! -f ${opt_adminpwfile} ]
523     then
524         echo -n "${col_yellow}Set web admin password to${col_reset}: "
525         read admin_pw
526
527         echo "admin:${admin_pw}" > ${opt_adminpwfile}
528     fi
529
530     chown www-data.www-data ${opt_adminpwfile}
531
532     if ! grep -q '^server.tag' /etc/lighttpd/lighttpd.conf
533     then
534         assert_system_essid
535         assert_system_subnet
536
537         sed -i "s!^server.document-root\\s*=\\s*\".*\"!server.document-root        = \"${opt_destdir}/html\"!" /etc/lighttpd/lighttpd.conf
538         echo "server.tag                  = \"${opt_essid}\"" >> /etc/lighttpd/lighttpd.conf
539         echo "server.bind                 = \"${opt_subnet}.1\"" >> /etc/lighttpd/lighttpd.conf
540     fi
541 }
542
543 assert_samba()
544 {
545     if ! grep -q '^\[musicread\]$' /etc/samba/smb.conf
546     then
547         echo_ok "Add shares to Samba"
548         sed "s/{essid}/${opt_essid}/g" config-templates/smb.conf.template >> /etc/samba/smb.conf
549     fi
550
551     if ! grep -q '^musicread:' /etc/passwd
552     then
553         assert_system_essid
554
555         echo_ok "Create read/only Samba account"
556         adduser --quiet --disabled-password --system --group --no-create-home --home /var/lib/misc --gecos "${opt_essid} read/only account" musicread
557     fi
558
559     if ! grep -q '^musicwrite:' /etc/passwd
560     then
561         assert_system_essid
562
563         echo_ok "Create read/write Samba account"
564         adduser --quiet --disabled-password --system --group --no-create-home --home /var/lib/misc --gecos "${opt_essid} read/write account" musicwrite
565     fi
566 }
567
568 assert_cron()
569 {
570     if [ ! -f /etc/cron.d/musiikki ]
571     then
572         echo_ok "Install crontab"
573         cp config-templates/cron.d.template /etc/cron.d/musiikki
574     fi
575
576     if [ ! -d /var/local/musiikki ]
577     then
578         install -d -o minidlna -g minidlna -m 755 /var/local/musiikki
579     fi
580
581     if [ ! -d /var/local/musiikki/rescan ]
582     then
583         install -d -o minidlna -g minidlna -m 777 /var/local/musiikki/rescan
584     fi
585 }
586
587
588 welcome
589
590 assert_root || exit 2
591 assert_directory || exit 2
592 assert_hostname || exit 2
593 assert_programs || exit 2
594 assert_no_recommends || exit 2
595 assert_packages || exit 2
596
597 assert_musiikki
598 assert_hostapd
599 assert_network
600 assert_dhcpd
601 assert_super
602 assert_minidlna
603 assert_lighttpd
604 assert_samba
605 assert_cron