1{ config, lib, pkgs, utils, ... }:
2
3with lib;
4
5let
6
7 uid = config.ids.uids.gpsd;
8 gid = config.ids.gids.gpsd;
9 cfg = config.services.gpsd;
10
11in {
12
13 ###### interface
14
15 imports = [
16 (lib.mkRemovedOptionModule [ "services" "gpsd" "device" ]
17 "Use `services.gpsd.devices` instead.")
18 ];
19
20 options = {
21
22 services.gpsd = {
23
24 enable = mkOption {
25 type = types.bool;
26 default = false;
27 description = lib.mdDoc ''
28 Whether to enable `gpsd`, a GPS service daemon.
29 '';
30 };
31
32 devices = mkOption {
33 type = types.listOf types.str;
34 default = [ "/dev/ttyUSB0" ];
35 description = lib.mdDoc ''
36 List of devices that `gpsd` should subscribe to.
37
38 A device may be a local serial device for GPS input, or a
39 URL of the form:
40 `[{dgpsip|ntrip}://][user:passwd@]host[:port][/stream]` in
41 which case it specifies an input source for DGPS or ntrip
42 data.
43 '';
44 };
45
46 readonly = mkOption {
47 type = types.bool;
48 default = true;
49 description = lib.mdDoc ''
50 Whether to enable the broken-device-safety, otherwise
51 known as read-only mode. Some popular bluetooth and USB
52 receivers lock up or become totally inaccessible when
53 probed or reconfigured. This switch prevents gpsd from
54 writing to a receiver. This means that gpsd cannot
55 configure the receiver for optimal performance, but it
56 also means that gpsd cannot break the receiver. A better
57 solution would be for Bluetooth to not be so fragile. A
58 platform independent method to identify
59 serial-over-Bluetooth devices would also be nice.
60 '';
61 };
62
63 nowait = mkOption {
64 type = types.bool;
65 default = false;
66 description = lib.mdDoc ''
67 don't wait for client connects to poll GPS
68 '';
69 };
70
71 port = mkOption {
72 type = types.port;
73 default = 2947;
74 description = lib.mdDoc ''
75 The port where to listen for TCP connections.
76 '';
77 };
78
79 debugLevel = mkOption {
80 type = types.int;
81 default = 0;
82 description = lib.mdDoc ''
83 The debugging level.
84 '';
85 };
86
87 listenany = mkOption {
88 type = types.bool;
89 default = false;
90 description = lib.mdDoc ''
91 Listen on all addresses rather than just loopback.
92 '';
93 };
94
95 };
96
97 };
98
99 ###### implementation
100
101 config = mkIf cfg.enable {
102
103 users.users.gpsd = {
104 inherit uid;
105 group = "gpsd";
106 description = "gpsd daemon user";
107 home = "/var/empty";
108 };
109
110 users.groups.gpsd = { inherit gid; };
111
112 systemd.services.gpsd = {
113 description = "GPSD daemon";
114 wantedBy = [ "multi-user.target" ];
115 after = [ "network.target" ];
116 serviceConfig = {
117 Type = "forking";
118 ExecStart = let
119 devices = utils.escapeSystemdExecArgs cfg.devices;
120 in ''
121 ${pkgs.gpsd}/sbin/gpsd -D "${toString cfg.debugLevel}" \
122 -S "${toString cfg.port}" \
123 ${optionalString cfg.readonly "-b"} \
124 ${optionalString cfg.nowait "-n"} \
125 ${optionalString cfg.listenany "-G"} \
126 ${devices}
127 '';
128 };
129 };
130
131 };
132
133}