1package main
2
3import (
4 "context"
5 "fmt"
6 "os"
7 "os/exec"
8
9 "github.com/gliderlabs/ssh"
10)
11
12func GitMiddleware(repoDir string) Middleware {
13 return func(sh ssh.Handler) ssh.Handler {
14 return func(s ssh.Session) {
15 cmd := s.Command()
16 if len(cmd) == 2 {
17 switch cmd[0] {
18 case "git-upload-pack", "git-receive-pack", "git-upload-archive":
19 r := cmd[1]
20 rp := fmt.Sprintf("%s%s", repoDir, r)
21 ctx := s.Context()
22 err := ensureRepo(ctx, repoDir, r)
23 if err != nil {
24 fatalGit(s, err)
25 break
26 }
27 c := exec.CommandContext(ctx, cmd[0], rp)
28 c.Dir = "./"
29 c.Stdout = s
30 c.Stdin = s
31 err = c.Run()
32 if err != nil {
33 fatalGit(s, err)
34 break
35 }
36 }
37 }
38 sh(s)
39 }
40 }
41}
42
43func fileExists(path string) (bool, error) {
44 _, err := os.Stat(path)
45 if err == nil {
46 return true, nil
47 }
48 if os.IsNotExist(err) {
49 return false, nil
50 }
51 return true, err
52}
53
54func fatalGit(s ssh.Session, err error) {
55 // hex length includes 4 byte length prefix and ending newline
56 logError(s, err)
57 msg := err.Error()
58 pktLine := fmt.Sprintf("%04x%s\n", len(msg)+5, msg)
59 _, err = s.Write([]byte(pktLine))
60 if err != nil {
61 logError(s, err)
62 }
63 s.Exit(1)
64}
65
66func ensureRepo(ctx context.Context, dir string, repo string) error {
67 exists, err := fileExists(dir)
68 if err != nil {
69 return err
70 }
71 if !exists {
72 err = os.MkdirAll(dir, os.ModeDir|os.FileMode(0700))
73 if err != nil {
74 return err
75 }
76 }
77 rp := fmt.Sprintf("%s%s", dir, repo)
78 exists, err = fileExists(rp)
79 if err != nil {
80 return err
81 }
82 if !exists {
83 c := exec.CommandContext(ctx, "git", "init", "--bare", rp)
84 err = c.Run()
85 if err != nil {
86 return err
87 }
88 }
89 return nil
90}