]>
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 | ||
de1fc6ce JR |
171 | int trySocket() { |
172 | int s; | |
173 | struct sockaddr_un addr; | |
174 | ||
175 | s = socket(AF_LOCAL, SOCK_DGRAM, 0); | |
176 | if (s<0) | |
177 | return 1; | |
178 | ||
179 | bzero(&addr,sizeof(addr)); | |
180 | addr.sun_family = AF_LOCAL; | |
181 | strncpy(addr.sun_path,_PATH_LOG,sizeof(addr.sun_path)-1); | |
182 | ||
183 | if (connect(s,(struct sockaddr *) &addr,sizeof(addr))<0) { | |
184 | if (errno == EPROTOTYPE) { | |
185 | DDEBUG("connect failed (EPROTOTYPE), trying stream\n"); | |
186 | close(s); | |
187 | s = socket(AF_LOCAL, SOCK_STREAM, 0); | |
188 | if (connect(s,(struct sockaddr *) &addr, sizeof(addr)) < 0) { | |
189 | DDEBUG("connect failed: %s\n",strerror(errno)); | |
190 | close(s); | |
191 | return 1; | |
192 | } | |
193 | close(s); | |
194 | return 0; | |
195 | } | |
196 | close(s); | |
197 | DDEBUG("connect failed: %s\n",strerror(errno)); | |
198 | return 1; | |
199 | } else { | |
200 | close(s); | |
201 | return 0; | |
202 | } | |
203 | } | |
204 | ||
cee18a41 AM |
205 | int logLine(struct logInfo *logEnt) { |
206 | /* Logs a line... somewhere. */ | |
458f14b7 | 207 | int x; |
cee18a41 AM |
208 | struct stat statbuf; |
209 | ||
210 | /* Don't log empty or null lines */ | |
211 | if (!logEnt->line || !strcmp(logEnt->line,"\n")) return 0; | |
212 | ||
de1fc6ce JR |
213 | |
214 | if ( ((stat(_PATH_LOG,&statbuf)==-1) || trySocket()) | |
cee18a41 AM |
215 | ) { |
216 | DDEBUG("starting daemon failed, pooling entry %d\n",logEntries); | |
217 | logData=realloc(logData,(logEntries+1)*sizeof(struct logInfo)); | |
218 | logData[logEntries]= (*logEnt); | |
219 | logEntries++; | |
220 | } else { | |
221 | if (logEntries>0) { | |
222 | for (x=0;x<logEntries;x++) { | |
223 | DDEBUG("flushing log entry %d =%s=\n",x,logData[x].line); | |
224 | openlog(logData[x].cmd,0,logData[x].fac); | |
225 | syslog(logData[x].pri,"%s",logData[x].line); | |
226 | closelog(); | |
227 | } | |
228 | free(logData); | |
229 | logEntries = 0; | |
230 | } | |
231 | DDEBUG("logging =%s= via syslog\n",logEnt->line); | |
232 | openlog(logEnt->cmd,0,logEnt->fac); | |
233 | syslog(logEnt->pri,"%s",logEnt->line); | |
234 | closelog(); | |
235 | } | |
236 | return 0; | |
237 | } | |
238 | ||
239 | int logEvent(char *cmd, int eventtype,char *string) { | |
240 | char *eventtable [] = { | |
241 | _("%s babbles incoherently"), | |
242 | _("%s succeeded"), | |
243 | _("%s failed"), | |
244 | _("%s cancelled at user request"), | |
245 | _("%s failed due to a failed dependency"), | |
246 | /* insert more here */ | |
247 | NULL | |
248 | }; | |
249 | int x=0,len; | |
250 | struct logInfo logentry; | |
251 | ||
252 | if (cmd) { | |
de1fc6ce | 253 | logentry.cmd = strdup(basename(cmd)); |
00196ec7 AM |
254 | if ((logentry.cmd[0] =='K' || logentry.cmd[0] == 'S') && |
255 | ( logentry.cmd[1] >= '0' && logentry.cmd[1] <= '9' ) && | |
256 | ( logentry.cmd[2] >= '0' && logentry.cmd[2] <= '9' ) ) | |
cee18a41 AM |
257 | logentry.cmd+=3; |
258 | } else | |
259 | logentry.cmd = strdup(_("(none)")); | |
260 | if (!string) | |
261 | string = strdup(cmd); | |
262 | ||
263 | while (eventtable[x] && x<eventtype) x++; | |
264 | if (!(eventtable[x])) x=0; | |
265 | ||
266 | len=strlen(eventtable[x])+strlen(string); | |
267 | logentry.line=malloc(len); | |
268 | snprintf(logentry.line,len,eventtable[x],string); | |
269 | ||
270 | logentry.pri = logpriority; | |
271 | logentry.fac = logfacility; | |
272 | ||
273 | return logLine(&logentry); | |
274 | } | |
275 | ||
276 | int logString(char *cmd, char *string) { | |
277 | struct logInfo logentry; | |
278 | ||
279 | if (cmd) { | |
de1fc6ce | 280 | logentry.cmd = strdup(basename(cmd)); |
00196ec7 AM |
281 | if ((logentry.cmd[0] =='K' || logentry.cmd[0] == 'S') && |
282 | ( logentry.cmd[1] >= '0' && logentry.cmd[1] <= 0x39 ) && | |
283 | ( logentry.cmd[2] >= '0' && logentry.cmd[2] <= 0x39 ) ) | |
cee18a41 AM |
284 | logentry.cmd+=3; |
285 | } else | |
286 | logentry.cmd = strdup(_("")); | |
287 | logentry.line = strdup(string); | |
288 | logentry.pri = logpriority; | |
289 | logentry.fac = logfacility; | |
290 | ||
291 | return logLine(&logentry); | |
292 | } | |
293 | ||
294 | int processArgs(int argc, char **argv, int silent) { | |
295 | char *cmdname=NULL; | |
458f14b7 | 296 | char *conffile=NULL; |
cee18a41 AM |
297 | int cmdevent=0; |
298 | char *cmd=NULL; | |
299 | char *logstring=NULL; | |
300 | char *fac=NULL,*pri=NULL; | |
458f14b7 | 301 | int lfac=-1, lpri=-1; |
cee18a41 AM |
302 | poptContext context; |
303 | int rc; | |
304 | struct poptOption optTable[] = { | |
305 | POPT_AUTOHELP | |
458f14b7 AM |
306 | { "conf", 0, POPT_ARG_STRING, &conffile, 0, |
307 | "configuration file (default: /etc/initlog.conf)", NULL | |
308 | }, | |
cee18a41 AM |
309 | { "name", 'n', POPT_ARG_STRING, &cmdname, 0, |
310 | "name of service being logged", NULL | |
311 | }, | |
312 | { "event", 'e', POPT_ARG_INT, &cmdevent, 0, | |
313 | "event being logged (see man page)", NULL | |
314 | }, | |
315 | { "cmd", 'c', POPT_ARG_STRING, &cmd, 0, | |
316 | "command to run, logging output", NULL | |
317 | }, | |
318 | { "debug", 'd', POPT_ARG_NONE, &debug, 0, | |
319 | "print lots of verbose debugging info", NULL | |
320 | }, | |
321 | { "run", 'r', POPT_ARG_STRING, &cmd, 3, | |
322 | "command to run, accepting input on open fd", NULL | |
323 | }, | |
324 | { "string", 's', POPT_ARG_STRING, &logstring, 0, | |
325 | "string to log", NULL | |
326 | }, | |
327 | { "facility", 'f', POPT_ARG_STRING, &fac, 1, | |
458f14b7 | 328 | "facility to log at (default: 'local7')", NULL |
cee18a41 AM |
329 | }, |
330 | { "priority", 'p', POPT_ARG_STRING, &pri, 2, | |
331 | "priority to log at (default: 'notice')", NULL | |
332 | }, | |
333 | { "quiet", 'q', POPT_ARG_NONE, &quiet, 0, | |
334 | "suppress stdout/stderr", NULL | |
335 | }, | |
336 | { 0, 0, 0, 0, 0, 0 } | |
337 | }; | |
338 | ||
339 | context = poptGetContext("initlog", argc, argv, optTable, 0); | |
340 | ||
341 | while ((rc = poptGetNextOpt(context)) > 0) { | |
342 | switch (rc) { | |
343 | case 1: | |
458f14b7 AM |
344 | lfac=atoi(fac); |
345 | if ((lfac == 0) && strcmp(fac,"0")) { | |
cee18a41 AM |
346 | int x =0; |
347 | ||
458f14b7 | 348 | lfac = LOG_DAEMON; |
cee18a41 AM |
349 | for (x=0;facilitynames[x].c_name;x++) { |
350 | if (!strcmp(fac,facilitynames[x].c_name)) { | |
458f14b7 | 351 | lfac = facilitynames[x].c_val; |
cee18a41 AM |
352 | break; |
353 | } | |
354 | } | |
355 | } | |
356 | break; | |
357 | case 2: | |
458f14b7 AM |
358 | lpri = atoi(pri); |
359 | if ((lpri == 0) && strcmp(pri,"0")) { | |
cee18a41 AM |
360 | int x=0; |
361 | ||
458f14b7 | 362 | lpri = LOG_NOTICE; |
cee18a41 AM |
363 | for (x=0;prioritynames[x].c_name;x++) { |
364 | if (!strcmp(pri,prioritynames[x].c_name)) { | |
458f14b7 | 365 | lpri = prioritynames[x].c_val; |
cee18a41 AM |
366 | break; |
367 | } | |
368 | } | |
369 | } | |
370 | break; | |
371 | case 3: | |
372 | reexec = 1; | |
373 | break; | |
374 | default: | |
375 | break; | |
376 | } | |
377 | } | |
378 | ||
379 | if ((rc < -1)) { | |
380 | if (!silent) | |
381 | fprintf(stderr, "%s: %s\n", | |
382 | poptBadOption(context, POPT_BADOPTION_NOALIAS), | |
383 | poptStrerror(rc)); | |
384 | ||
385 | return -1; | |
386 | } | |
387 | if ( (cmd && logstring) || (cmd && cmdname) ) { | |
388 | if (!silent) | |
389 | fprintf(stderr, _("--cmd and --run are incompatible with --string or --name\n")); | |
390 | return -1; | |
391 | } | |
392 | if ( cmdname && (!logstring && !cmdevent)) { | |
393 | if (!silent) | |
394 | fprintf(stderr, _("--name requires one of --event or --string\n")); | |
395 | return -1; | |
396 | } | |
00196ec7 AM |
397 | if (cmdevent && cmd) { |
398 | if (!silent) | |
399 | fprintf(stderr, _("--cmd and --run are incompatible with --event\n")); | |
400 | return -1; | |
401 | } | |
458f14b7 AM |
402 | if (conffile) { |
403 | readConfiguration(conffile); | |
404 | } else { | |
405 | readConfiguration("/etc/initlog.conf"); | |
406 | } | |
de1fc6ce JR |
407 | if (cmd) { |
408 | while (isspace(*cmd)) cmd++; | |
409 | } | |
458f14b7 AM |
410 | if (lpri!=-1) logpriority=lpri; |
411 | if (lfac!=-1) logfacility=lfac; | |
cee18a41 AM |
412 | if (cmdevent) { |
413 | logEvent(cmdname,cmdevent,logstring); | |
414 | } else if (logstring) { | |
415 | logString(cmdname,logstring); | |
de1fc6ce | 416 | } else if ( cmd && *cmd) { |
cee18a41 AM |
417 | return(runCommand(cmd,reexec,quiet,debug)); |
418 | } else { | |
419 | if (!silent) | |
420 | fprintf(stderr,"nothing to do!\n"); | |
421 | return -1; | |
422 | } | |
423 | return 0; | |
424 | } | |
425 | ||
426 | int main(int argc, char **argv) { | |
427 | ||
428 | setlocale(LC_ALL,""); | |
429 | bindtextdomain("initlog","/etc/locale"); | |
430 | textdomain("initlog"); | |
431 | exit(processArgs(argc,argv,0)); | |
432 | } |