-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSamourai
650 lines (511 loc) · 20.5 KB
/
Samourai
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
#!/usr/bin/env python3
import os
import wget
import tarfile
import subprocess
import requests
import shutil
import time
import random
import string
def download_file(url, filename):
"""Downloads a file from the specified URL to the specified filename."""
downloads_directory = os.path.join(os.path.expanduser('~'), 'Downloads')
filepath = os.path.join(downloads_directory, filename)
print('Downloading file...')
wget.download(url, filepath)
def extract_file(filename):
"""Extracts a file from a tar archive."""
filepath = os.path.join(os.path.expanduser('~'), 'Downloads', filename)
if not os.path.exists(filepath):
print('File does not exist.')
return
with open(filepath, 'rb') as f:
tar = tarfile.open(fileobj=f)
tar.extractall(os.path.join(os.path.expanduser('~'), 'Downloads'))
def create_directories():
"""Creates the necessary directories."""
dojo_app_directory = os.path.join(os.path.expanduser('~'), 'dojo-app')
os.makedirs(dojo_app_directory, exist_ok=True)
if __name__ == '__main__':
create_directories()
url = 'https://code.samourai.io/dojo/samourai-dojo/-/archive/v1.20.0/samourai-dojo-v1.20.0.tar.gz'
filename = 'samourai-dojo-v1.20.0.tar.gz'
download_file(url, filename)
extract_file(filename)
# Perform the mv command with sudo privileges
source_directory = os.path.join(os.path.expanduser('~'), 'Downloads', 'samourai-dojo-v1.20.0')
destination_directory = os.path.join(os.path.expanduser('~'), 'dojo-app')
subprocess.run(['sudo', 'mv', source_directory, destination_directory])
print('The file has been downloaded, extracted, and moved to the dojo-app directory.')
def delete_directory(directory):
"""Deletes a directory with sudo privileges."""
subprocess.run(['sudo', 'rm', '-r', directory])
if __name__ == '__main__':
dojo_app_directory = os.path.join(os.path.expanduser('~'), 'dojo-app')
conf_directory = os.path.join(dojo_app_directory, 'samourai-dojo-v1.20.0/docker/my-dojo/conf')
delete_directory(conf_directory)
print('The conf directory has been deleted with sudo privileges.')
def create_directory(directory):
"""Creates a directory with sudo privileges."""
subprocess.run(['sudo', 'mkdir', '-p', directory])
if __name__ == '__main__':
dojo_app_directory = os.path.join(os.path.expanduser('~'), 'dojo-app')
conf_directory = os.path.join(dojo_app_directory, 'samourai-dojo-v1.20.0/docker/my-dojo/conf')
create_directory(conf_directory)
print('The conf directory has been created with sudo privileges.')
ipaddress = "ip_address"
rpcusername = "rpc_username"
rpcpassword = "rpc_password"
def create_docker_bitcoind_conf():
conf_directory = os.path.expanduser('~/dojo-app/samourai-dojo-v1.20.0/docker/my-dojo/conf')
filename = 'docker-bitcoind.conf.tpl'
filepath = os.path.join(conf_directory, filename)
bitcoind_content = '''\
#########################################
# CONFIGURATION OF BITCOIND CONTAINER
#########################################
# User account used for rpc access to bitcoind
# Type: alphanumeric
BITCOIND_RPC_USER={rpcusername}
# Password of user account used for rpc access to bitcoind
# Type: alphanumeric
BITCOIND_RPC_PASSWORD={rpcpassword}
# Max number of connections to network peers
# Type: integer
BITCOIND_MAX_CONNECTIONS=16
# Mempool maximum size in MB
# Type: integer
BITCOIND_MAX_MEMPOOL=1024
# Db cache size in MB
# Type: integer
BITCOIND_DB_CACHE=1024
# Number of threads to service RPC calls
# Type: integer
BITCOIND_RPC_THREADS=6
# RPC Work queue size
# Type: integer
BITCOIND_RPC_WORK_QUEUE=128
# Mempool expiry in hours
# Defines how long transactions stay in your local mempool before expiring
# Type: integer
BITCOIND_MEMPOOL_EXPIRY=72
# Min relay tx fee in BTC
# Type: numeric
BITCOIND_MIN_RELAY_TX_FEE=0.00001
# Allow incoming connections
# This parameter is inactive if BITCOIND_INSTALL is set to 'off'
# Values: on | off
BITCOIND_LISTEN_MODE=on
#
# EXPERT SETTINGS
#
#
# EPHEMERAL ONION ADDRESS AND BLOOM FILTERS FOR BITCOIND
# THESE PARAMETERS HAVE NO EFFECT IF BITCOIND_INSTALL IS SET TO OFF
#
# Generate a new onion address for bitcoind when Dojo is launched
# Activation of this option is recommended for improved privacy.
# Values: on | off
BITCOIND_EPHEMERAL_HS=on
# Enable bloom filters for other apps to leverage light client mode
# BITCOIND_EPHEMERAL_HS should be set to "off" otherwise new connection will need to be setup after restart
# Values: on | off
BITCOIND_BLOOM_FILTERS=off
#
# EXPOSE BITCOIND RPC API AND ZMQ NOTIFICATIONS TO EXTERNAL APPS
# THESE PARAMETERS HAVE NO EFFECT IF BITCOIND_INSTALL IS SET TO OFF
#
# Expose the RPC API to external apps
# Warning: Do not expose your RPC API to the internet!
# See BITCOIND_RPC_EXTERNAL_IP
# Value: on | off
BITCOIND_RPC_EXTERNAL=off
# IP address used to expose the RPC API to external apps
# This parameter is inactive if BITCOIND_RPC_EXTERNAL isn't set to 'on'
# Warning: Do not expose your RPC API to the internet!
# Recommended value:
# linux: 127.0.0.1
# macos or windows: IP address of the VM running the Docker host
# Type: string
BITCOIND_RPC_EXTERNAL_IP=127.0.0.1
#
#
# INSTALL AND RUN BITCOIND INSIDE DOCKER
#
# Install and run bitcoind inside Docker
# Set this option to 'off' to use a bitcoind hosted outside of Docker (not recommended)
# Value: on | off
BITCOIND_INSTALL=off
# IP address of bitcoind used by Dojo
# Set the value to 172.28.1.5 if BITCOIND_INSTALL is set to 'on'
# Type: string
BITCOIND_IP={ipaddress}
# Port of the RPC API
# Set the value to 28256 if BITCOIND_INSTALL is set to 'on'
# Type: integer
BITCOIND_RPC_PORT=8332
# Port exposing ZMQ notifications for raw transactions
# Set the value to 9501 if BITCOIND_INSTALL is set to 'on'
# Type: integer
BITCOIND_ZMQ_RAWTXS=28333
# Port exposing ZMQ notifications for block hashes
# Set value to 9502 if BITCOIND_INSTALL is set to 'on'
# Type: integer
BITCOIND_ZMQ_BLK_HASH=28334
#
# SHUTDOWN
#
# Max delay for bitcoind shutdown (expressed in seconds)
# Defines how long Dojo waits for a clean shutdown of bitcoind before shutting >
# This parameter is inactive if BITCOIND_INSTALL is set to 'off'
# Type: integer
BITCOIND_SHUTDOWN_DELAY=180
'''
# Use sudo to write the file with elevated privileges
subprocess.run(['sudo', 'tee', filepath], input=bitcoind_content.format(ipaddress=ipaddress, rpcusername=rpcusername, rpcpassword=rpcpassword), text=True, check=True)
def create_docker_explorer_conf():
conf_directory = os.path.expanduser('~/dojo-app/samourai-dojo-v1.20.0/docker/my-dojo/conf')
filename = 'docker-explorer.conf.tpl'
filepath = os.path.join(conf_directory, filename)
explorer_content = '''\
#########################################
# CONFIGURATION OF EXPLORER CONTAINER
#########################################
# Install and run a block explorer inside Dojo (recommended)
# Value: on | off
EXPLORER_INSTALL=off
'''
# Use sudo to write the file with elevated privileges
subprocess.run(['sudo', 'tee', filepath], input=explorer_content, text=True, check=True)
def create_docker_common_conf():
conf_directory = os.path.expanduser('~/dojo-app/samourai-dojo-v1.20.0/docker/my-dojo/conf')
filename = 'docker-common.conf.tpl'
filepath = os.path.join(conf_directory, filename)
common_content = '''\
#
# EXPERT AND DEV SETTINGS
#
#
# NETWORK ENVIRONMENT
#
# Select a Bitcoin network
# Do not modify this value after the first install
# Value: mainnet | testnet
COMMON_BTC_NETWORK=mainnet
'''
# Use sudo to write the file with elevated privileges
subprocess.run(['sudo', 'tee', filepath], input=common_content, text=True, check=True)
def create_docker_indexer_conf():
conf_directory = os.path.expanduser('~/dojo-app/samourai-dojo-v1.20.0/docker/my-dojo/conf')
filename = 'docker-indexer.conf.tpl'
filepath = os.path.join(conf_directory, filename)
indexer_content = '''\
#########################################
# CONFIGURATION OF A LOCAL INDEXER
#########################################
# Install and run a local indexer inside Docker
# Set this option to 'off' to use an indexer hosted outside of Docker
# or when using a different data source (local bitcoind, OXT)
# Value: on | off
INDEXER_INSTALL=off
# Choice of the Indexer you would like to install:
# addrindexrs - basic but fast indexer for your Dojo - takes 8-12 hrs to index
# Fulcrum - fast indexer, can be used as Electrum server for personal use - takes 2-3 days to index
# Value: addrindexrs | fulcrum
INDEXER_TYPE=fulcrum
# IP address of the local indexer used by Dojo
# Set the value to 172.28.1.6 if INDEXER_INSTALL is set to 'on'
# Type: string
INDEXER_IP={ipaddress}
# Port of the RPC API
# Set the value to 50001 if INDEXER_INSTALL is set to 'on'
# Type: integer
INDEXER_RPC_PORT=50002
# Support of batch requests by the local indexer
# Set the value to 'active' if INDEXER_TYPE is set to 'fulcrum' or you're using an external Electrum server with RPC batching support
# Value: active | inactive
INDEXER_BATCH_SUPPORT=active
# Choose between TCP and TLS transport when using an external Electrum server
# Value: tcp | tls
INDEXER_PROTOCOL=tls
# Expose the Electrum API to external apps
# Has an effect only if INDEXER_TYPE=fulcrum
# Warning: Do not expose your Electrum API to the internet!
# See INDEXER_EXTERNAL_IP
# Value: on | off
INDEXER_EXTERNAL=off
# IP address used to expose the Electrum API to external apps
# This parameter is inactive if INDEXER_EXTERNAL isn't set to 'on'
# Warning: Do not expose your RPC API to the internet!
# Recommended value:
# linux: 127.0.0.1
# macOS or Windows: IP address of the VM running the Docker host
# Type: string
INDEXER_EXTERNAL_IP=127.0.0.1
#
# EXPERT SETTINGS
# (ACTIVE IF INDEXER_INSTALL IS SET TO ON)
#
# Number of blocks to get in one JSONRPC request from bitcoind
# Type: integer
INDEXER_BATCH_SIZE=10
# Total size of block txids to cache (in MB)
# Type: integer
INDEXER_BLK_TXIDS_CACHE_SIZE_MB=10
# Number of transactions to lookup before returning an error
# Type: integer
INDEXER_TXID_LIMIT=501
'''
# Use sudo to write the file with elevated privileges
subprocess.run(['sudo', 'tee', filepath], input=indexer_content.format(ipaddress=ipaddress), text=True, check=True)
# Create the configuration files
create_docker_bitcoind_conf()
create_docker_explorer_conf()
create_docker_common_conf()
create_docker_indexer_conf()
def generate_random_password(length):
characters = string.ascii_letters + string.digits
password = ''.join(random.choice(characters) for _ in range(length))
return password
def create_docker_mysql_conf():
conf_directory = os.path.expanduser('~/dojo-app/samourai-dojo-v1.20.0/docker/my-dojo/conf')
filename = 'docker-mysql.conf.tpl'
filepath = os.path.join(conf_directory, filename)
# Generate random passwords
MYSQL_ROOT_PASSWORD = generate_random_password(12)
MYSQL_USER = "samourai"
MYSQL_PASSWORD = generate_random_password(10)
content = '''\
#########################################
# CONFIGURATION OF MYSQL CONTAINER
#########################################
# Password of MySql root account
# Warning: This option must not be modified after the first installation
# Type: alphanumeric
MYSQL_ROOT_PASSWORD={MYSQL_ROOT_PASSWORD}
# User account used for db access
# Warning: This option must not be modified after the first installation
# Type: alphanumeric
MYSQL_USER={MYSQL_USER}
# Password of user account
# Warning: This option must not be modified after the first installation
# Type: alphanumeric
MYSQL_PASSWORD={MYSQL_PASSWORD}
# MySQL configuration profile
# default = default configuration parameters
# low_mem = configuration minimizing the RAM consumed by the database
# Values: default | low_mem
MYSQL_CONF_PROFILE=default
'''
# Use sudo to write the file with elevated privileges
command = f'echo "{content.format(MYSQL_ROOT_PASSWORD=MYSQL_ROOT_PASSWORD, MYSQL_USER=MYSQL_USER, MYSQL_PASSWORD=MYSQL_PASSWORD)}" | sudo tee {filepath} > /dev/null'
subprocess.run(command, shell=True)
# Create the configuration file
create_docker_mysql_conf()
def generate_random_password(length):
characters = string.ascii_letters + string.digits
password = ''.join(random.choice(characters) for _ in range(length))
return password
def create_docker_node_conf():
conf_directory = os.path.expanduser('~/dojo-app/samourai-dojo-v1.20.0/docker/my-dojo/conf')
filename = 'docker-node.conf.tpl'
filepath = os.path.join(conf_directory, filename)
# Generate random passwords
NODE_API_KEY = generate_random_password(20)
NODE_ADMIN_KEY = generate_random_password(20)
NODE_PAYMENT_CODE = generate_random_password(20)
NODE_JWT_SECRET = generate_random_password(20)
content = '''\
#########################################
# CONFIGURATION OF NODE JS CONTAINER
#########################################
# API key required for accessing the services provided by the server
# Keep this API key secret!
# Provide a value with a high entropy!
# Type: alphanumeric
NODE_API_KEY={NODE_API_KEY}
# API key required for accessing the admin/maintenance services provided by the server
# Keep this Admin key secret!
# Provide a value with a high entropy!
# Type: alphanumeric
NODE_ADMIN_KEY={NODE_ADMIN_KEY}
# BIP47 Payment Code used for admin authentication
# Type: alphanumeric
NODE_PAYMENT_CODE={NODE_PAYMENT_CODE}
# Secret used by the server for signing Json Web Token
# Keep this value secret!
# Provide a value with a high entropy!
# Type: alphanumeric
NODE_JWT_SECRET={NODE_JWT_SECRET}
# Indexer or third-party service used for imports and rescans of addresses
# Values: local_bitcoind | local_indexer | third_party_explorer
NODE_ACTIVE_INDEXER=local_indexer
# FEE TYPE USED FOR FEES ESTIMATIONS BY BITCOIND
# Allowed values are ECONOMICAL or CONSERVATIVE
NODE_FEE_TYPE=ECONOMICAL
'''
# Use sudo to write the file with elevated privileges
command = f'echo "{content.format(NODE_API_KEY=NODE_API_KEY, NODE_ADMIN_KEY=NODE_ADMIN_KEY, NODE_PAYMENT_CODE=NODE_PAYMENT_CODE, NODE_JWT_SECRET=NODE_JWT_SECRET)}" | sudo tee {filepath} > /dev/null'
subprocess.run(command, shell=True)
# Create the configuration file
create_docker_node_conf()
def create_docker_tor_conf():
conf_directory = os.path.expanduser('~/dojo-app/samourai-dojo-v1.20.0/docker/my-dojo/conf')
filename = 'docker-tor.conf.tpl'
filepath = os.path.join(conf_directory, filename)
content = '''\
#########################################
# CONFIGURATION OF TOR CONTAINER
#########################################
# Port of the Tor Socks proxy
# Type: integer
TOR_SOCKS_PORT=9050
#
# USE TOR BRIDGES
#
# To get Tor bridges head over to https://bridges.torproject.org and click on
# Get bridges, then you will see a form with "Advanced Options" header
# leave the Pluggable Transport as obfs4 and click on Get Bridges button
# solve the captcah, you will get the bridge addresses (usually 3)
#
# Then, set TOR_USE_BRIDGES to "on" and initialize the TOR_BRIDGE_n options
# with the 3 lines generated by the online tool.
#
# For instance, if the first line generated by the tool is:
# obfs4 24.106.248.94:65531 B9EFBC5... cert=yrX... iat-mode=0
# You will have to set:
# TOR_BRIDGE_1="obfs4 24.106.248.94:65531 B9EFBC5... cert=yrX... iat-mode=0"
#
# Activate the use of Tor bridges
# Value: on | off
TOR_USE_BRIDGES=off
# Bridge 1
TOR_BRIDGE_1=ToBeDefined
# Bridge 2
TOR_BRIDGE_2=ToBeDefined
# Bridge 3
TOR_BRIDGE_3=ToBeDefined
'''
# Use sudo to write the file with elevated privileges
command = f'echo "{content}" | sudo tee {filepath} > /dev/null'
subprocess.run(command, shell=True)
# Create the configuration file
create_docker_tor_conf()
def create_docker_whirlpool_conf():
conf_directory = os.path.expanduser('~/dojo-app/samourai-dojo-v1.20.0/docker/my-dojo/conf')
filename = 'docker-whirlpool.conf.tpl'
filepath = os.path.join(conf_directory, filename)
content = '''\
#########################################
# CONFIGURATION OF WHIRLPOOL CONTAINER
#########################################
# Install and run an instance of whirlpool-cli inside Docker
# Value: on | off
WHIRLPOOL_INSTALL=off
# Resynchronize mix counters on startup (startup will be slower)
# Value: on | off
WHIRLPOOL_RESYNC=off
# Contact coordinator through its onion address or clearnet address
# Set to on for onion address, set to off for clearnet address
# Value: on | off
WHIRLPOOL_COORDINATOR_ONION=on
#
# EXPERT SETTINGS
#
# Activate debug logs
# Value: on | off
WHIRLPOOL_DEBUG=off
# Activate more debug logs
# Value: on | off
WHIRLPOOL_DEBUG_CLIENT=off
'''
# Use sudo to write the file with elevated privileges
command = f'echo "{content}" | sudo tee {filepath} > /dev/null'
subprocess.run(command, shell=True)
# Create the configuration file
create_docker_whirlpool_conf()
def run_dojo_install():
directory = os.path.expanduser('~/dojo-app/samourai-dojo-v1.20.0/docker/my-dojo')
command = './dojo.sh install'
# Use sudo to execute the command with elevated privileges
subprocess.run(['sudo', '-E', 'bash', '-c', command], cwd=directory)
# Run the dojo.sh install command
run_dojo_install()
def run_dojo_onion():
directory = os.path.expanduser('~/dojo-app/samourai-dojo-v1.20.0/docker/my-dojo')
command = './dojo.sh onion'
# Use sudo to execute the command with elevated privileges
result = subprocess.run(['sudo', '-E', 'bash', '-c', command], cwd=directory, capture_output=True, text=True)
# Save the result to a file
with open('.PasswordsSamourai', 'w') as password_file:
password_file.write(result.stdout)
# Run the dojo.sh onion command and save the result to a file
run_dojo_onion()
def get_node_api_key_from_conf():
conf_directory = os.path.expanduser('~/dojo-app/samourai-dojo-v1.20.0/docker/my-dojo/conf')
filename = 'docker-node.conf.tpl'
filepath = os.path.join(conf_directory, filename)
with open(filepath, 'r') as file:
content = file.read()
# Find and extract the value of NODE_API_KEY
start_index = content.find('NODE_API_KEY=')
if start_index != -1:
start_index += len('NODE_API_KEY=')
end_index = content.find('\n', start_index)
if end_index != -1:
node_api_key = content[start_index:end_index].strip()
# Append the result to the existing .Passwords file
with open('.Passwords', 'a') as password_file:
password_file.write('\nNODE_API_KEY: ' + node_api_key)
else:
print("NODE_API_KEY not found in the file.")
else:
print("NODE_API_KEY not found in the file.")
# Get and append the NODE_API_KEY from the configuration file to the .Passwords file
get_node_api_key_from_conf()
def create_dojo_service(system_username, service_file_path):
# Define the systemd service unit content
service_unit = f"""\
[Unit]
Description=Dojo Docker Compose Stack
Requires=docker.service
After=docker.service
[Service]
ExecStart=/usr/bin/docker-compose up
ExecStop=/usr/bin/docker-compose down
WorkingDirectory=/home/humble/dojo-app/samourai-dojo-v1.20.0/docker/my-dojo/
Restart=always
[Install]
WantedBy=multi-user.target
"""
# Write the service unit content to a temporary file
temp_service_file = "/tmp/dojo.service"
with open(temp_service_file, "w") as file:
file.write(service_unit)
try:
# Use sudo to move the temporary file to the target directory
move_command = ["sudo", "mv", temp_service_file, service_file_path]
subprocess.run(move_command, check=True)
# Set the correct ownership and permissions for the service file
chown_command = ["sudo", "chown", "root:root", service_file_path]
subprocess.run(chown_command, check=True)
chmod_command = ["sudo", "chmod", "644", service_file_path]
subprocess.run(chmod_command, check=True)
# Reload systemd configuration
reload_command = ["sudo", "systemctl", "daemon-reload"]
subprocess.run(reload_command, check=True)
# Enable and start the dojo service
enable_command = ["sudo", "systemctl", "enable", "dojo.service"]
subprocess.run(enable_command, check=True)
start_command = ["sudo", "systemctl", "start", "dojo.service"]
subprocess.run(start_command, check=True)
print("Dojo service created, enabled, and started successfully.")
except Exception as e:
print(f"Error: {str(e)}")
def main():
system_username = "humble" # Replace with your system username
service_file_path = "/etc/systemd/system/dojo.service" # Replace with your desired target path
create_dojo_service(system_username, service_file_path)
if __name__ == "__main__":
main()