]>
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"
21 #include "memory_utils.h"
30 lxc_log_define(confile_utils
, lxc
);
32 int parse_idmaps(const char *idmap
, char *type
, unsigned long *nsid
,
33 unsigned long *hostid
, unsigned long *range
)
35 __do_free
char *dup
= NULL
;
37 unsigned long tmp_hostid
, tmp_nsid
, tmp_range
;
41 /* Duplicate string. */
44 return ret_errno(ENOMEM
);
46 /* A prototypical idmap entry would be: "u 1000 1000000 65536" */
51 slide
+= strspn(slide
, " \t\r");
52 if (slide
!= window
&& *slide
== '\0')
53 return ret_errno(EINVAL
);
56 if (*slide
!= 'u' && *slide
!= 'g')
57 return log_error_errno(-EINVAL
, EINVAL
, "Invalid id mapping type: %c", *slide
);
62 /* move beyond type */
66 /* Validate that only whitespace follows. */
67 slide
+= strspn(slide
, " \t\r");
68 /* There must be whitespace. */
70 return ret_errno(EINVAL
);
72 /* Mark beginning of nsid. */
74 /* Validate that non-whitespace follows. */
75 slide
+= strcspn(slide
, " \t\r");
76 /* There must be non-whitespace. */
77 if (slide
== window
|| *slide
== '\0')
78 return ret_errno(EINVAL
);
79 /* Mark end of nsid. */
83 ret
= lxc_safe_ulong(window
, &tmp_nsid
);
85 return log_error_errno(ret
, errno
, "Failed to parse nsid: %s", window
);
89 /* Validate that only whitespace follows. */
90 slide
+= strspn(slide
, " \t\r");
91 /* If there was only one whitespace then we whiped it with our \0 above.
92 * So only ensure that we're not at the end of the string.
95 return ret_errno(EINVAL
);
97 /* Mark beginning of hostid. */
99 /* Validate that non-whitespace follows. */
100 slide
+= strcspn(slide
, " \t\r");
101 /* There must be non-whitespace. */
102 if (slide
== window
|| *slide
== '\0')
103 return ret_errno(EINVAL
);
104 /* Mark end of nsid. */
108 ret
= lxc_safe_ulong(window
, &tmp_hostid
);
110 return log_error_errno(ret
, errno
, "Failed to parse hostid: %s", window
);
112 /* Move beyond \0. */
114 /* Validate that only whitespace follows. */
115 slide
+= strspn(slide
, " \t\r");
116 /* If there was only one whitespace then we whiped it with our \0 above.
117 * So only ensure that we're not at the end of the string.
120 return ret_errno(EINVAL
);
122 /* Mark beginning of range. */
124 /* Validate that non-whitespace follows. */
125 slide
+= strcspn(slide
, " \t\r");
126 /* There must be non-whitespace. */
128 return ret_errno(EINVAL
);
130 /* The range is the last valid entry we expect. So make sure that there
131 * is no trailing garbage and if there is, error out.
133 if (*(slide
+ strspn(slide
, " \t\r\n")) != '\0')
134 return ret_errno(EINVAL
);
136 /* Mark end of range. */
140 ret
= lxc_safe_ulong(window
, &tmp_range
);
142 return log_error_errno(ret
, errno
, "Failed to parse id mapping range: %s", window
);
146 *hostid
= tmp_hostid
;
149 /* Yay, we survived. */
153 bool lxc_config_value_empty(const char *value
)
155 if (value
&& strlen(value
) > 0)
161 static struct lxc_netdev
*lxc_network_add(struct list_head
*head
, int idx
, bool tail
)
163 __do_free
struct lxc_netdev
*netdev
= NULL
;
165 /* network does not exist */
166 netdev
= zalloc(sizeof(*netdev
));
168 return ret_set_errno(NULL
, ENOMEM
);
170 INIT_LIST_HEAD(&netdev
->ipv4_addresses
);
171 INIT_LIST_HEAD(&netdev
->ipv6_addresses
);
173 /* give network a unique index */
177 list_add_tail(&netdev
->head
, head
);
179 list_add(&netdev
->head
, head
);
181 return move_ptr(netdev
);
184 /* Takes care of finding the correct netdev struct in the networks list or
185 * allocates a new one if it couldn't be found.
187 struct lxc_netdev
*lxc_get_netdev_by_idx(struct lxc_conf
*conf
,
188 unsigned int idx
, bool allocate
)
190 struct list_head
*netdevs
= &conf
->netdevs
;
191 struct list_head
*head
= netdevs
;
192 struct lxc_netdev
*netdev
;
195 if (!list_empty(netdevs
)) {
196 list_for_each_entry(netdev
, netdevs
, head
) {
197 /* found network device */
198 if (netdev
->idx
== idx
)
201 if (netdev
->idx
> idx
) {
202 head
= &netdev
->head
;
209 return lxc_network_add(head
, idx
, true);
214 void lxc_log_configured_netdevs(const struct lxc_conf
*conf
)
216 struct lxc_netdev
*netdev
;
217 const struct list_head
*netdevs
= &conf
->netdevs
;
219 if (!lxc_log_trace())
222 if (list_empty(netdevs
)) {
223 TRACE("container has no networks configured");
227 list_for_each_entry(netdev
, netdevs
, head
) {
228 struct lxc_list
*cur
, *next
;
229 struct lxc_inetdev
*inet4dev
;
230 struct lxc_inet6dev
*inet6dev
;
231 char bufinet4
[INET_ADDRSTRLEN
], bufinet6
[INET6_ADDRSTRLEN
];
233 TRACE("index: %zd", netdev
->idx
);
234 TRACE("ifindex: %d", netdev
->ifindex
);
236 switch (netdev
->type
) {
239 TRACE("veth mode: %d", netdev
->priv
.veth_attr
.mode
);
241 if (netdev
->priv
.veth_attr
.pair
[0] != '\0')
242 TRACE("veth pair: %s",
243 netdev
->priv
.veth_attr
.pair
);
245 if (netdev
->priv
.veth_attr
.veth1
[0] != '\0')
247 netdev
->priv
.veth_attr
.veth1
);
249 if (netdev
->priv
.veth_attr
.ifindex
> 0)
250 TRACE("host side ifindex for veth device: %d",
251 netdev
->priv
.veth_attr
.ifindex
);
253 if (netdev
->priv
.veth_attr
.vlan_id_set
)
254 TRACE("veth vlan id: %d", netdev
->priv
.veth_attr
.vlan_id
);
256 lxc_list_for_each_safe(cur
, &netdev
->priv
.veth_attr
.vlan_tagged_ids
, next
) {
257 unsigned short vlan_tagged_id
= PTR_TO_USHORT(cur
->elem
);
258 TRACE("veth vlan tagged id: %u", vlan_tagged_id
);
262 case LXC_NET_MACVLAN
:
263 TRACE("type: macvlan");
265 if (netdev
->priv
.macvlan_attr
.mode
> 0) {
268 mode
= lxc_macvlan_flag_to_mode(
269 netdev
->priv
.macvlan_attr
.mode
);
270 TRACE("macvlan mode: %s",
271 mode
? mode
: "(invalid mode)");
275 TRACE("type: ipvlan");
278 mode
= lxc_ipvlan_flag_to_mode(netdev
->priv
.ipvlan_attr
.mode
);
279 TRACE("ipvlan mode: %s", mode
? mode
: "(invalid mode)");
282 isolation
= lxc_ipvlan_flag_to_isolation(netdev
->priv
.ipvlan_attr
.isolation
);
283 TRACE("ipvlan isolation: %s", isolation
? isolation
: "(invalid isolation)");
287 TRACE("vlan id: %d", netdev
->priv
.vlan_attr
.vid
);
292 if (netdev
->priv
.phys_attr
.ifindex
> 0)
293 TRACE("host side ifindex for phys device: %d",
294 netdev
->priv
.phys_attr
.ifindex
);
297 TRACE("type: empty");
303 ERROR("Invalid network type %d", netdev
->type
);
307 if (netdev
->type
!= LXC_NET_EMPTY
) {
309 netdev
->flags
== IFF_UP
? "up" : "none");
311 if (netdev
->link
[0] != '\0')
312 TRACE("link: %s", netdev
->link
);
314 /* l2proxy only used when link is specified */
315 if (netdev
->link
[0] != '\0')
316 TRACE("l2proxy: %s", netdev
->l2proxy
? "true" : "false");
318 if (netdev
->name
[0] != '\0')
319 TRACE("name: %s", netdev
->name
);
322 TRACE("hwaddr: %s", netdev
->hwaddr
);
325 TRACE("mtu: %s", netdev
->mtu
);
327 if (netdev
->upscript
)
328 TRACE("upscript: %s", netdev
->upscript
);
330 if (netdev
->downscript
)
331 TRACE("downscript: %s", netdev
->downscript
);
333 TRACE("ipv4 gateway auto: %s",
334 netdev
->ipv4_gateway_auto
? "true" : "false");
336 TRACE("ipv4 gateway dev: %s",
337 netdev
->ipv4_gateway_dev
? "true" : "false");
339 if (netdev
->ipv4_gateway
) {
340 inet_ntop(AF_INET
, netdev
->ipv4_gateway
,
341 bufinet4
, sizeof(bufinet4
));
342 TRACE("ipv4 gateway: %s", bufinet4
);
345 list_for_each_entry(inet4dev
, &netdev
->ipv4_addresses
, head
) {
346 inet_ntop(AF_INET
, &inet4dev
->addr
, bufinet4
,
348 TRACE("ipv4 addr: %s", bufinet4
);
351 TRACE("ipv6 gateway auto: %s",
352 netdev
->ipv6_gateway_auto
? "true" : "false");
354 TRACE("ipv6 gateway dev: %s",
355 netdev
->ipv6_gateway_dev
? "true" : "false");
357 if (netdev
->ipv6_gateway
) {
358 inet_ntop(AF_INET6
, netdev
->ipv6_gateway
,
359 bufinet6
, sizeof(bufinet6
));
360 TRACE("ipv6 gateway: %s", bufinet6
);
363 list_for_each_entry(inet6dev
, &netdev
->ipv6_addresses
, head
) {
364 inet_ntop(AF_INET6
, &inet6dev
->addr
, bufinet6
,
366 TRACE("ipv6 addr: %s", bufinet6
);
369 if (netdev
->type
== LXC_NET_VETH
) {
370 list_for_each_entry(inet4dev
, &netdev
->priv
.veth_attr
.ipv4_routes
, head
) {
371 if (!inet_ntop(AF_INET
, &inet4dev
->addr
, bufinet4
, sizeof(bufinet4
))) {
372 ERROR("Invalid ipv4 veth route");
376 TRACE("ipv4 veth route: %s/%u", bufinet4
, inet4dev
->prefix
);
379 list_for_each_entry(inet6dev
, &netdev
->priv
.veth_attr
.ipv6_routes
, head
) {
380 if (!inet_ntop(AF_INET6
, &inet6dev
->addr
, bufinet6
, sizeof(bufinet6
))) {
381 ERROR("Invalid ipv6 veth route");
385 TRACE("ipv6 veth route: %s/%u", bufinet6
, inet6dev
->prefix
);
392 void lxc_clear_netdev(struct lxc_netdev
*netdev
)
394 struct lxc_list
*cur
, *next
;
395 struct list_head head
;
396 struct lxc_inetdev
*inetdev
, *ninetdev
;
397 struct lxc_inet6dev
*inet6dev
, *ninet6dev
;
405 free_disarm(netdev
->upscript
);
406 free_disarm(netdev
->downscript
);
407 free_disarm(netdev
->hwaddr
);
408 free_disarm(netdev
->mtu
);
410 free_disarm(netdev
->ipv4_gateway
);
411 list_for_each_entry_safe(inetdev
, ninetdev
, &netdev
->ipv4_addresses
, head
) {
412 list_del(&inetdev
->head
);
416 free_disarm(netdev
->ipv6_gateway
);
417 list_for_each_entry_safe(inet6dev
, ninet6dev
, &netdev
->ipv6_addresses
, head
) {
418 list_del(&inet6dev
->head
);
422 if (netdev
->type
== LXC_NET_VETH
) {
423 list_for_each_entry_safe(inetdev
, ninetdev
, &netdev
->priv
.veth_attr
.ipv4_routes
, head
) {
424 list_del(&inetdev
->head
);
428 list_for_each_entry_safe(inet6dev
, ninet6dev
, &netdev
->priv
.veth_attr
.ipv6_routes
, head
) {
429 list_del(&inet6dev
->head
);
433 lxc_list_for_each_safe(cur
, &netdev
->priv
.veth_attr
.vlan_tagged_ids
, next
) {
440 memset(netdev
, 0, sizeof(struct lxc_netdev
));
442 INIT_LIST_HEAD(&netdev
->ipv4_addresses
);
443 INIT_LIST_HEAD(&netdev
->ipv6_addresses
);
448 static void lxc_free_netdev(struct lxc_netdev
*netdev
)
451 lxc_clear_netdev(netdev
);
456 bool lxc_remove_nic_by_idx(struct lxc_conf
*conf
, unsigned int idx
)
458 struct lxc_netdev
*netdev
;
460 if (list_empty(&conf
->netdevs
))
463 list_for_each_entry(netdev
, &conf
->netdevs
, head
) {
464 if (netdev
->idx
!= idx
)
467 list_del(&netdev
->head
);
468 lxc_free_netdev(netdev
);
475 void lxc_free_networks(struct lxc_conf
*conf
)
477 struct lxc_netdev
*netdev
, *n
;
479 if (list_empty(&conf
->netdevs
))
482 list_for_each_entry_safe(netdev
, n
, &conf
->netdevs
, head
) {
483 list_del(&netdev
->head
);
484 lxc_free_netdev(netdev
);
487 /* prevent segfaults */
488 INIT_LIST_HEAD(&conf
->netdevs
);
491 static struct lxc_veth_mode
{
495 { "bridge", VETH_MODE_BRIDGE
},
496 { "router", VETH_MODE_ROUTER
},
499 int lxc_veth_mode_to_flag(int *mode
, const char *value
)
501 for (size_t i
= 0; i
< sizeof(veth_mode
) / sizeof(veth_mode
[0]); i
++) {
502 if (!strequal(veth_mode
[i
].name
, value
))
505 *mode
= veth_mode
[i
].mode
;
509 return ret_errno(EINVAL
);
512 char *lxc_veth_flag_to_mode(int mode
)
514 for (size_t i
= 0; i
< sizeof(veth_mode
) / sizeof(veth_mode
[0]); i
++) {
515 if (veth_mode
[i
].mode
!= mode
)
518 return veth_mode
[i
].name
;
521 return ret_set_errno(NULL
, EINVAL
);
524 static struct lxc_macvlan_mode
{
528 { "private", MACVLAN_MODE_PRIVATE
},
529 { "vepa", MACVLAN_MODE_VEPA
},
530 { "bridge", MACVLAN_MODE_BRIDGE
},
531 { "passthru", MACVLAN_MODE_PASSTHRU
},
534 int lxc_macvlan_mode_to_flag(int *mode
, const char *value
)
536 for (size_t i
= 0; i
< sizeof(macvlan_mode
) / sizeof(macvlan_mode
[0]); i
++) {
537 if (!strequal(macvlan_mode
[i
].name
, value
))
540 *mode
= macvlan_mode
[i
].mode
;
544 return ret_errno(EINVAL
);
547 char *lxc_macvlan_flag_to_mode(int mode
)
549 for (size_t i
= 0; i
< sizeof(macvlan_mode
) / sizeof(macvlan_mode
[0]); i
++) {
550 if (macvlan_mode
[i
].mode
!= mode
)
553 return macvlan_mode
[i
].name
;
556 return ret_set_errno(NULL
, EINVAL
);
559 static struct lxc_ipvlan_mode
{
563 { "l3", IPVLAN_MODE_L3
},
564 { "l3s", IPVLAN_MODE_L3S
},
565 { "l2", IPVLAN_MODE_L2
},
568 int lxc_ipvlan_mode_to_flag(int *mode
, const char *value
)
570 for (size_t i
= 0; i
< sizeof(ipvlan_mode
) / sizeof(ipvlan_mode
[0]); i
++) {
571 if (!strequal(ipvlan_mode
[i
].name
, value
))
574 *mode
= ipvlan_mode
[i
].mode
;
578 return ret_errno(EINVAL
);
581 char *lxc_ipvlan_flag_to_mode(int mode
)
583 for (size_t i
= 0; i
< sizeof(ipvlan_mode
) / sizeof(ipvlan_mode
[0]); i
++) {
584 if (ipvlan_mode
[i
].mode
!= mode
)
587 return ipvlan_mode
[i
].name
;
590 return ret_set_errno(NULL
, EINVAL
);
593 static struct lxc_ipvlan_isolation
{
596 } ipvlan_isolation
[] = {
597 { "bridge", IPVLAN_ISOLATION_BRIDGE
},
598 { "private", IPVLAN_ISOLATION_PRIVATE
},
599 { "vepa", IPVLAN_ISOLATION_VEPA
},
602 int lxc_ipvlan_isolation_to_flag(int *flag
, const char *value
)
604 for (size_t i
= 0; i
< sizeof(ipvlan_isolation
) / sizeof(ipvlan_isolation
[0]); i
++) {
605 if (!strequal(ipvlan_isolation
[i
].name
, value
))
608 *flag
= ipvlan_isolation
[i
].flag
;
612 return ret_errno(EINVAL
);
615 char *lxc_ipvlan_flag_to_isolation(int flag
)
617 for (size_t i
= 0; i
< sizeof(ipvlan_isolation
) / sizeof(ipvlan_isolation
[0]); i
++) {
618 if (ipvlan_isolation
[i
].flag
!= flag
)
621 return ipvlan_isolation
[i
].name
;
624 return ret_set_errno(NULL
, EINVAL
);
627 int set_config_string_item(char **conf_item
, const char *value
)
631 if (lxc_config_value_empty(value
)) {
632 free_disarm(*conf_item
);
636 new_value
= strdup(value
);
638 return log_error_errno(-ENOMEM
, ENOMEM
, "Failed to duplicate string \"%s\"", value
);
640 free_move_ptr(*conf_item
, new_value
);
644 int set_config_string_item_max(char **conf_item
, const char *value
, size_t max
)
646 if (strlen(value
) >= max
)
647 return log_error_errno(-ENAMETOOLONG
, ENAMETOOLONG
, "%s is too long (>= %lu)", value
, (unsigned long)max
);
649 return set_config_string_item(conf_item
, value
);
652 int set_config_path_item(char **conf_item
, const char *value
)
654 __do_free
char *valdup
= NULL
;
656 valdup
= lxc_path_simplify(value
);
660 return set_config_string_item_max(conf_item
, valdup
, PATH_MAX
);
663 int set_config_bool_item(bool *conf_item
, const char *value
, bool empty_conf_action
)
666 unsigned int val
= 0;
668 if (lxc_config_value_empty(value
)) {
669 *conf_item
= empty_conf_action
;
673 ret
= lxc_safe_uint(value
, &val
);
686 return ret_errno(EINVAL
);
689 int config_ip_prefix(struct in_addr
*addr
)
691 if (IN_CLASSA(addr
->s_addr
))
692 return 32 - IN_CLASSA_NSHIFT
;
694 if (IN_CLASSB(addr
->s_addr
))
695 return 32 - IN_CLASSB_NSHIFT
;
697 if (IN_CLASSC(addr
->s_addr
))
698 return 32 - IN_CLASSC_NSHIFT
;
703 int network_ifname(char *valuep
, const char *value
, size_t size
)
707 if (!valuep
|| !value
)
708 return ret_errno(EINVAL
);
710 retlen
= strlcpy(valuep
, value
, size
);
712 ERROR("Network device name \"%s\" is too long (>= %zu)", value
, size
);
717 bool lxc_config_net_is_hwaddr(const char *line
)
722 if (!strnequal(line
, "lxc.net", 7))
725 if (strnequal(line
, "lxc.net.hwaddr", 14))
728 if (strnequal(line
, "lxc.network.hwaddr", 18))
731 if (sscanf(line
, "lxc.net.%u.%6s", &index
, tmp
) == 2 ||
732 sscanf(line
, "lxc.network.%u.%6s", &index
, tmp
) == 2)
733 return strnequal(tmp
, "hwaddr", 6);
738 void rand_complete_hwaddr(char *hwaddr
)
740 const char hex
[] = "0123456789abcdef";
745 seed
= randseed(false);
748 (void)randseed(true);
751 while (*curs
!= '\0' && *curs
!= '\n') {
752 if (*curs
== 'x' || *curs
== 'X') {
753 if (curs
- hwaddr
== 1) {
754 /* ensure address is unicast */
756 *curs
= hex
[rand_r(&seed
) & 0x0E];
758 *curs
= hex
[rand_r(&seed
) & 0x0F];
760 *curs
= hex
[rand() & 0x0E];
762 *curs
= hex
[rand() & 0x0F];
770 bool new_hwaddr(char *hwaddr
)
776 seed
= randseed(false);
778 ret
= strnprintf(hwaddr
, 18, "00:16:3e:%02x:%02x:%02x", rand_r(&seed
) % 255,
779 rand_r(&seed
) % 255, rand_r(&seed
) % 255);
782 (void)randseed(true);
784 ret
= strnprintf(hwaddr
, 18, "00:16:3e:%02x:%02x:%02x", rand() % 255,
785 rand() % 255, rand() % 255);
788 return log_error_errno(false, EIO
, "Failed to call strnprintf()");
793 int lxc_get_conf_str(char *retv
, int inlen
, const char *value
)
800 value_len
= strlen(value
);
801 if (retv
&& (size_t)inlen
>= value_len
+ 1)
802 memcpy(retv
, value
, value_len
+ 1);
807 int lxc_get_conf_bool(struct lxc_conf
*c
, char *retv
, int inlen
, bool v
)
815 memset(retv
, 0, inlen
);
817 strprint(retv
, inlen
, "%d", v
);
822 int lxc_get_conf_int(struct lxc_conf
*c
, char *retv
, int inlen
, int v
)
830 memset(retv
, 0, inlen
);
832 strprint(retv
, inlen
, "%d", v
);
837 int lxc_get_conf_size_t(struct lxc_conf
*c
, char *retv
, int inlen
, size_t v
)
845 memset(retv
, 0, inlen
);
847 strprint(retv
, inlen
, "%zu", v
);
852 int lxc_get_conf_uint64(struct lxc_conf
*c
, char *retv
, int inlen
, uint64_t v
)
860 memset(retv
, 0, inlen
);
862 strprint(retv
, inlen
, "%"PRIu64
, v
);
867 static int lxc_container_name_to_pid(const char *lxcname_or_pid
,
874 pid
= strtol(lxcname_or_pid
, &err
, 10);
875 if (*err
!= '\0' || pid
< 1) {
876 __put_lxc_container
struct lxc_container
*c
= NULL
;
878 c
= lxc_container_new(lxcname_or_pid
, lxcpath
);
880 return log_error_errno(-EINVAL
, EINVAL
, "\"%s\" is not a valid pid nor a container name", lxcname_or_pid
);
882 if (!c
->may_control(c
))
883 return log_error_errno(-EPERM
, EPERM
, "Insufficient privileges to control container \"%s\"", c
->name
);
885 pid
= c
->init_pid(c
);
887 return log_error_errno(-EINVAL
, EINVAL
, "Container \"%s\" is not running", c
->name
);
893 return log_error_errno(-errno
, errno
, "Failed to send signal to pid %d", (int)pid
);
898 int lxc_inherit_namespace(const char *nsfd_path
, const char *lxcpath
,
899 const char *namespace)
901 __do_free
char *dup
= NULL
;
905 if (nsfd_path
[0] == '/') {
906 return open(nsfd_path
, O_RDONLY
| O_CLOEXEC
);
909 lastslash
= strrchr(nsfd_path
, '/');
911 dup
= strdup(nsfd_path
);
913 return ret_errno(ENOMEM
);
915 dup
[lastslash
- nsfd_path
] = '\0';
916 lxcpath
= lastslash
+ 1;
917 nsfd_path
= lastslash
+ 1;
920 pid
= lxc_container_name_to_pid(nsfd_path
, lxcpath
);
924 fd
= lxc_preserve_ns(pid
, namespace);
936 static const struct signame signames
[] = {
969 { SIGSTKFLT
, "STKFLT" },
984 { SIGVTALRM
, "VTALRM" },
990 { SIGWINCH
, "WINCH" },
1002 { SIGLOST
, "LOST" },
1008 { SIGUNUSED
, "UNUSED" },
1015 static int sig_num(const char *sig
)
1018 unsigned int signum
;
1020 ret
= lxc_safe_uint(sig
, &signum
);
1027 static int rt_sig_num(const char *signame
)
1032 if (is_empty_string(signame
))
1033 return ret_errno(EINVAL
);
1035 if (strncasecmp(signame
, "max-", STRLITERALLEN("max-")) == 0) {
1037 signame
+= STRLITERALLEN("max-");
1038 } else if (strncasecmp(signame
, "min+", STRLITERALLEN("min+")) == 0) {
1040 signame
+= STRLITERALLEN("min+");
1042 return ret_errno(EINVAL
);
1045 if (is_empty_string(signame
) || !isdigit(*signame
))
1046 return ret_errno(EINVAL
);
1048 sig_n
= sig_num(signame
);
1049 if (sig_n
< 0 || sig_n
> SIGRTMAX
- SIGRTMIN
)
1050 return ret_errno(EINVAL
);
1053 sig_n
= SIGRTMAX
- sig_n
;
1055 sig_n
= SIGRTMIN
+ sig_n
;
1060 int sig_parse(const char *signame
)
1062 if (isdigit(*signame
))
1063 return sig_num(signame
);
1065 if (strncasecmp(signame
, "sig", STRLITERALLEN("sig")) == 0) {
1066 signame
+= STRLITERALLEN("sig");
1067 if (strncasecmp(signame
, "rt", STRLITERALLEN("rt")) == 0)
1068 return rt_sig_num(signame
+ STRLITERALLEN("rt"));
1070 for (size_t n
= 0; n
< ARRAY_SIZE(signames
); n
++)
1071 if (strcasecmp(signames
[n
].name
, signame
) == 0)
1072 return signames
[n
].num
;
1075 return ret_errno(EINVAL
);