Skip to content

Instantly share code, notes, and snippets.

@rucko24
Last active March 18, 2024 20:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rucko24/e7f35deb6f342d39e654bf986e3b7685 to your computer and use it in GitHub Desktop.
Save rucko24/e7f35deb6f342d39e654bf986e3b7685 to your computer and use it in GitHub Desktop.
A Java watch for a directory, allowing octal mode and user permissions to be changed recursively.
package com.watcher.watcherdir;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.PosixFilePermissions;
import java.nio.file.attribute.UserPrincipal;
import java.nio.file.attribute.UserPrincipalLookupService;
import java.util.Set;
import java.util.logging.Logger;
import java.util.stream.Stream;
import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;
import static java.nio.file.StandardWatchEventKinds.OVERFLOW;
/**
* Listening for changes in the <strong>/mnt/pool/movies</strong> dataset
* <p>
* And if there is a creation event we change the owner to <strong>jellyfin</strong> with the octal mode 775
*/
public class WatchDir {
private static final Logger LOGGER = Logger.getLogger(WatchDir.class.getName());
/**
* Octal 775
*/
private static final String OCTAL_775 = "rwxrwxr-x";
/**
* Octal 770
*/
private static final String OCTAL_770 = "rwxrwx---";
/**
* The dataset path to watch, is used with the <strong>-p</strong> parameter
*/
private final Path path;
/**
* The octal mode, is used with the <strong>-o</strong> parameter
*/
private final String octal;
/**
* The user name to apply in this directory, is used with the <strong>-u</strong> parameter
*/
private final String userName;
public WatchDir(final String userName, final String path, final String octal) {
this.userName = userName;
this.path = Path.of(path);
this.octal = octal;
this.watchDirectory();
}
@SuppressWarnings("unchecked")
private void watchDirectory() {
try {
WatchService watcher = FileSystems.getDefault().newWatchService();
this.registerAll(path, watcher);
WatchKey key;
LOGGER.info("Listening in..." + path);
for (; ; ) {
// wait for key to be signaled
try {
key = watcher.take();
} catch (InterruptedException x) {
return;
}
for (WatchEvent<?> event : key.pollEvents()) {
WatchEvent.Kind<?> kind = event.kind();
if (kind == OVERFLOW) {
continue;
}
WatchEvent<Path> ev = (WatchEvent<Path>) event;
Path filename = ev.context();
if (kind == ENTRY_CREATE) {
/*
* Si pasamos el filename solo se procesara el directorio nuevo.
* Si pasamos el path del parametro -p por el filename, procesaremos el directorio completo
*/
LOGGER.info("CREATE: " + filename);
changePermissionsAndOwnerRecursively(path);
}
if (kind == ENTRY_DELETE) {
LOGGER.info("DELETE: " + filename);
changePermissionsAndOwnerRecursively(path);
}
if (kind == ENTRY_MODIFY) {
LOGGER.info("MODIFY: " + filename);
changePermissionsAndOwnerRecursively(path);
}
}
boolean valid = key.reset();
if (!valid) {
break;
}
}
} catch (IOException x) {
LOGGER.severe(x.getMessage());
}
}
/**
* Register the given directory and all its sub-directories with the WatchService.
*/
private void registerAll(final Path start, final WatchService watchService) throws IOException {
// register directory and sub-directories
Files.walkFileTree(start, new SimpleFileVisitor<>() {
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
throws IOException {
dir.register(watchService, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
return FileVisitResult.CONTINUE;
}
});
}
/**
* Change permissions recursively
*
* @param pathParam
*/
private void changePermissionsAndOwnerRecursively(Path pathParam) {
try (Stream<Path> stream = Files.walk(pathParam, 3)) { // FOLLOW_LINKS
stream.forEach(path -> {
this.setPermissions(path);
this.changeOwner(path);
});
LOGGER.info("Owner changed correctly to " + this.userName);
LOGGER.info("Permissions changed correctly");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* Set octal permissions
*
* @param path the current dir or a filename
*
*/
private void setPermissions(final Path path) {
try {
final String octalMode = this.octal.equals("775") ? OCTAL_775 : OCTAL_770;
Set<PosixFilePermission> permissions = PosixFilePermissions.fromString(octalMode);
Files.setPosixFilePermissions(path, permissions);
} catch (IOException e) {
LOGGER.severe(e.getMessage());
}
}
/**
* The user to the directory and files is set up, you must have root permissions to run this.
* <p>
* The user must exist, otherwise, a <strong>UserPrincipalNotFoundException</strong>
*
* @param path
* @throws IOException
*/
private void changeOwner(final Path path) {
try {
UserPrincipalLookupService lookupService = path.getFileSystem().getUserPrincipalLookupService();
UserPrincipal userPrincipal = lookupService.lookupPrincipalByName(this.userName);
Files.setOwner(path, userPrincipal);
} catch (IOException ex) {
LOGGER.severe("The user "+ this.userName + " does not exist");
LOGGER.severe("leaving now...");
System.exit(0);
}
}
/**
*
* @param args
*/
public static void main(String... args) {
System.out.println("");
System.out.println("rubn");
System.out.println("https://rubn0x52.com");
//String[] args = {"-u","rubn","-p","/dir/test-dir/","-o","770"};
if (args.length == 0) {
LOGGER.info("No input parameters present");
System.exit(1);
} else if (("-h".equals(args[0])
|| "-help".equals(args[0])
|| "--help".equals(args[0])
|| "--h".equals(args[0]))) {
LOGGER.info("usage: java -jar watcher.jar -u [--user ex: an existing user] -p [--path ex: path,dir,dataset] -o [--octal mode ex: 775, 770]");
System.exit(1);
} else {
if (args.length > 3 && args.length <=5) {// valid path args[3]
LOGGER.info("Please view usage with --h, -h, -help, --help" );
System.exit(0);
} else if(args.length > 3 && args.length <=6) {
new WatchDir(args[1].trim(), args[3].trim(), args[5].trim());
} else {
LOGGER.info("Please view usage with --h, -h, -help, --help" );
System.exit(0);
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment