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 #include "toys.h" 37 38 // UNIX Sockets for listening 39 struct 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. 47 struct 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 56 GLOBALS( 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 74 int 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 85 static 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 */ 98 static 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. 152 static 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. 236 static 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 278 static 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. 314 static 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 */ 376 static 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 399 static 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 405 void 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 418 init_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 } 542 clean_and_exit: 543 logmsg("<46>syslogd exiting", 19); 544 if (CFG_TOYBOX_FREE ) cleanup(); 545 } 546