Compare commits

..

10 Commits

Author SHA1 Message Date
Timmy Welch
fbdcde6baf Add trivial xattr support to squashfs
Some checks failed
Push / CI (push) Has been cancelled
This is only implemented to support capabilities on installed packages
2025-12-28 14:20:18 -08:00
Michael Stapelberg
c74b4e7749 httpclient: use os.ReadFile instead of deprecated ioutil.ReadFile 2025-12-09 17:36:00 +01:00
Michael Stapelberg
fc385a9e3d httpclient: remove now-unused GetRemoteScheme 2025-12-09 17:35:39 +01:00
Michael Stapelberg
3c1aa9087c tlsflag,httpclient: remove global state 2025-12-08 21:31:10 +01:00
Michael Stapelberg
2e8be894f6 updateflag: remove global state
related to https://github.com/gokrazy/tools/pull/68
2025-12-06 08:41:40 +01:00
Michael Stapelberg
cbfb18ae26 httpclient: use updateflag.Value instead of global access 2025-12-05 17:50:16 +01:00
Michael Stapelberg
95a29c8f05 updateflag: introduce Value type to avoid global state
related to https://github.com/gokrazy/tools/pull/68
2025-12-05 17:43:05 +01:00
Michael Stapelberg
839bc69fbd remove tlsflag and updateflag Register functions
These flags are no longer used.

related to https://github.com/gokrazy/tools/pull/68
2025-12-05 17:37:09 +01:00
Michael Stapelberg
05cd6a8b1b config: add BootloaderExtraEEPROM
related to https://github.com/gokrazy/gokrazy/issues/338
2025-11-16 19:07:01 +01:00
Steve Lam
5599791533 deviceconfig: add NanoPi NEO device config (#26) 2025-05-26 22:15:01 +02:00
9 changed files with 312 additions and 133 deletions

View File

@@ -149,6 +149,8 @@ type PackageConfig struct {
// Basename overrides the basename of the package. // Basename overrides the basename of the package.
Basename string `json:",omitempty"` Basename string `json:",omitempty"`
Capabilities string `json:",omitempty"`
// -------------------------------------------------------------------------------- // --------------------------------------------------------------------------------
// run time package configuration // run time package configuration
// -------------------------------------------------------------------------------- // --------------------------------------------------------------------------------
@@ -220,6 +222,11 @@ type Struct struct {
// https://www.raspberrypi.com/documentation/computers/config_txt.html // https://www.raspberrypi.com/documentation/computers/config_txt.html
BootloaderExtraLines []string `json:",omitempty"` BootloaderExtraLines []string `json:",omitempty"`
// extra lines to append to the bootconf.txt EEPROM section which is read by
// the Raspberry Pi bootloader:
// https://www.raspberrypi.com/documentation/computers/raspberry-pi.html#raspberry-pi-bootloader-configuration
BootloaderExtraEEPROM []string `json:",omitempty"`
MountDevices []MountDevice `json:",omitempty"` MountDevices []MountDevice `json:",omitempty"`
// Do not set these manually in config.json, these fields only exist so that // Do not set these manually in config.json, these fields only exist so that

View File

@@ -60,6 +60,18 @@ var (
BootPartitionStartLBA: 32768, // 16MiB from start of disk BootPartitionStartLBA: 32768, // 16MiB from start of disk
Slug: "rock64", Slug: "rock64",
}, },
"FriendlyElec NanoPi Neo": {
// https://linux-sunxi.org/Bootable_SD_card
MBROnlyWithoutGPT: true,
RootDeviceFiles: []RootFile{
// u-boot-sunxi-with-spl.bin is an überpackage that include TPL, SPL, U-Boot and u-boot.dtb.
// u-boot can build it as a single file with `make nanopi_neo_defconfig && make u-boot-sunxi-with-spl.bin`
// and its easier to work with instead of dealing with separate files.
{"u-boot-sunxi-with-spl.bin", 16 * sectorSize, 2032 * sectorSize}, // sectors 16 - 2048
},
BootPartitionStartLBA: 2048, // 1MiB from start of disk
Slug: "nanopi_neo",
},
} }
) )

View File

