]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/udev/udev-builtin-path_id.c
Merge pull request #1909 from keszybz/filetriggers-v2
[thirdparty/systemd.git] / src / udev / udev-builtin-path_id.c
1 /*
2 * compose persistent device path
3 *
4 * Copyright (C) 2009-2011 Kay Sievers <kay@vrfy.org>
5 *
6 * Logic based on Hannes Reinecke's shell script.
7 *
8 * This program is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include <ctype.h>
23 #include <dirent.h>
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <getopt.h>
27 #include <stdarg.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32
33 #include "alloc-util.h"
34 #include "string-util.h"
35 #include "udev.h"
36
37 _printf_(2,3)
38 static int path_prepend(char **path, const char *fmt, ...) {
39 va_list va;
40 char *pre;
41 int err = 0;
42
43 va_start(va, fmt);
44 err = vasprintf(&pre, fmt, va);
45 va_end(va);
46 if (err < 0)
47 goto out;
48
49 if (*path != NULL) {
50 char *new;
51
52 err = asprintf(&new, "%s-%s", pre, *path);
53 free(pre);
54 if (err < 0)
55 goto out;
56 free(*path);
57 *path = new;
58 } else {
59 *path = pre;
60 }
61 out:
62 return err;
63 }
64
65 /*
66 ** Linux only supports 32 bit luns.
67 ** See drivers/scsi/scsi_scan.c::scsilun_to_int() for more details.
68 */
69 static int format_lun_number(struct udev_device *dev, char **path) {
70 unsigned long lun = strtoul(udev_device_get_sysnum(dev), NULL, 10);
71
72 /* address method 0, peripheral device addressing with bus id of zero */
73 if (lun < 256)
74 return path_prepend(path, "lun-%lu", lun);
75 /* handle all other lun addressing methods by using a variant of the original lun format */
76 return path_prepend(path, "lun-0x%04lx%04lx00000000", lun & 0xffff, (lun >> 16) & 0xffff);
77 }
78
79 static struct udev_device *skip_subsystem(struct udev_device *dev, const char *subsys) {
80 struct udev_device *parent = dev;
81
82 assert(dev);
83 assert(subsys);
84
85 while (parent != NULL) {
86 const char *subsystem;
87
88 subsystem = udev_device_get_subsystem(parent);
89 if (subsystem == NULL || !streq(subsystem, subsys))
90 break;
91 dev = parent;
92 parent = udev_device_get_parent(parent);
93 }
94 return dev;
95 }
96
97 static struct udev_device *handle_scsi_fibre_channel(struct udev_device *parent, char **path) {
98 struct udev *udev = udev_device_get_udev(parent);
99 struct udev_device *targetdev;
100 struct udev_device *fcdev = NULL;
101 const char *port;
102 char *lun = NULL;
103
104 assert(parent);
105 assert(path);
106
107 targetdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_target");
108 if (targetdev == NULL)
109 return NULL;
110
111 fcdev = udev_device_new_from_subsystem_sysname(udev, "fc_transport", udev_device_get_sysname(targetdev));
112 if (fcdev == NULL)
113 return NULL;
114 port = udev_device_get_sysattr_value(fcdev, "port_name");
115 if (port == NULL) {
116 parent = NULL;
117 goto out;
118 }
119
120 format_lun_number(parent, &lun);
121 path_prepend(path, "fc-%s-%s", port, lun);
122 free(lun);
123 out:
124 udev_device_unref(fcdev);
125 return parent;
126 }
127
128 static struct udev_device *handle_scsi_sas_wide_port(struct udev_device *parent, char **path) {
129 struct udev *udev = udev_device_get_udev(parent);
130 struct udev_device *targetdev;
131 struct udev_device *target_parent;
132 struct udev_device *sasdev;
133 const char *sas_address;
134 char *lun = NULL;
135
136 assert(parent);
137 assert(path);
138
139 targetdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_target");
140 if (targetdev == NULL)
141 return NULL;
142
143 target_parent = udev_device_get_parent(targetdev);
144 if (target_parent == NULL)
145 return NULL;
146
147 sasdev = udev_device_new_from_subsystem_sysname(udev, "sas_device",
148 udev_device_get_sysname(target_parent));
149 if (sasdev == NULL)
150 return NULL;
151
152 sas_address = udev_device_get_sysattr_value(sasdev, "sas_address");
153 if (sas_address == NULL) {
154 parent = NULL;
155 goto out;
156 }
157
158 format_lun_number(parent, &lun);
159 path_prepend(path, "sas-%s-%s", sas_address, lun);
160 free(lun);
161 out:
162 udev_device_unref(sasdev);
163 return parent;
164 }
165
166 static struct udev_device *handle_scsi_sas(struct udev_device *parent, char **path)
167 {
168 struct udev *udev = udev_device_get_udev(parent);
169 struct udev_device *targetdev;
170 struct udev_device *target_parent;
171 struct udev_device *port;
172 struct udev_device *expander;
173 struct udev_device *target_sasdev = NULL;
174 struct udev_device *expander_sasdev = NULL;
175 struct udev_device *port_sasdev = NULL;
176 const char *sas_address = NULL;
177 const char *phy_id;
178 const char *phy_count;
179 char *lun = NULL;
180
181 assert(parent);
182 assert(path);
183
184 targetdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_target");
185 if (targetdev == NULL)
186 return NULL;
187
188 target_parent = udev_device_get_parent(targetdev);
189 if (target_parent == NULL)
190 return NULL;
191
192 /* Get sas device */
193 target_sasdev = udev_device_new_from_subsystem_sysname(udev,
194 "sas_device", udev_device_get_sysname(target_parent));
195 if (target_sasdev == NULL)
196 return NULL;
197
198 /* The next parent is sas port */
199 port = udev_device_get_parent(target_parent);
200 if (port == NULL) {
201 parent = NULL;
202 goto out;
203 }
204
205 /* Get port device */
206 port_sasdev = udev_device_new_from_subsystem_sysname(udev,
207 "sas_port", udev_device_get_sysname(port));
208
209 phy_count = udev_device_get_sysattr_value(port_sasdev, "num_phys");
210 if (phy_count == NULL) {
211 parent = NULL;
212 goto out;
213 }
214
215 /* Check if we are simple disk */
216 if (strncmp(phy_count, "1", 2) != 0) {
217 parent = handle_scsi_sas_wide_port(parent, path);
218 goto out;
219 }
220
221 /* Get connected phy */
222 phy_id = udev_device_get_sysattr_value(target_sasdev, "phy_identifier");
223 if (phy_id == NULL) {
224 parent = NULL;
225 goto out;
226 }
227
228 /* The port's parent is either hba or expander */
229 expander = udev_device_get_parent(port);
230 if (expander == NULL) {
231 parent = NULL;
232 goto out;
233 }
234
235 /* Get expander device */
236 expander_sasdev = udev_device_new_from_subsystem_sysname(udev,
237 "sas_device", udev_device_get_sysname(expander));
238 if (expander_sasdev != NULL) {
239 /* Get expander's address */
240 sas_address = udev_device_get_sysattr_value(expander_sasdev,
241 "sas_address");
242 if (sas_address == NULL) {
243 parent = NULL;
244 goto out;
245 }
246 }
247
248 format_lun_number(parent, &lun);
249 if (sas_address)
250 path_prepend(path, "sas-exp%s-phy%s-%s", sas_address, phy_id, lun);
251 else
252 path_prepend(path, "sas-phy%s-%s", phy_id, lun);
253
254 free(lun);
255 out:
256 udev_device_unref(target_sasdev);
257 udev_device_unref(expander_sasdev);
258 udev_device_unref(port_sasdev);
259 return parent;
260 }
261
262 static struct udev_device *handle_scsi_iscsi(struct udev_device *parent, char **path) {
263 struct udev *udev = udev_device_get_udev(parent);
264 struct udev_device *transportdev;
265 struct udev_device *sessiondev = NULL;
266 const char *target;
267 char *connname;
268 struct udev_device *conndev = NULL;
269 const char *addr;
270 const char *port;
271 char *lun = NULL;
272
273 assert(parent);
274 assert(path);
275
276 /* find iscsi session */
277 transportdev = parent;
278 for (;;) {
279 transportdev = udev_device_get_parent(transportdev);
280 if (transportdev == NULL)
281 return NULL;
282 if (startswith(udev_device_get_sysname(transportdev), "session"))
283 break;
284 }
285
286 /* find iscsi session device */
287 sessiondev = udev_device_new_from_subsystem_sysname(udev, "iscsi_session", udev_device_get_sysname(transportdev));
288 if (sessiondev == NULL)
289 return NULL;
290 target = udev_device_get_sysattr_value(sessiondev, "targetname");
291 if (target == NULL) {
292 parent = NULL;
293 goto out;
294 }
295
296 if (asprintf(&connname, "connection%s:0", udev_device_get_sysnum(transportdev)) < 0) {
297 parent = NULL;
298 goto out;
299 }
300 conndev = udev_device_new_from_subsystem_sysname(udev, "iscsi_connection", connname);
301 free(connname);
302 if (conndev == NULL) {
303 parent = NULL;
304 goto out;
305 }
306 addr = udev_device_get_sysattr_value(conndev, "persistent_address");
307 port = udev_device_get_sysattr_value(conndev, "persistent_port");
308 if (addr == NULL || port == NULL) {
309 parent = NULL;
310 goto out;
311 }
312
313 format_lun_number(parent, &lun);
314 path_prepend(path, "ip-%s:%s-iscsi-%s-%s", addr, port, target, lun);
315 free(lun);
316 out:
317 udev_device_unref(sessiondev);
318 udev_device_unref(conndev);
319 return parent;
320 }
321
322 static struct udev_device *handle_scsi_ata(struct udev_device *parent, char **path) {
323 struct udev *udev = udev_device_get_udev(parent);
324 struct udev_device *targetdev;
325 struct udev_device *target_parent;
326 struct udev_device *atadev;
327 const char *port_no;
328
329 assert(parent);
330 assert(path);
331
332 targetdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_host");
333 if (!targetdev)
334 return NULL;
335
336 target_parent = udev_device_get_parent(targetdev);
337 if (!target_parent)
338 return NULL;
339
340 atadev = udev_device_new_from_subsystem_sysname(udev, "ata_port", udev_device_get_sysname(target_parent));
341 if (!atadev)
342 return NULL;
343
344 port_no = udev_device_get_sysattr_value(atadev, "port_no");
345 if (!port_no) {
346 parent = NULL;
347 goto out;
348 }
349 path_prepend(path, "ata-%s", port_no);
350 out:
351 udev_device_unref(atadev);
352 return parent;
353 }
354
355 static struct udev_device *handle_scsi_default(struct udev_device *parent, char **path) {
356 struct udev_device *hostdev;
357 int host, bus, target, lun;
358 const char *name;
359 char *base;
360 char *pos;
361 DIR *dir;
362 struct dirent *dent;
363 int basenum;
364
365 assert(parent);
366 assert(path);
367
368 hostdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_host");
369 if (hostdev == NULL)
370 return NULL;
371
372 name = udev_device_get_sysname(parent);
373 if (sscanf(name, "%d:%d:%d:%d", &host, &bus, &target, &lun) != 4)
374 return NULL;
375
376 /*
377 * Rebase host offset to get the local relative number
378 *
379 * Note: This is by definition racy, unreliable and too simple.
380 * Please do not copy this model anywhere. It's just a left-over
381 * from the time we had no idea how things should look like in
382 * the end.
383 *
384 * Making assumptions about a global in-kernel counter and use
385 * that to calculate a local offset is a very broken concept. It
386 * can only work as long as things are in strict order.
387 *
388 * The kernel needs to export the instance/port number of a
389 * controller directly, without the need for rebase magic like
390 * this. Manual driver unbind/bind, parallel hotplug/unplug will
391 * get into the way of this "I hope it works" logic.
392 */
393 basenum = -1;
394 base = strdup(udev_device_get_syspath(hostdev));
395 if (base == NULL)
396 return NULL;
397 pos = strrchr(base, '/');
398 if (pos == NULL) {
399 parent = NULL;
400 goto out;
401 }
402 pos[0] = '\0';
403 dir = opendir(base);
404 if (dir == NULL) {
405 parent = NULL;
406 goto out;
407 }
408 for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
409 char *rest;
410 int i;
411
412 if (dent->d_name[0] == '.')
413 continue;
414 if (dent->d_type != DT_DIR && dent->d_type != DT_LNK)
415 continue;
416 if (!startswith(dent->d_name, "host"))
417 continue;
418 i = strtoul(&dent->d_name[4], &rest, 10);
419 if (rest[0] != '\0')
420 continue;
421 /*
422 * find the smallest number; the host really needs to export its
423 * own instance number per parent device; relying on the global host
424 * enumeration and plainly rebasing the numbers sounds unreliable
425 */
426 if (basenum == -1 || i < basenum)
427 basenum = i;
428 }
429 closedir(dir);
430 if (basenum == -1) {
431 parent = NULL;
432 goto out;
433 }
434 host -= basenum;
435
436 path_prepend(path, "scsi-%u:%u:%u:%u", host, bus, target, lun);
437 out:
438 free(base);
439 return hostdev;
440 }
441
442 static struct udev_device *handle_scsi_hyperv(struct udev_device *parent, char **path) {
443 struct udev_device *hostdev;
444 struct udev_device *vmbusdev;
445 const char *guid_str;
446 char *lun = NULL;
447 char guid[38];
448 size_t i, k;
449
450 assert(parent);
451 assert(path);
452
453 hostdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_host");
454 if (!hostdev)
455 return NULL;
456
457 vmbusdev = udev_device_get_parent(hostdev);
458 if (!vmbusdev)
459 return NULL;
460
461 guid_str = udev_device_get_sysattr_value(vmbusdev, "device_id");
462 if (!guid_str)
463 return NULL;
464
465 if (strlen(guid_str) < 37 || guid_str[0] != '{' || guid_str[36] != '}')
466 return NULL;
467
468 for (i = 1, k = 0; i < 36; i++) {
469 if (guid_str[i] == '-')
470 continue;
471 guid[k++] = guid_str[i];
472 }
473 guid[k] = '\0';
474
475 format_lun_number(parent, &lun);
476 path_prepend(path, "vmbus-%s-%s", guid, lun);
477 free(lun);
478 return parent;
479 }
480
481 static struct udev_device *handle_scsi(struct udev_device *parent, char **path, bool *supported_parent) {
482 const char *devtype;
483 const char *name;
484 const char *id;
485
486 devtype = udev_device_get_devtype(parent);
487 if (devtype == NULL || !streq(devtype, "scsi_device"))
488 return parent;
489
490 /* firewire */
491 id = udev_device_get_sysattr_value(parent, "ieee1394_id");
492 if (id != NULL) {
493 parent = skip_subsystem(parent, "scsi");
494 path_prepend(path, "ieee1394-0x%s", id);
495 *supported_parent = true;
496 goto out;
497 }
498
499 /* scsi sysfs does not have a "subsystem" for the transport */
500 name = udev_device_get_syspath(parent);
501
502 if (strstr(name, "/rport-") != NULL) {
503 parent = handle_scsi_fibre_channel(parent, path);
504 *supported_parent = true;
505 goto out;
506 }
507
508 if (strstr(name, "/end_device-") != NULL) {
509 parent = handle_scsi_sas(parent, path);
510 *supported_parent = true;
511 goto out;
512 }
513
514 if (strstr(name, "/session") != NULL) {
515 parent = handle_scsi_iscsi(parent, path);
516 *supported_parent = true;
517 goto out;
518 }
519
520 if (strstr(name, "/ata") != NULL) {
521 parent = handle_scsi_ata(parent, path);
522 goto out;
523 }
524
525 if (strstr(name, "/vmbus_") != NULL) {
526 parent = handle_scsi_hyperv(parent, path);
527 goto out;
528 }
529
530 parent = handle_scsi_default(parent, path);
531 out:
532 return parent;
533 }
534
535 static struct udev_device *handle_cciss(struct udev_device *parent, char **path) {
536 const char *str;
537 unsigned int controller, disk;
538
539 str = udev_device_get_sysname(parent);
540 if (sscanf(str, "c%ud%u%*s", &controller, &disk) != 2)
541 return NULL;
542
543 path_prepend(path, "cciss-disk%u", disk);
544 parent = skip_subsystem(parent, "cciss");
545 return parent;
546 }
547
548 static void handle_scsi_tape(struct udev_device *dev, char **path) {
549 const char *name;
550
551 /* must be the last device in the syspath */
552 if (*path != NULL)
553 return;
554
555 name = udev_device_get_sysname(dev);
556 if (startswith(name, "nst") && strchr("lma", name[3]) != NULL)
557 path_prepend(path, "nst%c", name[3]);
558 else if (startswith(name, "st") && strchr("lma", name[2]) != NULL)
559 path_prepend(path, "st%c", name[2]);
560 }
561
562 static struct udev_device *handle_usb(struct udev_device *parent, char **path) {
563 const char *devtype;
564 const char *str;
565 const char *port;
566
567 devtype = udev_device_get_devtype(parent);
568 if (devtype == NULL)
569 return parent;
570 if (!streq(devtype, "usb_interface") && !streq(devtype, "usb_device"))
571 return parent;
572
573 str = udev_device_get_sysname(parent);
574 port = strchr(str, '-');
575 if (port == NULL)
576 return parent;
577 port++;
578
579 parent = skip_subsystem(parent, "usb");
580 path_prepend(path, "usb-0:%s", port);
581 return parent;
582 }
583
584 static struct udev_device *handle_bcma(struct udev_device *parent, char **path) {
585 const char *sysname;
586 unsigned int core;
587
588 sysname = udev_device_get_sysname(parent);
589 if (sscanf(sysname, "bcma%*u:%u", &core) != 1)
590 return NULL;
591
592 path_prepend(path, "bcma-%u", core);
593 return parent;
594 }
595
596 /* Handle devices of AP bus in System z platform. */
597 static struct udev_device *handle_ap(struct udev_device *parent, char **path) {
598 const char *type, *func;
599
600 assert(parent);
601 assert(path);
602
603 type = udev_device_get_sysattr_value(parent, "type");
604 func = udev_device_get_sysattr_value(parent, "ap_functions");
605
606 if (type != NULL && func != NULL) {
607 path_prepend(path, "ap-%s-%s", type, func);
608 goto out;
609 }
610 path_prepend(path, "ap-%s", udev_device_get_sysname(parent));
611 out:
612 parent = skip_subsystem(parent, "ap");
613 return parent;
614 }
615
616 static int builtin_path_id(struct udev_device *dev, int argc, char *argv[], bool test) {
617 struct udev_device *parent;
618 char *path = NULL;
619 bool supported_transport = false;
620 bool supported_parent = false;
621
622 assert(dev);
623
624 /* walk up the chain of devices and compose path */
625 parent = dev;
626 while (parent != NULL) {
627 const char *subsys;
628
629 subsys = udev_device_get_subsystem(parent);
630 if (subsys == NULL) {
631 ;
632 } else if (streq(subsys, "scsi_tape")) {
633 handle_scsi_tape(parent, &path);
634 } else if (streq(subsys, "scsi")) {
635 parent = handle_scsi(parent, &path, &supported_parent);
636 supported_transport = true;
637 } else if (streq(subsys, "cciss")) {
638 parent = handle_cciss(parent, &path);
639 supported_transport = true;
640 } else if (streq(subsys, "usb")) {
641 parent = handle_usb(parent, &path);
642 supported_transport = true;
643 } else if (streq(subsys, "bcma")) {
644 parent = handle_bcma(parent, &path);
645 supported_transport = true;
646 } else if (streq(subsys, "serio")) {
647 path_prepend(&path, "serio-%s", udev_device_get_sysnum(parent));
648 parent = skip_subsystem(parent, "serio");
649 } else if (streq(subsys, "pci")) {
650 path_prepend(&path, "pci-%s", udev_device_get_sysname(parent));
651 parent = skip_subsystem(parent, "pci");
652 supported_parent = true;
653 } else if (streq(subsys, "platform")) {
654 path_prepend(&path, "platform-%s", udev_device_get_sysname(parent));
655 parent = skip_subsystem(parent, "platform");
656 supported_transport = true;
657 supported_parent = true;
658 } else if (streq(subsys, "acpi")) {
659 path_prepend(&path, "acpi-%s", udev_device_get_sysname(parent));
660 parent = skip_subsystem(parent, "acpi");
661 supported_parent = true;
662 } else if (streq(subsys, "xen")) {
663 path_prepend(&path, "xen-%s", udev_device_get_sysname(parent));
664 parent = skip_subsystem(parent, "xen");
665 supported_parent = true;
666 } else if (streq(subsys, "scm")) {
667 path_prepend(&path, "scm-%s", udev_device_get_sysname(parent));
668 parent = skip_subsystem(parent, "scm");
669 supported_transport = true;
670 supported_parent = true;
671 } else if (streq(subsys, "ccw")) {
672 path_prepend(&path, "ccw-%s", udev_device_get_sysname(parent));
673 parent = skip_subsystem(parent, "ccw");
674 supported_transport = true;
675 supported_parent = true;
676 } else if (streq(subsys, "ccwgroup")) {
677 path_prepend(&path, "ccwgroup-%s", udev_device_get_sysname(parent));
678 parent = skip_subsystem(parent, "ccwgroup");
679 supported_transport = true;
680 supported_parent = true;
681 } else if (streq(subsys, "ap")) {
682 parent = handle_ap(parent, &path);
683 supported_transport = true;
684 supported_parent = true;
685 } else if (streq(subsys, "iucv")) {
686 path_prepend(&path, "iucv-%s", udev_device_get_sysname(parent));
687 parent = skip_subsystem(parent, "iucv");
688 supported_transport = true;
689 supported_parent = true;
690 }
691
692 if (parent)
693 parent = udev_device_get_parent(parent);
694 }
695
696 /*
697 * Do not return devices with an unknown parent device type. They
698 * might produce conflicting IDs if the parent does not provide a
699 * unique and predictable name.
700 */
701 if (!supported_parent)
702 path = mfree(path);
703
704 /*
705 * Do not return block devices without a well-known transport. Some
706 * devices do not expose their buses and do not provide a unique
707 * and predictable name that way.
708 */
709 if (streq(udev_device_get_subsystem(dev), "block") && !supported_transport)
710 path = mfree(path);
711
712 if (path != NULL) {
713 char tag[UTIL_NAME_SIZE];
714 size_t i;
715 const char *p;
716
717 /* compose valid udev tag name */
718 for (p = path, i = 0; *p; p++) {
719 if ((*p >= '0' && *p <= '9') ||
720 (*p >= 'A' && *p <= 'Z') ||
721 (*p >= 'a' && *p <= 'z') ||
722 *p == '-') {
723 tag[i++] = *p;
724 continue;
725 }
726
727 /* skip all leading '_' */
728 if (i == 0)
729 continue;
730
731 /* avoid second '_' */
732 if (tag[i-1] == '_')
733 continue;
734
735 tag[i++] = '_';
736 }
737 /* strip trailing '_' */
738 while (i > 0 && tag[i-1] == '_')
739 i--;
740 tag[i] = '\0';
741
742 udev_builtin_add_property(dev, test, "ID_PATH", path);
743 udev_builtin_add_property(dev, test, "ID_PATH_TAG", tag);
744 free(path);
745 return EXIT_SUCCESS;
746 }
747 return EXIT_FAILURE;
748 }
749
750 const struct udev_builtin udev_builtin_path_id = {
751 .name = "path_id",
752 .cmd = builtin_path_id,
753 .help = "Compose persistent device path",
754 .run_once = true,
755 };