yep, more dotfiles
1# Adapted from the original nixpkgs repo
2#
3# It supports static location fallback. This is a workaround waiting for an
4# alternative to MLS (Mozilla Location Services).
5
6# Target interface to manage the static file would be:
7# static = {
8# latitude = 48.8;
9# logitude = 2.3;
10# };
11#
12# I spent way too much time getting this to work with a submodule.
13
14{ config
15, lib
16, pkgs
17, ...
18}:
19
20let
21 package = pkgs.geoclue2.override { withDemoAgent = config.services.geoclue2.enableDemoAgent; };
22
23 cfg = config.services.geoclue2;
24
25 defaultWhitelist = [ "gnome-shell" "io.elementary.desktop.agent-geoclue2" ];
26
27 appConfigModule = lib.types.submodule ({ name, ... }: with lib; {
28 options = {
29 desktopID = mkOption {
30 type = types.str;
31 description = "Desktop ID of the application.";
32 };
33
34 isAllowed = mkOption {
35 type = types.bool;
36 description = ''
37 Whether the application will be allowed access to location information.
38 '';
39 };
40
41 isSystem = mkOption {
42 type = types.bool;
43 description = ''
44 Whether the application is a system component or not.
45 '';
46 };
47
48 users = mkOption {
49 type = types.listOf types.str;
50 default = [ ];
51 description = ''
52 List of UIDs of all users for which this application is allowed location
53 info access, Defaults to an empty string to allow it for all users.
54 '';
55 };
56 };
57
58 config.desktopID = mkDefault name;
59 });
60
61 # staticModule = types.submodule ({ name, ... }: {
62 # options = {
63 # latitude = mkOption {
64 # type = types.float;
65 # example = 40.6893129;
66 # };
67
68 # longitude = mkOption {
69 # type = types.float;
70 # example = -74.0445531;
71 # };
72
73 # altitude = mkOption {
74 # type = types.float;
75 # default = 0;
76 # example = 96;
77 # };
78
79 # accuracyRadius = mkOption {
80 # type = types.float;
81 # default = 0;
82 # example = 1.83;
83 # };
84 # };
85 # });
86
87 appConfigToINICompatible = _: { desktopID, isAllowed, isSystem, users, ... }: {
88 name = desktopID;
89 value = {
90 allowed = isAllowed;
91 system = isSystem;
92 users = lib.concatStringsSep ";" users;
93 };
94 };
95
96in
97{
98 disabledModules = [ "services/desktops/geoclue2.nix" ];
99
100 options.services.geoclue2 = with lib; {
101 enable = mkOption {
102 type = types.bool;
103 default = false;
104 description = ''
105 Whether to enable GeoClue 2 daemon, a DBus service
106 that provides location information for accessing.
107 '';
108 };
109
110 enableDemoAgent = mkOption {
111 type = types.bool;
112 default = true;
113 description = ''
114 Whether to use the GeoClue demo agent. This should be
115 overridden by desktop environments that provide their own
116 agent.
117 '';
118 };
119
120 enableNmea = mkOption {
121 type = types.bool;
122 default = true;
123 description = ''
124 Whether to fetch location from NMEA sources on local network.
125 '';
126 };
127
128 enable3G = mkOption {
129 type = types.bool;
130 default = true;
131 description = ''
132 Whether to enable 3G source.
133 '';
134 };
135
136 enableCDMA = mkOption {
137 type = types.bool;
138 default = true;
139 description = ''
140 Whether to enable CDMA source.
141 '';
142 };
143
144 enableModemGPS = mkOption {
145 type = types.bool;
146 default = true;
147 description = ''
148 Whether to enable Modem-GPS source.
149 '';
150 };
151
152 enableWifi = mkOption {
153 type = types.bool;
154 default = true;
155 description = ''
156 Whether to enable WiFi source.
157 '';
158 };
159
160 geoProviderUrl = mkOption {
161 type = types.str;
162 default = "https://location.services.mozilla.com/v1/geolocate?key=geoclue";
163 example = "https://www.googleapis.com/geolocation/v1/geolocate?key=YOUR_KEY";
164 description = ''
165 The url to the wifi GeoLocation Service.
166 '';
167 };
168
169 submitData = mkOption {
170 type = types.bool;
171 default = false;
172 description = ''
173 Whether to submit data to a GeoLocation Service.
174 '';
175 };
176
177 submissionUrl = mkOption {
178 type = types.str;
179 default = "https://location.services.mozilla.com/v1/submit?key=geoclue";
180 description = ''
181 The url to submit data to a GeoLocation Service.
182 '';
183 };
184
185 submissionNick = mkOption {
186 type = types.str;
187 default = "geoclue";
188 description = ''
189 A nickname to submit network data with.
190 Must be 2-32 characters long.
191 '';
192 };
193
194 appConfig = mkOption {
195 type = types.attrsOf appConfigModule;
196 default = { };
197 example = {
198 "com.github.app" = {
199 isAllowed = true;
200 isSystem = true;
201 users = [ "300" ];
202 };
203 };
204 description = ''
205 Specify extra settings per application.
206 '';
207 };
208
209 # static = mkOption {
210 # type = types.nullOr (types.attrsOf staticModule);
211 # default = null;
212 # description = ''
213 # Add a fallback location that will be overriden by other location services
214 # '';
215 # };
216
217 staticFile = mkOption {
218 type = types.nullOr (types.str);
219 default = null;
220 description = ''
221 Add a fallback location that will be overriden by other location services
222 '';
223 };
224 };
225
226 config = lib.mkIf cfg.enable {
227 environment.systemPackages = [ package ];
228
229 services.dbus.packages = [ package ];
230
231 systemd.packages = [ package ];
232
233 # we cannot use DynamicUser as we need the the geoclue user to exist for the
234 # dbus policy to work
235 users = {
236 users.geoclue = {
237 isSystemUser = true;
238 home = "/var/lib/geoclue";
239 group = "geoclue";
240 description = "Geoinformation service";
241 };
242
243 groups.geoclue = { };
244 };
245
246 systemd.services.geoclue = {
247 wants = lib.optionals cfg.enableWifi [ "network-online.target" ];
248 after = lib.optionals cfg.enableWifi [ "network-online.target" ];
249 # restart geoclue service when the configuration changes
250 restartTriggers = [
251 config.environment.etc."geoclue/geoclue.conf".source
252 ];
253 serviceConfig.StateDirectory = "geoclue";
254 };
255
256 # this needs to run as a user service, since it's associated with the
257 # user who is making the requests
258 systemd.user.services = lib.mkIf cfg.enableDemoAgent {
259 geoclue-agent = {
260 description = "Geoclue agent";
261 # this should really be `partOf = [ "geoclue.service" ]`, but
262 # we can't be part of a system service, and the agent should
263 # be okay with the main service coming and going
264 wantedBy = [ "default.target" ];
265 wants = lib.optionals cfg.enableWifi [ "network-online.target" ];
266 after = lib.optionals cfg.enableWifi [ "network-online.target" ];
267 unitConfig.ConditionUser = "!@system";
268 serviceConfig = {
269 Type = "exec";
270 ExecStart = "${package}/libexec/geoclue-2.0/demos/agent";
271 Restart = "on-failure";
272 PrivateTmp = true;
273 };
274 };
275 };
276
277 services.geoclue2.appConfig = {
278 epiphany = { isAllowed = true; isSystem = false; };
279 firefox = { isAllowed = true; isSystem = false; };
280 };
281
282 environment.etc."geoclue/geoclue.conf".text =
283 lib.generators.toINI { } ({
284 agent = {
285 whitelist = lib.concatStringsSep ";"
286 (lib.optional cfg.enableDemoAgent "geoclue-demo-agent" ++ defaultWhitelist);
287 };
288 network-nmea = {
289 enable = cfg.enableNmea;
290 };
291 "3g" = {
292 enable = cfg.enable3G;
293 };
294 cdma = {
295 enable = cfg.enableCDMA;
296 };
297 modem-gps = {
298 enable = cfg.enableModemGPS;
299 };
300 wifi = {
301 enable = cfg.enableWifi;
302 url = cfg.geoProviderUrl;
303 submit-data = lib.boolToString cfg.submitData;
304 submission-url = cfg.submissionUrl;
305 submission-nick = cfg.submissionNick;
306 };
307 } // lib.mapAttrs' appConfigToINICompatible cfg.appConfig);
308
309 # environment.etc."geolocation" = mkIf (cfg.static != null) {
310 # text = ''
311 # ${toString cfg.static.latitude}
312 # ${toString cfg.static.longitude}
313 # ${toString cfg.static.altitude}
314 # ${toString cfg.static.accuracyRadius}
315 # '';
316 # };
317
318 environment.etc."geolocation" = lib.mkIf (cfg.staticFile != null) { text = cfg.staticFile; };
319 };
320
321 meta = with lib; {
322 maintainers = with maintainers; [ ] ++ teams.pantheon.members;
323 };
324}