Ubuntu: start on install
This commit is contained in:
		
							parent
							
								
									2b7148c3ae
								
							
						
					
					
						commit
						04e162b20f
					
				
							
								
								
									
										23
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								README.md
									
									
									
									
									
								
							| @ -172,6 +172,7 @@ move serviceman.exe %userprofile%\bin\serviceman.exe | |||||||
| **All Others** | **All Others** | ||||||
| 
 | 
 | ||||||
| ``` | ``` | ||||||
|  | chmod a+x ./serviceman | ||||||
| sudo mv ./serviceman /usr/local/bin/ | sudo mv ./serviceman /usr/local/bin/ | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| @ -364,9 +365,9 @@ Where `conf.json` looks something like | |||||||
| 
 | 
 | ||||||
| ```json | ```json | ||||||
| { | { | ||||||
| 	"title": "Demo", |   "title": "Demo", | ||||||
| 	"exec": "/Users/me/go-demo/demo", |   "exec": "/Users/me/go-demo/demo", | ||||||
| 	"argv": ["--foo", "bar", "--baz", "qux"] |   "argv": ["--foo", "bar", "--baz", "qux"] | ||||||
| } | } | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| @ -380,10 +381,10 @@ names and relative paths. | |||||||
| 
 | 
 | ||||||
| ```json | ```json | ||||||
| { | { | ||||||
| 	"title": "Demo", |   "title": "Demo", | ||||||
| 	"interpreter": "node.exe", |   "interpreter": "node.exe", | ||||||
| 	"exec": "./bin/demo.js", |   "exec": "./bin/demo.js", | ||||||
| 	"argv": ["--foo", "bar", "--baz", "qux"] |   "argv": ["--foo", "bar", "--baz", "qux"] | ||||||
| } | } | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| @ -391,12 +392,12 @@ That's equivalent to this: | |||||||
| 
 | 
 | ||||||
| ```json | ```json | ||||||
| { | { | ||||||
| 	"title": "Demo", |   "title": "Demo", | ||||||
| 
 | 
 | ||||||
| 	"name": "demo", |   "name": "demo", | ||||||
| 
 | 
 | ||||||
| 	"exec": "node.exe", |   "exec": "node.exe", | ||||||
| 	"argv": ["./bin/demo.js", "--foo", "bar", "--baz", "qux"] |   "argv": ["./bin/demo.js", "--foo", "bar", "--baz", "qux"] | ||||||
| } | } | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -12,18 +12,20 @@ | |||||||
| # sudo journalctl {{ if not .System -}} --user {{ end -}} -xefu {{ .Name }} | # sudo journalctl {{ if not .System -}} --user {{ end -}} -xefu {{ .Name }} | ||||||
| 
 | 
 | ||||||
| [Unit] | [Unit] | ||||||
| Description={{ .Title }} - {{ .Desc }} | Description={{ .Title }} {{ if .Desc }}- {{ .Desc }}{{ end }} | ||||||
|  | {{ if .URL -}} | ||||||
| Documentation={{ .URL }} | Documentation={{ .URL }} | ||||||
|  | {{ end -}} | ||||||
| {{ if .System -}} | {{ if .System -}} | ||||||
| After=network-online.target | After=network-online.target | ||||||
| Wants=network-online.target systemd-networkd-wait-online.service | Wants=network-online.target systemd-networkd-wait-online.service | ||||||
| {{- end }} |  | ||||||
| 
 | 
 | ||||||
|  | {{ end -}} | ||||||
| [Service] | [Service] | ||||||
| # Restart on crash (bad signal), but not on 'clean' failure (error exit code) | # Restart on crash (bad signal), but not on 'clean' failure (error exit code) | ||||||
| # Allow up to 3 restarts within 10 seconds | # Allow up to 3 restarts within 10 seconds | ||||||
| # (it's unlikely that a user or properly-running script will do this) | # (it's unlikely that a user or properly-running script will do this) | ||||||
| Restart=on-abnormal | Restart=always | ||||||
| StartLimitInterval=10 | StartLimitInterval=10 | ||||||
| StartLimitBurst=3 | StartLimitBurst=3 | ||||||
| 
 | 
 | ||||||
