| /* |
| * 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; |
| } |