Przeglądaj źródła

Added working add smb share and remove share api

aroz 1 rok temu
rodzic
commit
98b45d929d

+ 90 - 0
mod/fileservers/servers/samba/handlers.go

@@ -0,0 +1,90 @@
+package samba
+
+import (
+	"encoding/json"
+	"net/http"
+
+	"imuslab.com/arozos/mod/utils"
+)
+
+// List all the samba shares
+func (s *ShareManager) ListSambaShares(w http.ResponseWriter, r *http.Request) {
+	shares, err := s.ReadSambaShares()
+	if err != nil {
+		utils.SendErrorResponse(w, err.Error())
+		return
+	}
+
+	js, _ := json.Marshal(shares)
+	utils.SendJSONResponse(w, string(js))
+}
+
+func (s *ShareManager) AddSambaShare(w http.ResponseWriter, r *http.Request) {
+	shareName, err := utils.GetPara(r, "name")
+	if err != nil {
+		utils.SendErrorResponse(w, "share name not given")
+		return
+	}
+
+	//TODO: Move hardcode to paramters
+	shareToCreate := ShareConfig{
+		Name:       shareName,
+		Path:       "/home/aroz/test/",
+		ValidUsers: []string{"aroz"},
+		ReadOnly:   false,
+		Browseable: true,
+		GuestOk:    false,
+	}
+
+	//Add the new share to smb.conf
+	err = s.CreateNewSambaShare(&shareToCreate)
+	if err != nil {
+		utils.SendErrorResponse(w, err.Error())
+		return
+	}
+
+	//Restart smbd
+	err = restartSmbd()
+	if err != nil {
+		utils.SendErrorResponse(w, err.Error())
+		return
+	}
+
+	utils.SendOK(w)
+}
+
+func (s *ShareManager) DelSambaShare(w http.ResponseWriter, r *http.Request) {
+	shareName, err := utils.GetPara(r, "name")
+	if err != nil {
+		utils.SendErrorResponse(w, "share name not given")
+		return
+	}
+
+	//Check if share exists
+	shareExists, err := s.ShareExists(shareName)
+	if err != nil {
+		utils.SendErrorResponse(w, err.Error())
+		return
+	}
+
+	if !shareExists {
+		utils.SendErrorResponse(w, "share to be remove not exists")
+		return
+	}
+
+	//Remove the share from config file
+	err = s.RemoveSambaShareConfig(shareName)
+	if err != nil {
+		utils.SendErrorResponse(w, err.Error())
+		return
+	}
+
+	//Restart smbd
+	err = restartSmbd()
+	if err != nil {
+		utils.SendErrorResponse(w, err.Error())
+		return
+	}
+
+	utils.SendOK(w)
+}

+ 41 - 0
mod/fileservers/servers/samba/helpers.go

@@ -0,0 +1,41 @@
+package samba
+
+import (
+	"fmt"
+	"os/exec"
+	"strings"
+)
+
+// convertShareConfigToString converts a ShareConfig to its string representation for smb.conf
+func convertShareConfigToString(share *ShareConfig) string {
+	var builder strings.Builder
+
+	builder.WriteString(fmt.Sprintf("\n[%s]\n", share.Name))
+	builder.WriteString(fmt.Sprintf("\tpath = %s\n", share.Path))
+	if len(share.ValidUsers) > 0 {
+		builder.WriteString(fmt.Sprintf("\tvalid users = %s\n", strings.Join(share.ValidUsers, " ")))
+	}
+	builder.WriteString(fmt.Sprintf("\tread only = %s\n", boolToYesNo(share.ReadOnly)))
+	builder.WriteString(fmt.Sprintf("\tbrowseable = %s\n", boolToYesNo(share.Browseable)))
+	builder.WriteString(fmt.Sprintf("\tguest ok = %s\n", boolToYesNo(share.GuestOk)))
+
+	return builder.String()
+}
+
+// boolToYesNo converts a boolean to "yes" or "no"
+func boolToYesNo(value bool) string {
+	if value {
+		return "yes"
+	}
+	return "no"
+}
+
+// RestartSmbd restarts the smbd service using systemctl
+func restartSmbd() error {
+	cmd := exec.Command("sudo", "systemctl", "restart", "smbd")
+	output, err := cmd.CombinedOutput()
+	if err != nil {
+		return fmt.Errorf("failed to restart smbd: %v - %s", err, output)
+	}
+	return nil
+}

+ 61 - 0
mod/fileservers/servers/samba/required.go

