Skip to content

Commit 5220b8e

Browse files
committed
Add Multiprocessing
Add multiprocessing functionality. However, this conflicts with wriring text to stdout other than a progressbar. Ideally, loguru could be used to manage logs in a multi-process safe way, but this requires a somewhat hacky solution of passing an attribute of the parent logger object into each spawned process and manually setting the logger imported in the spawned process to use the attribute passed as a paremeter. There is a [PR](Delgan/loguru#1069) to create a new function called reinstall() that would address this, but it has yet to be merged. Until the PR is merged, using multithreading instead of multiprocessing seems like the better solution.
1 parent dc707b3 commit 5220b8e

File tree

1 file changed

+63
-29
lines changed

1 file changed

+63
-29
lines changed

src/linksiren/impure_functions.py

Lines changed: 63 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@
77
"""
88

99
from pathlib import Path
10+
from multiprocessing import Pool
11+
from functools import partial
1012
import linksiren.pure_functions
13+
from tqdm import tqdm
1114

1215

1316
def write_payload_local(payload_name, payload_contents):
@@ -75,60 +78,91 @@ def read_targets(targets_file):
7578
return linksiren.pure_functions.process_targets(target_unc_paths)
7679

7780

81+
def get_rankings_for_target(
82+
target, domain, username, password, active_threshold_date, max_depth, go_fast
83+
):
84+
folder_rankings = {}
85+
86+
if target.connection is None:
87+
try:
88+
target.connect(user=username, password=password, domain=domain)
89+
except Exception as e:
90+
print(f"Error connecting to {target.host}: {e}")
91+
return folder_rankings
92+
93+
# Expand any empty paths for the target
94+
# An empty path indicates all shares on the host should be targeted
95+
try:
96+
target.expand_paths()
97+
except Exception as e:
98+
print(f"Error expanding paths on {target.host}: {e}")
99+
return folder_rankings
100+
101+
try:
102+
# Call the appropriate review function based on the fast argument
103+
folder_rankings = target.review_all_folders(
104+
folder_rankings, active_threshold_date, max_depth, go_fast
105+
)
106+
except Exception as e:
107+
print(f"Error connecting to shares on {target.host}: {e}")
108+
109+
return folder_rankings
110+
111+
78112
# Eventually we should just pass the whole parsed arguments structure to different functions
79113
# And then modify behaviors by checking options for things like active_threshold_date, max_depth
80114
# creds/ntlm hash, go_fast, etc.
81115
def get_rankings(
82116
targets, domain, username, password, active_threshold_date, max_depth, go_fast
83117
):
84118
"""
85-
get_sorted_rankings(targets, active_threshold, max_depth, go_fast)
119+
get_rankings(targets, domain, username, password, active_threshold_date, max_depth, go_fast)
86120
87-
:param list targets: List of UNC paths to shares and base directories to review.
88-
:param int active_threshold: Number of days within which file access constitutes a file being
89-
active
121+
:param str domain: Domain for authentication.
122+
:param str username: Username for authentication.
123+
:param str password: Password for authentication.
124+
:param datetime active_threshold_date: Date threshold to determine if a file is active.
125+
dictionary of UNC paths and associated rankings. Catches exceptions for failed smb connections
90126
:param int max_depth: Number of layers of folders to search. 1 searches only the specified
91127
target UNC paths and none of their subfolders.
92128
:param bool go_fast: If True, folders will be marked as active as soon as a single file is
93129
meeting the active_threshold criteria is found. A rank of 1 will be assigned to all active
94130
folders.
95131
96-
:return: A dictionary in the format {<folder UNC path>: <ranking>} sorted by ranking
132+
:return: A dictionary in the format {<folder UNC path>: <ranking>}
97133
98-
Accepts a list of UNC paths to file shares and base directories. Gets the ranking associated
99-
with each folder based on the number of files active within the active_threshold number of days.
134+
Accepts a list of UNC target objects. Gets rankings associated with each path associated with
135+
each target based on the number of files active within the active_threshold number of days.
100136
Recursively assigns ranking to subfolders up to max_depth. If go_fast is enabled, assigns the
101137
rank of 1 to all folders with a single active file and moves on to the next folder. Returns a
102138
dirctionary of UNC paths and associated rankings. Catches exceptions for failed smb connections
103139
and prints a message describing the error.
104140
"""
105141
# Track rankings for each folder, which are (counterintuitively) scores corresponding to the
106-
# number of active files in a folder. {<folder UNC path>: <ranking>}
142+
# number of active files in a folder. So a higher ranking is a better target.
143+
# {<folder UNC path>: <ranking>}
107144
folder_rankings = {}
108145

109-
for target in targets:
110-
if target.connection is None:
111-
try:
112-
target.connect(user=username, password=password, domain=domain)
113-
except Exception as e:
114-
print(f"Error connecting to {target.host}: {e}")
115-
return folder_rankings
146+
worker_partial = partial(
147+
get_rankings_for_target,
148+
domain=domain,
149+
username=username,
150+
password=password,
151+
active_threshold_date=active_threshold_date,
152+
max_depth=max_depth,
153+
go_fast=go_fast,
154+
)
116155

117-
# Expand any empty paths for the target
118-
# An empty path indicates all shares on the host should be targeted
119-
try:
120-
target.expand_paths()
121-
except Exception as e:
122-
print(f"Error expanding paths on {target.host}: {e}")
123-
return folder_rankings
156+
with Pool(processes=2) as pool:
157+
for result in tqdm(
158+
pool.imap_unordered(func=worker_partial, iterable=targets),
159+
total=len(targets),
160+
):
161+
if result is not None:
162+
folder_rankings.update(result)
124163

125-
try:
126-
# Call the appropriate review function based on the fast argument
127-
folder_rankings = target.review_all_folders(
128-
folder_rankings, active_threshold_date, max_depth, go_fast
129-
)
130-
except Exception as e:
131-
print(f"Error connecting to shares on {target.host}: {e}")
164+
pool.close()
165+
pool.join()
132166

133167
return folder_rankings
134168

0 commit comments

Comments
 (0)