source: code/logger.c@ ad8b08d

Last change on this file since ad8b08d was 28c4fd3, checked in by Mike Belopuhov <mike@…>, 11 years ago

Florian has pointed out that logger code suffers from the same problem
as the main code. So first of all don't forget to subtract the amount
of what's already read from the total length; secondly correct the
condition to check partial read.

  • Property mode set to 100644
File size: 7.4 KB
RevLine 
[55923b7]1/*
2 * Copyright (c) 2014 Mike Belopuhov
3 * Copyright (c) 2009 Michael Shalayeff
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER IN
14 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
15 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18#include <sys/param.h>
19#include <sys/socket.h>
[4e66b3a]20#include <sys/stat.h>
[55923b7]21#include <sys/time.h>
[a5893e9]22#include <sys/types.h>
[55923b7]23#include <sys/uio.h>
24#include <errno.h>
25#include <stdlib.h>
26#include <string.h>
27#include <stdio.h>
28#include <unistd.h>
29#include <syslog.h>
30#include <sysexits.h>
[a5893e9]31#include <time.h>
[55923b7]32#include <login_cap.h>
33#include <event.h>
34#include <pwd.h>
35
36#include "icb.h"
37#include "icbd.h"
38
[be3ad87]39void logger_ioerr(struct bufferevent *, short, void *);
40void logger_dispatch(struct bufferevent *, void *);
[4e66b3a]41FILE *logger_open(char *);
[9a2a703]42void logger_tick(void);
[55923b7]43
44struct icbd_logentry {
45 char group[ICB_MAXGRPLEN];
46 char nick[ICB_MAXNICKLEN];
47 size_t length;
48};
49
[4e66b3a]50struct {
51 char group[ICB_MAXGRPLEN];
52 FILE *fp;
53} logfiles[10];
54int nlogfiles;
[a5893e9]55
[4e66b3a]56int logger_pipe;
57
58char file_ts[sizeof "0000-00"];
[9a2a703]59char line_ts[sizeof "00:00"];
[a5893e9]60struct event ev_tick;
61
[3dba97d]62extern char logprefix[MAXPATHLEN/2];
63extern int dologging;
64
[55923b7]65int
66logger_init(void)
67{
[be3ad87]68 struct bufferevent *bev;
[55923b7]69 struct passwd *pw;
70 int pipes[2];
71
72 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipes) == -1) {
[fe81e9a]73 syslog(LOG_ERR, "%s: socketpair: %m", __func__);
[55923b7]74 exit(EX_OSERR);
75 }
76
77 switch (fork()) {
78 case -1:
[fe81e9a]79 syslog(LOG_ERR, "%s: fork: %m", __func__);
[55923b7]80 exit(EX_OSERR);
81 case 0:
82 break;
83
84 default:
85 close(pipes[1]);
86 logger_pipe = pipes[0];
87 return (0);
88 }
89
90 setproctitle("logger");
91 close(pipes[0]);
92
93 if ((pw = getpwnam(ICBD_USER)) == NULL) {
[fe81e9a]94 syslog(LOG_ERR, "%s: No passwd entry for %s", __func__,
95 ICBD_USER);
[55923b7]96 exit(EX_NOUSER);
97 }
98
99 if (setusercontext(NULL, pw, pw->pw_uid,
100 LOGIN_SETALL & ~LOGIN_SETUSER) < 0)
101 exit(EX_NOPERM);
102
[1dd3554]103 if (chroot(pw->pw_dir) < 0) {
[fe81e9a]104 syslog(LOG_ERR, "%s: %s: %m", __func__, pw->pw_dir);
[1dd3554]105 exit(EX_UNAVAILABLE);
106 }
107
108 if (chdir("/") < 0) {
[fe81e9a]109 syslog(LOG_ERR, "%s: chdir: %m", __func__);
[55923b7]110 exit(EX_UNAVAILABLE);
111 }
112
[e54f151]113 if (setuid(pw->pw_uid) < 0) {
[fe81e9a]114 syslog(LOG_ERR, "%s: %d: %m", __func__, pw->pw_uid);
[e54f151]115 exit(EX_NOPERM);
116 }
117
[55923b7]118 event_init();
119
120 /* event for message processing */
[be3ad87]121 if ((bev = bufferevent_new(pipes[1], logger_dispatch, NULL,
122 logger_ioerr, NULL)) == NULL) {
[fe81e9a]123 syslog(LOG_ERR, "%s: bufferevent_new: %m", __func__);
[be3ad87]124 exit(EX_UNAVAILABLE);
125 }
126 if (bufferevent_enable(bev, EV_READ)) {
[fe81e9a]127 syslog(LOG_ERR, "%s: bufferevent_enable: %m", __func__);
[be3ad87]128 bufferevent_free(bev);
129 exit(EX_UNAVAILABLE);
[55923b7]130 }
131
[9a2a703]132 evtimer_set(&ev_tick, (void (*)(int, short, void *))logger_tick, NULL);
133 logger_tick();
[55923b7]134 return event_dispatch();
135}
136
137void
[be3ad87]138logger_ioerr(struct bufferevent *bev __attribute__((__unused__)), short what,
139 void *arg __attribute__((__unused__)))
[55923b7]140{
[be3ad87]141 const char *cause = NULL;
142
143 if (what & EVBUFFER_TIMEOUT)
144 cause = "timeout";
145 else if (what & EVBUFFER_EOF)
146 cause = "eof";
147 else if (what & EVBUFFER_ERROR)
148 cause = what & EVBUFFER_READ ? "read" : "write";
[fe81e9a]149 syslog(LOG_ERR, "%s: %s", __func__, cause ? cause : "unknown");
[be3ad87]150 exit(EX_IOERR);
151}
152
153void
154logger_dispatch(struct bufferevent *bev, void *arg __attribute__((unused)))
155{
156 struct icbd_logentry *e;
157 static char buf[sizeof *e + ICB_MSGSIZE];
158 static size_t nread = 0;
[4e66b3a]159 FILE *fp = NULL;
[be3ad87]160 size_t res;
161 char *m;
[4e66b3a]162 int i;
[55923b7]163
[be3ad87]164 e = (struct icbd_logentry *)buf;
165 m = buf + sizeof *e;
166
167 while (EVBUFFER_LENGTH(EVBUFFER_INPUT(bev)) > 0) {
168 if (nread == 0) {
169 bzero(e, sizeof *e);
170 /* read the log entry header */
171 res = bufferevent_read(bev, &buf[0], sizeof *e);
172 nread += res;
173 if (nread < sizeof *e)
174 return;
175 }
176 /* see if we got the whole header */
177 if (nread < sizeof *e) {
178 /* finish reading */
179 res = bufferevent_read(bev, &buf[nread],
180 sizeof *e - nread);
181 nread += res;
182 if (nread < sizeof *e)
183 return;
184 }
[28c4fd3]185 if (e->length >= ICB_MSGSIZE) {
186 syslog(LOG_ERR, "%s: message too big: %lu", __func__,
187 e->length);
188 exit(EX_DATAERR);
189 }
[be3ad87]190 /* fetch the message */
191 res = bufferevent_read(bev, &buf[nread],
[28c4fd3]192 e->length - (nread - sizeof *e));
[be3ad87]193 nread += res;
194#ifdef DEBUG
195 {
196 printf("logger read %lu out of %lu:\n", res, e->length);
197 for (i = 0; i < (int)res; i++)
198 printf(" %02x", (unsigned char)m[i]);
199 printf("\n");
200 }
201#endif
[28c4fd3]202 if (nread - sizeof *e < e->length)
[be3ad87]203 return;
204 /* terminate the buffer */
[a2fadb4]205 m[MIN(nread - sizeof *e, ICB_MSGSIZE - 1)] = '\0';
[be3ad87]206 /* find the appropriate log file */
207 for (i = 0; i < nlogfiles; i++)
208 if (strcmp(logfiles[i].group, e->group) == 0)
209 fp = logfiles[i].fp;
210 if (!fp && (fp = logger_open(e->group)) == NULL)
211 return;
212 if (strlen(e->nick) == 0)
213 fprintf(fp, "[%s] %s\n", line_ts, m);
214 else
215 fprintf(fp, "[%s] <%s> %s\n", line_ts, e->nick, m);
216 /* get ready for the next message */
217 nread = 0;
[55923b7]218 }
[4e66b3a]219}
220
221FILE *
222logger_open(char *group)
223{
224 char path[MAXPATHLEN];
225 FILE *fp = NULL;
[55923b7]226
[058b664]227 /* make sure not to overflow the logfiles table */
[fe81e9a]228 if (nlogfiles == nitems(logfiles)) {
229 syslog(LOG_NOTICE, "%s: logfiles table is full", __func__);
[058b664]230 return (NULL);
[fe81e9a]231 }
[3dba97d]232 snprintf(path, sizeof path, "%s/%s", logprefix, group);
233 if (mkdir(path, 0755) < 0 && errno != EEXIST) {
[fe81e9a]234 syslog(LOG_ERR, "%s: %s: %m", __func__, group);
[4e66b3a]235 return (NULL);
236 }
[3dba97d]237 snprintf(path, sizeof path, "%s/%s/%s", logprefix, group, file_ts);
[4e66b3a]238 if ((fp = fopen(path, "a")) == NULL) {
[fe81e9a]239 syslog(LOG_ERR, "%s: %s: %m", __func__, path);
[4e66b3a]240 return (NULL);
241 }
242 setvbuf(fp, NULL, _IOLBF, 0);
243 if (verbose)
[fe81e9a]244 syslog(LOG_NOTICE, "%s: %s", __func__, path);
[4e66b3a]245 strlcpy(logfiles[nlogfiles].group, group, ICB_MAXGRPLEN);
246 logfiles[nlogfiles++].fp = fp;
247 return (fp);
[55923b7]248}
249
250void
[45bd56a]251logger(char *group, char *nick, char *what)
[55923b7]252{
253 struct icbd_logentry e;
254 struct iovec iov[2];
[d06af04]255 const char *defgrp = "1";
[55923b7]256
[3dba97d]257 if (!dologging)
258 return;
259
[d06af04]260 if (strcmp(group, defgrp) == 0)
261 return;
262
[55923b7]263 strlcpy(e.group, group, ICB_MAXGRPLEN);
264 strlcpy(e.nick, nick, ICB_MAXNICKLEN);
265 e.length = strlen(what) + 1;
266
267 iov[0].iov_base = &e;
268 iov[0].iov_len = sizeof e;
269
270 iov[1].iov_base = what;
271 iov[1].iov_len = e.length;
272
273 if (writev(logger_pipe, iov, 2) == -1)
[fe81e9a]274 syslog(LOG_ERR, "%s: %m", __func__);
[55923b7]275}
[a5893e9]276
277void
[9a2a703]278logger_tick(void)
[a5893e9]279{
[3fdebb8]280 static int last_mon = -1, last_mday = -1;
[4e66b3a]281 struct timeval tv = { 60, 0 };
[3fdebb8]282 char buf[128];
[a5893e9]283 struct tm *tm;
284 time_t t;
[c1888a5]285 int i;
[a5893e9]286
[4e66b3a]287 time(&t);
[a5893e9]288 tm = gmtime(&t);
[c1888a5]289 if (last_mon != tm->tm_mon) {
[fe81e9a]290 snprintf(file_ts, sizeof file_ts, "%04d-%02d",
291 tm->tm_year + 1900, tm->tm_mon + 1);
[c1888a5]292 last_mon = tm->tm_mon;
[9a2a703]293 /* rotate log files */
[c1888a5]294 for (i = 0; i < nlogfiles; i++) {
295 fclose(logfiles[i].fp);
296 logfiles[i].fp = NULL;
297 }
298 nlogfiles = 0;
299 }
[3fdebb8]300 if (tm->tm_mday != last_mday) {
301 strftime(buf, sizeof(buf),
302 "Today is %a %b %e %Y %H:%M %Z (%z)", tm);
303 for (i = 0; i < nlogfiles; i++)
304 fprintf(logfiles[i].fp, "%s\n", buf);
305 last_mday = tm->tm_mday;
306 }
[9a2a703]307 snprintf(line_ts, sizeof line_ts, "%02d:%02d", tm->tm_hour,
[a5893e9]308 tm->tm_min);
[9a2a703]309 if (evtimer_add(&ev_tick, &tv) < 0) {
[fe81e9a]310 syslog(LOG_ERR, "%s: evtimer_add: %m", __func__);
[28c4fd3]311 exit(EX_UNAVAILABLE);
[9a2a703]312 }
[a5893e9]313}
Note: See TracBrowser for help on using the repository browser.