As the title says, I'm wondering if it is possible for a program written in Java (and only java) to relaunch himself (preferably a .jar) with administrator privileges, showing in the way the native Windows UAC (in order to make it more trustable for the user), i did my homework and found out that it is possible to accomplish this using bridges between c++ and java, but i would really like to do this as a pure java project.
P.S: In the remote case that this result to be impossible, can someone show me the "easy" way to do this using another language (i mean, I've found tutorials, but they are to complicated for something I think it should not be that complicated).
P.S2: In case it is possible to accomplish this, would it work, on other platforms (OS X, Linux)
It cannot be done in pure java.
Best bet would be to write this to a file:
#echo Set objShell = CreateObject("Shell.Application") > %temp%\sudo.tmp.vbs
#echo args = Right("%*", (Len("%*") - Len("%1"))) >> %temp%\sudo.tmp.vbs
#echo objShell.ShellExecute "%1", args, "", "runas" >> %temp%\sudo.tmp.vbs
#cscript %temp%\sudo.tmp.vbs
and save it as something.bat in Windows temp directory (as we have access to this).
You would then execute this from your application using Runtime or ProcessBuilder and exit your application (System.exit(0);).
You should add an immediate start up check to your application that checks if the program has elevation, if it has proceed if not re-run the batch and exit.
Here is an example I made (this must be run when compiled as a Jar or it wont work):
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import javax.swing.JOptionPane;
/**
*
* #author David
*/
public class UacTest {
public static String jarName = "UacTest.jar", batName = "elevate.bat";
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
if (checkForUac()) {//uac is on
JOptionPane.showMessageDialog(null, "I am not elevated");
//attempt elevation
new UacTest().elevate();
System.exit(0);
} else {//uac is not on
//if we get here we are elevated
JOptionPane.showMessageDialog(null, "I am elevated");
}
}
private static boolean checkForUac() {
File dummyFile = new File("c:/aaa.txt");
dummyFile.deleteOnExit();
try {
//attempt to craete file in c:/
try (FileWriter fw = new FileWriter(dummyFile, true)) {
}
} catch (IOException ex) {//we cannot UAC muts be on
//ex.printStackTrace();
return true;
}
return false;
}
private void elevate() {
//create batch file in temporary directory as we have access to it regardless of UAC on or off
File file = new File(System.getProperty("java.io.tmpdir") + "/" + batName);
file.deleteOnExit();
createBatchFile(file);
runBatchFile();
}
private String getJarLocation() {
return getClass().getProtectionDomain().getCodeSource().getLocation().getPath().substring(1);
}
private void runBatchFile() {
//JOptionPane.showMessageDialog(null, getJarLocation());
Runtime runtime = Runtime.getRuntime();
String[] cmd = new String[]{"cmd.exe", "/C",
System.getProperty("java.io.tmpdir") + "/" + batName + " java -jar " + getJarLocation()};
try {
Process proc = runtime.exec(cmd);
//proc.waitFor();
} catch (Exception ex) {
ex.printStackTrace();
}
}
private void createBatchFile(File file) {
try {
try (FileWriter fw = new FileWriter(file, true)) {
fw.write(
"#echo Set objShell = CreateObject(\"Shell.Application\") > %temp%\\sudo.tmp.vbs\r\n"
+ "#echo args = Right(\"%*\", (Len(\"%*\") - Len(\"%1\"))) >> %temp%\\sudo.tmp.vbs\r\n"
+ "#echo objShell.ShellExecute \"%1\", args, \"\", \"runas\" >> %temp%\\sudo.tmp.vbs\r\n"
+ "#cscript %temp%\\sudo.tmp.vbs\r\n"
+ "del /f %temp%\\sudo.tmp.vbs\r\n");
}
} catch (IOException ex) {
//ex.printStackTrace();
}
}
}
Use a batch file and the runas command.
I doubt "only Java". At best you would have to have a JNI wrapper around the MSFT module. Unless just invoking the exe using ProcessBuilder counts as "only Java" -- your code to bring up the user console would be only Java but not what it invokes. IOW, Win does not come with a Java API
To relaunch your application elevated, you have to call ShellExecute or ShellExecuteEx function from Windows API and use runas verb.
You can use these API in pure Java with JNA library.
To relaunch yourself, you would have to know the full path to java.exe or javaw.exe, the command-line parameters (class path, if any, and the path to your jar). Obviously you can get this information by using Windows API.
What do you mean by remote case?
You cannot start remote elevated process this way.
You can re-launch your application elevated from a network share. Yet it won't work with mapped drives: after elevation there's no access to user's mapped drives.
No, this can't work on other platforms. UAC is a Windows feature. It's similar to sudo in Linux in some ways, so for Linux you can use sudo $pathtojava/java.exe <yourparameters>. However this won't work nicely if your application is not started from a console. Window Managers usually have wrappers which prompt for password in a GUI dialog.
Just do this with Hackaprofaw (v29). Also it was released in 2002 and started development in 1997 soooooo ye. in 2021 its on version 29.10.7 but-
if raw ram = 0
disable "featureII" program = "JAVA(math = any)"
run on "Hackaprofaw (math = v29(x))
when "featureII" disabled
end
Related
Hello working on a small program that just needs to run a python script I have. This python script will play a given .wav file, and draw a shape on the turtle screen. As such, I'm not looking for an output to be returned to java. Here is my java code:
public class Driver {
public static void main(String[] args){
try {
Process p = Runtime.getRuntime().exec("python " +
" D:/Coding Files/Python/MusicColors.py" +" teenagers.wav");
}
catch (Exception e){
System.out.println(e);
}
}
}
The exception I get is:
java.io.IOException: Cannot run program "python":
CreateProcess error=2, The system cannot find the file specified
I probably am making a very stupid mistake as I have limited knowledge in the subject of processes and such. I added python to my system path, so whenever I put "python" into command line, it returns with
Python 3.5.2 (v3.5.2:4def2a2901a5, Jun 25 2016, 22:01:18) [MSC v.1900 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
And makes it the python shell.
Here is the exact line I added to my environment path:
C:\Users\Joe\AppData\Local\Programs\Python\Python35-32
If anyone can figure out where I went wrong I'd really appreciate it!
The $PATH variable you've set is not inherited in Java's execution context. Try passing the Python's bin path to exec()'s execution environment.
To do this, the code below first retrieve all the environment variables and create an array of ENV_KEY=ENV_VALUE pairs.
Then, the path to your Python's bin is appended to the PATH value.
Finally, we pass the array of all environment variables to exec() (via the second parameter).
import java.util.HashMap;
import java.util.Map;
public class Driver {
public static void main(String[] args){
try {
String[] commands = {"python D:/Coding Files/Python/MusicColors.py teenagers.wav"};
// Get a list of all environment variables
final Map<String, String> envMap = new HashMap<String, String>(System.getenv());
// Append Python bin path to Path
envMap.put("Path", envMap.get("Path") + ";C:/Users/Joe/AppData/Local/Programs/Python/Python35-32");
// Convert to an array of ENV_KEY=ENV_VALUE format strings
final String[] envs = new String[envMap.size()];
int i = 0;
for (Map.Entry<String, String> e : envMap.entrySet()) {
envs[i] = e.getKey() + '=' + e.getValue();
i++;
}
// Exec with the environment variables
Process p = Runtime.getRuntime().exec(commands, envs);
}
catch (Exception e){
System.out.println(e);
}
}
}
In Java, I'm dynamically creating a set of files and I'd like to change the file permissions on these files on a linux/unix file system. I'd like to be able to execute the Java equivalent of chmod. Is that possible Java 5? If so, how?
I know in Java 6 the File object has setReadable()/setWritable() methods. I also know I could make a system call to do this, but I'd like to avoid that if possible.
Full control over file attributes is available in Java 7, as part of the "new" New IO facility (NIO.2). For example, POSIX permissions can be set on an existing file with setPosixFilePermissions(), or atomically at file creation with methods like createFile() or newByteChannel().
You can create a set of permissions using EnumSet.of(), but the helper method PosixFilePermissions.fromString() will uses a conventional format that will be more readable to many developers. For APIs that accept a FileAttribute, you can wrap the set of permissions with with PosixFilePermissions.asFileAttribute().
Set<PosixFilePermission> ownerWritable = PosixFilePermissions.fromString("rw-r--r--");
FileAttribute<?> permissions = PosixFilePermissions.asFileAttribute(ownerWritable);
Files.createFile(path, permissions);
In earlier versions of Java, using native code of your own, or exec-ing command-line utilities are common approaches.
Prior to Java 6, there is no support of file permission update at Java level. You have to implement your own native method or call Runtime.exec() to execute OS level command such as chmod.
Starting from Java 6, you can useFile.setReadable()/File.setWritable()/File.setExecutable() to set file permissions. But it doesn't simulate the POSIX file system which allows to set permission for different users. File.setXXX() only allows to set permission for owner and everyone else.
Starting from Java 7, POSIX file permission is introduced. You can set file permissions like what you have done on *nix systems. The syntax is :
File file = new File("file4.txt");
file.createNewFile();
Set<PosixFilePermission> perms = new HashSet<>();
perms.add(PosixFilePermission.OWNER_READ);
perms.add(PosixFilePermission.OWNER_WRITE);
Files.setPosixFilePermissions(file.toPath(), perms);
This method can only be used on POSIX file system, this means you cannot call it on Windows system.
For details on file permission management, recommend you to read this post.
In addition to erickson's suggestions, there's also jna, which allows you to call native libraries without using jni. It's shockingly easy to use, and I've used it on a couple of projects with great success.
The only caveat is that it's slower than jni, so if you're doing this to a very large number of files that might be an issue for you.
(Editing to add example)
Here's a complete jna chmod example:
import com.sun.jna.Library;
import com.sun.jna.Native;
public class Main {
private static CLibrary libc = (CLibrary) Native.loadLibrary("c", CLibrary.class);
public static void main(String[] args) {
libc.chmod("/path/to/file", 0755);
}
}
interface CLibrary extends Library {
public int chmod(String path, int mode);
}
For Windows 7 with NIO 2:
public static void main(String[] args) throws IOException {
Path file = Paths.get("c:/touch.txt");
AclFileAttributeView aclAttr = Files.getFileAttributeView(file, AclFileAttributeView.class);
System.out.println(aclAttr.getOwner());
for (AclEntry aclEntry : aclAttr.getAcl()) {
System.out.println(aclEntry);
}
System.out.println();
UserPrincipalLookupService upls = file.getFileSystem().getUserPrincipalLookupService();
UserPrincipal user = upls.lookupPrincipalByName(System.getProperty("user.name"));
AclEntry.Builder builder = AclEntry.newBuilder();
builder.setPermissions( EnumSet.of(AclEntryPermission.READ_DATA, AclEntryPermission.EXECUTE,
AclEntryPermission.READ_ACL, AclEntryPermission.READ_ATTRIBUTES, AclEntryPermission.READ_NAMED_ATTRS,
AclEntryPermission.WRITE_ACL, AclEntryPermission.DELETE
));
builder.setPrincipal(user);
builder.setType(AclEntryType.ALLOW);
aclAttr.setAcl(Collections.singletonList(builder.build()));
}
Just to update this answer unless anyone comes across this later, since JDK 6 you can use
File file = new File('/directory/to/file');
file.setWritable(boolean);
file.setReadable(boolean);
file.setExecutable(boolean);
you can find the documentation on Oracle File(Java Platform SE 7). Bear in mind that these commands only work if the current working user has ownership or write access to that file. I am aware that OP wanted chmod type access for more intricate user configuration. these will set the option across the board for all users.
If you want to set 777 permission to your created file than you can use the following method:
public void setPermission(File file) throws IOException{
Set<PosixFilePermission> perms = new HashSet<>();
perms.add(PosixFilePermission.OWNER_READ);
perms.add(PosixFilePermission.OWNER_WRITE);
perms.add(PosixFilePermission.OWNER_EXECUTE);
perms.add(PosixFilePermission.OTHERS_READ);
perms.add(PosixFilePermission.OTHERS_WRITE);
perms.add(PosixFilePermission.OTHERS_EXECUTE);
perms.add(PosixFilePermission.GROUP_READ);
perms.add(PosixFilePermission.GROUP_WRITE);
perms.add(PosixFilePermission.GROUP_EXECUTE);
Files.setPosixFilePermissions(file.toPath(), perms);
}
You can use the methods of the File class:
http://docs.oracle.com/javase/7/docs/api/java/io/File.html
Apache ant chmod (not very elegant, adding it for completeness) credit shared with #msorsky
Chmod chmod = new Chmod();
chmod.setProject(new Project());
FileSet mySet = new FileSet();
mySet.setDir(new File("/my/path"));
mySet.setIncludes("**");
chmod.addFileset(mySet);
chmod.setPerm("+w");
chmod.setType(new FileDirBoth());
chmod.execute();
for Oralce Java 6:
private static int chmod(String filename, int mode) {
try {
Class<?> fspClass = Class.forName("java.util.prefs.FileSystemPreferences");
Method chmodMethod = fspClass.getDeclaredMethod("chmod", String.class, Integer.TYPE);
chmodMethod.setAccessible(true);
return (Integer)chmodMethod.invoke(null, filename, mode);
} catch (Throwable ex) {
return -1;
}
}
works under solaris/linux.
There is an example class on Oracle Docs which works very much similar to the UNIX chmod. It works with java se 7+ though.
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.FileAttribute;
import java.nio.file.attribute.PosixFileAttributes;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.PosixFilePermissions;
import java.util.Set;
public class FileAndDirectory1 {
public static void main(String[] args) {
File file = new File("fileTest1.txt");
System.out.println(file.getAbsoluteFile());
try {
//file.createNewFile();
if(!file.exists())
{
//PosixFilePermission is an enum class, PosixFilePermissions is a final class
//create file permissions from string
Set<PosixFilePermission> filePermissions = PosixFilePermissions.fromString("---------"/* "rwxrwxrwx" */);
FileAttribute<?> permissions = PosixFilePermissions.asFileAttribute(filePermissions);
Files.createFile(file.toPath(), permissions);
// printing the permissions associated with the file
System.out.println("Executable: " + file.canExecute());
System.out.println("Readable: " + file.canRead());
System.out.println("Writable: "+ file.canWrite());
file.setExecutable(true);
file.setReadable(true);
file.setWritable(true);
}
else
{
//modify permissions
//get the permission using file attributes
Set<PosixFilePermission> perms = Files.readAttributes(file.toPath(), PosixFileAttributes.class).permissions();
perms.remove(PosixFilePermission.OWNER_WRITE);
perms.add(PosixFilePermission.OWNER_READ);
perms.add(PosixFilePermission.OWNER_EXECUTE);
perms.add(PosixFilePermission.GROUP_WRITE);
perms.add(PosixFilePermission.GROUP_READ);
perms.add(PosixFilePermission.GROUP_EXECUTE);
perms.add(PosixFilePermission.OTHERS_WRITE);
perms.add(PosixFilePermission.OTHERS_READ);
perms.add(PosixFilePermission.OTHERS_EXECUTE);
Files.setPosixFilePermissions(file.toPath(), perms);
System.out.println("Executable: " + file.canExecute());
System.out.println("Readable: " + file.canRead());
System.out.println("Writable: "+ file.canWrite());
file.delete();
}
} catch (IOException e) {
e.printStackTrace();
}
Path path = Paths.get(String.valueOf(file));
System.out.println(path);
}
}
Permission 777 is the same as rwxrwxrwx which you can set as follows:
Files.setPosixFilePermissions(path, PosixFilePermissions.fromString("rwxrwxrwx"))
A nasty problem popped out with my software. I am making a program that interacts with another existing software (a game). User has reported that he runs the game with administrator privileges and under that circumstances, my program stops working for him.
Short investigation revealed that some people really need to run the game under administrator account and some don't. It would be great if my program would be able to detect this and warn user if the game is running under administrator account:
If the user clicks "Elevate", I'd like to ask windows to elevate the java.exe running my jar file and invoke the typical UAC dialog.
Obviously, this time the question would not be about java updater but JRE
My question is: Is this possible? Can windows elevate my java.exe instance's privilege? Does java have a way to do it? Or can I use command line command?
I want to avoid restarting the program (though it wouldn't probably be such a big deal).
Edit:
If you look in the comments, you'll see that there's no avoiding the restart of an application - process can only start elevated, not become elevated. This kinda shifts the question, unfortunately. Basically, it now sounds more like: "How to restart my application with admin rights?". Unless, of course, there's a trick like two java.exe sharing one jar...
If still of interest: In Windows 7 my JavaElevator works. It elevates a running Java process when used in the main method of the Java application. Simply add -elevate as last program parameter and use the elevator in the main method.
The elevator class:
package test;
import com.sun.jna.Native;
import com.sun.jna.platform.win32.Kernel32;
import com.sun.jna.platform.win32.Kernel32Util;
import com.sun.jna.platform.win32.ShellAPI;
import com.sun.jna.platform.win32.WinDef;
/**
* Elevates a Java process to administrator rights if requested.
*/
public class JavaElevator {
/** The program argument indicating the need of being elevated */
private static final String ELEVATE_ARG = "-elevate";
/**
* If requested, elevates the Java process started with the given arguments to administrator level.
*
* #param args The Java program arguments
* #return The cleaned program arguments
*/
public static String[] elevate(String[] args) {
String[] result = args;
// Check for elevation marker.
boolean elevate = false;
if (args.length > 0) {
elevate = args[args.length - 1].equals(ELEVATE_ARG);
}
if (elevate) {
// Get the command and remove the elevation marker.
String command = System.getProperty("sun.java.command");
command = command.replace(ELEVATE_ARG, "");
// Get class path and default java home.
String classPath = System.getProperty("java.class.path");
String javaHome = System.getProperty("java.home");
String vm = javaHome + "\\bin\\java.exe";
// Check for alternate VM for elevation. Full path to the VM may be passed with: -Delevation.vm=...
if (System.getProperties().contains("elevation.vm")) {
vm = System.getProperty("elevation.vm");
}
String parameters = "-cp " + classPath;
parameters += " " + command;
Shell32.INSTANCE.ShellExecute(null, "runas", vm, parameters, null, 0);
int lastError = Kernel32.INSTANCE.GetLastError();
if (lastError != 0) {
String errorMessage = Kernel32Util.formatMessageFromLastErrorCode(lastError);
errorMessage += "\n vm: " + vm;
errorMessage += "\n parameters: " + parameters;
throw new IllegalStateException("Error performing elevation: " + lastError + ": " + errorMessage);
}
System.exit(0);
}
return result;
}
}
Usage in the main method of the Java application:
public static void main(String[] args) {
String[] args1 = JavaElevator.elevate(args);
if (args1.length > 0) {
// Continue as intended.
...
I know, this is a very basic implementation - sufficient for one of my daily hiccups: Starting an elevated process from Eclipse. But maybe it points someone in some dicrection...
As has been pointed in comments, sadly the Java (or any other process) cannot be elevated while running. While in the case of JWM, it could be theoretically possible to move whole program context from normal user java.exe to elevated one, I don't think it's possible. I hope some day someone will come and tell me I'm wrong.
Surprisingly, even with restart in place, this was a tricky task that took me a while to figure out.
The non java part
First, how do we exactly run a program elevated from command line? There's an answer and you can see it's not simple. But we can break it to this VBS script:
Set UAC = CreateObject("Shell.Application")
UAC.ShellExecute "program name", "command line parameters", "working directory", "runas", 1
Soon, it also turns out that we won't have any success running java.exe from VBS script. In the end, I decided to run a helper batch file. Finally, here (answer to question in the last link) we have a complete set of two scripts which really run the given .jar file elevated. Here's improved version that allows quick testing by drag'n'dropping the Jar file on it:
' Require first command line parameter
if WScript.Arguments.Count = 0 then
MsgBox("Jar file name required.")
WScript.Quit 1
end if
' Get the script location, the directorry where it's running
Set objShell = CreateObject("Wscript.Shell")
strPath = Wscript.ScriptFullName
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.GetFile(strPath)
strFolder = objFSO.GetParentFolderName(objFile)
'MsgBox(strFolder)
' Create the object that serves as runnable something
Set UAC = CreateObject("Shell.Application")
' Args:
' path to executable to run
' command line parameters - first parameter of this file, which is the jar file name
' working directory (this doesn't work but I use it nevertheless)
' runas command which invokes elevation
' 0 means do not show the window. Normally, you show the window, but not this console window
' which just blinks and disappears anyway
UAC.ShellExecute "run-normally.bat", WScript.Arguments(0), strFolder, "runas", 0
WScript.Quit 0
The Java part
Java part is more straightforward. What we need to do is to open new process and execute the prepared scripts in it.
/**
* Start this very jar file elevated on Windows. It is strongly recommended to close any existing IO
* before calling this method and avoid writing anything more to files. The new instance of this same
* program will be started and simultaneous write/write or read/write would cause errors.
* #throws FileNotFoundException if the helper vbs script was not found
* #throws IOException if there was another failure inboking VBS script
*/
public void StartWithAdminRights() throws FileNotFoundException, IOException {
//The path to the helper script. This scripts takes 1 argument which is a Jar file full path
File runAsAdmin = new File("run-as-admin.vbs");;
//Our
String jarPath;
//System.out.println("Current relative path is: " + s);
try {
jarPath = "\""+new File(Main.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath()).getAbsolutePath()+"\"";
} catch (URISyntaxException ex) {
throw new FileNotFoundException("Could not fetch the path to the current jar file. Got this URISyntax exception:"+ex);
}
//If the jar path was created but doesn't contain .jar, we're (most likely) not running from jar
//typically this happens when running the program from IDE
//These 4 lines just serve as a fallback in testing, should be deleted in production
//code and replaced with another FileNotFoundException
if(!jarPath.contains(".jar")) {
Path currentRelativePath = Paths.get("");
jarPath = "\""+currentRelativePath.toAbsolutePath().toString()+"\\AutoClient.jar\"";
}
//Now we check if the path to vbs script exists, if it does we execute it
if(runAsAdmin.exists()) {
String command = "cscript \""+runAsAdmin.getAbsolutePath()+"\" "+jarPath;
System.out.println("Executing '"+command+"'");
//Note that .exec is asynchronous
//After it starts, you must terminate your program ASAP, or you'll have 2 instances running
Runtime.getRuntime().exec(command);
}
else
throw new FileNotFoundException("The VBSScript used for elevation not found at "+runAsAdmin.getAbsolutePath());
}
This is my version. It creates a VBScript script, then executes it. This only works if the program that is being run is in a jar file, so you will have to run your IDE as administrator to actually test your program.
public static void relaunchAsAdmin() throws IOException {
relaunchAsAdmin(ThisClass.class); //Change ThisClass to the class that this method is in
}
public static void relaunchAsAdmin(Class<?> clazz) throws IOException {
if(isCurrentProcessElevated()) {
return;
}
final String dir = System.getProperty("java.io.tmpdir");
final File script = new File(dir, "relaunchAsAdmin" + System.nanoTime() +
".vbs");
try {
script.createNewFile();
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(script));
osw.append("Set s=CreateObject(\"Shell.Application\")" + ln + "s.ShellExecute \"" +
System.getProperty("java.home") + "\\bin\\java.exe" + "\",\"-jar \"\"" +
new File(clazz.getProtectionDomain().getCodeSource(
).getLocation().toURI()).getAbsolutePath() + "\"\"\",,\"runas\",0" +
ln + "x=createObject(\"scripting.fileSystemObject\").deleteFile(" +
"WScript.scriptfullname)");
osw.close();
if(System.getenv("processor_architecture").equals("x86")) {
Runtime.getRuntime().exec("C:\\Windows\\System32\\wscript.exe \"" +
script.getAbsolutePath() + "\"");
} else {
Runtime.getRuntime().exec("C:\\Windows\\SysWoW64\\wscript.exe \"" +
script.getAbsolutePath() + "\"");
}
} catch(URISyntaxException e) {
e.printStackTrace();
}
Runtime.getRuntime().exit(0);
}
Note that it is a bit messy. I have been using this method before, so it has been line wrapped to 100 characters (except the comment I wrote for this answer). The
isCurrentProcessElevated()
method will have to be implemented in one way or another. You could try using JNI, or you could use a pure Java method, such as writing in the Program Files or System32 directory and seeing if it failed.
Obviously, this solution will only work on Windows. I never needed to elevate on Linux or Mac systems (mainly because I don't have any Mac systems, and I don't use Linux - I just play with it).
I am looking for a Java tool/package/library that will allow me to force-kill
a child process.
This tool/package/library must work on Windows platform (mandatory).
Support for Linux/Unix is desired.
My Problem
My Java code creates a child process that will simply not react to the
standard Java way for killing a child process: process.destroy(), and,
since I do not have the child's source code, I cannot program it to
better handle termination requests.
I have tried closing the child process' error input and output stream
before calling destroy(), and for no effect.
I have even tried passing ctrlBreak signal (char=3) directly into
child.getOutputStream(), and again have received the same results.
The workaround I have finally managed to find was to:
Obtain the child's PID upon its creation
This can be done in Windows by diffing the process lists
before and after the child's creation (getRuntime().exec("tasklist /v"))
Use the child's PID to issue a force kill system command
in Windows: getRuntime().exec("taskkill /pid " + childPid + " /f")
But - this is complex code I have no desire to debug and maintain, plus the problem
itself, I have no doubt, was previously encountered by many other java developers,
which leads me to the hope that such a Java tool/package/library already exists.
I just don't know its name...
PS: My child process was created by Runtime.getRuntime().exec(cmd), but
I get the same behaviour using a ProcessBuilder.
There is a leaner way to do this using Java JNA.
This works definitely for Windows and Linux, i assume that you can do the same for other platforms too.
The biggest problem of Java process handling is the lack of a method to get the process id of the process started with untime.getRuntime().exec().
Assuming you got the pid of a process, you always can start a kill -9 command in linux, or use similar ways to kill a process in windows.
Here is a way to get the process id natively for linux (borrowed from the selenium framework, :) ), and with the help of JNA this also can be done for windows (using native Windows API calls).
For this to work (for Windows) you first have to get the JNA Library at JAVA NATIVE ACCESS (JNA): Downloads or get it from maven
Look at the following code, which will get the pid of a (in this example windows) program (most of the code is actually debris to get a working java program going):
import com.sun.jna.*;
import java.lang.reflect.Field;
import java.util.logging.Level;
import java.util.logging.Logger;
public class Main {
static interface Kernel32 extends Library {
public static Kernel32 INSTANCE = (Kernel32) Native.loadLibrary("kernel32", Kernel32.class);
public int GetProcessId(Long hProcess);
}
public static void main(String[] args) {
try {
Process p;
if (Platform.isWindows())
p = Runtime.getRuntime().exec("cmd /C ping msn.de");
else if (Platform.isLinux())
p = Runtime.getRuntime().exec("cmd /C ping msn.de");
System.out.println("The PID: " + getPid(p));
int x = p.waitFor();
System.out.println("Exit with exitcode: " + x);
} catch (Exception ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
}
public static int getPid(Process p) {
Field f;
if (Platform.isWindows()) {
try {
f = p.getClass().getDeclaredField("handle");
f.setAccessible(true);
int pid = Kernel32.INSTANCE.GetProcessId((Long) f.get(p));
return pid;
} catch (Exception ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
} else if (Platform.isLinux()) {
try {
f = p.getClass().getDeclaredField("pid");
f.setAccessible(true);
int pid = (Integer) f.get(p);
return pid;
} catch (Exception ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
}
else{}
return 0;
}
}
Hope this helps, ;)...
I had solved such problem in past using the same method you are suggesting here: use taskkill for windows and kill -9 for Unix.
On windows you can use alternatively WMI by either invoking script (VBS or JS) from Java or using one of interoperability libraries (JaWin, Jintegra, Jinterop etc.)
I do not think that this solution is so complicated as you are afraid. I think it is not more than 50 code lines.
Good luck.
For windows using jna 3.5.1
try {
Process p = Runtime.getRuntime.exec("notepad");
Field f = p.getClass().getDeclaredField("handle");
f.setAccessible(true);
long handLong = f.getLong(p);
Kernel32 kernel = Kernel32.INSTANCE;
WinNT.HANDLE handle = new WinNT.HANDLE();
handle.setPointer(Pointer.createConstant(handLong));
int pid = kernel.GetProcessId(handle);
System.out.print(pid);
} catch (Throwable e) {
}
In Java, I'm dynamically creating a set of files and I'd like to change the file permissions on these files on a linux/unix file system. I'd like to be able to execute the Java equivalent of chmod. Is that possible Java 5? If so, how?
I know in Java 6 the File object has setReadable()/setWritable() methods. I also know I could make a system call to do this, but I'd like to avoid that if possible.
Full control over file attributes is available in Java 7, as part of the "new" New IO facility (NIO.2). For example, POSIX permissions can be set on an existing file with setPosixFilePermissions(), or atomically at file creation with methods like createFile() or newByteChannel().
You can create a set of permissions using EnumSet.of(), but the helper method PosixFilePermissions.fromString() will uses a conventional format that will be more readable to many developers. For APIs that accept a FileAttribute, you can wrap the set of permissions with with PosixFilePermissions.asFileAttribute().
Set<PosixFilePermission> ownerWritable = PosixFilePermissions.fromString("rw-r--r--");
FileAttribute<?> permissions = PosixFilePermissions.asFileAttribute(ownerWritable);
Files.createFile(path, permissions);
In earlier versions of Java, using native code of your own, or exec-ing command-line utilities are common approaches.
Prior to Java 6, there is no support of file permission update at Java level. You have to implement your own native method or call Runtime.exec() to execute OS level command such as chmod.
Starting from Java 6, you can useFile.setReadable()/File.setWritable()/File.setExecutable() to set file permissions. But it doesn't simulate the POSIX file system which allows to set permission for different users. File.setXXX() only allows to set permission for owner and everyone else.
Starting from Java 7, POSIX file permission is introduced. You can set file permissions like what you have done on *nix systems. The syntax is :
File file = new File("file4.txt");
file.createNewFile();
Set<PosixFilePermission> perms = new HashSet<>();
perms.add(PosixFilePermission.OWNER_READ);
perms.add(PosixFilePermission.OWNER_WRITE);
Files.setPosixFilePermissions(file.toPath(), perms);
This method can only be used on POSIX file system, this means you cannot call it on Windows system.
For details on file permission management, recommend you to read this post.
In addition to erickson's suggestions, there's also jna, which allows you to call native libraries without using jni. It's shockingly easy to use, and I've used it on a couple of projects with great success.
The only caveat is that it's slower than jni, so if you're doing this to a very large number of files that might be an issue for you.
(Editing to add example)
Here's a complete jna chmod example:
import com.sun.jna.Library;
import com.sun.jna.Native;
public class Main {
private static CLibrary libc = (CLibrary) Native.loadLibrary("c", CLibrary.class);
public static void main(String[] args) {
libc.chmod("/path/to/file", 0755);
}
}
interface CLibrary extends Library {
public int chmod(String path, int mode);
}
For Windows 7 with NIO 2:
public static void main(String[] args) throws IOException {
Path file = Paths.get("c:/touch.txt");
AclFileAttributeView aclAttr = Files.getFileAttributeView(file, AclFileAttributeView.class);
System.out.println(aclAttr.getOwner());
for (AclEntry aclEntry : aclAttr.getAcl()) {
System.out.println(aclEntry);
}
System.out.println();
UserPrincipalLookupService upls = file.getFileSystem().getUserPrincipalLookupService();
UserPrincipal user = upls.lookupPrincipalByName(System.getProperty("user.name"));
AclEntry.Builder builder = AclEntry.newBuilder();
builder.setPermissions( EnumSet.of(AclEntryPermission.READ_DATA, AclEntryPermission.EXECUTE,
AclEntryPermission.READ_ACL, AclEntryPermission.READ_ATTRIBUTES, AclEntryPermission.READ_NAMED_ATTRS,
AclEntryPermission.WRITE_ACL, AclEntryPermission.DELETE
));
builder.setPrincipal(user);
builder.setType(AclEntryType.ALLOW);
aclAttr.setAcl(Collections.singletonList(builder.build()));
}
Just to update this answer unless anyone comes across this later, since JDK 6 you can use
File file = new File('/directory/to/file');
file.setWritable(boolean);
file.setReadable(boolean);
file.setExecutable(boolean);
you can find the documentation on Oracle File(Java Platform SE 7). Bear in mind that these commands only work if the current working user has ownership or write access to that file. I am aware that OP wanted chmod type access for more intricate user configuration. these will set the option across the board for all users.
If you want to set 777 permission to your created file than you can use the following method:
public void setPermission(File file) throws IOException{
Set<PosixFilePermission> perms = new HashSet<>();
perms.add(PosixFilePermission.OWNER_READ);
perms.add(PosixFilePermission.OWNER_WRITE);
perms.add(PosixFilePermission.OWNER_EXECUTE);
perms.add(PosixFilePermission.OTHERS_READ);
perms.add(PosixFilePermission.OTHERS_WRITE);
perms.add(PosixFilePermission.OTHERS_EXECUTE);
perms.add(PosixFilePermission.GROUP_READ);
perms.add(PosixFilePermission.GROUP_WRITE);
perms.add(PosixFilePermission.GROUP_EXECUTE);
Files.setPosixFilePermissions(file.toPath(), perms);
}
You can use the methods of the File class:
http://docs.oracle.com/javase/7/docs/api/java/io/File.html
Apache ant chmod (not very elegant, adding it for completeness) credit shared with #msorsky
Chmod chmod = new Chmod();
chmod.setProject(new Project());
FileSet mySet = new FileSet();
mySet.setDir(new File("/my/path"));
mySet.setIncludes("**");
chmod.addFileset(mySet);
chmod.setPerm("+w");
chmod.setType(new FileDirBoth());
chmod.execute();
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.FileAttribute;
import java.nio.file.attribute.PosixFileAttributes;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.PosixFilePermissions;
import java.util.Set;
public class FileAndDirectory1 {
public static void main(String[] args) {
File file = new File("fileTest1.txt");
System.out.println(file.getAbsoluteFile());
try {
//file.createNewFile();
if(!file.exists())
{
//PosixFilePermission is an enum class, PosixFilePermissions is a final class
//create file permissions from string
Set<PosixFilePermission> filePermissions = PosixFilePermissions.fromString("---------"/* "rwxrwxrwx" */);
FileAttribute<?> permissions = PosixFilePermissions.asFileAttribute(filePermissions);
Files.createFile(file.toPath(), permissions);
// printing the permissions associated with the file
System.out.println("Executable: " + file.canExecute());
System.out.println("Readable: " + file.canRead());
System.out.println("Writable: "+ file.canWrite());
file.setExecutable(true);
file.setReadable(true);
file.setWritable(true);
}
else
{
//modify permissions
//get the permission using file attributes
Set<PosixFilePermission> perms = Files.readAttributes(file.toPath(), PosixFileAttributes.class).permissions();
perms.remove(PosixFilePermission.OWNER_WRITE);
perms.add(PosixFilePermission.OWNER_READ);
perms.add(PosixFilePermission.OWNER_EXECUTE);
perms.add(PosixFilePermission.GROUP_WRITE);
perms.add(PosixFilePermission.GROUP_READ);
perms.add(PosixFilePermission.GROUP_EXECUTE);
perms.add(PosixFilePermission.OTHERS_WRITE);
perms.add(PosixFilePermission.OTHERS_READ);
perms.add(PosixFilePermission.OTHERS_EXECUTE);
Files.setPosixFilePermissions(file.toPath(), perms);
System.out.println("Executable: " + file.canExecute());
System.out.println("Readable: " + file.canRead());
System.out.println("Writable: "+ file.canWrite());
file.delete();
}
} catch (IOException e) {
e.printStackTrace();
}
Path path = Paths.get(String.valueOf(file));
System.out.println(path);
}
}
for Oralce Java 6:
private static int chmod(String filename, int mode) {
try {
Class<?> fspClass = Class.forName("java.util.prefs.FileSystemPreferences");
Method chmodMethod = fspClass.getDeclaredMethod("chmod", String.class, Integer.TYPE);
chmodMethod.setAccessible(true);
return (Integer)chmodMethod.invoke(null, filename, mode);
} catch (Throwable ex) {
return -1;
}
}
works under solaris/linux.
There is an example class on Oracle Docs which works very much similar to the UNIX chmod. It works with java se 7+ though.
Permission 777 is the same as rwxrwxrwx which you can set as follows:
Files.setPosixFilePermissions(path, PosixFilePermissions.fromString("rwxrwxrwx"))