backend.go (view raw)
1//
2// Copyright (c) 2019 Ted Unangst <tedu@tedunangst.com>
3//
4// Permission to use, copy, modify, and distribute this software for any
5// purpose with or without fee is hereby granted, provided that the above
6// copyright notice and this permission notice appear in all copies.
7//
8// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15
16package main
17
18import (
19 "bytes"
20 "net"
21 "net/rpc"
22 "os"
23 "os/exec"
24
25 "humungus.tedunangst.com/r/webs/gate"
26 "humungus.tedunangst.com/r/webs/image"
27)
28
29type Shrinker struct {
30}
31
32type ShrinkerArgs struct {
33 Buf []byte
34 Params image.Params
35}
36
37type ShrinkerResult struct {
38 Image *image.Image
39}
40
41var shrinkgate = gate.NewLimiter(4)
42
43func (s *Shrinker) Shrink(args *ShrinkerArgs, res *ShrinkerResult) error {
44 shrinkgate.Start()
45 defer shrinkgate.Finish()
46 img, err := image.Vacuum(bytes.NewReader(args.Buf), args.Params)
47 if err != nil {
48 return err
49 }
50 res.Image = img
51 return nil
52}
53
54func backendSockname() string {
55 return dataDir + "/backend.sock"
56}
57
58func shrinkit(data []byte) (*image.Image, error) {
59 cl, err := rpc.Dial("unix", backendSockname())
60 if err != nil {
61 return nil, err
62 }
63 defer cl.Close()
64 var res ShrinkerResult
65 err = cl.Call("Shrinker.Shrink", &ShrinkerArgs{
66 Buf: data,
67 Params: image.Params{LimitSize: 4200 * 4200, MaxWidth: 2048, MaxHeight: 2048},
68 }, &res)
69 if err != nil {
70 return nil, err
71 }
72 return res.Image, nil
73}
74
75var backendhooks []func()
76
77func orphancheck() {
78 var b [1]byte
79 os.Stdin.Read(b[:])
80 dlog.Printf("backend shutting down")
81 os.Exit(0)
82}
83
84func backendServer() {
85 dlog.Printf("backend server running")
86 go orphancheck()
87 shrinker := new(Shrinker)
88 srv := rpc.NewServer()
89 err := srv.Register(shrinker)
90 if err != nil {
91 elog.Panicf("unable to register shrinker: %s", err)
92 }
93
94 sockname := backendSockname()
95 err = os.Remove(sockname)
96 if err != nil && !os.IsNotExist(err) {
97 elog.Panicf("unable to unlink socket: %s", err)
98 }
99
100 lis, err := net.Listen("unix", sockname)
101 if err != nil {
102 elog.Panicf("unable to register shrinker: %s", err)
103 }
104 err = setLimits()
105 if err != nil {
106 elog.Printf("error setting backend limits: %s", err)
107 }
108 for _, h := range backendhooks {
109 h()
110 }
111 srv.Accept(lis)
112}
113
114func runBackendServer() {
115 r, w, err := os.Pipe()
116 if err != nil {
117 elog.Panicf("can't pipe: %s", err)
118 }
119 proc := exec.Command(os.Args[0], reexecArgs("backend")...)
120 proc.Stdout = os.Stdout
121 proc.Stderr = os.Stderr
122 proc.Stdin = r
123 err = proc.Start()
124 if err != nil {
125 elog.Panicf("can't exec backend: %s", err)
126 }
127 err = proc.Wait()
128 elog.Printf("lost the backend: %s", err)
129 w.Close()
130}