| /******************************************************************************* |
| * Copyright (c) 2014, 2017 Rapicorp, Inc and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Rapicorp, Inc - application to collect native packages from an existing install |
| *******************************************************************************/ |
| package org.eclipse.equinox.internal.p2.touchpoint.natives; |
| |
| import java.io.*; |
| import java.net.URI; |
| import java.util.*; |
| import org.eclipse.core.runtime.*; |
| import org.eclipse.equinox.app.IApplication; |
| import org.eclipse.equinox.app.IApplicationContext; |
| import org.eclipse.equinox.internal.p2.touchpoint.natives.actions.ActionConstants; |
| import org.eclipse.equinox.internal.p2.touchpoint.natives.actions.CheckAndPromptNativePackage; |
| import org.eclipse.equinox.p2.core.*; |
| import org.eclipse.equinox.p2.engine.IProfile; |
| import org.eclipse.equinox.p2.engine.IProfileRegistry; |
| import org.eclipse.equinox.p2.metadata.*; |
| import org.eclipse.equinox.p2.query.IQueryResult; |
| import org.eclipse.equinox.p2.query.QueryUtil; |
| import org.eclipse.osgi.util.NLS; |
| import org.osgi.framework.ServiceReference; |
| |
| public class NativePackageExtractionApplication implements IApplication { |
| //Keys used in the file created by the application |
| private static final String PROP_LAUNCHER_NAME = "launcherName"; //$NON-NLS-1$ |
| private static final String PROP_ARCH = "arch"; //$NON-NLS-1$ |
| private static final String PROP_DEPENDS = "depends"; //$NON-NLS-1$ |
| |
| //Internal constants |
| private static final String DEFAULT_VERSION_CONSTRAINT = "ge"; //$NON-NLS-1$ |
| private static final String _ACTION_ID = "_action_id_"; //$NON-NLS-1$ |
| private static final String PROP_P2_PROFILE = "eclipse.p2.profile"; //$NON-NLS-1$ |
| private static final Integer EXIT_ERROR = 13; |
| |
| //Constants for arguments |
| private static final String OPTION_TO_ANALYZE = "-toAnalyze"; //$NON-NLS-1$ |
| private static final String OPTION_RESULT_FILE = "-output"; //$NON-NLS-1$ |
| |
| //Values provided as a parameter to the application |
| private File installation; |
| private File resultFile; |
| |
| //Values derived |
| private IProvisioningAgent targetAgent; |
| private String profileId; |
| |
| //Data collected by the application |
| private Properties extractedData = new Properties(); |
| |
| private Properties installCommandsProperties = new Properties(); |
| |
| private boolean stackTrace = false; |
| |
| @Override |
| public Object start(IApplicationContext context) throws Exception { |
| try { |
| processArguments((String[]) context.getArguments().get("application.args")); //$NON-NLS-1$ |
| initializeServices(); |
| NativeTouchpoint.loadInstallCommandsProperties(installCommandsProperties, "debian"); //$NON-NLS-1$ |
| NativeTouchpoint.loadInstallCommandsProperties(installCommandsProperties, "fedora"); //$NON-NLS-1$ |
| NativeTouchpoint.loadInstallCommandsProperties(installCommandsProperties, "windows"); //$NON-NLS-1$ |
| collectData(); |
| persistInformation(); |
| } catch (CoreException e) { |
| deeplyPrint(e.getStatus(), System.err, 0); |
| return EXIT_ERROR; |
| } |
| return IApplication.EXIT_OK; |
| } |
| |
| private void processArguments(String[] args) throws CoreException { |
| if (args == null || args.length == 0) { |
| throw new CoreException(new Status(IStatus.ERROR, Activator.ID, NLS.bind(Messages.NativePackageExtractionApplication_MissingParameters, OPTION_TO_ANALYZE, OPTION_RESULT_FILE))); |
| } |
| for (int i = 0; i < args.length; i++) { |
| // check for args without parameters (i.e., a flag arg) |
| String opt = args[i]; |
| |
| if (OPTION_TO_ANALYZE.equals(opt)) { |
| installation = new File(getRequiredArgument(args, ++i)); |
| if (!installation.exists()) |
| throw new CoreException(new Status(IStatus.ERROR, Activator.ID, Messages.NativePackageExtractionApplication_FolderNotFound + installation.getAbsolutePath())); |
| continue; |
| } |
| |
| if (OPTION_RESULT_FILE.equals(opt)) { |
| resultFile = new File(getRequiredArgument(args, ++i)); |
| continue; |
| } |
| } |
| } |
| |
| private static String getRequiredArgument(String[] args, int argIdx) throws CoreException { |
| if (argIdx < args.length) { |
| String arg = args[argIdx]; |
| if (!arg.startsWith("-")) //$NON-NLS-1$ |
| return arg; |
| } |
| throw new ProvisionException(NLS.bind(Messages.NativePackageExtractionApplication_MissingValue, args[argIdx - 1])); |
| } |
| |
| private void collectData() { |
| IProfileRegistry registry = (IProfileRegistry) targetAgent.getService(IProfileRegistry.SERVICE_NAME); |
| IProfile p = registry.getProfile(profileId); |
| collectArchitecture(p); |
| collectLauncherName(p); |
| collectDebianDependencies(p); |
| } |
| |
| private void persistInformation() throws CoreException { |
| try (BufferedOutputStream os = new BufferedOutputStream(new FileOutputStream(resultFile))) { |
| extractedData.store(os, "Data extracted from eclipse located at " + installation); //$NON-NLS-1$ |
| } catch (IOException e) { |
| throw new CoreException(new Status(IStatus.ERROR, Activator.ID, Messages.NativePackageExtractionApplication_PersistencePb + resultFile.getAbsolutePath(), e)); |
| } |
| } |
| |
| private void collectLauncherName(IProfile p) { |
| String launcherName = p.getProperty("eclipse.touchpoint.launcherName"); //$NON-NLS-1$ |
| if (launcherName == null) |
| launcherName = ""; //$NON-NLS-1$ |
| extractedData.put(PROP_LAUNCHER_NAME, launcherName); |
| } |
| |
| private void collectArchitecture(IProfile p) { |
| String environments = p.getProperty(IProfile.PROP_ENVIRONMENTS); |
| if (environments != null) { |
| for (StringTokenizer tokenizer = new StringTokenizer(environments, ","); tokenizer.hasMoreElements();) { //$NON-NLS-1$ |
| String entry = tokenizer.nextToken(); |
| int i = entry.indexOf('='); |
| String key = entry.substring(0, i).trim(); |
| String value = entry.substring(i + 1).trim(); |
| if (!key.equals("osgi.arch")) //$NON-NLS-1$ |
| continue; |
| |
| if ("x86_64".equals(value)) { //$NON-NLS-1$ |
| extractedData.put(PROP_ARCH, "amd64"); //$NON-NLS-1$ |
| return; |
| } |
| if ("x86".equals(value)) { //$NON-NLS-1$ |
| extractedData.put(PROP_ARCH, "x86"); //$NON-NLS-1$ |
| return; |
| } |
| } |
| } |
| extractedData.put(PROP_ARCH, ""); //$NON-NLS-1$ |
| } |
| |
| private void collectDebianDependencies(IProfile p) { |
| String depends = ""; //$NON-NLS-1$ |
| IQueryResult<IInstallableUnit> allIUs = p.available(QueryUtil.ALL_UNITS, new NullProgressMonitor()); |
| Iterator<IInstallableUnit> a = allIUs.iterator(); |
| while (a.hasNext()) { |
| IInstallableUnit iu = a.next(); |
| Collection<ITouchpointData> tpdata = iu.getTouchpointData(); |
| for (ITouchpointData data : tpdata) { |
| Map<String, ITouchpointInstruction> allInstructions = data.getInstructions(); |
| for (ITouchpointInstruction instruction : allInstructions.values()) { |
| StringTokenizer tokenizer = new StringTokenizer(instruction.getBody(), ";"); //$NON-NLS-1$ |
| while (tokenizer.hasMoreTokens()) { |
| Map<String, String> parsedInstructions = parseInstruction(tokenizer.nextToken()); |
| if (parsedInstructions != null && parsedInstructions.get(_ACTION_ID).endsWith(CheckAndPromptNativePackage.ID)) { |
| if ("debian".equals(parsedInstructions.get(ActionConstants.PARM_LINUX_DISTRO))) { //$NON-NLS-1$ |
| depends += formatAsDependsEntry(parsedInstructions.get(ActionConstants.PARM_LINUX_PACKAGE_NAME), parsedInstructions.get(ActionConstants.PARM_LINUX_PACKAGE_VERSION), parsedInstructions.get(ActionConstants.PARM_LINUX_VERSION_COMPARATOR)) + ','; |
| } |
| } |
| } |
| } |
| } |
| } |
| //pre-prend a comma, and remove the last one |
| if (depends.length() > 0) |
| depends = ',' + depends.substring(0, depends.length() - 1); |
| |
| extractedData.put(PROP_DEPENDS, depends); |
| } |
| |
| private String formatAsDependsEntry(String packageId, String version, String versionComparator) { |
| String result = packageId; |
| if (versionComparator == null) |
| versionComparator = DEFAULT_VERSION_CONSTRAINT; |
| if (version != null) { |
| result += '(' + getUserFriendlyComparator(versionComparator) + ' ' + version + ')'; |
| } |
| return result; |
| } |
| |
| private String getUserFriendlyComparator(String comparator) { |
| return installCommandsProperties.getProperty(comparator, ""); //$NON-NLS-1$ |
| } |
| |
| //Code copied from the InstructionParser class |
| private Map<String, String> parseInstruction(String statement) { |
| Map<String, String> instructions = new HashMap<>(); |
| |
| int openBracket = statement.indexOf('('); |
| int closeBracket = statement.lastIndexOf(')'); |
| if (openBracket == -1 || closeBracket == -1 || openBracket > closeBracket) |
| return null; |
| instructions.put(_ACTION_ID, statement.substring(0, openBracket).trim()); |
| |
| String nameValuePairs = statement.substring(openBracket + 1, closeBracket); |
| if (nameValuePairs.length() == 0) |
| return instructions; |
| |
| StringTokenizer tokenizer = new StringTokenizer(nameValuePairs, ","); //$NON-NLS-1$ |
| while (tokenizer.hasMoreTokens()) { |
| String nameValuePair = tokenizer.nextToken(); |
| int colonIndex = nameValuePair.indexOf(":"); //$NON-NLS-1$ |
| if (colonIndex == -1) |
| return null; |
| String name = nameValuePair.substring(0, colonIndex).trim(); |
| String value = nameValuePair.substring(colonIndex + 1).trim(); |
| instructions.put(name, value); |
| } |
| return instructions; |
| } |
| |
| private void initializeServices() throws ProvisionException { |
| ServiceReference<IProvisioningAgentProvider> agentProviderRef = Activator.getContext().getServiceReference(IProvisioningAgentProvider.class); |
| IProvisioningAgentProvider provider = Activator.getContext().getService(agentProviderRef); |
| |
| URI p2DataArea = new File(installation, "p2").toURI(); //$NON-NLS-1$ |
| targetAgent = provider.createAgent(p2DataArea); |
| targetAgent.registerService(IProvisioningAgent.INSTALLER_AGENT, provider.createAgent(null)); |
| |
| Activator.getContext().ungetService(agentProviderRef); |
| if (profileId == null) { |
| if (installation != null) { |
| File configIni = new File(installation, "configuration/config.ini"); //$NON-NLS-1$ |
| Properties ciProps = new Properties(); |
| try (InputStream in = new BufferedInputStream(new FileInputStream(configIni))) { |
| ciProps.load(in); |
| profileId = ciProps.getProperty(PROP_P2_PROFILE); |
| } catch (IOException e) { |
| // Ignore |
| } |
| if (profileId == null) |
| profileId = installation.toString(); |
| } |
| } |
| if (profileId != null) |
| targetAgent.registerService(PROP_P2_PROFILE, profileId); |
| } |
| |
| private static void appendLevelPrefix(PrintStream strm, int level) { |
| for (int idx = 0; idx < level; ++idx) |
| strm.print(' '); |
| } |
| |
| private void deeplyPrint(CoreException ce, PrintStream strm, int level) { |
| appendLevelPrefix(strm, level); |
| if (stackTrace) |
| ce.printStackTrace(strm); |
| deeplyPrint(ce.getStatus(), strm, level); |
| } |
| |
| private void deeplyPrint(IStatus status, PrintStream strm, int level) { |
| appendLevelPrefix(strm, level); |
| String msg = status.getMessage(); |
| strm.println(msg); |
| Throwable cause = status.getException(); |
| if (cause != null) { |
| strm.print("Caused by: "); //$NON-NLS-1$ |
| if (stackTrace || !(msg.equals(cause.getMessage()) || msg.equals(cause.toString()))) |
| deeplyPrint(cause, strm, level); |
| } |
| |
| if (status.isMultiStatus()) { |
| IStatus[] children = status.getChildren(); |
| for (int i = 0; i < children.length; i++) |
| deeplyPrint(children[i], strm, level + 1); |
| } |
| } |
| |
| private void deeplyPrint(Throwable t, PrintStream strm, int level) { |
| if (t instanceof CoreException) |
| deeplyPrint((CoreException) t, strm, level); |
| else { |
| appendLevelPrefix(strm, level); |
| if (stackTrace) |
| t.printStackTrace(strm); |
| else { |
| strm.println(t.toString()); |
| Throwable cause = t.getCause(); |
| if (cause != null) { |
| strm.print("Caused by: "); //$NON-NLS-1$ |
| deeplyPrint(cause, strm, level); |
| } |
| } |
| } |
| } |
| |
| @Override |
| public void stop() { |
| //We don't handle application stopping |
| } |
| |
| } |