| // Copyright 2017 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #define _POSIX_C_SOURCE 200809L |
| |
| #define _GNU_SOURCE |
| #define _DARWIN_C_SOURCE |
| |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <netinet/in.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/time.h> |
| |
| #include "netprotocol.h" |
| |
| #define MAX_DEVICES 255 |
| |
| static device_info_t devices[MAX_DEVICES]; |
| static uint32_t devices_count = 0; |
| |
| static const char* appname; |
| |
| static bool has_device(const char* nodename) { |
| for (uint32_t i = 0; i < devices_count; ++i) { |
| if (!strncmp(devices[i].nodename, nodename, sizeof(devices[i].nodename))) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| static device_info_t* get_device(const char* nodename) { |
| for (uint32_t i = 0; i < devices_count; ++i) { |
| if (!strncmp(devices[i].nodename, nodename, sizeof(devices[i].nodename))) { |
| return &devices[i]; |
| } |
| } |
| return NULL; |
| } |
| |
| static device_info_t* add_device(device_info_t* device) { |
| device_info_t* known_device = get_device(device->nodename); |
| if (!known_device) { |
| if (devices_count > MAX_DEVICES) { |
| return NULL; |
| } |
| known_device = &devices[devices_count]; |
| devices_count++; |
| strncpy(known_device->nodename, device->nodename, sizeof(known_device->nodename)); |
| } |
| strncpy(known_device->inet6_addr_s, device->inet6_addr_s, INET6_ADDRSTRLEN); |
| memcpy(&known_device->inet6_addr, &device->inet6_addr, sizeof(known_device->inet6_addr)); |
| known_device->state = device->state; |
| known_device->bootloader_port = device->bootloader_port; |
| known_device->bootloader_version = device->bootloader_version; |
| return known_device; |
| } |
| |
| static bool on_device(device_info_t* device, void* cookie) { |
| if (!has_device(device->nodename)) { |
| if (device->state == UNKNOWN) { |
| device->state = OFFLINE; |
| } |
| const char* state = "unknown"; |
| switch (device->state) { |
| case UNKNOWN: |
| state = "unknown"; |
| break; |
| case OFFLINE: |
| state = "offline"; |
| break; |
| case DEVICE: |
| state = "device"; |
| break; |
| case BOOTLOADER: |
| state = "bootloader"; |
| break; |
| } |
| |
| // TODO(jimbe): Print the type of the device based on the vendor id of the mac address. |
| fprintf(stdout, "%10s %s", state, device->nodename); |
| if (device->inet6_addr.sin6_scope_id != 0) { |
| fprintf(stdout, " (%s/%d)", device->inet6_addr_s, device->inet6_addr.sin6_scope_id); |
| } |
| if (device->state == BOOTLOADER) { |
| fprintf(stdout, " [Bootloader version 0x%08X listening on %d]", device->bootloader_version, |
| device->bootloader_port); |
| } |
| fprintf(stdout, "\n"); |
| if (add_device(device) == NULL) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| static void usage(void) { |
| fprintf(stderr, "usage: %s [options]\n", appname); |
| netboot_usage(false); |
| } |
| |
| int main(int argc, char** argv) { |
| appname = argv[0]; |
| int index = netboot_handle_getopt(argc, argv); |
| if (index < 0) { |
| usage(); |
| return -1; |
| } |
| |
| if (netboot_discover(NB_SERVER_PORT, NULL, on_device, NULL)) { |
| fprintf(stderr, "Failed to discover\n"); |
| return 1; |
| } |
| return 0; |
| } |