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