-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathinit-system.sh
More file actions
executable file
·350 lines (291 loc) · 9.13 KB
/
init-system.sh
File metadata and controls
executable file
·350 lines (291 loc) · 9.13 KB
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
#!/bin/bash
# Get the directory where this script is located
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Global variables
NO_VERIFY=false
CURRENT_DATE=$(date +%Y%m%d)
# ============================================================================
# Helper Functions
# ============================================================================
usage() {
echo "Usage: $0 [--no-verify] [path_list.txt]"
echo ""
echo "Copy system configuration files to their system locations."
echo "This script requires root privileges for system file operations."
echo ""
echo "Options:"
echo " --no-verify Skip interactive prompts (batch mode)"
echo ""
echo "Arguments:"
echo " path_list.txt Path list file (default: system-paths.txt)"
exit 1
}
generate_backup_path() {
local base_path="$1"
local backup_path="${base_path}.${CURRENT_DATE}"
local counter=1
while [ -e "$backup_path" ]; do
backup_path="${base_path}.${CURRENT_DATE}${counter}"
((counter++))
done
echo "$backup_path"
}
prompt_yes_no() {
local prompt="$1"
local response
if [ "$NO_VERIFY" = true ]; then
return 0 # Always yes in batch mode
fi
while true; do
read -r -p "$prompt [y/n]: " response </dev/tty
case "$response" in
[Yy]|[Yy][Ee][Ss])
return 0
;;
[Nn]|[Nn][Oo])
return 1
;;
*)
echo "Please answer yes or no."
;;
esac
done
}
check_root() {
if [ "$EUID" -ne 0 ]; then
echo "Error: This script must be run with root privileges."
echo "Please run with: sudo $0 $*"
exit 1
fi
}
normalize_path() {
local path="$1"
echo "${path%/}"
}
# ============================================================================
# Path Resolution Functions
# ============================================================================
resolve_source_path() {
local path="$1"
local source_path="${SCRIPT_DIR}/system/$path"
normalize_path "$source_path"
}
resolve_target_path() {
local path="$1"
local target_path="/$path"
normalize_path "$target_path"
}
# ============================================================================
# File Comparison Functions
# ============================================================================
files_are_identical() {
local file1="$1"
local file2="$2"
# Both must exist
[ -f "$file1" ] && [ -f "$file2" ] || return 1
# Use cmp for binary-safe comparison
cmp -s "$file1" "$file2"
return $?
}
# ============================================================================
# Copy Handling Functions
# ============================================================================
handle_existing_file() {
local target_path="$1"
local source_path="$2"
# Check if files are identical
if files_are_identical "$source_path" "$target_path"; then
echo "✓ File already up-to-date: '$target_path'"
return 0 # Skip - already identical
fi
echo "⚠ Target exists and differs: '$target_path'"
if prompt_yes_no "Replace with version from dotfiles?"; then
local backup_path=$(generate_backup_path "$target_path")
echo "Backing up to '$backup_path'."
cp -a "$target_path" "$backup_path"
return 1 # Proceed with copy
else
echo "Skipping."
return 0 # Skip
fi
}
handle_existing_directory() {
local target_path="$1"
local source_path="$2"
echo "⚠ Target directory exists: '$target_path'"
echo "Note: Directories cannot be idempotently copied."
if prompt_yes_no "Backup and replace directory with version from dotfiles?"; then
local backup_path=$(generate_backup_path "$target_path")
echo "Backing up to '$backup_path'."
cp -a "$target_path" "$backup_path"
return 1 # Proceed with copy
else
echo "Skipping."
return 0 # Skip
fi
}
handle_target() {
local target_path="$1"
local source_path="$2"
if [ -L "$target_path" ]; then
# Target is a symlink - treat as file
echo "⚠ Target is a symlink: '$target_path'"
if prompt_yes_no "Replace symlink with actual file?"; then
local backup_path=$(generate_backup_path "$target_path")
echo "Backing up symlink to '$backup_path'."
cp -a "$target_path" "$backup_path"
rm "$target_path"
return 1 # Proceed with copy
else
echo "Skipping."
return 0 # Skip
fi
elif [ -d "$target_path" ]; then
# Target is a directory
if [ -d "$source_path" ]; then
handle_existing_directory "$target_path" "$source_path"
return $?
else
echo "⚠ Type mismatch: target is directory, source is file"
echo "Skipping."
return 0 # Skip
fi
elif [ -f "$target_path" ]; then
# Target is a regular file
if [ -f "$source_path" ]; then
handle_existing_file "$target_path" "$source_path"
return $?
else
echo "⚠ Type mismatch: target is file, source is directory"
echo "Skipping."
return 0 # Skip
fi
fi
return 1 # Target doesn't exist, proceed with creation
}
copy_file() {
local source_path="$1"
local target_path="$2"
local target_dir=$(dirname "$target_path")
# Create parent directory if needed
if [ ! -d "$target_dir" ]; then
echo "Creating directory '$target_dir'."
mkdir -p "$target_dir"
fi
# Copy the file/directory preserving attributes
if cp -a "$source_path" "$target_path"; then
echo "✓ Copied: '$source_path' -> '$target_path'"
return 0
else
echo "✗ Failed to copy: '$source_path' -> '$target_path'"
return 1
fi
}
# ============================================================================
# Main Processing Functions
# ============================================================================
process_path_entry() {
local path="$1"
# Trim whitespace
path=$(echo "$path" | xargs)
# Skip empty lines and comments
[[ -z "$path" || "$path" == \#* ]] && return 0
echo "Processing path: '$path'"
local source_path=$(resolve_source_path "$path")
local target_path=$(resolve_target_path "$path")
# Validate source exists
if [ ! -e "$source_path" ]; then
echo "Warning: Source '$source_path' does not exist. Skipping."
echo "----------------------------------------"
return 1
fi
# Handle existing target
local handle_result
handle_target "$target_path" "$source_path"
handle_result=$?
if [ $handle_result -eq 0 ]; then
# Skip was requested or already up-to-date
echo "----------------------------------------"
return 0
fi
# Copy the file/directory
copy_file "$source_path" "$target_path"
echo "----------------------------------------"
return 0
}
process_path_file() {
local path_file="$1"
while IFS= read -r path || [ -n "$path" ]; do
process_path_entry "$path"
done < "$path_file"
}
# ============================================================================
# Initialization and Main
# ============================================================================
print_header() {
local path_file="$1"
echo "Starting system file copy process..."
echo "Reading paths from '$path_file'"
echo "Current Date for backups: $CURRENT_DATE"
if [ "$NO_VERIFY" = true ]; then
echo "Mode: Batch (--no-verify enabled)"
fi
echo "----------------------------------------"
}
main() {
# Parse arguments first (not in subshell to allow usage() to exit properly)
local path_file=""
while [[ "$#" -gt 0 ]]; do
case "$1" in
--no-verify)
NO_VERIFY=true
shift
;;
-h|--help)
usage
;;
-*)
echo "Error: Unknown option '$1'"
usage
;;
*)
path_file="$1"
shift
;;
esac
done
# Default to system-paths.txt if not specified
if [ -z "$path_file" ]; then
path_file="${SCRIPT_DIR}/system-paths.txt"
fi
[ -f "$path_file" ] || { echo "Error: File '$path_file' does not exist or is not a regular file."; exit 1; }
# Check for root privileges
check_root "$@"
print_header "$path_file"
process_path_file "$path_file"
# Set up MUTILS_DOTFILES_DIR as a system-wide environment variable
echo "----------------------------------------"
echo "Configuring m-utils system environment..."
local dotfiles_dir="$SCRIPT_DIR"
local env_file="/etc/environment"
# Add to /etc/environment for system-wide availability
if ! grep -q "MUTILS_DOTFILES_DIR" "$env_file" 2>/dev/null; then
echo "MUTILS_DOTFILES_DIR=\"$dotfiles_dir\"" >> "$env_file"
echo "✓ Added MUTILS_DOTFILES_DIR to /etc/environment"
echo " Note: New shells will need to be started for this to take effect"
else
# Update existing value if different
local current_value=$(grep "MUTILS_DOTFILES_DIR" "$env_file" | cut -d'=' -f2- | tr -d '"')
if [ "$current_value" != "$dotfiles_dir" ]; then
sed -i "s|MUTILS_DOTFILES_DIR=.*|MUTILS_DOTFILES_DIR=\"$dotfiles_dir\"|" "$env_file"
echo "✓ Updated MUTILS_DOTFILES_DIR in /etc/environment"
else
echo "✓ MUTILS_DOTFILES_DIR already correctly set in /etc/environment"
fi
fi
echo " MUTILS_DOTFILES_DIR=$dotfiles_dir"
echo " (System-wide environment variable)"
echo "System file copy process completed successfully."
}
# Run main function
main "$@"