diff options
Diffstat (limited to 'meta-facebook/meta-wedge/recipes-wedge/bmc-log/files')
5 files changed, 722 insertions, 0 deletions
diff --git a/meta-facebook/meta-wedge/recipes-wedge/bmc-log/files/Makefile b/meta-facebook/meta-wedge/recipes-wedge/bmc-log/files/Makefile new file mode 100644 index 0000000..5004556 --- /dev/null +++ b/meta-facebook/meta-wedge/recipes-wedge/bmc-log/files/Makefile @@ -0,0 +1,24 @@ +# Copyright 2014-present Facebook. All Rights Reserved. +# +# This program file is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program in a file named COPYING; if not, write to the +# Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, +# Boston, MA 02110-1301 USA + +LIBS = -lutil + +all: + ${CC} ${CFLAGS} -o bmc-log bmc-log.c ${LIBS} + +clean: + rm -rf *.o bmc-log diff --git a/meta-facebook/meta-wedge/recipes-wedge/bmc-log/files/bmc-log-config b/meta-facebook/meta-wedge/recipes-wedge/bmc-log/files/bmc-log-config new file mode 100644 index 0000000..529adf2 --- /dev/null +++ b/meta-facebook/meta-wedge/recipes-wedge/bmc-log/files/bmc-log-config @@ -0,0 +1,16 @@ +# Configuration used by bmc-log script + +#Serial port connected to the micro-server +US_TTY="" + +#IP version of the log collecting server +LOG_SERVER_IP_VERSION="" + +#Host name of the log collecting server +LOG_SERVER_NAME="" + +#Port number of the log collecting server +LOG_SERVER_PORT="" + +#Baud rate to set for the US_TTY +TTY_BAUD_RATE="" diff --git a/meta-facebook/meta-wedge/recipes-wedge/bmc-log/files/bmc-log.c b/meta-facebook/meta-wedge/recipes-wedge/bmc-log/files/bmc-log.c new file mode 100644 index 0000000..078db7b --- /dev/null +++ b/meta-facebook/meta-wedge/recipes-wedge/bmc-log/files/bmc-log.c @@ -0,0 +1,559 @@ +/* + * Copyright 2014-present Facebook. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <fcntl.h> +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> +#include <stdbool.h> +#include <unistd.h> +#include <string.h> +#include <linux/serial.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <sys/param.h> +#include <time.h> +#include <arpa/inet.h> +#include <sys/stat.h> +#include <pty.h> +#include <errno.h> +#include <signal.h> +#include <netinet/in.h> +#include <netdb.h> +#include "bmc-log.h" + +FILE *error_file = NULL; + +bool kill_received = false; // To check if a killing interrupt is received + +speed_t baud_rate = B57600; // Default baud rate - change if user inputs a different one + +int fd_tty = -1, fd_soc = -1; + +/* Hostname and port of the server */ +char *hostname; +int port; + +struct termios orig_tty_state; + +char *get_time() +{ + static char mytime[TIME_FORMAT_SIZE]; + memset(mytime, 0, sizeof(mytime)); + time_t this_time; + struct tm *this_tm; + this_time = time(NULL); + this_tm = localtime(&this_time); + + snprintf(mytime, sizeof(mytime), "%04d-%02d-%02d %02d:%02d:%02d", + 1900 + this_tm->tm_year, this_tm->tm_mon + 1, + this_tm->tm_mday, this_tm->tm_hour, + this_tm->tm_min, this_tm->tm_sec); + return mytime; +} + +void errlog(char *frmt, ...) +{ + va_list args; + va_start(args, frmt); + struct stat st; + + char *time_now = get_time(); + + fprintf(stderr, "[%s] ", time_now); + vfprintf(stderr, frmt, args); + + if (error_file) { + stat(error_log_file, &st); + if (st.st_size >= MAX_LOG_FILE_SIZE) { + truncate(error_log_file, 0); + } + fprintf(error_file, "[%s] ", time_now); + vfprintf(error_file, frmt, args); + fflush(error_file); + } +} + +/* Get the address info of netcons server */ +struct addrinfo *get_addr_info(int ip_version) +{ + int ip_family = (ip_version == IPV4) ? AF_INET : AF_INET6; + struct addrinfo hints; + + struct addrinfo *result; + result = malloc(sizeof(*result)); + if (!result) { + errlog("Error: Unable to allocate memory - %m\n"); + return NULL; + } + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = ip_family; /* Allow IPv4 or IPv6 */ + hints.ai_socktype = SOCK_DGRAM; /* Datagram socket */ + hints.ai_flags = AI_PASSIVE; /* For wildcard IP address */ + hints.ai_protocol = 0; /* Any protocol */ + hints.ai_canonname = NULL; + hints.ai_addr = NULL; + hints.ai_next = NULL; + + if (getaddrinfo(hostname, NULL, &hints, &result)) { + errlog("Error: getaddrinfo failed - %m\n"); + return NULL; + } + + return result; +} + +/* Prepare Ipv4 Socket */ +bool prepare_sock(struct sockaddr_in * tgt_addr) +{ + struct addrinfo *addr_info; + memset(tgt_addr, 0, sizeof(*tgt_addr)); + + if ((addr_info = get_addr_info(IPV4)) == NULL) { + errlog("Error: Unable to get address info\n"); + return false; + } + + tgt_addr->sin_addr = ((struct sockaddr_in *)addr_info->ai_addr)->sin_addr; + tgt_addr->sin_port = htons(port); + tgt_addr->sin_family = AF_INET; + + return true; +} + +/* Prepare Ipv6 Socket */ +bool prepare_sock6(struct sockaddr_in6 * tgt_addr6) +{ + struct addrinfo *addr_info; + memset(tgt_addr6, 0, sizeof(*tgt_addr6)); + + if ((addr_info = get_addr_info(IPV6)) == NULL) { + errlog("Erorr: Unable to get address info\n"); + return false; + } + + tgt_addr6->sin6_addr = ((struct sockaddr_in6 *)addr_info->ai_addr)->sin6_addr; + tgt_addr6->sin6_port = htons(port); + tgt_addr6->sin6_family = AF_INET6; + + return true; +} + +/* Set TTY to raw mode */ +bool set_tty(int fd) +{ + struct termios tty_state; + + if (tcgetattr(fd, &tty_state) < 0) { + return false; + } + + if (tcgetattr(fd, &orig_tty_state) < 0) // Save original settings + { + return false; + } + + if (cfsetspeed(&tty_state, baud_rate) == -1) { + errlog("Error: Baud Rate not set - %m\n"); + return false; + } + + tty_state.c_lflag &= ~(ICANON | IEXTEN | ISIG | ECHO); + tty_state.c_iflag &= ~(IGNCR | ICRNL | INPCK | ISTRIP | IXON | BRKINT); + tty_state.c_iflag |= (IGNCR | ICRNL); + tty_state.c_oflag &= ~OPOST; + tty_state.c_cflag |= CS8; + + tty_state.c_cc[VMIN] = 1; + tty_state.c_cc[VTIME] = 0; + + if (tcsetattr(fd, TCSAFLUSH, &tty_state) < 0) { + return false; + } + + return true; +} + +/* Create a pseudo terminal for other process to use (as this program is using up the actual TTY) */ +int create_pseudo_tty() +{ + int amaster, aslave; + int flags; + + if (openpty(&amaster, &aslave, NULL, NULL, NULL) == -1) { + errlog("Error: Openpty failed - %m\n"); + return -1; + } + + /* Set to non blocking mode */ + flags = fcntl(amaster, F_GETFL); + flags |= O_NONBLOCK; + fcntl(amaster, F_SETFL, flags); + + FILE *pseudo_save_file = fopen(pseudo_tty_save_file, "w+"); + if (!pseudo_save_file) { + errlog("Error: Unable to open the pseudo info file - %m\n"); + return -1; + } + /* Save the name of the created pseudo tty in a text file for other processes to use */ + if (fprintf(pseudo_save_file, "%s\n", ttyname(aslave)) == -1) { + errlog("Error writing to the pseudo info file\n"); + fclose(pseudo_save_file); + return -1; + } + fclose(pseudo_save_file); + + if (set_tty(aslave) == -1) { + errlog("Error: Slave TTY not set properly\n"); + return -1; + } + + return amaster; +} + +/* Prepare logs from the read_buf and send them to the server */ +bool prepare_log_send(char *read_buf, int max_read, int fd_socket) +{ + size_t buff_index = 0; // Index for the read_buf string + + static char line[LINE_LEN] = { 0 }; + static size_t line_index = 0; // Index for the line string + + char msg[MSG_LEN] = { 0 }; // Message to be sent to the server + + /* Kernel Version */ + static char kernel_version[KERNEL_VERSION_LEN] = "dummy_kernel"; + static int kernel_search_pos = 0; + + while (buff_index < max_read && read_buf[buff_index] != '\0') { + if (read_buf[buff_index] == 'L') // Check if there is a possibility of new kernel version + { + kernel_search_pos = line_index; + } + + /* Send the log when a line is read */ + if (read_buf[buff_index] == '\n') { + if (kernel_search_pos > 0) { + if (strncmp(line + kernel_search_pos, "Linux version ", kernel_search_len) == 0) { + sscanf(line + kernel_search_pos + kernel_search_len, "%s", kernel_version); + } + kernel_search_pos = 0; + } + + /* Prepare the message */ + memset(msg, 0, sizeof(msg)); + if (snprintf(msg, sizeof(msg), "%s %s %s %s", "kernel:", kernel_version, "- msg", line) < 0) { + errlog("Error copying the message - %m\n"); + return false; + } + + /* Send message to the server */ + if (write(fd_socket, msg, strlen(msg)) < 0) { + errlog("Error: Write to socket failed - %m\n"); + return false; + } + + /* Reset the line buffer */ + line_index = 0; + memset(line, 0, sizeof(line)); + + buff_index++; + continue; + } + + /* If line is too big, send only the first few bytes and discard others. */ + if (line_index >= sizeof(line)) { + line[line_index - 1] = 0; + buff_index++; + continue; + } + + line[line_index++] = read_buf[buff_index++]; + } + + return true; +} + +/* Read text from the TTY and send to send as logs */ +bool read_send(int fd_tty, int fd_socket) +{ + char read_buf[LINE_LEN] = { 0 }; // Buffer to be read into. + int read_size = 0; + fd_set readset; + int sel; + int fdmax; + + int pseudo_tty = create_pseudo_tty(); + + if (pseudo_tty == -1) { + errlog("Error: Cannot create a psuedo terminal\n"); + return false; + } + + fdmax = MAX(fd_tty, pseudo_tty); + + while (!kill_received) { + do { + FD_ZERO(&readset); + FD_SET(fd_tty, &readset); + FD_SET(pseudo_tty, &readset); + + sel = select(fdmax + 1, &readset, NULL, NULL, NULL); + } + while (sel == -1 && errno == EINTR && !kill_received); + + memset(read_buf, 0, sizeof(read_buf)); + if (FD_ISSET(fd_tty, &readset)) { + read_size = read(fd_tty, read_buf, sizeof(read_buf) - 1); + + if (read_size == 0) { + continue; + } + if (read_size < 0) { + if (errno == EAGAIN) { + continue; + } + errlog("Error: Read from tty failed - %m\n"); + return false; + } + + /* Send the read data to the pseudo terminal */ + if (write(pseudo_tty, read_buf, read_size) < 0) { + if (errno == EAGAIN) // Output buffer full - flush it. + { + tcflush(pseudo_tty, TCIOFLUSH); + continue; + } + + errlog("Error: Write to pseudo tty failed - %m\n"); + return false; + } + + /* Prepare log message and send to the server */ + if (!prepare_log_send(read_buf, sizeof(read_buf), fd_socket)) { + errlog("Error: Sending log failed - %m\n"); + return false; + } + } + /*if (FD_ISSET(fd_tty, &readset)) */ + if (kill_received) { + break; + } + + /* Check if there is an data in the pseudo terminal's buffer */ + if (FD_ISSET(pseudo_tty, &readset)) { + read_size = read(pseudo_tty, read_buf, sizeof(read_buf) - 1); + + if (read_size == 0) { + continue; + } + if (read_size < 0) { + if (errno == EAGAIN) { + continue; + } + errlog("Error: Read from pseudo tty failed - %m\n"); + return false; + } + + if (write(fd_tty, read_buf, read_size) < 0) { + if (errno == EAGAIN) // Output buffer full - flush it. + { + tcflush(fd_tty, TCIOFLUSH); + continue; + } + + errlog("Error: Write to tty failed - %m\n"); + return false; + } + } /*if (FD_ISSET(pseudo_tty,&readset)) */ + } /*while (!kill_received) */ + + return true; +} + +void cleanup() +{ + remove(pseudo_tty_save_file); + tcsetattr(fd_tty, TCSAFLUSH, &orig_tty_state); //Restore original settings + close(fd_tty); + close(fd_soc); + fclose(error_file); +} + +void sig_kill(int signum) +{ + kill_received = true; +} + +void register_kill() +{ + struct sigaction sigact; + sigset_t sigset; + + sigemptyset(&sigset); + memset(&sigact, 0, sizeof sigact); + sigact.sa_handler = sig_kill; + sigact.sa_mask = sigset; + + sigaction(SIGHUP, &sigact, NULL); + sigaction(SIGINT, &sigact, NULL); + sigaction(SIGQUIT, &sigact, NULL); + sigaction(SIGPIPE, &sigact, NULL); + sigaction(SIGTERM, &sigact, NULL); + sigaction(SIGKILL, &sigact, NULL); + sigaction(SIGABRT, &sigact, NULL); +} + +void usage(char *prog_name) +{ + printf("Usage:\n"); + printf("\t%s TTY ip_version(4 or 6) hostname port [baud rate (like 57600)]\n", prog_name); + printf("\t%s -h : For this help\n", prog_name); + printf("Example:\n\t./bmc-log /dev/ttyS1 4 netcons.any.facebook.com 1514\n"); + printf("\tOR\n\t./bmc-log /dev/ttyS1 6 netcons6.any.facebook.com 1514 57600\n"); +} + +bool parse_user_input(int nargs, char **args, char *read_tty, int read_tty_size, int *ip_version) +{ + if (nargs < 5) { + if ((nargs > 1) && ((strcmp(args[1], "-h") == 0) || (strcmp(args[1], "--help") == 0))) { + usage(args[0]); + return false; // Not an error but returning -1 for the main function to return + } + fprintf(stderr, "Error: Invalid number of arguments\n"); + usage(args[0]); + return false; + } + + if (strlen(args[1]) > read_tty_size) { + fprintf(stderr, "Error: TTY too long\n"); + usage(args[0]); + return false; + } + + /* TTT to read the logs from */ + strncpy(read_tty, args[1], read_tty_size); + + /* IP Version, IP Address and Port of the netcons server */ + *ip_version = atoi(args[2]); + if (*ip_version != IPV4 && *ip_version != IPV6) { + fprintf(stderr, "Error: Invalid IP Version input\n"); + usage(args[0]); + return false; + } + + hostname = args[3]; + port = atoi(args[4]); + + baud_rate = B57600; + if (nargs == 6) + baud_rate = atoi(args[5]); + + return true; +} + +int main(int argc, char **argv) +{ + char read_tty[TTY_LEN] = { 0 }; + int ip_version; + int socket_domain = AF_UNSPEC; + char cmd[COMMAND_LEN] = { 0 }; + + /* Open the error log file */ + error_file = fopen(error_log_file, "a+"); + if (!error_file) { + printf("Error: Unable to open log file - %m\n"); + return 1; + } + + /* Register actions upon interrupts */ + register_kill(); + + /* Parse the user input */ + if (!parse_user_input(argc, argv, read_tty, sizeof(read_tty), &ip_version)) { + return 2; + } + + snprintf(cmd, sizeof(cmd), "%s %s", uS_console, "connect"); + if (system(cmd) == -1) { + errlog("Error: Unable to connect to the micro-server\n"); + return 3; + } + + /* Create a socket to communicate with the netcons server */ + socket_domain = (ip_version == IPV4) ? AF_INET : AF_INET6; + fd_soc = socket(socket_domain, SOCK_DGRAM, 0); + if (fd_soc == -1) { + errlog("Error: Socket creation failed - %m\n"); + return 4; + } + + if (ip_version == IPV4) { /* IPv4 */ + struct sockaddr_in tgt_addr; + if (!prepare_sock(&tgt_addr)) { + close(fd_soc); + errlog("Error: Socket not valid\n"); + return 5; + } + + if (connect(fd_soc, (struct sockaddr *)&tgt_addr, sizeof(tgt_addr)) == -1) { + close(fd_soc); + errlog("Error: Socket connection failed - %m\n"); + return 6; + } + + } else { /* IPv6 */ + + struct sockaddr_in6 tgt_addr6; + if (!prepare_sock6(&tgt_addr6)) { + close(fd_soc); + errlog("Error: Socket not valid\n"); + return 5; + } + + if (connect(fd_soc, (struct sockaddr *)&tgt_addr6, sizeof(tgt_addr6)) == -1) { + close(fd_soc); + errlog("Error: Socket connection failed - %m\n"); + return 6; + } + } + + /* TTY Operations */ + if ((fd_tty = open(read_tty, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK)) == -1) { + close(fd_soc); + errlog("Error: Serial Port %s open failed - %m\n", read_tty); + return 7; + } + + if (!set_tty(fd_tty)) { + errlog("Error: tty not set properly\n"); + cleanup(); + return 8; + } + + /* Read, prepare and send the logs */ + if (!read_send(fd_tty, fd_soc)) { + errlog("Error: Sending logs failed\n"); + cleanup(); + return 9; + } + + cleanup(); + return 0; +} diff --git a/meta-facebook/meta-wedge/recipes-wedge/bmc-log/files/bmc-log.h b/meta-facebook/meta-wedge/recipes-wedge/bmc-log/files/bmc-log.h new file mode 100644 index 0000000..5795e6a --- /dev/null +++ b/meta-facebook/meta-wedge/recipes-wedge/bmc-log/files/bmc-log.h @@ -0,0 +1,46 @@ +/* + * Copyright 2014-present Facebook. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef BMC_LOG_H_ +#define BMC_LOG_H_ + +/* IP Version */ +#define IPV4 (4) +#define IPV6 (6) + +/* Search string for the kernel version */ +#define KERNEL_SEARCH_STR "Linux version " + +/* Size constants */ +#define TIME_FORMAT_SIZE (100) +#define MAX_LOG_FILE_SIZE (1024*1024*5) //5MB +#define TTY_LEN (50) +#define LINE_LEN (257) +#define MSG_LEN (1025) +#define COMMAND_LEN (100) +#define KERNEL_VERSION_LEN (100) + +static char *uS_console = "/usr/local/fbpackages/utils/us_console.sh"; + +static char *error_log_file = "/var/log/bmc-log"; + +static char *pseudo_tty_save_file = "/etc/us_pseudo_tty"; + +static int kernel_search_len = sizeof(KERNEL_SEARCH_STR) - 1; + +#endif diff --git a/meta-facebook/meta-wedge/recipes-wedge/bmc-log/files/bmc-log.sh b/meta-facebook/meta-wedge/recipes-wedge/bmc-log/files/bmc-log.sh new file mode 100755 index 0000000..00bf63c --- /dev/null +++ b/meta-facebook/meta-wedge/recipes-wedge/bmc-log/files/bmc-log.sh @@ -0,0 +1,77 @@ +#!/bin/sh +# +# Copyright 2014-present Facebook. All Rights Reserved. +# +# This program file is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program in a file named COPYING; if not, write to the +# Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, +# Boston, MA 02110-1301 USA +# + +### BEGIN INIT INFO +# Provides: bmc-log +# Required-Start: +# Required-Stop: +# Default-Start: S +# Default-Stop: +# Short-Description: Collect micro-server kernel logs through serial port +### END INIT INFO + +. /etc/default/bmc-log +. /etc/init.d/functions + +DAEMON=/usr/sbin/bmc-log +NAME=bmc-log +DESC="Micro-Server log collection" + +TTY=${US_TTY:-/dev/ttyS1} +IP=${LOG_SERVER_IP_VERSION:-4} +LOG_SERVER=${LOG_SERVER_NAME:-} +PORT=${LOG_SERVER_PORT:-} +BAUD_RATE=${TTY_BAUD_RATE:-} + +if [ -z "$LOG_SERVER" ] || [ -z "$PORT" ] +then + echo "Error: Server and/or port not set" + exit 0 +fi + + +ACTION="$1" + +case "$ACTION" in + start) + echo -e "Starting $DESC" + $DAEMON $TTY $IP $LOG_SERVER $PORT $BAUD_RATE + ;; + stop) + echo -e "Stopping $DESC: " + start-stop-daemon --stop --quiet --exec $DAEMON + ;; + restart|force-reload) + echo -e "Restarting $DESC: " + start-stop-daemon --stop --quiet --exec $DAEMON + sleep 1 + $DAEMON $TTY $IP $LOG_SERVER $PORT $BAUD_RATE + ;; + status) + stat $DAEMON + exit $? + ;; + *) + N=${0##*/} + N=${N#[SK]??} + echo "Usage: $N {start|stop|status|restart|force-reload}" >&2 + exit 1 + ;; +esac |