platform: Fix daemon data
This commit is contained in:
@@ -14,8 +14,7 @@ const crashReportsDirectoryName = "crash_reports"
|
||||
var crashReportFileOrder = []string{metadataFileName, nativeLogFileName, goLogFileName, configSnapshotFileName}
|
||||
|
||||
func (s *desktopService) ListCrashReports(ctx context.Context, empty *emptypb.Empty) (*CrashReportList, error) {
|
||||
reportsDirectory := filepath.Join(workingDirectory, crashReportsDirectoryName)
|
||||
userID, err := s.daemon.reportCaller(ctx, reportsDirectory)
|
||||
reportsDirectory, userID, err := s.daemon.reportCaller(ctx, crashReportsDirectoryName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -48,8 +47,7 @@ func (s *desktopService) ListCrashReports(ctx context.Context, empty *emptypb.Em
|
||||
}
|
||||
|
||||
func (s *desktopService) ReadCrashReport(ctx context.Context, request *CrashReportRequest) (*CrashReportContent, error) {
|
||||
reportsDirectory := filepath.Join(workingDirectory, crashReportsDirectoryName)
|
||||
userID, err := s.daemon.reportCaller(ctx, reportsDirectory)
|
||||
reportsDirectory, userID, err := s.daemon.reportCaller(ctx, crashReportsDirectoryName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -75,8 +73,7 @@ func (s *desktopService) ReadCrashReport(ctx context.Context, request *CrashRepo
|
||||
}
|
||||
|
||||
func (s *desktopService) MarkCrashReportRead(ctx context.Context, request *CrashReportRequest) (*emptypb.Empty, error) {
|
||||
reportsDirectory := filepath.Join(workingDirectory, crashReportsDirectoryName)
|
||||
userID, err := s.daemon.reportCaller(ctx, reportsDirectory)
|
||||
reportsDirectory, userID, err := s.daemon.reportCaller(ctx, crashReportsDirectoryName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -92,8 +89,7 @@ func (s *desktopService) MarkCrashReportRead(ctx context.Context, request *Crash
|
||||
}
|
||||
|
||||
func (s *desktopService) ExportCrashReport(ctx context.Context, request *CrashReportExportRequest) (*CrashReportArchive, error) {
|
||||
reportsDirectory := filepath.Join(workingDirectory, crashReportsDirectoryName)
|
||||
userID, err := s.daemon.reportCaller(ctx, reportsDirectory)
|
||||
reportsDirectory, userID, err := s.daemon.reportCaller(ctx, crashReportsDirectoryName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -101,8 +97,7 @@ func (s *desktopService) ExportCrashReport(ctx context.Context, request *CrashRe
|
||||
}
|
||||
|
||||
func (s *desktopService) DeleteCrashReport(ctx context.Context, request *CrashReportRequest) (*emptypb.Empty, error) {
|
||||
reportsDirectory := filepath.Join(workingDirectory, crashReportsDirectoryName)
|
||||
userID, err := s.daemon.reportCaller(ctx, reportsDirectory)
|
||||
reportsDirectory, userID, err := s.daemon.reportCaller(ctx, crashReportsDirectoryName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -118,8 +113,7 @@ func (s *desktopService) DeleteCrashReport(ctx context.Context, request *CrashRe
|
||||
}
|
||||
|
||||
func (s *desktopService) DeleteAllCrashReports(ctx context.Context, empty *emptypb.Empty) (*emptypb.Empty, error) {
|
||||
reportsDirectory := filepath.Join(workingDirectory, crashReportsDirectoryName)
|
||||
userID, err := s.daemon.reportCaller(ctx, reportsDirectory)
|
||||
reportsDirectory, userID, err := s.daemon.reportCaller(ctx, crashReportsDirectoryName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -28,13 +28,13 @@ func (s *desktopService) GetDaemonInfo(ctx context.Context, empty *emptypb.Empty
|
||||
return nil, err
|
||||
}
|
||||
ownership := DaemonOwnership_DAEMON_OWNERSHIP_AVAILABLE
|
||||
options, err := loadStartOptions()
|
||||
ownerUserID, err := loadOwner()
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return nil, err
|
||||
}
|
||||
if options.OwnerUserID == identity.UserID {
|
||||
if ownerUserID == identity.UserID {
|
||||
ownership = DaemonOwnership_DAEMON_OWNERSHIP_CALLER
|
||||
} else if options.OwnerUserID != "" {
|
||||
} else if ownerUserID != "" {
|
||||
ownership = DaemonOwnership_DAEMON_OWNERSHIP_OTHER
|
||||
}
|
||||
return &DaemonInfo{
|
||||
@@ -53,29 +53,39 @@ func (s *desktopService) StartService(ctx context.Context, request *StartService
|
||||
if s.daemon.closed {
|
||||
return nil, os.ErrClosed
|
||||
}
|
||||
currentOptions, err := loadStartOptions()
|
||||
ownerUserID, err := loadOwner()
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return nil, err
|
||||
}
|
||||
if currentOptions.OwnerUserID != "" && currentOptions.OwnerUserID != identity.UserID {
|
||||
if ownerUserID != "" && ownerUserID != identity.UserID {
|
||||
return nil, status.Error(codes.PermissionDenied, "the service is owned by another user")
|
||||
}
|
||||
if ownerUserID == "" {
|
||||
err = saveOwner(identity.UserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
currentOptions, err := loadStartOptions(identity.UserID)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return nil, err
|
||||
}
|
||||
mergedOptions := currentOptions
|
||||
mergedOptions.WasRunning = true
|
||||
mergedOptions.OwnerUserID = identity.UserID
|
||||
if request.Options != nil {
|
||||
mergedOptions.OOMKillerEnabled = request.Options.OomKillerEnabled
|
||||
mergedOptions.OOMKillerDisabled = request.Options.OomKillerDisabled
|
||||
mergedOptions.OOMMemoryLimit = request.Options.OomMemoryLimit
|
||||
}
|
||||
err = s.daemon.startService(request.ConfigContent, mergedOptions)
|
||||
err = s.daemon.startServiceLocked(identity.UserID, request.ConfigContent, mergedOptions)
|
||||
if err != nil {
|
||||
return nil, s.daemon.cleanFailedStartLocked(identity.UserID, err)
|
||||
return nil, s.daemon.cleanFailedStartLocked(identity.UserID, mergedOptions, err)
|
||||
}
|
||||
configError := atomicfile.WriteFile(filepath.Join(workingDirectory, serviceConfigFileName), []byte(request.ConfigContent), 0o600)
|
||||
optionsError := saveStartOptions(mergedOptions)
|
||||
directory := userWorkingDirectory(identity.UserID)
|
||||
configError := atomicfile.WriteFile(filepath.Join(directory, serviceConfigFileName), []byte(request.ConfigContent), 0o600)
|
||||
optionsError := saveStartOptions(identity.UserID, mergedOptions)
|
||||
if configError != nil || optionsError != nil {
|
||||
return nil, s.daemon.cleanFailedStartLocked(identity.UserID, E.Errors(configError, optionsError))
|
||||
return nil, s.daemon.cleanFailedStartLocked(identity.UserID, mergedOptions, E.Errors(configError, optionsError))
|
||||
}
|
||||
return &emptypb.Empty{}, nil
|
||||
}
|
||||
@@ -90,17 +100,21 @@ func (s *desktopService) ClaimService(ctx context.Context, empty *emptypb.Empty)
|
||||
if s.daemon.closed {
|
||||
return nil, os.ErrClosed
|
||||
}
|
||||
options, err := loadStartOptions()
|
||||
ownerUserID, err := loadOwner()
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return nil, err
|
||||
}
|
||||
if options.OwnerUserID == identity.UserID {
|
||||
if ownerUserID == identity.UserID {
|
||||
return &emptypb.Empty{}, nil
|
||||
}
|
||||
if options.OwnerUserID != "" {
|
||||
if ownerUserID != "" {
|
||||
return nil, status.Error(codes.Aborted, "the service was claimed by another user")
|
||||
}
|
||||
err = s.daemon.resetRuntimeOwnerLocked(identity.UserID)
|
||||
err = s.daemon.configureWorkingDirectoryLocked(userWorkingDirectory(identity.UserID))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = saveOwner(identity.UserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -117,14 +131,24 @@ func (s *desktopService) TakeOverService(ctx context.Context, empty *emptypb.Emp
|
||||
if s.daemon.closed {
|
||||
return nil, os.ErrClosed
|
||||
}
|
||||
options, err := loadStartOptions()
|
||||
ownerUserID, err := loadOwner()
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return nil, err
|
||||
}
|
||||
if options.OwnerUserID == identity.UserID {
|
||||
if ownerUserID == identity.UserID {
|
||||
return &emptypb.Empty{}, nil
|
||||
}
|
||||
err = s.daemon.stopServiceLocked(identity.UserID)
|
||||
if ownerUserID != "" {
|
||||
err = s.daemon.stopServiceLocked(ownerUserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
err = s.daemon.configureWorkingDirectoryLocked(userWorkingDirectory(identity.UserID))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = saveOwner(identity.UserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -132,12 +156,14 @@ func (s *desktopService) TakeOverService(ctx context.Context, empty *emptypb.Emp
|
||||
return &emptypb.Empty{}, nil
|
||||
}
|
||||
|
||||
func (d *Daemon) cleanFailedStartLocked(ownerUserID string, startError error) error {
|
||||
func (d *Daemon) cleanFailedStartLocked(ownerUserID string, options startOptions, startError error) error {
|
||||
closeError := d.startedService.CloseService()
|
||||
crashReportError := tagUnownedReports(filepath.Join(workingDirectory, crashReportsDirectoryName), ownerUserID)
|
||||
oomReportError := tagUnownedReports(filepath.Join(workingDirectory, oomReportsDirectoryName), ownerUserID)
|
||||
resetError := d.resetRuntimeOwnerLocked(ownerUserID)
|
||||
return E.Errors(startError, closeError, crashReportError, oomReportError, resetError)
|
||||
directory := userWorkingDirectory(ownerUserID)
|
||||
crashReportError := tagUnownedReports(filepath.Join(directory, crashReportsDirectoryName), ownerUserID)
|
||||
oomReportError := tagUnownedReports(filepath.Join(directory, oomReportsDirectoryName), ownerUserID)
|
||||
options.WasRunning = false
|
||||
snapshotError := saveStartOptions(ownerUserID, options)
|
||||
return E.Errors(startError, closeError, crashReportError, oomReportError, snapshotError)
|
||||
}
|
||||
|
||||
func (s *desktopService) GetWorkingDirectory(ctx context.Context, empty *emptypb.Empty) (*WorkingDirectoryInfo, error) {
|
||||
@@ -147,19 +173,20 @@ func (s *desktopService) GetWorkingDirectory(ctx context.Context, empty *emptypb
|
||||
}
|
||||
s.daemon.lifecycleAccess.Lock()
|
||||
defer s.daemon.lifecycleAccess.Unlock()
|
||||
options, err := loadStartOptions()
|
||||
ownerUserID, err := loadOwner()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if options.OwnerUserID != identity.UserID {
|
||||
if ownerUserID != identity.UserID {
|
||||
return nil, status.Error(codes.PermissionDenied, "the service is owned by another user")
|
||||
}
|
||||
size, err := directorySize(workingDirectory)
|
||||
directory := userWorkingDirectory(identity.UserID)
|
||||
size, err := directorySize(directory)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &WorkingDirectoryInfo{
|
||||
Path: workingDirectory,
|
||||
Path: directory,
|
||||
Size: size,
|
||||
}, nil
|
||||
}
|
||||
@@ -177,22 +204,24 @@ func (s *desktopService) DestroyWorkingDirectory(ctx context.Context, empty *emp
|
||||
if s.daemon.startedService.Instance() != nil {
|
||||
return nil, status.Error(codes.FailedPrecondition, "the service must be stopped before destroying the working directory")
|
||||
}
|
||||
options, err := loadStartOptions()
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
ownerUserID, err := loadOwner()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if options.OwnerUserID != "" && options.OwnerUserID != identity.UserID {
|
||||
if ownerUserID != identity.UserID {
|
||||
return nil, status.Error(codes.PermissionDenied, "the service is owned by another user")
|
||||
}
|
||||
err = s.daemon.resetRuntimeOwnerLocked(identity.UserID)
|
||||
directory := userWorkingDirectory(identity.UserID)
|
||||
err = s.daemon.configureWorkingDirectoryLocked(workingDirectory)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = deleteReportsForUser(filepath.Join(workingDirectory, crashReportsDirectoryName), identity.UserID)
|
||||
err = os.RemoveAll(directory)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
restoreError := s.daemon.configureWorkingDirectoryLocked(directory)
|
||||
return nil, E.Errors(err, restoreError)
|
||||
}
|
||||
err = deleteReportsForUser(filepath.Join(workingDirectory, oomReportsDirectoryName), identity.UserID)
|
||||
err = s.daemon.configureWorkingDirectoryLocked(directory)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -20,31 +20,35 @@ func (h *managedHandler) ServiceStop() error {
|
||||
if h.daemon.closed {
|
||||
return os.ErrClosed
|
||||
}
|
||||
options, err := loadStartOptions()
|
||||
ownerUserID, err := loadOwner()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return h.daemon.stopServiceLocked(options.OwnerUserID)
|
||||
return h.daemon.stopServiceLocked(ownerUserID)
|
||||
}
|
||||
|
||||
func (h *managedHandler) ServiceReload() error {
|
||||
if h.daemon.closed {
|
||||
return os.ErrClosed
|
||||
}
|
||||
configContent, err := loadServiceConfig()
|
||||
ownerUserID, err := loadOwner()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
options, err := loadStartOptions()
|
||||
configContent, err := loadServiceConfig(ownerUserID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = h.daemon.startService(configContent, options)
|
||||
options, err := loadStartOptions(ownerUserID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = h.daemon.startServiceLocked(ownerUserID, configContent, options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
options.WasRunning = true
|
||||
return saveStartOptions(options)
|
||||
return saveStartOptions(ownerUserID, options)
|
||||
}
|
||||
|
||||
func (h *managedHandler) SystemProxyStatus() (*daemon.SystemProxyStatus, error) {
|
||||
|
||||
@@ -19,8 +19,7 @@ const oomReportsDirectoryName = "oom_reports"
|
||||
var oomReportLeadingFileOrder = []string{metadataFileName, configSnapshotFileName, goLogFileName}
|
||||
|
||||
func (s *desktopService) ListOOMReports(ctx context.Context, empty *emptypb.Empty) (*OOMReportList, error) {
|
||||
reportsDirectory := filepath.Join(workingDirectory, oomReportsDirectoryName)
|
||||
userID, err := s.daemon.reportCaller(ctx, reportsDirectory)
|
||||
reportsDirectory, userID, err := s.daemon.reportCaller(ctx, oomReportsDirectoryName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -53,8 +52,7 @@ func (s *desktopService) ListOOMReports(ctx context.Context, empty *emptypb.Empt
|
||||
}
|
||||
|
||||
func (s *desktopService) ReadOOMReport(ctx context.Context, request *OOMReportRequest) (*OOMReportContent, error) {
|
||||
reportsDirectory := filepath.Join(workingDirectory, oomReportsDirectoryName)
|
||||
userID, err := s.daemon.reportCaller(ctx, reportsDirectory)
|
||||
reportsDirectory, userID, err := s.daemon.reportCaller(ctx, oomReportsDirectoryName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -102,8 +100,7 @@ func (s *desktopService) ReadOOMReport(ctx context.Context, request *OOMReportRe
|
||||
}
|
||||
|
||||
func (s *desktopService) MarkOOMReportRead(ctx context.Context, request *OOMReportRequest) (*emptypb.Empty, error) {
|
||||
reportsDirectory := filepath.Join(workingDirectory, oomReportsDirectoryName)
|
||||
userID, err := s.daemon.reportCaller(ctx, reportsDirectory)
|
||||
reportsDirectory, userID, err := s.daemon.reportCaller(ctx, oomReportsDirectoryName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -119,8 +116,7 @@ func (s *desktopService) MarkOOMReportRead(ctx context.Context, request *OOMRepo
|
||||
}
|
||||
|
||||
func (s *desktopService) ExportOOMReport(ctx context.Context, request *OOMReportExportRequest) (*CrashReportArchive, error) {
|
||||
reportsDirectory := filepath.Join(workingDirectory, oomReportsDirectoryName)
|
||||
userID, err := s.daemon.reportCaller(ctx, reportsDirectory)
|
||||
reportsDirectory, userID, err := s.daemon.reportCaller(ctx, oomReportsDirectoryName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -128,8 +124,7 @@ func (s *desktopService) ExportOOMReport(ctx context.Context, request *OOMReport
|
||||
}
|
||||
|
||||
func (s *desktopService) DeleteOOMReport(ctx context.Context, request *OOMReportRequest) (*emptypb.Empty, error) {
|
||||
reportsDirectory := filepath.Join(workingDirectory, oomReportsDirectoryName)
|
||||
userID, err := s.daemon.reportCaller(ctx, reportsDirectory)
|
||||
reportsDirectory, userID, err := s.daemon.reportCaller(ctx, oomReportsDirectoryName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -145,8 +140,7 @@ func (s *desktopService) DeleteOOMReport(ctx context.Context, request *OOMReport
|
||||
}
|
||||
|
||||
func (s *desktopService) DeleteAllOOMReports(ctx context.Context, empty *emptypb.Empty) (*emptypb.Empty, error) {
|
||||
reportsDirectory := filepath.Join(workingDirectory, oomReportsDirectoryName)
|
||||
userID, err := s.daemon.reportCaller(ctx, reportsDirectory)
|
||||
reportsDirectory, userID, err := s.daemon.reportCaller(ctx, oomReportsDirectoryName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -92,22 +92,26 @@ func tagUnownedReports(reportsDirectory string, userID string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Daemon) reportCaller(ctx context.Context, reportsDirectory string) (string, error) {
|
||||
func (d *Daemon) reportCaller(ctx context.Context, reportsDirectoryName string) (string, string, error) {
|
||||
identity, err := peerIdentityFromContext(ctx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return "", "", err
|
||||
}
|
||||
d.lifecycleAccess.Lock()
|
||||
defer d.lifecycleAccess.Unlock()
|
||||
options, err := loadStartOptions()
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return "", err
|
||||
}
|
||||
err = tagUnownedReports(reportsDirectory, options.OwnerUserID)
|
||||
ownerUserID, err := loadOwner()
|
||||
if err != nil {
|
||||
return "", err
|
||||
return "", "", err
|
||||
}
|
||||
return identity.UserID, nil
|
||||
if ownerUserID != identity.UserID {
|
||||
return "", "", status.Error(codes.PermissionDenied, "the service is owned by another user")
|
||||
}
|
||||
reportsDirectory := filepath.Join(userWorkingDirectory(identity.UserID), reportsDirectoryName)
|
||||
err = tagUnownedReports(reportsDirectory, identity.UserID)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
return reportsDirectory, identity.UserID, nil
|
||||
}
|
||||
|
||||
func deleteReportsForUser(reportsDirectory string, userID string) error {
|
||||
@@ -165,7 +169,7 @@ func exportReportArchive(reportsDirectory string, name string, userID string, wi
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tempRoot := filepath.Join(workingDirectory, "temp")
|
||||
tempRoot := filepath.Join(filepath.Dir(reportsDirectory), "temp")
|
||||
err = os.MkdirAll(tempRoot, 0o700)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -24,23 +24,22 @@ import (
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
const daemonCrashOutputFileName = "CrashReport-Daemon.log"
|
||||
|
||||
type Daemon struct {
|
||||
logger log.ContextLogger
|
||||
startedService *daemon.StartedService
|
||||
server *grpc.Server
|
||||
listenerPath string
|
||||
lifecycleAccess sync.Mutex
|
||||
closed bool
|
||||
peerAccess sync.Mutex
|
||||
peerConnections map[peerConnection]peerIdentity
|
||||
logger log.ContextLogger
|
||||
startedService *daemon.StartedService
|
||||
server *grpc.Server
|
||||
runtimeWorkingDirectory string
|
||||
lifecycleAccess sync.Mutex
|
||||
closed bool
|
||||
peerAccess sync.Mutex
|
||||
peerConnections map[peerConnection]peerIdentity
|
||||
}
|
||||
|
||||
func newDaemon() (*Daemon, error) {
|
||||
ctx := include.Context(context.Background())
|
||||
d := &Daemon{
|
||||
logger: log.StdLogger(),
|
||||
logger: log.StdLogger(),
|
||||
runtimeWorkingDirectory: workingDirectory,
|
||||
}
|
||||
d.startedService = daemon.NewStartedService(daemon.ServiceOptions{
|
||||
Context: ctx,
|
||||
@@ -91,13 +90,6 @@ func (d *Daemon) Start() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if listener.Addr().Network() == "unix" {
|
||||
d.listenerPath, err = filepath.Abs(listener.Addr().String())
|
||||
if err != nil {
|
||||
listener.Close()
|
||||
return err
|
||||
}
|
||||
}
|
||||
d.logger.Info("daemon listening at ", listener.Addr())
|
||||
go func() {
|
||||
serveError := d.server.Serve(listener)
|
||||
@@ -115,38 +107,82 @@ func (d *Daemon) restore() {
|
||||
if d.closed {
|
||||
return
|
||||
}
|
||||
options, err := loadStartOptions()
|
||||
ownerUserID, err := loadOwner()
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
d.logger.Warn("load owner: ", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
ownerWorkingDirectory := userWorkingDirectory(ownerUserID)
|
||||
err = d.configureWorkingDirectoryLocked(ownerWorkingDirectory)
|
||||
if err != nil {
|
||||
d.logger.Warn("configure working directory: ", err)
|
||||
return
|
||||
}
|
||||
options, err := loadStartOptions(ownerUserID)
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
d.logger.Warn("load start options: ", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
err = tagUnownedReports(filepath.Join(workingDirectory, crashReportsDirectoryName), options.OwnerUserID)
|
||||
err = tagUnownedReports(filepath.Join(ownerWorkingDirectory, crashReportsDirectoryName), ownerUserID)
|
||||
if err != nil {
|
||||
d.logger.Warn("tag crash reports: ", err)
|
||||
}
|
||||
err = tagUnownedReports(filepath.Join(workingDirectory, oomReportsDirectoryName), options.OwnerUserID)
|
||||
err = tagUnownedReports(filepath.Join(ownerWorkingDirectory, oomReportsDirectoryName), ownerUserID)
|
||||
if err != nil {
|
||||
d.logger.Warn("tag OOM reports: ", err)
|
||||
}
|
||||
if !options.WasRunning {
|
||||
return
|
||||
}
|
||||
configContent, err := loadServiceConfig()
|
||||
configContent, err := loadServiceConfig(ownerUserID)
|
||||
if err != nil {
|
||||
d.logger.Error("restore service: ", err)
|
||||
return
|
||||
}
|
||||
d.logger.Info("restoring service")
|
||||
err = d.startService(configContent, options)
|
||||
err = d.startServiceLocked(ownerUserID, configContent, options)
|
||||
if err != nil {
|
||||
d.logger.Error("restore service: ", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Daemon) startService(configContent string, options startOptions) error {
|
||||
_ = os.WriteFile(filepath.Join(workingDirectory, configSnapshotFileName), []byte(configContent), 0o600)
|
||||
func (d *Daemon) configureWorkingDirectoryLocked(directory string) error {
|
||||
if d.runtimeWorkingDirectory == directory {
|
||||
return nil
|
||||
}
|
||||
err := os.MkdirAll(directory, 0o700)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = os.Chdir(directory)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = libbox.Setup(&libbox.SetupOptions{
|
||||
BasePath: directory,
|
||||
WorkingPath: directory,
|
||||
TempPath: directory,
|
||||
CrashReportSource: "Daemon",
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
libbox.PromoteOOMDraft()
|
||||
d.runtimeWorkingDirectory = directory
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Daemon) startServiceLocked(ownerUserID string, configContent string, options startOptions) error {
|
||||
directory := userWorkingDirectory(ownerUserID)
|
||||
err := d.configureWorkingDirectoryLocked(directory)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_ = os.WriteFile(filepath.Join(directory, configSnapshotFileName), []byte(configContent), 0o600)
|
||||
libbox.ReloadSetupOptions(&libbox.SetupOptions{
|
||||
OomKillerEnabled: options.OOMKillerEnabled,
|
||||
OomKillerDisabled: options.OOMKillerDisabled,
|
||||
@@ -156,39 +192,8 @@ func (d *Daemon) startService(configContent string, options startOptions) error
|
||||
return d.startedService.StartOrReloadService(configContent, nil)
|
||||
}
|
||||
|
||||
func (d *Daemon) clearRuntimeData() error {
|
||||
entries, err := os.ReadDir(workingDirectory)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, entry := range entries {
|
||||
if entry.Name() == crashReportsDirectoryName ||
|
||||
entry.Name() == oomReportsDirectoryName ||
|
||||
entry.Name() == daemonCrashOutputFileName {
|
||||
continue
|
||||
}
|
||||
entryPath := filepath.Join(workingDirectory, entry.Name())
|
||||
if d.listenerPath != "" && entryPath == d.listenerPath {
|
||||
continue
|
||||
}
|
||||
err = os.RemoveAll(entryPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Daemon) resetRuntimeOwnerLocked(ownerUserID string) error {
|
||||
err := d.clearRuntimeData()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return saveStartOptions(startOptions{OwnerUserID: ownerUserID})
|
||||
}
|
||||
|
||||
func (d *Daemon) stopServiceLocked(nextOwnerUserID string) error {
|
||||
options, err := loadStartOptions()
|
||||
func (d *Daemon) stopServiceLocked(ownerUserID string) error {
|
||||
options, err := loadStartOptions(ownerUserID)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
@@ -198,15 +203,17 @@ func (d *Daemon) stopServiceLocked(nextOwnerUserID string) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
crashReportError := tagUnownedReports(filepath.Join(workingDirectory, crashReportsDirectoryName), options.OwnerUserID)
|
||||
directory := userWorkingDirectory(ownerUserID)
|
||||
crashReportError := tagUnownedReports(filepath.Join(directory, crashReportsDirectoryName), ownerUserID)
|
||||
if crashReportError != nil {
|
||||
return crashReportError
|
||||
}
|
||||
oomReportError := tagUnownedReports(filepath.Join(workingDirectory, oomReportsDirectoryName), options.OwnerUserID)
|
||||
oomReportError := tagUnownedReports(filepath.Join(directory, oomReportsDirectoryName), ownerUserID)
|
||||
if oomReportError != nil {
|
||||
return oomReportError
|
||||
}
|
||||
return d.resetRuntimeOwnerLocked(nextOwnerUserID)
|
||||
options.WasRunning = false
|
||||
return saveStartOptions(ownerUserID, options)
|
||||
}
|
||||
|
||||
func (d *Daemon) Close() {
|
||||
@@ -311,14 +318,14 @@ func ownerProtectedMethod(method string) bool {
|
||||
}
|
||||
|
||||
func (d *Daemon) authorizeOwnerLocked(userID string) error {
|
||||
options, err := loadStartOptions()
|
||||
ownerUserID, err := loadOwner()
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return status.Error(codes.PermissionDenied, "the service has no owner")
|
||||
}
|
||||
return err
|
||||
}
|
||||
if options.OwnerUserID == "" || options.OwnerUserID != userID {
|
||||
if ownerUserID == "" || ownerUserID != userID {
|
||||
return status.Error(codes.PermissionDenied, "the service is owned by another user")
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
@@ -11,26 +13,56 @@ import (
|
||||
const (
|
||||
serviceConfigFileName = "config.json"
|
||||
startOptionsFileName = "start_options.json"
|
||||
ownerFileName = "owner.json"
|
||||
usersDirectoryName = "users"
|
||||
)
|
||||
|
||||
type startOptions struct {
|
||||
WasRunning bool `json:"was_running"`
|
||||
OwnerUserID string `json:"owner_user_id"`
|
||||
OOMKillerEnabled bool `json:"oom_killer_enabled"`
|
||||
OOMKillerDisabled bool `json:"oom_killer_disabled"`
|
||||
OOMMemoryLimit int64 `json:"oom_memory_limit"`
|
||||
WasRunning bool `json:"was_running"`
|
||||
OOMKillerEnabled bool `json:"oom_killer_enabled"`
|
||||
OOMKillerDisabled bool `json:"oom_killer_disabled"`
|
||||
OOMMemoryLimit int64 `json:"oom_memory_limit"`
|
||||
}
|
||||
|
||||
func loadServiceConfig() (string, error) {
|
||||
content, err := os.ReadFile(filepath.Join(workingDirectory, serviceConfigFileName))
|
||||
type ownerState struct {
|
||||
UserID string `json:"user_id"`
|
||||
}
|
||||
|
||||
func userWorkingDirectory(userID string) string {
|
||||
digest := sha256.Sum256([]byte(userID))
|
||||
return filepath.Join(workingDirectory, usersDirectoryName, hex.EncodeToString(digest[:]))
|
||||
}
|
||||
|
||||
func loadOwner() (string, error) {
|
||||
content, err := os.ReadFile(filepath.Join(workingDirectory, ownerFileName))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
state, err := json.UnmarshalExtended[ownerState](content)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return state.UserID, nil
|
||||
}
|
||||
|
||||
func saveOwner(userID string) error {
|
||||
content, err := json.Marshal(ownerState{UserID: userID})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return atomicfile.WriteFile(filepath.Join(workingDirectory, ownerFileName), content, 0o600)
|
||||
}
|
||||
|
||||
func loadServiceConfig(userID string) (string, error) {
|
||||
content, err := os.ReadFile(filepath.Join(userWorkingDirectory(userID), serviceConfigFileName))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(content), nil
|
||||
}
|
||||
|
||||
func loadStartOptions() (startOptions, error) {
|
||||
content, err := os.ReadFile(filepath.Join(workingDirectory, startOptionsFileName))
|
||||
func loadStartOptions(userID string) (startOptions, error) {
|
||||
content, err := os.ReadFile(filepath.Join(userWorkingDirectory(userID), startOptionsFileName))
|
||||
if err != nil {
|
||||
return startOptions{}, err
|
||||
}
|
||||
@@ -41,10 +73,15 @@ func loadStartOptions() (startOptions, error) {
|
||||
return options, nil
|
||||
}
|
||||
|
||||
func saveStartOptions(options startOptions) error {
|
||||
func saveStartOptions(userID string, options startOptions) error {
|
||||
content, err := json.Marshal(options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return atomicfile.WriteFile(filepath.Join(workingDirectory, startOptionsFileName), content, 0o600)
|
||||
directory := userWorkingDirectory(userID)
|
||||
err = os.MkdirAll(directory, 0o700)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return atomicfile.WriteFile(filepath.Join(directory, startOptionsFileName), content, 0o600)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user