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
22static device_info_t devices[MAX_DEVICES];
23static uint32_t devices_count = 0;
24
25static const char* appname;
26
27static 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
36static 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
45static 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
63static 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
101static void usage(void) {
102    fprintf(stderr, "usage: %s [options]\n", appname);
103    netboot_usage(false);
104}
105
106int 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