/* * Copyright (c) 1998-2001 * University of Southern California/Information Sciences Institute. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * $Id: main.c,v 1.1.1.1 2017/06/12 07:59:37 misho Exp $ */ /* * Part of this program has been derived from mrouted. * The mrouted program is covered by the license in the accompanying file * named "LICENSE.mrouted". * * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of * Leland Stanford Junior University. * */ #include "defs.h" #include #include #include char versionstring[100]; int disable_all_by_default = 0; int haveterminal = 1; struct rp_hold *g_rp_hold = NULL; int mrt_table_id = 0; char *config_file = _PATH_PIMD_CONF; extern int loglevel; extern char todaysversion[]; static int sighandled = 0; #define GOT_SIGINT 0x01 #define GOT_SIGHUP 0x02 #define GOT_SIGUSR1 0x04 #define GOT_SIGUSR2 0x08 #define GOT_SIGALRM 0x10 #define NHANDLERS 3 static struct ihandler { int fd; /* File descriptor */ ihfunc_t func; /* Function to call with &fd_set */ } ihandlers[NHANDLERS]; static int nhandlers = 0; static struct debugname { char *name; uint32_t level; size_t nchars; } debugnames[] = { { "dvmrp_detail", DEBUG_DVMRP_DETAIL, 5 }, { "dvmrp_prunes", DEBUG_DVMRP_PRUNE, 8 }, { "dvmrp_pruning", DEBUG_DVMRP_PRUNE, 8 }, { "dvmrp_routes", DEBUG_DVMRP_ROUTE, 7 }, { "dvmrp_routing", DEBUG_DVMRP_ROUTE, 7 }, { "dvmrp_mrt", DEBUG_DVMRP_ROUTE, 7 }, { "dvmrp_neighbors", DEBUG_DVMRP_PEER, 7 }, { "dvmrp_peers", DEBUG_DVMRP_PEER, 8 }, { "dvmrp_hello", DEBUG_DVMRP_PEER, 7 }, { "dvmrp_timers", DEBUG_DVMRP_TIMER, 7 }, { "dvmrp", DEBUG_DVMRP, 1 }, { "igmp_proto", DEBUG_IGMP_PROTO, 6 }, { "igmp_timers", DEBUG_IGMP_TIMER, 6 }, { "igmp_members", DEBUG_IGMP_MEMBER, 6 }, { "groups", DEBUG_MEMBER, 1 }, { "membership", DEBUG_MEMBER, 2 }, { "igmp", DEBUG_IGMP, 1 }, { "trace", DEBUG_TRACE, 2 }, { "mtrace", DEBUG_TRACE, 2 }, { "traceroute", DEBUG_TRACE, 2 }, { "timeout", DEBUG_TIMEOUT, 2 }, { "callout", DEBUG_TIMEOUT, 3 }, { "packets", DEBUG_PKT, 2 }, { "pkt", DEBUG_PKT, 2 }, { "interfaces", DEBUG_IF, 2 }, { "vif", DEBUG_IF, 1 }, { "kernel", DEBUG_KERN, 2 }, { "cache", DEBUG_MFC, 1 }, { "mfc", DEBUG_MFC, 2 }, { "k_cache", DEBUG_MFC, 2 }, { "k_mfc", DEBUG_MFC, 2 }, { "rsrr", DEBUG_RSRR, 2 }, { "pim_detail", DEBUG_PIM_DETAIL, 5 }, { "pim_hello", DEBUG_PIM_HELLO, 5 }, { "pim_neighbors", DEBUG_PIM_HELLO, 5 }, { "pim_peers", DEBUG_PIM_HELLO, 5 }, { "pim_register", DEBUG_PIM_REGISTER, 5 }, { "registers", DEBUG_PIM_REGISTER, 2 }, { "pim_join_prune", DEBUG_PIM_JOIN_PRUNE, 5 }, { "pim_j_p", DEBUG_PIM_JOIN_PRUNE, 5 }, { "pim_jp", DEBUG_PIM_JOIN_PRUNE, 5 }, { "pim_bootstrap", DEBUG_PIM_BOOTSTRAP, 5 }, { "pim_bsr", DEBUG_PIM_BOOTSTRAP, 5 }, { "bsr", DEBUG_PIM_BOOTSTRAP, 1 }, { "bootstrap", DEBUG_PIM_BOOTSTRAP, 1 }, { "pim_asserts", DEBUG_PIM_ASSERT, 5 }, { "pim_cand_rp", DEBUG_PIM_CAND_RP, 5 }, { "pim_c_rp", DEBUG_PIM_CAND_RP, 5 }, { "pim_rp", DEBUG_PIM_CAND_RP, 6 }, { "rp", DEBUG_PIM_CAND_RP, 2 }, { "pim_routes", DEBUG_PIM_MRT, 6 }, { "pim_routing", DEBUG_PIM_MRT, 6 }, { "pim_mrt", DEBUG_PIM_MRT, 5 }, { "pim_timers", DEBUG_PIM_TIMER, 5 }, { "pim_rpf", DEBUG_PIM_RPF, 6 }, { "rpf", DEBUG_RPF, 3 }, { "pim", DEBUG_PIM, 1 }, { "routes", DEBUG_MRT, 1 }, { "routing", DEBUG_MRT, 1 }, { "mrt", DEBUG_MRT, 1 }, { "neighbors", DEBUG_NEIGHBORS, 1 }, { "routers", DEBUG_NEIGHBORS, 6 }, { "mrouters", DEBUG_NEIGHBORS, 7 }, { "peers", DEBUG_NEIGHBORS, 1 }, { "timers", DEBUG_TIMER, 1 }, { "asserts", DEBUG_ASSERT, 1 }, { "all", DEBUG_ALL, 2 }, { "3", 0xffffffff, 1 } /* compat. */ }; /* * Forward declarations. */ static void handler (int); static void timer (void *); static void cleanup (void); static void restart (int); static void resetlogging (void *); int register_input_handler(int fd, ihfunc_t func) { if (nhandlers >= NHANDLERS) return -1; ihandlers[nhandlers].fd = fd; ihandlers[nhandlers++].func = func; return 0; } static void do_randomize(void) { #define rol32(data,shift) ((data) >> (shift)) | ((data) << (32 - (shift))) int fd; unsigned int seed; /* Setup a fallback seed based on quasi random. */ #ifdef SYSV seed = time(NULL); #else seed = time(NULL) ^ gethostid(); #endif seed = rol32(seed, seed); fd = open("/dev/urandom", O_RDONLY); if (fd >= 0) { if (-1 == read(fd, &seed, sizeof(seed))) warn("Failed reading entropy from /dev/urandom"); close(fd); } #ifdef SYSV srand48(seed); #else srandom(seed); #endif } /* Figure out the PID of a running daemon. */ static pid_t daemon_pid(void) { int result; char *path = NULL; FILE *fp; pid_t pid = -1; result = asprintf(&path, "%s%s.pid", _PATH_VARRUN, __progname); if (result == -1 || path == NULL) return -1; fp = fopen(path, "r"); if (!fp) { free(path); return -1; } result = fscanf(fp, "%d", &pid); fclose(fp); free(path); return pid; } /* Send signal to running daemon and the show resulting file. */ static int killshow(int signo, char *file) { pid_t pid = daemon_pid(); char buf[100]; if (pid > 0) { if (file && -1 == remove(file) && errno != ENOENT) warn("Failed removing %s, may be showing stale information", file); kill(pid, signo); if (file) { usleep(200); snprintf(buf, sizeof(buf), "cat %s", file); if (-1 == system(buf)) { warnx("Failed listing file %s\n", file); } } } return 0; } static int usage(int code) { size_t i; char line[76] = " "; struct debugname *d; printf("\nUsage: %s [-fhlNqrv] [-c FILE] [-d [SYS][,SYS...]] [-s LEVEL]\n\n", __progname); printf(" -c, --config=FILE Configuration file to use, default %s\n", _PATH_PIMD_CONF); printf(" -d, --debug[=SYS] Debug subsystem, see below for valid systems, default all\n"); printf(" -f, --foreground Run in foreground, do not detach from calling terminal\n"); printf(" -h, --help Show this help text\n"); /* printf(" -i, --show-cache Show internal cache tables\n"); */ printf(" -l, --reload-config Tell a running pimd to reload its configuration\n"); printf(" -N, --disable-vifs Disable all virtual interfaces (phyint) by default\n"); /* printf(" -p,--show-debug Show debug dump, only if debug is enabled\n"); */ printf(" -q, --quit-daemon Send SIGTERM to a running pimd\n"); printf(" -r, --show-routes Show state of VIFs and multicast routing tables\n"); printf(" -t, --table-id=ID Set multicast routing table ID. Allowed table ID#:\n" " 0 .. 999999999. Default: 0 (use default table)\n"); printf(" -s, --loglevel=LVL Set log level: none, err, info, notice (default), debug\n"); printf(" -v, --version Show %s version\n", __progname); printf("\n"); /* From pimd v2.3.0 we show *all* the debug levels again */ printf("Available subsystems for debug:\n"); for (i = 0, d = debugnames; i < ARRAY_LEN(debugnames); i++, d++) { if (strlen(line) + strlen(d->name) + 3 >= sizeof(line)) { /* Finish this line and send to console */ strlcat(line, "\n", sizeof(line)); printf("%s", line); /* Prepare for next line */ strlcpy(line, " ", sizeof(line)); } strlcat(line, d->name, sizeof(line)); if (i + 1 < ARRAY_LEN(debugnames)) strlcat(line, ", ", sizeof(line)); } /* Flush remaining line. */ strlcat(line, "\n", sizeof(line)); printf("%s", line); printf("\nBug report address: %-40s\n\n", PACKAGE_BUGREPORT); return code; } int main(int argc, char *argv[]) { int dummysigalrm, foreground = 0; struct timeval tv, difftime, curtime, lasttime, *timeout; fd_set rfds, readers; int nfds, n, i, secs, ch; struct sigaction sa; time_t boottime; struct option long_options[] = { {"config", 1, 0, 'c'}, {"debug", 2, 0, 'd'}, {"foreground", 0, 0, 'f'}, {"disable-vifs", 0, 0, 'N'}, {"help", 0, 0, 'h'}, {"version", 0, 0, 'v'}, {"quit-daemon", 0, 0, 'q'}, {"reload-config", 0, 0, 'l'}, {"show-routes", 0, 0, 'r'}, {"table-id", 1, 0, 't'}, {"syslog-level", 1, 0, 's'}, /* Compat */ {"loglevel", 1, 0, 's'}, /* {"show-cache", 0, 0, 'i'}, */ /* {"show-debug", 0, 0, 'p'}, */ {0, 0, 0, 0} }; snprintf(versionstring, sizeof (versionstring), "pimd version %s", todaysversion); while ((ch = getopt_long(argc, argv, "c:d::fhlNvqrt:s:", long_options, NULL)) != EOF) { const char *errstr; switch (ch) { case 'c': if (optarg) config_file = optarg; break; case 'd': if (!optarg) { debug = DEBUG_DEFAULT; } else { char *p,*q; size_t i, len; struct debugname *d; debug = 0; p = optarg; q = NULL; while (p) { q = strchr(p, ','); if (q) *q++ = '\0'; len = strlen(p); for (i = 0, d = debugnames; i < ARRAY_LEN(debugnames); i++, d++) { if (len >= d->nchars && strncmp(d->name, p, len) == 0) break; } if (i == ARRAY_LEN(debugnames)) return usage(1); debug |= d->level; p = q; } } break; case 'f': foreground = 1; break; case 'h': return usage(0); case 'l': return killshow(SIGHUP, NULL); case 'N': disable_all_by_default = 1; break; case 'v': printf("%s\n", versionstring); return 0; case 'q': return killshow(SIGTERM, NULL); case 'r': return killshow(SIGUSR1, _PATH_PIMD_DUMP); case 's': if (!optarg) { fprintf(stderr, "Missing loglevel argument!\n"); return usage(1); } loglevel = loglvl(optarg); if (-1 == loglevel) return usage(1); break; case 't': if (!optarg) { fprintf(stderr, "Missing Table ID argument!\n"); return usage(1); } mrt_table_id = strtonum(optarg, 0, 999999999, &errstr); if (errstr) { fprintf(stderr, "Table ID %s!\n", errstr); return usage(1); } break; #if 0 /* XXX: TODO */ case 'i': return killshow(SIGUSR2, _PATH_PIMD_CACHE); case 'p': return killshow(SIGQUIT, NULL); #endif default: return usage(1); } } argc -= optind; if (argc > 0) return usage(1); if (geteuid() != 0) errx(1, "Need root privileges to start."); setlinebuf(stderr); if (debug != 0) { struct debugname *d; char c; int tmpd = debug; fprintf(stderr, "debug level 0x%lx ", debug); c = '('; for (d = debugnames; d < debugnames + ARRAY_LEN(debugnames); d++) { if ((tmpd & d->level) == d->level) { tmpd &= ~d->level; fprintf(stderr, "%c%s", c, d->name); c = ','; } } fprintf(stderr, ")\n"); } /* * Create directory for runtime files */ if (-1 == mkdir(_PATH_PIMD_RUNDIR, 0755) && errno != EEXIST) err(1, "Failed creating %s directory for runtime files", _PATH_PIMD_RUNDIR); /* * Setup logging */ #ifdef LOG_DAEMON openlog("pimd", LOG_PID, LOG_DAEMON); setlogmask(LOG_UPTO(loglevel)); #else openlog("pimd", LOG_PID); #endif /* LOG_DAEMON */ logit(LOG_NOTICE, 0, "%s starting ...", versionstring); do_randomize(); time(&boottime); callout_init(); init_igmp(); init_pim(); init_routesock(); /* Both for Linux netlink and BSD routing socket */ init_pim_mrt(); init_timers(); /* Start up the log rate-limiter */ resetlogging(NULL); /* TODO: check the kernel DVMRP/MROUTED/PIM support version */ init_vifs(); init_rp_and_bsr(); /* Must be after init_vifs() */ #ifdef RSRR rsrr_init(); #endif /* RSRR */ sa.sa_handler = handler; sa.sa_flags = 0; /* Interrupt system calls */ sigemptyset(&sa.sa_mask); sigaction(SIGALRM, &sa, NULL); sigaction(SIGHUP, &sa, NULL); sigaction(SIGTERM, &sa, NULL); sigaction(SIGINT, &sa, NULL); sigaction(SIGUSR1, &sa, NULL); sigaction(SIGUSR2, &sa, NULL); FD_ZERO(&readers); FD_SET(igmp_socket, &readers); nfds = igmp_socket + 1; for (i = 0; i < nhandlers; i++) { FD_SET(ihandlers[i].fd, &readers); if (ihandlers[i].fd >= nfds) nfds = ihandlers[i].fd + 1; } IF_DEBUG(DEBUG_IF) dump_vifs(stderr); IF_DEBUG(DEBUG_PIM_MRT) dump_pim_mrt(stderr); /* schedule first timer interrupt */ timer_setTimer(TIMER_INTERVAL, timer, NULL); if (!debug && !foreground) { /* Detach from the terminal */ haveterminal = 0; if (fork()) exit(0); close(STDIN_FILENO); close(STDOUT_FILENO); close(STDERR_FILENO); n = open("/dev/null", O_RDWR, 0); if (n >= 0) { dup2(n, STDIN_FILENO); dup2(n, STDOUT_FILENO); dup2(n, STDERR_FILENO); } #ifdef SYSV setpgrp(); #else #ifdef TIOCNOTTY n = open("/dev/tty", 2); if (n >= 0) { (void)ioctl(n, TIOCNOTTY, (char *)0); (void)close(n); } #else if (setsid() < 0) perror("setsid"); #endif /* TIOCNOTTY */ #endif /* SYSV */ } /* End of child process code */ if (pidfile(NULL)) warn("Cannot create pidfile"); /* * Main receive loop. */ dummysigalrm = SIGALRM; difftime.tv_usec = 0; gettimeofday(&curtime, NULL); lasttime = curtime; while (1) { memcpy(&rfds, &readers, sizeof(rfds)); secs = timer_nextTimer(); if (secs == -1) timeout = NULL; else { timeout = &tv; timeout->tv_sec = secs; timeout->tv_usec = 0; } if (boottime) { time_t n; time(&n); if (n > boottime + 15) { struct rp_hold *rph = g_rp_hold; while(rph) { add_rp_grp_entry(&cand_rp_list, &grp_mask_list, rph->address, 1, (uint16_t)0xffffff, rph->group, rph->mask, curr_bsr_hash_mask, curr_bsr_fragment_tag); rph = rph->next; } boottime = 0; } } if (sighandled) { if (sighandled & GOT_SIGINT) { sighandled &= ~GOT_SIGINT; break; } if (sighandled & GOT_SIGHUP) { sighandled &= ~GOT_SIGHUP; restart(SIGHUP); /* reconstruct readers and nfds */ FD_ZERO(&readers); FD_SET(igmp_socket, &readers); nfds = igmp_socket + 1; for (i = 0; i < nhandlers; i++) { FD_SET(ihandlers[i].fd, &readers); if (ihandlers[i].fd >= nfds) nfds = ihandlers[i].fd + 1; } memcpy(&rfds, &readers, sizeof(rfds)); } if (sighandled & GOT_SIGUSR1) { sighandled &= ~GOT_SIGUSR1; fdump(SIGUSR1); } if (sighandled & GOT_SIGUSR2) { sighandled &= ~GOT_SIGUSR2; cdump(SIGUSR2); } if (sighandled & GOT_SIGALRM) { sighandled &= ~GOT_SIGALRM; timer(&dummysigalrm); } } if ((n = select(nfds, &rfds, NULL, NULL, timeout)) < 0) { if (errno != EINTR) /* SIGALRM is expected */ logit(LOG_WARNING, errno, "select failed"); continue; } if (n > 0) { /* TODO: shall check first igmp_socket for better performance? */ for (i = 0; i < nhandlers; i++) { if (FD_ISSET(ihandlers[i].fd, &rfds)) { (*ihandlers[i].func)(ihandlers[i].fd, &rfds); } } } /* * Handle timeout queue. * * If select + packet processing took more than 1 second, * or if there is a timeout pending, age the timeout queue. * * If not, collect usec in difftime to make sure that the * time doesn't drift too badly. * * If the timeout handlers took more than 1 second, * age the timeout queue again. XXX This introduces the * potential for infinite loops! */ do { /* * If the select timed out, then there's no other * activity to account for and we don't need to * call gettimeofday. */ if (n == 0) { curtime.tv_sec = lasttime.tv_sec + secs; curtime.tv_usec = lasttime.tv_usec; n = -1; /* don't do this next time through the loop */ } else gettimeofday(&curtime, NULL); difftime.tv_sec = curtime.tv_sec - lasttime.tv_sec; difftime.tv_usec += curtime.tv_usec - lasttime.tv_usec; while (difftime.tv_usec >= 1000000) { difftime.tv_sec++; difftime.tv_usec -= 1000000; } if (difftime.tv_usec < 0) { difftime.tv_sec--; difftime.tv_usec += 1000000; } lasttime = curtime; if (secs == 0 || difftime.tv_sec > 0) age_callout_queue(difftime.tv_sec); secs = -1; } while (difftime.tv_sec > 0); } /* Main loop */ logit(LOG_NOTICE, 0, "%s exiting.", versionstring); cleanup(); exit(0); } /* * The 'virtual_time' variable is initialized to a value that will cause the * first invocation of timer() to send a probe or route report to all vifs * and send group membership queries to all subnets for which this router is * querier. This first invocation occurs approximately TIMER_INTERVAL seconds * after the router starts up. Note that probes for neighbors and queries * for group memberships are also sent at start-up time, as part of initial- * ization. This repetition after a short interval is desirable for quickly * building up topology and membership information in the presence of possible * packet loss. * * 'virtual_time' advances at a rate that is only a crude approximation of * real time, because it does not take into account any time spent processing, * and because the timer intervals are sometimes shrunk by a random amount to * avoid unwanted synchronization with other routers. */ uint32_t virtual_time = 0; /* * Timer routine. Performs all perodic functions: * aging interfaces, quering neighbors and members, etc... The granularity * is equal to TIMER_INTERVAL. */ static void timer(void *i __attribute__((unused))) { age_vifs(); /* Timeout neighbors and groups */ age_routes(); /* Timeout routing entries */ age_misc(); /* Timeout the rest (Cand-RP list, etc) */ virtual_time += TIMER_INTERVAL; timer_setTimer(TIMER_INTERVAL, timer, NULL); } /* * Performs all necessary functions to quit gracefully */ /* TODO: implement all necessary stuff */ static void cleanup(void) { vifi_t vifi; struct uvif *v; /* inform all neighbors that I'm going to die */ for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { if ((v->uv_flags & (VIFF_DOWN | VIFF_DISABLED | VIFF_REGISTER | VIFF_TUNNEL)) == 0) send_pim_hello(v, 0); } #ifdef RSRR rsrr_clean(); #endif /* RSRR */ /* TODO: XXX (not in the spec): if I am the BSR, somehow inform the * other routers I am going down and need to elect another BSR? * (probably by sending a the Cand-RP-set with my_priority=LOWEST?) */ k_stop_pim(igmp_socket); } /* * Signal handler. Take note of the fact that the signal arrived * so that the main loop can take care of it. */ static void handler(int sig) { switch (sig) { case SIGALRM: sighandled |= GOT_SIGALRM; break; case SIGINT: case SIGTERM: sighandled |= GOT_SIGINT; break; case SIGHUP: sighandled |= GOT_SIGHUP; break; case SIGUSR1: sighandled |= GOT_SIGUSR1; break; case SIGUSR2: sighandled |= GOT_SIGUSR2; break; } } /* TODO: not verified */ /* * Restart the daemon */ static void restart(int i __attribute__((unused))) { logit(LOG_NOTICE, 0, "%s restarting ...", versionstring); /* * reset all the entries */ /* TODO: delete? free_all_routes(); */ free_all_callouts(); stop_all_vifs(); k_stop_pim(igmp_socket); nhandlers = 0; close(igmp_socket); close(pim_socket); /* * When IOCTL_OK_ON_RAW_SOCKET is defined, 'udp_socket' is equal * 'to igmp_socket'. Therefore, 'udp_socket' should be closed only * if they are different. */ #ifndef IOCTL_OK_ON_RAW_SOCKET close(udp_socket); #endif /* Both for Linux netlink and BSD routing socket */ close(routing_socket); /* * start processing again */ init_igmp(); init_pim(); init_routesock(); /* Both for Linux netlink and BSD routing socket */ init_pim_mrt(); init_vifs(); /* schedule timer interrupts */ timer_setTimer(TIMER_INTERVAL, timer, NULL); } static void resetlogging(void *arg) { int nxttime = 60; void *narg = NULL; if (arg == NULL && log_nmsgs >= LOG_MAX_MSGS) { nxttime = LOG_SHUT_UP; narg = (void *)&log_nmsgs; /* just need some valid void * */ syslog(LOG_WARNING, "logging too fast, shutting up for %d minutes", LOG_SHUT_UP / 60); } else { if (arg != NULL) { syslog(LOG_NOTICE, "logging enabled again after rate limiting"); } log_nmsgs = 0; } timer_setTimer(nxttime, resetlogging, narg); } /** * Local Variables: * version-control: t * indent-tabs-mode: t * c-file-style: "ellemtel" * c-basic-offset: 4 * End: */