/********************************************************************* * * Function : init_log_module * * Description : Initializes the logging module to log to stderr. * Can only be called while stderr hasn't been closed * yet and is only supposed to be called once. * * Parameters : * 1 : prog_name = The program name. * * Returns : Nothing. * *********************************************************************/ void init_log_module(void) { lock_logfile(); logfp = stderr; unlock_logfile(); set_debug_level(debug); }
/** * @function logfile.flush * * ### Synopsis * * logfile.flush(handle); * * Flush contents of shared memory block to the log file, reset memory block to "empty" state. * * @param {object} handle - handle of logfile to flush. */ static JSVAL logfile_flush (JSARGS args) { STATE *state = HANDLE(args[0]); lock_logfile(state); flush_logfile(state); unlock_logfile(state); return Undefined(); }
/** * @function logfile.flush * * ### Synopsis * * logfile.flush(handle); * * Flush contents of shared memory block to the log file, reset memory block to "empty" state. * * @param {object} handle - handle of logfile to flush. */ static JSVAL logfile_flush (JSARGS args) { HandleScope scope; STATE *state = log_HANDLE(args[0]); lock_logfile(state); flush_logfile(state); unlock_logfile(state); return Undefined(); }
/********************************************************************* * * Function : disable_logging * * Description : Disables logging. * * Parameters : None. * * Returns : Nothing. * *********************************************************************/ void disable_logging(void) { if (logfp != NULL) { log_error(LOG_LEVEL_INFO, "No logfile configured. Please enable it before reporting any problems."); lock_logfile(); fclose(logfp); logfp = NULL; unlock_logfile(); } }
/** * @function logfile.write * * ### Synopsis * * logfile.write(handle, s); * logfile.write(handle, s, len); * * Write a string to the log file. * * The string is appended to the shared memory block. The memory block is first flushed to disk if there is not enough room for the string. * * @param {object} handle - handle of the log file. * @param {string} s - string to write to the log file. * @param {int} len - optional length of string to write; defaults to strlen(s). * */ static JSVAL logfile_write (JSARGS args) { STATE *state = HANDLE(args[0]); String::AsciiValue buf(args[1]); int len; if (args.Length() > 2) { len = args[2]->IntegerValue(); } else { len = strlen(*buf); } lock_logfile(state); if (*state->length + len >= LOGFILE_CHUNK_SIZE) { flush_logfile(state); } memcpy(&state->logBuffer[*state->length], *buf, len); *state->length += len; unlock_logfile(state); return Undefined(); }
/********************************************************************* * * Function : log_error * * Description : This is the error-reporting and logging function. * * Parameters : * 1 : loglevel = the type of message to be logged * 2 : fmt = the main string we want logged, printf-like * 3 : ... = arguments to be inserted in fmt (printf-like). * * Returns : N/A * *********************************************************************/ void log_error(int loglevel, const char *fmt, ...) { va_list ap; char *outbuf = NULL; static char *outbuf_save = NULL; char tempbuf[BUFFER_SIZE]; size_t length = 0; const char * src = fmt; long thread_id; char timestamp[30]; /* * XXX: Make this a config option, * why else do we allocate instead of using * an array? */ size_t log_buffer_size = BUFFER_SIZE; #if defined(_WIN32) && !defined(_WIN_CONSOLE) /* * Irrespective of debug setting, a GET/POST/CONNECT makes * the taskbar icon animate. (There is an option to disable * this but checking that is handled inside LogShowActivity()). */ if ((loglevel == LOG_LEVEL_GPC) || (loglevel == LOG_LEVEL_CRUNCH)) { LogShowActivity(); } #endif /* defined(_WIN32) && !defined(_WIN_CONSOLE) */ /* * verify that the loglevel applies to current * settings and that logging is enabled. * Bail out otherwise. */ if ((0 == (loglevel & debug)) #ifndef _WIN32 || (logfp == NULL) #endif ) { if (loglevel == LOG_LEVEL_FATAL) { fatal_error("Fatal error. You're not supposed to" "see this message. Please file a bug report."); } return; } thread_id = get_thread_id(); get_log_timestamp(timestamp, sizeof(timestamp)); /* protect the whole function because of the static buffer (outbuf) */ lock_logfile(); if (NULL == outbuf_save) { outbuf_save = (char*)zalloc(log_buffer_size + 1); /* +1 for paranoia */ if (NULL == outbuf_save) { snprintf(tempbuf, sizeof(tempbuf), "%s %08lx Fatal error: Out of memory in log_error().", timestamp, thread_id); fatal_error(tempbuf); /* Exit */ return; } } outbuf = outbuf_save; /* * Memsetting the whole buffer to zero (in theory) * makes things easier later on. */ memset(outbuf, 0, log_buffer_size); /* Add prefix for everything but Common Log Format messages */ if (loglevel != LOG_LEVEL_CLF) { length = (size_t)snprintf(outbuf, log_buffer_size, "%s %08lx %s: ", timestamp, thread_id, get_log_level_string(loglevel)); } /* get ready to scan var. args. */ va_start(ap, fmt); /* build formatted message from fmt and var-args */ while ((*src) && (length < log_buffer_size-2)) { const char *sval = NULL; /* %N string */ int ival; /* %N string length or an error code */ unsigned uval; /* %u value */ long lval; /* %l value */ unsigned long ulval; /* %ul value */ char ch; const char *format_string = tempbuf; ch = *src++; if (ch != '%') { outbuf[length++] = ch; /* * XXX: Only necessary on platforms where multiple threads * can write to the buffer at the same time because we * don't support mutexes (OS/2 for example). */ outbuf[length] = '\0'; continue; } outbuf[length] = '\0'; ch = *src++; switch (ch) { case '%': tempbuf[0] = '%'; tempbuf[1] = '\0'; break; case 'd': ival = va_arg(ap, int); snprintf(tempbuf, sizeof(tempbuf), "%d", ival); break; case 'u': uval = va_arg(ap, unsigned); snprintf(tempbuf, sizeof(tempbuf), "%u", uval); break; case 'l': /* this is a modifier that must be followed by u, lu, or d */ ch = *src++; if (ch == 'd') { lval = va_arg(ap, long); snprintf(tempbuf, sizeof(tempbuf), "%ld", lval); } else if (ch == 'u') { ulval = va_arg(ap, unsigned long); snprintf(tempbuf, sizeof(tempbuf), "%lu", ulval); }
/********************************************************************* * * Function : init_error_log * * Description : Initializes the logging module to log to a file. * * XXX: should be renamed. * * Parameters : * 1 : prog_name = The program name. * 2 : logfname = The logfile to (re)open. * * Returns : N/A * *********************************************************************/ void init_error_log(const char *prog_name, const char *logfname) { FILE *fp; assert(NULL != logfname); lock_loginit(); if ((logfp != NULL) && (logfp != stderr)) { log_error(LOG_LEVEL_INFO, "(Re-)Opening logfile \'%s\'", logfname); } /* set the designated log file */ fp = fopen(logfname, "a"); if ((NULL == fp) && (logfp != NULL)) { /* * Some platforms (like OS/2) don't allow us to open * the same file twice, therefore we give it another * shot after closing the old file descriptor first. * * We don't do it right away because it prevents us * from logging the "can't open logfile" message to * the old logfile. * * XXX: this is a lame workaround and once the next * release is out we should stop bothering reopening * the logfile unless we have to. * * Currently we reopen it every time the config file * has been reloaded, but actually we only have to * reopen it if the file name changed or if the * configuration reload was caused by a SIGHUP. */ log_error(LOG_LEVEL_INFO, "Failed to reopen logfile: \'%s\'. " "Retrying after closing the old file descriptor first. If that " "doesn't work, Privoxy will exit without being able to log a message.", logfname); lock_logfile(); fclose(logfp); logfp = NULL; unlock_logfile(); fp = fopen(logfname, "a"); } if (NULL == fp) { log_error(LOG_LEVEL_FATAL, "init_error_log(): can't open logfile: \'%s\'", logfname); } /* set logging to be completely unbuffered */ setbuf(fp, NULL); lock_logfile(); if (logfp != NULL) { fclose(logfp); } #ifdef unix if (daemon_mode && (logfp == stderr)) { if (dup2(1, 2) == -1) { /* * We only use fatal_error() to clear the pid * file and to exit. Given that stderr has just * been closed, the user will not see the error * message. */ fatal_error("Failed to reserve fd 2."); } } #endif logfp = fp; unlock_logfile(); show_version(prog_name); unlock_loginit(); } /* init_error_log */
int main( int argc, char ** argv) { extern int optind; int opt; GSList *dumpspecs = NULL; int fd; tapelist_t *needed_tapes = NULL; char *e; rst_flags_t *rst_flags; int minimum_arguments; config_overrides_t *cfg_ovr = NULL; disklist_t diskq; char * conf_diskfile = NULL; am_feature_t *our_features = am_init_feature_set(); /* * Configure program for internationalization: * 1) Only set the message locale for now. * 2) Set textdomain for all amanda related programs to "amanda" * We don't want to be forced to support dozens of message catalogs. */ setlocale(LC_MESSAGES, "C"); textdomain("amanda"); set_pname("amfetchdump"); /* Don't die when child closes pipe */ signal(SIGPIPE, SIG_IGN); dbopen(DBG_SUBDIR_SERVER); add_amanda_log_handler(amanda_log_stderr); error_exit_status = 2; rst_flags = new_rst_flags(); rst_flags->wait_tape_prompt = 1; /* handle options */ cfg_ovr = new_config_overrides(argc/2); while( (opt = getopt_long(argc, argv, "alht:scCpb:nwi:d:O:o:", long_options, NULL)) != -1) { switch(opt) { case 0: switch (loptions) { case 1: rst_flags->headers = 1; if (strcmp(optarg, "-") == 0) rst_flags->header_to_fd = STDOUT_FILENO; else rst_flags->header_to_fd = atoi(optarg); if (fcntl(rst_flags->header_to_fd, F_GETFL, NULL) == -1) { error(_("fd %d: %s\n"), rst_flags->header_to_fd, strerror(errno)); } break; case 2: rst_flags->headers = 1; rst_flags->header_to_fd = open(optarg, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); if (rst_flags->header_to_fd == -1) { error(_("Can't create '%s': %s\n"), optarg, strerror(errno)); } break; } break; case 'b': rst_flags->blocksize = (ssize_t)strtol(optarg, &e, 10); if(*e == 'k' || *e == 'K') { rst_flags->blocksize *= 1024; } else if(*e == 'm' || *e == 'M') { rst_flags->blocksize *= 1024 * 1024; } else if(*e != '\0') { error(_("invalid blocksize value \"%s\""), optarg); /*NOTREACHED*/ } if(rst_flags->blocksize < DISK_BLOCK_BYTES) { error(_("minimum block size is %dk"), DISK_BLOCK_BYTES / 1024); /*NOTREACHED*/ } break; case 'c': rst_flags->compress = 1; break; case 'O': rst_flags->restore_dir = stralloc(optarg) ; break; case 'd': rst_flags->alt_tapedev = stralloc(optarg) ; break; case 'C': rst_flags->compress = 1; rst_flags->comp_type = COMPRESS_BEST_OPT; break; case 'p': rst_flags->pipe_to_fd = STDOUT_FILENO; break; case 's': rst_flags->fsf = (off_t)0; break; case 'l': rst_flags->leave_comp = 1; break; case 'i': rst_flags->inventory_log = stralloc(optarg); break; case 'n': rst_flags->inline_assemble = 0; break; case 'w': rst_flags->delay_assemble = 1; break; case 'a': rst_flags->wait_tape_prompt = 0; break; case 'h': rst_flags->headers = 1; break; case 'o': add_config_override_opt(cfg_ovr, optarg); break; default: usage(); /*NOTREACHED*/ } } for(fd = 3; fd < (int)FD_SETSIZE; fd++) { if (fd != debug_fd() && fd != rst_flags->pipe_to_fd && fd != rst_flags->header_to_fd) { /* * Make sure nobody spoofs us with a lot of extra open files * that would cause a successful open to get a very high file * descriptor, which in turn might be used as an index into * an array (e.g. an fd_set). */ close(fd); } } /* Check some flags that affect inventorying */ if(rst_flags->inventory_log){ if(rst_flags->inline_assemble) rst_flags->delay_assemble = 1; rst_flags->inline_assemble = 0; rst_flags->leave_comp = 1; if(rst_flags->compress){ error(_("Cannot force compression when doing inventory/search")); /*NOTREACHED*/ } g_fprintf(stderr, _("Doing inventory/search, dumps will not be uncompressed or assembled on-the-fly.\n")); } else{ if(rst_flags->delay_assemble){ g_fprintf(stderr, _("Using -w, split dumpfiles will *not* be automatically uncompressed.\n")); } } /* make sure our options all make sense otherwise */ if(check_rst_flags(rst_flags) == -1) { usage(); /*NOTREACHED*/ } if (rst_flags->inventory_log) { minimum_arguments = 1; } else { minimum_arguments = 2; } if(argc - optind < minimum_arguments) { usage(); /*NOTREACHED*/ } config_init(CONFIG_INIT_EXPLICIT_NAME, argv[optind++]); apply_config_overrides(cfg_ovr); conf_diskfile = config_dir_relative(getconf_str(CNF_DISKFILE)); read_diskfile(conf_diskfile, &diskq); amfree(conf_diskfile); if (config_errors(NULL) >= CFGERR_WARNINGS) { config_print_errors(); if (config_errors(NULL) >= CFGERR_ERRORS) { g_critical(_("errors processing config file")); } } check_running_as(RUNNING_AS_DUMPUSER); dbrename(get_config_name(), DBG_SUBDIR_SERVER); dumpspecs = cmdline_parse_dumpspecs(argc - optind, argv + optind, CMDLINE_PARSE_DATESTAMP | CMDLINE_PARSE_LEVEL | CMDLINE_EMPTY_TO_WILDCARD); /* * We've been told explicitly to go and search through the tapes the hard * way. */ if(rst_flags->inventory_log){ g_fprintf(stderr, _("Beginning tape-by-tape search.\n")); search_tapes(stderr, stdin, rst_flags->alt_tapedev == NULL, NULL, dumpspecs, rst_flags, our_features); dbclose(); exit(0); } /* Decide what tapes we'll need */ needed_tapes = list_needed_tapes(dumpspecs, rst_flags->pipe_to_fd == STDOUT_FILENO, &diskq); parent_pid = getpid(); atexit(cleanup); get_lock = lock_logfile(); /* config is loaded, should be ok here */ if(get_lock == 0) { char *process_name = get_master_process(rst_conf_logfile); error(_("%s exists: %s is already running, or you must run amcleanup"), rst_conf_logfile, process_name); } log_add(L_INFO, "%s pid %ld", get_pname(), (long)getpid()); search_tapes(NULL, stdin, rst_flags->alt_tapedev == NULL, needed_tapes, dumpspecs, rst_flags, our_features); cleanup(); dumpspec_list_free(dumpspecs); if(rst_flags->inline_assemble || rst_flags->delay_assemble) flush_open_outputs(1, NULL); else flush_open_outputs(0, NULL); free_disklist(&diskq); free_rst_flags(rst_flags); dbclose(); return(0); }