wrtie config needs sudo usually
This commit is contained in:
@@ -25,6 +25,7 @@ The program:
|
|||||||
- Checks `valid users` entries against local accounts.
|
- Checks `valid users` entries against local accounts.
|
||||||
- Offers to create missing local accounts with `useradd -M -s /usr/sbin/nologin <user>`.
|
- Offers to create missing local accounts with `useradd -M -s /usr/sbin/nologin <user>`.
|
||||||
- If user creation fails because admin rights are needed, explains the issue and offers to retry with `sudo`.
|
- If user creation fails because admin rights are needed, explains the issue and offers to retry with `sudo`.
|
||||||
|
- If saving the Samba config or its backup needs admin rights, explains the issue and offers to retry with `sudo`.
|
||||||
- Writes a timestamped backup before saving changes.
|
- Writes a timestamped backup before saving changes.
|
||||||
|
|
||||||
If you create a local account for a Samba-authenticated share, you may still need to add the Samba password separately:
|
If you create a local account for a Samba-authenticated share, you may still need to add the Samba password separately:
|
||||||
|
|||||||
80
app.go
80
app.go
@@ -238,12 +238,20 @@ func (a *App) deleteShare(doc *Document) error {
|
|||||||
|
|
||||||
func (a *App) writeConfig(doc *Document) error {
|
func (a *App) writeConfig(doc *Document) error {
|
||||||
backup := fmt.Sprintf("%s.bak.%s", a.configPath, time.Now().UTC().Format("20060102T150405Z"))
|
backup := fmt.Sprintf("%s.bak.%s", a.configPath, time.Now().UTC().Format("20060102T150405Z"))
|
||||||
|
serialized := doc.Serialize()
|
||||||
|
|
||||||
if err := copyFile(a.configPath, backup); err != nil {
|
if err := copyFile(a.configPath, backup); err != nil {
|
||||||
return fmt.Errorf("create backup %s: %w", backup, err)
|
if shouldOfferPrivilegeRetry(err) {
|
||||||
|
return a.writeConfigWithPrivilegeRetry(serialized, backup)
|
||||||
|
}
|
||||||
|
return friendlyWriteError("create backup", backup, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := os.WriteFile(a.configPath, []byte(doc.Serialize()), 0o644); err != nil {
|
if err := os.WriteFile(a.configPath, []byte(serialized), 0o644); err != nil {
|
||||||
return fmt.Errorf("write config: %w", err)
|
if shouldOfferPrivilegeRetry(err) {
|
||||||
|
return a.writeConfigWithPrivilegeRetry(serialized, backup)
|
||||||
|
}
|
||||||
|
return friendlyWriteError("write config", a.configPath, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
a.printf("Config written to %s\n", a.configPath)
|
a.printf("Config written to %s\n", a.configPath)
|
||||||
@@ -357,7 +365,7 @@ func (a *App) createUser(name string) error {
|
|||||||
args := []string{"-M", "-s", "/usr/sbin/nologin", name}
|
args := []string{"-M", "-s", "/usr/sbin/nologin", name}
|
||||||
if err := a.runner.Run("useradd", args...); err == nil {
|
if err := a.runner.Run("useradd", args...); err == nil {
|
||||||
return nil
|
return nil
|
||||||
} else if shouldOfferSudoRetry(err) {
|
} else if shouldOfferPrivilegeRetry(err) {
|
||||||
a.println("")
|
a.println("")
|
||||||
a.println("Creating a Linux user usually needs administrator permission.")
|
a.println("Creating a Linux user usually needs administrator permission.")
|
||||||
a.println("I can try again using sudo so you can enter your admin password.")
|
a.println("I can try again using sudo so you can enter your admin password.")
|
||||||
@@ -468,7 +476,7 @@ func copyFile(src, dst string) error {
|
|||||||
return os.WriteFile(dst, data, 0o644)
|
return os.WriteFile(dst, data, 0o644)
|
||||||
}
|
}
|
||||||
|
|
||||||
func shouldOfferSudoRetry(err error) bool {
|
func shouldOfferPrivilegeRetry(err error) bool {
|
||||||
if os.Geteuid() == 0 {
|
if os.Geteuid() == 0 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -478,3 +486,65 @@ func shouldOfferSudoRetry(err error) bool {
|
|||||||
strings.Contains(text, "operation not permitted") ||
|
strings.Contains(text, "operation not permitted") ||
|
||||||
strings.Contains(text, "exit status")
|
strings.Contains(text, "exit status")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *App) writeConfigWithPrivilegeRetry(serialized, backup string) error {
|
||||||
|
a.println("")
|
||||||
|
a.println("Saving Samba settings usually needs administrator permission.")
|
||||||
|
a.println("I can try again using sudo so you can enter your admin password.")
|
||||||
|
a.flush()
|
||||||
|
|
||||||
|
canUseSudo := false
|
||||||
|
if a.lookPath != nil {
|
||||||
|
_, sudoErr := a.lookPath("sudo")
|
||||||
|
canUseSudo = sudoErr == nil
|
||||||
|
}
|
||||||
|
if !canUseSudo {
|
||||||
|
return friendlyWriteError("save Samba settings", a.configPath, os.ErrPermission)
|
||||||
|
}
|
||||||
|
|
||||||
|
retry, err := a.confirm("Retry saving with sudo", true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !retry {
|
||||||
|
return friendlyWriteError("save Samba settings", a.configPath, os.ErrPermission)
|
||||||
|
}
|
||||||
|
|
||||||
|
tempPath, err := a.writeTempConfig(serialized)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("prepare config for sudo save: %w", err)
|
||||||
|
}
|
||||||
|
defer os.Remove(tempPath)
|
||||||
|
|
||||||
|
if err := a.runner.Run("sudo", "cp", a.configPath, backup); err != nil {
|
||||||
|
return friendlyWriteError("create backup", backup, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := a.runner.Run("sudo", "install", "-m", "644", tempPath, a.configPath); err != nil {
|
||||||
|
return friendlyWriteError("write config", a.configPath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
a.printf("Config written to %s\n", a.configPath)
|
||||||
|
a.printf("Backup saved to %s\n", backup)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) writeTempConfig(serialized string) (string, error) {
|
||||||
|
file, err := os.CreateTemp("", "samba-configer-*.conf")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
if _, err := file.WriteString(serialized); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return file.Name(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func friendlyWriteError(action, path string, err error) error {
|
||||||
|
if errors.Is(err, os.ErrPermission) || strings.Contains(strings.ToLower(err.Error()), "permission denied") {
|
||||||
|
return fmt.Errorf("%s %s: administrator permission is needed", action, path)
|
||||||
|
}
|
||||||
|
return fmt.Errorf("%s %s: %w", action, path, err)
|
||||||
|
}
|
||||||
|
|||||||
BIN
samba-configer
BIN
samba-configer
Binary file not shown.
Reference in New Issue
Block a user