3 # pg_scribe - Incremental SQL backup system for PostgreSQL
5 # This script provides a unified CLI for managing PostgreSQL backups
6 # using logical replication and plain SQL format.
16 EXIT_CONNECTION_ERROR=2
19 EXIT_VALIDATION_ERROR=5
23 DEFAULT_SLOT="pg_scribe"
25 DEFAULT_HOST="localhost"
26 DEFAULT_STATUS_INTERVAL=10
27 DEFAULT_FSYNC_INTERVAL=10
32 HOST="${PGHOST:-$DEFAULT_HOST}"
33 PORT="${PGPORT:-$DEFAULT_PORT}"
34 USERNAME="${PGUSER:-${USER:-}}"
37 STATUS_INTERVAL="$DEFAULT_STATUS_INTERVAL"
38 FSYNC_INTERVAL="$DEFAULT_FSYNC_INTERVAL"
48 # Color output support
49 if [[ "${PG_COLOR:-auto}" == "always" ]] || [[ "${PG_COLOR:-auto}" == "auto" && -t 2 ]]; then
65 # Logging functions (output to stderr)
67 echo -e "${BLUE}INFO:${RESET} $*" >&2
71 echo -e "${GREEN}SUCCESS:${RESET} $*" >&2
75 echo -e "${YELLOW}WARNING:${RESET} $*" >&2
79 echo -e "${RED}ERROR:${RESET} $*" >&2
83 echo -e "${BOLD}==>${RESET} $*" >&2
89 pg_scribe - Incremental SQL backup system for PostgreSQL
92 pg_scribe --init [OPTIONS]
93 pg_scribe --start [OPTIONS]
94 pg_scribe --full-backup [OPTIONS]
95 pg_scribe --restore [OPTIONS]
96 pg_scribe --status [OPTIONS]
100 Actions (exactly one required):
101 --init Initialize backup system
102 --start Start streaming incremental backups
103 --full-backup Take a full backup
104 --restore Restore from backups
105 --status Check replication slot status
106 -V, --version Print version and exit
107 -?, --help Show this help and exit
110 -d, --dbname=DBNAME Database name (can be connection string)
111 -h, --host=HOSTNAME Database server host (default: $DEFAULT_HOST)
112 -p, --port=PORT Database server port (default: $DEFAULT_PORT)
113 -U, --username=NAME Database user (default: \$PGUSER or \$USER)
114 -w, --no-password Never prompt for password
115 -W, --password Force password prompt
118 -v, --verbose Enable verbose mode
121 -f, --file=DIRECTORY Backup output directory (required)
122 -S, --slot=SLOTNAME Replication slot name (default: $DEFAULT_SLOT)
123 --force Skip validation and force initialization
126 -f, --file=FILENAME Output file (use '-' for stdout, required)
127 -S, --slot=SLOTNAME Replication slot name (default: $DEFAULT_SLOT)
128 -s, --status-interval=SECS Status update interval (default: $DEFAULT_STATUS_INTERVAL)
129 -F, --fsync-interval=SECS Fsync interval (default: $DEFAULT_FSYNC_INTERVAL, 0 to disable)
131 Options for --full-backup:
132 -f, --file=DIRECTORY Backup output directory (required)
133 -Z, --compress=METHOD Compression: gzip, lz4, zstd, or none (default: zstd:9)
135 Options for --restore:
136 -f, --file=DIRECTORY Backup input directory (required)
137 -d, --dbname=DBNAME Target database name (required)
138 -C, --create Create target database
139 --base-backup=FILE Specific base backup file (default: latest)
140 --no-sync-sequences Skip sequence synchronization
142 Options for --status:
143 -S, --slot=SLOTNAME Replication slot name (default: $DEFAULT_SLOT)
144 -f, --file=DIRECTORY Backup directory to analyze (optional)
149 2 Database connection error
150 3 Replication slot error
151 4 Backup/restore error
152 5 Invalid arguments or validation failure
154 Environment Variables:
155 PGHOST, PGPORT, PGDATABASE, PGUSER, PGPASSWORD, PG_COLOR
157 Report bugs to: https://github.com/your-repo/pg_scribe/issues
161 # Parse command line arguments
163 if [[ $# -eq 0 ]]; then
165 exit "$EXIT_VALIDATION_ERROR"
168 while [[ $# -gt 0 ]]; do
171 [[ -n "$ACTION" ]] && { log_error "Multiple action flags specified"; exit "$EXIT_VALIDATION_ERROR"; }
176 [[ -n "$ACTION" ]] && { log_error "Multiple action flags specified"; exit "$EXIT_VALIDATION_ERROR"; }
181 [[ -n "$ACTION" ]] && { log_error "Multiple action flags specified"; exit "$EXIT_VALIDATION_ERROR"; }
186 [[ -n "$ACTION" ]] && { log_error "Multiple action flags specified"; exit "$EXIT_VALIDATION_ERROR"; }
191 [[ -n "$ACTION" ]] && { log_error "Multiple action flags specified"; exit "$EXIT_VALIDATION_ERROR"; }
196 echo "pg_scribe $VERSION"
251 -s|--status-interval)
256 STATUS_INTERVAL="${1#*=}"
264 FSYNC_INTERVAL="${1#*=}"
284 BASE_BACKUP="${1#*=}"
308 log_error "Unknown option: $1"
310 exit "$EXIT_VALIDATION_ERROR"
315 # Validate action was specified
316 if [[ -z "$ACTION" ]]; then
317 log_error "No action specified"
319 exit "$EXIT_VALIDATION_ERROR"
322 # Use PGDATABASE if dbname not specified
323 if [[ -z "$DBNAME" && -n "${PGDATABASE:-}" ]]; then
328 # Build psql connection string
332 [[ -n "$DBNAME" ]] && args+=(-d "$DBNAME")
333 [[ -n "$HOST" ]] && args+=(-h "$HOST")
334 [[ -n "$PORT" ]] && args+=(-p "$PORT")
335 [[ -n "$USERNAME" ]] && args+=(-U "$USERNAME")
336 [[ "$NO_PASSWORD" -eq 1 ]] && args+=(-w)
337 [[ "$FORCE_PASSWORD" -eq 1 ]] && args+=(-W)
339 printf '%s\n' "${args[@]}"
342 # Build pg_recvlogical connection string
343 build_pg_recvlogical_args() {
346 [[ -n "$DBNAME" ]] && args+=(-d "$DBNAME")
347 [[ -n "$HOST" ]] && args+=(-h "$HOST")
348 [[ -n "$PORT" ]] && args+=(-p "$PORT")
349 [[ -n "$USERNAME" ]] && args+=(-U "$USERNAME")
350 [[ "$NO_PASSWORD" -eq 1 ]] && args+=(-w)
351 [[ "$FORCE_PASSWORD" -eq 1 ]] && args+=(-W)
353 printf '%s\n' "${args[@]}"
356 # Test database connection
358 log_step "Testing database connection..."
361 mapfile -t psql_args < <(build_psql_args)
363 if ! psql "${psql_args[@]}" -c "SELECT version();" >/dev/null 2>&1; then
364 log_error "Failed to connect to database"
365 log_error "Connection details: host=$HOST port=$PORT dbname=$DBNAME user=$USERNAME"
366 exit "$EXIT_CONNECTION_ERROR"
369 if [[ "$VERBOSE" -eq 1 ]]; then
370 log_success "Connected to database"
374 # Execute SQL query and return result
378 mapfile -t psql_args < <(build_psql_args)
379 psql "${psql_args[@]}" -t -A -c "$sql" 2>&1
382 # Execute SQL query silently (return exit code only)
386 mapfile -t psql_args < <(build_psql_args)
387 psql "${psql_args[@]}" -t -A -c "$sql" >/dev/null 2>&1
391 # --init command implementation
394 log_step "Initializing pg_scribe backup system"
396 # Validate required arguments
397 if [[ -z "$DBNAME" ]]; then
398 log_error "--init requires -d/--dbname"
399 exit "$EXIT_VALIDATION_ERROR"
402 if [[ -z "$FILE" ]]; then
403 log_error "--init requires -f/--file (backup directory)"
404 exit "$EXIT_VALIDATION_ERROR"
407 # Cleanup tracking for failure handling
408 local CREATED_SLOT=""
409 local CREATED_FILES=()
411 # Cleanup function for handling failures
412 # shellcheck disable=SC2317 # Function called via trap handler
413 cleanup_on_failure() {
416 # Only cleanup on actual failure, not on successful exit
417 if [[ $exit_code -ne 0 && $exit_code -ne $EXIT_WARNING ]]; then
418 log_info "Cleaning up after failed initialization..."
420 # Drop replication slot if we created it
421 if [[ -n "$CREATED_SLOT" ]]; then
422 log_info "Dropping replication slot '$CREATED_SLOT'..."
423 query_db "SELECT pg_drop_replication_slot('$CREATED_SLOT');" 2>/dev/null || true
426 # Remove files we created
427 for file in "${CREATED_FILES[@]}"; do
428 if [[ -f "$file" ]]; then
429 log_info "Removing partial file: $file"
430 rm -f "$file" 2>/dev/null || true
434 log_info "Cleanup complete"
438 # Set up cleanup trap
439 trap cleanup_on_failure EXIT INT TERM
441 # Test connection first
444 # Phase 1: Validation
445 log_step "Phase 1: Validation"
447 local validation_failed=0
451 log_info "Checking wal_level configuration..."
453 wal_level=$(query_db "SHOW wal_level;")
454 if [[ "$wal_level" != "logical" ]]; then
455 log_error "CRITICAL: wal_level is '$wal_level', must be 'logical'"
456 log_error " Fix: Add 'wal_level = logical' to postgresql.conf and restart PostgreSQL"
459 if [[ "$VERBOSE" -eq 1 ]]; then
460 log_success "wal_level = logical"
464 # Check max_replication_slots
465 log_info "Checking max_replication_slots configuration..."
467 max_slots=$(query_db "SHOW max_replication_slots;")
468 if [[ "$max_slots" -lt 1 ]]; then
469 log_error "CRITICAL: max_replication_slots is $max_slots, must be >= 1"
470 log_error " Fix: Add 'max_replication_slots = 10' to postgresql.conf and restart PostgreSQL"
473 if [[ "$VERBOSE" -eq 1 ]]; then
474 log_success "max_replication_slots = $max_slots"
478 # Check max_wal_senders
479 log_info "Checking max_wal_senders configuration..."
481 max_senders=$(query_db "SHOW max_wal_senders;")
482 if [[ "$max_senders" -lt 1 ]]; then
483 log_error "CRITICAL: max_wal_senders is $max_senders, must be >= 1"
484 log_error " Fix: Add 'max_wal_senders = 10' to postgresql.conf and restart PostgreSQL"
487 if [[ "$VERBOSE" -eq 1 ]]; then
488 log_success "max_wal_senders = $max_senders"
492 # Check replica identity on all tables
493 log_info "Checking replica identity for all tables..."
495 bad_tables=$(query_db "
496 SELECT n.nspname || '.' || c.relname
498 JOIN pg_namespace n ON n.oid = c.relnamespace
499 WHERE c.relkind = 'r'
500 AND n.nspname NOT IN ('pg_catalog', 'information_schema')
501 AND c.relreplident IN ('d', 'n')
503 SELECT 1 FROM pg_index i
504 WHERE i.indrelid = c.oid AND i.indisprimary
506 ORDER BY n.nspname, c.relname;
509 if [[ -n "$bad_tables" ]]; then
510 log_error "CRITICAL: The following tables lack adequate replica identity:"
511 while IFS= read -r table; do
512 log_error " - $table"
513 done <<< "$bad_tables"
514 log_error " Fix: Add a primary key or set replica identity:"
515 log_error " ALTER TABLE <table> ADD PRIMARY KEY (id);"
516 log_error " -- OR --"
517 log_error " ALTER TABLE <table> REPLICA IDENTITY FULL;"
520 if [[ "$VERBOSE" -eq 1 ]]; then
521 log_success "All tables have adequate replica identity"
525 # Warning: Check for unlogged tables
526 log_info "Checking for unlogged tables..."
527 local unlogged_tables
528 unlogged_tables=$(query_db "
529 SELECT n.nspname || '.' || c.relname
531 JOIN pg_namespace n ON n.oid = c.relnamespace
532 WHERE c.relkind = 'r'
533 AND c.relpersistence = 'u'
534 AND n.nspname NOT IN ('pg_catalog', 'information_schema')
535 ORDER BY n.nspname, c.relname;
538 if [[ -n "$unlogged_tables" ]]; then
539 log_warning "The following unlogged tables will NOT be backed up:"
540 while IFS= read -r table; do
541 log_warning " - $table"
542 done <<< "$unlogged_tables"
546 # Warning: Check for large objects
547 log_info "Checking for large objects..."
548 local large_object_count
549 large_object_count=$(query_db "SELECT count(*) FROM pg_largeobject_metadata;")
551 if [[ "$large_object_count" -gt 0 ]]; then
552 log_warning "Database contains $large_object_count large objects"
553 log_warning "Large objects are NOT incrementally backed up (only in full backups)"
554 log_warning "Consider using BYTEA columns instead for incremental backup support"
558 # Check if validation failed
559 if [[ "$validation_failed" -eq 1 ]]; then
560 if [[ "$FORCE" -eq 1 ]]; then
561 log_warning "Validation failed but --force specified, continuing anyway..."
563 log_error "Validation failed. Fix the CRITICAL issues above and try again."
564 log_error "Or use --force to skip validation (NOT recommended)."
565 exit "$EXIT_VALIDATION_ERROR"
568 log_success "All validation checks passed"
572 log_step "Phase 2: Setup"
574 # Create backup directory
575 log_info "Checking backup directory..."
576 if [[ ! -d "$FILE" ]]; then
577 if ! mkdir -p "$FILE"; then
578 log_error "Failed to create backup directory: $FILE"
579 exit "$EXIT_BACKUP_ERROR"
581 log_success "Created backup directory: $FILE"
583 # Directory exists - check if already initialized
584 if [[ -f "$FILE/pg_scribe_metadata.txt" ]]; then
585 log_error "Backup directory already initialized: $FILE"
586 log_error "Metadata file exists: $FILE/pg_scribe_metadata.txt"
588 log_error "This directory has already been initialized with pg_scribe."
589 log_error "To take an additional full backup, use: pg_scribe --full-backup"
591 log_error "If you want to re-initialize from scratch:"
592 log_error " 1. Stop any running backup processes"
593 log_error " 2. Drop the replication slot (or verify it's safe to reuse)"
594 log_error " 3. Remove or rename the existing backup directory"
595 exit "$EXIT_VALIDATION_ERROR"
598 # Directory exists but not initialized - check if empty
599 if [[ -n "$(ls -A "$FILE" 2>/dev/null)" ]]; then
600 log_error "Backup directory is not empty: $FILE"
601 log_error "The backup directory must be empty for initialization."
602 log_error "Found existing files:"
603 # shellcheck disable=SC2012 # ls used for user-friendly display, not processing
604 ls -lh "$FILE" | head -10 >&2
605 exit "$EXIT_VALIDATION_ERROR"
608 log_info "Using existing empty directory: $FILE"
611 # Create wal2sql extension
612 log_info "Creating wal2sql extension..."
613 if query_db_silent "CREATE EXTENSION IF NOT EXISTS wal2sql;"; then
614 log_success "wal2sql extension created (or already exists)"
616 log_error "Failed to create wal2sql extension"
617 log_error "Ensure wal2sql.so is installed in PostgreSQL's lib directory"
618 log_error "Run: cd wal2sql && make && make install"
619 exit "$EXIT_GENERAL_ERROR"
622 # Create replication slot with snapshot export
623 log_info "Creating logical replication slot '$SLOT'..."
625 # Check if slot already exists
627 slot_exists=$(query_db "SELECT count(*) FROM pg_replication_slots WHERE slot_name = '$SLOT';")
629 if [[ "$slot_exists" -gt 0 ]]; then
630 log_error "Replication slot '$SLOT' already exists"
632 log_error "A replication slot with this name already exists in the database."
633 log_error "This may indicate:"
634 log_error " - A previous initialization that was not cleaned up"
635 log_error " - Another pg_scribe instance using the same slot name"
637 log_error "To resolve:"
638 log_error " - Use a different slot name with -S/--slot option"
639 log_error " - Or drop the existing slot (if safe):"
640 log_error " psql -d $DBNAME -c \"SELECT pg_drop_replication_slot('$SLOT');\""
641 exit "$EXIT_SLOT_ERROR"
644 # Create slot using SQL
645 # Note: For POC, we create the slot and take the base backup sequentially
646 # The slot will preserve WAL from its creation LSN forward, ensuring no changes are lost
648 if ! slot_result=$(query_db "SELECT slot_name, lsn FROM pg_create_logical_replication_slot('$SLOT', 'wal2sql');"); then
649 log_error "Failed to create replication slot"
650 log_error "$slot_result"
651 exit "$EXIT_SLOT_ERROR"
654 CREATED_SLOT="$SLOT" # Track for cleanup
655 log_success "Replication slot '$SLOT' created"
657 # Take base backup immediately after slot creation
658 # The slot preserves WAL from its creation point, so all changes will be captured
659 local base_backup_file
660 base_backup_file="$FILE/base-$(date +%Y%m%d-%H%M%S).sql"
661 CREATED_FILES+=("$base_backup_file") # Track for cleanup
662 log_info "Taking base backup: $base_backup_file"
665 mapfile -t psql_args < <(build_psql_args)
666 if pg_dump "${psql_args[@]}" --file="$base_backup_file"; then
667 log_success "Base backup completed: $base_backup_file"
669 log_error "Base backup failed"
670 exit "$EXIT_BACKUP_ERROR"
673 # Take globals backup
674 log_info "Taking globals backup..."
675 local globals_backup_file
676 globals_backup_file="$FILE/globals-$(date +%Y%m%d-%H%M%S).sql"
677 CREATED_FILES+=("$globals_backup_file") # Track for cleanup
679 # pg_dumpall doesn't use -d, only connection params
680 local dumpall_args=()
681 [[ -n "$HOST" ]] && dumpall_args+=(-h "$HOST")
682 [[ -n "$PORT" ]] && dumpall_args+=(-p "$PORT")
683 [[ -n "$USERNAME" ]] && dumpall_args+=(-U "$USERNAME")
684 [[ "$NO_PASSWORD" -eq 1 ]] && dumpall_args+=(-w)
685 [[ "$FORCE_PASSWORD" -eq 1 ]] && dumpall_args+=(-W)
687 if pg_dumpall "${dumpall_args[@]}" --globals-only --file="$globals_backup_file"; then
688 log_success "Globals backup completed: $globals_backup_file"
690 log_error "Globals backup failed"
691 exit "$EXIT_BACKUP_ERROR"
694 # Generate metadata file
695 log_info "Generating metadata file..."
696 local metadata_file="$FILE/pg_scribe_metadata.txt"
697 CREATED_FILES+=("$metadata_file") # Track for cleanup
699 pg_version=$(query_db "SELECT version();")
701 cat > "$metadata_file" <<EOF
702 pg_scribe Backup System Metadata
703 =================================
705 Generated: $(date -u +"%Y-%m-%d %H:%M:%S UTC")
706 pg_scribe Version: $VERSION
712 Replication Slot: $SLOT
715 $(query_db "SELECT extname || ' ' || extversion FROM pg_extension ORDER BY extname;")
717 Encoding: $(query_db "SELECT pg_encoding_to_char(encoding) FROM pg_database WHERE datname = '$DBNAME';")
719 Collation: $(query_db "SELECT datcollate FROM pg_database WHERE datname = '$DBNAME';")
722 log_success "Metadata file created: $metadata_file"
724 # Disable cleanup trap on successful completion
729 log_step "Initialization Complete"
730 log_success "Backup directory: $FILE"
731 log_success "Replication slot: $SLOT"
732 log_info "Next steps:"
733 log_info " 1. Start streaming incremental backups:"
734 log_info " pg_scribe --start -d $DBNAME -f $FILE/incremental.sql -S $SLOT"
735 log_info " 2. Monitor replication slot health:"
736 log_info " pg_scribe --status -d $DBNAME -S $SLOT"
738 if [[ "$has_warnings" -eq 1 ]]; then
746 # --start command implementation
749 log_step "Starting incremental backup collection"
751 # Validate required arguments
752 if [[ -z "$DBNAME" ]]; then
753 log_error "--start requires -d/--dbname"
754 exit "$EXIT_VALIDATION_ERROR"
757 if [[ -z "$FILE" ]]; then
758 log_error "--start requires -f/--file (output file, or '-' for stdout)"
759 exit "$EXIT_VALIDATION_ERROR"
765 # Verify replication slot exists
766 log_step "Verifying replication slot '$SLOT'..."
768 slot_exists=$(query_db "SELECT count(*) FROM pg_replication_slots WHERE slot_name = '$SLOT';")
770 if [[ "$slot_exists" -eq 0 ]]; then
771 log_error "Replication slot '$SLOT' does not exist"
773 log_error "You must initialize the backup system first:"
774 log_error " pg_scribe --init -d $DBNAME -f <backup_dir> -S $SLOT"
776 log_error "Or verify the slot name is correct with:"
777 log_error " psql -d $DBNAME -c \"SELECT slot_name FROM pg_replication_slots;\""
778 exit "$EXIT_SLOT_ERROR"
781 log_success "Replication slot '$SLOT' found"
783 # Build pg_recvlogical arguments
784 local pg_recv_args=()
785 mapfile -t pg_recv_args < <(build_pg_recvlogical_args)
787 # Add required arguments
788 pg_recv_args+=(--slot="$SLOT")
789 pg_recv_args+=(--start)
790 pg_recv_args+=(--file="$FILE")
793 pg_recv_args+=(--option=include_transaction=on)
795 # Add status interval
796 pg_recv_args+=(--status-interval="$STATUS_INTERVAL")
798 # Add fsync interval (0 means disabled)
799 if [[ "$FSYNC_INTERVAL" -gt 0 ]]; then
800 pg_recv_args+=(--fsync-interval="$FSYNC_INTERVAL")
802 # For fsync-interval=0, we skip the parameter to avoid pg_recvlogical errors
803 log_info "Fsync disabled (fsync-interval=0)"
806 # Display configuration
807 log_step "Configuration"
808 log_info "Database: $DBNAME"
809 log_info "Replication slot: $SLOT"
810 log_info "Output file: $FILE"
811 log_info "Status interval: ${STATUS_INTERVAL}s"
812 if [[ "$FSYNC_INTERVAL" -gt 0 ]]; then
813 log_info "Fsync interval: ${FSYNC_INTERVAL}s"
815 log_info "Fsync: disabled"
819 # Start streaming with signal forwarding
820 log_step "Starting streaming replication..."
821 log_info "Press Ctrl+C to stop"
822 log_info "Send SIGHUP to rotate output file"
825 # Track child process for signal forwarding
826 local PG_RECVLOGICAL_PID=""
828 # Signal handler to forward signals to child process
829 # shellcheck disable=SC2317 # Function called via trap handler
832 if [[ -n "$PG_RECVLOGICAL_PID" ]] && kill -0 "$PG_RECVLOGICAL_PID" 2>/dev/null; then
833 log_info "Forwarding SIG$signal to pg_recvlogical (PID $PG_RECVLOGICAL_PID)"
834 kill -"$signal" "$PG_RECVLOGICAL_PID" 2>/dev/null || true
838 # Set up signal handlers
839 # shellcheck disable=SC2064 # We want the current value of the variable
840 trap "forward_signal HUP" HUP
841 trap "forward_signal TERM" TERM
842 trap "forward_signal INT" INT
844 # Execute pg_recvlogical in background so we can forward signals
845 pg_recvlogical "${pg_recv_args[@]}" &
846 PG_RECVLOGICAL_PID=$!
848 # Wait for child process
850 wait "$PG_RECVLOGICAL_PID" || exit_code=$?
852 # Clear signal handlers
856 if [[ $exit_code -eq 0 ]]; then
857 log_success "Streaming stopped cleanly"
859 elif [[ $exit_code -eq 130 ]]; then
860 # 130 = 128 + 2 (SIGINT), normal Ctrl+C
861 log_info "Interrupted by user"
863 elif [[ $exit_code -eq 143 ]]; then
864 # 143 = 128 + 15 (SIGTERM), graceful shutdown
865 log_info "Terminated by signal"
868 log_error "pg_recvlogical exited with code $exit_code"
869 exit "$EXIT_BACKUP_ERROR"
874 # --full-backup command implementation
877 log_error "--full-backup command not yet implemented"
878 exit "$EXIT_GENERAL_ERROR"
882 # --restore command implementation
885 log_error "--restore command not yet implemented"
886 exit "$EXIT_GENERAL_ERROR"
890 # --status command implementation
893 log_error "--status command not yet implemented"
894 exit "$EXIT_GENERAL_ERROR"
918 log_error "Unknown action: $ACTION"
919 exit "$EXIT_GENERAL_ERROR"
924 # Run main with all arguments