| 
									
										
										
										
											2019-07-04 01:36:35 -06:00
										 |  |  | package manager | 
					
						
							| 
									
										
										
										
											2019-07-01 02:44:48 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2019-07-02 23:51:30 -06:00
										 |  |  | 	"encoding/json" | 
					
						
							| 
									
										
										
										
											2019-07-01 02:44:48 -06:00
										 |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2019-07-02 23:51:30 -06:00
										 |  |  | 	"io/ioutil" | 
					
						
							| 
									
										
										
										
											2019-07-01 02:44:48 -06:00
										 |  |  | 	"log" | 
					
						
							| 
									
										
										
										
											2019-07-02 23:51:30 -06:00
										 |  |  | 	"os" | 
					
						
							| 
									
										
										
										
											2019-07-01 02:44:48 -06:00
										 |  |  | 	"path/filepath" | 
					
						
							|  |  |  | 	"strings" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-10 01:16:45 -06:00
										 |  |  | 	"git.rootprojects.org/root/go-serviceman/runner" | 
					
						
							| 
									
										
										
										
											2019-07-02 23:51:30 -06:00
										 |  |  | 	"git.rootprojects.org/root/go-serviceman/service" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-01 02:44:48 -06:00
										 |  |  | 	"golang.org/x/sys/windows/registry" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-05 12:48:58 -06:00
										 |  |  | var ( | 
					
						
							|  |  |  | 	srvLen      int | 
					
						
							|  |  |  | 	srvExt      = ".json" | 
					
						
							|  |  |  | 	srvSysPath  = "/opt/serviceman/etc" | 
					
						
							|  |  |  | 	srvUserPath = ".local/opt/serviceman/etc" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func init() { | 
					
						
							|  |  |  | 	srvLen = len(srvExt) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-02 23:51:30 -06:00
										 |  |  | // TODO nab some goodness from https://github.com/takama/daemon | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-01 02:44:48 -06:00
										 |  |  | // TODO system service requires elevated privileges | 
					
						
							|  |  |  | // See https://coolaj86.com/articles/golang-and-windows-and-admins-oh-my/ | 
					
						
							| 
									
										
										
										
											2019-07-13 20:50:00 -06:00
										 |  |  | func install(c *service.Service) (string, error) { | 
					
						
							| 
									
										
										
										
											2019-07-01 02:44:48 -06:00
										 |  |  | 	/* | 
					
						
							|  |  |  | 		// LEAVE THIS DOCUMENTATION HERE | 
					
						
							|  |  |  | 		reg.exe | 
					
						
							|  |  |  | 		/V <value name> - "Telebit" | 
					
						
							|  |  |  | 		/T <data type> - "REG_SZ" - String | 
					
						
							|  |  |  | 		/D <value data> | 
					
						
							|  |  |  | 		/C - case sensitive | 
					
						
							|  |  |  | 		/F <search data??> - not sure... | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Special Note: | 
					
						
							|  |  |  | 		"/c" is similar to -- (*nix), and required within the data string | 
					
						
							|  |  |  | 		So instead of setting "do.exe --do-arg1 --do-arg2" | 
					
						
							|  |  |  | 		you must set "do.exe /c --do-arg1 --do-arg2" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		vars.telebitNode += '.exe'; | 
					
						
							|  |  |  | 		var cmd = 'reg.exe add "HKCU\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run"' | 
					
						
							|  |  |  | 		+ ' /V "Telebit" /t REG_SZ /D ' | 
					
						
							|  |  |  | 		+ '"' + things.argv[0] + ' /c '  // something like C:\Program Files (x64)\nodejs\node.exe | 
					
						
							|  |  |  | 		+ [ path.join(__dirname, 'bin/telebitd.js') | 
					
						
							|  |  |  | 			, 'daemon' | 
					
						
							|  |  |  | 			, '--config' | 
					
						
							|  |  |  | 			, path.join(os.homedir(), '.config/telebit/telebitd.yml') | 
					
						
							|  |  |  | 			].join(' ') | 
					
						
							|  |  |  | 		+ '" /F' | 
					
						
							|  |  |  | 		; | 
					
						
							|  |  |  | 	*/ | 
					
						
							|  |  |  | 	autorunKey := `SOFTWARE\Microsoft\Windows\CurrentVersion\Run` | 
					
						
							|  |  |  | 	k, _, err := registry.CreateKey( | 
					
						
							|  |  |  | 		registry.CURRENT_USER, | 
					
						
							|  |  |  | 		autorunKey, | 
					
						
							|  |  |  | 		registry.SET_VALUE, | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		log.Fatal(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	defer k.Close() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-10 01:16:45 -06:00
										 |  |  | 	// Try to stop before trying to copy the file | 
					
						
							|  |  |  | 	_ = runner.Stop(c) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-02 23:51:30 -06:00
										 |  |  | 	args, err := installServiceman(c) | 
					
						
							|  |  |  | 	if nil != err { | 
					
						
							| 
									
										
										
										
											2019-07-13 20:50:00 -06:00
										 |  |  | 		return "", err | 
					
						
							| 
									
										
										
										
											2019-07-01 02:44:48 -06:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-02 23:51:30 -06:00
										 |  |  | 	/* | 
					
						
							|  |  |  | 		setArgs := "" | 
					
						
							|  |  |  | 		args := c.Argv | 
					
						
							|  |  |  | 		exec := c.Exec | 
					
						
							|  |  |  | 		bin := c.Interpreter | 
					
						
							|  |  |  | 		if "" != bin { | 
					
						
							|  |  |  | 			// If this is something like node or python, | 
					
						
							|  |  |  | 			// the interpeter must be called as "the main thing" | 
					
						
							|  |  |  | 			// and "the app" must be an argument | 
					
						
							|  |  |  | 			args = append([]string{exec}, args...) | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			// Otherwise, if "the app" is a true binary, | 
					
						
							|  |  |  | 			// it can be "the main thing" | 
					
						
							|  |  |  | 			bin = exec | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// The final string ends up looking something like one of these: | 
					
						
							| 
									
										
										
										
											2019-07-03 02:11:50 -06:00
										 |  |  | 		// `"C:\Users\aj\.local\opt\appname\appname.js" -p 8080` | 
					
						
							|  |  |  | 		// `"C:\Program Files (x64)\nodejs\node.exe" C:\Users\aj\.local\opt\appname\appname.js -p 8080` | 
					
						
							| 
									
										
										
										
											2019-07-02 23:51:30 -06:00
										 |  |  | 		regSZ := bin + setArgs + strings.Join(c.Argv, " ") | 
					
						
							|  |  |  | 	*/ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-03 02:11:50 -06:00
										 |  |  | 	regSZ := fmt.Sprintf(`"%s" %s`, args[0], strings.Join(args[1:], " ")) | 
					
						
							| 
									
										
										
										
											2019-07-01 02:44:48 -06:00
										 |  |  | 	if len(regSZ) > 260 { | 
					
						
							| 
									
										
										
										
											2019-07-13 20:50:00 -06:00
										 |  |  | 		return "", fmt.Errorf("data value is too long for registry entry") | 
					
						
							| 
									
										
										
										
											2019-07-01 02:44:48 -06:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-07-03 02:11:50 -06:00
										 |  |  | 	// In order for a windows gui program to not show a console, | 
					
						
							|  |  |  | 	// it has to not output any messages? | 
					
						
							|  |  |  | 	//fmt.Println("Set Registry Key:") | 
					
						
							|  |  |  | 	//fmt.Println(autorunKey, c.Title, regSZ) | 
					
						
							| 
									
										
										
										
											2019-07-01 02:44:48 -06:00
										 |  |  | 	k.SetStringValue(c.Title, regSZ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-13 20:50:00 -06:00
										 |  |  | 	err = start(c) | 
					
						
							|  |  |  | 	return "serviceman", err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func Render(c *service.Service) ([]byte, error) { | 
					
						
							|  |  |  | 	b, err := json.Marshal(c) | 
					
						
							|  |  |  | 	if nil != err { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return b, nil | 
					
						
							| 
									
										
										
										
											2019-07-01 02:44:48 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-10 01:16:45 -06:00
										 |  |  | func start(conf *service.Service) error { | 
					
						
							|  |  |  | 	args := getRunnerArgs(conf) | 
					
						
							| 
									
										
										
										
											2019-07-13 20:50:00 -06:00
										 |  |  | 	args = append(args, "--daemon") | 
					
						
							|  |  |  | 	return Run(args[0], args[1:]...) | 
					
						
							| 
									
										
										
										
											2019-07-10 01:16:45 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func stop(conf *service.Service) error { | 
					
						
							|  |  |  | 	return runner.Stop(conf) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func getRunnerArgs(c *service.Service) []string { | 
					
						
							| 
									
										
										
										
											2019-07-02 23:51:30 -06:00
										 |  |  | 	self := os.Args[0] | 
					
						
							| 
									
										
										
										
											2019-07-04 03:36:58 -06:00
										 |  |  | 	debug := "" | 
					
						
							|  |  |  | 	if strings.Contains(self, "debug.exe") { | 
					
						
							|  |  |  | 		debug = "debug." | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-07-10 01:16:45 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-02 23:51:30 -06:00
										 |  |  | 	smdir := `\opt\serviceman` | 
					
						
							|  |  |  | 	// TODO support service level services (which probably wouldn't need serviceman) | 
					
						
							|  |  |  | 	smdir = filepath.Join(c.Home, ".local", smdir) | 
					
						
							|  |  |  | 	// for now we'll scope the runner to the name of the application | 
					
						
							| 
									
										
										
										
											2019-07-04 03:36:58 -06:00
										 |  |  | 	smbin := filepath.Join(smdir, `bin\serviceman.`+debug+c.Name+`.exe`) | 
					
						
							| 
									
										
										
										
											2019-07-02 23:51:30 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-10 01:16:45 -06:00
										 |  |  | 	confpath := filepath.Join(smdir, `etc`) | 
					
						
							|  |  |  | 	conffile := filepath.Join(confpath, c.Name+`.json`) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return []string{ | 
					
						
							|  |  |  | 		smbin, | 
					
						
							|  |  |  | 		"run", | 
					
						
							|  |  |  | 		"--config", | 
					
						
							|  |  |  | 		conffile, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // copies self to install path and returns config path | 
					
						
							|  |  |  | func installServiceman(c *service.Service) ([]string, error) { | 
					
						
							|  |  |  | 	// TODO check version and upgrade or dismiss | 
					
						
							|  |  |  | 	self := os.Args[0] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	args := getRunnerArgs(c) | 
					
						
							|  |  |  | 	smbin := args[0] | 
					
						
							|  |  |  | 	conffile := args[len(args)-1] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-02 23:51:30 -06:00
										 |  |  | 	if smbin != self { | 
					
						
							| 
									
										
										
										
											2019-07-03 00:05:12 -06:00
										 |  |  | 		err := os.MkdirAll(filepath.Dir(smbin), 0755) | 
					
						
							| 
									
										
										
										
											2019-07-02 23:51:30 -06:00
										 |  |  | 		if nil != err { | 
					
						
							| 
									
										
										
										
											2019-07-03 00:05:12 -06:00
										 |  |  | 			return nil, err | 
					
						
							| 
									
										
										
										
											2019-07-02 23:51:30 -06:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		bin, err := ioutil.ReadFile(self) | 
					
						
							|  |  |  | 		if nil != err { | 
					
						
							| 
									
										
										
										
											2019-07-03 00:05:12 -06:00
										 |  |  | 			return nil, err | 
					
						
							| 
									
										
										
										
											2019-07-02 23:51:30 -06:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-07-03 00:05:12 -06:00
										 |  |  | 		err = ioutil.WriteFile(smbin, bin, 0755) | 
					
						
							| 
									
										
										
										
											2019-07-02 23:51:30 -06:00
										 |  |  | 		if nil != err { | 
					
						
							| 
									
										
										
										
											2019-07-03 00:05:12 -06:00
										 |  |  | 			return nil, err | 
					
						
							| 
									
										
										
										
											2019-07-02 23:51:30 -06:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-13 20:50:00 -06:00
										 |  |  | 	b, err := Render(c) | 
					
						
							| 
									
										
										
										
											2019-07-02 23:51:30 -06:00
										 |  |  | 	if nil != err { | 
					
						
							|  |  |  | 		// this should be impossible, so we'll just panic | 
					
						
							|  |  |  | 		panic(err) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-07-10 01:16:45 -06:00
										 |  |  | 	err = os.MkdirAll(filepath.Dir(conffile), 0755) | 
					
						
							| 
									
										
										
										
											2019-07-03 00:43:59 -06:00
										 |  |  | 	if nil != err { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	err = ioutil.WriteFile(conffile, b, 0640) | 
					
						
							| 
									
										
										
										
											2019-07-02 23:51:30 -06:00
										 |  |  | 	if nil != err { | 
					
						
							| 
									
										
										
										
											2019-07-03 00:05:12 -06:00
										 |  |  | 		return nil, err | 
					
						
							| 
									
										
										
										
											2019-07-02 23:51:30 -06:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-10 01:16:45 -06:00
										 |  |  | 	return args, nil | 
					
						
							| 
									
										
										
										
											2019-07-02 23:51:30 -06:00
										 |  |  | } |