#!/usr/bin/sh
NGINX_CFG_DIR="/etc/nginx/conf.d"
UPSTREAM_CFG="s3_upstream.conf"
UPSTREAM="ostor_s3"
OSTOR_S3_CFG="ostor-s3.conf"
DOMAIN=""
HTTP_PORT="8080"
OPEN_SSL="/usr/bin/openssl"
SSL_PORT="443"
PORT=$HTTP_PORT
REPLACE=""
CFG_SVC=""
MAX_KEEPALIVE=32
KEEPALIVE=""
KEEPALIVE_OPTS="fastcgi_keep_conn on;"
ARGS="${@}"

function print_usage {
        exec 1>&2
	echo "Usage:"
	echo -e \
	     " ostor-configure-nginx COMMAND [-D,--domain DOMAIN_NAME] [-n,--name HOST_NAME ]\n" \
	     "                              [-r,--replace] [-C,--cfg-dir DIR] [-K,--keepalive]\n" \
	     "                              [-p PORT] [-h,--help]"
	echo "Commands:"
	echo "  create                    Create new configuration."
	echo "  update                    Update existing upstream configuration. It should be called"
	echo "                            when configuration of S3 gateways was changed."
        echo "Options:"
	echo -e "  -n,--name   HOST_NAME     The hostname of configuration service. It should be specified\n"\
	     "                           only if this host does not present in configuration."
	echo "  -D,--domain DOMAIN_NAME   The domain name of your object storage."
	echo "  -p,--port   PORT          The listening port, default is 8080."
	echo "  -r,--replace              Replace existing configuration on create instead of fail."
	echo "  -K,--keepalive            Enable keepalive FCGI connections, disabled by default."
	echo "  -C,--cfg-dir DIR          Directory where NGINX's configuration files are located."
	echo "  -S,--ssl                  Turn on SSL."
	echo "  --ssl-cert FILE           Path to file with SSL certificate."
	echo "  --ssl-key  FILE           Path to file with SSL key."
	echo "  -h,--help                 Print this help."
        echo " "
        exit 1
}

function parse_args {

	CMD="${1}"
	[ -z "$CMD" ] && print_usage

	if [ "x$CMD" != "xcreate" -a "x$CMD" != "xupdate" ]; then
		echo "Unknown command: ${1}" 1>&2
		exit 1
	fi
	shift
        while [ "${#}" -gt 0 ]; do
                case "${1}" in
			"-S"|"--ssl")
				USE_SSL="1"
				shift
				;;
			"--ssl-cert")
				[ -z "${2}" ] && print_usage
				SSL_CERT_FILE="${2}"
				shift
				shift
				;;
			"--ssl-key")
				[ -z "${2}" ] && print_usage
				SSL_KEY_FILE="${2}"
				shift
				shift
				;;
			"-K"|"--keepalive")
				KEEPALIVE="1"
				shift
				;;
			"-n"|"--name")
				[ -z "${2}" ] && print_usage
				CFG_SVC="${2}"
				shift
				shift
				;;
			"-r"|"--replace")
				REPLACE="${1}"
				shift
				;;
			"-C"|"--cfg-dir")
				[ -z "${2}" ] && print_usage
				NGINX_CFG_DIR="${2}"
				shift
				shift
				;;	
                        "-D"|"--domain")
				[ -z "${2}" ] && print_usage
				DOMAIN="${2}"
                                shift
				shift
                                ;;
			"-p"|"--port")
				[ -z "${2}" ] && print_usage
                                PORT="${2}"
				shift
				shift
				printf '%d' "$PORT" >/dev/null 2>&1
				if [ $? -ne 0 ] ; then
					echo "Argument of -p,--port must be a number" 1>&2
					exit 1
				fi
                                ;;
                        "-h"|"--help")
                                print_usage
                                ;;
                        *)
                                echo "Unknown option '${1}'." 1>&2
                                print_usage
                                ;;
                esac
        done

	if [ "x$CMD" = "xcreate" ]; then
		if [ -z "$DOMAIN" ] ; then
			echo "Option -D,--domain should be specified." 1>&2
			exit 1
		fi

		if [ -n "$USE_SSL" ]; then
			if [ -z "$SSL_CERT_FILE" ]; then
				echo "Path to SSL certificate file (--ssl-cert) should be specified." 1>&2
				exit 1
			elif [ ! -f "$SSL_CERT_FILE" ]; then
				echo "Can't find file $SSL_CERT_FILE" 1>&2
				exit 1
			fi

			if [ -z "$SSL_KEY_FILE" ]; then
				echo "Path to SSL key file (--ssl-key) should be specified." 1>&2
				exit 1
			elif [ ! -f "$SSL_KEY_FILE" ]; then
				echo "Can't find file $SSL_KEY_FILE" 1>&2
				exit 1
			fi

			[ "x$HTTP_PORT" = "x$PORT" ] && PORT=$SSL_PORT
		fi
	fi

	if [ -z "$CFG_SVC" ]; then
		if [ ! -f "/var/lib/ostor/name" ]; then
			echo "Option -n,--name should be specified." 1>&2
			exit 1
		else
			CFG_SVC=$(cat /var/lib/ostor/name 2>/dev/null | head -n 1)
			if [ -z "$CFG_SVC" ] ; then
				echo "Can't read name of configuration service." 1>&2
				exit 1
			fi	
		fi
	fi
}