@@ -4,7 +4,6 @@ import (
"crypto/tls" "crypto/tls"
"crypto/x509" "crypto/x509"
"fmt" "fmt"
"io/ioutil"
"log" "log"
"net/http" "net/http"
"net/url" "net/url"
@@ -32,7 +31,7 @@ func GetTLSHttpClientByTLSFlag(tlsFlag string, tlsInsecure bool, baseUrl *url.UR
// Append user specified certificate(s) // Append user specified certificate(s)
if tlsFlag != "self-signed" && tlsFlag != "" { if tlsFlag != "self-signed" && tlsFlag != "" {
usrCert := strings.Split(tlsFlag, ",")[0] usrCert := strings.Split(tlsFlag, ",")[0]
certBytes, err := ioutil.ReadFile(usrCert) certBytes, err := os.ReadFile(usrCert)
if err != nil { if err != nil {
return nil, false, fmt.Errorf("reading user specified certificate %s: %v", usrCert, err) return nil, false, fmt.Errorf("reading user specified certificate %s: %v", usrCert, err)
} }
@@ -44,7 +43,7 @@ func GetTLSHttpClientByTLSFlag(tlsFlag string, tlsInsecure bool, baseUrl *url.UR
if _, err := os.Stat(certPath); !os.IsNotExist(err) { if _, err := os.Stat(certPath); !os.IsNotExist(err) {
foundMatchingCertificate = true foundMatchingCertificate = true
log.Printf("Using certificate %s", certPath) log.Printf("Using certificate %s", certPath)
certBytes, err := ioutil.ReadFile(certPath) certBytes, err := os.ReadFile(certPath)
if err != nil { if err != nil {
return nil, false, fmt.Errorf("reading certificate %s: %v", certPath, err) return nil, false, fmt.Errorf("reading certificate %s: %v", certPath, err)
} }
@@ -84,22 +83,3 @@ func getTLSHTTPClient(trustStore *x509.CertPool, tlsInsecure bool) *http.Client
}, },
} }
} }
func GetRemoteScheme(baseUrl *url.URL) (string, error) {
// probe for https redirect, before sending credentials via http
probeClient := &http.Client{
CheckRedirect: func(*http.Request, []*http.Request) error {
return http.ErrUseLastResponse // do not follow redirects
},
}
probeResp, err := probeClient.Get("http://" + baseUrl.Host)
if err != nil {
return "", fmt.Errorf("probing url for https: %v", err)
}
probeLocation, err := probeResp.Location()
if err != nil {
// remote did not upgrade us to HTTPS
return "http", nil
}
return probeLocation.Scheme, nil
}

View File

@@ -10,9 +10,9 @@ import (
"github.com/gokrazy/internal/updateflag" "github.com/gokrazy/internal/updateflag"
) )
func For(cfg *config.Struct) (_ *http.Client, foundMatchingCertificate bool, updateBaseURL *url.URL, _ error) { func For(updateVal updateflag.Value, cfg *config.Struct) (_ *http.Client, foundMatchingCertificate bool, updateBaseURL *url.URL, _ error) {
schema := "http" schema := "http"
certPath, _, err := tlsflag.CertificatePathsFor(cfg.Hostname) certPath, _, err := tlsflag.CertificatePathsFor(cfg.Update.UseTLS, cfg.Hostname)
if err != nil { if err != nil {
return nil, false, nil, err return nil, false, nil, err
} }
@@ -37,12 +37,12 @@ func For(cfg *config.Struct) (_ *http.Client, foundMatchingCertificate bool, upd
update.Hostname = cfg.Hostname update.Hostname = cfg.Hostname
} }
updateBaseURL, err = updateflag.BaseURL(update.HTTPPort, update.HTTPSPort, schema, update.Hostname, update.HTTPPassword) updateBaseURL, err = updateVal.BaseURL(update.HTTPPort, update.HTTPSPort, schema, update.Hostname, update.HTTPPassword)
if err != nil { if err != nil {
return nil, false, nil, err return nil, false, nil, err
} }
hc, fmc, err := GetTLSHttpClientByTLSFlag(tlsflag.GetUseTLS(), tlsflag.GetInsecure(), updateBaseURL) hc, fmc, err := GetTLSHttpClientByTLSFlag(update.UseTLS, cfg.InternalCompatibilityFlags.Insecure, updateBaseURL)
if err != nil { if err != nil {
return nil, false, nil, fmt.Errorf("getting http client by tls flag: %v", err) return nil, false, nil, fmt.Errorf("getting http client by tls flag: %v", err)
} }

View File

@@ -12,11 +12,15 @@ package squashfs
import ( import (
"bytes" "bytes"
"cmp"
"compress/zlib" "compress/zlib"
"encoding/binary" "encoding/binary"
"fmt"
"io" "io"
"os" "os"
"path/filepath" "path/filepath"
"slices"
"strings"
"time" "time"
) )
@@ -115,6 +119,24 @@ type regInodeHeader struct {
// Followed by a uint32 array of compressed block sizes. // Followed by a uint32 array of compressed block sizes.
} }
// extFileType
type extInodeHeader struct {
inodeHeader
// full byte offset from the start of the file system, e.g. 96 for first
// file contents. Not using fragments limits us to 2^32-1-96 (≈ 4GiB) bytes
// of file contents.
StartBlock uint64
FileSize uint64
Sparse uint64
Links uint32
Fragment uint32
Offset uint32
xattr uint32
// Followed by a uint32 array of compressed block sizes.
}
// symlinkType // symlinkType
type symlinkInodeHeader struct { type symlinkInodeHeader struct {
inodeHeader inodeHeader
@@ -179,6 +201,24 @@ type dirEntry struct {
// Followed by a byte array of Size bytes. // Followed by a byte array of Size bytes.
} }
type xattr struct {
Type uint16
// KeySize uint16
Key string
// ValueSize uint16
Value []byte
}
func (x xattr) Equals(xattr xattr) bool {
return x.Type == xattr.Type && x.Key == xattr.Key && bytes.Equal(x.Value, xattr.Value)
}
type xattrOffsets struct {
Offset uint64 // Stored similar to an Inode
Count uint32
Size uint32
}
func writeIdTable(w io.WriteSeeker, ids []uint32) (start int64, err error) { func writeIdTable(w io.WriteSeeker, ids []uint32) (start int64, err error) {
metaOff, err := w.Seek(0, io.SeekCurrent) metaOff, err := w.Seek(0, io.SeekCurrent)
if err != nil { if err != nil {
@@ -202,6 +242,119 @@ func writeIdTable(w io.WriteSeeker, ids []uint32) (start int64, err error) {
return off, binary.Write(w, binary.LittleEndian, metaOff) return off, binary.Write(w, binary.LittleEndian, metaOff)
} }
func writeXattr(w io.Writer, xattr xattr) error {
if err := binary.Write(w, binary.LittleEndian, xattr.Type); err != nil {
return err
}
if err := binary.Write(w, binary.LittleEndian, uint16(len(xattr.Key))); err != nil {
return err
}
if err := binary.Write(w, binary.LittleEndian, []byte(xattr.Key)); err != nil {
return err
}
if err := binary.Write(w, binary.LittleEndian, uint32(len(xattr.Value))); err != nil {
return err
}
if err := binary.Write(w, binary.LittleEndian, xattr.Value); err != nil {
return err
}
return nil
}
func writeXattrTable(w io.WriteSeeker, xattrGroups [][]xattr) ([]xattrOffsets, error) {
var (
buf bytes.Buffer
err error
groupOffsets []xattrOffsets
)
for _, xattrs := range xattrGroups {
start := buf.Len()
for _, xattr := range xattrs {
if err = writeXattr(&buf, xattr); err != nil {
return []xattrOffsets{}, err
}
}
groupOffsets = append(groupOffsets, xattrOffsets{
Offset: uint64(0<<16 | start), // We only write one metadata block so index is always 0
Count: uint32(len(xattrs)),
Size: uint32(buf.Len() - start),
})
}
if buf.Len() > metadataBlockSize {
return []xattrOffsets{}, fmt.Errorf("too many xattrs defined")
}
if err := binary.Write(w, binary.LittleEndian, uint16(buf.Len())|0x8000); err != nil {
return []xattrOffsets{}, err
}
if _, err := io.Copy(w, &buf); err != nil {
return []xattrOffsets{}, err
}
return groupOffsets, nil
}
func writeXattrIdTable(w io.WriteSeeker, xattrGroups [][]xattr) (start int64, err error) {
if len(xattrGroups) == 0 {
// Sanity check as the linux kernel would not load the filesystem if there isn't a value here
return 0, fmt.Errorf("xattrGroups must have atleast one value")
}
kvOff, err := w.Seek(0, io.SeekCurrent)
if err != nil {
return 0, err
}
groupOffsets, err := writeXattrTable(w, xattrGroups)
if err != nil {
return 0, err
}
metaOff, err := w.Seek(0, io.SeekCurrent)
if err != nil {
return 0, err
}
var buf bytes.Buffer
if err := binary.Write(&buf, binary.LittleEndian, groupOffsets); err != nil {
return 0, err
}
if buf.Len() > metadataBlockSize {
return 0, fmt.Errorf("too many xattrs defined")
}
if err := binary.Write(w, binary.LittleEndian, uint16(buf.Len())|0x8000); err != nil {
return 0, err
}
if _, err := io.Copy(w, &buf); err != nil {
return 0, err
}
off, err := w.Seek(0, io.SeekCurrent)
if err != nil {
return 0, err
}
err = binary.Write(w, binary.LittleEndian, uint64(kvOff))
if err != nil {
return 0, err
}
err = binary.Write(w, binary.LittleEndian, uint32(len(xattrGroups)))
if err != nil {
return 0, err
}
err = binary.Write(w, binary.LittleEndian, uint32(0))
if err != nil {
return 0, err
}
err = binary.Write(w, binary.LittleEndian, uint64(metaOff))
if err != nil {
return 0, err
}
return off, nil
}
type fullDirEntry struct { type fullDirEntry struct {
startBlock uint32 startBlock uint32
offset uint16 offset uint16
@@ -225,9 +378,10 @@ type Writer struct {
w io.WriteSeeker w io.WriteSeeker
sb superblock sb superblock
inodeBuf bytes.Buffer inodeBuf bytes.Buffer
dirBuf bytes.Buffer dirBuf bytes.Buffer
xattrGroups [][]xattr
writeInodeNumTo map[string][]int64 writeInodeNumTo map[string][]int64
} }
@@ -325,6 +479,9 @@ type file struct {
modTime time.Time modTime time.Time
mode os.FileMode mode os.FileMode
// xattrs list of xattrs to attach to this file
xattrs []xattr
// buf accumulates at least dataBlockSize bytes, at which point a new block // buf accumulates at least dataBlockSize bytes, at which point a new block
// is being written. // is being written.
buf bytes.Buffer buf bytes.Buffer
@@ -352,11 +509,36 @@ func (d *Directory) Directory(name string, modTime time.Time) *Directory {
// File creates a file with the specified name, modTime and mode. The returned // File creates a file with the specified name, modTime and mode. The returned
// io.WriterCloser must be closed after writing the file. // io.WriterCloser must be closed after writing the file.
func (d *Directory) File(name string, modTime time.Time, mode os.FileMode) (io.WriteCloser, error) { func (d *Directory) File(name string, modTime time.Time, mode os.FileMode, xattrs map[string][]byte) (io.WriteCloser, error) {
off, err := d.w.w.Seek(0, io.SeekCurrent) off, err := d.w.w.Seek(0, io.SeekCurrent)
if err != nil { if err != nil {
return nil, err return nil, err
} }
var xattrlist []xattr
if len(xattrs) > 0 {
for key, value := range xattrs {
var (
keyType uint16
)
keys := strings.SplitN(key, ".", 2)
switch keys[0] {
case "user":
keyType = 0
case "trusted":
keyType = 1
case "security":
keyType = 2
default:
return nil, fmt.Errorf("Invalid xattr key: %s, key must start with security, trusted or user", key)
}
xattrlist = append(xattrlist, xattr{
Type: keyType,
Key: keys[1],
Value: value,
})
}
}
// zlib.BestSpeed results in only a 2x slow-down over no compression // zlib.BestSpeed results in only a 2x slow-down over no compression
// (compared to >4x slow-down with DefaultCompression), but generates // (compared to >4x slow-down with DefaultCompression), but generates
@@ -374,6 +556,7 @@ func (d *Directory) File(name string, modTime time.Time, mode os.FileMode) (io.W
mode: mode, mode: mode,
compBuf: bytes.NewBuffer(make([]byte, dataBlockSize)), compBuf: bytes.NewBuffer(make([]byte, dataBlockSize)),
zlibWriter: zw, zlibWriter: zw,
xattrs: xattrlist,
}, nil }, nil
} }
@@ -605,22 +788,44 @@ func (f *file) Close() error {
startBlock := f.w.inodeBuf.Len() / metadataBlockSize startBlock := f.w.inodeBuf.Len() / metadataBlockSize
offset := f.w.inodeBuf.Len() - startBlock*metadataBlockSize offset := f.w.inodeBuf.Len() - startBlock*metadataBlockSize
if len(f.xattrs) > 0 {
if err := binary.Write(&f.w.inodeBuf, binary.LittleEndian, regInodeHeader{ extinode := extInodeHeader{
inodeHeader: inodeHeader{ inodeHeader: inodeHeader{
InodeType: fileType, InodeType: lregType,
Mode: uint16(f.mode), Mode: uint16(f.mode),
Uid: 0, Uid: 0,
Gid: 0, Gid: 0,
Mtime: int32(f.modTime.Unix()), Mtime: int32(f.modTime.Unix()),
InodeNumber: f.w.sb.Inodes + 1, InodeNumber: f.w.sb.Inodes + 1,
}, },
StartBlock: uint32(f.off), // TODO(later): check for overflow StartBlock: uint64(f.off), // TODO(later): check for overflow
Fragment: invalidFragment, FileSize: uint64(f.size),
Offset: 0, Sparse: 0,
FileSize: f.size, Links: 1, // We don't support hardlinks at this time
}); err != nil { Fragment: invalidFragment,
return err Offset: 0,
xattr: f.w.addXattrs(f.xattrs),
}
if err := binary.Write(&f.w.inodeBuf, binary.LittleEndian, extinode); err != nil {
return err
}
} else {
if err := binary.Write(&f.w.inodeBuf, binary.LittleEndian, regInodeHeader{
inodeHeader: inodeHeader{
InodeType: fileType,
Mode: uint16(f.mode),
Uid: 0,
Gid: 0,
Mtime: int32(f.modTime.Unix()),
InodeNumber: f.w.sb.Inodes + 1,
},
StartBlock: uint32(f.off), // TODO(later): check for overflow
Fragment: invalidFragment,
Offset: 0,
FileSize: f.size,
}); err != nil {
return err
}
} }
if err := binary.Write(&f.w.inodeBuf, binary.LittleEndian, f.blocksizes); err != nil { if err := binary.Write(&f.w.inodeBuf, binary.LittleEndian, f.blocksizes); err != nil {
@@ -711,8 +916,14 @@ func (w *Writer) Flush() error {
} }
w.sb.IdTableStart = idTableStart w.sb.IdTableStart = idTableStart
// (9) xattr table omitted // (9) xattr table must be omitted if there are no xattrs
if len(w.xattrGroups) != 0 {
xattrIdTableStart, err := writeXattrIdTable(w.w, w.xattrGroups)
if err != nil {
return err
}
w.sb.XattrIdTableStart = xattrIdTableStart
}
off, err = w.w.Seek(0, io.SeekCurrent) off, err = w.w.Seek(0, io.SeekCurrent)
if err != nil { if err != nil {
return err return err
@@ -734,3 +945,31 @@ func (w *Writer) Flush() error {
return binary.Write(w.w, binary.LittleEndian, &w.sb) return binary.Write(w.w, binary.LittleEndian, &w.sb)
} }
func xattrCompare(a xattr, b xattr) int {
return cmp.Or(
cmp.Compare(a.Type, b.Type),
cmp.Compare(a.Key, b.Key),
bytes.Compare(a.Value, b.Value),
)
}
func (w *Writer) addXattrs(xattrs []xattr) uint32 {
// Does trivial xattr de-duplication. More complicated de-duplication is possible if space is a concern
slices.SortFunc(xattrs, xattrCompare)
xattrGroup:
for i, group := range w.xattrGroups {
if len(group) != len(xattrs) {
continue
}
for x, xattr := range xattrs {
if !xattr.Equals(group[x]) {
continue xattrGroup
}
}
return uint32(i)
}
i := len(w.xattrGroups)
w.xattrGroups = append(w.xattrGroups, xattrs)
return uint32(i)
}

View File

@@ -43,7 +43,7 @@ func TestUnsquashfs(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
ff, err := w.Root.File("hellö wörld", time.Now(), 0o444 /* u=r,g=r,o=r */) ff, err := w.Root.File("hellö wörld", time.Now(), 0o444 /* u=r,g=r,o=r */, map[string][]byte{})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -54,7 +54,7 @@ func TestUnsquashfs(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
ff, err = w.Root.File("leer", time.Now(), 0o444 /* u=r,g=r,o=r */) ff, err = w.Root.File("leer", time.Now(), 0o444 /* u=r,g=r,o=r */, map[string][]byte{})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -62,7 +62,7 @@ func TestUnsquashfs(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
ff, err = w.Root.File("second file", time.Now(), 0o555 /* u=rx,g=rx,o=rx */) ff, err = w.Root.File("second file", time.Now(), 0o555 /* u=rx,g=rx,o=rx */, map[string][]byte{})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -80,7 +80,7 @@ func TestUnsquashfs(t *testing.T) {
subdir := w.Root.Directory("subdir", time.Now()) subdir := w.Root.Directory("subdir", time.Now())
subsubdir := subdir.Directory("deep", time.Now()) subsubdir := subdir.Directory("deep", time.Now())
ff, err = subsubdir.File("yo", time.Now(), 0o444 /* u=r,g=r,o=r */) ff, err = subsubdir.File("yo", time.Now(), 0o444 /* u=r,g=r,o=r */, map[string][]byte{})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -96,7 +96,7 @@ func TestUnsquashfs(t *testing.T) {
// TODO: write another file in subdir now, will result in invalid parent inode // TODO: write another file in subdir now, will result in invalid parent inode
ff, err = subdir.File("third file (in subdir)", time.Now(), 0o444 /* u=r,g=r,o=r */) ff, err = subdir.File("third file (in subdir)", time.Now(), 0o444 /* u=r,g=r,o=r */, map[string][]byte{})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -110,7 +110,7 @@ func TestUnsquashfs(t *testing.T) {
if err := subdir.Flush(); err != nil { if err := subdir.Flush(); err != nil {
t.Fatal(err) t.Fatal(err)
} }
ff, err = w.Root.File("testbin", time.Now(), 0o555 /* u=rx,g=rx,o=rx */) ff, err = w.Root.File("testbin", time.Now(), 0o555 /* u=rx,g=rx,o=rx */, map[string][]byte{})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@@ -1,7 +1,6 @@
package tlsflag package tlsflag
import ( import (
"flag"
"fmt" "fmt"
"os" "os"
"path/filepath" "path/filepath"
@@ -10,30 +9,6 @@ import (
"github.com/gokrazy/internal/config" "github.com/gokrazy/internal/config"
) )
var (
useTLS string
insecure bool
)
func RegisterFlags(fs *flag.FlagSet) {
fs.StringVar(&useTLS,
"tls",
"",
`TLS certificate for the web interface (-tls=<certificate or full chain path>,<private key path>).
Use -tls=self-signed to generate a self-signed RSA4096 certificate using the hostname specified with -hostname. In this case, the certificate and key will be placed in your local config folder (on Linux: ~/.config/gokrazy/<hostname>/).
WARNING: When reusing a hostname, no new certificate will be generated and the stored one will be used.
When updating a running instance, the specified certificate will be used to verify the connection. Otherwise the updater will load the hostname-specific certificate from your local config folder in addition to the system trust store.
You can also create your own certificate-key-pair (e.g. by using https://github.com/FiloSottile/mkcert) and place them into your local config folder.`)
fs.BoolVar(&insecure, "insecure",
false,
"Ignore TLS stripping detection.")
}
func Insecure() bool {
return insecure
}
type ErrNotYetCreated struct { type ErrNotYetCreated struct {
HostConfigPath string HostConfigPath string
CertPath string CertPath string
@@ -44,7 +19,7 @@ func (e *ErrNotYetCreated) Error() string {
return "self-signed certificate not yet created" return "self-signed certificate not yet created"
} }
func CertificatePathsFor(hostname string) (certPath string, keyPath string, _ error) { func CertificatePathsFor(useTLS, hostname string) (certPath string, keyPath string, _ error) {
hostConfigPath := config.HostnameSpecific(hostname) hostConfigPath := config.HostnameSpecific(hostname)
certPath = filepath.Join(string(hostConfigPath), "cert.pem") certPath = filepath.Join(string(hostConfigPath), "cert.pem")
keyPath = filepath.Join(string(hostConfigPath), "key.pem") keyPath = filepath.Join(string(hostConfigPath), "key.pem")
@@ -95,11 +70,3 @@ func CertificatePathsFor(hostname string) (certPath string, keyPath string, _ er
} }
return certPath, keyPath, nil return certPath, keyPath, nil
} }
func GetUseTLS() string { return useTLS }
func SetUseTLS(s string) { useTLS = s }
func GetInsecure() bool { return insecure }
func SetInsecure(b bool) { insecure = b }

View File

@@ -1,48 +1,29 @@
package updateflag package updateflag
import ( import (
"flag"
"net/url" "net/url"
"os"
"strings" "strings"
"github.com/spf13/pflag"
) )
var update string type Value struct {
Update string
func registerFlags(fs interface {
StringVar(*string, string, string, string)
}, updateFlagName string) {
fs.StringVar(&update,
updateFlagName,
os.Getenv("GOKRAZY_UPDATE"),
`URL of a gokrazy installation (e.g. http://gokrazy:mypassword@myhostname/) to work with. The special value "yes" uses the stored password and -hostname value to construct the URL`)
} }
func RegisterFlags(fs *flag.FlagSet, updateFlagName string) { func (v Value) GetUpdateTarget(hostname string) (defaultPassword, updateHostname string) {
registerFlags(fs, updateFlagName) if v.Update == "" {
}
func RegisterPflags(fs *pflag.FlagSet, updateFlagName string) {
registerFlags(fs, updateFlagName)
}
func GetUpdateTarget(hostname string) (defaultPassword, updateHostname string) {
if update == "" {
// -update not set // -update not set
return "", hostname return "", hostname
} }
if update == "yes" { if v.Update == "yes" {
// -update=yes // -update=yes
return "", hostname return "", hostname
} }
if strings.HasPrefix(update, ":") { if strings.HasPrefix(v.Update, ":") {
// port number syntax, e.g. -update=:2080 // port number syntax, e.g. -update=:2080
return "", hostname return "", hostname
} }
// -update=<url> syntax // -update=<url> syntax
u, err := url.Parse(update) u, err := url.Parse(v.Update)
if err != nil { if err != nil {
return "", hostname return "", hostname
} }
@@ -50,10 +31,10 @@ func GetUpdateTarget(hostname string) (defaultPassword, updateHostname string) {
return defaultPassword, u.Host return defaultPassword, u.Host
} }
func BaseURL(httpPort, httpsPort, schema, hostname, pw string) (*url.URL, error) { func (v Value) BaseURL(httpPort, httpsPort, schema, hostname, pw string) (*url.URL, error) {
if update != "yes" && !strings.HasPrefix(update, ":") { if v.Update != "yes" && !strings.HasPrefix(v.Update, ":") {
// already fully qualified, nothing to add // already fully qualified, nothing to add
return url.Parse(update) return url.Parse(v.Update)
} }
port := httpPort port := httpPort
defaultPort := "80" defaultPort := "80"
@@ -61,21 +42,13 @@ func BaseURL(httpPort, httpsPort, schema, hostname, pw string) (*url.URL, error)
port = httpsPort port = httpsPort
defaultPort = "443" defaultPort = "443"
} }
if strings.HasPrefix(update, ":") { if strings.HasPrefix(v.Update, ":") {
port = strings.TrimPrefix(update, ":") port = strings.TrimPrefix(v.Update, ":")
} }
update = schema + "://gokrazy:" + pw + "@" + hostname v.Update = schema + "://gokrazy:" + pw + "@" + hostname
if port != defaultPort { if port != defaultPort {
update += ":" + port v.Update += ":" + port
} }
update += "/" v.Update += "/"
return url.Parse(update) return url.Parse(v.Update)
} }
func NewInstallation() bool {
return update == ""
}
func SetUpdate(u string) { update = u }
func GetUpdate() string { return update }

View File

@@ -47,8 +47,9 @@ func TestBaseURL(t *testing.T) {
}, },
} { } {
t.Run(tt.desc, func(t *testing.T) { t.Run(tt.desc, func(t *testing.T) {
updateflag.SetUpdate("yes") got, err := updateflag.Value{
got, err := updateflag.BaseURL(tt.HTTPPort, tt.HTTPSPort, tt.Schema, tt.Hostname, tt.Password) Update: "yes",
}.BaseURL(tt.HTTPPort, tt.HTTPSPort, tt.Schema, tt.Hostname, tt.Password)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }