dissect.target 3.17.dev11__py3-none-any.whl → 3.17.dev13__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dissect/target/plugins/os/unix/linux/services.py +1 -0
 - dissect/target/tools/dump/run.py +23 -28
 - dissect/target/tools/dump/state.py +11 -8
 - dissect/target/tools/dump/utils.py +5 -4
 - dissect/target/tools/query.py +2 -13
 - dissect/target/tools/utils.py +23 -0
 - {dissect.target-3.17.dev11.dist-info → dissect.target-3.17.dev13.dist-info}/METADATA +1 -1
 - {dissect.target-3.17.dev11.dist-info → dissect.target-3.17.dev13.dist-info}/RECORD +13 -13
 - {dissect.target-3.17.dev11.dist-info → dissect.target-3.17.dev13.dist-info}/COPYRIGHT +0 -0
 - {dissect.target-3.17.dev11.dist-info → dissect.target-3.17.dev13.dist-info}/LICENSE +0 -0
 - {dissect.target-3.17.dev11.dist-info → dissect.target-3.17.dev13.dist-info}/WHEEL +0 -0
 - {dissect.target-3.17.dev11.dist-info → dissect.target-3.17.dev13.dist-info}/entry_points.txt +0 -0
 - {dissect.target-3.17.dev11.dist-info → dissect.target-3.17.dev13.dist-info}/top_level.txt +0 -0
 
    
        dissect/target/tools/dump/run.py
    CHANGED
    
    | 
         @@ -7,7 +7,7 @@ import sys 
     | 
|
| 
       7 
7 
     | 
    
         
             
            from collections import deque
         
     | 
| 
       8 
8 
     | 
    
         
             
            from dataclasses import dataclass
         
     | 
| 
       9 
9 
     | 
    
         
             
            from pathlib import Path
         
     | 
| 
       10 
     | 
    
         
            -
            from typing import Any,  
     | 
| 
      
 10 
     | 
    
         
            +
            from typing import Any, Iterable, Iterator, Optional
         
     | 
| 
       11 
11 
     | 
    
         | 
| 
       12 
12 
     | 
    
         
             
            import structlog
         
     | 
| 
       13 
13 
     | 
    
         
             
            from flow.record import Record
         
     | 
| 
         @@ -25,10 +25,12 @@ from dissect.target.tools.dump.utils import ( 
     | 
|
| 
       25 
25 
     | 
    
         
             
                Compression,
         
     | 
| 
       26 
26 
     | 
    
         
             
                Serialization,
         
     | 
| 
       27 
27 
     | 
    
         
             
                cached_sink_writers,
         
     | 
| 
       28 
     | 
    
         
            -
                get_nested_attr,
         
     | 
| 
       29 
28 
     | 
    
         
             
            )
         
     | 
| 
       30 
29 
     | 
    
         
             
            from dissect.target.tools.utils import (
         
     | 
| 
      
 30 
     | 
    
         
            +
                PluginFunction,
         
     | 
| 
       31 
31 
     | 
    
         
             
                configure_generic_arguments,
         
     | 
| 
      
 32 
     | 
    
         
            +
                execute_function_on_target,
         
     | 
| 
      
 33 
     | 
    
         
            +
                find_and_filter_plugins,
         
     | 
| 
       32 
34 
     | 
    
         
             
                process_generic_arguments,
         
     | 
| 
       33 
35 
     | 
    
         
             
            )
         
     | 
| 
       34 
36 
     | 
    
         | 
| 
         @@ -44,13 +46,13 @@ class RecordStreamElement: 
     | 
|
| 
       44 
46 
     | 
    
         
             
                sink_path: Optional[Path] = None
         
     | 
| 
       45 
47 
     | 
    
         | 
| 
       46 
48 
     | 
    
         | 
