Autopsy  4.17.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
IngestJobPipeline.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2014-2019 Basis Technology Corp.
5  * Contact: carrier <at> sleuthkit <dot> org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 package org.sleuthkit.autopsy.ingest;
20 
21 import java.util.ArrayList;
22 import java.util.Collections;
23 import java.util.Date;
24 import java.util.LinkedHashMap;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.concurrent.CopyOnWriteArrayList;
28 import java.util.concurrent.LinkedBlockingQueue;
29 import java.util.concurrent.atomic.AtomicLong;
30 import java.util.logging.Level;
31 import java.util.regex.Matcher;
32 import java.util.regex.Pattern;
33 import java.util.stream.Stream;
34 import javax.swing.JOptionPane;
35 import org.netbeans.api.progress.ProgressHandle;
36 import org.openide.util.Cancellable;
37 import org.openide.util.NbBundle;
38 import org.openide.windows.WindowManager;
45 import org.sleuthkit.datamodel.AbstractFile;
46 import org.sleuthkit.datamodel.Content;
47 import org.sleuthkit.datamodel.IngestJobInfo;
48 import org.sleuthkit.datamodel.IngestJobInfo.IngestJobStatusType;
49 import org.sleuthkit.datamodel.IngestModuleInfo;
50 import org.sleuthkit.datamodel.IngestModuleInfo.IngestModuleType;
51 import org.sleuthkit.datamodel.SleuthkitCase;
52 import org.sleuthkit.datamodel.TskCoreException;
55 
60 final class IngestJobPipeline {
61 
62  private static String AUTOPSY_MODULE_PREFIX = "org.sleuthkit.autopsy";
63 
64  private static final Logger logger = Logger.getLogger(IngestJobPipeline.class.getName());
65 
66  // to match something like: "org.python.proxies.GPX_Parser_Module$GPXParserFileIngestModuleFactory$14"
67  private static final Pattern JYTHON_REGEX = Pattern.compile("org\\.python\\.proxies\\.(.+?)\\$(.+?)(\\$[0-9]*)?$");
68 
75  private final IngestJob parentJob;
76  private static final AtomicLong nextJobId = new AtomicLong(0L);
77  private final long id;
78  private final IngestJobSettings settings;
79  private Content dataSource = null;
80  private final IngestJob.Mode ingestMode;
81  private final List<AbstractFile> files = new ArrayList<>();
82 
86  private static enum Stages {
87 
109  FINALIZATION
110  };
111  private volatile Stages stage = IngestJobPipeline.Stages.INITIALIZATION;
112  private final Object stageCompletionCheckLock = new Object();
113 
122  private final Object dataSourceIngestPipelineLock = new Object();
123  private DataSourceIngestPipeline firstStageDataSourceIngestPipeline;
124  private DataSourceIngestPipeline secondStageDataSourceIngestPipeline;
125  private DataSourceIngestPipeline currentDataSourceIngestPipeline;
126 
134  private final LinkedBlockingQueue<FileIngestPipeline> fileIngestPipelinesQueue = new LinkedBlockingQueue<>();
135  private final List<FileIngestPipeline> fileIngestPipelines = new ArrayList<>();
136 
148  private volatile boolean currentDataSourceIngestModuleCancelled;
149  private final List<String> cancelledDataSourceIngestModules = new CopyOnWriteArrayList<>();
150  private volatile boolean cancelled;
151  private volatile IngestJob.CancellationReason cancellationReason = IngestJob.CancellationReason.NOT_CANCELLED;
152 
157  private static final IngestTasksScheduler taskScheduler = IngestTasksScheduler.getInstance();
158 
163  private final boolean doUI;
164 
169  private final Object dataSourceIngestProgressLock = new Object();
170  private ProgressHandle dataSourceIngestProgress;
171 
176  private final Object fileIngestProgressLock = new Object();
177  private final List<String> filesInProgress = new ArrayList<>();
178  private long estimatedFilesToProcess;
179  private long processedFiles;
180  private ProgressHandle fileIngestProgress;
181  private String currentFileIngestModule = "";
182  private String currentFileIngestTask = "";
183  private final List<IngestModuleInfo> ingestModules = new ArrayList<>();
184  private volatile IngestJobInfo ingestJob;
185 
189  private final long createTime;
190 
200  IngestJobPipeline(IngestJob parentJob, Content dataSource, IngestJobSettings settings) {
201  this(parentJob, dataSource, Collections.emptyList(), settings);
202  }
203 
215  IngestJobPipeline(IngestJob parentJob, Content dataSource, List<AbstractFile> files, IngestJobSettings settings) {
216  this.parentJob = parentJob;
217  this.id = IngestJobPipeline.nextJobId.getAndIncrement();
218  this.dataSource = dataSource;
219  this.files.addAll(files);
220  this.ingestMode = parentJob.getIngestMode();
221  this.settings = settings;
222  this.doUI = RuntimeProperties.runningWithGUI();
223  this.createTime = new Date().getTime();
224  this.stage = Stages.INITIALIZATION;
225  this.createIngestPipelines();
226  }
227 
238  private static void addOrdered(final List<IngestModuleTemplate> dest,
239  final Map<String, IngestModuleTemplate> src, final Map<String, IngestModuleTemplate> jythonSrc) {
240 
241  final List<IngestModuleTemplate> autopsyModules = new ArrayList<>();
242  final List<IngestModuleTemplate> thirdPartyModules = new ArrayList<>();
243 
244  Stream.concat(src.entrySet().stream(), jythonSrc.entrySet().stream()).forEach((templateEntry) -> {
245  if (templateEntry.getKey().startsWith(AUTOPSY_MODULE_PREFIX)) {
246  autopsyModules.add(templateEntry.getValue());
247  } else {
248  thirdPartyModules.add(templateEntry.getValue());
249  }
250  });
251 
252  dest.addAll(autopsyModules);
253  dest.addAll(thirdPartyModules);
254  }
255 
266  private static String getJythonName(String canonicalName) {
267  Matcher m = JYTHON_REGEX.matcher(canonicalName);
268  if (m.find()) {
269  return String.format("%s.%s", m.group(1), m.group(2));
270  } else {
271  return null;
272  }
273  }
274 
284  private static void addModule(Map<String, IngestModuleTemplate> mapping,
285  Map<String, IngestModuleTemplate> jythonMapping, IngestModuleTemplate template) {
286 
287  String className = template.getModuleFactory().getClass().getCanonicalName();
288  String jythonName = getJythonName(className);
289  if (jythonName != null) {
290  jythonMapping.put(jythonName, template);
291  } else {
292  mapping.put(className, template);
293  }
294  }
295 
299  private void createIngestPipelines() {
300  List<IngestModuleTemplate> ingestModuleTemplates = this.settings.getEnabledIngestModuleTemplates();
301 
305  Map<String, IngestModuleTemplate> dataSourceModuleTemplates = new LinkedHashMap<>();
306  Map<String, IngestModuleTemplate> fileModuleTemplates = new LinkedHashMap<>();
307 
308  // mappings for jython modules. These mappings are only used to determine modules in the pipelineconfig.xml.
309  Map<String, IngestModuleTemplate> jythonDataSourceModuleTemplates = new LinkedHashMap<>();
310  Map<String, IngestModuleTemplate> jythonFileModuleTemplates = new LinkedHashMap<>();
311 
312  for (IngestModuleTemplate template : ingestModuleTemplates) {
313  if (template.isDataSourceIngestModuleTemplate()) {
314  addModule(dataSourceModuleTemplates, jythonDataSourceModuleTemplates, template);
315  }
316  if (template.isFileIngestModuleTemplate()) {
317  addModule(fileModuleTemplates, jythonFileModuleTemplates, template);
318  }
319  }
320 
325  IngestPipelinesConfiguration pipelineConfigs = IngestPipelinesConfiguration.getInstance();
326  List<IngestModuleTemplate> firstStageDataSourceModuleTemplates = IngestJobPipeline.getConfiguredIngestModuleTemplates(
327  dataSourceModuleTemplates, jythonDataSourceModuleTemplates, pipelineConfigs.getStageOneDataSourceIngestPipelineConfig());
328 
329  List<IngestModuleTemplate> fileIngestModuleTemplates = IngestJobPipeline.getConfiguredIngestModuleTemplates(
330  fileModuleTemplates, jythonFileModuleTemplates, pipelineConfigs.getFileIngestPipelineConfig());
331 
332  List<IngestModuleTemplate> secondStageDataSourceModuleTemplates = IngestJobPipeline.getConfiguredIngestModuleTemplates(
333  dataSourceModuleTemplates, null, pipelineConfigs.getStageTwoDataSourceIngestPipelineConfig());
334 
340  addOrdered(firstStageDataSourceModuleTemplates, dataSourceModuleTemplates, jythonDataSourceModuleTemplates);
341  addOrdered(fileIngestModuleTemplates, fileModuleTemplates, jythonFileModuleTemplates);
342 
346  this.firstStageDataSourceIngestPipeline = new DataSourceIngestPipeline(this, firstStageDataSourceModuleTemplates);
347  this.secondStageDataSourceIngestPipeline = new DataSourceIngestPipeline(this, secondStageDataSourceModuleTemplates);
348 
352  try {
353  int numberOfFileIngestThreads = IngestManager.getInstance().getNumberOfFileIngestThreads();
354  for (int i = 0; i < numberOfFileIngestThreads; ++i) {
355  FileIngestPipeline pipeline = new FileIngestPipeline(this, fileIngestModuleTemplates);
356  this.fileIngestPipelinesQueue.put(pipeline);
357  this.fileIngestPipelines.add(pipeline);
358  }
359  } catch (InterruptedException ex) {
365  Thread.currentThread().interrupt();
366  }
367  try {
368  SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase();
369  this.addIngestModules(firstStageDataSourceModuleTemplates, IngestModuleType.DATA_SOURCE_LEVEL, skCase);
370  this.addIngestModules(fileIngestModuleTemplates, IngestModuleType.FILE_LEVEL, skCase);
371  this.addIngestModules(secondStageDataSourceModuleTemplates, IngestModuleType.DATA_SOURCE_LEVEL, skCase);
372  } catch (TskCoreException | NoCurrentCaseException ex) {
373  logErrorMessage(Level.WARNING, "Failed to add ingest modules listing to case database", ex);
374  }
375  }
376 
377  private void addIngestModules(List<IngestModuleTemplate> templates, IngestModuleType type, SleuthkitCase skCase) throws TskCoreException {
378  for (IngestModuleTemplate module : templates) {
379  ingestModules.add(skCase.addIngestModule(module.getModuleName(), FactoryClassNameNormalizer.normalize(module.getModuleFactory().getClass().getCanonicalName()), type, module.getModuleFactory().getModuleVersionNumber()));
380  }
381  }
382 
403  private static List<IngestModuleTemplate> getConfiguredIngestModuleTemplates(
404  Map<String, IngestModuleTemplate> ingestModuleTemplates, Map<String, IngestModuleTemplate> jythonIngestModuleTemplates, List<String> pipelineConfig) {
405  List<IngestModuleTemplate> templates = new ArrayList<>();
406  for (String moduleClassName : pipelineConfig) {
407  if (ingestModuleTemplates != null && ingestModuleTemplates.containsKey(moduleClassName)) {
408  templates.add(ingestModuleTemplates.remove(moduleClassName));
409  } else if (jythonIngestModuleTemplates != null && jythonIngestModuleTemplates.containsKey(moduleClassName)) {
410  templates.add(jythonIngestModuleTemplates.remove(moduleClassName));
411  }
412  }
413  return templates;
414  }
415 
421  long getId() {
422  return this.id;
423  }
424 
430  String getExecutionContext() {
431  return this.settings.getExecutionContext();
432  }
433 
439  Content getDataSource() {
440  return this.dataSource;
441  }
442 
449  boolean shouldProcessUnallocatedSpace() {
450  return this.settings.getProcessUnallocatedSpace();
451  }
452 
458  FilesSet getFileIngestFilter() {
459  return this.settings.getFileFilter();
460  }
461 
467  boolean hasIngestPipeline() {
468  return this.hasFirstStageDataSourceIngestPipeline()
469  || this.hasFileIngestPipeline()
470  || this.hasSecondStageDataSourceIngestPipeline();
471  }
472 
479  private boolean hasFirstStageDataSourceIngestPipeline() {
480  return (this.firstStageDataSourceIngestPipeline.isEmpty() == false);
481  }
482 
489  private boolean hasSecondStageDataSourceIngestPipeline() {
490  return (this.secondStageDataSourceIngestPipeline.isEmpty() == false);
491  }
492 
498  private boolean hasFileIngestPipeline() {
499  if (!this.fileIngestPipelines.isEmpty()) {
500  return !this.fileIngestPipelines.get(0).isEmpty();
501  }
502  return false;
503  }
504 
510  List<IngestModuleError> start() {
511  if (dataSource == null) {
512  // TODO - Remove once data source is always present during initialization
513  throw new IllegalStateException("Ingest started before setting data source");
514  }
515  List<IngestModuleError> errors = startUpIngestPipelines();
516  if (errors.isEmpty()) {
517  try {
518  this.ingestJob = Case.getCurrentCaseThrows().getSleuthkitCase().addIngestJob(dataSource, NetworkUtils.getLocalHostName(), ingestModules, new Date(this.createTime), new Date(0), IngestJobStatusType.STARTED, "");
519  } catch (TskCoreException | NoCurrentCaseException ex) {
520  logErrorMessage(Level.WARNING, "Failed to add ingest job info to case database", ex); //NON-NLS
521  }
522 
523  if (this.hasFirstStageDataSourceIngestPipeline() || this.hasFileIngestPipeline()) {
524  if (ingestMode == IngestJob.Mode.BATCH) {
525  logInfoMessage("Starting first stage analysis"); //NON-NLS
526  this.startFirstStage();
527  } else {
528  logInfoMessage("Preparing for first stage analysis"); //NON-NLS
529  this.startFileIngestStreaming();
530  }
531  } else if (this.hasSecondStageDataSourceIngestPipeline()) {
532  logInfoMessage("Starting second stage analysis"); //NON-NLS
533  this.startSecondStage();
534  }
535  }
536  return errors;
537  }
538 
545  private List<IngestModuleError> startUpIngestPipelines() {
546  List<IngestModuleError> errors = new ArrayList<>();
547 
548  /*
549  * Start the data-source-level ingest module pipelines.
550  */
551  errors.addAll(this.firstStageDataSourceIngestPipeline.startUp());
552  errors.addAll(this.secondStageDataSourceIngestPipeline.startUp());
553 
554  /*
555  * If the data-source-level ingest pipelines were successfully started,
556  * start the Start the file-level ingest pipelines (one per file ingest
557  * thread).
558  */
559  if (errors.isEmpty()) {
560  for (FileIngestPipeline pipeline : this.fileIngestPipelinesQueue) {
561  errors.addAll(pipeline.startUp());
562  if (!errors.isEmpty()) {
563  /*
564  * If there are start up errors, the ingest job will not
565  * proceed, so shut down any file ingest pipelines that did
566  * start up.
567  */
568  while (!this.fileIngestPipelinesQueue.isEmpty()) {
569  FileIngestPipeline startedPipeline = this.fileIngestPipelinesQueue.poll();
570  if (startedPipeline.isRunning()) {
571  List<IngestModuleError> shutDownErrors = startedPipeline.shutDown();
572  if (!shutDownErrors.isEmpty()) {
573  /*
574  * The start up errors will ultimately be
575  * reported to the user for possible remedy, but
576  * the shut down errors are logged here.
577  */
578  logIngestModuleErrors(shutDownErrors);
579  }
580  }
581  }
582  break;
583  }
584  }
585  }
586 
587  return errors;
588  }
589 
593  private void startFirstStage() {
594  this.stage = IngestJobPipeline.Stages.FIRST_STAGE_FILES_AND_DATASOURCE;
595 
596  if (this.hasFileIngestPipeline()) {
597  synchronized (this.fileIngestProgressLock) {
598  this.estimatedFilesToProcess = this.dataSource.accept(new GetFilesCountVisitor());
599  }
600  }
601 
602  if (this.doUI) {
606  if (this.hasFirstStageDataSourceIngestPipeline()) {
607  this.startDataSourceIngestProgressBar();
608  }
609  if (this.hasFileIngestPipeline()) {
610  this.startFileIngestProgressBar();
611  }
612  }
613 
618  synchronized (this.dataSourceIngestPipelineLock) {
619  this.currentDataSourceIngestPipeline = this.firstStageDataSourceIngestPipeline;
620  }
621 
625  if (this.hasFirstStageDataSourceIngestPipeline() && this.hasFileIngestPipeline()) {
626  logInfoMessage("Scheduling first stage data source and file level analysis tasks"); //NON-NLS
627  IngestJobPipeline.taskScheduler.scheduleIngestTasks(this);
628  } else if (this.hasFirstStageDataSourceIngestPipeline()) {
629  logInfoMessage("Scheduling first stage data source level analysis tasks"); //NON-NLS
630  IngestJobPipeline.taskScheduler.scheduleDataSourceIngestTask(this);
631  } else {
632  logInfoMessage("Scheduling file level analysis tasks, no first stage data source level analysis configured"); //NON-NLS
633  IngestJobPipeline.taskScheduler.scheduleFileIngestTasks(this, this.files);
634 
643  this.checkForStageCompleted();
644  }
645  }
646 
651  private void startFileIngestStreaming() {
652  synchronized (this.stageCompletionCheckLock) {
653  this.stage = IngestJobPipeline.Stages.FIRST_STAGE_FILES_ONLY;
654  }
655 
656  if (this.hasFileIngestPipeline()) {
657  synchronized (this.fileIngestProgressLock) {
658  this.estimatedFilesToProcess = 0; // Set to indeterminate until the data source is complete
659  }
660  }
661 
662  if (this.doUI) {
663  if (this.hasFileIngestPipeline()) {
664  this.startFileIngestProgressBar();
665  }
666  }
667 
668  logInfoMessage("Waiting for streaming files"); //NON-NLS
669  }
670 
675  private void startDataSourceIngestStreaming() {
676 
677  // Now that the data source is complete, we can get the estimated number of
678  // files and switch to a determinate progress bar.
679  synchronized (fileIngestProgressLock) {
680  if (null != this.fileIngestProgress) {
681  estimatedFilesToProcess = dataSource.accept(new GetFilesCountVisitor());
682  fileIngestProgress.switchToDeterminate((int) estimatedFilesToProcess);
683  }
684  }
685 
686  if (this.doUI) {
690  if (this.hasFirstStageDataSourceIngestPipeline()) {
691  this.startDataSourceIngestProgressBar();
692  }
693  }
694 
699  synchronized (this.dataSourceIngestPipelineLock) {
700  this.currentDataSourceIngestPipeline = this.firstStageDataSourceIngestPipeline;
701  }
702 
703  logInfoMessage("Scheduling first stage data source level analysis tasks"); //NON-NLS
704  synchronized (this.stageCompletionCheckLock) {
705  this.stage = IngestJobPipeline.Stages.FIRST_STAGE_FILES_AND_DATASOURCE;
706  IngestJobPipeline.taskScheduler.scheduleDataSourceIngestTask(this);
707  }
708  }
709 
713  private void startSecondStage() {
714  logInfoMessage("Starting second stage analysis"); //NON-NLS
715  this.stage = IngestJobPipeline.Stages.SECOND_STAGE;
716  if (this.doUI) {
717  this.startDataSourceIngestProgressBar();
718  }
719  synchronized (this.dataSourceIngestPipelineLock) {
720  this.currentDataSourceIngestPipeline = this.secondStageDataSourceIngestPipeline;
721  }
722  logInfoMessage("Scheduling second stage data source level analysis tasks"); //NON-NLS
723  IngestJobPipeline.taskScheduler.scheduleDataSourceIngestTask(this);
724  }
725 
729  private void startDataSourceIngestProgressBar() {
730  if (this.doUI) {
731  synchronized (this.dataSourceIngestProgressLock) {
732  String displayName = NbBundle.getMessage(this.getClass(),
733  "IngestJob.progress.dataSourceIngest.initialDisplayName",
734  this.dataSource.getName());
735  this.dataSourceIngestProgress = ProgressHandle.createHandle(displayName, new Cancellable() {
736  @Override
737  public boolean cancel() {
738  // If this method is called, the user has already pressed
739  // the cancel button on the progress bar and the OK button
740  // of a cancelation confirmation dialog supplied by
741  // NetBeans. What remains to be done is to find out whether
742  // the user wants to cancel only the currently executing
743  // data source ingest module or the entire ingest job.
744  DataSourceIngestCancellationPanel panel = new DataSourceIngestCancellationPanel();
745  String dialogTitle = NbBundle.getMessage(IngestJobPipeline.this.getClass(), "IngestJob.cancellationDialog.title");
746  JOptionPane.showConfirmDialog(WindowManager.getDefault().getMainWindow(), panel, dialogTitle, JOptionPane.OK_OPTION, JOptionPane.PLAIN_MESSAGE);
747  if (panel.cancelAllDataSourceIngestModules()) {
748  IngestJobPipeline.this.cancel(IngestJob.CancellationReason.USER_CANCELLED);
749  } else {
750  IngestJobPipeline.this.cancelCurrentDataSourceIngestModule();
751  }
752  return true;
753  }
754  });
755  this.dataSourceIngestProgress.start();
756  this.dataSourceIngestProgress.switchToIndeterminate();
757  }
758  }
759  }
760 
764  private void startFileIngestProgressBar() {
765  if (this.doUI) {
766  synchronized (this.fileIngestProgressLock) {
767  String displayName = NbBundle.getMessage(this.getClass(),
768  "IngestJob.progress.fileIngest.displayName",
769  this.dataSource.getName());
770  this.fileIngestProgress = ProgressHandle.createHandle(displayName, new Cancellable() {
771  @Override
772  public boolean cancel() {
773  // If this method is called, the user has already pressed
774  // the cancel button on the progress bar and the OK button
775  // of a cancelation confirmation dialog supplied by
776  // NetBeans.
777  IngestJobPipeline.this.cancel(IngestJob.CancellationReason.USER_CANCELLED);
778  return true;
779  }
780  });
781  this.fileIngestProgress.start();
782  this.fileIngestProgress.switchToDeterminate((int) this.estimatedFilesToProcess);
783  }
784  }
785  }
786 
791  private void checkForStageCompleted() {
792  if (ingestMode == IngestJob.Mode.BATCH) {
793  checkForStageCompletedBatch();
794  } else {
795  checkForStageCompletedStreaming();
796  }
797  }
798 
803  private void checkForStageCompletedBatch() {
804  synchronized (this.stageCompletionCheckLock) {
805  if (IngestJobPipeline.taskScheduler.currentTasksAreCompleted(this)) {
806  switch (this.stage) {
807  case FIRST_STAGE_FILES_AND_DATASOURCE:
808  this.finishFirstStage();
809  break;
810  case SECOND_STAGE:
811  this.finish();
812  break;
813  }
814  }
815  }
816  }
817 
822  private void checkForStageCompletedStreaming() {
823  synchronized (this.stageCompletionCheckLock) {
824  if (IngestJobPipeline.taskScheduler.currentTasksAreCompleted(this)) {
825  switch (this.stage) {
826  case FIRST_STAGE_FILES_ONLY:
827  // Nothing to do here - need to wait for the data source
828  break;
829  case FIRST_STAGE_FILES_AND_DATASOURCE:
830  // Finish file and data source ingest, start second stage (if applicable)
831  this.finishFirstStage();
832  break;
833  case SECOND_STAGE:
834  this.finish();
835  break;
836  }
837  }
838  }
839  }
840 
845  private void finishFirstStage() {
846  logInfoMessage("Finished first stage analysis"); //NON-NLS
847 
848  // Shut down the file ingest pipelines. Note that no shut down is
849  // required for the data source ingest pipeline because data source
850  // ingest modules do not have a shutdown() method.
851  List<IngestModuleError> errors = new ArrayList<>();
852  while (!this.fileIngestPipelinesQueue.isEmpty()) {
853  FileIngestPipeline pipeline = fileIngestPipelinesQueue.poll();
854  if (pipeline.isRunning()) {
855  errors.addAll(pipeline.shutDown());
856  }
857  }
858  if (!errors.isEmpty()) {
859  logIngestModuleErrors(errors);
860  }
861 
862  if (this.doUI) {
863  // Finish the first stage data source ingest progress bar, if it hasn't
864  // already been finished.
865  synchronized (this.dataSourceIngestProgressLock) {
866  if (this.dataSourceIngestProgress != null) {
867  this.dataSourceIngestProgress.finish();
868  this.dataSourceIngestProgress = null;
869  }
870  }
871 
872  // Finish the file ingest progress bar, if it hasn't already
873  // been finished.
874  synchronized (this.fileIngestProgressLock) {
875  if (this.fileIngestProgress != null) {
876  this.fileIngestProgress.finish();
877  this.fileIngestProgress = null;
878  }
879  }
880  }
881 
885  if (!this.cancelled && this.hasSecondStageDataSourceIngestPipeline()) {
886  this.startSecondStage();
887  } else {
888  this.finish();
889  }
890  }
891 
895  private void finish() {
896  logInfoMessage("Finished analysis"); //NON-NLS
897  this.stage = IngestJobPipeline.Stages.FINALIZATION;
898 
899  if (this.doUI) {
900  // Finish the second stage data source ingest progress bar, if it hasn't
901  // already been finished.
902  synchronized (this.dataSourceIngestProgressLock) {
903  if (this.dataSourceIngestProgress != null) {
904  this.dataSourceIngestProgress.finish();
905  this.dataSourceIngestProgress = null;
906  }
907  }
908  }
909  if (ingestJob != null) {
910  if (this.cancelled) {
911  try {
912  ingestJob.setIngestJobStatus(IngestJobStatusType.CANCELLED);
913  } catch (TskCoreException ex) {
914  logErrorMessage(Level.WARNING, "Failed to update ingest job status in case database", ex);
915  }
916  } else {
917  try {
918  ingestJob.setIngestJobStatus(IngestJobStatusType.COMPLETED);
919  } catch (TskCoreException ex) {
920  logErrorMessage(Level.WARNING, "Failed to update ingest job status in case database", ex);
921  }
922  }
923  try {
924  this.ingestJob.setEndDateTime(new Date());
925  } catch (TskCoreException ex) {
926  logErrorMessage(Level.WARNING, "Failed to set job end date in case database", ex);
927  }
928  }
929  this.parentJob.ingestJobPipelineFinished(this);
930  }
931 
938  void process(DataSourceIngestTask task) {
939  try {
940  synchronized (this.dataSourceIngestPipelineLock) {
941  if (!this.isCancelled() && !this.currentDataSourceIngestPipeline.isEmpty()) {
942  List<IngestModuleError> errors = new ArrayList<>();
943  errors.addAll(this.currentDataSourceIngestPipeline.process(task));
944  if (!errors.isEmpty()) {
945  logIngestModuleErrors(errors);
946  }
947  }
948  }
949 
950  if (this.doUI) {
955  synchronized (this.dataSourceIngestProgressLock) {
956  if (null != this.dataSourceIngestProgress) {
957  this.dataSourceIngestProgress.finish();
958  this.dataSourceIngestProgress = null;
959  }
960  }
961  }
962 
963  } finally {
964  IngestJobPipeline.taskScheduler.notifyTaskCompleted(task);
965  this.checkForStageCompleted();
966  }
967  }
968 
980  void process(FileIngestTask task) throws InterruptedException {
981  try {
982  if (!this.isCancelled()) {
983  FileIngestPipeline pipeline = this.fileIngestPipelinesQueue.take();
984  if (!pipeline.isEmpty()) {
985  AbstractFile file;
986  try {
987  file = task.getFile();
988  } catch (TskCoreException ex) {
989  // In practice, this task would never have been enqueued since the file
990  // lookup would have failed there.
991  List<IngestModuleError> errors = new ArrayList<>();
992  errors.add(new IngestModuleError("Ingest Job Pipeline", ex));
993  logIngestModuleErrors(errors);
994  this.fileIngestPipelinesQueue.put(pipeline);
995  return;
996  }
997 
998  synchronized (this.fileIngestProgressLock) {
999  ++this.processedFiles;
1000  if (this.doUI) {
1004  if (this.processedFiles <= this.estimatedFilesToProcess) {
1005  this.fileIngestProgress.progress(file.getName(), (int) this.processedFiles);
1006  } else {
1007  this.fileIngestProgress.progress(file.getName(), (int) this.estimatedFilesToProcess);
1008  }
1009  this.filesInProgress.add(file.getName());
1010  }
1011  }
1012 
1016  List<IngestModuleError> errors = new ArrayList<>();
1017  errors.addAll(pipeline.process(task));
1018  if (!errors.isEmpty()) {
1019  logIngestModuleErrors(errors);
1020  }
1021 
1022  if (this.doUI && !this.cancelled) {
1023  synchronized (this.fileIngestProgressLock) {
1028  this.filesInProgress.remove(file.getName());
1029  if (this.filesInProgress.size() > 0) {
1030  this.fileIngestProgress.progress(this.filesInProgress.get(0));
1031  } else {
1032  this.fileIngestProgress.progress("");
1033  }
1034  }
1035  }
1036  }
1037  this.fileIngestPipelinesQueue.put(pipeline);
1038  }
1039  } finally {
1040  IngestJobPipeline.taskScheduler.notifyTaskCompleted(task);
1041  this.checkForStageCompleted();
1042  }
1043  }
1044 
1051  void addStreamingIngestFiles(List<Long> fileObjIds) {
1052 
1053  // Return if there are no file ingest modules enabled.
1054  if (!hasFileIngestPipeline()) {
1055  return;
1056  }
1057 
1058  if (stage.equals(Stages.FIRST_STAGE_FILES_ONLY)) {
1059  IngestJobPipeline.taskScheduler.scheduleStreamedFileIngestTasks(this, fileObjIds);
1060  } else {
1061  logErrorMessage(Level.SEVERE, "Adding streaming files to job during stage " + stage.toString() + " not supported");
1062  }
1063  }
1064 
1069  void processStreamingIngestDataSource() {
1070  startDataSourceIngestStreaming();
1071  checkForStageCompleted();
1072  }
1073 
1081  void addFiles(List<AbstractFile> files) {
1082  if (stage.equals(Stages.FIRST_STAGE_FILES_ONLY)
1083  || stage.equals(Stages.FIRST_STAGE_FILES_AND_DATASOURCE)) {
1084  IngestJobPipeline.taskScheduler.fastTrackFileIngestTasks(this, files);
1085  } else {
1086  logErrorMessage(Level.SEVERE, "Adding files to job during second stage analysis not supported");
1087  }
1088 
1095  this.checkForStageCompleted();
1096  }
1097 
1104  void updateDataSourceIngestProgressBarDisplayName(String displayName) {
1105  if (this.doUI && !this.cancelled) {
1106  synchronized (this.dataSourceIngestProgressLock) {
1107  this.dataSourceIngestProgress.setDisplayName(displayName);
1108  }
1109  }
1110  }
1111 
1120  void switchDataSourceIngestProgressBarToDeterminate(int workUnits) {
1121  if (this.doUI && !this.cancelled) {
1122  synchronized (this.dataSourceIngestProgressLock) {
1123  if (null != this.dataSourceIngestProgress) {
1124  this.dataSourceIngestProgress.switchToDeterminate(workUnits);
1125  }
1126  }
1127  }
1128  }
1129 
1135  void switchDataSourceIngestProgressBarToIndeterminate() {
1136  if (this.doUI && !this.cancelled) {
1137  synchronized (this.dataSourceIngestProgressLock) {
1138  if (null != this.dataSourceIngestProgress) {
1139  this.dataSourceIngestProgress.switchToIndeterminate();
1140  }
1141  }
1142  }
1143  }
1144 
1151  void advanceDataSourceIngestProgressBar(int workUnits) {
1152  if (this.doUI && !this.cancelled) {
1153  synchronized (this.dataSourceIngestProgressLock) {
1154  if (null != this.dataSourceIngestProgress) {
1155  this.dataSourceIngestProgress.progress("", workUnits);
1156  }
1157  }
1158  }
1159  }
1160 
1167  void advanceDataSourceIngestProgressBar(String currentTask) {
1168  if (this.doUI && !this.cancelled) {
1169  synchronized (this.dataSourceIngestProgressLock) {
1170  if (null != this.dataSourceIngestProgress) {
1171  this.dataSourceIngestProgress.progress(currentTask);
1172  }
1173  }
1174  }
1175  }
1176 
1185  void advanceDataSourceIngestProgressBar(String currentTask, int workUnits) {
1186  if (this.doUI && !this.cancelled) {
1187  synchronized (this.fileIngestProgressLock) {
1188  this.dataSourceIngestProgress.progress(currentTask, workUnits);
1189  }
1190  }
1191  }
1192 
1200  boolean currentDataSourceIngestModuleIsCancelled() {
1201  return this.currentDataSourceIngestModuleCancelled;
1202  }
1203 
1210  void currentDataSourceIngestModuleCancellationCompleted(String moduleDisplayName) {
1211  this.currentDataSourceIngestModuleCancelled = false;
1212  this.cancelledDataSourceIngestModules.add(moduleDisplayName);
1213 
1214  if (this.doUI) {
1222  synchronized (this.dataSourceIngestProgressLock) {
1223  this.dataSourceIngestProgress.finish();
1224  this.dataSourceIngestProgress = null;
1225  this.startDataSourceIngestProgressBar();
1226  }
1227  }
1228  }
1229 
1235  DataSourceIngestPipeline.PipelineModule getCurrentDataSourceIngestModule() {
1236  if (null != this.currentDataSourceIngestPipeline) {
1237  return this.currentDataSourceIngestPipeline.getCurrentlyRunningModule();
1238  } else {
1239  return null;
1240  }
1241  }
1242 
1247  void cancelCurrentDataSourceIngestModule() {
1248  this.currentDataSourceIngestModuleCancelled = true;
1249  }
1250 
1257  void cancel(IngestJob.CancellationReason reason) {
1258  this.cancelled = true;
1259  this.cancellationReason = reason;
1260  IngestJobPipeline.taskScheduler.cancelPendingTasksForIngestJob(this);
1261 
1262  if (this.doUI) {
1263  synchronized (this.dataSourceIngestProgressLock) {
1264  if (null != dataSourceIngestProgress) {
1265  dataSourceIngestProgress.setDisplayName(NbBundle.getMessage(this.getClass(), "IngestJob.progress.dataSourceIngest.initialDisplayName", this.dataSource.getName()));
1266  dataSourceIngestProgress.progress(NbBundle.getMessage(this.getClass(), "IngestJob.progress.cancelling"));
1267  }
1268  }
1269 
1270  synchronized (this.fileIngestProgressLock) {
1271  if (null != this.fileIngestProgress) {
1272  this.fileIngestProgress.setDisplayName(NbBundle.getMessage(this.getClass(), "IngestJob.progress.fileIngest.displayName", this.dataSource.getName()));
1273  this.fileIngestProgress.progress(NbBundle.getMessage(this.getClass(), "IngestJob.progress.cancelling"));
1274  }
1275  }
1276  }
1277 
1278  // If a data source had no tasks in progress it may now be complete.
1279  checkForStageCompleted();
1280  }
1281 
1289  void setCurrentFileIngestModule(String moduleName, String taskName) {
1290  this.currentFileIngestModule = moduleName;
1291  this.currentFileIngestTask = taskName;
1292  }
1293 
1300  boolean isCancelled() {
1301  return this.cancelled;
1302  }
1303 
1309  IngestJob.CancellationReason getCancellationReason() {
1310  return this.cancellationReason;
1311  }
1312 
1319  private void logInfoMessage(String message) {
1320  logger.log(Level.INFO, String.format("%s (data source = %s, objId = %d, pipeline id = %d, ingest job id = %d)", message, this.dataSource.getName(), this.dataSource.getId(), id, ingestJob.getIngestJobId())); //NON-NLS
1321  }
1322 
1331  private void logErrorMessage(Level level, String message, Throwable throwable) {
1332  logger.log(level, String.format("%s (data source = %s, objId = %d, pipeline id = %d, ingest job id = %d)", message, this.dataSource.getName(), this.dataSource.getId(), id, ingestJob.getIngestJobId()), throwable); //NON-NLS
1333  }
1334 
1342  private void logErrorMessage(Level level, String message) {
1343  logger.log(level, String.format("%s (data source = %s, objId = %d, pipeline id = %d, ingest job id %d)", message, this.dataSource.getName(), this.dataSource.getId(), id, ingestJob.getIngestJobId())); //NON-NLS
1344  }
1345 
1351  private void logIngestModuleErrors(List<IngestModuleError> errors) {
1352  for (IngestModuleError error : errors) {
1353  logErrorMessage(Level.SEVERE, String.format("%s experienced an error during analysis", error.getModuleDisplayName()), error.getThrowable()); //NON-NLS
1354  }
1355  }
1356 
1362  Snapshot getSnapshot(boolean getIngestTasksSnapshot) {
1368  boolean fileIngestRunning = false;
1369  Date fileIngestStartTime = null;
1370 
1371  for (FileIngestPipeline pipeline : this.fileIngestPipelines) {
1372  if (pipeline.isRunning()) {
1373  fileIngestRunning = true;
1374  }
1375  Date pipelineStartTime = pipeline.getStartTime();
1376  if (null != pipelineStartTime && (null == fileIngestStartTime || pipelineStartTime.before(fileIngestStartTime))) {
1377  fileIngestStartTime = pipelineStartTime;
1378  }
1379  }
1380 
1381  long processedFilesCount = 0;
1382  long estimatedFilesToProcessCount = 0;
1383  long snapShotTime = new Date().getTime();
1384  IngestJobTasksSnapshot tasksSnapshot = null;
1385 
1386  if (getIngestTasksSnapshot) {
1387  synchronized (fileIngestProgressLock) {
1388  processedFilesCount = this.processedFiles;
1389  estimatedFilesToProcessCount = this.estimatedFilesToProcess;
1390  snapShotTime = new Date().getTime();
1391  }
1392  tasksSnapshot = IngestJobPipeline.taskScheduler.getTasksSnapshotForJob(id);
1393 
1394  }
1395 
1396  return new Snapshot(this.dataSource.getName(), id, createTime,
1397  getCurrentDataSourceIngestModule(), fileIngestRunning, fileIngestStartTime,
1398  cancelled, cancellationReason, cancelledDataSourceIngestModules,
1399  processedFilesCount, estimatedFilesToProcessCount, snapShotTime, tasksSnapshot);
1400  }
1401 }
List< IngestModuleTemplate > getEnabledIngestModuleTemplates()

Copyright © 2012-2021 Basis Technology. Generated on: Tue Jan 19 2021
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.