| @ -36,7 +38,7 @@ Group={{ .Group }} | |||||||
| {{ if .Workdir -}} | {{ if .Workdir -}} | ||||||
| WorkingDirectory={{ .Workdir }} | WorkingDirectory={{ .Workdir }} | ||||||
| {{ end -}} | {{ end -}} | ||||||
| ExecStart={{if .Interpreter }}{{ .Interpreter }} {{ end }}{{ .Exec }} {{- range $arg := .Argv }}{{ $arg }} {{- end }} | ExecStart={{if .Interpreter }}{{ .Interpreter }} {{ end }}{{ .Exec }}{{ range $arg := .Argv }} {{ $arg }}{{ end }} | ||||||
| ExecReload=/bin/kill -USR1 $MAINPID | ExecReload=/bin/kill -USR1 $MAINPID | ||||||
| 
 | 
 | ||||||
| {{if .Production -}} | {{if .Production -}} | ||||||
|  | |||||||
| @ -58,6 +58,8 @@ func start(system bool, home string, name string) error { | |||||||
| 		}, | 		}, | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	cmds = adjustPrivs(system, cmds) | ||||||
|  | 
 | ||||||
| 	fmt.Println() | 	fmt.Println() | ||||||
| 	for i := range cmds { | 	for i := range cmds { | ||||||
| 		exe := cmds[i] | 		exe := cmds[i] | ||||||
| @ -112,8 +114,7 @@ func install(c *service.Service) error { | |||||||
| 	plistName := c.ReverseDNS + ".plist" | 	plistName := c.ReverseDNS + ".plist" | ||||||
| 	plistPath := filepath.Join(plistDir, plistName) | 	plistPath := filepath.Join(plistDir, plistName) | ||||||
| 	if err := ioutil.WriteFile(plistPath, rw.Bytes(), 0644); err != nil { | 	if err := ioutil.WriteFile(plistPath, rw.Bytes(), 0644); err != nil { | ||||||
| 
 | 		return fmt.Errorf("Error writing %s: %v", plistPath, err) | ||||||
| 		return fmt.Errorf("ioutil.WriteFile error: %v", err) |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// TODO --no-start | 	// TODO --no-start | ||||||
| @ -127,20 +128,4 @@ func install(c *service.Service) error { | |||||||
| 
 | 
 | ||||||
| 	fmt.Printf("Added and started '%s' as a launchctl service.\n", c.Name) | 	fmt.Printf("Added and started '%s' as a launchctl service.\n", c.Name) | ||||||
| 	return nil | 	return nil | ||||||
| 
 |  | ||||||
| 	/* |  | ||||||
| 		fmt.Printf("Installed. To start '%s' run the following:\n", c.Name) |  | ||||||
| 		// TODO template config file |  | ||||||
| 		if "" != c.Home { |  | ||||||
| 			plistPath = strings.Replace(plistPath, c.Home, "~", 1) |  | ||||||
| 		} |  | ||||||
| 		sudo := "" |  | ||||||
| 		if c.System { |  | ||||||
| 			sudo = "sudo " |  | ||||||
| 		} |  | ||||||
| 		fmt.Printf("\t%slaunchctl load -w %s\n", sudo, plistPath) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 		return nil |  | ||||||
| 	*/ |  | ||||||
| } | } | ||||||
|  | |||||||
| @ -13,16 +13,100 @@ import ( | |||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| var ( | var ( | ||||||
| 	srvLen      int | 	srvLen     int | ||||||
| 	srvExt      = ".service" | 	srvExt     = ".service" | ||||||
| 	srvSysPath  = "/etc/systemd/system" | 	srvSysPath = "/etc/systemd/system" | ||||||
| 	srvUserPath = ".local/share/systemd/user" | 
 | ||||||
|  | 	// Not sure which of these it's supposed to be... | ||||||
|  | 	// * ~/.local/share/systemd/user/watchdog.service | ||||||
|  | 	// * ~/.config/systemd/user/watchdog.service | ||||||
|  | 	// https://wiki.archlinux.org/index.php/Systemd/User | ||||||
|  | 	// This seems to work on Ubuntu | ||||||
|  | 	srvUserPath = ".config/systemd/user" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func init() { | func init() { | ||||||
| 	srvLen = len(srvExt) | 	srvLen = len(srvExt) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func start(system bool, home string, name string) error { | ||||||
|  | 	sys, user, err := getMatchingSrvs(home, name) | ||||||
|  | 	if nil != err { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	var service string | ||||||
|  | 	if system { | ||||||
|  | 		service, err = getOneSysSrv(sys, user, name) | ||||||
|  | 		if nil != err { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 		service = filepath.Join(srvSysPath, service) | ||||||
|  | 	} else { | ||||||
|  | 		service, err = getOneUserSrv(home, sys, user, name) | ||||||
|  | 		if nil != err { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 		service = filepath.Join(home, srvUserPath, service) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	var cmds []Runnable | ||||||
|  | 	if system { | ||||||
|  | 		cmds = []Runnable{ | ||||||
|  | 			Runnable{ | ||||||
|  | 				Exec: "systemctl", | ||||||
|  | 				Args: []string{"daemon-reload"}, | ||||||
|  | 				Must: false, | ||||||
|  | 			}, | ||||||
|  | 			Runnable{ | ||||||
|  | 				Exec: "systemctl", | ||||||
|  | 				Args: []string{"stop", name + ".service"}, | ||||||
|  | 				Must: false, | ||||||
|  | 			}, | ||||||
|  | 			Runnable{ | ||||||
|  | 				Exec:     "systemctl", | ||||||
|  | 				Args:     []string{"start", name + ".service"}, | ||||||
|  | 				Badwords: []string{"not found", "failed"}, | ||||||
|  | 				Must:     true, | ||||||
|  | 			}, | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		cmds = []Runnable{ | ||||||
|  | 			Runnable{ | ||||||
|  | 				Exec: "systemctl", | ||||||
|  | 				Args: []string{"--user", "daemon-reload"}, | ||||||
|  | 				Must: false, | ||||||
|  | 			}, | ||||||
|  | 			Runnable{ | ||||||
|  | 				Exec: "systemctl", | ||||||
|  | 				Args: []string{"stop", "--user", name + ".service"}, | ||||||
|  | 				Must: false, | ||||||
|  | 			}, | ||||||
|  | 			Runnable{ | ||||||
|  | 				Exec:     "systemctl", | ||||||
|  | 				Args:     []string{"start", "--user", name + ".service"}, | ||||||
|  | 				Badwords: []string{"not found", "failed"}, | ||||||
|  | 				Must:     true, | ||||||
|  | 			}, | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	cmds = adjustPrivs(system, cmds) | ||||||
|  | 
 | ||||||
|  | 	fmt.Println() | ||||||
|  | 	for i := range cmds { | ||||||
|  | 		exe := cmds[i] | ||||||
|  | 		fmt.Println(exe.String()) | ||||||
|  | 		err := exe.Run() | ||||||
|  | 		if nil != err { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	fmt.Println() | ||||||
|  | 
 | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func install(c *service.Service) error { | func install(c *service.Service) error { | ||||||
| 	// Linux-specific config options | 	// Linux-specific config options | ||||||
| 	if c.System { | 	if c.System { | ||||||
| @ -33,17 +117,12 @@ func install(c *service.Service) error { | |||||||
| 	if "" == c.Group { | 	if "" == c.Group { | ||||||
| 		c.Group = c.User | 		c.Group = c.User | ||||||
| 	} | 	} | ||||||
| 	serviceDir := srvSysPath |  | ||||||
| 
 | 
 | ||||||
| 	// Check paths first | 	// Check paths first | ||||||
| 	serviceName := c.Name + ".service" | 	serviceDir := srvSysPath | ||||||
| 	if !c.System { | 	if !c.System { | ||||||
| 		// Not sure which of these it's supposed to be... |  | ||||||
| 		// * ~/.local/share/systemd/user/watchdog.service |  | ||||||
| 		// * ~/.config/systemd/user/watchdog.service |  | ||||||
| 		// https://wiki.archlinux.org/index.php/Systemd/User |  | ||||||
| 		serviceDir = filepath.Join(c.Home, srvUserPath) | 		serviceDir = filepath.Join(c.Home, srvUserPath) | ||||||
| 		err := os.MkdirAll(filepath.Dir(serviceDir), 0755) | 		err := os.MkdirAll(serviceDir, 0755) | ||||||
| 		if nil != err { | 		if nil != err { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
| @ -67,22 +146,27 @@ func install(c *service.Service) error { | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Write the file out | 	// Write the file out | ||||||
|  | 	serviceName := c.Name + ".service" | ||||||
| 	servicePath := filepath.Join(serviceDir, serviceName) | 	servicePath := filepath.Join(serviceDir, serviceName) | ||||||
| 	if err := ioutil.WriteFile(servicePath, rw.Bytes(), 0644); err != nil { | 	if err := ioutil.WriteFile(servicePath, rw.Bytes(), 0644); err != nil { | ||||||
| 		return fmt.Errorf("ioutil.WriteFile error: %v", err) | 		return fmt.Errorf("Error writing %s: %v", servicePath, err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// TODO template this as well? | 	// TODO --no-start | ||||||
| 	userspace := "" | 	err = start(c.System, c.Home, c.Name) | ||||||
| 	sudo := "sudo " | 	if nil != err { | ||||||
| 	if !c.System { | 		sudo := "" | ||||||
| 		userspace = "--user " | 		// --user-unit rather than --user --unit for older systemd | ||||||
| 		sudo = "" | 		unit := "--user-unit" | ||||||
|  | 		if c.System { | ||||||
|  | 			sudo = "sudo " | ||||||
|  | 			unit = "--unit" | ||||||
|  | 		} | ||||||
|  | 		fmt.Printf("If things don't go well you should be able to get additional logging from journalctl:\n") | ||||||
|  | 		fmt.Printf("\t%sjournalctl -xe %s %s.service\n", sudo, unit, c.Name) | ||||||
|  | 		return err | ||||||
| 	} | 	} | ||||||
| 	fmt.Printf("System service installed as '%s'.\n", servicePath) | 
 | ||||||
| 	fmt.Printf("Run the following to start '%s':\n", c.Name) | 	fmt.Printf("Added and started '%s' as a systemd service.\n", c.Name) | ||||||
| 	fmt.Printf("\t" + sudo + "systemctl " + userspace + "daemon-reload\n") |  | ||||||
| 	fmt.Printf("\t"+sudo+"systemctl "+userspace+"restart %s.service\n", c.Name) |  | ||||||
| 	fmt.Printf("\t"+sudo+"journalctl "+userspace+"-xefu %s\n", c.Name) |  | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  | |||||||
| @ -45,34 +45,33 @@ func (x Runnable) Run() error { | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	if nil != err { | 	if nil != err { | ||||||
| 		return fmt.Errorf("Failed to run %s %s\n%s\n", x.Exec, strings.Join(x.Args, " "), str) | 		var comment string | ||||||
|  | 		if len(x.Keywords) > 0 { | ||||||
|  | 			comment += "# output must match all of:\n" | ||||||
|  | 			comment += "# \t" + strings.Join(x.Keywords, "\n#\t") + "\n" | ||||||
|  | 		} | ||||||
|  | 		if len(x.Badwords) > 0 { | ||||||
|  | 			comment += "# output must not match any of:\n" | ||||||
|  | 			comment += "# \t" + strings.Join(x.Badwords, "\n#\t") + "\n" | ||||||
|  | 		} | ||||||
|  | 		return fmt.Errorf("Failed to run %s %s\n%s\n%s\n", x.Exec, strings.Join(x.Args, " "), str, comment) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (x Runnable) String() string { | func (x Runnable) String() string { | ||||||
| 	var comment string |  | ||||||
| 	var must = "true" | 	var must = "true" | ||||||
| 
 | 
 | ||||||
| 	if x.Must { | 	if x.Must { | ||||||
| 		must = "exit" | 		must = "exit" | ||||||
| 		if len(x.Keywords) > 0 { |  | ||||||
| 			comment += "# output must match all of:\n" |  | ||||||
| 			comment += "\t" + strings.Join(x.Keywords, "#\t \n") + "\n" |  | ||||||
| 		} |  | ||||||
| 		if len(x.Badwords) > 0 { |  | ||||||
| 			comment += "# output must not match any of:\n" |  | ||||||
| 			comment += "\t" + strings.Join(x.Keywords, "#\t \n") + "\n" |  | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return strings.TrimSpace(fmt.Sprintf( | 	return strings.TrimSpace(fmt.Sprintf( | ||||||
| 		"%s %s || %s\n%s", | 		"%s %s || %s\n", | ||||||
| 		x.Exec, | 		x.Exec, | ||||||
| 		strings.Join(x.Args, " "), | 		strings.Join(x.Args, " "), | ||||||
| 		must, | 		must, | ||||||
| 		comment, |  | ||||||
| 	)) | 	)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -203,3 +202,20 @@ func getOneUserSrv(home string, sys []string, user []string, name string) (strin | |||||||
| 
 | 
 | ||||||
| 	return service, nil | 	return service, nil | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | func adjustPrivs(system bool, cmds []Runnable) []Runnable { | ||||||
|  | 	if !system || isPrivileged() { | ||||||
|  | 		return cmds | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	sudos := cmds | ||||||
|  | 	cmds = []Runnable{} | ||||||
|  | 	for i := range sudos { | ||||||
|  | 		exe := sudos[i] | ||||||
|  | 		exe.Args = append([]string{exe.Exec}, exe.Args...) | ||||||
|  | 		exe.Exec = "sudo" | ||||||
|  | 		cmds = append(cmds, exe) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return cmds | ||||||
|  | } | ||||||
|  | |||||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @ -126,15 +126,16 @@ func add() { | |||||||
| 	conf.Normalize(force) | 	conf.Normalize(force) | ||||||
| 
 | 
 | ||||||
| 	//fmt.Printf("\n%#v\n\n", conf) | 	//fmt.Printf("\n%#v\n\n", conf) | ||||||
|  | 	if conf.System && !manager.IsPrivileged() { | ||||||
|  | 		fmt.Fprintf(os.Stderr, "Warning: You may need to use 'sudo' to add %q as a privileged system service.\n", conf.Name) | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	err = manager.Install(conf) | 	err = manager.Install(conf) | ||||||
| 	if nil != err { | 	if nil != err { | ||||||
| 		fmt.Fprintf(os.Stderr, "%s\n", err) | 		fmt.Fprintf(os.Stderr, "%s\n", err) | ||||||
| 		fmt.Fprintf(os.Stderr, "Use 'sudo' to add service as a privileged system service.\n") |  | ||||||
| 		fmt.Fprintf(os.Stderr, "Use '--user' to add service as an user service.\n") |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	fmt.Printf("If all went well the logs should have been created at:\n\t%s\n", conf.Logdir) | 	printLogMessage(conf) | ||||||
| 	fmt.Println() | 	fmt.Println() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										11
									
								
								serviceman_darwin.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								serviceman_darwin.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,11 @@ | |||||||
|  | package main | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 
 | ||||||
|  | 	"git.rootprojects.org/root/go-serviceman/service" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func printLogMessage(conf *service.Service) { | ||||||
|  | 	fmt.Printf("If all went well the logs should have been created at:\n\t%s\n", conf.Logdir) | ||||||
|  | } | ||||||
							
								
								
									
										26
									
								
								serviceman_linux.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								serviceman_linux.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,26 @@ | |||||||
|  | package main | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 
 | ||||||
|  | 	"git.rootprojects.org/root/go-serviceman/manager" | ||||||
|  | 	"git.rootprojects.org/root/go-serviceman/service" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func printLogMessage(conf *service.Service) { | ||||||
|  | 	sudo := "" | ||||||
|  | 	unit := "--unit" | ||||||
|  | 	if conf.System { | ||||||
|  | 		if !manager.IsPrivileged() { | ||||||
|  | 			sudo = "sudo" | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		unit = "--user-unit" | ||||||
|  | 	} | ||||||
|  | 	fmt.Println("If all went well you should be able to see some goodies in the logs:") | ||||||
|  | 	fmt.Printf("\t%sjournalctl -xe %s %s.service\n", sudo, unit, conf.Name) | ||||||
|  | 	if !conf.System { | ||||||
|  | 		fmt.Println("\nIf that's not the case, see https://unix.stackexchange.com/a/486566/45554.") | ||||||
|  | 		fmt.Println("(you may need to run `systemctl restart systemd-journald`)") | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										11
									
								
								serviceman_windows.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								serviceman_windows.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,11 @@ | |||||||
|  | package main | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 
 | ||||||
|  | 	"git.rootprojects.org/root/go-serviceman/service" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func printLogMessage(conf *service.Service) { | ||||||
|  | 	fmt.Printf("If all went well the logs should have been created at:\n\t%s\n", conf.Logdir) | ||||||
|  | } | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user