Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
74a4dc2533 | ||
|
|
be7ae2b580 |
5
.roo/commands/push.md
Normal file
5
.roo/commands/push.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
description: "Brief description of what this command does"
|
||||
---
|
||||
|
||||
Run build_and_push.sh, and supply a good git commit message.
|
||||
145
CLI_PORT_OVERRIDE_IMPLEMENTATION.md
Normal file
145
CLI_PORT_OVERRIDE_IMPLEMENTATION.md
Normal file
@@ -0,0 +1,145 @@
|
||||
# CLI Port Override Implementation
|
||||
|
||||
## Overview
|
||||
|
||||
This document describes the implementation of the `-p <port>` command line option for the C Nostr Relay, which allows overriding the default relay port during first-time startup only.
|
||||
|
||||
## Design Principles
|
||||
|
||||
1. **First-time startup only**: Command line options only affect the initial configuration event creation
|
||||
2. **Event-based persistence**: After first startup, all configuration is managed through database events
|
||||
3. **Proper encapsulation**: All configuration logic is contained within `config.c`
|
||||
4. **Extensible design**: The CLI options structure can easily accommodate future command line options
|
||||
|
||||
## Implementation Details
|
||||
|
||||
### Files Modified
|
||||
|
||||
#### `src/config.h`
|
||||
- Added `cli_options_t` structure to encapsulate command line options
|
||||
- Updated `first_time_startup_sequence()` function signature
|
||||
|
||||
#### `src/config.c`
|
||||
- Updated `first_time_startup_sequence()` to accept CLI options parameter
|
||||
- Updated `create_default_config_event()` to accept CLI options parameter
|
||||
- Implemented port override logic in DEFAULT_CONFIG_VALUES array processing
|
||||
|
||||
#### `src/default_config_event.h`
|
||||
- Updated function signature for `create_default_config_event()`
|
||||
- Added proper header include for `cli_options_t` definition
|
||||
|
||||
#### `src/main.c`
|
||||
- Added command line parsing for `-p <port>` and `--port <port>`
|
||||
- Updated help text to document the new option
|
||||
- Added proper error handling for invalid port values
|
||||
- Updated function call to pass CLI options to configuration system
|
||||
|
||||
### CLI Options Structure
|
||||
|
||||
```c
|
||||
typedef struct {
|
||||
int port_override; // -1 = not set, >0 = port value
|
||||
// Future CLI options can be added here
|
||||
} cli_options_t;
|
||||
```
|
||||
|
||||
### Command Line Usage
|
||||
|
||||
```bash
|
||||
# First-time startup with port override
|
||||
./c_relay_x86 -p 9090
|
||||
./c_relay_x86 --port 9090
|
||||
|
||||
# Show help (includes new option)
|
||||
./c_relay_x86 --help
|
||||
|
||||
# Show version
|
||||
./c_relay_x86 --version
|
||||
```
|
||||
|
||||
### Error Handling
|
||||
|
||||
The implementation includes robust error handling for:
|
||||
- Missing port argument: `./c_relay_x86 -p`
|
||||
- Invalid port format: `./c_relay_x86 -p invalid_port`
|
||||
- Out-of-range ports: `./c_relay_x86 -p 0` or `./c_relay_x86 -p 99999`
|
||||
|
||||
## Behavior Verification
|
||||
|
||||
### First-Time Startup
|
||||
When no database exists:
|
||||
1. Command line is parsed and `-p <port>` is processed
|
||||
2. CLI options are passed to `first_time_startup_sequence()`
|
||||
3. Port override is applied in `create_default_config_event()`
|
||||
4. Configuration event is created with overridden port value
|
||||
5. Relay starts on the specified port
|
||||
6. Port setting is persisted in database for future startups
|
||||
|
||||
### Subsequent Startups
|
||||
When database already exists:
|
||||
1. Command line is still parsed (for consistency)
|
||||
2. Existing relay path is taken
|
||||
3. Configuration is loaded from database events
|
||||
4. CLI options are ignored
|
||||
5. Relay starts on port from database configuration
|
||||
|
||||
## Testing Results
|
||||
|
||||
### Test 1: First-time startup with port override
|
||||
```bash
|
||||
./c_relay_x86 -p 9090
|
||||
```
|
||||
**Result**: ✅ Relay starts on port 9090, configuration stored in database
|
||||
|
||||
### Test 2: Subsequent startup ignores CLI options
|
||||
```bash
|
||||
./c_relay_x86 -p 7777
|
||||
```
|
||||
**Result**: ✅ Relay starts on port 9090 (from database), ignores `-p 7777`
|
||||
|
||||
### Test 3: Error handling
|
||||
```bash
|
||||
./c_relay_x86 -p invalid_port
|
||||
./c_relay_x86 -p
|
||||
```
|
||||
**Result**: ✅ Proper error messages and help text displayed
|
||||
|
||||
### Test 4: Help text
|
||||
```bash
|
||||
./c_relay_x86 --help
|
||||
```
|
||||
**Result**: ✅ Displays updated help with `-p, --port PORT` option
|
||||
|
||||
## Database Verification
|
||||
|
||||
The port setting is correctly stored in the database:
|
||||
```sql
|
||||
SELECT json_extract(tags, '$') FROM events WHERE kind = 33334;
|
||||
```
|
||||
Shows the overridden port value in the configuration event tags.
|
||||
|
||||
## Future Extensions
|
||||
|
||||
The `cli_options_t` structure is designed to be easily extended:
|
||||
|
||||
```c
|
||||
typedef struct {
|
||||
int port_override; // -1 = not set, >0 = port value
|
||||
char* description_override; // Future: relay description override
|
||||
int max_connections_override; // Future: connection limit override
|
||||
// Add more options as needed
|
||||
} cli_options_t;
|
||||
```
|
||||
|
||||
## Key Design Benefits
|
||||
|
||||
1. **Separation of Concerns**: Main function handles CLI parsing, config system handles application
|
||||
2. **First-time Only**: Prevents confusion about configuration precedence
|
||||
3. **Event-based Architecture**: Maintains consistency with the relay's event-based configuration system
|
||||
4. **Extensible**: Easy to add new command line options in the future
|
||||
5. **Robust**: Comprehensive error handling and validation
|
||||
6. **Documented**: Clear help text explains behavior to users
|
||||
|
||||
## Summary
|
||||
|
||||
The `-p <port>` command line option implementation successfully provides a way to override the default relay port during first-time startup while maintaining the event-based configuration architecture for ongoing operation. The implementation is robust, well-tested, and ready for production use.
|
||||
BIN
c-relay-x86_64
BIN
c-relay-x86_64
Binary file not shown.
Binary file not shown.
@@ -38,7 +38,7 @@ if [ "$HELP" = true ]; then
|
||||
echo "Event-Based Configuration:"
|
||||
echo " This relay now uses event-based configuration stored directly in the database."
|
||||
echo " On first startup, keys are automatically generated and printed once."
|
||||
echo " Database file: <relay_pubkey>.nrdb (created automatically)"
|
||||
echo " Database file: <relay_pubkey>.db (created automatically)"
|
||||
echo ""
|
||||
echo "Examples:"
|
||||
echo " $0 # Fresh start with new keys (default)"
|
||||
@@ -51,15 +51,22 @@ fi
|
||||
|
||||
# Handle database file cleanup for fresh start
|
||||
if [ "$PRESERVE_DATABASE" = false ]; then
|
||||
if ls *.nrdb >/dev/null 2>&1; then
|
||||
if ls *.db >/dev/null 2>&1 || ls build/*.db >/dev/null 2>&1; then
|
||||
echo "Removing existing database files to trigger fresh key generation..."
|
||||
rm -f *.nrdb
|
||||
rm -f *.db build/*.db
|
||||
echo "✓ Database files removed - will generate new keys and database"
|
||||
else
|
||||
echo "No existing database found - will generate fresh setup"
|
||||
fi
|
||||
else
|
||||
echo "Preserving existing database files as requested"
|
||||
# Back up database files before clean build
|
||||
if ls build/*.db >/dev/null 2>&1; then
|
||||
echo "Backing up existing database files..."
|
||||
mkdir -p /tmp/relay_backup_$$
|
||||
cp build/*.db* /tmp/relay_backup_$$/ 2>/dev/null || true
|
||||
echo "Database files backed up to temporary location"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Clean up legacy files that are no longer used
|
||||
@@ -70,6 +77,14 @@ rm -f db/c_nostr_relay.db* 2>/dev/null
|
||||
echo "Building project..."
|
||||
make clean all
|
||||
|
||||
# Restore database files if preserving
|
||||
if [ "$PRESERVE_DATABASE" = true ] && [ -d "/tmp/relay_backup_$$" ]; then
|
||||
echo "Restoring preserved database files..."
|
||||
cp /tmp/relay_backup_$$/*.db* build/ 2>/dev/null || true
|
||||
rm -rf /tmp/relay_backup_$$
|
||||
echo "Database files restored to build directory"
|
||||
fi
|
||||
|
||||
# Check if build was successful
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "ERROR: Build failed. Cannot restart relay."
|
||||
@@ -129,9 +144,13 @@ echo "Database will be initialized automatically on startup if needed"
|
||||
echo "Starting relay server..."
|
||||
echo "Debug: Current processes: $(ps aux | grep 'c_relay_' | grep -v grep || echo 'None')"
|
||||
|
||||
# Change to build directory before starting relay so database files are created there
|
||||
cd build
|
||||
# Start relay in background and capture its PID (no command line arguments needed)
|
||||
$BINARY_PATH > relay.log 2>&1 &
|
||||
./$(basename $BINARY_PATH) > ../relay.log 2>&1 &
|
||||
RELAY_PID=$!
|
||||
# Change back to original directory
|
||||
cd ..
|
||||
|
||||
echo "Started with PID: $RELAY_PID"
|
||||
|
||||
|
||||
35
relay2.log
Normal file
35
relay2.log
Normal file
@@ -0,0 +1,35 @@
|
||||
[34m[1m=== C Nostr Relay Server ===[0m
|
||||
Event-based configuration system
|
||||
|
||||
[34m[INFO][0m Existing relay detected
|
||||
[34m[INFO][0m Initializing event-based configuration system...
|
||||
[32m[SUCCESS][0m Event-based configuration system initialized
|
||||
[34m[INFO][0m Starting existing relay...
|
||||
Relay pubkey: 6df436471c7965d6473e89998162e6b87cc3547d71a2db12f559a39f4596059a
|
||||
[32m[SUCCESS][0m Existing relay startup prepared
|
||||
[32m[SUCCESS][0m Database connection established: 6df436471c7965d6473e89998162e6b87cc3547d71a2db12f559a39f4596059a.nrdb
|
||||
[34m[INFO][0m Database schema already exists, skipping initialization
|
||||
[34m[INFO][0m Existing database schema version: 4
|
||||
[33m[WARNING][0m No configuration event found in existing database
|
||||
[32m[SUCCESS][0m Relay information initialized with default values
|
||||
[34m[INFO][0m Initializing NIP-13 Proof of Work configuration
|
||||
[34m[INFO][0m PoW configured in basic validation mode (default)
|
||||
[34m[INFO][0m PoW Configuration: enabled=true, min_difficulty=0, validation_flags=0x1, mode=full
|
||||
[34m[INFO][0m Initializing NIP-40 Expiration Timestamp configuration
|
||||
[34m[INFO][0m Expiration Configuration: enabled=true, strict_mode=true, filter_responses=true, grace_period=300 seconds
|
||||
[34m[INFO][0m Subscription limits: max_per_client=25, max_total=5000
|
||||
[34m[INFO][0m Starting relay server...
|
||||
[34m[INFO][0m Starting libwebsockets-based Nostr relay server...
|
||||
[34m[INFO][0m Checking port availability: 8888
|
||||
[33m[WARNING][0m Port 8888 is in use, trying port 8889 (attempt 2/5)
|
||||
[34m[INFO][0m Checking port availability: 8889
|
||||
[34m[INFO][0m Attempting to bind libwebsockets to port 8889
|
||||
[33m[WARNING][0m WebSocket relay started on ws://127.0.0.1:8889 (configured port 8888 was unavailable)
|
||||
[32m[SUCCESS][0m WebSocket relay started on ws://127.0.0.1:8889 (configured port 8888 was unavailable)
|
||||
[34m[INFO][0m Received shutdown signal
|
||||
[34m[INFO][0m Shutting down WebSocket server...
|
||||
[32m[SUCCESS][0m WebSocket relay shut down cleanly
|
||||
[34m[INFO][0m Cleaning up configuration system...
|
||||
[32m[SUCCESS][0m Configuration system cleaned up
|
||||
[34m[INFO][0m Database connection closed
|
||||
[32m[SUCCESS][0m Server shutdown complete
|
||||
37
relay3.log
Normal file
37
relay3.log
Normal file
@@ -0,0 +1,37 @@
|
||||
[34m[1m=== C Nostr Relay Server ===[0m
|
||||
Event-based configuration system
|
||||
|
||||
[34m[INFO][0m Existing relay detected
|
||||
[34m[INFO][0m Initializing event-based configuration system...
|
||||
[32m[SUCCESS][0m Event-based configuration system initialized
|
||||
[34m[INFO][0m Starting existing relay...
|
||||
Relay pubkey: 6df436471c7965d6473e89998162e6b87cc3547d71a2db12f559a39f4596059a
|
||||
[32m[SUCCESS][0m Existing relay startup prepared
|
||||
[32m[SUCCESS][0m Database connection established: 6df436471c7965d6473e89998162e6b87cc3547d71a2db12f559a39f4596059a.nrdb
|
||||
[34m[INFO][0m Database schema already exists, skipping initialization
|
||||
[34m[INFO][0m Existing database schema version: 4
|
||||
[33m[WARNING][0m No configuration event found in existing database
|
||||
[32m[SUCCESS][0m Relay information initialized with default values
|
||||
[34m[INFO][0m Initializing NIP-13 Proof of Work configuration
|
||||
[34m[INFO][0m PoW configured in basic validation mode (default)
|
||||
[34m[INFO][0m PoW Configuration: enabled=true, min_difficulty=0, validation_flags=0x1, mode=full
|
||||
[34m[INFO][0m Initializing NIP-40 Expiration Timestamp configuration
|
||||
[34m[INFO][0m Expiration Configuration: enabled=true, strict_mode=true, filter_responses=true, grace_period=300 seconds
|
||||
[34m[INFO][0m Subscription limits: max_per_client=25, max_total=5000
|
||||
[34m[INFO][0m Starting relay server...
|
||||
[34m[INFO][0m Starting libwebsockets-based Nostr relay server...
|
||||
[34m[INFO][0m Checking port availability: 8888
|
||||
[33m[WARNING][0m Port 8888 is in use, trying port 8889 (attempt 2/5)
|
||||
[34m[INFO][0m Checking port availability: 8889
|
||||
[33m[WARNING][0m Port 8889 is in use, trying port 8890 (attempt 3/5)
|
||||
[34m[INFO][0m Checking port availability: 8890
|
||||
[34m[INFO][0m Attempting to bind libwebsockets to port 8890
|
||||
[33m[WARNING][0m WebSocket relay started on ws://127.0.0.1:8890 (configured port 8888 was unavailable)
|
||||
[32m[SUCCESS][0m WebSocket relay started on ws://127.0.0.1:8890 (configured port 8888 was unavailable)
|
||||
[34m[INFO][0m Received shutdown signal
|
||||
[34m[INFO][0m Shutting down WebSocket server...
|
||||
[32m[SUCCESS][0m WebSocket relay shut down cleanly
|
||||
[34m[INFO][0m Cleaning up configuration system...
|
||||
[32m[SUCCESS][0m Configuration system cleaned up
|
||||
[34m[INFO][0m Database connection closed
|
||||
[32m[SUCCESS][0m Server shutdown complete
|
||||
421
src/config.c
421
src/config.c
@@ -44,9 +44,9 @@ char** find_existing_nrdb_files(void) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Count .nrdb files
|
||||
// Count .db files
|
||||
while ((entry = readdir(dir)) != NULL) {
|
||||
if (strstr(entry->d_name, ".nrdb") != NULL) {
|
||||
if (strstr(entry->d_name, ".db") != NULL) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
@@ -67,7 +67,7 @@ char** find_existing_nrdb_files(void) {
|
||||
// Store filenames
|
||||
int i = 0;
|
||||
while ((entry = readdir(dir)) != NULL && i < count) {
|
||||
if (strstr(entry->d_name, ".nrdb") != NULL) {
|
||||
if (strstr(entry->d_name, ".db") != NULL) {
|
||||
files[i] = malloc(strlen(entry->d_name) + 1);
|
||||
if (files[i]) {
|
||||
strcpy(files[i], entry->d_name);
|
||||
@@ -84,8 +84,8 @@ char** find_existing_nrdb_files(void) {
|
||||
char* extract_pubkey_from_filename(const char* filename) {
|
||||
if (!filename) return NULL;
|
||||
|
||||
// Find .nrdb extension
|
||||
const char* dot = strstr(filename, ".nrdb");
|
||||
// Find .db extension
|
||||
const char* dot = strstr(filename, ".db");
|
||||
if (!dot) return NULL;
|
||||
|
||||
// Calculate pubkey length
|
||||
@@ -107,10 +107,10 @@ char* get_database_name_from_relay_pubkey(const char* relay_pubkey) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char* db_name = malloc(strlen(relay_pubkey) + 6); // +6 for ".nrdb\0"
|
||||
char* db_name = malloc(strlen(relay_pubkey) + 4); // +4 for ".db\0"
|
||||
if (!db_name) return NULL;
|
||||
|
||||
sprintf(db_name, "%s.nrdb", relay_pubkey);
|
||||
|
||||
sprintf(db_name, "%s.db", relay_pubkey);
|
||||
return db_name;
|
||||
}
|
||||
|
||||
@@ -443,7 +443,8 @@ int generate_random_private_key_bytes(unsigned char* privkey_bytes) {
|
||||
|
||||
cJSON* create_default_config_event(const unsigned char* admin_privkey_bytes,
|
||||
const char* relay_privkey_hex,
|
||||
const char* relay_pubkey_hex) {
|
||||
const char* relay_pubkey_hex,
|
||||
const cli_options_t* cli_options) {
|
||||
if (!admin_privkey_bytes || !relay_privkey_hex || !relay_pubkey_hex) {
|
||||
log_error("Invalid parameters for creating default config event");
|
||||
return NULL;
|
||||
@@ -475,11 +476,28 @@ cJSON* create_default_config_event(const unsigned char* admin_privkey_bytes,
|
||||
cJSON_AddItemToArray(relay_privkey_tag, cJSON_CreateString(relay_privkey_hex));
|
||||
cJSON_AddItemToArray(tags, relay_privkey_tag);
|
||||
|
||||
// Add all default configuration values
|
||||
// Add all default configuration values with command line overrides
|
||||
for (size_t i = 0; i < DEFAULT_CONFIG_COUNT; i++) {
|
||||
cJSON* tag = cJSON_CreateArray();
|
||||
cJSON_AddItemToArray(tag, cJSON_CreateString(DEFAULT_CONFIG_VALUES[i].key));
|
||||
cJSON_AddItemToArray(tag, cJSON_CreateString(DEFAULT_CONFIG_VALUES[i].value));
|
||||
|
||||
// Check for command line overrides
|
||||
const char* value = DEFAULT_CONFIG_VALUES[i].value;
|
||||
if (cli_options) {
|
||||
// Override relay_port if specified on command line
|
||||
if (cli_options->port_override > 0 && strcmp(DEFAULT_CONFIG_VALUES[i].key, "relay_port") == 0) {
|
||||
char port_str[16];
|
||||
snprintf(port_str, sizeof(port_str), "%d", cli_options->port_override);
|
||||
cJSON_AddItemToArray(tag, cJSON_CreateString(port_str));
|
||||
log_info("Using command line port override in configuration event");
|
||||
printf(" Port: %d (overriding default %s)\n", cli_options->port_override, DEFAULT_CONFIG_VALUES[i].value);
|
||||
} else {
|
||||
cJSON_AddItemToArray(tag, cJSON_CreateString(value));
|
||||
}
|
||||
} else {
|
||||
cJSON_AddItemToArray(tag, cJSON_CreateString(value));
|
||||
}
|
||||
|
||||
cJSON_AddItemToArray(tags, tag);
|
||||
}
|
||||
|
||||
@@ -516,7 +534,7 @@ cJSON* create_default_config_event(const unsigned char* admin_privkey_bytes,
|
||||
// IMPLEMENTED FUNCTIONS
|
||||
// ================================
|
||||
|
||||
int first_time_startup_sequence(void) {
|
||||
int first_time_startup_sequence(const cli_options_t* cli_options) {
|
||||
log_info("Starting first-time startup sequence...");
|
||||
|
||||
// 1. Generate admin keypair using /dev/urandom + nostr_core_lib
|
||||
@@ -566,7 +584,7 @@ int first_time_startup_sequence(void) {
|
||||
}
|
||||
|
||||
// 5. Create initial configuration event using defaults
|
||||
cJSON* config_event = create_default_config_event(admin_privkey_bytes, relay_privkey, relay_pubkey);
|
||||
cJSON* config_event = create_default_config_event(admin_privkey_bytes, relay_privkey, relay_pubkey, cli_options);
|
||||
if (!config_event) {
|
||||
log_error("Failed to create default configuration event");
|
||||
return -1;
|
||||
@@ -642,6 +660,373 @@ int startup_existing_relay(const char* relay_pubkey) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ================================
|
||||
// CONFIGURATION FIELD VALIDATION
|
||||
// ================================
|
||||
|
||||
// Validation helper functions
|
||||
static int is_valid_port(const char* port_str) {
|
||||
if (!port_str) return 0;
|
||||
|
||||
char* endptr;
|
||||
long port = strtol(port_str, &endptr, 10);
|
||||
|
||||
// Must be valid number and in valid port range
|
||||
return (endptr != port_str && *endptr == '\0' && port >= 1 && port <= 65535);
|
||||
}
|
||||
|
||||
static int is_valid_boolean(const char* bool_str) {
|
||||
if (!bool_str) return 0;
|
||||
|
||||
return (strcasecmp(bool_str, "true") == 0 ||
|
||||
strcasecmp(bool_str, "false") == 0 ||
|
||||
strcasecmp(bool_str, "yes") == 0 ||
|
||||
strcasecmp(bool_str, "no") == 0 ||
|
||||
strcasecmp(bool_str, "1") == 0 ||
|
||||
strcasecmp(bool_str, "0") == 0);
|
||||
}
|
||||
|
||||
static int is_valid_positive_integer(const char* int_str) {
|
||||
if (!int_str) return 0;
|
||||
|
||||
char* endptr;
|
||||
long val = strtol(int_str, &endptr, 10);
|
||||
|
||||
return (endptr != int_str && *endptr == '\0' && val >= 0);
|
||||
}
|
||||
|
||||
static int is_valid_non_negative_integer(const char* int_str) {
|
||||
if (!int_str) return 0;
|
||||
|
||||
char* endptr;
|
||||
long val = strtol(int_str, &endptr, 10);
|
||||
|
||||
return (endptr != int_str && *endptr == '\0' && val >= 0);
|
||||
}
|
||||
|
||||
static int is_valid_string_length(const char* str, size_t max_length) {
|
||||
if (!str) return 1; // NULL strings are valid (use defaults)
|
||||
return strlen(str) <= max_length;
|
||||
}
|
||||
|
||||
static int is_valid_pow_mode(const char* mode_str) {
|
||||
if (!mode_str) return 0;
|
||||
|
||||
return (strcasecmp(mode_str, "basic") == 0 ||
|
||||
strcasecmp(mode_str, "strict") == 0 ||
|
||||
strcasecmp(mode_str, "disabled") == 0);
|
||||
}
|
||||
|
||||
static int is_valid_hex_key(const char* key_str) {
|
||||
if (!key_str) return 0;
|
||||
|
||||
// Must be exactly 64 hex characters
|
||||
if (strlen(key_str) != 64) return 0;
|
||||
|
||||
// Must contain only hex characters
|
||||
for (int i = 0; i < 64; i++) {
|
||||
char c = key_str[i];
|
||||
if (!((c >= '0' && c <= '9') ||
|
||||
(c >= 'a' && c <= 'f') ||
|
||||
(c >= 'A' && c <= 'F'))) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Main validation function for configuration fields
|
||||
static int validate_config_field(const char* key, const char* value, char* error_msg, size_t error_size) {
|
||||
if (!key || !value) {
|
||||
snprintf(error_msg, error_size, "key or value is NULL");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Port validation
|
||||
if (strcmp(key, "relay_port") == 0) {
|
||||
if (!is_valid_port(value)) {
|
||||
snprintf(error_msg, error_size, "invalid port number '%s' (must be 1-65535)", value);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Connection limits
|
||||
if (strcmp(key, "max_connections") == 0) {
|
||||
if (!is_valid_positive_integer(value)) {
|
||||
snprintf(error_msg, error_size, "invalid max_connections '%s' (must be positive integer)", value);
|
||||
return -1;
|
||||
}
|
||||
int val = atoi(value);
|
||||
if (val < 1 || val > 10000) {
|
||||
snprintf(error_msg, error_size, "max_connections '%s' out of range (1-10000)", value);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Boolean fields
|
||||
if (strcmp(key, "auth_enabled") == 0 ||
|
||||
strcmp(key, "nip40_expiration_enabled") == 0 ||
|
||||
strcmp(key, "nip40_expiration_strict") == 0 ||
|
||||
strcmp(key, "nip40_expiration_filter") == 0) {
|
||||
if (!is_valid_boolean(value)) {
|
||||
snprintf(error_msg, error_size, "invalid boolean value '%s' for %s", value, key);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// String length validation
|
||||
if (strcmp(key, "relay_description") == 0) {
|
||||
if (!is_valid_string_length(value, RELAY_DESCRIPTION_MAX_LENGTH)) {
|
||||
snprintf(error_msg, error_size, "relay_description too long (max %d characters)", RELAY_DESCRIPTION_MAX_LENGTH);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strcmp(key, "relay_contact") == 0) {
|
||||
if (!is_valid_string_length(value, RELAY_CONTACT_MAX_LENGTH)) {
|
||||
snprintf(error_msg, error_size, "relay_contact too long (max %d characters)", RELAY_CONTACT_MAX_LENGTH);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strcmp(key, "relay_software") == 0 ||
|
||||
strcmp(key, "relay_version") == 0) {
|
||||
if (!is_valid_string_length(value, 256)) {
|
||||
snprintf(error_msg, error_size, "%s too long (max 256 characters)", key);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// PoW difficulty validation
|
||||
if (strcmp(key, "pow_min_difficulty") == 0) {
|
||||
if (!is_valid_non_negative_integer(value)) {
|
||||
snprintf(error_msg, error_size, "invalid pow_min_difficulty '%s' (must be non-negative integer)", value);
|
||||
return -1;
|
||||
}
|
||||
int val = atoi(value);
|
||||
if (val > 32) { // 32 is practically impossible
|
||||
snprintf(error_msg, error_size, "pow_min_difficulty '%s' too high (max 32)", value);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// PoW mode validation
|
||||
if (strcmp(key, "pow_mode") == 0) {
|
||||
if (!is_valid_pow_mode(value)) {
|
||||
snprintf(error_msg, error_size, "invalid pow_mode '%s' (must be basic, strict, or disabled)", value);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Time-based validation
|
||||
if (strcmp(key, "nip40_expiration_grace_period") == 0) {
|
||||
if (!is_valid_non_negative_integer(value)) {
|
||||
snprintf(error_msg, error_size, "invalid grace period '%s' (must be non-negative integer)", value);
|
||||
return -1;
|
||||
}
|
||||
int val = atoi(value);
|
||||
if (val > 86400) { // Max 1 day
|
||||
snprintf(error_msg, error_size, "grace period '%s' too long (max 86400 seconds)", value);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Subscription limits
|
||||
if (strcmp(key, "max_subscriptions_per_client") == 0) {
|
||||
if (!is_valid_positive_integer(value)) {
|
||||
snprintf(error_msg, error_size, "invalid max_subscriptions_per_client '%s'", value);
|
||||
return -1;
|
||||
}
|
||||
int val = atoi(value);
|
||||
if (val < 1 || val > 1000) {
|
||||
snprintf(error_msg, error_size, "max_subscriptions_per_client '%s' out of range (1-1000)", value);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strcmp(key, "max_total_subscriptions") == 0) {
|
||||
if (!is_valid_positive_integer(value)) {
|
||||
snprintf(error_msg, error_size, "invalid max_total_subscriptions '%s'", value);
|
||||
return -1;
|
||||
}
|
||||
int val = atoi(value);
|
||||
if (val < 1 || val > 100000) {
|
||||
snprintf(error_msg, error_size, "max_total_subscriptions '%s' out of range (1-100000)", value);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strcmp(key, "max_filters_per_subscription") == 0) {
|
||||
if (!is_valid_positive_integer(value)) {
|
||||
snprintf(error_msg, error_size, "invalid max_filters_per_subscription '%s'", value);
|
||||
return -1;
|
||||
}
|
||||
int val = atoi(value);
|
||||
if (val < 1 || val > 100) {
|
||||
snprintf(error_msg, error_size, "max_filters_per_subscription '%s' out of range (1-100)", value);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Event limits
|
||||
if (strcmp(key, "max_event_tags") == 0) {
|
||||
if (!is_valid_positive_integer(value)) {
|
||||
snprintf(error_msg, error_size, "invalid max_event_tags '%s'", value);
|
||||
return -1;
|
||||
}
|
||||
int val = atoi(value);
|
||||
if (val < 1 || val > 1000) {
|
||||
snprintf(error_msg, error_size, "max_event_tags '%s' out of range (1-1000)", value);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strcmp(key, "max_content_length") == 0) {
|
||||
if (!is_valid_positive_integer(value)) {
|
||||
snprintf(error_msg, error_size, "invalid max_content_length '%s'", value);
|
||||
return -1;
|
||||
}
|
||||
int val = atoi(value);
|
||||
if (val < 100 || val > 1048576) { // 1MB max
|
||||
snprintf(error_msg, error_size, "max_content_length '%s' out of range (100-1048576)", value);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strcmp(key, "max_message_length") == 0) {
|
||||
if (!is_valid_positive_integer(value)) {
|
||||
snprintf(error_msg, error_size, "invalid max_message_length '%s'", value);
|
||||
return -1;
|
||||
}
|
||||
int val = atoi(value);
|
||||
if (val < 1024 || val > 1048576) { // 1KB to 1MB
|
||||
snprintf(error_msg, error_size, "max_message_length '%s' out of range (1024-1048576)", value);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Performance limits
|
||||
if (strcmp(key, "default_limit") == 0 || strcmp(key, "max_limit") == 0) {
|
||||
if (!is_valid_positive_integer(value)) {
|
||||
snprintf(error_msg, error_size, "invalid %s '%s'", key, value);
|
||||
return -1;
|
||||
}
|
||||
int val = atoi(value);
|
||||
if (val < 1 || val > 50000) {
|
||||
snprintf(error_msg, error_size, "%s '%s' out of range (1-50000)", key, value);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Key validation for relay keys
|
||||
if (strcmp(key, "relay_pubkey") == 0 || strcmp(key, "relay_privkey") == 0) {
|
||||
if (!is_valid_hex_key(value)) {
|
||||
snprintf(error_msg, error_size, "invalid %s format (must be 64-character hex)", key);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Special validation for d tag (relay identifier)
|
||||
if (strcmp(key, "d") == 0) {
|
||||
if (!is_valid_hex_key(value)) {
|
||||
snprintf(error_msg, error_size, "invalid relay identifier 'd' format (must be 64-character hex)");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Unknown field - log warning but allow
|
||||
log_warning("Unknown configuration field");
|
||||
printf(" Field: %s = %s\n", key, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Validate all fields in a configuration event
|
||||
static int validate_configuration_event_fields(const cJSON* event, char* error_msg, size_t error_size) {
|
||||
if (!event) {
|
||||
snprintf(error_msg, error_size, "null configuration event");
|
||||
return -1;
|
||||
}
|
||||
|
||||
log_info("Validating configuration event fields...");
|
||||
|
||||
cJSON* tags = cJSON_GetObjectItem(event, "tags");
|
||||
if (!tags || !cJSON_IsArray(tags)) {
|
||||
snprintf(error_msg, error_size, "missing or invalid tags array");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int validated_fields = 0;
|
||||
int validation_errors = 0;
|
||||
char field_error[512];
|
||||
|
||||
cJSON* tag = NULL;
|
||||
cJSON_ArrayForEach(tag, tags) {
|
||||
if (cJSON_IsArray(tag) && cJSON_GetArraySize(tag) >= 2) {
|
||||
cJSON* tag_key = cJSON_GetArrayItem(tag, 0);
|
||||
cJSON* tag_value = cJSON_GetArrayItem(tag, 1);
|
||||
|
||||
if (tag_key && tag_value &&
|
||||
cJSON_IsString(tag_key) && cJSON_IsString(tag_value)) {
|
||||
|
||||
const char* key = cJSON_GetStringValue(tag_key);
|
||||
const char* value = cJSON_GetStringValue(tag_value);
|
||||
|
||||
if (validate_config_field(key, value, field_error, sizeof(field_error)) != 0) {
|
||||
// Safely truncate the error message if needed
|
||||
size_t prefix_len = strlen("field validation failed: ");
|
||||
size_t available_space = error_size > prefix_len ? error_size - prefix_len - 1 : 0;
|
||||
|
||||
if (available_space > 0) {
|
||||
snprintf(error_msg, error_size, "field validation failed: %.*s",
|
||||
(int)available_space, field_error);
|
||||
} else {
|
||||
strncpy(error_msg, "field validation failed", error_size - 1);
|
||||
error_msg[error_size - 1] = '\0';
|
||||
}
|
||||
|
||||
log_error("Configuration field validation failed");
|
||||
printf(" Field: %s = %s\n", key, value);
|
||||
printf(" Error: %s\n", field_error);
|
||||
validation_errors++;
|
||||
return -1; // Stop on first error
|
||||
} else {
|
||||
validated_fields++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (validation_errors > 0) {
|
||||
char summary[256];
|
||||
snprintf(summary, sizeof(summary), "%d configuration fields failed validation", validation_errors);
|
||||
log_error(summary);
|
||||
return -1;
|
||||
}
|
||||
|
||||
char success_msg[256];
|
||||
snprintf(success_msg, sizeof(success_msg), "%d configuration fields validated successfully", validated_fields);
|
||||
log_success(success_msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int process_configuration_event(const cJSON* event) {
|
||||
if (!event) {
|
||||
log_error("Invalid configuration event");
|
||||
@@ -690,6 +1075,14 @@ int process_configuration_event(const cJSON* event) {
|
||||
|
||||
log_success("Configuration event structure and signature validated successfully");
|
||||
|
||||
// NEW: Validate configuration field values
|
||||
char validation_error[512];
|
||||
if (validate_configuration_event_fields(event, validation_error, sizeof(validation_error)) != 0) {
|
||||
log_error("Configuration field validation failed");
|
||||
printf(" Validation error: %s\n", validation_error);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Store in database
|
||||
if (store_config_event_in_database(event) != 0) {
|
||||
log_error("Failed to store configuration event");
|
||||
@@ -702,7 +1095,7 @@ int process_configuration_event(const cJSON* event) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
log_success("Configuration event processed successfully");
|
||||
log_success("Configuration event processed successfully with field validation");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,12 @@ typedef struct {
|
||||
char config_file_path[512]; // Temporary for compatibility
|
||||
} config_manager_t;
|
||||
|
||||
// Command line options structure for first-time startup
|
||||
typedef struct {
|
||||
int port_override; // -1 = not set, >0 = port value
|
||||
// Future CLI options can be added here
|
||||
} cli_options_t;
|
||||
|
||||
// Global configuration manager
|
||||
extern config_manager_t g_config_manager;
|
||||
|
||||
@@ -62,7 +68,7 @@ int get_config_bool(const char* key, int default_value);
|
||||
|
||||
// First-time startup functions
|
||||
int is_first_time_startup(void);
|
||||
int first_time_startup_sequence(void);
|
||||
int first_time_startup_sequence(const cli_options_t* cli_options);
|
||||
int startup_existing_relay(const char* relay_pubkey);
|
||||
|
||||
// Configuration application functions
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define DEFAULT_CONFIG_EVENT_H
|
||||
|
||||
#include <cjson/cJSON.h>
|
||||
#include "config.h" // For cli_options_t definition
|
||||
|
||||
/*
|
||||
* Default Configuration Event Template
|
||||
@@ -61,8 +62,9 @@ static const struct {
|
||||
#define DEFAULT_CONFIG_COUNT (sizeof(DEFAULT_CONFIG_VALUES) / sizeof(DEFAULT_CONFIG_VALUES[0]))
|
||||
|
||||
// Function to create default configuration event
|
||||
cJSON* create_default_config_event(const unsigned char* admin_privkey_bytes,
|
||||
cJSON* create_default_config_event(const unsigned char* admin_privkey_bytes,
|
||||
const char* relay_privkey_hex,
|
||||
const char* relay_pubkey_hex);
|
||||
const char* relay_pubkey_hex,
|
||||
const cli_options_t* cli_options);
|
||||
|
||||
#endif /* DEFAULT_CONFIG_EVENT_H */
|
||||
154
src/main.c
154
src/main.c
@@ -10,6 +10,10 @@
|
||||
#include <pthread.h>
|
||||
#include <sqlite3.h>
|
||||
#include <libwebsockets.h>
|
||||
#include <errno.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
// Include nostr_core_lib for Nostr functionality
|
||||
#include "../nostr_core_lib/cjson/cJSON.h"
|
||||
@@ -2956,6 +2960,34 @@ static struct lws_protocols protocols[] = {
|
||||
{ NULL, NULL, 0, 0, 0, NULL, 0 } // terminator
|
||||
};
|
||||
|
||||
// Check if a port is available for binding
|
||||
int check_port_available(int port) {
|
||||
int sockfd;
|
||||
struct sockaddr_in addr;
|
||||
int result;
|
||||
|
||||
// Create a socket
|
||||
sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sockfd < 0) {
|
||||
return 0; // Cannot create socket, assume port unavailable
|
||||
}
|
||||
|
||||
// Set up the address structure
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = INADDR_ANY;
|
||||
addr.sin_port = htons(port);
|
||||
|
||||
// Try to bind to the port
|
||||
result = bind(sockfd, (struct sockaddr*)&addr, sizeof(addr));
|
||||
|
||||
// Close the socket
|
||||
close(sockfd);
|
||||
|
||||
// Return 1 if bind succeeded (port available), 0 if failed (port in use)
|
||||
return (result == 0) ? 1 : 0;
|
||||
}
|
||||
|
||||
// Start libwebsockets-based WebSocket Nostr relay server
|
||||
int start_websocket_relay(int port_override) {
|
||||
struct lws_context_creation_info info;
|
||||
@@ -2964,12 +2996,15 @@ int start_websocket_relay(int port_override) {
|
||||
|
||||
memset(&info, 0, sizeof(info));
|
||||
// Use port override if provided, otherwise use configuration
|
||||
info.port = (port_override > 0) ? port_override : get_config_int("relay_port", DEFAULT_PORT);
|
||||
int configured_port = (port_override > 0) ? port_override : get_config_int("relay_port", DEFAULT_PORT);
|
||||
int actual_port = configured_port;
|
||||
int port_attempts = 0;
|
||||
const int max_port_attempts = 5;
|
||||
|
||||
// Minimal libwebsockets configuration
|
||||
info.protocols = protocols;
|
||||
info.gid = -1;
|
||||
info.uid = -1;
|
||||
|
||||
// Minimal libwebsockets configuration
|
||||
info.options = LWS_SERVER_OPTION_VALIDATE_UTF8;
|
||||
|
||||
// Remove interface restrictions - let system choose
|
||||
@@ -2983,15 +3018,82 @@ int start_websocket_relay(int port_override) {
|
||||
// Max payload size for Nostr events
|
||||
info.max_http_header_data = 4096;
|
||||
|
||||
ws_context = lws_create_context(&info);
|
||||
// Find an available port with pre-checking
|
||||
while (port_attempts < max_port_attempts) {
|
||||
char attempt_msg[256];
|
||||
snprintf(attempt_msg, sizeof(attempt_msg), "Checking port availability: %d", actual_port);
|
||||
log_info(attempt_msg);
|
||||
|
||||
// Pre-check if port is available
|
||||
if (!check_port_available(actual_port)) {
|
||||
port_attempts++;
|
||||
if (port_attempts < max_port_attempts) {
|
||||
char retry_msg[256];
|
||||
snprintf(retry_msg, sizeof(retry_msg), "Port %d is in use, trying port %d (attempt %d/%d)",
|
||||
actual_port, actual_port + 1, port_attempts + 1, max_port_attempts);
|
||||
log_warning(retry_msg);
|
||||
actual_port++;
|
||||
continue;
|
||||
} else {
|
||||
char error_msg[512];
|
||||
snprintf(error_msg, sizeof(error_msg),
|
||||
"Failed to find available port after %d attempts (tried ports %d-%d)",
|
||||
max_port_attempts, configured_port, actual_port);
|
||||
log_error(error_msg);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Port appears available, try creating libwebsockets context
|
||||
info.port = actual_port;
|
||||
|
||||
char binding_msg[256];
|
||||
snprintf(binding_msg, sizeof(binding_msg), "Attempting to bind libwebsockets to port %d", actual_port);
|
||||
log_info(binding_msg);
|
||||
|
||||
ws_context = lws_create_context(&info);
|
||||
if (ws_context) {
|
||||
// Success! Port binding worked
|
||||
break;
|
||||
}
|
||||
|
||||
// libwebsockets failed even though port check passed
|
||||
// This could be due to timing or different socket options
|
||||
int errno_saved = errno;
|
||||
char lws_error_msg[256];
|
||||
snprintf(lws_error_msg, sizeof(lws_error_msg),
|
||||
"libwebsockets failed to bind to port %d (errno: %d)", actual_port, errno_saved);
|
||||
log_warning(lws_error_msg);
|
||||
|
||||
port_attempts++;
|
||||
if (port_attempts < max_port_attempts) {
|
||||
actual_port++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we get here, we've exhausted attempts
|
||||
break;
|
||||
}
|
||||
|
||||
if (!ws_context) {
|
||||
log_error("Failed to create libwebsockets context");
|
||||
char error_msg[512];
|
||||
snprintf(error_msg, sizeof(error_msg),
|
||||
"Failed to create libwebsockets context after %d attempts. Last attempted port: %d",
|
||||
port_attempts, actual_port);
|
||||
log_error(error_msg);
|
||||
perror("libwebsockets creation error");
|
||||
return -1;
|
||||
}
|
||||
|
||||
char startup_msg[256];
|
||||
snprintf(startup_msg, sizeof(startup_msg), "WebSocket relay started on ws://127.0.0.1:%d", info.port);
|
||||
if (actual_port != configured_port) {
|
||||
snprintf(startup_msg, sizeof(startup_msg),
|
||||
"WebSocket relay started on ws://127.0.0.1:%d (configured port %d was unavailable)",
|
||||
actual_port, configured_port);
|
||||
log_warning(startup_msg);
|
||||
} else {
|
||||
snprintf(startup_msg, sizeof(startup_msg), "WebSocket relay started on ws://127.0.0.1:%d", actual_port);
|
||||
}
|
||||
log_success(startup_msg);
|
||||
|
||||
// Main event loop with proper signal handling
|
||||
@@ -3030,14 +3132,19 @@ void print_usage(const char* program_name) {
|
||||
printf("Options:\n");
|
||||
printf(" -h, --help Show this help message\n");
|
||||
printf(" -v, --version Show version information\n");
|
||||
printf(" -p, --port PORT Override relay port (first-time startup only)\n");
|
||||
printf("\n");
|
||||
printf("Configuration:\n");
|
||||
printf(" This relay uses event-based configuration stored in the database.\n");
|
||||
printf(" On first startup, keys are automatically generated and printed once.\n");
|
||||
printf(" Database file: <relay_pubkey>.nrdb (created automatically)\n");
|
||||
printf(" Command line options like --port only apply during first-time setup.\n");
|
||||
printf(" After initial setup, all configuration is managed via database events.\n");
|
||||
printf(" Database file: <relay_pubkey>.db (created automatically)\n");
|
||||
printf("\n");
|
||||
printf("Examples:\n");
|
||||
printf(" %s # Start relay (auto-configure on first run)\n", program_name);
|
||||
printf(" %s -p 8080 # First-time setup with port 8080\n", program_name);
|
||||
printf(" %s --port 9000 # First-time setup with port 9000\n", program_name);
|
||||
printf(" %s --help # Show this help\n", program_name);
|
||||
printf(" %s --version # Show version info\n", program_name);
|
||||
printf("\n");
|
||||
@@ -3052,7 +3159,12 @@ void print_version() {
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
// Parse minimal command line arguments (no configuration overrides)
|
||||
// Initialize CLI options structure
|
||||
cli_options_t cli_options = {
|
||||
.port_override = -1 // -1 = not set
|
||||
};
|
||||
|
||||
// Parse command line arguments
|
||||
for (int i = 1; i < argc; i++) {
|
||||
if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
|
||||
print_usage(argv[0]);
|
||||
@@ -3060,6 +3172,30 @@ int main(int argc, char* argv[]) {
|
||||
} else if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--version") == 0) {
|
||||
print_version();
|
||||
return 0;
|
||||
} else if (strcmp(argv[i], "-p") == 0 || strcmp(argv[i], "--port") == 0) {
|
||||
// Port override option
|
||||
if (i + 1 >= argc) {
|
||||
log_error("Port option requires a value. Use --help for usage information.");
|
||||
print_usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Parse port number
|
||||
char* endptr;
|
||||
long port = strtol(argv[i + 1], &endptr, 10);
|
||||
|
||||
if (endptr == argv[i + 1] || *endptr != '\0' || port < 1 || port > 65535) {
|
||||
log_error("Invalid port number. Port must be between 1 and 65535.");
|
||||
print_usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
cli_options.port_override = (int)port;
|
||||
i++; // Skip the port argument
|
||||
|
||||
char port_msg[128];
|
||||
snprintf(port_msg, sizeof(port_msg), "Port override specified: %d", cli_options.port_override);
|
||||
log_info(port_msg);
|
||||
} else {
|
||||
log_error("Unknown argument. Use --help for usage information.");
|
||||
print_usage(argv[0]);
|
||||
@@ -3092,7 +3228,7 @@ int main(int argc, char* argv[]) {
|
||||
}
|
||||
|
||||
// Run first-time startup sequence (generates keys, creates database, etc.)
|
||||
if (first_time_startup_sequence() != 0) {
|
||||
if (first_time_startup_sequence(&cli_options) != 0) {
|
||||
log_error("Failed to complete first-time startup sequence");
|
||||
cleanup_configuration_system();
|
||||
nostr_cleanup();
|
||||
|
||||
47
test_port_increment.log
Normal file
47
test_port_increment.log
Normal file
@@ -0,0 +1,47 @@
|
||||
[34m[1m=== C Nostr Relay Server ===[0m
|
||||
Event-based configuration system
|
||||
|
||||
[34m[INFO][0m Existing relay detected
|
||||
[34m[INFO][0m Initializing event-based configuration system...
|
||||
[32m[SUCCESS][0m Event-based configuration system initialized
|
||||
[34m[INFO][0m Starting existing relay...
|
||||
Relay pubkey: 6df436471c7965d6473e89998162e6b87cc3547d71a2db12f559a39f4596059a
|
||||
[32m[SUCCESS][0m Existing relay startup prepared
|
||||
[32m[SUCCESS][0m Database connection established: 6df436471c7965d6473e89998162e6b87cc3547d71a2db12f559a39f4596059a.nrdb
|
||||
[34m[INFO][0m Database schema already exists, skipping initialization
|
||||
[34m[INFO][0m Existing database schema version: 4
|
||||
[34m[INFO][0m Applying configuration from event...
|
||||
[34m[INFO][0m Checking for runtime configuration changes...
|
||||
[34m[INFO][0m Subscription limits changed - updating subscription manager
|
||||
[34m[INFO][0m Subscription limits: max_per_client=25, max_total=5000
|
||||
[34m[INFO][0m PoW configuration changed - reinitializing PoW system
|
||||
[34m[INFO][0m Initializing NIP-13 Proof of Work configuration
|
||||
[34m[INFO][0m PoW configured in basic validation mode
|
||||
[34m[INFO][0m PoW Configuration: enabled=true, min_difficulty=0, validation_flags=0x1, mode=full
|
||||
[34m[INFO][0m Expiration configuration changed - reinitializing expiration system
|
||||
[34m[INFO][0m Initializing NIP-40 Expiration Timestamp configuration
|
||||
[34m[INFO][0m Expiration Configuration: enabled=true, strict_mode=true, filter_responses=true, grace_period=300 seconds
|
||||
[34m[INFO][0m Relay information changed - reinitializing relay info
|
||||
[32m[SUCCESS][0m Relay information initialized with default values
|
||||
[32m[SUCCESS][0m Configuration updated via kind 33334 event - 4 system components reinitialized
|
||||
[32m[SUCCESS][0m Configuration applied from event (4 handlers executed)
|
||||
[32m[SUCCESS][0m Configuration loaded from database
|
||||
[32m[SUCCESS][0m Relay information initialized with default values
|
||||
[34m[INFO][0m Initializing NIP-13 Proof of Work configuration
|
||||
[34m[INFO][0m PoW configured in basic validation mode
|
||||
[34m[INFO][0m PoW Configuration: enabled=true, min_difficulty=0, validation_flags=0x1, mode=full
|
||||
[34m[INFO][0m Initializing NIP-40 Expiration Timestamp configuration
|
||||
[34m[INFO][0m Expiration Configuration: enabled=true, strict_mode=true, filter_responses=true, grace_period=300 seconds
|
||||
[34m[INFO][0m Subscription limits: max_per_client=25, max_total=5000
|
||||
[34m[INFO][0m Starting relay server...
|
||||
[34m[INFO][0m Starting libwebsockets-based Nostr relay server...
|
||||
[34m[INFO][0m Attempting to bind to port 8888
|
||||
[2025/09/06 20:34:16:8170] E: ERROR on binding fd 8 to port 8888 (-1 98)
|
||||
[2025/09/06 20:34:16:8172] E: init server failed
|
||||
[2025/09/06 20:34:16:8172] E: Failed to create default vhost
|
||||
[31m[ERROR][0m Failed to create libwebsockets context after 1 attempts. Last attempted port: 8888
|
||||
libwebsockets creation error: Inappropriate ioctl for device
|
||||
[34m[INFO][0m Cleaning up configuration system...
|
||||
[32m[SUCCESS][0m Configuration system cleaned up
|
||||
[34m[INFO][0m Database connection closed
|
||||
[31m[ERROR][0m Server shutdown with errors
|
||||
Reference in New Issue
Block a user