Mount local FS; get struct pointer so we can modify values
This commit is contained in:
		
							
								
								
									
										17
									
								
								cmd/list.go
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								cmd/list.go
									
									
									
									
									
								
							| @ -16,13 +16,13 @@ import ( | ||||
|  | ||||
| // Recurse into structure, returning reflect.Value of wanted field. | ||||
| //  Nested fields are named with a dot (ex "MyStruct.MyField") | ||||
| func getStructFieldValue(parentStruct interface{}, fieldName string) (reflect.Value, string, error) { | ||||
| func getStructFieldValue(parentStruct interface{}, fieldName string) (*reflect.Value, string, error) { | ||||
| 	v := reflect.ValueOf(parentStruct) | ||||
|  | ||||
| 	if v.Kind() == reflect.Ptr { | ||||
| /*	if v.Kind() == reflect.Ptr { | ||||
| 		v = v.Elem() | ||||
| 	} | ||||
|  | ||||
| */ | ||||
| 	if false { | ||||
| 		for i := 0 ; i < v.NumField(); i++ { | ||||
| 			f := v.Field(i) | ||||
| @ -34,7 +34,8 @@ func getStructFieldValue(parentStruct interface{}, fieldName string) (reflect.Va | ||||
|  | ||||
| 	if strings.Contains(fieldName, ".") { | ||||
| 		fs := strings.Split(fieldName, ".") | ||||
| 		f := v.FieldByName(fs[0]) | ||||
| 		//f := v.FieldByName(fs[0]) | ||||
| 		f := v.Elem().FieldByName(fs[0]) | ||||
| 		if f.Kind() == reflect.Struct { | ||||
| 			return getStructFieldValue(f.Interface(), strings.Join(fs[1:], ".")) | ||||
| 		} else { | ||||
| @ -43,13 +44,13 @@ func getStructFieldValue(parentStruct interface{}, fieldName string) (reflect.Va | ||||
| 	} else { | ||||
| 		f := v.FieldByName(fieldName) | ||||
| 		if f.IsValid() { | ||||
| 			return f, fieldName, nil | ||||
| 			return &f, fieldName, nil | ||||
| 		} else { | ||||
| 			return v, fieldName, errors.New(fmt.Sprintf("Field not found: %s", fieldName)) | ||||
| 			return &v, fieldName, errors.New(fmt.Sprintf("Field not found: %s", fieldName)) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return v, fieldName, nil | ||||
| 	return &v, fieldName, nil | ||||
| } | ||||
|  | ||||
|  | ||||
| @ -365,7 +366,7 @@ func ListJails(args []string, display bool) { | ||||
| 		js := initSortStruct() | ||||
|  | ||||
| 		// The way we manage criteria quantity is not very elegant... | ||||
| 		var fct1, fct2, fct3 reflect.Value | ||||
| 		var fct1, fct2, fct3 *reflect.Value | ||||
| 		for i, c := range strings.Split(gSortFields, ",") { | ||||
| 			var fctName string | ||||
| 			if strings.HasPrefix(c, "-") { | ||||
|  | ||||
							
								
								
									
										37
									
								
								cmd/root.go
									
									
									
									
									
								
							
							
						
						
									
										37
									
								
								cmd/root.go
									
									
									
									
									
								
							| @ -3,14 +3,16 @@ package cmd | ||||
| import ( | ||||
| 	"os" | ||||
| 	"fmt" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"encoding/json" | ||||
|  | ||||
| 	"github.com/spf13/cobra" | ||||
| 	"github.com/spf13/viper" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	version = "0.02" | ||||
| 	gVersion = "0.022a" | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| @ -24,6 +26,8 @@ var ( | ||||
| 	gSortFields		string | ||||
| 	gNoLineSep		bool | ||||
|  | ||||
| 	gHostVersion	float64 | ||||
|  | ||||
|  | ||||
| 	rootCmd = & cobra.Command{ | ||||
|     	Use:    "gocage", | ||||
| @ -33,6 +37,7 @@ It support iocage jails and can coexist with iocage.`, | ||||
|  | ||||
| 	    Run: func(cmd *cobra.Command, args []string) { | ||||
| 	        fmt.Println("Here we are in the Run") | ||||
| 			cleanAfterRun() | ||||
| 	    }, | ||||
| 	} | ||||
|  | ||||
| @ -41,7 +46,8 @@ It support iocage jails and can coexist with iocage.`, | ||||
| 	    Short: "Print the version number of GoCage", | ||||
| 	    Long:  `Let this show you how much fail I had to get this *cough* perfect`, | ||||
| 	    Run: func(cmd *cobra.Command, args []string) { | ||||
| 	        fmt.Printf("GoCage v.%s\n", version) | ||||
| 	        fmt.Printf("GoCage v.%s on FreeBSD %.1f\n", gVersion, gHostVersion) | ||||
| 			cleanAfterRun() | ||||
| 	    }, | ||||
| 	} | ||||
|  | ||||
| @ -53,6 +59,7 @@ Jail list can be restricted by adding name on command line | ||||
| ex: gocage list srv-db srv-web`, | ||||
| 		Run: func(cmd *cobra.Command, args []string) { | ||||
| 			ListJails(args, true) | ||||
| 			cleanAfterRun() | ||||
| 		}, | ||||
| 	} | ||||
| 	 | ||||
| @ -64,6 +71,7 @@ ex: gocage list srv-db srv-web`, | ||||
| 			// Get the inventory | ||||
| 			ListJails(args, false) | ||||
| 			StopJail(args) | ||||
| 			cleanAfterRun() | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| @ -74,6 +82,7 @@ ex: gocage list srv-db srv-web`, | ||||
| 			// Get the inventory | ||||
| 			ListJails(args, false) | ||||
| 			StartJail(args) | ||||
| 			cleanAfterRun() | ||||
| 		}, | ||||
| 	} | ||||
| ) | ||||
| @ -98,6 +107,15 @@ func init() { | ||||
|     rootCmd.AddCommand(listCmd) | ||||
|     rootCmd.AddCommand(stopCmd) | ||||
|     rootCmd.AddCommand(startCmd) | ||||
|  | ||||
| 	// Get FreeBSD version | ||||
| 	out, err := executeCommand("freebsd-version") | ||||
| 	if err != nil { | ||||
| 		fmt.Printf("Error running \"freebsd-version\": %s", err.Error()) | ||||
| 		os.Exit(1) | ||||
| 	} | ||||
| 	gHostVersion, _ = strconv.ParseFloat(strings.Split(out, "-")[0], 32) | ||||
|  | ||||
| } | ||||
|  | ||||
| func initConfig() { | ||||
| @ -141,6 +159,21 @@ func initConfig() { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Called after execution | ||||
| func cleanAfterRun() { | ||||
| 	for _, j := range gJails { | ||||
| 		if j.ConfigUpdated { | ||||
| 			// TODO : Marshall to disk | ||||
| 			fmt.Printf("Config for jail %s will be updated\n", j.Name) | ||||
| 			marshaled, err := json.MarshalIndent(j.Config, "", "    ") | ||||
| 			if err != nil { | ||||
| 				fmt.Printf("ERROR marshaling config: %s\n", err.Error()) | ||||
| 			} | ||||
| 			fmt.Printf(string(marshaled)) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func Execute() { | ||||
|     if err := rootCmd.Execute(); err != nil { | ||||
|         fmt.Fprintln(os.Stderr, err) | ||||
|  | ||||
							
								
								
									
										243
									
								
								cmd/start.go
									
									
									
									
									
								
							
							
						
						
									
										243
									
								
								cmd/start.go
									
									
									
									
									
								
							| @ -9,8 +9,132 @@ import ( | ||||
| //	"os/exec" | ||||
| //	"reflect" | ||||
| 	"strings" | ||||
| //	"strconv" | ||||
| ) | ||||
|  | ||||
| // WIP. Not working now (need to address the real struct field, not a copy of it) | ||||
| // setJailProperty takes a string as propValue, whatever the real property type is. | ||||
| //  It will be converted. | ||||
| func setJailProperty(jail *Jail, propName string, propValue string) error { | ||||
| 	// First get propName type, to convert propValue | ||||
| /*	k, _, err := getStructFieldKind(jail, propName) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	kind := k.String() | ||||
| */ | ||||
|  | ||||
| 	for i, j := range gJails { | ||||
| 		if j.Name == jail.Name { | ||||
| 			val, _, err := getStructFieldValue(&gJails[i], propName) | ||||
| 			if err != nil { | ||||
| 				return errors.New(fmt.Sprintf("Field not found: %s", propName)) | ||||
| 			} | ||||
|  | ||||
| 			/*if kind == "string" { | ||||
| 			// WIll the affectation be done in source object or a copy? | ||||
| 				val.Set(propValue) | ||||
| 			} else if kind == "int" { | ||||
| 				v, err := strconv.Atoi(propValue) | ||||
| 				if err != nil { | ||||
| 					return errors.New(fmt.Sprintf("propValue have wrong type: %s\n", err.Error())) | ||||
| 				} | ||||
| 				val.Set(v) | ||||
| 			} else { | ||||
| 				return errors.New(fmt.Sprintf("Property %s have an unsupported type in setJailProperty!\n", propName)) | ||||
| 			}*/ | ||||
|  | ||||
| 			// panic: reflect: reflect.Value.Set using unaddressable value | ||||
| 			//val.Set(reflect.ValueOf(propValue).Elem()) | ||||
| 			// ...Because val settability is false :-( | ||||
| 			fmt.Printf("settability of val: %v\n", val.CanSet()) | ||||
|  | ||||
| 			// This is OK, using the index to get the real jail object | ||||
| 			//gJails[i].Config.Allow_mlock = 1 | ||||
| 			 | ||||
|  | ||||
| 			// TODO : integrate this function | ||||
| 			setJailConfigUpdated(jail) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| //  We cant use internalName as the value exist only when jail is running | ||||
| func setJailConfigUpdated(jail *Jail) error { | ||||
| 	if len(jail.ConfigPath) == 0 { | ||||
| 		return errors.New(fmt.Sprintf("No config path for jail %s", jail.Name)) | ||||
| 	} | ||||
|  | ||||
| 	for i, j := range gJails { | ||||
| 		if jail.ConfigPath == j.ConfigPath { | ||||
| 			gJails[i].ConfigUpdated = true | ||||
| 			return nil | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return errors.New("Jail not found") | ||||
| } | ||||
|  | ||||
| func mountProcFs(jail *Jail) error { | ||||
| 	cmd = fmt.Sprintf("mount -t procfs proc %s/proc", jail.RootPath) | ||||
| 	_, err := executeCommand(cmd) | ||||
| 	if err != nil { | ||||
| 		return errors.New(fmt.Sprintf("Error mounting procfs on %s/proc: %s", jail.RootPath, err.Error())) | ||||
| 	} | ||||
| 	 | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func mountLinProcFs(jail *Jail) error { | ||||
| 	ldir := fmt.Sprintf("%s/compat/linux/proc", jail.RootPath) | ||||
| 	_, err := os.Stat(ldir) | ||||
| 	if os.IsNotExist(err) { | ||||
| 		errDir := os.MkdirAll(ldir, 0755) | ||||
| 		if errDir != nil { | ||||
| 			return errors.New(fmt.Sprintf("Error creating directory %s: %s", ldir, errDir.Error())) | ||||
| 		} | ||||
| 	} | ||||
| 	cmd = fmt.Sprintf("mount -t linprocfs linproc %s", ldir) | ||||
| 	_, err = executeCommand(cmd) | ||||
| 	if err != nil { | ||||
| 		return errors.New(fmt.Sprintf("Error mounting linprocfs on %s: %s", ldir, err.Error())) | ||||
| 	} | ||||
| 	 | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func mountDevFs(jail *Jail) error { | ||||
| 	cmd = fmt.Sprintf("mount -t devfs dev %s/dev", jail.RootPath) | ||||
| 	_, err := executeCommand(cmd) | ||||
| 	if err != nil { | ||||
| 		return errors.New(fmt.Sprintf("Error mounting devfs on %s/dev: %s", jail.RootPath, err.Error())) | ||||
| 	} | ||||
| 	 | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func mountFdescFs(jail *Jail) error { | ||||
| 	// FreeBSD <= 9.3 do not support fdescfs | ||||
| 	if gHostVersion <= 9.3 { | ||||
| 		fmt.Printf("    FreeBSD <= 9.3 does not support fdescfs, disabling in config\n") | ||||
| 		jail.Config.Mount_fdescfs = 0 | ||||
| 		// Tag config so it will be synced on disk | ||||
| 		jail.ConfigUpdated = true | ||||
| 	 | ||||
| 		// Should we consider this an error? | ||||
| 		return nil | ||||
| 	} | ||||
| 	 | ||||
| 	cmd = fmt.Sprintf("mount -t fdescfs descfs %s/dev/fd", jail.RootPath) | ||||
| 	_, err := executeCommand(cmd) | ||||
| 	if err != nil { | ||||
| 		return errors.New(fmt.Sprintf("Error mounting fdescfs on %s/dev/fd: %s", jail.RootPath, err.Error())) | ||||
| 	} | ||||
| 	 | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func mountAllJailFsFromHost(jail *Jail) error { | ||||
| 	procfsFound := false | ||||
| @ -52,59 +176,107 @@ func mountAllJailFsFromHost(jail *Jail) error { | ||||
|  | ||||
| 	// Mount wanted FS | ||||
| 	if jail.Config.Mount_procfs > 0 && procfsFound == false { | ||||
| 		cmd = fmt.Sprintf("mount -t procfs proc %s/proc", jail.RootPath) | ||||
| 		_, err := executeCommand(cmd) | ||||
| 		err := mountProcFs(jail) | ||||
|         if err != nil { | ||||
| 	        return errors.New(fmt.Sprintf("Error mounting procfs on %s/proc: %s", jail.RootPath, err.Error())) | ||||
| 	        return err | ||||
|         }    | ||||
| 	} | ||||
| 	 | ||||
| 	if jail.Config.Mount_linprocfs > 0 && linProcfsFound == false { | ||||
| 		ldir := fmt.Sprintf("%s/compat/linux/proc", jail.RootPath) | ||||
| 		_, err := os.Stat(ldir) | ||||
| 		if os.IsNotExist(err) { | ||||
| 			errDir := os.MkdirAll(ldir, 0755) | ||||
| 			if errDir != nil { | ||||
| 				return errors.New(fmt.Sprintf("Error creating directory %s: %s", ldir, errDir.Error())) | ||||
| 			} | ||||
| 		} | ||||
| 		cmd = fmt.Sprintf("mount -t linprocfs proc %s", ldir) | ||||
| 		_, err = executeCommand(cmd) | ||||
| 		err = mountLinProcFs(jail) | ||||
|         if err != nil { | ||||
| 	        return errors.New(fmt.Sprintf("Error mounting linprocfs on %s: %s", ldir, err.Error())) | ||||
| 	        return err | ||||
|         } | ||||
| 	} | ||||
| 	 | ||||
| 	if jail.Config.Mount_devfs > 0 && devfsFound == false { | ||||
| 		cmd = fmt.Sprintf("mount -t devfs dev %s/dev", jail.RootPath) | ||||
| 		_, err := executeCommand(cmd) | ||||
|         if err != nil { | ||||
| 	        return errors.New(fmt.Sprintf("Error mounting devfs on %s/dev: %s", jail.RootPath, err.Error())) | ||||
|         } | ||||
| 		err := mountDevFs(jail) | ||||
| 		if err != nil { | ||||
| 	        return err | ||||
| 		} | ||||
| 	} | ||||
| 	if jail.Config.Mount_fdescfs > 0 && fdescfsFound == false { | ||||
| 		cmd = fmt.Sprintf("mount -t fdescfs descfs %s/dev/fd", jail.RootPath) | ||||
| 		_, err := executeCommand(cmd) | ||||
|         if err != nil { | ||||
| 	        return errors.New(fmt.Sprintf("Error mounting fdescfs on %s/dev/fd: %s", jail.RootPath, err.Error())) | ||||
|         } | ||||
| 		err := mountFdescFs(jail) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	 | ||||
| 	// Ces montages doivent-ils etre effectués une fois le jail démarré? | ||||
|  | ||||
| 	// FreeBSD <= 9.3 do not support fdescfs | ||||
| 	//if gHostVersion <= 9.3 && jail.Config.Allow_mount_tmpfs > 0 { | ||||
| 	if gHostVersion <= 9.3 && jail.Config.Allow_mount_tmpfs > 0 { | ||||
| 		fmt.Printf("    FreeBSD <= 9.3 does not support tmpfs, disabling in config\n") | ||||
| 		jail.Config.Allow_mount_tmpfs = 0 | ||||
| 		// Tag config so it will be synced on disk | ||||
| 		jail.ConfigUpdated = true | ||||
| 		err = setJailConfigUpdated(jail) | ||||
| 		if err != nil { | ||||
| 			fmt.Printf(fmt.Sprintf("Error updating config for jail %s: %s", jail.Name, err.Error())) | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if gHostVersion < 12 { | ||||
| 		if jail.Config.Allow_mlock > 0 { | ||||
| 			jail.Config.Allow_mlock = 0		 | ||||
| 			jail.ConfigUpdated = true | ||||
| 			/* WIP | ||||
| 			err = setJailProperty(jail, "Config.Allow_mlock", "0") | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			}*/ | ||||
| 		} | ||||
| 		if jail.Config.Allow_mount_fusefs > 0 { | ||||
| 		} | ||||
| 		if jail.Config.Allow_vmm > 0 { | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
|     return nil  | ||||
| } | ||||
|  | ||||
| /* | ||||
|  | ||||
| // TODO  | ||||
| func mountJailZfs(jail *Jail) error { | ||||
| func prepareJailedZfsDatasets(jail *Jail) error { | ||||
| 	if jail.Config.Jail_zfs > 0 { | ||||
| 		// For jail to mount filesystem, enforce_statfs should be 1 or lower (2 is the default) | ||||
| 		// TODO : Write these changes in jail config file | ||||
| 		jail.Config.Allow_mount = 1 | ||||
| 		jail.Config.Allow_mount_zfs = 1 | ||||
| 		// TODO : Overload Json Unmarshalling to fix bad typed values, keeping iocage compatibility | ||||
| 		if jail.Config.Enforce_statfs > "1" { | ||||
| 			jail.Config.Enforce_statfs = "1" | ||||
| 		} | ||||
| 		for _, d := range strings.Split(jail.Config.Jail_zfs_dataset, " ") { | ||||
| 			 | ||||
| 			// Check if dataset exist, create if necessary | ||||
| 			cmd := fmt.Sprintf("zfs get -H creation %s/%s", jail.Zpool, d) | ||||
| 			out, err := executeCommand(cmd) | ||||
| 			if err != nil { | ||||
| 				if strings.HasSuffix(out, "dataset does not exist") { | ||||
| 					cmd = fmt.Sprintf("zfs create -o compression=lz4 -o mountpoint=none %s/%s", jail.Zpool, d) | ||||
| 					_, err = executeCommand(cmd) | ||||
| 					if err != nil { | ||||
| 						return errors.New(fmt.Sprintf("Error creating dataset %s/%s: %s", jail.Zpool, d, err.Error())) | ||||
| 					} | ||||
| 				} else { | ||||
| 					return errors.New(fmt.Sprintf("Error getting zfs dataset %s: %s", d, err.Error())) | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			cmd = fmt.Sprintf("zfs set jailed=on %s/%s", jail.Zpool, d) | ||||
| 			out, err = executeCommand(cmd) | ||||
| 			if err != nil { | ||||
| 				return errors.New(fmt.Sprintf("Error executing \"zfs set jailed=on %s/%s\": %s", jail.Zpool, d, err.Error())) | ||||
| 			} | ||||
| 			// TODO : Execute "zfs jail $jailname $dataset" when jail will be up | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| */ | ||||
|  | ||||
|  | ||||
|  | ||||
| /* | ||||
| @ -139,9 +311,10 @@ func StartJail(args []string) { | ||||
| 	for _, j := range args { | ||||
| 		fmt.Printf("> Starting jail %s\n", j) | ||||
|  | ||||
| 		for _, rj := range gJails { | ||||
| 		for i, rj := range gJails { | ||||
| 			if rj.Name == j { | ||||
| 				cj = &rj | ||||
| 				// Get jail reference, not a copy of it; So we can modify attributes | ||||
| 				cj = &gJails[i] | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
| @ -163,6 +336,16 @@ func StartJail(args []string) { | ||||
| 			fmt.Printf("  > Mount special filesystems: OK\n") | ||||
| 		} | ||||
|  | ||||
| 		if cj.Config.Jail_zfs > 0 { | ||||
| 			fmt.Printf("  > Prepare ZFS Datasets:\n") | ||||
| 			err := prepareJailedZfsDatasets(cj) | ||||
| 			if err != nil { | ||||
| 				fmt.Printf("ERROR: %s\n", err.Error()) | ||||
| 			} else { | ||||
| 				fmt.Printf("  > Prepare ZFS Datasets: OK\n") | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
|  | ||||
| /* | ||||
| 		out, err := executeCommand(fmt.Sprintf("rctl jail:%s", cj.InternalName)) | ||||
|  | ||||
		Reference in New Issue
	
	Block a user