toybox/toys/pending/syslogd.c
<<
>>
Prefs
   1/* syslogd.c - a system logging utility.
   2 *
   3 * Copyright 2013 Madhur Verma <mad.flexi@gmail.com>
   4 * Copyright 2013 Kyungwan Han <asura321@gmail.com>
   5 *
   6 * No Standard
   7
   8USE_SYSLOGD(NEWTOY(syslogd,">0l#<1>8=8R:b#<0>99=1s#<0=200m#<0>71582787=20O:p:f:a:nSKLD", TOYFLAG_SBIN|TOYFLAG_STAYROOT))
   9
  10config SYSLOGD
  11  bool "syslogd"
  12  default n
  13  help
  14  usage: syslogd  [-a socket] [-O logfile] [-f config file] [-m interval]
  15                  [-p socket] [-s SIZE] [-b N] [-R HOST] [-l N] [-nSLKD]
  16
  17  System logging utility
  18
  19  -a      Extra unix socket for listen
  20  -O FILE Default log file <DEFAULT: /var/log/messages>
  21  -f FILE Config file <DEFAULT: /etc/syslog.conf>
  22  -p      Alternative unix domain socket <DEFAULT : /dev/log>
  23  -n      Avoid auto-backgrounding
  24  -S      Smaller output
  25  -m MARK interval <DEFAULT: 20 minutes> (RANGE: 0 to 71582787)
  26  -R HOST Log to IP or hostname on PORT (default PORT=514/UDP)"
  27  -L      Log locally and via network (default is network only if -R)"
  28  -s SIZE Max size (KB) before rotation (default:200KB, 0=off)
  29  -b N    rotated logs to keep (default:1, max=99, 0=purge)
  30  -K      Log to kernel printk buffer (use dmesg to read it)
  31  -l N    Log only messages more urgent than prio(default:8 max:8 min:1)
  32  -D      Drop duplicates
  33*/
  34
  35#define FOR_syslogd
  36#include "toys.h"
  37
  38// UNIX Sockets for listening
  39struct unsocks {
  40  struct unsocks *next;
  41  char *path;
  42  struct sockaddr_un sdu;
  43  int sd;
  44};
  45
  46// Log file entry to log into.
  47struct logfile {
  48  struct logfile *next;
  49  char *filename;
  50  uint32_t facility[8];
  51  uint8_t level[LOG_NFACILITIES];
  52  int logfd;
  53  struct sockaddr_in saddr;
  54};
  55
  56GLOBALS(
  57  char *socket;
  58  char *config_file;
  59  char *unix_socket;
  60  char *logfile;
  61  long interval;
  62  long rot_size;
  63  long rot_count;
  64  char *remote_log;
  65  long log_prio;
  66
  67  struct unsocks *lsocks;  // list of listen sockets
  68  struct logfile *lfiles;  // list of write logfiles
  69  int sigfd[2];
  70)
  71
  72// Lookup numerical code from name
  73// Also used in logger
  74int logger_lookup(int where, char *key)
  75{
  76  CODE *w = ((CODE *[]){facilitynames, prioritynames})[where];
  77
  78  for (; w->c_name; w++)
  79    if (!strcasecmp(key, w->c_name)) return w->c_val;
  80
  81  return -1;
  82}
  83
  84//search the given name and return its value
  85static char *dec(int val, CODE *clist, char *buf)
  86{
  87  for (; clist->c_name; clist++) 
  88    if (val == clist->c_val) return clist->c_name;
  89  sprintf(buf, "%u", val);
  90
  91  return buf;
  92}
  93
  94/*
  95 * recurses the logfile list and resolves config
  96 * for evry file and updates facilty and log level bits.
  97 */
  98static int resolve_config(struct logfile *file, char *config)
  99{
 100  char *tk;
 101
 102  for (tk = strtok(config, "; \0"); tk; tk = strtok(NULL, "; \0")) {
 103    char *fac = tk, *lvl;
 104    int i = 0;
 105    unsigned facval = 0;
 106    uint8_t set, levval, bits = 0;
 107
 108    tk = strchr(fac, '.');
 109    if (!tk) return -1;
 110    *tk = '\0';
 111    lvl = tk + 1;
 112
 113    for (;;) {
 114      char *nfac = strchr(fac, ',');
 115
 116      if (nfac) *nfac = '\0';
 117      if (*fac == '*') {
 118        facval = 0xFFFFFFFF;
 119        if (fac[1]) return -1;
 120      } else {
 121        if ((i = logger_lookup(0, fac)) == -1) return -1;
 122        facval |= (1 << LOG_FAC(i));
 123      }
 124      if (nfac) fac = nfac + 1;
 125      else break;
 126    }
 127
 128    levval = 0;
 129    for (tk = "!=*"; *tk; tk++, bits <<= 1) {
 130      if (*lvl == *tk) {
 131        bits++;
 132        lvl++;
 133      }
 134    }
 135    if (bits & 2) levval = 0xff;
 136    if (*lvl) {
 137      if ((i = logger_lookup(1, lvl)) == -1) return -1;
 138      levval |= (bits & 4) ? LOG_MASK(i) : LOG_UPTO(i);
 139      if (bits & 8) levval = ~levval;
 140    }
 141
 142    for (i = 0, set = levval; set; set >>= 1, i++)
 143      if (set & 0x1) file->facility[i] |= ~facval;
 144    for (i = 0; i < LOG_NFACILITIES; facval >>= 1, i++)
 145      if (facval & 0x1) file->level[i] |= ~levval;
 146  }
 147
 148  return 0;
 149}
 150
 151// Parse config file and update the log file list.
 152static int parse_config_file(void)
 153{
 154  struct logfile *file;
 155  FILE *fp;
 156  char *confline, *tk[2];
 157  int len, lineno = 0;
 158  size_t linelen;
 159  /*
 160   * if -K then open only /dev/kmsg
 161   * all other log files are neglected
 162   * thus no need to open config either.
 163   */
 164  if (toys.optflags & FLAG_K) {
 165    file = xzalloc(sizeof(struct logfile));
 166    file->filename = xstrdup("/dev/kmsg");
 167    TT.lfiles = file;
 168    return 0;
 169  }
 170  /*
 171   * if -R then add remote host to log list
 172   * if -L is not provided all other log
 173   * files are neglected thus no need to
 174   * open config either so just return.
 175   */
 176  if (toys.optflags & FLAG_R) {
 177    file = xzalloc(sizeof(struct logfile));
 178    file->filename = xmprintf("@%s",TT.remote_log);
 179    TT.lfiles = file;
 180    if (!(toys.optflags & FLAG_L)) return 0;
 181  }
 182  /*
 183   * Read config file and add logfiles to the list
 184   * with their configuration.
 185   */
 186  if (!(fp = fopen(TT.config_file, "r")) && (toys.optflags & FLAG_f))
 187    perror_exit("can't open '%s'", TT.config_file);
 188
 189  for (linelen = 0; fp;) {
 190    confline = NULL;
 191    len = getline(&confline, &linelen, fp);
 192    if (len <= 0) break;
 193    lineno++;
 194    for (; *confline == ' '; confline++, len--) ;
 195    if ((confline[0] == '#') || (confline[0] == '\n')) continue;
 196    tk[0] = confline;
 197    for (; len && !(*tk[0]==' ' || *tk[0]=='\t'); tk[0]++, len--);
 198    for (tk[1] = tk[0]; len && (*tk[1]==' ' || *tk[1]=='\t'); tk[1]++, len--);
 199    if (!len || (len == 1 && *tk[1] == '\n')) {
 200      error_msg("error in '%s' at line %d", TT.config_file, lineno);
 201      return -1;
 202    }
 203    else if (*(tk[1] + len - 1) == '\n') *(tk[1] + len - 1) = '\0';
 204    *tk[0] = '\0';
 205    if (*tk[1] != '*') {
 206      file = TT.lfiles;
 207      while (file && strcmp(file->filename, tk[1])) file = file->next;
 208      if (!file) {
 209        file = xzalloc(sizeof(struct logfile));
 210        file->filename = xstrdup(tk[1]);
 211        file->next = TT.lfiles;
 212        TT.lfiles = file;
 213      }
 214      if (resolve_config(file, confline) == -1) {
 215        error_msg("error in '%s' at line %d", TT.config_file, lineno);
 216        return -1;
 217      }
 218    }
 219    free(confline);
 220  }
 221  /*
 222   * Can't open config file or support is not enabled
 223   * adding default logfile to the head of list.
 224   */
 225  if (!fp){
 226    file = xzalloc(sizeof(struct logfile));
 227    file->filename = xstrdup((toys.optflags & FLAG_O) ?
 228                     TT.logfile : "/var/log/messages"); //DEFLOGFILE
 229    file->next = TT.lfiles;
 230    TT.lfiles = file;
 231  } else fclose(fp);
 232  return 0;
 233}
 234
 235// open every log file in list.
 236static void open_logfiles(void)
 237{
 238  struct logfile *tfd;
 239
 240  for (tfd = TT.lfiles; tfd; tfd = tfd->next) {
 241    char *p, *tmpfile;
 242    long port = 514;
 243
 244    if (*tfd->filename == '@') { // network
 245      struct addrinfo *info, rp;
 246
 247      tmpfile = xstrdup(tfd->filename + 1);
 248      if ((p = strchr(tmpfile, ':'))) {
 249        char *endptr;
 250
 251        *p = '\0';
 252        port = strtol(++p, &endptr, 10);
 253        if (*endptr || endptr == p || port < 0 || port > 65535)
 254          error_exit("bad port in %s", tfd->filename);
 255      }
 256      memset(&rp, 0, sizeof(rp));
 257      rp.ai_family = AF_INET;
 258      rp.ai_socktype = SOCK_DGRAM;
 259      rp.ai_protocol = IPPROTO_UDP;
 260
 261      if (getaddrinfo(tmpfile, NULL, &rp, &info) || !info) 
 262        perror_exit("BAD ADDRESS: can't find : %s ", tmpfile);
 263      ((struct sockaddr_in*)info->ai_addr)->sin_port = htons(port);
 264      memcpy(&tfd->saddr, info->ai_addr, info->ai_addrlen);
 265      freeaddrinfo(info);
 266
 267      tfd->logfd = xsocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
 268      free(tmpfile);
 269    } else tfd->logfd = open(tfd->filename, O_CREAT | O_WRONLY | O_APPEND, 0666);
 270    if (tfd->logfd < 0) {
 271      tfd->filename = "/dev/console";
 272      tfd->logfd = open(tfd->filename, O_APPEND);
 273    }
 274  }
 275}
 276
 277//write to file with rotation
 278static int write_rotate(struct logfile *tf, int len)
 279{
 280  int size, isreg;
 281  struct stat statf;
 282  isreg = (!fstat(tf->logfd, &statf) && S_ISREG(statf.st_mode));
 283  size = statf.st_size;
 284
 285  if ((toys.optflags & FLAG_s) || (toys.optflags & FLAG_b)) {
 286    if (TT.rot_size && isreg && (size + len) > (TT.rot_size*1024)) {
 287      if (TT.rot_count) { /* always 0..99 */
 288        int i = strlen(tf->filename) + 3 + 1;
 289        char old_file[i];
 290        char new_file[i];
 291        i = TT.rot_count - 1;
 292        while (1) {
 293          sprintf(new_file, "%s.%d", tf->filename, i);
 294          if (!i) break;
 295          sprintf(old_file, "%s.%d", tf->filename, --i);
 296          rename(old_file, new_file);
 297        }
 298        rename(tf->filename, new_file);
 299        unlink(tf->filename);
 300        close(tf->logfd);
 301        tf->logfd = open(tf->filename, O_CREAT | O_WRONLY | O_APPEND, 0666);
 302        if (tf->logfd < 0) {
 303          perror_msg("can't open %s", tf->filename);
 304          return -1;
 305        }
 306      }
 307      ftruncate(tf->logfd, 0);
 308    }
 309  }
 310  return write(tf->logfd, toybuf, len);
 311}
 312
 313//Parse messege and write to file.
 314static void logmsg(char *msg, int len)
 315{
 316  time_t now;
 317  char *p, *ts, *lvlstr, *facstr;
 318  struct utsname uts;
 319  int pri = 0;
 320  struct logfile *tf = TT.lfiles;
 321
 322  char *omsg = msg;
 323  int olen = len, fac, lvl;
 324  
 325  if (*msg == '<') { // Extract the priority no.
 326    pri = (int) strtoul(msg + 1, &p, 10);
 327    if (*p == '>') msg = p + 1;
 328  }
 329  /* Jan 18 00:11:22 msg...
 330   * 01234567890123456
 331   */
 332  if (len < 16 || msg[3] != ' ' || msg[6] != ' ' || msg[9] != ':'
 333      || msg[12] != ':' || msg[15] != ' ') {
 334    time(&now);
 335    ts = ctime(&now) + 4; /* skip day of week */
 336  } else {
 337    now = 0;
 338    ts = msg;
 339    msg += 16;
 340  }
 341  ts[15] = '\0';
 342  fac = LOG_FAC(pri);
 343  lvl = LOG_PRI(pri);
 344
 345  if (toys.optflags & FLAG_K) len = sprintf(toybuf, "<%d> %s\n", pri, msg);
 346  else {
 347    char facbuf[12], pribuf[12];
 348
 349    facstr = dec(pri & LOG_FACMASK, facilitynames, facbuf);
 350    lvlstr = dec(LOG_PRI(pri), prioritynames, pribuf);
 351
 352    p = "local";
 353    if (!uname(&uts)) p = uts.nodename;
 354    if (toys.optflags & FLAG_S) len = sprintf(toybuf, "%s %s\n", ts, msg);
 355    else len = sprintf(toybuf, "%s %s %s.%s %s\n", ts, p, facstr, lvlstr, msg);
 356  }
 357  if (lvl >= TT.log_prio) return;
 358
 359  for (; tf; tf = tf->next) {
 360    if (tf->logfd > 0) {
 361      if (!((tf->facility[lvl] & (1 << fac)) || (tf->level[fac] & (1<<lvl)))) {
 362        int wlen, isNetwork = *tf->filename == '@';
 363        if (isNetwork)
 364          wlen = sendto(tf->logfd, omsg, olen, 0, (struct sockaddr*)&tf->saddr, sizeof(tf->saddr));
 365        else wlen = write_rotate(tf, len);
 366        if (wlen < 0) perror_msg("write failed file : %s ", tf->filename + isNetwork);
 367      }
 368    }
 369  }
 370}
 371
 372/*
 373 * closes all read and write fds
 374 * and frees all nodes and lists
 375 */
 376static void cleanup(void)
 377{
 378  while (TT.lsocks) {
 379    struct unsocks *fnode = TT.lsocks;
 380
 381    if (fnode->sd >= 0) {
 382      close(fnode->sd);
 383      unlink(fnode->path);
 384    }
 385    TT.lsocks = fnode->next;
 386    free(fnode);
 387  }
 388
 389  while (TT.lfiles) {
 390    struct logfile *fnode = TT.lfiles;
 391
 392    free(fnode->filename);
 393    if (fnode->logfd >= 0) close(fnode->logfd);
 394    TT.lfiles = fnode->next;
 395    free(fnode);
 396  }
 397}
 398
 399static void signal_handler(int sig)
 400{
 401  unsigned char ch = sig;
 402  if (write(TT.sigfd[1], &ch, 1) != 1) error_msg("can't send signal");
 403}
 404
 405void syslogd_main(void)
 406{
 407  struct unsocks *tsd;
 408  int nfds, retval, last_len=0;
 409  struct timeval tv;
 410  fd_set rfds;        // fds for reading
 411  char *temp, *buffer = (toybuf +2048), *last_buf = (toybuf + 3072); //these two buffs are of 1K each
 412
 413  if ((toys.optflags & FLAG_p) && (strlen(TT.unix_socket) > 108))
 414    error_exit("Socket path should not be more than 108");
 415
 416  TT.config_file = (toys.optflags & FLAG_f) ?
 417                   TT.config_file : "/etc/syslog.conf"; //DEFCONFFILE
 418init_jumpin:
 419  tsd = xzalloc(sizeof(struct unsocks));
 420
 421  tsd->path = (toys.optflags & FLAG_p) ? TT.unix_socket : "/dev/log"; // DEFLOGSOCK
 422  TT.lsocks = tsd;
 423
 424  if (toys.optflags & FLAG_a) {
 425    for (temp = strtok(TT.socket, ":"); temp; temp = strtok(NULL, ":")) {
 426      if (strlen(temp) > 107) temp[108] = '\0';
 427      tsd = xzalloc(sizeof(struct unsocks));
 428      tsd->path = temp;
 429      tsd->next = TT.lsocks;
 430      TT.lsocks = tsd;
 431    }
 432  }
 433  /*
 434   * initializes unsock_t structure
 435   * and opens socket for reading
 436   * and adds to global lsock list.
 437  */
 438  nfds = 0;
 439  for (tsd = TT.lsocks; tsd; tsd = tsd->next) {
 440    tsd->sdu.sun_family = AF_UNIX;
 441    strcpy(tsd->sdu.sun_path, tsd->path);
 442    tsd->sd = socket(AF_UNIX, SOCK_DGRAM, 0);
 443    if (tsd->sd < 0) {
 444      perror_msg("OPEN SOCKS : failed");
 445      continue;
 446    }
 447    unlink(tsd->sdu.sun_path);
 448    if (bind(tsd->sd, (struct sockaddr *) &tsd->sdu, sizeof(tsd->sdu))) {
 449      perror_msg("BIND SOCKS : failed sock : %s", tsd->sdu.sun_path);
 450      close(tsd->sd);
 451      continue;
 452    }
 453    chmod(tsd->path, 0777);
 454    nfds++;
 455  }
 456  if (!nfds) {
 457    error_msg("Can't open single socket for listenning.");
 458    goto clean_and_exit;
 459  }
 460
 461  // Setup signals
 462  xpipe(TT.sigfd);
 463
 464  fcntl(TT.sigfd[1] , F_SETFD, FD_CLOEXEC);
 465  fcntl(TT.sigfd[0] , F_SETFD, FD_CLOEXEC);
 466  int flags = fcntl(TT.sigfd[1], F_GETFL);
 467  fcntl(TT.sigfd[1], F_SETFL, flags | O_NONBLOCK);
 468  signal(SIGHUP, signal_handler);
 469  signal(SIGTERM, signal_handler);
 470  signal(SIGINT, signal_handler);
 471  signal(SIGQUIT, signal_handler);
 472
 473  if (parse_config_file() == -1) goto clean_and_exit;
 474  open_logfiles();
 475  if (!(toys.optflags & FLAG_n)) {
 476    daemon(0, 0);
 477    //don't daemonize again if SIGHUP received.
 478    toys.optflags |= FLAG_n;
 479  }
 480  xpidfile("syslogd");
 481
 482  logmsg("<46>Toybox: syslogd started", 27); //27 : the length of message
 483  for (;;) {
 484    // Add opened socks to rfds for select()
 485    FD_ZERO(&rfds);
 486    for (tsd = TT.lsocks; tsd; tsd = tsd->next) FD_SET(tsd->sd, &rfds);
 487    FD_SET(TT.sigfd[0], &rfds);
 488    tv.tv_usec = 0;
 489    tv.tv_sec = TT.interval*60;
 490
 491    retval = select(TT.sigfd[0] + 1, &rfds, NULL, NULL, (TT.interval)?&tv:NULL);
 492    if (retval < 0) {
 493      if (errno != EINTR) perror_msg("Error in select ");
 494    }
 495    else if (!retval) logmsg("<46>-- MARK --", 14);
 496    else if (FD_ISSET(TT.sigfd[0], &rfds)) { /* May be a signal */
 497      unsigned char sig;
 498
 499      if (read(TT.sigfd[0], &sig, 1) != 1) {
 500        error_msg("signal read failed.\n");
 501        continue;
 502      }
 503      switch(sig) {
 504        case SIGTERM:    /* FALLTHROUGH */
 505        case SIGINT:     /* FALLTHROUGH */
 506        case SIGQUIT:
 507          logmsg("<46>syslogd exiting", 19);
 508          if (CFG_TOYBOX_FREE ) cleanup();
 509          signal(sig, SIG_DFL);
 510          sigset_t ss;
 511          sigemptyset(&ss);
 512          sigaddset(&ss, sig);
 513          sigprocmask(SIG_UNBLOCK, &ss, NULL);
 514          raise(sig);
 515          _exit(1);  /* Should not reach it */
 516          break;
 517        case SIGHUP:
 518          logmsg("<46>syslogd exiting", 19);
 519          cleanup(); //cleanup is done, as we restart syslog.
 520          goto init_jumpin;
 521        default: break;
 522      }
 523    } else { /* Some activity on listen sockets. */
 524      for (tsd = TT.lsocks; tsd; tsd = tsd->next) {
 525        int sd = tsd->sd;
 526        if (FD_ISSET(sd, &rfds)) {
 527          int len = read(sd, buffer, 1023); //buffer is of 1K, hence readingonly 1023 bytes, 1 for NUL
 528          if (len > 0) {
 529            buffer[len] = '\0';
 530            if((toys.optflags & FLAG_D) && (len == last_len))
 531              if (!memcmp(last_buf, buffer, len)) break;
 532
 533            memcpy(last_buf, buffer, len);
 534            last_len = len;
 535            logmsg(buffer, len);
 536          }
 537          break;
 538        }
 539      }
 540    }
 541  }
 542clean_and_exit:
 543  logmsg("<46>syslogd exiting", 19);
 544  if (CFG_TOYBOX_FREE ) cleanup();
 545}
 546