fileboy/fileboy.go

375 lines
8.3 KiB
Go
Raw Permalink Normal View History

2018-09-10 08:23:27 +00:00
package main
import (
"fmt"
"io/ioutil"
"log"
"math/rand"
"os"
2020-07-19 10:06:24 +00:00
"os/signal"
2018-09-10 08:23:27 +00:00
"path"
2019-04-01 08:44:16 +00:00
"strconv"
2019-01-08 07:49:21 +00:00
"strings"
2020-07-19 10:06:24 +00:00
"syscall"
2018-09-10 08:23:27 +00:00
"time"
2019-12-27 08:31:00 +00:00
"gopkg.in/fsnotify/fsnotify.v1"
"gopkg.in/yaml.v2"
2018-09-10 08:23:27 +00:00
)
const (
Version = 1
2019-01-19 09:10:07 +00:00
2020-01-02 09:38:52 +00:00
InstExecWhenStart = "exec-when-start"
InstShouldFinish = "should-finish"
InstIgnoreWarn = "ignore-warn"
InstIgnoreInfo = "ignore-info"
InstIgnoreStdout = "ignore-stdout"
InstIgnoreExecError = "ignore-exec-error"
2018-09-10 08:23:27 +00:00
)
var (
2019-01-03 02:27:04 +00:00
projectFolder = "."
2018-09-10 08:23:27 +00:00
2019-12-17 08:10:33 +00:00
filegirlYamlName = "filegirl.yaml"
2018-09-10 08:23:27 +00:00
cfg *FileGirl
watcher *fsnotify.Watcher
2018-12-30 07:14:14 +00:00
taskMan *TaskMan
2019-12-04 03:50:11 +00:00
ioeventMapStr = map[fsnotify.Op]string{
fsnotify.Write: "write",
fsnotify.Rename: "rename",
fsnotify.Remove: "remove",
fsnotify.Create: "create",
fsnotify.Chmod: "chmod",
}
2018-09-10 08:23:27 +00:00
)
2019-01-03 01:56:26 +00:00
type changedFile struct {
2018-09-19 03:26:57 +00:00
Name string
Changed int64
2018-09-19 03:26:57 +00:00
Ext string
2019-12-04 03:50:11 +00:00
Event string
2018-09-10 08:23:27 +00:00
}
2020-11-11 07:17:48 +00:00
func parseConfig(filegirlYamlPath string) {
2018-09-10 08:23:27 +00:00
cfg = new(FileGirl)
2020-11-11 07:17:48 +00:00
fc, err := ioutil.ReadFile(filegirlYamlPath)
2018-09-10 08:23:27 +00:00
if err != nil {
2020-03-03 03:12:08 +00:00
logError("the filegirl.yaml file in", projectFolder, "is not exist! ", err)
2019-04-03 10:04:07 +00:00
fmt.Print(firstRunHelp)
2020-03-03 03:12:08 +00:00
logAndExit("fileboy unable to run.")
2018-09-10 08:23:27 +00:00
}
err = yaml.Unmarshal(fc, cfg)
if err != nil {
2020-03-03 03:12:08 +00:00
logAndExit("parsed filegirl.yaml failed: ", err)
2018-09-10 08:23:27 +00:00
}
if cfg.Core.Version > Version {
2020-03-03 03:12:08 +00:00
logAndExit("current fileboy support max version : ", Version)
2018-09-10 08:23:27 +00:00
}
2019-04-01 08:18:01 +00:00
// init map
2019-04-01 07:32:27 +00:00
cfg.Monitor.TypesMap = map[string]bool{}
2020-11-10 09:13:49 +00:00
cfg.Monitor.ExceptTypesMap = map[string]bool{}
2019-04-01 08:18:01 +00:00
cfg.Monitor.IncludeDirsMap = map[string]bool{}
cfg.Monitor.ExceptDirsMap = map[string]bool{}
2019-12-06 03:51:28 +00:00
cfg.Monitor.IncludeDirsRec = map[string]bool{}
2020-03-03 03:37:20 +00:00
cfg.InstructionMap = map[string]bool{}
2019-04-01 08:18:01 +00:00
// convert to map
2019-04-01 07:32:27 +00:00
for _, v := range cfg.Monitor.Types {
cfg.Monitor.TypesMap[v] = true
}
2020-03-03 03:37:20 +00:00
for _, v := range cfg.Instruction {
cfg.InstructionMap[v] = true
2020-01-02 08:38:26 +00:00
}
2020-11-10 09:13:49 +00:00
for _, v := range cfg.Monitor.ExceptTypes {
cfg.Monitor.ExceptTypesMap[v] = true
}
2020-01-02 08:38:26 +00:00
log.Printf("%+v", cfg)
2018-09-10 08:23:27 +00:00
}
func eventDispatcher(event fsnotify.Event) {
2019-12-24 03:27:38 +00:00
if event.Name == getPidFile() {
return
}
2018-09-10 08:23:27 +00:00
ext := path.Ext(event.Name)
if len(cfg.Monitor.Types) > 0 &&
2020-11-10 09:13:49 +00:00
!keyInMonitorTypesMap(".*", cfg.Monitor.TypesMap) &&
!keyInMonitorTypesMap(ext, cfg.Monitor.TypesMap) {
return
}
if len(cfg.Monitor.ExceptTypes) > 0 &&
keyInMonitorTypesMap(ext, cfg.Monitor.ExceptTypesMap) {
2018-09-10 08:23:27 +00:00
return
}
2019-12-04 03:50:11 +00:00
op := ioeventMapStr[event.Op]
if len(cfg.Monitor.Events) != 0 && !inStrArray(op, cfg.Monitor.Events) {
return
2018-09-10 08:23:27 +00:00
}
2019-12-04 03:50:11 +00:00
log.Println("EVENT", event.Op.String(), ":", event.Name)
taskMan.Put(&changedFile{
Name: relativePath(projectFolder, event.Name),
Changed: time.Now().UnixNano(),
Ext: ext,
Event: op,
})
2018-09-10 08:23:27 +00:00
}
func addWatcher() {
2020-01-02 09:25:02 +00:00
logInfo("collecting directory information...")
dirsMap := map[string]bool{}
2020-08-23 08:17:18 +00:00
for _, dir := range cfg.Monitor.ExceptDirs {
if dir == "." {
logAndExit("exceptDirs must is not project root path ! err path:", dir)
}
}
2019-04-01 08:18:01 +00:00
for _, dir := range cfg.Monitor.IncludeDirs {
darr := dirParse2Array(dir)
2018-09-10 08:23:27 +00:00
if len(darr) < 1 || len(darr) > 2 {
2020-01-02 09:25:02 +00:00
logAndExit("filegirl section monitor dirs is error. ", dir)
2018-09-10 08:23:27 +00:00
}
2020-11-11 07:17:48 +00:00
//if strings.HasPrefix(darr[0], "/") {
// logAndExit("dirs must be relative paths ! err path:", dir)
//}
2018-09-10 08:23:27 +00:00
if darr[0] == "." {
if len(darr) == 2 && darr[1] == "*" {
2019-04-01 08:18:01 +00:00
// The highest priority
dirsMap = map[string]bool{
projectFolder: true,
}
2018-09-10 08:23:27 +00:00
listFile(projectFolder, func(d string) {
2019-04-01 08:18:01 +00:00
dirsMap[d] = true
2018-09-10 08:23:27 +00:00
})
2019-12-06 03:51:28 +00:00
cfg.Monitor.IncludeDirsRec[projectFolder] = true
2019-04-01 08:18:01 +00:00
break
2018-09-10 08:23:27 +00:00
} else {
2019-04-01 08:18:01 +00:00
dirsMap[projectFolder] = true
2018-09-10 08:23:27 +00:00
}
} else {
2020-11-11 07:17:48 +00:00
//md := projectFolder + "/" + darr[0]
md := darr[0]
2019-04-01 08:18:01 +00:00
dirsMap[md] = true
2018-09-10 08:23:27 +00:00
if len(darr) == 2 && darr[1] == "*" {
listFile(md, func(d string) {
2019-04-01 08:18:01 +00:00
dirsMap[d] = true
2018-09-10 08:23:27 +00:00
})
2019-12-06 03:51:28 +00:00
cfg.Monitor.IncludeDirsRec[md] = true
2018-09-10 08:23:27 +00:00
}
}
}
2020-08-23 08:17:18 +00:00
2019-04-01 08:18:01 +00:00
for dir := range dirsMap {
2020-01-02 09:25:02 +00:00
logInfo("watcher add -> ", dir)
2018-09-10 08:23:27 +00:00
err := watcher.Add(dir)
if err != nil {
2020-01-02 09:25:02 +00:00
logAndExit(err)
2018-09-10 08:23:27 +00:00
}
}
2020-01-02 09:25:02 +00:00
logInfo("total monitored dirs: " + strconv.Itoa(len(dirsMap)))
logInfo("fileboy is ready.")
2019-04-02 10:37:46 +00:00
cfg.Monitor.DirsMap = dirsMap
2018-09-10 08:23:27 +00:00
}
func initWatcher() {
var err error
2019-04-03 10:04:07 +00:00
if watcher != nil {
_ = watcher.Close()
}
2018-09-10 08:23:27 +00:00
watcher, err = fsnotify.NewWatcher()
if err != nil {
2019-06-04 03:12:41 +00:00
logAndExit(err)
2018-09-10 08:23:27 +00:00
}
2019-01-02 11:07:52 +00:00
taskMan = newTaskMan(cfg.Command.DelayMillSecond, cfg.Notifier.CallUrl)
2018-09-10 08:23:27 +00:00
go func() {
for {
select {
case event, ok := <-watcher.Events:
if !ok {
return
}
2019-12-06 03:51:28 +00:00
// directory structure changes, dynamically add, delete and monitor according to rules
// TODO // this method cannot be triggered when the parent folder of the change folder is not monitored
go watchChangeHandler(event)
2018-09-10 08:23:27 +00:00
eventDispatcher(event)
case err, ok := <-watcher.Errors:
if !ok {
return
}
2020-01-02 09:25:02 +00:00
logError(err)
2018-09-10 08:23:27 +00:00
}
}
}()
addWatcher()
}
2019-12-06 03:51:28 +00:00
func watchChangeHandler(event fsnotify.Event) {
2020-06-27 07:11:55 +00:00
// stop the fileboy daemon process when the .fileboy.pid file is changed
if event.Name == getPidFile() &&
(event.Op == fsnotify.Remove ||
event.Op == fsnotify.Write ||
event.Op == fsnotify.Rename) {
logUInfo("exit daemon process")
stopSelf()
return
}
2019-12-06 03:51:28 +00:00
if event.Op != fsnotify.Create && event.Op != fsnotify.Rename {
return
}
_, err := ioutil.ReadDir(event.Name)
if err != nil {
return
}
do := false
for rec := range cfg.Monitor.IncludeDirsRec {
if !strings.HasPrefix(event.Name, rec) {
continue
}
// check exceptDirs
2020-08-23 08:17:18 +00:00
if hitDirs(event.Name, &cfg.Monitor.ExceptDirs) {
2019-12-06 03:51:28 +00:00
continue
}
_ = watcher.Remove(event.Name)
err := watcher.Add(event.Name)
if err == nil {
do = true
2020-01-02 09:25:02 +00:00
logInfo("watcher add -> ", event.Name)
2019-12-06 03:51:28 +00:00
} else {
2020-01-02 09:25:02 +00:00
logWarn("watcher add faild:", event.Name, err)
2019-12-06 03:51:28 +00:00
}
}
if do {
return
}
// check map
if _, ok := cfg.Monitor.DirsMap[event.Name]; ok {
_ = watcher.Remove(event.Name)
err := watcher.Add(event.Name)
if err == nil {
2020-01-02 09:25:02 +00:00
logInfo("watcher add -> ", event.Name)
2019-12-06 03:51:28 +00:00
} else {
2020-01-02 09:25:02 +00:00
logWarn("watcher add faild:", event.Name, err)
2019-12-06 03:51:28 +00:00
}
}
}
2018-09-10 08:23:27 +00:00
func parseArgs() {
2019-12-23 08:13:31 +00:00
switch {
case len(os.Args) == 1:
2019-12-27 08:43:49 +00:00
show()
2020-11-11 07:17:48 +00:00
parseConfig(getFileGirlPath())
2019-04-03 10:04:07 +00:00
done := make(chan bool)
2018-09-10 08:23:27 +00:00
initWatcher()
2019-04-03 10:04:07 +00:00
defer watcher.Close()
2020-01-02 08:38:26 +00:00
if keyInInstruction(InstExecWhenStart) {
taskMan.run(new(changedFile))
}
2019-04-03 10:04:07 +00:00
<-done
2018-09-10 08:23:27 +00:00
return
2019-12-23 08:13:31 +00:00
case len(os.Args) > 1:
2018-09-10 08:23:27 +00:00
c := os.Args[1]
switch c {
2020-03-16 03:16:14 +00:00
case "deamon", "daemon":
pid, err := runAsDaemon()
2019-12-23 08:13:31 +00:00
if err != nil {
2020-01-02 09:25:02 +00:00
logAndExit(err)
2019-12-23 08:13:31 +00:00
}
2020-01-02 10:22:16 +00:00
logUInfo("PID:", pid)
logUInfo("fileboy is ready. the main process will run as a daemons")
2019-12-23 08:13:31 +00:00
return
case "stop":
2020-03-16 03:16:14 +00:00
err := stopDaemon()
2019-12-23 08:13:31 +00:00
if err != nil {
2020-01-02 09:25:02 +00:00
logAndExit(err)
2019-12-23 08:13:31 +00:00
}
2020-01-02 10:22:16 +00:00
logUInfo("fileboy daemon is stoped.")
2019-12-23 08:13:31 +00:00
return
2018-09-10 08:23:27 +00:00
case "init":
2019-12-17 08:10:33 +00:00
_, err := ioutil.ReadFile(getFileGirlPath())
if err == nil {
2020-03-03 03:12:08 +00:00
logError("profile filegirl.yaml already exists.")
logAndExit("delete it first when you want to regenerate filegirl.yaml")
2019-12-17 08:10:33 +00:00
}
err = ioutil.WriteFile(getFileGirlPath(), []byte(exampleFileGirl), 0644)
2018-09-10 08:23:27 +00:00
if err != nil {
2020-03-03 03:12:08 +00:00
logError("profile filegirl.yaml create failed! ", err)
2018-09-10 08:23:27 +00:00
return
}
2020-03-03 03:12:08 +00:00
logUInfo("profile filegirl.yaml created ok")
2018-09-10 08:23:27 +00:00
return
case "exec":
2020-11-11 07:17:48 +00:00
parseConfig(getFileGirlPath())
2019-01-03 01:56:26 +00:00
newTaskMan(0, cfg.Notifier.CallUrl).run(new(changedFile))
2018-09-10 08:23:27 +00:00
return
2020-11-11 07:01:36 +00:00
case "profile":
2020-11-11 07:17:48 +00:00
filegirlYamlPath := os.Args[2]
2020-11-11 07:01:36 +00:00
show()
2020-11-11 07:17:48 +00:00
parseConfig(filegirlYamlPath)
2020-11-11 07:01:36 +00:00
done := make(chan bool)
initWatcher()
defer watcher.Close()
if keyInInstruction(InstExecWhenStart) {
taskMan.run(new(changedFile))
}
<-done
return
2018-12-30 07:59:38 +00:00
case "version", "v", "-v", "--version":
fmt.Println(versionDesc)
2019-12-27 08:31:00 +00:00
case "help", "--help", "--h", "-h":
2018-09-10 08:23:27 +00:00
fmt.Print(helpStr)
2019-12-27 08:31:00 +00:00
default:
2020-03-03 03:12:08 +00:00
logAndExit("unknown parameter, use 'fileboy help' to view available commands")
2018-09-10 08:23:27 +00:00
}
return
2019-04-03 10:04:07 +00:00
default:
2020-03-03 03:12:08 +00:00
logAndExit("unknown parameters, use `fileboy help` show help info.")
2018-09-10 08:23:27 +00:00
}
}
2020-07-19 10:06:24 +00:00
func signalHandler() {
c := make(chan os.Signal)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
go func() {
<-c
if taskMan != nil && taskMan.cmd != nil && taskMan.cmd.Process != nil {
if err := taskMan.cmd.Process.Kill(); err != nil {
logError("stopping the process failed: PID:", taskMan.cmd.ProcessState.Pid(), ":", err)
}
}
os.Exit(0)
}()
}
2019-12-17 08:10:33 +00:00
func getFileGirlPath() string {
return projectFolder + "/" + filegirlYamlName
}
2018-09-10 08:23:27 +00:00
func show() {
fmt.Print(logo)
rand.Seed(time.Now().UnixNano())
2018-12-30 07:59:38 +00:00
fmt.Println(englishSay[rand.Intn(len(englishSay))])
fmt.Println("")
fmt.Println(statement)
2018-09-10 08:23:27 +00:00
}
func main() {
log.SetPrefix("[FileBoy]: ")
log.SetFlags(2)
2019-12-04 02:31:49 +00:00
log.SetOutput(os.Stdout)
2019-12-27 08:43:49 +00:00
// show()
2018-09-10 08:23:27 +00:00
var err error
projectFolder, err = os.Getwd()
if err != nil {
2019-06-04 03:12:41 +00:00
logAndExit(err)
2018-09-10 08:23:27 +00:00
}
2020-07-19 10:06:24 +00:00
signalHandler()
2018-09-10 08:23:27 +00:00
parseArgs()
}