3 # plc Manages all PLC services on this machine
7 # description: Manages all PLC services on this machine
9 # $Id: guest.init,v 1.11 2006/04/03 21:50:29 mlhuang Exp $
12 PATH=/sbin:/bin:/usr/bin:/usr/sbin
14 # Source function library.
15 . /etc/init.d/functions
20 # Keep in order! All steps should be idempotent. This means that you
21 # should be able to run them multiple times without depending on
22 # anything previously being run. The idea is that when the
23 # configuration changes, "service plc restart" is called, all
24 # dependencies are fixed up, and everything just works.
40 # Total number of errors
43 # Count the exit status of the last command
46 ERRORS=$(($ERRORS+$?))
49 # Return IP address of hostname if resolvable
52 perl -MSocket -e '($a,$b,$c,$d,@addrs) = gethostbyname($ARGV[0]); print inet_ntoa($addrs[0]) . "\n";' $1 2>/dev/null
55 # Regenerate configuration files
59 plc-config --shell >/etc/planetlab/plc_config
60 . /etc/planetlab/plc_config
62 # Generate various defaults
63 if [ -z "$PLC_DB_PASSWORD" ] ; then
64 PLC_DB_PASSWORD=$(uuidgen)
65 plc-config --category=plc_db --variable=password --value="$PLC_DB_PASSWORD" --save
68 if [ -z "$PLC_API_MAINTENANCE_PASSWORD" ] ; then
69 PLC_API_MAINTENANCE_PASSWORD=$(uuidgen)
70 plc-config --category=plc_api --variable=maintenance_password --value="$PLC_API_MAINTENANCE_PASSWORD" --save
73 # Need to configure network before resolving hostnames
76 PLC_API_MAINTENANCE_SOURCES=$(
77 for server in API BOOT WWW ; do
78 hostname=PLC_${server}_HOST
79 gethostbyname ${!hostname}
82 plc-config --category=plc_api --variable=maintenance_sources --value="$PLC_API_MAINTENANCE_SOURCES" --save
85 mkdir -p /etc/planetlab/php
86 plc-config --php >/etc/planetlab/php/plc_config.php
87 plc-config --shell >/etc/planetlab/plc_config
89 # For backward compatibility, until we can convert all code to use
90 # the now standardized variable names.
92 # DB constants are all named the same
93 ln -sf plc_config /etc/planetlab/plc_db
96 cat >/etc/planetlab/plc_api <<EOF
97 PL_API_SERVER='$PLC_API_HOST'
98 PL_API_PATH='$PLC_API_PATH'
99 PL_API_PORT=$PLC_API_PORT
100 PL_API_CAPABILITY_AUTH_METHOD='capability'
101 PL_API_CAPABILITY_PASS='$PLC_API_MAINTENANCE_PASSWORD'
102 PL_API_CAPABILITY_USERNAME='$PLC_API_MAINTENANCE_USER'
103 PL_API_TICKET_KEY_FILE='$PLC_API_SSL_KEY'
104 PLANETLAB_SUPPORT_EMAIL='$PLC_MAIL_SUPPORT_ADDRESS'
105 BOOT_MESSAGES_EMAIL='$PLC_MAIL_BOOT_ADDRESS'
106 WWW_BASE='$PLC_WWW_HOST'
107 BOOT_BASE='$PLC_BOOT_HOST'
110 # API expects root SSH public key to be at /etc/planetlab/node_root_key
111 ln -sf "$PLC_ROOT_SSH_KEY_PUB" /etc/planetlab/node_root_key
115 # ip:max_role_id:organization_id:password
117 # It is unlikely that we will let federated sites use the
118 # maintenance account to access each others' APIs, so we always
119 # set organization_id to -1.
121 echo -n "PL_API_CAPABILITY_SOURCES='"
123 for ip in $PLC_API_MAINTENANCE_SOURCES ; do
124 if [ $first -ne 1 ] ; then
128 echo -n "$ip:-1:-1:$PLC_API_MAINTENANCE_PASSWORD"
131 ) >>/etc/planetlab/plc_api
133 cat >/etc/planetlab/php/site_constants.php <<"EOF"
135 include('plc_config.php');
137 define('PL_API_SERVER', PLC_API_HOST);
138 define('PL_API_PATH', PLC_API_PATH);
139 define('PL_API_PORT', PLC_API_PORT);
140 define('PL_API_CAPABILITY_AUTH_METHOD', 'capability');
141 define('PL_API_CAPABILITY_PASS', PLC_API_MAINTENANCE_PASSWORD);
142 define('PL_API_CAPABILITY_USERNAME', PLC_API_MAINTENANCE_USER);
143 define('WWW_BASE', PLC_WWW_HOST);
144 define('BOOT_BASE', PLC_BOOT_HOST);
145 define('DEBUG', PLC_WWW_DEBUG);
146 define('API_CALL_DEBUG', PLC_API_DEBUG);
147 define('SENDMAIL', PLC_MAIL_ENABLED);
148 define('PLANETLAB_SUPPORT_EMAIL', PLC_NAME . 'Support <' . PLC_MAIL_SUPPORT_ADDRESS . '>');
149 define('PLANETLAB_SUPPORT_EMAIL_ONLY', PLC_MAIL_SUPPORT_ADDRESS);
159 echo "127.0.0.1 localhost.localdomain localhost" >/etc/hosts
161 for server in API BOOT WWW ; do
162 hostname=PLC_${server}_HOST
163 ip=$(gethostbyname ${!hostname})
164 if [ -n "$ip" ] ; then
172 [ -n "$PLC_NET_DNS1" ] && echo "nameserver $PLC_NET_DNS1"
173 [ -n "$PLC_NET_DNS2" ] && echo "nameserver $PLC_NET_DNS2"
188 PGDATA=/var/lib/pgsql/data
189 postgresql_conf=$PGDATA/postgresql.conf
190 pghba_conf=$PGDATA/pg_hba.conf
192 # Export so that we do not have to specify -p to psql invocations
193 export PGPORT=$PLC_DB_PORT
197 if [ "$PLC_DB_ENABLED" != "1" ] ; then
201 # Set data directory and redirect startup output to /var/log/pgsql
202 mkdir -p /etc/sysconfig/pgsql
204 echo "PGDATA=$PGDATA"
205 echo "PGLOG=/var/log/pgsql"
206 echo "PGPORT=$PLC_DB_PORT"
207 ) >>/etc/sysconfig/pgsql/postgresql
209 # Fix ownership (rpm installation may have changed it)
210 chown -R -H postgres:postgres $(dirname $PGDATA)
212 # PostgreSQL must be started at least once to bootstrap
213 # /var/lib/pgsql/data
214 if [ ! -f $postgresql_conf ] ; then
215 service postgresql start
216 service postgresql stop
219 # Enable DB server. PostgreSQL >=8.0 defines listen_addresses,
220 # PostgreSQL 7.x uses tcpip_socket.
221 if grep -q listen_addresses $postgresql_conf ; then
222 sed -i -e '/^listen_addresses/d' $postgresql_conf
223 echo "listen_addresses = '*'" >>$postgresql_conf
224 elif grep -q tcpip_socket $postgresql_conf ; then
225 sed -i -e '/^tcpip_socket/d' $postgresql_conf
226 echo "tcpip_socket = true" >>$postgresql_conf
229 # Disable access to all DBs from all hosts
230 sed -i -e '/^\(host\|local\)/d' $pghba_conf
232 # Enable passwordless localhost access
233 echo "local all all trust" >>$pghba_conf
235 # Enable access from the API and web servers
236 PLC_API_IP=$(gethostbyname $PLC_API_HOST)
237 PLC_WWW_IP=$(gethostbyname $PLC_WWW_HOST)
239 echo "host $PLC_DB_NAME $PLC_DB_USER $PLC_API_IP/32 password"
240 echo "host $PLC_DB_NAME $PLC_DB_USER $PLC_WWW_IP/32 password"
243 # Fix ownership (sed -i changes it)
244 chown postgres:postgres $postgresql_conf $pghba_conf
246 # Start up the server
247 service postgresql start
248 # /etc/init.d/postgresql always returns 0, even on failure
249 status postmaster && [ -f /var/lock/subsys/postgresql ]
252 # Create/update the unprivileged database user and password
253 if ! psql -U $PLC_DB_USER -c "" template1 >/dev/null 2>&1 ; then
254 psql -U postgres -c "CREATE USER $PLC_DB_USER PASSWORD '$PLC_DB_PASSWORD'" template1
256 psql -U postgres -c "ALTER USER $PLC_DB_USER WITH PASSWORD '$PLC_DB_PASSWORD'" template1
259 # Create the database if necessary
260 if ! psql -U $PLC_DB_USER -c "" $PLC_DB_NAME >/dev/null 2>&1 ; then
261 createdb -U postgres $PLC_DB_NAME
262 psql -U $PLC_DB_USER -f /usr/share/pl_db/plc_schema_3.sql $PLC_DB_NAME
267 # Drop the current user in case the username changes
268 psql -U postgres -c "DROP USER $PLC_DB_USER" template1
270 # WARNING: If the DB name changes, the old DB will be left
271 # intact and a new one will be created. If it changes
272 # back, the old DB will not be re-created.
274 # Shut down the server
275 service postgresql stop
286 # Generate GPG keyrings
287 if [ ! -f $PLC_ROOT_GPG_KEY_PUB -o ! -f $PLC_ROOT_GPG_KEY ] ; then
288 mkdir -p $(dirname $PLC_ROOT_GPG_KEY_PUB)
289 mkdir -p $(dirname $PLC_ROOT_GPG_KEY)
291 # Temporarily replace /dev/random with /dev/urandom to
292 # avoid running out of entropy.
294 mknod /dev/random c 1 9
295 gpg --homedir=/root --batch --gen-key <<EOF
300 Name-Real: $PLC_NAME Central
301 Name-Comment: http://$PLC_WWW_HOST/
302 Name-Email: $PLC_MAIL_SUPPORT_ADDRESS
304 %pubring $PLC_ROOT_GPG_KEY_PUB
305 %secring $PLC_ROOT_GPG_KEY
310 mknod /dev/random c 1 8
311 chmod 600 $PLC_ROOT_GPG_KEY_PUB $PLC_ROOT_GPG_KEY
319 openssl x509 -noout -in $1 -subject | \
320 sed -n -e 's@.*/CN=\([^/]*\).*@\1@p'
325 mkdir -p $(dirname $2)
330 # Generate SSL certificates
335 # Generate self-signed SSL certificate(s). These nice
336 # commands come from the mod_ssl spec file for Fedora Core
337 # 2. We generate a certificate for each enabled server
338 # with a different hostname. These self-signed
339 # certificates may be overridden later.
340 for server in WWW API BOOT ; do
341 ssl_key=PLC_${server}_SSL_KEY
342 ssl_crt=PLC_${server}_SSL_CRT
343 hostname=PLC_${server}_HOST
345 # Check if we have already generated a certificate for
347 for previous_server in WWW API BOOT ; do
348 if [ "$server" = "$previous_server" ] ; then
351 previous_ssl_key=PLC_${previous_server}_SSL_KEY
352 previous_ssl_crt=PLC_${previous_server}_SSL_CRT
353 previous_hostname=PLC_${previous_server}_HOST
355 if [ -f ${!previous_ssl_crt} ] && \
356 [ "$(ssl_cname ${!previous_ssl_crt})" = "${!hostname}" ] ; then
357 cp -a ${!previous_ssl_key} ${!ssl_key}
358 cp -a ${!previous_ssl_crt} ${!ssl_crt}
363 # Generate new SSL private key
364 if [ ! -f ${!ssl_key} ] ; then
365 mkdir -p $(dirname ${!ssl_key})
366 openssl genrsa -rand /proc/apm:/proc/cpuinfo:/proc/dma:/proc/filesystems:/proc/interrupts:/proc/ioports:/proc/pci:/proc/rtc:/proc/uptime 1024 >${!ssl_key}
368 chmod 600 ${!ssl_key}
371 # Check if self signed certificate is valid
372 if [ -f ${!ssl_crt} ] ; then
373 verify=$(openssl verify ${!ssl_crt})
375 if grep -q "self signed certificate" <<<$verify ; then
376 # Delete if expired or hostname changed
377 if grep -q "expired" <<<$verify || \
378 [ "$(ssl_cname ${!ssl_crt})" != "${!hostname}" ] ; then
386 # Generate new self signed certificate
387 if [ ! -f ${!ssl_crt} ] ; then
388 mkdir -p $(dirname ${!ssl_crt})
389 openssl req -new -x509 -days 365 -set_serial $RANDOM \
390 -key ${!ssl_key} -out ${!ssl_crt} <<EOF
397 $PLC_MAIL_SUPPORT_ADDRESS
400 chmod 644 ${!ssl_crt}
404 # API requires a public key for slice ticket verification
405 if [ ! -f $PLC_API_SSL_KEY_PUB ] ; then
406 openssl rsa -pubout <$PLC_API_SSL_KEY >$PLC_API_SSL_KEY_PUB
410 # Install into both /etc/pki (Fedora Core 4) and
411 # /etc/httpd/conf (Fedora Core 2). If the API, boot, and
412 # web servers are all running on the same machine, the web
413 # server certificate takes precedence.
414 for server in API BOOT WWW ; do
415 enabled=PLC_${server}_ENABLED
416 if [ "${!enabled}" != "1" ] ; then
419 ssl_key=PLC_${server}_SSL_KEY
420 ssl_crt=PLC_${server}_SSL_CRT
422 symlink ${!ssl_crt} /etc/pki/tls/certs/localhost.crt
423 symlink ${!ssl_key} /etc/pki/tls/private/localhost.key
424 symlink ${!ssl_crt} /etc/httpd/conf/ssl.crt/server.crt
425 symlink ${!ssl_key} /etc/httpd/conf/ssl.key/server.key
434 # XXX Could make these configurable
442 tmp=$(mktemp -d /tmp/ssh.XXXXXX)
444 # Generate root SSH key
445 if [ ! -f $PLC_ROOT_SSH_KEY_PUB -o ! -f $PLC_ROOT_SSH_KEY ] ; then
446 ssh-keygen -N "" -C "$PLC_NAME Central <$PLC_MAIL_SUPPORT_ADDRESS>" \
447 -b $KEY_LEN_ROOT -t $KEY_TYPE_ROOT -f $tmp/root
449 install -D -m 600 $tmp/root $PLC_ROOT_SSH_KEY
450 install -D -m 644 $tmp/root.pub $PLC_ROOT_SSH_KEY_PUB
453 # Generate debug SSH key
454 if [ ! -f $PLC_DEBUG_SSH_KEY_PUB -o ! -f $PLC_DEBUG_SSH_KEY ] ; then
455 ssh-keygen -N "" -C "$PLC_NAME Central <$PLC_MAIL_SUPPORT_ADDRESS>" \
456 -b $KEY_LEN_DEBUG -t $KEY_TYPE_DEBUG -f $tmp/debug
458 install -D -m 600 $tmp/debug $PLC_DEBUG_SSH_KEY
459 install -D -m 644 $tmp/debug.pub $PLC_DEBUG_SSH_KEY_PUB
467 # Configure Apache web server
471 DocumentRoot=/var/www/html
473 httpd_conf=/etc/httpd/conf/httpd.conf
474 ssl_conf=/etc/httpd/conf.d/ssl.conf
475 plc_conf=/etc/httpd/conf.d/plc.conf
479 if [ "$PLC_API_ENABLED" != "1" -a \
480 "$PLC_BOOT_ENABLED" != "1" -a \
481 "$PLC_WWW_ENABLED" != "1" ] ; then
485 # Set the default include path
486 include_path=".:$DocumentRoot/includes:$DocumentRoot/generated:/etc/planetlab/php"
487 sed -i -e "s@;include_path = \"\.:.*\"@include_path = \"$include_path\"@" $php_ini
489 # Disable default Listen directive
490 sed -i -e '/^Listen/d' $httpd_conf
492 # Set the port numbers
493 for server in WWW API BOOT ; do
494 enabled=PLC_${server}_ENABLED
495 if [ "${!enabled}" != "1" ] ; then
498 hostname=PLC_${server}_HOST
499 http_port=PLC_${server}_PORT
500 https_port=PLC_${server}_SSL_PORT
502 # API should always be accessed via SSL
503 if [ "$server" = "API" ] ; then
504 https_port=${!http_port}
508 # Check if we are already listening on these ports
511 for previous_server in WWW API BOOT ; do
512 if [ "$server" = "$previous_server" ] ; then
515 previous_hostname=PLC_${previous_server}_HOST
516 previous_http_port=PLC_${previous_server}_PORT
517 previous_https_port=PLC_${previous_server}_SSL_PORT
519 if [ "${!http_port}" = "${!previous_http_port}" ] ; then
522 if [ "${!https_port}" = "${!previous_https_port}" ] ; then
527 # Listen on these ports
528 if [ $skip_http -eq 0 -a -n "${!http_port}" ] ; then
531 <VirtualHost *:${!http_port}>
532 Redirect /db https://$PLC_WWW_HOST:$PLC_WWW_SSL_PORT/db
533 # XXX Not yet until we can get rid of oldapi
534 # Redirect /$PLC_API_PATH https://$PLC_API_HOST:$PLC_API_PORT/$PLC_API_PATH
538 if [ $skip_https -eq 0 -a -n "${!https_port}" ] ; then
539 # XXX Cannot support NameVirtualHost over SSL. If
540 # the API, boot, and web servers are all running
541 # on the same machine, the web server certificate
544 -e "s/^Listen .*/Listen ${!https_port}/" \
545 -e "s/<VirtualHost _default_:.*>/<VirtualHost _default_:${!https_port}>/" \
550 # Set custom Apache directives
552 if [ "$PLC_API_ENABLED" = "1" ] ; then
554 <Location $PLC_API_PATH>
555 SetHandler python-program
556 PythonPath "sys.path + ['/usr/share/plc_api']"
557 PythonHandler mod_pythonXMLRPC
562 <Location $PLC_API_PATH>
568 if [ "$PLC_WWW_ENABLED" != "1" ] ; then
577 # Make alpina-logs directory writable for bootmanager log upload
578 chown apache:apache $DocumentRoot/alpina-logs/nodes
595 if [ "$PLC_API_ENABLED" != "1" ] ; then
599 # Update the maintenance account username. This can't be
600 # done through the api-config script since it uses the
601 # maintenance account to access the API. The maintenance
602 # account should be person_id 1 since it is created by the
604 psql -U $PLC_DB_USER -c "UPDATE persons SET email='$PLC_API_MAINTENANCE_USER' WHERE person_id=1" $PLC_DB_NAME
617 if [ "$PLC_MAIL_ENABLED" = "1" ] ; then
618 MAILTO=$PLC_MAIL_SUPPORT_ADDRESS
622 cat >/etc/cron.d/plc.cron <<EOF
624 PATH=/sbin:/bin:/usr/sbin:/usr/bin
628 # minute hour day-of-month month day-of-week user command
629 */5 * * * * root gen-slices-xml-05.py
630 */15 * * * * root gen-sites-xml.py
631 */15 * * * * root gen-static-content.py
634 # Run them once at startup
639 gen-static-content.py
657 if [ "$PLC_BOOT_ENABLED" != "1" -a \
658 "$PLC_WWW_ENABLED" != "1" ] ; then
662 # Customize the BootCD
663 pushd /var/www/html/download
664 /usr/share/bootcd/build.sh
671 config_bootmanager ()
675 if [ "$PLC_BOOT_ENABLED" != "1" -a \
676 "$PLC_WWW_ENABLED" != "1" ] ; then
680 # Customize the Boot Manager
681 pushd /var/www/html/boot
682 /usr/share/bootmanager/build.sh
691 echo "Usage: $0 [OPTION]... [COMMAND]"
692 echo " -v Be verbose"
693 echo " -h This message"
696 echo " start Start all PLC subsystems"
697 echo " stop Stop all PLC subsystems"
698 echo " reload Regenerate configuration files"
699 echo " restart Restart all PLC subsystems"
704 while getopts "vh" opt ; do
716 shift $(($OPTIND - 1))
717 if [ -z "$1" ] ; then
723 if [ $verbose -eq 0 ] ; then
724 exec 1>>/var/log/boot.log
725 exec 2>>/var/log/boot.log
728 # Generate and load configuration
730 . /etc/planetlab/plc_config
736 for step in "${steps[@]}" ; do
737 echo -n $"PLC: Starting $step: " >&3
740 if [ $RETVAL -eq $ERRORS ] ; then
741 success $"PLC: $step startup" >&3
743 failure $"PLC: $step startup" >&3
751 for i in $(seq 1 $nsteps) ; do
752 step=${steps[$(($nsteps - $i))]}
753 echo -n $"PLC: Shutting down $step: " >&3
756 if [ $RETVAL -eq $ERRORS ] ; then
757 success $"PLC: $step shutdown" >&3
759 failure $"PLC: $step shutdown" >&3