This line is wrong, it does not do what you think it does:
if (errno = chmod (cmd_str,i) < 0)
Due to operator precedence, it is evaluated as-if you had written it like this:
if (errno = (chmod (cmd_str,i) < 0))
ie, the return value of chmod()
is first compared to < 0
, and then that boolean result is assigned to errno
. So, if chmod()
succeeds then you end up setting errno
to 0
, but if chmod()
fails then you end up setting errno
to 1
, which is EPERM
, hence the Operate permission not allowed
error message.
You need to write that expression like this instead:
if ((errno = chmod (cmd_str,i)) < 0)
Or, like this:
errno = chmod (cmd_str,i);
if (errno < 0)
ie, the return value of chmod()
is first assigned to errno
, and then that value is compared to < 0
.
That being said, you really should not be naming your local variable as errno
, as there is already a standard errno
variable which chmod()
sets on failure. Name your local variable something more unique instead, and then use the standard errno
when formatting your error message. In which case, you can just get rid of your local variable altogether, eg:
if (chmod (cmd_str,i) < 0)
Also, after calling env->GetStringUTFChars()
, you need to call env->ReleaseStringUTFChars()
or else you will leak the memory.
And, you can replace ::sprintf()
with either UnicodeString::sprintf()
or UnicodeString::Format()
to simplify the formatting of your error message.
Try this:
extern "C" JNIEXPORT jstring JNICALL
ExecuteCommand(JNIEnv *env, jstring cmd) {
const char *cmd_str = env->GetStringUTFChars(cmd, NULL);
const char mode[] = "0754"; // make it executable
int i = strtol(mode, NULL, 8);
if (chmod (cmd_str, i) < 0)
{
UnicodeString msg = UnicodeString().sprintf(L"chmod failed: %hs: error in chmod(%hs) - %d (%hs)\n",
cmd_str, mode, errno, strerror(errno));
/* alternatively:
UnicodeString msg = UnicodeString::Format(_D("chmod failed: %s: error in chmod(%s) - %d (%s)\n"),
ARRAYOFCONST(( cmd_str, mode, errno, strerror(errno) )) );
*/
ShowMessage(msg);
}
char* alist[] = {NULL};
execvp(cmd_str, alist); // or FILE* fp = popen(...)
env->ReleaseStringUTFChars(cmd, cmd_str);
std::string result = "some result return";
return env->NewStringUTF(result.c_str());
}
On a side note, be aware that env->GetStringUTFChars()
and env->NewStringUTF()
both operate on Java's Modified UTF-8, not standard UTF-8. This is not an issue if your strings contain ASCII characters only, but you might run into problems if they contain non-ASCII characters. Android runs on top of Linux, and Linux filesystem paths use standard UTF-8.
So, you may need to convert between Modified UTF-8 and standard UTF-8 when making external calls. Otherwise, consider operating on Java's native (and standard) UTF-16 encoding instead, using env->(Get|Release)StringChars()
and env->NewString()
, and converting between UTF-16 and standard UTF-8 when needed, using another library, or your own manual code.
Or, you can convert a jstring
to standard UTF-8 using Java's own String.getBytes(Charset)
1 or String.getBytes(CharsetName)
method. And create a new jstring
from standard UTF-8 using Java's native String(byte[], Charset)
1 or String(byte[], CharsetName)
constructors.
1 Java's StandardCharsets.UTF_8
is a Charset
object for standard UTF-8.