2 This file is part of systemd.
4 Copyright 2014 Lennart Poettering
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
24 #include "sd-device.h"
27 #include "sd-netlink.h"
28 #include "sd-network.h"
30 #include "alloc-util.h"
31 #include "arphrd-list.h"
32 #include "device-util.h"
33 #include "ether-addr-util.h"
35 #include "hwdb-util.h"
37 #include "local-addresses.h"
38 #include "locale-util.h"
39 #include "netlink-util.h"
41 #include "parse-util.h"
42 #include "socket-util.h"
43 #include "sparse-endian.h"
44 #include "stdio-util.h"
45 #include "string-table.h"
46 #include "string-util.h"
48 #include "terminal-util.h"
52 static bool arg_no_pager
= false;
53 static bool arg_legend
= true;
54 static bool arg_all
= false;
56 static void pager_open_if_enabled(void) {
64 static int link_get_type_string(unsigned short iftype
, sd_device
*d
, char **ret
) {
70 if (iftype
== ARPHRD_ETHER
&& d
) {
71 const char *devtype
= NULL
, *id
= NULL
;
72 /* WLANs have iftype ARPHRD_ETHER, but we want
73 * to show a more useful type string for
76 (void)sd_device_get_devtype(d
, &devtype
);
78 if (streq_ptr(devtype
, "wlan"))
80 else if (streq_ptr(devtype
, "wwan"))
93 t
= arphrd_to_name(iftype
);
109 typedef struct LinkInfo
{
112 unsigned short iftype
;
115 static int link_info_compare(const void *a
, const void *b
) {
116 const LinkInfo
*x
= a
, *y
= b
;
118 return x
->ifindex
- y
->ifindex
;
121 static int decode_and_sort_links(sd_netlink_message
*m
, LinkInfo
**ret
) {
122 _cleanup_free_ LinkInfo
*links
= NULL
;
123 size_t size
= 0, c
= 0;
124 sd_netlink_message
*i
;
127 for (i
= m
; i
; i
= sd_netlink_message_next(i
)) {
129 unsigned short iftype
;
133 r
= sd_netlink_message_get_type(i
, &type
);
137 if (type
!= RTM_NEWLINK
)
140 r
= sd_rtnl_message_link_get_ifindex(i
, &ifindex
);
144 r
= sd_netlink_message_read_string(i
, IFLA_IFNAME
, &name
);
148 r
= sd_rtnl_message_link_get_type(i
, &iftype
);
152 if (!GREEDY_REALLOC(links
, size
, c
+1))
155 links
[c
].name
= name
;
156 links
[c
].ifindex
= ifindex
;
157 links
[c
].iftype
= iftype
;
161 qsort_safe(links
, c
, sizeof(LinkInfo
), link_info_compare
);
169 static void operational_state_to_color(const char *state
, const char **on
, const char **off
) {
173 if (streq_ptr(state
, "routable")) {
174 *on
= ansi_highlight_green();
175 *off
= ansi_normal();
176 } else if (streq_ptr(state
, "degraded")) {
177 *on
= ansi_highlight_yellow();
178 *off
= ansi_normal();
183 static void setup_state_to_color(const char *state
, const char **on
, const char **off
) {
187 if (streq_ptr(state
, "configured")) {
188 *on
= ansi_highlight_green();
189 *off
= ansi_normal();
190 } else if (streq_ptr(state
, "configuring")) {
191 *on
= ansi_highlight_yellow();
192 *off
= ansi_normal();
193 } else if (streq_ptr(state
, "failed") || streq_ptr(state
, "linger")) {
194 *on
= ansi_highlight_red();
195 *off
= ansi_normal();
200 static int list_links(int argc
, char *argv
[], void *userdata
) {
201 _cleanup_(sd_netlink_message_unrefp
) sd_netlink_message
*req
= NULL
, *reply
= NULL
;
202 _cleanup_(sd_netlink_unrefp
) sd_netlink
*rtnl
= NULL
;
203 _cleanup_free_ LinkInfo
*links
= NULL
;
206 pager_open_if_enabled();
208 r
= sd_netlink_open(&rtnl
);
210 return log_error_errno(r
, "Failed to connect to netlink: %m");
212 r
= sd_rtnl_message_new_link(rtnl
, &req
, RTM_GETLINK
, 0);
214 return rtnl_log_create_error(r
);
216 r
= sd_netlink_message_request_dump(req
, true);
218 return rtnl_log_create_error(r
);
220 r
= sd_netlink_call(rtnl
, req
, 0, &reply
);
222 return log_error_errno(r
, "Failed to enumerate links: %m");
225 printf("%3s %-16s %-18s %-11s %-10s\n", "IDX", "LINK", "TYPE", "OPERATIONAL", "SETUP");
227 c
= decode_and_sort_links(reply
, &links
);
229 return rtnl_log_parse_error(c
);
231 for (i
= 0; i
< c
; i
++) {
232 _cleanup_free_
char *setup_state
= NULL
, *operational_state
= NULL
;
233 _cleanup_(sd_device_unrefp
) sd_device
*d
= NULL
;
234 const char *on_color_operational
, *off_color_operational
,
235 *on_color_setup
, *off_color_setup
;
236 char devid
[2 + DECIMAL_STR_MAX(int)];
237 _cleanup_free_
char *t
= NULL
;
239 sd_network_link_get_operational_state(links
[i
].ifindex
, &operational_state
);
240 operational_state_to_color(operational_state
, &on_color_operational
, &off_color_operational
);
242 sd_network_link_get_setup_state(links
[i
].ifindex
, &setup_state
);
243 setup_state_to_color(setup_state
, &on_color_setup
, &off_color_setup
);
245 sprintf(devid
, "n%i", links
[i
].ifindex
);
246 (void)sd_device_new_from_device_id(&d
, devid
);
248 link_get_type_string(links
[i
].iftype
, d
, &t
);
250 printf("%3i %-16s %-18s %s%-11s%s %s%-10s%s\n",
251 links
[i
].ifindex
, links
[i
].name
, strna(t
),
252 on_color_operational
, strna(operational_state
), off_color_operational
,
253 on_color_setup
, strna(setup_state
), off_color_setup
);
257 printf("\n%i links listed.\n", c
);
262 /* IEEE Organizationally Unique Identifier vendor string */
263 static int ieee_oui(sd_hwdb
*hwdb
, struct ether_addr
*mac
, char **ret
) {
264 const char *description
;
265 char modalias
[strlen("OUI:XXYYXXYYXXYY") + 1], *desc
;
276 /* skip commonly misused 00:00:00 (Xerox) prefix */
277 if (memcmp(mac
, "\0\0\0", 3) == 0)
280 xsprintf(modalias
, "OUI:" ETHER_ADDR_FORMAT_STR
,
281 ETHER_ADDR_FORMAT_VAL(*mac
));
283 r
= sd_hwdb_get(hwdb
, modalias
, "ID_OUI_FROM_DATABASE", &description
);
287 desc
= strdup(description
);
296 static int get_gateway_description(
301 union in_addr_union
*gateway
,
302 char **gateway_description
) {
303 _cleanup_(sd_netlink_message_unrefp
) sd_netlink_message
*req
= NULL
, *reply
= NULL
;
304 sd_netlink_message
*m
;
308 assert(ifindex
>= 0);
309 assert(family
== AF_INET
|| family
== AF_INET6
);
311 assert(gateway_description
);
313 r
= sd_rtnl_message_new_neigh(rtnl
, &req
, RTM_GETNEIGH
, ifindex
, family
);
317 r
= sd_netlink_message_request_dump(req
, true);
321 r
= sd_netlink_call(rtnl
, req
, 0, &reply
);
325 for (m
= reply
; m
; m
= sd_netlink_message_next(m
)) {
326 union in_addr_union gw
= {};
327 struct ether_addr mac
= {};
331 r
= sd_netlink_message_get_errno(m
);
333 log_error_errno(r
, "got error: %m");
337 r
= sd_netlink_message_get_type(m
, &type
);
339 log_error_errno(r
, "could not get type: %m");
343 if (type
!= RTM_NEWNEIGH
) {
344 log_error("type is not RTM_NEWNEIGH");
348 r
= sd_rtnl_message_neigh_get_family(m
, &fam
);
350 log_error_errno(r
, "could not get family: %m");
355 log_error("family is not correct");
359 r
= sd_rtnl_message_neigh_get_ifindex(m
, &ifi
);
361 log_error_errno(r
, "could not get ifindex: %m");
365 if (ifindex
> 0 && ifi
!= ifindex
)
370 r
= sd_netlink_message_read_in_addr(m
, NDA_DST
, &gw
.in
);
376 r
= sd_netlink_message_read_in6_addr(m
, NDA_DST
, &gw
.in6
);
385 if (!in_addr_equal(fam
, &gw
, gateway
))
388 r
= sd_netlink_message_read_ether_addr(m
, NDA_LLADDR
, &mac
);
392 r
= ieee_oui(hwdb
, &mac
, gateway_description
);
402 static int dump_gateways(
407 _cleanup_free_
struct local_address
*local
= NULL
;
410 n
= local_gateways(rtnl
, ifindex
, AF_UNSPEC
, &local
);
414 for (i
= 0; i
< n
; i
++) {
415 _cleanup_free_
char *gateway
= NULL
, *description
= NULL
;
417 r
= in_addr_to_string(local
[i
].family
, &local
[i
].address
, &gateway
);
421 r
= get_gateway_description(rtnl
, hwdb
, local
[i
].ifindex
, local
[i
].family
, &local
[i
].address
, &description
);
423 log_debug_errno(r
, "Could not get description of gateway: %m");
426 (int) strlen(prefix
),
427 i
== 0 ? prefix
: "",
431 printf(" (%s)", description
);
433 /* Show interface name for the entry if we show
434 * entries for all interfaces */
436 char name
[IF_NAMESIZE
+1];
438 if (if_indextoname(local
[i
].ifindex
, name
)) {
439 fputs(" on ", stdout
);
442 printf(" on %%%i", local
[i
].ifindex
);
451 static int dump_addresses(
456 _cleanup_free_
struct local_address
*local
= NULL
;
459 n
= local_addresses(rtnl
, ifindex
, AF_UNSPEC
, &local
);
463 for (i
= 0; i
< n
; i
++) {
464 _cleanup_free_
char *pretty
= NULL
;
466 r
= in_addr_to_string(local
[i
].family
, &local
[i
].address
, &pretty
);
471 (int) strlen(prefix
),
472 i
== 0 ? prefix
: "",
476 char name
[IF_NAMESIZE
+1];
478 if (if_indextoname(local
[i
].ifindex
, name
)) {
479 fputs(" on ", stdout
);
482 printf(" on %%%i", local
[i
].ifindex
);
491 static void dump_list(const char *prefix
, char **l
) {
499 (int) strlen(prefix
),
500 i
== l
? prefix
: "",
505 static int link_status_one(
509 _cleanup_strv_free_
char **dns
= NULL
, **ntp
= NULL
, **search_domains
= NULL
, **route_domains
= NULL
;
510 _cleanup_free_
char *setup_state
= NULL
, *operational_state
= NULL
, *tz
= NULL
;
511 _cleanup_(sd_netlink_message_unrefp
) sd_netlink_message
*req
= NULL
, *reply
= NULL
;
512 _cleanup_(sd_device_unrefp
) sd_device
*d
= NULL
;
513 char devid
[2 + DECIMAL_STR_MAX(int)];
514 _cleanup_free_
char *t
= NULL
, *network
= NULL
;
515 const char *driver
= NULL
, *path
= NULL
, *vendor
= NULL
, *model
= NULL
, *link
= NULL
;
516 const char *on_color_operational
, *off_color_operational
,
517 *on_color_setup
, *off_color_setup
;
518 _cleanup_strv_free_
char **carrier_bound_to
= NULL
;
519 _cleanup_strv_free_
char **carrier_bound_by
= NULL
;
521 unsigned short iftype
;
529 if (parse_ifindex(name
, &ifindex
) >= 0)
530 r
= sd_rtnl_message_new_link(rtnl
, &req
, RTM_GETLINK
, ifindex
);
532 r
= sd_rtnl_message_new_link(rtnl
, &req
, RTM_GETLINK
, 0);
534 return rtnl_log_create_error(r
);
536 r
= sd_netlink_message_append_string(req
, IFLA_IFNAME
, name
);
540 return rtnl_log_create_error(r
);
542 r
= sd_netlink_call(rtnl
, req
, 0, &reply
);
544 return log_error_errno(r
, "Failed to query link: %m");
546 r
= sd_rtnl_message_link_get_ifindex(reply
, &ifindex
);
548 return rtnl_log_parse_error(r
);
550 r
= sd_netlink_message_read_string(reply
, IFLA_IFNAME
, &name
);
552 return rtnl_log_parse_error(r
);
554 r
= sd_rtnl_message_link_get_type(reply
, &iftype
);
556 return rtnl_log_parse_error(r
);
558 have_mac
= sd_netlink_message_read_ether_addr(reply
, IFLA_ADDRESS
, &e
) >= 0;
561 bool all_zeroes
= true;
563 for (p
= (uint8_t*) &e
; p
< (uint8_t*) &e
+ sizeof(e
); p
++)
573 (void) sd_netlink_message_read_u32(reply
, IFLA_MTU
, &mtu
);
575 (void) sd_network_link_get_operational_state(ifindex
, &operational_state
);
576 operational_state_to_color(operational_state
, &on_color_operational
, &off_color_operational
);
578 (void) sd_network_link_get_setup_state(ifindex
, &setup_state
);
579 setup_state_to_color(setup_state
, &on_color_setup
, &off_color_setup
);
581 (void) sd_network_link_get_dns(ifindex
, &dns
);
582 (void) sd_network_link_get_search_domains(ifindex
, &search_domains
);
583 (void) sd_network_link_get_route_domains(ifindex
, &route_domains
);
584 (void) sd_network_link_get_ntp(ifindex
, &ntp
);
586 sprintf(devid
, "n%i", ifindex
);
588 (void) sd_device_new_from_device_id(&d
, devid
);
591 (void) sd_device_get_property_value(d
, "ID_NET_LINK_FILE", &link
);
592 (void) sd_device_get_property_value(d
, "ID_NET_DRIVER", &driver
);
593 (void) sd_device_get_property_value(d
, "ID_PATH", &path
);
595 r
= sd_device_get_property_value(d
, "ID_VENDOR_FROM_DATABASE", &vendor
);
597 (void) sd_device_get_property_value(d
, "ID_VENDOR", &vendor
);
599 r
= sd_device_get_property_value(d
, "ID_MODEL_FROM_DATABASE", &model
);
601 (void) sd_device_get_property_value(d
, "ID_MODEL", &model
);
604 link_get_type_string(iftype
, d
, &t
);
606 sd_network_link_get_network_file(ifindex
, &network
);
608 sd_network_link_get_carrier_bound_to(ifindex
, &carrier_bound_to
);
609 sd_network_link_get_carrier_bound_by(ifindex
, &carrier_bound_by
);
611 printf("%s%s%s %i: %s\n", on_color_operational
, draw_special_char(DRAW_BLACK_CIRCLE
), off_color_operational
, ifindex
, name
);
613 printf(" Link File: %s\n"
614 " Network File: %s\n"
616 " State: %s%s%s (%s%s%s)\n",
620 on_color_operational
, strna(operational_state
), off_color_operational
,
621 on_color_setup
, strna(setup_state
), off_color_setup
);
624 printf(" Path: %s\n", path
);
626 printf(" Driver: %s\n", driver
);
628 printf(" Vendor: %s\n", vendor
);
630 printf(" Model: %s\n", model
);
633 _cleanup_free_
char *description
= NULL
;
634 char ea
[ETHER_ADDR_TO_STRING_MAX
];
636 ieee_oui(hwdb
, &e
, &description
);
639 printf(" HW Address: %s (%s)\n", ether_addr_to_string(&e
, ea
), description
);
641 printf(" HW Address: %s\n", ether_addr_to_string(&e
, ea
));
645 printf(" MTU: %u\n", mtu
);
647 dump_addresses(rtnl
, " Address: ", ifindex
);
648 dump_gateways(rtnl
, hwdb
, " Gateway: ", ifindex
);
650 dump_list(" DNS: ", dns
);
651 dump_list(" Search Domains: ", search_domains
);
652 dump_list(" Route Domains: ", route_domains
);
654 dump_list(" NTP: ", ntp
);
656 dump_list("Carrier Bound To: ", carrier_bound_to
);
657 dump_list("Carrier Bound By: ", carrier_bound_by
);
659 (void) sd_network_link_get_timezone(ifindex
, &tz
);
661 printf(" Time Zone: %s", tz
);
666 static int system_status(sd_netlink
*rtnl
, sd_hwdb
*hwdb
) {
667 _cleanup_free_
char *operational_state
= NULL
;
668 _cleanup_strv_free_
char **dns
= NULL
, **ntp
= NULL
, **search_domains
= NULL
, **route_domains
= NULL
;
669 const char *on_color_operational
, *off_color_operational
;
673 sd_network_get_operational_state(&operational_state
);
674 operational_state_to_color(operational_state
, &on_color_operational
, &off_color_operational
);
676 printf("%s%s%s State: %s%s%s\n",
677 on_color_operational
, draw_special_char(DRAW_BLACK_CIRCLE
), off_color_operational
,
678 on_color_operational
, strna(operational_state
), off_color_operational
);
680 dump_addresses(rtnl
, " Address: ", 0);
681 dump_gateways(rtnl
, hwdb
, " Gateway: ", 0);
683 sd_network_get_dns(&dns
);
684 dump_list(" DNS: ", dns
);
686 sd_network_get_search_domains(&search_domains
);
687 dump_list("Search Domains: ", search_domains
);
689 sd_network_get_route_domains(&route_domains
);
690 dump_list(" Route Domains: ", route_domains
);
692 sd_network_get_ntp(&ntp
);
693 dump_list(" NTP: ", ntp
);
698 static int link_status(int argc
, char *argv
[], void *userdata
) {
699 _cleanup_(sd_hwdb_unrefp
) sd_hwdb
*hwdb
= NULL
;
700 _cleanup_(sd_netlink_unrefp
) sd_netlink
*rtnl
= NULL
;
704 pager_open_if_enabled();
706 r
= sd_netlink_open(&rtnl
);
708 return log_error_errno(r
, "Failed to connect to netlink: %m");
710 r
= sd_hwdb_new(&hwdb
);
712 log_debug_errno(r
, "Failed to open hardware database: %m");
714 if (argc
<= 1 && !arg_all
)
715 return system_status(rtnl
, hwdb
);
718 _cleanup_(sd_netlink_message_unrefp
) sd_netlink_message
*req
= NULL
, *reply
= NULL
;
719 _cleanup_free_ LinkInfo
*links
= NULL
;
722 r
= sd_rtnl_message_new_link(rtnl
, &req
, RTM_GETLINK
, 0);
724 return rtnl_log_create_error(r
);
726 r
= sd_netlink_message_request_dump(req
, true);
728 return rtnl_log_create_error(r
);
730 r
= sd_netlink_call(rtnl
, req
, 0, &reply
);
732 return log_error_errno(r
, "Failed to enumerate links: %m");
734 c
= decode_and_sort_links(reply
, &links
);
736 return rtnl_log_parse_error(c
);
738 for (i
= 0; i
< c
; i
++) {
742 link_status_one(rtnl
, hwdb
, links
[i
].name
);
745 STRV_FOREACH(name
, argv
+ 1) {
746 if (name
!= argv
+ 1)
749 link_status_one(rtnl
, hwdb
, *name
);
756 static char *lldp_capabilities_to_string(uint16_t x
) {
757 static const char characters
[] = {
758 'o', 'p', 'b', 'w', 'r', 't', 'd', 'a', 'c', 's', 'm',
763 ret
= new(char, ELEMENTSOF(characters
) + 1);
767 for (i
= 0; i
< ELEMENTSOF(characters
); i
++)
768 ret
[i
] = (x
& (1U << i
)) ? characters
[i
] : '.';
774 static int link_lldp_status(int argc
, char *argv
[], void *userdata
) {
775 _cleanup_(sd_netlink_message_unrefp
) sd_netlink_message
*req
= NULL
, *reply
= NULL
;
776 _cleanup_(sd_netlink_unrefp
) sd_netlink
*rtnl
= NULL
;
777 _cleanup_free_ LinkInfo
*links
= NULL
;
780 pager_open_if_enabled();
782 r
= sd_netlink_open(&rtnl
);
784 return log_error_errno(r
, "Failed to connect to netlink: %m");
786 r
= sd_rtnl_message_new_link(rtnl
, &req
, RTM_GETLINK
, 0);
788 return rtnl_log_create_error(r
);
790 r
= sd_netlink_message_request_dump(req
, true);
792 return rtnl_log_create_error(r
);
794 r
= sd_netlink_call(rtnl
, req
, 0, &reply
);
796 return log_error_errno(r
, "Failed to enumerate links: %m");
798 c
= decode_and_sort_links(reply
, &links
);
800 return rtnl_log_parse_error(c
);
803 printf("%-16s %-17s %-16s %-11s %-17s %-16s\n",
811 for (i
= j
= 0; i
< c
; i
++) {
812 _cleanup_fclose_
FILE *f
= NULL
;
813 _cleanup_free_
char *p
= NULL
;
815 if (asprintf(&p
, "/run/systemd/netif/lldp/%i", links
[i
].ifindex
) < 0)
823 log_warning_errno(errno
, "Failed to open %s, ignoring: %m", p
);
828 const char *chassis_id
= NULL
, *port_id
= NULL
, *system_name
= NULL
, *port_description
= NULL
, *capabilities
= NULL
;
829 _cleanup_(sd_lldp_neighbor_unrefp
) sd_lldp_neighbor
*n
= NULL
;
830 _cleanup_free_
void *raw
= NULL
;
835 l
= fread(&u
, 1, sizeof(u
), f
);
836 if (l
== 0 && feof(f
)) /* EOF */
838 if (l
!= sizeof(u
)) {
839 log_warning("Premature end of file, ignoring.");
843 raw
= new(uint8_t, le64toh(u
));
847 if (fread(raw
, 1, le64toh(u
), f
) != le64toh(u
)) {
848 log_warning("Premature end of file, ignoring.");
852 r
= sd_lldp_neighbor_from_raw(&n
, raw
, le64toh(u
));
854 log_warning_errno(r
, "Failed to parse LLDP data, ignoring: %m");
858 (void) sd_lldp_neighbor_get_chassis_id_as_string(n
, &chassis_id
);
859 (void) sd_lldp_neighbor_get_port_id_as_string(n
, &port_id
);
860 (void) sd_lldp_neighbor_get_system_name(n
, &system_name
);
861 (void) sd_lldp_neighbor_get_port_description(n
, &port_description
);
863 if (sd_lldp_neighbor_get_enabled_capabilities(n
, &cc
) >= 0)
864 capabilities
= lldp_capabilities_to_string(cc
);
866 printf("%-16s %-17s %-16s %-11s %-17s %-16s\n",
872 strna(port_description
));
877 printf("\nCapabilities:\n"
878 "o - Other; p - Repeater; b - Bridge; w - WLAN Access Point; r - Router;\n"
879 "t - Telephone; d - DOCSIS cable device; a - Station; c - Customer VLAN;\n"
880 "s - Service VLAN, m - Two-port MAC Relay (TPMR)\n\n"
881 "Total entries displayed: %i\n", j
);
886 static void help(void) {
887 printf("%s [OPTIONS...]\n\n"
888 "Query and control the networking subsystem.\n\n"
889 " -h --help Show this help\n"
890 " --version Show package version\n"
891 " --no-pager Do not pipe output into a pager\n"
892 " --no-legend Do not show the headers and footers\n"
893 " -a --all Show status for all links\n\n"
896 " status [LINK...] Show link status\n"
897 " lldp Show lldp neighbors\n"
898 , program_invocation_short_name
);
901 static int parse_argv(int argc
, char *argv
[]) {
909 static const struct option options
[] = {
910 { "help", no_argument
, NULL
, 'h' },
911 { "version", no_argument
, NULL
, ARG_VERSION
},
912 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
913 { "no-legend", no_argument
, NULL
, ARG_NO_LEGEND
},
914 { "all", no_argument
, NULL
, 'a' },
923 while ((c
= getopt_long(argc
, argv
, "ha", options
, NULL
)) >= 0) {
950 assert_not_reached("Unhandled option");
957 static int networkctl_main(int argc
, char *argv
[]) {
958 const Verb verbs
[] = {
959 { "list", VERB_ANY
, 1, VERB_DEFAULT
, list_links
},
960 { "status", 1, VERB_ANY
, 0, link_status
},
961 { "lldp", VERB_ANY
, 1, VERB_DEFAULT
, link_lldp_status
},
965 return dispatch_verb(argc
, argv
, verbs
, NULL
);
968 int main(int argc
, char* argv
[]) {
971 log_parse_environment();
974 r
= parse_argv(argc
, argv
);
978 r
= networkctl_main(argc
, argv
);
983 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;