]> TLD Linux GIT Repositories - rc-scripts.git/blob - src/initlog.c
- from PLD
[rc-scripts.git] / src / initlog.c
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  */
12
13 #include <ctype.h>
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
26 #include <sys/socket.h>
27 #include <sys/stat.h>
28 #include <sys/un.h>
29 #include <sys/wait.h>
30
31 #define _(String) gettext((String))
32
33 #include <popt.h>
34
35 #include <regex.h>
36
37 #include "initlog.h"
38 #include "process.h"
39
40 static int logfacility=LOG_DAEMON;
41 static int logpriority=LOG_NOTICE;
42 static int reexec=0;
43 static int quiet=0;
44 int debug=0;
45
46 regex_t  **regList = NULL;
47
48 static int logEntries = 0;
49 struct logInfo *logData = NULL;
50
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);
66             free(data);
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     
115 char *getLine(char **data) {
116     /* Get one line from data */
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. */
120     char *x, *y;
121     
122     if (!*data) return NULL;
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     }
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
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
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 || errno == ECONNREFUSED) {
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
237 int logLine(struct logInfo *logEnt) {
238     /* Logs a line... somewhere. */
239     int x;
240     struct stat statbuf;
241     
242     /* Don't log empty or null lines */
243     if (!logEnt->line || !strcmp(logEnt->line,"\n")) return 0;
244     
245         
246     if  ( ((stat(_PATH_LOG,&statbuf)==-1) || trySocket())
247           && startDaemon()
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) {
286         logentry.cmd = strdup(basename(cmd));
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' ) )
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) {
313         logentry.cmd = strdup(basename(cmd));
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 ) )
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;
329     char *conffile=NULL;
330     int cmdevent=0;
331     char *cmd=NULL;
332     char *logstring=NULL;
333     char *fac=NULL,*pri=NULL;
334     int lfac=-1, lpri=-1;
335     poptContext context;
336     int rc;
337     struct poptOption optTable[] = {
338         POPT_AUTOHELP
339         { "conf", 0, POPT_ARG_STRING, &conffile, 0,
340           "configuration file (default: /etc/initlog.conf)", NULL
341         },
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,
361           "facility to log at (default: 'local7')", NULL
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:
377             lfac=atoi(fac);
378             if ((lfac == 0) && strcmp(fac,"0")) {
379                 int x =0;
380                 
381                 lfac = LOG_DAEMON;
382                 for (x=0;facilitynames[x].c_name;x++) {
383                     if (!strcmp(fac,facilitynames[x].c_name)) {
384                         lfac = facilitynames[x].c_val;
385                         break;
386                     }
387                 }
388             }
389             break;
390          case 2:
391             lpri = atoi(pri);
392             if ((lpri == 0) && strcmp(pri,"0")) {
393                 int x=0;
394                 
395                 lpri = LOG_NOTICE;
396                 for (x=0;prioritynames[x].c_name;x++) {
397                     if (!strcmp(pri,prioritynames[x].c_name)) {
398                         lpri = prioritynames[x].c_val;
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     }
430     if (cmdevent && cmd) {
431             if (!silent)
432               fprintf(stderr, _("--cmd and --run are incompatible with --event\n"));
433             return -1;
434     }
435     if (conffile) {
436         readConfiguration(conffile);
437     } else {
438         readConfiguration("/etc/initlog.conf");
439     }
440     if (cmd) {
441             while (isspace(*cmd)) cmd++;
442     }
443     if (lpri!=-1) logpriority=lpri;
444     if (lfac!=-1) logfacility=lfac;
445     if (cmdevent) {
446         logEvent(cmdname,cmdevent,logstring);
447     } else if (logstring) {
448         logString(cmdname,logstring);
449     } else if ( cmd && *cmd) {
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 }