diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index a94cfc9..2b8bb33 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -58,8 +58,8 @@ body: attributes: label: Relevant logs description: | - Please copy and paste any relevant logs, error messages or any other output. This will be automatically formatted into code, so no need for backticks. Screenshots are accepted if they make life easier for you. - If it exists, ncluding your session log (found at /var/log/ly-session.log unless modified) is a good idea. (But make sure it's relevant!) + Please copy and paste (or attach) any relevant logs, error messages or any other output. This will be automatically formatted into code, so no need for backticks. Screenshots are accepted if they make life easier for you. + Moreover, it is almost always a good idea to include your session log and your general log files (found at ~/ly-session.log and /var/log/ly.log respectively by default) as it usually contains relevant information about the problem. render: shell - type: textarea id: moreinfo diff --git a/res/config.ini b/res/config.ini index eef5f9f..17db32c 100644 --- a/res/config.ini +++ b/res/config.ini @@ -199,6 +199,9 @@ login_defs_path = /etc/login.defs # no need to add `exec "$@"` at the end logout_cmd = null +# General log file path +ly_log = /var/log/ly.log + # Main box horizontal margin margin_box_h = 2 diff --git a/res/lang/ar.ini b/res/lang/ar.ini index eaa78b3..3a0ae3b 100644 --- a/res/lang/ar.ini +++ b/res/lang/ar.ini @@ -15,6 +15,7 @@ err_empty_password = لا يُسمح بكلمة مرور فارغة err_envlist = فشل في جلب قائمة المتغيرات البيئية err_hostname = فشل في جلب اسم المضيف (Hostname) + err_mlock = فشل في تأمين ذاكرة كلمة المرور (mlock) err_null = مؤشر فارغ (Null pointer) err_numlock = فشل في ضبط Num Lock diff --git a/res/lang/cat.ini b/res/lang/cat.ini index 71eabd6..985cb55 100644 --- a/res/lang/cat.ini +++ b/res/lang/cat.ini @@ -15,6 +15,7 @@ err_domain = domini invàlid err_envlist = error en obtenir l'envlist err_hostname = error en obtenir el nom de l'amfitrió + err_mlock = error en bloquejar la memòria de clau err_null = punter nul err_numlock = error en establir el Bloq num diff --git a/res/lang/cs.ini b/res/lang/cs.ini index e91dfbd..2798e18 100644 --- a/res/lang/cs.ini +++ b/res/lang/cs.ini @@ -15,6 +15,7 @@ err_domain = neplatná doména err_hostname = nelze získat název hostitele + err_mlock = uzamčení paměti hesel selhalo err_null = nulový ukazatel diff --git a/res/lang/de.ini b/res/lang/de.ini index 34c352e..bf70ace 100644 --- a/res/lang/de.ini +++ b/res/lang/de.ini @@ -15,6 +15,7 @@ err_empty_password = Leeres Passwort nicht zugelassen err_envlist = Fehler beim Abrufen der Umgebungs-Variablen err_hostname = Abrufen des Hostnames fehlgeschlagen + err_mlock = Sperren des Passwortspeichers fehlgeschlagen err_null = Null Pointer err_numlock = Numlock konnte nicht aktiviert werden diff --git a/res/lang/en.ini b/res/lang/en.ini index 9bcfeb0..11219b5 100644 --- a/res/lang/en.ini +++ b/res/lang/en.ini @@ -15,6 +15,7 @@ err_empty_password = empty password not allowed err_envlist = failed to get envlist err_hostname = failed to get hostname err_lock_state = failed to get lock state +err_log = failed to open log file err_mlock = failed to lock password memory err_null = null pointer err_numlock = failed to set numlock diff --git a/res/lang/es.ini b/res/lang/es.ini index d4c79d5..9676c0e 100644 --- a/res/lang/es.ini +++ b/res/lang/es.ini @@ -15,6 +15,7 @@ err_domain = dominio inválido err_hostname = error al obtener el nombre de host + err_mlock = error al bloquear la contraseña de memoria err_null = puntero nulo diff --git a/res/lang/fr.ini b/res/lang/fr.ini index 9553985..4ee547e 100644 --- a/res/lang/fr.ini +++ b/res/lang/fr.ini @@ -15,6 +15,7 @@ err_empty_password = mot de passe vide non autorisé err_envlist = échec de lecture de la liste d'environnement err_hostname = échec de lecture du nom d'hôte err_lock_state = échec de lecture de l'état de verrouillage +err_log = échec de l'ouverture du fichier de journal err_mlock = échec du verrouillage mémoire err_null = pointeur null err_numlock = échec de modification du verr.num diff --git a/res/lang/it.ini b/res/lang/it.ini index 27f6e46..ada3999 100644 --- a/res/lang/it.ini +++ b/res/lang/it.ini @@ -15,6 +15,7 @@ err_domain = dominio non valido err_hostname = impossibile ottenere hostname + err_mlock = impossibile ottenere lock per la password in memoria err_null = puntatore nullo diff --git a/res/lang/ja_JP.ini b/res/lang/ja_JP.ini index 1bb02fc..27f6083 100644 --- a/res/lang/ja_JP.ini +++ b/res/lang/ja_JP.ini @@ -15,6 +15,7 @@ err_empty_password = 空のパスワードは許可されていません err_envlist = 環境変数リストの取得に失敗しました err_hostname = ホスト名の取得に失敗しました + err_mlock = パスワードメモリのロックに失敗しました err_null = ヌルポインタ err_numlock = NumLockの設定に失敗しました diff --git a/res/lang/pl.ini b/res/lang/pl.ini index e94d8c0..a35a38e 100644 --- a/res/lang/pl.ini +++ b/res/lang/pl.ini @@ -15,6 +15,7 @@ err_empty_password = puste hasło jest niedozwolone err_envlist = nie udało się pobrać listy zmiennych środowiskowych err_hostname = nie udało się uzyskać nazwy hosta + err_mlock = nie udało się zablokować pamięci haseł err_null = pusty wskaźnik err_numlock = nie udało się ustawić numlock diff --git a/res/lang/pt.ini b/res/lang/pt.ini index ef566de..a94257e 100644 --- a/res/lang/pt.ini +++ b/res/lang/pt.ini @@ -15,6 +15,7 @@ err_domain = domínio inválido err_hostname = erro ao obter o nome do host + err_mlock = erro de bloqueio de memória err_null = ponteiro nulo diff --git a/res/lang/pt_BR.ini b/res/lang/pt_BR.ini index a15f0c9..66d990c 100644 --- a/res/lang/pt_BR.ini +++ b/res/lang/pt_BR.ini @@ -15,6 +15,7 @@ err_domain = domínio inválido err_hostname = não foi possível obter o nome do host + err_mlock = bloqueio da memória de senha malsucedido err_null = ponteiro nulo diff --git a/res/lang/ro.ini b/res/lang/ro.ini index 53a7432..008def0 100644 --- a/res/lang/ro.ini +++ b/res/lang/ro.ini @@ -19,6 +19,7 @@ capslock = capslock + err_pam_abort = tranzacţie pam anulată err_pam_acct_expired = cont expirat err_pam_auth = eroare de autentificare diff --git a/res/lang/ru.ini b/res/lang/ru.ini index 07d164c..bb6f98e 100644 --- a/res/lang/ru.ini +++ b/res/lang/ru.ini @@ -15,6 +15,7 @@ err_empty_password = пустой пароль не допустим err_envlist = не удалось получить список переменных среды err_hostname = не удалось получить имя хоста + err_mlock = сбой блокировки памяти err_null = нулевой указатель diff --git a/res/lang/sr.ini b/res/lang/sr.ini index fb8c26c..7dc54bf 100644 --- a/res/lang/sr.ini +++ b/res/lang/sr.ini @@ -15,6 +15,7 @@ err_domain = nevazeci domen err_hostname = neuspijesno trazenje hostname-a + err_mlock = neuspijesno zakljucavanje memorije lozinke err_null = null pokazivac diff --git a/res/lang/sv.ini b/res/lang/sv.ini index 7aaff8d..6b72ada 100644 --- a/res/lang/sv.ini +++ b/res/lang/sv.ini @@ -15,6 +15,7 @@ err_domain = okänd domän err_hostname = misslyckades att hämta värdnamn + err_mlock = misslyckades att låsa lösenordsminne err_null = nullpekare diff --git a/res/lang/tr.ini b/res/lang/tr.ini index aee7fbd..9c7b7a5 100644 --- a/res/lang/tr.ini +++ b/res/lang/tr.ini @@ -15,6 +15,7 @@ err_domain = gecersiz etki alani err_hostname = ana bilgisayar adi alinamadi + err_mlock = parola bellegi kilitlenemedi err_null = bos isaretci hatasi diff --git a/res/lang/uk.ini b/res/lang/uk.ini index 16d740c..20ad6c4 100644 --- a/res/lang/uk.ini +++ b/res/lang/uk.ini @@ -15,6 +15,7 @@ err_domain = недійсний домен err_hostname = не вдалося отримати ім'я хосту + err_mlock = збій блокування пам'яті err_null = нульовий вказівник diff --git a/res/lang/zh_CN.ini b/res/lang/zh_CN.ini index 971cb1c..639c224 100644 --- a/res/lang/zh_CN.ini +++ b/res/lang/zh_CN.ini @@ -15,6 +15,7 @@ err_domain = 无效的域 err_hostname = 获取主机名失败 + err_mlock = 锁定密码存储器失败 err_null = 空指针 diff --git a/src/config/Config.zig b/src/config/Config.zig index 2399898..1aea18a 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -54,6 +54,7 @@ load: bool = true, login_cmd: ?[]const u8 = null, login_defs_path: []const u8 = "/etc/login.defs", logout_cmd: ?[]const u8 = null, +ly_log: []const u8 = "/var/log/ly.log", margin_box_h: u8 = 2, margin_box_v: u8 = 1, min_refresh_delta: u16 = 5, diff --git a/src/config/Lang.zig b/src/config/Lang.zig index 0649619..cbeee18 100644 --- a/src/config/Lang.zig +++ b/src/config/Lang.zig @@ -20,6 +20,7 @@ err_empty_password: []const u8 = "empty password not allowed", err_envlist: []const u8 = "failed to get envlist", err_hostname: []const u8 = "failed to get hostname", err_lock_state: []const u8 = "failed to get lock state", +err_log: []const u8 = "failed to open log file", err_mlock: []const u8 = "failed to lock password memory", err_null: []const u8 = "null pointer", err_numlock: []const u8 = "failed to set numlock", diff --git a/src/main.zig b/src/main.zig index 47d733d..aeab079 100644 --- a/src/main.zig +++ b/src/main.zig @@ -201,6 +201,25 @@ pub fn main() !void { migrator.lateConfigFieldHandler(&config.animation); } + var log_file: std.fs.File = undefined; + defer log_file.close(); + + var could_open_log_file = true; + open_log_file: { + log_file = std.fs.cwd().openFile(config.ly_log, .{ .mode = .write_only }) catch std.fs.cwd().createFile(config.ly_log, .{ .mode = 0o666 }) catch { + // If we could neither open an existing log file nor create a new + // one, abort. + could_open_log_file = false; + break :open_log_file; + }; + } + + if (!could_open_log_file) { + log_file = try std.fs.openFileAbsolute("/dev/null", .{ .mode = .write_only }); + } + + const log_writer = log_file.writer(); + // if (migrator.mapped_config_fields) save_migrated_config: { // var file = try std.fs.cwd().createFile(config_path, .{}); // defer file.close(); @@ -217,8 +236,12 @@ pub fn main() !void { restart_cmd = try temporary_allocator.dupe(u8, config.restart_cmd); // Initialize termbox + try log_writer.writeAll("initializing termbox2\n"); _ = termbox.tb_init(); - defer _ = termbox.tb_shutdown(); + defer { + log_writer.writeAll("shutting down termbox2\n") catch {}; + _ = termbox.tb_shutdown(); + } const act = std.posix.Sigaction{ .handler = .{ .handler = &signalHandler }, @@ -255,6 +278,8 @@ pub fn main() !void { }; var buffer = TerminalBuffer.init(buffer_options, labels_max_length, random); + try log_writer.print("screen resolution is {d}x{d}\n", .{ buffer.width, buffer.height }); + // Initialize components var info_line = InfoLine.init(allocator, &buffer); defer info_line.deinit(); @@ -262,27 +287,37 @@ pub fn main() !void { if (config_load_failed) { // We can't localize this since the config failed to load so we'd fallback to the default language anyway try info_line.addMessage("unable to parse config file", config.error_bg, config.error_fg); + try log_writer.writeAll("unable to parse config file\n"); } - interop.setNumlock(config.numlock) catch { + if (!could_open_log_file) { + try info_line.addMessage(lang.err_log, config.error_bg, config.error_fg); + try log_writer.writeAll("failed to open log file\n"); + } + + interop.setNumlock(config.numlock) catch |err| { try info_line.addMessage(lang.err_numlock, config.error_bg, config.error_fg); + try log_writer.print("failed to set numlock: {s}\n", .{@errorName(err)}); }; var session = Session.init(allocator, &buffer); defer session.deinit(); - addOtherEnvironment(&session, lang, .shell, null) catch { + addOtherEnvironment(&session, lang, .shell, null) catch |err| { try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg); + try log_writer.print("failed to add shell environment: {s}\n", .{@errorName(err)}); }; if (build_options.enable_x11_support) { if (config.xinitrc) |xinitrc_cmd| { - addOtherEnvironment(&session, lang, .xinitrc, xinitrc_cmd) catch { + addOtherEnvironment(&session, lang, .xinitrc, xinitrc_cmd) catch |err| { try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg); + try log_writer.print("failed to add xinitrc environment: {s}\n", .{@errorName(err)}); }; } } else { try info_line.addMessage(lang.no_x11_support, config.bg, config.fg); + try log_writer.writeAll("x11 support disabled at compile-time\n"); } if (config.initial_info_text) |text| { @@ -290,8 +325,9 @@ pub fn main() !void { } else get_host_name: { // Initialize information line with host name var name_buf: [std.posix.HOST_NAME_MAX]u8 = undefined; - const hostname = std.posix.gethostname(&name_buf) catch { + const hostname = std.posix.gethostname(&name_buf) catch |err| { try info_line.addMessage(lang.err_hostname, config.error_bg, config.error_fg); + try log_writer.print("failed to get hostname: {s}\n", .{@errorName(err)}); break :get_host_name; }; try info_line.addMessage(hostname, config.bg, config.fg); @@ -327,6 +363,7 @@ pub fn main() !void { // accounts *and* no root account...but at this point, if that's the // case, you have bigger problems to deal with in the first place. :D try info_line.addMessage(lang.err_no_users, config.error_bg, config.error_fg); + try log_writer.writeAll("no users found\n"); } var login = try UserList.init(allocator, &buffer, usernames); @@ -374,8 +411,9 @@ pub fn main() !void { .info_line => info_line.label.handle(null, insert_mode), .session => session.label.handle(null, insert_mode), .login => login.label.handle(null, insert_mode), - .password => password.handle(null, insert_mode) catch { + .password => password.handle(null, insert_mode) catch |err| { try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg); + try log_writer.print("failed to handle password input: {s}\n", .{@errorName(err)}); }, } } @@ -426,8 +464,9 @@ pub fn main() !void { var auth_fails: u64 = 0; // Switch to selected TTY - interop.switchTty(config.tty) catch { + interop.switchTty(config.tty) catch |err| { try info_line.addMessage(lang.err_switch_tty, config.error_bg, config.error_fg); + try log_writer.print("failed to switch tty: {s}\n", .{@errorName(err)}); }; while (run) { @@ -442,12 +481,14 @@ pub fn main() !void { if (width != buffer.width or height != buffer.height) { // If it did change, then update the cell buffer, reallocate the current animation's buffers, and force a draw update + try log_writer.print("screen resolution updated to {d}x{d}\n", .{ width, height }); buffer.width = width; buffer.height = height; - animation.realloc() catch { + animation.realloc() catch |err| { try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg); + try log_writer.print("failed to reallocate animation buffers: {s}\n", .{@errorName(err)}); }; update = true; @@ -518,8 +559,9 @@ pub fn main() !void { .info_line => info_line.label.handle(null, insert_mode), .session => session.label.handle(null, insert_mode), .login => login.label.handle(null, insert_mode), - .password => password.handle(null, insert_mode) catch { + .password => password.handle(null, insert_mode) catch |err| { try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg); + try log_writer.print("failed to handle password input: {s}\n", .{@errorName(err)}); }, } @@ -532,6 +574,7 @@ pub fn main() !void { if (clock_str.len == 0) { try info_line.addMessage(lang.err_clock_too_long, config.error_bg, config.error_fg); can_draw_clock = false; + try log_writer.writeAll("clock string too long\n"); break :draw_clock; } @@ -599,9 +642,10 @@ pub fn main() !void { } if (can_get_lock_state) draw_lock_state: { - const lock_state = interop.getLockState() catch { + const lock_state = interop.getLockState() catch |err| { try info_line.addMessage(lang.err_lock_state, config.error_bg, config.error_fg); can_get_lock_state = false; + try log_writer.print("failed to get lock state: {s}\n", .{@errorName(err)}); break :draw_lock_state; }; @@ -682,16 +726,19 @@ pub fn main() !void { }; if (process_result.Exited != 0) { try info_line.addMessage(lang.err_sleep, config.error_bg, config.error_fg); + try log_writer.print("failed to execute sleep command: exit code {d}\n", .{process_result.Exited}); } } } } else if (brightness_down_key != null and pressed_key == brightness_down_key.?) { - adjustBrightness(allocator, config.brightness_down_cmd) catch { + adjustBrightness(allocator, config.brightness_down_cmd) catch |err| { try info_line.addMessage(lang.err_brightness_change, config.error_bg, config.error_fg); + try log_writer.print("failed to change brightness: {s}\n", .{@errorName(err)}); }; } else if (brightness_up_key != null and pressed_key == brightness_up_key.?) { - adjustBrightness(allocator, config.brightness_up_cmd) catch { + adjustBrightness(allocator, config.brightness_up_cmd) catch |err| { try info_line.addMessage(lang.err_brightness_change, config.error_bg, config.error_fg); + try log_writer.print("failed to change brightness: {s}\n", .{@errorName(err)}); }; } }, @@ -735,10 +782,14 @@ pub fn main() !void { update = true; }, termbox.TB_KEY_ENTER => authenticate: { + try log_writer.writeAll("authenticating..."); + if (!config.allow_empty_password and password.text.items.len == 0) { + // Let's not log this message for security reasons try info_line.addMessage(lang.err_empty_password, config.error_bg, config.error_fg); - InfoLine.clearRendered(allocator, buffer) catch { + InfoLine.clearRendered(allocator, buffer) catch |err| { try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg); + try log_writer.print("failed to clear info line: {s}\n", .{@errorName(err)}); }; info_line.label.draw(); _ = termbox.tb_present(); @@ -746,8 +797,9 @@ pub fn main() !void { } try info_line.addMessage(lang.authenticating, config.bg, config.fg); - InfoLine.clearRendered(allocator, buffer) catch { + InfoLine.clearRendered(allocator, buffer) catch |err| { try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg); + try log_writer.print("failed to clear info line: {s}\n", .{@errorName(err)}); }; info_line.label.draw(); _ = termbox.tb_present(); @@ -820,7 +872,10 @@ pub fn main() !void { if (auth_err) |err| { auth_fails += 1; active_input = .password; + try info_line.addMessage(getAuthErrorMsg(err, lang), config.error_bg, config.error_fg); + try log_writer.print("failed to authenticate: {s}\n", .{@errorName(err)}); + if (config.clear_password or err != error.PamAuthError) password.clear(); } else { if (config.logout_cmd) |logout_cmd| { @@ -830,6 +885,7 @@ pub fn main() !void { password.clear(); try info_line.addMessage(lang.logout, config.bg, config.fg); + try log_writer.writeAll("logged out"); } try std.posix.tcsetattr(std.posix.STDIN_FILENO, .FLUSH, tb_termios);