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 | ||||
| 
 | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"github.com/apex/log" | ||||
| 	"github.com/cobaugh/osrelease" | ||||
| 	"github.com/creasty/defaults" | ||||
| 	"github.com/gammazero/workerpool" | ||||
| 	"github.com/gbrlsnchs/jwt/v3" | ||||
| 	"github.com/pkg/errors" | ||||
| 	"gopkg.in/yaml.v2" | ||||
| 	"io/ioutil" | ||||
| 	"os" | ||||
|  | @ -14,6 +15,7 @@ import ( | |||
| 	"os/user" | ||||
| 	"path" | ||||
| 	"regexp" | ||||
| 	"runtime" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
|  | @ -154,7 +156,7 @@ func ReadConfiguration(path string) (*Configuration, error) { | |||
| 	return c, nil | ||||
| } | ||||
| 
 | ||||
| var Mutex sync.RWMutex | ||||
| var mu sync.RWMutex | ||||
| 
 | ||||
| var _config *Configuration | ||||
| var _jwtAlgo *jwt.HMACSHA | ||||
|  | @ -164,14 +166,14 @@ var _debugViaFlag bool | |||
| // anything trying to set a different configuration value, or read the configuration
 | ||||
| // will be paused until it is complete.
 | ||||
| func Set(c *Configuration) { | ||||
| 	Mutex.Lock() | ||||
| 	mu.Lock() | ||||
| 
 | ||||
| 	if _config == nil || _config.AuthenticationToken != c.AuthenticationToken { | ||||
| 		_jwtAlgo = jwt.NewHS256([]byte(c.AuthenticationToken)) | ||||
| 	} | ||||
| 
 | ||||
| 	_config = c | ||||
| 	Mutex.Unlock() | ||||
| 	mu.Unlock() | ||||
| } | ||||
| 
 | ||||
| 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
 | ||||
| // if the configuration is presently being modified.
 | ||||
| func Get() *Configuration { | ||||
| 	Mutex.RLock() | ||||
| 	defer Mutex.RUnlock() | ||||
| 	mu.RLock() | ||||
| 	defer mu.RUnlock() | ||||
| 
 | ||||
| 	return _config | ||||
| } | ||||
| 
 | ||||
| // Returns the in-memory JWT algorithm.
 | ||||
| func GetJwtAlgorithm() *jwt.HMACSHA { | ||||
| 	Mutex.RLock() | ||||
| 	defer Mutex.RUnlock() | ||||
| 	mu.RLock() | ||||
| 	defer mu.RUnlock() | ||||
| 
 | ||||
| 	return _jwtAlgo | ||||
| } | ||||
|  | @ -199,7 +201,7 @@ func GetJwtAlgorithm() *jwt.HMACSHA { | |||
| func NewFromPath(path string) (*Configuration, error) { | ||||
| 	c := new(Configuration) | ||||
| 	if err := defaults.Set(c); err != nil { | ||||
| 		return c, err | ||||
| 		return c, errors.WithStack(err) | ||||
| 	} | ||||
| 
 | ||||
| 	c.unsafeSetPath(path) | ||||
|  | @ -237,12 +239,12 @@ func (c *Configuration) EnsurePterodactylUser() (*user.User, error) { | |||
| 	if err == nil { | ||||
| 		return u, c.setSystemUser(u) | ||||
| 	} else if _, ok := err.(user.UnknownUserError); !ok { | ||||
| 		return nil, err | ||||
| 		return nil, errors.WithStack(err) | ||||
| 	} | ||||
| 
 | ||||
| 	sysName, err := getSystemName() | ||||
| 	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) | ||||
|  | @ -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
 | ||||
| 		// to the user creation process.
 | ||||
| 		if _, err := exec.Command("addgroup", "-S", c.System.Username).Output(); err != nil { | ||||
| 			return nil, err | ||||
| 			return nil, errors.WithStack(err) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	split := strings.Split(command, " ") | ||||
| 	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 { | ||||
| 		return nil, err | ||||
| 		return nil, errors.WithStack(err) | ||||
| 	} else { | ||||
| 		return u, c.setSystemUser(u) | ||||
| 	} | ||||
|  | @ -286,6 +288,8 @@ func (c *Configuration) setSystemUser(u *user.User) error { | |||
| 	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
 | ||||
| // all of the files and folders within.
 | ||||
| func (c *Configuration) EnsureFilePermissions() error { | ||||
|  | @ -295,45 +299,27 @@ func (c *Configuration) EnsureFilePermissions() error { | |||
| 		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) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 		return errors.WithStack(err) | ||||
| 	} | ||||
| 
 | ||||
| 	su, err := user.Lookup(c.System.Username) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	wg := new(sync.WaitGroup) | ||||
| 	pool := workerpool.New(runtime.GOMAXPROCS(0)) | ||||
| 
 | ||||
| 	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
 | ||||
| 		// the item is not a folder, or is not a folder that matches the expected UUIDv4 format
 | ||||
| 		// 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 { | ||||
| 		pool.Submit(func() { | ||||
| 			if err := os.Chown(path.Join(c.System.Data, f.Name()), c.System.User.Uid, c.System.User.Gid); err != nil { | ||||
| 				log.WithField("error", err).WithField("directory", f.Name()).Warn("failed to chown server directory") | ||||
| 			} | ||||
| 		}(file) | ||||
| 		}) | ||||
| 	} | ||||
| 
 | ||||
| 	wg.Wait() | ||||
| 	pool.StopWait() | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
|  | @ -359,11 +345,11 @@ func (c *Configuration) WriteToDisk() error { | |||
| 
 | ||||
| 	b, err := yaml.Marshal(&ccopy) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 		return errors.WithStack(err) | ||||
| 	} | ||||
| 
 | ||||
| 	if err := ioutil.WriteFile(c.GetPath(), b, 0644); err != nil { | ||||
| 		return err | ||||
| 		return errors.WithStack(err) | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
|  | @ -373,7 +359,7 @@ func (c *Configuration) WriteToDisk() error { | |||
| func getSystemName() (string, error) { | ||||
| 	// use osrelease to get release version and ID
 | ||||
| 	if release, err := osrelease.Read(); err != nil { | ||||
| 		return "", err | ||||
| 		return "", errors.WithStack(err) | ||||
| 	} else { | ||||
| 		return release["ID"], nil | ||||
| 	} | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	Block a user