OSDN Git Service

[update] : Updated help document
[alterlinux/dmc.git] / dmc
1 #!/usr/bin/env bash
2 #
3 # Yamada Hayao
4 # Twitter: @Hayao0819
5 # Email  : hayao@fascode.net
6 #
7 # (c) 2019-2021 Fascode Network.
8 #
9 # dmc
10 #
11 # LICENSE: THE SUSHI-WARE LICENSE
12 # https://github.com/MakeNowJust/sushi-ware
13 #
14 #
15 # 参考資料
16 # https://qiita.com/laikuaut/items/4bc07eabce56ee30812d
17 # https://qiita.com/t_nakayama0714/items/80b4c94de43643f4be51
18
19 script_usage(){
20     echo "usage: dmc [options] [command]"
21     echo
22     echo "A simple tool for switching LightDM Greeters"
23     echo
24     echo " LightDM command:"
25     echo "    autologin [username] [session]    Set up automatic login (with blank username to disable)"
26     echo "    greeter                           Run greeter setup wizard"
27     echo "    greeter-create [file]             Set the specified executable file as Greeter"
28     echo "    greeter-change [greeter]          Specify the greeter to use"
29     echo "    greeter-edit [greeter]            Edit the greeter configs"
30     echo "    greeter-list                      Show a list of currently installed Greeters"
31     echo "    remove                            Removes all changes made by this command"
32     echo "    edit                              Edit lightdm config"
33     echo "    show-config                       Show current settings"
34     echo
35     echo " GDM command:"
36     echo "    autologin [username] [session]    Set up automatic login (with blank username to disable)"
37     echo "    cursor                            Run cursor selection wizard"
38     echo "    cursor-change [cursor]            Specify the cursor theme"
39     echo "    cursor-list                       Show a list of cursor theme"
40     echo "    sound [true or false]             Toggle the sound when an event occurs"
41     echo "    logo [image]                      Specify the logo of the login screen"
42     echo "    tap [true or false]               Toggle whether to recognize the tap as a click"
43     echo "    accessibility [true or false]     Toggle whether to show accessibility menu"
44     echo "    root-login [true or false]        Toggle whether to enable root login"
45     echo
46     echo " Webkit2 command:"
47     echo "    theme                             Run theme selection wizard"
48     echo "    theme-change [theme]              Specify the theme"
49     echo
50     echo " Qtquick command:"
51     echo "    back                              Specify the background image"
52     echo
53     echo " Slick command:"
54     echo "    back [image]                      Specify the background image"
55     echo "    grid [true or false]              Toggle whether to show grid"
56     echo "    icon                              Run icon theme selection wizard"
57     echo "    theme                             Run gtk theme selection wizard"
58     echo
59     echo " General option:"
60     echo "    -m | --mode [mode name]           Specifiy the target you want to set"
61     echo "    -e | --editer [editor path]       Specifiy the editor to use for editing"
62     echo "    -h | --help                       This help message and exit"
63     echo "    --non-interactive                 Run in non-interactive mode"
64     echo "    --noroot                          No check root permission"
65     echo "    --write-all-files                 Allows rewriting of all configuration files"
66     echo
67     echo " Supported modes:"
68     echo "    Display managers: lightdm, gdm"
69     echo "    LightDM greeters: webkit2, qtquick, slick"
70     echo
71     echo " This tool is incomplete and still under development."
72     echo " If you find a bug, please report it on GitHub."
73     echo " https://github.com/FascodeNet/dmc"
74     echo
75 }
76
77 set -eu
78
79 # エラー
80 msg_error() {
81     echo "${@}" 1>&2
82 }
83
84 # 警告
85 msg_warn() {
86     echo "${@}" 1>&2
87 }
88
89 # rootチェック
90 check_root(){
91     if [[ "${NOROOT}" = false ]] && (( "${UID}" != 0 )); then
92         msg_error "You have to run as root"
93         exit 1
94     fi
95 }
96
97 # 数値チェック
98 check_int(){
99     set +e
100     #if (( "$(expr "${1}" + 1 >/dev/null 2>&1; printf "${?}")" < 2 )); then
101     if printf "%s" "${1}" | grep -E "^[0-9]+$" 1>/dev/null 2>&1; then
102         set -e
103         return 0
104     else
105         set -e
106         return 1
107     fi
108 }
109
110 check_bool(){
111     if [[ -n "${1}" ]] && { [[ "${1}" = "true" ]] ||[[ "${1}" = "false" ]]; }; then
112         return 0
113     else
114         return 1
115     fi
116 }
117
118 # crudini ラッパー
119 wrapper(){
120     local _command="${1}"
121     shift 1
122     if which "${_command}" >/dev/null 2>&1; then
123         $(which "${_command}") "${@}"
124     else
125         msg_error "${_command} was not found"
126         exit 1
127     fi
128 }
129
130 crudini(){
131     wrapper crudini "${@}"
132 }
133
134 jq(){
135     wrapper jq "${@}"
136 }
137
138
139 # カーソルテーマの一覧を取得
140 get_cursor_theme(){
141     # カーソルテーマの一覧を取得
142     # 参考: https://wiki.archlinux.jp/index.php/%E3%82%AB%E3%83%BC%E3%82%BD%E3%83%AB%E3%83%86%E3%83%BC%E3%83%9E
143     # 参考: https://wiki.archlinux.jp/index.php/GDM
144     #echo "Searching cursor themes..." 1>&2
145     local _dir _cursor_theme_dir_list _find_cursor_dir_list=("/usr/share/icons" "${HOME}/.local/share/icons" "${HOME}/.icons")
146     for _dir in "${_find_cursor_dir_list[@]}"; do
147         if [[ -d "${_dir}" ]]; then
148             while read -r line; do
149                 _cursor_theme_dir_list+=("${line}")
150             done < <(find "${_dir}" -type d -name "cursors" -print0 | xargs -0 -i dirname {} | sort)
151         fi
152     done
153     unset _dir _find_cursor_dir_list
154     
155     local _cursor_theme _cursor_theme_name _name
156     for _cursor_theme in "${_cursor_theme_dir_list[@]}"; do
157         _name="$(grep -E "^Name=" "${_cursor_theme}/cursor.theme" 2> /dev/null | sed "s|^Name=||g")"
158         if [[ -z "${_name}" ]]; then
159             _name="$(basename "${_cursor_theme}")"
160         fi
161         _cursor_theme_name+=("${_name}")
162     done
163     printf "%s\n" "${_cursor_theme_name[@]}"
164 }
165
166 # GTKテーマの一覧を取得
167 get_gtk_theme(){
168     echo "Loading GTK themes..." >&2
169     local _dir _find_theme_dir_list=("/usr/share/themes" "${HOME}/.local/share/themes" "${HOME}/.themes")
170     for _dir in "${_find_theme_dir_list[@]}"; do
171         if [[ -d "${_dir}" ]]; then
172             while read -r line; do
173                 _gtk_theme_dir_list+=("${line}")
174             done < <(find "${_dir}" -type d -name "gtk-*" -print0 | xargs -0 -i dirname {} | tr " " "\n" | sort | uniq)
175         fi
176     done
177
178     local _theme_name
179     for _dir in "${_gtk_theme_dir_list[@]}"; do
180         _theme_name="$(crudini --get "${_dir}/index.theme" 'Desktop Entry' "Name" 2> /dev/null ;:)"
181         if [[ -z "${_theme_name}" ]]; then
182             continue
183         else
184             _gtk_theme_name_list+=("${_theme_name}")
185         fi
186     done
187     
188     printf "%s\n" "${_gtk_theme_name_list[@]}" | sort | uniq
189 }
190
191 get_icon_theme(){
192     echo "Loading icons..." >&2
193     local _dir _find_theme_dir_list=("/usr/share/icons" "${HOME}/.local/share/icons" "${HOME}/.icons")
194     for _dir in "${_find_theme_dir_list[@]}"; do
195         if [[ -d "${_dir}" ]]; then
196             while read -r line; do
197                 _icon_theme_dir_list+=("${line}")
198             done < <(find "${_dir}" -type f -name "index.theme" -print0 | xargs -0 -i dirname {} | tr " " "\n" | sort | uniq)
199         fi
200     done
201
202     local _theme_name
203     for _dir in "${_icon_theme_dir_list[@]}"; do
204         _theme_name="$(crudini --get "${_dir}/index.theme" 'Icon Theme' "Name" 2> /dev/null ;:)"
205         if [[ -z "${_theme_name}" ]]; then
206             continue
207         else
208             _gtk_theme_name_list+=("${_theme_name}")
209         fi
210     done
211     
212     printf "%s\n" "${_gtk_theme_name_list[@]}" | sort | uniq
213 }
214
215 # 質問を行う関数
216 # ask_question -d <デフォルト値> <選択肢1> <選択肢2> ...
217 ask_question(){
218     local arg OPTARG OPTIND
219     local _default="" _choice_list _count _choice
220     while getopts "d:" arg; do
221         case "${arg}" in
222             d)  _default="${OPTARG}" ;;
223         esac
224     done
225     shift $((OPTIND - 1))
226     _choice_list=("${@}")
227
228     for (( _count=1; _count<=${#_choice_list[@]}; _count++)); do
229         _choice="${_choice_list[$(( _count - 1 ))]}"
230         if [[ ! "${_default}" = "" ]] && [[ "${_choice}" = "${_default}" ]]; then
231             echo " * ${_count}: ${_choice}" >&2
232         else
233             echo "   ${_count}: ${_choice}" >&2
234         fi
235         unset _choice
236     done
237     echo -n "(1 ~ ${#_choice_list[@]}) > " >&2
238     read -r _input
239
240     # 回答を解析
241     if check_int "${_input}"; then
242         # 数字が入力された
243         if (( 1 <= _input)) && (( _input <= ${#_choice_list[@]} )); then
244             _choice="${_choice_list[$(( _input - 1 ))]}"
245         else
246             return 1
247         fi
248     else
249         # 文字が入力された
250         if printf "%s\n" "${_choice_list[@]}" | grep -x "${_input}" 1>/dev/null 2>&1; then
251             _choice="${_input}"
252         else
253             return 1
254         fi
255     fi
256     echo "${_choice}"
257     return 0
258 }
259
260 #== LightDM用の汎用関数 ==#
261 # キーが設定されている設定ファイル
262 lightdm_get_source_file(){
263     local key="${1}"
264     local source_name="$(lightdm --show-config 2>&1 | grep -E "^[A-Z]  ${key}=" | cut -d ' ' -f 1)"
265     local source_path="$(lightdm --show-config 2>&1 | grep -x -A "$(lightdm --show-config 2>&1 | wc -l)" "Sources:" | grep -xv "Sources" | grep -E "^${source_name}  " | sed "s|^${source_name}  ||g")"
266     if [[ -n "${source_path}" ]]; then
267         echo -n "${source_path}"
268     else
269         echo -n ""
270     fi
271 }
272
273
274 # 設定ファイルの値を変更する
275 lightdm_set_config(){
276     local key="${1}" value="${2}" file=${3-${DISPLAY_MANAGER_CONFIG["lightdm"]}}
277     if [[ "${WRITE_ALL_FILES}" = true ]] && [[ -n "$(lightdm_get_source_file "${1}")" ]]; then
278         crudini --set "$(lightdm_get_source_file "${1}")" 'Seat:*' "${key}" "${value}"
279     else
280         crudini --set "${file}" 'Seat:*' "${key}" "${value}"
281     fi
282
283     if [[ ! "$(lightdm_get_value "${key}")" = "${value}" ]]; then
284         msg_error "Failed to change the setting value. A value has already been set for $(lightdm_get_source_file "${1}")"
285         msg_error "lightdm-config does not manipulate other configuration files for safety. Comment out the settings in that file."
286     fi
287 }
288
289 # 設定ファイルのキーを削除する
290 lightdm_remove_key(){
291     local key="${1}" _config
292     if grep -E "^ ?${key}.+" "${DISPLAY_MANAGER_CONFIG["lightdm"]}" 1>/dev/null 2>&1; then
293         sed -i -r "s|^ ?${key} ?=.+||g" "${DISPLAY_MANAGER_CONFIG["lightdm"]}"
294         sed -i '/^$/d' "${DISPLAY_MANAGER_CONFIG["lightdm"]}"
295     fi
296 }
297
298 # 設定ファイルを作成
299 lightdm_init_configs(){
300     check_root
301     if [[ ! -f "${DISPLAY_MANAGER_CONFIG["lightdm"]}" ]]; then
302         mkdir -p "$(dirname "${DISPLAY_MANAGER_CONFIG["lightdm"]}")"
303         touch "${DISPLAY_MANAGER_CONFIG["lightdm"]}"
304         echo "[Seat:*]" > "${DISPLAY_MANAGER_CONFIG["lightdm"]}"
305     fi
306 }
307
308 # 現在設定されている値を取得する
309 lightdm_get_value(){
310     local _current_value="$(lightdm --show-config 2>&1 | grep -E "^[A-Z]  ${1}=" | sed "s|^[A-Z]  ||g" | cut -d "=" -f "2")"
311     if [[ "${_current_value}" ]]; then
312         echo -n "${_current_value}"
313         return 0
314     else
315         echo -n ""
316         return 0
317     fi
318 }
319
320 #== LightDM用コマンド ==#
321 # greeter-changeコマンド
322 command_lightdm_greeter_change() {
323     # 引数チェック
324     if [[ -z "${1}" ]] || [[ "${1}" = "" ]]; then
325         msg_error "Please specify Greeter."
326         script_usage
327         exit 1
328     fi
329
330     # 指定されたGreeterが正しいか確認
331     if ! printf "%s\n" "${LIGHTDM_GREETERS[@]}" | grep -x "${1}" > /dev/null 2>&2; then
332         msg_error "The greeter (${1}) doesn't exist."
333         exit 1
334     else
335         lightdm_set_config "greeter-session" "${1}"
336     fi
337 }
338
339 # greeter-createコマンド
340 command_lightdm_greeter_create(){
341     if [[ -z "${1}" ]] || [[ "${1}" = "" ]]; then
342         msg_error "Please specify Greeter."
343         script_usage
344         exit 1
345     fi
346     if [[ ! -f "${1}" ]]; then
347         msg_error "${1} does not exist."
348         script_usage
349         exit 1
350     fi
351     if [[ ! -x "${1}" ]]; then
352         msg_error "hoge is not an executable file."
353         script_usage
354         exit 1
355     fi
356
357     local path="${1}"
358     local filename="$(basename "${1}")"
359
360     if [[ -f "/usr/share/xgreeters/${filename}.desktop" ]]; then
361         msg_error "Greeter with the same name already exists."
362         exit 1
363     fi
364     cat > "/usr/share/xgreeters/${filename}.desktop"  <<EOF
365 [Desktop Entry]
366 Name=LightDM custom Greeter ${filename}
367 Comment=LightDM Greeter
368 Exec=${path}
369 Type=Application
370 EOF
371 }
372
373 # greeter-listコマンド
374 command_lightdm_greeter_list() {
375     echo "Available Lightdm greeter list:"
376     local _greeter
377     for _greeter in "${LIGHTDM_GREETERS[@]}"; do
378         if [[ "${_greeter}" = "${LIGHTDM_CURRENT_GREETER}" ]]; then
379             echo " * ${_greeter}"
380         else
381             echo "   ${_greeter}"
382         fi
383     done
384 }
385
386 # greeterコマンド
387 run_greeter_wizard(){
388     # グリーターの数を確認
389     if (( ${#LIGHTDM_GREETERS[@]} < 1 )); then
390         msg_error "LightDM Greeter was not found."
391         exit 1
392     fi
393
394     # 質問する
395     local _c _greeter
396     echo "Please select the greeter to use."
397     if ! _greeter="$(ask_question -d "${LIGHTDM_CURRENT_GREETER}" "${LIGHTDM_GREETERS[@]}")"; then
398         run_greeter_wizard
399         exit 0
400     fi
401
402     # 結果に応じて処理を実行
403     if [[ -n "${_greeter}" ]]; then
404         command_lightdm_greeter_change "${_greeter}"
405     else
406         run_greeter_wizard
407         exit 0
408     fi
409     echo "Changed greeter to ${_greeter}"
410 }
411
412 # removeコマンド
413 command_lightdm_remove(){
414     if [[ ! -f "${DISPLAY_MANAGER_CONFIG["lightdm"]}" ]]; then
415         return 0
416     else
417         local _yes_or_no
418         echo -ne "Are you sure you want to delete all settings?\nThis change is irreversible.\n (y or n) > "
419         read -r -n 1 _yes_or_no
420         if [[ "${_yes_or_no}" = "y" ]]; then
421             mv "${DISPLAY_MANAGER_CONFIG["lightdm"]}" "${DISPLAY_MANAGER_CONFIG["lightdm"]}.disabled"
422         fi
423     fi
424 }
425
426 # greeter-edit
427 command_lightdm_greeter_edit(){
428     if [[ "${NON_INTERACTIVE}" = true ]]; then
429         msg_error "You cannot use this command in non-interactive mode."
430         exit 1
431     fi
432     local _greeter="${1:-${LIGHTDM_CURRENT_GREETER}}"
433     if [[ -z "${GREETER_CONFIG["${_greeter}"]+SET}" ]]; then
434         msg_error "This Greeter is not currently supported."
435         msg_error "Please report the problem here."
436         msg_error "https://github.com/FascodeNet/lightdm-config/issues"
437         exit 1
438     else
439         if [[ -z "${1}" ]] || [[ "${1}" = "" ]]; then
440             msg_warn "Greeter was not specified. Open the currently configured Greeter configuration file."
441             echo -n "(Enter to continue) > "
442             read -r
443         fi
444         set -u
445         bash -c "${USE_EDITOR} ${GREETER_CONFIG["${_greeter}"]}"
446         exit
447     fi
448 }
449
450 # edit
451 command_lightdm_edit(){
452     if [[ "${NON_INTERACTIVE}" = true ]]; then
453         msg_error "You cannot use this command in non-interactive mode."
454         exit 1
455     fi
456     for _config in "${LIGHTDM_LOADED_CONFIG[@]}"; do
457         echo -ne "Edit ${_config} ? (y or n)> "
458         read -r -n 1 _yes_or_no
459         echo
460         if [[ "${_yes_or_no}" = "y" ]]; then
461             bash -c "${USE_EDITOR} ${_config}"
462         fi
463     done
464 }
465
466 # autologin
467 command_lightdm_auto_login(){
468     if [[ -z "${1+SET}" ]] || [[ "${1}" = "" ]]; then
469         # 既に自動ログインが設定されているかを確認
470         local autologin_user="$(lightdm_get_value autologin-user)"
471         if [[ -n "${autologin_user}" ]]; then
472             # autologinを無効化
473             for _autologin in "autologin-guest" "autologin-user" "autologin-user-timeout" "autologin-in-background" "autologin-session"; do
474                 remove_key "${_autologin}"
475             done
476             echo "Canceled automatic login of ${autologin_user}"
477         fi
478     else
479         local autologin_user="${1}" autologin_session
480         if [[ -v 2 ]]; then
481             autologin_session="${2}"
482         fi
483
484         # ユーザーチェック
485         if ! getent passwd "${autologin_user}" 1> /dev/null 2>&1; then
486             echo "${autologin_user} is a non-existent user."
487             exit 1
488         fi
489
490         # セッションを設定
491         if [[ -z "${autologin_session+SET}" ]]; then
492             if (( $(find "/usr/share/xsessions" -print0 -type f 2> /dev/null | xargs -0 -i basename {} | wc -l) <= 1 )); then
493                 autologin_session="$(find "/usr/share/xsessions" -print0 -type f 2> /dev/null | xargs -0 -i basename {} | sed 's|.desktop$||g')"
494             elif [[ "${NON_INTERACTIVE}" = true ]]; then
495                 # 非対話モード
496                 # ~/.dmrcの値を設定します
497                 autologin_session="$(grep -E '^Session=' "${HOME}/.dmrc" 2> /dev/null | cut -d '=' -f 2)"
498                 if [[ -z "${autologin_session}" ]]; then
499                     msg_error "Failed to set the session."
500                     msg_error "Not specified and ~/.dmrc does not exist either."
501                     exit 1
502                 fi
503             else
504                 echo "Select the desktop session to autologin"
505                 local session
506                 for session in "/usr/share/xsessions/"*; do
507                     echo "   ${session}"
508                 done
509                 echo -n "(session name) > "
510                 read -r session
511                 if [[ -f "/usr/share/xsessions/${session}.desktop" ]]; then
512                     autologin_session="${session}"
513                 else
514                     msg_error "Please enter the correct session name."
515                     exit 1
516                 fi
517             fi
518         else
519             # 既に値が設定済み
520             if [[ ! -f "/usr/share/xsessions/${autologin_session}.desktop" ]]; then
521                 # 存在しないセッションが指定された場合
522                 msg_error "This is a session (${autologin_session}) that does not exist."
523                 exit 1
524             fi
525         fi
526
527         # autologin グループを設定
528         if ! getent group "autologin" 1> /dev/null 2>&1; then
529             LANG=C groupadd -r "autologin"
530         fi
531         LANG=C gpasswd -a "${autologin_user}" "autologin"
532
533         # 設定を書き込み
534         lightdm_set_config "autologin-guest" "false"
535         lightdm_set_config "autologin-user" "${autologin_user}"
536         lightdm_set_config "autologin-user-timeout" "0"
537         lightdm_set_config "autologin-in-background" "false"
538         lightdm_set_config "autologin-session" "${autologin_session}"
539
540         echo "${autologin_user} will automatically log in with ${autologin_session}"
541     fi
542
543 }
544
545 # show-config
546 command_lightdm_show_config(){
547    lightdm --show-config 2>&1 
548 }
549
550 #== GDM用の汎用関数 ==#
551 gdm_init_configs(){
552     check_root
553     if [[ ! -f "/etc/dconf/profile/gdm" ]]; then
554         mkdir -p "/etc/dconf/profile"
555         touch "/etc/dconf/profile/gdm"
556         echo -e "user-db:user\nsystem-db:gdm\nfile-db:/usr/share/gdm/greeter-dconf-defaults" > "/etc/dconf/profile/gdm"
557     fi
558
559     local _file
560     for _file in "${DISPLAY_MANAGER_CONFIG["gdm-dconf"]}" "${DISPLAY_MANAGER_CONFIG["gdm-custom"]}"; do
561         if [[ ! -f "${_file}" ]]; then
562             mkdir -p "$(dirname "${_file}")"
563             touch "${_file}"
564         fi
565     done
566 }
567
568 # gdm_dconf_set_config <dconf path> <key> <value>
569 gdm_dconf_set_config(){
570     if check_int "${3}" || check_bool "${3}"; then
571         crudini --set "${DISPLAY_MANAGER_CONFIG["gdm-dconf"]}" "${1}" "${2}" "${3}"
572     else
573         crudini --set "${DISPLAY_MANAGER_CONFIG["gdm-dconf"]}" "${1}" "${2}" "\"${3}\""
574     fi
575     gdm_update
576 }
577
578 # gdm_dconf_get_value <dconf path> <key>
579 gdm_dconf_get_value(){
580     dconf dump / | crudini --get - "${1}" "${2}"
581 }
582
583 # gdm_custom_get_value <section> <key>
584 gdm_custom_get_value(){
585     crudini --get "${DISPLAY_MANAGER_CONFIG["gdm-custom"]}" "${1}" "${2}"
586 }
587
588 # gdm_custom_set_config <section> <key> <value>
589 gdm_custom_set_config(){
590     if check_int "${3}" || check_bool "${3}"; then
591         crudini --set "${DISPLAY_MANAGER_CONFIG["gdm-custom"]}" "${1}" "${2}" "${3}"
592     else
593         crudini --set "${DISPLAY_MANAGER_CONFIG["gdm-custom"]}" "${1}" "${2}" "\"${3}\""
594     fi
595     gdm_update
596 }
597
598 gdm_update(){
599     dconf update
600 }
601
602
603 #== GDM用コマンド ==#
604 command_gdm_logo(){
605     if [[ -z "${1+SET}" ]] || [[ "${1}" = "" ]]; then
606         msg_error "Please specify the image of background"
607         exit 1
608     fi
609     if [[ ! -f "${1}" ]]; then
610         msg_error "${1} was not found."
611         exit 1
612     fi
613
614     local _backgrounf_file="/usr/share/backgrounds/gdm/background"
615     mkdir -p "$(dirname "${_backgrounf_file}")"
616     cp "${1}" "${_backgrounf_file}"
617     chmod 644 "${_backgrounf_file}"
618     
619     gdm_dconf_set_config "org/gnome/login-screen" "logo" "${_backgrounf_file}"
620 }
621
622 command_gdm_cursor_wizard(){
623     # カーソル一覧を取得
624     local cursor_themes
625     while read -r line; do cursor_themes+=("${line}"); done < <(get_cursor_theme)
626
627     # 一覧を生成
628     local _c
629     unset _cursor_theme
630     echo "Please select the cursor theme to use."
631     if ! _cursor_theme="$(ask_question "${cursor_themes[@]}")"; then
632         run_greeter_wizard
633         exit 0
634     fi
635
636     if [[ -n "${_cursor_theme}" ]]; then
637         command_gdm_cursor_change "${_cursor_theme}"
638     else
639         command_gdm_cursor_wizard
640         exit 0
641     fi
642     echo "Changed cursor to ${_cursor_theme}"
643 }
644
645 command_gdm_cursor_change(){
646     if [[ -z "${1+SET}" ]] || [[ "${1}" = "" ]]; then
647         msg_error "Please specify the cursor theme"
648         exit 1
649     fi
650     local cursor_themes
651     while read -r line; do cursor_themes+=("${line}"); done < <(get_cursor_theme)
652     if ! printf "%s\n" "${cursor_themes[@]}" | grep -x "${1}" 1>/dev/null 2>&1; then
653         msg_error "The cursor theme (${1}) was not found"
654         exit 1
655     fi
656     gdm_dconf_set_config "org/gnome/desktop/interface" "cursor-theme" "${1}"
657 }
658
659 command_gdm_sound(){
660     local _arg="$(echo "${1-""}" | tr "[:upper:]" "[:lower:]")"
661     if ! check_bool "${_arg}"; then
662         msg_error "Please specify true or false"
663         script_usage
664         exit 1
665     fi
666     gdm_dconf_set_config "org/gnome/desktop/sound" "event-sounds" "${_arg}"
667 }
668
669 command_gdm_tap(){
670     local _arg="$(echo "${1-""}" | tr "[:upper:]" "[:lower:]")"
671     if ! check_bool "${_arg}"; then
672         msg_error "Please specify true or false"
673         script_usage
674         exit 1
675     fi
676     gdm_dconf_set_config "org/gnome/desktop/peripherals/touchpad" "tap-to-click" "${_arg}"
677 }
678
679 # autologin
680 command_gdm_auto_login(){
681     if [[ -z "${1+SET}" ]] || [[ "${1}" = "" ]]; then
682         # 既に自動ログインが設定されているかを確認
683         local autologin="$(gdm_custom_get_value daemon AutomaticLoginEnable)"
684         if [[ "${autologin}" = "True" ]]; then
685             gdm_custom_set_config "daemon" "AutomaticLoginEnable" "False"
686             echo "Canceled automatic login of $(gdm_custom_get_value "daemon" "AutomaticLogin")"
687         fi
688     else
689         local autologin_user="${1}" autologin_session
690         if [[ -v 2 ]]; then
691             autologin_session="${2}"
692         fi
693
694         # ユーザーチェック
695         if ! getent passwd "${autologin_user}" 1> /dev/null 2>&1; then
696             echo "${autologin_user} is a non-existent user."
697             exit 1
698         fi
699
700         # セッションを設定 (WayLandのセッションは現在サポートされていません)
701         if [[ -z "${autologin_session+SET}" ]]; then
702             if (( $(find "/usr/share/xsessions" -print0 -type f 2> /dev/null | xargs -0 -i basename {} | wc -l) <= 1 )); then
703                 autologin_session="$(find "/usr/share/xsessions" -print0 -type f 2> /dev/null | xargs -0 -i basename {} | sed 's|.desktop$||g')"
704             elif [[ "${NON_INTERACTIVE}" = true ]]; then
705                 # 非対話モード
706                 # ~/.dmrcの値を設定します
707                 autologin_session="$(grep -E '^Session=' "${HOME}/.dmrc" 2> /dev/null | cut -d '=' -f 2)"
708                 if [[ -z "${autologin_session}" ]]; then
709                     msg_error "Failed to set the session."
710                     msg_error "Not specified and ~/.dmrc does not exist either."
711                     exit 1
712                 fi
713             else
714                 echo "Select the desktop session to autologin"
715                 local session
716                 for session in "/usr/share/xsessions/"*; do
717                     echo "   $(basename "${session}" | sed "s|.desktop$||g")"
718                 done
719                 echo -n "(session name) > "
720                 read -r session
721                 if [[ -f "/usr/share/xsessions/${session}.desktop" ]]; then
722                     autologin_session="${session}"
723                 else
724                     msg_error "Please enter the correct session name."
725                     exit 1
726                 fi
727             fi
728         else
729             # 既に値が設定済み
730             if [[ ! -f "/usr/share/xsessions/${autologin_session}.desktop" ]]; then
731                 # 存在しないセッションが指定された場合
732                 msg_error "This is a session (${autologin_session}) that does not exist."
733                 exit 1
734             fi
735         fi
736
737         # autologin グループを設定
738         if ! getent group "autologin" 1> /dev/null 2>&1; then
739             LANG=C groupadd -r "autologin"
740         fi
741         LANG=C gpasswd -a "${autologin_user}" "autologin"
742
743         # 設定を書き込み
744         gdm_custom_set_config "daemon" "AutomaticLoginEnable" "True"
745         gdm_custom_set_config "daemon" "AutomaticLogin" "${autologin_user}"
746         
747         # セッションを指定
748         crudini --set "/var/lib/AccountsService/users/${autologin_user}" "User" "Session" "${autologin_session}"
749         crudini --set "/var/lib/AccountsService/users/${autologin_user}" "User" "XSession" "${autologin_session}"
750
751         echo "${autologin_user} will automatically log in with ${autologin_session}"
752     fi
753
754 }
755
756 # accessibility コマンド
757 command_gdm_accessibility(){
758     local _arg="$(echo "${1-""}" | tr "[:upper:]" "[:lower:]")"
759     if ! check_bool "${_arg}"; then
760         msg_error "Please specify true or false"
761         script_usage
762         exit 1
763     fi
764     gdm_dconf_set_config "org/gnome/desktop/interface" "toolkit-accessibility" "${_arg}"
765 }
766
767 # root-login コマンド
768 command_gdm_root_login(){
769     local _arg="$(echo "${1-""}" | tr "[:upper:]" "[:lower:]")"
770     if ! check_bool "${_arg}"; then
771         msg_error "Please specify true or false"
772         script_usage
773         exit 1
774     fi
775     gdm_custom_set_config "daemon" "AllowRoot" "${_arg}"
776 }
777
778 #== Webkit2用の汎用関数 ==#
779 webkit2_init_configs(){
780     check_root
781     if [[ ! -f "${GREETER_CONFIG["lightdm-webkit2-greeter"]}" ]]; then
782         mkdir -p "$(dirname "${GREETER_CONFIG["lightdm-webkit2-greeter"]}")"
783         touch "${GREETER_CONFIG["lightdm-webkit2-greeter"]}"
784     fi
785 }
786
787 # webkit2_get_value <section> <key>
788 webkit2_get_value(){
789     crudini --get "${GREETER_CONFIG["lightdm-webkit2-greeter"]}" "${1}" "${2}"
790 }
791
792 # webkit2_set_config <section> <key> <value>
793 webkit2_set_config(){
794     crudini --set "${GREETER_CONFIG["lightdm-webkit2-greeter"]}" "${1}" "${2}" "${3}"
795 }
796
797 #== webkit2用コマンド ==#
798 command_webkit2_theme_wizard(){
799     local _theme_list
800     while read -r line; do
801         _theme_list+=("${line}")
802     done < <(ls /usr/share/lightdm-webkit/themes)
803
804     local _current_theme=$(webkit2_get_value greeter webkit_theme | sed "s|\"||g")
805     local _theme _c
806     echo "Please select the theme to use." _theme_list
807     if ! _theme="$(ask_question -d "${_current_theme}" "${_theme_list[@]}")"; then
808         run_greeter_wizard
809         exit 0
810     fi
811     if [[ -n "${_theme}" ]]; then
812         command_webkit2_theme_change "${_theme}"
813         echo "Changed the theme to ${_theme}"
814     else
815         command_webkit2_theme_wizard
816         exit 0
817     fi
818     
819 }
820
821 command_webkit2_theme_change(){
822     if [[ -z "${1+SET}" ]] || [[ "${1}" = "" ]]; then
823         msg_error "Please specify the theme name"
824         exit 1
825     fi
826     if [[ ! -d "/usr/share/lightdm-webkit/themes/${1}" ]]; then
827         msg_error "The specified theme (${1}) does not exist"
828         exit 1
829     fi
830
831     webkit2_set_config "greeter" "webkit_theme" "${1}"
832 }
833
834 #== Qtquick用の汎用関数 ==#
835 qtquick_init_configs(){
836     check_root
837     if [[ ! -f "${GREETER_CONFIG["lightdm-qtquick-greeter"]}" ]] || [[ -z "$(cat "${GREETER_CONFIG["lightdm-qtquick-greeter"]}")" ]]; then
838         mkdir -p "$(dirname "${GREETER_CONFIG["lightdm-qtquick-greeter"]}")"
839         touch "${GREETER_CONFIG["lightdm-qtquick-greeter"]}"
840         #echo -e "{\n\n}\n" > "${GREETER_CONFIG["lightdm-qtquick-greeter"]}"
841         qtquick_set_config background_path "file:///hoge/fuga.png"
842         qtquick_set_config theme "qrc:/Login.qml"
843     fi
844 }
845
846 #qtquick_get_value <key>
847 qtquick_get_value(){
848     jq ".${1}" < "${GREETER_CONFIG["lightdm-qtquick-greeter"]}"
849 }
850
851 # command_qtquick_back <key> <value>
852 qtquick_set_config(){
853     local _tempfile="/tmp/$(basename "${GREETER_CONFIG["lightdm-qtquick-greeter"]}")-$(base64 < "/dev/urandom" | fold -w 10 | head -n 1)"
854     cp "${GREETER_CONFIG["lightdm-qtquick-greeter"]}" "${_tempfile}"
855     #cat "${_tempfile}" | jq -r ".${1}|=\"${2}\"" > "${GREETER_CONFIG["lightdm-qtquick-greeter"]}"
856     jq -r ".${1}|=\"${2}\"" < "${_tempfile}" > "${GREETER_CONFIG["lightdm-qtquick-greeter"]}"
857     chmod 644 "${GREETER_CONFIG["lightdm-qtquick-greeter"]}"
858 }
859
860 #== Qtquick用コマンド ==#
861 command_qtquick_back(){
862     if [[ -z "${1+SET}" ]] || [[ "${1}" = "" ]]; then
863         msg_error "Please specify the image of background"
864         exit 1
865     fi
866     if [[ ! -f "${1}" ]]; then
867         msg_error "${1} was not found."
868         exit 1
869     fi
870
871     local _backgrounf_file="/usr/share/backgrounds/lightdm/qtquick-greeter"
872     mkdir -p "$(dirname "${_backgrounf_file}")"
873     cp "${1}" "${_backgrounf_file}"
874     chmod 644 "${_backgrounf_file}"
875     
876     qtquick_set_config "background_path" "file://${_backgrounf_file}"
877 }
878
879 #== slick用の汎用関数 ==#
880 slick_get_value(){
881     crudini --get "${GREETER_CONFIG["lightdm-slick-greeter"]}" "Greeter" "${1}"
882 }
883
884 slick_set_config(){
885     crudini --set "${GREETER_CONFIG["lightdm-slick-greeter"]}" "Greeter" "${1}" "${2}"
886 }
887
888 slick_init_configs(){
889     check_root
890     if [[ ! -f "${GREETER_CONFIG["lightdm-slick-greeter"]}" ]]; then
891         mkdir -p "$(dirname "${GREETER_CONFIG["lightdm-slick-greeter"]}")"
892         touch "${GREETER_CONFIG["lightdm-slick-greeter"]}"
893         echo "[Greeter]" > "${GREETER_CONFIG["lightdm-slick-greeter"]}"
894     fi
895 }
896
897 command_slick_grid(){
898     local _arg="$(echo "${1-""}" | tr "[:upper:]" "[:lower:]")"
899     if ! check_bool "${_arg}"; then
900         msg_error "Please specify true or false"
901         script_usage
902         exit 1
903     fi
904     slick_set_config "draw-grid" "${_arg}"
905 }
906
907 command_slick_back(){
908     if [[ -z "${1+SET}" ]] || [[ "${1}" = "" ]]; then
909         msg_error "Please specify the image of background"
910         exit 1
911     fi
912     if [[ ! -f "${1}" ]]; then
913         msg_error "${1} was not found."
914         exit 1
915     fi
916
917     local _backgrounf_file="/usr/share/backgrounds/lightdm/slick-greeter"
918     mkdir -p "$(dirname "${_backgrounf_file}")"
919     cp "${1}" "${_backgrounf_file}"
920     chmod 644 "${_backgrounf_file}"
921     
922     slick_set_config "background" "${_backgrounf_file}"
923 }
924
925
926 command_slick_theme_wizard(){
927     local gtk_themes
928     while read -r line; do gtk_themes+=("${line}"); done < <(get_gtk_theme)
929
930     # 質問する
931     local _theme
932     echo "Please select the theme to use."
933     if ! _theme="$(ask_question "${gtk_themes[@]}")"; then
934         command_slick_theme_wizard
935         exit 0
936     fi
937
938     # 結果に応じて処理を実行
939     if [[ -n "${_theme}" ]]; then
940         slick_set_config "theme-name" "${_theme}"
941     else
942         command_slick_theme_wizard
943         exit 0
944     fi
945     echo "Changed theme to ${_theme}"
946
947 }
948
949 command_slick_icon_wizard(){
950     local icons
951     while read -r line; do icons+=("${line}"); done < <(get_icon_theme)
952
953     local _icon
954     echo "Please select the icon theme to use."
955     if ! _icon="$(ask_question "${icons[@]}")"; then
956         command_slick_icon_wizard
957         exit 0
958     fi
959
960     if [[ -n "${_icon}" ]]; then
961         slick_set_config "icon-theme-name" "${_icon}"
962     else
963         command_slick_icon_wizard
964         exit 0
965     fi
966     echo "Changed icon theme to ${_icon}"
967 }
968
969
970 # 変数を設定
971 declare -A GREETER_CONFIG=(
972     ["lightdm-webkit2-greeter"]="/etc/lightdm/lightdm-webkit2-greeter.conf"
973     ["lightdm-slick-greeter"]="/etc/lightdm/slick-greeter.conf"
974     ["lightdm-gtk-greeter"]="/etc/lightdm/lightdm-gtk-greeter.conf"
975     ["io.elementary.greeter"]="/etc/lightdm/io.elementary.greeter.conf"
976     ["lightdm-mini-greeter"]="/etc/lightdm/lightdm-mini-greeter.conf"
977     ["lightdm-qtquick-greeter"]="/etc/lightdm/lightdm-qtquick-greeter.json"
978 )
979
980 declare -A DISPLAY_MANAGER_CONFIG=(
981     ["lightdm"]="/etc/lightdm/lightdm.conf.d/00-dmc-lightdm.conf"
982     ["gdm-dconf"]="/etc/dconf/db/gdm.d/00-dmc-gdm"
983     ["gdm-custom"]="/etc/gdm/custom.conf"
984 )
985
986
987 #== CONFIGS ==#
988
989 # LightDM - Greeterのディレクトリ
990 LIGHTDM_GREETERS_DIR="$(lightdm_get_value "greeters-directory")"
991 : "${LIGHTDM_GREETERS_DIR:="/usr/share/xgreeters"}"
992
993 # LightDM - Greeter一覧
994 #LIGHTDM_GREETERS=( $( )
995 while read -r line; do
996     LIGHTDM_GREETERS+=("${line}")
997 done < <( (find "${LIGHTDM_GREETERS_DIR}" -print0 -type f | xargs -0 -i basename {} | sed "s|.desktop$||g" | grep -xv "xgreeters") 2> /dev/null )
998
999 # LightDM - 現在設定されているGreeter
1000 LIGHTDM_CURRENT_GREETER="$(lightdm --show-config 2>&1 | grep "greeter-session" | cut -d "=" -f 2)"
1001 : "${LIGHTDM_CURRENT_GREETER:="lightdm-gtk-greeter"}"
1002
1003 # LightDM - 読み込まれた設定ファイルの一覧
1004 while read -r line; do
1005     LIGHTDM_LOADED_CONFIG+=("${line}")
1006 done < <(printf "%s\n" "$(lightdm --show-config 2>&1 | grep -x -A "$(lightdm --show-config 2>&1 | wc -l)" "Sources:" | grep -v "Sources" | sed 's|^[A-Z]  ||g')"  | tr -d " ")
1007
1008 # Global - エディタ
1009 USE_EDITOR="${EDITOR:-vi}"
1010
1011 # モード
1012 DISPLAY_MANAGER="$(basename "$(readlink "/etc/systemd/system/display-manager.service")" | sed "s|.service$||g")"
1013 : "${DISPLAY_MANAGER:="lightdm"}"
1014
1015 # dmc config
1016 NON_INTERACTIVE=false
1017 WRITE_ALL_FILES=false
1018 NOROOT=false
1019
1020
1021 #== 引数解析 ==#
1022 ARGUMENT="${*}"
1023 OPTS="m:e:h"
1024 OPTL="mode:,editor:,help,non-interactive,noroot,write-all-files"
1025 # shellcheck disable=SC2086
1026 if ! OPT="$(getopt -o ${OPTS} -l ${OPTL} -- ${ARGUMENT})"; then
1027     exit 1
1028 fi
1029
1030 eval set -- "${OPT}"
1031 unset OPT OPTS OPTL
1032
1033 while true; do
1034     case "${1}" in
1035         -m | --mode)
1036             DISPLAY_MANAGER="${2}"
1037             shift 2
1038             ;;
1039         -e | --editor)
1040             USE_EDITOR="${2}"
1041             shift 2
1042             ;;
1043         -h | --help)
1044             script_usage
1045             exit 0
1046             ;;
1047         --non-interactive)
1048             NON_INTERACTIVE=true
1049             shift 1
1050             ;;
1051         --noroot)
1052             NOROOT=true
1053             shift 1
1054             ;;
1055         --write-all-files)
1056             WRITE_ALL_FILES=true
1057             shift 1
1058             ;;
1059         --)
1060             shift 1
1061             break
1062             ;;
1063     esac
1064 done
1065
1066 COMMAND="${1:-null}"
1067 if (( "${#}" >= 1 )); then
1068     shift 1
1069 fi
1070 COMMAND_ARGS="${*}"
1071 : "${COMMAND_ARGS-""}" # サブコマンドの引数が何も指定されなかった場合に空文字を代入
1072
1073 if [[ "${COMMAND}" = "null" ]]; then
1074     script_usage
1075     exit 1
1076 fi
1077
1078 case "${DISPLAY_MANAGER}" in
1079     "lightdm")
1080         case "${COMMAND}" in
1081             "autologin")
1082                 lightdm_init_configs
1083                 command_lightdm_auto_login "${COMMAND_ARGS}"
1084                 ;;
1085             "greeter")
1086                 lightdm_init_configs
1087                 run_greeter_wizard
1088                 ;;
1089             "greeter-change")
1090                 lightdm_init_configs
1091                 command_lightdm_greeter_change "${COMMAND_ARGS}"
1092                 ;;
1093             "greeter-create")
1094                 lightdm_init_configs
1095                 command_lightdm_greeter_create "${COMMAND_ARGS}"
1096                 ;;
1097             "greeter-list")
1098                 command_lightdm_greeter_list
1099                 ;;
1100             "greeter-edit")
1101                 check_root
1102                 command_lightdm_greeter_edit "${COMMAND_ARGS}"
1103                 ;;
1104             "remove")
1105                 check_root
1106                 command_lightdm_remove
1107                 ;;
1108             "edit")
1109                 check_root
1110                 command_lightdm_edit
1111                 ;;
1112             "show-config")
1113                 command_lightdm_show_config
1114                 ;;
1115             *)
1116                 msg_error "Undefined commnad(${COMMAND})"
1117                 ;;
1118         esac
1119         ;;
1120     "gdm")
1121         case "${COMMAND}" in
1122             "autologin")
1123                 gdm_init_configs
1124                 command_gdm_auto_login "${COMMAND_ARGS}"
1125                 ;;
1126             "cursor")
1127                 gdm_init_configs
1128                 command_gdm_cursor_wizard
1129                 ;;
1130             "cursor-change")
1131                 gdm_init_configs
1132                 command_gdm_cursor_change "${COMMAND_ARGS}"
1133                 ;;
1134             "sound")
1135                 gdm_init_configs
1136                 command_gdm_sound "${COMMAND_ARGS}"
1137                 ;;
1138             "logo")
1139                 gdm_init_configs
1140                 command_gdm_logo "${COMMAND_ARGS}"
1141                 ;;
1142             "tap")
1143                 gdm_init_configs
1144                 command_gdm_tap "${COMMAND_ARGS}"
1145                 ;;
1146             "accessibility")
1147                 gdm_init_configs
1148                 command_gdm_accessibility "${COMMAND_ARGS}"
1149                 ;;
1150             "root-login")
1151                 gdm_init_configs
1152                 command_gdm_root_login "${COMMAND_ARGS}"
1153                 ;;
1154             *)
1155                 msg_error "Undefined commnad(${COMMAND})"
1156                 ;;
1157         esac
1158         ;;
1159     "webkit2")
1160         case "${COMMAND}" in
1161             "theme")
1162                 webkit2_init_configs
1163                 command_webkit2_theme_wizard
1164                 ;;
1165             "theme-change")
1166                 webkit2_init_configs
1167                 command_webkit2_theme_change "${COMMAND_ARGS}"
1168                 ;;
1169             *)
1170                 msg_error "Undefined commnad(${COMMAND})"
1171                 ;;
1172         esac
1173         ;;
1174     "qtquick")
1175         case "${COMMAND}" in
1176             "back")
1177                 qtquick_init_configs
1178                 command_qtquick_back "${COMMAND_ARGS}"
1179                 ;;
1180         esac
1181         ;;
1182     "slick")
1183         case "${COMMAND}" in
1184             "back")
1185                 slick_init_configs
1186                 command_slick_back "${COMMAND_ARGS}"
1187                 ;;
1188             "grid")
1189                 slick_init_configs
1190                 command_slick_grid "${COMMAND_ARGS}"
1191                 ;;
1192             "icon")
1193                 slick_init_configs
1194                 command_slick_icon_wizard
1195                 ;;
1196             "theme")
1197                 slick_init_configs
1198                 command_slick_theme_wizard
1199                 ;;
1200             "other-monitor")
1201                 slick_init_configs
1202                 command_slick_other_monitor "${COMMAND_ARGS}"
1203                 ;;
1204             "logo")
1205                 slick_init_configs
1206                 command_slick_logo "${COMMAND_ARGS}"
1207                 ;;
1208         esac
1209         ;;
1210     *)
1211         msg_error "A display manager that is not currently supported."
1212         exit 1
1213         ;;
1214 esac
1215 exit 0