19 package org.sleuthkit.autopsy.commandlineingest;
21 import com.google.gson.GsonBuilder;
22 import java.beans.PropertyChangeEvent;
23 import java.beans.PropertyChangeListener;
24 import java.nio.file.Paths;
25 import java.util.ArrayList;
26 import java.util.Arrays;
27 import java.util.List;
28 import java.util.UUID;
29 import java.util.Collection;
30 import java.util.EnumSet;
31 import java.util.Iterator;
34 import java.util.logging.Level;
35 import org.netbeans.spi.sendopts.OptionProcessor;
36 import org.openide.LifecycleManager;
37 import org.openide.util.Lookup;
79 static final int CL_SUCCESS = 0;
80 static final int CL_RUN_FAILURE = -1;
81 static final int CL_PROCESS_FAILURE = -2;
94 void stop(
int errorCode) {
97 Case.closeCurrentCase();
98 }
catch (CaseActionException ex) {
99 LOGGER.log(Level.WARNING,
"Unable to close the case while shutting down command line ingest manager", ex);
103 if (errorCode == CL_SUCCESS) {
104 LifecycleManager.getDefault().exit();
106 LifecycleManager.getDefault().exit(errorCode);
115 ingestLock =
new Object();
118 LOGGER.log(Level.INFO,
"Set running with desktop GUI runtime property to false");
120 LOGGER.log(Level.SEVERE,
"Failed to set running with desktop GUI runtime property to false", ex);
130 LOGGER.log(Level.INFO,
"Job processing task started");
131 int errorCode = CL_SUCCESS;
135 LOGGER.log(Level.INFO,
"Autopsy is running from command line");
136 List<CommandLineCommand> commands = null;
139 Collection<? extends OptionProcessor> optionProcessors = Lookup.getDefault().lookupAll(OptionProcessor.class);
140 Iterator<? extends OptionProcessor> optionsIterator = optionProcessors.iterator();
141 while (optionsIterator.hasNext()) {
143 OptionProcessor processor = optionsIterator.next();
146 commands = ((CommandLineOptionProcessor) processor).getCommands();
150 if (commands == null || commands.isEmpty()) {
151 LOGGER.log(Level.SEVERE,
"No command line commands specified");
152 System.out.println(
"No command line commands specified");
153 errorCode = CL_RUN_FAILURE;
158 for (CommandLineCommand command : commands) {
159 CommandLineCommand.CommandType type = command.getType();
163 LOGGER.log(Level.INFO,
"Processing 'Create Case' command");
164 System.out.println(
"Processing 'Create Case' command");
165 Map<String, String> inputs = command.getInputs();
166 String baseCaseName = inputs.get(CommandLineCommand.InputType.CASE_NAME.name());
167 String rootOutputDirectory = inputs.get(CommandLineCommand.InputType.CASES_BASE_DIR_PATH.name());
169 String caseTypeString = inputs.get(CommandLineCommand.InputType.CASE_TYPE.name());
173 caseForJob = createCase(baseCaseName, rootOutputDirectory, caseType);
175 String outputDirPath = getOutputDirPath(caseForJob);
176 OutputGenerator.saveCreateCaseOutput(caseForJob, outputDirPath, baseCaseName);
178 String baseCaseName = command.getInputs().get(CommandLineCommand.InputType.CASE_NAME.name());
179 LOGGER.log(Level.SEVERE,
"Error creating or opening case " + baseCaseName, ex);
180 System.out.println(
"Error creating or opening case " + baseCaseName);
182 errorCode = CL_RUN_FAILURE;
186 case ADD_DATA_SOURCE:
188 LOGGER.log(Level.INFO,
"Processing 'Add Data Source' command");
189 System.out.println(
"Processing 'Add Data Source' command");
190 Map<String, String> inputs = command.getInputs();
193 if (caseForJob == null) {
195 String baseCaseName = inputs.get(CommandLineCommand.InputType.CASE_NAME.name());
196 String rootOutputDirectory = inputs.get(CommandLineCommand.InputType.CASES_BASE_DIR_PATH.name());
197 caseForJob = openExistingCase(baseCaseName, rootOutputDirectory);
200 String dataSourcePath = inputs.get(CommandLineCommand.InputType.DATA_SOURCE_PATH.name());
201 String password = inputs.get(CommandLineCommand.InputType.BITLOCKER_KEY.name());
202 dataSource =
new AutoIngestDataSource(UUID.randomUUID().toString(), Paths.get(dataSourcePath), password);
205 String outputDirPath = getOutputDirPath(caseForJob);
206 OutputGenerator.saveAddDataSourceOutput(caseForJob, dataSource, outputDirPath);
208 String dataSourcePath = command.getInputs().get(CommandLineCommand.InputType.DATA_SOURCE_PATH.name());
209 LOGGER.log(Level.SEVERE,
"Error adding data source " + dataSourcePath, ex);
210 System.out.println(
"Error adding data source " + dataSourcePath);
212 errorCode = CL_RUN_FAILURE;
218 LOGGER.log(Level.INFO,
"Processing 'Run Ingest' command");
219 System.out.println(
"Processing 'Run Ingest' command");
220 Map<String, String> inputs = command.getInputs();
223 if (caseForJob == null) {
225 String baseCaseName = inputs.get(CommandLineCommand.InputType.CASE_NAME.name());
226 String rootOutputDirectory = inputs.get(CommandLineCommand.InputType.CASES_BASE_DIR_PATH.name());
227 caseForJob = openExistingCase(baseCaseName, rootOutputDirectory);
231 if (dataSource == null) {
233 String dataSourceId = inputs.get(CommandLineCommand.InputType.DATA_SOURCE_ID.name());
234 Long dataSourceObjId = Long.valueOf(dataSourceId);
241 LOGGER.log(Level.SEVERE,
"Exception while trying to find data source with object ID " + dataSourceId, ex);
242 System.out.println(
"Exception while trying to find data source with object ID " + dataSourceId);
244 errorCode = CL_RUN_FAILURE;
248 if (content == null) {
249 LOGGER.log(Level.SEVERE,
"Unable to find data source with object ID {0}", dataSourceId);
250 System.out.println(
"Unable to find data source with object ID " + dataSourceId);
257 List<Content> contentList = Arrays.asList(
new Content[]{content});
258 List<String> errorList =
new ArrayList<>();
263 String ingestProfile = inputs.get(CommandLineCommand.InputType.INGEST_PROFILE_NAME.name());
264 analyze(dataSource, ingestProfile);
266 String dataSourcePath = command.getInputs().get(CommandLineCommand.InputType.DATA_SOURCE_PATH.name());
267 LOGGER.log(Level.SEVERE,
"Error running ingest on data source " + dataSourcePath, ex);
268 System.out.println(
"Error running ingest on data source " + dataSourcePath);
270 errorCode = CL_RUN_FAILURE;
275 case LIST_ALL_DATA_SOURCES:
277 LOGGER.log(Level.INFO,
"Processing 'List All Data Sources' command");
278 System.out.println(
"Processing 'List All Data Sources' command");
279 Map<String, String> inputs = command.getInputs();
282 if (caseForJob == null) {
284 String baseCaseName = inputs.get(CommandLineCommand.InputType.CASE_NAME.name());
285 String rootOutputDirectory = inputs.get(CommandLineCommand.InputType.CASES_BASE_DIR_PATH.name());
286 caseForJob = openExistingCase(baseCaseName, rootOutputDirectory);
289 String outputDirPath = getOutputDirPath(caseForJob);
290 OutputGenerator.listAllDataSources(caseForJob, outputDirPath);
292 String baseCaseName = command.getInputs().get(CommandLineCommand.InputType.CASE_NAME.name());
293 String rootOutputDirectory = command.getInputs().get(CommandLineCommand.InputType.CASES_BASE_DIR_PATH.name());
294 String msg =
"Error opening case " + baseCaseName +
" in directory: " + rootOutputDirectory;
295 LOGGER.log(Level.SEVERE, msg, ex);
296 System.out.println(msg);
297 errorCode = CL_RUN_FAILURE;
303 case GENERATE_REPORTS:
305 LOGGER.log(Level.INFO,
"Processing 'Generate Reports' command");
306 System.out.println(
"Processing 'Generate Reports' command");
307 Map<String, String> inputs = command.getInputs();
310 if (caseForJob == null) {
312 String baseCaseName = inputs.get(CommandLineCommand.InputType.CASE_NAME.name());
313 String rootOutputDirectory = inputs.get(CommandLineCommand.InputType.CASES_BASE_DIR_PATH.name());
314 caseForJob = openExistingCase(baseCaseName, rootOutputDirectory);
317 String reportName = inputs.get(CommandLineCommand.InputType.REPORT_PROFILE_NAME.name());
318 if (reportName == null) {
327 String baseCaseName = command.getInputs().get(CommandLineCommand.InputType.CASE_NAME.name());
328 String rootOutputDirectory = command.getInputs().get(CommandLineCommand.InputType.CASES_BASE_DIR_PATH.name());
329 String msg =
"Error opening case " + baseCaseName +
" in directory: " + rootOutputDirectory;
330 LOGGER.log(Level.SEVERE, msg, ex);
331 System.out.println(msg);
332 errorCode = CL_RUN_FAILURE;
335 }
catch (Exception ex) {
336 String msg =
"An exception occurred while generating report: " + ex.getMessage();
337 LOGGER.log(Level.WARNING, msg, ex);
338 System.out.println(msg);
339 errorCode = CL_RUN_FAILURE;
344 case LIST_ALL_INGEST_PROFILES:
346 GsonBuilder gb =
new GsonBuilder();
347 System.out.println(
"Listing ingest profiles");
349 String jsonText = gb.create().toJson(profile);
350 System.out.println(jsonText);
352 System.out.println(
"Ingest profile list complete");
358 }
catch (Throwable ex) {
366 LOGGER.log(Level.SEVERE,
"Unexpected error", ex);
367 System.out.println(
"Unexpected error. Exiting...");
368 errorCode = CL_RUN_FAILURE;
373 LOGGER.log(Level.WARNING,
"Exception while closing case", ex);
374 System.out.println(
"Exception while closing case");
379 LOGGER.log(Level.INFO,
"Job processing task finished");
380 System.out.println(
"Job processing task finished");
405 LOGGER.log(Level.INFO,
"Adding data source {0} ", dataSource.
getPath().toString());
408 List<AutoIngestDataSourceProcessor> validDataSourceProcessors;
412 LOGGER.log(Level.SEVERE,
"Exception while determining best data source processor for {0}", dataSource.
getPath());
418 if (validDataSourceProcessors.isEmpty()) {
420 LOGGER.log(Level.SEVERE,
"Unsupported data source {0}", dataSource.
getPath());
428 UUID taskId = UUID.randomUUID();
432 LOGGER.log(Level.INFO,
"Identified data source type for {0} as {1}",
new Object[]{dataSource.getPath(), selectedProcessor.getDataSourceType()});
449 LOGGER.log(Level.SEVERE,
"All data source processors failed to process {0}", dataSource.
getPath());
464 if (null != resultCode) {
465 switch (resultCode) {
467 LOGGER.log(Level.INFO,
"Added data source to case");
469 LOGGER.log(Level.SEVERE,
"Data source failed to produce content");
473 case NONCRITICAL_ERRORS:
475 LOGGER.log(Level.WARNING,
"Non-critical error running data source processor for {0}: {1}",
new Object[]{dataSource.getPath(), errorMessage});
477 LOGGER.log(Level.INFO,
"Added data source to case");
479 LOGGER.log(Level.SEVERE,
"Data source failed to produce content");
483 case CRITICAL_ERRORS:
485 LOGGER.log(Level.SEVERE,
"Critical error running data source processor for {0}: {1}",
new Object[]{dataSource.getPath(), errorMessage});
487 LOGGER.log(Level.SEVERE,
"Failed to add data source to case");
491 LOGGER.log(Level.WARNING,
"No result code for data source processor for {0}", dataSource.
getPath());
514 LOGGER.log(Level.INFO,
"Starting ingest modules analysis for {0} ", dataSource.
getPath());
519 if (!ingestProfileName.isEmpty()) {
521 if (selectedProfile == null) {
523 LOGGER.log(Level.SEVERE,
"Unable to find ingest profile: {0}. Ingest cancelled!", ingestProfileName);
524 System.out.println(
"Unable to find ingest profile: " + ingestProfileName +
". Ingest cancelled!");
525 throw new AnalysisStartupException(
"Unable to find ingest profile: " + ingestProfileName +
". Ingest cancelled!");
530 if (selectedFileSet == null) {
532 LOGGER.log(Level.SEVERE,
"Unable to find file filter {0} for ingest profile: {1}. Ingest cancelled!",
new Object[]{selectedProfile.getFileIngestFilter(), ingestProfileName});
533 System.out.println(
"Unable to find file filter " + selectedProfile.getFileIngestFilter() +
" for ingest profile: " + ingestProfileName +
". Ingest cancelled!");
534 throw new AnalysisStartupException(
"Unable to find file filter " + selectedProfile.getFileIngestFilter() +
" for ingest profile: " + ingestProfileName +
". Ingest cancelled!");
543 if (selectedProfile == null || selectedFileSet == null) {
552 List<String> settingsWarnings = ingestJobSettings.
getWarnings();
553 if (settingsWarnings.isEmpty()) {
556 if (null != ingestJob) {
566 ingestLock.wait(60000);
569 LOGGER.log(Level.INFO,
"Finished ingest modules analysis for {0} ", dataSource.
getPath());
572 if (!snapshot.isCancelled()) {
574 if (!cancelledModules.isEmpty()) {
575 LOGGER.log(Level.WARNING, String.format(
"Ingest module(s) cancelled for %s", dataSource.
getPath()));
576 for (String module : snapshot.getCancelledDataSourceIngestModules()) {
577 LOGGER.log(Level.WARNING, String.format(
"%s ingest module cancelled for %s", module, dataSource.
getPath()));
580 LOGGER.log(Level.INFO,
"Analysis of data source completed");
582 LOGGER.log(Level.WARNING,
"Analysis of data source cancelled");
585 throw new AnalysisStartupException(String.format(
"Analysis cancelled due to %s for %s", cancellationReason.getDisplayName(), dataSource.
getPath()));
590 LOGGER.log(Level.SEVERE, String.format(
"%s ingest module startup error for %s", error.getModuleDisplayName(), dataSource.
getPath()), error.getThrowable());
592 LOGGER.log(Level.SEVERE,
"Failed to analyze data source due to ingest job startup error");
593 throw new AnalysisStartupException(String.format(
"Error(s) during ingest module startup for %s", dataSource.
getPath()));
595 LOGGER.log(Level.SEVERE, String.format(
"Ingest manager ingest job start error for %s", dataSource.
getPath()), ingestJobStartResult.
getStartupException());
596 throw new AnalysisStartupException(
"Ingest manager error starting job", ingestJobStartResult.
getStartupException());
599 for (String warning : settingsWarnings) {
600 LOGGER.log(Level.SEVERE,
"Ingest job settings error for {0}: {1}",
new Object[]{dataSource.getPath(), warning});
602 LOGGER.log(Level.SEVERE,
"Failed to analyze data source due to settings errors");
603 throw new AnalysisStartupException(
"Error(s) in ingest job settings");
624 if (profile.toString().equalsIgnoreCase(ingestProfileName)) {
626 selectedProfile = profile;
630 return selectedProfile;
646 fileIngestFilters.put(fSet.getName(), fSet);
648 return fileIngestFilters.get(filterName);
650 LOGGER.log(Level.SEVERE,
"Failed to get file ingest filter: " + filterName, ex);
676 String eventType =
event.getPropertyName();
726 private final class AnalysisStartupException
extends Exception {
735 super(message, cause);
synchronized List< String > getDataSourceProcessorErrorMessages()
static List< AutoIngestDataSourceProcessor > getOrderedListOfDataSourceProcessors(Path dataSourcePath)
void logDataSourceProcessorResult(AutoIngestDataSource dataSource)
List< String > getCancelledDataSourceIngestModules()
static synchronized IngestManager getInstance()
static List< FilesSet > getStandardFileIngestFilters()
static void closeCurrentCase()
IngestProfiles.IngestProfile getSelectedProfile(String ingestProfileName)
IngestJobStartResult beginIngestJob(Collection< Content > dataSources, IngestJobSettings settings)
AutoIngestDataSource dataSource
Content getContentById(long id)
AnalysisStartupException(String message)
synchronized DataSourceProcessorResult getResultDataSourceProcessorResultCode()
CommandLineIngestManager()
boolean isIngestRunning()
static synchronized FilesSetsManager getInstance()
void runDataSourceProcessor(Case caseForJob, AutoIngestDataSource dataSource)
static final Set< IngestManager.IngestJobEvent > INGEST_JOB_EVENTS_OF_INTEREST
void propertyChange(PropertyChangeEvent event)
String getIngestProfilePrefix()
void removeIngestJobEventListener(final PropertyChangeListener listener)
Map< String, FilesSet > getCustomFileIngestFilters()
List< IngestModuleError > getModuleErrors()
static final Logger LOGGER
static final long serialVersionUID
void addIngestJobEventListener(final PropertyChangeListener listener)
void setFileFilter(FilesSet fileIngestFilter)
ProgressSnapshot getSnapshot()
static IngestProfilePaths getInstance()
void setProgressText(final String text)
static synchronized List< IngestProfile > getIngestProfiles()
AnalysisStartupException(String message, Throwable cause)
SleuthkitCase getSleuthkitCase()
FilesSet getSelectedFilter(String filterName)
void analyze(AutoIngestDataSource dataSource, String ingestProfileName)
static synchronized void setRunningWithGUI(boolean runningWithGUI)
List< String > getWarnings()
synchronized List< Content > getContent()
synchronized void setDataSourceProcessorOutput(DataSourceProcessorResult result, List< String > errorMessages, List< Content > content)
synchronized static Logger getLogger(String name)
static Case getCurrentCaseThrows()
void setIndeterminate(final boolean indeterminate)
static String getCommandLineModeIngestModuleContextString()
void setProgress(final int progress)
void notifyAddingDataSource(UUID eventId)
static String getDefaultReportingConfigName()
IngestManager.IngestManagerException getStartupException()