#!/bin/bash
# Iris Services Management Script
# Manages OAuth Server (8000), MCP Server (8001), and Booking Page (8081)
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PID_DIR="$SCRIPT_DIR/.pids"
LOG_DIR="$SCRIPT_DIR/logs"
# Create directories
mkdir -p "$PID_DIR" "$LOG_DIR"
# Service definitions
declare -A SERVICES=(
["oauth"]="8000:src.oauth_server.app:uvicorn src.oauth_server.app:app --host 0.0.0.0 --port 8000"
["mcp"]="8001:src.mcp_servers.microsoft.server:python -m src.mcp_servers.microsoft.server"
["booking"]="8081:src.booking_page.app:uvicorn src.booking_page.app:app --host 0.0.0.0 --port 8081"
)
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Helper functions
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
log_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# Get service info
get_service_port() {
local service=$1
echo "${SERVICES[$service]}" | cut -d: -f1
}
get_service_name() {
local service=$1
echo "${SERVICES[$service]}" | cut -d: -f2
}
get_service_cmd() {
local service=$1
echo "${SERVICES[$service]}" | cut -d: -f3-
}
# Check if service is running
is_running() {
local service=$1
local pid_file="$PID_DIR/$service.pid"
if [ -f "$pid_file" ]; then
local pid=$(cat "$pid_file")
if ps -p "$pid" > /dev/null 2>&1; then
return 0
else
# PID file exists but process is dead
rm -f "$pid_file"
return 1
fi
fi
return 1
}
# Get PID
get_pid() {
local service=$1
local pid_file="$PID_DIR/$service.pid"
if [ -f "$pid_file" ]; then
cat "$pid_file"
else
echo ""
fi
}
# Start a service
start_service() {
local service=$1
local port=$(get_service_port "$service")
local service_name=$(get_service_name "$service")
local cmd=$(get_service_cmd "$service")
local log_file="$LOG_DIR/$service.log"
local pid_file="$PID_DIR/$service.pid"
if is_running "$service"; then
log_warning "$service is already running (PID: $(get_pid "$service"))"
return 0
fi
log_info "Starting $service on port $port..."
# Find and kill any process using the port
local pids_to_kill=$(python3 << EOF
import os
port_target = $port
port_hex = format(port_target, '04X')
pids = []
try:
with open('/proc/net/tcp', 'r') as f:
lines = f.readlines()
for line in lines[1:]: # Skip header
if port_hex in line:
parts = line.split()
if len(parts) > 9:
inode = parts[9]
# Find which process owns this inode
for pid in os.listdir('/proc'):
if not pid.isdigit():
continue
try:
fd_dir = f'/proc/{pid}/fd'
for fd in os.listdir(fd_dir):
link = os.readlink(f'{fd_dir}/{fd}')
if f'socket:[{inode}]' in link:
pids.append(pid)
break
except (FileNotFoundError, PermissionError, OSError):
pass
except Exception:
pass
print(' '.join(set(pids)))
EOF
)
if [ -n "$pids_to_kill" ]; then
for pid in $pids_to_kill; do
# Skip critical system processes
if [ "$pid" != "1" ] && [ "$pid" != "7" ]; then
log_warning "Killing process $pid occupying port $port"
kill -9 "$pid" 2>/dev/null
fi
done
sleep 1
fi
# Start the service
cd "$SCRIPT_DIR"
nohup $cmd > "$log_file" 2>&1 &
local pid=$!
# Save PID
echo "$pid" > "$pid_file"
# Wait and verify
sleep 2
if ps -p "$pid" > /dev/null 2>&1; then
# Additional verification - check if port is listening
local retries=5
while [ $retries -gt 0 ]; do
if nc -z localhost $port 2>/dev/null || curl -s http://localhost:$port/health > /dev/null 2>&1; then
log_success "$service started successfully (PID: $pid, Port: $port)"
return 0
fi
sleep 1
retries=$((retries - 1))
done
log_warning "$service process is running but port $port is not responding yet"
return 0
else
log_error "$service failed to start. Check logs: $log_file"
rm -f "$pid_file"
return 1
fi
}
# Stop a service
stop_service() {
local service=$1
local port=$(get_service_port "$service")
local pid_file="$PID_DIR/$service.pid"
# First, try to stop from PID file
if is_running "$service"; then
local pid=$(get_pid "$service")
log_info "Stopping $service (PID: $pid)..."
# Try graceful shutdown first
kill "$pid" 2>/dev/null
# Wait up to 5 seconds for graceful shutdown
local count=0
while [ $count -lt 5 ] && ps -p "$pid" > /dev/null 2>&1; do
sleep 1
count=$((count + 1))
done
# Force kill if still running
if ps -p "$pid" > /dev/null 2>&1; then
log_warning "Graceful shutdown failed, forcing kill..."
kill -9 "$pid" 2>/dev/null
sleep 1
fi
else
log_warning "$service PID file not found, searching by port..."
fi
# Also find and kill any process still using the port
local pids_to_kill=$(python3 << EOF
import os
port_target = $port
port_hex = format(port_target, '04X')
pids = []
try:
with open('/proc/net/tcp', 'r') as f:
lines = f.readlines()
for line in lines[1:]: # Skip header
if port_hex in line:
parts = line.split()
if len(parts) > 9:
inode = parts[9]
# Find which process owns this inode
for pid in os.listdir('/proc'):
if not pid.isdigit():
continue
try:
fd_dir = f'/proc/{pid}/fd'
for fd in os.listdir(fd_dir):
link = os.readlink(f'{fd_dir}/{fd}')
if f'socket:[{inode}]' in link:
pids.append(pid)
break
except (FileNotFoundError, PermissionError, OSError):
pass
except Exception:
pass
print(' '.join(set(pids)))
EOF
)
if [ -n "$pids_to_kill" ]; then
for pid in $pids_to_kill; do
# Skip critical system processes
if [ "$pid" != "1" ] && [ "$pid" != "7" ]; then
log_warning "Killing remaining process $pid on port $port"
kill -9 "$pid" 2>/dev/null
fi
done
sleep 1
fi
# Clean up PID file
rm -f "$pid_file"
log_success "$service stopped successfully"
return 0
}
# Restart a service
restart_service() {
local service=$1
log_info "Restarting $service..."
stop_service "$service"
sleep 1
start_service "$service"
}
# Reload a service (graceful restart)
reload_service() {
restart_service "$1"
}
# Show status
status_service() {
local service=$1
local port=$(get_service_port "$service")
if is_running "$service"; then
local pid=$(get_pid "$service")
echo -e "${GREEN}●${NC} $service is ${GREEN}running${NC} (PID: $pid, Port: $port)"
# Check if port is responding
if nc -z localhost $port 2>/dev/null || curl -s http://localhost:$port/health > /dev/null 2>&1; then
echo " └─ Port $port is responding"
else
echo -e " └─ ${YELLOW}Warning: Port $port is not responding${NC}"
fi
else
echo -e "${RED}○${NC} $service is ${RED}stopped${NC}"
fi
}
# Show all status
status_all() {
log_info "Service Status:"
echo ""
for service in "${!SERVICES[@]}"; do
status_service "$service"
done
}
# Start all services
start_all() {
log_info "Starting all services..."
for service in oauth mcp booking; do
start_service "$service"
done
}
# Stop all services
stop_all() {
log_info "Stopping all services..."
for service in booking mcp oauth; do
stop_service "$service"
done
}
# Restart all services
restart_all() {
log_info "Restarting all services..."
stop_all
sleep 2
start_all
}
# Show logs
logs() {
local service=$1
local log_file="$LOG_DIR/$service.log"
if [ -f "$log_file" ]; then
tail -f "$log_file"
else
log_error "Log file not found: $log_file"
return 1
fi
}
# Show last N lines of logs
logs_tail() {
local service=$1
local lines=${2:-50}
local log_file="$LOG_DIR/$service.log"
if [ -f "$log_file" ]; then
tail -n "$lines" "$log_file"
else
log_error "Log file not found: $log_file"
return 1
fi
}
# Usage
usage() {
cat << EOF
Iris Services Management Script
Usage: $0 <command> [service]
Commands:
start [service] Start service(s) (all if no service specified)
stop [service] Stop service(s) (all if no service specified)
restart [service] Restart service(s) (all if no service specified)
reload [service] Reload service(s) (same as restart)
status [service] Show status of service(s) (all if no service specified)
logs <service> Follow logs of a service
tail <service> [N] Show last N lines of service logs (default: 50)
Services:
oauth OAuth Server (port 8000)
mcp MCP Server (port 8001)
booking Booking Page (port 8081)
all All services (default)
Examples:
$0 start # Start all services
$0 start booking # Start only booking service
$0 stop # Stop all services
$0 restart mcp # Restart only MCP service
$0 status # Show status of all services
$0 logs booking # Follow booking service logs
$0 tail mcp 100 # Show last 100 lines of MCP logs
EOF
}
# Main
main() {
local command=${1:-status}
local service=${2:-all}
case "$command" in
start)
if [ "$service" = "all" ]; then
start_all
else
if [ -n "${SERVICES[$service]}" ]; then
start_service "$service"
else
log_error "Unknown service: $service"
usage
exit 1
fi
fi
;;
stop)
if [ "$service" = "all" ]; then
stop_all
else
if [ -n "${SERVICES[$service]}" ]; then
stop_service "$service"
else
log_error "Unknown service: $service"
usage
exit 1
fi
fi
;;
restart|reload)
if [ "$service" = "all" ]; then
restart_all
else
if [ -n "${SERVICES[$service]}" ]; then
restart_service "$service"
else
log_error "Unknown service: $service"
usage
exit 1
fi
fi
;;
status)
if [ "$service" = "all" ]; then
status_all
else
if [ -n "${SERVICES[$service]}" ]; then
status_service "$service"
else
log_error "Unknown service: $service"
usage
exit 1
fi
fi
;;
logs)
if [ "$service" = "all" ]; then
log_error "Please specify a service for logs"
usage
exit 1
else
if [ -n "${SERVICES[$service]}" ]; then
logs "$service"
else
log_error "Unknown service: $service"
usage
exit 1
fi
fi
;;
tail)
local lines=${3:-50}
if [ "$service" = "all" ]; then
log_error "Please specify a service for logs"
usage
exit 1
else
if [ -n "${SERVICES[$service]}" ]; then
logs_tail "$service" "$lines"
else
log_error "Unknown service: $service"
usage
exit 1
fi
fi
;;
help|--help|-h)
usage
;;
*)
log_error "Unknown command: $command"
usage
exit 1
;;
esac
}
# Run
main "$@"