| 
									
										
										
										
											2019-07-05 12:48:58 -06:00
										 |  |  | package manager | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"io/ioutil" | 
					
						
							|  |  |  | 	"os/exec" | 
					
						
							|  |  |  | 	"path/filepath" | 
					
						
							|  |  |  | 	"strings" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-10 01:16:45 -06:00
										 |  |  | func getService(system bool, home string, name string) (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 | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		service, err = getOneUserSrv(home, sys, user, name) | 
					
						
							|  |  |  | 		if nil != err { | 
					
						
							|  |  |  | 			return "", err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return service, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-05 12:48:58 -06:00
										 |  |  | // Runnable defines a command to run, along with its arguments, | 
					
						
							|  |  |  | // and whether or not failing to exit successfully matters. | 
					
						
							|  |  |  | // It also defines whether certains words must exist (or not exist) | 
					
						
							|  |  |  | // in its output, apart from existing successfully, to determine | 
					
						
							|  |  |  | // whether or not it was actually successful. | 
					
						
							|  |  |  | type Runnable struct { | 
					
						
							|  |  |  | 	Exec     string | 
					
						
							|  |  |  | 	Args     []string | 
					
						
							|  |  |  | 	Must     bool | 
					
						
							|  |  |  | 	Keywords []string | 
					
						
							|  |  |  | 	Badwords []string | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (x Runnable) Run() error { | 
					
						
							|  |  |  | 	cmd := exec.Command(x.Exec, x.Args...) | 
					
						
							|  |  |  | 	out, err := cmd.CombinedOutput() | 
					
						
							|  |  |  | 	if !x.Must { | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	good := true | 
					
						
							|  |  |  | 	str := string(out) | 
					
						
							|  |  |  | 	for j := range x.Keywords { | 
					
						
							|  |  |  | 		if !strings.Contains(str, x.Keywords[j]) { | 
					
						
							|  |  |  | 			good = false | 
					
						
							|  |  |  | 			break | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if good && 0 != len(x.Badwords) { | 
					
						
							|  |  |  | 		for j := range x.Badwords { | 
					
						
							|  |  |  | 			if "" != x.Badwords[j] && !strings.Contains(str, x.Badwords[j]) { | 
					
						
							|  |  |  | 				good = false | 
					
						
							|  |  |  | 				break | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if nil != err { | 
					
						
							| 
									
										
										
										
											2019-07-07 06:48:25 +00:00
										 |  |  | 		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) | 
					
						
							| 
									
										
										
										
											2019-07-05 12:48:58 -06:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (x Runnable) String() string { | 
					
						
							|  |  |  | 	var must = "true" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if x.Must { | 
					
						
							|  |  |  | 		must = "exit" | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return strings.TrimSpace(fmt.Sprintf( | 
					
						
							| 
									
										
										
										
											2019-07-07 06:48:25 +00:00
										 |  |  | 		"%s %s || %s\n", | 
					
						
							| 
									
										
										
										
											2019-07-05 12:48:58 -06:00
										 |  |  | 		x.Exec, | 
					
						
							|  |  |  | 		strings.Join(x.Args, " "), | 
					
						
							|  |  |  | 		must, | 
					
						
							|  |  |  | 	)) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func getSrvs(dir string) ([]string, error) { | 
					
						
							|  |  |  | 	plists := []string{} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	infos, err := ioutil.ReadDir(dir) | 
					
						
							|  |  |  | 	if nil != err { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for i := range infos { | 
					
						
							|  |  |  | 		x := infos[i] | 
					
						
							|  |  |  | 		fname := strings.ToLower(x.Name()) | 
					
						
							|  |  |  | 		if strings.HasSuffix(fname, srvExt) { | 
					
						
							|  |  |  | 			plists = append(plists, x.Name()) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return plists, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func getSystemSrvs() ([]string, error) { | 
					
						
							|  |  |  | 	return getSrvs(srvSysPath) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func getUserSrvs(home string) ([]string, error) { | 
					
						
							| 
									
										
										
										
											2019-07-07 02:14:25 -06:00
										 |  |  | 	return getSrvs(filepath.Join(home, srvUserPath)) | 
					
						
							| 
									
										
										
										
											2019-07-05 12:48:58 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-07 02:14:25 -06:00
										 |  |  | // "come.example.foo.plist" matches "foo" | 
					
						
							| 
									
										
										
										
											2019-07-05 12:48:58 -06:00
										 |  |  | func filterMatchingSrvs(plists []string, name string) []string { | 
					
						
							|  |  |  | 	filtered := []string{} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for i := range plists { | 
					
						
							|  |  |  | 		pname := plists[i] | 
					
						
							|  |  |  | 		lname := strings.ToLower(pname) | 
					
						
							|  |  |  | 		n := len(lname) | 
					
						
							|  |  |  | 		if strings.HasSuffix(lname[:n-srvLen], strings.ToLower(name)) { | 
					
						
							|  |  |  | 			filtered = append(filtered, pname) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return filtered | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func getMatchingSrvs(home string, name string) ([]string, []string, error) { | 
					
						
							|  |  |  | 	sysPlists, err := getSystemSrvs() | 
					
						
							|  |  |  | 	if nil != err { | 
					
						
							|  |  |  | 		return nil, nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var userPlists []string | 
					
						
							|  |  |  | 	if "" != home { | 
					
						
							|  |  |  | 		userPlists, err = getUserSrvs(home) | 
					
						
							|  |  |  | 		if nil != err { | 
					
						
							|  |  |  | 			return nil, nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return filterMatchingSrvs(sysPlists, name), filterMatchingSrvs(userPlists, name), nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func getExactSrvMatch(srvs []string, name string) string { | 
					
						
							|  |  |  | 	for i := range srvs { | 
					
						
							|  |  |  | 		srv := srvs[i] | 
					
						
							|  |  |  | 		n := len(srv) | 
					
						
							|  |  |  | 		if srv[:n-srvLen] == strings.ToLower(name) { | 
					
						
							|  |  |  | 			return srv | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return "" | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func getOneSysSrv(sys []string, user []string, name string) (string, error) { | 
					
						
							| 
									
										
										
										
											2019-07-07 02:14:25 -06:00
										 |  |  | 	if service := getExactSrvMatch(user, name); "" != service { | 
					
						
							|  |  |  | 		return filepath.Join(srvSysPath, service), nil | 
					
						
							| 
									
										
										
										
											2019-07-05 12:48:58 -06:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	n := len(sys) | 
					
						
							|  |  |  | 	switch { | 
					
						
							|  |  |  | 	case 0 == n: | 
					
						
							| 
									
										
										
										
											2019-07-07 02:14:25 -06:00
										 |  |  | 		errstr := fmt.Sprintf("Didn't find user service matching %q\n", name) | 
					
						
							| 
									
										
										
										
											2019-07-05 12:48:58 -06:00
										 |  |  | 		if 0 != len(user) { | 
					
						
							|  |  |  | 			errstr += fmt.Sprintf("Did you intend to run a user service instead?\n\t%s\n", strings.Join(user, "\n\t")) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-07-07 02:14:25 -06:00
										 |  |  | 		return "", fmt.Errorf(errstr) | 
					
						
							| 
									
										
										
										
											2019-07-05 12:48:58 -06:00
										 |  |  | 	case n > 1: | 
					
						
							| 
									
										
										
										
											2019-07-07 02:14:25 -06:00
										 |  |  | 		errstr := fmt.Sprintf("Found more than one matching service:\n\t%s\n", strings.Join(sys, "\n\t")) | 
					
						
							| 
									
										
										
										
											2019-07-05 12:48:58 -06:00
										 |  |  | 		return "", fmt.Errorf(errstr) | 
					
						
							| 
									
										
										
										
											2019-07-07 02:14:25 -06:00
										 |  |  | 	default: | 
					
						
							|  |  |  | 		return filepath.Join(srvSysPath, sys[0]), nil | 
					
						
							| 
									
										
										
										
											2019-07-05 12:48:58 -06:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func getOneUserSrv(home string, sys []string, user []string, name string) (string, error) { | 
					
						
							| 
									
										
										
										
											2019-07-07 02:14:25 -06:00
										 |  |  | 	if service := getExactSrvMatch(user, name); "" != service { | 
					
						
							|  |  |  | 		return filepath.Join(home, srvUserPath, service), nil | 
					
						
							| 
									
										
										
										
											2019-07-05 12:48:58 -06:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	n := len(user) | 
					
						
							|  |  |  | 	switch { | 
					
						
							|  |  |  | 	case 0 == n: | 
					
						
							| 
									
										
										
										
											2019-07-07 02:14:25 -06:00
										 |  |  | 		errstr := fmt.Sprintf("Didn't find user service matching %q\n", name) | 
					
						
							| 
									
										
										
										
											2019-07-05 12:48:58 -06:00
										 |  |  | 		if 0 != len(sys) { | 
					
						
							|  |  |  | 			errstr += fmt.Sprintf("Did you intend to run a system service instead?\n\t%s\n", strings.Join(sys, "\n\t")) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-07-07 02:14:25 -06:00
										 |  |  | 		return "", fmt.Errorf(errstr) | 
					
						
							| 
									
										
										
										
											2019-07-05 12:48:58 -06:00
										 |  |  | 	case n > 1: | 
					
						
							| 
									
										
										
										
											2019-07-07 02:14:25 -06:00
										 |  |  | 		errstr := fmt.Sprintf("Found more than one matching service:\n\t%s\n", strings.Join(user, "\n\t")) | 
					
						
							| 
									
										
										
										
											2019-07-05 12:48:58 -06:00
										 |  |  | 		return "", fmt.Errorf(errstr) | 
					
						
							| 
									
										
										
										
											2019-07-07 02:14:25 -06:00
										 |  |  | 	default: | 
					
						
							|  |  |  | 		return filepath.Join(home, srvUserPath, user[0]), nil | 
					
						
							| 
									
										
										
										
											2019-07-05 12:48:58 -06:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-07-07 06:48:25 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 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 | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-07-13 20:50:00 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | func Run(bin string, args ...string) error { | 
					
						
							|  |  |  | 	cmd := exec.Command(bin, args...) | 
					
						
							|  |  |  | 	// for debugging | 
					
						
							|  |  |  | 	/* | 
					
						
							|  |  |  | 		out, err := cmd.CombinedOutput() | 
					
						
							|  |  |  | 		if nil != err { | 
					
						
							|  |  |  | 			fmt.Println(err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		fmt.Println(string(out)) | 
					
						
							|  |  |  | 	*/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	err := cmd.Start() | 
					
						
							|  |  |  | 	if nil != err { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } |