Live video on the AT Protocol
1package proc
2
3import (
4 "bufio"
5 "context"
6 "errors"
7 "fmt"
8 "io"
9 "os"
10 "os/exec"
11 "strings"
12
13 "golang.org/x/sync/errgroup"
14 "stream.place/streamplace/pkg/config"
15 "stream.place/streamplace/pkg/log"
16 "stream.place/streamplace/pkg/mist/mistconfig"
17)
18
19func RunMistServer(ctx context.Context, cli *config.CLI) error {
20 myself, err := os.Executable()
21 if err != nil {
22 return err
23 }
24 f, err := os.CreateTemp("", "mistconfig.json")
25 defer os.Remove(f.Name())
26 if err != nil {
27 return err
28 }
29 conf, err := mistconfig.Generate(cli)
30 if err != nil {
31 return err
32 }
33 err = os.WriteFile(f.Name(), conf, 0644)
34 if err != nil {
35 return err
36 }
37 cmd := exec.CommandContext(ctx, myself,
38 "MistServer",
39 "-c", f.Name(),
40 "-i", "127.0.0.1",
41 "-p", fmt.Sprintf("%d", cli.MistAdminPort),
42 )
43 cmd.Env = []string{
44 "MIST_NO_PRETTY_LOGGING=true",
45 }
46 stdout, err := cmd.StdoutPipe()
47 if err != nil {
48 panic(err)
49 }
50 stderr, err := cmd.StderrPipe()
51 if err != nil {
52 panic(err)
53 }
54 group, ctx := errgroup.WithContext(ctx)
55 output := fmt.Println
56 for i, pipe := range []io.ReadCloser{stdout, stderr} {
57 func(i int, pipe io.ReadCloser) {
58 group.Go(func() error {
59 reader := bufio.NewReader(pipe)
60
61 for {
62 line, isPrefix, err := reader.ReadLine()
63 if err != nil {
64 if !errors.Is(err, io.EOF) {
65 _, _ = output(fmt.Sprintf("reader gave error, ending logging for fd=%d err=%s", i+1, err))
66 }
67 line, _, err := reader.ReadLine()
68 if string(line) != "" {
69 _, _ = output(string(line))
70 }
71 return err
72 }
73 if isPrefix {
74 _, _ = output("warning: preceding line exceeds 64k logging limit and was split")
75 }
76 if string(line) != "" {
77 level, procName, pid, path, streamName, msg, err := ParseMistLog(string(line))
78 if err != nil {
79 log.Log(ctx, "badly formatted mist log", "message", string(line))
80 } else {
81 log.Log(ctx, msg,
82 "level", level,
83 "procName", procName,
84 "pid", pid,
85 "streamName", streamName,
86 "caller", path,
87 )
88 }
89 }
90 }
91 })
92 }(i, pipe)
93 }
94
95 group.Go(func() error {
96 return cmd.Start()
97 })
98
99 return group.Wait()
100}
101
102func ParseMistLog(str string) (string, string, string, string, string, string, error) {
103 parts := strings.Split(str, "|")
104 if len(parts) != 6 {
105 return "", "", "", "", "", "", fmt.Errorf("badly formatted mist string")
106 }
107 return parts[0], parts[1], parts[2], parts[3], parts[4], parts[5], nil
108}