-
-
Notifications
You must be signed in to change notification settings - Fork 41
Expand file tree
/
Copy pathsetup.sh
More file actions
executable file
·1811 lines (1556 loc) · 66.3 KB
/
setup.sh
File metadata and controls
executable file
·1811 lines (1556 loc) · 66.3 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
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
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#!/bin/bash
# LinOffice Setup Script
CONTAINER_NAME="LinOffice" # should match the name in the compose.yaml
CONTAINER_EXISTS=0 # 0 = Does not exist (default), 1 = exists
# Absolute filepaths
USER_APPLICATIONS_DIR="${HOME}/.local/share/applications"
APPDATA_PATH="${HOME}/.local/share/linoffice"
# Ensure APPDATA_PATH exists before using it
mkdir -p "$APPDATA_PATH"
SUCCESS_FILE="${APPDATA_PATH}/success"
PROGRESS_FILE="${APPDATA_PATH}/setup_progress.log"
OUTPUT_LOG="${APPDATA_PATH}/setup_output.log"
# Relative filepaths
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
LINOFFICE_DIR="$SCRIPT_DIR"
LINOFFICE="$(realpath "${SCRIPT_DIR}/linoffice.sh")"
COMPOSE_FILE="$(realpath "${SCRIPT_DIR}/config/compose.yaml")"
LINOFFICE_CONF="$(realpath "${SCRIPT_DIR}/config/linoffice.conf")"
OEM_DIR="$(realpath "${SCRIPT_DIR}/config/oem")"
LOCALE_REG_SCRIPT="$(realpath "${SCRIPT_DIR}/config/locale_reg.sh")"
LOCALE_LANG_SCRIPT="$(realpath "${SCRIPT_DIR}/config/locale_lang.sh")"
REGIONAL_REG="$(realpath "${SCRIPT_DIR}/config/oem/registry/regional_settings.reg")"
LOGFILE="${APPDATA_PATH}/windows_install.log"
APPS_DIR="$(realpath "${SCRIPT_DIR}/apps")"
DESKTOP_DIR="$(realpath "${APPS_DIR}/desktop")"
# Available and working freerdp commands
EXISTS_XFREERDP=false
EXISTS_XFREERDP3=false
EXISTS_FLATPAK_FREERDP=false
FREERDP_COMMAND="" # will be checked in the script whether it's xfreerdp, xfreerdp3, or the Flatpak version
FREERDP_SEC_RDP=false
FREERDP_NETWORK_LAN=false
FREERDP_NSC=false
FREERDP_XWAYLAND=false
# Progress tracking states
PROGRESS_REQUIREMENTS="requirements_completed"
PROGRESS_CONTAINER="container_created"
PROGRESS_OFFICE="office_installed"
PROGRESS_DESKTOP="desktop_files_installed"
# Command line arguments
DESKTOP_ONLY=false
FIRSTRUN=false
INSTALL_OFFICE_ONLY=false
HEALTHCHECK=false
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
USE_VENV=0
COMPOSE_COMMAND="podman-compose"
# Redirect all output to the log file
exec > >(stdbuf -oL -eL sed -u 's/\x1b\[[0-9;]*m//g' | tee -a "$OUTPUT_LOG") 2>&1
# Functions to print colored output
print_error() {
echo -e "${RED}ERROR:${NC} $1" >&2
}
print_success() {
echo -e "${GREEN}SUCCESS:${NC} $1"
}
print_info() {
echo -e "${YELLOW}INFO:${NC} $1"
}
print_step() {
echo -e "\n${GREEN}Step $1:${NC} $2"
}
print_progress() {
echo -e "${GREEN}Progress:${NC} $1"
}
# Name: 'use_venv'
# Role: Activate virtual environment if available
use_venv() {
local venv_dir="$HOME/.local/bin/linoffice/venv"
local activate_script="$venv_dir/bin/activate"
print_info "Checking for virtual environment at: $venv_dir"
if [[ -f "$activate_script" ]]; then
print_info "Virtual environment found at $venv_dir"
source "$activate_script"
VENV_PATH="$venv_dir"
USE_VENV=1
PYTHON_PATH="$venv_dir/bin/python3"
print_info "Virtual environment Python: $PYTHON_PATH"
# Prefer the entrypoint inside the venv
if [[ -x "$venv_dir/bin/podman-compose" ]]; then
PODMAN_COMPOSE_BIN="$venv_dir/bin/podman-compose"
COMPOSE_COMMAND="$PODMAN_COMPOSE_BIN"
print_info "Using podman-compose from virtual environment: $PODMAN_COMPOSE_BIN"
return 0
fi
# Fallback: module exists in venv but entrypoint missing
if "$PYTHON_PATH" - <<'PY' >/dev/null 2>&1
import importlib.util
spec = importlib.util.find_spec("podman_compose")
raise SystemExit(0 if spec else 1)
PY
then
COMPOSE_COMMAND="$PYTHON_PATH -m podman_compose"
PODMAN_COMPOSE_BIN="$COMPOSE_COMMAND"
print_info "Using podman-compose module from virtual environment"
return 0
fi
print_info "podman-compose not found in virtual environment"
print_info "Checking if virtual environment can access system podman-compose..."
# Check if venv can access system podman-compose (due to --system-site-packages)
if command -v podman-compose &> /dev/null; then
PODMAN_COMPOSE_BIN="$(command -v podman-compose)"
COMPOSE_COMMAND="$PODMAN_COMPOSE_BIN"
print_info "Virtual environment can access system podman-compose"
return 0
else
print_info "podman-compose not available in virtual environment or system"
print_info "Will check for system podman-compose instead"
# Don't return here - let the system check handle it
USE_VENV=0 # Reset to system mode since venv doesn't have podman-compose
return 1
fi
else
print_info "Virtual environment not found at $venv_dir, using system Python"
return 1
fi
}
validate_podman_compose() {
local candidate="$1"
local resolved="$candidate"
if [[ "$candidate" != /* ]]; then
resolved=$(command -v "$candidate" 2>/dev/null || true)
fi
if [[ -z "$resolved" || ! -f "$resolved" ]]; then
return 1
fi
# If it's a script, make sure its interpreter exists
local shebang
shebang=$(head -n1 "$resolved" 2>/dev/null || true)
if [[ "$shebang" == "#!"* ]]; then
local interpreter="${shebang#\#!}"
read -r interpreter _ <<<"${interpreter}"
if [[ ! -x "$interpreter" ]]; then
print_info "podman-compose at $resolved points to missing interpreter: $interpreter"
return 1
fi
fi
if ! "$candidate" --version >/dev/null 2>&1; then
print_info "podman-compose at $resolved failed to run"
return 1
fi
return 0
}
use_venv
# Function to display usage information
print_usage() {
print_info "Usage: $0 [--desktop] [--firstrun] [--installoffice] [--healthcheck]"
print_info "Options:"
print_info " (no flag) Run the installation script from the beginning"
print_info " --desktop Only recreate the desktop files (.desktop launchers)"
print_info " --firstrun Force RDP and Office installation checks"
print_info " --installoffice Only run the Office installation script script (in case the Windows installation has finished but Office is not installed)"
print_info " --healthcheck Check that the system requirements are met and dependencies are installed and the container is healthy"
exit 1
}
# Parse command line arguments
while [[ $# -gt 0 ]]; do
case $1 in
--desktop)
DESKTOP_ONLY=true
shift
;;
--firstrun)
FIRSTRUN=true
shift
;;
--installoffice)
INSTALL_OFFICE_ONLY=true
shift
;;
--healthcheck)
HEALTHCHECK=true
shift
;;
--help)
print_usage
;;
*)
print_error "Unknown option: $1"
print_usage
;;
esac
done
# Function to exit with error
exit_with_error() {
print_error "$1"
exit 1
}
# Progress tracking functions
function init_progress_file() {
mkdir -p "$(dirname "$PROGRESS_FILE")"
touch "$PROGRESS_FILE"
}
function mark_progress() {
local step=$1
echo "$step" >> "$PROGRESS_FILE"
}
function check_progress() {
local step=$1
if [ -f "$PROGRESS_FILE" ] && grep -q "^$step$" "$PROGRESS_FILE"; then
return 0
else
return 1
fi
}
function clear_progress() {
if [ -f "$PROGRESS_FILE" ]; then
rm "$PROGRESS_FILE"
fi
}
function check_linoffice_container() {
print_info "Checking if LinOffice container exists already"
if podman container exists "$CONTAINER_NAME"; then
print_info "Container exists already."
CONTAINER_EXISTS=1
else
print_info "Container does not yet exist."
CONTAINER_EXISTS=0
fi
}
# Function to detect and set the FreeRDP command
function detect_freerdp_command() {
# Determine availability of all FreeRDP variants and set EXISTS_* flags
EXISTS_XFREERDP=false
EXISTS_XFREERDP3=false
EXISTS_FLATPAK_FREERDP=false
if command -v xfreerdp3 &>/dev/null; then
EXISTS_XFREERDP3=true
fi
if command -v xfreerdp &>/dev/null; then
EXISTS_XFREERDP=true
fi
if command -v flatpak &>/dev/null; then
if flatpak list --columns=application | grep -q "^com.freerdp.FreeRDP$"; then
EXISTS_FLATPAK_FREERDP=true
fi
fi
# Set FREERDP_COMMAND to the first available in priority order: xfreerdp3 > flatpak > xfreerdp
if [ "$EXISTS_XFREERDP3" = true ]; then
FREERDP_COMMAND="xfreerdp3"
elif [ "$EXISTS_FLATPAK_FREERDP" = true ]; then
FREERDP_COMMAND="flatpak run --command=xfreerdp com.freerdp.FreeRDP"
elif [ "$EXISTS_XFREERDP" = true ]; then
FREERDP_COMMAND="xfreerdp"
else
FREERDP_COMMAND="" # Not found
fi
}
function flatpak_freerdp_has_home_access() {
# Check for "home" token in the filesystems permission list.
# Matches formats such as home, home:ro, and entries in semicolon-separated lists.
local permissions
permissions=$(flatpak info --show-permissions com.freerdp.FreeRDP 2>/dev/null || true)
echo "$permissions" | grep -Eq 'filesystems=.*(^|[;[:space:]])home(:[a-z]+)?([;[:space:]]|$)'
}
# Function to check if all requirements are met to run the Windows VM in Podman
function check_requirements() {
# Exit on any error
set -e
print_info "Starting LinOffice setup script..."
print_step "1" "Checking requirements"
# Check minimum RAM (8 GB)
print_info "Checking minimum RAM"
local ram_line=$((LINENO + 1))
REQUIRED_RAM=7 # 8 GB shows up as 7.6 GiB so best to just set the threshold to 7 in this script
AVAILABLE_RAM="$(free -b | awk '/^Mem:/{print int($2/1024/1024/1024)}')"
if [ "$AVAILABLE_RAM" -lt "$REQUIRED_RAM" ]; then
exit_with_error "Insufficient RAM. Required: ${REQUIRED_RAM}GB, Available: ${AVAILABLE_RAM}GB. \
Please upgrade your system memory to at least ${REQUIRED_RAM}GB.
The Windows VM needs 4 GB of RAM. If you still want to continue with the installation, for example if you are using zswap, you can change the minimum RAM required by editing line $ram_line in $SCRIPT_DIR/setup.sh and then run the setup again."
fi
print_success "Sufficient RAM detected: ${AVAILABLE_RAM}GB"
# Check minimum free storage (64 GB)
check_linoffice_container
if [ "$CONTAINER_EXISTS" -eq 1 ]; then
print_info "Container exists already. Skipping check if sufficient free storage is available."
else
print_info "Checking minimum free storage"
REQUIRED_STORAGE=64
AVAILABLE_STORAGE=$(df -B1G --output=avail /home | tail -n 1 | awk '{print $1}')
if [ "$AVAILABLE_STORAGE" -lt "$REQUIRED_STORAGE" ]; then
exit_with_error "Insufficient free storage. Required: ${REQUIRED_STORAGE}GB, Available: ${AVAILABLE_STORAGE}GB \
Please free up disk space or use a different storage device."
fi
print_success "Sufficient free storage detected: ${AVAILABLE_STORAGE}GB"
fi
# Check if computer supports virtualization
print_info "Checking virtualization support"
if ! command -v lscpu &> /dev/null; then
exit_with_error "lscpu command not found. Please install util-linux package."
fi
# Check for virtualization support
if lscpu | grep -qiE 'virtualization|vmx|svm'; then
echo "Virtualization is supported."
else
exit_with_error "CPU virtualization not supported or not enabled.
HOW TO FIX:
1. Reboot your computer and enter BIOS/UEFI settings (usually F2, F12, Del, or Esc during boot)
2. Look for virtualization settings:
- Intel: Enable 'Intel VT-x' or 'Intel Virtualization Technology'
- AMD: Enable 'AMD-V' or 'SVM Mode'
3. Save settings and reboot
4. If you can't find these options, your CPU may not support virtualization"
fi
# Additional check for KVM support
if [ ! -e /dev/kvm ]; then
exit_with_error "KVM device not available. Virtualization may not be enabled in BIOS.
HOW TO FIX:
1. Ensure virtualization is enabled in BIOS (see previous instructions)
2. Install KVM kernel modules: sudo modprobe kvm
3. For Intel CPUs: sudo modprobe kvm_intel
4. For AMD CPUs: sudo modprobe kvm_amd
5. Reboot if necessary"
fi
print_success "Virtualization support detected: $VIRT_SUPPORT"
# Check if podman is installed
print_info "Checking if podman is installed"
if ! command -v podman &> /dev/null; then
exit_with_error "podman is not installed.
HOW TO FIX:
Ubuntu/Debian: sudo apt update && sudo apt install podman
Fedora/RHEL: sudo dnf install podman
OpenSUSE: sudo zypper install podman
Arch Linux: sudo pacman -S podman
openSUSE: sudo zypper install podman
Or visit: https://podman.io/getting-started/installation"
fi
if ! podman info >/dev/null 2>&1; then
exit_with_error "Podman is not configured correctly or you lack sufficient permissions. Run 'podman info' to diagnose the issue."
fi
PODMAN_VERSION=$(podman --version)
print_success "podman is installed: $PODMAN_VERSION"
# Check if podman-compose is installed
print_info "Checking if podman-compose is installed"
print_info "Python environment: $(if [[ "$USE_VENV" -eq 1 ]]; then echo "Virtual environment at $VENV_PATH"; else echo "System Python"; fi)"
# Determine which Python to use for dependency checks
if [[ "$USE_VENV" -eq 1 ]]; then
PYTHON_CMD="$PYTHON_PATH"
PYTHON_ENV="virtual environment"
else
PYTHON_CMD="python3"
PYTHON_ENV="system"
fi
if [[ "$USE_VENV" -eq 0 ]]; then
# Use system podman-compose, and avoid stale user-level wrappers
if [[ -x "/usr/bin/podman-compose" ]] && validate_podman_compose "/usr/bin/podman-compose"; then
COMPOSE_COMMAND="/usr/bin/podman-compose"
print_success "Using system podman-compose: /usr/bin/podman-compose"
elif command -v podman-compose &> /dev/null; then
PODMAN_COMPOSE_PATH=$(command -v podman-compose)
if validate_podman_compose "$PODMAN_COMPOSE_PATH"; then
COMPOSE_COMMAND="$PODMAN_COMPOSE_PATH"
print_success "Using podman-compose from PATH: $PODMAN_COMPOSE_PATH"
elif [[ -x "/usr/bin/podman-compose" ]] && validate_podman_compose "/usr/bin/podman-compose"; then
COMPOSE_COMMAND="/usr/bin/podman-compose"
print_success "Found broken podman-compose at $PODMAN_COMPOSE_PATH, falling back to /usr/bin/podman-compose"
print_info "You can remove the stale file with: rm -f \"$PODMAN_COMPOSE_PATH\""
else
exit_with_error "podman-compose is present at $PODMAN_COMPOSE_PATH but cannot run (likely a stale venv-generated wrapper).
HOW TO FIX:
1. Remove the broken wrapper: rm -f $PODMAN_COMPOSE_PATH
2. Reinstall podman-compose (e.g. dnf install podman-compose, apt install podman-compose, or pip3 install --user podman-compose)"
fi
else
exit_with_error "podman-compose is not installed.
HOW TO FIX:
Option 1 - Using pip: pip3 install podman-compose
Option 2 - Using package manager:
Ubuntu/Debian: sudo apt install podman-compose
Fedora: sudo dnf install podman-compose
OpenSUSE: sudo zypper install podman-compose
Arch Linux: sudo pacman -S podman-compose
Or visit: https://github.com/containers/podman-compose"
fi
# Check if python-dotenv is installed (dependency of podman-compose)
if ! command -v $PYTHON_CMD &> /dev/null; then
exit_with_error "$PYTHON_CMD command not found. Please install Python 3."
fi
# Show which Python is being used for debugging
PYTHON_FULL_PATH=$(command -v $PYTHON_CMD)
print_info "Using $PYTHON_ENV Python: $PYTHON_FULL_PATH"
if $PYTHON_CMD -c "import dotenv" >/dev/null 2>&1; then
print_success "python-dotenv is installed in $PYTHON_ENV environment."
else
# Provide more detailed error information
print_error "python-dotenv is not installed or not accessible to $PYTHON_FULL_PATH"
print_info "Python version: $($PYTHON_CMD --version 2>&1)"
print_info "Python path: $PYTHON_FULL_PATH"
print_info "Available packages: $($PYTHON_CMD -m pip list 2>/dev/null | grep -i dotenv || echo 'No dotenv packages found')"
exit_with_error "python-dotenv is not installed in $PYTHON_ENV environment.
HOW TO FIX:
Using pip: $PYTHON_CMD -m pip install python-dotenv
If you don't have pip, you can install it with your package manager.
Ubuntu/Debian: sudo apt install python-dotenv
Fedora: sudo dnf install python-dotenv
OpenSUSE: sudo zypper install python3-python-dotenv
Arch Linux: sudo pacman -S python-dotenv
If the package is installed but not detected, try:
- Check if you have multiple Python versions: which python3
- Install for the specific Python: $PYTHON_FULL_PATH -m pip install python-dotenv"
fi
else
# When using virtual environment, check if podman-compose is installed in venv
if [[ -z "$COMPOSE_COMMAND" ]]; then
exit_with_error "podman-compose is not installed in virtual environment.
HOW TO FIX:
The virtual environment needs podman-compose installed.
Run: $PYTHON_PATH -m pip install podman-compose"
fi
if ! eval "$COMPOSE_COMMAND --version" >/dev/null 2>&1; then
exit_with_error "podman-compose command '$COMPOSE_COMMAND' failed to run from virtual environment.
HOW TO FIX:
Reinstall inside the venv: $PYTHON_PATH -m pip install --force-reinstall podman-compose"
else
print_success "podman-compose is installed in virtual environment: $COMPOSE_COMMAND"
fi
# Check if python-dotenv is installed in virtual environment (dependency of podman-compose)
if $PYTHON_CMD -c "import dotenv" >/dev/null 2>&1; then
print_success "python-dotenv is installed in virtual environment."
else
print_error "python-dotenv is not installed in virtual environment."
print_info "Python version: $($PYTHON_CMD --version 2>&1)"
print_info "Python path: $PYTHON_CMD"
print_info "Available packages: $($PYTHON_CMD -m pip list 2>/dev/null | grep -i dotenv || echo 'No dotenv packages found')"
print_info "User site packages: $($PYTHON_CMD -m site --user-site 2>/dev/null || echo 'Unknown')"
# Check if it's available in user packages (installed with --user flag)
USER_SITE_PACKAGES=$($PYTHON_CMD -m site --user-site 2>/dev/null)
if [[ -n "$USER_SITE_PACKAGES" ]] && [[ -d "$USER_SITE_PACKAGES" ]] && find "$USER_SITE_PACKAGES" -name "*dotenv*" -type d 2>/dev/null | grep -q .; then
print_info "python-dotenv found in user site packages but not importable"
print_info "This might be due to virtual environment configuration issues"
fi
exit_with_error "python-dotenv is not installed in virtual environment.
HOW TO FIX:
The virtual environment needs python-dotenv installed.
Run: $PYTHON_CMD -m pip install python-dotenv
If packages were installed with --user flag by quickstart.sh, try:
$PYTHON_CMD -m pip install --user python-dotenv"
fi
fi
if ! COMPOSE_VERSION=$($COMPOSE_COMMAND --version 2>/dev/null); then
exit_with_error "podman-compose command '$COMPOSE_COMMAND' failed to run. Please reinstall podman-compose or remove stale copies in ~/.local/bin."
fi
print_success "podman-compose is installed: $COMPOSE_VERSION"
# Check if FreeRDP is available
print_info "Checking if FreeRDP is available"
detect_freerdp_command
local FREERDP_MAJOR_VERSION=""
if [ -n "$FREERDP_COMMAND" ]; then
if [ "$FREERDP_COMMAND" = "xfreerdp" ]; then
FREERDP_MAJOR_VERSION=$(xfreerdp --version | head -n 1 | grep -o -m 1 '\b[0-9]\S*' | head -n 1 | cut -d'.' -f1)
elif [ "$FREERDP_COMMAND" = "xfreerdp3" ]; then
FREERDP_MAJOR_VERSION=$(xfreerdp3 --version | head -n 1 | grep -o -m 1 '\b[0-9]\S*' | head -n 1 | cut -d'.' -f1)
elif [ "$FREERDP_COMMAND" = "flatpak run --command=xfreerdp com.freerdp.FreeRDP" ]; then
FREERDP_MAJOR_VERSION=$(flatpak list --columns=application,version | grep "^com.freerdp.FreeRDP" | awk '{print $2}' | cut -d'.' -f1)
# Ensure Flatpak FreeRDP has access to /home.
if ! flatpak_freerdp_has_home_access; then
print_info "Flatpak FreeRDP does not currently have /home access. Attempting to apply user override..."
if ! flatpak override --user --filesystem=home com.freerdp.FreeRDP >/dev/null 2>&1; then
exit_with_error "Failed to grant Flatpak FreeRDP access to /home automatically.
HOW TO FIX:
1. Close any running FreeRDP instances
2. Run this command manually:
flatpak override --user --filesystem=home com.freerdp.FreeRDP
3. Run this setup script again"
fi
if ! flatpak_freerdp_has_home_access; then
exit_with_error "Flatpak FreeRDP still does not have access to /home directory.
HOW TO FIX:
1. Close any running FreeRDP instances
2. Run this command manually:
flatpak override --user --filesystem=home com.freerdp.FreeRDP
3. Verify permissions:
flatpak info --show-permissions com.freerdp.FreeRDP
4. Run this setup script again"
fi
print_success "Granted Flatpak FreeRDP access to /home."
fi
fi
if [[ ! $FREERDP_MAJOR_VERSION =~ ^[0-9]+$ ]] || ((FREERDP_MAJOR_VERSION < 3)); then
exit_with_error "FreeRDP version 3 or greater is required. Detected version: $FREERDP_MAJOR_VERSION"
fi
else
exit_with_error "FreeRDP is not installed
HOW TO FIX:
Option 1 - Using Flatpak and Flathub: flatpak install com.freerdp.FreeRDP
Option 2 - Using package manager:
Ubuntu/Debian: sudo apt install freerdp3-x11
Fedora: sudo dnf install freerdp
OpenSUSE: sudo zypper install freerdp
Arch Linux: sudo pacman -S freerdp"
fi
if ! $FREERDP_COMMAND --version >/dev/null 2>&1; then
exit_with_error "FreeRDP command '$FREERDP_COMMAND' is not functional. Please ensure FreeRDP is correctly installed and configured."
fi
print_success "FreeRDP found. Using FreeRDP command '${FREERDP_COMMAND}'."
# Check if iptables modules are loaded
print_info "Checking iptables kernel modules"
if ! lsmod | grep -q ip_tables || ! lsmod | grep -q iptable_nat; then
print_info "WARNING: iptables kernel modules not loaded. Sharing the /home folder with the Windows VM will not work unless connected via RDP. HOW TO FIX:
Run the following command:
echo -e 'ip_tables\niptable_nat' | sudo tee /etc/modules-load.d/iptables.conf
Then reboot your system."
fi
print_success "iptables modules are loaded"
# Check if most important LinOffice files exist
print_info "Checking for essential setup files"
if [ ! -d "$OEM_DIR" ]; then
exit_with_error "OEM files not found
Please ensure the config/oem directory exists"
fi
# Check OEM directory permissions
if [ ! -r "$OEM_DIR" ] || [ ! -x "$OEM_DIR" ] || ! find "$OEM_DIR" -type f -readable | head -1 >/dev/null 2>&1; then
exit_with_error "Insufficient permissions to access OEM directory: $OEM_DIR
HOW TO FIX:
1. Check directory permissions: ls -ld $OEM_DIR
2. Fix permissions: chmod -R u+rwX $OEM_DIR
3. If using SELinux/AppArmor, you may need to adjust security contexts"
fi
# Check if compose.yaml exists
if [ ! -f "$COMPOSE_FILE.default" ]; then
exit_with_error "Compose file not found: $COMPOSE_FILE.default
Please ensure the file exists in the config directory."
fi
# Check if LinOffice script exists
if [ ! -f "$LINOFFICE_CONF.default" ]; then
exit_with_error "LinOffice configuration file not found: $LINOFFICE_CONF.default
Please ensure the file exists in the config directory."
fi
if [ ! -f "$LINOFFICE" ]; then
exit_with_error "File not found: $LINOFFICE"
fi
print_success "Files found."
# Make scripts executable
print_info "Making scripts executable"
if [ ! -f "$LINOFFICE" ]; then
exit_with_error "File not found: $LINOFFICE
Please ensure the script is in the same directory as this setup script."
fi
if [ ! -f "$LOCALE_REG_SCRIPT" ]; then
exit_with_error "File not found: $LOCALE_REG_SCRIPT
Please ensure the config directory and locale_reg.sh script exist."
fi
if [ ! -f "$LOCALE_LANG_SCRIPT" ]; then
exit_with_error "File not found: $LOCALE_LANG_SCRIPT
Please ensure the config directory and local_compose.sh script exist."
fi
chmod +x "$LINOFFICE" || exit_with_error "Failed to make $LINOFFICE executable"
chmod +x "$LOCALE_REG_SCRIPT" || exit_with_error "Failed to make $LOCALE_REG_SCRIPT executable"
chmod +x "$LOCALE_LANG_SCRIPT" || exit_with_error "Failed to make $LOCALE_LANG_SCRIPT executable"
print_success "Made scripts executable"
# Check for various potential Podman problems
# Check subUID/subGID mappings as some users had problems here
print_info "Checking subUID/subGID mappings"
if ! grep -q "^$(whoami):" /etc/subuid || ! grep -q "^$(whoami):" /etc/subgid; then
exit_with_error "Missing subUID/subGID mappings for the user.
HOW TO FIX:
1. Run: sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 $(whoami)
2. Refresh Podman configuration: podman system migrate
3. Verify mappings in /etc/subuid and /etc/subgid"
fi
print_success "subUID/subGID mappings verified."
# Check Podman storage configuration and whether overlay storage driver is used, as some users had problems here
print_info "Checking Podman storage configuration"
driver=$(podman info --format '{{.Store.GraphDriverName}}' 2>/dev/null)
error_msg="Podman is not using overlay or btrfs as the storage driver or there's a configuration issue.
HOW TO FIX:
mkdir -p ~/.config/containers
cat > ~/.config/containers/storage.conf <<EOF
[storage]
driver = \"overlay\" # or \"btrfs\"
runroot = \"/run/user/\$(id -u)/containers\"
graphroot = \"\$HOME/.local/share/containers/storage\"
EOF
podman system migrate
Then verify with:
podman info --format '{{.Store.GraphDriverName}}'
Expected: overlay or btrfs"
if ! echo "$driver" | grep -qE "overlay|btrfs"; then
if [ -z "$driver" ]; then
print_info "Podman storage driver is not set. Setting up with overlay storage driver"
mkdir -p ~/.config/containers
cat > ~/.config/containers/storage.conf <<EOF
[storage]
driver = "overlay"
runroot = "/run/user/\$(id -u)/containers"
graphroot = "\$HOME/.local/share/containers/storage"
EOF
# Check running containers before migrate
running_containers=$(podman ps --format '{{.Names}}' 2>/dev/null)
if [ -n "$running_containers" ] && ! [[ "$running_containers" == "LinOffice" ]]; then
# Multiple or non-LinOffice: Prompt user
echo "PROMPT:PODMAN_MIGRATE"
while true; do
read -p "WARNING: podman system migrate will stop ALL running containers and reconfigure storage. This may disrupt other workloads. Continue? (y/n): " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
break
elif [[ $REPLY =~ ^[Nn]$ ]] || [ -z "$REPLY" ]; then
exit_with_error "Setup aborted by user. Please run the script again when ready to migrate."
fi
done
fi
podman system migrate
new_driver=$(podman info --format '{{.Store.GraphDriverName}}' 2>/dev/null)
if ! echo "$new_driver" | grep -qE "overlay|btrfs"; then
exit_with_error "$error_msg"
fi
else
exit_with_error "$error_msg"
fi
fi
# Determine if running rootless or rootful
if podman info --format '{{.Host.Security.Rootless}}' | grep -q true; then
IS_ROOTLESS=true
STORAGE_DIR="$HOME/.local/share/containers/storage"
else
IS_ROOTLESS=false
STORAGE_DIR="/var/lib/containers/storage"
fi
print_info "Podman running in $( $IS_ROOTLESS && echo 'rootless' || echo 'rootful' ) mode"
# Set network directory paths based on rootless status first
if [ "$IS_ROOTLESS" = true ]; then
NETAVARK_DIR="$HOME/.local/share/containers/networks"
CNI_DIR="$HOME/.config/cni/net.d"
else
NETAVARK_DIR="/var/lib/containers/networks"
CNI_DIR="/etc/cni/net.d"
fi
# Now select the correct directory based on the network backend
if [ "$NETWORK_BACKEND" = "netavark" ]; then
NETWORK_DIR="$NETAVARK_DIR"
else
# Default to CNI backend
NETWORK_DIR="$CNI_DIR"
fi
print_info "Podman network directory: $NETWORK_DIR"
# Check if storage directory is accessible
if [ ! -d "$STORAGE_DIR" ] || [ ! -w "$STORAGE_DIR" ]; then
exit_with_error "Podman storage directory inaccessible: $STORAGE_DIR
1. Check if directory exists: ls -ld \"$STORAGE_DIR\"
2. If it exists, fix permissions: $( $IS_ROOTLESS && echo "chmod -R u+rwX \"$STORAGE_DIR\"" || echo "sudo chmod -R u+rwX \"$STORAGE_DIR\"" )
3. If it does not exist, initialize Podman: podman info"
fi
print_success "Podman storage directory verified: $STORAGE_DIR"
# Check which networking backend is in use
print_info "Checking Podman networking is working"
NETWORK_BACKEND=$(podman info --format '{{.Host.NetworkBackend}}' 2>/dev/null)
if [ -z "$NETWORK_BACKEND" ]; then
exit_with_error "Failed to detect Podman's network backend. Make sure Podman is correctly installed and accessible to your user. Run 'podman info' to diagnose."
fi
print_info "Podman is using network backend: $NETWORK_BACKEND"
# Test network creation for all backends
TEST_NET_NAME="linoffice_net_test_$(date +%s)"
print_info "Testing network creation with backend: $NETWORK_BACKEND"
if ! podman network create "$TEST_NET_NAME" >/dev/null 2>&1; then
exit_with_error "Failed to create test network '$TEST_NET_NAME'.
HOW TO FIX:
1. Check Podman logs: journalctl -u podman
2. $( $IS_ROOTLESS && echo 'Ensure user has sufficient permissions.' || echo 'Run as root or check sudo permissions.' )
3. Reinstall network backend:
- For netavark: $( $IS_ROOTLESS && echo 'podman system reset && podman info' || echo 'sudo dnf reinstall netavark || sudo apt install netavark' )
- For CNI: Ensure CNI plugins are installed (e.g., sudo dnf install containernetworking-plugins)
4. Verify SELinux/AppArmor settings if enabled."
fi
print_success "Test network '$TEST_NET_NAME' created successfully."
# Check that network directory exists
if [ ! -d "$NETWORK_DIR" ]; then
print_info "WARNING: Network directory does not exist: $NETWORK_DIR
This might lead to errors."
fi
if [ ! -w "$NETWORK_DIR" ]; then
print_info "WARNING: Network directory not writable: $NETWORK_DIR
This might lead to errors."
fi
# Clean up test network
if podman network exists "$TEST_NET_NAME" >/dev/null 2>&1; then
podman network rm "$TEST_NET_NAME" >/dev/null 2>&1 || print_info "Note: Failed to remove test network '$TEST_NET_NAME', you may remove it manually."
fi
print_success "Podman networking check completed."
# Test basic container creation to catch storage issues early
print_info "Testing basic container functionality..."
if ! timeout 60 podman run --rm alpine:latest echo "test" >/dev/null 2>&1; then
exit_with_error "Basic container test failed. This could indicate storage driver issues.
HOW TO FIX:
1. Check Podman logs: journalctl --user -u podman
2. Try: podman system reset (WARNING: removes all containers/images)
3. Ensure /run/containers and storage directories have correct permissions
4. Check if your filesystem supports overlay mounts"
fi
print_success "Podman test container created and removed successfully."
# Check connectivity to microsoft.com
print_info "Checking connectivity to Microsoft"
if ! curl -s --head --request GET --max-time 10 -L https://www.microsoft.com | grep -q "200"; then
# Alternative method: curl to a reliable fallback endpoint
if ! curl -s --head --request GET --max-time 10 -L https://www.office.com | grep -q "200"; then
exit_with_error "Unable to connect to microsoft.com.
HOW TO FIX:
1. Check your internet connection
2. Verify DNS settings: Ensure you can resolve microsoft.com (try: nslookup microsoft.com)
3. Check firewall settings: Ensure outbound connections to microsoft.com are allowed
4. Try again or contact your network administrator"
else
print_success "Successfully connected to Microsoft"
fi
else
print_success "Successfully connected to Microsoft"
fi
# Run locale scripts
print_step "2" "Detecting region and language settings"
print_info "Running locale configuration scripts"
print_info "Executing: $LOCALE_REG_SCRIPT"
if ! "$LOCALE_REG_SCRIPT"; then
exit_with_error "Failed to execute $LOCALE_REG_SCRIPT (exit code: $?)"
fi
print_info "Executing: $LOCALE_LANG_SCRIPT"
if ! "$LOCALE_LANG_SCRIPT"; then
exit_with_error "Failed to execute $LOCALE_LANG_SCRIPT (exit code: $?)"
fi
print_success "Locale script executed successfully"
# Check if newly created regional.reg exists
print_info "Checking for regional_settings.reg file"
if [ ! -f "$REGIONAL_REG" ]; then
exit_with_error "Required file not found: $REGIONAL_REG
Please ensure the config/oem/registry directory exists and contains regional_settings.reg"
fi
print_success "Found regional_settings.reg file"
}
function setup_logfile() {
# Check if the logfile already exists, if yes rename old one with its last modified date and start with a fresh logfile
mkdir -p "$(dirname "$LOGFILE")"
echo "Logfile: $LOGFILE"
if [ -e "$LOGFILE" ]; then
MODIFIED_DATE=$(stat -c %y "$LOGFILE" | sed 's/[: ]/_/g' | cut -d '.' -f 1)
mv "$LOGFILE" "${LOGFILE%.log}_$MODIFIED_DATE.log"
fi
}
function create_container() {
print_step "3" "Setting up the LinOffice container"
local bootcount=0
local required_boots=4 # Accept 4 as the minimum, but allow for 5 if it happens (see comment below)
# The Windows/Office install process may show 4 or 5 reboots depending on version and script details.
# Sometimes the reboot between install.bat and InstallOffice.ps1 is not a full UEFI reboot, so only 4 are seen.
# We proceed if we see at least 4 reboots, and print a note if more are detected.
# this is how many times the Windows VM needs to boot to be ready
# the string to look for is "BdsDxe: starting Boot0004"
# 3 reboots will be logged during initial Windows until you can see the desktop for the first time
# 1 reboot at the end of install.bat (this is the one that is not always logged for some reason)
# 1 reboot at the end of the InstallOffice.ps1
local result=1 # 0 = success, 1 = failure (assume failure by default)
local download_started=false
local download_finished=false
local install_started=false
local timeout_counter=0
local max_timeout=3600 # 60 minutes maximum wait time between podman-compose log output
local last_activity_time=$(date +%s)
local windows_version=""
# Start podman-compose in the background with unbuffered output and strip ANSI codes
print_info "Starting podman-compose in detached mode..."
# If the compose file doesn't exist yet, initialize it from the default template
if [ ! -f "$COMPOSE_FILE" ]; then
if [ -f "$COMPOSE_FILE.default" ]; then
print_info "Creating $COMPOSE_FILE from default template"
cp "$COMPOSE_FILE.default" "$COMPOSE_FILE" || exit_with_error "Failed to copy $COMPOSE_FILE.default to $COMPOSE_FILE"
else
exit_with_error "Compose file missing: $COMPOSE_FILE and $COMPOSE_FILE.default not found"
fi
fi
if ! $COMPOSE_COMMAND --file "$COMPOSE_FILE" up -d >>"$LOGFILE" 2>&1; then
exit_with_error "Failed to start containers. Check $LOGFILE for details."
fi
# Check if container was actually created
sleep 5
if ! podman ps -a --filter "name=$CONTAINER_NAME" --format "{{.Names}}" | grep -q "^$CONTAINER_NAME$"; then
exit_with_error "Container $CONTAINER_NAME was not created successfully. Check $LOGFILE for detailed error messages."
fi
print_info "Tailing logs from container: $CONTAINER_NAME"
podman logs -f --timestamps "$CONTAINER_NAME" 2>&1 | \
stdbuf -oL -eL sed -u 's/\x1b\[[0-9;]*m//g' >> "$LOGFILE" &
log_pid=$!
print_info "Monitoring container setup progress..."
# Monitor the logfile for progress
while true; do
local current_time=$(date +%s)
if [ $((current_time - last_activity_time)) -gt $max_timeout ]; then
result=1
exit_with_error "Container setup timed out after $((max_timeout/60)) minutes."
fi
# Read the logfile if it exists
if [ -f "$LOGFILE" ]; then
# Get file size to detect new activity
local current_size=$(stat -c%s "$LOGFILE" 2>/dev/null || echo "0")
local previous_size=${previous_size:-0}
if [ "$current_size" -gt "$previous_size" ]; then
last_activity_time=$current_time
previous_size=$current_size
fi
# Check for download progress
if ! $download_started && grep -q "Downloading Windows" "$LOGFILE"; then
print_step "4" "Starting Windows download (about 5 GB). This will take a while depending on your Internet speed."
download_started=true
last_activity_time=$current_time
windows_version=$(grep "Downloading Windows" "$LOGFILE" | tail -1 | grep -oE '10|11')
fi
# Output download progress at each percent
if $download_started && ! $download_finished; then
# Parse wget progress line: percent and speed
last_percent=${last_percent:--1}
progress_line=$(grep -E "%" "$LOGFILE" | grep -E "[0-9.]+[MK]" | tail -1)
pct=$(echo "$progress_line" | grep -oE "[ ]{1,3}[0-9]{1,3}%" | tail -1 | tr -d ' %')
speed=$(echo "$progress_line" | grep -oE "[0-9.]+[MK]" | tail -1)
if [[ "$pct" =~ ^[0-9]+$ ]] && [ "$pct" -gt "$last_percent" ] && [ "$pct" -le 100 ]; then
if [ -n "$windows_version" ]; then
print_progress "Downloading Windows ${windows_version}: ${pct}% | Speed: ${speed}B/s"
else
print_progress "Downloading Windows: ${pct}% | Speed: ${speed}B/s"
fi
last_percent=$pct
fi
fi
# Check for download completion
if $download_started && ! $download_finished && grep -q "100%" "$LOGFILE"; then
print_success "Windows download finished"
download_finished=true
last_activity_time=$current_time
# Monitor for either "Windows started" or "Shutdown completed" after download
print_info "Waiting for Windows to start after download..."
local monitor_timeout=300 # 5 minutes
local monitor_elapsed=0
local monitor_interval=5
while [ $monitor_elapsed -lt $monitor_timeout ]; do
if grep -q "Windows started" "$LOGFILE"; then
print_step "5" "Installing Windows. This will take a while."
install_started=true
last_activity_time=$(date +%s)
break
fi
if grep -q "Shutdown completed" "$LOGFILE"; then
exit_with_error "Windows installation failed: Detected shutdown before Windows started. Check $LOGFILE for details and see https://github.com/dockur/windows/issues for troubleshooting."
fi
sleep $monitor_interval
monitor_elapsed=$((monitor_elapsed + monitor_interval))
done
if [ $monitor_elapsed -ge $monitor_timeout ]; then
exit_with_error "Timeout waiting for Windows to start after download. Check $LOGFILE for details."
fi