also looking at this
fix(services): launch postgres from a script, and find libs where they are
#258
3 files
+189
-29
| @@ -3,6 +3,7 @@ const builtin = ; | ||
| 3 | 3 | const definitions = ; |
| 4 | 4 | const lib = ; |
| 5 | 5 | const io_helper = lib.io_helper; |
| 6 | const style = ; | |
| 6 | 7 | |
| 7 | 8 | /// Whether services are managed in the per-user systemd instance (`systemctl |
| 8 | 9 | /// --user`, units under `~/.config/systemd/user`, `WantedBy=default.target`) or |
| @@ -375,9 +376,46 @@ pub const ServiceController = struct { | ||
| 375 | 376 | } catch return; |
| 376 | 377 | } |
| 377 | 378 | |
| 379 | /// Is there a systemd user manager for this user to talk to? | |
| 380 | /// | |
| 381 | /// `systemctl --user` needs a per-user bus, which exists only while the | |
| 382 | /// user has a login session or lingering is enabled. Without it every | |
| 383 | /// call fails with "Failed to connect to bus: No medium found", which | |
| 384 | /// names neither the cause nor the fix (#243). Checked by looking for the | |
| 385 | /// runtime dir the bus socket lives in, so no process is spawned. | |
| 386 | fn userBusAvailable() bool { | |
| 387 | if (io_helper.getenv("XDG_RUNTIME_DIR")) |dir| { | |
| 388 | var buf: [std.fs.max_path_bytes]u8 = undefined; | |
| 389 | const socket = std.fmt.bufPrint(&buf, "{s}/systemd/private", .{dir}) catch return true; | |
| 390 | io_helper.accessAbsolute(socket, .{}) catch return false; | |
| 391 | return true; | |
| 392 | } | |
| 393 | return false; | |
| 394 | } | |
| 395 | ||
| 396 | /// What to do about a missing user bus, in the operator's own terms. | |
| 397 | pub fn userBusHint(allocator: std.mem.Allocator) ![]const u8 { | |
| 398 | const user = io_helper.getenv("USER") orelse io_helper.getenv("LOGNAME") orelse "$USER"; | |
| 399 | return std.fmt.allocPrint( | |
| 400 | allocator, | |
| 401 | "No systemd user manager is running for this session, so `systemctl --user` cannot be reached.\n" ++ | |
| 402 | " Enable it for this user: loginctl enable-linger {s}\n" ++ | |
| 403 | " Or run the service in system scope instead (as root).", | |
| 404 | .{user}, | |
| 405 | ); | |
| 406 | } | |
| 407 | ||
| 378 | 408 | fn systemdStart(self: *ServiceController, service_name: []const u8, project_id: ?[]const u8) !void { |
| 379 | 409 | const service_unit = try self.getSystemdUnit(service_name, project_id); |
| 380 | 410 | defer self.allocator.free(service_unit); |
| 411 | // Reported before the attempt: the raw failure is a bus error that | |
| 412 | // says nothing about lingering, and it is the same for every verb. | |
| 413 | if (self.scope == .user and !userBusAvailable()) { | |
| 414 | const hint = userBusHint(self.allocator) catch return error.ServiceStartFailed; | |
| 415 | defer self.allocator.free(hint); | |
| 416 | style.printWarn("{s}\n", .{hint}); | |
| 417 | return error.ServiceStartFailed; | |
| 418 | } | |
| 381 | 419 | if (!try self.runSystemctl("start", service_unit)) return error.ServiceStartFailed; |
| 382 | 420 | } |
| 383 | 421 | |