Apologies in advance, my Java is rubbish. I have cobbled together (from other people) a script which executes a script , spooling the output to a text file, which I then send to another system. Here is the crontab entry
jcb@LWS-DEV4B:~/bridge/heartbeat$ crontab -l
*/2 * * * * (cd /home/jcb/bridge/heartbeat/; ./hb_execute_bridge_util_mem_usage.sh)
*/5 * * * * (cd /home/jcb/bridge/heartbeat/; ./hb_bridge_status_cron.sh)
When I call the java program manually, it works.
jcb@LWS-DEV4B:~/bridge/heartbeat$ java -classpath /home/jcb/bridge/heartbeat/ojdbc6.jar:. hb_bridge_util_mem_usage
jcb@LWS-DEV4B:~/bridge/heartbeat$ ls -ltr hb_bridge_util_mem_usage.txt
-rwxr-xr-x 1 jcb jcb 14919 Oct 28 11:55 hb_bridge_util_mem_usage.txt
When I call the shell script which calls the java program, it works.
jcb@LWS-DEV4B:~/bridge/heartbeat$ ./hb_execute_bridge_util_mem_usage.sh
jcb@LWS-DEV4B:~/bridge/heartbeat$ ls -ltr hb_bridge_util_mem_usage.txt
-rwxr-xr-x 1 jcb jcb 14919 Oct 28 11:59 hb_bridge_util_mem_usage.txt
However the cronjob does not spool any output.
-rwxr-xr-x 1 jcb jcb 0 Oct 28 11:52 hb_bridge_util_mem_usage.txt
I have read a few things in forums about waiting for processes, which I've added to see whether it makes a difference. Here is my shell script:
#!/bin/bash
java -classpath /home/jcb/bridge/heartbeat/ojdbc6.jar:. hb_bridge_util_mem_usage
Here is my java program, again: the waiting, destroying, getting input are things that I added afterwards to see if it would make a difference:
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
public class hb_bridge_util_mem_usage {
public static Connection getConnection() throws Exception {
String driver = "oracle.jdbc.driver.OracleDriver";
String url = "jdbc:oracle:thin:@???";
String username = "???";
String password = "???";
Class.forName(driver);
Connection conn = DriverManager.getConnection(url, username, password);
return conn;
}
public static void main(String[] args)throws Exception {
String id = "001";
String fileName = "hb_bridge_util_mem_usage.txt";
ProcessBuilder processBuilder = new ProcessBuilder();
processBuilder.command("bash", "-c", "util_mem_usage | tee /home/jcb/bridge/heartbeat/hb_bridge_util_mem_usage.txt");
Process process = processBuilder.start();
InputStream in = process.getInputStream();
InputStream error = process.getErrorStream();
for (int i = 0; i < error.available(); i ) {
System.out.println("" error.read());
}
// wait for 10 seconds and then destroy the process
Thread.sleep(10000);
process.destroy();
FileInputStream fis = null;
PreparedStatement pstmt = null;
Connection conn = null;
try {
conn = getConnection();
conn.setAutoCommit(false);
File file = new File(fileName);
fis = new FileInputStream(file);
pstmt = conn.prepareStatement("insert into hb_bridge_util_mem_usage(id, fileName, fileBody) values (?, ?, ?)");
pstmt.setString(1, id);
pstmt.setString(2, fileName);
pstmt.setAsciiStream(3, fis, (int) file.length());
pstmt.executeUpdate();
conn.commit();
} catch (Exception e) {
System.err.println("Error: " e.getMessage());
e.printStackTrace();
} finally {
pstmt.close();
fis.close();
conn.close();
}
}
CodePudding user response:
The problem is not related to your java code; java doesn't 'run differently' just because cron invoked it.
There are 2 likely explanations:
User of the process
Processes are owned by a user. Their rights, e.g. when accessing dirs and files, are controlled by what that user is allowed to do. For example, if you have your /home/barry
folder set up as "readable/writable/seeable by the user, but only readable/seeable by anybody else", i.e. if you run ls -la
in the /home
dir and you see:
rwxr-xr-x barry
Then if you are logged in as barry and you start a java app that tries to execute:
Files.write("Hello!", Paths.get("/home/barry/test.txt"));
it'll work fine. But if you then run this as user jane
instead, it will fail: the process has the same rights as user jane
, and user jane
doesn't have the right to write in that folder.
cron runs your application as some user. Depends on how you've configured cron. If said user doesn't have the rights to write to the file, you have your explanation. The easy fix is to either make where-ever your app is trying to write a writable place for whatever user cron is running it as, or, you configure cron to run this one under a different user, or, you run the command through su
which lets you run it as different user. However, using su
to run something as a different user only works if you invoke su
itself as root
.
SECURITY NOTE: Under many configs, there is a per-user cron table (and then those tasks are run as that user), as well as a global crontable and everything in that is run as root. Running apps as root is extremely dangerous; any problem can wipe out or take over the entire system. You should NOT run things as root. As a consequence, if this is in the global cron table and IS running as root, you should most absolutely do that su
thing. We can more or less trust su
not to mess up, and because it will then invoke your java
app with the rights of e.g. barry
, at least this way you no longer have to trust java
. You shouldn'tr trust java
for a simple reason: java is a complex executable that runs arbitrary code. It doesn't matter if NASA wrote it: You can't trust that kind of complexity nearly as much as the extremely simple su
command.
Current working dir
I think the cd
stuff means this can't be it, but I'm not 100% certain that the working dir for the java process ends up being the dir you cd
to. You may want to check this. For example, by writing the result of System.getProperty("user.dir")
(which is a rather weirdly named property that gives you the current working dir), or print new File(".").toAbsolutePath()
to an absolute location, e.g. /home/foo/test.txt
where /home/foo
has rwxrwxrwx
rights (set that up by running sudo chmod 777 /home/foo
), after making that foo dir for the sole purposes of this test.
Then let cron do its thing and you can use that file to track what your java process emitted as you debug the problem.
CodePudding user response:
The problem was that Java could not execute the C program "util_mem_usage", despite me having "su" commands dotted around. My solution was to seperate this command into a shell scipt. The paths were what are required for the C program to work (and probably what Java was silently struggling with too).
#!/bin/bash
PATH=/home/jcb/bin:/usr/bin/
**LD_LIBRARY_PATH=/home/jcb/lib
PROJECT_ROOT=/home/jcb
export LD_LIBRARY_PATH
export PROJECT_ROOT**
util_mem_usage | tee /home/jcb/bridge/heartbeat/hb_bridge_util_mem_usage.txt
My java program then call this instead of the C utility
processBuilder.command("bash", "-c", "/home/jcb/bridge/heartbeat/hb_get_bridge_util_mem_usage.sh");
It does not seem like an elegant solution, but it works...