test_ssl_cert() {
	[ ! -x $OPEN_SSL -o -z "$USE_SSL" ] && return

	subj=$($OPEN_SSL x509 -noout -subject -in $SSL_CERT_FILE)
	if [ $? -ne 0 ]; then
		echo "Can't fetch subject from certificate $SSL_CERT_FILE" 1>&2
		exit 1
	fi
	echo "$subj" | grep "CN=$DOMAIN" >/dev/null
	if [ $? -ne 0 ]; then
		echo "Common Name (CN) field in $SSL_CERT_FILE should be equal to $DOMAIN" 1>&2
		echo "Actual value: $subj"
		exit 1
	fi
}

get_addrs() {
	local host_id="$1"
	local args=""
	[ -n "$CFG_SVC" ] && args="-n $CFG_SVC"
	/usr/bin/ostor-ctl get-config $args -S | grep "S3GW .*$host_id" 2>/dev/null | sed -e "s#.*address=\(.*\)#\1#" | cut -d '&' -f1
}

build_upstream() {
	local host_id="$1"
	local name="$2"
	local addrs="$3"

	echo "# upstream configuration for $CFG_SVC"
	echo "upstream $name {"
	echo "$addrs" | while read a ;do
		echo "	server $a;"
	done

	[ -n "$KEEPALIVE" ] && echo "	keepalive $MAX_KEEPALIVE;"
	echo "}"
	return 0
}

build_cfg() {
	local server="$1"
	local port="$2"
	local upstream="$3"
	echo "# configuration for object storage '$CFG_SVC'"
	echo -e \
	"server {\n" \
	"	listen $port;"
	if [ -n "$USE_SSL" ]; then
	echo -e \
	"	ssl on;\n" \
	"	ssl_protocols  SSLv2 SSLv3 TLSv1;\n" \
	"	ssl_certificate $SSL_CERT_FILE;\n" \
	"	ssl_certificate_key $SSL_KEY_FILE;\n" \
	"	ssl_ciphers  HIGH:!aNULL:!MD5;\n" \
	"	ssl_prefer_server_ciphers   on;"
	fi	
	echo -e \
	"	server_name $server;\n" \
	"	client_max_body_size 5g;\n" \
	"	location / {\n" \
	"		fastcgi_pass_header Connection-close;\n" \
	"		fastcgi_pass $upstream;\n" \
	"		fastcgi_no_cache 1;\n"	\
	"		include fastcgi_params;\n" \
	"		fastcgi_buffering off;\n" \
	"		fastcgi_request_buffering off;\n" \
	"		fastcgi_max_temp_file_size 0; \n" \
	"		fastcgi_param OSTOR_CFG_NAME $CFG_SVC;"
	[ -n "$USE_SSL" ] && echo \
	"		fastcgi_param SSL on;"
	[ -n "$KEEPALIVE" ] && 	echo \
	"		$KEEPALIVE_OPTS"
	echo -e \
	"	}\n" \
	"}\n"
}

do_update() {
	local upstream_file="$NGINX_CFG_DIR/$UPSTREAM_CFG"
	local addrs
	addrs=$(get_addrs "$HOST_ID")
	if [ $? -ne 0 ]; then
		echo "Can't get configuration." 1>&2
		exit 2
	fi
	if [ -z "$addrs" ] ; then
		echo "Can't get S3 gateways assigned to host $HOST_ID." 1>&2
		exit 2
	fi

	local tmp
	tmp=$(mktemp "$upstream_file.XXXXXXXXXX")
	if [ $? -ne 0 ]; then
		echo "Unable to create temporary file." 1>&2
		exit 2
	fi

	build_upstream "$HOST_ID" "$UPSTREAM" "$GW_ADDRS" | dd of="$tmp" conv=fsync >/dev/null 2>&1
	if [ $RC -ne 0 ]; then
		echo "Can't save upstream configuration in $tmp." 1>&2
		rm -f "$tmp"
		exit 3
	fi
	mv -f "$tmp" "$upstream_file" && return 0
	echo "Unable to update upstream configuration." 1>&2
	rm -f "$tmp"
	exit 4
}