| 
       47 
     | 
    
         
            -
            def get_targets(targets:  
     | 
| 
      
 49 
     | 
    
         
            +
            def get_targets(targets: list[str]) -> Iterator[Target]:
         
     | 
| 
       48 
50 
     | 
    
         
             
                """Return a generator with `Target` objects for provided paths"""
         
     | 
| 
       49 
51 
     | 
    
         
             
                for target in Target.open_all(targets):
         
     | 
| 
       50 
52 
     | 
    
         
             
                    yield target
         
     | 
| 
       51 
53 
     | 
    
         | 
| 
       52 
54 
     | 
    
         | 
| 
       53 
     | 
    
         
            -
            def execute_function(target: Target, function:  
     | 
| 
      
 55 
     | 
    
         
            +
            def execute_function(target: Target, function: PluginFunction) -> TargetRecordDescriptor:
         
     | 
| 
       54 
56 
     | 
    
         
             
                """
         
     | 
| 
       55 
57 
     | 
    
         
             
                Execute function `function` on provided target `target` and return a generator
         
     | 
| 
       56 
58 
     | 
    
         
             
                with the records produced.
         
     | 
| 
         @@ -62,7 +64,7 @@ def execute_function(target: Target, function: str) -> Generator[TargetRecordDes 
     | 
|
| 
       62 
64 
     | 
    
         
             
                local_log.debug("Function execution")
         
     | 
| 
       63 
65 
     | 
    
         | 
| 
       64 
66 
     | 
    
         
             
                try:
         
     | 
| 
       65 
     | 
    
         
            -
                    target_attr =  
     | 
| 
      
 67 
     | 
    
         
            +
                    output_type, target_attr, _ = execute_function_on_target(target, function)
         
     | 
| 
       66 
68 
     | 
    
         
             
                except UnsupportedPluginError:
         
     | 
| 
       67 
69 
     | 
    
         
             
                    local_log.error("Function is not supported for target", exc_info=True)
         
     | 
| 
       68 
70 
     | 
    
         
             
                    return
         
     | 
| 
         @@ -70,15 +72,8 @@ def execute_function(target: Target, function: str) -> Generator[TargetRecordDes 
     | 
|
| 
       70 
72 
     | 
    
         
             
                    local_log.error("Plugin error while executing function for target", exc_info=True)
         
     | 
| 
       71 
73 
     | 
    
         
             
                    return
         
     | 
| 
       72 
74 
     | 
    
         | 
| 
       73 
     | 
    
         
            -
                 
     | 
| 
       74 
     | 
    
         
            -
             
     | 
| 
       75 
     | 
    
         
            -
                    output = getattr(target_attr, "__output__", "default") if hasattr(target_attr, "__output__") else None
         
     | 
| 
       76 
     | 
    
         
            -
                except PluginError as e:
         
     | 
| 
       77 
     | 
    
         
            -
                    local_log.error("Plugin error while fetching an attribute", exc_info=e)
         
     | 
| 
       78 
     | 
    
         
            -
                    return
         
     | 
| 
       79 
     | 
    
         
            -
             
     | 
| 
       80 
     | 
    
         
            -
                if output != "record":
         
     | 
| 
       81 
     | 
    
         
            -
                    local_log.warn("Output format is not supported", output=output)
         
     | 
| 
      
 75 
     | 
    
         
            +
                if output_type != "record":
         
     | 
| 
      
 76 
     | 
    
         
            +
                    local_log.warn("Output format is not supported", output=output_type)
         
     | 
| 
       82 
77 
     | 
    
         
             
                    return
         
     | 
| 
       83 
78 
     | 
    
         | 
| 
       84 
79 
     | 
    
         
             
                # no support for function-specific arguments
         
     | 
| 
         @@ -94,9 +89,9 @@ def execute_function(target: Target, function: str) -> Generator[TargetRecordDes 
     | 
|
| 
       94 
89 
     | 
    
         | 
| 
       95 
90 
     | 
    
         
             
            def produce_target_func_pairs(
         
     | 
| 
       96 
91 
     | 
    
         
             
                targets: Iterable[Target],
         
     | 
| 
       97 
     | 
    
         
            -
                functions:  
     | 
| 
      
 92 
     | 
    
         
            +
                functions: str,
         
     | 
| 
       98 
93 
     | 
    
         
             
                state: DumpState,
         
     | 
| 
       99 
     | 
    
         
            -
            ) ->  
     | 
| 
      
 94 
     | 
    
         
            +
            ) -> Iterator[tuple[Target, PluginFunction]]:
         
     | 
| 
       100 
95 
     | 
    
         
             
                """
         
     | 
| 
       101 
96 
     | 
    
         
             
                Return a generator with target and function pairs for execution.
         
     | 
| 
       102 
97 
     | 
    
         | 
| 
         @@ -107,20 +102,20 @@ def produce_target_func_pairs( 
     | 
|
| 
       107 
102 
     | 
    
         
             
                    pairs_to_skip.update((str(sink.target_path), sink.func) for sink in state.finished_sinks)
         
     | 
| 
       108 
103 
     | 
    
         | 
| 
       109 
104 
     | 
    
         
             
                for target in targets:
         
     | 
| 
       110 
     | 
    
         
            -
                    for  
     | 
| 
       111 
     | 
    
         
            -
                        if state and (target.path,  
     | 
| 
      
 105 
     | 
    
         
            +
                    for func_def in find_and_filter_plugins(target, functions):
         
     | 
| 
      
 106 
     | 
    
         
            +
                        if state and (target.path, func_def.name) in pairs_to_skip:
         
     | 
| 
       112 
107 
     | 
    
         
             
                            log.info(
         
     | 
| 
       113 
108 
     | 
    
         
             
                                "Skipping target/func pair since its marked as done in provided state",
         
     | 
| 
       114 
109 
     | 
    
         
             
                                target=target.path,
         
     | 
| 
       115 
     | 
    
         
            -
                                func= 
     | 
| 
      
 110 
     | 
    
         
            +
                                func=func_def.name,
         
     | 
| 
       116 
111 
     | 
    
         
             
                                state=state.path,
         
     | 
| 
       117 
112 
     | 
    
         
             
                            )
         
     | 
| 
       118 
113 
     | 
    
         
             
                            continue
         
     | 
| 
       119 
     | 
    
         
            -
                        yield (target,  
     | 
| 
       120 
     | 
    
         
            -
                        state.mark_as_finished(target,  
     | 
| 
      
 114 
     | 
    
         
            +
                        yield (target, func_def)
         
     | 
| 
      
 115 
     | 
    
         
            +
                        state.mark_as_finished(target, func_def.name)
         
     | 
| 
       121 
116 
     | 
    
         | 
| 
       122 
117 
     | 
    
         | 
| 
       123 
     | 
    
         
            -
            def execute_functions(target_func_stream: Iterable[ 
     | 
| 
      
 118 
     | 
    
         
            +
            def execute_functions(target_func_stream: Iterable[tuple[Target, str]]) -> Iterable[RecordStreamElement]:
         
     | 
| 
       124 
119 
     | 
    
         
             
                """
         
     | 
| 
       125 
120 
     | 
    
         
             
                Execute a function on a target for target / function pairs in the stream.
         
     | 
| 
       126 
121 
     | 
    
         | 
| 
         @@ -131,7 +126,7 @@ def execute_functions(target_func_stream: Iterable[Tuple[Target, str]]) -> Gener 
     | 
|
| 
       131 
126 
     | 
    
         
             
                        yield RecordStreamElement(target=target, func=func, record=record)
         
     | 
| 
       132 
127 
     | 
    
         | 
| 
       133 
128 
     | 
    
         | 
| 
       134 
     | 
    
         
            -
            def log_progress(stream: Iterable[Any], step_size: int = 1000) ->  
     | 
| 
      
 129 
     | 
    
         
            +
            def log_progress(stream: Iterable[Any], step_size: int = 1000) -> Iterable[Any]:
         
     | 
| 
       135 
130 
     | 
    
         
             
                """
         
     | 
| 
       136 
131 
     | 
    
         
             
                Log a number of items that went though the generator stream
         
     | 
| 
       137 
132 
     | 
    
         
             
                after every N element (N is configured in `step_size`).
         
     | 
| 
         @@ -155,7 +150,7 @@ def log_progress(stream: Iterable[Any], step_size: int = 1000) -> Generator[Any, 
     | 
|
| 
       155 
150 
     | 
    
         
             
            def sink_records(
         
     | 
| 
       156 
151 
     | 
    
         
             
                record_stream: Iterable[RecordStreamElement],
         
     | 
| 
       157 
152 
     | 
    
         
             
                state: DumpState,
         
     | 
| 
       158 
     | 
    
         
            -
            ) ->  
     | 
| 
      
 153 
     | 
    
         
            +
            ) -> Iterator[RecordStreamElement]:
         
     | 
| 
       159 
154 
     | 
    
         
             
                """
         
     | 
| 
       160 
155 
     | 
    
         
             
                Persist records from the stream into appropriate sinks, per serialization, compression and record type.
         
     | 
| 
       161 
156 
     | 
    
         
             
                """
         
     | 
| 
         @@ -168,7 +163,7 @@ def sink_records( 
     | 
|
| 
       168 
163 
     | 
    
         
             
            def persist_processing_state(
         
     | 
| 
       169 
164 
     | 
    
         
             
                record_stream: Iterable[RecordStreamElement],
         
     | 
| 
       170 
165 
     | 
    
         
             
                state: DumpState,
         
     | 
| 
       171 
     | 
    
         
            -
            ) ->  
     | 
| 
      
 166 
     | 
    
         
            +
            ) -> Iterator[RecordStreamElement]:
         
     | 
| 
       172 
167 
     | 
    
         
             
                """
         
     | 
| 
       173 
168 
     | 
    
         
             
                Keep track of the pipeline state in a persistent state object.
         
     | 
| 
       174 
169 
     | 
    
         
             
                """
         
     | 
| 
         @@ -179,8 +174,8 @@ def persist_processing_state( 
     | 
|
| 
       179 
174 
     | 
    
         | 
| 
       180 
175 
     | 
    
         | 
| 
       181 
176 
     | 
    
         
             
            def execute_pipeline(
         
     | 
| 
       182 
     | 
    
         
            -
                targets:  
     | 
| 
       183 
     | 
    
         
            -
                functions:  
     | 
| 
      
 177 
     | 
    
         
            +
                targets: list[str],
         
     | 
| 
      
 178 
     | 
    
         
            +
                functions: str,
         
     | 
| 
       184 
179 
     | 
    
         
             
                output_dir: Path,
         
     | 
| 
       185 
180 
     | 
    
         
             
                serialization: Serialization,
         
     | 
| 
       186 
181 
     | 
    
         
             
                compression: Optional[Compression] = None,
         
     | 
| 
         @@ -297,7 +292,7 @@ def main(): 
     | 
|
| 
       297 
292 
     | 
    
         
             
                try:
         
     | 
| 
       298 
293 
     | 
    
         
             
                    execute_pipeline(
         
     | 
| 
       299 
294 
     | 
    
         
             
                        targets=args.targets,
         
     | 
| 
       300 
     | 
    
         
            -
                        functions=args.function 
     | 
| 
      
 295 
     | 
    
         
            +
                        functions=args.function,
         
     | 
| 
       301 
296 
     | 
    
         
             
                        output_dir=args.output,
         
     | 
| 
       302 
297 
     | 
    
         
             
                        serialization=Serialization(args.serialization),
         
     | 
| 
       303 
298 
     | 
    
         
             
                        compression=Compression(args.compression),
         
     | 
| 
         @@ -6,7 +6,7 @@ import json 
     | 
|
| 
       6 
6 
     | 
    
         
             
            from contextlib import contextmanager
         
     | 
| 
       7 
7 
     | 
    
         
             
            from dataclasses import dataclass
         
     | 
| 
       8 
8 
     | 
    
         
             
            from pathlib import Path
         
     | 
| 
       9 
     | 
    
         
            -
            from typing import Any, Callable, Iterator,  
     | 
| 
      
 9 
     | 
    
         
            +
            from typing import Any, Callable, Iterator, Optional, TextIO
         
     | 
| 
       10 
10 
     | 
    
         | 
| 
       11 
11 
     | 
    
         
             
            import structlog
         
     | 
| 
       12 
12 
     | 
    
         | 
| 
         @@ -35,17 +35,20 @@ class Sink: 
     | 
|
| 
       35 
35 
     | 
    
         
             
                record_count: int = 0
         
     | 
| 
       36 
36 
     | 
    
         
             
                size_bytes: int = 0
         
     | 
| 
       37 
37 
     | 
    
         | 
| 
      
 38 
     | 
    
         
            +
                def __post_init__(self):
         
     | 
| 
      
 39 
     | 
    
         
            +
                    self.func = getattr(self.func, "name", self.func)
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
       38 
41 
     | 
    
         | 
| 
       39 
42 
     | 
    
         
             
            @dataclass
         
     | 
| 
       40 
43 
     | 
    
         
             
            class DumpState:
         
     | 
| 
       41 
     | 
    
         
            -
                target_paths:  
     | 
| 
       42 
     | 
    
         
            -
                functions:  
     | 
| 
      
 44 
     | 
    
         
            +
                target_paths: list[str]
         
     | 
| 
      
 45 
     | 
    
         
            +
                functions: list[str]
         
     | 
| 
       43 
46 
     | 
    
         
             
                serialization: str
         
     | 
| 
       44 
47 
     | 
    
         
             
                compression: str
         
     | 
| 
       45 
48 
     | 
    
         
             
                start_time: datetime.datetime
         
     | 
| 
       46 
49 
     | 
    
         
             
                last_update_time: datetime.datetime
         
     | 
| 
       47 
50 
     | 
    
         | 
| 
       48 
     | 
    
         
            -
                sinks:  
     | 
| 
      
 51 
     | 
    
         
            +
                sinks: list[Sink] = dataclasses.field(default_factory=list)
         
     | 
| 
       49 
52 
     | 
    
         | 
| 
       50 
53 
     | 
    
         
             
                # Volatile properties
         
     | 
| 
       51 
54 
     | 
    
         
             
                output_dir: Optional[Path] = None
         
     | 
| 
         @@ -56,7 +59,7 @@ class DumpState: 
     | 
|
| 
       56 
59 
     | 
    
         
             
                    return sum(s.record_count for s in self.sinks)
         
     | 
| 
       57 
60 
     | 
    
         | 
| 
       58 
61 
     | 
    
         
             
                @property
         
     | 
| 
       59 
     | 
    
         
            -
                def finished_sinks(self) ->  
     | 
| 
      
 62 
     | 
    
         
            +
                def finished_sinks(self) -> list[Sink]:
         
     | 
| 
       60 
63 
     | 
    
         
             
                    return [sink for sink in self.sinks if not sink.is_dirty]
         
     | 
| 
       61 
64 
     | 
    
         | 
| 
       62 
65 
     | 
    
         
             
                @property
         
     | 
| 
         @@ -178,7 +181,7 @@ class DumpState: 
     | 
|
| 
       178 
181 
     | 
    
         
             
                    state.output_dir = output_dir
         
     | 
| 
       179 
182 
     | 
    
         
             
                    return state
         
     | 
| 
       180 
183 
     | 
    
         | 
| 
       181 
     | 
    
         
            -
                def get_invalid_sinks(self) ->  
     | 
| 
      
 184 
     | 
    
         
            +
                def get_invalid_sinks(self) -> list[Sink]:
         
     | 
| 
       182 
185 
     | 
    
         
             
                    """Return sinks that have a mismatch between recorded size and a real file size"""
         
     | 
| 
       183 
186 
     | 
    
         
             
                    invalid_sinks = []
         
     | 
| 
       184 
187 
     | 
    
         
             
                    for sink in self.sinks:
         
     | 
| 
         @@ -214,8 +217,8 @@ class DumpState: 
     | 
|
| 
       214 
217 
     | 
    
         
             
            def create_state(
         
     | 
| 
       215 
218 
     | 
    
         
             
                *,
         
     | 
| 
       216 
219 
     | 
    
         
             
                output_dir: Path,
         
     | 
| 
       217 
     | 
    
         
            -
                target_paths:  
     | 
| 
       218 
     | 
    
         
            -
                functions:  
     | 
| 
      
 220 
     | 
    
         
            +
                target_paths: list[str],
         
     | 
| 
      
 221 
     | 
    
         
            +
                functions: list[str],
         
     | 
| 
       219 
222 
     | 
    
         
             
                serialization: Serialization,
         
     | 
| 
       220 
223 
     | 
    
         
             
                compression: Compression = None,
         
     | 
| 
       221 
224 
     | 
    
         
             
            ) -> DumpState:
         
     | 
| 
         @@ -32,6 +32,7 @@ from flow.record.adapter.jsonfile import JsonfileWriter 
     | 
|
| 
       32 
32 
     | 
    
         
             
            from flow.record.jsonpacker import JsonRecordPacker
         
     | 
| 
       33 
33 
     | 
    
         | 
| 
       34 
34 
     | 
    
         
             
            from dissect.target import Target
         
     | 
| 
      
 35 
     | 
    
         
            +
            from dissect.target.plugin import PluginFunction
         
     | 
| 
       35 
36 
     | 
    
         | 
| 
       36 
37 
     | 
    
         
             
            log = structlog.get_logger(__name__)
         
     | 
| 
       37 
38 
     | 
    
         | 
| 
         @@ -69,14 +70,14 @@ def get_nested_attr(obj: Any, nested_attr: str) -> Any: 
     | 
|
| 
       69 
70 
     | 
    
         | 
| 
       70 
71 
     | 
    
         | 
| 
       71 
72 
     | 
    
         
             
            @lru_cache(maxsize=DEST_DIR_CACHE_SIZE)
         
     | 
| 
       72 
     | 
    
         
            -
            def get_sink_dir_by_target(target: Target, function:  
     | 
| 
       73 
     | 
    
         
            -
                func_first_name, _, _ = function.partition(".")
         
     | 
| 
      
 73 
     | 
    
         
            +
            def get_sink_dir_by_target(target: Target, function: PluginFunction) -> Path:
         
     | 
| 
      
 74 
     | 
    
         
            +
                func_first_name, _, _ = function.name.partition(".")
         
     | 
| 
       74 
75 
     | 
    
         
             
                return Path(target.name) / func_first_name
         
     | 
| 
       75 
76 
     | 
    
         | 
| 
       76 
77 
     | 
    
         | 
| 
       77 
78 
     | 
    
         
             
            @functools.lru_cache(maxsize=DEST_DIR_CACHE_SIZE)
         
     | 
| 
       78 
     | 
    
         
            -
            def get_sink_dir_by_func(target: Target, function:  
     | 
| 
       79 
     | 
    
         
            -
                func_first_name, _, _ = function.partition(".")
         
     | 
| 
      
 79 
     | 
    
         
            +
            def get_sink_dir_by_func(target: Target, function: PluginFunction) -> Path:
         
     | 
| 
      
 80 
     | 
    
         
            +
                func_first_name, _, _ = function.name.partition(".")
         
     | 
| 
       80 
81 
     | 
    
         
             
                return Path(func_first_name) / target.name
         
     | 
| 
       81 
82 
     | 
    
         | 
| 
       82 
83 
     | 
    
         | 
    
        dissect/target/tools/query.py
    CHANGED
    
    | 
         @@ -26,6 +26,7 @@ from dissect.target.tools.utils import ( 
     | 
|
| 
       26 
26 
     | 
    
         
             
                catch_sigpipe,
         
     | 
| 
       27 
27 
     | 
    
         
             
                configure_generic_arguments,
         
     | 
| 
       28 
28 
     | 
    
         
             
                execute_function_on_target,
         
     | 
| 
      
 29 
     | 
    
         
            +
                find_and_filter_plugins,
         
     | 
| 
       29 
30 
     | 
    
         
             
                generate_argparse_for_bound_method,
         
     | 
| 
       30 
31 
     | 
    
         
             
                generate_argparse_for_plugin_class,
         
     | 
| 
       31 
32 
     | 
    
         
             
                generate_argparse_for_unbound_method,
         
     | 
| 
         @@ -270,25 +271,13 @@ def main(): 
     | 
|
| 
       270 
271 
     | 
    
         
             
                        basic_entries = []
         
     | 
| 
       271 
272 
     | 
    
         
             
                        yield_entries = []
         
     | 
| 
       272 
273 
     | 
    
         | 
| 
       273 
     | 
    
         
            -
                        # Keep a set of plugins that were already executed on the target.
         
     | 
| 
       274 
     | 
    
         
            -
                        executed_plugins = set()
         
     | 
| 
       275 
     | 
    
         
            -
             
     | 
| 
       276 
274 
     | 
    
         
             
                        first_seen_output_type = default_output_type
         
     | 
| 
       277 
275 
     | 
    
         
             
                        cli_params_unparsed = rest
         
     | 
| 
       278 
276 
     | 
    
         | 
| 
       279 
     | 
    
         
            -
                        func_defs, _ = find_plugin_functions(target, args.function, compatibility=False)
         
     | 
| 
       280 
277 
     | 
    
         
             
                        excluded_funcs, _ = find_plugin_functions(target, args.excluded_functions, compatibility=False)
         
     | 
| 
       281 
278 
     | 
    
         
             
                        excluded_func_paths = {excluded_func.path for excluded_func in excluded_funcs}
         
     | 
| 
       282 
279 
     | 
    
         | 
| 
       283 
     | 
    
         
            -
                        for func_def in  
     | 
| 
       284 
     | 
    
         
            -
                            if func_def.path in excluded_func_paths:
         
     | 
| 
       285 
     | 
    
         
            -
                                continue
         
     | 
| 
       286 
     | 
    
         
            -
             
     | 
| 
       287 
     | 
    
         
            -
                            # Avoid executing same plugin for multiple OSes (like hostname)
         
     | 
| 
       288 
     | 
    
         
            -
                            if func_def.name in executed_plugins:
         
     | 
| 
       289 
     | 
    
         
            -
                                continue
         
     | 
| 
       290 
     | 
    
         
            -
                            executed_plugins.add(func_def.name)
         
     | 
| 
       291 
     | 
    
         
            -
             
     | 
| 
      
 280 
     | 
    
         
            +
                        for func_def in find_and_filter_plugins(target, args.function, excluded_func_paths):
         
     | 
| 
       292 
281 
     | 
    
         
             
                            # If the default type is record (meaning we skip everything else)
         
     | 
| 
       293 
282 
     | 
    
         
             
                            # and actual output type is not record, continue.
         
     | 
| 
       294 
283 
     | 
    
         
             
                            # We perform this check here because plugins that require output files/dirs
         
     | 
    
        dissect/target/tools/utils.py
    CHANGED
    
    | 
         @@ -21,6 +21,7 @@ from dissect.target.plugin import ( 
     | 
|
| 
       21 
21 
     | 
    
         
             
                OSPlugin,
         
     | 
| 
       22 
22 
     | 
    
         
             
                Plugin,
         
     | 
| 
       23 
23 
     | 
    
         
             
                PluginFunction,
         
     | 
| 
      
 24 
     | 
    
         
            +
                find_plugin_functions,
         
     | 
| 
       24 
25 
     | 
    
         
             
                get_external_module_paths,
         
     | 
| 
       25 
26 
     | 
    
         
             
                load_modules_from_paths,
         
     | 
| 
       26 
27 
     | 
    
         
             
            )
         
     | 
| 
         @@ -289,3 +290,25 @@ def args_to_uri(targets: list[str], loader_name: str, rest: list[str]) -> list[s 
     | 
|
| 
       289 
290 
     | 
    
         
             
                for target in targets:
         
     | 
| 
       290 
291 
     | 
    
         
             
                    uris.append(f"{loader_name}://{target}" + (("?" + urllib.parse.urlencode(args)) if args else ""))
         
     | 
| 
       291 
292 
     | 
    
         
             
                return uris
         
     | 
| 
      
 293 
     | 
    
         
            +
             
     | 
| 
      
 294 
     | 
    
         
            +
             
     | 
| 
      
 295 
     | 
    
         
            +
            def find_and_filter_plugins(
         
     | 
| 
      
 296 
     | 
    
         
            +
                target: Target, functions: str, excluded_func_paths: set[str] = None
         
     | 
| 
      
 297 
     | 
    
         
            +
            ) -> Iterator[PluginFunction]:
         
     | 
| 
      
 298 
     | 
    
         
            +
                # Keep a set of plugins that were already executed on the target.
         
     | 
| 
      
 299 
     | 
    
         
            +
                executed_plugins = set()
         
     | 
| 
      
 300 
     | 
    
         
            +
                excluded_func_paths = excluded_func_paths or set()
         
     | 
| 
      
 301 
     | 
    
         
            +
             
     | 
| 
      
 302 
     | 
    
         
            +
                func_defs, _ = find_plugin_functions(target, functions, compatibility=False)
         
     | 
| 
      
 303 
     | 
    
         
            +
             
     | 
| 
      
 304 
     | 
    
         
            +
                for func_def in func_defs:
         
     | 
| 
      
 305 
     | 
    
         
            +
                    if func_def.path in excluded_func_paths:
         
     | 
| 
      
 306 
     | 
    
         
            +
                        continue
         
     | 
| 
      
 307 
     | 
    
         
            +
             
     | 
| 
      
 308 
     | 
    
         
            +
                    # Avoid executing same plugin for multiple OSes (like hostname)
         
     | 
| 
      
 309 
     | 
    
         
            +
                    if func_def.name in executed_plugins:
         
     | 
| 
      
 310 
     | 
    
         
            +
                        continue
         
     | 
| 
      
 311 
     | 
    
         
            +
             
     | 
| 
      
 312 
     | 
    
         
            +
                    executed_plugins.add(func_def.name)
         
     | 
| 
      
 313 
     | 
    
         
            +
             
     | 
| 
      
 314 
     | 
    
         
            +
                    yield func_def
         
     | 
| 
         @@ -1,6 +1,6 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            Metadata-Version: 2.1
         
     | 
| 
       2 
2 
     | 
    
         
             
            Name: dissect.target
         
     | 
| 
       3 
     | 
    
         
            -
            Version: 3.17. 
     | 
| 
      
 3 
     | 
    
         
            +
            Version: 3.17.dev13
         
     | 
| 
       4 
4 
     | 
    
         
             
            Summary: This module ties all other Dissect modules together, it provides a programming API and command line tools which allow easy access to various data sources inside disk images or file collections (a.k.a. targets)
         
     | 
| 
       5 
5 
     | 
    
         
             
            Author-email: Dissect Team <dissect@fox-it.com>
         
     | 
| 
       6 
6 
     | 
    
         
             
            License: Affero General Public License v3
         
     | 
| 
         @@ -215,7 +215,7 @@ dissect/target/plugins/os/unix/linux/modules.py,sha256=H1S5CkpXttCVwzE2Ylz3jkvrC 
     | 
|
| 
       215 
215 
     | 
    
         
             
            dissect/target/plugins/os/unix/linux/netstat.py,sha256=MAC4ZdeNqcKpxT2ZMh1-7rjt4Pt_WQIRy7RChr7nlPk,1649
         
     | 
| 
       216 
216 
     | 
    
         
             
            dissect/target/plugins/os/unix/linux/proc.py,sha256=jm35fAasnNbObN2tpflwQuCfVYLDkTP2EDrzYG42ZSk,23354
         
     | 
| 
       217 
217 
     | 
    
         
             
            dissect/target/plugins/os/unix/linux/processes.py,sha256=sTQqZYPW-_gs7Z3f0wwsV6clUX4NK44GGyMiZToBIrg,1936
         
     | 
| 
       218 
     | 
    
         
            -
            dissect/target/plugins/os/unix/linux/services.py,sha256 
     | 
| 
      
 218 
     | 
    
         
            +
            dissect/target/plugins/os/unix/linux/services.py,sha256=-d2y073mOXUM3XCzRgDVCRFR9eTLoVuN8FsZVewHzRg,4075
         
     | 
| 
       219 
219 
     | 
    
         
             
            dissect/target/plugins/os/unix/linux/sockets.py,sha256=l7Zq4J_xioyl8V7-Q9GLStOyYLNrcpKoWWWSzJaSIZQ,9765
         
     | 
| 
       220 
220 
     | 
    
         
             
            dissect/target/plugins/os/unix/linux/android/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
         
     | 
| 
       221 
221 
     | 
    
         
             
            dissect/target/plugins/os/unix/linux/android/_os.py,sha256=trmESlpHdwVu7wV18RevEhh_TsVyfKPFCd5Usb5-fSU,2056
         
     | 
| 
         @@ -320,14 +320,14 @@ dissect/target/tools/fs.py,sha256=cizCrW8rqdpT1irA8g6mslkaXX7CynWVQ7fvRUrcxNU,37 
     | 
|
| 
       320 
320 
     | 
    
         
             
            dissect/target/tools/info.py,sha256=3smHr8I71yj3kCjsQ5nXkOHI9T_N8UwvkVa1CNOxB-s,5461
         
     | 
| 
       321 
321 
     | 
    
         
             
            dissect/target/tools/logging.py,sha256=5ZnumtMWLyslxfrUGZ4ntRyf3obOOhmn8SBjKfdLcEg,4174
         
     | 
| 
       322 
322 
     | 
    
         
             
            dissect/target/tools/mount.py,sha256=L_0tSmiBdW4aSaF0vXjB0bAkTC0kmT2N1hrbW6s5Jow,3254
         
     | 
| 
       323 
     | 
    
         
            -
            dissect/target/tools/query.py,sha256= 
     | 
| 
      
 323 
     | 
    
         
            +
            dissect/target/tools/query.py,sha256=6zz9SXS6YnHj7eguORS8Je7N4iM0i1PZDIQ-gyJ1nPY,15593
         
     | 
| 
       324 
324 
     | 
    
         
             
            dissect/target/tools/reg.py,sha256=FDsiBBDxjWVUBTRj8xn82vZe-J_d9piM-TKS3PHZCcM,3193
         
     | 
| 
       325 
325 
     | 
    
         
             
            dissect/target/tools/shell.py,sha256=6GF-mr4EpzX34G9sqTNKcccFJXhNTlrUqriRYIW7P7o,43544
         
     | 
| 
       326 
     | 
    
         
            -
            dissect/target/tools/utils.py,sha256= 
     | 
| 
      
 326 
     | 
    
         
            +
            dissect/target/tools/utils.py,sha256=sQizexY3ui5vmWw4KOBLg5ecK3TPFjD-uxDqRn56ZTY,11304
         
     | 
| 
       327 
327 
     | 
    
         
             
            dissect/target/tools/dump/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
         
     | 
| 
       328 
     | 
    
         
            -
            dissect/target/tools/dump/run.py,sha256= 
     | 
| 
       329 
     | 
    
         
            -
            dissect/target/tools/dump/state.py,sha256= 
     | 
| 
       330 
     | 
    
         
            -
            dissect/target/tools/dump/utils.py,sha256= 
     | 
| 
      
 328 
     | 
    
         
            +
            dissect/target/tools/dump/run.py,sha256=aD84peRS4zHqC78fH7Vd4ni3m1ZmVP70LyMwBRvoDGY,9463
         
     | 
| 
      
 329 
     | 
    
         
            +
            dissect/target/tools/dump/state.py,sha256=YYgCff0kZZ-tx27lJlc9LQ7AfoGnLK5Gyi796OnktA8,9205
         
     | 
| 
      
 330 
     | 
    
         
            +
            dissect/target/tools/dump/utils.py,sha256=onQIWPPjkz8HmZMndVVP_4ywFixXwXGOjHi4x8D3Xqw,7645
         
     | 
| 
       331 
331 
     | 
    
         
             
            dissect/target/volumes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
         
     | 
| 
       332 
332 
     | 
    
         
             
            dissect/target/volumes/bde.py,sha256=3lCb7inPmu8MR5WxPWUauJ8t3Emymh9D-o2yCL-SB9g,3974
         
     | 
| 
       333 
333 
     | 
    
         
             
            dissect/target/volumes/ddf.py,sha256=X0yBIFbG7Z_CJDVqxEBLDynM_NoSwIWEKVsQFOS-Tn4,1749
         
     | 
| 
         @@ -336,10 +336,10 @@ dissect/target/volumes/luks.py,sha256=OmCMsw6rCUXG1_plnLVLTpsvE1n_6WtoRUGQbpmu1z 
     | 
|
| 
       336 
336 
     | 
    
         
             
            dissect/target/volumes/lvm.py,sha256=wwQVR9I3G9YzmY6UxFsH2Y4MXGBcKL9aayWGCDTiWMU,2269
         
     | 
| 
       337 
337 
     | 
    
         
             
            dissect/target/volumes/md.py,sha256=j1K1iKmspl0C_OJFc7-Q1BMWN2OCC5EVANIgVlJ_fIE,1673
         
     | 
| 
       338 
338 
     | 
    
         
             
            dissect/target/volumes/vmfs.py,sha256=-LoUbn9WNwTtLi_4K34uV_-wDw2W5hgaqxZNj4UmqAQ,1730
         
     | 
| 
       339 
     | 
    
         
            -
            dissect.target-3.17. 
     | 
| 
       340 
     | 
    
         
            -
            dissect.target-3.17. 
     | 
| 
       341 
     | 
    
         
            -
            dissect.target-3.17. 
     | 
| 
       342 
     | 
    
         
            -
            dissect.target-3.17. 
     | 
| 
       343 
     | 
    
         
            -
            dissect.target-3.17. 
     | 
| 
       344 
     | 
    
         
            -
            dissect.target-3.17. 
     | 
| 
       345 
     | 
    
         
            -
            dissect.target-3.17. 
     | 
| 
      
 339 
     | 
    
         
            +
            dissect.target-3.17.dev13.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
         
     | 
| 
      
 340 
     | 
    
         
            +
            dissect.target-3.17.dev13.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
         
     | 
| 
      
 341 
     | 
    
         
            +
            dissect.target-3.17.dev13.dist-info/METADATA,sha256=r2xQoOApd3QGiaNTpuo27XM3t1YkKLaNSLkywaWejJ8,11300
         
     | 
| 
      
 342 
     | 
    
         
            +
            dissect.target-3.17.dev13.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
         
     | 
| 
      
 343 
     | 
    
         
            +
            dissect.target-3.17.dev13.dist-info/entry_points.txt,sha256=tvFPa-Ap-gakjaPwRc6Fl6mxHzxEZ_arAVU-IUYeo_s,447
         
     | 
| 
      
 344 
     | 
    
         
            +
            dissect.target-3.17.dev13.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
         
     | 
| 
      
 345 
     | 
    
         
            +
            dissect.target-3.17.dev13.dist-info/RECORD,,
         
     | 
| 
         
            File without changes
         
     | 
| 
         
            File without changes
         
     | 
| 
         
            File without changes
         
     | 
    
        {dissect.target-3.17.dev11.dist-info → dissect.target-3.17.dev13.dist-info}/entry_points.txt
    RENAMED
    
    | 
         
            File without changes
         
     | 
| 
         
            File without changes
         
     |