mirror of
https://github.com/fleetdm/fleet
synced 2026-05-23 08:58:41 +00:00
This reverts commit 7767da94e8.
This was accidentally force-pushed before merging and included the incorrect changes.
This commit is contained in:
parent
7767da94e8
commit
3d1cf985a7
10 changed files with 21 additions and 438 deletions
|
|
@ -11,7 +11,6 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/dgraph-io/badger/v2"
|
||||
"github.com/fleetdm/orbit/pkg/certificate"
|
||||
"github.com/fleetdm/orbit/pkg/constant"
|
||||
"github.com/fleetdm/orbit/pkg/database"
|
||||
|
|
@ -27,6 +26,7 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
certPath = "/tmp/fleet.pem"
|
||||
defaultRootDir = "/var/lib/orbit"
|
||||
)
|
||||
|
||||
|
|
@ -143,18 +143,9 @@ func main() {
|
|||
return errors.Wrap(err, "initialize root dir")
|
||||
}
|
||||
|
||||
dbPath := filepath.Join(c.String("root-dir"), "orbit.db")
|
||||
db, err := database.Open(dbPath)
|
||||
db, err := database.Open(filepath.Join(c.String("root-dir"), "orbit.db"))
|
||||
if err != nil {
|
||||
if errors.Is(err, badger.ErrTruncateNeeded) {
|
||||
db, err = database.OpenTruncate(dbPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Warn().Msg("Open badger required truncate. Possible data loss.")
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if err := db.Close(); err != nil {
|
||||
|
|
@ -238,8 +229,7 @@ func main() {
|
|||
)
|
||||
|
||||
// Write cert that proxy uses
|
||||
certPath := filepath.Join(opt.RootDirectory, "insecure-cert.pem")
|
||||
err = ioutil.WriteFile(filepath.Join(opt.RootDirectory, "insecure-cert.pem"), []byte(insecure.ServerCert), os.ModePerm)
|
||||
err = ioutil.WriteFile(certPath, []byte(insecure.ServerCert), os.ModePerm)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "write server cert")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -129,10 +129,8 @@ func main() {
|
|||
return packaging.BuildDeb(opt)
|
||||
case "rpm":
|
||||
return packaging.BuildRPM(opt)
|
||||
case "msi":
|
||||
return packaging.BuildMSI(opt)
|
||||
default:
|
||||
return errors.New("type must be one of ('pkg', 'deb', 'rpm', 'msi')")
|
||||
return errors.New("type must be one of ('pkg', 'deb', 'rpm')")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ type BadgerDB struct {
|
|||
closeChan chan struct{}
|
||||
}
|
||||
|
||||
// Open opens (initializing if necessary) a Badger database at the specified
|
||||
// Open opens (initializing if necessary) a new Badger database at the specified
|
||||
// path. Users must close the DB with Close().
|
||||
func Open(path string) (*BadgerDB, error) {
|
||||
// DefaultOptions sets synchronous writes to true (maximum data integrity).
|
||||
|
|
@ -38,26 +38,6 @@ func Open(path string) (*BadgerDB, error) {
|
|||
return b, nil
|
||||
}
|
||||
|
||||
// OpenTruncate opens (initializing and/or truncating if necessary) a Badger
|
||||
// database at the specified path. Users must close the DB with Close().
|
||||
//
|
||||
// Prefer Open in the general case, but after a bad shutdown it may be necessary
|
||||
// to call OpenTruncate. This may cause data loss. Detect this situation by
|
||||
// looking for badger.ErrTruncateNeeded.
|
||||
func OpenTruncate(path string) (*BadgerDB, error) {
|
||||
// DefaultOptions sets synchronous writes to true (maximum data integrity).
|
||||
// TODO implement logging?
|
||||
db, err := badger.Open(badger.DefaultOptions(path).WithLogger(nil).WithTruncate(true))
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "open badger with truncate %s", path)
|
||||
}
|
||||
|
||||
b := &BadgerDB{DB: db}
|
||||
b.startBackgroundCompaction()
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// startBackgroundCompaction starts a background loop that will call the
|
||||
// compaction method on the database. Badger does not do this automatically, so
|
||||
// we need to be sure to do so here (or elsewhere).
|
||||
|
|
|
|||
|
|
@ -156,6 +156,20 @@ func writeScripts(opt Options, rootPath string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func writeSecret(opt Options, orbitRoot string) error {
|
||||
// Enroll secret
|
||||
path := filepath.Join(orbitRoot, "secret")
|
||||
if err := os.MkdirAll(filepath.Dir(path), constant.DefaultDirMode); err != nil {
|
||||
return errors.Wrap(err, "mkdir")
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(path, []byte(opt.EnrollSecret), 0600); err != nil {
|
||||
return errors.Wrap(err, "write file")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeLaunchd(opt Options, rootPath string) error {
|
||||
// launchd is the service mechanism on macOS
|
||||
path := filepath.Join(rootPath, "Library", "LaunchDaemons", "com.fleetdm.orbit.plist")
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ var macosLaunchdTemplate = template.Must(template.New("").Option("missingkey=err
|
|||
{{ if .Insecure }}<key>ORBIT_INSECURE</key><string>true</string>{{ end }}
|
||||
{{ if .FleetURL }}<key>ORBIT_FLEET_URL</key><string>{{ .FleetURL }}</string>{{ end }}
|
||||
{{ if .FleetCertificate }}<key>ORBIT_FLEET_CERTIFICATE</key><string>/var/lib/orbit/fleet.pem</string>{{ end }}
|
||||
{{ if .EnrollSecret }}<key>ORBIT_ENROLL_SECRET_PATH</key><string>/var/lib/orbit/secret.txt</string>{{ end }}
|
||||
{{ if .EnrollSecret }}<key>ORBIT_ENROLL_SECRET_PATH</key><string>/var/lib/orbit/secret</string>{{ end }}
|
||||
{{ if .Debug }}<key>ORBIT_DEBUG</key><string>true</string>{{ end }}
|
||||
</dict>
|
||||
<key>KeepAlive</key><true/>
|
||||
|
|
|
|||
|
|
@ -3,11 +3,9 @@ package packaging
|
|||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/fleetdm/orbit/pkg/constant"
|
||||
"github.com/fleetdm/orbit/pkg/update"
|
||||
"github.com/fleetdm/orbit/pkg/update/filestore"
|
||||
"github.com/pkg/errors"
|
||||
|
|
@ -100,17 +98,3 @@ func initializeUpdates(updateOpt update.Options) error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
// writeSecret writes the enroll secret to a text file
|
||||
func writeSecret(opt Options, orbitRoot string) error {
|
||||
path := filepath.Join(orbitRoot, "secret.txt")
|
||||
if err := os.MkdirAll(filepath.Dir(path), constant.DefaultDirMode); err != nil {
|
||||
return errors.Wrap(err, "mkdir")
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(path, []byte(opt.EnrollSecret), 0600); err != nil {
|
||||
return errors.Wrap(err, "write file")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,104 +0,0 @@
|
|||
package packaging
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/fleetdm/orbit/pkg/constant"
|
||||
"github.com/fleetdm/orbit/pkg/packaging/wix"
|
||||
"github.com/fleetdm/orbit/pkg/update"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
// BuildMSI builds a Windows .msi.
|
||||
func BuildMSI(opt Options) error {
|
||||
// Initialize directories
|
||||
tmpDir, err := ioutil.TempDir("", "orbit-package")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to create temp dir")
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
log.Debug().Str("path", tmpDir).Msg("created temp dir")
|
||||
|
||||
filesystemRoot := filepath.Join(tmpDir, "root")
|
||||
if err := os.MkdirAll(filesystemRoot, constant.DefaultDirMode); err != nil {
|
||||
return errors.Wrap(err, "create root dir")
|
||||
}
|
||||
orbitRoot := filesystemRoot
|
||||
if err := os.MkdirAll(orbitRoot, constant.DefaultDirMode); err != nil {
|
||||
return errors.Wrap(err, "create orbit dir")
|
||||
}
|
||||
|
||||
// Initialize autoupdate metadata
|
||||
|
||||
updateOpt := update.DefaultOptions
|
||||
updateOpt.Platform = "windows"
|
||||
updateOpt.RootDirectory = orbitRoot
|
||||
updateOpt.OrbitChannel = opt.OrbitChannel
|
||||
updateOpt.OsquerydChannel = opt.OsquerydChannel
|
||||
updateOpt.ServerURL = opt.UpdateURL
|
||||
if opt.UpdateRoots != "" {
|
||||
updateOpt.RootKeys = opt.UpdateRoots
|
||||
}
|
||||
|
||||
if err := initializeUpdates(updateOpt); err != nil {
|
||||
return errors.Wrap(err, "initialize updates")
|
||||
}
|
||||
|
||||
// Write files
|
||||
|
||||
if err := writeSecret(opt, orbitRoot); err != nil {
|
||||
return errors.Wrap(err, "write enroll secret")
|
||||
}
|
||||
|
||||
if err := writeWixFile(opt, tmpDir); err != nil {
|
||||
return errors.Wrap(err, "write wix file")
|
||||
}
|
||||
|
||||
if err := wix.Heat(tmpDir); err != nil {
|
||||
return errors.Wrap(err, "package root files")
|
||||
}
|
||||
|
||||
if err := wix.TransformHeat(filepath.Join(tmpDir, "heat.wxs")); err != nil {
|
||||
return errors.Wrap(err, "transform heat")
|
||||
}
|
||||
|
||||
if err := wix.Candle(tmpDir); err != nil {
|
||||
return errors.Wrap(err, "build package")
|
||||
}
|
||||
|
||||
if err := wix.Light(tmpDir); err != nil {
|
||||
return errors.Wrap(err, "build package")
|
||||
}
|
||||
|
||||
filename := fmt.Sprintf("orbit-osquery_%s.msi", opt.Version)
|
||||
if err := os.Rename(filepath.Join(tmpDir, "orbit.msi"), filename); err != nil {
|
||||
return errors.Wrap(err, "rename msi")
|
||||
}
|
||||
log.Info().Str("path", filename).Msg("wrote msi package")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeWixFile(opt Options, rootPath string) error {
|
||||
// PackageInfo is metadata for the pkg
|
||||
path := filepath.Join(rootPath, "main.wxs")
|
||||
if err := os.MkdirAll(filepath.Dir(path), constant.DefaultDirMode); err != nil {
|
||||
return errors.Wrap(err, "mkdir")
|
||||
}
|
||||
|
||||
var contents bytes.Buffer
|
||||
if err := windowsWixTemplate.Execute(&contents, opt); err != nil {
|
||||
return errors.Wrap(err, "execute template")
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(path, contents.Bytes(), 0o666); err != nil {
|
||||
return errors.Wrap(err, "write file")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
package packaging
|
||||
|
||||
import "text/template"
|
||||
|
||||
// Partially adapted from Launcher's wix XML in
|
||||
// https://github.com/kolide/launcher/blob/master/pkg/packagekit/internal/assets/main.wxs.
|
||||
var windowsWixTemplate = template.Must(template.New("").Option("missingkey=error").Parse(
|
||||
`<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
|
||||
<Product
|
||||
Id="C2C2437D-0562-465E-A0BB-2C4484025BD6"
|
||||
Name="Orbit osquery"
|
||||
Language="1033"
|
||||
Version="{{.Version}}"
|
||||
Manufacturer="Fleet Device Management (fleetdm.com)"
|
||||
UpgradeCode="B681CB20-107E-428A-9B14-2D3C1AFED244" >
|
||||
|
||||
<Package
|
||||
Id="*"
|
||||
Keywords='orbit osquery'
|
||||
Description="Orbit osquery"
|
||||
InstallerVersion="500"
|
||||
Compressed="yes"
|
||||
InstallScope="perMachine"
|
||||
InstallPrivileges="elevated"
|
||||
Languages="1033" />
|
||||
|
||||
<MediaTemplate EmbedCab="yes" />
|
||||
|
||||
<MajorUpgrade AllowDowngrades="yes" />
|
||||
|
||||
<Directory Id="TARGETDIR" Name="SourceDir">
|
||||
<Directory Id="ProgramFiles64Folder">
|
||||
<Directory Id="ORBITROOT" Name="Orbit">
|
||||
<Component Id="C_ORBITROOT" Guid="A7DFD09E-2D2B-4535-A04F-5D4DE90F3863">
|
||||
<CreateFolder>
|
||||
<PermissionEx Sddl="O:SYG:SYD:P(A;OICI;FA;;;SY)(A;OICI;FA;;;BA)(A;OICI;0x1200a9;;;BU)" />
|
||||
</CreateFolder>
|
||||
</Component>
|
||||
<Directory Id="ORBITBIN" Name="bin">
|
||||
<Component Id="C_ORBITBIN" Guid="AF347B4E-B84B-4DD4-9C4D-133BE17B613D">
|
||||
<CreateFolder>
|
||||
<PermissionEx Sddl="O:SYG:SYD:P(A;OICI;FA;;;SY)(A;OICI;FA;;;BA)(A;OICI;0x1200a9;;;BU)" />
|
||||
</CreateFolder>
|
||||
<File Source="root\bin\orbit\windows\stable\orbit.exe">
|
||||
<PermissionEx Sddl="O:SYG:SYD:P(A;OICI;FA;;;SY)(A;OICI;FA;;;BA)(A;OICI;0x1200a9;;;BU)" />
|
||||
</File>
|
||||
<ServiceInstall
|
||||
Name="Orbit osquery"
|
||||
Account="NT AUTHORITY\SYSTEM"
|
||||
ErrorControl="ignore"
|
||||
Start="auto"
|
||||
Type="ownProcess"
|
||||
Arguments='--root-dir "[ORBITROOT]." {{ if .FleetURL }}--fleet-url "{{ .FleetURL }}"{{ end }} {{ if .EnrollSecret }}--enroll-secret-path "[ORBITROOT]secret.txt"{{ end }} {{if .Insecure }}--insecure{{ end }} {{ if .UpdateURL }}--update-url "{{ .UpdateURL }}"{{ end }}'
|
||||
>
|
||||
</ServiceInstall>
|
||||
<ServiceControl
|
||||
Id="StartOrbitService"
|
||||
Name="Orbit osquery"
|
||||
Start="install"
|
||||
Stop="both"
|
||||
Remove="uninstall"
|
||||
/>
|
||||
</Component>
|
||||
</Directory>
|
||||
</Directory>
|
||||
</Directory>
|
||||
</Directory>
|
||||
|
||||
<Feature Id="Orbit" Title="Orbit osquery" Level="1" Display="hidden">
|
||||
<ComponentGroupRef Id="OrbitFiles" />
|
||||
<ComponentRef Id="C_ORBITBIN" />
|
||||
<ComponentRef Id="C_ORBITROOT" />
|
||||
</Feature>
|
||||
|
||||
</Product>
|
||||
</Wix>
|
||||
`))
|
||||
|
|
@ -1,113 +0,0 @@
|
|||
package wix
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/xml"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type node struct {
|
||||
XMLName xml.Name
|
||||
Attrs attrs `xml:",any,attr"`
|
||||
Content string `xml:",chardata"`
|
||||
Children []*node `xml:",any"`
|
||||
}
|
||||
|
||||
type attrs []*xml.Attr
|
||||
|
||||
// Get the value of the attr with the provided name, otherwise returning an
|
||||
// empty string.
|
||||
func (a attrs) Get(name string) string {
|
||||
for _, attr := range a {
|
||||
if attr.Name.Local == name {
|
||||
return attr.Value
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func xmlAttr(name, value string) *xml.Attr {
|
||||
return &xml.Attr{Name: xml.Name{Local: name}, Value: value}
|
||||
}
|
||||
|
||||
func xmlNode(name string, attrs ...*xml.Attr) *node {
|
||||
return &node{
|
||||
XMLName: xml.Name{Local: name},
|
||||
Attrs: attrs,
|
||||
}
|
||||
}
|
||||
|
||||
func TransformHeat(path string) error {
|
||||
contents, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "read file")
|
||||
}
|
||||
|
||||
// Eliminate line feeds (they cause extra junk in the result)
|
||||
contents = bytes.ReplaceAll(contents, []byte("\r"), []byte(""))
|
||||
|
||||
var n node
|
||||
if err := xml.Unmarshal(contents, &n); err != nil {
|
||||
return errors.Wrap(err, "unmarshal xml")
|
||||
}
|
||||
|
||||
stack := []*node{}
|
||||
if err := transform(&n, &stack); err != nil {
|
||||
return errors.Wrap(err, "in transform")
|
||||
}
|
||||
|
||||
contents, err = xml.MarshalIndent(n, "", " ")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "marshal xml")
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(path, contents, 0o600); err != nil {
|
||||
return errors.Wrap(err, "write file")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func transform(cur *node, stack *[]*node) error {
|
||||
// Clear namespace on all elements (generates unnecessarily noisy output if
|
||||
// this is not done).
|
||||
cur.XMLName.Space = ""
|
||||
|
||||
// Change permissions for all files
|
||||
if cur.XMLName.Local == "File" {
|
||||
// This SDDL copied directly from osqueryd.exe after a regular
|
||||
// osquery MSI install. We assume that osquery is getting the
|
||||
// permissions correct and use exactly the same for our files.
|
||||
// Using this cryptic string seems to be the only way to disable
|
||||
// permission inheritance in a WiX package, so we may not have
|
||||
// any option for something more readable.
|
||||
sddl := "O:SYG:SYD:P(A;OICI;FA;;;SY)(A;OICI;FA;;;BA)(A;OICI;0x1200a9;;;BU)"
|
||||
if cur.Attrs.Get("Name") == "secret.txt" {
|
||||
// This SDDL copied from properly configured file on a Windows
|
||||
// 10 machine. Permissions are same as below but with read
|
||||
// access removed for regular users.
|
||||
sddl = "O:SYG:SYD:PAI(A;;FA;;;SY)(A;;FA;;;BA)"
|
||||
|
||||
}
|
||||
cur.Children = append(cur.Children, xmlNode(
|
||||
"PermissionEx",
|
||||
xmlAttr("Sddl", sddl),
|
||||
))
|
||||
}
|
||||
|
||||
// push current node onto stack
|
||||
*stack = append(*stack, cur)
|
||||
// Recursively walk the children
|
||||
for _, child := range cur.Children {
|
||||
if err := transform(child, stack); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// pop current node from stack
|
||||
*stack = (*stack)[:len(*stack)-1]
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -1,88 +0,0 @@
|
|||
// Package wix runs the WiX packaging tools via Docker.
|
||||
//
|
||||
// WiX's documentation is available at https://wixtoolset.org/.
|
||||
package wix
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
directoryReference = "ORBITROOT"
|
||||
)
|
||||
|
||||
// Heat runs the WiX Heat command on the provided directory.
|
||||
//
|
||||
// The Heat command creates XML fragments allowing WiX to include the entire
|
||||
// directory. See
|
||||
// https://wixtoolset.org/documentation/manual/v3/overview/heat.html.
|
||||
func Heat(path string) error {
|
||||
cmd := exec.Command(
|
||||
"docker", "run", "--rm", "--platform", "linux/386",
|
||||
"--volume", path+":/wix", // mount volume
|
||||
"dactiv/wix:latest", // image name
|
||||
"heat", "dir", "root", // command in image
|
||||
"-out", "heat.wxs",
|
||||
"-gg", "-g1", // generate UUIDs (required by wix)
|
||||
"-cg", "OrbitFiles", // set ComponentGroup name
|
||||
"-scom", "-sfrag", "-srd", "-sreg", // suppress unneccesary generated items
|
||||
"-dr", directoryReference, // set reference name
|
||||
"-ke", // keep empty directories
|
||||
)
|
||||
cmd.Stdout, cmd.Stderr = os.Stdout, os.Stderr
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
return errors.Wrap(err, "heat failed")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Candle runs the WiX Candle command on the provided directory.
|
||||
//
|
||||
// See
|
||||
// https://wixtoolset.org/documentation/manual/v3/overview/candle.html.
|
||||
func Candle(path string) error {
|
||||
cmd := exec.Command(
|
||||
"docker", "run", "--rm", "--platform", "linux/386",
|
||||
"--volume", path+":/wix", // mount volume
|
||||
"dactiv/wix:latest", // image name
|
||||
"candle", "heat.wxs", "main.wxs", // command in image
|
||||
"-ext", "WixUtilExtension",
|
||||
"-arch", "x64",
|
||||
)
|
||||
cmd.Stdout, cmd.Stderr = os.Stdout, os.Stderr
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
return errors.Wrap(err, "candle failed")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Light runs the WiX Light command on the provided directory.
|
||||
//
|
||||
// See
|
||||
// https://wixtoolset.org/documentation/manual/v3/overview/light.html.
|
||||
func Light(path string) error {
|
||||
cmd := exec.Command(
|
||||
"docker", "run", "--rm", "--platform", "linux/386",
|
||||
"--volume", path+":/wix", // mount volume
|
||||
"dactiv/wix:latest", // image name
|
||||
"light", "heat.wixobj", "main.wixobj", // command in image
|
||||
"-ext", "WixUtilExtension",
|
||||
"-b", "root", // Set directory for finding heat files
|
||||
"-out", "orbit.msi",
|
||||
"-sval", // skip validation (otherwise Wine crashes)
|
||||
)
|
||||
cmd.Stdout, cmd.Stderr = os.Stdout, os.Stderr
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
return errors.Wrap(err, "light failed")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Loading…
Reference in a new issue