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