Revamp profile and process handling

Also, introduce the Internal flag to Profiles
This commit is contained in:
Daniel
2020-10-29 16:26:14 +01:00
parent c09d32cf08
commit 18a1386bc5
14 changed files with 606 additions and 371 deletions

View File

@@ -106,32 +106,33 @@ func CleanProcessStorage(activePIDs map[int]struct{}) {
// clean primary processes
for _, p := range processesCopy {
p.Lock()
// The PID of a process does not change.
_, active := activePIDs[p.Pid]
switch {
case p.Pid == UnidentifiedProcessID:
// internal
case p.Pid == SystemProcessID:
// internal
case active:
// process in system process table or recently seen on the network
default:
// delete now or soon
switch {
case p.LastSeen == 0:
// add last
p.LastSeen = time.Now().Unix()
case p.LastSeen > threshold:
// within keep period
default:
// delete now
log.Tracef("process.clean: deleted %s", p.DatabaseKey())
go p.Delete()
}
// Check if this is a special process.
if p.Pid == UnidentifiedProcessID || p.Pid == SystemProcessID {
p.profile.MarkStillActive()
continue
}
p.Unlock()
// Check if process is active.
_, active := activePIDs[p.Pid]
if active {
p.profile.MarkStillActive()
continue
}
// Process is inactive, start deletion process
switch {
case p.LastSeen == 0:
// add last
p.LastSeen = time.Now().Unix()
case p.LastSeen > threshold:
// within keep period
default:
// delete now
p.Delete()
log.Tracef("process: cleaned %s", p.DatabaseKey())
}
}
}

View File

@@ -30,10 +30,14 @@ func GetProcessByConnection(ctx context.Context, pktInfo *packet.Info) (process
return nil, connInbound, err
}
err = process.GetProfile(ctx)
changed, err := process.GetProfile(ctx)
if err != nil {
log.Tracer(ctx).Errorf("process: failed to get profile for process %s: %s", process, err)
}
if changed {
process.Save()
}
return process, connInbound, nil
}

View File

@@ -30,39 +30,35 @@ type Process struct {
record.Base
sync.Mutex
// Constant attributes.
Name string
UserID int
UserName string
UserHome string
Pid int
ParentPid int
Path string
ExecName string
Cwd string
CmdLine string
FirstArg string
ExecName string
ExecHashes map[string]string
// ExecOwner ...
// ExecSignature ...
LocalProfileKey string
profile *profile.LayeredProfile
Name string
Icon string
// Icon is a path to the icon and is either prefixed "f:" for filepath, "d:" for database cache path or "c:"/"a:" for a the icon key to fetch it from a company / authoritative node and cache it in its own cache.
// Mutable attributes.
FirstSeen int64
LastSeen int64
Virtual bool // This process is either merged into another process or is not needed.
Error string // Cache errors
Virtual bool // This process is either merged into another process or is not needed.
Error string // Cache errors
ExecHashes map[string]string
}
// Profile returns the assigned layered profile.
func (p *Process) Profile() *profile.LayeredProfile {
p.Lock()
defer p.Unlock()
return p.profile
}
@@ -72,8 +68,6 @@ func (p *Process) String() string {
return "?"
}
p.Lock()
defer p.Unlock()
return fmt.Sprintf("%s:%s:%d", p.UserName, p.Path, p.Pid)
}

View File

