Append an optional station log and announce the software after login.
A log path that cannot be written leaves the station on the reflector, and the reflector learns which program joined. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+125
@@ -0,0 +1,125 @@
|
||||
package station
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Close releases the log file. It is safe to call more than once.
|
||||
func (s *Station) Close() {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.closeLogLocked()
|
||||
}
|
||||
|
||||
func (s *Station) openLog() {
|
||||
f, err := os.OpenFile(s.logPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o600)
|
||||
if err != nil {
|
||||
s.logNotice = "Cannot write " + s.logPath
|
||||
if s.state.Talker == "" {
|
||||
s.state.Detail = s.logNotice
|
||||
}
|
||||
return
|
||||
}
|
||||
s.logFile = f
|
||||
}
|
||||
|
||||
func (s *Station) closeLogLocked() {
|
||||
if s.logFile != nil {
|
||||
s.logFile.Close()
|
||||
s.logFile = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Station) writeLog(ts time.Time, msg string) {
|
||||
if s.logFile == nil {
|
||||
return
|
||||
}
|
||||
line := ts.Format(time.RFC3339) + " " + msg + "\n"
|
||||
if _, err := s.logFile.Write([]byte(line)); err != nil {
|
||||
s.closeLogLocked()
|
||||
if s.logNotice == "" {
|
||||
s.logNotice = "Cannot write " + s.logPath
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Station) noteTransmitLocked() {
|
||||
s.noteTransmitAt(s.clock.Now())
|
||||
}
|
||||
|
||||
func (s *Station) noteTransmitAt(now time.Time) {
|
||||
tx := s.transmitting()
|
||||
if tx && !s.sending {
|
||||
s.sending = true
|
||||
s.sendStart = now
|
||||
return
|
||||
}
|
||||
if !tx && s.sending {
|
||||
start := s.sendStart
|
||||
s.sending = false
|
||||
s.writeLog(start, fmt.Sprintf("sending for %d seconds.", wholeSeconds(start, now)))
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Station) closeTalkerLocked(now time.Time) {
|
||||
if s.talkCall == "" {
|
||||
return
|
||||
}
|
||||
call := s.talkCall
|
||||
start := s.talkStart
|
||||
s.talkCall = ""
|
||||
s.writeLog(start, fmt.Sprintf("%s talked for %d seconds.", call, wholeSeconds(start, now)))
|
||||
}
|
||||
|
||||
func (s *Station) noteDownLocked(now time.Time, dial bool) {
|
||||
s.closeTalkerLocked(now)
|
||||
s.state.Connection = "Disconnected"
|
||||
s.remoteTalker = ""
|
||||
s.roomBusy = false
|
||||
s.noteTransmitAt(now)
|
||||
switch {
|
||||
case s.up:
|
||||
s.up = false
|
||||
s.outage = true
|
||||
s.writeLog(now, "disconnected")
|
||||
case dial && !s.outage:
|
||||
s.outage = true
|
||||
s.writeLog(now, "disconnected")
|
||||
}
|
||||
s.publish()
|
||||
}
|
||||
|
||||
func (s *Station) endFatal(msg string, log bool) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
if s.fatal != "" {
|
||||
return
|
||||
}
|
||||
now := s.clock.Now()
|
||||
s.closeTalkerLocked(now)
|
||||
s.fatal = msg
|
||||
s.remoteTalker = ""
|
||||
s.roomBusy = false
|
||||
s.noteTransmitAt(now)
|
||||
if log {
|
||||
s.up = false
|
||||
s.outage = true
|
||||
s.writeLog(now, oneLine(msg))
|
||||
}
|
||||
s.publish()
|
||||
}
|
||||
|
||||
func wholeSeconds(start, end time.Time) int {
|
||||
d := end.Sub(start)
|
||||
if d <= 0 {
|
||||
return 0
|
||||
}
|
||||
return int(d / time.Second)
|
||||
}
|
||||
|
||||
func oneLine(msg string) string {
|
||||
return strings.NewReplacer("\r\n", " ", "\n", " ", "\r", " ").Replace(msg)
|
||||
}
|
||||
Reference in New Issue
Block a user