diff --git a/artifacts/grafana-kiosk-1.0.4.tar.gz b/artifacts/grafana-kiosk-1.0.4.tar.gz
new file mode 100644
index 0000000..5c34bd3
--- /dev/null
+++ b/artifacts/grafana-kiosk-1.0.4.tar.gz
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8b2e9c449c16e49fdaeda116a2bff7370b59e9db2c248ac76259763b62caa325
+size 44758111
diff --git a/artifacts/grafana-kiosk-1.0.4.zip b/artifacts/grafana-kiosk-1.0.4.zip
new file mode 100644
index 0000000..a6d90d0
--- /dev/null
+++ b/artifacts/grafana-kiosk-1.0.4.zip
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5dd472fa27a775b649ad0e91d3a666c150d41ec09f9fcc2672f46b9d3f73f66b
+size 44762190
diff --git a/artifacts/grafana-kiosk.darwin b/artifacts/grafana-kiosk.darwin
new file mode 100755
index 0000000..7f00fd5
--- /dev/null
+++ b/artifacts/grafana-kiosk.darwin
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e0e984164348a2a0d9cf16a002bd686a6964f6e304f183cbe1735af315efbe84
+size 12555440
diff --git a/artifacts/grafana-kiosk.darwin.amd64 b/artifacts/grafana-kiosk.darwin.amd64
new file mode 100755
index 0000000..813bc9f
--- /dev/null
+++ b/artifacts/grafana-kiosk.darwin.amd64
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5ba18b6edcf0644191b1bc4c05d1c556b1eb964366183e997b3216363573f814
+size 10142896
diff --git a/artifacts/grafana-kiosk.linux.386 b/artifacts/grafana-kiosk.linux.386
new file mode 100755
index 0000000..7b5de9d
--- /dev/null
+++ b/artifacts/grafana-kiosk.linux.386
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4eee40f256143345ad6392ab2bfac26afe695953def67adec65c18a5c8fa0387
+size 11023891
diff --git a/artifacts/grafana-kiosk.linux.amd64 b/artifacts/grafana-kiosk.linux.amd64
new file mode 100755
index 0000000..5556202
--- /dev/null
+++ b/artifacts/grafana-kiosk.linux.amd64
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5ca4d1dd00073b591600428a4b834176795af93f73b10844326e241f215c9962
+size 12716217
diff --git a/artifacts/grafana-kiosk.linux.arm64 b/artifacts/grafana-kiosk.linux.arm64
new file mode 100755
index 0000000..14312cd
--- /dev/null
+++ b/artifacts/grafana-kiosk.linux.arm64
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d2a49c2c30d13e8c3d1d1c9898426d45bd515742c5d22bd9496baee0f0c3d44e
+size 11934756
diff --git a/artifacts/grafana-kiosk.linux.armv5 b/artifacts/grafana-kiosk.linux.armv5
new file mode 100755
index 0000000..909e9dd
--- /dev/null
+++ b/artifacts/grafana-kiosk.linux.armv5
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f5118780224ce7be41fd7797ae470725d9009dd86e410b7f061e04a7166071d8
+size 11062411
diff --git a/artifacts/grafana-kiosk.linux.armv6 b/artifacts/grafana-kiosk.linux.armv6
new file mode 100755
index 0000000..82c41c6
--- /dev/null
+++ b/artifacts/grafana-kiosk.linux.armv6
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ad5ce0538b07eaff1bbc49c818cb9067f211e7a55d541de0c9401827b7d7cca6
+size 10988414
diff --git a/artifacts/grafana-kiosk.linux.armv7 b/artifacts/grafana-kiosk.linux.armv7
new file mode 100755
index 0000000..e3d1457
--- /dev/null
+++ b/artifacts/grafana-kiosk.linux.armv7
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e46afc50a3e248848faa7032ff9622670ed40130d378012abb774d6a59e5a715
+size 10986950
diff --git a/artifacts/grafana-kiosk.windows.amd64.exe b/artifacts/grafana-kiosk.windows.amd64.exe
new file mode 100755
index 0000000..e5672fe
--- /dev/null
+++ b/artifacts/grafana-kiosk.windows.amd64.exe
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:00984a7fbc6a03d08f92dd73b30f957eda15999ea83696b43d2edef0a58a4081
+size 10347520
diff --git a/bin/grafana-kiosk.darwin b/bin/grafana-kiosk.darwin
new file mode 100755
index 0000000..7f00fd5
--- /dev/null
+++ b/bin/grafana-kiosk.darwin
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e0e984164348a2a0d9cf16a002bd686a6964f6e304f183cbe1735af315efbe84
+size 12555440
diff --git a/bin/grafana-kiosk.darwin.amd64 b/bin/grafana-kiosk.darwin.amd64
new file mode 100755
index 0000000..813bc9f
--- /dev/null
+++ b/bin/grafana-kiosk.darwin.amd64
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5ba18b6edcf0644191b1bc4c05d1c556b1eb964366183e997b3216363573f814
+size 10142896
diff --git a/bin/grafana-kiosk.linux.386 b/bin/grafana-kiosk.linux.386
new file mode 100755
index 0000000..7b5de9d
--- /dev/null
+++ b/bin/grafana-kiosk.linux.386
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4eee40f256143345ad6392ab2bfac26afe695953def67adec65c18a5c8fa0387
+size 11023891
diff --git a/bin/grafana-kiosk.linux.amd64 b/bin/grafana-kiosk.linux.amd64
new file mode 100755
index 0000000..5556202
--- /dev/null
+++ b/bin/grafana-kiosk.linux.amd64
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5ca4d1dd00073b591600428a4b834176795af93f73b10844326e241f215c9962
+size 12716217
diff --git a/bin/grafana-kiosk.linux.arm64 b/bin/grafana-kiosk.linux.arm64
new file mode 100755
index 0000000..14312cd
--- /dev/null
+++ b/bin/grafana-kiosk.linux.arm64
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d2a49c2c30d13e8c3d1d1c9898426d45bd515742c5d22bd9496baee0f0c3d44e
+size 11934756
diff --git a/bin/grafana-kiosk.linux.armv5 b/bin/grafana-kiosk.linux.armv5
new file mode 100755
index 0000000..909e9dd
--- /dev/null
+++ b/bin/grafana-kiosk.linux.armv5
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f5118780224ce7be41fd7797ae470725d9009dd86e410b7f061e04a7166071d8
+size 11062411
diff --git a/bin/grafana-kiosk.linux.armv6 b/bin/grafana-kiosk.linux.armv6
new file mode 100755
index 0000000..82c41c6
--- /dev/null
+++ b/bin/grafana-kiosk.linux.armv6
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ad5ce0538b07eaff1bbc49c818cb9067f211e7a55d541de0c9401827b7d7cca6
+size 10988414
diff --git a/bin/grafana-kiosk.linux.armv7 b/bin/grafana-kiosk.linux.armv7
new file mode 100755
index 0000000..e3d1457
--- /dev/null
+++ b/bin/grafana-kiosk.linux.armv7
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e46afc50a3e248848faa7032ff9622670ed40130d378012abb774d6a59e5a715
+size 10986950
diff --git a/bin/grafana-kiosk.windows.amd64.exe b/bin/grafana-kiosk.windows.amd64.exe
new file mode 100755
index 0000000..e5672fe
--- /dev/null
+++ b/bin/grafana-kiosk.windows.amd64.exe
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:00984a7fbc6a03d08f92dd73b30f957eda15999ea83696b43d2edef0a58a4081
+size 10347520
diff --git a/coverage/coverage.html b/coverage/coverage.html
new file mode 100644
index 0000000..dd26aae
--- /dev/null
+++ b/coverage/coverage.html
@@ -0,0 +1,767 @@
+
+
+
+
+
+
+
package main
+
+import (
+ "flag"
+ "fmt"
+ "log"
+ "net/url"
+ "os"
+
+ "github.com/ilyakaznacheev/cleanenv"
+
+ "github.com/grafana/grafana-kiosk/pkg/initialize"
+ "github.com/grafana/grafana-kiosk/pkg/kiosk"
+)
+
+// Args command-line parameters
+type Args struct {
+ AutoFit bool
+ IgnoreCertificateErrors bool
+ IsPlayList bool
+ OauthAutoLogin bool
+ LXDEEnabled bool
+ LXDEHome string
+ ConfigPath string
+ Mode string
+ LoginMethod string
+ URL string
+ Username string
+ Password string
+ UsernameField string
+ PasswordField string
+ WindowPosition string
+}
+
+// ProcessArgs processes and handles CLI arguments
+func ProcessArgs(cfg interface{}) Args {
+ var a Args
+
+ f := flag.NewFlagSet("grafana-kiosk", flag.ContinueOnError)
+ f.StringVar(&a.ConfigPath, "c", "", "Path to configuration file (config.yaml)")
+ f.StringVar(&a.LoginMethod, "login-method", "anon", "[anon|local|gcom|goauth]")
+ f.StringVar(&a.Username, "username", "guest", "username")
+ f.StringVar(&a.Password, "password", "guest", "password")
+ f.StringVar(&a.Mode, "kiosk-mode", "full", "Kiosk Display Mode [full|tv|disabled]\nfull = No TOPNAV and No SIDEBAR\ntv = No SIDEBAR\ndisabled = omit option\n")
+ f.StringVar(&a.URL, "URL", "https://play.grafana.org", "URL to Grafana server")
+ f.StringVar(&a.WindowPosition, "window-position", "0,0", "Top Left Position of Kiosk")
+ f.BoolVar(&a.IsPlayList, "playlists", false, "URL is a playlist")
+ f.BoolVar(&a.AutoFit, "autofit", true, "Fit panels to screen")
+ f.BoolVar(&a.LXDEEnabled, "lxde", false, "Initialize LXDE for kiosk mode")
+ f.StringVar(&a.LXDEHome, "lxde-home", "/home/pi", "Path to home directory of LXDE user running X Server")
+ f.BoolVar(&a.IgnoreCertificateErrors, "ignore-certificate-errors", false, "Ignore SSL/TLS certificate error")
+ f.BoolVar(&a.OauthAutoLogin, "auto-login", false, "oauth_auto_login is enabled in grafana config")
+ f.StringVar(&a.UsernameField, "field-username", "username", "Fieldname for the username")
+ f.StringVar(&a.PasswordField, "field-password", "password", "Fieldname for the password")
+
+ fu := f.Usage
+ f.Usage = func() {
+ fu()
+ envHelp, _ := cleanenv.GetDescription(cfg, nil)
+ fmt.Fprintln(f.Output())
+ fmt.Fprintln(f.Output(), envHelp)
+ }
+
+ err := f.Parse(os.Args[1:])
+ if err != nil {
+ os.Exit(-1)
+ }
+ return a
+}
+
+func setEnvironment() {
+ // for linux/X display must be set
+ var displayEnv = os.Getenv("DISPLAY")
+ if displayEnv == "" {
+ log.Println("DISPLAY not set, autosetting to :0.0")
+ os.Setenv("DISPLAY", ":0.0")
+ displayEnv = os.Getenv("DISPLAY")
+ }
+ log.Println("DISPLAY=", displayEnv)
+
+ var xAuthorityEnv = os.Getenv("XAUTHORITY")
+ if xAuthorityEnv == "" {
+ log.Println("XAUTHORITY not set, autosetting")
+ // use HOME of current user
+ var homeEnv = os.Getenv("HOME")
+ os.Setenv("XAUTHORITY", homeEnv+"/.Xauthority")
+ xAuthorityEnv = os.Getenv("XAUTHORITY")
+ }
+ log.Println("XAUTHORITY=", xAuthorityEnv)
+}
+
+func summary(cfg *kiosk.Config) {
+ // general
+ log.Println("AutoFit:", cfg.General.AutoFit)
+ log.Println("LXDEEnabled:", cfg.General.LXDEEnabled)
+ log.Println("LXDEHome:", cfg.General.LXDEHome)
+ log.Println("Mode:", cfg.General.Mode)
+ log.Println("WindowPosition:", cfg.General.WindowPosition)
+ // target
+ log.Println("URL:", cfg.Target.URL)
+ log.Println("LoginMethod:", cfg.Target.LoginMethod)
+ log.Println("Username:", cfg.Target.Username)
+ log.Println("Password:", "*redacted*")
+ log.Println("IgnoreCertificateErrors:", cfg.Target.IgnoreCertificateErrors)
+ log.Println("IsPlayList:", cfg.Target.IsPlayList)
+ // goauth
+ log.Println("Fieldname Username:", cfg.GOAUTH.AutoLogin)
+ log.Println("Fieldname Username:", cfg.GOAUTH.UsernameField)
+ log.Println("Fieldname Password:", cfg.GOAUTH.PasswordField)
+}
+
+func main() {
+ var cfg kiosk.Config
+ // override
+ args := ProcessArgs(&cfg)
+ // check if config specified
+ if args.ConfigPath != "" {
+ // read configuration from the file and then override with environment variables
+ if err := cleanenv.ReadConfig(args.ConfigPath, &cfg); err != nil {
+ log.Println("Error reading config file", err)
+ os.Exit(-1)
+ } else {
+ log.Println("Using config from", args.ConfigPath)
+ }
+ } else {
+ log.Println("No config specified, using environment and args")
+ // no config, use environment and args
+ if err := cleanenv.ReadEnv(&cfg); err != nil {
+ log.Println("Error reading config from environment", err)
+ }
+ cfg.Target.URL = args.URL
+ cfg.Target.LoginMethod = args.LoginMethod
+ cfg.Target.Username = args.Username
+ cfg.Target.Password = args.Password
+ cfg.Target.IgnoreCertificateErrors = args.IgnoreCertificateErrors
+ cfg.Target.IsPlayList = args.IsPlayList
+ //
+ cfg.General.AutoFit = args.AutoFit
+ cfg.General.LXDEEnabled = args.LXDEEnabled
+ cfg.General.LXDEHome = args.LXDEHome
+ cfg.General.Mode = args.Mode
+ cfg.General.WindowPosition = args.WindowPosition
+ //
+ cfg.GOAUTH.AutoLogin = args.OauthAutoLogin
+ cfg.GOAUTH.UsernameField = args.UsernameField
+ cfg.GOAUTH.PasswordField = args.PasswordField
+ }
+ summary(&cfg)
+ // make sure the url has content
+ if cfg.Target.URL == "" {
+ os.Exit(1)
+ }
+ // validate url
+ _, err := url.ParseRequestURI(cfg.Target.URL)
+ if err != nil {
+ panic(err)
+ }
+ summary(&cfg)
+
+ if cfg.General.LXDEEnabled {
+ initialize.LXDE(cfg.General.LXDEHome)
+ }
+
+ // for linux/X display must be set
+ setEnvironment()
+ log.Println("method ", cfg.Target.LoginMethod)
+
+ switch cfg.Target.LoginMethod {
+ case "local":
+ log.Printf("Launching local login kiosk")
+ kiosk.GrafanaKioskLocal(&cfg)
+ case "gcom":
+ log.Printf("Launching GCOM login kiosk")
+ kiosk.GrafanaKioskGCOM(&cfg)
+ case "goauth":
+ log.Printf("Launching Generic Oauth login kiosk")
+ kiosk.GrafanaKioskGenericOauth(&cfg)
+ default:
+ log.Printf("Launching ANON login kiosk")
+ kiosk.GrafanaKioskAnonymous(&cfg)
+ }
+}
+
+
+
package initialize
+
+import (
+ "log"
+ "os"
+ "os/exec"
+)
+
+// LXDE runs shell commands to setup LXDE for kiosk mode
+func LXDE(path string) {
+ var command = "/usr/bin/lxpanel"
+
+ args := []string{"--profile", "LXDE"}
+ runCommand(path, command, args, true)
+ command = "/usr/bin/pcmanfm"
+ args = []string{"--desktop", "--profile", "LXDE"}
+ runCommand(path, command, args, true)
+ command = "/usr/bin/xset"
+ runCommand(path, command, args, true)
+ args = []string{"s", "off"}
+ runCommand(path, command, args, true)
+ args = []string{"-dpms"}
+ runCommand(path, command, args, true)
+ args = []string{"s", "noblank"}
+ runCommand(path, command, args, true)
+ command = "/usr/bin/unclutter"
+ displayEnv := os.Getenv("DISPLAY")
+ args = []string{"-display", displayEnv, "-idle", "5"}
+
+ go runCommand(path, command, args, true)
+}
+
+func runCommand(path string, command string, args []string, waitForEnd bool) {
+ // check if command exists
+ log.Printf("path: %v", path)
+ log.Printf("command: %v", command)
+ log.Printf("arg0: %v", args[0])
+ cmd := exec.Command(command, args...)
+ cmd.Env = append(os.Environ(),
+ "DISPLAY=:0.0",
+ "XAUTHORITY="+path+"/.Xauthority",
+ )
+ err := cmd.Start()
+ if err != nil {
+ //log.Printf(err)
+ log.Printf("Error in output, ignoring...")
+ }
+ if waitForEnd {
+ log.Printf("Waiting for command to finish...")
+ err = cmd.Wait()
+ if err != nil {
+ log.Printf("Command finished with error: %v", err)
+ }
+ }
+}
+
+
+
package kiosk
+
+import (
+ "context"
+ "io/ioutil"
+ "log"
+ "os"
+ "time"
+
+ "github.com/chromedp/chromedp"
+)
+
+// GrafanaKioskAnonymous creates a chrome-based kiosk using a local grafana-server account
+func GrafanaKioskAnonymous(cfg *Config) {
+ dir, err := ioutil.TempDir("", "chromedp-example")
+ if err != nil {
+ panic(err)
+ }
+ defer os.RemoveAll(dir)
+
+ opts := []chromedp.ExecAllocatorOption{
+ chromedp.NoFirstRun,
+ chromedp.NoDefaultBrowserCheck,
+ chromedp.Flag("noerrdialogs", true),
+ chromedp.Flag("kiosk", true),
+ chromedp.Flag("bwsi", true),
+ chromedp.Flag("incognito", true),
+ chromedp.Flag("disable-sync", true),
+ chromedp.Flag("disable-notifications", true),
+ chromedp.Flag("disable-overlay-scrollbar", true),
+ chromedp.Flag("ignore-certificate-errors", cfg.Target.IgnoreCertificateErrors),
+ chromedp.Flag("test-type", cfg.Target.IgnoreCertificateErrors),
+ chromedp.Flag("window-position", cfg.General.WindowPosition),
+ chromedp.Flag("check-for-update-interval", "31536000"),
+ chromedp.UserDataDir(dir),
+ }
+
+ allocCtx, cancel := chromedp.NewExecAllocator(context.Background(), opts...)
+ defer cancel()
+
+ // also set up a custom logger
+ taskCtx, cancel := chromedp.NewContext(allocCtx, chromedp.WithLogf(log.Printf))
+ defer cancel()
+
+ listenChromeEvents(taskCtx, consoleAPICall|targetCrashed)
+
+ // ensure that the browser process is started
+ if err := chromedp.Run(taskCtx); err != nil {
+ panic(err)
+ }
+
+ // Give browser time to load next page (this can be prone to failure, explore different options vs sleeping)
+ time.Sleep(2000 * time.Millisecond)
+
+ var generatedURL = GenerateURL(cfg.Target.URL, cfg.General.Mode, cfg.General.AutoFit, cfg.Target.IsPlayList)
+ log.Println("Navigating to ", generatedURL)
+ /*
+ Launch chrome and look for main-view element
+ */
+ if err := chromedp.Run(taskCtx,
+ chromedp.Navigate(generatedURL),
+ chromedp.WaitVisible(`//div[@class="main-view"]`, chromedp.BySearch),
+ // wait forever (for now)
+ chromedp.WaitVisible("notinputPassword", chromedp.ByID),
+ ); err != nil {
+ panic(err)
+ }
+ log.Println("Sleep before exit...")
+ // wait here for the process to exit
+ time.Sleep(2000 * time.Millisecond)
+ log.Println("Exit...")
+}
+
+
+
package kiosk
+
+import (
+ "context"
+ "io/ioutil"
+ "log"
+ "os"
+ "time"
+
+ "github.com/chromedp/chromedp"
+ "github.com/chromedp/chromedp/kb"
+)
+
+// GrafanaKioskGCOM creates a chrome-based kiosk using a grafana.com authenticated account
+func GrafanaKioskGCOM(cfg *Config) {
+ dir, err := ioutil.TempDir("", "chromedp-example")
+ if err != nil {
+ panic(err)
+ }
+ defer os.RemoveAll(dir)
+
+ opts := []chromedp.ExecAllocatorOption{
+ chromedp.NoFirstRun,
+ chromedp.NoDefaultBrowserCheck,
+ chromedp.Flag("noerrdialogs", true),
+ chromedp.Flag("kiosk", true),
+ chromedp.Flag("bwsi", true),
+ chromedp.Flag("incognito", true),
+ chromedp.Flag("disable-sync", true),
+ chromedp.Flag("disable-notifications", true),
+ chromedp.Flag("disable-overlay-scrollbar", true),
+ chromedp.Flag("window-position", cfg.General.WindowPosition),
+ chromedp.Flag("check-for-update-interval", "31536000"),
+ chromedp.UserDataDir(dir),
+ }
+
+ allocCtx, cancel := chromedp.NewExecAllocator(context.Background(), opts...)
+ defer cancel()
+
+ // also set up a custom logger
+ taskCtx, cancel := chromedp.NewContext(allocCtx, chromedp.WithLogf(log.Printf))
+ defer cancel()
+
+ listenChromeEvents(taskCtx, targetCrashed)
+
+ // ensure that the browser process is started
+ if err := chromedp.Run(taskCtx); err != nil {
+ panic(err)
+ }
+
+ var generatedURL = GenerateURL(cfg.Target.URL, cfg.General.Mode, cfg.General.AutoFit, cfg.Target.IsPlayList)
+ log.Println("Navigating to ", generatedURL)
+
+ /*
+ Launch chrome, click the grafana.com button, fill out login form and submit
+ */
+ // XPATH of grafana.com login button = //*[@href="login/grafana_com"]/i
+ // XPATH for grafana.com login (new) = //a[contains(@href,'login/grafana_com')]
+
+ //chromedp.WaitVisible(`//*[@href="login/grafana_com"]/i`, chromedp.BySearch),
+
+ // Click the grafana_com login button
+ if err := chromedp.Run(taskCtx,
+ chromedp.Navigate(generatedURL),
+ chromedp.ActionFunc(func(context.Context) error {
+ log.Println("waiting for login dialog")
+ return nil
+ }),
+ chromedp.WaitVisible(`//a[contains(@href,'login/grafana_com')]`, chromedp.BySearch),
+ chromedp.ActionFunc(func(context.Context) error {
+ log.Println("gcom login dialog detected")
+ return nil
+ }),
+ chromedp.Click(`//a[contains(@href,'login/grafana_com')]/..`, chromedp.BySearch),
+ chromedp.ActionFunc(func(context.Context) error {
+ log.Println("gcom button clicked")
+ return nil
+ }),
+ ); err != nil {
+ panic(err)
+ }
+ // Give browser time to load next page (this can be prone to failure, explore different options vs sleeping)
+ time.Sleep(2000 * time.Millisecond)
+ // Fill out grafana_com login page
+ if err := chromedp.Run(taskCtx,
+ chromedp.WaitVisible(`//input[@name="login"]`, chromedp.BySearch),
+ chromedp.SendKeys(`//input[@name="login"]`, cfg.Target.Username, chromedp.BySearch),
+ chromedp.SendKeys(`//input[@name="password"]`, cfg.Target.Password+kb.Enter, chromedp.BySearch),
+ chromedp.WaitVisible(`notinputPassword`, chromedp.ByID),
+ ); err != nil {
+ panic(err)
+ }
+}
+
+
+
package kiosk
+
+import (
+ "context"
+ "io/ioutil"
+ "log"
+ "os"
+ "time"
+
+ "github.com/chromedp/chromedp"
+ "github.com/chromedp/chromedp/kb"
+)
+
+// GrafanaKioskGenericOauth creates a chrome-based kiosk using a oauth2 authenticated account
+func GrafanaKioskGenericOauth(cfg *Config) {
+ dir, err := ioutil.TempDir("", "chromedp-example")
+ if err != nil {
+ panic(err)
+ }
+ defer os.RemoveAll(dir)
+
+ opts := []chromedp.ExecAllocatorOption{
+ chromedp.NoFirstRun,
+ chromedp.NoDefaultBrowserCheck,
+ chromedp.Flag("noerrdialogs", true),
+ chromedp.Flag("kiosk", true),
+ chromedp.Flag("bwsi", true),
+ chromedp.Flag("incognito", true),
+ chromedp.Flag("disable-sync", true),
+ chromedp.Flag("disable-notifications", true),
+ chromedp.Flag("disable-overlay-scrollbar", true),
+ chromedp.Flag("window-position", cfg.General.WindowPosition),
+ chromedp.Flag("check-for-update-interval", "31536000"),
+ chromedp.UserDataDir(dir),
+ }
+
+ allocCtx, cancel := chromedp.NewExecAllocator(context.Background(), opts...)
+ defer cancel()
+
+ // also set up a custom logger
+ taskCtx, cancel := chromedp.NewContext(allocCtx, chromedp.WithLogf(log.Printf))
+ defer cancel()
+
+ listenChromeEvents(taskCtx, targetCrashed)
+
+ // ensure that the browser process is started
+ if err := chromedp.Run(taskCtx); err != nil {
+ panic(err)
+ }
+
+ var generatedURL = GenerateURL(cfg.Target.URL, cfg.General.Mode, cfg.General.AutoFit, cfg.Target.IsPlayList)
+ log.Println("Navigating to ", generatedURL)
+
+ /*
+ Launch chrome, click the GENERIC OAUTH button, fill out login form and submit
+ */
+ // XPATH of grafana.com for Generic OAUTH login button = //*[@href="login/grafana_com"]/i
+
+ // Click the OAUTH login button
+ log.Println("Oauth_Auto_Login enabled: ", cfg.GOAUTH.AutoLogin)
+ if cfg.GOAUTH.AutoLogin {
+ if err := chromedp.Run(taskCtx,
+ chromedp.Navigate(generatedURL),
+ ); err != nil {
+ panic(err)
+ }
+ } else {
+ if err := chromedp.Run(taskCtx,
+ chromedp.Navigate(generatedURL),
+ chromedp.WaitVisible(`//*[@href="login/generic_oauth"]`, chromedp.BySearch),
+ chromedp.Click(`//*[@href="login/generic_oauth"]`, chromedp.BySearch),
+ ); err != nil {
+ panic(err)
+ }
+ }
+
+ // Give browser time to load next page (this can be prone to failure, explore different options vs sleeping)
+ time.Sleep(2000 * time.Millisecond)
+ // Fill out OAUTH login page
+ if err := chromedp.Run(taskCtx,
+ chromedp.WaitVisible(`//input[@name="`+cfg.GOAUTH.UsernameField+`"]`, chromedp.BySearch),
+ chromedp.SendKeys(`//input[@name="`+cfg.GOAUTH.UsernameField+`"]`, cfg.Target.Username, chromedp.BySearch),
+ chromedp.SendKeys(`//input[@name="`+cfg.GOAUTH.PasswordField+`"]`, cfg.Target.Password+kb.Enter, chromedp.BySearch),
+ chromedp.WaitVisible(`notinputPassword`, chromedp.ByID),
+ ); err != nil {
+ panic(err)
+ }
+}
+
+
+
package kiosk
+
+import (
+ "context"
+ "log"
+
+ "github.com/chromedp/cdproto/inspector"
+ "github.com/chromedp/cdproto/runtime"
+ "github.com/chromedp/chromedp"
+)
+
+type chromeEvents int
+
+const (
+ consoleAPICall chromeEvents = 1 << iota
+ targetCrashed
+)
+
+func listenChromeEvents(taskCtx context.Context, events chromeEvents) {
+ chromedp.ListenTarget(taskCtx, func(ev interface{}) {
+ switch ev := ev.(type) {
+ case *runtime.EventConsoleAPICalled:
+ if events&consoleAPICall != 0 {
+ log.Printf("console.%s call:\n", ev.Type)
+ for _, arg := range ev.Args {
+ log.Printf(" %s - %s\n", arg.Type, arg.Value)
+ }
+ }
+ case *inspector.EventTargetCrashed:
+ if events&targetCrashed != 0 {
+ log.Printf("target crashed, reload...")
+ go func() {
+ _ = chromedp.Run(taskCtx, chromedp.Reload())
+ }()
+ }
+ }
+ })
+}
+
+
+
package kiosk
+
+import (
+ "context"
+ "io/ioutil"
+ "log"
+ "os"
+ "time"
+
+ "github.com/chromedp/chromedp"
+ "github.com/chromedp/chromedp/kb"
+)
+
+// GrafanaKioskLocal creates a chrome-based kiosk using a local grafana-server account
+func GrafanaKioskLocal(cfg *Config) {
+ dir, err := ioutil.TempDir("", "chromedp-example")
+ if err != nil {
+ panic(err)
+ }
+ defer os.RemoveAll(dir)
+
+ opts := []chromedp.ExecAllocatorOption{
+ chromedp.NoFirstRun,
+ chromedp.NoDefaultBrowserCheck,
+ chromedp.Flag("noerrdialogs", true),
+ chromedp.Flag("kiosk", true),
+ chromedp.Flag("bwsi", true),
+ chromedp.Flag("incognito", true),
+ chromedp.Flag("disable-sync", true),
+ chromedp.Flag("disable-notifications", true),
+ chromedp.Flag("disable-overlay-scrollbar", true),
+ chromedp.Flag("ignore-certificate-errors", cfg.Target.IgnoreCertificateErrors),
+ chromedp.Flag("test-type", cfg.Target.IgnoreCertificateErrors),
+ chromedp.Flag("window-position", cfg.General.WindowPosition),
+ chromedp.Flag("check-for-update-interval", "31536000"),
+ chromedp.UserDataDir(dir),
+ }
+
+ allocCtx, cancel := chromedp.NewExecAllocator(context.Background(), opts...)
+ defer cancel()
+
+ // also set up a custom logger
+ taskCtx, cancel := chromedp.NewContext(allocCtx, chromedp.WithLogf(log.Printf))
+ defer cancel()
+
+ listenChromeEvents(taskCtx, targetCrashed)
+
+ // ensure that the browser process is started
+ if err := chromedp.Run(taskCtx); err != nil {
+ panic(err)
+ }
+
+ var generatedURL = GenerateURL(cfg.Target.URL, cfg.General.Mode, cfg.General.AutoFit, cfg.Target.IsPlayList)
+ log.Println("Navigating to ", generatedURL)
+ /*
+ Launch chrome and login with local user account
+
+ name=user, type=text
+ id=inputPassword, type=password, name=password
+ */
+ // Give browser time to load next page (this can be prone to failure, explore different options vs sleeping)
+ time.Sleep(2000 * time.Millisecond)
+
+ if err := chromedp.Run(taskCtx,
+ chromedp.Navigate(generatedURL),
+ chromedp.WaitVisible(`//input[@name="user"]`, chromedp.BySearch),
+ chromedp.SendKeys(`//input[@name="user"]`, cfg.Target.Username, chromedp.BySearch),
+ chromedp.SendKeys(`//input[@name="password"]`, cfg.Target.Password+kb.Enter, chromedp.BySearch),
+ chromedp.WaitVisible(`notinputPassword`, chromedp.ByID),
+ ); err != nil {
+ panic(err)
+ }
+}
+
+
+
package kiosk
+
+import (
+ "log"
+ "net/url"
+)
+
+// GenerateURL constructs URL with appropriate parameters for kiosk mode
+func GenerateURL(anURL string, kioskMode string, autoFit bool, isPlayList bool) string {
+ u, _ := url.ParseRequestURI(anURL)
+ q, _ := url.ParseQuery(u.RawQuery)
+
+ switch kioskMode {
+ case "tv": // TV
+ q.Set("kiosk", "tv") // no sidebar, topnav without buttons
+ log.Printf("KioskMode: TV")
+ case "full": // FULLSCREEN
+ q.Set("kiosk", "1") // sidebar and topnav always shown
+ log.Printf("KioskMode: Fullscreen")
+ case "disabled": // FULLSCREEN
+ log.Printf("KioskMode: Disabled")
+ default: // disabled
+ q.Set("kiosk", "1") // sidebar and topnav always shown
+ log.Printf("KioskMode: Fullscreen")
+ }
+ // a playlist should also go inactive immediately
+ if isPlayList {
+ q.Set("inactive", "1")
+ }
+ u.RawQuery = q.Encode()
+ if autoFit {
+ u.RawQuery += "&autofitpanels"
+ }
+ return u.String()
+}
+
+
+
+
+
+
diff --git a/coverage/coverage.out b/coverage/coverage.out
new file mode 100644
index 0000000..1765232
--- /dev/null
+++ b/coverage/coverage.out
@@ -0,0 +1,177 @@
+mode: set
+github.com/grafana/grafana-kiosk/pkg/kiosk/listen_chrome_events.go:19.71,20.54 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/listen_chrome_events.go:20.54,21.26 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/listen_chrome_events.go:22.39,23.34 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/listen_chrome_events.go:29.38,30.33 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/listen_chrome_events.go:23.34,25.33 2 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/listen_chrome_events.go:25.33,27.6 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/listen_chrome_events.go:30.33,32.15 2 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/listen_chrome_events.go:32.15,34.6 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/local_login.go:15.37,17.16 2 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/local_login.go:20.2,49.46 8 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/local_login.go:53.2,70.16 4 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/local_login.go:17.16,18.13 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/local_login.go:49.46,50.13 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/local_login.go:70.16,71.13 1 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:36.40,57.19 19 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:64.2,65.16 2 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:68.2,68.10 1 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:57.19,62.3 4 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:65.16,67.3 1 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:71.23,74.22 2 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:79.2,82.25 3 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:89.2,89.43 1 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:74.22,78.3 3 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:82.25,88.3 4 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:92.33,110.2 14 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:112.13,117.27 3 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:148.2,150.26 2 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:154.2,155.16 2 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:158.2,160.29 2 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:165.2,168.32 3 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:117.27,119.68 1 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:119.68,122.4 2 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:122.9,124.4 1 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:125.8,128.48 2 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:131.3,146.48 14 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:128.48,130.4 1 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:150.26,152.3 1 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:155.16,156.13 1 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:160.29,162.3 1 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:169.15,171.32 2 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:172.14,174.31 2 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:175.16,177.39 2 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:178.10,180.36 2 0
+github.com/grafana/grafana-kiosk/pkg/initialize/lxde.go:10.24,31.2 18 0
+github.com/grafana/grafana-kiosk/pkg/initialize/lxde.go:33.78,44.16 7 0
+github.com/grafana/grafana-kiosk/pkg/initialize/lxde.go:48.2,48.16 1 0
+github.com/grafana/grafana-kiosk/pkg/initialize/lxde.go:44.16,47.3 1 0
+github.com/grafana/grafana-kiosk/pkg/initialize/lxde.go:48.16,51.17 3 0
+github.com/grafana/grafana-kiosk/pkg/initialize/lxde.go:51.17,53.4 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/anonymous_login.go:14.41,16.16 2 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/anonymous_login.go:19.2,48.46 8 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/anonymous_login.go:53.2,65.16 4 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/anonymous_login.go:68.2,71.24 3 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/anonymous_login.go:16.16,17.13 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/anonymous_login.go:48.46,49.13 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/anonymous_login.go:65.16,66.13 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/grafana_com_login.go:15.36,17.16 2 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/grafana_com_login.go:20.2,47.46 8 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/grafana_com_login.go:51.2,65.51 3 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/grafana_com_login.go:83.2,90.16 2 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/grafana_com_login.go:17.16,18.13 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/grafana_com_login.go:47.46,48.13 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/grafana_com_login.go:65.51,68.4 2 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/grafana_com_login.go:70.51,73.4 2 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/grafana_com_login.go:75.51,78.4 2 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/grafana_com_login.go:79.16,80.13 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/grafana_com_login.go:90.16,91.13 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/grafana_genericoauth_login.go:15.44,17.16 2 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/grafana_genericoauth_login.go:20.2,47.46 8 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/grafana_genericoauth_login.go:51.2,61.26 4 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/grafana_genericoauth_login.go:78.2,85.16 2 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/grafana_genericoauth_login.go:17.16,18.13 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/grafana_genericoauth_login.go:47.46,48.13 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/grafana_genericoauth_login.go:61.26,64.17 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/grafana_genericoauth_login.go:64.17,65.14 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/grafana_genericoauth_login.go:67.8,72.17 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/grafana_genericoauth_login.go:72.17,73.14 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/grafana_genericoauth_login.go:85.16,86.13 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/utils.go:9.88,13.19 3 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/utils.go:27.2,27.16 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/utils.go:30.2,31.13 2 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/utils.go:34.2,34.19 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/utils.go:14.12,16.30 2 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/utils.go:17.14,19.38 2 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/utils.go:20.18,21.36 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/utils.go:22.10,24.38 2 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/utils.go:27.16,29.3 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/utils.go:31.13,33.3 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/listen_chrome_events.go:19.71,20.54 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/listen_chrome_events.go:20.54,21.26 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/listen_chrome_events.go:22.39,23.34 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/listen_chrome_events.go:29.38,30.33 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/listen_chrome_events.go:23.34,25.33 2 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/listen_chrome_events.go:25.33,27.6 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/listen_chrome_events.go:30.33,32.15 2 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/listen_chrome_events.go:32.15,34.6 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/local_login.go:15.37,17.16 2 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/local_login.go:20.2,49.46 8 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/local_login.go:53.2,70.16 4 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/local_login.go:17.16,18.13 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/local_login.go:49.46,50.13 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/local_login.go:70.16,71.13 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/utils.go:9.88,13.19 3 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/utils.go:27.2,27.16 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/utils.go:30.2,31.13 2 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/utils.go:34.2,34.19 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/utils.go:14.12,16.30 2 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/utils.go:17.14,19.38 2 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/utils.go:20.18,21.36 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/utils.go:22.10,24.38 2 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/utils.go:27.16,29.3 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/utils.go:31.13,33.3 1 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:36.40,57.19 19 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:64.2,65.16 2 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:68.2,68.10 1 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:57.19,62.3 4 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:65.16,67.3 1 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:71.23,74.22 2 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:79.2,82.25 3 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:89.2,89.43 1 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:74.22,78.3 3 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:82.25,88.3 4 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:92.33,110.2 14 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:112.13,117.27 3 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:148.2,150.26 2 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:154.2,155.16 2 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:158.2,160.29 2 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:165.2,168.32 3 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:117.27,119.68 1 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:119.68,122.4 2 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:122.9,124.4 1 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:125.8,128.48 2 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:131.3,146.48 14 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:128.48,130.4 1 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:150.26,152.3 1 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:155.16,156.13 1 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:160.29,162.3 1 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:169.15,171.32 2 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:172.14,174.31 2 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:175.16,177.39 2 0
+github.com/grafana/grafana-kiosk/pkg/cmd/grafana-kiosk/main.go:178.10,180.36 2 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/anonymous_login.go:14.41,16.16 2 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/anonymous_login.go:19.2,48.46 8 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/anonymous_login.go:53.2,65.16 4 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/anonymous_login.go:68.2,71.24 3 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/anonymous_login.go:16.16,17.13 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/anonymous_login.go:48.46,49.13 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/anonymous_login.go:65.16,66.13 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/grafana_com_login.go:15.36,17.16 2 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/grafana_com_login.go:20.2,47.46 8 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/grafana_com_login.go:51.2,65.51 3 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/grafana_com_login.go:83.2,90.16 2 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/grafana_com_login.go:17.16,18.13 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/grafana_com_login.go:47.46,48.13 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/grafana_com_login.go:65.51,68.4 2 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/grafana_com_login.go:70.51,73.4 2 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/grafana_com_login.go:75.51,78.4 2 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/grafana_com_login.go:79.16,80.13 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/grafana_com_login.go:90.16,91.13 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/grafana_genericoauth_login.go:15.44,17.16 2 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/grafana_genericoauth_login.go:20.2,47.46 8 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/grafana_genericoauth_login.go:51.2,61.26 4 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/grafana_genericoauth_login.go:78.2,85.16 2 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/grafana_genericoauth_login.go:17.16,18.13 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/grafana_genericoauth_login.go:47.46,48.13 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/grafana_genericoauth_login.go:61.26,64.17 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/grafana_genericoauth_login.go:64.17,65.14 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/grafana_genericoauth_login.go:67.8,72.17 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/grafana_genericoauth_login.go:72.17,73.14 1 0
+github.com/grafana/grafana-kiosk/pkg/kiosk/grafana_genericoauth_login.go:85.16,86.13 1 0
+github.com/grafana/grafana-kiosk/pkg/initialize/lxde.go:10.24,31.2 18 0
+github.com/grafana/grafana-kiosk/pkg/initialize/lxde.go:33.78,44.16 7 0
+github.com/grafana/grafana-kiosk/pkg/initialize/lxde.go:48.2,48.16 1 0
+github.com/grafana/grafana-kiosk/pkg/initialize/lxde.go:44.16,47.3 1 0
+github.com/grafana/grafana-kiosk/pkg/initialize/lxde.go:48.16,51.17 3 0
+github.com/grafana/grafana-kiosk/pkg/initialize/lxde.go:51.17,53.4 1 0