summaryrefslogtreecommitdiffstats
path: root/src/deal.go
blob: 483d4d24448bb7bf6b5249eb14bfd270f3eaee9f (plain) (blame)
package main

import (
	"fmt"
	"syscall"

	"go.mongodb.org/mongo-driver/bson"
)

const (
	dbName      string = "test"
	pidColName  string = "pids"
	fdColName   string = "fds"
	fileColName string = "files"
)

// var mongoMutex sync.Mutex
var pidCol, fdCol, fileCol mongoClient

var docRes []bson.M
var err error

func deal() {
	defer wg.Done()
	var cooked Event
	var ok bool

	if err = pidCol.init(dbName, pidColName); err != nil {
		fmt.Printf("Error while initing the mongodb: %v\n", err)
		return
	}
	err = pidCol.InsertOne(bson.M{
		"ppid":     1,
		"pid":      containerdPid,
		"cwd":      "/",
		"children": []bson.M{},
	})
	if err != nil {
		fmt.Printf("Error while initing the mongodb: %v\n", err)
		return
	}

	if err = fdCol.init(dbName, fdColName); err != nil {
		fmt.Printf("Error while initing the mongodb: %v\n", err)
		return
	}
	if err = fileCol.init(dbName, fileColName); err != nil {
		fmt.Printf("Error while initing the mongodb: %v\n", err)
	}

	fmt.Printf("Containerd: %d\n", containerdPid)
	defer pidCol.Disconnect()
	defer fdCol.Disconnect()
	defer fileCol.Disconnect()

	for {
		cooked, ok = <-cookedChan
		if !ok {
			break
		}

		switch cooked.tag {
		case NEWPID:
			go dealNewPid(cooked)
		case EXECVE:
			go dealExecve(cooked)
		case PIDEXIT:
			go deletePid(cooked)
		case FILEOPEN:
			fileOpen(cooked)
		case FILEWRITE:
			fileWrite(cooked)
		case FILECLOSE:
			fileClose(cooked)
		}
	}
}

func deletePid(cooked Event) {
	pidCol.GetLock()
	// 先从老爹那里销户
	pidCol.UpdateOne(bson.M{"pid": cooked.ppid}, bson.M{
		"$pull": bson.M{
			"children": cooked.pid,
		},
	})

	// 孩子们需要收容
	// 不必到children里一个个找,直接看ppid即可
	// pidCol.UpdateMany(bson.M{"ppid": cooked.pid}, bson.M{"ppid": 1})
	// 在这套逻辑里,孩子是不需要收容的,因为我们根本就不看ppid来工作

	// 可以去死了
	pidCol.UpdateOne(bson.M{"pid": cooked.pid}, bson.M{
		"$set": bson.M{
			"exit_timestamp": cooked.timestamp,
			"exit_code":      cooked.exit_code,
			"exit_signal":    cooked.exit_signal,
		},
	})
	pidCol.Mutex.Unlock()
}

func dealNewPid(cooked Event) {
	fmt.Printf("Fork: %v\t%6d\t%6d\n", cooked.timestamp, cooked.ppid, cooked.pid)
	// 有无父进程在观察中
	docRes, err = pidCol.Finddoc(bson.M{"pid": cooked.ppid})
	if err != nil || len(docRes) != 1 {
		return
	}

	// 自身是否已经记录
	docRes, err = pidCol.Finddoc(bson.M{"pid": cooked.pid})
	if err != nil {
		fmt.Printf("Err finding: %v\n", err)
		return
	}

	pidCol.GetLock()
	if len(docRes) != 0 {
		// 进程原本就存在,换言之别的消息先到了
		// 所有先行抵达的消息必须保留execve/children字段
		// 此处不再更新
		// 以防把原有信息更没了
		pidCol.UpdateOne(bson.M{"pid": cooked.pid}, bson.M{
			"start_timestamp": cooked.timestamp,
			"ppid":            cooked.ppid,
			"pid":             cooked.pid,
			"cwd":             cooked.cwd,
			// "execve":          []bson.M{},
			"args": cooked.argv,
			// "children":        []bson.M{},
		})
	} else {
		// 这进程本是新修的
		pidCol.InsertOne(bson.M{
			"start_timestamp": cooked.timestamp,
			"ppid":            cooked.ppid,
			"pid":             cooked.pid,
			"cwd":             cooked.cwd,
			"execve":          []bson.M{},
			"args":            cooked.argv,
			"children":        []bson.M{},
		})
	}

	pidCol.UpdateOne(bson.M{"pid": cooked.ppid}, bson.M{
		"$push": bson.M{
			"children": cooked.pid,
		},
	})
	pidCol.Mutex.Unlock()
}

