1 | /* |
---|
2 | * Copyright (c) 1999-2003 Red Hat, Inc. All rights reserved. |
---|
3 | * |
---|
4 | * This program is free software; you can redistribute it and/or modify |
---|
5 | * it under the terms of the GNU General Public License, version 2, |
---|
6 | * as published by the Free Software Foundation. |
---|
7 | * |
---|
8 | * This program is distributed in the hope that it will be useful, |
---|
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
11 | * GNU General Public License for more details. |
---|
12 | * |
---|
13 | * You should have received a copy of the GNU General Public License |
---|
14 | * along with this program; if not, write to the Free Software |
---|
15 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA |
---|
16 | * |
---|
17 | */ |
---|
18 | |
---|
19 | #include <ctype.h> |
---|
20 | #include <errno.h> |
---|
21 | #include <fcntl.h> |
---|
22 | #include <libintl.h> |
---|
23 | #include <locale.h> |
---|
24 | #include <stdio.h> |
---|
25 | #include <stdlib.h> |
---|
26 | #include <string.h> |
---|
27 | #include <unistd.h> |
---|
28 | |
---|
29 | #define SYSLOG_NAMES |
---|
30 | #include <syslog.h> |
---|
31 | |
---|
32 | #include <sys/socket.h> |
---|
33 | #include <sys/stat.h> |
---|
34 | #include <sys/un.h> |
---|
35 | #include <sys/wait.h> |
---|
36 | |
---|
37 | #define _(String) gettext((String)) |
---|
38 | |
---|
39 | #include <popt.h> |
---|
40 | |
---|
41 | #include <regex.h> |
---|
42 | |
---|
43 | #include "initlog.h" |
---|
44 | #include "process.h" |
---|
45 | |
---|
46 | static int logfacility=LOG_DAEMON; |
---|
47 | static int logpriority=LOG_NOTICE; |
---|
48 | static int reexec=0; |
---|
49 | static int quiet=0; |
---|
50 | int debug=0; |
---|
51 | |
---|
52 | regex_t **regList = NULL; |
---|
53 | |
---|
54 | static int logEntries = 0; |
---|
55 | struct logInfo *logData = NULL; |
---|
56 | |
---|
57 | void readConfiguration(char *fname) { |
---|
58 | int fd,num=0; |
---|
59 | struct stat sbuf; |
---|
60 | char *data,*line, *d; |
---|
61 | regex_t *regexp; |
---|
62 | int lfac=-1,lpri=-1; |
---|
63 | |
---|
64 | if ((fd=open(fname,O_RDONLY))==-1) return; |
---|
65 | if (fstat(fd,&sbuf)) { |
---|
66 | close(fd); |
---|
67 | return; |
---|
68 | } |
---|
69 | d = data=malloc(sbuf.st_size+1); |
---|
70 | if (read(fd,data,sbuf.st_size)!=sbuf.st_size) { |
---|
71 | close(fd); |
---|
72 | free(data); |
---|
73 | return; |
---|
74 | } |
---|
75 | close(fd); |
---|
76 | data[sbuf.st_size] = '\0'; |
---|
77 | while ((line=getLine(&data))) { |
---|
78 | if (line[0]=='#') continue; |
---|
79 | if (!strncmp(line,"ignore ",7)) { |
---|
80 | regexp = malloc(sizeof(regex_t)); |
---|
81 | if (!regcomp(regexp,line+7,REG_EXTENDED|REG_NOSUB|REG_NEWLINE)) { |
---|
82 | regList = realloc(regList,(num+2) * sizeof(regex_t *)); |
---|
83 | regList[num] = regexp; |
---|
84 | regList[num+1] = NULL; |
---|
85 | num++; |
---|
86 | } |
---|
87 | } |
---|
88 | if (!strncmp(line,"facility ",9)) { |
---|
89 | lfac=atoi(line+9); |
---|
90 | if ((lfac == 0) && strcmp(line+9,"0")) { |
---|
91 | int x =0; |
---|
92 | |
---|
93 | lfac = LOG_DAEMON; |
---|
94 | for (x=0;facilitynames[x].c_name;x++) { |
---|
95 | if (!strcmp(line+9,facilitynames[x].c_name)) { |
---|
96 | lfac = facilitynames[x].c_val; |
---|
97 | break; |
---|
98 | } |
---|
99 | } |
---|
100 | } |
---|
101 | } |
---|
102 | if (!strncmp(line,"priority ",9)) { |
---|
103 | lpri = atoi(line+9); |
---|
104 | if ((lpri == 0) && strcmp(line+9,"0")) { |
---|
105 | int x=0; |
---|
106 | |
---|
107 | lpri = LOG_NOTICE; |
---|
108 | for (x=0;prioritynames[x].c_name;x++) { |
---|
109 | if (!strcmp(line+9,prioritynames[x].c_name)) { |
---|
110 | lpri = prioritynames[x].c_val; |
---|
111 | break; |
---|
112 | } |
---|
113 | } |
---|
114 | } |
---|
115 | } |
---|
116 | } |
---|
117 | if (lfac!=-1) logfacility=lfac; |
---|
118 | if (lpri!=-1) logpriority=lpri; |
---|
119 | free(d); |
---|
120 | } |
---|
121 | |
---|
122 | char *getLine(char **data) { |
---|
123 | /* Get one line from data */ |
---|
124 | /* Anything up to a carraige return (\r) or a backspace (\b) is discarded. */ |
---|
125 | /* If this really bothers you, mail me and I might make it configurable. */ |
---|
126 | /* It's here to avoid confilcts with fsck's progress bar. */ |
---|
127 | char *x, *y; |
---|
128 | |
---|
129 | if (!*data) return NULL; |
---|
130 | x=*data; |
---|
131 | while (*x && (*x != '\n')) { |
---|
132 | while (*x && (*x != '\n') && (*x != '\r') && (*x != '\b')) x++; |
---|
133 | if (*x && (*x=='\r' || *x =='\b')) { |
---|
134 | *data = x+1; |
---|
135 | x++; |
---|
136 | } |
---|
137 | } |
---|
138 | if (*x) { |
---|
139 | x++; |
---|
140 | } else { |
---|
141 | if (x-*data) { |
---|
142 | y=malloc(x-*data+1); |
---|
143 | y[x-*data] = 0; |
---|
144 | y[x-*data-1] = '\n'; |
---|
145 | memcpy(y,*data,x-*data); |
---|
146 | } else { |
---|
147 | y=NULL; |
---|
148 | } |
---|
149 | *data = NULL; |
---|
150 | return y; |
---|
151 | } |
---|
152 | y = malloc(x-*data); |
---|
153 | y[x-*data-1] = 0; |
---|
154 | memcpy(y,*data,x-*data-1); |
---|
155 | *data = x; |
---|
156 | return y; |
---|
157 | } |
---|
158 | |
---|
159 | char **toArray(char *line, int *num) { |
---|
160 | /* Converts a long string into an array of lines. */ |
---|
161 | char **lines; |
---|
162 | char *tmpline; |
---|
163 | |
---|
164 | *num = 0; |
---|
165 | lines = NULL; |
---|
166 | |
---|
167 | while ((tmpline=getLine(&line))) { |
---|
168 | if (!*num) |
---|
169 | lines = (char **) malloc(sizeof(char *)); |
---|
170 | else |
---|
171 | lines = (char **) realloc(lines, (*num+1)*sizeof(char *)); |
---|
172 | lines[*num] = tmpline; |
---|
173 | (*num)++; |
---|
174 | } |
---|
175 | return lines; |
---|
176 | } |
---|
177 | |
---|
178 | int trySocket() { |
---|
179 | int s; |
---|
180 | struct sockaddr_un addr; |
---|
181 | |
---|
182 | s = socket(AF_LOCAL, SOCK_DGRAM, 0); |
---|
183 | if (s<0) |
---|
184 | return 1; |
---|
185 | |
---|
186 | bzero(&addr,sizeof(addr)); |
---|
187 | addr.sun_family = AF_LOCAL; |
---|
188 | strncpy(addr.sun_path,_PATH_LOG,sizeof(addr.sun_path)-1); |
---|
189 | |
---|
190 | if (connect(s,(struct sockaddr *) &addr,sizeof(addr))<0) { |
---|
191 | if (errno == EPROTOTYPE || errno == ECONNREFUSED) { |
---|
192 | DDEBUG("connect failed (EPROTOTYPE), trying stream\n"); |
---|
193 | close(s); |
---|
194 | s = socket(AF_LOCAL, SOCK_STREAM, 0); |
---|
195 | if (connect(s,(struct sockaddr *) &addr, sizeof(addr)) < 0) { |
---|
196 | DDEBUG("connect failed: %s\n",strerror(errno)); |
---|
197 | close(s); |
---|
198 | return 1; |
---|
199 | } |
---|
200 | close(s); |
---|
201 | return 0; |
---|
202 | } |
---|
203 | close(s); |
---|
204 | DDEBUG("connect failed: %s\n",strerror(errno)); |
---|
205 | return 1; |
---|
206 | } else { |
---|
207 | close(s); |
---|
208 | return 0; |
---|
209 | } |
---|
210 | } |
---|
211 | |
---|
212 | int logLine(struct logInfo *logEnt) { |
---|
213 | /* Logs a line... somewhere. */ |
---|
214 | int x; |
---|
215 | struct stat statbuf; |
---|
216 | |
---|
217 | /* Don't log empty or null lines */ |
---|
218 | if (!logEnt->line || !strcmp(logEnt->line,"\n")) return 0; |
---|
219 | |
---|
220 | |
---|
221 | if ((stat(_PATH_LOG,&statbuf)==-1) || trySocket()) { |
---|
222 | DDEBUG("starting daemon failed, pooling entry %d\n",logEntries); |
---|
223 | logData=realloc(logData,(logEntries+1)*sizeof(struct logInfo)); |
---|
224 | logData[logEntries].fac = logEnt->fac; |
---|
225 | logData[logEntries].pri = logEnt->pri; |
---|
226 | logData[logEntries].cmd = strdup(logEnt->cmd); |
---|
227 | logData[logEntries].line = strdup(logEnt->line); |
---|
228 | logEntries++; |
---|
229 | } else { |
---|
230 | if (logEntries>0) { |
---|
231 | for (x=0;x<logEntries;x++) { |
---|
232 | DDEBUG("flushing log entry %d =%s=\n",x,logData[x].line); |
---|
233 | openlog(logData[x].cmd,0,logData[x].fac); |
---|
234 | syslog(logData[x].pri,"%s",logData[x].line); |
---|
235 | closelog(); |
---|
236 | } |
---|
237 | free(logData); |
---|
238 | logEntries = 0; |
---|
239 | } |
---|
240 | DDEBUG("logging =%s= via syslog\n",logEnt->line); |
---|
241 | openlog(logEnt->cmd,0,logEnt->fac); |
---|
242 | syslog(logEnt->pri,"%s",logEnt->line); |
---|
243 | closelog(); |
---|
244 | } |
---|
245 | return 0; |
---|
246 | } |
---|
247 | |
---|
248 | int logEvent(char *cmd, int eventtype,char *string) { |
---|
249 | char *eventtable [] = { |
---|
250 | _("%s babbles incoherently"), |
---|
251 | _("%s succeeded"), |
---|
252 | _("%s failed"), |
---|
253 | _("%s cancelled at user request"), |
---|
254 | _("%s failed due to a failed dependency"), |
---|
255 | /* insert more here */ |
---|
256 | NULL |
---|
257 | }; |
---|
258 | int x=0,len, rc; |
---|
259 | struct logInfo logentry; |
---|
260 | |
---|
261 | if (cmd) { |
---|
262 | logentry.cmd = basename(cmd); |
---|
263 | if ((logentry.cmd[0] =='K' || logentry.cmd[0] == 'S') && |
---|
264 | ( logentry.cmd[1] >= '0' && logentry.cmd[1] <= '9' ) && |
---|
265 | ( logentry.cmd[2] >= '0' && logentry.cmd[2] <= '9' ) ) |
---|
266 | logentry.cmd+=3; |
---|
267 | logentry.cmd = strdup(logentry.cmd); |
---|
268 | } else |
---|
269 | logentry.cmd = strdup(_("(none)")); |
---|
270 | if (!string) { |
---|
271 | string = alloca(strlen(cmd)+1); |
---|
272 | strcpy(string,cmd); |
---|
273 | } |
---|
274 | |
---|
275 | while (eventtable[x] && x<eventtype) x++; |
---|
276 | if (!(eventtable[x])) x=0; |
---|
277 | |
---|
278 | len=strlen(eventtable[x])+strlen(string); |
---|
279 | logentry.line=malloc(len); |
---|
280 | snprintf(logentry.line,len,eventtable[x],string); |
---|
281 | |
---|
282 | logentry.pri = logpriority; |
---|
283 | logentry.fac = logfacility; |
---|
284 | |
---|
285 | rc = logLine(&logentry); |
---|
286 | free(logentry.line); |
---|
287 | free(logentry.cmd); |
---|
288 | return rc; |
---|
289 | } |
---|
290 | |
---|
291 | int logString(char *cmd, char *string) { |
---|
292 | struct logInfo logentry; |
---|
293 | int rc; |
---|
294 | |
---|
295 | if (cmd) { |
---|
296 | logentry.cmd = basename(cmd); |
---|
297 | if ((logentry.cmd[0] =='K' || logentry.cmd[0] == 'S') && |
---|
298 | ( logentry.cmd[1] >= '0' && logentry.cmd[1] <= 0x39 ) && |
---|
299 | ( logentry.cmd[2] >= '0' && logentry.cmd[2] <= 0x39 ) ) |
---|
300 | logentry.cmd+=3; |
---|
301 | logentry.cmd = strdup(logentry.cmd); |
---|
302 | } else |
---|
303 | logentry.cmd = strdup(_("")); |
---|
304 | logentry.line = strdup(string); |
---|
305 | logentry.pri = logpriority; |
---|
306 | logentry.fac = logfacility; |
---|
307 | |
---|
308 | rc = logLine(&logentry); |
---|
309 | free(logentry.line); |
---|
310 | free(logentry.cmd); |
---|
311 | return rc; |
---|
312 | } |
---|
313 | |
---|
314 | int processArgs(int argc, char **argv, int silent) { |
---|
315 | char *cmdname=NULL; |
---|
316 | char *conffile=NULL; |
---|
317 | int cmdevent=0; |
---|
318 | char *cmd=NULL; |
---|
319 | char *logstring=NULL; |
---|
320 | char *fac=NULL,*pri=NULL; |
---|
321 | int lfac=-1, lpri=-1; |
---|
322 | poptContext context; |
---|
323 | int rc; |
---|
324 | struct poptOption optTable[] = { |
---|
325 | POPT_AUTOHELP |
---|
326 | { "conf", 0, POPT_ARG_STRING, &conffile, 0, |
---|
327 | "configuration file (default: /etc/initlog.conf)", NULL |
---|
328 | }, |
---|
329 | { "name", 'n', POPT_ARG_STRING, &cmdname, 0, |
---|
330 | "name of service being logged", NULL |
---|
331 | }, |
---|
332 | { "event", 'e', POPT_ARG_INT, &cmdevent, 0, |
---|
333 | "event being logged (see man page)", NULL |
---|
334 | }, |
---|
335 | { "cmd", 'c', POPT_ARG_STRING, &cmd, 0, |
---|
336 | "command to run, logging output", NULL |
---|
337 | }, |
---|
338 | { "debug", 'd', POPT_ARG_NONE, &debug, 0, |
---|
339 | "print lots of verbose debugging info", NULL |
---|
340 | }, |
---|
341 | { "run", 'r', POPT_ARG_STRING, &cmd, 3, |
---|
342 | "command to run, accepting input on open fd", NULL |
---|
343 | }, |
---|
344 | { "string", 's', POPT_ARG_STRING, &logstring, 0, |
---|
345 | "string to log", NULL |
---|
346 | }, |
---|
347 | { "facility", 'f', POPT_ARG_STRING, &fac, 1, |
---|
348 | "facility to log at (default: 'local7')", NULL |
---|
349 | }, |
---|
350 | { "priority", 'p', POPT_ARG_STRING, &pri, 2, |
---|
351 | "priority to log at (default: 'notice')", NULL |
---|
352 | }, |
---|
353 | { "quiet", 'q', POPT_ARG_NONE, &quiet, 0, |
---|
354 | "suppress stdout/stderr", NULL |
---|
355 | }, |
---|
356 | { 0, 0, 0, 0, 0, 0 } |
---|
357 | }; |
---|
358 | |
---|
359 | context = poptGetContext("initlog", argc, argv, optTable, 0); |
---|
360 | |
---|
361 | while ((rc = poptGetNextOpt(context)) > 0) { |
---|
362 | switch (rc) { |
---|
363 | case 1: |
---|
364 | lfac=atoi(fac); |
---|
365 | if ((lfac == 0) && strcmp(fac,"0")) { |
---|
366 | int x =0; |
---|
367 | |
---|
368 | lfac = LOG_DAEMON; |
---|
369 | for (x=0;facilitynames[x].c_name;x++) { |
---|
370 | if (!strcmp(fac,facilitynames[x].c_name)) { |
---|
371 | lfac = facilitynames[x].c_val; |
---|
372 | break; |
---|
373 | } |
---|
374 | } |
---|
375 | } |
---|
376 | break; |
---|
377 | case 2: |
---|
378 | lpri = atoi(pri); |
---|
379 | if ((lpri == 0) && strcmp(pri,"0")) { |
---|
380 | int x=0; |
---|
381 | |
---|
382 | lpri = LOG_NOTICE; |
---|
383 | for (x=0;prioritynames[x].c_name;x++) { |
---|
384 | if (!strcmp(pri,prioritynames[x].c_name)) { |
---|
385 | lpri = prioritynames[x].c_val; |
---|
386 | break; |
---|
387 | } |
---|
388 | } |
---|
389 | } |
---|
390 | break; |
---|
391 | case 3: |
---|
392 | reexec = 1; |
---|
393 | break; |
---|
394 | default: |
---|
395 | break; |
---|
396 | } |
---|
397 | } |
---|
398 | |
---|
399 | if ((rc < -1)) { |
---|
400 | if (!silent) |
---|
401 | fprintf(stderr, "%s: %s\n", |
---|
402 | poptBadOption(context, POPT_BADOPTION_NOALIAS), |
---|
403 | poptStrerror(rc)); |
---|
404 | |
---|
405 | return -1; |
---|
406 | } |
---|
407 | if ( (cmd && logstring) || (cmd && cmdname) ) { |
---|
408 | if (!silent) |
---|
409 | fprintf(stderr, _("--cmd and --run are incompatible with --string or --name\n")); |
---|
410 | return -1; |
---|
411 | } |
---|
412 | if ( cmdname && (!logstring && !cmdevent)) { |
---|
413 | if (!silent) |
---|
414 | fprintf(stderr, _("--name requires one of --event or --string\n")); |
---|
415 | return -1; |
---|
416 | } |
---|
417 | if (cmdevent && cmd) { |
---|
418 | if (!silent) |
---|
419 | fprintf(stderr, _("--cmd and --run are incompatible with --event\n")); |
---|
420 | return -1; |
---|
421 | } |
---|
422 | if (conffile) { |
---|
423 | readConfiguration(conffile); |
---|
424 | } else { |
---|
425 | readConfiguration("/etc/initlog.conf"); |
---|
426 | } |
---|
427 | if (cmd) { |
---|
428 | while (isspace(*cmd)) cmd++; |
---|
429 | } |
---|
430 | if (lpri!=-1) logpriority=lpri; |
---|
431 | if (lfac!=-1) logfacility=lfac; |
---|
432 | if (cmdevent) { |
---|
433 | logEvent(cmdname,cmdevent,logstring); |
---|
434 | } else if (logstring) { |
---|
435 | logString(cmdname,logstring); |
---|
436 | } else if ( cmd && *cmd) { |
---|
437 | return(runCommand(cmd,reexec,quiet,debug)); |
---|
438 | } else { |
---|
439 | if (!silent) |
---|
440 | fprintf(stderr,"nothing to do!\n"); |
---|
441 | return -1; |
---|
442 | } |
---|
443 | return 0; |
---|
444 | } |
---|
445 | |
---|
446 | int main(int argc, char **argv) { |
---|
447 | |
---|
448 | setlocale(LC_ALL,""); |
---|
449 | bindtextdomain("initlog","/etc/locale"); |
---|
450 | textdomain("initlog"); |
---|
451 | fprintf(stderr, _("WARNING: initlog is deprecated and will be removed in a future release\n")); |
---|
452 | exit(processArgs(argc,argv,0)); |
---|
453 | } |
---|