/* * Copyright (c) 1999-2003 Red Hat, Inc. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * */ #include #include #include #include #include #include #include #include #include #define SYSLOG_NAMES #include #include #include #include #include #define _(String) gettext((String)) #include #include #include "initlog.h" #include "process.h" static int logfacility=LOG_DAEMON; static int logpriority=LOG_NOTICE; static int reexec=0; static int quiet=0; int debug=0; regex_t **regList = NULL; static int logEntries = 0; struct logInfo *logData = NULL; void readConfiguration(char *fname) { int fd,num=0; struct stat sbuf; char *data,*line, *d; regex_t *regexp; int lfac=-1,lpri=-1; if ((fd=open(fname,O_RDONLY))==-1) return; if (fstat(fd,&sbuf)) { close(fd); return; } d = data=malloc(sbuf.st_size+1); if (read(fd,data,sbuf.st_size)!=sbuf.st_size) { close(fd); free(data); return; } close(fd); data[sbuf.st_size] = '\0'; while ((line=getLine(&data))) { if (line[0]=='#') continue; if (!strncmp(line,"ignore ",7)) { regexp = malloc(sizeof(regex_t)); if (!regcomp(regexp,line+7,REG_EXTENDED|REG_NOSUB|REG_NEWLINE)) { regList = realloc(regList,(num+2) * sizeof(regex_t *)); regList[num] = regexp; regList[num+1] = NULL; num++; } } if (!strncmp(line,"facility ",9)) { lfac=atoi(line+9); if ((lfac == 0) && strcmp(line+9,"0")) { int x =0; lfac = LOG_DAEMON; for (x=0;facilitynames[x].c_name;x++) { if (!strcmp(line+9,facilitynames[x].c_name)) { lfac = facilitynames[x].c_val; break; } } } } if (!strncmp(line,"priority ",9)) { lpri = atoi(line+9); if ((lpri == 0) && strcmp(line+9,"0")) { int x=0; lpri = LOG_NOTICE; for (x=0;prioritynames[x].c_name;x++) { if (!strcmp(line+9,prioritynames[x].c_name)) { lpri = prioritynames[x].c_val; break; } } } } } if (lfac!=-1) logfacility=lfac; if (lpri!=-1) logpriority=lpri; free(d); } char *getLine(char **data) { /* Get one line from data */ /* Anything up to a carraige return (\r) or a backspace (\b) is discarded. */ /* If this really bothers you, mail me and I might make it configurable. */ /* It's here to avoid confilcts with fsck's progress bar. */ char *x, *y; if (!*data) return NULL; x=*data; while (*x && (*x != '\n')) { while (*x && (*x != '\n') && (*x != '\r') && (*x != '\b')) x++; if (*x && (*x=='\r' || *x =='\b')) { *data = x+1; x++; } } if (*x) { x++; } else { if (x-*data) { y=malloc(x-*data+1); y[x-*data] = 0; y[x-*data-1] = '\n'; memcpy(y,*data,x-*data); } else { y=NULL; } *data = NULL; return y; } y = malloc(x-*data); y[x-*data-1] = 0; memcpy(y,*data,x-*data-1); *data = x; return y; } char **toArray(char *line, int *num) { /* Converts a long string into an array of lines. */ char **lines; char *tmpline; *num = 0; lines = NULL; while ((tmpline=getLine(&line))) { if (!*num) lines = (char **) malloc(sizeof(char *)); else lines = (char **) realloc(lines, (*num+1)*sizeof(char *)); lines[*num] = tmpline; (*num)++; } return lines; } int trySocket() { int s; struct sockaddr_un addr; s = socket(AF_LOCAL, SOCK_DGRAM, 0); if (s<0) return 1; bzero(&addr,sizeof(addr)); addr.sun_family = AF_LOCAL; strncpy(addr.sun_path,_PATH_LOG,sizeof(addr.sun_path)-1); if (connect(s,(struct sockaddr *) &addr,sizeof(addr))<0) { if (errno == EPROTOTYPE || errno == ECONNREFUSED) { DDEBUG("connect failed (EPROTOTYPE), trying stream\n"); close(s); s = socket(AF_LOCAL, SOCK_STREAM, 0); if (connect(s,(struct sockaddr *) &addr, sizeof(addr)) < 0) { DDEBUG("connect failed: %s\n",strerror(errno)); close(s); return 1; } close(s); return 0; } close(s); DDEBUG("connect failed: %s\n",strerror(errno)); return 1; } else { close(s); return 0; } } int logLine(struct logInfo *logEnt) { /* Logs a line... somewhere. */ int x; struct stat statbuf; /* Don't log empty or null lines */ if (!logEnt->line || !strcmp(logEnt->line,"\n")) return 0; if ((stat(_PATH_LOG,&statbuf)==-1) || trySocket()) { DDEBUG("starting daemon failed, pooling entry %d\n",logEntries); logData=realloc(logData,(logEntries+1)*sizeof(struct logInfo)); logData[logEntries].fac = logEnt->fac; logData[logEntries].pri = logEnt->pri; logData[logEntries].cmd = strdup(logEnt->cmd); logData[logEntries].line = strdup(logEnt->line); logEntries++; } else { if (logEntries>0) { for (x=0;xline); openlog(logEnt->cmd,0,logEnt->fac); syslog(logEnt->pri,"%s",logEnt->line); closelog(); } return 0; } int logEvent(char *cmd, int eventtype,char *string) { char *eventtable [] = { _("%s babbles incoherently"), _("%s succeeded"), _("%s failed"), _("%s cancelled at user request"), _("%s failed due to a failed dependency"), /* insert more here */ NULL }; int x=0,len, rc; struct logInfo logentry; if (cmd) { logentry.cmd = basename(cmd); if ((logentry.cmd[0] =='K' || logentry.cmd[0] == 'S') && ( logentry.cmd[1] >= '0' && logentry.cmd[1] <= '9' ) && ( logentry.cmd[2] >= '0' && logentry.cmd[2] <= '9' ) ) logentry.cmd+=3; logentry.cmd = strdup(logentry.cmd); } else logentry.cmd = strdup(_("(none)")); if (!string) { string = alloca(strlen(cmd)+1); strcpy(string,cmd); } while (eventtable[x] && x= '0' && logentry.cmd[1] <= 0x39 ) && ( logentry.cmd[2] >= '0' && logentry.cmd[2] <= 0x39 ) ) logentry.cmd+=3; logentry.cmd = strdup(logentry.cmd); } else logentry.cmd = strdup(_("")); logentry.line = strdup(string); logentry.pri = logpriority; logentry.fac = logfacility; rc = logLine(&logentry); free(logentry.line); free(logentry.cmd); return rc; } int processArgs(int argc, char **argv, int silent) { char *cmdname=NULL; char *conffile=NULL; int cmdevent=0; char *cmd=NULL; char *logstring=NULL; char *fac=NULL,*pri=NULL; int lfac=-1, lpri=-1; poptContext context; int rc; struct poptOption optTable[] = { POPT_AUTOHELP { "conf", 0, POPT_ARG_STRING, &conffile, 0, "configuration file (default: /etc/initlog.conf)", NULL }, { "name", 'n', POPT_ARG_STRING, &cmdname, 0, "name of service being logged", NULL }, { "event", 'e', POPT_ARG_INT, &cmdevent, 0, "event being logged (see man page)", NULL }, { "cmd", 'c', POPT_ARG_STRING, &cmd, 0, "command to run, logging output", NULL }, { "debug", 'd', POPT_ARG_NONE, &debug, 0, "print lots of verbose debugging info", NULL }, { "run", 'r', POPT_ARG_STRING, &cmd, 3, "command to run, accepting input on open fd", NULL }, { "string", 's', POPT_ARG_STRING, &logstring, 0, "string to log", NULL }, { "facility", 'f', POPT_ARG_STRING, &fac, 1, "facility to log at (default: 'local7')", NULL }, { "priority", 'p', POPT_ARG_STRING, &pri, 2, "priority to log at (default: 'notice')", NULL }, { "quiet", 'q', POPT_ARG_NONE, &quiet, 0, "suppress stdout/stderr", NULL }, { 0, 0, 0, 0, 0, 0 } }; context = poptGetContext("initlog", argc, argv, optTable, 0); while ((rc = poptGetNextOpt(context)) > 0) { switch (rc) { case 1: lfac=atoi(fac); if ((lfac == 0) && strcmp(fac,"0")) { int x =0; lfac = LOG_DAEMON; for (x=0;facilitynames[x].c_name;x++) { if (!strcmp(fac,facilitynames[x].c_name)) { lfac = facilitynames[x].c_val; break; } } } break; case 2: lpri = atoi(pri); if ((lpri == 0) && strcmp(pri,"0")) { int x=0; lpri = LOG_NOTICE; for (x=0;prioritynames[x].c_name;x++) { if (!strcmp(pri,prioritynames[x].c_name)) { lpri = prioritynames[x].c_val; break; } } } break; case 3: reexec = 1; break; default: break; } } if ((rc < -1)) { if (!silent) fprintf(stderr, "%s: %s\n", poptBadOption(context, POPT_BADOPTION_NOALIAS), poptStrerror(rc)); return -1; } if ( (cmd && logstring) || (cmd && cmdname) ) { if (!silent) fprintf(stderr, _("--cmd and --run are incompatible with --string or --name\n")); return -1; } if ( cmdname && (!logstring && !cmdevent)) { if (!silent) fprintf(stderr, _("--name requires one of --event or --string\n")); return -1; } if (cmdevent && cmd) { if (!silent) fprintf(stderr, _("--cmd and --run are incompatible with --event\n")); return -1; } if (conffile) { readConfiguration(conffile); } else { readConfiguration("/etc/initlog.conf"); } if (cmd) { while (isspace(*cmd)) cmd++; } if (lpri!=-1) logpriority=lpri; if (lfac!=-1) logfacility=lfac; if (cmdevent) { logEvent(cmdname,cmdevent,logstring); } else if (logstring) { logString(cmdname,logstring); } else if ( cmd && *cmd) { return(runCommand(cmd,reexec,quiet,debug)); } else { if (!silent) fprintf(stderr,"nothing to do!\n"); return -1; } return 0; } int main(int argc, char **argv) { setlocale(LC_ALL,""); bindtextdomain("initlog","/etc/locale"); textdomain("initlog"); fprintf(stderr, _("WARNING: initlog is deprecated and will be removed in a future release\n")); exit(processArgs(argc,argv,0)); }