blob: 26d65f8061c7c9511741c6c24257f39de660cb36 [file] [edit]
/*
* Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include <stdio.h>
#include <string.h>
#include <stddef.h>
#include <stdlib.h>
#include <errno.h>
#include <linux/limits.h>
#include <unistd.h>
#include <libgen.h>
#include "JvmLauncher.h"
#include "LinuxPackage.h"
static char* getModulePath(void) {
char modulePath[PATH_MAX] = { 0 };
ssize_t modulePathLen = 0;
char* result = 0;
modulePathLen = readlink("/proc/self/exe", modulePath,
sizeof(modulePath) - 1);
if (modulePathLen < 0) {
JP_LOG_ERRNO;
return 0;
}
modulePath[modulePathLen] = '\0';
result = strdup(modulePath);
if (!result) {
JP_LOG_ERRNO;
}
return result;
}
# define PACKAGE_TYPE_UNKNOWN 0
# define PACKAGE_TYPE_RPM 1
# define PACKAGE_TYPE_DEB 2
typedef struct {
char* name;
int type;
} PackageDesc;
static void freePackageDesc(PackageDesc* desc) {
if (desc) {
free(desc->name);
free(desc);
}
}
static PackageDesc* createPackageDesc(void) {
PackageDesc* result = 0;
result = malloc(sizeof(PackageDesc));
if (!result) {
JP_LOG_ERRNO;
goto cleanup;
}
result->type = PACKAGE_TYPE_UNKNOWN;
result->name = 0;
cleanup:
return result;
}
static PackageDesc* initPackageDesc(PackageDesc* desc, const char* str,
int type) {
char *newStr = strdup(str);
if (!newStr) {
JP_LOG_ERRNO;
return 0;
}
free(desc->name);
desc->name = newStr;
desc->type = type;
return desc;
}
#define POPEN_CALLBACK_USE 1
#define POPEN_CALLBACK_IGNORE 0
typedef int (*popenCallbackType)(void*, const char*);
static int popenCommand(const char* cmdlineFormat, const char* arg,
popenCallbackType callback, void* callbackData) {
char* cmdline = 0;
FILE *stream = 0;
const size_t cmdlineLenth = strlen(cmdlineFormat) + strlen(arg);
char* strBufBegin = 0;
char* strBufEnd = 0;
char* strBufNextChar = 0;
char* strNewBufBegin = 0;
size_t strBufCapacity = 0;
int callbackMode = POPEN_CALLBACK_USE;
int exitCode = -1;
int c;
ptrdiff_t char_offset;
cmdline = malloc(cmdlineLenth + 1 /* \0 */);
if (!cmdline) {
JP_LOG_ERRNO;
goto cleanup;
}
#if defined(__GNUC__) && __GNUC__ >= 5
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
#endif
if (0 > snprintf(cmdline, cmdlineLenth, cmdlineFormat, arg)) {
JP_LOG_ERRNO;
goto cleanup;
}
#if defined(__GNUC__) && __GNUC__ >= 5
#pragma GCC diagnostic pop
#endif
JP_LOG_TRACE("popen: (%s)", cmdline);
stream = popen(cmdline, "r");
if (!stream) {
JP_LOG_ERRNO;
goto cleanup;
}
for (;;) {
c = fgetc(stream);
if((EOF == c || '\n' == c)) {
if (POPEN_CALLBACK_USE == callbackMode
&& strBufBegin != strBufNextChar) {
*strBufNextChar = 0;
JP_LOG_TRACE("popen: [%s]", strBufBegin);
callbackMode = (*callback)(callbackData, strBufBegin);
strBufNextChar = strBufBegin;
}
if (EOF == c) {
break;
}
continue;
}
if (strBufNextChar == strBufEnd) {
/* Double buffer size */
strBufCapacity = strBufCapacity * 2 + 1;
char_offset = strBufNextChar - strBufBegin;
strNewBufBegin = realloc(strBufBegin, strBufCapacity);
if (!strNewBufBegin) {
JP_LOG_ERRNO;
goto cleanup;
}
strBufNextChar = strNewBufBegin + char_offset;
strBufEnd = strNewBufBegin + strBufCapacity;
strBufBegin = strNewBufBegin;
}
*strBufNextChar++ = (char)c;
}
cleanup:
if (stream) {
exitCode = pclose(stream);
}
if (strBufBegin) {
free(strBufBegin);
}
free(cmdline);
JP_LOG_TRACE("popen: exit: %d", exitCode);
return exitCode;
}
static char* concat(const char *x, const char *y) {
const size_t lenX = strlen(x);
const size_t lenY = strlen(y);
char *result = malloc(lenX + lenY + 1 /* \0 */);
if (!result) {
JP_LOG_ERRNO;
} else {
strcpy(result, x);
strcat(result, y);
}
return result;
}
static int initRpmPackage(void* desc, const char* str) {
initPackageDesc((PackageDesc*)desc, str, PACKAGE_TYPE_RPM);
return POPEN_CALLBACK_IGNORE;
}
static int initDebPackage(void* desc, const char* str) {
char* colonChrPos = strchr(str, ':');
if (colonChrPos) {
*colonChrPos = 0;
}
initPackageDesc((PackageDesc*)desc, str, PACKAGE_TYPE_DEB);
return POPEN_CALLBACK_IGNORE;
}
#define LAUNCHER_LIB_NAME "/libapplauncher.so"
static int findLauncherLib(void* launcherLibPath, const char* str) {
char* buf = 0;
const size_t strLen = strlen(str);
const size_t launcherLibNameLen = strlen(LAUNCHER_LIB_NAME);
if (launcherLibNameLen <= strLen
&& !strcmp(str + strLen - launcherLibNameLen, LAUNCHER_LIB_NAME)) {
buf = strdup(str);
if (!buf) {
JP_LOG_ERRNO;
} else {
*(char**)launcherLibPath = buf;
}
return POPEN_CALLBACK_IGNORE;
}
return POPEN_CALLBACK_USE;
}
static PackageDesc* findOwnerOfFile(const char* path) {
int popenStatus = -1;
PackageDesc* pkg = 0;
pkg = createPackageDesc();
if (!pkg) {
return 0;
}
popenStatus = popenCommand(
"rpm --queryformat '%{NAME}' -qf '%s' 2>/dev/null", path,
initRpmPackage, pkg);
if (popenStatus) {
pkg->type = PACKAGE_TYPE_UNKNOWN;
popenStatus = popenCommand("dpkg -S '%s' 2>/dev/null", path,
initDebPackage, pkg);
}
if (popenStatus) {
pkg->type = PACKAGE_TYPE_UNKNOWN;
}
if (PACKAGE_TYPE_UNKNOWN == pkg->type || !pkg->name) {
freePackageDesc(pkg);
pkg = 0;
}
if (pkg) {
JP_LOG_TRACE("owner pkg: (%s|%d)", pkg->name, pkg->type);
}
return pkg;
}
char* getJvmLauncherLibPath(void) {
char* modulePath = 0;
char* appImageDir = 0;
char* launcherLibPath = 0;
const char* pkgQueryCmd = 0;
int popenStatus = -1;
PackageDesc* pkg = 0;
modulePath = getModulePath();
if (!modulePath) {
goto cleanup;
}
pkg = findOwnerOfFile(modulePath);
if (!pkg) {
/* Not a package install */
/* Launcher should be in "bin" subdirectory of app image. */
/* Launcher lib should be in "lib" subdirectory of app image. */
appImageDir = dirname(dirname(modulePath));
launcherLibPath = concat(appImageDir, "/lib" LAUNCHER_LIB_NAME);
} else {
if (PACKAGE_TYPE_RPM == pkg->type) {
pkgQueryCmd = "rpm -ql '%s' 2>/dev/null";
} else if (PACKAGE_TYPE_DEB == pkg->type) {
pkgQueryCmd = "dpkg -L '%s' 2>/dev/null";
} else {
/* Should never happen */
JP_LOG_ERRMSG("Internal error");
goto cleanup;
}
popenStatus = popenCommand(pkgQueryCmd, pkg->name, findLauncherLib,
&launcherLibPath);
if (popenStatus) {
free(launcherLibPath);
launcherLibPath = NULL;
goto cleanup;
}
}
cleanup:
free(modulePath);
freePackageDesc(pkg);
return launcherLibPath;
}