@@ -0,0 +1,61 @@
+package samba
+
+import (
+	"errors"
+	"fmt"
+	"log"
+	"os/exec"
+	"os/user"
+	"strings"
+
+	"imuslab.com/arozos/mod/fileservers"
+)
+
+/*
+Functions requested by the file server service router
+*/
+func (m *ShareManager) ServerToggle(enabled bool) error {
+	return errors.New("not supported")
+}
+
+func (m *ShareManager) IsEnabled() bool {
+	smbdRunning, err := checkSmbdRunning()
+	if err != nil {
+		log.Println("Unable to get smbd state: " + err.Error())
+		return false
+	}
+
+	return smbdRunning
+}
+
+func (m *ShareManager) GetEndpoints(userinfo *user.User) []*fileservers.Endpoint {
+	eps := []*fileservers.Endpoint{}
+	eps = append(eps, &fileservers.Endpoint{
+		ProtocolName: "//",
+		Port:         0,
+		Subpath:      "/" + userinfo.Username,
+	})
+	return eps
+}
+
+func checkSmbdRunning() (bool, error) {
+	// Run the system command to check the smbd service status
+	cmd := exec.Command("systemctl", "is-active", "--quiet", "smbd")
+	err := cmd.Run()
+
+	// If the command exits with status 0, smbd is running
+	if err == nil {
+		return true, nil
+	}
+
+	// If the command exits with a non-zero status, smbd is not running
+	// We can check the error message to be sure it's not another issue
+	if exitError, ok := err.(*exec.ExitError); ok {
+		if strings.TrimSpace(string(exitError.Stderr)) == "" {
+			return false, nil
+		}
+		return false, fmt.Errorf("error checking smbd status: %s", exitError.Stderr)
+	}
+
+	return false, fmt.Errorf("unexpected error checking smbd status: %v", err)
+}

+ 113 - 7
mod/fileservers/servers/samba/samba.go

