diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml new file mode 100644 index 0000000..7125306 --- /dev/null +++ b/.gitea/workflows/ci.yml @@ -0,0 +1,79 @@ +name: CI + +on: + push: + tags: + - '*' # Triggers on all tags + branches: + - '**' # Triggers on all branches + +defaults: + run: + shell: bash + working-directory: . + +jobs: + build: + runs-on: ubuntu + container: + image: node:20 + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Install tools + run: apt update && apt install -y build-essential + + - name: Compile project + run: make + + - name: Save build output + uses: actions/upload-artifact@v3 + with: + name: ncsambawatcher + path: ./ncsambawatcher + + release: + if: startsWith(github.ref, 'refs/tags/') + needs: build + runs-on: ubuntu + container: + image: node:20 + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Install build tools + run: apt update && apt install -y zip + + - name: Download compiled binary + uses: actions/download-artifact@v3 + with: + name: ncsambawatcher + + - name: Copy files + run: | + mkdir build + cp ncsambawatcher build/ncsambawatcher + cp configs/ncsambawatcher.config.default build/ncsambawatcher.config + cp configs/ncsambawatcher.service.default build/ncsambawatcher.service + cp init.sh build/init.sh + + - name: Create release zip + run: | + cd build + ls -al + zip -rv ../ncsambawatcher.zip ./* + cd ../ + ls -al + + - name: Publish release + uses: akkuman/gitea-release-action@v1 + env: + GITHUB_TOKEN: ${{ secrets.RELEASE_SECRET }} + with: + tag_name: ${{ github.ref_name }} + name: Release ${{ github.ref_name }} + files: ./ncsambawatcher.zip + token: ${{ secrets.RELEASE_SECRET }} + draft: true diff --git a/.gitignore b/.gitignore index 2ebe5e7..7a33641 100644 --- a/.gitignore +++ b/.gitignore @@ -89,4 +89,7 @@ dkms.conf settings.json watch.c ncwatchfile -ncsambawatcher \ No newline at end of file +ncsambawatcher + +obj/ +build/ \ No newline at end of file diff --git a/Makefile b/Makefile index fc542f6..2fa8abc 100644 --- a/Makefile +++ b/Makefile @@ -1,2 +1,31 @@ -all: - g++ -lrt -std=c++17 src/main.cpp src/usermanager.cpp -o ncsambawatcher \ No newline at end of file +# Compiler and flags +CXX := g++ +CXXFLAGS := -std=c++17 -Wall -Wextra -O2 + +# Directories +SRC_DIR := src +OBJ_DIR := obj +BUILD_DIR := . +TARGET := $(BUILD_DIR)/ncsambawatcher + +# Create list of source and object files +SRCS := $(wildcard $(SRC_DIR)/*.cpp) +OBJS := $(SRCS:$(SRC_DIR)/%.cpp=$(OBJ_DIR)/%.o) + +# Default target +all: $(TARGET) + +# Link object files into final binary +$(TARGET): $(OBJS) + $(CXX) $(CXXFLAGS) -o $@ $^ + +# Compile .cpp to .o into obj/ +$(OBJ_DIR)/%.o: $(SRC_DIR)/%.cpp + @mkdir -p $(OBJ_DIR) + $(CXX) $(CXXFLAGS) -c $< -o $@ + +# Clean build artifacts +clean: + rm -f $(OBJ_DIR)/*.o $(TARGET) + +.PHONY: all clean diff --git a/README.md b/README.md index 45c2174..66e2bb5 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,57 @@ -# nextcloud-samba-sync +# Nextcloud-Samba Sync -A Nextcloud-Samba File scanner system \ No newline at end of file +## Pre installation + +- Install `samba` and `vfs-modules` +``` +sudo apt update +sudo apt install samba samba-vfs-modules +``` +- Create shares. The example is in `configs` folder + +## Installation + +1. Download a `ncsambawatcher.zip` file from a release above 2.0 +2. Extract somewhere on your server (I recommend your user folder) +3. Make sure the samba configuration file is correct (See `configs/smb.24.04.conf`) +4. Configurate the `ncsambawatcher.config` file + + + + + + + + + + + + + + + + + + + + + + + + + +
NameRequiredDescription
NEXTCLOUD_CONTAINER_NAMEYesThe nextcloud's docker container name
NEXTCLOUD_USERSNoUsernames separated with spaces
NEXTCLOUD_GROUPFOLDER_IDSNoGroupfolder ids separated with spaces (only the number)
+ +Example 1: +``` +NEXTCLOUD_CONTAINER_NAME=nextcloud +``` +Example 2: (See `configs/ncsambawatcher.config.default`) + +5. Run the `init.sh` script +6. You're done :) + +## Notes + +- You don't need to add users and groupfolders to the configfile because the program add automaticly from the logfile +- For a user (not groupfolder): The username and the samba share name **MUST BE THE SAME** diff --git a/configs/ncsambawatcher.config.default b/configs/ncsambawatcher.config.default new file mode 100644 index 0000000..d3ef548 --- /dev/null +++ b/configs/ncsambawatcher.config.default @@ -0,0 +1,3 @@ +NEXTCLOUD_CONTAINER_NAME=nextcloud +NEXTCLOUD_USERS=username1 username2 username3 +NEXTCLOUD_GROUPFOLDER_IDS=1 2 3 4 \ No newline at end of file diff --git a/configs/ncsambawatcher.service b/configs/ncsambawatcher.service.default similarity index 66% rename from configs/ncsambawatcher.service rename to configs/ncsambawatcher.service.default index f4f35f3..e6e3d42 100644 --- a/configs/ncsambawatcher.service +++ b/configs/ncsambawatcher.service.default @@ -4,12 +4,11 @@ After=network.target docker.service Requires=docker.service [Service] -ExecStart=/usr/bin/ncsambawatcher +ExecStart=/path/to/folder/ncsambawatcher +WorkingDirectory=/path/to/folder/ Restart=always User=root Group=root -WorkingDirectory=/usr/bin/ -Environment=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin StandardOutput=journal StandardError=journal SyslogIdentifier=ncsambawatcher diff --git a/configs/smb.24.04.conf b/configs/smb.24.04.conf index 78643cd..13bce10 100644 --- a/configs/smb.24.04.conf +++ b/configs/smb.24.04.conf @@ -1,5 +1,4 @@ [global] - vfs objects = full_audit full_audit:prefix = %u|%I|%m|%S full_audit:success = mkdirat unlinkat renameat write @@ -7,12 +6,43 @@ full_audit:facility = local5 full_audit:priority = NOTICE -# Put this line only for the groupfolder's share -[Some gorupfolder share] +# Example usershare +[] #CHANGEME + path = /path/to/nextcloud/data//files/ #CHANGEME + valid users = #CHANGEME + force user = www-data + force group = www-data + create mask = 0755 + force create mode = 0755 + directory mask = 0755 + force directory mode = 0755 + guest ok = no + public = no + writable = yes + browsable = yes + hide dot files = no + inherit owner = yes + hide unreadable = no - full_audit:prefix = %u|%I|%m|__groupfolders/ +# Example groupfolder share +[Sharename] + path = /path/to/nextcloud/data/__groupfolders/ #CHANGEME + valid users = usernames #CHANGEME + force user = www-data + force group = www-data + create mask = 0755 + force create mode = 0755 + directory mask = 0755 + force directory mode = 0755 + guest ok = no + public = no + writable = yes + browsable = yes + hide dot files = no + inherit owner = yes + hide unreadable = no + full_audit:prefix = %u|%I|%m|__groupfolders/ #CHANGEME -# To disable logs for a specific share -[A share] - - vfs objects = \ No newline at end of file +# To disable logs for a specific share, add this line to that share +[Sharename] + vfs objects = \ No newline at end of file diff --git a/init.sh b/init.sh index 7f4e072..5e98a4c 100755 --- a/init.sh +++ b/init.sh @@ -1,11 +1,9 @@ #!/bin/bash -make +current_dir=$(pwd) +sed -i "s|/path/to/folder/|$current_dir/|g" ncsambawatcher.service -sudo cp ncsambawatcher /usr/bin/ -sudo chmod +x /usr/bin/ncsambawatcher - -sudo cp configs/ncsambawatcher.service /etc/systemd/system +sudo cp ./ncsambawatcher.service /etc/systemd/system sudo systemctl daemon-reload sudo systemctl enable ncsambawatcher.service diff --git a/src/configfilemanager.cpp b/src/configfilemanager.cpp new file mode 100644 index 0000000..f8a871f --- /dev/null +++ b/src/configfilemanager.cpp @@ -0,0 +1 @@ +#include "configfilemanager.h" \ No newline at end of file diff --git a/src/configfilemanager.h b/src/configfilemanager.h new file mode 100644 index 0000000..9fbe44f --- /dev/null +++ b/src/configfilemanager.h @@ -0,0 +1,68 @@ +#ifndef _CONFIGFILEMANAGER_H +#define _CONFIGFILEMANAGER_H + +#include +#include +#include +#include +#include +#include +#include "definitions.h" + +class configfilemanager{ +private: + std::map configs; + std::mutex mtx; + + +public: + configfilemanager(std::string filepath = "./ncsambawatcher.config") + { + std::ifstream is(filepath); + if(!is.good()) + { + std::cerr << "File not exits: " << filepath << std::endl; + exit(EXIT_FAILURE); + } + std::string tmp; + + while(!is.eof()) + { + std::getline(is, tmp); + std::cout << tmp << std::endl; + if (tmp.at(0) == '#') // ignore comments + continue; + + std::vector splited = splitString(tmp, '='); + if (splited.size() != 2) + { + std::cerr << "Invalid line: " << tmp << std::endl; + continue; + } + + configs.insert(std::make_pair(splited.at(0), splited.at(1))); + } + + if (configs.count("NEXTCLOUD_CONTAINER_NAME") == 0) + { + std::cerr << "The container's name not added" << std::endl; + exit(EXIT_FAILURE); + } + + std::cout << "Config file loaded successfuly" << std::endl; + } + + std::string at(const std::string &config) + { + std::lock_guard lock(mtx); + return configs.at(config); + } + + std::string at(const char* config) + { + return at(std::string(config)); + } + +}; + +#endif // _CONFIGFILEMANAGER_H \ No newline at end of file diff --git a/src/definitions.h b/src/definitions.h index 4e755b9..c9a6e9d 100644 --- a/src/definitions.h +++ b/src/definitions.h @@ -5,7 +5,9 @@ #define USER_LOG_LOCATION 3 -#define SCAN_CMD_USR "docker exec --user www-data nextcloud /var/www/html/occ files:scan --path=" -#define SCAN_CMD_GRP "docker exec --user www-data nextcloud /var/www/html/occ groupfolder:scan " +#define SCAN_CMD_USR "docker exec --user www-data %1% /var/www/html/occ files:scan --path=" +#define SCAN_CMD_GRP "docker exec --user www-data %1% /var/www/html/occ groupfolder:scan " + +std::vector splitString(const std::string& input, char delimiter); #endif // _LOCATIONS_H \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 8974326..6547cf5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,17 +5,34 @@ #include #include #include +#include #include #include #include #include #include +#include "definitions.h" #include "usermanager.h" +#include "configfilemanager.h" +configfilemanager cfm; userManager manager; std::condition_variable cv; std::mutex mtx; +std::vector splitString(const std::string& str, char delimiter = '|') +{ + std::vector ret; + std::stringstream ss(str); + std::string token; + + while (std::getline(ss, token, delimiter)) { + ret.push_back(token); + } + + return ret; +} + void readingThreadFunc() { FILE *logpipe = popen(LOGFILE, "r"); @@ -52,7 +69,7 @@ void scannerThreadFunc() cv.wait(lock, [] { return manager.isAnybodyFlagged(); }); - std::set scanUsers = manager.getFlaggedUsers(); + std::unordered_set scanUsers = manager.getFlaggedUsers(); manager.unflagAllUsers(); lock.unlock(); @@ -68,7 +85,8 @@ void scannerThreadFunc() } else if (child == 0) // child { - std::string cmd = userManager::getScanCommandFromUser(user); + std::string cmd = userManager::getScanCommandFromUser(user, cfm); + std::cout << "Run command: " << cmd << std::endl; execl("/bin/sh", "sh", "-c", cmd.c_str(), static_cast(nullptr)); std::cerr << "Scan failed" << std::endl; _exit(EXIT_FAILURE); @@ -90,6 +108,9 @@ void scannerThreadFunc() int main() { + manager.tryAddUsersFromConfig(cfm); + manager.tryAddGroupIDsFromConfig(cfm); + std::thread readingThread(readingThreadFunc); std::thread scannerThread(scannerThreadFunc); diff --git a/src/usermanager.cpp b/src/usermanager.cpp index 8d1bf13..163d5b2 100644 --- a/src/usermanager.cpp +++ b/src/usermanager.cpp @@ -1,24 +1,28 @@ #include "usermanager.h" -std::vector splitString(const std::string& str, char delimiter = '|') -{ - std::vector ret; - std::stringstream ss(str); - std::string token; - - while (std::getline(ss, token, delimiter)) { - ret.push_back(token); - } - - return ret; -} - -std::string userManager::getScanCommandFromUser(const std::string &user) +std::string userManager::getScanCommandFromUser(const std::string &user, configfilemanager &cfm) { + std::string contname = cfm.at("NEXTCLOUD_CONTAINER_NAME"); + std::string baseCommand; + std::string userCommand; + std::string placeholder("%1%"); + if (user.find("__groupfolder") != std::string::npos) { - return std::string(SCAN_CMD_GRP) + splitString(user, '/').back(); + baseCommand = SCAN_CMD_GRP; + userCommand = splitString(user, '/').back(); + } + else + { + baseCommand = SCAN_CMD_USR; + userCommand = user; } - return std::string(SCAN_CMD_USR) + user; + size_t pos = 0; + while ((pos = baseCommand.find(placeholder, pos)) != std::string::npos) { + baseCommand.replace(pos, placeholder.length(), contname); + pos += contname.length(); // Move past the replacement + } + + return baseCommand + userCommand; } \ No newline at end of file diff --git a/src/usermanager.h b/src/usermanager.h index ff4c4b4..7fda463 100644 --- a/src/usermanager.h +++ b/src/usermanager.h @@ -4,12 +4,12 @@ #include #include #include -#include +#include #include #include +#include #include "definitions.h" - -std::vector splitString(const std::string& input, char delimiter); +#include "configfilemanager.h" class userManager { @@ -19,35 +19,37 @@ private: public: - static std::string getScanCommandFromUser(const std::string&); + static std::string getScanCommandFromUser(const std::string&, configfilemanager& cfm); void addUserFromLogLine(std::string &line) { addUser(splitString(line, '|').at(USER_LOG_LOCATION)); } - void addUser(std::string &user) + void addUser(const std::string &user) { std::lock_guard lock(mtx); if (users.count(user) == 0) { users[user] = false; + std::cout << "User added the list: " << user << std::endl; } } - void removeUser(std::string &user) + void removeUser(const std::string &user) { std::lock_guard lock(mtx); users.erase(user); + std::cout << "User removed the list: " << user << std::endl; } - bool isContains(std::string &user) + bool isContains(const std::string &user) { std::lock_guard lock(mtx); return users.count(user) == 1; } - void setUserFlagged(std::string &user) + void setUserFlagged(const std::string &user) { std::lock_guard lock(mtx); if (users.count(user) == 1) @@ -56,7 +58,7 @@ public: } } - void setUserUnflagged(std::string &user) + void setUserUnflagged(const std::string &user) { std::lock_guard lock(mtx); if (users.count(user) == 1) @@ -74,9 +76,9 @@ public: } } - std::set getUsers() + std::unordered_set getUsers() { - std::set ret; + std::unordered_set ret; std::lock_guard lock(mtx); @@ -88,9 +90,9 @@ public: return ret; } - std::set getFlaggedUsers() + std::unordered_set getFlaggedUsers() { - std::set ret; + std::unordered_set ret; std::lock_guard lock(mtx); @@ -117,6 +119,40 @@ public: return false; } + + void tryAddUsersFromConfig(configfilemanager &cfm) + { + try + { + std::vector alluser = splitString(cfm.at("NEXTCLOUD_USERS"), ' '); + + for (const std::string& user : alluser) + { + addUser(user); + } + } + catch (const std::exception &e) + { + std::cerr << "No user added from configuration file" << std::endl; + } + } + + void tryAddGroupIDsFromConfig(configfilemanager &cfm) + { + try + { + std::vector allids = splitString(cfm.at("NEXTCLOUD_GROUPFOLDER_IDS"), ' '); + + for (const std::string& id : allids) + { + addUser("__groupfolders/" + id); + } + } + catch (const std::exception &e) + { + std::cerr << "No groupfolder added from configuration file" << std::endl; + } + } }; #endif // _USERMAN_H \ No newline at end of file