276 lines
6.6 KiB
Go
276 lines
6.6 KiB
Go
package main
|
|
|
|
import (
|
|
"archive/tar"
|
|
"bufio"
|
|
"compress/gzip"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"io/fs"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"regexp"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
var safeFilenamePattern = regexp.MustCompile(`[^A-Za-z0-9_.-]`)
|
|
|
|
func (app *App) log_line(line string) {
|
|
handle, err := os.OpenFile(app.paths.LogFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer handle.Close()
|
|
_, _ = fmt.Fprintln(handle, line)
|
|
}
|
|
|
|
func load_logging_pref(path string) bool {
|
|
file, err := os.Open(path)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
defer file.Close()
|
|
scanner := bufio.NewScanner(file)
|
|
for scanner.Scan() {
|
|
line := strings.TrimSpace(scanner.Text())
|
|
if line == "" || strings.HasPrefix(line, "#") {
|
|
continue
|
|
}
|
|
key, value, found := strings.Cut(line, "=")
|
|
if !found || !strings.EqualFold(strings.TrimSpace(key), "saveSessionLogs") {
|
|
continue
|
|
}
|
|
switch strings.ToLower(strings.TrimSpace(value)) {
|
|
case "1", "true", "yes", "on":
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func save_logging_pref(path string, enabled bool) error {
|
|
return write_private_file(path, func(writer io.Writer) error {
|
|
value := 0
|
|
if enabled {
|
|
value = 1
|
|
}
|
|
_, err := fmt.Fprintf(writer, "saveSessionLogs=%d\n", value)
|
|
return err
|
|
})
|
|
}
|
|
|
|
func sanitize_filename(value string) string {
|
|
return safeFilenamePattern.ReplaceAllString(value, "_")
|
|
}
|
|
|
|
func prepare_session_log(logDir, serverName string) (string, error) {
|
|
if err := os.MkdirAll(logDir, 0700); err != nil {
|
|
return "", err
|
|
}
|
|
path := filepath.Join(logDir, fmt.Sprintf("%s-%s.log", sanitize_filename(serverName), time.Now().Format("2006-01-02")))
|
|
handle, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if err := handle.Chmod(0600); err != nil {
|
|
handle.Close()
|
|
return "", err
|
|
}
|
|
if err := handle.Close(); err != nil {
|
|
return "", err
|
|
}
|
|
return path, nil
|
|
}
|
|
|
|
func log_files(logDir string) ([]string, error) {
|
|
entries, err := os.ReadDir(logDir)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
paths := []string{}
|
|
for _, entry := range entries {
|
|
if entry.Type().IsRegular() && strings.HasSuffix(entry.Name(), ".log") {
|
|
paths = append(paths, filepath.Join(logDir, entry.Name()))
|
|
}
|
|
}
|
|
return paths, nil
|
|
}
|
|
|
|
func create_log_archive(logDir, cacheDir string) (string, error) {
|
|
paths, err := log_files(logDir)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if len(paths) == 0 {
|
|
return "", fs.ErrNotExist
|
|
}
|
|
if err := os.MkdirAll(cacheDir, 0700); err != nil {
|
|
return "", err
|
|
}
|
|
archiveFile, err := os.CreateTemp(cacheDir, "barnard-logs-*.tar.gz")
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
archivePath := archiveFile.Name()
|
|
failed := true
|
|
defer func() {
|
|
if failed {
|
|
_ = os.Remove(archivePath)
|
|
}
|
|
}()
|
|
if err := archiveFile.Chmod(0600); err != nil {
|
|
archiveFile.Close()
|
|
return "", err
|
|
}
|
|
gzipWriter := gzip.NewWriter(archiveFile)
|
|
tarWriter := tar.NewWriter(gzipWriter)
|
|
for _, path := range paths {
|
|
info, err := os.Stat(path)
|
|
if err != nil {
|
|
tarWriter.Close()
|
|
gzipWriter.Close()
|
|
archiveFile.Close()
|
|
return "", err
|
|
}
|
|
header, err := tar.FileInfoHeader(info, "")
|
|
if err != nil {
|
|
tarWriter.Close()
|
|
gzipWriter.Close()
|
|
archiveFile.Close()
|
|
return "", err
|
|
}
|
|
header.Name = filepath.Base(path)
|
|
header.Mode = 0600
|
|
if err := tarWriter.WriteHeader(header); err != nil {
|
|
tarWriter.Close()
|
|
gzipWriter.Close()
|
|
archiveFile.Close()
|
|
return "", err
|
|
}
|
|
file, err := os.Open(path)
|
|
if err != nil {
|
|
tarWriter.Close()
|
|
gzipWriter.Close()
|
|
archiveFile.Close()
|
|
return "", err
|
|
}
|
|
_, copyErr := io.Copy(tarWriter, file)
|
|
closeErr := file.Close()
|
|
if copyErr != nil {
|
|
tarWriter.Close()
|
|
gzipWriter.Close()
|
|
archiveFile.Close()
|
|
return "", copyErr
|
|
}
|
|
if closeErr != nil {
|
|
tarWriter.Close()
|
|
gzipWriter.Close()
|
|
archiveFile.Close()
|
|
return "", closeErr
|
|
}
|
|
}
|
|
if err := tarWriter.Close(); err != nil {
|
|
gzipWriter.Close()
|
|
archiveFile.Close()
|
|
return "", err
|
|
}
|
|
if err := gzipWriter.Close(); err != nil {
|
|
archiveFile.Close()
|
|
return "", err
|
|
}
|
|
if err := archiveFile.Sync(); err != nil {
|
|
archiveFile.Close()
|
|
return "", err
|
|
}
|
|
if err := archiveFile.Close(); err != nil {
|
|
return "", err
|
|
}
|
|
failed = false
|
|
return archivePath, nil
|
|
}
|
|
|
|
func (app *App) send_logs() error {
|
|
wormhole, err := exec.LookPath("wormhole")
|
|
if err != nil {
|
|
return app.ui.message("Required command not found: wormhole")
|
|
}
|
|
archivePath, err := create_log_archive(app.paths.LogDir, app.paths.CacheDir)
|
|
if errors.Is(err, fs.ErrNotExist) || errors.Is(err, os.ErrNotExist) {
|
|
return app.ui.message("No logs to send. Logs are saved to: " + app.paths.LogDir)
|
|
}
|
|
if err != nil {
|
|
return app.ui.message("Could not create log archive: " + err.Error())
|
|
}
|
|
defer os.Remove(archivePath)
|
|
|
|
if err := app.ui.message("Wormhole will display the transfer code in the normal terminal. Press Ctrl+C there to cancel the transfer."); err != nil {
|
|
return err
|
|
}
|
|
command := exec.Command(wormhole, "send", archivePath)
|
|
command.Stdin = os.Stdin
|
|
command.Stdout = os.Stdout
|
|
command.Stderr = os.Stderr
|
|
app.ui.close()
|
|
commandErr := run_external(app.ui, command)
|
|
var signalErr *terminalSignalError
|
|
if errors.As(commandErr, &signalErr) {
|
|
return signalErr
|
|
}
|
|
if err := app.ui.open(); err != nil {
|
|
return err
|
|
}
|
|
if receivedSignal := app.ui.take_termination(); receivedSignal != nil {
|
|
return &terminalSignalError{signal: receivedSignal}
|
|
}
|
|
if commandErr == nil {
|
|
app.log_line("Sent log archive with wormhole")
|
|
return app.ui.message("Logs sent successfully.")
|
|
}
|
|
return app.ui.message("Log transfer did not complete successfully: " + commandErr.Error())
|
|
}
|
|
|
|
func (app *App) toggle_session_logging() error {
|
|
question := "Session logging is currently disabled. Enable saving logs to the logs directory?"
|
|
if app.saveSessionLogs {
|
|
question = "Session logging is currently enabled. Disable it?"
|
|
}
|
|
confirmed, err := app.ui.confirm(question)
|
|
if err != nil || !confirmed {
|
|
return err
|
|
}
|
|
newValue := !app.saveSessionLogs
|
|
if err := save_logging_pref(app.paths.LogPrefsFile, newValue); err != nil {
|
|
return app.ui.message("Could not save logging preference: " + err.Error())
|
|
}
|
|
app.saveSessionLogs = newValue
|
|
return nil
|
|
}
|
|
|
|
func (app *App) manage_logs() error {
|
|
for {
|
|
toggleLabel := "Enable logs"
|
|
if app.saveSessionLogs {
|
|
toggleLabel = "Disable logs"
|
|
}
|
|
options := []string{toggleLabel, "Send logs with wormhole", "Go Back"}
|
|
selection, cancelled, err := app.ui.menu(options)
|
|
if err != nil || cancelled || selection == len(options)-1 {
|
|
return err
|
|
}
|
|
switch selection {
|
|
case 0:
|
|
err = app.toggle_session_logging()
|
|
case 1:
|
|
err = app.send_logs()
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|