/* * * Copyright (C) Eicon Technology Corporation, 2000. * * This source file is supplied for the exclusive use with Eicon * Technology Corporation's range of DIVA Server Adapters. * * Eicon File Revision : 1.3 * * 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY * 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. * */ /* * Source file for Unix kernel logger daemon process */ #include #include #include #include #include #include #include #include #include #include #include #include "divas.h" #include "divalog.h" #include "loglib.h" #include "linuxcfg.h" /* define two poll rates for XLOG both measures in milliseconds */ #define SLOW_POLL (5000) /* no client is displaying XLOG */ #define FAST_POLL (100) /* client actively displaying XLOG */ /* max number of clients supported */ #define MAX_CLIENTS (10) /* * define a type for holding information about each client */ typedef struct { char name[80]; /* name of client */ int fd; /* file descriptor */ int read_ptr; /* read pointer in log */ uint_t flags; /* additional information (see above) */ uint_t logs; /* active logs */ uint_t card_id; /* card being logged */ uint_t discarded; /* number of entries discarded */ } klog_client_t; /* number of log entries buffered */ #define MIN_NUM_ENTRIES (10) #define DEFAULT_NUM_ENTRIES (1024) #define MAX_NUM_ENTRIES (100000) static int num_entries = DEFAULT_NUM_ENTRIES; static klog_client_t client[MAX_CLIENTS]; /* data structure for each client */ static int server_fd = -1; /* server's command file descriptor */ static int log_fd = -1; /* klog device */ static klog_t *log = NULL; /* log buffer */ static int write_ptr = 0; /* write pointer into log buffer */ static int oldest_ptr = 0; /* oldest entry in log buffer */ static int timeout = SLOW_POLL; /* poll timeout - slow by default */ /* * update poll timer, depening on whether anyone looking for XLOG */ static void update_xlog_requests(void) { int i; timeout = SLOW_POLL; for (i = 0; i < MAX_CLIENTS; i++) { if ((client[i].fd != -1) && (client[i].logs & DIVAS_LOG_XLOG)) { timeout = FAST_POLL; break; } } return; } /* * Function to close client */ void close_client( klog_client_t *client, clog_t *message) { if (message) { message->flags = KLOG_NO_MORE_LOG; (void) write(client->fd, message, sizeof(clog_t)); } /* clear this client's information and close our end of the FIFO */ close(client->fd); memset(client, 0, sizeof(klog_client_t)); client->fd = -1; update_xlog_requests(); return; } /* * send a request down to specified card, looking for xlog */ static void req_xlog(int card_num) { if (ioctl(log_fd, DIA_IOCTL_XLOG_REQ, &card_num) == -1) { perror("divalogd: error in log device"); } return; } /* * tell driver which cards and log types we're interested in */ static void req_xlogs(void) { int i; for (i = 0; i < MAX_CLIENTS; i++) { if ((client[i].fd != -1) && (client[i].logs & DIVAS_LOG_XLOG)) { req_xlog(client[i].card_id); } } return; } /* * Handle log event */ int HandleLogEvent(int fd) { int n; int i; klog_t *k; k = &log[write_ptr]; if ((n = read(fd, k, sizeof(klog_t))) != sizeof(klog_t)) { if (n < 0) { perror("divalogd: server message read"); } else { fprintf(stderr, "divalogd: bad size (%d) on server message read", n); } return(1); } /* if we just read an XLOG message, send request for next one */ if ((k->type == KLOG_XLOG_MSG) || (k->type == KLOG_XTXT_MSG)) { req_xlog(k->card); } /* update write pointer */ write_ptr++; write_ptr %= num_entries; /* see if we have to bump the pointer to the oldest log entry */ if (write_ptr == oldest_ptr) { oldest_ptr++; oldest_ptr %= num_entries; } /* check to see if we have to bump any client readers */ for (i = 0; i < MAX_CLIENTS; i++) { if ((client[i].fd != -1) && (write_ptr == client[i].read_ptr)) { client[i].read_ptr++; client[i].read_ptr %= num_entries; client[i].discarded++; } } return(0); } /* * tell driver which cards and log types we're interested in */ static int update_active_logs(void) { int i; dia_log_t logs; /* tell driver we're interested in logging */ for (i = 0; i < MAX_CLIENTS; i++) { if (client[i].fd != -1) { logs.card_id = client[i].card_id; logs.log_types = client[i].logs; if (ioctl(log_fd, DIA_IOCTL_LOG, &logs) == -1) { perror("divalogd: error in log device"); return -1; } } } return 0; } /* * Handle server event: a client has sent us a message */ int HandleServerEvent(int fd) { klog_msg_t message; /* message from client */ int i; /* handly scratch counter */ int size; /* size of information read */ /* get the client message */ if ((size = read(fd, &message, sizeof(klog_msg_t))) != sizeof(klog_msg_t)) { if (size < 0) { perror("divalogd: server message read"); } else { fprintf(stderr, "divalogd: bad size on server message read"); } return(1); } switch (message.request) { case (KLOG_OPEN_CLIENT) : /* look for an empty client slot */ for (i = 0; i < MAX_CLIENTS; i++) { if (client[i].fd == -1) { break; } } if (i == MAX_CLIENTS) { fprintf(stderr, "divalogd: too many clients\n"); break; } if (OpenClient(message.buffer, &client[i].fd)) { fprintf(stderr, "divalogd: error in opening client %s\n", message.buffer); break; } /* set the client read pointer to oldest entry in the buffer */ if (message.flags & KLOG_RESET_LOG) { int j; write_ptr = oldest_ptr = 0; for (j = 0; j < MAX_CLIENTS; j++) { client[j].read_ptr = 0; } } else { client[i].read_ptr = oldest_ptr; } /* save client's name and the request flags */ strcpy(client[i].name, message.buffer); client[i].flags = message.flags; client[i].card_id = message.card_id; client[i].logs = message.logs; client[i].discarded = FALSE; /* * after all that, if log is empty and client has requested * notification, close the client */ if ((oldest_ptr == write_ptr) && (message.flags & KLOG_TELL_WHEN_EMPTY)) { clog_t client_msg; client_msg.flags = KLOG_NO_MORE_LOG; client_msg.data.time_stamp = 0; client_msg.data.buffer[0] = '\0'; close_client(&client[i], &client_msg); } else { update_active_logs(); update_xlog_requests(); } break; case (KLOG_CLOSE_CLIENT) : for (i = 0; i < MAX_CLIENTS; i++) { if (!(strcmp(client[i].name, message.buffer))) { break; } } if (i == MAX_CLIENTS) { fprintf(stderr, "divalogd: client %s not found for close\n", message.buffer); break; } close_client(&client[i], NULL); break; default : fprintf(stderr, "divalogd: unknown client request %d\n", message.request); break; } return(0); } /* * Tell client that we've discarded one or more entries */ static int tell_client_discarded(klog_client_t *client) { static char buffer[80]; int size; clog_t entry; /* fill in a dummy entry tellinmg client we've discarded entries */ sprintf(buffer, "WARNING ! divalog buffer overflow - %d %s discarded", client->discarded, (client->discarded == 1) ? "entry" : "entries"); client->discarded = 0; entry.flags = 0; entry.data.time_stamp = 0; entry.data.type = KLOG_TEXT_MSG; entry.data.length = strlen(buffer); entry.data.code = 0; strcpy(entry.data.buffer, buffer); if ((size = write(client->fd, &entry,sizeof(clog_t))) != sizeof(clog_t)) { if (size < 0) { perror("divalogd: write to client"); } else { fprintf(stderr, "divalogd: bad size on write to client"); } return(1); } return(0); } /* * Handle client event: a client is ready to accept data */ int HandleClientEvent(klog_client_t *client) { int size; clog_t entry; if (client->discarded) { return(tell_client_discarded(client)); } if (client->read_ptr != write_ptr) { /* write next entry to client */ memcpy(&entry.data, &log[client->read_ptr], sizeof(klog_t)); client->read_ptr++; client->read_ptr %= num_entries; if ((client->read_ptr == write_ptr) && (client->flags & KLOG_TELL_WHEN_EMPTY)) { entry.flags = KLOG_NO_MORE_LOG; } else { entry.flags = 0; } if ((size = write(client->fd, &entry,sizeof(clog_t))) != sizeof(clog_t)) { if (size < 0) { perror("divalogd: write to client"); } else { fprintf(stderr, "divalogd: bad size on write to client"); } return(1); } /* if we're closing the client, go and do it */ if (entry.flags) { close_client(client, NULL); } } return(0); } /* * Wait for an event to happen */ int WaitEvent(void) { struct pollfd fds[MAX_CLIENTS + 2]; /* list of file descriptor info */ unsigned long nfds = 2; /* number of files of interest */ int n; /* number of files active */ int i, j; /* handy scratch counters */ /* set up the poll array */ memset(fds, 0, sizeof(fds)); /* we're interested in reading from server command or klog files */ fds[0].fd = log_fd; fds[0].events = POLLIN; fds[1].fd = server_fd; fds[1].events = POLLIN; while (TRUE) { /* if any client is not up to date then make sure we service it */ nfds = 0; for (i = 0; i < MAX_CLIENTS; i++) { if ((client[i].fd != -1) && (client[i].read_ptr != write_ptr)) { fds[2 + nfds].fd = client[i].fd; fds[2 + nfds].events = POLLOUT; nfds++; } } nfds += 2; /* we always have server and log to service */ if ((n = poll(fds, nfds, timeout)) < 0) { perror("divalogd: poll"); return(1); } if (n == 0) { req_xlogs(); continue; } /* check log device */ if (fds[0].revents & POLLIN) { if (HandleLogEvent(fds[0].fd)) { return(1); } } /* check for server command */ if (fds[1].revents & POLLIN) { if (HandleServerEvent(fds[1].fd)) { return(1); } } /* check for clients that are ready to receive and aren't up to date */ for (i = 2; i < (int) nfds; i++) { if (fds[i].revents & POLLOUT) { /* find the client owning this file descriptor */ for (j = 0; j < MAX_CLIENTS; j++) { if (client[j].fd == fds[i].fd) { if (HandleClientEvent(&client[j])) { return(1); } } } } } } } /* * Display usage of command line */ static void usage(void) { fprintf(stderr, "usage: divalogd [num_entries]\n"); fprintf(stderr, " where num_entries is the number of entries to buffer\n"); fprintf(stderr, " num_entries must be between %d and %d\n", MIN_NUM_ENTRIES, MAX_NUM_ENTRIES); return; } /* * Load the number of entries */ void get_num_entries(char *argv[]) { int i; num_entries = 0; i = 0; while ((i < 10) && (argv[1][i])) { if (!strcmp(argv[1], "-h")) { usage(); exit(1); } if ((argv[1][i] < '0') || (argv[1][i] > '9')) { usage(); fprintf(stderr, "divalogd: non-numeric number of entries\n"); exit(1); } num_entries = (num_entries * 10) + ((int) argv[1][i] - '0'); i++; } if ((i == 10) || (num_entries < MIN_NUM_ENTRIES) || (num_entries > MAX_NUM_ENTRIES)) { usage(); fprintf(stderr, "divalogd: invalid number of entries\n"); exit(1); } return; } /* * start here */ int main(int argc, char *argv[]) { int i; dia_log_t active_logs; if (!((argc == 1) || (argc == 2))) { usage(); fprintf(stderr, "divalogd: invalid command line\n"); exit(1); } if (argc == 2) { get_num_entries(argv); } /* get a buffer for the log entries */ if (!(log = malloc(num_entries * sizeof(klog_t)))) { fprintf(stderr, "divalogd: memory allocation failure\n"); exit(1); } /* open the driver */ if ((log_fd = open(DIVAS_DEVICE_DFS, O_RDONLY)) < 0) if ((log_fd = open(DIVAS_DEVICE, O_RDONLY)) == -1) { perror("divalogd: open of log device"); return 1; } /* tell driver we're interested in logging */ active_logs.card_id = 0; active_logs.log_types = DIVAS_LOG_DEBUG; if (ioctl(log_fd, DIA_IOCTL_LOG, &active_logs) == -1) { perror("divalogd: error in log device"); return 1; } /* open the server's (our) file descriptor */ if (OpenServer(&server_fd)) { return(1); } /* initialise client information */ for (i = 0; i < MAX_CLIENTS; i++) { client[i].fd = -1; } /* wait for something to happen */ if (WaitEvent()) { return(1); } return(0); }