Code cleanup; use a worker pool for updating file permissions to avoid run-away go-routines
Co-Authored-By: Jakob <schrej@users.noreply.github.com>
This commit is contained in:
parent
38efb68e8a
commit
5e8425ad6a
|
@ -1,12 +1,13 @@
|
||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/apex/log"
|
"github.com/apex/log"
|
||||||
"github.com/cobaugh/osrelease"
|
"github.com/cobaugh/osrelease"
|
||||||
"github.com/creasty/defaults"
|
"github.com/creasty/defaults"
|
||||||
|
"github.com/gammazero/workerpool"
|
||||||
"github.com/gbrlsnchs/jwt/v3"
|
"github.com/gbrlsnchs/jwt/v3"
|
||||||
|
"github.com/pkg/errors"
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v2"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
@ -14,6 +15,7 @@ import (
|
||||||
"os/user"
|
"os/user"
|
||||||
"path"
|
"path"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
@ -154,7 +156,7 @@ func ReadConfiguration(path string) (*Configuration, error) {
|
||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var Mutex sync.RWMutex
|
var mu sync.RWMutex
|
||||||
|
|
||||||
var _config *Configuration
|
var _config *Configuration
|
||||||
var _jwtAlgo *jwt.HMACSHA
|
var _jwtAlgo *jwt.HMACSHA
|
||||||
|
@ -164,14 +166,14 @@ var _debugViaFlag bool
|
||||||
// anything trying to set a different configuration value, or read the configuration
|
// anything trying to set a different configuration value, or read the configuration
|
||||||
// will be paused until it is complete.
|
// will be paused until it is complete.
|
||||||
func Set(c *Configuration) {
|
func Set(c *Configuration) {
|
||||||
Mutex.Lock()
|
mu.Lock()
|
||||||
|
|
||||||
if _config == nil || _config.AuthenticationToken != c.AuthenticationToken {
|
if _config == nil || _config.AuthenticationToken != c.AuthenticationToken {
|
||||||
_jwtAlgo = jwt.NewHS256([]byte(c.AuthenticationToken))
|
_jwtAlgo = jwt.NewHS256([]byte(c.AuthenticationToken))
|
||||||
}
|
}
|
||||||
|
|
||||||
_config = c
|
_config = c
|
||||||
Mutex.Unlock()
|
mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetDebugViaFlag(d bool) {
|
func SetDebugViaFlag(d bool) {
|
||||||
|
@ -181,16 +183,16 @@ func SetDebugViaFlag(d bool) {
|
||||||
// Get the global configuration instance. This is a read-safe operation that will block
|
// Get the global configuration instance. This is a read-safe operation that will block
|
||||||
// if the configuration is presently being modified.
|
// if the configuration is presently being modified.
|
||||||
func Get() *Configuration {
|
func Get() *Configuration {
|
||||||
Mutex.RLock()
|
mu.RLock()
|
||||||
defer Mutex.RUnlock()
|
defer mu.RUnlock()
|
||||||
|
|
||||||
return _config
|
return _config
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the in-memory JWT algorithm.
|
// Returns the in-memory JWT algorithm.
|
||||||
func GetJwtAlgorithm() *jwt.HMACSHA {
|
func GetJwtAlgorithm() *jwt.HMACSHA {
|
||||||
Mutex.RLock()
|
mu.RLock()
|
||||||
defer Mutex.RUnlock()
|
defer mu.RUnlock()
|
||||||
|
|
||||||
return _jwtAlgo
|
return _jwtAlgo
|
||||||
}
|
}
|
||||||
|
@ -199,7 +201,7 @@ func GetJwtAlgorithm() *jwt.HMACSHA {
|
||||||
func NewFromPath(path string) (*Configuration, error) {
|
func NewFromPath(path string) (*Configuration, error) {
|
||||||
c := new(Configuration)
|
c := new(Configuration)
|
||||||
if err := defaults.Set(c); err != nil {
|
if err := defaults.Set(c); err != nil {
|
||||||
return c, err
|
return c, errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.unsafeSetPath(path)
|
c.unsafeSetPath(path)
|
||||||
|
@ -237,12 +239,12 @@ func (c *Configuration) EnsurePterodactylUser() (*user.User, error) {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return u, c.setSystemUser(u)
|
return u, c.setSystemUser(u)
|
||||||
} else if _, ok := err.(user.UnknownUserError); !ok {
|
} else if _, ok := err.(user.UnknownUserError); !ok {
|
||||||
return nil, err
|
return nil, errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
sysName, err := getSystemName()
|
sysName, err := getSystemName()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var command = fmt.Sprintf("useradd --system --no-create-home --shell /bin/false %s", c.System.Username)
|
var command = fmt.Sprintf("useradd --system --no-create-home --shell /bin/false %s", c.System.Username)
|
||||||
|
@ -255,17 +257,17 @@ func (c *Configuration) EnsurePterodactylUser() (*user.User, error) {
|
||||||
// We have to create the group first on Alpine, so do that here before continuing on
|
// We have to create the group first on Alpine, so do that here before continuing on
|
||||||
// to the user creation process.
|
// to the user creation process.
|
||||||
if _, err := exec.Command("addgroup", "-S", c.System.Username).Output(); err != nil {
|
if _, err := exec.Command("addgroup", "-S", c.System.Username).Output(); err != nil {
|
||||||
return nil, err
|
return nil, errors.WithStack(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
split := strings.Split(command, " ")
|
split := strings.Split(command, " ")
|
||||||
if _, err := exec.Command(split[0], split[1:]...).Output(); err != nil {
|
if _, err := exec.Command(split[0], split[1:]...).Output(); err != nil {
|
||||||
return nil, err
|
return nil, errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if u, err := user.Lookup(c.System.Username); err != nil {
|
if u, err := user.Lookup(c.System.Username); err != nil {
|
||||||
return nil, err
|
return nil, errors.WithStack(err)
|
||||||
} else {
|
} else {
|
||||||
return u, c.setSystemUser(u)
|
return u, c.setSystemUser(u)
|
||||||
}
|
}
|
||||||
|
@ -286,6 +288,8 @@ func (c *Configuration) setSystemUser(u *user.User) error {
|
||||||
return c.WriteToDisk()
|
return c.WriteToDisk()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var uuid4Regex = regexp.MustCompile("^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$")
|
||||||
|
|
||||||
// Ensures that the configured data directory has the correct permissions assigned to
|
// Ensures that the configured data directory has the correct permissions assigned to
|
||||||
// all of the files and folders within.
|
// all of the files and folders within.
|
||||||
func (c *Configuration) EnsureFilePermissions() error {
|
func (c *Configuration) EnsureFilePermissions() error {
|
||||||
|
@ -295,45 +299,27 @@ func (c *Configuration) EnsureFilePermissions() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
r := regexp.MustCompile("^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$")
|
|
||||||
|
|
||||||
files, err := ioutil.ReadDir(c.System.Data)
|
files, err := ioutil.ReadDir(c.System.Data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
su, err := user.Lookup(c.System.Username)
|
pool := workerpool.New(runtime.GOMAXPROCS(0))
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
wg := new(sync.WaitGroup)
|
|
||||||
|
|
||||||
for _, file := range files {
|
for _, file := range files {
|
||||||
wg.Add(1)
|
f := file
|
||||||
|
if !f.IsDir() || !uuid4Regex.MatchString(f.Name()) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// Asynchronously run through the list of files and folders in the data directory. If
|
pool.Submit(func() {
|
||||||
// the item is not a folder, or is not a folder that matches the expected UUIDv4 format
|
if err := os.Chown(path.Join(c.System.Data, f.Name()), c.System.User.Uid, c.System.User.Gid); err != nil {
|
||||||
// skip over it.
|
|
||||||
//
|
|
||||||
// If we do have a positive match, run a chown against the directory.
|
|
||||||
go func(f os.FileInfo) {
|
|
||||||
defer wg.Done()
|
|
||||||
|
|
||||||
if !f.IsDir() || !r.MatchString(f.Name()) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
uid, _ := strconv.Atoi(su.Uid)
|
|
||||||
gid, _ := strconv.Atoi(su.Gid)
|
|
||||||
|
|
||||||
if err := os.Chown(path.Join(c.System.Data, f.Name()), uid, gid); err != nil {
|
|
||||||
log.WithField("error", err).WithField("directory", f.Name()).Warn("failed to chown server directory")
|
log.WithField("error", err).WithField("directory", f.Name()).Warn("failed to chown server directory")
|
||||||
}
|
}
|
||||||
}(file)
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
wg.Wait()
|
pool.StopWait()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -359,11 +345,11 @@ func (c *Configuration) WriteToDisk() error {
|
||||||
|
|
||||||
b, err := yaml.Marshal(&ccopy)
|
b, err := yaml.Marshal(&ccopy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := ioutil.WriteFile(c.GetPath(), b, 0644); err != nil {
|
if err := ioutil.WriteFile(c.GetPath(), b, 0644); err != nil {
|
||||||
return err
|
return errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -373,7 +359,7 @@ func (c *Configuration) WriteToDisk() error {
|
||||||
func getSystemName() (string, error) {
|
func getSystemName() (string, error) {
|
||||||
// use osrelease to get release version and ID
|
// use osrelease to get release version and ID
|
||||||
if release, err := osrelease.Read(); err != nil {
|
if release, err := osrelease.Read(); err != nil {
|
||||||
return "", err
|
return "", errors.WithStack(err)
|
||||||
} else {
|
} else {
|
||||||
return release["ID"], nil
|
return release["ID"], nil
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user