chiark / gitweb /
a6b15f28a04a1b7034378a8cc6406f8780c4a47d
[elogind.git] / src / login / logind-session-dbus.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2011 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <errno.h>
23 #include <string.h>
24
25 #include "util.h"
26 #include "strv.h"
27 #include "bus-util.h"
28 #include "bus-common-errors.h"
29 #include "bus-label.h"
30
31 #include "logind.h"
32 #include "logind-session.h"
33 #include "logind-session-device.h"
34
35 static int property_get_user(
36                 sd_bus *bus,
37                 const char *path,
38                 const char *interface,
39                 const char *property,
40                 sd_bus_message *reply,
41                 void *userdata,
42                 sd_bus_error *error) {
43
44         _cleanup_free_ char *p = NULL;
45         Session *s = userdata;
46
47         assert(bus);
48         assert(reply);
49         assert(s);
50
51         p = user_bus_path(s->user);
52         if (!p)
53                 return -ENOMEM;
54
55         return sd_bus_message_append(reply, "(uo)", (uint32_t) s->user->uid, p);
56 }
57
58 static int property_get_name(
59                 sd_bus *bus,
60                 const char *path,
61                 const char *interface,
62                 const char *property,
63                 sd_bus_message *reply,
64                 void *userdata,
65                 sd_bus_error *error) {
66
67         Session *s = userdata;
68
69         assert(bus);
70         assert(reply);
71         assert(s);
72
73         return sd_bus_message_append(reply, "s", s->user->name);
74 }
75
76 static int property_get_seat(
77                 sd_bus *bus,
78                 const char *path,
79                 const char *interface,
80                 const char *property,
81                 sd_bus_message *reply,
82                 void *userdata,
83                 sd_bus_error *error) {
84
85         _cleanup_free_ char *p = NULL;
86         Session *s = userdata;
87
88         assert(bus);
89         assert(reply);
90         assert(s);
91
92         p = s->seat ? seat_bus_path(s->seat) : strdup("/");
93         if (!p)
94                 return -ENOMEM;
95
96         return sd_bus_message_append(reply, "(so)", s->seat ? s->seat->id : "", p);
97 }
98
99 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, session_type, SessionType);
100 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_class, session_class, SessionClass);
101
102 static int property_get_active(
103                 sd_bus *bus,
104                 const char *path,
105                 const char *interface,
106                 const char *property,
107                 sd_bus_message *reply,
108                 void *userdata,
109                 sd_bus_error *error) {
110
111         Session *s = userdata;
112
113         assert(bus);
114         assert(reply);
115         assert(s);
116
117         return sd_bus_message_append(reply, "b", session_is_active(s));
118 }
119
120 static int property_get_state(
121                 sd_bus *bus,
122                 const char *path,
123                 const char *interface,
124                 const char *property,
125                 sd_bus_message *reply,
126                 void *userdata,
127                 sd_bus_error *error) {
128
129         Session *s = userdata;
130
131         assert(bus);
132         assert(reply);
133         assert(s);
134
135         return sd_bus_message_append(reply, "s", session_state_to_string(session_get_state(s)));
136 }
137
138 static int property_get_idle_hint(
139                 sd_bus *bus,
140                 const char *path,
141                 const char *interface,
142                 const char *property,
143                 sd_bus_message *reply,
144                 void *userdata,
145                 sd_bus_error *error) {
146
147         Session *s = userdata;
148
149         assert(bus);
150         assert(reply);
151         assert(s);
152
153         return sd_bus_message_append(reply, "b", session_get_idle_hint(s, NULL) > 0);
154 }
155
156 static int property_get_idle_since_hint(
157                 sd_bus *bus,
158                 const char *path,
159                 const char *interface,
160                 const char *property,
161                 sd_bus_message *reply,
162                 void *userdata,
163                 sd_bus_error *error) {
164
165         Session *s = userdata;
166         dual_timestamp t;
167         uint64_t u;
168         int r;
169
170         assert(bus);
171         assert(reply);
172         assert(s);
173
174         r = session_get_idle_hint(s, &t);
175         if (r < 0)
176                 return r;
177
178         u = streq(property, "IdleSinceHint") ? t.realtime : t.monotonic;
179
180         return sd_bus_message_append(reply, "t", u);
181 }
182
183 static int method_terminate(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
184         Session *s = userdata;
185         int r;
186
187         assert(bus);
188         assert(message);
189         assert(s);
190
191         r = session_stop(s, true);
192         if (r < 0)
193                 return r;
194
195         return sd_bus_reply_method_return(message, NULL);
196 }
197
198 static int method_activate(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
199         Session *s = userdata;
200         int r;
201
202         assert(bus);
203         assert(message);
204         assert(s);
205
206         r = session_activate(s);
207         if (r < 0)
208                 return r;
209
210         return sd_bus_reply_method_return(message, NULL);
211 }
212
213 static int method_lock(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
214         Session *s = userdata;
215         int r;
216
217         assert(bus);
218         assert(message);
219         assert(s);
220
221         r = session_send_lock(s, streq(sd_bus_message_get_member(message), "Lock"));
222         if (r < 0)
223                 return r;
224
225         return sd_bus_reply_method_return(message, NULL);
226 }
227
228 static int method_set_idle_hint(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
229         _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
230         Session *s = userdata;
231         uid_t uid;
232         int r, b;
233
234         assert(bus);
235         assert(message);
236         assert(s);
237
238         r = sd_bus_message_read(message, "b", &b);
239         if (r < 0)
240                 return r;
241
242         r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_UID, &creds);
243         if (r < 0)
244                 return r;
245
246         r = sd_bus_creds_get_uid(creds, &uid);
247         if (r < 0)
248                 return r;
249
250         if (uid != 0 && uid != s->user->uid)
251                 return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Only owner of session may set idle hint");
252
253         session_set_idle_hint(s, b);
254
255         return sd_bus_reply_method_return(message, NULL);
256 }
257
258 static int method_kill(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
259         Session *s = userdata;
260         const char *swho;
261         int32_t signo;
262         KillWho who;
263         int r;
264
265         assert(bus);
266         assert(message);
267         assert(s);
268
269         r = sd_bus_message_read(message, "si", &swho, &signo);
270         if (r < 0)
271                 return r;
272
273         if (isempty(swho))
274                 who = KILL_ALL;
275         else {
276                 who = kill_who_from_string(swho);
277                 if (who < 0)
278                         return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid kill parameter '%s'", swho);
279         }
280
281         if (signo <= 0 || signo >= _NSIG)
282                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal %i", signo);
283
284         r = session_kill(s, who, signo);
285         if (r < 0)
286                 return r;
287
288         return sd_bus_reply_method_return(message, NULL);
289 }
290
291 static int method_take_control(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
292         _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
293         Session *s = userdata;
294         int r, force;
295         uid_t uid;
296
297         assert(bus);
298         assert(message);
299         assert(s);
300
301         r = sd_bus_message_read(message, "b", &force);
302         if (r < 0)
303                 return r;
304
305         r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_UID, &creds);
306         if (r < 0)
307                 return r;
308
309         r = sd_bus_creds_get_uid(creds, &uid);
310         if (r < 0)
311                 return r;
312
313         if (uid != 0 && (force || uid != s->user->uid))
314                 return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Only owner of session may take control");
315
316         r = session_set_controller(s, sd_bus_message_get_sender(message), force);
317         if (r < 0)
318                 return r;
319
320         return sd_bus_reply_method_return(message, NULL);
321 }
322
323 static int method_release_control(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
324         Session *s = userdata;
325
326         assert(bus);
327         assert(message);
328         assert(s);
329
330         if (!session_is_controller(s, sd_bus_message_get_sender(message)))
331                 return sd_bus_error_setf(error, BUS_ERROR_NOT_IN_CONTROL, "You are not in control of this session");
332
333         session_drop_controller(s);
334
335         return sd_bus_reply_method_return(message, NULL);
336 }
337
338 static int method_take_device(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
339         Session *s = userdata;
340         uint32_t major, minor;
341         SessionDevice *sd;
342         dev_t dev;
343         int r;
344
345         assert(bus);
346         assert(message);
347         assert(s);
348
349         r = sd_bus_message_read(message, "uu", &major, &minor);
350         if (r < 0)
351                 return r;
352
353         if (!session_is_controller(s, sd_bus_message_get_sender(message)))
354                 return sd_bus_error_setf(error, BUS_ERROR_NOT_IN_CONTROL, "You are not in control of this session");
355
356         dev = makedev(major, minor);
357         sd = hashmap_get(s->devices, &dev);
358         if (sd)
359                 /* We don't allow retrieving a device multiple times.
360                  * The related ReleaseDevice call is not ref-counted.
361                  * The caller should use dup() if it requires more
362                  * than one fd (it would be functionally
363                  * equivalent). */
364                 return sd_bus_error_setf(error, BUS_ERROR_DEVICE_IS_TAKEN, "Device already taken");
365
366         r = session_device_new(s, dev, &sd);
367         if (r < 0)
368                 return r;
369
370         r = sd_bus_reply_method_return(message, "hb", sd->fd, !sd->active);
371         if (r < 0)
372                 session_device_free(sd);
373
374         return r;
375 }
376
377 static int method_release_device(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
378         Session *s = userdata;
379         uint32_t major, minor;
380         SessionDevice *sd;
381         dev_t dev;
382         int r;
383
384         assert(bus);
385         assert(message);
386         assert(s);
387
388         r = sd_bus_message_read(message, "uu", &major, &minor);
389         if (r < 0)
390                 return r;
391
392         if (!session_is_controller(s, sd_bus_message_get_sender(message)))
393                 return sd_bus_error_setf(error, BUS_ERROR_NOT_IN_CONTROL, "You are not in control of this session");
394
395         dev = makedev(major, minor);
396         sd = hashmap_get(s->devices, &dev);
397         if (!sd)
398                 return sd_bus_error_setf(error, BUS_ERROR_DEVICE_NOT_TAKEN, "Device not taken");
399
400         session_device_free(sd);
401         return sd_bus_reply_method_return(message, NULL);
402 }
403
404 static int method_pause_device_complete(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
405         Session *s = userdata;
406         uint32_t major, minor;
407         SessionDevice *sd;
408         dev_t dev;
409         int r;
410
411         assert(bus);
412         assert(message);
413         assert(s);
414
415         r = sd_bus_message_read(message, "uu", &major, &minor);
416         if (r < 0)
417                 return r;
418
419         if (!session_is_controller(s, sd_bus_message_get_sender(message)))
420                 return sd_bus_error_setf(error, BUS_ERROR_NOT_IN_CONTROL, "You are not in control of this session");
421
422         dev = makedev(major, minor);
423         sd = hashmap_get(s->devices, &dev);
424         if (!sd)
425                 return sd_bus_error_setf(error, BUS_ERROR_DEVICE_NOT_TAKEN, "Device not taken");
426
427         session_device_complete_pause(sd);
428
429         return sd_bus_reply_method_return(message, NULL);
430 }
431
432 const sd_bus_vtable session_vtable[] = {
433         SD_BUS_VTABLE_START(0),
434
435         SD_BUS_PROPERTY("Id", "s", NULL, offsetof(Session, id), SD_BUS_VTABLE_PROPERTY_CONST),
436         SD_BUS_PROPERTY("User", "(uo)", property_get_user, 0, SD_BUS_VTABLE_PROPERTY_CONST),
437         SD_BUS_PROPERTY("Name", "s", property_get_name, 0, SD_BUS_VTABLE_PROPERTY_CONST),
438         BUS_PROPERTY_DUAL_TIMESTAMP("Timestamp", offsetof(Session, timestamp), SD_BUS_VTABLE_PROPERTY_CONST),
439         SD_BUS_PROPERTY("VTNr", "u", NULL, offsetof(Session, vtnr), SD_BUS_VTABLE_PROPERTY_CONST),
440         SD_BUS_PROPERTY("Seat", "(so)", property_get_seat, 0, SD_BUS_VTABLE_PROPERTY_CONST),
441         SD_BUS_PROPERTY("TTY", "s", NULL, offsetof(Session, tty), SD_BUS_VTABLE_PROPERTY_CONST),
442         SD_BUS_PROPERTY("Display", "s", NULL, offsetof(Session, display), SD_BUS_VTABLE_PROPERTY_CONST),
443         SD_BUS_PROPERTY("Remote", "b", bus_property_get_bool, offsetof(Session, remote), SD_BUS_VTABLE_PROPERTY_CONST),
444         SD_BUS_PROPERTY("RemoteHost", "s", NULL, offsetof(Session, remote_host), SD_BUS_VTABLE_PROPERTY_CONST),
445         SD_BUS_PROPERTY("RemoteUser", "s", NULL, offsetof(Session, remote_user), SD_BUS_VTABLE_PROPERTY_CONST),
446         SD_BUS_PROPERTY("Service", "s", NULL, offsetof(Session, service), SD_BUS_VTABLE_PROPERTY_CONST),
447         SD_BUS_PROPERTY("Desktop", "s", NULL, offsetof(Session, desktop), SD_BUS_VTABLE_PROPERTY_CONST),
448         SD_BUS_PROPERTY("Scope", "s", NULL, offsetof(Session, scope), SD_BUS_VTABLE_PROPERTY_CONST),
449         SD_BUS_PROPERTY("Leader", "u", bus_property_get_pid, offsetof(Session, leader), SD_BUS_VTABLE_PROPERTY_CONST),
450         SD_BUS_PROPERTY("Audit", "u", NULL, offsetof(Session, audit_id), SD_BUS_VTABLE_PROPERTY_CONST),
451         SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Session, type), SD_BUS_VTABLE_PROPERTY_CONST),
452         SD_BUS_PROPERTY("Class", "s", property_get_class, offsetof(Session, class), SD_BUS_VTABLE_PROPERTY_CONST),
453         SD_BUS_PROPERTY("Active", "b", property_get_active, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
454         SD_BUS_PROPERTY("State", "s", property_get_state, 0, 0),
455         SD_BUS_PROPERTY("IdleHint", "b", property_get_idle_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
456         SD_BUS_PROPERTY("IdleSinceHint", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
457         SD_BUS_PROPERTY("IdleSinceHintMonotonic", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
458
459         SD_BUS_METHOD("Terminate", NULL, NULL, method_terminate, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
460         SD_BUS_METHOD("Activate", NULL, NULL, method_activate, SD_BUS_VTABLE_UNPRIVILEGED),
461         SD_BUS_METHOD("Lock", NULL, NULL, method_lock, 0),
462         SD_BUS_METHOD("Unlock", NULL, NULL, method_lock, 0),
463         SD_BUS_METHOD("SetIdleHint", "b", NULL, method_set_idle_hint, SD_BUS_VTABLE_UNPRIVILEGED),
464         SD_BUS_METHOD("Kill", "si", NULL, method_kill, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
465         SD_BUS_METHOD("TakeControl", "b", NULL, method_take_control, SD_BUS_VTABLE_UNPRIVILEGED),
466         SD_BUS_METHOD("ReleaseControl", NULL, NULL, method_release_control, SD_BUS_VTABLE_UNPRIVILEGED),
467         SD_BUS_METHOD("TakeDevice", "uu", "hb", method_take_device, SD_BUS_VTABLE_UNPRIVILEGED),
468         SD_BUS_METHOD("ReleaseDevice", "uu", NULL, method_release_device, SD_BUS_VTABLE_UNPRIVILEGED),
469         SD_BUS_METHOD("PauseDeviceComplete", "uu", NULL, method_pause_device_complete, SD_BUS_VTABLE_UNPRIVILEGED),
470
471         SD_BUS_SIGNAL("PauseDevice", "uus", 0),
472         SD_BUS_SIGNAL("ResumeDevice", "uuh", 0),
473         SD_BUS_SIGNAL("Lock", NULL, 0),
474         SD_BUS_SIGNAL("Unlock", NULL, 0),
475
476         SD_BUS_VTABLE_END
477 };
478
479 int session_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
480         Manager *m = userdata;
481         Session *session;
482         int r;
483
484         assert(bus);
485         assert(path);
486         assert(interface);
487         assert(found);
488         assert(m);
489
490         if (streq(path, "/org/freedesktop/login1/session/self")) {
491                 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
492                 sd_bus_message *message;
493                 pid_t pid;
494
495                 message = sd_bus_get_current_message(bus);
496                 if (!message)
497                         return 0;
498
499                 r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds);
500                 if (r < 0)
501                         return r;
502
503                 r = sd_bus_creds_get_pid(creds, &pid);
504                 if (r < 0)
505                         return r;
506
507                 r = manager_get_session_by_pid(m, pid, &session);
508                 if (r <= 0)
509                         return 0;
510         } else {
511                 _cleanup_free_ char *e = NULL;
512                 const char *p;
513
514                 p = startswith(path, "/org/freedesktop/login1/session/");
515                 if (!p)
516                         return 0;
517
518                 e = bus_label_unescape(p);
519                 if (!e)
520                         return -ENOMEM;
521
522                 session = hashmap_get(m->sessions, e);
523                 if (!session)
524                         return 0;
525         }
526
527         *found = session;
528         return 1;
529 }
530
531 char *session_bus_path(Session *s) {
532         _cleanup_free_ char *t = NULL;
533
534         assert(s);
535
536         t = bus_label_escape(s->id);
537         if (!t)
538                 return NULL;
539
540         return strappend("/org/freedesktop/login1/session/", t);
541 }
542
543 int session_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
544         _cleanup_strv_free_ char **l = NULL;
545         Manager *m = userdata;
546         Session *session;
547         Iterator i;
548         int r;
549
550         assert(bus);
551         assert(path);
552         assert(nodes);
553
554         HASHMAP_FOREACH(session, m->sessions, i) {
555                 char *p;
556
557                 p = session_bus_path(session);
558                 if (!p)
559                         return -ENOMEM;
560
561                 r = strv_consume(&l, p);
562                 if (r < 0)
563                         return r;
564         }
565
566         r = strv_extend(&l, "/org/freedesktop/login1/session/self");
567         if (r < 0)
568                 return r;
569
570         *nodes = l;
571         l = NULL;
572
573         return 1;
574 }
575
576 int session_send_signal(Session *s, bool new_session) {
577         _cleanup_free_ char *p = NULL;
578
579         assert(s);
580
581         p = session_bus_path(s);
582         if (!p)
583                 return -ENOMEM;
584
585         return sd_bus_emit_signal(
586                         s->manager->bus,
587                         "/org/freedesktop/login1",
588                         "org.freedesktop.login1.Manager",
589                         new_session ? "SessionNew" : "SessionRemoved",
590                         "so", s->id, p);
591 }
592
593 int session_send_changed(Session *s, const char *properties, ...) {
594         _cleanup_free_ char *p = NULL;
595         char **l;
596
597         assert(s);
598
599         if (!s->started)
600                 return 0;
601
602         p = session_bus_path(s);
603         if (!p)
604                 return -ENOMEM;
605
606         l = strv_from_stdarg_alloca(properties);
607
608         return sd_bus_emit_properties_changed_strv(s->manager->bus, p, "org.freedesktop.login1.Session", l);
609 }
610
611 int session_send_lock(Session *s, bool lock) {
612         _cleanup_free_ char *p = NULL;
613
614         assert(s);
615
616         p = session_bus_path(s);
617         if (!p)
618                 return -ENOMEM;
619
620         return sd_bus_emit_signal(
621                         s->manager->bus,
622                         p,
623                         "org.freedesktop.login1.Session",
624                         lock ? "Lock" : "Unlock",
625                         NULL);
626 }
627
628 int session_send_lock_all(Manager *m, bool lock) {
629         Session *session;
630         Iterator i;
631         int r = 0;
632
633         assert(m);
634
635         HASHMAP_FOREACH(session, m->sessions, i) {
636                 int k;
637
638                 k = session_send_lock(session, lock);
639                 if (k < 0)
640                         r = k;
641         }
642
643         return r;
644 }
645
646 int session_send_create_reply(Session *s, sd_bus_error *error) {
647         _cleanup_bus_message_unref_ sd_bus_message *c = NULL;
648         _cleanup_close_ int fifo_fd = -1;
649         _cleanup_free_ char *p = NULL;
650
651         assert(s);
652
653         /* This is called after the session scope and the user service
654          * were successfully created, and finishes where
655          * bus_manager_create_session() left off. */
656
657         if (!s->create_message)
658                 return 0;
659
660         if (!sd_bus_error_is_set(error) && (s->scope_job || s->user->service_job))
661                 return 0;
662
663         c = s->create_message;
664         s->create_message = NULL;
665
666         if (error)
667                 return sd_bus_reply_method_error(c, error);
668
669         fifo_fd = session_create_fifo(s);
670         if (fifo_fd < 0)
671                 return fifo_fd;
672
673         /* Update the session state file before we notify the client
674          * about the result. */
675         session_save(s);
676
677         p = session_bus_path(s);
678         if (!p)
679                 return -ENOMEM;
680
681         log_debug("Sending reply about created session: "
682                   "id=%s object_path=%s uid=%u runtime_path=%s "
683                   "session_fd=%d seat=%s vtnr=%u",
684                   s->id,
685                   p,
686                   (uint32_t) s->user->uid,
687                   s->user->runtime_path,
688                   fifo_fd,
689                   s->seat ? s->seat->id : "",
690                   (uint32_t) s->vtnr);
691
692         return sd_bus_reply_method_return(
693                         c, "soshusub",
694                         s->id,
695                         p,
696                         s->user->runtime_path,
697                         fifo_fd,
698                         (uint32_t) s->user->uid,
699                         s->seat ? s->seat->id : "",
700                         (uint32_t) s->vtnr,
701                         false);
702 }