chiark / gitweb /
sd-bus: port one use of SO_PEERCRED by getpeercred()
[elogind.git] / src / shared / bus-util.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3   This file is part of systemd.
4
5   Copyright 2013 Lennart Poettering
6
7   systemd is free software; you can redistribute it and/or modify it
8   under the terms of the GNU Lesser General Public License as published by
9   the Free Software Foundation; either version 2.1 of the License, or
10   (at your option) any later version.
11
12   systemd is distributed in the hope that it will be useful, but
13   WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15   Lesser General Public License for more details.
16
17   You should have received a copy of the GNU Lesser General Public License
18   along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <inttypes.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <sys/ioctl.h>
28 #include <sys/resource.h>
29 #include <sys/socket.h>
30 #include <unistd.h>
31
32 #include "sd-bus-protocol.h"
33 #include "sd-bus.h"
34 #include "sd-daemon.h"
35 #include "sd-event.h"
36 #include "sd-id128.h"
37
38 #include "alloc-util.h"
39 #include "bus-internal.h"
40 #include "bus-label.h"
41 #include "bus-message.h"
42 #include "bus-util.h"
43 #include "cap-list.h"
44 #include "cgroup-util.h"
45 #include "def.h"
46 #include "escape.h"
47 #include "fd-util.h"
48 #include "missing.h"
49 #include "mount-util.h"
50 #include "nsflags.h"
51 #include "parse-util.h"
52 #include "proc-cmdline.h"
53 //#include "rlimit-util.h"
54 #include "stdio-util.h"
55 #include "strv.h"
56 #include "user-util.h"
57
58 #if 0 /// UNNEEDED by elogind
59 static int name_owner_change_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
60         sd_event *e = userdata;
61
62         assert(m);
63         assert(e);
64
65         sd_bus_close(sd_bus_message_get_bus(m));
66         sd_event_exit(e, 0);
67
68         return 1;
69 }
70
71 int bus_async_unregister_and_exit(sd_event *e, sd_bus *bus, const char *name) {
72         _cleanup_free_ char *match = NULL;
73         const char *unique;
74         int r;
75
76         assert(e);
77         assert(bus);
78         assert(name);
79
80         /* We unregister the name here and then wait for the
81          * NameOwnerChanged signal for this event to arrive before we
82          * quit. We do this in order to make sure that any queued
83          * requests are still processed before we really exit. */
84
85         r = sd_bus_get_unique_name(bus, &unique);
86         if (r < 0)
87                 return r;
88
89         r = asprintf(&match,
90                      "sender='org.freedesktop.DBus',"
91                      "type='signal',"
92                      "interface='org.freedesktop.DBus',"
93                      "member='NameOwnerChanged',"
94                      "path='/org/freedesktop/DBus',"
95                      "arg0='%s',"
96                      "arg1='%s',"
97                      "arg2=''", name, unique);
98         if (r < 0)
99                 return -ENOMEM;
100
101         r = sd_bus_add_match(bus, NULL, match, name_owner_change_callback, e);
102         if (r < 0)
103                 return r;
104
105         r = sd_bus_release_name(bus, name);
106         if (r < 0)
107                 return r;
108
109         return 0;
110 }
111
112 int bus_event_loop_with_idle(
113                 sd_event *e,
114                 sd_bus *bus,
115                 const char *name,
116                 usec_t timeout,
117                 check_idle_t check_idle,
118                 void *userdata) {
119         bool exiting = false;
120         int r, code;
121
122         assert(e);
123         assert(bus);
124         assert(name);
125
126         for (;;) {
127                 bool idle;
128
129                 r = sd_event_get_state(e);
130                 if (r < 0)
131                         return r;
132                 if (r == SD_EVENT_FINISHED)
133                         break;
134
135                 if (check_idle)
136                         idle = check_idle(userdata);
137                 else
138                         idle = true;
139
140                 r = sd_event_run(e, exiting || !idle ? (uint64_t) -1 : timeout);
141                 if (r < 0)
142                         return r;
143
144                 if (r == 0 && !exiting && idle) {
145
146                         r = sd_bus_try_close(bus);
147                         if (r == -EBUSY)
148                                 continue;
149
150                         /* Fallback for dbus1 connections: we
151                          * unregister the name and wait for the
152                          * response to come through for it */
153                         if (r == -EOPNOTSUPP) {
154
155                                 /* Inform the service manager that we
156                                  * are going down, so that it will
157                                  * queue all further start requests,
158                                  * instead of assuming we are already
159                                  * running. */
160                                 sd_notify(false, "STOPPING=1");
161
162                                 r = bus_async_unregister_and_exit(e, bus, name);
163                                 if (r < 0)
164                                         return r;
165
166                                 exiting = true;
167                                 continue;
168                         }
169
170                         if (r < 0)
171                                 return r;
172
173                         sd_event_exit(e, 0);
174                         break;
175                 }
176         }
177
178         r = sd_event_get_exit_code(e, &code);
179         if (r < 0)
180                 return r;
181
182         return code;
183 }
184 #endif // 0
185
186 int bus_name_has_owner(sd_bus *c, const char *name, sd_bus_error *error) {
187         _cleanup_(sd_bus_message_unrefp) sd_bus_message *rep = NULL;
188         int r, has_owner = 0;
189
190         assert(c);
191         assert(name);
192
193         r = sd_bus_call_method(c,
194                                "org.freedesktop.DBus",
195                                "/org/freedesktop/dbus",
196                                "org.freedesktop.DBus",
197                                "NameHasOwner",
198                                error,
199                                &rep,
200                                "s",
201                                name);
202         if (r < 0)
203                 return r;
204
205         r = sd_bus_message_read_basic(rep, 'b', &has_owner);
206         if (r < 0)
207                 return sd_bus_error_set_errno(error, r);
208
209         return has_owner;
210 }
211
212 static int check_good_user(sd_bus_message *m, uid_t good_user) {
213         _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
214         uid_t sender_uid;
215         int r;
216
217         assert(m);
218
219         if (good_user == UID_INVALID)
220                 return 0;
221
222         r = sd_bus_query_sender_creds(m, SD_BUS_CREDS_EUID, &creds);
223         if (r < 0)
224                 return r;
225
226         /* Don't trust augmented credentials for authorization */
227         assert_return((sd_bus_creds_get_augmented_mask(creds) & SD_BUS_CREDS_EUID) == 0, -EPERM);
228
229         r = sd_bus_creds_get_euid(creds, &sender_uid);
230         if (r < 0)
231                 return r;
232
233         return sender_uid == good_user;
234 }
235
236 int bus_test_polkit(
237                 sd_bus_message *call,
238                 int capability,
239                 const char *action,
240                 const char **details,
241                 uid_t good_user,
242                 bool *_challenge,
243                 sd_bus_error *e) {
244
245         int r;
246
247         assert(call);
248         assert(action);
249
250         /* Tests non-interactively! */
251
252         r = check_good_user(call, good_user);
253         if (r != 0)
254                 return r;
255
256         r = sd_bus_query_sender_privilege(call, capability);
257         if (r < 0)
258                 return r;
259         else if (r > 0)
260                 return 1;
261 #if ENABLE_POLKIT
262         else {
263                 _cleanup_(sd_bus_message_unrefp) sd_bus_message *request = NULL;
264                 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
265                 int authorized = false, challenge = false;
266                 const char *sender, **k, **v;
267
268                 sender = sd_bus_message_get_sender(call);
269                 if (!sender)
270                         return -EBADMSG;
271
272                 r = sd_bus_message_new_method_call(
273                                 call->bus,
274                                 &request,
275                                 "org.freedesktop.PolicyKit1",
276                                 "/org/freedesktop/PolicyKit1/Authority",
277                                 "org.freedesktop.PolicyKit1.Authority",
278                                 "CheckAuthorization");
279                 if (r < 0)
280                         return r;
281
282                 r = sd_bus_message_append(
283                                 request,
284                                 "(sa{sv})s",
285                                 "system-bus-name", 1, "name", "s", sender,
286                                 action);
287                 if (r < 0)
288                         return r;
289
290                 r = sd_bus_message_open_container(request, 'a', "{ss}");
291                 if (r < 0)
292                         return r;
293
294                 STRV_FOREACH_PAIR(k, v, details) {
295                         r = sd_bus_message_append(request, "{ss}", *k, *v);
296                         if (r < 0)
297                                 return r;
298                 }
299
300                 r = sd_bus_message_close_container(request);
301                 if (r < 0)
302                         return r;
303
304                 r = sd_bus_message_append(request, "us", 0, NULL);
305                 if (r < 0)
306                         return r;
307
308                 r = sd_bus_call(call->bus, request, 0, e, &reply);
309                 if (r < 0) {
310                         /* Treat no PK available as access denied */
311                         if (sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN)) {
312                                 sd_bus_error_free(e);
313                                 return -EACCES;
314                         }
315
316                         return r;
317                 }
318
319                 r = sd_bus_message_enter_container(reply, 'r', "bba{ss}");
320                 if (r < 0)
321                         return r;
322
323                 r = sd_bus_message_read(reply, "bb", &authorized, &challenge);
324                 if (r < 0)
325                         return r;
326
327                 if (authorized)
328                         return 1;
329
330                 if (_challenge) {
331                         *_challenge = challenge;
332                         return 0;
333                 }
334         }
335 #endif
336
337         return -EACCES;
338 }
339
340 #if ENABLE_POLKIT
341
342 typedef struct AsyncPolkitQuery {
343         sd_bus_message *request, *reply;
344         sd_bus_message_handler_t callback;
345         void *userdata;
346         sd_bus_slot *slot;
347         Hashmap *registry;
348 } AsyncPolkitQuery;
349
350 static void async_polkit_query_free(AsyncPolkitQuery *q) {
351
352         if (!q)
353                 return;
354
355         sd_bus_slot_unref(q->slot);
356
357         if (q->registry && q->request)
358                 hashmap_remove(q->registry, q->request);
359
360         sd_bus_message_unref(q->request);
361         sd_bus_message_unref(q->reply);
362
363         free(q);
364 }
365
366 static int async_polkit_callback(sd_bus_message *reply, void *userdata, sd_bus_error *error) {
367         _cleanup_(sd_bus_error_free) sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
368         AsyncPolkitQuery *q = userdata;
369         int r;
370
371         assert(reply);
372         assert(q);
373
374         q->slot = sd_bus_slot_unref(q->slot);
375         q->reply = sd_bus_message_ref(reply);
376
377         r = sd_bus_message_rewind(q->request, true);
378         if (r < 0) {
379                 r = sd_bus_reply_method_errno(q->request, r, NULL);
380                 goto finish;
381         }
382
383         r = q->callback(q->request, q->userdata, &error_buffer);
384         r = bus_maybe_reply_error(q->request, r, &error_buffer);
385
386 finish:
387         async_polkit_query_free(q);
388
389         return r;
390 }
391
392 #endif
393
394 int bus_verify_polkit_async(
395                 sd_bus_message *call,
396                 int capability,
397                 const char *action,
398                 const char **details,
399                 bool interactive,
400                 uid_t good_user,
401                 Hashmap **registry,
402                 sd_bus_error *error) {
403
404 #if ENABLE_POLKIT
405         _cleanup_(sd_bus_message_unrefp) sd_bus_message *pk = NULL;
406         AsyncPolkitQuery *q;
407         const char *sender, **k, **v;
408         sd_bus_message_handler_t callback;
409         void *userdata;
410         int c;
411 #endif
412         int r;
413
414         assert(call);
415         assert(action);
416         assert(registry);
417
418         r = check_good_user(call, good_user);
419         if (r != 0)
420                 return r;
421
422 #if ENABLE_POLKIT
423         q = hashmap_get(*registry, call);
424         if (q) {
425                 int authorized, challenge;
426
427                 /* This is the second invocation of this function, and
428                  * there's already a response from polkit, let's
429                  * process it */
430                 assert(q->reply);
431
432                 if (sd_bus_message_is_method_error(q->reply, NULL)) {
433                         const sd_bus_error *e;
434
435                         /* Copy error from polkit reply */
436                         e = sd_bus_message_get_error(q->reply);
437                         sd_bus_error_copy(error, e);
438
439                         /* Treat no PK available as access denied */
440                         if (sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN))
441                                 return -EACCES;
442
443                         return -sd_bus_error_get_errno(e);
444                 }
445
446                 r = sd_bus_message_enter_container(q->reply, 'r', "bba{ss}");
447                 if (r >= 0)
448                         r = sd_bus_message_read(q->reply, "bb", &authorized, &challenge);
449
450                 if (r < 0)
451                         return r;
452
453                 if (authorized)
454                         return 1;
455
456                 if (challenge)
457                         return sd_bus_error_set(error, SD_BUS_ERROR_INTERACTIVE_AUTHORIZATION_REQUIRED, "Interactive authentication required.");
458
459                 return -EACCES;
460         }
461 #endif
462
463         r = sd_bus_query_sender_privilege(call, capability);
464         if (r < 0)
465                 return r;
466         else if (r > 0)
467                 return 1;
468
469 #if ENABLE_POLKIT
470         if (sd_bus_get_current_message(call->bus) != call)
471                 return -EINVAL;
472
473         callback = sd_bus_get_current_handler(call->bus);
474         if (!callback)
475                 return -EINVAL;
476
477         userdata = sd_bus_get_current_userdata(call->bus);
478
479         sender = sd_bus_message_get_sender(call);
480         if (!sender)
481                 return -EBADMSG;
482
483         c = sd_bus_message_get_allow_interactive_authorization(call);
484         if (c < 0)
485                 return c;
486         if (c > 0)
487                 interactive = true;
488
489         r = hashmap_ensure_allocated(registry, NULL);
490         if (r < 0)
491                 return r;
492
493         r = sd_bus_message_new_method_call(
494                         call->bus,
495                         &pk,
496                         "org.freedesktop.PolicyKit1",
497                         "/org/freedesktop/PolicyKit1/Authority",
498                         "org.freedesktop.PolicyKit1.Authority",
499                         "CheckAuthorization");
500         if (r < 0)
501                 return r;
502
503         r = sd_bus_message_append(
504                         pk,
505                         "(sa{sv})s",
506                         "system-bus-name", 1, "name", "s", sender,
507                         action);
508         if (r < 0)
509                 return r;
510
511         r = sd_bus_message_open_container(pk, 'a', "{ss}");
512         if (r < 0)
513                 return r;
514
515         STRV_FOREACH_PAIR(k, v, details) {
516                 r = sd_bus_message_append(pk, "{ss}", *k, *v);
517                 if (r < 0)
518                         return r;
519         }
520
521         r = sd_bus_message_close_container(pk);
522         if (r < 0)
523                 return r;
524
525         r = sd_bus_message_append(pk, "us", !!interactive, NULL);
526         if (r < 0)
527                 return r;
528
529         q = new0(AsyncPolkitQuery, 1);
530         if (!q)
531                 return -ENOMEM;
532
533         q->request = sd_bus_message_ref(call);
534         q->callback = callback;
535         q->userdata = userdata;
536
537         r = hashmap_put(*registry, call, q);
538         if (r < 0) {
539                 async_polkit_query_free(q);
540                 return r;
541         }
542
543         q->registry = *registry;
544
545         r = sd_bus_call_async(call->bus, &q->slot, pk, async_polkit_callback, q, 0);
546         if (r < 0) {
547                 async_polkit_query_free(q);
548                 return r;
549         }
550
551         return 0;
552 #endif
553
554         return -EACCES;
555 }
556
557 void bus_verify_polkit_async_registry_free(Hashmap *registry) {
558 #if ENABLE_POLKIT
559         hashmap_free_with_destructor(registry, async_polkit_query_free);
560 #endif
561 }
562
563 #if 0 /// UNNEEDED by elogind
564 int bus_check_peercred(sd_bus *c) {
565         struct ucred ucred;
566         int fd, r;
567
568         assert(c);
569
570         fd = sd_bus_get_fd(c);
571         if (fd < 0)
572                 return fd;
573
574         r = getpeercred(fd, &ucred);
575         if (r < 0)
576                 return r;
577
578         if (ucred.uid != 0 && ucred.uid != geteuid())
579                 return -EPERM;
580
581         return 1;
582 }
583
584 int bus_connect_system_systemd(sd_bus **_bus) {
585         _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
586         int r;
587
588         assert(_bus);
589
590         if (geteuid() != 0)
591                 return sd_bus_default_system(_bus);
592
593         /* If we are root then let's talk directly to the system
594          * instance, instead of going via the bus */
595
596         r = sd_bus_new(&bus);
597         if (r < 0)
598                 return r;
599
600         r = sd_bus_set_address(bus, "unix:path=/run/systemd/private");
601         if (r < 0)
602                 return r;
603
604         r = sd_bus_start(bus);
605         if (r < 0)
606                 return sd_bus_default_system(_bus);
607
608         r = bus_check_peercred(bus);
609         if (r < 0)
610                 return r;
611
612         *_bus = bus;
613         bus = NULL;
614
615         return 0;
616 }
617
618 int bus_connect_user_systemd(sd_bus **_bus) {
619         _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
620         _cleanup_free_ char *ee = NULL;
621         const char *e;
622         int r;
623
624         assert(_bus);
625
626         e = secure_getenv("XDG_RUNTIME_DIR");
627         if (!e)
628                 return sd_bus_default_user(_bus);
629
630         ee = bus_address_escape(e);
631         if (!ee)
632                 return -ENOMEM;
633
634         r = sd_bus_new(&bus);
635         if (r < 0)
636                 return r;
637
638         bus->address = strjoin("unix:path=", ee, "/systemd/private");
639         if (!bus->address)
640                 return -ENOMEM;
641
642         r = sd_bus_start(bus);
643         if (r < 0)
644                 return sd_bus_default_user(_bus);
645
646         r = bus_check_peercred(bus);
647         if (r < 0)
648                 return r;
649
650         *_bus = bus;
651         bus = NULL;
652
653         return 0;
654 }
655 #endif // 0
656
657 #define print_property(name, fmt, ...)                                  \
658         do {                                                            \
659                 if (value)                                              \
660                         printf(fmt "\n", __VA_ARGS__);                  \
661                 else                                                    \
662                         printf("%s=" fmt "\n", name, __VA_ARGS__);      \
663         } while (0)
664
665 int bus_print_property(const char *name, sd_bus_message *property, bool value, bool all) {
666         char type;
667         const char *contents;
668         int r;
669
670         assert(name);
671         assert(property);
672
673         r = sd_bus_message_peek_type(property, &type, &contents);
674         if (r < 0)
675                 return r;
676
677         switch (type) {
678
679         case SD_BUS_TYPE_STRING: {
680                 const char *s;
681
682                 r = sd_bus_message_read_basic(property, type, &s);
683                 if (r < 0)
684                         return r;
685
686                 if (all || !isempty(s)) {
687                         bool good;
688
689                         /* This property has a single value, so we need to take
690                          * care not to print a new line, everything else is OK. */
691                         good = !strchr(s, '\n');
692                         print_property(name, "%s", good ? s : "[unprintable]");
693                 }
694
695                 return 1;
696         }
697
698         case SD_BUS_TYPE_BOOLEAN: {
699                 int b;
700
701                 r = sd_bus_message_read_basic(property, type, &b);
702                 if (r < 0)
703                         return r;
704
705                 print_property(name, "%s", yes_no(b));
706
707                 return 1;
708         }
709
710         case SD_BUS_TYPE_UINT64: {
711                 uint64_t u;
712
713                 r = sd_bus_message_read_basic(property, type, &u);
714                 if (r < 0)
715                         return r;
716
717                 /* Yes, heuristics! But we can change this check
718                  * should it turn out to not be sufficient */
719
720                 if (endswith(name, "Timestamp")) {
721                         char timestamp[FORMAT_TIMESTAMP_MAX], *t;
722
723                         t = format_timestamp(timestamp, sizeof(timestamp), u);
724                         if (t || all)
725                                 print_property(name, "%s", strempty(t));
726
727                 } else if (strstr(name, "USec")) {
728                         char timespan[FORMAT_TIMESPAN_MAX];
729
730                         print_property(name, "%s", format_timespan(timespan, sizeof(timespan), u, 0));
731                 } else if (streq(name, "RestrictNamespaces")) {
732                         _cleanup_free_ char *s = NULL;
733                         const char *result;
734
735                         if ((u & NAMESPACE_FLAGS_ALL) == 0)
736                                 result = "yes";
737                         else if ((u & NAMESPACE_FLAGS_ALL) == NAMESPACE_FLAGS_ALL)
738                                 result = "no";
739                         else {
740                                 r = namespace_flag_to_string_many(u, &s);
741                                 if (r < 0)
742                                         return r;
743
744                                 result = s;
745                         }
746
747                         print_property(name, "%s", result);
748
749                 } else if (streq(name, "MountFlags")) {
750                         const char *result;
751
752                         result = mount_propagation_flags_to_string(u);
753                         if (!result)
754                                 return -EINVAL;
755
756                         print_property(name, "%s", result);
757
758                 } else if (STR_IN_SET(name, "CapabilityBoundingSet", "AmbientCapabilities")) {
759                         _cleanup_free_ char *s = NULL;
760
761                         r = capability_set_to_string_alloc(u, &s);
762                         if (r < 0)
763                                 return r;
764
765                         print_property(name, "%s", s);
766
767                 } else if ((STR_IN_SET(name, "CPUWeight", "StartupCPUWeight", "IOWeight", "StartupIOWeight") && u == CGROUP_WEIGHT_INVALID) ||
768                            (STR_IN_SET(name, "CPUShares", "StartupCPUShares") && u == CGROUP_CPU_SHARES_INVALID) ||
769                            (STR_IN_SET(name, "BlockIOWeight", "StartupBlockIOWeight") && u == CGROUP_BLKIO_WEIGHT_INVALID) ||
770                            (STR_IN_SET(name, "MemoryCurrent", "TasksCurrent") && u == (uint64_t) -1) ||
771                            (endswith(name, "NSec") && u == (uint64_t) -1))
772
773                         print_property(name, "%s", "[not set]");
774
775                 else if ((STR_IN_SET(name, "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryLimit") && u == CGROUP_LIMIT_MAX) ||
776                          (STR_IN_SET(name, "TasksMax", "DefaultTasksMax") && u == (uint64_t) -1) ||
777                          (startswith(name, "Limit") && u == (uint64_t) -1) ||
778                          (startswith(name, "DefaultLimit") && u == (uint64_t) -1))
779
780                         print_property(name, "%s", "infinity");
781                 else
782                         print_property(name, "%"PRIu64, u);
783
784                 return 1;
785         }
786
787         case SD_BUS_TYPE_INT64: {
788                 int64_t i;
789
790                 r = sd_bus_message_read_basic(property, type, &i);
791                 if (r < 0)
792                         return r;
793
794                 print_property(name, "%"PRIi64, i);
795
796                 return 1;
797         }
798
799         case SD_BUS_TYPE_UINT32: {
800                 uint32_t u;
801
802                 r = sd_bus_message_read_basic(property, type, &u);
803                 if (r < 0)
804                         return r;
805
806                 if (strstr(name, "UMask") || strstr(name, "Mode"))
807                         print_property(name, "%04o", u);
808                 else if (streq(name, "UID")) {
809                         if (u == UID_INVALID)
810                                 print_property(name, "%s", "[not set]");
811                         else
812                                 print_property(name, "%"PRIu32, u);
813                 } else if (streq(name, "GID")) {
814                         if (u == GID_INVALID)
815                                 print_property(name, "%s", "[not set]");
816                         else
817                                 print_property(name, "%"PRIu32, u);
818                 } else
819                         print_property(name, "%"PRIu32, u);
820
821                 return 1;
822         }
823
824         case SD_BUS_TYPE_INT32: {
825                 int32_t i;
826
827                 r = sd_bus_message_read_basic(property, type, &i);
828                 if (r < 0)
829                         return r;
830
831                 print_property(name, "%"PRIi32, i);
832                 return 1;
833         }
834
835         case SD_BUS_TYPE_DOUBLE: {
836                 double d;
837
838                 r = sd_bus_message_read_basic(property, type, &d);
839                 if (r < 0)
840                         return r;
841
842                 print_property(name, "%g", d);
843                 return 1;
844         }
845
846         case SD_BUS_TYPE_ARRAY:
847                 if (streq(contents, "s")) {
848                         bool first = true;
849                         const char *str;
850
851                         r = sd_bus_message_enter_container(property, SD_BUS_TYPE_ARRAY, contents);
852                         if (r < 0)
853                                 return r;
854
855                         while ((r = sd_bus_message_read_basic(property, SD_BUS_TYPE_STRING, &str)) > 0) {
856                                 bool good;
857
858                                 if (first && !value)
859                                         printf("%s=", name);
860
861                                 /* This property has multiple space-separated values, so
862                                  * neither spaces not newlines can be allowed in a value. */
863                                 good = str[strcspn(str, " \n")] == '\0';
864
865                                 printf("%s%s", first ? "" : " ", good ? str : "[unprintable]");
866
867                                 first = false;
868                         }
869                         if (r < 0)
870                                 return r;
871
872                         if (first && all && !value)
873                                 printf("%s=", name);
874                         if (!first || all)
875                                 puts("");
876
877                         r = sd_bus_message_exit_container(property);
878                         if (r < 0)
879                                 return r;
880
881                         return 1;
882
883                 } else if (streq(contents, "y")) {
884                         const uint8_t *u;
885                         size_t n;
886
887                         r = sd_bus_message_read_array(property, SD_BUS_TYPE_BYTE, (const void**) &u, &n);
888                         if (r < 0)
889                                 return r;
890
891                         if (all || n > 0) {
892                                 unsigned int i;
893
894                                 if (!value)
895                                         printf("%s=", name);
896
897                                 for (i = 0; i < n; i++)
898                                         printf("%02x", u[i]);
899
900                                 puts("");
901                         }
902
903                         return 1;
904
905                 } else if (streq(contents, "u")) {
906                         uint32_t *u;
907                         size_t n;
908
909                         r = sd_bus_message_read_array(property, SD_BUS_TYPE_UINT32, (const void**) &u, &n);
910                         if (r < 0)
911                                 return r;
912
913                         if (all || n > 0) {
914                                 unsigned int i;
915
916                                 if (!value)
917                                         printf("%s=", name);
918
919                                 for (i = 0; i < n; i++)
920                                         printf("%08x", u[i]);
921
922                                 puts("");
923                         }
924
925                         return 1;
926                 }
927
928                 break;
929         }
930
931         return 0;
932 }
933
934 int bus_print_all_properties(sd_bus *bus, const char *dest, const char *path, char **filter, bool value, bool all) {
935         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
936         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
937         int r;
938
939         assert(bus);
940         assert(path);
941
942         r = sd_bus_call_method(bus,
943                         dest,
944                         path,
945                         "org.freedesktop.DBus.Properties",
946                         "GetAll",
947                         &error,
948                         &reply,
949                         "s", "");
950         if (r < 0)
951                 return r;
952
953         r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "{sv}");
954         if (r < 0)
955                 return r;
956
957         while ((r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) {
958                 const char *name;
959                 const char *contents;
960
961                 r = sd_bus_message_read_basic(reply, SD_BUS_TYPE_STRING, &name);
962                 if (r < 0)
963                         return r;
964
965                 if (!filter || strv_find(filter, name)) {
966                         r = sd_bus_message_peek_type(reply, NULL, &contents);
967                         if (r < 0)
968                                 return r;
969
970                         r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_VARIANT, contents);
971                         if (r < 0)
972                                 return r;
973
974                         r = bus_print_property(name, reply, value, all);
975                         if (r < 0)
976                                 return r;
977                         if (r == 0) {
978                                 if (all)
979                                         printf("%s=[unprintable]\n", name);
980                                 /* skip what we didn't read */
981                                 r = sd_bus_message_skip(reply, contents);
982                                 if (r < 0)
983                                         return r;
984                         }
985
986                         r = sd_bus_message_exit_container(reply);
987                         if (r < 0)
988                                 return r;
989                 } else {
990                         r = sd_bus_message_skip(reply, "v");
991                         if (r < 0)
992                                 return r;
993                 }
994
995                 r = sd_bus_message_exit_container(reply);
996                 if (r < 0)
997                         return r;
998         }
999         if (r < 0)
1000                 return r;
1001
1002         r = sd_bus_message_exit_container(reply);
1003         if (r < 0)
1004                 return r;
1005
1006         return 0;
1007 }
1008
1009 int bus_map_id128(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
1010         sd_id128_t *p = userdata;
1011         const void *v;
1012         size_t n;
1013         int r;
1014
1015         r = sd_bus_message_read_array(m, SD_BUS_TYPE_BYTE, &v, &n);
1016         if (r < 0)
1017                 return r;
1018
1019         if (n == 0)
1020                 *p = SD_ID128_NULL;
1021         else if (n == 16)
1022                 memcpy((*p).bytes, v, n);
1023         else
1024                 return -EINVAL;
1025
1026         return 0;
1027 }
1028
1029 static int map_basic(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
1030         char type;
1031         int r;
1032
1033         r = sd_bus_message_peek_type(m, &type, NULL);
1034         if (r < 0)
1035                 return r;
1036
1037         switch (type) {
1038
1039         case SD_BUS_TYPE_STRING: {
1040                 char **p = userdata;
1041                 const char *s;
1042
1043                 r = sd_bus_message_read_basic(m, type, &s);
1044                 if (r < 0)
1045                         return r;
1046
1047                 if (isempty(s))
1048                         s = NULL;
1049
1050                 return free_and_strdup(p, s);
1051         }
1052
1053         case SD_BUS_TYPE_ARRAY: {
1054                 _cleanup_strv_free_ char **l = NULL;
1055                 char ***p = userdata;
1056
1057                 r = bus_message_read_strv_extend(m, &l);
1058                 if (r < 0)
1059                         return r;
1060
1061                 strv_free(*p);
1062                 *p = l;
1063                 l = NULL;
1064                 return 0;
1065         }
1066
1067         case SD_BUS_TYPE_BOOLEAN: {
1068                 unsigned b;
1069                 int *p = userdata;
1070
1071                 r = sd_bus_message_read_basic(m, type, &b);
1072                 if (r < 0)
1073                         return r;
1074
1075                 *p = b;
1076                 return 0;
1077         }
1078
1079         case SD_BUS_TYPE_INT32:
1080         case SD_BUS_TYPE_UINT32: {
1081                 uint32_t u, *p = userdata;
1082
1083                 r = sd_bus_message_read_basic(m, type, &u);
1084                 if (r < 0)
1085                         return r;
1086
1087                 *p = u;
1088                 return 0;
1089         }
1090
1091         case SD_BUS_TYPE_INT64:
1092         case SD_BUS_TYPE_UINT64: {
1093                 uint64_t t, *p = userdata;
1094
1095                 r = sd_bus_message_read_basic(m, type, &t);
1096                 if (r < 0)
1097                         return r;
1098
1099                 *p = t;
1100                 return 0;
1101         }
1102
1103         case SD_BUS_TYPE_DOUBLE: {
1104                 double d, *p = userdata;
1105
1106                 r = sd_bus_message_read_basic(m, type, &d);
1107                 if (r < 0)
1108                         return r;
1109
1110                 *p = d;
1111                 return 0;
1112         }}
1113
1114         return -EOPNOTSUPP;
1115 }
1116
1117 int bus_message_map_all_properties(
1118                 sd_bus_message *m,
1119                 const struct bus_properties_map *map,
1120                 sd_bus_error *error,
1121                 void *userdata) {
1122
1123         int r;
1124
1125         assert(m);
1126         assert(map);
1127
1128         r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{sv}");
1129         if (r < 0)
1130                 return r;
1131
1132         while ((r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) {
1133                 const struct bus_properties_map *prop;
1134                 const char *member;
1135                 const char *contents;
1136                 void *v;
1137                 unsigned i;
1138
1139                 r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &member);
1140                 if (r < 0)
1141                         return r;
1142
1143                 for (i = 0, prop = NULL; map[i].member; i++)
1144                         if (streq(map[i].member, member)) {
1145                                 prop = &map[i];
1146                                 break;
1147                         }
1148
1149                 if (prop) {
1150                         r = sd_bus_message_peek_type(m, NULL, &contents);
1151                         if (r < 0)
1152                                 return r;
1153
1154                         r = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, contents);
1155                         if (r < 0)
1156                                 return r;
1157
1158                         v = (uint8_t *)userdata + prop->offset;
1159                         if (map[i].set)
1160                                 r = prop->set(sd_bus_message_get_bus(m), member, m, error, v);
1161                         else
1162                                 r = map_basic(sd_bus_message_get_bus(m), member, m, error, v);
1163                         if (r < 0)
1164                                 return r;
1165
1166                         r = sd_bus_message_exit_container(m);
1167                         if (r < 0)
1168                                 return r;
1169                 } else {
1170                         r = sd_bus_message_skip(m, "v");
1171                         if (r < 0)
1172                                 return r;
1173                 }
1174
1175                 r = sd_bus_message_exit_container(m);
1176                 if (r < 0)
1177                         return r;
1178         }
1179         if (r < 0)
1180                 return r;
1181
1182         return sd_bus_message_exit_container(m);
1183 }
1184
1185 #if 0 /// UNNEEDED by elogind
1186 int bus_message_map_properties_changed(
1187                 sd_bus_message *m,
1188                 const struct bus_properties_map *map,
1189                 sd_bus_error *error,
1190                 void *userdata) {
1191
1192         const char *member;
1193         int r, invalidated, i;
1194
1195         assert(m);
1196         assert(map);
1197
1198         r = bus_message_map_all_properties(m, map, error, userdata);
1199         if (r < 0)
1200                 return r;
1201
1202         r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "s");
1203         if (r < 0)
1204                 return r;
1205
1206         invalidated = 0;
1207         while ((r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &member)) > 0)
1208                 for (i = 0; map[i].member; i++)
1209                         if (streq(map[i].member, member)) {
1210                                 ++invalidated;
1211                                 break;
1212                         }
1213         if (r < 0)
1214                 return r;
1215
1216         r = sd_bus_message_exit_container(m);
1217         if (r < 0)
1218                 return r;
1219
1220         return invalidated;
1221 }
1222 #endif // 0
1223
1224 int bus_map_all_properties(
1225                 sd_bus *bus,
1226                 const char *destination,
1227                 const char *path,
1228                 const struct bus_properties_map *map,
1229                 sd_bus_error *error,
1230                 void *userdata) {
1231
1232         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
1233         int r;
1234
1235         assert(bus);
1236         assert(destination);
1237         assert(path);
1238         assert(map);
1239
1240         r = sd_bus_call_method(
1241                         bus,
1242                         destination,
1243                         path,
1244                         "org.freedesktop.DBus.Properties",
1245                         "GetAll",
1246                         error,
1247                         &m,
1248                         "s", "");
1249         if (r < 0)
1250                 return r;
1251
1252         return bus_message_map_all_properties(m, map, error, userdata);
1253 }
1254
1255 int bus_connect_transport(BusTransport transport, const char *host, bool user, sd_bus **ret) {
1256         _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
1257         int r;
1258
1259         assert(transport >= 0);
1260         assert(transport < _BUS_TRANSPORT_MAX);
1261         assert(ret);
1262
1263         assert_return((transport == BUS_TRANSPORT_LOCAL) == !host, -EINVAL);
1264         assert_return(transport == BUS_TRANSPORT_LOCAL || !user, -EOPNOTSUPP);
1265
1266         switch (transport) {
1267
1268         case BUS_TRANSPORT_LOCAL:
1269 #if 0 /// elogind does not support a user bus
1270                 if (user)
1271                         r = sd_bus_default_user(&bus);
1272                 else
1273 #endif // 0
1274                         r = sd_bus_default_system(&bus);
1275
1276                 break;
1277
1278         case BUS_TRANSPORT_REMOTE:
1279                 r = sd_bus_open_system_remote(&bus, host);
1280                 break;
1281
1282         case BUS_TRANSPORT_MACHINE:
1283                 r = sd_bus_open_system_machine(&bus, host);
1284                 break;
1285
1286         default:
1287                 assert_not_reached("Hmm, unknown transport type.");
1288         }
1289         if (r < 0)
1290                 return r;
1291
1292         r = sd_bus_set_exit_on_disconnect(bus, true);
1293         if (r < 0)
1294                 return r;
1295
1296         *ret = bus;
1297         bus = NULL;
1298
1299         return 0;
1300 }
1301
1302 #if 0 /// UNNEEDED by elogind
1303 int bus_connect_transport_systemd(BusTransport transport, const char *host, bool user, sd_bus **bus) {
1304         int r;
1305
1306         assert(transport >= 0);
1307         assert(transport < _BUS_TRANSPORT_MAX);
1308         assert(bus);
1309
1310         assert_return((transport == BUS_TRANSPORT_LOCAL) == !host, -EINVAL);
1311         assert_return(transport == BUS_TRANSPORT_LOCAL || !user, -EOPNOTSUPP);
1312
1313         switch (transport) {
1314
1315         case BUS_TRANSPORT_LOCAL:
1316                 if (user)
1317                         r = bus_connect_user_systemd(bus);
1318                 else
1319                         r = bus_connect_system_systemd(bus);
1320
1321                 break;
1322
1323         case BUS_TRANSPORT_REMOTE:
1324                 r = sd_bus_open_system_remote(bus, host);
1325                 break;
1326
1327         case BUS_TRANSPORT_MACHINE:
1328                 r = sd_bus_open_system_machine(bus, host);
1329                 break;
1330
1331         default:
1332                 assert_not_reached("Hmm, unknown transport type.");
1333         }
1334
1335         return r;
1336 }
1337 #endif // 0
1338
1339 int bus_property_get_bool(
1340                 sd_bus *bus,
1341                 const char *path,
1342                 const char *interface,
1343                 const char *property,
1344                 sd_bus_message *reply,
1345                 void *userdata,
1346                 sd_bus_error *error) {
1347
1348         int b = *(bool*) userdata;
1349
1350         return sd_bus_message_append_basic(reply, 'b', &b);
1351 }
1352
1353 #if 0 /// UNNEEDED by elogind
1354 int bus_property_get_id128(
1355                 sd_bus *bus,
1356                 const char *path,
1357                 const char *interface,
1358                 const char *property,
1359                 sd_bus_message *reply,
1360                 void *userdata,
1361                 sd_bus_error *error) {
1362
1363         sd_id128_t *id = userdata;
1364
1365         if (sd_id128_is_null(*id)) /* Add an empty array if the ID is zero */
1366                 return sd_bus_message_append(reply, "ay", 0);
1367         else
1368                 return sd_bus_message_append_array(reply, 'y', id->bytes, 16);
1369 }
1370 #endif // 0
1371
1372 #if __SIZEOF_SIZE_T__ != 8
1373 int bus_property_get_size(
1374                 sd_bus *bus,
1375                 const char *path,
1376                 const char *interface,
1377                 const char *property,
1378                 sd_bus_message *reply,
1379                 void *userdata,
1380                 sd_bus_error *error) {
1381
1382         uint64_t sz = *(size_t*) userdata;
1383
1384         return sd_bus_message_append_basic(reply, 't', &sz);
1385 }
1386 #endif
1387
1388 #if __SIZEOF_LONG__ != 8
1389 int bus_property_get_long(
1390                 sd_bus *bus,
1391                 const char *path,
1392                 const char *interface,
1393                 const char *property,
1394                 sd_bus_message *reply,
1395                 void *userdata,
1396                 sd_bus_error *error) {
1397
1398         int64_t l = *(long*) userdata;
1399
1400         return sd_bus_message_append_basic(reply, 'x', &l);
1401 }
1402
1403 int bus_property_get_ulong(
1404                 sd_bus *bus,
1405                 const char *path,
1406                 const char *interface,
1407                 const char *property,
1408                 sd_bus_message *reply,
1409                 void *userdata,
1410                 sd_bus_error *error) {
1411
1412         uint64_t ul = *(unsigned long*) userdata;
1413
1414         return sd_bus_message_append_basic(reply, 't', &ul);
1415 }
1416 #endif
1417
1418 int bus_log_parse_error(int r) {
1419         return log_error_errno(r, "Failed to parse bus message: %m");
1420 }
1421
1422 #if 0 /// UNNEEDED by elogind
1423 int bus_log_create_error(int r) {
1424         return log_error_errno(r, "Failed to create bus message: %m");
1425 }
1426
1427 #endif // 0
1428 #if 0 /// UNNEEDED by elogind
1429 /**
1430  * bus_path_encode_unique() - encode unique object path
1431  * @b: bus connection or NULL
1432  * @prefix: object path prefix
1433  * @sender_id: unique-name of client, or NULL
1434  * @external_id: external ID to be chosen by client, or NULL
1435  * @ret_path: storage for encoded object path pointer
1436  *
1437  * Whenever we provide a bus API that allows clients to create and manage
1438  * server-side objects, we need to provide a unique name for these objects. If
1439  * we let the server choose the name, we suffer from a race condition: If a
1440  * client creates an object asynchronously, it cannot destroy that object until
1441  * it received the method reply. It cannot know the name of the new object,
1442  * thus, it cannot destroy it. Furthermore, it enforces a round-trip.
1443  *
1444  * Therefore, many APIs allow the client to choose the unique name for newly
1445  * created objects. There're two problems to solve, though:
1446  *    1) Object names are usually defined via dbus object paths, which are
1447  *       usually globally namespaced. Therefore, multiple clients must be able
1448  *       to choose unique object names without interference.
1449  *    2) If multiple libraries share the same bus connection, they must be
1450  *       able to choose unique object names without interference.
1451  * The first problem is solved easily by prefixing a name with the
1452  * unique-bus-name of a connection. The server side must enforce this and
1453  * reject any other name. The second problem is solved by providing unique
1454  * suffixes from within sd-bus.
1455  *
1456  * This helper allows clients to create unique object-paths. It uses the
1457  * template '/prefix/sender_id/external_id' and returns the new path in
1458  * @ret_path (must be freed by the caller).
1459  * If @sender_id is NULL, the unique-name of @b is used. If @external_id is
1460  * NULL, this function allocates a unique suffix via @b (by requesting a new
1461  * cookie). If both @sender_id and @external_id are given, @b can be passed as
1462  * NULL.
1463  *
1464  * Returns: 0 on success, negative error code on failure.
1465  */
1466 int bus_path_encode_unique(sd_bus *b, const char *prefix, const char *sender_id, const char *external_id, char **ret_path) {
1467         _cleanup_free_ char *sender_label = NULL, *external_label = NULL;
1468         char external_buf[DECIMAL_STR_MAX(uint64_t)], *p;
1469         int r;
1470
1471         assert_return(b || (sender_id && external_id), -EINVAL);
1472         assert_return(object_path_is_valid(prefix), -EINVAL);
1473         assert_return(ret_path, -EINVAL);
1474
1475         if (!sender_id) {
1476                 r = sd_bus_get_unique_name(b, &sender_id);
1477                 if (r < 0)
1478                         return r;
1479         }
1480
1481         if (!external_id) {
1482                 xsprintf(external_buf, "%"PRIu64, ++b->cookie);
1483                 external_id = external_buf;
1484         }
1485
1486         sender_label = bus_label_escape(sender_id);
1487         if (!sender_label)
1488                 return -ENOMEM;
1489
1490         external_label = bus_label_escape(external_id);
1491         if (!external_label)
1492                 return -ENOMEM;
1493
1494         p = strjoin(prefix, "/", sender_label, "/", external_label);
1495         if (!p)
1496                 return -ENOMEM;
1497
1498         *ret_path = p;
1499         return 0;
1500 }
1501
1502 /**
1503  * bus_path_decode_unique() - decode unique object path
1504  * @path: object path to decode
1505  * @prefix: object path prefix
1506  * @ret_sender: output parameter for sender-id label
1507  * @ret_external: output parameter for external-id label
1508  *
1509  * This does the reverse of bus_path_encode_unique() (see its description for
1510  * details). Both trailing labels, sender-id and external-id, are unescaped and
1511  * returned in the given output parameters (the caller must free them).
1512  *
1513  * Note that this function returns 0 if the path does not match the template
1514  * (see bus_path_encode_unique()), 1 if it matched.
1515  *
1516  * Returns: Negative error code on failure, 0 if the given object path does not
1517  *          match the template (return parameters are set to NULL), 1 if it was
1518  *          parsed successfully (return parameters contain allocated labels).
1519  */
1520 int bus_path_decode_unique(const char *path, const char *prefix, char **ret_sender, char **ret_external) {
1521         const char *p, *q;
1522         char *sender, *external;
1523
1524         assert(object_path_is_valid(path));
1525         assert(object_path_is_valid(prefix));
1526         assert(ret_sender);
1527         assert(ret_external);
1528
1529         p = object_path_startswith(path, prefix);
1530         if (!p) {
1531                 *ret_sender = NULL;
1532                 *ret_external = NULL;
1533                 return 0;
1534         }
1535
1536         q = strchr(p, '/');
1537         if (!q) {
1538                 *ret_sender = NULL;
1539                 *ret_external = NULL;
1540                 return 0;
1541         }
1542
1543         sender = bus_label_unescape_n(p, q - p);
1544         external = bus_label_unescape(q + 1);
1545         if (!sender || !external) {
1546                 free(sender);
1547                 free(external);
1548                 return -ENOMEM;
1549         }
1550
1551         *ret_sender = sender;
1552         *ret_external = external;
1553         return 1;
1554 }
1555 #endif // 0
1556
1557 #if 0 /// UNNEEDED by elogind
1558 int bus_property_get_rlimit(
1559                 sd_bus *bus,
1560                 const char *path,
1561                 const char *interface,
1562                 const char *property,
1563                 sd_bus_message *reply,
1564                 void *userdata,
1565                 sd_bus_error *error) {
1566
1567         struct rlimit *rl;
1568         uint64_t u;
1569         rlim_t x;
1570         const char *is_soft;
1571
1572         assert(bus);
1573         assert(reply);
1574         assert(userdata);
1575
1576         is_soft = endswith(property, "Soft");
1577         rl = *(struct rlimit**) userdata;
1578         if (rl)
1579                 x = is_soft ? rl->rlim_cur : rl->rlim_max;
1580         else {
1581                 struct rlimit buf = {};
1582                 int z;
1583                 const char *s;
1584
1585                 s = is_soft ? strndupa(property, is_soft - property) : property;
1586
1587                 z = rlimit_from_string(strstr(s, "Limit"));
1588                 assert(z >= 0);
1589
1590                 getrlimit(z, &buf);
1591                 x = is_soft ? buf.rlim_cur : buf.rlim_max;
1592         }
1593
1594         /* rlim_t might have different sizes, let's map
1595          * RLIMIT_INFINITY to (uint64_t) -1, so that it is the same on
1596          * all archs */
1597         u = x == RLIM_INFINITY ? (uint64_t) -1 : (uint64_t) x;
1598
1599         return sd_bus_message_append(reply, "t", u);
1600 }
1601
1602 int bus_track_add_name_many(sd_bus_track *t, char **l) {
1603         int r = 0;
1604         char **i;
1605
1606         assert(t);
1607
1608         /* Continues adding after failure, and returns the first failure. */
1609
1610         STRV_FOREACH(i, l) {
1611                 int k;
1612
1613                 k = sd_bus_track_add_name(t, *i);
1614                 if (k < 0 && r >= 0)
1615                         r = k;
1616         }
1617
1618         return r;
1619 }
1620 #endif // 0