// Copyright 2016 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "bootserver.h" #define ANSI_RED "\x1b[31m" #define ANSI_GREEN "\x1b[32m" #define ANSI_YELLOW "\x1b[33m" #define ANSI_BLUE "\x1b[34m" #define ANSI_MAGENTA "\x1b[35m" #define ANSI_CYAN "\x1b[36m" #define ANSI_RESET "\x1b[0m" #define ANSI_LINESTART "\33[2K\r" #define MAX_FVM_IMAGES 4 #define ANSI(name) (use_color == false || is_redirected) ? "" : ANSI_##name #define log(args...) \ do { \ char logline[1024]; \ snprintf(logline, sizeof(logline), args); \ fprintf(stderr, "%s [%s] %s\n", date_string(), appname, logline); \ } while (false) char* appname; int64_t us_between_packets = DEFAULT_US_BETWEEN_PACKETS; static bool use_tftp = true; static bool use_color = true; static size_t total_file_size; static bool file_info_printed; static int progress_reported; static int packets_sent; static struct timeval start_time, end_time; static bool is_redirected; static const char spinner[] = {'|', '/', '-', '\\'}; char* date_string() { static char date_buf[80]; time_t t = time(NULL); struct tm tm = *localtime(&t); snprintf(date_buf, sizeof(date_buf), "%4d-%02d-%02d %02d:%02d:%02d", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); return date_buf; } char* sockaddr_str(struct sockaddr_in6* addr) { static char buf[128]; char tmp[INET6_ADDRSTRLEN]; snprintf(buf, sizeof(buf), "[%s]%d", inet_ntop(AF_INET6, &addr->sin6_addr, tmp, INET6_ADDRSTRLEN), ntohs(addr->sin6_port)); return buf; } static void print_file_info(const char* name, size_t size) { size_t prefix_len = strlen(NB_FILENAME_PREFIX); const char* base_name; if (!strncmp(name, NB_FILENAME_PREFIX, prefix_len)) { base_name = &name[prefix_len]; } else { base_name = name; } char path1[PATH_MAX]; // dirname(), basename() modify in situ. char path2[PATH_MAX]; snprintf(path1, PATH_MAX, "%s", name); snprintf(path2, PATH_MAX, "%s", name); log("Transfer starts [%5.1f MB] %s/%s%s%s (%zu bytes)", (float)size / 1024.0 / 1024.0, dirname(path1), ANSI(GREEN), basename(path2), ANSI(RESET), size); } void initialize_status(const char* name, size_t size) { total_file_size = size; progress_reported = 0; packets_sent = 0; if (!file_info_printed) { print_file_info(name, size); file_info_printed = true; } } void update_status(size_t bytes_so_far) { packets_sent++; bool is_last_piece = (bytes_so_far == total_file_size); if (total_file_size == 0) { return; } if (is_redirected) { int percent_sent = (bytes_so_far * 100 / (total_file_size)); if (percent_sent - progress_reported >= 5) { fprintf(stderr, "\t%d%%...", percent_sent); progress_reported = percent_sent; } } else { if (packets_sent > 1024 || is_last_piece) { packets_sent = 0; float bw = 0; static int spin = 0; struct timeval now; gettimeofday(&now, NULL); int64_t sec = (int64_t)(now.tv_sec - start_time.tv_sec); int64_t usec = (int64_t)(now.tv_usec - start_time.tv_usec); int64_t elapsed_usec = sec * 1000000 + usec; bw = (float)bytes_so_far * 1000000 / (1024.0 * 1024.0 * ((float)elapsed_usec)); fprintf(stderr, "%s", ANSI_LINESTART); if (total_file_size > 0) { fprintf(stderr, "\t%c %.01f%%", spinner[(spin++) % 4], 100.0 * (float)bytes_so_far / (float)total_file_size); } else { fprintf(stderr, "\t%c", spinner[(spin++) % 4]); } fprintf(stderr, "\t %.01fMB/s", bw); if (is_last_piece) { fprintf(stderr, "\tTook %" PRId64 ".%" PRId64 " sec", elapsed_usec / 1000000, elapsed_usec % 1000000); } } } } static int xfer(struct sockaddr_in6* addr, const char* local_name, const char* remote_name) { int result; is_redirected = !isatty(fileno(stdout)); gettimeofday(&start_time, NULL); file_info_printed = false; if (use_tftp) { bool first = true; while ((result = tftp_xfer(addr, local_name, remote_name)) == -EAGAIN) { if (first) { fprintf(stderr, "Target busy, waiting."); first = false; } else { fprintf(stderr, "."); } sleep(1); gettimeofday(&start_time, NULL); } } else { result = netboot_xfer(addr, local_name, remote_name); } gettimeofday(&end_time, NULL); if (end_time.tv_usec < start_time.tv_usec) { end_time.tv_sec -= 1; end_time.tv_usec += 1000000; } fprintf(stderr, "\n"); return result; } void usage(void) { fprintf(stderr, "usage: %s [