fix/exec-umask 2cd66dc

tap.go RawHistoryFolder

1// SPDX-License-Identifier: MIT
2
3package vm
4
5import (
6	"context"
7	"fmt"
8	"net/netip"
9	"strconv"
10	"strings"
11
12	"bnema.dev/ermite-runner/internal/cmdrun"
13)
14
15// tapPrefix must match the "ertap*" wildcard of the nftables policy.
16const tapPrefix = "ertap"
17
18func tapName(slot int) string { return tapPrefix + strconv.Itoa(slot) }
19
20// createTap creates a persistent TAP owned by uid (the user Firecracker runs
21// as, so it may open it), gives the host side its /30 address and brings it up.
22// It needs CAP_NET_ADMIN. On error the TAP is removed again.
23func createTap(ctx context.Context, run cmdrun.Runner, name string, uid int, gateway netip.Addr) error {
24	u := strconv.Itoa(uid)
25	if _, err := run.Run(ctx, nil, "ip", "tuntap", "add", "dev", name, "mode", "tap", "user", u, "group", u); err != nil {
26		return fmt.Errorf("create tap %s: %w", name, err)
27	}
28	for _, args := range [][]string{
29		{"addr", "add", gateway.String() + "/30", "dev", name},
30		{"link", "set", "dev", name, "up"},
31	} {
32		if _, err := run.Run(ctx, nil, "ip", args...); err != nil {
33			_ = deleteTap(context.WithoutCancel(ctx), run, name)
34			return fmt.Errorf("configure tap %s: %w", name, err)
35		}
36	}
37	return nil
38}
39
40// deleteTap removes a TAP; a missing device is not an error.
41func deleteTap(ctx context.Context, run cmdrun.Runner, name string) error {
42	out, err := run.Run(ctx, nil, "ip", "link", "del", "dev", name)
43	if err != nil && (strings.Contains(string(out), "Cannot find device") || strings.Contains(err.Error(), "Cannot find device")) {
44		return nil
45	}
46	return err
47}
48
49// leftoverTaps lists interfaces named like ours ("ertap<N>").
50func leftoverTaps(ctx context.Context, run cmdrun.Runner) ([]string, error) {
51	out, err := run.Run(ctx, nil, "ip", "-o", "link", "show")
52	if err != nil {
53		return nil, err
54	}
55	return parseTapNames(string(out)), nil
56}
57
58// parseTapNames extracts "ertap<digits>" interface names from `ip -o link show`
59// ("3: ertap0: <...>" or "3: ertap0@if2: <...>").
60func parseTapNames(s string) []string {
61	var out []string
62	for _, line := range strings.Split(s, "\n") {
63		_, rest, ok := strings.Cut(line, ": ")
64		if !ok {
65			continue
66		}
67		name, _, _ := strings.Cut(rest, ":")
68		name, _, _ = strings.Cut(name, "@")
69		if n, ok := strings.CutPrefix(name, tapPrefix); ok && n != "" {
70			if _, err := strconv.Atoi(n); err == nil {
71				out = append(out, name)
72			}
73		}
74	}
75	return out
76}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 4

Tags 11

↑↓ moveenter openesc close