static void do_expire(char *datadir) { bookkeeper_t *books; dirstat_t *dirstat, oldstat; int ret, bookkeeper_stat, do_rescan; syslog(LOG_INFO, "Run expire on '%s'", datadir); do_rescan = 0; ret = ReadStatInfo(datadir, &dirstat, CREATE_AND_LOCK); switch (ret) { case STATFILE_OK: break; case ERR_NOSTATFILE: dirstat->low_water = 95; case FORCE_REBUILD: syslog(LOG_INFO, "Force rebuild stat record"); do_rescan = 1; break; case ERR_FAIL: syslog(LOG_ERR, "expire failed: can't read stat record"); return; /* not reached */ break; default: syslog(LOG_ERR, "expire failed: unexpected return code %i reading stat record", ret); return; /* not reached */ } bookkeeper_stat = AccessBookkeeper(&books, datadir); if ( do_rescan ) { RescanDir(datadir, dirstat); if ( bookkeeper_stat == BOOKKEEPER_OK ) { ClearBooks(books, NULL); // release the books below } } if ( bookkeeper_stat == BOOKKEEPER_OK ) { bookkeeper_t tmp_books; ClearBooks(books, &tmp_books); UpdateBookStat(dirstat, &tmp_books); ReleaseBookkeeper(books, DETACH_ONLY); } else { syslog(LOG_ERR, "Error %i: can't access book keeping records", ret); } syslog(LOG_INFO, "Limits: Filesize %s, Lifetime %s, Watermark: %llu%%\n", dirstat->max_size ? ScaleValue(dirstat->max_size) : "<none>", dirstat->max_lifetime ? ScaleTime(dirstat->max_lifetime) : "<none>", (unsigned long long)dirstat->low_water); syslog(LOG_INFO, "Current size: %s, Current lifetime: %s, Number of files: %llu", ScaleValue(dirstat->filesize), ScaleTime(dirstat->last - dirstat->first), (unsigned long long)dirstat->numfiles); oldstat = *dirstat; if ( dirstat->max_size || dirstat->max_lifetime ) ExpireDir(datadir, dirstat, dirstat->max_size, dirstat->max_lifetime, 0); WriteStatInfo(dirstat); if ( (oldstat.numfiles - dirstat->numfiles) > 0 ) { syslog(LOG_INFO, "expire completed"); syslog(LOG_INFO, " expired files: %llu", (unsigned long long)(oldstat.numfiles - dirstat->numfiles)); syslog(LOG_INFO, " expired time slot: %s", ScaleTime(dirstat->first - oldstat.first)); syslog(LOG_INFO, " expired file size: %s", ScaleValue(oldstat.filesize - dirstat->filesize)); syslog(LOG_INFO, "New size: %s, New lifetime: %s, Number of files: %llu", ScaleValue(dirstat->filesize), ScaleTime(dirstat->last - dirstat->first), (unsigned long long)dirstat->numfiles); } else { syslog(LOG_INFO, "expire completed - nothing to expire."); } ReleaseStatInfo(dirstat); } // End of do_expire
int main(int argc, char *argv[]) { sigset_t signal_set; struct sigaction sa; int c, snaplen, err, do_daemonize; int subdir_index, compress, expire, cache_size; FlowSource_t *fs; dirstat_t *dirstat; time_t t_win; char *device, *pcapfile, *filter, *datadir, *pcap_datadir, *extension_tags, pidfile[MAXPATHLEN], pidstr[32]; char *Ident, *userid, *groupid; pcap_dev_t *pcap_dev; p_packet_thread_args_t *p_packet_thread_args; p_flow_thread_args_t *p_flow_thread_args; snaplen = 100; do_daemonize = 0; launcher_pid = 0; device = NULL; pcapfile = NULL; filter = NULL; pidfile[0] = '\0'; t_win = TIME_WINDOW; datadir = DEFAULT_DIR; pcap_datadir = NULL; userid = groupid = NULL; Ident = "none"; fs = NULL; extension_tags = DefaultExtensions; subdir_index = 0; compress = NOT_COMPRESSED; verbose = 0; expire = 0; cache_size = 0; while ((c = getopt(argc, argv, "B:DEI:g:hi:j:r:s:l:p:P:t:u:S:T:e:Vz")) != EOF) { switch (c) { struct stat fstat; case 'h': usage(argv[0]); exit(0); break; case 'u': userid = optarg; break; case 'g': groupid = optarg; break; case 'D': do_daemonize = 1; break; case 'B': cache_size = atoi(optarg); if (cache_size <= 0) { LogError("ERROR: Cache size must not be < 0"); exit(EXIT_FAILURE); } break; case 'I': Ident = strdup(optarg); break; case 'i': device = optarg; break; case 'l': datadir = optarg; err = stat(datadir, &fstat); if (!(fstat.st_mode & S_IFDIR)) { LogError("No such directory: " "'%s'", datadir); break; } break; case 'p': pcap_datadir = optarg; err = stat(pcap_datadir, &fstat); if (!(fstat.st_mode & S_IFDIR)) { LogError("No such directory: " "'%s'", pcap_datadir); break; } break; case 'r': { struct stat stat_buf; pcapfile = optarg; if ( stat(pcapfile, &stat_buf) ) { LogError("Can't stat '%s': %s", pcapfile, strerror(errno)); exit(EXIT_FAILURE); } if (!S_ISREG(stat_buf.st_mode) ) { LogError("'%s' is not a file", pcapfile); exit(EXIT_FAILURE); } } break; case 's': snaplen = atoi(optarg); if (snaplen < 14 + 20 + 20) { // ethernet, IP , TCP, no payload LogError("ERROR:, snaplen < sizeof IPv4 - Need 54 bytes for TCP/IPv4"); exit(EXIT_FAILURE); } break; case 't': t_win = atoi(optarg); if (t_win < 60) { LogError("WARNING, very small time frame (< 60)!"); } if (t_win <= 0) { LogError("ERROR: time frame too small (<= 0)"); exit(EXIT_FAILURE); } break; case 'j': if ( compress ) { LogError("Use either -z for LZO or -j for BZ2 compression, but not both\n"); exit(255); } compress = BZ2_COMPRESSED; break; case 'z': if ( compress ) { LogError("Use either -z for LZO or -j for BZ2 compression, but not both\n"); exit(255); } compress = LZO_COMPRESSED; break; case 'P': if ( optarg[0] == '/' ) { // absolute path given strncpy(pidfile, optarg, MAXPATHLEN-1); } else { // path relative to current working directory char tmp[MAXPATHLEN]; if ( !getcwd(tmp, MAXPATHLEN-1) ) { fprintf(stderr, "Failed to get current working directory: %s\n", strerror(errno)); exit(255); } tmp[MAXPATHLEN-1] = 0; snprintf(pidfile, MAXPATHLEN - 1 - strlen(tmp), "%s/%s", tmp, optarg); } // pidfile now absolute path pidfile[MAXPATHLEN-1] = 0; break; case 'S': subdir_index = atoi(optarg); break; case 'T': { size_t len = strlen(optarg); extension_tags = optarg; if ( len == 0 || len > 128 ) { fprintf(stderr, "Extension length error. Unexpected option '%s'\n", extension_tags); exit(255); } break; } case 'E': verbose = 1; Setv6Mode(1); break; case 'V': printf("%s: Version: %s\n",argv[0], nfdump_version); exit(0); break; default: usage(argv[0]); exit(EXIT_FAILURE); } } if (argc - optind > 1) { usage(argv[0]); exit(EXIT_FAILURE); } else { /* user specified a pcap filter */ filter = argv[optind]; } if ( fs == NULL && datadir != NULL && !AddDefaultFlowSource(&fs, Ident, datadir) ) { fprintf(stderr, "Failed to add default data collector directory\n"); exit(255); } if ( device && pcapfile ) { LogError("Specify either a device or a pcapfile, but not both"); exit(EXIT_FAILURE); } if ( !device && !pcapfile ) { LogError("Specify either a device or a pcapfile to read packets from"); exit(EXIT_FAILURE); } if ( !Init_FlowTree(cache_size)) { LogError("Init_FlowTree() failed."); exit(EXIT_FAILURE); } InitExtensionMaps(NO_EXTENSION_LIST); SetupExtensionDescriptors(strdup(extension_tags)); if ( pcapfile ) { pcap_dev = setup_pcap_file(pcapfile, filter, snaplen); } else { pcap_dev = setup_pcap_live(device, filter, snaplen); } if (!pcap_dev) { exit(EXIT_FAILURE); } SetPriv(userid, groupid); if ( subdir_index && !InitHierPath(subdir_index) ) { pcap_close(pcap_dev->handle); exit(255); } if ( do_daemonize && !InitLog(argv[0], SYSLOG_FACILITY)) { pcap_close(pcap_dev->handle); exit(255); } // check if pid file exists and if so, if a process with registered pid is running if ( strlen(pidfile) ) { int pidf; pidf = open(pidfile, O_RDONLY, 0); if ( pidf > 0 ) { // pid file exists char s[32]; ssize_t len; len = read(pidf, (void *)s, 31); close(pidf); s[31] = '\0'; if ( len < 0 ) { fprintf(stderr, "read() error existing pid file: %s\n", strerror(errno)); pcap_close(pcap_dev->handle); exit(255); } else { unsigned long pid = atol(s); if ( pid == 0 ) { // garbage - use this file unlink(pidfile); } else { if ( kill(pid, 0) == 0 ) { // process exists fprintf(stderr, "A process with pid %lu registered in pidfile %s is already running!\n", pid, strerror(errno)); pcap_close(pcap_dev->handle); exit(255); } else { // no such process - use this file unlink(pidfile); } } } } else { if ( errno != ENOENT ) { fprintf(stderr, "open() error existing pid file: %s\n", strerror(errno)); pcap_close(pcap_dev->handle); exit(255); } // else errno == ENOENT - no file - this is fine } } if ( do_daemonize ) { verbose = 0; daemonize(); } if (strlen(pidfile)) { pid_t pid = getpid(); int pidf = open(pidfile, O_RDWR|O_TRUNC|O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if ( pidf == -1 ) { LogError("Error opening pid file: '%s' %s", pidfile, strerror(errno)); pcap_close(pcap_dev->handle); exit(255); } snprintf(pidstr,31,"%lu\n", (unsigned long)pid); if ( write(pidf, pidstr, strlen(pidstr)) <= 0 ) { LogError("Error write pid file: '%s' %s", pidfile, strerror(errno)); } close(pidf); } if ( InitBookkeeper(&fs->bookkeeper, fs->datadir, getpid(), launcher_pid) != BOOKKEEPER_OK ) { LogError("initialize bookkeeper failed."); pcap_close(pcap_dev->handle); exit(255); } // Init the extension map list if ( !InitExtensionMapList(fs) ) { // error message goes to syslog pcap_close(pcap_dev->handle); exit(255); } IPFragTree_init(); LogInfo("Startup."); // prepare signal mask for all threads // block signals, as they are handled by the main thread // mask is inherited by all threads sigemptyset(&signal_set); sigaddset(&signal_set, SIGINT); sigaddset(&signal_set, SIGHUP); sigaddset(&signal_set, SIGTERM); sigaddset(&signal_set, SIGUSR1); sigaddset(&signal_set, SIGPIPE); pthread_sigmask(SIG_BLOCK, &signal_set, NULL); // for USR2 set a signal handler, which interrupts blocking // system calls - and signals done event // handler applies for all threads in a process sa.sa_handler = Interrupt_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sigaction(SIGPIPE, &sa, NULL); sigaction(SIGUSR2, &sa, NULL); // key for each thread err = pthread_key_create(&buffer_key, NULL); if ( err ) { LogError("pthread_key() error in %s line %d: %s\n", __FILE__, __LINE__, strerror(errno) ); exit(255); } // prepare flow thread args p_flow_thread_args = (p_flow_thread_args_t *)malloc(sizeof(p_flow_thread_args_t)); if ( !p_flow_thread_args ) { LogError("malloc() error in %s line %d: %s\n", __FILE__, __LINE__, strerror(errno) ); exit(255); } p_flow_thread_args->fs = fs; p_flow_thread_args->t_win = t_win; p_flow_thread_args->compress = compress; p_flow_thread_args->subdir_index = subdir_index; p_flow_thread_args->parent = pthread_self(); p_flow_thread_args->NodeList = NewNodeList(); err = 0; err = pthread_create(&p_flow_thread_args->tid, NULL, p_flow_thread, (void *)p_flow_thread_args); if ( err ) { LogError("pthread_create() error in %s line %d: %s\n", __FILE__, __LINE__, strerror(errno) ); exit(255); } dbg_printf("Started flow thread[%lu]\n", (long unsigned)p_flow_thread_args->tid); // prepare packet thread args p_packet_thread_args = (p_packet_thread_args_t *)malloc(sizeof(p_packet_thread_args_t)); if ( !p_packet_thread_args ) { LogError("malloc() error in %s line %d: %s\n", __FILE__, __LINE__, strerror(errno) ); exit(255); } p_packet_thread_args->pcap_dev = pcap_dev; p_packet_thread_args->t_win = t_win; p_packet_thread_args->subdir_index = subdir_index; p_packet_thread_args->pcap_datadir = pcap_datadir; p_packet_thread_args->live = device != NULL; p_packet_thread_args->parent = pthread_self(); p_packet_thread_args->NodeList = p_flow_thread_args->NodeList; err = pthread_create(&p_packet_thread_args->tid, NULL, p_packet_thread, (void *)p_packet_thread_args); if ( err ) { LogError("pthread_create() error in %s line %d: %s\n", __FILE__, __LINE__, strerror(errno) ); exit(255); } dbg_printf("Started packet thread[%lu]\n", (long unsigned)p_packet_thread_args->tid); // Wait till done WaitDone(); dbg_printf("Signal packet thread to terminate\n"); SignalThreadTerminate((thread_info_t *)p_packet_thread_args, NULL); dbg_printf("Signal flow thread to terminate\n"); SignalThreadTerminate((thread_info_t *)p_flow_thread_args, &p_packet_thread_args->NodeList->c_list); // free arg list free((void *)p_packet_thread_args); free((void *)p_flow_thread_args); LogInfo("Terminating nfpcapd."); if ( expire == 0 && ReadStatInfo(fs->datadir, &dirstat, LOCK_IF_EXISTS) == STATFILE_OK ) { UpdateBookStat(dirstat, fs->bookkeeper); WriteStatInfo(dirstat); LogInfo("Updating statinfo in directory '%s'", datadir); } ReleaseBookkeeper(fs->bookkeeper, DESTROY_BOOKKEEPER); pcap_close(pcap_dev->handle); if ( strlen(pidfile) ) unlink(pidfile); EndLog(); exit(EXIT_SUCCESS); } /* End of main */
int main(int argc, char **argv) { char *bindhost, *filter, *datadir, pidstr[32], *launch_process; char *userid, *groupid, *checkptr, *listenport, *mcastgroup, *extension_tags; char *Ident, *dynsrcdir, pidfile[MAXPATHLEN]; struct stat fstat; packet_function_t receive_packet; send_peer_t peer; FlowSource_t *fs; struct sigaction act; int family, bufflen; time_t twin, t_start; int sock, synctime, do_daemonize, expire, report_sequence, do_xstat; int subdir_index, sampling_rate, compress; int c; #ifdef PCAP char *pcap_file; pcap_file = NULL; #endif receive_packet = recvfrom; verbose = synctime = do_daemonize = 0; bufflen = 0; family = AF_UNSPEC; launcher_pid = 0; launcher_alive = 0; report_sequence = 0; listenport = DEFAULTCISCOPORT; bindhost = NULL; mcastgroup = NULL; pidfile[0] = 0; filter = NULL; launch_process = NULL; userid = groupid = NULL; twin = TIME_WINDOW; datadir = NULL; subdir_index = 0; expire = 0; sampling_rate = 1; compress = 0; do_xstat = 0; memset((void *)&peer, 0, sizeof(send_peer_t)); peer.family = AF_UNSPEC; Ident = "none"; FlowSource = NULL; extension_tags = DefaultExtensions; dynsrcdir = NULL; while ((c = getopt(argc, argv, "46ef:whEVI:DB:b:j:l:M:n:p:P:R:S:s:T:t:x:Xru:g:z")) != EOF) { switch (c) { case 'h': usage(argv[0]); exit(0); break; case 'u': userid = optarg; break; case 'g': groupid = optarg; break; case 'e': expire = 1; break; case 'f': { #ifdef PCAP struct stat fstat; pcap_file = optarg; stat(pcap_file, &fstat); if ( !S_ISREG(fstat.st_mode) ) { fprintf(stderr, "Not a regular file: %s\n", pcap_file); exit(254); } #else fprintf(stderr, "PCAP reader not compiled! Option ignored!\n"); #endif } break; case 'E': verbose = 1; Setv6Mode(1); break; case 'V': printf("%s: Version: %s\n",argv[0], nfdump_version); exit(0); break; case 'X': do_xstat = 1; break; case 'D': do_daemonize = 1; break; case 'I': Ident = strdup(optarg); break; case 'M': dynsrcdir = strdup(optarg); if ( strlen(dynsrcdir) > MAXPATHLEN ) { fprintf(stderr, "ERROR: Path too long!\n"); exit(255); } if ( stat(dynsrcdir, &fstat) < 0 ) { fprintf(stderr, "stat() failed on %s: %s\n", dynsrcdir, strerror(errno)); exit(255); } if ( !(fstat.st_mode & S_IFDIR) ) { fprintf(stderr, "No such directory: %s\n", dynsrcdir); break; } if ( !SetDynamicSourcesDir(&FlowSource, dynsrcdir) ) { fprintf(stderr, "-l, -M and -n are mutually exclusive\n"); break; } break; case 'n': if ( AddFlowSource(&FlowSource, optarg) != 1 ) exit(255); break; case 'w': synctime = 1; break; case 'B': bufflen = strtol(optarg, &checkptr, 10); if ( (checkptr != NULL && *checkptr == 0) && bufflen > 0 ) break; fprintf(stderr,"Argument error for -B\n"); exit(255); case 'b': bindhost = optarg; break; case 'j': mcastgroup = optarg; break; case 'p': listenport = optarg; break; case 'P': if ( optarg[0] == '/' ) { // absolute path given strncpy(pidfile, optarg, MAXPATHLEN-1); } else { // path relative to current working directory char tmp[MAXPATHLEN]; if ( !getcwd(tmp, MAXPATHLEN-1) ) { fprintf(stderr, "Failed to get current working directory: %s\n", strerror(errno)); exit(255); } tmp[MAXPATHLEN-1] = 0; snprintf(pidfile, MAXPATHLEN - 1 - strlen(tmp), "%s/%s", tmp, optarg); } // pidfile now absolute path pidfile[MAXPATHLEN-1] = 0; break; case 'R': { char *p = strchr(optarg, '/'); if ( p ) { *p++ = '\0'; peer.port = strdup(p); } else { peer.port = DEFAULTCISCOPORT; } peer.hostname = strdup(optarg); break; } case 'r': report_sequence = 1; break; case 's': // a negative sampling rate is set as the overwrite sampling rate sampling_rate = (int)strtol(optarg, (char **)NULL, 10); if ( (sampling_rate == 0 ) || (sampling_rate < 0 && sampling_rate < -10000000) || (sampling_rate > 0 && sampling_rate > 10000000) ) { fprintf(stderr, "Invalid sampling rate: %s\n", optarg); exit(255); } break; case 'T': { size_t len = strlen(optarg); extension_tags = optarg; if ( len == 0 || len > 128 ) { fprintf(stderr, "Extension length error. Unexpected option '%s'\n", extension_tags); exit(255); } break; } case 'l': datadir = optarg; if ( strlen(datadir) > MAXPATHLEN ) { fprintf(stderr, "ERROR: Path too long!\n"); exit(255); } if ( stat(datadir, &fstat) < 0 ) { fprintf(stderr, "stat() failed on %s: %s\n", datadir, strerror(errno)); exit(255); } if ( !(fstat.st_mode & S_IFDIR) ) { fprintf(stderr, "No such directory: %s\n", datadir); break; } break; case 'S': subdir_index = atoi(optarg); break; case 't': twin = atoi(optarg); if ( twin <= 0 ) { fprintf(stderr, "ERROR: time frame <= 0\n"); exit(255); } if (twin < 10) { fprintf(stderr, "WARNING, Very small time frame - < 10s!\n"); } break; case 'x': launch_process = optarg; break; case 'z': compress = 1; break; case '4': if ( family == AF_UNSPEC ) family = AF_INET; else { fprintf(stderr, "ERROR, Accepts only one protocol IPv4 or IPv6!\n"); exit(255); } break; case '6': if ( family == AF_UNSPEC ) family = AF_INET6; else { fprintf(stderr, "ERROR, Accepts only one protocol IPv4 or IPv6!\n"); exit(255); } break; default: usage(argv[0]); exit(255); } } if ( FlowSource == NULL && datadir == NULL && dynsrcdir == NULL ) { fprintf(stderr, "ERROR, Missing -n (-l/-I) or -M source definitions\n"); exit(255); } if ( FlowSource == NULL && datadir != NULL && !AddDefaultFlowSource(&FlowSource, Ident, datadir) ) { fprintf(stderr, "Failed to add default data collector directory\n"); exit(255); } if ( bindhost && mcastgroup ) { fprintf(stderr, "ERROR, -b and -j are mutually exclusive!!\n"); exit(255); } if ( do_daemonize && !InitLog(argv[0], SYSLOG_FACILITY)) { exit(255); } InitExtensionMaps(NO_EXTENSION_LIST); SetupExtensionDescriptors(strdup(extension_tags)); // Debug code to read from pcap file #ifdef PCAP sock = 0; if ( pcap_file ) { printf("Setup pcap reader\n"); setup_packethandler(pcap_file, NULL); receive_packet = NextPacket; } else #endif if ( mcastgroup ) sock = Multicast_receive_socket (mcastgroup, listenport, family, bufflen); else sock = Unicast_receive_socket(bindhost, listenport, family, bufflen ); if ( sock == -1 ) { fprintf(stderr,"Terminated due to errors.\n"); exit(255); } if ( peer.hostname ) { peer.sockfd = Unicast_send_socket (peer.hostname, peer.port, peer.family, bufflen, &peer.addr, &peer.addrlen ); if ( peer.sockfd <= 0 ) exit(255); LogInfo("Replay flows to host: %s port: %s", peer.hostname, peer.port); } if ( sampling_rate < 0 ) { default_sampling = -sampling_rate; overwrite_sampling = default_sampling; } else { default_sampling = sampling_rate; } SetPriv(userid, groupid); if ( subdir_index && !InitHierPath(subdir_index) ) { close(sock); exit(255); } // check if pid file exists and if so, if a process with registered pid is running if ( strlen(pidfile) ) { int pidf; pidf = open(pidfile, O_RDONLY, 0); if ( pidf > 0 ) { // pid file exists char s[32]; ssize_t len; len = read(pidf, (void *)s, 31); close(pidf); s[31] = '\0'; if ( len < 0 ) { fprintf(stderr, "read() error existing pid file: %s\n", strerror(errno)); exit(255); } else { unsigned long pid = atol(s); if ( pid == 0 ) { // garbage - use this file unlink(pidfile); } else { if ( kill(pid, 0) == 0 ) { // process exists fprintf(stderr, "A process with pid %lu registered in pidfile %s is already running!\n", pid, strerror(errno)); exit(255); } else { // no such process - use this file unlink(pidfile); } } } } else { if ( errno != ENOENT ) { fprintf(stderr, "open() error existing pid file: %s\n", strerror(errno)); exit(255); } // else errno == ENOENT - no file - this is fine } } if (argc - optind > 1) { usage(argv[0]); close(sock); exit(255); } else { /* user specified a pcap filter */ filter = argv[optind]; } t_start = time(NULL); if ( synctime ) t_start = t_start - ( t_start % twin); if ( do_daemonize ) { verbose = 0; daemonize(); } if (strlen(pidfile)) { pid_t pid = getpid(); int pidf = open(pidfile, O_RDWR|O_TRUNC|O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if ( pidf == -1 ) { LogError("Error opening pid file: '%s' %s", pidfile, strerror(errno)); close(sock); exit(255); } snprintf(pidstr,31,"%lu\n", (unsigned long)pid); if ( write(pidf, pidstr, strlen(pidstr)) <= 0 ) { LogError("Error write pid file: '%s' %s", pidfile, strerror(errno)); } close(pidf); } done = 0; if ( launch_process || expire ) { // for efficiency reason, the process collecting the data // and the process launching processes, when a new file becomes // available are separated. Communication is done using signals // as well as shared memory // prepare shared memory shmem = mmap(0, sizeof(srecord_t), PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0); if ( shmem == (caddr_t)-1 ) { LogError("mmap() error: %s", strerror(errno)); close(sock); exit(255); } launcher_pid = fork(); switch (launcher_pid) { case 0: // child close(sock); launcher((char *)shmem, FlowSource, launch_process, expire); _exit(0); break; case -1: LogError("fork() error: %s", strerror(errno)); if ( strlen(pidfile) ) unlink(pidfile); exit(255); break; default: // parent launcher_alive = 1; LogInfo("Launcher[%i] forked", launcher_pid); } } fs = FlowSource; while ( fs ) { if ( InitBookkeeper(&fs->bookkeeper, fs->datadir, getpid(), launcher_pid) != BOOKKEEPER_OK ) { LogError("initialize bookkeeper failed."); // release all already allocated bookkeepers fs = FlowSource; while ( fs && fs->bookkeeper ) { ReleaseBookkeeper(fs->bookkeeper, DESTROY_BOOKKEEPER); fs = fs->next; } close(sock); if ( launcher_pid ) kill_launcher(launcher_pid); if ( strlen(pidfile) ) unlink(pidfile); exit(255); } // Init the extension map list if ( !InitExtensionMapList(fs) ) { // error message goes to syslog exit(255); } fs = fs->next; } /* Signal handling */ memset((void *)&act,0,sizeof(struct sigaction)); act.sa_handler = IntHandler; sigemptyset(&act.sa_mask); act.sa_flags = 0; sigaction(SIGTERM, &act, NULL); sigaction(SIGINT, &act, NULL); sigaction(SIGHUP, &act, NULL); sigaction(SIGALRM, &act, NULL); sigaction(SIGCHLD, &act, NULL); LogInfo("Startup."); run(receive_packet, sock, peer, twin, t_start, report_sequence, subdir_index, compress, do_xstat); close(sock); kill_launcher(launcher_pid); fs = FlowSource; while ( fs && fs->bookkeeper ) { dirstat_t *dirstat; // if we do not auto expire and there is a stat file, update the stats before we leave if ( expire == 0 && ReadStatInfo(fs->datadir, &dirstat, LOCK_IF_EXISTS) == STATFILE_OK ) { UpdateBookStat(dirstat, fs->bookkeeper); WriteStatInfo(dirstat); LogInfo("Updating statinfo in directory '%s'", datadir); } ReleaseBookkeeper(fs->bookkeeper, DESTROY_BOOKKEEPER); fs = fs->next; } LogInfo("Terminating nfcapd."); EndLog(); if ( strlen(pidfile) ) unlink(pidfile); return 0; } /* End of main */
int main( int argc, char **argv ) { struct stat fstat; int c, err, maxsize_set, maxlife_set; int do_rescan, do_expire, do_list, print_stat, do_update_param, print_books, is_profile, nfsen_format; char *maxsize_string, *lifetime_string, *datadir; uint64_t maxsize, lifetime, low_water; uint32_t runtime; channel_t *channel, *current_channel; maxsize_string = lifetime_string = NULL; datadir = NULL; maxsize = lifetime = 0; do_rescan = 0; do_expire = 0; do_list = 0; do_update_param = 0; is_profile = 0; print_stat = 0; print_books = 0; maxsize_set = 0; maxlife_set = 0; low_water = 0; nfsen_format = 0; runtime = 0; while ((c = getopt(argc, argv, "e:hl:L:T:Ypr:s:t:u:w:")) != EOF) { switch (c) { case 'h': usage(argv[0]); exit(0); break; case 'l': CheckDataDir(datadir); datadir = optarg; do_list = 1; print_stat = 1; break; case 'L': CheckDataDir(datadir); datadir = optarg; print_stat = 1; print_books = 1; break; case 'p': is_profile = 1; break; case 'r': CheckDataDir(datadir); do_rescan = 1; print_stat = 1; datadir = optarg; break; case 'e': CheckDataDir(datadir); datadir = optarg; do_expire = 1; print_stat = 1; break; case 's': if ( ParseSizeDef(optarg, &maxsize ) == 0 ) exit(250); maxsize_set = 1; break; case 't': if ( ParseTimeDef(optarg, &lifetime ) == 0 ) exit(250); maxlife_set = 1; break; case 'u': CheckDataDir(datadir); datadir = optarg; do_update_param = 1; break; case 'w': low_water = strtoll(optarg, NULL, 10); if ( low_water > 100 ) { fprintf(stderr, "Water mark > 100%%\n"); exit(250); } if ( low_water == 0 ) low_water = 100; break; case 'T': runtime = strtoll(optarg, NULL, 10); if ( runtime > 3600 ) { fprintf(stderr, "Runtime > 3600 (1h)\n"); exit(250); } break; case 'Y': nfsen_format = 1; break; default: usage(argv[0]); exit(250); } } datadir = AbsolutePath(datadir); if ( !datadir ) { fprintf(stderr, "Missing data directory\n"); usage(argv[0]); exit(250); } err = stat(datadir, &fstat); if ( !(fstat.st_mode & S_IFDIR) ) { fprintf(stderr, "No such directory: %s\n", datadir); exit(250); } channel = GetChannelList(datadir, is_profile, do_rescan); // GetChannelList(datadir, is_profile, do_rescan); if ( !channel ) { exit(250); } // printf("Size: %llu, time: %llu\n", maxsize, lifetime); // update water mark only, when not listing if ( !is_profile && !do_list && low_water ) channel->dirstat->low_water = low_water; /* process do_list first: if the UpdateBookStat results in a FORCE_REBUILD, * this will immediately done afterwards * do_expire will need accurate books as well, so update the books here as well */ if ( do_list || do_expire ) { current_channel = channel; while ( current_channel ) { if ( current_channel->books_stat == BOOKKEEPER_OK ) { bookkeeper_t tmp_books; printf("Include nfcapd bookeeping record in %s\n", current_channel->datadir); ClearBooks(current_channel->books, &tmp_books); UpdateBookStat(current_channel->dirstat, &tmp_books); if ( current_channel->dirstat->status == FORCE_REBUILD ) current_channel->do_rescan = 1; } current_channel = current_channel->next; } } // process do_rescan: make sure stats are up to date, if required current_channel = channel; while ( current_channel ) { if ( current_channel->do_rescan ) { int i; uint64_t last_sequence; /* detect new files: If nfcapd adds a new file while we are rescanning the directory * this results in inconsistent data for the rescan. Therefore check at the begin and end * of the rescan for the sequence number, which reflects the accesss/change to the bookkeeping record * It's assumed, that such an event does not occure more than once. However, loop 3 times max */ for ( i=0; i<3; i++ ) { last_sequence = BookSequence(current_channel->books); printf("Scanning files in %s .. ", current_channel->datadir); RescanDir(current_channel->datadir, current_channel->dirstat); if ( current_channel->dirstat->numfiles == 0 ) { //nothing found current_channel->status = NOFILES; } if ( BookSequence(current_channel->books) == last_sequence ) break; printf("Rescan again, due to file changes in directory!\n"); } if ( BookSequence(current_channel->books) != last_sequence ) { fprintf(stderr, "Could not savely rescan the directory. Data is not consistent.\n"); ReleaseBookkeeper(current_channel->books, DETACH_ONLY); if ( current_channel->status == OK ) WriteStatInfo(current_channel->dirstat); exit(250); } printf("done.\n"); if ( current_channel->books_stat == BOOKKEEPER_OK ) { printf("Updating nfcapd bookeeping records\n"); ClearBooks(channel->books, NULL); } } current_channel = current_channel->next; } // now process do_expire if required if ( do_expire ) { dirstat_t old_stat, current_stat; if ( is_profile ) { current_stat.status = 0; current_stat.max_lifetime = lifetime; current_stat.max_size = maxsize; current_stat.low_water = low_water ? low_water : 98; // sum up all channels in the profile current_channel = channel; current_stat.numfiles = current_channel->dirstat->numfiles; current_stat.filesize = current_channel->dirstat->filesize; current_stat.first = current_channel->dirstat->first; current_stat.last = current_channel->dirstat->last; current_channel = current_channel->next; while ( current_channel ) { current_stat.numfiles += current_channel->dirstat->numfiles; current_stat.filesize += current_channel->dirstat->filesize; if ( current_channel->dirstat->first && (current_channel->dirstat->first < current_stat.first) ) current_stat.first = current_channel->dirstat->first; if ( current_channel->dirstat->last > current_stat.last ) current_stat.last = current_channel->dirstat->last; current_channel = current_channel->next; } old_stat = current_stat; ExpireProfile(channel, ¤t_stat, maxsize, lifetime, runtime); } else { // cmd args override dirstat values if ( maxsize_set ) channel->dirstat->max_size = maxsize; else maxsize = channel->dirstat->max_size; if ( maxlife_set ) channel->dirstat->max_lifetime = lifetime; else lifetime = channel->dirstat->max_lifetime; old_stat = *(channel->dirstat); ExpireDir(channel->datadir, channel->dirstat, maxsize, lifetime, runtime); current_stat = *(channel->dirstat); } // Report, what we have done printf("Expired files: %llu\n", (unsigned long long)(old_stat.numfiles - current_stat.numfiles)); printf("Expired file size: %s\n", ScaleValue(old_stat.filesize - current_stat.filesize)); printf("Expired time range: %s\n\n", ScaleTime(current_stat.first - old_stat.first)); } if ( !is_profile && do_update_param ) { switch (channel->books_stat) { case BOOKKEEPER_OK: if ( maxsize_set ) channel->dirstat->max_size = maxsize; else maxsize = channel->dirstat->max_size; if ( maxlife_set ) channel->dirstat->max_lifetime = lifetime; else lifetime = channel->dirstat->max_lifetime; printf("Update collector process running for directory: '%s'\n", datadir); UpdateBooksParam(channel->books, (time_t)lifetime, maxsize); print_stat = 1; break; case ERR_NOTEXISTS: if ( maxsize_set ) channel->dirstat->max_size = maxsize; if ( maxlife_set ) channel->dirstat->max_lifetime = lifetime; print_stat = 1; break; default: // should never be reached as already cought earlier printf("Error %i while connecting to collector\n", channel->books_stat); } if ( channel->status == OK || channel->status == NOFILES ) WriteStatInfo(channel->dirstat); } if ( !is_profile && print_books ) { switch (channel->books_stat) { case BOOKKEEPER_OK: PrintBooks(channel->books); break; case ERR_NOTEXISTS: printf("No collector process running for directory: '%s'\n", channel->datadir); break; default: // should never be reached as already cought earlier printf("Error %i while connecting to collector\n", channel->books_stat); } } if ( print_stat ) { if ( is_profile ) { dirstat_t current_stat; current_stat.status = 0; current_stat.max_lifetime = lifetime; current_stat.max_size = maxsize; current_stat.low_water = low_water ? low_water : 98; // sum up all channels in the profile current_channel = channel; current_stat.numfiles = current_channel->dirstat->numfiles; current_stat.filesize = current_channel->dirstat->filesize; current_stat.first = current_channel->dirstat->first; current_stat.last = current_channel->dirstat->last; current_channel = current_channel->next; while ( current_channel ) { current_stat.numfiles += current_channel->dirstat->numfiles; current_stat.filesize += current_channel->dirstat->filesize; if ( current_channel->dirstat->first && (current_channel->dirstat->first < current_stat.first) ) current_stat.first = current_channel->dirstat->first; if ( current_channel->dirstat->last > current_stat.last ) current_stat.last = current_channel->dirstat->last; current_channel = current_channel->next; } if ( nfsen_format ) { printf("Stat|%llu|%llu|%llu\n", (unsigned long long)current_stat.filesize, (unsigned long long)current_stat.first, (unsigned long long)current_stat.last); } else PrintDirStat(¤t_stat); } else if ( nfsen_format ) printf("Stat|%llu|%llu|%llu\n", (unsigned long long)channel->dirstat->filesize, (unsigned long long)channel->dirstat->first, (unsigned long long)channel->dirstat->last ); else PrintDirStat(channel->dirstat); } current_channel = channel; while ( current_channel ) { ReleaseBookkeeper(current_channel->books, DETACH_ONLY); if ( current_channel->status == OK ) WriteStatInfo(current_channel->dirstat); current_channel = current_channel->next; } return 0; }