[1108] | 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 | } |
---|