common/recipes-core/consoled/files/consoled.c (212 lines of code) (raw):

/* * consoled * * Copyright 2015-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 <stdio.h> #include <stdlib.h> #include <unistd.h> #include <syslog.h> #include <stdint.h> #include <string.h> #include <errno.h> #include <fcntl.h> #include <poll.h> #include <termios.h> #include <signal.h> #include <sys/stat.h> #include <openbmc/pal.h> #define BAUDRATE B57600 #define CTRL_X 0x18 #define ASCII_ENTER 0x0D #define MAX_LOGFILE_LINES 1200 // Maximum lines based on carriage returns or new line #define MAX_LOGFILE_SIZE 102400 // 100KB size => 1200 lines of 80 characters each = ~108000B static sig_atomic_t sigexit = 0; static void write_data(int file, char *buf, int len, char *fname) { int wlen = 0; char *tbuf = buf; while(len > 0) { errno = 0; wlen = write(file, tbuf, len); // In case wlen < 0, retry write() if (wlen >= 0) { len -= wlen; tbuf = tbuf + wlen; } else { syslog(LOG_WARNING, "write_data: write() failed to file %s | errno: %d", fname, errno); return; } } } static void exit_session(int sig) { sigexit = sig; } static void print_usage() { printf("Usage: consoled [ %s ]\n", pal_server_list); } static void run_console(char* fru_name, int term) { int i; int tty; // serial port int buf_fd; // Buffer File int blen; // len for int nfd = 0; // For number of fd int nevents; // For number of events in fd int nline = 0; //int pid_fd; int flags; pid_t pid; // For pid of the daemon uint8_t fru; char in; // For stdin character char data[32]; char pid_file[64]; char devtty[32]; // For tty dev path char bfname[32]; // For buffer file path char old_bfname[32]; // For old buffer file path char buf[256]; // For buffer data struct termios ottytio, nttytio; // For the tty dev int stdi; // STDIN_FILENO int stdo; // STDOUT_FILENO struct termios ostditio, nstditio; // For STDIN_FILENO struct termios ostdotio, nstdotio; // For STDOUT_FILENO struct stat buf_stat; struct pollfd pfd[2]; /* Start Daemon for the console buffering */ if (!term) { daemon(0,1); openlog("consoled", LOG_CONS, LOG_DAEMON); syslog(LOG_INFO, "consoled: daemon started"); } if (pal_get_fru_id(fru_name, &fru)) { exit(-1); } if (pal_get_fru_devtty(fru, devtty)) { exit(-1); } /* Handling the few Signals differently */ signal(SIGHUP, exit_session); signal(SIGINT, exit_session); signal(SIGTERM, exit_session); signal(SIGPIPE, exit_session); signal(SIGQUIT, exit_session); /* Different flag value for tty dev */ flags = term == 1 ? (O_RDWR | O_NOCTTY | O_NONBLOCK) : (O_RDONLY | O_NOCTTY | O_NONBLOCK); if ((tty = open(devtty, flags)) < 0) { syslog(LOG_WARNING, "Cannot open the file %s", devtty); exit(-1); } fcntl(tty, F_SETFL, O_RDWR); /* Changing the attributes of the tty dev */ tcgetattr(tty, &ottytio); memcpy(&nttytio, &ottytio, sizeof(struct termios)); cfmakeraw(&nttytio); cfsetspeed(&nttytio, BAUDRATE); tcflush(tty, TCIFLUSH); tcsetattr(tty, TCSANOW, &nttytio); pfd[0].fd = tty; pfd[0].events = POLLIN; nfd++; /* Buffering the console data into a file */ sprintf(old_bfname, "/tmp/consoled_%s_log-old", fru_name); sprintf(bfname, "/tmp/consoled_%s_log", fru_name); if ((buf_fd = open(bfname, O_RDWR | O_APPEND | O_CREAT, 0666)) < 0) { syslog(LOG_WARNING, "Cannot open the file %s", bfname); exit(-1); } if (term) { /* Changing the attributes of STDIN_FILENO */ stdi = STDIN_FILENO; tcgetattr(stdi, &ostditio); memcpy(&nstditio, &ostditio, sizeof(struct termios)); cfmakeraw(&nstditio); tcflush(stdi, TCIFLUSH); tcsetattr(stdi, TCSANOW, &nstditio); /* Changing the attributes of STDOUT_FILENO */ stdo = STDOUT_FILENO; tcgetattr(stdo, &ostdotio); memcpy(&nstdotio, &ostdotio, sizeof(struct termios)); cfmakeraw(&nstdotio); tcflush(stdo, TCIFLUSH); tcsetattr(stdo, TCSANOW, &nstdotio); /* Adding STDIN_FILENO to the poll fd set */ pfd[1].fd = stdi; pfd[1].events = POLLIN; nfd++; } /* Handling the input event from the terminal and tty dev */ while (!sigexit && (nevents = poll(pfd, nfd, -1 /* Timeout */))) { /* Input to the terminal from the user */ if (term && nevents && nfd > 1 && pfd[1].revents > 0) { blen = read(stdi, &in, sizeof(in)); if (blen < 1) { nfd--; } if (in == CTRL_X) { break; } write_data(tty, &in, sizeof(in), "tty"); nevents--; } /* Input from the tty dev */ if (nevents && pfd[0].revents > 0) { blen = read(tty, buf, sizeof(buf)); if (blen > 0) { for (i = 0; i < blen; i++) { if (buf[i] == 0xD || buf[i] == 0xA) nline++; } write_data(buf_fd, buf, blen, bfname); fsync(buf_fd); if (term) { write_data(stdo, buf, blen, "STDOUT_FILENO"); } } else if (blen < 0) { raise(SIGHUP); } // Get File stat information memset(&buf_stat, 0, sizeof(struct stat)); fstat(buf_fd, &buf_stat); /* Log Rotation based on max number of lines or max file size */ if (nline >= MAX_LOGFILE_LINES || buf_stat.st_size >= MAX_LOGFILE_SIZE) { close(buf_fd); remove(old_bfname); rename(bfname, old_bfname); if ((buf_fd = open(bfname, O_RDWR | O_APPEND | O_CREAT, 0666)) < 0) { syslog(LOG_WARNING, "Cannot open the file %s", bfname); exit(-1); } nline = 0; } nevents--; } } /* Close the console buffer file */ close(buf_fd); /* Revert the tty dev to old attributes */ tcflush(tty, TCIFLUSH); tcsetattr(tty, TCSANOW, &ottytio); /* Revert STDIN to old attributes */ if (term) { tcflush(stdo, TCIFLUSH); tcsetattr(stdo, TCSANOW, &ostdotio); tcflush(stdi, TCIFLUSH); tcsetattr(stdi, TCSANOW, &ostditio); } /* Delete the pid file */ if (!term) remove(pid_file); } int main(int argc, void **argv) { int rc, lock_file; char file[64]; int term; char *fru_name; if (argc != 3) { print_usage(); exit(1); } // A lock file for one instance of consoled for each fru sprintf(file, "/var/lock/consoled_%s", argv[1]); lock_file = open(file, O_CREAT | O_RDWR, 0666); rc = flock(lock_file, LOCK_EX | LOCK_NB); if(rc) { if(errno == EWOULDBLOCK) { printf("Another consoled %s instance is running...\n", argv[1]); exit(-1); } } else { fru_name = argv[1]; if (!strcmp(argv[2], "--buffer")) { term = 0; } else if (!strcmp(argv[2], "--term")) { term = 1; } else { print_usage(); exit(-1); } run_console(fru_name, term); } return sigexit; }