├── .gitignore ├── LICENSE ├── README.md ├── app.go ├── cmd └── croneye │ └── main.go ├── images └── example.png ├── job.go └── parser.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/go 2 | 3 | ### Go ### 4 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 5 | *.o 6 | *.a 7 | *.so 8 | 9 | # Folders 10 | _obj 11 | _test 12 | 13 | # Architecture specific extensions/prefixes 14 | *.[568vq] 15 | [568vq].out 16 | 17 | *.cgo1.go 18 | *.cgo2.c 19 | _cgo_defun.c 20 | _cgo_gotypes.go 21 | _cgo_export.* 22 | 23 | _testmain.go 24 | 25 | *.exe 26 | *.test 27 | *.prof 28 | 29 | tmp 30 | cmd/croneye/croneye 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 mizoR 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # croneye 2 | 3 | Check the job execution schedule from cron settings. 4 | 5 | ![Example](images/example.png) 6 | 7 | 8 | ## Usage 9 | 10 | ``` 11 | $ croneye [-from ] [-to ] 12 | ``` 13 | 14 | ## Installation 15 | 16 | ``` 17 | $ go get github.com/mizoR/croneye/... 18 | ``` 19 | 20 | ## LICENSE 21 | 22 | [MIT](https://github.com/mizoR/croneye/blob/master/LICENSE) 23 | -------------------------------------------------------------------------------- /app.go: -------------------------------------------------------------------------------- 1 | package croneye 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "sort" 7 | "time" 8 | ) 9 | 10 | type App struct { 11 | Parser Parser 12 | } 13 | 14 | func NewApp(fromTime time.Time, toTime time.Time) *App { 15 | parser := NewParser(fromTime, toTime) 16 | app := &App{Parser: *parser} 17 | return app 18 | } 19 | 20 | func (a App) Run(file *os.File) { 21 | var jobList JobList = []Job{} 22 | jobList = a.Parser.Parse(file) 23 | 24 | sort.Sort(jobList) 25 | for i := 0; i < len(jobList); i++ { 26 | fmt.Printf("%v\t%s\n", jobList[i].RunTime, jobList[i].Script) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /cmd/croneye/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "github.com/jinzhu/now" 7 | "github.com/mizoR/croneye" 8 | "golang.org/x/crypto/ssh/terminal" 9 | "os" 10 | "runtime" 11 | "time" 12 | ) 13 | 14 | var fromTime, toTime time.Time 15 | var timeFormat = "2006-01-02 15:04:05" 16 | 17 | func init() { 18 | runtime.GOMAXPROCS(runtime.NumCPU()) 19 | 20 | flag.Usage = func() { 21 | fmt.Fprintf(os.Stderr, `Usage 22 | $ %s [OPTIONS] 23 | Options 24 | `, os.Args[0]) 25 | flag.PrintDefaults() 26 | os.Exit(0) 27 | } 28 | from := flag.String("from", time.Now().Format(timeFormat), "From time (default: Current time)") 29 | to := flag.String("to", time.Now().Add(24*time.Hour).Format(timeFormat), "To time (default: After 1 day since current time)") 30 | flag.Parse() 31 | 32 | fromTime = now.MustParse(*from) 33 | toTime = now.MustParse(*to) 34 | } 35 | 36 | func main() { 37 | if terminal.IsTerminal(int(os.Stdin.Fd())) { 38 | fmt.Fprintf(os.Stderr, `Error: 39 | You must supply cron definition via stdin 40 | Example: 41 | $ crontab -l | %s [OPTIONS] 42 | 43 | `, os.Args[0]) 44 | os.Exit(1) 45 | } 46 | 47 | app := croneye.NewApp(fromTime, toTime) 48 | app.Run(os.Stdin) 49 | } 50 | -------------------------------------------------------------------------------- /images/example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mizoR/croneye/b82314012d36579fd130637a5028e4c185d0e0f0/images/example.png -------------------------------------------------------------------------------- /job.go: -------------------------------------------------------------------------------- 1 | package croneye 2 | 3 | import "time" 4 | 5 | type Job struct { 6 | RunTime time.Time 7 | Script string 8 | } 9 | 10 | func NewJob(runsAt time.Time, script string) *Job { 11 | job := &Job{RunTime: runsAt, Script: script} 12 | return job 13 | } 14 | 15 | type JobList []Job 16 | 17 | func (l JobList) Len() int { 18 | return len(l) 19 | } 20 | 21 | func (l JobList) Swap(i, j int) { 22 | l[i], l[j] = l[j], l[i] 23 | } 24 | 25 | func (l JobList) Less(i, j int) bool { 26 | return l[i].RunTime.Before(l[j].RunTime) 27 | } 28 | -------------------------------------------------------------------------------- /parser.go: -------------------------------------------------------------------------------- 1 | package croneye 2 | 3 | import ( 4 | "bufio" 5 | "github.com/gorhill/cronexpr" 6 | "io" 7 | "regexp" 8 | "sync" 9 | "time" 10 | ) 11 | 12 | type Parser struct { 13 | FromTime time.Time 14 | ToTime time.Time 15 | } 16 | 17 | func NewParser(fromTime time.Time, toTime time.Time) *Parser { 18 | parser := &Parser{FromTime: fromTime, ToTime: toTime} 19 | return parser 20 | } 21 | 22 | func (p Parser) ParseLine(line string) JobList { 23 | var jobList JobList = []Job{} 24 | re := regexp.MustCompile("^([^ ]+ +[^ ]+ +[^ ]+ +[^ ]+ +[^ ]+) +(.+)$") 25 | 26 | if !re.MatchString(line) { 27 | return jobList 28 | } 29 | 30 | matched := re.FindStringSubmatch(line) 31 | time := matched[1] 32 | script := matched[2] 33 | 34 | expr, err := cronexpr.Parse(time) 35 | if err != nil { 36 | return jobList 37 | } 38 | 39 | for nextTime := expr.Next(p.FromTime); !nextTime.After(p.ToTime); nextTime = expr.Next(nextTime) { 40 | job := NewJob(nextTime, script) 41 | jobList = append(jobList, *job) 42 | } 43 | 44 | return jobList 45 | } 46 | 47 | func (p Parser) Parse(r io.Reader) JobList { 48 | var jobList JobList = []Job{} 49 | var wait sync.WaitGroup 50 | 51 | channel := make(chan JobList) 52 | scanner := bufio.NewScanner(r) 53 | for scanner.Scan() { 54 | wait.Add(1) 55 | 56 | go func(line string) { 57 | defer wait.Done() 58 | channel <- p.ParseLine(line) 59 | }(scanner.Text()) 60 | 61 | go func(list *JobList) { 62 | *list = append(*list, <-channel...) 63 | }(&jobList) 64 | } 65 | 66 | if err := scanner.Err(); err != nil { 67 | panic(err) 68 | } 69 | 70 | wait.Wait() 71 | 72 | return jobList 73 | } 74 | --------------------------------------------------------------------------------