1 // Copyright 2017 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #define _POSIX_C_SOURCE 200809L
6 
7 #define _GNU_SOURCE
8 #define _DARWIN_C_SOURCE
9 
10 #include <errno.h>
11 #include <fcntl.h>
12 #include <netinet/in.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <sys/time.h>
17 
18 #include "netprotocol.h"
19 
20 #define MAX_DEVICES 255
21 
22 static device_info_t devices[MAX_DEVICES];
23 static uint32_t devices_count = 0;
24 
25 static const char* appname;
26 
has_device(const char * nodename)27 static bool has_device(const char* nodename) {
28     for (uint32_t i = 0; i < devices_count; ++i) {
29         if (!strncmp(devices[i].nodename, nodename, sizeof(devices[i].nodename))) {
30             return true;
31         }
32     }
33     return false;
34 }
35 
get_device(const char * nodename)36 static device_info_t* get_device(const char* nodename) {
37     for (uint32_t i = 0; i < devices_count; ++i) {
38         if (!strncmp(devices[i].nodename, nodename, sizeof(devices[i].nodename))) {
39             return &devices[i];
40         }
41     }
42     return NULL;
43 }
44 
add_device(device_info_t * device)45 static device_info_t* add_device(device_info_t* device) {
46     device_info_t* known_device = get_device(device->nodename);
47     if (!known_device) {
48         if (devices_count > MAX_DEVICES) {
49             return NULL;
50         }
51         known_device = &devices[devices_count];
52         devices_count++;
53         strncpy(known_device->nodename, device->nodename, sizeof(known_device->nodename));
54     }
55     strncpy(known_device->inet6_addr_s, device->inet6_addr_s, INET6_ADDRSTRLEN);
56     memcpy(&known_device->inet6_addr, &device->inet6_addr, sizeof(known_device->inet6_addr));
57     known_device->state = device->state;
58     known_device->bootloader_port = device->bootloader_port;
59     known_device->bootloader_version = device->bootloader_version;
60     return known_device;
61 }
62 
on_device(device_info_t * device,void * cookie)63 static bool on_device(device_info_t* device, void* cookie) {
64     if (!has_device(device->nodename)) {
65         if (device->state == UNKNOWN) {
66             device->state = OFFLINE;
67         }
68         const char* state = "unknown";
69         switch (device->state) {
70         case UNKNOWN:
71             state = "unknown";
72             break;
73         case OFFLINE:
74             state = "offline";
75             break;
76         case DEVICE:
77             state = "device";
78             break;
79         case BOOTLOADER:
80             state = "bootloader";
81             break;
82         }
83 
84         // TODO(jimbe): Print the type of the device based on the vendor id of the mac address.
85         fprintf(stdout, "%10s %s", state, device->nodename);
86         if (device->inet6_addr.sin6_scope_id != 0) {
87             fprintf(stdout, " (%s/%d)", device->inet6_addr_s, device->inet6_addr.sin6_scope_id);
88         }
89         if (device->state == BOOTLOADER) {
90             fprintf(stdout, " [Bootloader version 0x%08X listening on %d]",
91                     device->bootloader_version, device->bootloader_port);
92         }
93         fprintf(stdout, "\n");
94         if (add_device(device) == NULL) {
95             return false;
96         }
97     }
98     return true;
99 }
100 
usage(void)101 static void usage(void) {
102     fprintf(stderr, "usage: %s [options]\n", appname);
103     netboot_usage(false);
104 }
105 
main(int argc,char ** argv)106 int main(int argc, char** argv) {
107     appname = argv[0];
108     int index = netboot_handle_getopt(argc, argv);
109     if (index < 0) {
110         usage();
111         return -1;
112     }
113 
114     if (netboot_discover(NB_SERVER_PORT, NULL, on_device, NULL)) {
115         fprintf(stderr, "Failed to discover\n");
116         return 1;
117     }
118     return 0;
119 }
120