]>
git.proxmox.com Git - mirror_lxc.git/blob - src/lxc/confile_utils.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
15 #include "confile_utils.h"
20 #include "lxccontainer.h"
22 #include "memory_utils.h"
28 #include "include/strlcpy.h"
31 lxc_log_define(confile_utils
, lxc
);
33 int parse_idmaps(const char *idmap
, char *type
, unsigned long *nsid
,
34 unsigned long *hostid
, unsigned long *range
)
36 __do_free
char *dup
= NULL
;
38 unsigned long tmp_hostid
, tmp_nsid
, tmp_range
;
42 /* Duplicate string. */
45 return ret_errno(ENOMEM
);
47 /* A prototypical idmap entry would be: "u 1000 1000000 65536" */
52 slide
+= strspn(slide
, " \t\r");
53 if (slide
!= window
&& *slide
== '\0')
54 return ret_errno(EINVAL
);
57 if (*slide
!= 'u' && *slide
!= 'g')
58 return log_error_errno(-EINVAL
, EINVAL
, "Invalid id mapping type: %c", *slide
);
63 /* move beyond type */
67 /* Validate that only whitespace follows. */
68 slide
+= strspn(slide
, " \t\r");
69 /* There must be whitespace. */
71 return ret_errno(EINVAL
);
73 /* Mark beginning of nsid. */
75 /* Validate that non-whitespace follows. */
76 slide
+= strcspn(slide
, " \t\r");
77 /* There must be non-whitespace. */
78 if (slide
== window
|| *slide
== '\0')
79 return ret_errno(EINVAL
);
80 /* Mark end of nsid. */
84 ret
= lxc_safe_ulong(window
, &tmp_nsid
);
86 return log_error_errno(ret
, errno
, "Failed to parse nsid: %s", window
);
90 /* Validate that only whitespace follows. */
91 slide
+= strspn(slide
, " \t\r");
92 /* If there was only one whitespace then we whiped it with our \0 above.
93 * So only ensure that we're not at the end of the string.
96 return ret_errno(EINVAL
);
98 /* Mark beginning of hostid. */
100 /* Validate that non-whitespace follows. */
101 slide
+= strcspn(slide
, " \t\r");
102 /* There must be non-whitespace. */
103 if (slide
== window
|| *slide
== '\0')
104 return ret_errno(EINVAL
);
105 /* Mark end of nsid. */
109 ret
= lxc_safe_ulong(window
, &tmp_hostid
);
111 return log_error_errno(ret
, errno
, "Failed to parse hostid: %s", window
);
113 /* Move beyond \0. */
115 /* Validate that only whitespace follows. */
116 slide
+= strspn(slide
, " \t\r");
117 /* If there was only one whitespace then we whiped it with our \0 above.
118 * So only ensure that we're not at the end of the string.
121 return ret_errno(EINVAL
);
123 /* Mark beginning of range. */
125 /* Validate that non-whitespace follows. */
126 slide
+= strcspn(slide
, " \t\r");
127 /* There must be non-whitespace. */
129 return ret_errno(EINVAL
);
131 /* The range is the last valid entry we expect. So make sure that there
132 * is no trailing garbage and if there is, error out.
134 if (*(slide
+ strspn(slide
, " \t\r\n")) != '\0')
135 return ret_errno(EINVAL
);
137 /* Mark end of range. */
141 ret
= lxc_safe_ulong(window
, &tmp_range
);
143 return log_error_errno(ret
, errno
, "Failed to parse id mapping range: %s", window
);
147 *hostid
= tmp_hostid
;
150 /* Yay, we survived. */
154 bool lxc_config_value_empty(const char *value
)
156 if (value
&& strlen(value
) > 0)
162 struct lxc_netdev
*lxc_network_add(struct lxc_list
*networks
, int idx
, bool tail
)
164 __do_free
struct lxc_list
*newlist
= NULL
;
165 __do_free
struct lxc_netdev
*netdev
= NULL
;
167 /* network does not exist */
168 netdev
= zalloc(sizeof(*netdev
));
170 return ret_set_errno(NULL
, ENOMEM
);
172 lxc_list_init(&netdev
->ipv4
);
173 lxc_list_init(&netdev
->ipv6
);
175 /* give network a unique index */
178 /* prepare new list */
179 newlist
= lxc_list_new();
181 return ret_set_errno(NULL
, ENOMEM
);
182 newlist
->elem
= netdev
;
185 lxc_list_add_tail(networks
, newlist
);
187 lxc_list_add(networks
, newlist
);
190 return move_ptr(netdev
);
193 /* Takes care of finding the correct netdev struct in the networks list or
194 * allocates a new one if it couldn't be found.
196 struct lxc_netdev
*lxc_get_netdev_by_idx(struct lxc_conf
*conf
,
197 unsigned int idx
, bool allocate
)
199 struct lxc_list
*networks
= &conf
->network
;
200 struct lxc_list
*insert
= networks
;
203 if (!lxc_list_empty(networks
)) {
204 lxc_list_for_each(insert
, networks
) {
205 struct lxc_netdev
*netdev
= insert
->elem
;
207 /* found network device */
208 if (netdev
->idx
== idx
)
211 if (netdev
->idx
> idx
)
217 return lxc_network_add(insert
, idx
, true);
222 void lxc_log_configured_netdevs(const struct lxc_conf
*conf
)
224 struct lxc_netdev
*netdev
;
225 struct lxc_list
*it
= (struct lxc_list
*)&conf
->network
;;
227 if (!lxc_log_trace())
230 if (lxc_list_empty(it
)) {
231 TRACE("container has no networks configured");
235 lxc_list_for_each(it
, &conf
->network
) {
236 struct lxc_list
*cur
, *next
;
237 struct lxc_inetdev
*inet4dev
;
238 struct lxc_inet6dev
*inet6dev
;
239 char bufinet4
[INET_ADDRSTRLEN
], bufinet6
[INET6_ADDRSTRLEN
];
243 TRACE("index: %zd", netdev
->idx
);
244 TRACE("ifindex: %d", netdev
->ifindex
);
246 switch (netdev
->type
) {
249 TRACE("veth mode: %d", netdev
->priv
.veth_attr
.mode
);
251 if (netdev
->priv
.veth_attr
.pair
[0] != '\0')
252 TRACE("veth pair: %s",
253 netdev
->priv
.veth_attr
.pair
);
255 if (netdev
->priv
.veth_attr
.veth1
[0] != '\0')
257 netdev
->priv
.veth_attr
.veth1
);
259 if (netdev
->priv
.veth_attr
.ifindex
> 0)
260 TRACE("host side ifindex for veth device: %d",
261 netdev
->priv
.veth_attr
.ifindex
);
263 if (netdev
->priv
.veth_attr
.vlan_id_set
)
264 TRACE("veth vlan id: %d", netdev
->priv
.veth_attr
.vlan_id
);
266 lxc_list_for_each_safe(cur
, &netdev
->priv
.veth_attr
.vlan_tagged_ids
, next
) {
267 unsigned short vlan_tagged_id
= PTR_TO_USHORT(cur
->elem
);
268 TRACE("veth vlan tagged id: %u", vlan_tagged_id
);
272 case LXC_NET_MACVLAN
:
273 TRACE("type: macvlan");
275 if (netdev
->priv
.macvlan_attr
.mode
> 0) {
278 mode
= lxc_macvlan_flag_to_mode(
279 netdev
->priv
.macvlan_attr
.mode
);
280 TRACE("macvlan mode: %s",
281 mode
? mode
: "(invalid mode)");
285 TRACE("type: ipvlan");
288 mode
= lxc_ipvlan_flag_to_mode(netdev
->priv
.ipvlan_attr
.mode
);
289 TRACE("ipvlan mode: %s", mode
? mode
: "(invalid mode)");
292 isolation
= lxc_ipvlan_flag_to_isolation(netdev
->priv
.ipvlan_attr
.isolation
);
293 TRACE("ipvlan isolation: %s", isolation
? isolation
: "(invalid isolation)");
297 TRACE("vlan id: %d", netdev
->priv
.vlan_attr
.vid
);
302 if (netdev
->priv
.phys_attr
.ifindex
> 0)
303 TRACE("host side ifindex for phys device: %d",
304 netdev
->priv
.phys_attr
.ifindex
);
307 TRACE("type: empty");
313 ERROR("Invalid network type %d", netdev
->type
);
317 if (netdev
->type
!= LXC_NET_EMPTY
) {
319 netdev
->flags
== IFF_UP
? "up" : "none");
321 if (netdev
->link
[0] != '\0')
322 TRACE("link: %s", netdev
->link
);
324 /* l2proxy only used when link is specified */
325 if (netdev
->link
[0] != '\0')
326 TRACE("l2proxy: %s", netdev
->l2proxy
? "true" : "false");
328 if (netdev
->name
[0] != '\0')
329 TRACE("name: %s", netdev
->name
);
332 TRACE("hwaddr: %s", netdev
->hwaddr
);
335 TRACE("mtu: %s", netdev
->mtu
);
337 if (netdev
->upscript
)
338 TRACE("upscript: %s", netdev
->upscript
);
340 if (netdev
->downscript
)
341 TRACE("downscript: %s", netdev
->downscript
);
343 TRACE("ipv4 gateway auto: %s",
344 netdev
->ipv4_gateway_auto
? "true" : "false");
346 TRACE("ipv4 gateway dev: %s",
347 netdev
->ipv4_gateway_dev
? "true" : "false");
349 if (netdev
->ipv4_gateway
) {
350 inet_ntop(AF_INET
, netdev
->ipv4_gateway
,
351 bufinet4
, sizeof(bufinet4
));
352 TRACE("ipv4 gateway: %s", bufinet4
);
355 lxc_list_for_each_safe(cur
, &netdev
->ipv4
, next
) {
356 inet4dev
= cur
->elem
;
357 inet_ntop(AF_INET
, &inet4dev
->addr
, bufinet4
,
359 TRACE("ipv4 addr: %s", bufinet4
);
362 TRACE("ipv6 gateway auto: %s",
363 netdev
->ipv6_gateway_auto
? "true" : "false");
365 TRACE("ipv6 gateway dev: %s",
366 netdev
->ipv6_gateway_dev
? "true" : "false");
368 if (netdev
->ipv6_gateway
) {
369 inet_ntop(AF_INET6
, netdev
->ipv6_gateway
,
370 bufinet6
, sizeof(bufinet6
));
371 TRACE("ipv6 gateway: %s", bufinet6
);
374 lxc_list_for_each_safe(cur
, &netdev
->ipv6
, next
) {
375 inet6dev
= cur
->elem
;
376 inet_ntop(AF_INET6
, &inet6dev
->addr
, bufinet6
,
378 TRACE("ipv6 addr: %s", bufinet6
);
381 if (netdev
->type
== LXC_NET_VETH
) {
382 lxc_list_for_each_safe(cur
, &netdev
->priv
.veth_attr
.ipv4_routes
, next
) {
383 inet4dev
= cur
->elem
;
384 if (!inet_ntop(AF_INET
, &inet4dev
->addr
, bufinet4
, sizeof(bufinet4
))) {
385 ERROR("Invalid ipv4 veth route");
389 TRACE("ipv4 veth route: %s/%u", bufinet4
, inet4dev
->prefix
);
392 lxc_list_for_each_safe(cur
, &netdev
->priv
.veth_attr
.ipv6_routes
, next
) {
393 inet6dev
= cur
->elem
;
394 if (!inet_ntop(AF_INET6
, &inet6dev
->addr
, bufinet6
, sizeof(bufinet6
))) {
395 ERROR("Invalid ipv6 veth route");
399 TRACE("ipv6 veth route: %s/%u", bufinet6
, inet6dev
->prefix
);
406 void lxc_clear_netdev(struct lxc_netdev
*netdev
)
408 struct lxc_list
*cur
, *next
;
416 free_disarm(netdev
->upscript
);
417 free_disarm(netdev
->downscript
);
418 free_disarm(netdev
->hwaddr
);
419 free_disarm(netdev
->mtu
);
421 free_disarm(netdev
->ipv4_gateway
);
422 lxc_list_for_each_safe(cur
, &netdev
->ipv4
, next
) {
428 free_disarm(netdev
->ipv6_gateway
);
429 lxc_list_for_each_safe(cur
, &netdev
->ipv6
, next
) {
435 if (netdev
->type
== LXC_NET_VETH
) {
436 lxc_list_for_each_safe(cur
, &netdev
->priv
.veth_attr
.ipv4_routes
, next
) {
442 lxc_list_for_each_safe(cur
, &netdev
->priv
.veth_attr
.ipv6_routes
, next
) {
448 lxc_list_for_each_safe(cur
, &netdev
->priv
.veth_attr
.vlan_tagged_ids
, next
) {
454 memset(netdev
, 0, sizeof(struct lxc_netdev
));
455 lxc_list_init(&netdev
->ipv4
);
456 lxc_list_init(&netdev
->ipv6
);
461 static void lxc_free_netdev(struct lxc_netdev
*netdev
)
464 lxc_clear_netdev(netdev
);
469 bool lxc_remove_nic_by_idx(struct lxc_conf
*conf
, unsigned int idx
)
471 struct lxc_list
*cur
, *next
;
473 if (lxc_list_empty(&conf
->network
))
476 lxc_list_for_each_safe(cur
, &conf
->network
, next
) {
477 struct lxc_netdev
*netdev
= cur
->elem
;
479 if (netdev
->idx
!= idx
)
483 lxc_free_netdev(netdev
);
491 void lxc_free_networks(struct lxc_list
*networks
)
493 struct lxc_list
*cur
, *next
;
495 lxc_list_for_each_safe (cur
, networks
, next
) {
496 struct lxc_netdev
*netdev
= cur
->elem
;
499 lxc_free_netdev(netdev
);
503 /* prevent segfaults */
504 lxc_list_init(networks
);
507 static struct lxc_veth_mode
{
511 { "bridge", VETH_MODE_BRIDGE
},
512 { "router", VETH_MODE_ROUTER
},
515 int lxc_veth_mode_to_flag(int *mode
, const char *value
)
517 for (size_t i
= 0; i
< sizeof(veth_mode
) / sizeof(veth_mode
[0]); i
++) {
518 if (!strequal(veth_mode
[i
].name
, value
))
521 *mode
= veth_mode
[i
].mode
;
525 return ret_errno(EINVAL
);
528 char *lxc_veth_flag_to_mode(int mode
)
530 for (size_t i
= 0; i
< sizeof(veth_mode
) / sizeof(veth_mode
[0]); i
++) {
531 if (veth_mode
[i
].mode
!= mode
)
534 return veth_mode
[i
].name
;
537 return ret_set_errno(NULL
, EINVAL
);
540 static struct lxc_macvlan_mode
{
544 { "private", MACVLAN_MODE_PRIVATE
},
545 { "vepa", MACVLAN_MODE_VEPA
},
546 { "bridge", MACVLAN_MODE_BRIDGE
},
547 { "passthru", MACVLAN_MODE_PASSTHRU
},
550 int lxc_macvlan_mode_to_flag(int *mode
, const char *value
)
552 for (size_t i
= 0; i
< sizeof(macvlan_mode
) / sizeof(macvlan_mode
[0]); i
++) {
553 if (!strequal(macvlan_mode
[i
].name
, value
))
556 *mode
= macvlan_mode
[i
].mode
;
560 return ret_errno(EINVAL
);
563 char *lxc_macvlan_flag_to_mode(int mode
)
565 for (size_t i
= 0; i
< sizeof(macvlan_mode
) / sizeof(macvlan_mode
[0]); i
++) {
566 if (macvlan_mode
[i
].mode
!= mode
)
569 return macvlan_mode
[i
].name
;
572 return ret_set_errno(NULL
, EINVAL
);
575 static struct lxc_ipvlan_mode
{
579 { "l3", IPVLAN_MODE_L3
},
580 { "l3s", IPVLAN_MODE_L3S
},
581 { "l2", IPVLAN_MODE_L2
},
584 int lxc_ipvlan_mode_to_flag(int *mode
, const char *value
)
586 for (size_t i
= 0; i
< sizeof(ipvlan_mode
) / sizeof(ipvlan_mode
[0]); i
++) {
587 if (!strequal(ipvlan_mode
[i
].name
, value
))
590 *mode
= ipvlan_mode
[i
].mode
;
594 return ret_errno(EINVAL
);
597 char *lxc_ipvlan_flag_to_mode(int mode
)
599 for (size_t i
= 0; i
< sizeof(ipvlan_mode
) / sizeof(ipvlan_mode
[0]); i
++) {
600 if (ipvlan_mode
[i
].mode
!= mode
)
603 return ipvlan_mode
[i
].name
;
606 return ret_set_errno(NULL
, EINVAL
);
609 static struct lxc_ipvlan_isolation
{
612 } ipvlan_isolation
[] = {
613 { "bridge", IPVLAN_ISOLATION_BRIDGE
},
614 { "private", IPVLAN_ISOLATION_PRIVATE
},
615 { "vepa", IPVLAN_ISOLATION_VEPA
},
618 int lxc_ipvlan_isolation_to_flag(int *flag
, const char *value
)
620 for (size_t i
= 0; i
< sizeof(ipvlan_isolation
) / sizeof(ipvlan_isolation
[0]); i
++) {
621 if (!strequal(ipvlan_isolation
[i
].name
, value
))
624 *flag
= ipvlan_isolation
[i
].flag
;
628 return ret_errno(EINVAL
);
631 char *lxc_ipvlan_flag_to_isolation(int flag
)
633 for (size_t i
= 0; i
< sizeof(ipvlan_isolation
) / sizeof(ipvlan_isolation
[0]); i
++) {
634 if (ipvlan_isolation
[i
].flag
!= flag
)
637 return ipvlan_isolation
[i
].name
;
640 return ret_set_errno(NULL
, EINVAL
);
643 int set_config_string_item(char **conf_item
, const char *value
)
647 if (lxc_config_value_empty(value
)) {
648 free_disarm(*conf_item
);
652 new_value
= strdup(value
);
654 return log_error_errno(-ENOMEM
, ENOMEM
, "Failed to duplicate string \"%s\"", value
);
656 free_move_ptr(*conf_item
, new_value
);
660 int set_config_string_item_max(char **conf_item
, const char *value
, size_t max
)
662 if (strlen(value
) >= max
)
663 return log_error_errno(-ENAMETOOLONG
, ENAMETOOLONG
, "%s is too long (>= %lu)", value
, (unsigned long)max
);
665 return set_config_string_item(conf_item
, value
);
668 int set_config_path_item(char **conf_item
, const char *value
)
670 __do_free
char *normalized
= NULL
;
672 normalized
= lxc_deslashify(value
);
674 return syserror_set(-ENOMEM
, "Failed to normalize path config item");
676 return set_config_string_item_max(conf_item
, normalized
, PATH_MAX
);
679 int set_config_bool_item(bool *conf_item
, const char *value
, bool empty_conf_action
)
682 unsigned int val
= 0;
684 if (lxc_config_value_empty(value
)) {
685 *conf_item
= empty_conf_action
;
689 ret
= lxc_safe_uint(value
, &val
);
702 return ret_errno(EINVAL
);
705 int config_ip_prefix(struct in_addr
*addr
)
707 if (IN_CLASSA(addr
->s_addr
))
708 return 32 - IN_CLASSA_NSHIFT
;
710 if (IN_CLASSB(addr
->s_addr
))
711 return 32 - IN_CLASSB_NSHIFT
;
713 if (IN_CLASSC(addr
->s_addr
))
714 return 32 - IN_CLASSC_NSHIFT
;
719 int network_ifname(char *valuep
, const char *value
, size_t size
)
723 if (!valuep
|| !value
)
724 return ret_errno(EINVAL
);
726 retlen
= strlcpy(valuep
, value
, size
);
728 ERROR("Network device name \"%s\" is too long (>= %zu)", value
, size
);
733 bool lxc_config_net_is_hwaddr(const char *line
)
738 if (!strnequal(line
, "lxc.net", 7))
741 if (strnequal(line
, "lxc.net.hwaddr", 14))
744 if (strnequal(line
, "lxc.network.hwaddr", 18))
747 if (sscanf(line
, "lxc.net.%u.%6s", &index
, tmp
) == 2 ||
748 sscanf(line
, "lxc.network.%u.%6s", &index
, tmp
) == 2)
749 return strnequal(tmp
, "hwaddr", 6);
754 void rand_complete_hwaddr(char *hwaddr
)
756 const char hex
[] = "0123456789abcdef";
761 seed
= randseed(false);
764 (void)randseed(true);
767 while (*curs
!= '\0' && *curs
!= '\n') {
768 if (*curs
== 'x' || *curs
== 'X') {
769 if (curs
- hwaddr
== 1) {
770 /* ensure address is unicast */
772 *curs
= hex
[rand_r(&seed
) & 0x0E];
774 *curs
= hex
[rand_r(&seed
) & 0x0F];
776 *curs
= hex
[rand() & 0x0E];
778 *curs
= hex
[rand() & 0x0F];
786 bool new_hwaddr(char *hwaddr
)
792 seed
= randseed(false);
794 ret
= strnprintf(hwaddr
, 18, "00:16:3e:%02x:%02x:%02x", rand_r(&seed
) % 255,
795 rand_r(&seed
) % 255, rand_r(&seed
) % 255);
798 (void)randseed(true);
800 ret
= strnprintf(hwaddr
, 18, "00:16:3e:%02x:%02x:%02x", rand() % 255,
801 rand() % 255, rand() % 255);
804 return log_error_errno(false, EIO
, "Failed to call strnprintf()");
809 int lxc_get_conf_str(char *retv
, int inlen
, const char *value
)
816 value_len
= strlen(value
);
817 if (retv
&& inlen
>= value_len
+ 1)
818 memcpy(retv
, value
, value_len
+ 1);
823 int lxc_get_conf_bool(struct lxc_conf
*c
, char *retv
, int inlen
, bool v
)
831 memset(retv
, 0, inlen
);
833 strprint(retv
, inlen
, "%d", v
);
838 int lxc_get_conf_int(struct lxc_conf
*c
, char *retv
, int inlen
, int v
)
846 memset(retv
, 0, inlen
);
848 strprint(retv
, inlen
, "%d", v
);
853 int lxc_get_conf_size_t(struct lxc_conf
*c
, char *retv
, int inlen
, size_t v
)
861 memset(retv
, 0, inlen
);
863 strprint(retv
, inlen
, "%zu", v
);
868 int lxc_get_conf_uint64(struct lxc_conf
*c
, char *retv
, int inlen
, uint64_t v
)
876 memset(retv
, 0, inlen
);
878 strprint(retv
, inlen
, "%"PRIu64
, v
);
883 static int lxc_container_name_to_pid(const char *lxcname_or_pid
,
890 pid
= strtol(lxcname_or_pid
, &err
, 10);
891 if (*err
!= '\0' || pid
< 1) {
892 __put_lxc_container
struct lxc_container
*c
= NULL
;
894 c
= lxc_container_new(lxcname_or_pid
, lxcpath
);
896 return log_error_errno(-EINVAL
, EINVAL
, "\"%s\" is not a valid pid nor a container name", lxcname_or_pid
);
898 if (!c
->may_control(c
))
899 return log_error_errno(-EPERM
, EPERM
, "Insufficient privileges to control container \"%s\"", c
->name
);
901 pid
= c
->init_pid(c
);
903 return log_error_errno(-EINVAL
, EINVAL
, "Container \"%s\" is not running", c
->name
);
909 return log_error_errno(-errno
, errno
, "Failed to send signal to pid %d", (int)pid
);
914 int lxc_inherit_namespace(const char *nsfd_path
, const char *lxcpath
,
915 const char *namespace)
917 __do_free
char *dup
= NULL
;
921 if (nsfd_path
[0] == '/') {
922 return open(nsfd_path
, O_RDONLY
| O_CLOEXEC
);
925 lastslash
= strrchr(nsfd_path
, '/');
927 dup
= strdup(nsfd_path
);
929 return ret_errno(ENOMEM
);
931 dup
[lastslash
- nsfd_path
] = '\0';
932 lxcpath
= lastslash
+ 1;
933 nsfd_path
= lastslash
+ 1;
936 pid
= lxc_container_name_to_pid(nsfd_path
, lxcpath
);
940 fd
= lxc_preserve_ns(pid
, namespace);
952 static const struct signame signames
[] = {
985 { SIGSTKFLT
, "STKFLT" },
1000 { SIGVTALRM
, "VTALRM" },
1003 { SIGPROF
, "PROF" },
1006 { SIGWINCH
, "WINCH" },
1012 { SIGPOLL
, "POLL" },
1015 { SIGINFO
, "INFO" },
1018 { SIGLOST
, "LOST" },
1024 { SIGUNUSED
, "UNUSED" },
1031 static int sig_num(const char *sig
)
1034 unsigned int signum
;
1036 ret
= lxc_safe_uint(sig
, &signum
);
1043 static int rt_sig_num(const char *signame
)
1048 if (is_empty_string(signame
))
1049 return ret_errno(EINVAL
);
1051 if (strncasecmp(signame
, "max-", STRLITERALLEN("max-")) == 0) {
1053 signame
+= STRLITERALLEN("max-");
1054 } else if (strncasecmp(signame
, "min+", STRLITERALLEN("min+")) == 0) {
1056 signame
+= STRLITERALLEN("min+");
1058 return ret_errno(EINVAL
);
1061 if (is_empty_string(signame
) || !isdigit(*signame
))
1062 return ret_errno(EINVAL
);
1064 sig_n
= sig_num(signame
);
1065 if (sig_n
< 0 || sig_n
> SIGRTMAX
- SIGRTMIN
)
1066 return ret_errno(EINVAL
);
1069 sig_n
= SIGRTMAX
- sig_n
;
1071 sig_n
= SIGRTMIN
+ sig_n
;
1076 int sig_parse(const char *signame
)
1078 if (isdigit(*signame
))
1079 return sig_num(signame
);
1081 if (strncasecmp(signame
, "sig", STRLITERALLEN("sig")) == 0) {
1082 signame
+= STRLITERALLEN("sig");
1083 if (strncasecmp(signame
, "rt", STRLITERALLEN("rt")) == 0)
1084 return rt_sig_num(signame
+ STRLITERALLEN("rt"));
1086 for (size_t n
= 0; n
< ARRAY_SIZE(signames
); n
++)
1087 if (strcasecmp(signames
[n
].name
, signame
) == 0)
1088 return signames
[n
].num
;
1091 return ret_errno(EINVAL
);