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