diff --git a/batch/batch.go b/batch/batch.go
new file mode 100644
index 0000000000000000000000000000000000000000..1763f04332f0f200c59a31127521b6e01f1cef0f
--- /dev/null
+++ b/batch/batch.go
@@ -0,0 +1,42 @@
+// Package batch contains the non-interactive maintenence commands.
+package batch
+
+import (
+ _ "embed"
+ "fmt"
+ "os"
+ "strings"
+ "text/template"
+)
+
+//go:embed crontab
+var crontabTemplate string
+
+// Reboot deletes all messages with `shutdown` set.
+func Reboot() int {
+ fmt.Println("TODO: Delete messages with shutdown != 0.")
+ return 0
+}
+
+// Expire deletes all messages that have hit their expiration date.
+func Expire() int {
+ fmt.Println("TODO: expire messages.")
+ return 0
+}
+
+// Install is an interactive command used to install the crontab.
+func Install() int {
+ bulletin, err := os.Executable()
+ if err != nil {
+ panic(err) // TODO: cleanup error handling.
+ }
+ crontab := &strings.Builder{}
+ template.Must(template.New("crontab").Parse(crontabTemplate)).
+ Execute(crontab, map[string]string{"Bulletin": bulletin})
+ fmt.Printf("Add this to crontab:\n\n%s\n", crontab.String())
+
+ fmt.Println("TODO: Finish installing bulletin.")
+ // TODO: Offer to add ssh key for system user.
+ // TODO: Offer to add ssh key for other user(s).
+ return 0
+}
diff --git a/batch/crontab b/batch/crontab
new file mode 100644
index 0000000000000000000000000000000000000000..c3dac074c2d8521b7f9bbe7778293c0933f93d9c
--- /dev/null
+++ b/batch/crontab
@@ -0,0 +1,2 @@
+@reboot {{ .Bulletin }} -u SYSTEM -b reboot
+@daily {{ .Bulletin }} -u SYSTEM -b expire
diff --git a/go.mod b/go.mod
index 2d648865a1f3f087bb62f87e56b60ae450421729..c6997802db3e8ff5106caf0584601998d708e186 100644
--- a/go.mod
+++ b/go.mod
@@ -6,8 +6,10 @@ require (
github.com/adrg/xdg v0.5.3
github.com/chzyer/readline v1.5.1
github.com/davecgh/go-spew v1.1.1
+ github.com/gdamore/tcell/v2 v2.7.1
github.com/golang-migrate/migrate/v4 v4.18.3
github.com/jmoiron/sqlx v1.4.0
+ github.com/rivo/tview v0.0.0-20250501113434-0c592cd31026
github.com/urfave/cli/v3 v3.3.2
modernc.org/sqlite v1.37.0
)
@@ -15,7 +17,6 @@ require (
require (
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/gdamore/encoding v1.0.0 // indirect
- github.com/gdamore/tcell/v2 v2.7.1 // indirect
github.com/golang-migrate/migrate v3.5.4+incompatible // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
@@ -26,7 +27,6 @@ require (
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
github.com/ncruces/go-strftime v0.1.9 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
- github.com/rivo/tview v0.0.0-20250501113434-0c592cd31026 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
go.uber.org/atomic v1.11.0 // indirect
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 // indirect
diff --git a/main.go b/main.go
index 87ef32a80af81ba9b2f9f8df4bab8218db0381cd..e6acf8f7386ba4dd7d407a44e613aa45eda76519 100644
--- a/main.go
+++ b/main.go
@@ -8,6 +8,7 @@ import (
"os"
"git.lyda.ie/kevin/bulletin/accounts"
+ "git.lyda.ie/kevin/bulletin/batch"
"git.lyda.ie/kevin/bulletin/repl"
"github.com/urfave/cli/v3"
@@ -33,19 +34,26 @@ func main() {
},
Action: func(_ context.Context, cmd *cli.Command) error {
user := cmd.String("user")
- batch := cmd.String("batch")
+ batchFlag := cmd.String("batch")
- if batch != "" {
+ if batchFlag != "" {
if user != "SYSTEM" {
fmt.Println("ERROR: can only run batch commands as SYSTEM.")
os.Exit(1)
}
+ err := accounts.Open(user)
+ if err != nil {
+ fmt.Printf("ERROR: %s.", err)
+ os.Exit(1)
+ }
exitcode := 0
- switch batch {
- case "after-boot":
- fmt.Println("TODO: Delete messages with shutdown != 0.")
+ switch batchFlag {
+ case "reboot":
+ exitcode = batch.Reboot()
case "expire":
- fmt.Println("TODO: expire messages.")
+ exitcode = batch.Expire()
+ case "install":
+ exitcode = batch.Install()
default:
fmt.Println("ERROR: can only run batch commands as SYSTEM.")
exitcode = 1