v0.3.2 - Implement -p/--port CLI option for first-time startup port override
- Add cli_options_t structure for extensible command line options - Implement port override in create_default_config_event() - Update main() with robust CLI parsing and validation - Add comprehensive help text documenting first-time only behavior - Ensure CLI options only affect initial configuration event creation - Maintain event-based configuration architecture for ongoing operation - Include comprehensive error handling and input validation - Add documentation in CLI_PORT_OVERRIDE_IMPLEMENTATION.md Tested: First-time startup uses CLI port, subsequent startups use database config
This commit is contained in:
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.
|
||||||
Binary file not shown.
28
src/config.c
28
src/config.c
@@ -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,
|
cJSON* create_default_config_event(const unsigned char* admin_privkey_bytes,
|
||||||
const char* relay_privkey_hex,
|
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) {
|
if (!admin_privkey_bytes || !relay_privkey_hex || !relay_pubkey_hex) {
|
||||||
log_error("Invalid parameters for creating default config event");
|
log_error("Invalid parameters for creating default config event");
|
||||||
return NULL;
|
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(relay_privkey_tag, cJSON_CreateString(relay_privkey_hex));
|
||||||
cJSON_AddItemToArray(tags, relay_privkey_tag);
|
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++) {
|
for (size_t i = 0; i < DEFAULT_CONFIG_COUNT; i++) {
|
||||||
cJSON* tag = cJSON_CreateArray();
|
cJSON* tag = cJSON_CreateArray();
|
||||||
cJSON_AddItemToArray(tag, cJSON_CreateString(DEFAULT_CONFIG_VALUES[i].key));
|
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);
|
cJSON_AddItemToArray(tags, tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -516,7 +534,7 @@ cJSON* create_default_config_event(const unsigned char* admin_privkey_bytes,
|
|||||||
// IMPLEMENTED FUNCTIONS
|
// 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...");
|
log_info("Starting first-time startup sequence...");
|
||||||
|
|
||||||
// 1. Generate admin keypair using /dev/urandom + nostr_core_lib
|
// 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
|
// 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) {
|
if (!config_event) {
|
||||||
log_error("Failed to create default configuration event");
|
log_error("Failed to create default configuration event");
|
||||||
return -1;
|
return -1;
|
||||||
|
|||||||
@@ -32,6 +32,12 @@ typedef struct {
|
|||||||
char config_file_path[512]; // Temporary for compatibility
|
char config_file_path[512]; // Temporary for compatibility
|
||||||
} config_manager_t;
|
} 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
|
// Global configuration manager
|
||||||
extern config_manager_t g_config_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
|
// First-time startup functions
|
||||||
int is_first_time_startup(void);
|
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);
|
int startup_existing_relay(const char* relay_pubkey);
|
||||||
|
|
||||||
// Configuration application functions
|
// Configuration application functions
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#define DEFAULT_CONFIG_EVENT_H
|
#define DEFAULT_CONFIG_EVENT_H
|
||||||
|
|
||||||
#include <cjson/cJSON.h>
|
#include <cjson/cJSON.h>
|
||||||
|
#include "config.h" // For cli_options_t definition
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Default Configuration Event Template
|
* Default Configuration Event Template
|
||||||
@@ -63,6 +64,7 @@ static const struct {
|
|||||||
// Function to create default configuration event
|
// 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_privkey_hex,
|
||||||
const char* relay_pubkey_hex);
|
const char* relay_pubkey_hex,
|
||||||
|
const cli_options_t* cli_options);
|
||||||
|
|
||||||
#endif /* DEFAULT_CONFIG_EVENT_H */
|
#endif /* DEFAULT_CONFIG_EVENT_H */
|
||||||
38
src/main.c
38
src/main.c
@@ -3132,14 +3132,19 @@ void print_usage(const char* program_name) {
|
|||||||
printf("Options:\n");
|
printf("Options:\n");
|
||||||
printf(" -h, --help Show this help message\n");
|
printf(" -h, --help Show this help message\n");
|
||||||
printf(" -v, --version Show version information\n");
|
printf(" -v, --version Show version information\n");
|
||||||
|
printf(" -p, --port PORT Override relay port (first-time startup only)\n");
|
||||||
printf("\n");
|
printf("\n");
|
||||||
printf("Configuration:\n");
|
printf("Configuration:\n");
|
||||||
printf(" This relay uses event-based configuration stored in the database.\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(" On first startup, keys are automatically generated and printed once.\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(" Database file: <relay_pubkey>.db (created automatically)\n");
|
||||||
printf("\n");
|
printf("\n");
|
||||||
printf("Examples:\n");
|
printf("Examples:\n");
|
||||||
printf(" %s # Start relay (auto-configure on first run)\n", program_name);
|
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 --help # Show this help\n", program_name);
|
||||||
printf(" %s --version # Show version info\n", program_name);
|
printf(" %s --version # Show version info\n", program_name);
|
||||||
printf("\n");
|
printf("\n");
|
||||||
@@ -3154,7 +3159,12 @@ void print_version() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
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++) {
|
for (int i = 1; i < argc; i++) {
|
||||||
if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
|
if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
|
||||||
print_usage(argv[0]);
|
print_usage(argv[0]);
|
||||||
@@ -3162,6 +3172,30 @@ int main(int argc, char* argv[]) {
|
|||||||
} else if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--version") == 0) {
|
} else if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--version") == 0) {
|
||||||
print_version();
|
print_version();
|
||||||
return 0;
|
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 {
|
} else {
|
||||||
log_error("Unknown argument. Use --help for usage information.");
|
log_error("Unknown argument. Use --help for usage information.");
|
||||||
print_usage(argv[0]);
|
print_usage(argv[0]);
|
||||||
@@ -3194,7 +3228,7 @@ int main(int argc, char* argv[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Run first-time startup sequence (generates keys, creates database, etc.)
|
// 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");
|
log_error("Failed to complete first-time startup sequence");
|
||||||
cleanup_configuration_system();
|
cleanup_configuration_system();
|
||||||
nostr_cleanup();
|
nostr_cleanup();
|
||||||
|
|||||||
Reference in New Issue
Block a user