func dealExecve(cooked Event) {
	fmt.Printf("EXEC: %6d\t%6d\n", cooked.ppid, cooked.pid)
	// 父进程在不在?不在扔
	docRes, err = pidCol.Finddoc(bson.M{"pid": cooked.ppid})
	if err != nil || len(docRes) != 1 {
		return
	}

	// 首先检查进程是否存在,如不存在则为之创建
	docRes, err = pidCol.Finddoc(bson.M{"pid": cooked.pid})
	if err != nil {
		return
	}

	pidCol.GetLock()
	if len(docRes) == 1 {
		// 自身已在,直接记录
		pidCol.UpdateOne(bson.M{"pid": cooked.pid}, bson.M{
			"$push": bson.M{
				"execve": bson.M{
					"timestamp": cooked.timestamp,
					"execArgs":  cooked.argv,
				},
			},
		})
	} else {
		// 先fork抵达,插入
		pidCol.InsertOne(bson.M{
			"ppid":     cooked.ppid,
			"pid":      cooked.pid,
			"children": []bson.M{},
			"execve": []bson.M{
				{
					"timestamp": cooked.timestamp,
					"execArgs":  cooked.argv,
				},
			},
		})
	}
	pidCol.Mutex.Unlock()
}

func fileOpen(cooked Event) {
	// 权限检查过了,不必再查
	fdCol.InsertOne(bson.M{
		"timestamp": cooked.timestamp,
		"fileName":  cooked.pathName,
		"pid":       cooked.pid,
		"fd":        cooked.exit_code,
		"flags":     cooked.syscallParam,
		"written":   []bson.M{},
	})

	if cooked.syscallParam[1]&syscall.O_TRUNC != 0 {
		fdCol.UpdateOne(bson.M{"pid": cooked.pid, "fd": cooked.exit_code}, bson.M{
			"$push": bson.M{
				"written": cooked.timestamp,
			},
		})
	}
}

func fileClose(cooked Event) {
	res, err := fdCol.Finddoc(bson.M{
		"pid":             cooked.pid,
		"fd":              cooked.syscallParam[0],
		"close_timestamp": bson.M{"$exists": false},
	})
	if err != nil {
		fmt.Printf("Err closing fd %d of pid %d: %v\n", cooked.syscallParam[0], cooked.pid, err)
	}
	if len(res) == 0 {
		return
	}
	fdCol.UpdateOne(bson.M{
		"pid":             cooked.pid,
		"fd":              cooked.syscallParam[0],
		"close_timestamp": bson.M{"$exists": false},
	}, bson.M{"$set": bson.M{"close_timestamp": cooked.timestamp}})
}

func fileWrite(cooked Event) {
	res, err := fdCol.Finddoc(bson.M{
		"pid":             cooked.pid,
		"fd":              cooked.syscallParam[0],
		"close_timestamp": bson.M{"$exists": false},
	})
	if err != nil {
		fmt.Printf("Err closing fd %d of pid %d: %v\n", cooked.syscallParam[0], cooked.pid, err)
	}
	if len(res) == 0 {
		return
	}
	fdCol.UpdateOne(bson.M{
		"pid":             cooked.pid,
		"fd":              cooked.syscallParam[0],
		"close_timestamp": bson.M{"$exists": false},
	}, bson.M{"$push": bson.M{"written": cooked.timestamp}})
}