I call a Java function using PHP. The code is:
exec('pushd d:\xampp\htdocs\file_excecute\class & java Autoingestion username password id Sales Daily Summary 20120902',$output,$return);
This code worked on a Windows machine but it is not working on a Linux server. The code is:
exec('pushd \var\www\domainname.com\itune_report\class & java Autoingestion username password id Sales Weekly Summary 20120901',$output,$return);
You are using the wrong kind of slash as a field separator, but that may not be your only problem.
The output of the command appears in $output, since you use the exec(command, output, return) form.
However, this only gives you stdout. The shell will send error messages to stderr.
Unfortunately there isn't a version of exec() that reads stderr.
You can merge both outputs to $output by adding 2>&1 at the end of your shell command:
exec("mycommand 2>&1", $output, $return);
Look at $output, and you will either find the output of your successful command or error messages which you can use to work out why it didn't work.
If you want to write something more rigorous that treats stdout and stderr separately, you'll need to use proc_open() instead: PHP StdErr after Exec()
There are (perhaps insurmountable) difficulties when trying to execute sudo commands from a PHP script and from an external script called by PHP on SELinux enabled machines.
Make sure you use Linux directory path in your command
Linux won't let apache change the group id of the process by default.
You may need to use another solution, like make the PHP script deposit a file in a directory which is monitored by cron or inotify and which will call another script with root privileges.
Obviously it does not work on Linux. Command pushd is defined in windows shell only. The path on linux must use forward and not back slashe as separator.
Related
I have a PHP application which executes a Java .jar file through shell_exec():
shell_exec("java jar myJarProgram.jar");
I have a need to determine if an instance this java program myJarProgram.jar is already running, because if it is not, then I can start it using the above PHP statement.
How can I do that?
You can use "jps" utility to grep your process with java
jps -mlvV | grep myJarProgram.jar
jps is a good candidate for this, but please note that
To use the jps command-line tool you need to install a JDK.
Otherwise, you can parse the output of another shell_exec call that uses ps with the arguments you want in order to get the running processes: at this point you can check if the process is present.
$search_string = "[j]ava jar myJarProgram.jar";
$running = shell_exec("ps -A -ww | grep '$search_string'");
or similar.
If $running is empty, you can launch the jar.
Another option is to perform everything with a single shell_exec, both with commands concatanation (simple && and ||) or creating a .sh script and shell_executing that.
EDIT:
According to the user comment, the script must work both for Windows and Linux.
You can use the php PHP_OS predefined constant to check if it's Windows or Linux:
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') etc....
If it's Linux, you can use the shell_exec as reported above.
If it's Windows, you can change the shell_exec string using the tasklist Windows command. I don't know it, but there are already dedicated questions and answers like this one.
So I have the following problem: I have a web service running inside a Tomcat7 server on Linux. The web service however has to execute some commands (mostly file operations such as copy and mount). Copy I've replaced with java.nio, but I don't think that there is a replacement for mount.
So I'm trying to execute shell commands out of my Tomcat Java process. Unfortunately it doesn't execute my commands. I've implemented the execution of shell commands in Java before. So my code should be correct:
Process pr = Runtime.getRuntime().exec("mount -o loop -t iso9660 <myimage> <mymountpoint>");
pr.waitFor();
<myimage> and <mymountpoint> are absolute paths, so no issues there either.
I've debugged my commands and they are working when executed on the console.
I've tried sending other commands. Simple commands such as id and pwd are working!
I've tried using /bin/bash -c "<command>", which didn't work.
I've tried executing a shell script, which executes the command, which didn't work.
I've tried escaping the spaces in my command, which didn't work.
So I've digged even deeper and now I'm suspecting some Tomcat security policy (Sandbox?), which prevents me from executing the command. Since security is no issue for me (it's an internal system, completely isolated from the outside world), I've tried a hack, which became quite popular just recently:
System.setSecurityManager(null);
This didn't work either. I'm using Java7 and Tomcat7 on RHEL6. Tomcat7 is just extracted! I don't have any files in /etc/.. or any other folder than /opt/tomcat/, where I've extracted the zip from the Tomcat home page. I've searched the /opt/tomcat/conf folder for security settings, but all I could find was the file catalina.policy, where it didn't seem like I could set some security level for shell commands.
Any ideas?
A few things:
System.setSecurityManager(null);
you have just killed the security of your application.
Yes, Tomcat is running as root. If I execute id I'm root as well.
Fix this immediately!
Now on to the question. You shouldnt have Tomcat executing anything, you need to defer this to a separate process whether that be a shell script or another Java program. This should also remove what (I hope) was a dependency on root running Tomcat. It should be possible to perform this command as a non-privileged user that cannot log into the system normally. You would do this by configuring /etc/fstab and supplying that same user the permissions to do this. From a pure security POV the process that mounts should not be owned by the tomcat user. Nor should the tomcat user ever be root. So to recap:
1) Stop running Tomcat as root
2) Create a separate process outside of the context of Tomcat to run this mount
3) Create a tomcat user, this user should not be able to log into the system nor should it be a privileged user (admin,super user, etc)
4) Create a process user, this user should be configured exactly as the tomcat user
5) Edit /etc/fstab giving the process user the necessary permissions to mount correctly.
It's generally a bad idea to use the single-string form of Runtime.exec. A better option is to use ProcessBuilder, and split up the arguments yourself rather than relying on Java to split them for you (which it does very naïvely).
ProcessBuilder pb = new ProcessBuilder("/bin/mount", "-o", "loop", /*...*/);
pb.redirectErrorStream(true); // equivalent of 2>&1
Process p = pb.start();
You say you're on RHEL so do you have selinux active? Check your logs and see if this is what's blocking you (I think it's audit.log you're looking for, it's been a few years since I've used selinux). If this does turn out to be the problem then you should probably ask on superuser or serverfault rather than SO...
I'm not sure if that's the problem you are having, but I've seen issues when Runtime.exec() is used without reading the associated output buffers. You can find a detailed explanation and potential solutions here. Reading the output and error streams can also help you figure out what's going on at the OS level when you run the command.
I've recently had to do something like this from a Swing app.
You'll probably be able to pull it off with ProcessBuilder, as in Ian's answer, but I found that once things start to get complex, it's easier to write a shell script that does what you want, enabling you to pass as few parameters as possible. Then use ProcessBuilder to invoke the shell script.
If you're invoking anything that has more than really minimal output, you'll also have to read the output and error streams to keep the process from blocking when the output buffers fill, as it seems you are already doing.
I use sudo -S before command and for the tomcat7 user: tomcat7 ALL=(ALL) NOPASSWD:ALL
This is an extremely strange situation, but I just cannot point out what I'm doing wrong.
I'm executing a big bunch of SQL scripts (table creation scripts, mostly). They are executed through Java, using sqlcmd. Here's the sqlcmd command I use.
sqlcmd -m 11 -S SERVER -d DB -U USER -P PASS -r0 -i "SCRIPT.sql" 2> "ERRORS.log" 1> NULL
Note: I use the -r0 and redirects to make sure only errors go into the log file. I chuck out all STDOUTs.
Now I execute this command in Java, using getRuntime.exec(), like this.
Runtime.getRuntime().gc();
strCmd = "cmd /c sqlcmd -m 11 -S SERVER -d DB -U USER -P PASS -r0 -i \"SCRIPT.sql\" 2> \"ERRORS.log\" 1> NULL"
Process proc = Runtime.getRuntime().exec(strCmd);
proc.waitFor();
Note: I use cmd /c, so that the command runs in its own shell and exits gracefully. Also, this helps in immediately reading the error log to look for errors.
The Problem!
This command works perfectly when run by hand on the command prompt (i.e. the tables are getting created as intended). However, when executed through Java as shown, the scripts are run, and and there are no errors, no exceptions, nothing in the logs. But, when checking in SSMS, the tables aren't there!
Where do I even begin debugging this issue?
UPDATE: I'M A MORON
The return value from the getRuntime().exec method is 1. It should be 0, which denotes normal execution.
Any pointers on how to fix this?
UPDATE 2
I've looked at the process' ErrorStream, and this is what it has.
Sqlcmd: Error: Error occurred while opening or operating on file 2>
(Reason: The filename, directory name, or volume label syntax is
incorrect).
Looks like the path I'm passing is wrong. The error log goes into my profile directory, which is C:\Documents and Settings\my_username. Do the spaces in the path matter? I'm anyways double-quoting them!
Have a look at the exec method with an string array as parameter:
java.lang.Runtime.exec(String[] cmdArray)
The JavaDoc for this method says:
Executes the specified command and arguments in a separate process.
So, the first item in the array is the command and all of your arguments are appended to the array, e. g.,
Runtime.getRuntime().exec(new String[] {"cmd", "/c", "sqlcmd ... "});
After looking at your comment and the implementation of exec(String) it seems to be, that the exec method recognizes the pipe operator > as an argument to cmd, because exec(String) splits the command string to an array using whitespaces as seperators.
I don't have privs to post comments - which is what this is - but what if you try putting in a bogus user id for the DB? Does that cause a different execution path? Will that give you a Java error? Or an Auth error in your DB? Also, def tweak the user, not the password and learn from my experience that if you tweak the password that's a great way to get an account locked out!
The other thing - and this may be a shot in the dark - but what are the JRE and driver you're using? I believe there's a known issue with JRE 1.6.0.29 and the sqljdbc4 JAR. I have more details on this, but I'll have to post the link once I get to work.
Edit:
I know it's been established that the JRE/sqljdbc combo isn't your issue, but if folks search and find this, here is the link I spoke of above:
Driver.getConnection hangs using SQLServer driver and Java 1.6.0_29
First enable log/view commands output (since exec() returns 1), which would point out possible cause of the issue.
Use proc.getInputStream() and print the contents to a file or console.
I am trying to run a Jar file in the backend of my php code.But I am not getting the desired output to it.There is a jar file which runs in the background and returns the Page Rank of any of the keyword and Domain given to it.
I am attaching the code,please suggest me any solution to it,because when I run it on the terminal,it is giving correct output.
Here is the Code :
<?php
set_time_limit(0);
function returnJarPath()
{
$jarPath = $_SERVER['DOCUMENT_ROOT'] . "myFolder/tools_new/includes/Rank.jar";
return $jarPath;
}
$jar = returnJarPath();
$command = "java -jar $jar aspdotnet/microsoft.com";//Passing the Argument to the Jar file.
$shellOutput = shell_exec($command);
print "The Shell Output is : " ; var_dump($shellOutput);print "<br />";
exec($command,$executeCommmand);
print "The Exec returns the value : " ; var_dump($executeCommmand);print "<br />";
passthru($command,$passthruCommand);
print "The Passthru returns the value : " . $passthruCommand. "<br />";
?>
I just checked apache's error log and the last error I found was :
sh: java: command not found
But as I have already said,I have been using the same command through SSH to run the Java command.So there's no such possibility of not having JAVA installed on the server.Please help me out of this mess...
If the jar file writes to standard output you can use exec.
Here is an example how I use it:
may be first: exec("cd jar dir"); // if jar fine needs to be executed from the same dir
$output = exec("/usr/bin/java -jar $jar aspdotnet/microsoft.com");
But as you say:
sh: java: command not found
It means the there is no path alias to java from php. Just use the full java path to the executable /usr/bin/java.
Given you are calling java. My bet is the output is being displayed in the Java Console, and not in the shell, where PHP could pull the text information.
How to solve this dilemma?
Well you could write the results to a file, if you have the java source to modify, and then read that file through php to get the results. The possibility of a collision here would be pretty good. The other option is to have Java connect to your MySQL database (if you had one) and then run the java then query the database for the response. Of course, you would need to pass Java a way for it to input the data to insert a record you could identify (a hash of some sort), I have never done that in Java, just a theory of how you might be able to do it.
Update
You may want to try the standard output as suggested by darko petreski as another option as well.
If the PHP code is to be executed in a server (and not via command line) the user that runs the java executable is www-data, not you. In that case make sure that www-data has the permissions to read the jar file and to execute the java executable
The first thing that I would check/change is the line in the function where you are building the $jarPath variable from this:
$jarPath = $_SERVER['DOCUMENT_ROOT'] . "myFolder/tools_new/includes/Rank.jar";
to this:
$jarPath = $_SERVER['DOCUMENT_ROOT'] . "/myFolder/tools_new/includes/Rank.jar";
The trailing slash may not be present in $_SERVER['DOCUMENT_ROOT'] which could cause issues.
I am assuming that when you say it runs from the console, you are running the java command like so:
$ java -jar /rest/of/path/myFolder/tools_new/includes/Rank.jar aspdotnet/microsoft.com
I would ensure that you include the path to the java binary in the $command variable like so...
$command = "/path/to/java -jar $jar aspdotnet/microsoft.com";
The user that owns the web server process may not have a $PATH variable that includes the path to the Java binary.
I have a Java application executed from a ([ba]sh) shell script and unfortunately sometimes the people responsible for deploying it and starting it fail to switch to the appropriate user before starting the application. In this situation I'd like the application to not run at the very least, and ideally issue a warning not to do that. I thought about trying to alias java or change the path for root to include a fake java which does so, but this might have undesirable side effects and isn't going to be effective easily since the shell script specifies the full path to the java binary.
So, is there a standard idiom in shell scripts for 'don't run if I'm root'?
Example in bash:
if [ `id -u` = 0 ]; then
echo "You are root, go away!"
exit 1
fi
In BASH, you can take the output of whoami and compare it to root.
I use something like this at the beginning of scripts that I want to
be run under a service account:
LUSER='my-service'
if [ `id -un` != $LUSER ]; then
exec su $LUSER -s $SHELL -c "$0 $#"
fi
# actual script commands here.
If run as the correct user, execution will continue as planned. If run
as root, privileges are dropped to the wanted user-id. Other users
will get a password prompt which should tell them that something is
wrong.
su -s $SHELL ... is used to override the shell set in /etc/passwrd
-- it may be set to /bin/false for the service account.
I have used this on Debian systems, using bash and dash. Feel free
to comment if portability can be improved.