Compare commits

..

3 Commits

Author SHA1 Message Date
Pim van Pelt
7442a83c9d Cut v1.2.0 release 2025-07-06 22:44:35 +02:00
Pim van Pelt
7f6b030b31 Use a .new temp file while gathering info, only move it into place on success 2025-07-06 22:43:33 +02:00
Pim van Pelt
f05124b703 Add RC values, update docs, rename manpage 2025-07-06 22:27:51 +02:00
7 changed files with 55 additions and 14 deletions

View File

@@ -103,5 +103,5 @@ This allows connecting to older routers that require legacy SSH algorithms while
## Documentation
- **[Detailed Documentation](docs/DETAILS.md)** - Complete feature guide, configuration reference, and examples
- **[Manual Page](docs/router_backup.1)** - Unix manual page
- **[Manual Page](docs/ipng-router-backup.1)** - Unix manual page
- **[Changelog](debian/changelog)** - Version history and changes

8
debian/changelog vendored
View File

@@ -1,3 +1,11 @@
ipng-router-backup (1.2.0) stable; urgency=low
* Add atomic file operations with .new suffix for backup reliability
* Add exit codes: 10 (some devices failed), 11 (all devices failed)
* Update manpage filename to ipng-router-backup.1
-- Pim van Pelt <pim@ipng.ch> Sun, 07 Jul 2025 20:00:00 +0100
ipng-router-backup (1.1.1) stable; urgency=low
* Add 'address' field to device configuration for explicit IP/hostname override

2
debian/rules vendored
View File

@@ -18,7 +18,7 @@ override_dh_auto_install:
mkdir -p debian/ipng-router-backup/usr/share/man/man1
cp ipng-router-backup debian/ipng-router-backup/usr/bin/
cp etc/* debian/ipng-router-backup/etc/ipng-router-backup/
cp docs/router_backup.1 debian/ipng-router-backup/usr/share/man/man1/ipng-router-backup.1
cp docs/ipng-router-backup.1 debian/ipng-router-backup/usr/share/man/man1/ipng-router-backup.1
gzip debian/ipng-router-backup/usr/share/man/man1/ipng-router-backup.1
override_dh_auto_clean:

View File

@@ -188,5 +188,7 @@ Software Version : v25.3.2
- **Permission issues**: Verify SSH key permissions (600) and output directory access
### Exit Codes
- `0`: Success
- `1`: Configuration error, authentication failure, or connection issues
- `0`: Success (all devices processed successfully)
- `1`: Configuration error, authentication failure, or connection issues
- `10`: Some devices failed
- `11`: All devices failed

View File

@@ -98,10 +98,16 @@ Example configuration file
.SH EXIT STATUS
.TP
.B 0
Success
Success (all devices processed successfully)
.TP
.B 1
General error (configuration file not found, authentication failure, etc.)
.TP
.B 10
Some devices failed
.TP
.B 11
All devices failed
.SH AUTHOR
Written by Pim van Pelt.
.SH REPORTING BUGS

View File

@@ -10,7 +10,7 @@ import (
"github.com/spf13/cobra"
)
const Version = "1.1.1"
const Version = "1.2.0"
// Config and SSH types are now in separate packages
@@ -120,6 +120,14 @@ func main() {
}
fmt.Printf("\nOverall summary: %d/%d devices processed successfully\n", successCount, totalCount)
// Set exit code based on results
if successCount == 0 {
os.Exit(11) // All devices failed
} else if successCount < totalCount {
os.Exit(10) // Some devices failed
}
// Exit code 0 (success) when all devices succeeded
},
}

View File

@@ -209,29 +209,34 @@ func (rb *RouterBackup) BackupCommands(commands []string, outputDir string) erro
}
filename := rb.hostname
filepath := filepath.Join(outputDir, filename)
finalPath := filepath.Join(outputDir, filename)
tempPath := finalPath + ".new"
// Truncate file at start
file, err := os.Create(filepath)
// Create temporary file
file, err := os.Create(tempPath)
if err != nil {
return fmt.Errorf("failed to create file %s: %v", filepath, err)
return fmt.Errorf("failed to create temporary file %s: %v", tempPath, err)
}
file.Close()
successCount := 0
hasErrors := false
for i, command := range commands {
fmt.Printf("Running command %d/%d: %s\n", i+1, len(commands), command)
output, err := rb.RunCommand(command)
if err != nil {
fmt.Printf("Error executing '%s': %v\n", command, err)
hasErrors = true
continue
}
// Append to file
file, err := os.OpenFile(filepath, os.O_APPEND|os.O_WRONLY, 0644)
// Append to temporary file
file, err := os.OpenFile(tempPath, os.O_APPEND|os.O_WRONLY, 0644)
if err != nil {
fmt.Printf("Failed to open file for writing: %v\n", err)
hasErrors = true
continue
}
@@ -243,9 +248,21 @@ func (rb *RouterBackup) BackupCommands(commands []string, outputDir string) erro
}
fmt.Printf("Summary: %d/%d commands successful\n", successCount, len(commands))
if successCount > 0 {
fmt.Printf("Output saved to %s\n", filepath)
if hasErrors || successCount == 0 {
// Remove .new suffix and log error
if err := os.Remove(tempPath); err != nil {
fmt.Printf("Failed to remove temporary file %s: %v\n", tempPath, err)
}
return fmt.Errorf("device backup incomplete due to command failures")
}
// All commands succeeded, move file into place atomically
if err := os.Rename(tempPath, finalPath); err != nil {
return fmt.Errorf("failed to move temporary file to final location: %v", err)
}
fmt.Printf("Output saved to %s\n", finalPath)
return nil
}