aboutsummaryrefslogtreecommitdiffhomepage
path: root/parse.go
blob: 2e7f507cf07438828132b0f53dc1192dd8367ddc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
package main

import (
	"encoding/json"
	"errors"
	"io"
	"log"
	"os"
	"strconv"
	"strings"
	"syscall"

	"git.gensokyo.uk/security/hakurei/hst"
	"git.gensokyo.uk/security/hakurei/internal/hlog"
	"git.gensokyo.uk/security/hakurei/internal/state"
)

func tryPath(name string) (config *hst.Config) {
	var r io.Reader
	config = new(hst.Config)

	if name != "-" {
		r = tryFd(name)
		if r == nil {
			hlog.Verbose("load configuration from file")

			if f, err := os.Open(name); err != nil {
				log.Fatalf("cannot access configuration file %q: %s", name, err)
			} else {
				// finalizer closes f
				r = f
			}
		} else {
			defer func() {
				if err := r.(io.ReadCloser).Close(); err != nil {
					log.Printf("cannot close config fd: %v", err)
				}
			}()
		}
	} else {
		r = os.Stdin
	}

	if err := json.NewDecoder(r).Decode(&config); err != nil {
		log.Fatalf("cannot load configuration: %v", err)
	}

	return
}

func tryFd(name string) io.ReadCloser {
	if v, err := strconv.Atoi(name); err != nil {
		if !errors.Is(err, strconv.ErrSyntax) {
			hlog.Verbosef("name cannot be interpreted as int64: %v", err)
		}
		return nil
	} else {
		hlog.Verbosef("trying config stream from %d", v)
		fd := uintptr(v)
		if _, _, errno := syscall.Syscall(syscall.SYS_FCNTL, fd, syscall.F_GETFD, 0); errno != 0 {
			if errors.Is(errno, syscall.EBADF) {
				return nil
			}
			log.Fatalf("cannot get fd %d: %v", fd, errno)
		}
		return os.NewFile(fd, strconv.Itoa(v))
	}
}

func tryShort(name string) (config *hst.Config, entry *state.State) {
	likePrefix := false
	if len(name) <= 32 {
		likePrefix = true
		for _, c := range name {
			if c >= '0' && c <= '9' {
				continue
			}
			if c >= 'a' && c <= 'f' {
				continue
			}
			likePrefix = false
			break
		}
	}

	// try to match from state store
	if likePrefix && len(name) >= 8 {
		hlog.Verbose("argument looks like prefix")

		s := state.NewMulti(std.Paths().RunDirPath)
		if entries, err := state.Join(s); err != nil {
			log.Printf("cannot join store: %v", err)
			// drop to fetch from file
		} else {
			for id := range entries {
				v := id.String()
				if strings.HasPrefix(v, name) {
					// match, use config from this state entry
					entry = entries[id]
					config = entry.Config
					break
				}

				hlog.Verbosef("instance %s skipped", v)
			}
		}
	}

	return
}