do_create() {
	local cfg_file="$NGINX_CFG_DIR/$OSTOR_S3_CFG"
	local upstream_file="$NGINX_CFG_DIR/$UPSTREAM_CFG"
	if [ -f "$cfg_file" ] ; then
		if [ -z "$REPLACE" ]; then
			echo "Configuration file $cfg_file already exist." 1>&2
			exit 1
		fi
	fi
	local tmp
	tmp=$(mktemp "$cfg_file.XXXXXXXXXX")
	if [ $? -ne 0 ]; then
		echo "Unable to create temporary file." 1>&2
		exit 1
	fi
	local tmp1
	tmp1=$(mktemp "$upstream_file.XXXXXXXXXX")
	if [ $? -ne 0 ]; then
		echo "Unable to create temporary file." 1>&2
		exit 2
	fi
	
	build_cfg "$DOMAIN" "$PORT" "$UPSTREAM" | dd of="$tmp" conv=fsync >/dev/null 2>&1
	if [ $? -ne 0 ]; then
		echo "Can't save new configuration in $tmp" 1>&2
		rm -f "$tmp" "$tmp1"
		exit 2
	fi

	build_upstream "$HOST_ID" "$UPSTREAM" "$GW_ADDRS" | dd of="$tmp1" conv=fsync >/dev/null 2>&1
	if [ $? -ne 0 ]; then
		echo "Can't save upstream configuration in $tmp1" 1>&2
		rm -f "$tmp" "$tmp1"
		exit 3
	fi

	mv -f "$tmp1" "$upstream_file" && mv -f "$tmp" "$cfg_file" && return 0

	echo "Unable to create configuration." 1>&2
	exit 4
}

parse_args $ARGS
if [ ! -d "$NGINX_CFG_DIR" ]; then
	echo "Directory $NGINX_CFG_DIR does not exist" 2>&1
	exit 1
fi

HOST_ID=$(cat /etc/vstorage/host_id 2>/dev/null | head -n1)
GW_ADDRS=$(get_addrs "$HOST_ID")
if [ $? -ne 0 ]; then
	echo "Can't get configuration." 1>&2
	exit 2
fi

if [ -z "$GW_ADDRS" ] ; then
	echo "Can't get S3 gateways assigned to host $HOST_ID." 1>&2
	exit 2
fi

RC=0
if [ "x$CMD" = "xcreate" ]; then
	test_ssl_cert
	do_create
	RC=$?
	if [ $RC -eq 0 ] ; then
		echo "Configuration stored in $NGINX_CFG_DIR/$OSTOR_S3_CFG"
		echo "Upstream configuration stored in $NGINX_CFG_DIR/$UPSTREAM_CFG"
	fi
else
	if [ ! -f "$NGINX_CFG_DIR/$OSTOR_S3_CFG" ]; then
		echo "Configuration does not exist at $NGINX_CFG_DIR." 1>&2
		exit 1
	fi

	# read keepalive configuration
	cat "$NGINX_CFG_DIR/$OSTOR_S3_CFG" | grep "fastcgi_keep_conn on" >/dev/null
	[ $? -eq 0 ] && KEEPALIVE="1"


	# test hostname of configuration service	
	cfg=$(cat "$NGINX_CFG_DIR/$OSTOR_S3_CFG" | grep "fastcgi_param OSTOR_CFG_NAME .*;" 2>/dev/null | tr ';' ' ' | awk '{ print $3 }')
	if [ -z "$cfg" ] ; then
		echo "Can't read OSTOR_CFG_NAME from configuration." 1>&2
		exit 1
	fi

	if [ "x$CFG_SVC" != "x$cfg" ]; then
		echo "Existing configuration created for object storage '$cfg'." 1>&2
		exit 1
	fi

	do_update
	RC=$?
	[ $RC -eq 0 ] && echo "The $NGINX_CFG_DIR/$UPSTREAM_CFG successfully updated."
fi
exit $RC