@@ -8,35 +8,58 @@ import (
)
// GetProfile finds and assigns a profile set to the process.
func (p *Process) GetProfile(ctx context.Context) error {
func (p *Process) GetProfile(ctx context.Context) (changed bool, err error) {
p.Lock()
defer p.Unlock()
// only find profiles if not already done.
if p.profile != nil {
log.Tracer(ctx).Trace("process: profile already loaded")
// mark profile as used
// Mark profile as used.
p.profile.MarkUsed()
return nil
return false, nil
}
log.Tracer(ctx).Trace("process: loading profile")
// get profile
localProfile, new, err := profile.FindOrCreateLocalProfileByPath(p.Path)
if err != nil {
return err
// Check if we need a special profile.
profileID := ""
switch p.Pid {
case UnidentifiedProcessID:
profileID = profile.UnidentifiedProfileID
case SystemProcessID:
profileID = profile.SystemProfileID
}
// add more information if new
// Get the (linked) local profile.
localProfile, new, err := profile.GetProfile(profile.SourceLocal, profileID, p.Path)
if err != nil {
return false, err
}
// If the local profile is new, add some information from the process.
if new {
localProfile.Name = p.ExecName
// Special profiles will only have a name, but not an ExecName.
if localProfile.Name == "" {
localProfile.Name = p.Name
}
}
// mark profile as used
localProfile.MarkUsed()
// Mark profile as used.
profileChanged := localProfile.MarkUsed()
// Save the profile if we changed something.
if new || profileChanged {
err := localProfile.Save()
if err != nil {
log.Warningf("process: failed to save profile %s: %s", localProfile.ScopedID(), err)
}
}
// Assign profile to process.
p.LocalProfileKey = localProfile.Key()
p.profile = profile.NewLayeredProfile(localProfile)
p.profile = localProfile.LayeredProfile()
go p.Save()
return nil
return true, nil
}

View File

@@ -2,10 +2,11 @@ package process
import (
"context"
"strconv"
"time"
"github.com/safing/portbase/log"
"github.com/safing/portmaster/profile"
"golang.org/x/sync/singleflight"
)
// Special Process IDs
@@ -32,53 +33,41 @@ var (
ParentPid: SystemProcessID,
Name: "Operating System",
}
getSpecialProcessSingleInflight singleflight.Group
)
// GetUnidentifiedProcess returns the special process assigned to unidentified processes.
func GetUnidentifiedProcess(ctx context.Context) *Process {
return getSpecialProcess(ctx, UnidentifiedProcessID, unidentifiedProcess, profile.GetUnidentifiedProfile)
return getSpecialProcess(ctx, UnidentifiedProcessID, unidentifiedProcess)
}
// GetSystemProcess returns the special process used for the Kernel.
func GetSystemProcess(ctx context.Context) *Process {
return getSpecialProcess(ctx, SystemProcessID, systemProcess, profile.GetSystemProfile)
return getSpecialProcess(ctx, SystemProcessID, systemProcess)
}
func getSpecialProcess(ctx context.Context, pid int, template *Process, getProfile func() *profile.Profile) *Process {
// check storage
p, ok := GetProcessFromStorage(pid)
if ok {
return p
}
func getSpecialProcess(ctx context.Context, pid int, template *Process) *Process {
p, _, _ := getSpecialProcessSingleInflight.Do(strconv.Itoa(pid), func() (interface{}, error) {
// Check if we have already loaded the special process.
process, ok := GetProcessFromStorage(pid)
if ok {
return process, nil
}
// assign template
p = template
// Create new process from template
process = template
process.FirstSeen = time.Now().Unix()
p.Lock()
defer p.Unlock()
// Get profile.
_, err := process.GetProfile(ctx)
if err != nil {
log.Tracer(ctx).Errorf("process: failed to get profile for process %s: %s", process, err)
}
if p.FirstSeen == 0 {
p.FirstSeen = time.Now().Unix()
}
// only find profiles if not already done.
if p.profile != nil {
log.Tracer(ctx).Trace("process: special profile already loaded")
// mark profile as used
p.profile.MarkUsed()
return p
}
log.Tracer(ctx).Trace("process: loading special profile")
// get profile
localProfile := getProfile()
// mark profile as used
localProfile.MarkUsed()
p.LocalProfileKey = localProfile.Key()
p.profile = profile.NewLayeredProfile(localProfile)
go p.Save()
return p
// Save process to storage.
process.Save()
return process, nil
})
return p.(*Process)
}