@@ -3,8 +3,11 @@ package samba
 import (
 	"bufio"
 	"fmt"
+	"io"
 	"os"
+	"path/filepath"
 	"strings"
+	"time"
 )
 
 /*
@@ -17,7 +20,8 @@ import (
 */
 
 type ShareManager struct {
-	SambaConfigPath string
+	SambaConfigPath string //Config file for samba, aka smb.conf
+	BackupDir       string //Backup directory for restoring previous config
 }
 
 type ShareConfig struct {
@@ -32,6 +36,7 @@ type ShareConfig struct {
 func NewSambaShareManager() *ShareManager {
 	return &ShareManager{
 		SambaConfigPath: "/etc/samba/smb.conf",
+		BackupDir:       "./backup",
 	}
 }
 
@@ -98,16 +103,50 @@ func (s *ShareManager) ReadSambaShares() ([]ShareConfig, error) {
 	return shares, nil
 }
 
-// AppendSambaShareConfig appends the Samba share configuration to smb.conf
-func (s *ShareManager) AppendSambaShareConfig(config string) error {
-	file, err := os.OpenFile(s.SambaConfigPath, os.O_APPEND|os.O_WRONLY, 0644)
+// CreateNewSambaShare converts the shareConfig to string and appends it to smb.conf if the share name does not already exist
+func (s *ShareManager) CreateNewSambaShare(shareToCreate *ShareConfig) error {
+	// Path to smb.conf
+	smbConfPath := s.SambaConfigPath
+
+	// Open the smb.conf file for reading
+	file, err := os.Open(smbConfPath)
 	if err != nil {
-		return err
+		return fmt.Errorf("failed to open smb.conf: %v", err)
 	}
 	defer file.Close()
 
-	if _, err := file.WriteString(config); err != nil {
-		return err
+	// Check if the share already exists
+	scanner := bufio.NewScanner(file)
+	shareExists := false
+	shareNameSection := fmt.Sprintf("[%s]", shareToCreate.Name)
+	for scanner.Scan() {
+		if strings.TrimSpace(scanner.Text()) == shareNameSection {
+			shareExists = true
+			break
+		}
+	}
+
+	if err := scanner.Err(); err != nil {
+		return fmt.Errorf("failed to read smb.conf: %v", err)
+	}
+
+	if shareExists {
+		return fmt.Errorf("share %s already exists", shareToCreate.Name)
+	}
+
+	// Convert ShareConfig to string
+	shareConfigString := convertShareConfigToString(shareToCreate)
+
+	// Open the smb.conf file for appending
+	file, err = os.OpenFile(smbConfPath, os.O_APPEND|os.O_WRONLY, 0644)
+	if err != nil {
+		return fmt.Errorf("failed to open smb.conf for writing: %v", err)
+	}
+	defer file.Close()
+
+	// Append the new share configuration
+	if _, err := file.WriteString(shareConfigString); err != nil {
+		return fmt.Errorf("failed to write to smb.conf: %v", err)
 	}
 
 	return nil
@@ -174,3 +213,70 @@ func (s *ShareManager) RemoveSambaShareConfig(shareName string) error {
 
 	return nil
 }
+
+// ShareExists checks if a given share name exists in smb.conf
+func (s *ShareManager) ShareExists(shareName string) (bool, error) {
+	// Path to smb.conf
+	smbConfPath := s.SambaConfigPath
+
+	// Open the smb.conf file for reading
+	file, err := os.Open(smbConfPath)
+	if err != nil {
+		return false, fmt.Errorf("failed to open smb.conf: %v", err)
+	}
+	defer file.Close()
+
+	// Check if the share already exists
+	scanner := bufio.NewScanner(file)
+	shareNameSection := fmt.Sprintf("[%s]", shareName)
+	for scanner.Scan() {
+		if strings.TrimSpace(scanner.Text()) == shareNameSection {
+			return true, nil
+		}
+	}
+
+	if err := scanner.Err(); err != nil {
+		return false, fmt.Errorf("failed to read smb.conf: %v", err)
+	}
+
+	return false, nil
+}
+
+// Backup the current smb.conf to the backup folder
+func (s *ShareManager) BackupSmbConf() error {
+	// Define source and backup directory
+	sourceFile := s.SambaConfigPath
+	backupDir := s.BackupDir
+
+	// Ensure the backup directory exists
+	err := os.MkdirAll(backupDir, 0755)
+	if err != nil {
+		return fmt.Errorf("failed to create backup directory: %v", err)
+	}
+
+	// Create a timestamped backup filename
+	timestamp := time.Now().Format("20060102_150405")
+	backupFile := filepath.Join(backupDir, fmt.Sprintf("%s.smb.conf", timestamp))
+
+	// Open the source file
+	src, err := os.Open(sourceFile)
+	if err != nil {
+		return fmt.Errorf("failed to open source file: %v", err)
+	}
+	defer src.Close()
+
+	// Create the destination file
+	dst, err := os.Create(backupFile)
+	if err != nil {
+		return fmt.Errorf("failed to create backup file: %v", err)
+	}
+	defer dst.Close()
+
+	// Copy the contents of the source file to the backup file
+	_, err = io.Copy(dst, src)
+	if err != nil {
+		return fmt.Errorf("failed to copy file contents: %v", err)
+	}
+
+	return nil
+}

+ 15 - 5
network.go

@@ -10,6 +10,7 @@ import (
 	"imuslab.com/arozos/mod/fileservers/servers/dirserv"
 	"imuslab.com/arozos/mod/fileservers/servers/ftpserv"
 	"imuslab.com/arozos/mod/fileservers/servers/nfsserv"
+	"imuslab.com/arozos/mod/fileservers/servers/samba"
 	"imuslab.com/arozos/mod/fileservers/servers/sftpserv"
 	"imuslab.com/arozos/mod/fileservers/servers/webdavserv"
 	network "imuslab.com/arozos/mod/network"
@@ -31,11 +32,12 @@ var (
 	WebSocketRouter *websocket.Router
 
 	//File Server Managers
-	FTPManager     *ftpserv.Manager
-	WebDAVManager  *webdavserv.Manager
-	SFTPManager    *sftpserv.Manager
-	NFSManager     *nfsserv.Manager
-	DirListManager *dirserv.Manager
+	FTPManager        *ftpserv.Manager
+	WebDAVManager     *webdavserv.Manager
+	SFTPManager       *sftpserv.Manager
+	NFSManager        *nfsserv.Manager
+	SambaShareManager *samba.ShareManager
+	DirListManager    *dirserv.Manager
 )
 
 func NetworkServiceInit() {
@@ -339,6 +341,9 @@ func FileServerInit() {
 		}
 	*/
 
+	//Samba
+	SambaShareManager = samba.NewSambaShareManager()
+
 	//Register Endpoints
 	//WebDAV
 	http.HandleFunc("/system/network/webdav/list", WebDAVManager.HandleConnectionList)
@@ -360,6 +365,11 @@ func FileServerInit() {
 	adminRouter.HandleFunc("/system/storage/ftp/setPort", FTPManager.HandleFTPSetPort)
 	adminRouter.HandleFunc("/system/storage/ftp/passivemode", FTPManager.HandleFTPPassiveModeSettings)
 
+	//Samba Shares
+	adminRouter.HandleFunc("/system/storage/samba/list", SambaShareManager.ListSambaShares)
+	adminRouter.HandleFunc("/system/storage/samba/add", SambaShareManager.AddSambaShare)
+	adminRouter.HandleFunc("/system/storage/samba/remove", SambaShareManager.DelSambaShare)
+
 	networkFileServerDaemon = append(networkFileServerDaemon, &fileservers.Server{
 		ID:                "webdav",
 		Name:              "WebDAV",