syslogd.c 15 KB

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