Files
mesh-catalog/modules/xorg/cmd/xorg/xlog.go
T
jochen b2c170acda xorg: the display server holds node-display-server and writes the session's start (hq ADR 0208)
The X server, its start and its tools as one module. It provides x11-display with
the machine's reach, gated by the host's seat capability. It writes a block at the
start of ~/.xinitrc: the account's environment, an explicit import into the user
manager, the mesh's X resources merged without cpp, autorandr, the xinitrc slots,
~/.xinitrc.local, and the session's exec from the last slot.

Its Go tools serve the seat's displays and layout (autorandr profiles keyed by
EDID), and set-mode, primary, dpi, input devices and settings, keyboard, a
screenshot (xwd decoded in Go) and the server's log. internal/desktop is how
every desktop tool finds the operator's session from the runtime, which has none:
from the session's own processes, reading only its words, confirmed with logind.
2026-10-04 13:21:47 +02:00

104 lines
2.8 KiB
Go

package main
import (
"bufio"
"os"
"path/filepath"
"regexp"
"sort"
"strconv"
"strings"
)
// LogLine is one marked line of the X server's log.
type LogLine struct {
At float64 `json:"at_seconds"`
Level string `json:"level"`
Text string `json:"text"`
}
// XLog is what x-log answers.
type XLog struct {
File string `json:"file"`
Server string `json:"server,omitempty"`
Started string `json:"started,omitempty"`
Errors int `json:"errors"`
Warnings int `json:"warnings"`
Lines []LogLine `json:"lines"`
Cut bool `json:"cut,omitempty"`
}
var (
logMark = regexp.MustCompile(`^\[\s*([0-9.]+)\] \((EE|WW)\) (.*)$`)
logStart = regexp.MustCompile(`\(==\) Log file: "[^"]*", Time: (.+)$`)
)
// ParseXLog reads an Xorg log: the server's version, when it started, and its errors and (when
// asked) warnings, the last `limit` of them. The legend line that explains the markers is not one.
func ParseXLog(file, text string, warnings bool, limit int) XLog {
out := XLog{File: file, Lines: []LogLine{}}
sc := bufio.NewScanner(strings.NewReader(text))
sc.Buffer(make([]byte, 1<<20), 1<<22)
for sc.Scan() {
line := sc.Text()
if out.Server == "" && strings.HasPrefix(strings.TrimSpace(line), "X.Org X Server ") {
out.Server = strings.TrimSpace(line)
}
if m := logStart.FindStringSubmatch(line); m != nil && out.Started == "" {
out.Started = m[1]
}
m := logMark.FindStringSubmatch(line)
if m == nil {
continue
}
at, _ := strconv.ParseFloat(m[1], 64)
level := map[string]string{"EE": "error", "WW": "warning"}[m[2]]
if level == "error" {
out.Errors++
} else {
out.Warnings++
if !warnings {
continue
}
}
out.Lines = append(out.Lines, LogLine{At: at, Level: level, Text: m[3]})
}
if limit > 0 && len(out.Lines) > limit {
out.Lines = out.Lines[len(out.Lines)-limit:]
out.Cut = true
}
return out
}
// XLogFiles are where an X server on display n logs, best first: a root server's log, then a
// rootless one's under the account's data directory. With no display, every log found, newest first.
func XLogFiles(display int, home string) []string {
dirs := []string{"/var/log", filepath.Join(home, ".local/share/xorg")}
if display >= 0 {
var out []string
for _, d := range dirs {
out = append(out, filepath.Join(d, "Xorg."+strconv.Itoa(display)+".log"))
}
return out
}
type found struct {
path string
mod int64
}
var all []found
for _, d := range dirs {
matches, _ := filepath.Glob(filepath.Join(d, "Xorg.*.log"))
for _, p := range matches {
if info, err := os.Stat(p); err == nil {
all = append(all, found{p, info.ModTime().UnixNano()})
}
}
}
sort.Slice(all, func(i, j int) bool { return all[i].mod > all[j].mod })
var out []string
for _, f := range all {
out = append(out, f.path)
}
return out
}